@asrunjs/asrun 0.5.2 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,175 @@
1
+ # Command reference
2
+
3
+ Every command runs in a workspace (the folder that owns `profiles.yaml`); see
4
+ [install.md](./install.md#which-workspace-a-command-runs-against). In a project the scripts of `package.json` are
5
+ thin wrappers, so `pnpm asrun`, `pnpm signin` and the rest work as below. The same commands through the package are
6
+ `pnpm exec asrun ...`.
7
+
8
+ | I want to... | Command |
9
+ |---|---|
10
+ | Install / update the framework | `pnpm dlx @asrunjs/asrun init --install`, `pnpm add -D @asrunjs/asrun@latest` |
11
+ | Prepare or check the workspace | `pnpm asrun init [--update]`, `pnpm asrun doctor` |
12
+ | Start the web UI | `pnpm go`, `pnpm ui`, `pnpm ui:dev` |
13
+ | See which profiles are logged in | `pnpm status [--env <env>]` |
14
+ | Log in a profile (a person, with 2FA) | `pnpm signin <profile-id>` |
15
+ | Run tests | `pnpm asrun --env <env> [filters]` |
16
+ | Open the last report | `pnpm report` |
17
+ | Look around a page as a profile | `pnpm explore <profile-id> [path]` |
18
+ | Read what the last report says about a case | `pnpm heal:evidence --case <case-id> --env <env>` |
19
+ | List or forget test data that could not be removed | `pnpm cleanup --env <env> [--forget]` |
20
+ | Create or validate a case | `pnpm case:new <id>`, `pnpm case:check [case-id]` |
21
+ | Check a spec | `pnpm spec:lint [case-id]`, `pnpm baselines:review [case-id] [--new]` |
22
+ | Look at a PDF | `pnpm pdf:render <file.pdf> --out <dir> [--pages all] [--corner top-left]` |
23
+ | Type-check the workspace | `pnpm typecheck` |
24
+
25
+ ## Running tests: `pnpm asrun`
26
+
27
+ ```bash
28
+ pnpm asrun --env staging
29
+ ```
30
+
31
+ `--env` is **required**; there is no default. A run selects the profiles whose `env` matches, checks their sessions,
32
+ runs the selected cases once per profile, and writes one report folder per run.
33
+
34
+ ```bash
35
+ pnpm asrun --env production --profile example-production
36
+ pnpm asrun --env production --skip-profile example-staging,example-production
37
+ pnpm asrun --env staging --tag us
38
+ pnpm asrun --env production --logged-in
39
+ pnpm asrun --env production --suite smoke
40
+ pnpm asrun --env production --suite smoke,regression
41
+ pnpm asrun --env production --grep landing
42
+ pnpm asrun --env production --read-only
43
+ pnpm asrun --env staging --ui
44
+ ```
45
+
46
+ | Flag | Effect |
47
+ |---|---|
48
+ | `--env <env>` | Required. The environment to run. |
49
+ | `--profile a,b` | Only these profiles. |
50
+ | `--skip-profile a,b` | Every profile except these (for example the ones that are not logged in). |
51
+ | `--tag <tag>` | Only the profiles that carry the tag (comma-separated for several). |
52
+ | `--logged-in` | Only the profiles whose saved session works; the others are left out instead of failing. |
53
+ | `--suite <name>` | Only the cases whose front matter lists the suite (comma-separated; a case in two chosen suites runs once). `--priority` is the old name. |
54
+ | `--grep <text>` | Only the cases whose title matches (Playwright `--grep`). |
55
+ | `--read-only` | Leave out every case that writes data. |
56
+ | `--allow-writes=<env>` | Run the write cases in that environment without the prompt. For unattended runs only; AI agents never use it. |
57
+ | `--step-text` | Overlay the current test step on the videos (`=1\|true\|yes\|on`; off by default). |
58
+ | `--update-baselines <case,case>` | Overwrite the screenshot baselines of exactly these cases. See below. |
59
+ | `--report-id <id>` | Name of the report folder (default: the timestamp). |
60
+ | `--ui`, other Playwright options | Passed through to Playwright. `--ui` opens its interactive mode. |
61
+
62
+ A case that writes data runs only in the environments it lists under `writes`; elsewhere it is skipped with a notice.
63
+ In **staging** no confirmation is needed. In any other environment a person confirms every run: `pnpm asrun` lists
64
+ the write cases and asks you to type the environment name; the web UI asks in a dialog. Unattended runs combine the
65
+ prompt with `--allow-writes=<env>`; AI agents must not. `--read-only` leaves the cases out instead.
66
+
67
+ ## Reports and evidence
68
+
69
+ Every run writes to `reports/<run-id>/`, where the run id is the timestamp and the environment
70
+ (`reports/2026-10-28T07-30-22-staging`), and points `reports/latest` at the newest one.
71
+
72
+ - **Video:** one per test, with the recordings of every browser tab joined in order into `reports/<run>/videos/`.
73
+ Joining needs ffmpeg; without it the report lists the recordings separately. `--step-text` writes the current step
74
+ on the video.
75
+ - **Trace:** the page before and after every action, plus every network request. Open a trace directly with
76
+ `pnpm exec playwright show-trace <trace.zip>`.
77
+ - **Screenshots:** on failure, and the baselines of visual checks with their diff.
78
+ - **Leftover test data:** listed in red in the report and in the terminal.
79
+
80
+ `pnpm report` opens the HTML report of the latest run. `pnpm asrun` also prints the paths of the videos and traces
81
+ when it finishes. Recordings contain real pages: do not share them outside the team.
82
+
83
+ ## Baselines
84
+
85
+ Baselines are per profile (`...-example-staging-darwin.png`) and are the source of truth for pixel checks.
86
+
87
+ Fix the spec first (the check must look at the element its target card names), then:
88
+
89
+ ```bash
90
+ pnpm asrun --env staging --profile example-staging --grep header-logo --update-baselines header-logo
91
+ pnpm baselines:review header-logo
92
+ ```
93
+
94
+ - `--update-baselines` lists the cases whose images are overwritten; they must be among the cases the filters run.
95
+ Playwright has one global switch, so those cases run as a second pass with their own report
96
+ (`<run-id>-baselines`, for example `2026-10-28T07-30-22-staging-baselines`); every other case is compared with its
97
+ baselines as usual.
98
+ - Without it, Playwright's own `--update-snapshots` still rewrites every image of the run.
99
+ - In the web UI every case in **Cases to run** has an **Update baselines** box (unticked by default), with a
100
+ confirmation, and the new images are shown next to their target cards when the run ends.
101
+ - An agent never updates a baseline: that is a person's decision.
102
+
103
+ ## Login and sessions
104
+
105
+ ```bash
106
+ pnpm status # every profile, or: pnpm status --env staging
107
+ pnpm signin example-staging
108
+ ```
109
+
110
+ The login is done by a person, including 2FA; the session is saved to `.auth/<profile-id>.json` and verified. See
111
+ [profiles.md](./profiles.md).
112
+
113
+ ## Cases and checks
114
+
115
+ ```bash
116
+ pnpm case:new header-logo
117
+ pnpm case:new documents/header-logo
118
+ pnpm case:check
119
+ pnpm spec:lint header-logo
120
+ pnpm baselines:review --new
121
+ pnpm pdf:render report.pdf --out .explore/targets/header-logo --corner top-left
122
+ ```
123
+
124
+ - `case:new` creates the case from the template. One optional group folder, created on demand.
125
+ - `case:check` validates front matter, values and target cards; see [cases.md](./cases.md).
126
+ - `spec:lint` is a static check of the specs: it fails on sleeps, `networkidle`, image checks without a target,
127
+ `.first()` on images, raw download events, a magic-number-only PDF check. `pnpm spec:lint <case-id>` limits it.
128
+ - `baselines:review` opens nothing; it lists every baseline next to the target card that says what it must show.
129
+ `--new` lists only the images that are new or changed (by git).
130
+ - `pdf:render` writes page images and prints the text, so a person (or an exploring agent) can look at a PDF.
131
+
132
+ ## Exploring and healing evidence
133
+
134
+ ```bash
135
+ pnpm explore example-staging /app/home
136
+ pnpm heal:evidence --case header-logo --env staging
137
+ ```
138
+
139
+ `explore` opens a logged-in browser at the profile's `startPath` (or the path you pass, or the site root). Downloads
140
+ are saved to `~/Downloads` and never overwrite an existing file. `heal:evidence` prints the last run's status, failed
141
+ step, error, call log and screenshots for a case, and whether the report is stale; it is what `/heal-spec` reads
142
+ first.
143
+
144
+ ## Cleanup
145
+
146
+ ```bash
147
+ pnpm cleanup --env staging
148
+ pnpm cleanup --env staging --forget
149
+ ```
150
+
151
+ A write case registers everything it creates (named `[asrun] ...`) and removes it after the test, also after a
152
+ failure. Whatever could not be removed is listed in the report and by `cleanup`. Delete it by hand in the product,
153
+ then run `--forget` so it is not listed again. `--env` is required.
154
+
155
+ ## Web UI
156
+
157
+ ```bash
158
+ pnpm go # install + build + start + open the browser (the usual command)
159
+ pnpm ui # build the web app and serve http://127.0.0.1:4173
160
+ pnpm ui:dev # Vite on :5173 with hot reload, API in watch mode
161
+ ```
162
+
163
+ The UI only listens on `127.0.0.1`. Set `ASRUN_UI_PORT` to change the port (`ASRUN_UI_WEB_PORT` for the Vite dev
164
+ server). See [web-ui.md](./web-ui.md).
165
+
166
+ ## Workspace
167
+
168
+ ```bash
169
+ pnpm asrun init [--update] [--force <file>] [--reset-rules] [--root <dir>] [--no-env] [--install [spec]]
170
+ pnpm asrun doctor
171
+ ```
172
+
173
+ `init` writes the workspace files; `--update` refreshes only the generated ones. `doctor` checks profiles,
174
+ `tsconfig.json`, `playwright.config.ts`, the generated files, the installed version, the project rule table and the
175
+ skill overrides. Full details in [install.md](./install.md).
@@ -0,0 +1,174 @@
1
+ # Installing AsRun
2
+
3
+ AsRun is a normal devDependency. A **workspace** is the folder that owns `profiles.yaml`, `cases/` and `tests/`;
4
+ everything AsRun needs at run time is read through it. The framework itself lives in `node_modules/@asrunjs/asrun`
5
+ and is never edited: a project commits only its own test content.
6
+
7
+ ## Requirements
8
+
9
+ - **Node.js 22 or newer** and **pnpm**.
10
+ - **ffmpeg** joins the recordings of one test (a new browser tab has its own video) into one video for the report.
11
+ `pnpm go` installs it with Homebrew on macOS or winget on Windows; on Linux run `sudo apt install ffmpeg`. When it
12
+ is missing, the optional `ffmpeg-static` package is used instead; if neither works, the report lists the videos
13
+ separately. Not fatal.
14
+ - **Claude Code or OpenCode** for the AI steps (make cases, explore, generate, heal). Everything else works without
15
+ them; `pnpm go` warns when neither command is found.
16
+
17
+ ## Install in a project
18
+
19
+ ```bash
20
+ pnpm dlx @asrunjs/asrun init --install
21
+ pnpm go
22
+ ```
23
+
24
+ `init --install` scaffolds the workspace and then adds the dependency. Running `init` first matters on pnpm 12: it
25
+ writes `pnpm-workspace.yaml` with the `allowBuilds` entry (`esbuild` through `tsx`, `ffmpeg-static`), and without it
26
+ `pnpm add` exits with `ERR_PNPM_IGNORED_BUILDS`. An existing `pnpm-workspace.yaml` that lacks the entry gets a note
27
+ instead: add the block by hand (see "Manual install").
28
+
29
+ `pnpm go` then does the rest and is the command to remember:
30
+
31
+ 1. checks Node (22+) and ffmpeg, and warns when no AI CLI is installed;
32
+ 2. installs the dependencies and the Chromium browser (`pnpm install` + `playwright install chromium`);
33
+ 3. runs `asrun init` (workspace files and `.env`) and `asrun doctor`;
34
+ 4. starts the web UI at http://127.0.0.1:4173 and opens it.
35
+
36
+ Run it again on any day: the install step then takes seconds, and if the UI is already running it only opens it.
37
+ `pnpm go --no-open` does not open a browser. Set `ASRUN_UI_PORT` to change the port.
38
+
39
+ The UI is optional: all commands in [commands.md](./commands.md) work without it.
40
+
41
+ ### Manual install
42
+
43
+ Same result, step by step:
44
+
45
+ ```bash
46
+ pnpm add -D @asrunjs/asrun
47
+ pnpm asrun init
48
+ pnpm go
49
+ ```
50
+
51
+ If pnpm refuses the build scripts of `esbuild` / `ffmpeg-static`, add this to `pnpm-workspace.yaml` and run the
52
+ install again:
53
+
54
+ ```yaml
55
+ allowBuilds:
56
+ esbuild: true
57
+ ffmpeg-static: true
58
+ ```
59
+
60
+ To pin a version:
61
+
62
+ ```bash
63
+ pnpm add -D @asrunjs/asrun@<version>
64
+ ```
65
+
66
+ `pnpm asrun init` is safe to run many times: it never overwrites a file the project owns.
67
+
68
+ ## What `init` writes
69
+
70
+ | Kind | Files | On `--update` |
71
+ |---|---|---|
72
+ | Generated | `playwright.config.ts`, `tests/seed.spec.ts`, `.claude/commands/*`, `.opencode/commands/*` | rewritten |
73
+ | Once | `AGENTS.md`, `CLAUDE.md`, `.mcp.json`, `opencode.jsonc`, `.claude/settings.json`, `tsconfig.json`, `pnpm-workspace.yaml`, `profiles.yaml`, `cases/_template.md`, `package.json` (scripts and devDependencies merged in) | kept |
74
+
75
+ It also writes the asrun block in `.gitignore`, the `ASRUN_ROOT` line in `.env`, and `.asrun.json` (the marker with
76
+ the framework version and the generated file list).
77
+
78
+ Flags:
79
+
80
+ | Flag | Meaning |
81
+ |---|---|
82
+ | `--root <dir>` | initialize that folder instead of the current one |
83
+ | `--update` | refresh only the generated files |
84
+ | `--force <path>` | overwrite one file init owns (repeatable) |
85
+ | `--reset-rules` | allow `--force` on `AGENTS.md`, `CLAUDE.md` or `.claude/skills/**` |
86
+ | `--no-env` | do not write `.env` |
87
+ | `--install [spec]` | also add the dependency (default `@asrunjs/asrun`) |
88
+
89
+ `AGENTS.md`, `CLAUDE.md` and `.claude/skills/**` belong to the project. `init --force` refuses them without
90
+ `--reset-rules`, so a framework update can never replace a project's rules.
91
+
92
+ ## Update
93
+
94
+ ```bash
95
+ pnpm add -D @asrunjs/asrun@latest
96
+ pnpm asrun init --update
97
+ pnpm asrun doctor
98
+ ```
99
+
100
+ `init --update` rewrites only the generated files and leaves project rules, case files, values and baselines alone.
101
+ `doctor` tells you when the workspace is behind the installed version.
102
+
103
+ There is no folder to move: the framework lives in `node_modules`, which the package manager owns. To change the
104
+ framework itself, `pnpm patch @asrunjs/asrun` (a committed patch).
105
+
106
+ ## `pnpm asrun doctor`
107
+
108
+ Run it after changing profiles, rules or overrides, and after an update. It checks:
109
+
110
+ - every entry in `profiles.yaml` (ids, environments, URLs, session check settings);
111
+ - `tsconfig.json`, `playwright.config.ts` (its re-export of the framework config) and the generated files;
112
+ - that `.gitignore` ignores `.auth/` and `reports/`;
113
+ - whether the installed version is behind the package's templates, and whether `dist/` is out of date;
114
+ - the project rule table in `AGENTS.md` §2 (unknown ids, a missing `Effect`, a non-negotiable rule);
115
+ - the framework skills and the project overrides under `.claude/skills/<skill>.md`, and lists the ones it sees.
116
+
117
+ ## Which workspace a command runs against
118
+
119
+ The order is:
120
+
121
+ 1. a real environment variable `ASRUN_ROOT` (it wins over everything);
122
+ 2. `ASRUN_ROOT` in the `.env` next to the command, or in the framework package's own `.env` (relative to that file);
123
+ 3. otherwise the nearest folder at or above the working directory that has `profiles.yaml` or `.asrun.json`;
124
+ 4. in the framework repository, the framework's own `.env` points at `examples/demo`.
125
+
126
+ A real environment variable always wins, so one checkout can point at another project for a single command:
127
+
128
+ ```bash
129
+ ASRUN_ROOT=../my-real-project pnpm asrun --env staging --read-only
130
+ ```
131
+
132
+ ## Workspace layout
133
+
134
+ ```
135
+ profiles.yaml one entry per environment: URL, account label, tags, environment (no expected values)
136
+ cases/ human-written cases (+ _template.md); optional group folders: cases/<group>/<name>.md
137
+ tests/ generated specs (same folders as cases/) and *-snapshots/ baselines per profile
138
+ playwright.config.ts re-exports node_modules/@asrunjs/asrun/dist/playwright.config.js
139
+ tsconfig.json plain compiler options (specs import the framework by package name)
140
+ AGENTS.md project rules: they point at the framework baseline; §2 says what differs
141
+ CLAUDE.md imports AGENTS.md for Claude Code
142
+ .claude/commands/ /make-case, /explore-case, /gen-spec, /heal-spec (thin shells that load the framework skill)
143
+ .claude/skills/ optional project overrides of a skill: <skill>.md
144
+ .opencode/commands/ the same commands for OpenCode (it does not read .claude/commands)
145
+ .mcp.json the playwright-test MCP server for Claude Code
146
+ opencode.jsonc the same for OpenCode, plus an optional model
147
+ .asrun.json marker: framework version and the generated file list
148
+ package.json the workspace scripts (`asrun`, `signin`, ...) and devDependencies
149
+ .env ASRUN_ROOT (which workspace the commands use) and local settings
150
+ node_modules/@asrunjs/asrun/ the framework (installed dependency; never edited)
151
+ .auth/ saved logins, one per profile
152
+ reports/ one folder per run; reports/latest points at the newest
153
+ .tmp/ .explore/ job logs, hints, reference screenshots (local state)
154
+ ```
155
+
156
+ The framework repository's own layout is in [agents/framework-dev.md](./agents/framework-dev.md).
157
+
158
+ ## What to commit
159
+
160
+ Commit `profiles.yaml`, `cases/`, `tests/` (with `*-snapshots/`), `AGENTS.md`, `CLAUDE.md`, `.claude/`,
161
+ `.opencode/commands/`, `.mcp.json`, `opencode.jsonc`, `.asrun.json`, the generated files, `package.json`, `tsconfig.json`
162
+ and `pnpm-workspace.yaml`.
163
+
164
+ Never commit or share `.auth/` (one saved login per profile, as sensitive as a password), `reports/`, `.tmp/`,
165
+ `.explore/`, `.env` or `node_modules/`. The asrun block of `.gitignore` covers them.
166
+
167
+ ## Uninstall
168
+
169
+ ```bash
170
+ pnpm remove @asrunjs/asrun
171
+ ```
172
+
173
+ Then delete the workspace files you no longer want (`profiles.yaml`, `cases/`, `tests/`, the generated commands and
174
+ configs, `.asrun.json`). They are ordinary files: keep them if another tool still uses them.
@@ -0,0 +1,139 @@
1
+ # Profiles and environments
2
+
3
+ A **profile** is one environment of the system under test: a URL, an account label, optional tags and the environment
4
+ name. Each entry in `profiles.yaml` becomes one Playwright project named after its `id`, and every spec runs once per
5
+ profile. Two profiles of the same site give you two independent views, logins and baselines.
6
+
7
+ ```yaml
8
+ profiles:
9
+ - id: example-staging
10
+ name: Example (staging)
11
+ env: staging
12
+ baseURL: https://staging.example.com
13
+ account: qa@example.com
14
+ tags: [core, us]
15
+ email: false
16
+ startPath: /app/home
17
+ sessionCheckPath: /settings
18
+ sessionCheck:
19
+ loggedOutUrl: /login
20
+ loggedInSelector: "input[name='username']"
21
+ timeoutMs: 5000
22
+ ```
23
+
24
+ ## Fields
25
+
26
+ | Field | Meaning |
27
+ |---|---|
28
+ | `id` | Lowercase words with single dashes. It names the Playwright project, the saved session (`.auth/<id>.json`) and the baseline files. |
29
+ | `name` | What people see in the UI and the reports. |
30
+ | `env` | The environment this profile points at (`production`, `staging`, ...). `--env <env>` selects the profiles of one environment. Any environment except `staging` protects writing: a case that writes there needs a person's confirmation on every run. |
31
+ | `baseURL` | The site address, without a path. Must be a full `http(s)` URL. |
32
+ | `account` | A label for humans (a mailbox or username). Never a password: logins live in `.auth/`. |
33
+ | `tags` | Optional lowercase words, for grouping and `pnpm asrun --tag <tag>`. Duplicates are refused. |
34
+ | `email` | `true` only when a real mailbox is connected, so tests may send email. Defaults to `false`; `true` is refused for `production`. |
35
+ | `startPath` | The page "Visit" opens after a login, for example `/app/home`. Empty (the default) opens the site root. A path on the site: one leading `/`, no spaces, never another host. |
36
+ | `sessionCheckPath` | A page a signed-in visitor sees (default `/account/`). "Verify" and the pre-run check use it to tell whether a saved login still works. |
37
+ | `sessionCheck` | How that page shows a missing login. See below. |
38
+
39
+ Expected values are **not** here. They live in each case's `## Values` block: `profiles.yaml` holds connection
40
+ settings only, so adding a profile never touches a case and two profiles can never fight over one case's values.
41
+
42
+ ## There is no default environment
43
+
44
+ `pnpm asrun` and every AI command require `--env <env>`; the Run tests page requires one radio button. A run selects
45
+ the profiles whose `env` matches.
46
+
47
+ `pnpm signin <profile-id>` and `pnpm explore <profile-id>` take no `--env`: the profile already names its environment.
48
+
49
+ ## Logins
50
+
51
+ Sessions are saved per profile in `.auth/<profile-id>.json`, one file per profile, and are as sensitive as passwords.
52
+
53
+ ```bash
54
+ pnpm signin example-staging
55
+ ```
56
+
57
+ A normal browser opens with a small card in its bottom-left corner. Log in yourself, including 2FA, wait for the
58
+ logged-in page, then press **Done, save the session** on the card (or press Enter in the terminal, which does the
59
+ same). The session is saved and verified. Nothing about 2FA is automated.
60
+
61
+ `pnpm status` shows the login state of every profile; `pnpm status --env staging` only of one environment.
62
+
63
+ Before every run, `pnpm asrun` checks the session of each profile in the chosen environment. A profile without a
64
+ saved session, or with an expired one, fails right away (no retries) and the run prints what to do:
65
+
66
+ ```
67
+ Not logged in (tests for these profiles will FAIL):
68
+ example-production expired -> pnpm signin example-production
69
+ Or skip them for this run: pnpm asrun --env production --skip-profile example-production
70
+ ```
71
+
72
+ Either log in or leave the profile out (`--skip-profile`, or `--logged-in` to keep only the working ones). If a
73
+ session expires in the middle of a run, the failed test is marked "session expired".
74
+
75
+ Login itself is not a test case. Never write a spec for the login page or the login flow.
76
+
77
+ ### Making the session check fit your site
78
+
79
+ The defaults fit a site that redirects a signed-out visitor to `/login`. Sites differ: a single-page app redirects
80
+ after it has started, some answer `401` or `403`, some show the login form at the same address. `sessionCheck` names
81
+ what your site does:
82
+
83
+ | Key | What it says |
84
+ |---|---|
85
+ | `loggedOutUrl` | Where a signed-out visitor ends up (default `/login`): a path on the site or a full URL for single sign-on. A `401`/`403` response counts too. |
86
+ | `loggedInSelector` | A selector that only exists while signed in, for example `"input[name='username']"`. |
87
+ | `loggedOutSelector` | A selector that proves the visitor is signed out, for example the password field of a login form shown at the same address. |
88
+ | `timeoutMs` | How long to wait for one of those signals (default 3000, 500–60000). |
89
+
90
+ Without a `loggedInSelector`, "not sent to the login page" counts as signed in, which can mislead on a slow
91
+ single-page app.
92
+
93
+ ## Visit and explore
94
+
95
+ **Visit** (Profiles page, or `pnpm explore <profile-id> [path]`) opens a logged-in browser window at the profile's
96
+ `startPath`, or at the site root when it is empty. Files downloaded in that window go to `~/Downloads` (an existing
97
+ file is never overwritten; a ` (1)` is added) and the saved path is printed in the terminal or the job log.
98
+
99
+ Use it when you need to see what a logged-in person sees, without writing anything. The exploring agent uses the same
100
+ mechanism, with the Playwright MCP server and the profile's project.
101
+
102
+ ## Adding a profile
103
+
104
+ 1. Add an entry to `profiles.yaml` (or use **Add a new profile** on the Profiles page, which appends it for you).
105
+ 2. `pnpm signin <id>` and log in.
106
+ 3. Add the values for that profile to the cases it runs: a column in each `## Values` block, or nothing when
107
+ `default` already fits.
108
+
109
+ Deleting a profile removes its entry and its saved login; screenshot baselines and old reports keep the id in their
110
+ file names and stay on disk. Duplicating one copies the settings under a new id and does **not** copy the saved login.
111
+
112
+ ## How a profile becomes a test
113
+
114
+ The profile id is the Playwright project name. The `profile` fixture exposes the running profile to a spec:
115
+
116
+ ```ts
117
+ // case: cases/example.md
118
+ import {caseTags} from '@asrunjs/asrun/cases';
119
+ import {configureCase, expect, test} from '@asrunjs/asrun/fixtures';
120
+ import {requireValues} from '@asrunjs/asrun/values';
121
+
122
+ test.describe('Example', {tag: caseTags('example')}, () => {
123
+ configureCase('example');
124
+ test('one outcome', async ({page, profile}) => {
125
+ // profile.id, profile.env, profile.baseURL, profile.account, profile.tags, profile.email
126
+ const expected = requireValues<{homeTitle: string}>('example', profile);
127
+ await test.step('1. Open the home page', async () => {
128
+ await page.goto('/');
129
+ await expect(page).toHaveTitle(expected.homeTitle);
130
+ });
131
+ });
132
+ });
133
+ ```
134
+
135
+ Expected values come from the case document through `requireValues(caseId, profile)`: its `## Values` block with
136
+ `default` deep-merged under the profile's own entry. A case with no values for the running profile, or with empty or
137
+ `TODO` values, fails with a plain-language message instead of a crash.
138
+
139
+ See [cases.md](./cases.md) for the document format and [commands.md](./commands.md) for the run filters.
@@ -0,0 +1,101 @@
1
+ # Troubleshooting
2
+
3
+ ## Login
4
+
5
+ **`Profile "x" is not logged in`** — run `pnpm signin <profile-id>` and log in. To leave that profile out instead:
6
+ `pnpm asrun --env <env> --skip-profile <profile-id>`, or `--logged-in` to keep only the working ones. Login itself is
7
+ never a test; a missing or expired session fails right away, without retries.
8
+
9
+ **A saved session expires in the middle of a run** — the failed test is marked "session expired". Log in again and
10
+ rerun.
11
+
12
+ **`pnpm signin` opened a browser but nothing was saved** — the session is saved when you press **Done, save the
13
+ session** on the card in the bottom-left corner of that window (or press Enter in the terminal). Nothing about 2FA is
14
+ automated.
15
+
16
+ ## Running
17
+
18
+ **`Choose the environment to test`** — there is no default. Add `--env production` or `--env staging`; the Run tests
19
+ page needs one radio button.
20
+
21
+ **A case is skipped, not failed** — a case that writes data runs only in the environments under `writes`; a case with
22
+ `envs` runs only there. The run prints "skipped" with the reason.
23
+
24
+ **`This case writes data and has no permission to run in PRODUCTION`** — a person confirms production writes on every
25
+ run (type the environment name, or use the dialog in the web UI). `--read-only` leaves the write cases out. Unattended
26
+ runs use `--allow-writes=<env>`; AI agents must not.
27
+
28
+ **`--update-baselines` was refused** — the named cases must be among the cases the filters run, and cannot be combined
29
+ with Playwright's own `--update-snapshots`.
30
+
31
+ **The report is empty or missing** — run `pnpm asrun --env <env>` first. `pnpm report` opens `reports/latest`.
32
+
33
+ **Screenshot mismatch** — open the report: the diff view shows expected, actual and diff. If the change is intended,
34
+ update the baseline on purpose ([commands.md](./commands.md#baselines)); if not, fix the spec or the product. Never let
35
+ an agent overwrite a baseline to hide a difference.
36
+
37
+ **Videos are separate per tab, or there is no merged video** — ffmpeg is missing. `pnpm go` tries to install it
38
+ (Homebrew, winget); on Linux run `sudo apt install ffmpeg`. Without it, the optional `ffmpeg-static` package is used;
39
+ if neither works, the report lists the recordings separately.
40
+
41
+ **Downloads did not arrive** — files downloaded in a **Visit** window (the UI) or with `pnpm explore` are saved to
42
+ `~/Downloads` and never overwrite an existing file; the saved path is printed in the job log or terminal.
43
+
44
+ **The UI port is busy** — set `ASRUN_UI_PORT` (and `ASRUN_UI_WEB_PORT` for `pnpm ui:dev`).
45
+
46
+ ## Cases and specs
47
+
48
+ **`Missing values: cases/<id>.md has no "## Values" for profile ...`** — add the profile's values (or a `default`
49
+ entry) to the case's `## Values` block. See [cases.md](./cases.md#values).
50
+
51
+ **`Incomplete values: ... still has empty or TODO values`** — fill in the named keys, or answer the `TODO` in the case
52
+ and regenerate the spec.
53
+
54
+ **`pnpm case:check` fails on a target card** — the card is missing a required field (`What`, `Where`,
55
+ `Identified by`), refers to a reference image outside `.explore/targets/<case-id>/`, or a check refers to a `[T<n>]`
56
+ that has no card. A blocking question (`🚫 TODO [blocking]`) is the normal way to wait for a person's answer.
57
+
58
+ **`pnpm spec:lint` fails** — read the rule name in the message: `no-sleep` (`waitForTimeout`), `no-networkidle`,
59
+ download handling, a screenshot or image check without `// target: T<n>`, a PDF check on a magic number alone. The
60
+ fixes are in the framework's `generating-specs` conventions; do not silence the rule.
61
+
62
+ **A case step marked `[input]` or `[writes]` stops `/gen-spec` with `needs-input`** — that step has no
63
+ `- Recipe:` line. Run `/explore-case <case-id> <env> --attended` and do the step in the browser once, so the recipe
64
+ is recorded.
65
+
66
+ **`pnpm case:check` warns that a reference image is not on this machine** — `.explore/` is git-ignored: a fresh
67
+ checkout has the card but not the screenshot. Run the exploration again on this machine (or look at the card and the
68
+ report's baseline).
69
+
70
+ ## AI steps
71
+
72
+ **An AI command says the `playwright-test` MCP server is missing** — run `pnpm asrun doctor`. OpenCode reads only
73
+ `opencode.jsonc`; Claude Code reads only `.mcp.json`. `pnpm asrun init` writes both. Check with `opencode mcp list` /
74
+ `claude mcp list`; `failed: ... path specified` means the `playwright` path in that file does not resolve (repair it
75
+ with `pnpm asrun init --force <file>`).
76
+
77
+ **An AI job runs but uses a slow or limited model** — the top bar's **Model** picker sets the model for UI runs;
78
+ without one, a one-shot OpenCode run uses its built-in free model, which has a daily usage limit. Set `"model"` in
79
+ `opencode.jsonc` to stop depending on the browser's choice. For Claude Code, the picker lists what its settings,
80
+ environment, aliases or gateway expose.
81
+
82
+ **Thinking does nothing** — it is an OpenCode flag. Claude Code has no such flag; its model and settings decide.
83
+
84
+ **A project rule does nothing** — `AGENTS.md` §2 needs the rule id and an `Effect` of `narrows` or `replaces`.
85
+ `F1`..`F6` cannot be changed. `pnpm asrun doctor` names a wrong id, a missing `Effect` or a non-negotiable rule.
86
+
87
+ **A skill override does nothing** — it must be `.claude/skills/<skill>.md` (the skill name, for example
88
+ `generating-specs`, not the command name), and it is not discovered as a standalone skill: the command and the skill
89
+ read it. `doctor` lists the overrides it sees.
90
+
91
+ **`pnpm asrun init --force AGENTS.md` is refused** — `AGENTS.md`, `CLAUDE.md` and `.claude/skills/**` belong to the
92
+ project. Add `--reset-rules` to replace them with the template, and expect to lose the project's rules.
93
+
94
+ **`ERR_PNPM_IGNORED_BUILDS` while installing** — pnpm refused the build scripts of `esbuild` / `ffmpeg-static`. Run
95
+ `pnpm dlx @asrunjs/asrun init` first (it writes the `allowBuilds` entry), or add it to `pnpm-workspace.yaml` by hand;
96
+ see [install.md](./install.md#manual-install).
97
+
98
+ **The AI did something you did not expect** — exploring and generating only read pages and edit the case file; healing
99
+ edits the spec and runs it under the same write gate as any run (staging writes are allowed, any other environment
100
+ needs a person's confirmation). None of them writes product data on its own, and none updates a baseline. If a step
101
+ needs a person to write, run it with `--attended`; that is where a person acts, in the browser.