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.
Configures Playwright for E2E testing with fixtures, page objects, and CI workflows. Triggered when setting up end-to-end tests, no E2E framework is detected, or user requests browser testing configuration.
/plugin marketplace add gruckion/marathon-ralph/plugin install marathon-ralph@marathon-ralphThis skill is limited to using the following tools:
Configure Playwright as the end-to-end testing framework with fixtures and best practices.
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
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,
},
})
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
Add to .gitignore:
# Playwright
playwright-report/
test-results/
playwright/.auth/
!playwright/.auth/.gitkeep
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"
}
}
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 })
})
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 }
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()
}
}
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
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
| 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 |
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).
This skill should be used when the user asks to "create a slash command", "add a command", "write a custom command", "define command arguments", "use command frontmatter", "organize commands", "create command with file references", "interactive command", "use AskUserQuestion in command", or needs guidance on slash command structure, YAML frontmatter fields, dynamic arguments, bash execution in commands, user interaction patterns, or command development best practices for Claude Code.
This skill should be used when the user asks to "create an agent", "add an agent", "write a subagent", "agent frontmatter", "when to use description", "agent examples", "agent tools", "agent colors", "autonomous agent", or needs guidance on agent structure, system prompts, triggering conditions, or agent development best practices for Claude Code plugins.
This skill should be used when the user asks to "create a hook", "add a PreToolUse/PostToolUse/Stop hook", "validate tool use", "implement prompt-based hooks", "use ${CLAUDE_PLUGIN_ROOT}", "set up event-driven automation", "block dangerous commands", or mentions hook events (PreToolUse, PostToolUse, Stop, SubagentStop, SessionStart, SessionEnd, UserPromptSubmit, PreCompact, Notification). Provides comprehensive guidance for creating and implementing Claude Code plugin hooks with focus on advanced prompt-based hooks API.