The config file is the control center of your Playwright setup. Every team lead will ask you to tweak this file. Every CI pipeline depends on it. Let me walk you through every important option. Think of this as the settings app on your phone -- you need to know what each toggle does.
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
// Where are your test files?
testDir: './tests',
// Run tests in each file one after another
// Set to true if tests in the same file depend on each other
fullyParallel: true,
// Fail the build on CI if you accidentally left test.only
forbidOnly: !!process.env.CI,
// Retry failed tests. 0 in dev, 2 in CI.
retries: process.env.CI ? 2 : 0,
// How many tests run in parallel. Defaults to half your CPU cores.
workers: process.env.CI ? 1 : undefined,
// Reporter -- what format should test results be in?
reporter: 'html',
// Shared settings for all tests
use: {
// Base URL for page.goto('/login') instead of full URL
baseURL: 'https://www.testerrank.com',
// Record trace on first retry (best for CI debugging)
trace: 'on-first-retry',
// Take screenshot only on failure
screenshot: 'only-on-failure',
// Record video only on failure
video: 'retain-on-failure',
},
// Test against multiple browsers
projects: [
{
name: 'chromium',
use: { ...devices['Desktop Chrome'] },
},
{
name: 'firefox',
use: { ...devices['Desktop Firefox'] },
},
{
name: 'webkit',
use: { ...devices['Desktop Safari'] },
},
],
});| Option | What It Controls | Common Values |
|---|---|---|
| testDir | Folder where Playwright looks for .spec.ts files | ./tests (default) |
| fullyParallel | Whether tests within a file run in parallel | true for independent tests, false if tests share state |
| retries | How many times to retry a failed test | 0 for local dev, 2 for CI |
| workers | Number of parallel worker processes | undefined (auto) or 1 for CI |
| reporter | Output format for results | html, list, json, dot, junit |
| baseURL | Prefix for relative URLs in page.goto() | https://www.testerrank.com or your staging URL |
| trace | When to record traces | on-first-retry (recommended for CI) |
| projects | Which browsers to test against | Array of browser configs |
| timeout | Max time for each test (ms) | 30000 (30 seconds default) |
| expect.timeout | Max time for each expect assertion (ms) | 5000 (5 seconds default) |
Projects are how Playwright runs your tests across multiple browsers. Each project is a separate browser configuration. When you run npx playwright test, it runs ALL tests against ALL projects. That means if you have 10 tests and 3 projects, Playwright runs 30 test instances.
Use process.env.CI to toggle between dev and CI settings. GitHub Actions, Jenkins, and most CI tools set this variable automatically. Your config adapts without you changing a single line.
Do not set timeout to a very high value like 120 seconds thinking it will fix flaky tests. If a test needs 2 minutes, something is wrong with the test or the app. The default 30 seconds is generous. If you need more, fix the root cause.
Q: Walk me through your playwright.config.ts. What options do you set and why?
A: I set baseURL so all page.goto calls use relative paths -- easy to switch between environments. Retries are 0 locally (fail fast) and 2 in CI (handle infrastructure flakiness). I use html reporter so anyone can open the report and see results visually. Trace is set to on-first-retry in CI -- it only records when a test fails, saving storage while giving full debug data. Projects define which browsers to test: chromium, firefox, and webkit. Workers are auto locally but 1 in CI for stability.
Key Point: playwright.config.ts controls everything: browsers, timeouts, retries, reporters, and base URL. Master this file and you control your entire test infrastructure.