# E2e Setup

> One-time bootstrap that stands up a Playwright e2e suite in a repo that has none, so the e2e-retrofit workflow can then run against it. Copies the bundled reference harness into the project, installs Playwright + browser binaries, registers the Playwright MCP, wires the harness helpers to the real app (auth, API base, login selectors) via MCP recon, writes a single smoke test to prove the wiring…

- **Type:** Skill
- **Install:** `agentstack add skill-cakehurstryan-qe-skills-e2e-setup`
- **Verified:** Yes — security-reviewed for prompt injection and unsafe behavior
- **Seller:** [cakehurstryan](https://agentstack.voostack.com/s/cakehurstryan)
- **Installs:** 0
- **Category:** [Web & Browser](https://agentstack.voostack.com/c/web-and-browser)
- **Latest version:** 0.1.0
- **License:** MIT
- **Upstream author:** [cakehurstryan](https://github.com/cakehurstryan)
- **Source:** https://github.com/cakehurstryan/QE-Skills/tree/main/.claude/skills/e2e-setup

## Install

```sh
agentstack add skill-cakehurstryan-qe-skills-e2e-setup
```

Requires the [AgentStack CLI](https://agentstack.voostack.com/docs/cli). Works with Claude Code, Cursor, and any MCP-compatible agent.

## About

# E2E Setup — one-time Playwright bootstrap

Stands up a working Playwright e2e suite in a repo that has none. **Run once per repo.** Once it's done, every future run uses the **`e2e-retrofit`** skill (the many-times workflow) — this skill is the run-once seed.

> **Scope — what setup does and does NOT produce.** Setup wires the harness to
> the app and writes **exactly one spec: `smoke.spec.ts`**, just enough to prove
> the wiring works end to end. It does **not** write feature/acceptance tests,
> and it does **not** mark any coverage as done — that is `e2e-retrofit`'s job,
> one section per batch. Setup that emits a pile of green feature specs is
> over-reaching: it has no requirements to test against, so those specs would
> only assert "the app does what it currently does" (characterization), and
> marking them "covered" is self-grading. Keep setup to wiring + smoke.

The reference scaffold is bundled here under `harness/`. See `harness/README.md` for its layout and the **infrastructure** (keep, adapt) vs **example** (replace with your domain) labelling of every file. The selector/assertion rules this bootstrap follows when wiring helpers live in the sibling skill's `../e2e-retrofit/patterns.md`.

This skill drives **two** Playwrights, set up separately:

| | What | Where it lives | Used by |
|---|---|---|---|
| **Test runner** | `@playwright/test` — runs the specs | the project's e2e dir | retrofit Phases 3–6 |
| **Playwright MCP** | browser-automation server Claude drives | registered in Claude Code, not the repo | recon (setup + retrofit Phase 2) |

## Step 0 — Detect: is setup already done?

Look for an e2e dir with a `playwright.config.ts` (e.g. `e2e/`, `tests/e2e/`).
- **Found** → setup has already run. **Stop.** Tell the user to use the `e2e-retrofit` skill; do not re-instantiate.
- **Not found** → greenfield. Continue.

## Step 1 — Clarify (one round, then proceed)

> **HARD GATE — the target environment is a blocking prerequisite.** Do **not**
> proceed to Step 2 (or any scaffolding, install, or wiring) until the user has
> given you a concrete app to point at and you have confirmed it is reachable.
> The entire point of this skill is to wire the harness to a **real running
> app**; without one there is nothing to wire, smoke-verify, or seed a matrix
> from. If the repo has no app of its own (e.g. it's a library, a CLI, or this
> skills repo itself), that does **not** license you to invent, scaffold, or
> assume one — **stop and ask** which app/URL to target. Never guess the base
> URL, never build a stand-in app unless the user explicitly asks for one.
> Question 1 below is the gate; questions 2–5 can be inferred or deferred, but
> question 1 cannot.
>
> **The gate is cleared by exactly one thing: the user naming the target in the
> current conversation.** Nothing else counts. In particular:
> - **Ambient artifacts are not the user.** A `.playwright-mcp/` recon dump, an
>   existing `COVERAGE.md` draft, a half-written `.env`, prior-session logs, or a
>   URL in the git history tell you what *was* looked at once — not what the user
>   wants wired *now*. Inferring the target from these **is** the assumption the
>   gate forbids. If they make you confident, that is exactly when to ask the user
>   to confirm, not when to skip asking. Treating them as the answer is a gate
>   bypass even if they turn out to be right.
> - **Confidence does not collapse the gate.** "I'm sure I know the target" is not
>   a license to proceed — a hard gate holds *regardless* of how certain you feel.
>   If you were uncertain you would ask anyway; being certain changes nothing.
> - **A reachability check is already proceeding.** Do not `curl`, `browser_navigate`,
>   or otherwise probe a candidate URL to "confirm" it before the user has named it.
>   Probing a guessed URL is acting on the guess. Reachability is verified *after*
>   the user gives the target (Step 1, Q1), never as a way to validate a hunch first.
>
> If you catch yourself reasoning "the user clearly reconned X" or "the draft
> points at X, so I'll just verify it" — stop. That is the bypass. Ask.

Ask only what you can't infer from the repo:
1. **Target environment (REQUIRED — gate)** — tests need a running app to point at. Either:
   - **Local:** the app + API base URLs (e.g. `http://localhost:3000`) and the `$DEV_UP` command that starts services (`npm run dev`, `docker compose up`, `make dev`, …); or
   - **Running staging/remote:** the base URL of an already-running shared env to target instead.

   At least one must be reachable. If only a remote env is available, there's no `$DEV_UP` — point `.env` at it. **Confirm the base URL actually responds before continuing** (a quick reachability check, or have the user start `$DEV_UP`). If you can't get a reachable target, do not scaffold — report that setup is blocked on a target environment.
2. **Auth model** — dev auto-login (navigating to `/` lands you in), or a real login form? Where do test-user credentials live? Any HTTP basic-auth on remote/QA? (No auth at all is fine — wire the auth helper to a bare navigate.)
3. **e2e directory** — default `e2e/` at repo root. Confirm or override.
4. **Targets** — local only, remote/staging only, or both?
5. **Tooling (optional)** — issue tracker + code host CLIs present (for retrofit later)? Note but don't block; both are optional.

## Step 2 — Instantiate the harness

> **Precondition:** Step 1's gate is cleared — you have a concrete, reachable target environment. If not, go back; do not scaffold against an unknown app.

Path placeholders used below: **``** = this skill's folder, `.claude/skills/e2e-setup` (under whichever `.claude/skills/` dir it's installed in — resolve it before copying). **``** = the target repo root (`git rev-parse --show-toplevel`).

Copy the bundled scaffold into the project's e2e dir:
```sh
cp -R /harness /e2e        # e.g. cp -R .claude/skills/e2e-setup/harness ./e2e
```
This is a **template** — every file gets adapted or replaced (Steps 5–6). It is not run from the skill folder.

## Step 3 — Install the test runner

Prerequisite: Node ≥ 18 and npm on PATH (`node -v`). If the repo isn't a JS project, Node still has to be installed for the e2e suite to run.

```sh
cd /e2e
npm install
npx playwright install chromium        # browser binaries — without this nothing runs
```

## Step 4 — Register the Playwright MCP

The recon in Step 5 (and later in retrofit Phase 2) drives a browser via the **Playwright MCP** — the `mcp__playwright__browser_*` tools. It's a Claude Code MCP server, registered **once globally**, not shipped in the repo.

```sh
claude mcp add playwright -s user -- npx @playwright/mcp@latest
```
Equivalent `~/.claude.json` entry under top-level `mcpServers`:
```json
"playwright": { "type": "stdio", "command": "npx", "args": ["@playwright/mcp@latest"], "env": {} }
```
**Project-scoped alternative** (shareable via a committed `.mcp.json` at the *repo root* — not inside `e2e/`, where Claude Code won't read it):
```json
{ "mcpServers": { "playwright": { "type": "stdio", "command": "npx", "args": ["@playwright/mcp@latest"] } } }
```
Then:
- **Verify** — `/mcp` lists `playwright` connected and `mcp__playwright__browser_*` tools are available.
- **Gitignore the MCP output** — `.playwright-mcp/` (snapshots/console/network dumps; can contain real app data — keep it out of the repo) and `*/.playwright-cli/`. The bundled `harness/.gitignore` covers the e2e dir, but the MCP writes to the directory Claude Code runs from — usually the **repo root** — so add `.playwright-mcp/` to the repo-root `.gitignore` too (not conditional).

## Step 5 — Wire the harness to the real app (the actual work)

The harness ships generic `widget`/`collection` examples. Adapt the **infrastructure** edges to the real app — use MCP recon to discover the truth rather than guessing. Authenticate the recon browser first: it's a fresh session that does NOT inherit storageState, so `browser_navigate` to the base URL (dev auto-login) or drive the login form once.

Discover and wire:
- **`helpers/urls.ts`** — real `BASE_URL` / `API_BASE_URL` and the id-from-route shape.
- **`helpers/auth.ts`** — the real login flow + selectors (or confirm dev auto-login). Update `formLogin`/`devAutoLogin` (replace the placeholder `waitForURL('**/**')` with a real post-login assertion).
- **`helpers/api.ts`** — the real API login/token endpoint in `loginApi`, and replace the `widget`/`collection` seed helpers + response predicates with **one real entity** wired just enough to prove the client works (login → create → get → delete), **exercised by the smoke test below, not by a feature spec**. This proves the seeding infrastructure is sound; building feature coverage on top of it is `e2e-retrofit`'s job. (No API? Delete `api.ts` and drop its `.env` vars.)
- **`tests/smoke.spec.ts`** — wire the reachability check (the example pings `/api/health`) to a real endpoint for your API, and the page-loads test to a real page. **Keep smoke dead simple — it is a reachability gate, not a feature test.** Three cheap assertions are the target: the app loads, one real page renders (assert a stable heading/landmark), and the API responds. Prefer a **direct `page.goto('/some-page')`** over clicking through nav — and if any interaction turns flaky (consent/cookie overlays, ad scripts, async timing on a third-party or heavy app), **do not fight it**: drop to the simplest assertion that proves the page renders. Resist adding waits, retries, multi-step flows, or overlay-dismissal gymnastics here; that complexity belongs in real `e2e-retrofit` specs, not the smoke gate. This is the success gate in Step 7.
- **`helpers/db.ts.example`** — only if a test needs to read state the API doesn't expose: rename to `db.ts` and `npm i -D pg @types/pg` (or your driver). Otherwise leave it as `.example` (it isn't compiled) and drop the `DB_*` vars from `.env`.
- **`README.md`** — the copied harness `README.md` documents the demo `widget`/`collection` domain. Repoint it to the real app (target URL, auth + API model, layout) or delete it — don't ship a README describing entities the project no longer contains. (Step 6 treats `README.md` as the home for app prose, so leaving it stale contradicts that. `fixtures/README.md` is generic — leave it.)
- Follow `../e2e-retrofit/patterns.md` §2 (selectors) and §3–4 (assertions) while wiring, so the seed is canonical from day one.

**Delete every example feature spec** (`tests/widgets/`, `tests/collections/` — the CRUD + Manage demos) and any example helper you didn't wire. Setup writes **no** feature tests — `smoke.spec.ts` is the only spec left in the project. The harness's example specs remain in the **`e2e-setup` skill folder** (`harness/tests/`) as the pattern reference `e2e-retrofit`'s first batch mirrors; they are not shipped into the project.

## Step 6 — Scaffold the coverage matrix (gated choice)

Step 2 copied an example `COVERAGE.md` (the demo widgets/collections matrix). Its demo content must be **replaced**, but *how* is the **user's call** — this is a hard gate, ask before touching the file:

> **GATE — ask the user, don't assume:**
> **(a) Seed from observation** — I recon the app's nav and enumerate the real sections + workflows as matrix rows (unchecked), or
> **(b) Blank template** — I leave an empty matrix skeleton (headers, legend, env columns, one commented example row) for you to fill in yourself.

Then, whichever they pick:

- **Every row stays `[ ]` (not covered) in both modes.** Setup writes no feature tests, so nothing is covered yet — marking a row `[x]` would claim coverage that doesn't exist. The matrix is the *work-list*, not a result.
- **Match the env columns to the real targets.** The demo ships **Local + Remote** columns; a single-target app (remote-only or local-only) gets **one `Covered` column**. Don't carry a column for a target that doesn't exist — collapse the matrix (and the Coverage Summary) to the targets the suite actually runs against.
- **It's a tracker, not docs.** Keep the file to the matrix + legend. Don't add prose describing the app, its architecture, the environment ("static page, no API, no login"), or how a feature behaves — that belongs in the e2e `README.md` or a spec comment, not the coverage file.
- **Mode (a) — seeded from observation:** populate `### {Section}` blocks + workflow rows from recon, keeping the file's format (priority-tagged rows, env columns, legend, summary). **Recon tells you what the app *does*, not what *matters* or what's *correct*** — so treat the section taxonomy and especially the **priority labels as *proposed*, flagged for the user to confirm/correct**, not as authoritative fact. Don't manufacture a coverage-summary percentage.
- **Mode (b) — blank:** strip all demo rows; leave the headers, legend, env columns, and a single commented example row showing the row shape.

This file is the `$COVERAGE_MATRIX` — the work-list every later retrofit batch scopes from.

## Step 7 — Verify

Make sure a target environment is **running and reachable** first — either start local services (`$DEV_UP`) or point `.env` at an already-running staging/remote env. Confirm the base URL responds before running specs.

```sh
cd /e2e
cp .env.example .env        # then fill: E2E_TARGET, BASE_URL/API_BASE_URL, test user
# ensure the target env is up — `$DEV_UP` for local, or a reachable staging URL — then:
npx playwright test smoke.spec.ts     # app loads + login works + API reachable
```
Note: `smoke.spec.ts` runs in the authenticated project, so it exercises the **full auth path** (Step 5 wiring) before its assertions — it's a real end-to-end check, not a bare ping. A green smoke run **is the whole success gate** — it's the only spec setup writes. (There are no leftover example specs to ignore; Step 5 deleted them.) Always run from the e2e dir, never the repo root (`../e2e-retrofit/patterns.md` §11).

## Step 8 — Commit and hand off

- Commit the scaffold — **scope the commit to the e2e dir** (plus the repo-root `.gitignore` change from Step 4); no secrets (`.env`, `.auth/`, `.playwright-mcp/` stay gitignored). If the repo has **no initial commit yet, or unrelated staged/dirty changes**, do not sweep them into the setup commit: do a scoped `git add ` commit, or — when scoping is ambiguous — hand the diff to the user to commit rather than bundling. (Setup is the only step that commits a scaffold; branch/PR handling is `e2e-retrofit`'s job.)
- Report what was wired (entity, auth mode, targets), the smoke result, the coverage-matrix **mode chosen** (seeded vs blank), and — explicitly — that **no feature specs were written** (that's the next step's job).
- **Tell the user: setup is complete — from here use the `e2e-retrofit` skill** ("retrofit e2e tests for {ticket}"), which writes the actual feature coverage one section at a time. This skill should not be run again on this repo.

## Done-when checklist

- [ ] `e2e/` exists with `playwright.config.ts`; `npx playwright test --list` parses; `npx tsc --noEmit` is clean.
- [ ] Playwright MCP connected and verified.
- [ ] `helpers/` wired to the real app; if there's an API, one real entity seeds + reads + cleans up (proven via smoke, not a feature spec).
- [ ] **Exactly one spec exists — `smoke.spec.ts`.** All example feature specs (CRUD/Manage) deleted; no feature tests written.
- [ ] `README.md` repointed to the real app or deleted — no leftover `widget`/`collection` demo prose.
- [ ] `COVERAGE.md` scaffolded per the user's gated choice (seeded-from-recon or blank); **no rows marked `[x]`**; priorities/sections flagged as proposed if seeded.
- [ ] `smoke.spec.ts` green against the target env (local or staging/remote).
- [ ] `.env` populated (gitignored); `.playwright-mcp/` gitignored.
- [ ] Scaffold committed; user handed off to `e2e-retrofit`.

## Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

- **Author:** [cakehurstryan](https://github.com/cakehurstryan)
- **Source:** [cakehurstryan/QE-Skills](https://github.com/cakehurstryan/QE-Skills)
- **License:** MIT

Install and usage instructions live in the source repository linked above.

## Pricing

- **Free** — Free

## Security capabilities

Automated source analysis of v0.1.0 — what this tool can access:

- **Network access:** yes
- **Filesystem access:** no
- **Shell / process execution:** no
- **Environment & secrets:** yes
- **Dynamic code execution:** no

*"Yes" means the capability is present in the source — more access means more to trust, not that it is unsafe.*


## Versions

- **0.1.0** — security scan: passed — Imported from the upstream source.

## Links

- Listing page: https://agentstack.voostack.com/l/skill-cakehurstryan-qe-skills-e2e-setup
- Seller: https://agentstack.voostack.com/s/cakehurstryan
- Browse the marketplace: https://agentstack.voostack.com/browse

---
Listed on AgentStack — the marketplace for AI agent skills and MCP servers. Every listing is security-reviewed. Creators keep 70%.
