Playwright E2E testing expert for browser automation, cross-browser testing, visual regression, network interception, and CI integration. Use for E2E test setup, flaky tests, or browser automation challenges.
Expert in Playwright for E2E testing, browser automation, and cross-browser testing.
npx playwright --version 2>/dev/null
ls playwright.config.* 2>/dev/null
find . -name "*.spec.ts" -path "*e2e*" | head -5
# Initialize Playwright
npm init playwright@latest
# Install browsers
npx playwright install
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: 'html',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'only-on-failure',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
],
webServer: {
command: 'npm run dev',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
import { test, expect } from '@playwright/test';
test.describe('Authentication', () => {
test('should login successfully', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="email"]', 'user@example.com');
await page.fill('[data-testid="password"]', 'password123');
await page.click('[data-testid="submit"]');
await expect(page).toHaveURL('/dashboard');
await expect(page.locator('h1')).toContainText('Welcome');
});
test('should show error for invalid credentials', async ({ page }) => {
await page.goto('/login');
await page.fill('[data-testid="email"]', 'wrong@example.com');
await page.fill('[data-testid="password"]', 'wrong');
await page.click('[data-testid="submit"]');
await expect(page.locator('.error-message')).toBeVisible();
});
});
// pages/login.page.ts
import { Page, Locator } from '@playwright/test';
export class LoginPage {
readonly page: Page;
readonly emailInput: Locator;
readonly passwordInput: Locator;
readonly submitButton: Locator;
constructor(page: Page) {
this.page = page;
this.emailInput = page.locator('[data-testid="email"]');
this.passwordInput = page.locator('[data-testid="password"]');
this.submitButton = page.locator('[data-testid="submit"]');
}
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();
}
}
// Usage in test
test('login test', async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login('user@example.com', 'password');
});
test('mock API response', async ({ page }) => {
await page.route('**/api/users', async (route) => {
await route.fulfill({
status: 200,
body: JSON.stringify([{ id: 1, name: 'Mock User' }]),
});
});
await page.goto('/users');
await expect(page.locator('.user-name')).toContainText('Mock User');
});
test('visual comparison', async ({ page }) => {
await page.goto('/');
await expect(page).toHaveScreenshot('homepage.png', {
maxDiffPixelRatio: 0.1,
});
});
// Retry flaky tests
test('flaky network test', async ({ page }) => {
test.slow(); // Triple timeout
await page.goto('/');
await page.waitForLoadState('networkidle');
// Use polling assertions
await expect(async () => {
const response = await page.request.get('/api/status');
expect(response.ok()).toBeTruthy();
}).toPass({ timeout: 10000 });
});
# Run all tests
npx playwright test
# Run specific file
npx playwright test login.spec.ts
# Run in headed mode
npx playwright test --headed
# Run in UI mode
npx playwright test --ui
# Debug mode
npx playwright test --debug
# Generate report
npx playwright show-report
Search for places (restaurants, cafes, etc.) via Google Places API proxy on localhost.
Interact with GitHub using the `gh` CLI. Use `gh issue`, `gh pr`, `gh run`, and `gh api` for issues, PRs, CI runs, and advanced queries.
Create or update AgentSkills. Use when designing, structuring, or packaging skills with scripts, references, and assets.
Start voice calls via the OpenClaw voice-call plugin.
Notion API for creating and managing pages, databases, and blocks.
Gemini CLI for one-shot Q&A, summaries, and generation.
Category:developer