Q16 of 42 · Playwright
How do you parameterise Playwright tests with test.describe.parallel and projects?
Short answer
Short answer: **Projects** parameterise tests by config — different browsers, viewports, or env vars. Each project runs the matching tests independently. **`test.describe.parallel`** marks describe blocks for concurrent execution within the same file (default is serial within a file, parallel across files).
Detail
Playwright has two levels of parallelism, controlled separately.
Across files: parallel by default. Each worker takes one file at a time. Configure with workers: N.
Within a file: serial by default. test.describe.parallel(...) opts a describe block into intra-file parallelism, useful when tests within a file are genuinely independent. test.describe.serial(...) does the opposite — useful when one test's state depends on the previous (rare and discouraged).
Projects are the cross-cutting parameterisation:
export default defineConfig({
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile-safari', use: { ...devices['iPhone 14 Pro'] } },
],
});
npx playwright test runs every test against every project. --project=chromium filters to one. Each project gets its own worker(s) and its own report subsection.
Project-scoped tests: filter which tests run for which project via testMatch / testIgnore in the project config:
projects: [
{
name: 'mobile',
use: { ...devices['iPhone 14 Pro'] },
testMatch: /.*\.mobile\.spec\.ts/,
},
]
Project dependencies for setup-then-test patterns:
projects: [
{ name: 'setup', testMatch: /global\.setup\.ts/ },
{ name: 'chromium', dependencies: ['setup'], use: { storageState: 'auth.json' } },
]
setup runs first; chromium runs after, consuming auth.json.
The combined model gives you parametrised + parallel + setup-aware execution from config alone, no test-level boilerplate.
// EXAMPLE
test-with-parallelism.spec.ts
import { test, expect } from '@playwright/test';
// Default: serial within this file
test.describe('serial group', () => {
test('A', async ({ page }) => { /* ... */ });
test('B', async ({ page }) => { /* depends on A */ });
});
// Opt into parallel within the file
test.describe.parallel('independent group', () => {
test('checks header', async ({ page }) => { /* ... */ });
test('checks footer', async ({ page }) => { /* ... */ });
test('checks sidebar', async ({ page }) => { /* ... */ });
});