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.