Perform QA testing on running web applications using cmux_browser. Covers functional testing, accessibility auditing, edge case testing, and structured reporting with evidence. **Triggers — use this skill when:** - User says "test this", "QA this", "QA this PR", "check the app" - User says "run QA", "verify the build", "test the UI" - User asks to "check if it works", "validate the feature" - User says "acceptance testing", "smoke test", "regression test" - User asks to "find bugs", "test fo...
Scanned 9/12/2026
Install to Claude Code
npx -y skills add espennilsen/pi --skill qa-testing --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of Qa Testing?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/espennilsen-qa-testing)More formats (shields.io, HTML) on the badges page.
---
name: qa-testing
description: >
Perform QA testing on running web applications using cmux_browser.
Covers functional testing, accessibility auditing, edge case testing,
and structured reporting with evidence.
**Triggers — use this skill when:**
- User says "test this", "QA this", "QA this PR", "check the app"
- User says "run QA", "verify the build", "test the UI"
- User asks to "check if it works", "validate the feature"
- User says "acceptance testing", "smoke test", "regression test"
- User asks to "find bugs", "test for bugs", "break it"
- User wants to "check accessibility", "run axe", "a11y audit"
**Covers:** Any web application accessible via HTTP. Uses cmux_browser
(Playwright under the hood) for all browser interactions.
---
# QA Testing — Web Application Testing with cmux_browser
Test running web applications by interacting as a real user via cmux_browser, then report findings with structured evidence.
## Prerequisites
- The application must be running and accessible via HTTP
- cmux_browser tool must be available
- If the app isn't running, start it first (see Setup below)
## Workflow
### Step 1: Setup
**If the app is already running**, skip to Step 2.
**If the app needs to be started:**
```
# Start in a background cmux pane
cmux_split({ direction: "down", command: "cd /path/to/project && npm run dev\n" })
# Wait for it to be ready (check the pane output)
cmux_read({ surface: "surface:N" })
# Or poll the URL
cmux_browser({ action: "open", url: "http://localhost:3000" })
cmux_browser({ action: "wait", waitCondition: "load-state", loadState: "networkidle" })
```
**If Docker is needed:**
```bash
cd /path/to/project && docker compose up -d
# Wait for healthy
timeout 60 bash -c 'until curl -sf http://localhost:3000 > /dev/null; do sleep 2; done'
```
### Step 2: Smoke Test
Verify the app loads at all before deeper testing.
```
# Open the app
cmux_browser({ action: "open", url: "http://localhost:3000" })
# Wait for it to load
cmux_browser({ action: "wait", waitCondition: "load-state", loadState: "networkidle" })
# Screenshot the initial state
cmux_browser({ action: "screenshot" })
# Check for JavaScript errors
cmux_browser({ action: "errors" })
# Check console output
cmux_browser({ action: "console" })
# Get a DOM snapshot (accessibility tree)
cmux_browser({ action: "snapshot" })
```
If the smoke test fails (page doesn't load, crash errors), stop and report immediately.
### Step 3: Functional Testing
For each acceptance criterion, follow this pattern:
```
# 1. Navigate to the relevant state
cmux_browser({ action: "navigate", url: "/some-page" })
cmux_browser({ action: "wait", waitCondition: "load-state", loadState: "networkidle" })
# 2. Interact as a real user
cmux_browser({ action: "click", selector: "button.submit" })
cmux_browser({ action: "fill", selector: "input[name='email']", value: "test@example.com" })
cmux_browser({ action: "press", key: "Enter" })
# 3. Verify outcomes
cmux_browser({ action: "wait", waitCondition: "text", text: "Success" })
cmux_browser({ action: "snapshot", interactive: true }) # check element states
cmux_browser({ action: "get", selector: ".result", subaction: "textContent" })
cmux_browser({ action: "is", selector: ".modal", subaction: "visible" })
# 4. Screenshot as evidence
cmux_browser({ action: "screenshot" })
# 5. Check for errors after the interaction
cmux_browser({ action: "errors" })
```
**Tips for reliable element selection:**
- Use `snapshot` to see the accessibility tree and find the right selectors
- Use `find` with `role` for semantic targeting: `cmux_browser({ action: "find", subaction: "role", name: "Submit" })`
- Use `identify` to see interactive elements on the page
- Prefer `data-testid`, `aria-label`, or semantic selectors over fragile CSS paths
### Step 4: Edge Case Testing
**Be skeptical.** Agents often ship code that works for the happy path but breaks on edge cases. Actively try to break things:
| Test | How | What to look for |
|------|-----|-----------------|
| **Empty state** | Clear all data, visit pages with no content | Crashes, blank screens, missing "no data" messages |
| **Empty inputs** | Submit forms with empty required fields | Missing validation, silent failures, crashes |
| **Long text** | Paste 500+ character strings into inputs | Overflow, layout breaking, truncation without indication |
| **Special characters** | Input `<script>alert(1)</script>`, emoji 🎉, Unicode ñ | XSS, encoding errors, display issues |
| **Rapid clicks** | Double-click submit buttons, rapidly toggle switches | Duplicate submissions, race conditions, broken state |
| **Back button** | Navigate forward through a flow, then press back | Lost state, stale data, errors |
| **Refresh** | F5 / reload mid-flow | Lost state, errors, unexpected redirects |
| **Network errors** | Disconnect WiFi / block API calls (if possible) | Missing error handling, infinite spinners, blank screens |
```
# Example: test empty form submission
cmux_browser({ action: "click", selector: "button[type='submit']" })
cmux_browser({ action: "screenshot" })
cmux_browser({ action: "errors" })
# Example: test long text
cmux_browser({ action: "fill", selector: "input[name='title']", value: "A".repeat(500) })
cmux_browser({ action: "screenshot" })
# Example: test special characters
cmux_browser({ action: "fill", selector: "input[name='name']", value: "<script>alert('xss')</script>" })
cmux_browser({ action: "click", selector: "button[type='submit']" })
cmux_browser({ action: "screenshot" })
cmux_browser({ action: "errors" })
```
### Step 5: Accessibility Audit
Inject axe-core and run a full accessibility audit:
```
# Inject axe-core library
cmux_browser({ action: "eval", value: `
await new Promise((resolve, reject) => {
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/axe-core/4.10.2/axe.min.js';
script.onload = resolve;
script.onerror = reject;
document.head.appendChild(script);
});
const results = await axe.run();
return JSON.stringify({
violations: results.violations.map(v => ({
id: v.id,
impact: v.impact,
description: v.description,
helpUrl: v.helpUrl,
nodes: v.nodes.length
})),
passes: results.passes.length,
incomplete: results.incomplete.length,
inapplicable: results.inapplicable.length
}, null, 2);
` })
```
**CDN fallback:** If the CDN is unreachable (air-gapped CI, network restriction, outage), the script injection will fail. In that case, fall back to:
- `npx axe-cli http://localhost:3000 --save axe-report.json` (CLI-based audit)
- or skip the accessibility audit and note "A11y audit skipped — axe-core CDN unavailable" in the report
**Security note:** For production use, add Subresource Integrity (SRI) verification to the script tag: `script.integrity = "sha384-..."; script.crossOrigin = "anonymous";` (hash available on cdnjs.com).
**Interpreting axe-core results:**
- **critical** impact — Must fix. Screen readers can't use the page.
- **serious** impact — Should fix. Major barrier for some users.
- **moderate** impact — Nice to fix. Some users affected.
- **minor** impact — Optional. Best practice improvements.
**Scoring accessibility:**
| Violations | Score |
|-----------|-------|
| 0 violations | 10 |
| 1-3 minor | 8-9 |
| 1-3 serious | 6-7 |
| 4-10 mixed | 4-5 |
| 10+ or any critical | 2-3 |
### Step 6: Performance (Optional)
Run Lighthouse for performance auditing when requested:
```bash
npx lighthouse http://localhost:3000 \
--output=json \
--output-path=./lighthouse-report.json \
--chrome-flags="--headless --no-sandbox" \
--only-categories=performance,accessibility,best-practices \
--quiet
```
Then read and summarize the results:
```
read lighthouse-report.json
```
### Step 7: Report
Produce a structured QA report. Always include:
1. **Verdict** — PASS (all dimensions ≥ 6, no critical failures) or FAIL
2. **Scores** — 1-10 for each dimension with brief justification
3. **Acceptance criteria checklist** — each item explicitly PASS or FAIL
4. **Bugs** — with severity, reproduction steps, expected vs actual, screenshot
5. **Edge cases tested** — what you tried and what happened
6. **Accessibility results** — axe-core violation count and details
7. **Screenshots** — numbered, referenced in bug descriptions
### Step 8: Cleanup
If you started a dev server or Docker container in Step 1, clean up:
```
# Stop a dev server running in a cmux pane
cmux_close({ surface: "surface:N" })
# Or stop Docker containers
cd /path/to/project && docker compose down
```
This prevents orphaned processes and keeps the environment clean for the next test run.
## Report Template
```markdown
# QA Report: [Feature/PR Name]
**Date:** YYYY-MM-DD
**App URL:** http://localhost:XXXX
**Tested by:** QA Agent
## Verdict: PASS ✅ / FAIL ❌
## Scores
| Dimension | Score | Notes |
|-----------|-------|-------|
| Functionality | X/10 | Brief justification |
| Completeness | X/10 | Brief justification |
| UX | X/10 | Brief justification |
| Robustness | X/10 | Brief justification |
| Accessibility | X/10 | N violations (N critical, N serious) |
**Average:** X.X/10
## Acceptance Criteria
- [x] Criterion 1 — PASS
- [x] Criterion 2 — PASS
- [ ] Criterion 3 — FAIL: [specific issue]
## Bugs Found
### 🔴 Bug 1: [Title] (Critical)
- **Steps:** 1. Navigate to /page 2. Click button 3. ...
- **Expected:** Form submits and shows success
- **Actual:** Page crashes with TypeError in console
- **Screenshot:** #3
### 🟡 Bug 2: [Title] (Major)
...
### 🔵 Bug 3: [Title] (Minor)
...
## Edge Cases Tested
| Test | Result | Notes |
|------|--------|-------|
| Empty form submission | ✅ PASS | Shows validation errors |
| Long text (500 chars) | ⚠️ WARN | Text overflows container |
| Special characters | ✅ PASS | Properly escaped |
| Back button | ❌ FAIL | State lost, shows blank page |
| Rapid double-click | ✅ PASS | Button disabled after first click |
## Accessibility (axe-core)
- **Violations:** N
- **Passes:** N
- **Details:**
- [serious] button-name: 2 buttons missing accessible names
- [moderate] color-contrast: 3 elements with insufficient contrast
## Screenshots
1. Initial load — [description]
2. After form submission — [description]
3. Bug #1 evidence — [description]
```
## Grading Rubric Reference
| Dimension | 10 (Exceptional) | 8-9 (Very Good) | 6-7 (Good) | 4-5 (Acceptable) | 2-3 (Poor) |
|-----------|-------------------|-----------------|------------|------------------|------------|
| **Functionality** | All criteria pass, flows smooth | — | Most pass, minor issues | Some criteria fail | Core flows broken |
| **Completeness** | Everything built and working | — | Minor features missing | Significant gaps | Mostly stubs |
| **UX** | Polished, delightful | — | Good, minor rough edges | Functional but clunky | Confusing/broken |
| **Robustness** | Handles everything gracefully | — | Handles common cases | Some edge cases crash | Fragile |
| **Accessibility** | 0 violations | 1-3 minor violations | 1-3 serious violations | 4-10 mixed violations | 10+ or any critical |
## Verdict Rules
- **PASS** = All dimensions ≥ 6 AND no critical functionality failures
- **FAIL** = Any dimension < 6 OR acceptance criteria not met
When reporting FAIL, always provide specific, actionable feedback the builder can use to fix the issues. Reference exact elements, URLs, and steps.
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!