Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    vinceh

    playwright-testing

    vinceh/playwright-testing
    Coding
    2

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Comprehensive guide for modern front-end testing with Playwright (2025)...

    SKILL.md

    Playwright Testing

    Overview

    To build reliable, maintainable end-to-end tests for modern web applications, follow Playwright best practices with TypeScript, Page Object Model patterns, and automated CI/CD integration. This skill provides comprehensive guidance for testing SPAs (React, Vue, Angular) and traditional web applications using Playwright's powerful automation capabilities.

    Current Playwright Version: 1.48+ (as of 2025) Language: TypeScript with strict typing Test Runner: Playwright Test (@playwright/test) CI/CD: GitHub Actions, Docker support Standard Patterns: Page Object Model, Fixtures, Component Testing

    Decision Tree: Choosing Your Testing Approach

    User Request → What type of test are you creating?
        |
        ├─ End-to-end user flow test?
        │   ├─ Single page interaction? → Use Quick Start: Basic Test
        │   └─ Multi-page workflow? → Use Page Object Model Pattern
        │
        ├─ Component testing in isolation?
        │   └─ Load references/component-testing-guide.md
        │
        ├─ Visual regression testing?
        │   └─ Load references/visual-testing-guide.md
        │
        ├─ Setting up new Playwright project?
        │   └─ Run scripts/init-playwright.ts
        │
        ├─ Creating reusable page objects?
        │   ├─ Run scripts/generate-page-object.ts for scaffolding
        │   └─ Load references/page-object-patterns.md for patterns
        │
        ├─ Debugging flaky tests?
        │   └─ Load references/anti-patterns.md
        │
        ├─ Optimizing test selectors?
        │   └─ Load references/locator-strategies.md
        │
        └─ Setting up CI/CD pipeline?
            └─ Use assets/workflows/playwright-tests.yml
    

    Quick Start: Basic Test

    Simple Test with TypeScript

    import { test, expect } from '@playwright/test';
    
    test('user can login successfully', async ({ page }) => {
      // Navigate to application
      await page.goto('https://app.example.com');
    
      // Interact with elements using recommended locators
      await page.getByLabel('Email').fill('user@example.com');
      await page.getByLabel('Password').fill('SecurePass123');
      await page.getByRole('button', { name: 'Sign in' }).click();
    
      // Assert expected outcomes
      await expect(page).toHaveURL('/dashboard');
      await expect(page.getByRole('heading', { name: 'Welcome back' })).toBeVisible();
    });
    

    Test with Multiple Assertions

    import { test, expect } from '@playwright/test';
    
    test.describe('Shopping Cart', () => {
      test.beforeEach(async ({ page }) => {
        await page.goto('/products');
      });
    
      test('add item to cart', async ({ page }) => {
        // Find and click product
        const product = page.getByRole('article').filter({ hasText: 'Premium Headphones' });
        await product.getByRole('button', { name: 'Add to cart' }).click();
    
        // Verify cart updated
        await expect(page.getByTestId('cart-count')).toHaveText('1');
    
        // Navigate to cart
        await page.getByRole('link', { name: 'Cart' }).click();
    
        // Verify item in cart
        await expect(page.getByRole('heading', { name: 'Premium Headphones' })).toBeVisible();
        await expect(page.getByText('$199.99')).toBeVisible();
      });
    });
    

    Workflow: Creating Comprehensive Test Suites

    Follow this structured approach to build maintainable test suites for your application.

    Step 1: Analyze Application Structure

    To begin testing effectively:

    1. Map user journeys: Identify critical paths users take
    2. Identify page boundaries: Determine logical page groupings
    3. List reusable components: Find common UI patterns
    4. Document test data needs: Understand data dependencies

    Step 2: Set Up Project Structure

    Initialize your Playwright project with proper TypeScript configuration:

    # Run initialization script
    npx tsx scripts/init-playwright.ts
    

    This creates:

    • TypeScript configuration with strict typing
    • Playwright config with parallel execution
    • Base fixture setup
    • Initial folder structure

    Step 3: Implement Page Objects

    Create page objects for reusable, maintainable tests:

    // pages/LoginPage.ts
    import { Page, 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');
        this.passwordInput = page.getByLabel('Password');
        this.submitButton = page.getByRole('button', { name: 'Sign in' });
        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();
      }
    
      async expectError(message: string) {
        await expect(this.errorMessage).toContainText(message);
      }
    }
    

    Use the page object generator for scaffolding:

    npx tsx scripts/generate-page-object.ts --url="/login" --name="LoginPage"
    

    Step 4: Write Test Scenarios

    Structure tests using Given-When-Then pattern:

    import { test, expect } from '@playwright/test';
    import { LoginPage } from '../pages/LoginPage';
    
    test.describe('Authentication', () => {
      let loginPage: LoginPage;
    
      test.beforeEach(async ({ page }) => {
        loginPage = new LoginPage(page);
        await loginPage.goto();
      });
    
      test('successful login redirects to dashboard', async ({ page }) => {
        // Given: Valid user credentials
        const email = 'user@example.com';
        const password = 'ValidPass123';
    
        // When: User attempts to login
        await loginPage.login(email, password);
    
        // Then: User is redirected to dashboard
        await expect(page).toHaveURL('/dashboard');
      });
    
      test('invalid credentials show error', async () => {
        // When: User enters invalid credentials
        await loginPage.login('bad@email.com', 'wrong');
    
        // Then: Error message is displayed
        await loginPage.expectError('Invalid email or password');
      });
    });
    

    Step 5: Implement Visual Testing

    Add visual regression tests for UI consistency:

    import { test, expect } from '@playwright/test';
    
    test('homepage visual consistency', async ({ page }) => {
      await page.goto('/');
    
      // Full page screenshot
      await expect(page).toHaveScreenshot('homepage.png', {
        fullPage: true,
        animations: 'disabled'
      });
    
      // Component screenshot
      const header = page.getByRole('banner');
      await expect(header).toHaveScreenshot('header.png');
    });
    

    Update baselines when intentional changes are made:

    npx tsx scripts/visual-baseline.ts --update
    

    Step 6: Configure CI/CD

    Deploy the GitHub Actions workflow for automated testing:

    # .github/workflows/playwright.yml
    name: Playwright Tests
    on:
      push:
        branches: [main, develop]
      pull_request:
        branches: [main]
    
    jobs:
      test:
        runs-on: ubuntu-latest
        steps:
          - uses: actions/checkout@v4
          - uses: actions/setup-node@v4
            with:
              node-version: 20
          - run: npm ci
          - run: npx playwright install --with-deps
          - run: npm run test:e2e
          - uses: actions/upload-artifact@v4
            if: failure()
            with:
              name: playwright-report
              path: playwright-report/
    

    Core Testing Principles

    1. Test User Behavior, Not Implementation

    Write tests from the user's perspective:

    // ✅ Good: Tests user-visible behavior
    await page.getByRole('button', { name: 'Submit order' }).click();
    await expect(page.getByText('Order confirmed')).toBeVisible();
    
    // ❌ Bad: Tests implementation details
    await page.locator('#submit-btn-2947').click();
    await expect(page.locator('.success-msg-div')).toHaveClass('visible');
    

    2. Use Semantic Locators

    Prefer accessible, semantic locators that won't break with styling changes:

    // Priority order for locators:
    // 1. User-facing attributes
    await page.getByRole('button', { name: 'Save' });
    await page.getByLabel('Email address');
    await page.getByPlaceholder('Search products...');
    await page.getByText('Welcome back');
    
    // 2. Test IDs (when needed for complex elements)
    await page.getByTestId('product-card-12345');
    
    // 3. CSS/XPath (avoid when possible)
    await page.locator('css=.dynamic-content'); // Last resort
    

    3. Isolate Test State

    Each test should run independently:

    test.describe('User Profile', () => {
      // Create fresh user for each test
      test.beforeEach(async ({ page, request }) => {
        const user = await createTestUser(request);
        await authenticateUser(page, user);
      });
    
      test.afterEach(async ({ request }, testInfo) => {
        // Cleanup test data
        await cleanupTestUser(request, testInfo);
      });
    
      test('update profile name', async ({ page }) => {
        // Test runs with isolated user
      });
    });
    

    4. Handle Asynchronous Operations

    Playwright auto-waits, but be explicit when needed:

    // Auto-wait handles most cases
    await page.getByRole('button', { name: 'Load data' }).click();
    await expect(page.getByTestId('data-table')).toBeVisible();
    
    // Explicit wait for specific conditions
    await page.waitForResponse(resp =>
      resp.url().includes('/api/data') && resp.status() === 200
    );
    
    // Wait for network idle
    await page.waitForLoadState('networkidle');
    

    5. Implement Retry Logic Wisely

    Configure retries for flaky scenarios, but investigate root causes:

    // playwright.config.ts
    export default defineConfig({
      retries: process.env.CI ? 2 : 0,
      use: {
        // Retry navigation on failure
        navigationTimeout: 30000,
        actionTimeout: 15000,
      },
    });
    
    // Per-test retry configuration
    test('flaky integration test', async ({ page }) => {
      test.info().annotations.push({
        type: 'flaky',
        description: 'External API occasionally slow'
      });
      // Test implementation
    });
    

    Testing Modern SPAs

    React Testing Patterns

    // Wait for React to hydrate
    await page.waitForFunction(() => window.React && window.React.version);
    
    // Test React components
    const component = page.getByTestId('user-list');
    await expect(component).toBeVisible();
    await expect(component.locator('[data-testid="user-item"]')).toHaveCount(5);
    

    Vue Testing Patterns

    // Wait for Vue app to mount
    await page.waitForFunction(() => window.Vue && document.querySelector('#app').__vue__);
    
    // Interact with Vue components
    await page.getByRole('button', { name: 'Toggle' }).click();
    await expect(page.getByTestId('toggle-state')).toHaveText('active');
    

    Angular Testing Patterns

    // Wait for Angular to bootstrap
    await page.waitForFunction(() => window.getAllAngularTestabilities);
    
    // Test Angular forms
    await page.getByLabel('Username').fill('testuser');
    await page.getByLabel('Email').fill('test@example.com');
    await expect(page.getByRole('button', { name: 'Submit' })).toBeEnabled();
    

    Quick Reference: Common Patterns

    Authentication Flow

    export async function authenticateUser(page: Page, credentials: UserCredentials) {
      await page.goto('/login');
      await page.getByLabel('Email').fill(credentials.email);
      await page.getByLabel('Password').fill(credentials.password);
      await page.getByRole('button', { name: 'Sign in' }).click();
      await page.waitForURL('/dashboard');
    }
    

    API Mocking

    await page.route('**/api/users', async route => {
      await route.fulfill({
        status: 200,
        contentType: 'application/json',
        body: JSON.stringify([{ id: 1, name: 'Test User' }])
      });
    });
    

    File Upload

    const fileInput = page.getByLabel('Upload file');
    await fileInput.setInputFiles('path/to/file.pdf');
    await expect(page.getByText('file.pdf')).toBeVisible();
    

    Accessibility Testing

    import { injectAxe, checkA11y } from 'axe-playwright';
    
    test('page is accessible', async ({ page }) => {
      await page.goto('/');
      await injectAxe(page);
      await checkA11y(page, null, {
        detailedReport: true,
        detailedReportOptions: { html: true }
      });
    });
    

    Resources

    This skill includes comprehensive reference materials and templates:

    references/page-object-patterns.md

    Advanced Page Object Model patterns including fixtures, page managers, composition patterns, and enterprise-scale organization strategies.

    Load when: Implementing page objects, organizing large test suites, or creating reusable test components.

    references/component-testing-guide.md

    Guide for testing UI components in isolation for React, Vue, and Angular applications, including mounting strategies and state management.

    Load when: Testing individual components, setting up component test harnesses, or testing component interactions.

    references/visual-testing-guide.md

    Visual regression testing strategies including screenshot comparison, viewport testing, cross-browser visual testing, and baseline management.

    Load when: Implementing visual tests, managing screenshot baselines, or debugging visual differences.

    references/locator-strategies.md

    Modern selector strategies for reliable element location, including auto-waiting patterns, shadow DOM traversal, and dynamic content handling.

    Load when: Writing resilient selectors, debugging flaky locators, or handling complex DOM structures.

    references/anti-patterns.md

    Common Playwright testing mistakes and how to avoid them, including test coupling, hard-coded waits, and improper assertion patterns.

    Load when: Reviewing test quality, debugging failures, or training team members on best practices.

    Scripts

    • init-playwright.ts: Initialize Playwright in existing project with TypeScript
    • generate-page-object.ts: Generate page object scaffolding from URL
    • visual-baseline.ts: Manage visual regression baselines

    Templates

    • base-page.template.ts: Base page class with common functionality
    • component-test.template.ts: Component testing setup
    • fixture.template.ts: Custom fixture patterns
    • playwright.config.template.ts: Production-ready configuration

    Tips for Success

    1. Start with critical paths: Test the most important user journeys first
    2. Use data-testid sparingly: Prefer semantic selectors, use test IDs only when necessary
    3. Run tests in parallel: Configure workers for faster execution
    4. Mock external dependencies: Use route handlers to control external services
    5. Review test reports: Analyze failures to identify patterns
    6. Keep tests focused: One test should verify one behavior
    7. Maintain test independence: Tests should not depend on execution order

    When to Load Reference Files

    To optimize context usage, load reference files strategically:

    • Load page-object-patterns.md when:

      • Implementing Page Object Model
      • Creating reusable test utilities
      • Organizing large test suites
      • Need fixture patterns
    • Load component-testing-guide.md when:

      • Testing components in isolation
      • Setting up component test environment
      • Working with framework-specific components
    • Load visual-testing-guide.md when:

      • Implementing screenshot tests
      • Managing visual baselines
      • Setting up visual regression workflow
    • Load locator-strategies.md when:

      • Writing complex selectors
      • Debugging flaky tests
      • Working with dynamic content
    • Load anti-patterns.md when:

      • Reviewing existing tests
      • Training team members
      • Debugging test failures
      • Improving test quality
    Repository
    vinceh/recipe
    Files