browser-qa
Skill4 starsChanged 24 days agoMITView on GitHub
Use to exercise the running app in a real browser — after UI changes, before ship, or when the user asks to QA the site, smoke-test a branch, or check "does it actually work". Drives affected routes headlessly via Playwright, captures console errors, failed requests, and screenshots, and produces a report with a health score. Report-only — it finds bugs, it never fixes them.
- Reads credentials
- Installs packages
We split this skill into the rules it gives an agent
12 rules. 0 are common, 6 are distinctive, and 6 are very specific to this project.
0
Common
Most of the 20 edited versions we read keep these.
6
Distinctive
This author's own choices. Worth a look.
6
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 25
Every run — even T2 — ends with a health score and the plain-language summary.
- Distinctive: line 29
Test what changed, not the whole site.
- Very specific: line 49
Prefer the built-in
verifyandrunskills: when available in this environment: run knows how to launch this project's app. - Distinctive: line 51
If they cover the need, use them and fold their observations into the report below.
- Distinctive: line 53
If the host supplies browser tools, prefer those and adapt the checks and evidence below to that API.
- Very specific: line 60
1. Get the app running: Check common dev ports first — Next.js :3000, Vite :5173, Astro :4321:
- Very specific: line 67
If nothing is up, start npm run dev in the background, wait for the ready line, and note the port.
- Very specific: line 76
If MISSING, ask the user once: "Browser QA needs Playwright (dev-only dependency).
- Very specific: line 152
Never report an issue with no evidence at all.
- Distinctive: line 205
If nothing is broken, say so plainly: score, routes covered, "no user-facing issues found".
- Distinctive: line 211
Test as a user, not a developer: reading source is for *scoping routes only*, never for judging "that probably works".
- Very specific: line 212
Keep all screenshots and scripts in $QA_OUT.
“Common” is measured: kept by at least 6 in 10 of the 20 edited versions of this file we could read. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Browser QA: exercise the running app, report what breaks
Part of the Verify phase — see the workflow skill for tiers and sequencing.
You are a QA engineer. Test the app like a real user — load the pages this change touched, click what a user would click, watch the console. Produce a report with evidence. NEVER fix anything. Fixes go through the normal Build/Verify loop (see Report-only discipline at the end).
Tier plan
Match effort to the tier the workflow skill assigned (or infer from the change's risk):
| Tier | What to do |
| T1 (typo, comment, config-only) | Skip browser QA entirely. Say so and stop. |
| T2 (normal features, including refactors; bug fixes) | Smoke the affected routes only: load, console clean, no failed requests, screenshot, one DOM assert per route. |
| T3 (money, auth, user data, migrations — or pre-ship of such work) | Full plan: affected routes plus adjacent routes and the top navigation targets, per-page checklist on each, health score with all categories. |
Every run — even T2 — ends with a health score and the plain-language summary.
Scope: derive routes from the diff
Test what changed, not the whole site. From git diff main...HEAD --name-only and
git log main..HEAD --oneline, map changed files to routes:
A multi-route QA run is token-heavy — a delegate trigger. Ask the user delegate-or-inline before running it (
delegateskill).
| Changed file | Routes to test |
Route/page file (app/**/page.tsx, pages/foo.tsx, src/pages/foo.astro) | The route it serves (/foo) |
| Shared component | Grep for importers; test the routes that render them |
| Layout, global CSS, providers | Every route inherits it — test homepage + one route per layout |
API handler (app/api/**, src/api/**) | Hit the endpoint directly (fetch in the script or curl), plus the page that calls it |
| Model/service/lib code | Routes whose pages call into it |
| Backend/config/infra with no obvious route | Do NOT skip — smoke homepage + top 5 nav targets; backend changes still break pages |
Cross-reference commit messages to understand intent: what is this change supposed to do? Verify it does that, not just that pages load.
Driving the browser
Prefer the built-in verify and run skills when available in this environment:
run knows how to launch this project's app; verify drives a changed flow end-to-end.
If they cover the need, use them and fold their observations into the report below.
If the host supplies browser tools, prefer those and adapt the checks and evidence below to that API. In Codex, follow the installed browser skill; don't install Playwright or drive it through Bash when the host requires its own browser tools.
Otherwise, drive Chromium yourself via Playwright through Bash.
1. Get the app running. Check common dev ports first — Next.js :3000,
Vite :5173, Astro :4321:
for p in 3000 5173 4321; do curl -sf -o /dev/null "http://localhost:$p" && echo "UP :$p"; doneIf nothing is up, start npm run dev in the background, wait for the ready line,
and note the port. If the port can't be determined, ask the user for the URL.
2. Check Playwright is available.
node -e "require.resolve('playwright')" 2>/dev/null && echo READY || echo MISSINGIf MISSING, ask the user once: "Browser QA needs Playwright (dev-only dependency).
OK to run npm i -D playwright && npx playwright install chromium?" Wait for a yes
before installing. Never install silently.
3. Set up an evidence directory in the session scratchpad (never inside the repo):
QA_OUT="$SCRATCHPAD/browser-qa-$(date +%Y%m%d-%H%M%S)" # use this session's scratchpad dir
mkdir -p "$QA_OUT"4. Write the smoke script to $QA_OUT/qa-smoke.mjs and run it with the route list:
// qa-smoke.mjs — node qa-smoke.mjs <baseURL> [path ...] (QA_OUT=screenshot dir)
import { chromium } from 'playwright';
const [base, ...paths] = process.argv.slice(2);
const routes = paths.length ? paths.map(p => new URL(p, base).href) : [base];
const out = process.env.QA_OUT || '.';
const browser = await chromium.launch();
const page = await browser.newPage();
const findings = [];
page.on('console', m => { if (m.type() === 'error') findings.push(`[console] ${page.url()} :: ${m.text()}`); });
page.on('pageerror', e => findings.push(`[exception] ${page.url()} :: ${e.message}`));
page.on('requestfailed', r => findings.push(`[netfail] ${r.url()} :: ${r.failure()?.errorText}`));
page.on('response', r => { if (r.status() >= 400) findings.push(`[http ${r.status()}] ${r.url()}`); });
for (const url of routes) {
const res = await page.goto(url, { waitUntil: 'networkidle', timeout: 15000 })
.catch(e => { findings.push(`[nav] ${url} :: ${e.message}`); return null; });
if (!res) continue;
const slug = new URL(url).pathname.replace(/\W+/g, '-').replace(/^-|-$/g, '') || 'home';
await page.screenshot({ path: `${out}/${slug}.png`, fullPage: true });
const body = (await page.textContent('body')) ?? '';
if (!body.trim()) findings.push(`[blank] ${url} :: body rendered empty`);
console.log(`VISITED ${url} -> ${slug}.png (status ${res.status()})`);
}
await browser.close();
console.log(findings.length ? findings.join('\n') : 'NO FINDINGS');QA_OUT="$QA_OUT" node "$QA_OUT/qa-smoke.mjs" http://localhost:3000 / /pricing /dashboardPer-route DOM asserts and interactions: extend a copy of the script for the
specific change — e.g. await page.getByText('Saved!').waitFor({ timeout: 5000 })
after clicking submit, or page.getByRole('button', { name: 'Add' }).click() then
re-screenshot. One throwaway script per flow is fine; they all live in $QA_OUT.
For SPAs, click links in-page rather than only goto — client-side routing bugs
don't show up on direct navigation. After every screenshot, Read the file so the
user sees it inline.
Per-page checklist (T3; for T2 do steps 1, 5, and one item of 2)
- Loads — 2xx, no blank body, screenshot looks like a page, not an error.
- Interactive elements — click buttons/links/controls a user would use. Do they do what they say?
- Forms — fill and submit; try empty and one invalid input. Errors shown clearly?
- States — empty, loading, error, overflow (long text).
- Console + network — zero new JS errors or failed requests after interactions.
- Navigation — paths in and out work; browser back doesn't break the app.
- Responsive (if the change touched layout) — re-run at
viewport: { width: 375, height: 812 }. - Accessibility — interactive elements are keyboard-reachable (Tab order works); images have alt text.
- Performance — page is interactive under ~3s on the dev server; no single request over 2MB.
- Content — no lorem/placeholder text or broken images.
Spend depth where users spend time: homepage, dashboard, checkout, search — not the terms-of-service page.
Evidence tiers
Label every finding with how you know it:
- Verified — reproduced twice; screenshot + console/network excerpt attached. Full weight in scoring.
- Observed — seen once with evidence captured, not re-reproduced. Counts, note the uncertainty.
- Assumed — inferred from the diff or a lone log line, never seen in the browser. List under "Unverified concerns", excluded from the health score.
Never report an issue with no evidence at all. Retry once before documenting so a
fluke doesn't become a finding. Never put real credentials in the report — [REDACTED].
Prioritize by WTF-likelihood
Order testing and the report by how likely a real user is to hit the problem: first click on the homepage > a form everyone submits > a keyboard trap in a rarely opened modal. Self-regulate the same way: when the last several findings are all low-severity edge cases no user would plausibly hit, stop exploring — you're done, write the report. Depth over breadth: 5–10 well-evidenced issues beat 20 vague ones.
Health score
Severity (see per-finding deductions): critical = blocks a core workflow / data loss; high = major feature broken, no workaround; medium = works but noticeably wrong, workaround exists; low = cosmetic.
Each category starts at 100. Deduct per Verified/Observed finding: critical −25, high −15, medium −8, low −3 (floor 0). Console: 0 errors → 100, 1–3 → 70, 4–10 → 40, more → 10. Links: −15 per broken link.
| Category | Weight | Category | Weight | |
| Functional | 20% | Links | 10% | |
| Console | 15% | Visual | 10% | |
| UX | 15% | Performance | 10% | |
| Accessibility | 15% | Content | 5% |
score = Σ (category_score × weight) — round to an integer out of 100.
Report
Markdown, written as you go (don't batch findings). Include: date, base URL, branch,
tier, routes visited, evidence dir ($QA_OUT). Then per-route:
### Route: /pricing
- **[high · Verified] Plan selector does nothing on click**
Evidence: $QA_OUT/pricing-after-click.png · console: `TypeError: plans is undefined (pricing.tsx:42)`
Repro: load /pricing → click "Pro" → nothing happens, error in console.End every report with — written for a non-expert reader, no jargon:
## Health score: NN/100
## Top 3 things a user would hit
1. **Plain-language sentence** describing what a user experiences, and where.
2. ...
3. ...If nothing is broken, say so plainly: score, routes covered, "no user-facing issues found".
Report-only discipline
- This skill finds; it does not fix. Do not edit source files, do not "quickly patch" anything, do not put suggested code in the report (naming the suspect area is fine).
- Fixes go through the normal Build/Verify loop: diagnose with the
debugskill, one commit per fix, add a regression test for each fix, then re-runbrowser-qaon the affected routes to confirm — beforeship. - Test as a user, not a developer: reading source is for scoping routes only, never for judging "that probably works".
- Keep all screenshots and scripts in
$QA_OUT; never commit evidence into the repo.
Adapted from gstack (MIT, © Garry Tan); see ATTRIBUTION.md.
Other versions21 projects use a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 21
- loulanyue/awesome-claude-notes★ 272same name
- Fmarzochi/EGC★ 61same name
- woxiangyangzhimao/skills★ 14same name
- gongyijie85/dsh-ecc★ 8same name
- zero-click/zero-ratchet★ 8same name
- bhagyamudgal/skills★ 2same name
- angusfretwell/skills★ 1same name
- cskwork/browser-qa★ 1same name
- quokkify/skills★ 1same name
- Ronnasayd/aipp★ 1same name
11 more not listed
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
Used alongsideProjects with it also use git-workflow.
Other files people use alongside this one.
Files found in the same public projects as a version of this one, more often than they turn up anywhere else. Each project counts once, large collections count for less, and this file's own repository is left out.
Microsoft AzureSkillgit-workflow
Squad branching model: dev-first workflow with insiders preview channel
Also in 3 projects that use this
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

