Back to skills
SKILL.md
Playwright Best Practices
ASecurityUse when Playwright End-to-End (E2E) testing mastery. Resilient selectors, auto-waiting mechanisms, parallel test execution, mocking network requests, fixture management, and cross-browser CI configurations. Use when configuring, deploying, or writing E2E web tests.
- 5 stars
- 0 votes
- 0 copies
- 0 views
- Added September 27, 2026
Works with
Security analysis
100/100npx -y skills add Harmitx7/tribunal-kit --skill playwright-best-practices --agent claude-codeAre you the author of Playwright Best Practices?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/harmitx7-playwright-best-practices-tribunal-kit)---
name: playwright-best-practices
description: "Use when Playwright End-to-End (E2E) testing mastery. Resilient selectors, auto-waiting mechanisms, parallel test execution, mocking network requests, fixture management, and cross-browser CI configurations. Use when configuring, deploying, or writing E2E web tests."
version: 5.0.0
last-updated: 2026-09-13
skills:
- testing-patterns
- webapp-testing
- qa-automation-engineer
tools: Read, Grep, Glob, Bash, Edit, Write
scripts-binding:
- .agent/scripts/test_runner.js
- .agent/scripts/verify_all.js
- .agent/scripts/lint_runner.js
---
# Playwright E2E β Bulletproof Testing Mastery
---
## π οΈ Technical Architecture & Reference Recipes
---
## Hallucination Traps (Read First)
- β Using `page.waitForTimeout(3000)` for synchronization -> β
Use `page.waitForSelector()`, `expect(locator).toBeVisible()`, or auto-waiting locators
- β Using CSS selectors or XPath for test locators -> β
Use `getByRole()`, `getByLabel()`, `getByTestId()` for resilient selectors
- β Running tests without `--workers=1` in CI debug mode -> β
Parallel tests with shared state cause flaky failures; isolate tests properly
- β Not using `test.describe.configure({ mode: 'serial' })` when tests have ordering dependencies -> β
Explicitly mark serial when needed
---
---
## 1. Resilience & Auto-Waiting
Playwright automatically waits for elements to be actionable (visible, stable, not obscured).
```typescript
// β FLAKY: Hardcoded sleeps. Fails on slow CI, wastes time on fast local rings.
await page.waitForTimeout(3000);
// β FLAKY: CSS selectors tied to layout/styling changes
await page.locator('.btn-primary > span').click();
// β
ROBUST: Playwright auto-waits for actionability based on user-centric selectors
await page.getByRole('button', { name: 'Submit Checkout' }).click();
// β
ROBUST: Testing for expected states
await expect(page.getByText('Order confirmed')).toBeVisible();
```
### The Selector Hierarchy (Best to Worst)
1. `page.getByRole()` β Checks accessibility simultaneously.
2. `page.getByText()` β Finds elements by raw text values.
3. `page.getByTestId()` β Resilient to text/translation updates (`data-testid`).
4. `page.locator('css')` β Brittle, bound to DOM structures. Use only as last resort.
---
## 2. Test Isolation & Fixtures
Do not cascade tests (where Test B requires Test A to pass first). Playwright gives every test a blank browser context isolated from the rest.
```typescript
import { test, expect } from '@playwright/test';
// β BAD: Cascading state
test.describe('Dashboard', () => {
test('Login', async ({ page }) => {
await login(page); // Next test assumes this succeeded
});
test('Action', async ({ page }) => {
await page.getByRole('button', { name: 'Save' }).click();
});
});
// β
GOOD: Isolated tests via beforeEach or Custom Fixtures
test.beforeEach(async ({ page }) => {
// Login directly via API to bypass slow UI login, seeding cookies
await performFastApiLogin(page);
await page.goto('/dashboard');
});
test('Should save settings', async ({ page }) => {
await page.getByRole('button', { name: 'Save' }).click();
await expect(page.getByRole('alert')).toHaveText('Saved successfully');
});
```
---
## 3. Network Mocking
E2E tests that rely on external 3rd party APIs (Stripe, SendGrid) will fail randomly due to network latency outside your control.
```typescript
test('Should block invalid credit cards', async ({ page }) => {
// Intercept the outgoing request to the payment processor
await page.route('**/api/v1/charge*', async route => {
// Return a mocked failure response immediately
const json = { status: 'declined', message: 'Insufficient funds' };
await route.fulfill({ status: 400, json });
});
await page.getByRole('button', { name: 'Purchase' }).click();
await expect(page.getByText('Insufficient funds')).toBeVisible();
});
```
---
## 4. Configuration for CI/CD
```typescript
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true, // Run tests concurrently
forbidOnly: !!process.env.CI, // Fail build if `.only` was left in code
retries: process.env.CI ? 2 : 0, // Retry flakes on CI only
workers: process.env.CI ? 1 : undefined, // Reduce CI overload
reporter: 'html',
use: {
trace: 'on-first-retry', // Record trace viewer ONLY on failure to save space
video: 'retain-on-failure',
baseURL: 'http://localhost:3000',
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'webkit', use: { ...devices['Desktop Safari'] } },
// Mobile Viewport Example
{ name: 'Mobile Safari', use: { ...devices['iPhone 13'] } },
],
// Spin up local server before running tests
webServer: {
command: 'npm run build && npm run start',
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
},
});
```
Attribution
Comments
Loading commentsβ¦