Marketplace
playwright-testing
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".
$ Installer
git clone https://github.com/fcakyon/claude-codex-settings /tmp/claude-codex-settings && cp -r /tmp/claude-codex-settings/plugins/playwright-tools/skills/playwright-testing ~/.claude/skills/claude-codex-settings// tip: Run this command in your terminal to install the skill
SKILL.md
name: playwright-testing description: 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".
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
// 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)
getByRole- Accessible, resilientgetByLabel- Form inputsgetByPlaceholder- When no labelgetByText- Visible textgetByTestId- When no better option- CSS/XPath - Last resort
Examples
// 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:
// global-setup.ts
async function globalSetup() {
const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto("/login");
await page.getByLabel("Email").fill(process.env.TEST_USER_EMAIL);
await page.getByLabel("Password").fill(process.env.TEST_USER_PASSWORD);
await page.getByRole("button", { name: "Sign in" }).click();
await page.waitForURL("/dashboard");
await page.context().storageState({ path: "auth.json" });
await browser.close();
}
// playwright.config.ts
export default defineConfig({
globalSetup: "./global-setup.ts",
use: {
storageState: "auth.json",
},
});
Multi-User Scenarios
// Create different auth states
const adminAuth = "admin-auth.json";
const userAuth = "user-auth.json";
test.describe("admin features", () => {
test.use({ storageState: adminAuth });
// Admin tests
});
test.describe("user features", () => {
test.use({ storageState: userAuth });
// User tests
});
File Upload Handling
Basic Upload
// Single file
await page.getByLabel("Upload file").setInputFiles("path/to/file.pdf");
// Multiple files
await page
.getByLabel("Upload files")
.setInputFiles(["path/to/file1.pdf", "path/to/file2.pdf"]);
// Clear file input
await page.getByLabel("Upload file").setInputFiles([]);
Drag and Drop Upload
// Create file from buffer
const buffer = Buffer.from("file content");
await page.getByTestId("dropzone").dispatchEvent("drop", {
dataTransfer: {
files: [{ name: "test.txt", mimeType: "text/plain", buffer }],
},
});
File Download
const downloadPromise = page.waitForEvent("download");
await page.getByRole("button", { name: "Download" }).click();
const download = await downloadPromise;
await download.saveAs("downloads/" + download.suggestedFilename());
Waiting Strategies
Auto-Wait (Preferred)
Playwright auto-waits for elements. Use assertions:
// Auto-waits for element to be visible and stable
await page.getByRole("button", { name: "Submit" }).click();
// Auto-waits for condition
await expect(page.getByText("Success")).toBeVisible();
Explicit Waits (When Needed)
// Wait for navigation
await page.waitForURL("**/dashboard");
// Wait for network idle
await page.waitForLoadState("networkidle");
// Wait for specific response
await page.waitForResponse((resp) => resp.url().includes("/api/data"));
Network Mocking
Mock API Responses
await page.route("**/api/users", async (route) => {
await route.fulfill({
status: 200,
contentType: "application/json",
body: JSON.stringify([{ id: 1, name: "Test User" }]),
});
});
// Mock error response
await page.route("**/api/users", async (route) => {
await route.fulfill({ status: 500 });
});
Intercept and Modify
await page.route("**/api/data", async (route) => {
const response = await route.fetch();
const json = await response.json();
json.modified = true;
await route.fulfill({ response, json });
});
CI/CD Integration
GitHub Actions Example
- name: Run Playwright tests
run: npx playwright test
env:
CI: true
- name: Upload test results
if: always()
uses: actions/upload-artifact@v3
with:
name: playwright-report
path: playwright-report/
Parallel Execution
// playwright.config.ts
export default defineConfig({
workers: process.env.CI ? 2 : undefined,
fullyParallel: true,
});
Debugging Failed Tests
Debug Tools
# Run with UI mode
npx playwright test --ui
# Run with inspector
npx playwright test --debug
# Show browser
npx playwright test --headed
Trace Viewer
// playwright.config.ts
use: {
trace: 'on-first-retry', // Capture trace on failure
}
Flaky Test Fixes
Common Causes and Solutions
Race conditions:
- Use proper assertions instead of hard waits
- Wait for network requests to complete
Animation issues:
- Disable animations in test config
- Wait for animation to complete
Dynamic content:
- Use flexible locators (text content, not position)
- Wait for loading states to resolve
Test isolation:
- Each test should set up its own state
- Don't depend on other tests' side effects
Anti-Patterns to Avoid
// Bad: Hard sleep
await page.waitForTimeout(5000);
// Good: Wait for condition
await expect(page.getByText("Loaded")).toBeVisible();
// Bad: Flaky selector
await page.locator(".btn:nth-child(3)").click();
// Good: Semantic selector
await page.getByRole("button", { name: "Submit" }).click();
Responsive Design Testing
For comprehensive responsive testing across viewport breakpoints, use the responsive-tester agent. It automatically:
- Tests pages across 7 standard breakpoints (375px to 1536px)
- Detects horizontal overflow issues
- Verifies mobile-first design patterns
- Checks touch target sizes (44x44px minimum)
- Flags anti-patterns like fixed widths without mobile fallback
Trigger it by asking to "test responsiveness", "check breakpoints", or "test mobile/desktop layout".
Repository

fcakyon
Author
fcakyon/claude-codex-settings/plugins/playwright-tools/skills/playwright-testing
275
Stars
33
Forks
Updated6d ago
Added1w ago