2021-05-27 20:30:03 -07:00
---
id: test-advanced
2021-05-31 22:01:21 -07:00
title: "Advanced: configuration"
2021-05-27 20:30:03 -07:00
---
<!-- TOC -->
2021-06-02 20:16:10 -07:00
## Configuration object
2021-05-30 15:14:44 -07:00
2021-12-10 11:15:01 -08:00
Configuration file exports a single [TestConfig] object. See [TestConfig] properties for available configuration options.
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
Note that each [test project ](#projects ) can provide its own [options][TestProject], for example two projects can run different tests by providing different `testDir` s.
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
Here is an example that defines a common timeout and two projects. The "Smoke" project runs a small subset of tests without retries, and "Default" project runs all other tests with retries.
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
```js js-flavor=ts
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test ';
const config: PlaywrightTestConfig = {
timeout: 60000, // Timeout is shared between all tests.
projects: [
{
name: 'Smoke',
testMatch: /.*smoke.spec.ts/,
retries: 0,
},
{
name: 'Default',
testIgnore: /.*smoke.spec.ts/,
retries: 2,
},
],
};
export default config;
```
2021-05-27 20:30:03 -07:00
2021-06-02 20:16:10 -07:00
```js js-flavor=js
2021-12-10 11:15:01 -08:00
// playwright.config.js
// @ts -check
/** @type {import('@playwright/test ').PlaywrightTestConfig} */
const config = {
timeout: 60000, // Timeout is shared between all tests.
projects: [
{
name: 'Smoke',
testMatch: /.*smoke.spec.ts/,
retries: 0,
},
{
name: 'Default',
testIgnore: /.*smoke.spec.ts/,
retries: 2,
},
],
};
module.exports = config;
2021-06-02 20:16:10 -07:00
```
2021-12-10 11:15:01 -08:00
## TestInfo object
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
Test functions, fixtures and hooks receive a [TestInfo] parameter that provides information about the currently running test as well as some useful utilities that include:
- Information about the test, for example `title` , `config` and `project` .
- Information about test execution, for example `expectedStatus` and `status` .
- Test artifact utilities, for example `outputPath()` and `attach()` .
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
See [TestInfo] methods and properties for all available information and utilities.
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
Here is an example test that saves information to a file using [TestInfo].
2021-06-02 20:16:10 -07:00
```js js-flavor=js
// example.spec.js
2021-06-03 14:46:58 -07:00
const { test } = require('@playwright/test ');
2021-06-02 20:16:10 -07:00
test('my test needs a file', async ({ table }, testInfo) => {
// Do something with the table...
// ... and then save contents.
const filePath = testInfo.outputPath('table.dat');
await table.saveTo(filePath);
});
```
```js js-flavor=ts
2021-05-27 20:30:03 -07:00
// example.spec.ts
2021-06-03 14:46:58 -07:00
import { test } from '@playwright/test ';
2021-05-27 20:30:03 -07:00
test('my test needs a file', async ({ table }, testInfo) => {
// Do something with the table...
// ... and then save contents.
const filePath = testInfo.outputPath('table.dat');
await table.saveTo(filePath);
});
```
2021-12-10 11:15:01 -08:00
Here is an example fixture that automatically saves debug logs when the test fails.
2021-06-02 20:16:10 -07:00
```js js-flavor=js
// my-test.js
const debug = require('debug');
const fs = require('fs');
2021-06-03 14:46:58 -07:00
const base = require('@playwright/test ');
2021-06-02 20:16:10 -07:00
// Note how we mark the fixture as { auto: true }.
// This way it is always instantiated, even if the test does not use it explicitly.
2021-06-21 19:56:30 +02:00
exports.test = base.test.extend({
2021-06-02 20:16:10 -07:00
saveLogs: [ async ({}, use, testInfo) => {
const logs = [];
debug.log = (...args) => logs.push(args.map(String).join(''));
debug.enable('mycomponent');
await use();
if (testInfo.status !== testInfo.expectedStatus)
fs.writeFileSync(testInfo.outputPath('logs.txt'), logs.join('\n'), 'utf8');
}, { auto: true } ]
});
```
```js js-flavor=ts
2021-05-27 20:30:03 -07:00
// my-test.ts
import * as debug from 'debug';
import * as fs from 'fs';
2021-06-03 14:46:58 -07:00
import { test as base } from '@playwright/test ';
2021-05-27 20:30:03 -07:00
// Note how we mark the fixture as { auto: true }.
// This way it is always instantiated, even if the test does not use it explicitly.
2021-06-02 20:16:10 -07:00
export const test = base.extend< { saveLogs: void }>({
2021-05-27 20:30:03 -07:00
saveLogs: [ async ({}, use, testInfo) => {
const logs = [];
debug.log = (...args) => logs.push(args.map(String).join(''));
debug.enable('mycomponent');
2021-06-02 20:16:10 -07:00
2021-05-27 20:30:03 -07:00
await use();
2021-06-02 20:16:10 -07:00
2021-05-27 20:30:03 -07:00
if (testInfo.status !== testInfo.expectedStatus)
fs.writeFileSync(testInfo.outputPath('logs.txt'), logs.join('\n'), 'utf8');
}, { auto: true } ]
});
```
2021-08-03 23:24:14 +02:00
## Launching a development web server during the tests
To launch a server during the tests, use the `webServer` option in the [configuration file ](#configuration-object ).
2022-02-08 15:57:36 -08:00
If `port` is specified in the config, test runner will wait for `127.0.0.1:port` or `::1:port` to be available before running the tests.
If `url` is specified in the config, test runner will wait for that `url` to return 2xx response before running the tests.
2021-08-03 23:24:14 +02:00
2022-02-08 15:57:36 -08:00
For continuous integration, you may want to use the `reuseExistingServer: !process.env.CI` option which does not use an existing server on the CI. To see the stdout, you can set the `DEBUG=pw:webserver` environment variable.
The `port` (but not the `url` ) gets passed over to Playwright as a [`property: TestOptions.baseURL` ]. For example port `8080` produces `baseURL` equal `http://localhost:8080` .
:::note
It is also recommended to specify [`property: TestOptions.baseURL` ] in the config, so that tests could use relative urls.
:::
2021-08-03 23:24:14 +02:00
```js js-flavor=ts
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test ';
const config: PlaywrightTestConfig = {
webServer: {
command: 'npm run start',
2022-03-23 16:07:30 -07:00
url: 'http://localhost:3000/app/',
2021-08-03 23:24:14 +02:00
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
},
2022-03-23 16:07:30 -07:00
use: {
baseURL: 'http://localhost:3000/app/',
},
2021-08-03 23:24:14 +02:00
};
export default config;
```
```js js-flavor=js
// playwright.config.js
// @ts -check
/** @type {import('@playwright/test ').PlaywrightTestConfig} */
const config = {
webServer: {
command: 'npm run start',
2022-03-23 16:07:30 -07:00
url: 'http://localhost:3000/app/',
2021-08-03 23:24:14 +02:00
timeout: 120 * 1000,
reuseExistingServer: !process.env.CI,
},
2022-03-23 16:07:30 -07:00
use: {
baseURL: 'http://localhost:3000/app/',
},
2021-08-03 23:24:14 +02:00
};
2021-08-12 07:58:00 -07:00
module.exports = config;
2021-08-03 23:24:14 +02:00
```
2022-03-23 16:07:30 -07:00
Now you can use a relative path when navigating the page:
2021-08-03 23:24:14 +02:00
```js js-flavor=ts
// test.spec.ts
2021-09-01 19:24:28 +03:00
import { test } from '@playwright/test ';
2022-03-23 16:07:30 -07:00
test('test', async ({ page }) => {
// baseURL is set in the config to http://localhost:3000/app/
// This will navigate to http://localhost:3000/app/login
await page.goto('./login');
2021-08-03 23:24:14 +02:00
});
```
```js js-flavor=js
// test.spec.js
const { test } = require('@playwright/test ');
2022-03-23 16:07:30 -07:00
test('test', async ({ page }) => {
// baseURL is set in the config to http://localhost:3000/app/
// This will navigate to http://localhost:3000/app/login
await page.goto('./login');
2021-08-03 23:24:14 +02:00
});
```
2021-05-27 20:30:03 -07:00
## Global setup and teardown
2021-09-01 15:35:46 -07:00
To set something up once before running all tests, use `globalSetup` option in the [configuration file ](#configuration-object ). Global setup file must export a single function that takes a config object. This function will be run once before all the tests.
2021-05-27 20:30:03 -07:00
2021-06-17 17:21:22 -07:00
Similarly, use `globalTeardown` to run something once after all the tests. Alternatively, let `globalSetup` return a function that will be used as a global teardown. You can pass data such as port number, authentication tokens, etc. from your global setup to your tests using environment.
2021-06-02 20:16:10 -07:00
2021-09-01 15:35:46 -07:00
Here is a global setup example that authenticates once and reuses authentication state in tests. It uses `baseURL` and `storageState` options from the configuration file.
2021-06-02 20:16:10 -07:00
```js js-flavor=js
// global-setup.js
2021-09-01 15:35:46 -07:00
const { chromium } = require('@playwright/test ');
module.exports = async config => {
const { baseURL, storageState } = config.projects[0].use;
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto(baseURL);
await page.fill('input[name="user"]', 'user');
await page.fill('input[name="password"]', 'password');
await page.click('text=Sign in');
await page.context().storageState({ path: storageState });
await browser.close();
2021-06-02 20:16:10 -07:00
};
```
```js js-flavor=ts
2021-05-27 20:30:03 -07:00
// global-setup.ts
2021-09-01 15:35:46 -07:00
import { chromium, FullConfig } from '@playwright/test ';
async function globalSetup(config: FullConfig) {
const { baseURL, storageState } = config.projects[0].use;
const browser = await chromium.launch();
const page = await browser.newPage();
2021-09-13 17:50:31 -07:00
await page.goto(baseURL!);
2021-09-01 15:35:46 -07:00
await page.fill('input[name="user"]', 'user');
await page.fill('input[name="password"]', 'password');
await page.click('text=Sign in');
2021-09-13 17:50:31 -07:00
await page.context().storageState({ path: storageState as string });
2021-09-01 15:35:46 -07:00
await browser.close();
2021-06-02 20:16:10 -07:00
}
2021-09-01 15:35:46 -07:00
2021-06-02 20:16:10 -07:00
export default globalSetup;
2021-05-27 20:30:03 -07:00
```
2021-09-01 15:35:46 -07:00
Specify `globalSetup` , `baseURL` and `storageState` in the configuration file.
2021-06-17 17:21:22 -07:00
2021-06-02 20:16:10 -07:00
```js js-flavor=js
// playwright.config.js
2021-06-21 19:56:30 +02:00
// @ts -check
/** @type {import('@playwright/test ').PlaywrightTestConfig} */
const config = {
2021-06-09 17:39:49 -07:00
globalSetup: require.resolve('./global-setup'),
2021-09-01 15:35:46 -07:00
use: {
baseURL: 'http://localhost:3000/',
storageState: 'state.json',
},
2021-05-27 20:30:03 -07:00
};
2021-06-21 19:56:30 +02:00
module.exports = config;
2021-05-27 20:30:03 -07:00
```
2021-06-02 20:16:10 -07:00
```js js-flavor=ts
// playwright.config.ts
2021-06-03 14:46:58 -07:00
import { PlaywrightTestConfig } from '@playwright/test ';
2021-05-27 20:30:03 -07:00
const config: PlaywrightTestConfig = {
2021-06-09 17:39:49 -07:00
globalSetup: require.resolve('./global-setup'),
2021-09-01 15:35:46 -07:00
use: {
baseURL: 'http://localhost:3000/',
storageState: 'state.json',
},
2021-05-27 20:30:03 -07:00
};
export default config;
```
2021-09-01 15:35:46 -07:00
Tests start already authenticated because we specify `storageState` that was populated by global setup.
2021-06-17 17:21:22 -07:00
2021-09-01 15:35:46 -07:00
```js js-flavor=ts
import { test } from '@playwright/test ';
2021-06-17 17:21:22 -07:00
2021-09-01 15:35:46 -07:00
test('test', async ({ page }) => {
await page.goto('/');
// You are signed in!
2021-06-17 17:21:22 -07:00
});
```
2021-09-01 15:35:46 -07:00
```js js-flavor=js
const { test } = require('@playwright/test ');
2021-06-17 17:21:22 -07:00
2021-09-01 15:35:46 -07:00
test('test', async ({ page }) => {
await page.goto('/');
// You are signed in!
2021-06-17 17:21:22 -07:00
});
```
2021-06-02 20:16:10 -07:00
## Projects
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
Playwright Test supports running multiple test projects at the same time. This is useful for running the same or different tests in multiple configurations.
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
### Same tests, different configuration
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
Here is an example that runs the same tests in different browsers:
2021-06-02 20:16:10 -07:00
```js js-flavor=js
2021-12-10 11:15:01 -08:00
// playwright.config.js
// @ts -check
const { devices } = require('@playwright/test ');
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
/** @type {import('@playwright/test ').PlaywrightTestConfig} */
const config = {
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
};
module.exports = config;
2021-06-02 20:16:10 -07:00
```
2021-05-27 20:30:03 -07:00
2021-06-02 20:16:10 -07:00
```js js-flavor=ts
2021-12-10 11:15:01 -08:00
// playwright.config.ts
import { PlaywrightTestConfig, devices } from '@playwright/test ';
2021-06-02 20:16:10 -07:00
2021-12-10 11:15:01 -08:00
const config: PlaywrightTestConfig = {
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
};
export default config;
2021-05-27 20:30:03 -07:00
```
2021-12-10 11:15:01 -08:00
You can run all projects or just a single one:
```bash
# Run both projects - each test will be run three times
npx playwright test
2021-06-02 20:16:10 -07:00
2021-12-10 11:15:01 -08:00
# Run a single project - each test will be run once
npx playwright test --project=chromium
2021-06-02 20:16:10 -07:00
```
2021-12-10 11:15:01 -08:00
### Different tests, different configuration
2021-06-02 20:16:10 -07:00
2021-12-10 11:15:01 -08:00
Each project can be configured separately, and run different set of tests with different options. You can use [`property: TestProject.testDir` ], [`property: TestProject.testMatch` ] and [`property: TestProject.testIgnore` ] to configure which tests should the project run.
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
Here is an example that runs projects with different tests and configurations. The "Smoke" project runs a small subset of tests without retries, and "Default" project runs all other tests with retries.
2021-06-21 19:56:30 +02:00
2021-12-10 11:15:01 -08:00
```js js-flavor=ts
// playwright.config.ts
import { PlaywrightTestConfig } from '@playwright/test ';
const config: PlaywrightTestConfig = {
timeout: 60000, // Timeout is shared between all tests.
2021-06-02 20:16:10 -07:00
projects: [
{
2021-12-10 11:15:01 -08:00
name: 'Smoke',
testMatch: /.*smoke.spec.ts/,
retries: 0,
2021-06-02 20:16:10 -07:00
},
{
2021-12-10 11:15:01 -08:00
name: 'Default',
testIgnore: /.*smoke.spec.ts/,
retries: 2,
2021-06-02 20:16:10 -07:00
},
2021-12-10 11:15:01 -08:00
],
2021-06-02 20:16:10 -07:00
};
2021-12-10 11:15:01 -08:00
export default config;
2021-06-02 20:16:10 -07:00
```
2021-12-10 11:15:01 -08:00
```js js-flavor=js
// playwright.config.js
// @ts -check
/** @type {import('@playwright/test ').PlaywrightTestConfig} */
const config = {
timeout: 60000, // Timeout is shared between all tests.
2021-06-02 20:16:10 -07:00
projects: [
{
2021-12-10 11:15:01 -08:00
name: 'Smoke',
testMatch: /.*smoke.spec.ts/,
retries: 0,
2021-06-02 20:16:10 -07:00
},
{
2021-12-10 11:15:01 -08:00
name: 'Default',
testIgnore: /.*smoke.spec.ts/,
retries: 2,
2021-06-02 20:16:10 -07:00
},
2021-12-10 11:15:01 -08:00
],
2021-05-27 20:30:03 -07:00
};
2021-12-10 11:15:01 -08:00
module.exports = config;
2021-05-27 20:30:03 -07:00
```
2021-06-02 20:16:10 -07:00
You can run all projects or just a single one:
```bash
2021-12-10 11:15:01 -08:00
# Run both projects
2021-06-02 20:16:10 -07:00
npx playwright test
2021-05-27 20:30:03 -07:00
2021-12-10 11:15:01 -08:00
# Run a single project
npx playwright test --project=Smoke
2021-06-02 20:16:10 -07:00
```
2021-12-10 11:15:01 -08:00
### Custom project parameters
Projects can be also used to parametrize tests with your custom configuration - take a look at [this separate guide ](./test-parameterize.md#parameterized-projects ).
## WorkerInfo object
Depending on the configuration and failures, Playwright Test might use different number of worker processes to run all the tests. For example, Playwright Test will always start a new worker process after a failing test.
Worker-scoped fixtures receive a [WorkerInfo] parameter that describes the current worker configuration. See [WorkerInfo] properties for available worker information.
Consider an example where we run a new http server per worker process, and use `workerIndex` to produce a unique port number:
```js js-flavor=js
// my-test.js
const base = require('@playwright/test ');
const http = require('http');
// Note how we mark the fixture as { scope: 'worker' }.
// Also note that we pass empty {} first, since we do not declare any test fixtures.
exports.test = base.test.extend({
server: [ async ({}, use, workerInfo) => {
// Start the server.
const server = http.createServer();
server.listen(9000 + workerInfo.workerIndex);
await new Promise(ready => server.once('listening', ready));
// Use the server in the tests.
await use(server);
// Cleanup.
await new Promise(done => server.close(done));
}, { scope: 'worker' } ]
});
```
```js js-flavor=ts
// my-test.ts
import { test as base } from '@playwright/test ';
import * as http from 'http';
// Note how we mark the fixture as { scope: 'worker' }.
// Also note that we pass empty {} first, since we do not declare any test fixtures.
export const test = base.extend< {}, { server: http.Server }>({
server: [ async ({}, use, workerInfo) => {
// Start the server.
const server = http.createServer();
server.listen(9000 + workerInfo.workerIndex);
await new Promise(ready => server.once('listening', ready));
// Use the server in the tests.
await use(server);
// Cleanup.
await new Promise(done => server.close(done));
}, { scope: 'worker' } ]
});
```
2021-11-08 17:50:48 -08:00
2021-06-02 20:16:10 -07:00
## Add custom matchers using expect.extend
Playwright Test uses [`expect` library ](https://jestjs.io/docs/expect ) under the hood which has the functionality to extend it with [custom matchers ](https://jestjs.io/docs/expect#expectextendmatchers ).
In this example we add a custom `toBeWithinRange` function in the configuration file.
```js js-flavor=js
// playwright.config.js
2021-06-03 14:46:58 -07:00
const { expect } = require('@playwright/test ');
2021-05-27 20:30:03 -07:00
2021-06-02 20:16:10 -07:00
expect.extend({
2021-06-21 19:56:30 +02:00
toBeWithinRange(received, floor, ceiling) {
2021-05-27 20:30:03 -07:00
const pass = received >= floor & & received < = ceiling;
if (pass) {
return {
message: () => 'passed',
pass: true,
};
} else {
return {
message: () => 'failed',
pass: false,
};
}
},
});
2021-06-02 20:16:10 -07:00
module.exports = {};
```
```js js-flavor=ts
// playwright.config.ts
2021-06-03 14:46:58 -07:00
import { expect, PlaywrightTestConfig } from '@playwright/test ';
2021-06-02 20:16:10 -07:00
expect.extend({
toBeWithinRange(received: number, floor: number, ceiling: number) {
const pass = received >= floor & & received < = ceiling;
if (pass) {
return {
message: () => 'passed',
pass: true,
};
} else {
return {
message: () => 'failed',
pass: false,
};
}
},
});
const config: PlaywrightTestConfig = {};
2021-05-27 20:30:03 -07:00
export default config;
```
2021-06-02 20:16:10 -07:00
Now we can use `toBeWithinRange` in the test.
```js js-flavor=js
// example.spec.js
2021-06-03 14:46:58 -07:00
const { test, expect } = require('@playwright/test ');
2021-06-02 20:16:10 -07:00
test('numeric ranges', () => {
expect(100).toBeWithinRange(90, 110);
expect(101).not.toBeWithinRange(0, 100);
});
```
```js js-flavor=ts
2021-05-27 20:30:03 -07:00
// example.spec.ts
2021-06-03 14:46:58 -07:00
import { test, expect } from '@playwright/test ';
2021-05-27 20:30:03 -07:00
test('numeric ranges', () => {
2021-06-02 20:16:10 -07:00
expect(100).toBeWithinRange(90, 110);
expect(101).not.toBeWithinRange(0, 100);
2021-05-27 20:30:03 -07:00
});
```
2021-06-02 20:16:10 -07:00
For TypeScript, also add the following to `global.d.ts` . You don't need it for JavaScript.
2022-02-23 14:35:40 +01:00
```js
2021-05-27 20:30:03 -07:00
// global.d.ts
2022-01-31 12:58:45 -05:00
declare global {
namespace PlaywrightTest {
2022-04-05 16:11:11 -07:00
interface Matchers< R , T > {
2022-01-31 12:58:45 -05:00
toBeWithinRange(a: number, b: number): R;
}
2021-05-27 20:30:03 -07:00
}
}
```