Playwright Codegen Best Practices

July 6, 2026 Β· View on GitHub

This guide explains how to improve Playwright codegen output to align with our lint standards.

Quick Fix Commands

1. Post-process any codegen test file

# After generating a test with codegen, run this to fix common issues:
npm run fix-codegen -- <path-to-test-file>

2. Generate and fix in one command

# Generate test and auto-fix it
npx playwright codegen && npm run fix-codegen -- playwright/tests/your-new-test.test.ts

Manual Improvements

Current Issues with Codegen Output

The codegen test you provided has these issues:

// ❌ No describe block wrapper
test("should toggle between light and dark themes", async ({ page }) => {

// ❌ Raw locator instead of semantic
page.getByTestId("app-container").getByText("πŸ“ŠUptime Watcher0%").first()

// ❌ Title could be improved
test("should toggle between light and dark themes"

Improved Version

/**
 * Theme switch UI tests
 *
 * @remarks Generated by Playwright codegen and improved for lint compliance
 * @file Playwright test for theme switching functionality
 */

import { test, expect } from "@playwright/test";

test.describe("theme switch UI", () => {
    test("should toggle between light and dark themes", async ({ page }) => {
        // Test initial state
        await expect(page.getByLabel("Toggle theme")).toContainText("β˜€οΈ");

        // Toggle to dark theme
        await page.getByRole("button", { name: "Toggle theme" }).click();
        await expect(page.getByLabel("Toggle theme")).toContainText("πŸŒ™");

        // Toggle back to light theme
        await page.getByRole("button", { name: "Toggle theme" }).click();
        await expect(page.getByLabel("Toggle theme")).toContainText("β˜€οΈ");

        // Verify app container is visible
        await expect(page.getByTestId("app-container")).toBeVisible();

        // Verify main UI elements
        await expect(page.getByTestId("app-container")).toMatchAriaSnapshot(\`
        - text: /πŸ“Š Uptime Watcher 0% Health 0 Up 0 Down 0 Pending \\d+ Paused \\d+ Total/
        - button "Add new site"
        - button "Toggle theme"
        - button "Settings"
        \`);
    });
});

Codegen Best Practices

1. Always wrap in describe blocks

test.describe("feature name", () => {
 test("should do something", async ({ page }) => {
  // test content
 });
});

2. Use semantic locators when possible

// βœ… Good - semantic
page.getByRole("button", { name: "Click me" });
page.getByLabel("Username");
page.getByPlaceholder("Enter email");

// ❌ Avoid - raw CSS
page.getByRole("button");
page.getByRole("#submit");
page.getByRole(".my-class");

3. Use testId for complex elements

// βœ… Good - when semantic locators aren't sufficient
page.getByTestId("complex-component");

// ❌ Avoid - complex CSS selectors
page.getByRole("div.container > ul.list > li:nth-child(3)");

4. Write descriptive test titles

// βœ… Good
test("should toggle between light and dark themes");
test("should display error message for invalid input");

// ❌ Avoid
test("test theme toggle");
test("Test error");
// βœ… Good - group related checks
const themeButton = page.getByRole("button", { name: "Toggle theme" });
await expect(themeButton).toBeVisible();
await expect(themeButton).toContainText("β˜€οΈ");

// ❌ Avoid - scattered individual assertions
await expect(page.getByRole("button", { name: "Toggle theme" })).toBeVisible();
// ... other code ...
await expect(page.getByRole("button", { name: "Toggle theme" })).toContainText(
 "β˜€οΈ"
);

Configuration Improvements

Our playwright.config.ts already includes:

  • testIdAttribute: "data-testid" - Makes codegen prefer test IDs
  • Proper timeout settings
  • TypeScript support

Automated Linting

After generating tests, always run:

npm run lint:fix
npm run lint

This will catch most issues automatically.