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.
