End-to-end Testing with Playwright
Learning Objectives
- You know how to add Playwright to a project for end-to-end testing.
- You know how to run Playwright tests using Docker.
Next, we setup Playwright for end-to-end testing. Playwright is a tool for automating browsers, and it can be used to test web applications.
Setting up e2e-tests directory for Playwright
To create a directory e2e-tests
and to setup Playwright, run the command deno run -A npm:create-playwright@latest e2e-tests
in the root folder of the project. The command creates a folder called e2e-tests
and initiates a project for running Playwright tests. The command asks for a few options — use the following:
If the command does not work as expected, skip to the section “Creating the e2e-tests folder manually”.
- Pick “JavaScript when asked whether you want to use TypeScript or JavaScript.
- Pick “tests” when asked where to put the end-to-end tests.
- Pick “false” when asked whether to add a GitHub Actions workflow.
- Pick “false” when asked whether to install Playwright browsers.
- Pick “false” when asked whether to install Playwright operating system dependencies.
Once done, the project has a new folder e2e-tests
with the following contents (omitting node_modules
).
cd e2e-tests
tree --dirsfirst
.
├── node_modules
│ └── ...
├── tests
│ └── example.spec.js
├── tests-examples
│ └── demo-todo-app.spec.js
├── package.json
├── package-lock.json
└── playwright.config.js
Creating the e2e-tests folder manually
If the command deno run -A npm:create-playwright@latest e2e-tests
does not work as expected, you can create the folder manually. To do this, create a folder e2e-tests
in the root folder of the project and do the following steps:
- Create a folder
tests
under the foldere2e-tests
, and create a file calledexample.spec.js
to the folder. Copy the following contents to the file.
// @ts-check
const { test, expect } = require('@playwright/test');
test('has title', async ({ page }) => {
await page.goto('https://playwright.dev/');
// Expect a title "to contain" a substring.
await expect(page).toHaveTitle(/Playwright/);
});
-
Create a file called
playwright.config.js
to the foldere2e-tests
. Copy the text “TODO” to the file. The actual contents needed will be shown in the section “Cleaning up the Playwright configuration” in a second. -
Create a file called
package.json
to the foldere2e-tests
and copy the following contents to the file.
{
"name": "e2e-tests",
"version": "1.0.0",
"main": "index.js",
"scripts": {},
"keywords": [],
"author": "",
"license": "ISC",
"description": "",
"devDependencies": {
"@playwright/test": "^1.48.2",
"@types/node": "^22.8.6"
}
}
Remember to save the files!
Once ready, run the command deno install --allow-scripts
in the folder. Now, you should be able to proceed.
Cleaning up the Playwright configuration
The playwright.config.js
provides the configuration for Playwright. For our purpose, the configuration is a bit excessive, so let’s cut it down. Modify the configuration to match the following.
const { defineConfig, devices } = require('@playwright/test');
/**
* @see https://playwright.dev/docs/test-configuration
*/
module.exports = defineConfig({
testDir: './tests',
reporter: 'list',
use: {
baseURL: 'http://localhost:5173',
trace: 'off',
},
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
],
});
In effect, we declare the directory for the tests and ask for the test results to be printed in a list format. Furthermore, we use http://localhost:5173
for the starting point for the tests, do not collect test traces, and run the tests using Chromium.
We’ll be running the tests with Docker and will download the necessary components there.
Playwright and Docker
To run the tests with Docker, we need to create a Dockerfile for Playwright.
Before starting, remove the
tests-examples
andnode_modules
folders from from thee2e-tests
folder, and remove also thepackage-lock.json
file. If you created thee2e-tests
folder manually, these folders / files do not exist.
Create a file called Dockerfile
into the folder e2e-tests
with the following contents.
FROM mcr.microsoft.com/playwright:v1.48.1-jammy
WORKDIR /app
COPY package*.json .
COPY *config.js .
RUN npm install
RUN npx playwright install chromium
COPY . .
CMD [ "npx", "playwright", "test" ]
The above configuration uses the image mcr.microsoft.com/playwright:v1.48.1-jammy
as the base image, creates a folder /app
to the image and copies the configuration files to the folder. Then, we install the dependencies and the Chromium browser, which are used to run the tests. This is followed by copying the rest of the contents (i.e., the tests) to the image and finally, at the last line, the image is configured to run the tests using the command npx playwright test
.
Next, we wish to add Playwright to the Docker Compose configuration to allow us to run the tests using Docker Compose. To do this, we add the following service to the compose.yml
file.
e2e-tests:
entrypoint: "/bin/true"
build: e2e-tests
network_mode: host
depends_on:
- client
volumes:
- ./e2e-tests/tests:/app/tests
The above configuration creates a service called e2e-tests
that uses the Dockerfile we just created. The entrypoint
is used to indicate that we do not wish to run the tests by default (i.e., whenever we run docker compose up
). The service is configured to use the host network, which allows the service to access applications running on the host machine. The service depends on the client
service, which is the user interface of the application. Finally, the service is configured to map the tests in the folder e2e-tests/tests
to the container.
At this point, the whole compose.yaml
file is as follows.
services:
client:
build: client
restart: unless-stopped
volumes:
- ./client/src:/app/src
ports:
- 5173:5173
depends_on:
- server
e2e-tests:
entrypoint: "/bin/true"
build: e2e-tests
network_mode: host
depends_on:
- client
volumes:
- ./e2e-tests/tests:/app/tests
server:
build: server
restart: unless-stopped
volumes:
- ./server:/app
ports:
- 8000:8000
With the adjustments to the configuration, run the command docker compose up --build
to start the services and to create the Playwright image — this takes a while on the first.
Running Playwright tests
With the project running, we can run the tests.
The tests are run using the command docker compose run --rm --entrypoint=npx e2e-tests playwright test
in the root folder of the project. Note! Keep the project running in a separate terminal window.
The command runs the tests and prints the results in the terminal, removing a created test container after it has finished.
docker compose run --rm --entrypoint=npx e2e-tests playwright test
[+] Creating 2/0
✔ Container wsd-walking-skeleton-api-1 Running
✔ Container wsd-walking-skeleton-client-1 Running
Running 2 tests using 1 worker
✓ 1 [chromium] › example.spec.js:4:1 › has title (879ms)
✓ 2 [chromium] › example.spec.js:11:1 › get started link (1.1s)
2 passed (2.8s)
If you created the
e2e-tests
folder manually, there’s just one test instead of two.
Testing our application
The tests that come with Playwright by default test the Playwright website website. Modify the test file example.spec.js
in the folder tests
of the Playwright project to match the following.
const { test, expect } = require("@playwright/test");
test('Pressing "Fetch message" shows message.', async ({ page }) => {
await page.goto("/");
// hydration hack, more on this later on in the course
await page.waitForTimeout(1000);
await page.getByRole("button", { name: "Fetch message" }).click();
await expect(page.getByText("Message is: Hello world!")).toBeVisible();
});
Now, when you run the tests, you see the following output.
$ docker compose run --rm --entrypoint=npx e2e-tests playwright test
[+] Creating 2/0
✔ Container wsd-walking-skeleton-api-1 Running 0.0s
✔ Container wsd-walking-skeleton-client-1 Running 0.0s
Running 1 test using 1 worker
✓ 1 [chromium] › example.spec.js:4:1 › Pressing "Fetch message" shows message. (1.4s)
1 passed (2.9s)
The test passes. At this point, our walking skeleton has a server that responds to messages, a client that can be used to fetch a message from the server and to show it, and end-to-end tests that can be used to verify that the client works as expected.