⚠️ This project is not ready for production. Stay tuned! ⚠️
🎭 Playwright test runner
Cross-browser end-to-end testing for web apps. Browser automation with Playwright, Jest-like assertions and built-in support for TypeScript.
Playwright test runner is available in preview and minor breaking changes could happen. We welcome your feedback to shape this towards 1.0.
Get started
Installation
npm i -D @playwright/test
Write a test
Create tests/example.spec.ts
to define your test. The test function uses the page
argument for browser automation.
const { test, expect } = require("@playwright/test");
test("is a basic test with the page", async ({ page }) => {
await page.goto("https://playwright.dev/");
const name = await page.innerText(".navbar__title");
expect(name).toBe("Playwright");
});
TypeScript version
import { test, expect } from "@playwright/test";
test("is a basic test with the page", async ({ page }) => {
await page.goto("https://playwright.dev/");
const name = await page.innerText(".navbar__title");
expect(name).toBe("Playwright");
});
Playwright test runner is based on the Folio framework, and includes all the features available in Folio. For example, Playwright test runner provides test fixtures for browser primitives.
These browser primitives are available as arguments to your test functions, and can be used one by one or together.
page
: Instance of Page. Each test gets a new isolated page to run the test.context
: Instance of BrowserContext. Each test gets a new isolated context to run the test. The page
object belongs to this context. Learn how to configure context creation.browser
: Instance of Browser. Browsers are shared across tests to optimize resources. Learn how to configure browser launch.browserName
: The name of the browser currently running the test. Either chromium
, firefox
or webkit
.
You can now run the test using the underlying Folio command line:
npx pwtest -c tests
Tests and assertions syntax
- Use
test
to write test functions. Run a single test with test.only
and skip a test with test.skip
. - Use
test.describe
to group related tests together. - Use
test.beforeAll
and test.afterAll
hooks to set up and tear down resources shared between tests. - Use
test.beforeEach
and test.afterEach
hooks to set up and tear down resources for each test individually. - For assertions, use the
expect
API.
const { test, expect } = require("@playwright/test");
test.describe("feature foo", () => {
test.beforeEach(async ({ page }) => {
await page.goto("https://my.start.url/feature-foo");
});
test("is working correctly", async ({ page }) => {
expect(page.url()).toBe("https://my.start.url/feature-foo");
});
});
TypeScript version
import { test, expect } from "@playwright/test";
test.describe("feature foo", () => {
test.beforeEach(async ({ page }) => {
await page.goto("https://my.start.url/feature-foo");
});
test("is working correctly", async ({ page }) => {
expect(page.url()).toBe("https://my.start.url/feature-foo");
});
});
Write a configuration file
Create pwtest.config.ts
file to configure your tests: specify browser launch options, run tests in multiple browsers and much more. Here is an example configuration that runs every test in Chromium, Firefox and WebKit.
module.exports = {
timeout: 30000,
testDir: 'tests',
projects: [
{
name: 'chromium',
use: {
browserName: 'chromium',
headless: true,
viewport: { width: 1280, height: 720 },
},
},
{
name: 'webkit',
use: {
browserName: 'webkit',
headless: true,
viewport: { width: 1280, height: 720 },
},
},
{
name: 'firefox',
use: {
browserName: 'firefox',
headless: true,
viewport: { width: 1280, height: 720 },
},
}
],
};
TypeScript version
import { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
timeout: 30000,
testDir: 'tests',
projects: [
{
name: 'chromium',
use: {
browserName: 'chromium',
headless: true,
viewport: { width: 1280, height: 720 },
},
},
{
name: 'webkit',
use: {
browserName: 'webkit',
headless: true,
viewport: { width: 1280, height: 720 },
},
},
{
name: 'firefox',
use: {
browserName: 'firefox',
headless: true,
viewport: { width: 1280, height: 720 },
},
}
],
};
export default config;
Run the test suite
Tests can be run in single or multiple browsers, in parallel or sequentially, using the underlying Folio command line:
-
Run all tests across Chromium, Firefox and WebKit
npx pwtest
-
Run tests on a single browser
npx pwtest --project=chromium
-
Run tests sequentially
npx pwtest --workers=1
-
Retry failing tests
npx pwtest --retries=2
-
See all options
npx pwtest --help
Refer to the command line documentation for all options.
Configure NPM scripts
Save the run command as an NPM script.
{
"scripts": {
"test": "npx pwtest"
}
}
Examples
Multiple pages
The default context
argument is a BrowserContext. Browser contexts are isolated execution environments that can host multiple pages. See multi-page scenarios for more examples.
const { test } = require("@playwright/test");
test("tests on multiple web pages", async ({ context }) => {
const pageFoo = await context.newPage();
const pageBar = await context.newPage();
});
TypeScript version
import { test } from "@playwright/test";
test("tests on multiple web pages", async ({ context }) => {
const pageFoo = await context.newPage();
const pageBar = await context.newPage();
});
Mobile emulation
use
section in the configuration file can be used to configure mobile emulation in the default context
.
const { devices } = require("playwright");
module.exports = {
timeout: 30000,
testDir: 'tests',
projects: [
{
name: 'chromium',
use: {
browserName: 'chromium',
headless: true,
...devices["Pixel 2"],
},
},
],
};
TypeScript version
import { PlaywrightTestConfig } from "@playwright/test";
import { devices } from "playwright";
const config: PlaywrightTestConfig = {
timeout: 30000,
testDir: 'tests',
projects: [
{
name: 'chromium',
use: {
browserName: 'chromium',
headless: true,
...devices["Pixel 2"],
},
},
],
};
export default config;
Network mocking
Define a custom route that mocks network calls for a browser context.
const { test, expect } = require("@playwright/test");
test.beforeEach(async ({ context }) => {
await context.route(/.css/, route => route.abort());
});
test("loads page without css", async ({ page }) => {
await page.route(/.png/, route => route.abort());
await page.goto("https://stackoverflow.com");
});
TypeScript version
import { test, expect } from "@playwright/test";
test.beforeEach(async ({ context }) => {
await context.route(/.css/, route => route.abort());
});
test("loads page without css", async ({ page }) => {
await page.route(/.png/, route => route.abort());
await page.goto("https://stackoverflow.com");
});
Visual comparisons
The expect
API supports visual comparisons with toMatchSnapshot
. This uses the pixelmatch library, and you can pass threshold
as an option.
const { test, expect } = require("@playwright/test");
test("compares page screenshot", async ({ page }) => {
await page.goto("https://stackoverflow.com");
const screenshot = await page.screenshot();
expect(screenshot).toMatchSnapshot(`test.png`, { threshold: 0.2 });
});
TypeScript version
import { test, expect } from "@playwright/test";
test("compares page screenshot", async ({ page }) => {
await page.goto("https://stackoverflow.com");
const screenshot = await page.screenshot();
expect(screenshot).toMatchSnapshot(`test.png`, { threshold: 0.2 });
});
On first execution, this will generate golden snapshots. Subsequent runs will compare against the golden snapshots. To update golden snapshots with new actuals, run with the --update-snapshots
flag.
npx pwtest --update-snapshots
Page object model
To introduce a Page Object for a particular page, create a class that will use the page
object.
Create a LoginPage
helper class to encapsulate common operations on the login page.
class LoginPage {
constructor(page: Page) {
this.page = page;
}
async goto() {
await this.page.goto("https://example.com/login");
}
async login() {
await this.page.fill("#username", TEST_USERNAME);
await this.page.fill("#password", TEST_PASSWORD);
await this.page.click("text=Login");
}
}
exports.LoginPage = LoginPage;
TypeScript version
import type { Page } from "playwright";
export class LoginPage {
page: Page;
constructor(page: Page) {
this.page = page;
}
async goto() {
await this.page.goto("https://example.com/login");
}
async login() {
await this.page.fill("#username", TEST_USERNAME);
await this.page.fill("#password", TEST_PASSWORD);
await this.page.click("text=Login");
}
}
Use the LoginPage
class in the tests.
const { test, expect } = require("@playwright/test");
const { LoginPage } = require("./login-page");
test("login works", async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login();
expect(await page.textContent("#user-info")).toBe("Welcome, Test User!");
});
TypeScript version
import { test, expect } from "@playwright/test";
import { LoginPage } from "./login-page";
test("login works", async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login();
expect(await page.textContent("#user-info")).toBe("Welcome, Test User!");
});
Configuration
Modify options
You can modify browser launch options, context creation options and testing options either globally in the configuration file, or locally in the test file.
Playwright test runner is based on the Folio framework, so it supports any configuration available in Folio, and adds a lot of Playwright-specific options.
Globally in the configuration file
You can specify different options for each browser using projects in the configuration file. Below is an example that changes some global testing options, and Chromium browser configuration.
module.exports = {
timeout: 90000,
retries: 2,
testDir: 'tests',
projects: [
{
name: 'chromium',
use: {
browserName: 'chromium',
headless: false,
slowMo: 50,
viewport: { width: 800, height: 600 },
ignoreHTTPSErrors: true,
video: 'retain-on-failure',
},
},
],
};
TypeScript version
import { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
timeout: 90000,
retries: 2,
testDir: 'tests',
projects: [
{
name: 'chromium',
use: {
browserName: 'chromium',
headless: false,
slowMo: 50,
viewport: { width: 800, height: 600 },
ignoreHTTPSErrors: true,
video: 'retain-on-failure',
},
},
],
};
export default config;
Locally in the test file
With test.use()
you can override some options for a file, or a describe
block.
const { test, expect } = require("@playwright/test");
test.use({ viewport: { width: 600, height: 900 } });
test('my test', async ({ page }) => {
});
TypeScript version
import { test, expect } from "@playwright/test";
test.use({ viewport: { width: 600, height: 900 } });
test('my test', async ({ page }) => {
});
Available options
See the full list of launch options in browserType.launch()
documentation.
See the full list of context options in browser.newContext()
documentation.
Available testing options:
screenshot: 'off' | 'on' | 'only-on-failure'
- Whether to capture a screenshot after each test, off by default.
off
- Do not capture screenshots.on
- Capture screenshot after each test.only-on-failure
- Capture screenshot after each test failure.
video: 'off' | 'on' | 'retain-on-failure' | 'retry-with-video'
- Whether to record video for each test, off by default.
off
- Do not record video.on
- Record video for each test.retain-on-failure
- Record video for each test, but remove all videos from successful test runs.retry-with-video
- Record video only when retrying a test.
Most notable testing options from Folio documentation:
forbidOnly: boolean
- Whether to exit with an error if any tests are marked as test.only
. Useful on CI.reporter: 'dot' | 'line' | 'list'
- Choose a reporter: minimalist dot
, concise line
or detailed list
. See Folio reporters for more details.retries: number
- Each failing test will be retried up to the certain number of times.testDir: string
- Directory where test runner should search for test files.testMatch: (string | RegExp)[]
- Patterns to find test files, for example /.*spec\.js/
.testIgnore: (string | RegExp)[]
- Patterns to ignore when looking for test files, for example /test_assets/
.timeout: number
- Timeout in milliseconds for each test.workers: number
- The maximum number of worker processes to run in parallel.
Skip tests with annotations
The Playwright test runner can annotate tests to skip under certain parameters. This is enabled by Folio annotations.
const { test, expect } = require("@playwright/test");
test("should be skipped on firefox", async ({ page, browserName }) => {
test.skip(browserName === "firefox", "optional description for the skip");
});
TypeScript version
import { test, expect } from "@playwright/test";
test("should be skipped on firefox", async ({ page, browserName }) => {
test.skip(browserName === "firefox", "optional description for the skip");
});
Run tests in parallel
Tests are run in parallel by default, using multiple worker processes. You can control the parallelism with the workers
option in the configuration file or from the command line.
-
Run just a single test at a time - no parallelization
npx pwtest --workers=1
-
Run up to 10 tests in parallel
npx pwtest --workers=10
-
Different value for CI
module.exports = {
worker: process.env.CI ? 1 : undefined,
testDir: 'tests',
projects: [
],
};
TypeScript version
import { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
worker: process.env.CI ? 1 : undefined,
testDir: 'tests',
projects: [
],
};
export default config;
By default, test runner chooses the number of workers based on available CPUs.
Reporters
Playwright test runner comes with a few built-in reporters for different needs and ability to provide custom reporters. The easiest way to try out built-in reporters is to pass --reporter
command line option. Built-in terminal reporters are minimalist dot
, concise line
and detailed list
.
npx pwtest --reporter=line
npx pwtest --reporter=dot
npx pwtest --reporter=list
Alternatively, you can specify the reporter in the configuration file.
module.exports = {
reporter: process.env.CI ? 'dot' : 'line',
testDir: 'tests',
projects: [
],
};
TypeScript version
import { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
reporter: process.env.CI ? 'dot' : 'line',
testDir: 'tests',
projects: [
],
};
export default config;
Export JUnit or JSON report
The Playwright test runner includes reporters that produce a JUnit compatible XML file or a JSON file with test results.
module.exports = {
testDir: 'tests',
reporter: [
'list',
{ name: 'junit', outputFile: 'report.xml' },
{ name: 'json', outputFile: 'report.json' },
],
projects: [
],
};
TypeScript version
import { PlaywrightTestConfig } from "@playwright/test";
const config: PlaywrightTestConfig = {
testDir: 'tests',
reporter: [
'list',
{ name: 'junit', outputFile: 'report.xml' },
{ name: 'json', outputFile: 'report.json' },
],
projects: [
],
};
export default config;