Use when writing Playwright automation code, building web scrapers, or creating E2E tests - provides best practices for selector strategies, waiting patterns, and robust automation that minimizes flakiness
View on GitHubed3dai/ed3d-plugins
ed3d-playwright
January 25, 2026
Select agents to install to:
npx add-skill https://github.com/ed3dai/ed3d-plugins/blob/main/plugins/ed3d-playwright/skills/playwright-patterns/SKILL.md -a claude-code --skill playwright-patternsInstallation paths:
.claude/skills/playwright-patterns/# Playwright Automation Patterns
## Overview
Reliable browser automation requires strategic selector choice, proper waiting, and defensive coding. This skill provides patterns that minimize test flakiness and maximize maintainability.
## When to Use
- Writing new Playwright scripts or tests
- Debugging flaky automation
- Refactoring unreliable selectors
- Building web scrapers that need to handle dynamic content
- Creating E2E tests that must be maintainable
**When NOT to use:**
- Simple one-time browser tasks
- When you need Playwright API documentation (use context7 MCP)
## Selector Strategy
### Priority Order
Use user-facing locators first (most resilient), then test IDs, then CSS/XPath as last resort:
1. **Role-based locators** (best - user-centric)
```javascript
await page.getByRole('button', { name: 'Submit' }).click();
await page.getByRole('textbox', { name: 'Email' }).fill('test@example.com');
```
2. **Other user-facing locators**
```javascript
await page.getByLabel('Password').fill('secret');
await page.getByPlaceholder('Search...').fill('query');
await page.getByText('Submit Order').click();
```
3. **Test ID attributes** (explicit contract)
```javascript
// Default uses data-testid
await page.getByTestId('submit-button').click();
// Can customize in playwright.config.ts:
// use: { testIdAttribute: 'data-pw' }
```
4. **CSS/ID selectors** (fragile, avoid if possible)
```javascript
await page.locator('#submit-btn').click();
await page.locator('.btn.btn-primary.submit').click();
```
### Strictness and Specificity
Locators are strict by default - operations throw if multiple elements match:
```javascript
// ERROR if 2+ buttons exist
await page.getByRole('button').click();
// Solutions:
// 1. Make locator more specific
await page.getByRole('button', { name: 'Submit' }).click();
// 2. Filter to narrow down
await page.getByRole('button')
.filter({ hasText: 'Submit' })
.click();
// 3. C