Configuration and Running
playwright.config.ts is where you decide which browsers run, how many tests run at once, what happens on failure and what gets reported. This page walks through the sample project's config line by line, then covers the commands you run every day. It is the load model and execute pages of the JMeter track, for a runner that cares about correctness rather than load.
The Config, Line by Line
playwright/sample/playwright.config.ts:
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: process.env.CI ? 'blob' : 'html',
use: {
baseURL: 'https://www.saucedemo.com',
testIdAttribute: 'data-test',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'setup', testMatch: /.*\.setup\.ts/ },
{
name: 'chromium',
use: {
...devices['Desktop Chrome'],
storageState: 'playwright/.auth/user.json',
},
dependencies: ['setup'],
},
],
});
| Option | What it does | My setting and why |
|---|---|---|
testDir | Where spec files live | ./tests |
fullyParallel | Run tests inside one file in parallel too, not just across files | true. Requires isolated tests, which you want anyway |
forbidOnly | Fail the run if a test.only was left in | On in CI only. Locally test.only is a useful focus tool |
retries | Re-run a failed test this many times before calling it failed | 2 in CI, 0 locally. A test that passes on retry is reported as flaky, not passed |
workers | Number of parallel worker processes | 1 in CI (predictable on small runners), default locally (half your CPU cores) |
reporter | How results are reported | html locally, blob in CI so shards can be merged (Section 10) |
use.baseURL | Prefix for page.goto('/') | The environment under test. Override per environment with an env var |
use.testIdAttribute | Attribute getByTestId reads | data-test because saucedemo uses that. Default is data-testid |
use.trace | When to record a trace | on-first-retry (Section 7) |
use.screenshot | When to take a screenshot | only-on-failure |
projects | Named browser and settings combinations | A setup project that logs in, and chromium that depends on it (Section 9) |
Timeouts
Defaults are sensible. Know them before changing them:
| Timeout | Default | Set with |
|---|---|---|
| Test timeout (whole test) | 30 s | timeout: 60_000 at the top level |
Assertion timeout (expect) | 5 s | expect: { timeout: 10_000 } |
| Action timeout (click, fill) | none, bounded by the test timeout | use: { actionTimeout: 10_000 } |
| Navigation timeout | none | use: { navigationTimeout: 30_000 } |
Raising the test timeout to hide a slow app is the UI equivalent of raising the JMeter response-time SLA. Fix the wait instead.
Environments
Do not hardcode staging in the config. Read it from the environment with a default:
use: {
baseURL: process.env.BASE_URL ?? 'https://www.saucedemo.com',
},
BASE_URL=https://staging.example.com npx playwright test
On Windows PowerShell: $env:BASE_URL="https://staging.example.com"; npx playwright test.
Projects: Browsers and Devices
The generated config has commented-out projects for Firefox, WebKit, mobile and branded browsers. Uncomment what you need:
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'Mobile Chrome', use: { ...devices['Pixel 5'] } },
{ name: 'Google Chrome', use: { ...devices['Desktop Chrome'], channel: 'chrome' } },
],
chromium,firefox,webkitare the bundled engines. Install them withnpx playwright install.channel: 'chrome'or'msedge'uses the installed branded browser instead of the bundled one.- Since 1.57 the default
chromiumproject runs headed tests in Chrome for Testing and headless tests in a slimmerchrome-headless-shell. Both are downloaded bynpx playwright install chromium.
Run one project with --project:
npx playwright test --project chromium
Tip: Start with Chromium only. Add Firefox and WebKit once the suite is stable. Every extra browser multiplies run time and the number of ways a test can be flaky.
Running Tests
| Command | What it does |
|---|---|
npx playwright test | Everything, headless, all projects |
npx playwright test tests/login.spec.ts | One file |
npx playwright test -g "cart" | Tests whose full name matches |
npx playwright test --project chromium | One project |
npx playwright test --headed | Watch the browser |
npx playwright test --ui | UI mode (Section 7) |
npx playwright test --debug | Inspector, step by step |
npx playwright test --workers 4 | Fixed worker count |
npx playwright test --retries 2 | Override retries |
npx playwright test --last-failed | Only the failures from the previous run |
npx playwright test --only-changed | Only spec files changed since the last commit |
npx playwright test --list | Print the tests without running them |
npx playwright show-report | Open the last HTML report |
The sample package.json wraps the common ones as scripts (npm test, npm run test:ui, npm run report) so nobody has to remember the flags.
Tags
Tag tests to run subsets:
test('valid user lands on the products page', { tag: '@smoke' }, async ({ page }) => {
// ...
});
npx playwright test --grep @smoke
npx playwright test --grep-invert @slow
Parallelism
Playwright runs worker processes, each with its own browser. Tests in different files always run in parallel across workers. fullyParallel: true also spreads tests from the same file across workers.
This only works if tests are isolated: own data, own login state, no shared mutable records. If two tests must run in order, opt out for that file:
test.describe.configure({ mode: 'serial' });
Treat serial as a smell to fix later, not a design.
Reports
HTML Report
reporter: 'html' writes playwright-report/index.html after every run. Open it with npx playwright show-report. It lists every test, its duration, retries, the error, screenshots, and a link to the trace when one was recorded. Since 1.57 it also has a Speedboard tab with the slowest tests.
Other Reporters
| Reporter | Use |
|---|---|
list | One line per test in the terminal. Good default for local runs |
line | Compact, one updating line |
dot | One character per test |
junit | XML for CI systems that read JUnit |
json | Machine-readable results |
blob | Raw results for merging sharded runs (Section 10) |
Combine them: reporter: [['list'], ['html']]. Add one for a single run without editing the config: npx playwright test --add-reporter junit.
Tips
-
Retries hide problems. Use them in CI to survive network blips, but read the flaky count in the report. A rising flaky count is a bug list.
-
One config, many environments. Env vars for base URL and credentials. Never a config file per environment.
-
Keep
workers: 1in CI until the suite is proven isolated, then raise it. Going parallel first and debugging isolation later is painful. -
Commit
playwright.config.ts, ignoreplaywright-report/andtest-results/. The init command already writes the right.gitignore.