ccqa 1.9.0 → 1.10.1
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.
- package/README.md +226 -68
- package/dist/bin/ccqa.mjs +6231 -5023
- package/dist/hub-client/index.d.mts +123 -40
- package/dist/hub-client/index.mjs +3 -0
- package/dist/package.json +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,19 +2,82 @@
|
|
|
2
2
|
|
|
3
3
|
**Your Claude subscription already includes a QA engineer.**
|
|
4
4
|
|
|
5
|
-
ccqa turns Claude Code into a browser test recorder and runner
|
|
5
|
+
ccqa turns Claude Code into a browser test recorder and runner. You write a
|
|
6
|
+
test spec in YAML; Claude drives a real browser **once** to discover the
|
|
7
|
+
route; ccqa compiles that recording into ordinary test code your CI replays
|
|
8
|
+
with no model in the loop.
|
|
9
|
+
|
|
10
|
+
Recording is where the subscription pays off — `claude` on your machine is
|
|
11
|
+
enough, no extra API key. CI is where it stops needing one: a recorded spec
|
|
12
|
+
replays as plain test code. Only the optional Claude-driven parts —
|
|
13
|
+
[failure analysis](#failure-analysis-and-drift), [drift](#failure-analysis-and-drift),
|
|
14
|
+
[change selection](#wire-it-into-ci), and `mode: live` specs — need a
|
|
15
|
+
credential in CI.
|
|
6
16
|
|
|
7
|
-
|
|
8
|
-
2. Claude drives a real browser **once** to discover the route
|
|
9
|
-
(`ccqa record`).
|
|
10
|
-
3. ccqa compiles the recording into runnable test code for your `target:`
|
|
11
|
-
— vitest replay, plain Playwright, or a runn runbook.
|
|
12
|
-
4. `ccqa run` replays everything into one report you can push to a
|
|
13
|
-
shared hub.
|
|
17
|
+
[日本語版 README](./docs/README.ja.md)
|
|
14
18
|
|
|
15
|
-
|
|
19
|
+
## Install
|
|
16
20
|
|
|
17
|
-
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add -D ccqa vitest agent-browser
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Requires Node.js **20+**.
|
|
26
|
+
[agent-browser](https://github.com/vercel-labs/agent-browser) and
|
|
27
|
+
[vitest](https://vitest.dev) are peer dependencies of the **default
|
|
28
|
+
agent-browser target** — they run its recorded tests. A project that only uses
|
|
29
|
+
an external target (`playwright`, `runn`) needs just `ccqa` plus that tool
|
|
30
|
+
(e.g. `pnpm add -D ccqa @playwright/test`); ccqa executes it through the
|
|
31
|
+
target's `runCommand`.
|
|
32
|
+
|
|
33
|
+
## Quick start
|
|
34
|
+
|
|
35
|
+
**1. Write a spec** — by hand, or interactively with
|
|
36
|
+
[`ccqa draft`](./docs/draft.md). (`ccqa init` scaffolds the `.ccqa/`
|
|
37
|
+
skeleton.)
|
|
38
|
+
|
|
39
|
+
```yaml
|
|
40
|
+
# .ccqa/features/tasks/test-cases/create-and-complete/spec.yaml
|
|
41
|
+
title: Create a task and mark it complete
|
|
42
|
+
|
|
43
|
+
steps:
|
|
44
|
+
- instruction: |
|
|
45
|
+
Open ${APP_URL}/login. Fill in email and password, submit the form.
|
|
46
|
+
expected: Redirected to /dashboard, user avatar visible in the header
|
|
47
|
+
|
|
48
|
+
- instruction: |
|
|
49
|
+
Click "New Task", fill in the title "Fix login bug", save.
|
|
50
|
+
expected: Task appears in the task list with status "Open"
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
**2. Tell ccqa what `${APP_URL}` is.** A spec names variables instead of
|
|
54
|
+
embedding an environment, so the same spec runs against local and staging. A
|
|
55
|
+
`.env` file covers you locally; in CI the values come from a hub
|
|
56
|
+
(`ccqa hub var set`) so nothing environment-specific lives in the repo. See
|
|
57
|
+
[Profiles and environment variables](./docs/running.md#profiles-and-environment-variables).
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
echo 'APP_URL=http://localhost:3000' >> .env
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
**3. Record once** — Claude drives the browser and generates the test:
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
ccqa record tasks/create-and-complete
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
**4. Run it** — vitest replays the recording; no LLM involved:
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
ccqa run tasks/create-and-complete
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
A `report.json` (+ step screenshots) is always written to `ccqa-report/`.
|
|
76
|
+
See [Running specs](./docs/running.md) for flags and the report format.
|
|
77
|
+
|
|
78
|
+
If the spec sits behind a login that a recording cannot reproduce — an SSO
|
|
79
|
+
redirect, a device-trust gate — record a session by hand once with
|
|
80
|
+
[`ccqa session bootstrap`](./docs/sessions.md) and name it in the spec.
|
|
18
81
|
|
|
19
82
|
## How it works
|
|
20
83
|
|
|
@@ -48,101 +111,196 @@ into**; every target is the same deterministic replay:
|
|
|
48
111
|
|
|
49
112
|
`runCommand` is the one-line command your repo already uses to run that
|
|
50
113
|
tool, declared once in `.ccqa/config.yaml` — e.g.
|
|
51
|
-
`pnpm exec playwright test {files}`.
|
|
52
|
-
|
|
53
|
-
`{artifactsDir}`; see [Generation targets](./docs/targets.md) for the full
|
|
54
|
-
contract.
|
|
114
|
+
`pnpm exec playwright test {files}`. See
|
|
115
|
+
[Generation targets](./docs/targets.md) for the substitution contract.
|
|
55
116
|
|
|
56
117
|
**Live (`mode: live`).** No codegen: Claude drives every run and judges
|
|
57
118
|
each step's `expected` — for fragile, timing-heavy UIs where a fixed
|
|
58
119
|
recording would break.
|
|
59
120
|
|
|
60
|
-
|
|
61
|
-
`ccqa run --failure-analysis [base]` gives every failing spec a root-cause
|
|
62
|
-
call (TEST_DRIFT / SPEC_CHANGE / PRODUCT_BUG) against the source diff since
|
|
63
|
-
`[base]`, gradable on the hub — and the hub learns from your grades.
|
|
121
|
+
## Failure analysis and drift
|
|
64
122
|
|
|
65
|
-
|
|
123
|
+
A failing E2E test does not say whose problem it is. ccqa answers that
|
|
124
|
+
question in one vocabulary, from two directions.
|
|
125
|
+
|
|
126
|
+
**When a spec fails**, `ccqa run --failure-analysis [base]` labels the cause
|
|
127
|
+
— `TEST_DRIFT`, `SPEC_CHANGE`, `PRODUCT_BUG`, or `UNKNOWN` when the evidence
|
|
128
|
+
does not support a call. The label comes with a drift audit of the same spec,
|
|
129
|
+
because "did the test break" and "does the test still describe the product"
|
|
130
|
+
are the same investigation. `[base]` is what the diff is read against: a git
|
|
131
|
+
ref, or `last-green` to have each spec diff against the commit where it last
|
|
132
|
+
passed. With neither, the label rests on the failure alone and says so.
|
|
133
|
+
|
|
134
|
+
**Before anything runs**, `ccqa drift` asks the second question on its own,
|
|
135
|
+
with no browser: does each spec still describe the code? For a deterministic
|
|
136
|
+
spec that means both artifacts — the spec a human wrote and the test code
|
|
137
|
+
compiled from it — since either can fall out of step. Which one drifted
|
|
138
|
+
decides the repair, so the audit reports it: stale generated code is
|
|
139
|
+
re-recorded, a stale spec needs a human.
|
|
140
|
+
|
|
141
|
+
Every call is gradable on the hub, and the hub learns from your grades. See
|
|
142
|
+
[Failure triage](./docs/running.md#failure-triage) and
|
|
143
|
+
[Drift detection](./docs/running.md#drift-detection).
|
|
144
|
+
|
|
145
|
+
## The hub
|
|
146
|
+
|
|
147
|
+
A hub is optional for one person on one machine. For a team, or for CI, it is
|
|
148
|
+
where the shared state lives — there is no second place to put it:
|
|
149
|
+
|
|
150
|
+
- the coverage inventory of what is tested
|
|
151
|
+
([perspectives](./docs/spec.md#inventory-coverage-with-perspectives)), kept
|
|
152
|
+
current by `record`/`generate`
|
|
153
|
+
- the variables `${…}` resolve to, and saved browser sessions, fetched at run
|
|
154
|
+
time — so CI holds one secret instead of an environment
|
|
155
|
+
- the deploy log behind `--changed=last-run`, and the drift ledger
|
|
156
|
+
- a dashboard of runs with per-step screenshots, triage grading, and the
|
|
157
|
+
prompts learned from those grades
|
|
66
158
|
|
|
67
159
|
```bash
|
|
68
|
-
|
|
160
|
+
export CCQA_HUB_TOKEN=$(openssl rand -hex 24)
|
|
161
|
+
export CCQA_HUB_ENCRYPTION_KEY=$(openssl rand -hex 32) # required to store
|
|
162
|
+
ccqa serve # sessions/variables
|
|
69
163
|
```
|
|
70
164
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
[
|
|
74
|
-
|
|
75
|
-
an external target (`playwright`, `runn`) needs just `ccqa` plus that tool
|
|
76
|
-
(e.g. `pnpm add -D ccqa @playwright/test`); ccqa executes it through the
|
|
77
|
-
target's `runCommand`.
|
|
165
|
+
The repository root also ships a `Dockerfile` and `docker-compose.yaml` for
|
|
166
|
+
container deployment — clone it, or copy them from
|
|
167
|
+
[Running the hub in a container](./docs/hub.md#running-the-hub-in-a-container);
|
|
168
|
+
they are not part of the npm package.
|
|
78
169
|
|
|
79
|
-
|
|
170
|
+
See [Hub](./docs/hub.md) for the full setup and
|
|
171
|
+
[Hub API](./docs/hub-api.md) to script it over HTTP.
|
|
80
172
|
|
|
81
|
-
|
|
82
|
-
[`ccqa draft`](./docs/draft.md). (`ccqa init` scaffolds the `.ccqa/`
|
|
83
|
-
skeleton.)
|
|
173
|
+
## Wire it into CI
|
|
84
174
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
title: Create a task and mark it complete
|
|
175
|
+
Three jobs. They are independent: the pull-request job on its own is a
|
|
176
|
+
complete adoption, and the other two can come later.
|
|
88
177
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
178
|
+
| Job | Trigger | Question it answers |
|
|
179
|
+
|---|---|---|
|
|
180
|
+
| Pre-merge run | `pull_request` | Does this change break a spec, and whose fault is it? |
|
|
181
|
+
| Post-deploy run | after a deploy | Which specs' last result is no longer trustworthy? |
|
|
182
|
+
| Drift audit | `schedule` | Do the specs still describe the code? |
|
|
93
183
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
184
|
+
All three need two things:
|
|
185
|
+
|
|
186
|
+
- **A Claude credential.** Replaying a recorded spec uses no model, but the
|
|
187
|
+
change selection, the failure analysis and the audit all do.
|
|
188
|
+
- **A running [hub](#the-hub)**, reached with `CCQA_HUB_URL` and
|
|
189
|
+
`CCQA_HUB_TOKEN`. Only a pre-merge run with no `--profile` and no
|
|
190
|
+
`--push-report` can do without one.
|
|
191
|
+
|
|
192
|
+
See [Environment variables](./docs/commands.md#environment-variables) for the
|
|
193
|
+
full list.
|
|
194
|
+
|
|
195
|
+
A **profile** is one deployed environment. It names a bucket of variables and
|
|
196
|
+
saved sessions on the hub, and — since two environments sit at different
|
|
197
|
+
commits — its own deploy history. Register the variables your specs reference
|
|
198
|
+
once, from your machine:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
ccqa hub var set APP_URL --value https://app.example --profile staging
|
|
97
202
|
```
|
|
98
203
|
|
|
99
|
-
|
|
204
|
+
Pass the same `--profile` and `--project` in every job. That is what makes the
|
|
205
|
+
jobs refer to the same environment.
|
|
206
|
+
|
|
207
|
+
### On a pull request
|
|
208
|
+
|
|
209
|
+
Run the specs the change reaches, and label what broke.
|
|
100
210
|
|
|
101
211
|
```bash
|
|
102
|
-
ccqa
|
|
212
|
+
ccqa run --changed --failure-analysis --profile staging \
|
|
213
|
+
--format github --push-report
|
|
103
214
|
```
|
|
104
215
|
|
|
105
|
-
|
|
216
|
+
- `--changed` selects the specs the diff reaches. A spec it cannot clear runs
|
|
217
|
+
anyway.
|
|
218
|
+
- `--failure-analysis` labels the cause of each failure.
|
|
219
|
+
- `--profile staging` fetches that environment's variables and saved sessions
|
|
220
|
+
from the hub. Without it, a spec's `${…}` references go unresolved.
|
|
221
|
+
- `--format github` annotates the pull request.
|
|
222
|
+
- `--push-report` streams results to the hub as the run executes.
|
|
223
|
+
|
|
224
|
+
**Set `fetch-depth: 0` on `actions/checkout`.** Both selection flags read
|
|
225
|
+
their baseline from `GITHUB_BASE_REF` and resolve it against `origin/<base>`,
|
|
226
|
+
which a shallow checkout does not have. Without it the run exits with a usage
|
|
227
|
+
error before the first test. Outside a `pull_request` workflow there is no
|
|
228
|
+
`GITHUB_BASE_REF`, so pass the base yourself: `--changed=origin/main`.
|
|
229
|
+
|
|
230
|
+
`--dry-run` prints the selection and stops. The selection costs one model call
|
|
231
|
+
either way.
|
|
232
|
+
|
|
233
|
+
### On a deploy
|
|
234
|
+
|
|
235
|
+
Two steps, in two jobs. First, when the deploy succeeds, tell the hub what
|
|
236
|
+
shipped:
|
|
106
237
|
|
|
107
238
|
```bash
|
|
108
|
-
ccqa
|
|
239
|
+
ccqa hub deploy record --profile staging --sha "$GITHUB_SHA" --select
|
|
109
240
|
```
|
|
110
241
|
|
|
111
|
-
|
|
112
|
-
See [Running specs](./docs/running.md) for flags, CI recipes, and the
|
|
113
|
-
report format.
|
|
114
|
-
|
|
115
|
-
**4. Optional: share results on a hub** — `ccqa serve` starts a small
|
|
116
|
-
self-hosted server (or use the bundled `docker-compose.yaml`). Pushing
|
|
117
|
-
reports to it gives your team:
|
|
118
|
-
|
|
119
|
-
- a dashboard of runs, with per-step screenshots
|
|
120
|
-
- a browsable inventory of what is tested
|
|
121
|
-
([perspectives](./docs/spec.md#inventory-coverage-with-perspectives)),
|
|
122
|
-
kept fresh automatically by `record`/`generate`
|
|
123
|
-
- triage grading — mark each failure call right or wrong; the hub learns
|
|
124
|
-
from the grades
|
|
125
|
-
- one place for shared sessions, variables, and learned prompts — CI
|
|
126
|
-
needs a single secret
|
|
242
|
+
Then, in a job of its own, run what that deploy invalidated:
|
|
127
243
|
|
|
128
244
|
```bash
|
|
129
|
-
|
|
130
|
-
ccqa serve # or: docker compose up -d
|
|
131
|
-
ccqa run tasks/create-and-complete --push-report \
|
|
132
|
-
--hub-url http://localhost:8787 --hub-token $CCQA_HUB_TOKEN
|
|
245
|
+
ccqa run --changed=last-run --profile staging --push-report
|
|
133
246
|
```
|
|
134
247
|
|
|
135
|
-
|
|
136
|
-
|
|
248
|
+
- `--select` records which specs the deployed range reaches. Without it, every
|
|
249
|
+
spec behind that entry answers `unknown` instead of `notNeeded`.
|
|
250
|
+
- `--changed=last-run` asks the hub, per spec, whether any deploy has touched
|
|
251
|
+
it since that spec last ran.
|
|
252
|
+
|
|
253
|
+
The hub has no checkout and never runs `git`, so it cannot work out what a
|
|
254
|
+
deploy changed. That is why the selection is submitted with the deploy rather
|
|
255
|
+
than reconstructed later — and why a deploy recorded without `--select` leaves
|
|
256
|
+
a hole nothing can fill in afterwards.
|
|
257
|
+
|
|
258
|
+
**Expect it to select nothing at first.** A spec with no recorded run is
|
|
259
|
+
`neverRun`; one whose baseline predates the deploy log is `unknown`. Neither
|
|
260
|
+
runs by default. Record a deploy, run every spec once with `--push-report`,
|
|
261
|
+
and the selection means something from the next deploy on. This job also reads
|
|
262
|
+
the spec inventory from the hub, so `ccqa perspectives` has to have run.
|
|
263
|
+
`--include-unknown` opts the undecided specs in.
|
|
264
|
+
|
|
265
|
+
### On a schedule
|
|
266
|
+
|
|
267
|
+
Audit every spec against the codebase, with no browser and no deploy.
|
|
268
|
+
|
|
269
|
+
```bash
|
|
270
|
+
ccqa drift --format github --push
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
- `--severity warn|error` (default `error`) decides whether a verdict fails
|
|
274
|
+
the job.
|
|
275
|
+
- `--push` records each verdict in the hub's per-spec drift ledger, shown in
|
|
276
|
+
the Perspectives tab. It never changes the exit code.
|
|
277
|
+
- `--changed --base <ref>` narrows the sweep on a `push` workflow, at the cost
|
|
278
|
+
of one more model call.
|
|
279
|
+
|
|
280
|
+
The pre-merge job already audits the specs that failed. This one covers the
|
|
281
|
+
rest, because a spec can pass and still describe a product that no longer
|
|
282
|
+
exists.
|
|
283
|
+
|
|
284
|
+
### Workflows
|
|
285
|
+
|
|
286
|
+
[CI integration](./docs/running.md#ci-integration) has runnable workflows for
|
|
287
|
+
the pre-merge run and the scheduled audit.
|
|
288
|
+
[`ccqa hub deploy record`](./docs/hub.md#ccqa-hub-deploy-record) covers the
|
|
289
|
+
deploy job, including a `curl`-only variant for pipelines with no Node.
|
|
137
290
|
|
|
138
291
|
## Documentation
|
|
139
292
|
|
|
140
293
|
| I want to… | Read |
|
|
141
294
|
|---|---|
|
|
295
|
+
| Look up a command or an environment variable | [Command reference](./docs/commands.md) |
|
|
142
296
|
| Write specs: fields, reusable blocks, file uploads, coverage inventory | [spec.yaml reference](./docs/spec.md) |
|
|
143
297
|
| Draft specs interactively with Claude | [Draft](./docs/draft.md) |
|
|
144
298
|
| Generate Playwright or runn tests that reuse my existing test code | [Generation targets](./docs/targets.md) |
|
|
145
|
-
| Run specs
|
|
299
|
+
| Run specs and read the report | [Running specs](./docs/running.md) |
|
|
300
|
+
| Classify failures and grade the calls | [Failure triage](./docs/running.md#failure-triage) |
|
|
301
|
+
| Audit specs against the codebase without running them | [Drift detection](./docs/running.md#drift-detection) |
|
|
302
|
+
| Replay only the specs a change reaches | [Scoping with `--changed`](./docs/running.md#scoping-with---changed) |
|
|
303
|
+
| Wire ccqa into GitHub Actions | [CI integration](./docs/running.md#ci-integration) |
|
|
146
304
|
| Run specs live (no codegen), with per-project guidance | [Live specs](./docs/live.md) |
|
|
147
305
|
| Start runs already signed in / skip device-trust gates | [Saved sessions](./docs/sessions.md) |
|
|
148
306
|
| See which assertions generated tests use | [Assertions](./docs/assertions.md) |
|