Skip to content
Back to skills

webtest-orch

ASecurity

End-to-end web app testing. Use when user says "test the app", "run e2e", "smoke test", "regression run", "check the login/onboarding/chat flow", "audit accessibility", "test responsive", or "find bugs in <url>" — even when Playwright is not named. Bootstraps Playwright + axe-core, runs LLM exploration on first run, deterministic replay afterward, emits markdown report + bugs.json + *.spec.ts files with run-diffing.

  • 4 stars
  • 0 votes
  • 0 copies
  • 2 views
  • Added May 26, 2026
testingpythongoshellbashnextjsnodefastapitestinggitapi

Works with

  • claude code
  • api
  • mcp

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 20 files and shows the line behind each finding

Scanned May 27, 2026

npx -y skills add CreatmanCEO/webtest-orch --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of webtest-orch?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for webtest-orch
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/creatmanceo-webtest-orch/badge)](https://www.skillsdirectory.com/skills/creatmanceo-webtest-orch)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: webtest-orch
description: End-to-end web app testing. Use when user says "test the app", "run e2e", "smoke test", "regression run", "check the login/onboarding/chat flow", "audit accessibility", "test responsive", or "find bugs in <url>" — even when Playwright is not named. Bootstraps Playwright + axe-core, runs LLM exploration on first run, deterministic replay afterward, emits markdown report + bugs.json + *.spec.ts files with run-diffing.
trigger: /test-app
---

# webtest-orch

End-to-end testing orchestrator for web applications. Splits into **first-run exploratory** (LLM-driven via Playwright MCP) and **nth-run deterministic replay** (`npx playwright test`, ~zero LLM tokens). Emits regression specs, normalized bugs.json, markdown + HTML report.

## Project state (auto-injected at skill load)

- Working dir:        !`pwd`
- Tests dir:          !`test -d tests && echo yes || echo no`
- Playwright deps:    !`test -f node_modules/.bin/playwright && echo yes || echo no`
- Config:             !`test -f playwright.config.ts && echo yes || echo no`
- Auth state:         !`test -f playwright/.auth/user.json && echo present || echo missing`
- Listening servers:  !`bash -c 'command -v lsof >/dev/null && lsof -iTCP:3000,5173,8000,8080,8081 -sTCP:LISTEN -P -n 2>/dev/null | tail -n +2 || (command -v ss >/dev/null && ss -tlnp 2>/dev/null | grep -E ":3000|:5173|:8000|:8080|:8081") || echo none'`
- Last run id:        !`bash -c 'r=$(ls -1t reports 2>/dev/null | head -1); echo "${r:-never}"'`
- Last bugs JSON:     !`bash -c 'b=$(ls -t reports/*/bugs.json 2>/dev/null | head -1); echo "${b:-none}"'`
- Isolation verified: !`test -f "${CLAUDE_SKILL_DIR}/.isolation-verified" && echo yes || echo no`
- Test creds file:    !`test -f .env.test && echo yes || echo missing`

## Image budget protection — READ FIRST, MANDATORY

**The problem:** Claude Code has two independent context limits — text tokens (large)
and **inline-image blocks** (~50–100 per session). Screenshots returned inline burn
the image budget far faster than the text budget; once exhausted, the user must
`/compact` even at 20% text-context usage.

**Distinction that matters:**
- ❌ **Inline image returns to parent context** burn the budget. This includes
  `browser_take_screenshot` default output (image returned to caller),
  `Read` on a `.png/.jpg/.webp/.gif/.bmp/.svg`, markdown report with `![]()` shown
  to parent.
- ✅ **On-disk artefacts that nobody Reads** are FREE. Playwright's failure
  screenshots go to `test-results/`, MCP browser tools may save `.png`s to a
  cache dir — none of these cost the parent context UNLESS you `Read` them.

**The hard rule, enforced by you (not by frontmatter):**

> **NEVER return screenshots to the parent skill context. ALWAYS dispatch a Task
> subagent (general-purpose) for anything that produces or consumes images.
> Subagent returns ONLY text — paths, descriptions, verdicts.**

This contract was attempted via `context: fork` frontmatter but Claude Code 2.1.x on Windows does not honor that field, so enforcement is delegated to *you reading these instructions*. Verified empirically 2026-04-28 (sub-agent isolation works; `context: fork` does not parse). See `${CLAUDE_SKILL_DIR}/.isolation-verified`.

**Forbidden in this skill's parent context:**
- ❌ `Playwright:browser_take_screenshot` (default returns image inline) — wrap it in a Task subagent
- ❌ `Read` on `*.png/.jpg/.webp/.gif/.bmp/.svg` from any path — Task subagent reads, summarizes
- ❌ Markdown reports with `![](path.png)` shown to parent — only print absolute filesystem paths
- ❌ `chrome-devtools:take_screenshot` — same Task wrapper rule

**Approved patterns:**

```
PATTERN A — text-only browser exploration (default 90% of work)
  Playwright:browser_navigate / browser_snapshot (ARIA tree → text)
  Playwright:browser_evaluate (DOM scrape → JSON)
  axe-core via spawned npx process → JSON violations
  console / network listeners → JSON
  → ALL outputs are text. No image budget cost.

PATTERN B — vision genuinely required (max 3-5 times per run)
  Task tool, subagent_type: "general-purpose", prompt:
    "Read ONE image at <absolute path>. Output: <severity>: <symptom> in <selector> at <viewport>.
     One line. No preamble. Do not return the image."
  → subagent burns its own image cap, parent stays clean.

PATTERN C — pixel-diff baseline (deterministic, scriptable)
  Spec uses toHaveScreenshot() — Playwright reports diff% as TEXT in JSON output.
  Diff > threshold → run Pattern B on the failed image only.
```

If you ever feel tempted to call `browser_take_screenshot` from this skill's parent context "just to check" — **STOP**. That single call costs the user a future `/compact`. Use `browser_snapshot` (ARIA tree) instead. If that's not enough, dispatch Pattern B.

If `${CLAUDE_SKILL_DIR}/.isolation-verified` is missing, run **Step 0** before any browser work.

## Step 0 — Image isolation self-test (once per install)

Skip if `Isolation verified: yes` above. Otherwise:

1. `bash -c 'python "${CLAUDE_SKILL_DIR}/scripts/_image_isolation_check.py" --gen-fixtures'`
2. Dispatch a Task subagent with this exact prompt:
   > "Read these 3 files with the Read tool and return one short text description per file: `${CLAUDE_SKILL_DIR}/fixtures/iso-test/a.png`, `${CLAUDE_SKILL_DIR}/fixtures/iso-test/b.png`, `${CLAUDE_SKILL_DIR}/fixtures/iso-test/c.png`. Output 3 lines, no preamble."
3. Verify response is 3 lines of text (no inline images leaked back).
4. `bash -c 'python "${CLAUDE_SKILL_DIR}/scripts/_image_isolation_check.py" --mark-verified'`

If step 3 returns inline images instead of text → STOP, escalate to user, do not run any further test work.

## Workflow

Copy this checklist into TodoWrite at session start; tick as you go.

- [ ] **1. State probe.** Read the auto-injected table above. Identify mode:
   - No `tests/` AND no `playwright.config.ts` → **BOOTSTRAP**
   - Both present, requested flow is covered by existing specs → **REPLAY**
   - Both present, requested flow is new → **HYBRID**
- [ ] **2. (BOOTSTRAP only)** Scaffold from `${CLAUDE_SKILL_DIR}/templates/`:
    - **Auth detection first:** read `.env.test`. If `TEST_USER_EMAIL` and `TEST_USER_PASSWORD` are present → **AUTHED FLOW**; if both missing → **PUBLIC FLOW**.
    - **AUTHED FLOW:**
        - `playwright.config.ts.tmpl` → `playwright.config.ts` (has setup project + storageState)
        - `auth.setup.ts.tmpl` → `tests/auth.setup.ts`
        - `fixture.ts.tmpl` → `tests/fixtures/index.ts`
        - Run `tests/auth.setup.ts` once → `playwright/.auth/user.json`
    - **PUBLIC FLOW:**
        - `playwright.config.public.ts.tmpl` → `playwright.config.ts` (no setup, no storageState)
        - Skip `auth.setup.ts` and `fixtures/`. Specs import directly from `@playwright/test`.
    - Substitute `__PROJECT_BASE_URL__` etc. from probe or `.env.test`
    - `npm i -D @playwright/test @axe-core/playwright dotenv`
    - `npx playwright install chromium webkit`
- [ ] **3. Scope.** Decide what to test:
   - Specific URL passed by user → that route only
   - "test the app" → discover from sitemap/`git diff HEAD~1` for changed routes
   - First run → minimal critical-path: home + auth + one main flow
- [ ] **4. Dev server up.** `python "${CLAUDE_SKILL_DIR}/scripts/with_server.py" --help`. Use it; **do not read its source unless `--help` doesn't cover the case.**
- [ ] **5a. EXPLORATORY** (BOOTSTRAP / new flow in HYBRID): use **Playwright MCP** with `Playwright:browser_snapshot` (ARIA tree, text). Walk the flow, generate POM in `tests/pages/<Page>.ts`, generate spec in `tests/specs/<flow>.spec.ts`. **Generate locators from ARIA tree refs you actually saw** — do NOT use generic regex like `getByPlaceholder(/john doe|name|имя/i)`, they cause strict-mode violations on first run. Either use exact strings from the snapshot OR add `.first()` explicitly. Run the spec once to confirm green.

  **🔴 SPEC GENERATION CONTRACT — non-negotiable.** Even if you skip the template
  and write a spec from scratch (when product context is rich), every generated
  `*.spec.ts` MUST contain ALL of these:
   1. **Console listeners attached BEFORE `page.goto()`**: `consoleErrors[]` from
      `page.on('pageerror')` and `page.on('console', m => m.type() === 'error')`.
   2. **Network listeners attached BEFORE `page.goto()`**: `failedRequests[]` from
      `page.on('response', r => r.status() >= 400 && ...)` and `page.on('requestfailed')`.
   3. **`AxeBuilder` scan** with `withTags(['wcag2a','wcag2aa','wcag21aa','wcag22aa'])`
      whose violations are pushed into `issues[]`.
   4. **`issues[]` collector pattern** — every soft check pushes a structured tag
      into `issues` (e.g. `a11y[serious] color-contrast: ...`, `heading-jump: ...`,
      `touch-target: ...`, `overflow: ...`, `html-lang: ...`).
   5. **Single hard `expect(issues).toEqual([])`** at the end with
      `${count} issues found:\n  - ...` message format so `run_suite.py` can split
      one test failure into one bug record per issue.
   6. **Trailing `expect(consoleErrors).toEqual([])` + `expect(failedRequests).toEqual([])`**.

  Skip ANY of these and the skill's console-audit / a11y / per-issue fingerprinting
  features stop working. Use `templates/spec.ts.tmpl` as the canonical reference —
  copy its skeleton into hand-written specs.
- [ ] **5b. REPLAY**: `npx playwright test --reporter=list,json,html`. **No Playwright MCP, no LLM browser actions.**
- [ ] **6. A11y** on each visited page: deterministic `@axe-core/playwright` (in spec) + qualitative checks via nested subagent if alt-text/heading/focus suspect. See `reference/a11y-patterns.md`.
- [ ] **7. Console + network.** Listeners attach BEFORE `page.goto()` (mandatory). Pipe captured logs through `python "${CLAUDE_SKILL_DIR}/scripts/triage_console.py" --help`.
- [ ] **8. Visual.** Default `toHaveScreenshot()` in spec. Diff fired → `python "${CLAUDE_SKILL_DIR}/scripts/visual_diff.py" --classify` spawns nested subagent on each failed image (text verdict only). Argos opt-in via `VISUAL_DIFF=argos`.
- [ ] **9. Fingerprint + diff.** `python "${CLAUDE_SKILL_DIR}/scripts/fingerprint_bugs.py" --current reports/<curr>/raw.json --previous reports/<prev>/bugs.json --out reports/<curr>/bugs.json`.
- [ ] **10. Report.** `python "${CLAUDE_SKILL_DIR}/scripts/generate_report.py" --run-dir reports/<run-id> [--app-name "My App"]`. Reads `bugs.json` + `diff.json` from that dir, writes `report.md` + `index.html` next to them. Print absolute path to `index.html`.

## Decision tree

```
detect_state.py → JSON
  ├─ no tests/ and no config       → BOOTSTRAP (full first run)
  ├─ tests/ + specs cover scope    → REPLAY (npx playwright test)
  └─ tests/ + new flow requested   → HYBRID (replay existing + explore new)
```

## MCP usage rules

- State **"using Playwright MCP"** explicitly the first time you reach for browser tools in a session. Without this, Claude often shells `npx playwright` instead.
- **Always** use fully-qualified names: `Playwright:browser_navigate`, `Playwright:browser_snapshot`, NOT bare `browser_navigate`.
- Default to `Playwright:browser_snapshot` (ARIA tree → text). Use `Playwright:browser_take_screenshot` ONLY when:
  1. Pixel-diff baseline establishment, AND
  2. Output destination is filesystem (`screenshot_path` argument), NOT inline.
- Use `chrome-devtools:performance_start_trace` ONLY for diagnostic flows (slow LCP, CORS, memory leak, sourcemap-resolved stacks). Don't use it for general exploration — its tokens are higher than Playwright MCP.

## Anti-patterns — DO NOT GENERATE

- ❌ `await page.waitForTimeout(2000)` — use web-first assertions
- ❌ `page.locator('div.btn-primary > span:nth-child(2)')` — use `getByRole`
- ❌ UI login per test — use `storageState` fixture
- ❌ Reading source of black-box scripts before trying `--help`
- ❌ Embedding credentials in spec files — read from `process.env` (loaded via `.env.test`)
- ❌ Generating tests outside `tests/specs/` — CI globs may miss them
- ❌ Returning screenshots inline to parent context — see Image budget protection
- ❌ `expect.poll` without timeout — flaky
- ❌ `nth(0)` / `first()` / `last()` without scoping via `.filter()`
- ❌ One-mega-test that does login + onboarding + checkout — split per flow

## Locator priority (enforce in generated specs)

1. `getByRole('button', { name: 'Sign in' })` — survives redesigns, aligns with a11y
2. `getByLabel`, `getByPlaceholder` — form fields
3. `getByText`, `getByAltText`, `getByTitle` — content-anchored
4. `getByTestId` — when semantics unavailable; require `data-testid` in app code
5. CSS/XPath — only with explicit code-comment justification, scoped via `.filter()`

## Black-box scripts (run with `--help` first; do not read source)

- `scripts/detect_state.py --json` — project state JSON
- `scripts/with_server.py --frontend 3000 --backend 8000` — server lifecycle
- `scripts/run_suite.py --out reports/<run-id> --project chromium-desktop` — wraps `playwright test`
- `scripts/triage_console.py --input console.json` — noise filter + LLM long tail
- `scripts/visual_diff.py --classify reports/<run-id>` — pixel diff + nested-subagent classification
- `scripts/fingerprint_bugs.py --current curr.json --previous prev.json` — bug dedup + diff
- `scripts/generate_report.py --run-dir reports/<run-id> [--app-name "X"]` — markdown + html (reads bugs.json/diff.json from --run-dir)
- `scripts/_image_isolation_check.py --verify` — image budget contract self-check

## When to dispatch a Task subagent

This skill runs in the parent chat context (no automatic fork). Always dispatch a Task subagent (`general-purpose`) for:
- **Any browser work that may produce screenshots** — see Image budget protection (mandatory wrap)
- **Test suites > 30 specs OR > 2 minutes wall-clock** — keep token budget under 25k auto-compaction ceiling
- **Vision classification of a single screenshot** — Pattern B in Image budget protection
- **Long-tail console triage that needs LLM inference per message** — batch-spawn one subagent per chunk

## Credentials — universal, no project hardcoding

Read in this order, first hit wins:
1. `<project>/.env.test` (project-scoped)
2. `${TEST_CREDENTIALS_FILE}` env var pointing to a global file
3. Prompt user once, write to `<project>/.env.test`, add to `.gitignore`

Required keys per project: `TEST_BASE_URL`, `TEST_USER_EMAIL`, `TEST_USER_PASSWORD`. Optional: `TEST_API_LOGIN_PATH` (default `/api/auth/login`), `TEST_API_TOKEN_FIELD` (default `access_token`), `TEST_USER_AGENT_KIND` (default `desktop`).

## References (load on demand, do not pre-read)

- `reference/playwright-patterns.md` — locator priority, web-first assertions, anti-flake patterns
- `reference/auth-strategies.md` — Day 2: API-login, JWT, storageState, OAuth fallback notes
- `reference/a11y-patterns.md` — Day 3: axe + qualitative checks via nested subagent
- `reference/responsive-checklist.md` — Day 3: viewports, touch targets, overflow detection
- `reference/console-noise-patterns.md` — Day 3: ignore-list defaults
- `reference/stack-specific.md` — Day 4: Next.js, FastAPI, Telegram WebApp, WebSocket/SSE
- `reference/reporting.md` — Day 4: JSON schema, markdown skeleton, tracker mappings

Files in this skill

  • .npmignore567 B
  • CHANGELOG.md11.4 KB
  • CODE_OF_CONDUCT.md1.7 KB
  • CONTRIBUTING.md2.6 KB
  • README.ru.md26 KB
  • SKILL.md15.1 KB
  • bin/webtest-orch.js7.4 KB
  • examples/authed-dashboard.spec.ts2.3 KB
  • examples/public-landing.spec.ts3.4 KB
  • examples/telegram-webapp.spec.ts2.7 KB
  • install.sh3.6 KB
  • package.json1.4 KB
  • pyproject.toml1.1 KB
  • reference/a11y-patterns.md5.3 KB
  • reference/auth-strategies.md7.2 KB
  • reference/console-noise-patterns.md5.2 KB
  • reference/playwright-patterns.md8.2 KB
  • reference/reporting.md5.5 KB
  • reference/responsive-checklist.md3.8 KB
  • reference/stack-specific.md7.1 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.