Claude Code Plugins

Community-maintained marketplace

Feedback

setup-playwright

@gruckion/marathon-ralph
0
0

Configure Playwright for E2E testing. Use when setting up end-to-end tests, when no E2E framework is detected, or when the user asks to configure browser testing.

Install Skill

1Download skill
2Enable skills in Claude

Open claude.ai/settings/capabilities and find the "Skills" section

3Upload to Claude

Click "Upload skill" and select the downloaded ZIP file

Note: Please verify skill by going through its instructions before using it.

SKILL.md

name setup-playwright
description Configure Playwright for E2E testing. Use when setting up end-to-end tests, when no E2E framework is detected, or when the user asks to configure browser testing.
allowed-tools Read, Write, Edit, Bash, Glob, Grep

Setup Playwright

Configure Playwright as the end-to-end testing framework with fixtures and best practices.

When to Use This Skill

  • No E2E framework is configured in the project
  • User requests to set up E2E testing
  • Project is a web application needing browser testing
  • Migrating from Cypress to Playwright

Installation

Use ni to auto-detect the package manager. Get the exec command from .claude/marathon-ralph.json under project.commands.exec:

# Install Playwright
ni -D @playwright/test

# Install browsers (use exec command from project state: bunx, pnpm exec, npx, etc.)
# Examples:
bunx playwright install --with-deps
# or: pnpm exec playwright install --with-deps
# or: npx playwright install --with-deps

# For CI optimization, install only needed browsers
bunx playwright install chromium --with-deps

Configuration

playwright.config.ts

Create playwright.config.ts at the project root:

import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
  // Test directory
  testDir: './tests/e2e',

  // Run tests in parallel
  fullyParallel: true,

  // Fail build on CI if test.only is left in code
  forbidOnly: !!process.env.CI,

  // Retry failed tests (more on CI)
  retries: process.env.CI ? 2 : 0,

  // Parallel workers
  workers: process.env.CI ? 1 : undefined,

  // Reporter configuration
  reporter: process.env.CI
    ? [['github'], ['html', { open: 'never' }]]
    : [['list'], ['html']],

  // Timeouts
  timeout: 30000,
  expect: {
    timeout: 5000,
  },

  // Shared settings for all projects
  use: {
    // Base URL for page.goto('/')
    baseURL: process.env.BASE_URL || 'http://localhost:3000',

    // Collect trace on first retry
    trace: 'on-first-retry',

    // Screenshot on failure
    screenshot: 'only-on-failure',

    // Video on first retry
    video: 'on-first-retry',
  },

  // Browser projects
  projects: [
    // Setup project for authentication
    {
      name: 'setup',
      testMatch: /.*\.setup\.ts/,
    },

    // Desktop Chrome
    {
      name: 'chromium',
      use: {
        ...devices['Desktop Chrome'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },

    // Desktop Firefox (optional)
    {
      name: 'firefox',
      use: {
        ...devices['Desktop Firefox'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },

    // Desktop Safari (optional)
    {
      name: 'webkit',
      use: {
        ...devices['Desktop Safari'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },

    // Mobile Chrome (optional)
    {
      name: 'Mobile Chrome',
      use: {
        ...devices['Pixel 5'],
        storageState: 'playwright/.auth/user.json',
      },
      dependencies: ['setup'],
    },
  ],

  // Run dev server before tests
  // IMPORTANT: Use the dev command from your package manager
  // Get from project state: project.commands.dev
  webServer: {
    command: process.env.DEV_COMMAND || 'bun run dev', // Adjust based on package manager
    url: 'http://localhost:3000',
    reuseExistingServer: !process.env.CI,
    timeout: 120 * 1000,
  },
})

Directory Structure

Create the recommended directory structure:

project/
├── tests/
│   └── e2e/
│       ├── fixtures/
│       │   └── test-fixtures.ts    # Custom fixtures
│       ├── pages/
│       │   ├── login.page.ts       # Page objects
│       │   └── home.page.ts
│       ├── auth.setup.ts           # Authentication setup
│       ├── home.spec.ts            # Test files
│       └── login.spec.ts
├── playwright/
│   └── .auth/
│       └── .gitkeep                # Auth state storage
├── playwright.config.ts
└── package.json

Create the directories:

mkdir -p tests/e2e/fixtures tests/e2e/pages playwright/.auth
touch playwright/.auth/.gitkeep

.gitignore

Add to .gitignore:

# Playwright
playwright-report/
test-results/
playwright/.auth/
!playwright/.auth/.gitkeep

Package.json Scripts

Add test scripts:

{
  "scripts": {
    "test:e2e": "playwright test",
    "test:e2e:ui": "playwright test --ui",
    "test:e2e:headed": "playwright test --headed",
    "test:e2e:debug": "playwright test --debug",
    "test:e2e:report": "playwright show-report"
  }
}

Authentication Setup

Create tests/e2e/auth.setup.ts for shared authentication:

import { test as setup, expect } from '@playwright/test'

const authFile = 'playwright/.auth/user.json'

setup('authenticate', async ({ page }) => {
  // Navigate to login
  await page.goto('/login')

  // Fill credentials
  await page.getByLabel(/email/i).fill('test@example.com')
  await page.getByLabel(/password/i).fill('password123')
  await page.getByRole('button', { name: /sign in/i }).click()

  // Wait for authentication to complete
  await expect(page).toHaveURL('/dashboard')

  // Save authentication state
  await page.context().storageState({ path: authFile })
})

Custom Fixtures

Create tests/e2e/fixtures/test-fixtures.ts:

import { test as base, expect } from '@playwright/test'
import { LoginPage } from '../pages/login.page'
import { HomePage } from '../pages/home.page'

// Declare fixture types
type MyFixtures = {
  loginPage: LoginPage
  homePage: HomePage
}

// Extend base test with custom fixtures
export const test = base.extend<MyFixtures>({
  loginPage: async ({ page }, use) => {
    const loginPage = new LoginPage(page)
    await use(loginPage)
  },

  homePage: async ({ page }, use) => {
    const homePage = new HomePage(page)
    await use(homePage)
  },
})

export { expect }

Page Object Model

Create tests/e2e/pages/login.page.ts:

import { type Page, type Locator } from '@playwright/test'

export class LoginPage {
  readonly page: Page
  readonly emailInput: Locator
  readonly passwordInput: Locator
  readonly submitButton: Locator
  readonly errorMessage: Locator

  constructor(page: Page) {
    this.page = page
    this.emailInput = page.getByLabel(/email/i)
    this.passwordInput = page.getByLabel(/password/i)
    this.submitButton = page.getByRole('button', { name: /sign in/i })
    this.errorMessage = page.getByRole('alert')
  }

  async goto() {
    await this.page.goto('/login')
  }

  async login(email: string, password: string) {
    await this.emailInput.fill(email)
    await this.passwordInput.fill(password)
    await this.submitButton.click()
  }
}

GitHub Actions CI

Create .github/workflows/playwright.yml:

Adjust commands based on your package manager (get from project.commands):

name: Playwright Tests

on:
  push:
    branches: [main, master]
  pull_request:
    branches: [main, master]

jobs:
  test:
    timeout-minutes: 60
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: lts/*

      # For bun projects
      - uses: oven-sh/setup-bun@v2
        # if using bun

      # Install dependencies - adjust for your package manager
      # bun: bun install
      # pnpm: pnpm install
      # yarn: yarn install
      # npm: npm ci
      - name: Install dependencies
        run: bun install

      # Install Playwright browsers - use your exec command
      - name: Install Playwright Browsers
        run: bunx playwright install chromium --with-deps

      # Run tests - use your exec command
      - name: Run Playwright tests
        run: bunx playwright test

      - uses: actions/upload-artifact@v4
        if: ${{ !cancelled() }}
        with:
          name: playwright-report
          path: playwright-report/
          retention-days: 30

Verification

After setup, verify with:

# Run all tests
nr test:e2e

# Run in UI mode
nr test:e2e:ui

# Run headed (visible browser)
nr test:e2e:headed

# Debug mode
nr test:e2e:debug

# View report
nr test:e2e:report

Key Configuration Options

Option Description Default
testDir Directory containing tests ./tests
fullyParallel Run tests in parallel true
retries Retry failed tests 0
workers Parallel workers Auto
timeout Test timeout (ms) 30000
trace Trace collection 'on-first-retry'
screenshot Screenshot capture 'only-on-failure'
video Video recording 'off'
baseURL Base URL for navigation Required

CLI Commands Reference

Use your exec command from project state (bunx, pnpm exec, npx, etc.):

Command Description
{exec} playwright test Run all tests
{exec} playwright test --ui UI mode
{exec} playwright test --headed Visible browser
{exec} playwright test --debug Debug mode
{exec} playwright test file.spec.ts Run specific file
{exec} playwright test --project=chromium Run specific project
{exec} playwright codegen Generate tests
{exec} playwright show-report Open HTML report

Replace {exec} with your package manager's exec command (e.g., bunx, pnpm exec, npx).