Runner, fixtures and CI
Projects and workers, typed fixtures for shared setup, and a CI plan that keeps traces when a test fails.
Configuring the runner
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests',
fullyParallel: true,
retries: process.env.CI ? 2 : 0,
reporter: process.env.CI ? [['html'], ['github']] : 'list',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure'
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'mobile', use: { ...devices['Pixel 7'] } }
],
webServer: {
command: 'npm run start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI
}
});projectsis how you cover browsers and devices: one suite, several targets.webServerstarts the application and waits for the URL, so CI needs no extra step.- Retries hide real races. Investigate a flaky test rather than leaning on them.
- Use a single worker only when tests genuinely share state.
Fixtures
import { test as base, expect } from '@playwright/test';
type Fixtures = { todoPage: TodoPage };
export const test = base.extend<Fixtures>({
todoPage: async ({ page }, use) => {
const todo = new TodoPage(page);
await todo.goto();
await use(todo); // the test body runs here
// teardown after use() returns
}
});
test('adds an item', async ({ todoPage }) => {
await todoPage.add('write the report');
await expect(todoPage.items).toHaveCount(1);
});💡
Fixtures are the idiomatic replacement for
beforeEach blocks: they are typed, lazy (built only when a test asks for them) and each test receives an isolated instance, which is what parallel runs need.Running in CI
# the ordered CI steps
npm ci
npx playwright install --with-deps chromium
npx playwright test --shard=1/3
npx playwright merge-reports --reporter html ./blob-report- Install only the browsers the job runs;
--with-depsalso pulls the Linux system libraries. - Shard the suite across machines and merge the reports into a single HTML artifact.
- Upload
test-results/when the job fails so the trace survives the run. - Set
CI=trueso retries, reporters andreuseExistingServerswitch automatically.
FAQ
How do I keep the suite fast?
Run browsers in parallel, shard across CI machines, and replace end-to-end coverage of pure logic with unit tests. Only critical user journeys need a real browser.
Should the tests run against a deployed environment?
Prefer the application started by
webServer in the same job: it is faster and the version under test matches the commit. Keep a small smoke set for a deployed environment.Related
Locators and assertions A real test example
Last refreshed 2026-09-18.