brainstorming-and-plan…
Use before ANY creative work — creating features, building components, adding functionality, modifying behavior, or starting a new project. Also use when…
Battle-tested Playwright E2E testing patterns for Next.js/React apps. Use when writing, running, debugging, or fixing Playwright tests. Also triggers on 'e2e', 'end-to-end', 'playwright', 'browser test', 'UI test', 'integration test with browser', 'flaky test', 'test keeps
$ npx -y skills add burhankhatri/e2e-testing --skill e2e-playwright --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/e2e-playwrightContext preview
The summary Claude sees to decide when to auto-load this skill.
Battle-tested Playwright E2E testing patterns for Next.js/React apps. Use when writing, running, debugging, or fixing Playwright tests. Also triggers on 'e2e', 'end-to-end', 'playwright', 'browser test', 'UI test', 'integration test with browser', 'flaky test', 'test keeps
name: e2e-playwright description: "Battle-tested Playwright E2E testing patterns for Next.js/React apps. Use when writing, running, debugging, or fixing Playwright tests. Also triggers on 'e2e', 'end-to-end', 'playwright', 'browser test', 'UI test', 'integration test with browser', 'flaky test', 'test keeps failing'. Covers locators, assertions, fixtures, auth, network mocking, flaky test diagnosis, Next.js-specific patterns, and debugging workflows."
> Production-tested patterns from the TestDino Playwright Skill. Every pattern includes when (and when *not*) to use it.
1. **`getByRole()` over CSS/XPath** — resilient to markup changes, mirrors how users see the page 2. **Never `page.waitForTimeout()`** — use `expect(locator).toBeVisible()` or `page.waitForURL()` 3. **Web-first assertions** — `expect(locator)` auto-retries; `expect(await locator.textContent())` does NOT 4. **Isolate every test** — no shared state, no execution-order dependencies 5. **`baseURL` in config** — zero hardcoded URLs in tests 6. **Retries: `2` in CI, `0` locally** — surface flakiness where it matters 7. **Traces: `'on-first-retry'`** — rich debugging artifacts without CI slowdown 8. **Fixtures over globals** — share state via `test.extend()`, not module-level variables 9. **One behavior per test** — multiple related `expect()` calls are fine 10. **Mock external services only** — never mock your own app; mock third-party APIs, payment gateways, email 11. **Real auth or stop** — never `test.skip(true, ...)` around missing login, never `.or(signIn)` assertions that pass on the auth wall. If auth setup doesn't exist, STOP and set up storage state with the user (one-time). A test that passes signed-out is not a feature test.
**Deep dives available in `references/` directory — read them when working on the relevant topic.**
---
Not all E2E tests are equal. Know what tier you're writing.
| Tier | What it tests | Example | Sufficient for feature coverage? | |------|--------------|---------|----------------------------------| | **Smoke** | Page loads, no 404, no crash | `goto('/canvas'); expect(heading).toBeVisible()` | **NO** — baseline only | | **Feature** | User completes a real workflow | Drag entry to project → rule created → future entries auto-link | **YES** — this is the goal | | **Navigation** | Links route correctly, active states work | Click "Canvas" in sidebar → URL is /canvas → heading visible | **Required when nav changes** |
**The rule:** Every feature shipped MUST have at least one tier-2 (feature) E2E test. Smoke tests are free but DO NOT count toward feature coverage.
**Ask yourself:** "If someone broke this feature tomorrow, would my E2E tests catch it?" If the answer is "only if they deleted the entire page" — you wrote smoke tests, not feature tests.
When you add or modify navigation (sidebar items, mobile tab bar, header links, route changes), you MUST write tests that verify:
1. Nav item is visible at the correct viewport (desktop sidebar, mobile tab bar) 2. Clicking it navigates to the correct URL 3. Destination page renders its primary content (not just "no 404") 4. Active/selected state highlights correctly
**Desktop + Mobile navigation test template:**
import { test, expect } from '@playwright/test';
test.describe('Navigation — Desktop', () => {
test.use({ viewport: { width: 1280, height: 800 } });
test('sidebar contains Canvas link and navigates correctly', async ({ page }) => {
await page.goto('/');
const sidebar = page.getByRole('navigation');
const canvasLink = sidebar.getByRole('link', { name: 'Canvas' });
await expect(canvasLink).toBeVisible();
await canvasLink.click();
await page.waitForURL('/canvas');
await expect(page.getByRole('heading', { name: 'Canvas' })).toBeVisible();
});
});
test.describe('Navigation — Mobile', () => {
test.use({ viewport: { width: 375, height: 812 } });
test('mobile tab bar contains Canvas and navigates correctly', async ({ page }) => {
await page.goto('/');
const tabBar = page.getByRole('navigation', { name: /mobile|tab/i });
const canvasTab = tabBar.getByRole('link', { name: 'Canvas' });
await expect(canvasTab).toBeVisible();
await canvasTab.click();
await page.waitForURL('/canvas');
await expect(page.getByRole('heading', { name: 'Canvas' })).toBeVisible();
});
});Adapt names/selectors to the actual app. The structure is: find nav → find link → click → verify URL → verify content.
---
// playwright.config.ts
import { defineConfig, devices } from '@playwright/test';
export default defineConfig({
testDir: './tests/e2e',
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? '50%' : undefined,
reporter: process.env.CI ? 'html' : 'list',
use: {
baseURL: 'http://localhost:3000',
trace: 'on-first-retry',
screenshot: 'on',
video: 'retain-on-failure',
},
expect: {
toHaveScreenshot: {
maxDiffPixelRatio: 0.01,
animations: 'disabled',
},
},
projects: [
{ name: 'chromium', use: { ...devices['Desktop Chrome'] } },
{ name: 'mobile', use: { ...devices['iPhone 14'] } },
],
webServer: {
command: process.env.CI
? 'npm run build && npm run start' // production build in CI
: 'npm run dev', // dev server locally
url: 'http://localhost:3000',
reuseExistingServer: !process.env.CI,
timeout: 120_000,
env: {
NODE_ENV: process.env.CI ? 'production' : 'test',
},
},
});**Environment variables:** Next.js loads `.env.test` automatically when `NODE_ENV=test`. Use `.env.test` for non-secret test config (committed), `.env.test.local` for secrets (gitignored).
**Gitignore additions:**
.env*.local
A set of 8 global skills for Claude Code that enforce disciplined, test-driven agentic development. Install once, use in any project.
Repo: burhankhatri/e2e-testing
Use before ANY creative work — creating features, building components, adding functionality, modifying behavior, or starting a new project. Also use when…
Use when a major project step has been completed and needs review against the plan and coding standards. Also use when someone says 'review this', 'check my…
Master orchestrator skill that kicks off the full development pipeline. Routes tasks through the correct skill chain (brainstorm, debug, tdd, e2e, verify)…
Use when encountering ANY bug, test failure, unexpected behavior, or error — before proposing fixes. Also use when someone says 'fix this', 'it's broken', 'not…
Enforces strict test-driven development. Use when implementing ANY feature, bugfix, or refactor — before writing implementation code. Also use when someone…
Use when you need to autonomously iterate through test-fix cycles without human intervention. Use when someone says 'make it work', 'run tests and fix',…