From test-automation-skills-agents
Writes, runs, debugs, and maintains Playwright (@playwright/test) TypeScript tests for E2E UI behavior, API validation, responsive design, and visual regression in web apps.
npx claudepluginhub fugazi/test-automation-skills-agents --plugin test-automation-skills-agentsThis skill uses the workspace's default tool permissions.
Comprehensive toolkit for end-to-end testing of web applications using Playwright with TypeScript. Enables robust UI testing, API validation, and responsive design verification following best practices.
Implements structured self-debugging workflow for AI agent failures: capture errors, diagnose patterns like loops or context overflow, apply contained recoveries, and generate introspection reports.
Monitors deployed URLs for regressions in HTTP status, console errors, performance metrics, content, network, and APIs after deploys, merges, or upgrades.
Provides React and Next.js patterns for component composition, compound components, state management, data fetching, performance optimization, forms, routing, and accessible UIs.
Comprehensive toolkit for end-to-end testing of web applications using Playwright with TypeScript. Enables robust UI testing, API validation, and responsive design verification following best practices.
Activation: This skill is triggered when working with Playwright tests, browser automation, E2E testing, API testing with Playwright, or test infrastructure setup.
request fixture or network interception during UI tests| 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 |
# Initialize new project
npm init playwright@latest
# Or add to existing project
npm install -D @playwright/test
npx playwright install
Before writing tests, clarify:
Always use @playwright/test with TypeScript for type safety and better IDE support.
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/);
});
| Priority | Locator | Example |
|---|---|---|
| 1 | Role + accessible name | getByRole('button', { name: 'Submit' }) |
| 2 | Label | getByLabel('Email') |
| 3 | Placeholder | getByPlaceholder('Enter email') |
| 4 | Text | getByText('Welcome') |
| 5 | Test ID | getByTestId('submit-btn') |
| 6 | CSS (avoid) | locator('.btn-primary') |
See Locator Strategies Guide for detailed patterns.
Playwright auto-waits for elements. Never use sleep() or arbitrary timeouts.
// ✅ Web-first assertions (auto-retry)
await expect(page.getByRole("alert")).toBeVisible();
await expect(page).toHaveURL(/dashboard/);
await expect(page.getByTestId("status")).toHaveText("Success!");
// ❌ Avoid manual waits
await page.waitForTimeout(2000); // Bad practice
Use test.step() for readable reports and failure localization:
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");
});
});
// 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;
test("API health check", async ({ request }) => {
const response = await request.get("/api/health");
expect(response.ok()).toBeTruthy();
expect(await response.json()).toMatchObject({ status: "ok" });
});
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");
});
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/);
});
}
Use playwright.config.ts for project-wide settings:
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,
},
});
| 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') |
| 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 |
Common shortcuts and "good enough" excuses that erode test quality — and the reality behind each.
| Rationalization | Reality |
|---|---|
| "I'll add assertions later" | A test without assertions is a script, not a test. Add meaningful assertions now — later never comes. |
| "This selector is stable enough" | CSS selectors break on refactor. Use data-testid or role-based locators for stability. |
| "The test passes on my machine" | Local passes don't guarantee CI passes. Always validate in the target environment. |
| "Skip the edge cases for now" | Edge cases are where production bugs live. Test them first, not last. |
| "Visual tests aren't needed" | Visual regression catches CSS/layout bugs that functional assertions miss entirely. |
| "This API won't change" | APIs evolve constantly. Contract tests prevent silent downstream failures. |
| "One browser is enough" | Cross-browser issues are real and common. Test at least Chromium and Firefox. |
"networkidle is fine for waiting" | networkidle is deprecated and unreliable. Wait for specific conditions (elements, responses, URL changes). |
| Document | Content |
|---|---|
| Snippets | Ready-to-use code patterns |
| Locator Strategies | Complete locator guide |
| Page Object Model | POM implementation patterns |
| Debugging Guide | Troubleshooting & debugging techniques |
After completing this skill's workflow, confirm:
*.spec.ts in the appropriate directorynew PageObject() calls in spec files; all POMs injected via fixturesgetByRole(), getByTestId(), or getByText(); no CSS selectors for interactive elementspage.waitForTimeout() or sleep() calls; all waits use waitForSelector(), waitForURL(), or built-in auto-waitingbeforeAll with shared mutable statenpx playwright test exits with code 0grep -R -E "test\.skip|test\.fixme" --include="*.spec.ts" . returns no results