TechSetupGuides
Intermediatetestingplaywrighte2e-testingautomationchromiumfirefoxwebkittypescriptjavascripttest-automationweb-testingcross-browser

Playwright - End-to-End Testing Framework

Modern end-to-end testing framework for web applications with cross-browser support for Chromium, Firefox, and WebKit using a single unified API.

  1. Step 1

    Overview

    Playwright is a framework for Web Testing and Automation developed by Microsoft. It enables reliable end-to-end testing for modern web apps across Chromium, Firefox, and WebKit with a single API. Playwright supports multiple programming languages (TypeScript, JavaScript, Python, Java, .NET), provides auto-wait capabilities, powerful debugging tools, and parallel test execution. It's designed to handle modern web features including SPAs, mobile emulation, network interception, and accessibility testing.

  2. Step 2

    Technology Stack

    Playwright's architecture and core technologies.

    Language: TypeScript
    License: Apache License 2.0
    Stars: ~90K
    Owner: microsoft
    Repo: https://github.com/microsoft/playwright
    Website: https://playwright.dev
    Created: November 2019
    
    Core Technologies:
    - TypeScript 6.0+ - Primary language
    - Node.js (>=18) - Runtime environment
    - Chromium-BiDi - Chrome DevTools Protocol integration
    - WebSocket (ws) - Browser communication
    - Vite - Build tool for Playwright UI
    - React 19 - Trace viewer and UI components
    - esbuild - Fast bundling
    - Babel - Code transformation
    - Debug - Logging utilities
    
    Key Features:
    - Cross-browser testing (Chromium, Firefox, WebKit)
    - Auto-wait and retry-ability
    - Network interception and mocking
    - Mobile device emulation
    - Parallel test execution
    - Built-in test runner (@playwright/test)
    - Trace viewer for debugging
    - Screenshots and videos
    - Accessibility testing
    - API testing capabilities
  3. Step 3

    Installation

    Install Playwright and browser binaries using npm, yarn, or pnpm.

    # Install with npm
    npm init playwright@latest
    
    # Or install manually
    npm install -D @playwright/test
    npx playwright install
    
    # Install specific browsers
    npx playwright install chromium
    npx playwright install firefox
    npx playwright install webkit
    
    # Install system dependencies (Linux)
    pnx playwright install-deps
  4. Step 4

    Project Setup

    The init command creates a basic project structure with example tests and configuration.

    # Initialize new Playwright project (interactive)
    npm init playwright@latest
    
    # Creates:
    # - playwright.config.ts (or .js)
    # - tests/ directory with example tests
    # - tests-examples/ directory
    # - .github/workflows/playwright.yml (CI config)
  5. Step 5

    Basic Configuration

    Configure Playwright through playwright.config.ts for test execution, browsers, and reporting.

    // playwright.config.ts
    import { defineConfig, devices } from '@playwright/test'
    
    export default defineConfig({
      testDir: './tests',
      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',
        video: 'retain-on-failure'
      },
    
      projects: [
        { name: 'chromium', use: { ...devices['Desktop Chrome'] } },
        { name: 'firefox', use: { ...devices['Desktop Firefox'] } },
        { name: 'webkit', use: { ...devices['Desktop Safari'] } },
        { name: 'mobile-chrome', use: { ...devices['Pixel 5'] } },
        { name: 'mobile-safari', use: { ...devices['iPhone 13'] } }
      ],
    
      webServer: {
        command: 'npm run dev',
        url: 'http://localhost:3000',
        reuseExistingServer: !process.env.CI
      }
    })
  6. Step 6

    Writing Your First Test

    Create test files in the tests directory with .spec.ts or .spec.js extension.

    // tests/example.spec.ts
    import { test, expect } from '@playwright/test'
    
    test('homepage has title and login link', async ({ page }) => {
      await page.goto('https://playwright.dev/')
      
      // Expect a title to contain Playwright
      await expect(page).toHaveTitle(/Playwright/)
      
      // Create a locator
      const getStarted = page.getByRole('link', { name: 'Get started' })
      
      // Expect link to be visible
      await expect(getStarted).toBeVisible()
      
      // Click the link
      await getStarted.click()
      
      // Expects page to have a heading
      await expect(page.getByRole('heading', { name: 'Installation' })).toBeVisible()
    })
  7. Step 7

    Running Tests

    Execute tests using the Playwright test runner.

    # Run all tests
    npx playwright test
    
    # Run tests in headed mode (visible browser)
    npx playwright test --headed
    
    # Run specific test file
    npx playwright test tests/example.spec.ts
    
    # Run tests in specific browser
    npx playwright test --project=chromium
    
    # Run tests in debug mode
    npx playwright test --debug
    
    # Run tests with UI mode (recommended for development)
    npx playwright test --ui
    
    # Run specific test by title
    npx playwright test -g "homepage has title"
    
    # Show test report
    npx playwright show-report
  8. Step 8

    Locators (Finding Elements)

    Playwright's locator strategy prioritizes user-facing attributes and auto-waits for elements.

    import { test, expect } from '@playwright/test'
    
    test('locator examples', async ({ page }) => {
      await page.goto('https://example.com')
      
      // Recommended: Role-based (accessibility-first)
      await page.getByRole('button', { name: 'Submit' }).click()
      await page.getByRole('textbox', { name: 'Email' }).fill('test@example.com')
      
      // By text content
      await page.getByText('Welcome').click()
      
      // By label (for form inputs)
      await page.getByLabel('Password').fill('secret')
      
      // By placeholder
      await page.getByPlaceholder('Search...').fill('query')
      
      // By test id (data-testid attribute)
      await page.getByTestId('submit-button').click()
      
      // By CSS or XPath (less recommended)
      await page.locator('css=.btn-primary').click()
      await page.locator('xpath=//button[text()="Submit"]').click()
      
      // Combining and filtering
      await page.getByRole('listitem').filter({ hasText: 'Active' }).click()
      await page.getByRole('heading').nth(2).click()
    }
  9. Step 9

    Assertions

    Playwright provides auto-retrying assertions that wait until the condition is met.

    import { test, expect } from '@playwright/test'
    
    test('assertion examples', async ({ page }) => {
      await page.goto('https://example.com')
      
      // Page assertions
      await expect(page).toHaveTitle(/Example/)
      await expect(page).toHaveURL(/example.com/)
      
      // Element visibility
      await expect(page.getByRole('button')).toBeVisible()
      await expect(page.getByText('Hidden')).toBeHidden()
      
      // Element state
      await expect(page.getByRole('button')).toBeEnabled()
      await expect(page.getByRole('button')).toBeDisabled()
      await expect(page.getByRole('checkbox')).toBeChecked()
      
      // Text content
      await expect(page.getByRole('heading')).toHaveText('Welcome')
      await expect(page.getByRole('heading')).toContainText('Wel')
      
      // Attributes
      await expect(page.getByRole('link')).toHaveAttribute('href', '/about')
      await expect(page.getByRole('img')).toHaveAttribute('alt', /logo/i)
      
      // Count
      await expect(page.getByRole('listitem')).toHaveCount(5)
      
      // Value (for inputs)
      await expect(page.getByLabel('Email')).toHaveValue('test@example.com')
      
      // Screenshots (visual regression)
      await expect(page).toHaveScreenshot('homepage.png')
    }
  10. Step 10

    Page Interactions

    Common user interactions with automatic waiting and actionability checks.

    import { test } from '@playwright/test'
    
    test('interaction examples', async ({ page }) => {
      await page.goto('https://example.com')
      
      // Click
      await page.getByRole('button', { name: 'Submit' }).click()
      
      // Double click
      await page.getByRole('button').dblclick()
      
      // Right click
      await page.getByRole('button').click({ button: 'right' })
      
      // Fill input
      await page.getByLabel('Email').fill('user@example.com')
      
      // Type with delay (simulates human typing)
      await page.getByLabel('Search').type('playwright', { delay: 100 })
      
      // Clear input
      await page.getByLabel('Email').clear()
      
      // Check/uncheck
      await page.getByLabel('Subscribe').check()
      await page.getByLabel('Subscribe').uncheck()
      
      // Select dropdown
      await page.getByLabel('Country').selectOption('us')
      await page.getByLabel('Country').selectOption({ label: 'United States' })
      
      // Upload file
      await page.getByLabel('Upload').setInputFiles('path/to/file.pdf')
      
      // Hover
      await page.getByRole('button').hover()
      
      // Focus
      await page.getByLabel('Email').focus()
      
      // Press keys
      await page.keyboard.press('Enter')
      await page.keyboard.type('Hello World')
    }
  11. Step 11

    Navigation and Waiting

    Navigate between pages and wait for specific conditions.

    import { test } from '@playwright/test'
    
    test('navigation examples', async ({ page }) => {
      // Navigate
      await page.goto('https://example.com')
      await page.goto('https://example.com', { waitUntil: 'networkidle' })
      
      // Wait for navigation
      await Promise.all([
        page.waitForNavigation(),
        page.getByRole('link').click()
      ])
      
      // Back/forward
      await page.goBack()
      await page.goForward()
      await page.reload()
      
      // Wait for selector
      await page.waitForSelector('.loaded')
      
      // Wait for load state
      await page.waitForLoadState('domcontentloaded')
      await page.waitForLoadState('networkidle')
      
      // Wait for URL
      await page.waitForURL('**/dashboard')
      
      // Wait for function
      await page.waitForFunction(() => document.title === 'Ready')
      
      // Wait for timeout (use sparingly)
      await page.waitForTimeout(1000)
      
      // Wait for element to be visible
      await page.waitForSelector('text=Welcome', { state: 'visible' })
    }
  12. Step 12

    Test Fixtures and Hooks

    Use fixtures for setup and teardown, and hooks for test lifecycle management.

    import { test, expect } from '@playwright/test'
    
    // Before and after hooks
    test.beforeEach(async ({ page }) => {
      await page.goto('https://example.com')
      await page.getByRole('button', { name: 'Login' }).click()
    })
    
    test.afterEach(async ({ page }) => {
      await page.getByRole('button', { name: 'Logout' }).click()
    })
    
    test.beforeAll(async () => {
      console.log('Before all tests')
    })
    
    test.afterAll(async () => {
      console.log('After all tests')
    })
    
    // Test with fixtures
    test('user can view dashboard', async ({ page, context }) => {
      await expect(page).toHaveURL(/dashboard/)
    })
    
    // Custom fixtures
    const test2 = test.extend<{ authenticatedPage: Page }>({
      authenticatedPage: async ({ page }, use) => {
        await page.goto('https://example.com/login')
        await page.getByLabel('Username').fill('testuser')
        await page.getByLabel('Password').fill('password')
        await page.getByRole('button', { name: 'Login' }).click()
        await use(page)
      }
    })
  13. Step 13

    Network Interception and Mocking

    Intercept and mock network requests for testing.

    import { test, expect } from '@playwright/test'
    
    test('network interception', async ({ page }) => {
      // Mock API response
      await page.route('**/api/users', async route => {
        await route.fulfill({
          status: 200,
          contentType: 'application/json',
          body: JSON.stringify([{ id: 1, name: 'Test User' }])
        })
      })
      
      // Abort images
      await page.route('**/*.{png,jpg,jpeg}', route => route.abort())
      
      // Modify request
      await page.route('**/api/**', async route => {
        const headers = await route.request().allHeaders()
        await route.continue({
          headers: { ...headers, 'X-Custom-Header': 'value' }
        })
      })
      
      // Wait for request
      const [request] = await Promise.all([
        page.waitForRequest('**/api/data'),
        page.getByRole('button', { name: 'Load' }).click()
      ])
      
      // Wait for response
      const [response] = await Promise.all([
        page.waitForResponse('**/api/data'),
        page.getByRole('button', { name: 'Load' }).click()
      ])
      
      expect(response.status()).toBe(200)
      const data = await response.json()
      expect(data).toHaveProperty('users')
    }
  14. Step 14

    Screenshots and Videos

    Capture screenshots and videos for debugging and documentation.

    import { test } from '@playwright/test'
    
    test('screenshot examples', async ({ page }) => {
      await page.goto('https://example.com')
      
      // Full page screenshot
      await page.screenshot({ path: 'screenshot.png', fullPage: true })
      
      // Element screenshot
      await page.getByRole('heading').screenshot({ path: 'heading.png' })
      
      // Screenshot to buffer
      const buffer = await page.screenshot()
      
      // Screenshot with options
      await page.screenshot({
        path: 'screenshot.png',
        type: 'png',
        quality: 90,
        fullPage: true
      })
    })
    
    // Video configuration in playwright.config.ts
    // Videos are automatically recorded when configured:
    // use: {
    //   video: 'on',  // or 'retain-on-failure', 'on-first-retry'
    //   videoSize: { width: 1280, height: 720 }
    // }
  15. Step 15

    Mobile Device Emulation

    Test on emulated mobile devices with touch and geolocation support.

    import { test, devices } from '@playwright/test'
    
    test('mobile emulation', async ({ page }) => {
      // Configure in playwright.config.ts:
      // { name: 'mobile', use: { ...devices['iPhone 13'] } }
      
      await page.goto('https://example.com')
      
      // Or use device descriptor directly
      const iPhone = devices['iPhone 13']
      await page.setViewportSize(iPhone.viewport)
      
      // Touch actions
      await page.tap('button')
      
      // Set geolocation
      await page.context().setGeolocation({
        latitude: 37.7749,
        longitude: -122.4194
      })
      await page.context().grantPermissions(['geolocation'])
      
      // Landscape orientation
      await page.setViewportSize({ width: 844, height: 390 })
    })
    
    // Available devices:
    const deviceList = [
      'iPhone 13', 'iPhone 13 Pro', 'iPhone 13 Pro Max',
      'Pixel 5', 'Galaxy S9+', 'iPad Pro',
      'Desktop Chrome', 'Desktop Firefox', 'Desktop Safari'
    ]
  16. Step 16

    API Testing

    Use Playwright's request context for API testing without a browser.

    import { test, expect } from '@playwright/test'
    
    test('API testing', async ({ request }) => {
      // GET request
      const response = await request.get('https://api.example.com/users')
      expect(response.ok()).toBeTruthy()
      expect(response.status()).toBe(200)
      const users = await response.json()
      expect(users).toHaveLength(10)
      
      // POST request
      const newUser = await request.post('https://api.example.com/users', {
        data: {
          name: 'Test User',
          email: 'test@example.com'
        }
      })
      expect(newUser.ok()).toBeTruthy()
      
      // With authentication
      const authenticatedRequest = await request.fetch('https://api.example.com/me', {
        headers: {
          'Authorization': 'Bearer token123'
        }
      })
      
      // Reuse authentication in browser tests
      const loginResponse = await request.post('https://example.com/api/login', {
        form: { username: 'user', password: 'pass' }
      })
      const cookies = await loginResponse.headers()['set-cookie']
      // Cookies are automatically shared with page context
    })
  17. Step 17

    Parallel Testing

    Configure parallel test execution for faster test runs.

    // playwright.config.ts
    import { defineConfig } from '@playwright/test'
    
    export default defineConfig({
      // Run tests in parallel
      fullyParallel: true,
      
      // Number of parallel workers
      workers: process.env.CI ? 2 : undefined,  // undefined = CPU cores
      
      // Limit parallel tests per file
      maxFailures: process.env.CI ? 10 : undefined,
      
      // Retry failed tests
      retries: process.env.CI ? 2 : 0
    })
    
    // Run sequentially in a file
    import { test } from '@playwright/test'
    
    test.describe.configure({ mode: 'serial' })
    
    test.describe('serial tests', () => {
      test('runs first', async ({ page }) => { })
      test('runs second', async ({ page }) => { })
    })
    
    // Run tests in parallel (default)
    test.describe.configure({ mode: 'parallel' })
  18. Step 18

    Debugging Tests

    Multiple debugging options including UI mode, debug mode, and trace viewer.

    # UI Mode (recommended for development)
    npx playwright test --ui
    
    # Debug mode (step through tests)
    npx playwright test --debug
    
    # Debug specific test
    npx playwright test tests/example.spec.ts:10 --debug
    
    # Headed mode (see browser)
    npx playwright test --headed
    
    # Slow motion (delay actions)
    npx playwright test --headed --slow-mo=1000
    
    # Generate trace
    npx playwright test --trace on
    
    # View trace
    npx playwright show-trace trace.zip
    
    # Codegen (record actions)
    npx playwright codegen https://example.com
    
    # Inspector
    PWDEBUG=1 npx playwright test
  19. Step 19

    Trace Viewer

    Powerful debugging tool that records test execution with DOM snapshots, network activity, and screenshots.

    # Configure in playwright.config.ts:
    # use: {
    #   trace: 'on-first-retry',  // or 'on', 'retain-on-failure'
    # }
    
    # View trace from failed test
    npx playwright show-trace test-results/example-chromium/trace.zip
    
    # Trace includes:
    # - DOM snapshots at every action
    # - Screenshots
    # - Network activity
    # - Console logs
    # - Source code
    # - Test steps timeline
  20. Step 20

    CI/CD Integration

    Configure Playwright for continuous integration environments.

    # .github/workflows/playwright.yml
    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: 18
        - name: Install dependencies
          run: npm ci
        - name: Install Playwright Browsers
          run: npx playwright install --with-deps
        - name: Run Playwright tests
          run: npx playwright test
        - uses: actions/upload-artifact@v4
          if: always()
          with:
            name: playwright-report
            path: playwright-report/
            retention-days: 30
  21. Step 21

    Authentication Storage

    Save and reuse authentication state across tests for faster execution.

    // auth.setup.ts
    import { test as setup } from '@playwright/test'
    
    const authFile = 'playwright/.auth/user.json'
    
    setup('authenticate', async ({ page }) => {
      await page.goto('https://example.com/login')
      await page.getByLabel('Username').fill('user@example.com')
      await page.getByLabel('Password').fill('password')
      await page.getByRole('button', { name: 'Sign in' }).click()
      await page.waitForURL('**/dashboard')
      
      // Save storage state
      await page.context().storageState({ path: authFile })
    })
    
    // playwright.config.ts
    export default defineConfig({
      projects: [
        { name: 'setup', testMatch: /.*\.setup\.ts/ },
        {
          name: 'chromium',
          use: {
            ...devices['Desktop Chrome'],
            storageState: authFile
          },
          dependencies: ['setup']
        }
      ]
    })
  22. Step 22

    Page Object Model

    Organize tests using the Page Object Model pattern for maintainability.

    // pages/LoginPage.ts
    import { Page, Locator } from '@playwright/test'
    
    export class LoginPage {
      readonly page: Page
      readonly usernameInput: Locator
      readonly passwordInput: Locator
      readonly submitButton: Locator
    
      constructor(page: Page) {
        this.page = page
        this.usernameInput = page.getByLabel('Username')
        this.passwordInput = page.getByLabel('Password')
        this.submitButton = page.getByRole('button', { name: 'Sign in' })
      }
    
      async login(username: string, password: string) {
        await this.usernameInput.fill(username)
        await this.passwordInput.fill(password)
        await this.submitButton.click()
      }
    
      async goto() {
        await this.page.goto('https://example.com/login')
      }
    }
    
    // tests/login.spec.ts
    import { test, expect } from '@playwright/test'
    import { LoginPage } from '../pages/LoginPage'
    
    test('user can login', async ({ page }) => {
      const loginPage = new LoginPage(page)
      await loginPage.goto()
      await loginPage.login('user@example.com', 'password')
      await expect(page).toHaveURL(/dashboard/)
    })
  23. Step 23

    Accessibility Testing

    Test accessibility using Playwright's built-in accessibility features.

    import { test, expect } from '@playwright/test'
    
    test('accessibility', async ({ page }) => {
      await page.goto('https://example.com')
      
      // Get accessibility tree
      const snapshot = await page.accessibility.snapshot()
      console.log(snapshot)
      
      // Test keyboard navigation
      await page.keyboard.press('Tab')
      const focused = await page.evaluate(() => document.activeElement?.tagName)
      expect(focused).toBe('BUTTON')
      
      // Check ARIA attributes
      await expect(page.getByRole('button')).toHaveAttribute('aria-label', 'Submit')
      
      // For comprehensive a11y testing, integrate @axe-core/playwright:
      // npm install -D @axe-core/playwright
      // import { injectAxe, checkA11y } from '@axe-core/playwright'
      // await injectAxe(page)
      // await checkA11y(page)
    }
  24. Step 24

    Test Reporters

    Configure different reporters for test output and CI integration.

    // playwright.config.ts
    import { defineConfig } from '@playwright/test'
    
    export default defineConfig({
      reporter: [
        ['html'],                           // HTML report (default)
        ['list'],                           // List reporter (console)
        ['json', { outputFile: 'results.json' }],
        ['junit', { outputFile: 'results.xml' }],
        ['github'],                         // GitHub Actions annotations
        ['dot'],                            // Minimal output
        ['line']                            // One line per test
      ]
    })
    
    // Custom reporter
    class MyReporter {
      onBegin(config, suite) {
        console.log(`Starting tests with ${suite.allTests().length} tests`)
      }
      
      onTestEnd(test, result) {
        console.log(`Finished ${test.title}: ${result.status}`)
      }
      
      onEnd(result) {
        console.log(`Finished the run: ${result.status}`)
      }
    }
    
    export default defineConfig({
      reporter: './my-reporter.ts'
    })
  25. Step 25

    Global Setup and Teardown

    Run setup and teardown code once for all tests.

    // global-setup.ts
    import { chromium, FullConfig } from '@playwright/test'
    
    async function globalSetup(config: FullConfig) {
      // Start a development server
      // Seed database
      // Setup test data
      console.log('Global setup')
      
      // Example: Create admin user
      const browser = await chromium.launch()
      const page = await browser.newPage()
      await page.goto('https://example.com/admin/setup')
      // ... setup logic
      await browser.close()
    }
    
    export default globalSetup
    
    // global-teardown.ts
    async function globalTeardown() {
      // Clean up test data
      // Stop servers
      console.log('Global teardown')
    }
    
    export default globalTeardown
    
    // playwright.config.ts
    export default defineConfig({
      globalSetup: require.resolve('./global-setup'),
      globalTeardown: require.resolve('./global-teardown')
    })
  26. Step 26

    Key Features Summary

    Comprehensive overview of Playwright's capabilities.

    1. Cross-browser: Chromium, Firefox, WebKit
    2. Auto-wait: Smart waiting for elements
    3. Trace Viewer: Time-travel debugging
    4. Codegen: Record and generate tests
    5. Parallel Execution: Fast test runs
    6. Network Control: Intercept and mock
    7. Mobile Emulation: Touch and geolocation
    8. API Testing: Test APIs without browser
    9. Screenshots & Videos: Visual documentation
    10. Multiple Languages: TS, JS, Python, Java, .NET
    11. UI Mode: Interactive test development
    12. Accessibility: Built-in a11y testing
    13. Retry & Timeout: Configurable resilience
    14. Authentication: State storage and reuse
    15. CI/CD Ready: GitHub Actions integration
    16. Component Testing: Isolated component tests
    17. Visual Comparisons: Screenshot assertions
    18. Reporters: HTML, JSON, JUnit, custom
  27. Step 27

    Use Cases

    Common scenarios where Playwright excels.

    1. E2E Testing: Full user flow testing
    2. Regression Testing: Prevent breaking changes
    3. Cross-browser: Ensure compatibility
    4. Mobile Web: Test responsive designs
    5. Visual Testing: Screenshot comparisons
    6. API Testing: Backend endpoint validation
    7. Web Scraping: Automated data extraction
    8. PDF Generation: Headless PDF creation
    9. Accessibility: A11y compliance testing
    10. Performance: Load time monitoring
    11. Integration: Third-party service testing
    12. Authentication: Login flow validation
    13. Forms: Complex form testing
    14. SPAs: Single-page app testing
    15. PWAs: Progressive web app testing
    16. Component: Isolated component tests
  28. Step 28

    Migration from Other Frameworks

    Guidelines for migrating from Selenium, Puppeteer, or Cypress.

    From Selenium:
    - Replace WebDriver with Page API
    - Auto-wait replaces explicit waits
    - Built-in test runner replaces Mocha/Jest
    - Locators use getBy* methods
    
    From Puppeteer:
    - Similar API, less manual waiting
    - Built-in test runner and assertions
    - Cross-browser support (not just Chromium)
    - Better TypeScript support
    
    From Cypress:
    - Similar auto-wait behavior
    - Native multi-tab/domain support
    - Parallel execution at file level
    - More flexible network control
    - Language choice (not just JS)
    
    Key differences:
    - No jQuery-style chaining
    - Page object model recommended
    - Context isolation per test
    - Browser-native events
  29. Step 29

    Best Practices

    Recommendations for effective Playwright testing.

    1. Use getByRole for accessibility-first locators
    2. Prefer auto-wait over manual timeouts
    3. Enable trace on first retry for debugging
    4. Use Page Object Model for organization
    5. Store authentication state to speed up tests
    6. Run tests in parallel for faster CI
    7. Use --ui mode for test development
    8. Mock network for flaky external APIs
    9. Use data-testid sparingly, prefer semantic selectors
    10. Configure retries for CI environments
    11. Screenshot/video on failure only
    12. Use fixtures for common setup
    13. Keep tests isolated and independent
    14. Use strict locators (fail on multiple matches)
    15. Test in multiple browsers
    16. Use TypeScript for better IDE support
  30. Step 30

    Resources

    Official documentation and community resources.

    Documentation: https://playwright.dev
    GitHub: https://github.com/microsoft/playwright
    API Reference: https://playwright.dev/docs/api/class-playwright
    Discord: https://aka.ms/playwright/discord
    Stack Overflow: [playwright] tag
    YouTube: Playwright Channel
    Examples: https://github.com/microsoft/playwright/tree/main/examples
    Best Practices: https://playwright.dev/docs/best-practices
    Community Guides: https://playwright.dev/community/welcome
    Release Notes: https://github.com/microsoft/playwright/releases
  31. Step 31

    File Locations

    Important files and directories in a Playwright project.

    playwright.config.ts       - Main configuration
    tests/                     - Test files (*.spec.ts)
    tests-examples/            - Example tests
    playwright-report/         - HTML test reports
    test-results/              - Screenshots, videos, traces
    playwright/.auth/          - Stored authentication states
    .github/workflows/         - CI/CD configurations
    node_modules/.playwright/  - Browser binaries
    global-setup.ts            - Global setup (optional)
    global-teardown.ts         - Global teardown (optional)
    pages/                     - Page objects (recommended)

Feature requests

Sign in to suggest features or vote on existing ones.

No feature requests yet.

Discussion

0 people marked this as worked·Sign in to mark your own.

Sign in to join the discussion.

No comments yet.