Playwright e2e
Skills published by 5dive — drop-in SKILL.md bundles for Claude Code, openclaw, hermes, and any harness that loads skills.sh-format prompts.
npx -y skills add 5dive-ai/skills --skill playwright-e2eAssembled from the repository path, not quoted from the project. Check it against their README if it does not work.
One thing to look at
- 1 stars1 stars. Stars are a popularity signal and not a quality one, but at this level it is likely that nobody has read this closely except its author, and you would be relying on your own review.
What its author says it does
Copied from the file, not written here
End-to-end testing and click-verification of web apps with Playwright — install, write specs, drive authenticated pages, take screenshots, and run in CI. Use when verifying a web change actually works in a real browser, testing user flows, debugging a UI bug live, or confirming an authed/gated page before shipping. Keywords Playwright, e2e, browser test, click test, screenshot, headless.
The file declares its own license as MIT. That is the author’s claim about this one file, and it is not the same thing as the license GitHub reports for the repository, which is listed with the other numbers below.
SKILL.md
5.6 KB, as published. Nobody here has run it
Playwright E2E & Click-Verification Guide
Drive a real browser to prove a web change works — not just that it compiles. Use this to
verify flows end-to-end, reproduce UI bugs, and screenshot pages. Pairs with nextjs-app.
Core principle: a green build is not a working feature. Before calling an interactive change done, click through the actual rendered page in a browser. Especially for authed routes, modals, and forms — those are exactly where "looks fine in code" silently breaks.
Install
npm install -D @playwright/test
npx playwright install chromium # or: --with-deps on CI / fresh boxes
Minimal config
// playwright.config.ts
import { defineConfig } from '@playwright/test'
export default defineConfig({
testDir: './e2e',
use: { baseURL: 'http://localhost:3000', trace: 'on-first-retry' },
webServer: { command: 'npm run dev', url: 'http://localhost:3000', reuseExistingServer: true },
})
A first spec
// e2e/home.spec.ts
import { test, expect } from '@playwright/test'
test('home renders and CTA navigates', async ({ page }) => {
await page.goto('/')
await expect(page.getByRole('heading', { name: /welcome/i })).toBeVisible()
await page.getByRole('link', { name: 'Get started' }).click()
await expect(page).toHaveURL(/.*dashboard/)
})
Run:
npx playwright test # headless, all specs
npx playwright test --headed # watch it
npx playwright test home.spec.ts -g CTA # filter
npx playwright show-report # HTML report after a run
Selectors — prefer user-facing, in this order
page.getByRole('button', { name: 'Save' })— accessible role + name (best)page.getByLabel('Email'),page.getByPlaceholder(…),page.getByText(…)page.getByTestId('submit')— adddata-testidfor fragile/ambiguous nodes- CSS/XPath — last resort; brittle
Playwright auto-waits for elements to be actionable — avoid manual waitForTimeout. If you
must wait on state, wait on a condition: await expect(locator).toBeVisible() or
page.waitForURL(…).
Testing authenticated pages
Most real apps gate the interesting pages behind login. Two reliable patterns:
A. Log in once, reuse the storage state
// e2e/auth.setup.ts (run as a setup project)
import { test as setup } from '@playwright/test'
setup('authenticate', async ({ page }) => {
await page.goto('/sign-in')
await page.getByLabel('Email').fill(process.env.TEST_EMAIL!)
await page.getByLabel('Password').fill(process.env.TEST_PASSWORD!)
await page.getByRole('button', { name: 'Sign in' }).click()
await page.waitForURL('**/dashboard')
await page.context().storageState({ path: 'e2e/.auth/user.json' })
})
// playwright.config.ts — wire the setup + reuse the session
projects: [
{ name: 'setup', testMatch: /auth\.setup\.ts/ },
{ name: 'app', use: { storageState: 'e2e/.auth/user.json' }, dependencies: ['setup'] },
]
B. Programmatic session (faster, no UI login)
Many auth providers (Clerk, Auth.js, custom JWT) let you mint a session token via their server SDK / admin API, then inject it so the browser starts already-authed:
// Mint a short-lived sign-in token server-side, then exchange it in the browser.
// Exact call depends on the provider; the shape is: get a ticket → land on a URL that
// consumes it → save storageState.
await page.goto(`/sign-in#__token=${signInToken}`) // provider-specific consume step
await page.waitForURL('**/dashboard')
await page.context().storageState({ path: 'e2e/.auth/user.json' })
Keep test creds/tokens in env vars (never commit them), scope them to a throwaway test user, and scrub any token out of logs/artifacts.
Quick one-off verification (no spec file)
For ad-hoc "does this actually render" checks, a short script beats a full suite:
// verify.mjs — node verify.mjs
import { chromium } from 'playwright'
const b = await chromium.launch()
const p = await b.newPage()
await p.goto('https://your-app.example.com/', { waitUntil: 'domcontentloaded' })
await p.screenshot({ path: 'shot.png', fullPage: true })
console.log('title:', await p.title())
await b.close()
Gotchas from real runs:
- Pages with autoplay video/streams never hit
networkidle— usewaitUntil: 'domcontentloaded'and wait on a concrete element instead. - Kill cookie/consent banners before screenshotting (
page.getByRole('button', { name: /accept/i }).click().catch(() => {})). - A client-inlined key (auth publishable key, analytics id) is baked at build time — if a locally-served build behaves oddly, verify against a real deployed URL instead of fighting the local server.
CI
# .github/workflows/e2e.yml
- run: npm ci
- run: npx playwright install --with-deps chromium
- run: npx playwright test
- uses: actions/upload-artifact@v4
if: failure()
with: { name: playwright-report, path: playwright-report }
Debugging
npx playwright test --debug # step through with the inspector
npx playwright codegen localhost:3000 # record clicks → generated spec
npx playwright show-trace trace.zip # time-travel a failed run