P

Pro Playwright Skill

Boost productivity using this complete, browser, automation, playwright. Includes structured workflows, validation checks, and reusable patterns for utilities.

SkillClipticsutilitiesv1.0.0MIT
0 views0 copies

Playwright Testing

A professional end-to-end testing skill for web applications using Playwright, covering test writing, page object patterns, visual regression testing, and CI/CD integration.

When to Use

Choose Playwright when:

  • Writing end-to-end tests that simulate real user interactions across browsers
  • Testing web applications that require JavaScript rendering and dynamic content
  • Building visual regression test suites with screenshot comparisons
  • Running parallel cross-browser tests in CI/CD pipelines

Consider alternatives when:

  • Writing unit tests for components — use Jest, Vitest, or Testing Library
  • Testing REST APIs — use Supertest or Postman/Newman
  • Performing simple smoke tests — use lightweight HTTP checking tools

Quick Start

# Initialize Playwright in a project npm init playwright@latest # Run tests npx playwright test # Run with UI mode for debugging npx playwright test --ui
import { test, expect, type Page } from '@playwright/test'; test.describe('Authentication', () => { test('should log in with valid credentials', async ({ page }) => { await page.goto('/login'); await page.fill('[data-testid="email"]', '[email protected]'); await page.fill('[data-testid="password"]', 'secure-password'); await page.click('[data-testid="login-button"]'); await expect(page).toHaveURL('/dashboard'); await expect(page.locator('[data-testid="welcome-message"]')) .toContainText('Welcome'); }); test('should show error for invalid credentials', async ({ page }) => { await page.goto('/login'); await page.fill('[data-testid="email"]', '[email protected]'); await page.fill('[data-testid="password"]', 'wrong'); await page.click('[data-testid="login-button"]'); await expect(page.locator('.error-alert')).toBeVisible(); await expect(page.locator('.error-alert')) .toContainText('Invalid credentials'); }); }); // API mocking for isolated testing test('should display products from API', async ({ page }) => { await page.route('**/api/products', async (route) => { await route.fulfill({ status: 200, contentType: 'application/json', body: JSON.stringify([ { id: 1, name: 'Product A', price: 29.99 }, { id: 2, name: 'Product B', price: 49.99 } ]) }); }); await page.goto('/products'); await expect(page.locator('.product-card')).toHaveCount(2); await expect(page.locator('.product-card').first()).toContainText('Product A'); });

Core Concepts

Test Organization Patterns

PatternUse CaseFiles
Page Object ModelUI interaction abstractionpages/*.ts
FixturesShared setup/teardownfixtures/*.ts
Test HelpersCommon assertions/actionshelpers/*.ts
API MocksDeterministic datamocks/*.ts
Visual BaselinesScreenshot referencesscreenshots/*.png

Page Object Model Implementation

// pages/DashboardPage.ts import { type Page, type Locator, expect } from '@playwright/test'; export class DashboardPage { readonly page: Page; readonly heading: Locator; readonly statsCards: Locator; readonly searchInput: Locator; readonly userMenu: Locator; constructor(page: Page) { this.page = page; this.heading = page.locator('h1'); this.statsCards = page.locator('[data-testid="stat-card"]'); this.searchInput = page.locator('[data-testid="search"]'); this.userMenu = page.locator('[data-testid="user-menu"]'); } async goto() { await this.page.goto('/dashboard'); await expect(this.heading).toBeVisible(); } async search(query: string) { await this.searchInput.fill(query); await this.searchInput.press('Enter'); await this.page.waitForResponse('**/api/search*'); } async getStats() { const cards = await this.statsCards.all(); return Promise.all(cards.map(async (card) => ({ label: await card.locator('.label').textContent(), value: await card.locator('.value').textContent() }))); } async logout() { await this.userMenu.click(); await this.page.click('[data-testid="logout"]'); await expect(this.page).toHaveURL('/login'); } } // tests/dashboard.spec.ts import { test, expect } from '@playwright/test'; import { DashboardPage } from '../pages/DashboardPage'; test('should display dashboard stats', async ({ page }) => { const dashboard = new DashboardPage(page); await dashboard.goto(); const stats = await dashboard.getStats(); expect(stats.length).toBeGreaterThan(0); expect(stats[0].value).toBeDefined(); });

Configuration

OptionDescriptionDefault
baseURLApplication base URL for tests"http://localhost:3000"
browsersBrowsers to test: chromium, firefox, webkit["chromium"]
headlessRun browsers in headless modetrue
workersParallel test workers50% of CPU cores
retriesNumber of test retries on failure0 (2 in CI)
timeoutTest timeout in milliseconds30000
screenshotScreenshot on failure: on, off, only-on-failure"only-on-failure"
traceTrace recording: on, off, on-first-retry"on-first-retry"

Best Practices

  1. Use data-testid attributes for selectors instead of CSS classes or dynamic IDs — test IDs are stable across redesigns and make it clear which elements are used by tests, preventing accidental breakage
  2. Enable tracing on first retry in CI so that failing tests produce a downloadable trace file showing every action, network request, and screenshot — this eliminates the "works on my machine" debugging problem
  3. Mock external APIs using page.route() to make tests deterministic and fast — real API calls introduce flakiness from network latency, rate limits, and changing data
  4. Run tests in parallel across browsers by configuring Playwright projects for chromium, firefox, and webkit, letting the test runner distribute work across available CPU cores
  5. Use visual regression testing sparingly for critical UI components like checkout forms and landing pages, not for every page — too many screenshot tests create a maintenance burden when the design evolves intentionally

Common Issues

Flaky tests in CI but stable locally: CI environments are slower and have different timing characteristics. Use Playwright's auto-waiting instead of manual timeouts, add waitForResponse after actions that trigger API calls, and enable retries in CI with retries: 2 in the Playwright config while keeping retries at 0 locally to catch issues early.

Authentication state not shared between tests: Each test starts with a fresh browser context. Use Playwright's storageState feature to save authentication cookies after a setup step, then load that state in subsequent tests so every test does not need to log in independently.

Visual regression tests failing on different OS: Font rendering differs between macOS, Linux, and Windows, causing screenshot diffs. Run visual tests in Docker containers with consistent fonts and rendering, or use a platform-aware threshold that allows minor pixel differences while still catching meaningful visual regressions.

Community

Reviews

Write a review

No reviews yet. Be the first to review this template!

Similar Templates