Generate comprehensive end-to-end tests following Playwright best practices for locators, assertions, fixtures, parallelism, and debugging.
Generate end-to-end tests following Playwright's recommended best practices for robust, maintainable test automation.
This skill helps you write Playwright tests that follow official best practices including:
When the user requests Playwright test generation or test improvements, follow these steps:
Ask the user:
**Test user-visible behavior:**
**Ensure test isolation:**
**Prioritize user-facing locators (best to worst):**
```typescript
// ✅ BEST: Role-based locators
await page.getByRole('button', { name: 'Submit' });
await page.getByRole('textbox', { name: 'Username' });
// ✅ GOOD: Text content
await page.getByText('Welcome back');
await page.getByLabel('Password');
// ✅ GOOD: Test IDs for dynamic content
await page.getByTestId('product-card-123');
// ❌ AVOID: CSS selectors (brittle)
await page.locator('button.btn-primary.submit-btn');
// ❌ AVOID: XPath (brittle)
await page.locator('//button[@class="submit"]');
```
**Use chaining and filtering:**
```typescript
const product = page.getByRole('listitem').filter({ hasText: 'Product 2' });
await product.getByRole('button', { name: 'Add to cart' }).click();
```
**Always await assertions:**
```typescript
// ✅ Correct: Auto-waits and retries
await expect(page.getByText('welcome')).toBeVisible();
await expect(page.getByRole('button')).toBeEnabled();
// ❌ Wrong: No auto-waiting
expect(await page.getByText('welcome').isVisible()).toBe(true);
```
**Common web-first assertions:**
**Use beforeEach for common setup:**
```typescript
import { test } from '@playwright/test';
test.beforeEach(async ({ page }) => {
// Runs before each test
await page.goto('https://example.com/login');
await page.getByLabel('Username').fill('testuser');
await page.getByLabel('Password').fill('password');
await page.getByRole('button', { name: 'Sign in' }).click();
});
test('user can view dashboard', async ({ page }) => {
// Already signed in from beforeEach
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
});
test('user can update profile', async ({ page }) => {
// Each test gets fresh signed-in state
await page.getByRole('link', { name: 'Profile' }).click();
});
```
**Create custom fixtures for reusable setup:**
```typescript
import { test as base } from '@playwright/test';
const test = base.extend({
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: 'Sign in' }).click();
await use(page);
},
});
test('dashboard test', async ({ authenticatedPage }) => {
await expect(authenticatedPage.getByRole('heading')).toBeVisible();
});
```
**In playwright.config.ts:**
```typescript
export default defineConfig({
// Run tests in parallel across files
fullyParallel: true,
// Number of workers (defaults to CPU cores / 2)
workers: process.env.CI ? 2 : undefined,
// Retry failed tests on CI
retries: process.env.CI ? 2 : 0,
// Multiple browsers
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'firefox', use: { ...devices['Desktop Firefox'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
{ name: 'mobile', use: { ...devices['Pixel 5'] } },
],
});
```
**Configure traces for CI failures:**
```typescript
export default defineConfig({
use: {
// Capture trace on first retry of failed test
trace: 'on-first-retry',
// Screenshot on failure
screenshot: 'only-on-failure',
// Video on failure
video: 'retain-on-failure',
},
});
```
**Local debugging commands:**
```bash
npx playwright test --debug
npx playwright test example.spec.ts:42 --debug
npx playwright test --trace on
npx playwright show-report
```
**Don't test third-party dependencies:**
```typescript
// ❌ Don't test external sites
await page.goto('https://external-api.com');
// ✅ Mock external dependencies
await page.route('**/api/external/**', route => route.fulfill({
status: 200,
body: JSON.stringify({ data: 'mocked' }),
}));
```
**Don't use manual waits:**
```typescript
// ❌ Arbitrary waits are flaky
await page.waitForTimeout(3000);
// ✅ Wait for specific conditions
await page.waitForLoadState('networkidle');
await expect(page.getByText('Loaded')).toBeVisible();
```
Create a test file with:
**Example structure:**
```typescript
import { test, expect } from '@playwright/test';
test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/feature-url');
});
test('should perform user action successfully', async ({ page }) => {
// Arrange: Get to initial state (often done in beforeEach)
// Act: Perform user action
await page.getByRole('button', { name: 'Action' }).click();
// Assert: Verify expected outcome
await expect(page.getByText('Success message')).toBeVisible();
});
test('should handle error case gracefully', async ({ page }) => {
// Test error scenarios
});
});
```
Include:
**Example 1: E-commerce checkout flow**
```typescript
test.describe('Checkout Flow', () => {
test.beforeEach(async ({ page }) => {
await page.goto('/products');
await page.getByRole('button', { name: 'Add to cart' }).first().click();
});
test('completes purchase successfully', async ({ page }) => {
await page.getByRole('link', { name: 'Cart' }).click();
await page.getByRole('button', { name: 'Checkout' }).click();
await page.getByLabel('Email').fill('[email protected]');
await page.getByLabel('Card number').fill('4242424242424242');
await page.getByRole('button', { name: 'Pay' }).click();
await expect(page.getByText('Order confirmed')).toBeVisible();
});
});
```
**Example 2: Form validation**
```typescript
test('shows validation errors for invalid input', async ({ page }) => {
await page.goto('/signup');
await page.getByRole('button', { name: 'Sign up' }).click();
await expect(page.getByText('Email is required')).toBeVisible();
await expect(page.getByText('Password is required')).toBeVisible();
await page.getByLabel('Email').fill('invalid-email');
await page.getByRole('button', { name: 'Sign up' }).click();
await expect(page.getByText('Invalid email format')).toBeVisible();
});
```
Leave a review
No reviews yet. Be the first to review this skill!
# Download SKILL.md from killerskills.ai/api/skills/playwright-best-practices-testing/raw