Advanced Playwright Configuration#
This guide covers advanced configuration options in Playwright to optimize your testing workflow and handle complex testing scenarios.
Complete Configuration Example#
Below is a comprehensive playwright.config.ts with detailed explanations:
import { defineConfig, devices } from "@playwright/test";
import path from "path";
export default defineConfig({
// Test directory and file pattern
testDir: "./tests",
testMatch: "**/*.spec.ts",
// How many failures before stopping?
maxFailures: process.env.CI ? 10 : 1,
// Timeout settings
timeout: 30000, // Global timeout (30 seconds)
expect: {
timeout: 5000, // Default timeout for assertions
},
// How many retries on failure?
retries: process.env.CI ? 2 : 0,
// Workers: parallel execution settings
workers: process.env.CI ? 1 : undefined,
// Reporter configuration
reporter: [
["html", { open: "never" }],
["json", { outputFile: "test-results/report.json" }],
["list"],
],
// Global test settings
use: {
// Base URL for navigation
baseURL: process.env.BASE_URL || "http://localhost:3000",
// Browser settings
headless: true,
viewport: { width: 1280, height: 720 },
// Test artifacts
screenshot: "only-on-failure",
video: "retain-on-failure",
trace: "on-first-retry",
// Browser context settings
ignoreHTTPSErrors: true,
// Locale and timezone
locale: "en-US",
timezoneId: "America/New_York",
// Custom user agent
userAgent: "Playwright Test Agent",
// Storage state (persisted cookies/localStorage)
storageState: path.join(__dirname, "storage-state.json"),
// Extra HTTP headers
extraHTTPHeaders: {
"X-Test-Header": "test-value",
},
// Default navigation options
navigationTimeout: 10000,
// Action options
actionTimeout: 5000,
},
// Browser configurations
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: "Mobile Safari",
use: {
...devices["iPhone 13"],
},
},
{
name: "tablet",
use: {
...devices["iPad (gen 7)"],
},
},
{
name: "authenticated",
testMatch: "authenticated/**/*.spec.ts",
use: {
storageState: path.join(__dirname, "storage-states/admin.json"),
},
dependencies: ["setup"],
},
{
name: "setup",
testMatch: "setup/**/*.ts",
},
],
// Define directories to include with tests
outputDir: "test-results/",
// Global setup/teardown files
globalSetup: require.resolve("./tests/global-setup"),
globalTeardown: require.resolve("./tests/global-teardown"),
});
Environment-Specific Configuration#
Create multiple configs for different environments:
// Base configuration (common settings)
const baseConfig = {
testDir: "./tests",
use: {
screenshot: "only-on-failure",
video: "retain-on-failure",
trace: "on-first-retry",
},
};
// Environment-specific configurations
const envConfig = {
development: {
use: { baseURL: "http://localhost:3000" },
workers: undefined, // Use all cores
},
staging: {
use: { baseURL: "https://staging.example.com" },
retries: 1,
},
production: {
use: { baseURL: "https://example.com" },
retries: 2,
workers: 1, // Careful with production
},
};
// Select config based on environment variable
const environment = process.env.TEST_ENV || "development";
export default defineConfig({
...baseConfig,
...envConfig[environment],
});
Custom Fixture Setup#
Create custom test fixtures for reusing test setup:
// fixtures.ts
import { test as base } from "@playwright/test";
import { LoginPage } from "./pages/login-page";
// Define fixture types
type Fixtures = {
loginPage: LoginPage;
loggedInPage: LoginPage;
};
// Define fixtures
export const test = base.extend<Fixtures>({
// LoginPage fixture - creates a new LoginPage
loginPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await use(loginPage);
},
// LoggedInPage fixture - creates a logged-in session
loggedInPage: async ({ page }, use) => {
const loginPage = new LoginPage(page);
await loginPage.navigate();
await loginPage.login("testuser", "password123");
await use(loginPage);
},
});
Usage in tests:
// Use custom fixtures in tests
import { test } from "./fixtures";
test("logged in user can access dashboard", async ({ loggedInPage, page }) => {
// Test already starts with logged-in user
await page.getByText("Dashboard").click();
// Test dashboard functionality
});
Global Setup: Authentication State#
Create pre-authenticated states to speed up tests:
// global-setup.ts
import { chromium } from "@playwright/test";
import path from "path";
import fs from "fs";
async function globalSetup() {
// Setup for admin user
const adminBrowser = await chromium.launch();
const adminContext = await adminBrowser.newContext();
const adminPage = await adminContext.newPage();
// Login as admin
await adminPage.goto("https://example.com/login");
await adminPage.fill('input[name="username"]', "admin");
await adminPage.fill('input[name="password"]', "admin-password");
await adminPage.click('button[type="submit"]');
await adminPage.waitForURL("**/dashboard");
// Save storage state
const adminStorageStatePath = path.join(
__dirname,
"storage-states/admin.json"
);
await fs.promises.mkdir(path.dirname(adminStorageStatePath), {
recursive: true,
});
await adminContext.storageState({ path: adminStorageStatePath });
await adminBrowser.close();
// Repeat for other user types...
}
export default globalSetup;
Visual Testing Configuration#
Configure visual comparisons:
// Visual comparison settings
use: {
// Visual comparison options
screenshot: {
fullPage: true,
omitBackground: true,
},
}
Custom Test Commands#
Extend Playwright’s functionality with custom commands:
// extend-test.ts
import { test as baseTest } from "@playwright/test";
export const test = baseTest.extend({
page: async ({ page }, use) => {
// Add custom methods to the page object
page.navigateToHome = async () => {
await page.goto("/");
};
page.login = async (username: string, password: string) => {
await page.goto("/login");
await page.fill('input[name="username"]', username);
await page.fill('input[name="password"]', password);
await page.click('button[type="submit"]');
};
await use(page);
},
});
API Tests Configuration#
Configure API testing:
// api-playwright.config.ts
import { defineConfig } from "@playwright/test";
export default defineConfig({
testDir: "./tests/api",
use: {
// Base URL for API requests
baseURL: "https://api.example.com",
extraHTTPHeaders: {
Accept: "application/json",
Authorization: `Bearer ${process.env.API_TOKEN}`,
},
// Ensure network requests are properly captured
trace: "on",
},
// API tests typically run sequentially
workers: 1,
});
By implementing these advanced configuration techniques, you can create a highly customized and efficient Playwright testing environment.
Timeouts#
// playwright.config.ts
export default defineConfig({
timeout: 30000, // Global timeout
expect: {
timeout: 5000, // Assertion timeout
},
});
Parallel Execution#
export default defineConfig({
workers: 4, // Run 4 tests in parallel
fullyParallel: true, // Run tests in files in parallel
});
Retries#
export default defineConfig({
retries: process.env.CI ? 2 : 0, // Retry twice on CI, none locally
});
Reporter Configuration#
export default defineConfig({
reporter: [
["html"], // HTML report
["junit", { outputFile: "results.xml" }], // JUnit report
["json", { outputFile: "results.json" }], // JSON report
],
});
Browser Options#
export default defineConfig({
use: {
browserName: "chromium",
headless: true,
viewport: { width: 1280, height: 720 },
ignoreHTTPSErrors: true,
video: "on-first-retry",
screenshot: "only-on-failure",
trace: "on",
},
});
Tip
Configure different settings for local development versus CI environments for better efficiency.