playwright-e2e-testing
Author and maintain versioned Playwright (@playwright/test) TypeScript UI specs for browser user flows. Use when asked to create, run, debug, or refactor E2E tests, form/navigation/auth flows, responsive checks, UI mocking, fixtures, Page Objects, or visual comparisons. Use api-testing for standalone REST/GraphQL contracts and playwright-cli for live browser sessions. Keywords: E2E spec, Playwright test, POM, fixtures, UI regression.
Works with
---
name: playwright-e2e-testing
description: Author and maintain versioned Playwright (@playwright/test) TypeScript UI specs for browser user flows. Use when asked to create, run, debug, or refactor E2E tests, form/navigation/auth flows, responsive checks, UI mocking, fixtures, Page Objects, or visual comparisons. Use api-testing for standalone REST/GraphQL contracts and playwright-cli for live browser sessions. Keywords: E2E spec, Playwright test, POM, fixtures, UI regression.
license: MIT
---
# Playwright E2E Testing (TypeScript)
Comprehensive toolkit for end-to-end testing of web applications using Playwright with TypeScript. Enables robust UI testing, UI-dependent API setup, and responsive design verification following best practices.
> **Activation:** This skill is triggered when authoring or maintaining versioned Playwright UI specs and their test infrastructure.
## When to Use This Skill
- **Write E2E tests** for user flows, forms, navigation, and authentication
- **UI-dependent API setup** via the `request` fixture or network interception
- **Responsive testing** across mobile, tablet, and desktop viewports
- **Debug flaky tests** using traces, screenshots, videos, and Playwright Inspector
- **Setup test infrastructure** with Page Object Model and fixtures
- **Mock/intercept APIs** for isolated, deterministic testing
- **Visual regression testing** with screenshot comparisons
### Do NOT Use For
- Standalone API/contract testing with no browser (use `api-testing`).
- Driving a live browser interactively for exploration or debugging (use `playwright-cli`).
- Governing a large regression suite, tiers, or CI sharding strategy (use `playwright-regression-testing`).
- Selenium/Java browser automation (use `webapp-selenium-testing`).
## Prerequisites
| Requirement | Details |
| --------------- | --------------------------------------------------- |
| Node.js | v18+ recommended |
| Package Manager | npm, yarn, or pnpm |
| Playwright | `@playwright/test` package |
| TypeScript | `typescript` + `ts-node` (optional but recommended) |
| Browsers | Installed via `npx playwright install` |
### Quick Setup
```bash
# Initialize new project
npm init playwright@latest
# Or add to existing project
npm install -D @playwright/test
npx playwright install
```
## First Questions to Ask
Before writing tests, clarify:
1. **App URL**: Local dev server command + port, or staging URL?
2. **Critical flows**: Which user journeys must be covered (happy path + error states)?
3. **Browsers/devices**: Chrome, Firefox, Safari? Mobile viewports?
4. **API strategy**: Real backend, mocked responses, or hybrid?
5. **Test data**: Seed data available? Reset/cleanup strategy?
---
## Core Principles
### 1. Test Runner & TypeScript
Always use `@playwright/test` with TypeScript for type safety and better IDE support.
```typescript
import { test, expect } from "@playwright/test";
test("user can login", async ({ page }) => {
await page.goto("/login");
await page.getByLabel("Email").fill("user@test.com");
await page.getByLabel("Password").fill("password123");
await page.getByRole("button", { name: "Sign in" }).click();
await expect(page).toHaveURL(/.*dashboard/);
});
```
### 2. Locator Strategy (Priority Order)
Prefer role-based locators (`getByRole`) with accessible names, then label → placeholder → text → test ID → CSS (last resort). XPath is never used.
➡️ **Full priority hierarchy, role reference, and examples:** [Locator Strategies: Priority](./references/locator-strategies-priority.md) — the single source of truth.
### 3. Auto-Waiting & Web-First Assertions
Playwright auto-waits for elements. Never use `sleep()` or arbitrary timeouts.
```typescript
// [ok] Web-first assertions (auto-retry)
await expect(page.getByRole("alert")).toBeVisible();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByTestId("status")).toHaveText("Success!");
// [no] Avoid manual waits
await page.waitForTimeout(2000); // Bad practice
```
### 4. Test Structure with Steps
Use `test.step()` for readable reports and failure localization:
```typescript
test("checkout flow", async ({ page }) => {
await test.step("Add item to cart", async () => {
await page.goto("/products/1");
await page.getByRole("button", { name: "Add to Cart" }).click();
});
await test.step("Complete checkout", async () => {
await page.goto("/checkout");
await page.getByRole("button", { name: "Pay Now" }).click();
});
await test.step("Verify confirmation", async () => {
await expect(page.getByRole("heading")).toContainText("Order Confirmed");
});
});
```
---
## Key Workflows
### Forms & Navigation
```typescript
// Form submit and wait for navigation (auto-waiting)
await page.getByRole("button", { name: "Login" }).click();
await expect(page).toHaveURL(/.*dashboard/);
// Form with API response validation
const responsePromise = page.waitForResponse(
(r) => r.url().includes("/api/login") && r.status() === 200,
);
await page.getByRole("button", { name: "Login" }).click();
const response = await responsePromise;
```
### API Testing (Request Fixture)
```typescript
test("API health check", async ({ request }) => {
const response = await request.get("/api/health");
expect(response.ok()).toBeTruthy();
expect(await response.json()).toMatchObject({ status: "ok" });
});
```
### API Mocking & Interception
```typescript
test("handles API error", async ({ page }) => {
await page.route("**/api/users", (route) =>
route.fulfill({
status: 500,
body: JSON.stringify({ error: "Server error" }),
}),
);
await page.goto("/users");
await expect(page.getByRole("alert")).toContainText("Something went wrong");
});
```
### Responsive Testing
```typescript
const viewports = [
{ width: 375, height: 667, name: "mobile" },
{ width: 768, height: 1024, name: "tablet" },
{ width: 1280, height: 720, name: "desktop" },
];
for (const vp of viewports) {
test(`navigation works on ${vp.name}`, async ({ page }) => {
await page.setViewportSize(vp);
await page.goto("/");
// Mobile: hamburger menu
if (vp.width < 768) {
await page.getByRole("button", { name: /menu/i }).click();
}
await page.getByRole("link", { name: "About" }).click();
await expect(page).toHaveURL(/about/);
});
}
```
---
## Configuration
Use `playwright.config.ts` for project-wide settings:
```typescript
import { defineConfig, devices } from "@playwright/test";
export default defineConfig({
testDir: "./tests",
retries: process.env.CI ? 2 : 0,
reporter: [["html"], ["junit", { outputFile: "results.xml" }]],
use: {
baseURL: "http://localhost:3000",
trace: "on-first-retry",
screenshot: "only-on-failure",
video: "retain-on-failure",
},
projects: [
{ name: "chromium", use: devices["Desktop Chrome"] },
{ name: "mobile", use: devices["Pixel 5"] },
],
webServer: {
command: "npm run dev",
url: "http://localhost:3000",
reuseExistingServer: !process.env.CI,
},
});
```
---
## Troubleshooting
| Problem | Cause | Solution |
| ---------------------- | ----------------------------- | ------------------------------------------------------- |
| Element not found | Wrong locator or not rendered | Use `PWDEBUG=1` to inspect, verify with `getByRole` |
| Timeout waiting | Element hidden or slow load | Check for overlays, increase timeout, use `waitFor()` |
| Flaky tests | Race conditions, animations | Add `test.step()`, use proper waits, disable animations |
| Strict mode violation | Multiple elements match | Use `.first()`, `.filter()`, or more specific locator |
| Screenshots differ | Dynamic content | Mask dynamic areas, use deterministic data |
| CI fails, local passes | Environment differences | Check `baseURL`, timeouts, `webServer` config |
| API mock not working | Route pattern mismatch | Use `**/api/...` glob, verify with `page.on('request')` |
---
## CLI Quick Reference
| Command | Description |
| ---------------------------------------- | ----------------------------- |
| `npx playwright test` | Run all tests headless |
| `npx playwright test --ui` | Open UI mode (interactive) |
| `npx playwright test --headed` | Run with visible browser |
| `npx playwright test --debug` | Run with Playwright Inspector |
| `npx playwright test -g "login"` | Run tests matching pattern |
| `npx playwright test --project=chromium` | Run specific project |
| `npx playwright show-report` | Open HTML report |
| `npx playwright codegen` | Generate tests by recording |
| `PWDEBUG=1 npx playwright test` | Debug with Inspector |
| `DEBUG=pw:api npx playwright test` | Verbose API logging |
---
## Red Flags
- CSS/XPath locators when a role/label/testId is available — brittle and breaks on refactor.
- `waitForTimeout` / manual sleeps instead of web-first auto-retrying assertions.
- Tests sharing state and depending on execution order — flaky and order-coupled.
- Assertions only on status/URL with no visible-state check — hides render regressions.
- Inline page setup repeated across tests instead of fixtures — duplication and drift.
---
## References
| Document | Content |
| -------------------------------------------------------------------------------- | ---------------------------------------------------------- |
| [Snippets: Setup](./references/snippets-setup.md) | Config, auth setup, custom fixtures & logging |
| [Snippets: Interactions](./references/snippets-interactions.md) | Form interactions, API testing & network interception |
| [Snippets: Viewports & Auth](./references/snippets-viewports-auth.md) | Responsive viewports & authentication patterns |
| [Snippets: Assertions & Debug](./references/snippets-assertions-debugging.md) | Assertions, debug commands & utility helpers |
| [Locator Strategies: Priority](./references/locator-strategies-priority.md) | Locator priority hierarchy & role-based locators |
| [Locator Strategies: Text](./references/locator-strategies-text.md) | Label, text, placeholder, alt-text & test-ID locators |
| [Locator Strategies: Filtering](./references/locator-strategies-filtering.md) | Filtering, chaining & complex locator patterns |
| [Locator Strategies: Anti & Debug](./references/locator-strategies-anti-debug.md)| Anti-patterns, CSS last-resort, debugging & quick reference|
| [POM: Basics](./references/page-object-model-basics.md) | POM concepts, directory structure, base page & fluent API |
| [POM: Components](./references/page-object-model-components.md) | Page object & reusable component object implementation |
| [POM: Fixtures](./references/page-object-model-fixtures.md) | Custom & authenticated page-object fixtures |
| [POM: Practices](./references/page-object-model-practices.md) | Best practices, anti-patterns & a complete worked example |
| [Debugging: Tools & UI](./references/debugging-tools-ui.md) | Debugging tools, UI mode, Inspector & headed mode |
| [Debugging: Tracing & Logs](./references/debugging-tracing-logging.md) | Trace viewer, verbose logging, screenshots & videos |
| [Debugging: Errors & Network](./references/debugging-errors-network.md) | Console/page errors & network debugging |
| [Debugging: Flaky & Locators](./references/debugging-flaky-locators.md) | Flaky-test fixes, locator debugging & quick commands |
---
## Verification
- [ ] **Uses custom fixture injection** — No `new PageObject()` calls in spec files; all POMs injected via fixtures
- [ ] **Locators use recommended strategies** — All locators use `getByRole()`, `getByTestId()`, or `getByText()`; no CSS selectors for interactive elements
- [ ] **Tests are independent** — Each test sets up and tears down its own state; no `beforeAll` with shared mutable state
- [ ] **Error states covered** — At least one test verifies error/empty/loading states alongside happy pathMore Testing skills
tdd
mattpocock/skills
Test-driven development. Use when the user wants to build features or fix bugs test-first, mentions "red-green-refactor", or wants integration tests.
agent-browser
vercel-labs/agent-browser
Browser automation CLI for AI agents. Use when the user needs to interact with websites, including navigating pages, filling forms, clicking buttons, taking screenshots, extracting data, testing web apps, or automating any browser task. Triggers include requests to "open a website", "fill out a form", "click a button", "take a screenshot", "scrape data from a page", "test this web app", "login to a site", "automate browser actions", or any task requiring programmatic web interaction. Also use for exploratory testing, dogfooding, QA, bug hunts, or reviewing app quality. Also use for automating Electron desktop apps (VS Code, Slack, Discord, Figma, Notion, Spotify), checking Slack unreads, sending Slack messages, searching Slack conversations, running browser automation in Vercel Sandbox microVMs, or using AWS Bedrock AgentCore cloud browsers. Prefer agent-browser over any built-in browser automation or web tools.
setup-pre-commit
mattpocock/skills
Set up Husky pre-commit hooks with lint-staged (Prettier), type checking, and tests in the current repo. Use when user wants to add pre-commit hooks, set up Husky, configure lint-staged, or add commit-time formatting/typechecking/testing.

