This skill should be used when user asks about "Playwright", "responsiveness test", "test with playwright", "test login flow", "file upload test", "handle authentication in tests", or "fix flaky tests".
View on GitHubfcakyon/claude-codex-settings
playwright-tools
January 15, 2026
Select agents to install to:
npx add-skill https://github.com/fcakyon/claude-codex-settings/blob/main/plugins/playwright-tools/skills/playwright-testing/SKILL.md -a claude-code --skill playwright-testingInstallation paths:
.claude/skills/playwright-testing/# Playwright Testing Best Practices
## Test Organization
### File Structure
```
tests/
├── auth/
│ ├── login.spec.ts
│ └── signup.spec.ts
├── dashboard/
│ └── dashboard.spec.ts
├── fixtures/
│ └── test-data.ts
├── pages/
│ └── login.page.ts
└── playwright.config.ts
```
### Naming Conventions
- Files: `feature-name.spec.ts`
- Tests: Describe user behavior, not implementation
- Good: `test('user can reset password via email')`
- Bad: `test('test reset password')`
## Page Object Model
### Basic Pattern
```typescript
// pages/login.page.ts
export class LoginPage {
constructor(private page: Page) {}
async goto() {
await this.page.goto("/login");
}
async login(email: string, password: string) {
await this.page.getByLabel("Email").fill(email);
await this.page.getByLabel("Password").fill(password);
await this.page.getByRole("button", { name: "Sign in" }).click();
}
}
// tests/login.spec.ts
test("successful login", async ({ page }) => {
const loginPage = new LoginPage(page);
await loginPage.goto();
await loginPage.login("user@example.com", "password");
await expect(page).toHaveURL("/dashboard");
});
```
## Locator Strategies
### Priority Order (Best to Worst)
1. **`getByRole`** - Accessible, resilient
2. **`getByLabel`** - Form inputs
3. **`getByPlaceholder`** - When no label
4. **`getByText`** - Visible text
5. **`getByTestId`** - When no better option
6. **CSS/XPath** - Last resort
### Examples
```typescript
// Preferred
await page.getByRole("button", { name: "Submit" }).click();
await page.getByLabel("Email address").fill("user@example.com");
// Acceptable
await page.getByTestId("submit-button").click();
// Avoid
await page.locator("#submit-btn").click();
await page.locator('//button[@type="submit"]').click();
```
## Authentication Handling
### Storage State (Recommended)
Save logged-in state and reuse across tests:
```typescript
// global-setup.ts
async function globalSetup() {
const browser = await chro