AgentSkillsCN

playwright

掌握 Playwright 的 Web 应用端到端测试最佳实践,涵盖测试设计、定位器策略以及断言模式。

SKILL.md
--- frontmatter
name: playwright
description: Playwright end-to-end testing best practices for web applications, covering test design, locator strategies, and assertion patterns.

Playwright Testing Best Practices

You are a Senior QA Automation Engineer expert in TypeScript, JavaScript, and Playwright end-to-end testing.

Test Design Principles

Test Structure

  • Create descriptive test names that clearly explain expected behavior
  • Use Playwright fixtures (test, page, expect) for test isolation
  • Implement test.beforeEach and test.afterEach for clean state management
  • Keep tests DRY by extracting reusable logic into helper functions
typescript
import { test, expect } from '@playwright/test'

test.describe('User Authentication', () => {
	test.beforeEach(async ({ page }) => {
		await page.goto('/login')
	})

	test('should login successfully with valid credentials', async ({ page }) => {
		await page.getByLabel('Email').fill('user@example.com')
		await page.getByLabel('Password').fill('password123')
		await page.getByRole('button', { name: 'Sign In' }).click()

		await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible()
	})
})

Locator Strategy

Recommended Locators

  • page.getByRole() - Best for accessibility and user perspective
  • page.getByLabel() - For form inputs with labels
  • page.getByText() - For elements with visible text
  • page.getByTestId() - When data-testid attributes exist
  • page.getByPlaceholder() - For inputs with placeholder text
typescript
// Recommended
await page.getByRole('button', { name: 'Submit' }).click()
await page.getByLabel('Email address').fill('test@example.com')

// Avoid
await page.locator('.btn-primary').click()

Assertions and Waits

Web-First Assertions

Prefer web-first assertions that automatically wait and retry:

  • toBeVisible() - Element is visible
  • toHaveText() - Element has specific text
  • toHaveValue() - Input has specific value
  • toHaveURL() - Page URL assertion
typescript
// Recommended - web-first assertions
await expect(page.getByRole('alert')).toBeVisible()
await expect(page).toHaveURL('/dashboard')

// Avoid - hardcoded timeouts
await page.waitForTimeout(5000) // Never do this

Waiting Best Practices

  • Avoid hardcoded timeouts
  • Use page.waitForLoadState() for navigation
  • Use page.waitForResponse() for API calls

Configuration

typescript
import { defineConfig, devices } from '@playwright/test'

export default defineConfig({
	testDir: './tests',
	fullyParallel: true,
	retries: process.env.CI ? 2 : 0,
	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: 'mobile', use: { ...devices['iPhone 13'] } },
	],
})

Best Practices

  • Focus on critical user paths reflecting real behavior
  • Keep tests independent and deterministic
  • Add JSDoc comments for helper functions
  • Implement proper error handling and logging