From dm-work
QA web applications using Chrome DevTools MCP. Use when testing running apps, verifying acceptance criteria against a live UI, checking for console errors, evaluating UI behavior, or running regression checks. Requires the chrome-devtools MCP server to be connected. Complements dm-work:review (code-level) with runtime verification.
npx claudepluginhub rbergman/dark-matter-marketplace --plugin dm-workThis skill uses the workspace's default tool permissions.
Test running web applications by actually navigating, clicking, and asserting — not just reading code.
Generates design tokens/docs from CSS/Tailwind/styled-components codebases, audits visual consistency across 10 dimensions, detects AI slop in UI.
Records polished WebM UI demo videos of web apps using Playwright with cursor overlay, natural pacing, and three-phase scripting. Activates for demo, walkthrough, screen recording, or tutorial requests.
Delivers idiomatic Kotlin patterns for null safety, immutability, sealed classes, coroutines, Flows, extensions, DSL builders, and Gradle DSL. Use when writing, reviewing, refactoring, or designing Kotlin code.
Test running web applications by actually navigating, clicking, and asserting — not just reading code.
This skill covers standard web applications testable via Chrome DevTools. It is NOT suitable for:
When the evaluator encounters projects outside browser-qa's scope, it should mark runtime criteria as UNTESTABLE and note the recommended verification method.
Find testable criteria from (in priority order):
bd show <id> → check the design field for acceptance criteriadocs/ or history/ for the spec that spawned this worknavigate_page(url: "http://localhost:<port>/<path>")
take_snapshot()
The snapshot returns a text representation of the page's accessibility tree. Each interactive element has a UID you can target with click, fill, etc.
Always snapshot before interacting — UIDs are ephemeral and change on navigation.
For each acceptance criterion, drive the corresponding user flow.
Navigate and verify content:
navigate_page(url: "<target URL>")
wait_for(text: ["<expected content>"])
take_snapshot() # confirm element is present in a11y tree
Fill and submit a form:
take_snapshot() # get current UIDs
fill(uid: "<input-uid>", value: "<test value>") # type into field
click(uid: "<submit-uid>", includeSnapshot: true) # submit + get new state
wait_for(text: ["<success indicator>"]) # wait for result
Multi-field forms (more efficient):
fill_form(elements: [
{ uid: "<email-uid>", value: "test@example.com" },
{ uid: "<name-uid>", value: "Test User" },
{ uid: "<role-uid>", value: "admin" }
])
click(uid: "<save-uid>", includeSnapshot: true)
Keyboard interaction:
press_key(key: "Enter") # submit
press_key(key: "Escape") # dismiss modal
press_key(key: "Tab") # focus next
press_key(key: "Control+A") # select all
Drag and drop:
drag(from_uid: "<source>", to_uid: "<target>", includeSnapshot: true)
After each flow step, verify using the cheapest sufficient method:
Text/element presence (cheapest — prefer this):
take_snapshot()
# Scan the a11y tree for expected text, roles, states
Visual verification (use when layout/styling matters):
take_screenshot()
# Review for correct layout, colors, typography, spacing
Console errors (always check):
list_console_messages(types: ["error"])
# Should be empty. Warnings are informational, errors are failures.
Network request success:
list_network_requests(resourceTypes: ["fetch", "xhr"])
# Verify status codes (200, 201, 204). Flag 4xx/5xx.
Custom JS assertions (for state that isn't visible in the a11y tree):
evaluate_script(expression: "document.querySelector('.toast.success') !== null")
evaluate_script(expression: "window.__APP_STATE__.user.role === 'admin'")
Beyond the specific acceptance criteria, check for collateral damage:
list_console_messages(types: ["error"]) on every page visitedlist_network_requests()emulate(viewport: "375x667,mobile,touch") then re-check key elementsemulate(colorScheme: "dark") then screenshotlighthouse_audit(mode: "snapshot") for quick a11y scoreStructure the report as pass/fail per acceptance criterion:
## QA Report: <bead-id or description>
### Acceptance Criteria Results
| # | Criterion | Result | Notes |
|---|-----------|--------|-------|
| 1 | User can navigate to /settings | PASS | |
| 2 | Form validates email client-side | PASS | Shows red border + message |
| 3 | Save button disabled during submit | FAIL | Button stays enabled |
| 4 | Success toast after save | PASS | |
| 5 | No console errors | PASS | 0 errors |
### Regression Checks
- Console errors: 0
- Network failures: 0
- Mobile responsive: PASS
- Lighthouse accessibility: 92
### Verdict: FAIL (1/5 criteria failed)
Failing criteria:
- #3: Save button not disabled during form submission. The button remains
clickable, allowing duplicate submissions.
Integration with beads: If a criterion fails, create a linked bead:
bd create --title="Save button not disabled during submission" --type=bug --priority=2 --deps discovered-from:<parent-bead>
| Tool | Purpose | When to use |
|---|---|---|
navigate_page | Go to URL, back/forward/reload | Start of each flow |
take_snapshot | A11y tree with element UIDs | Before any interaction; for text/structure assertions |
take_screenshot | Visual capture (PNG) | Layout/styling verification; design quality checks |
click | Click element by UID | Buttons, links, toggles |
fill | Input/textarea/select by UID | Single form fields |
fill_form | Multiple fields at once | Multi-field forms (more efficient) |
type_text | Raw keystrokes (no targeting) | When you need to type into the focused element |
press_key | Key/combo (Enter, Escape, Ctrl+A) | Submit, dismiss, shortcuts |
wait_for | Wait for text to appear | After navigation or async operations |
drag | Drag element to element | Drag-and-drop UIs |
list_console_messages | Console output (filter by type) | Error checking — always run |
list_network_requests | Network activity (filter by type) | API call verification |
get_network_request | Single request details + body | Deep API response inspection |
get_console_message | Single console message details | Investigating specific errors |
lighthouse_audit | A11y, SEO, best practices scores | Quick accessibility check |
evaluate_script | Run JS in page context | Custom assertions on app state |
emulate | Viewport, dark mode, network, geo | Responsive/a11y/offline testing |
upload_file | File input interaction | Upload flows |
take_snapshot over take_screenshot — text is far cheaper than images in contexttake_screenshot only when you need to verify visual appearance (layout, colors, styling)wait_for before asserting — async UI needs time to settle after interactionslist_console_messages(types: ["error"]) is the cheapest smoke test — run it on every pagewait_for expected content after navigation — don't assume instant renderincludeSnapshot: true on click/fill to get the post-action state in one call instead of twofilePath) when running multiple checks to avoid bloating contextlighthouse_audit(mode: "snapshot") is faster than mode: "navigation" (no reload)Cheapest possible verification — use when you just need to know the page works:
navigate_page(url: "<url>")
wait_for(text: ["<any expected text>"])
list_console_messages(types: ["error"]) # should be empty
navigate_page(url: "<form page>")
take_snapshot()
fill_form(elements: [<fields>])
click(uid: "<submit>", includeSnapshot: true)
wait_for(text: ["<success message>"])
list_console_messages(types: ["error"])
list_network_requests(resourceTypes: ["fetch"]) # verify API call
navigate_page(url: "<page>")
wait_for(text: ["<loaded indicator>"])
take_screenshot(filePath: "qa-screenshots/desktop.png")
emulate(viewport: "375x667,mobile,touch")
take_screenshot(filePath: "qa-screenshots/mobile.png")
emulate(colorScheme: "dark")
take_screenshot(filePath: "qa-screenshots/dark-mobile.png")
navigate_page(url: "<page>")
take_screenshot(fullPage: true, filePath: "qa-screenshots/full-page.png")
# Review screenshot for AI slop patterns:
# - Generic purple/blue gradients over white cards
# - Template-default typography and spacing
# - Identical component styling across unrelated sections
# - Over-reliance on rounded corners + drop shadows + blur
# - No distinct identity or mood — looks like every other AI-generated site