qa-engineer 0.10.0 → 0.12.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.
- package/README.md +167 -59
- package/package.json +1 -1
- package/packages/engine/bin/qa-engine.mjs +232 -8
- package/packages/engine/lib/analysis/har.mjs +34 -0
- package/packages/engine/lib/analysis/network.mjs +237 -0
- package/packages/engine/lib/analysis/report-html.mjs +47 -734
- package/packages/engine/lib/artifacts/manager.mjs +453 -0
- package/packages/engine/lib/artifacts/mime.mjs +109 -0
- package/packages/engine/lib/artifacts/zip-write.mjs +143 -0
- package/packages/engine/lib/report/components/charts.mjs +424 -0
- package/packages/engine/lib/report/components/evidence.mjs +207 -0
- package/packages/engine/lib/report/components/findings.mjs +258 -0
- package/packages/engine/lib/report/components/nav.mjs +99 -0
- package/packages/engine/lib/report/components/primitives.mjs +246 -0
- package/packages/engine/lib/report/components/runtime.mjs +246 -0
- package/packages/engine/lib/report/components/timeline.mjs +65 -0
- package/packages/engine/lib/report/core/model.mjs +270 -0
- package/packages/engine/lib/report/core/normalize.mjs +226 -0
- package/packages/engine/lib/report/core/sections.mjs +978 -0
- package/packages/engine/lib/report/export/bundle.mjs +293 -0
- package/packages/engine/lib/report/export/html.mjs +183 -0
- package/packages/engine/lib/report/export/machine.mjs +290 -0
- package/packages/engine/lib/report/export/markdown.mjs +323 -0
- package/packages/engine/lib/report/schemas/qa-report.schema.json +555 -0
- package/packages/engine/lib/report/theme/css.mjs +529 -0
- package/packages/engine/lib/report/theme/tokens.mjs +137 -0
- package/packages/engine/lib/report/version.mjs +78 -0
- package/packages/engine/package.json +2 -2
- package/packages/installer/lib/agents/targets.mjs +90 -0
- package/packages/installer/lib/agents/user-level.mjs +80 -0
- package/packages/installer/lib/cli/flags.mjs +20 -2
- package/packages/installer/lib/commands/doctor.mjs +6 -4
- package/packages/installer/lib/commands/install.mjs +134 -93
- package/packages/installer/lib/commands/repair.mjs +10 -6
- package/packages/installer/lib/commands/self-test.mjs +4 -3
- package/packages/installer/lib/commands/uninstall.mjs +14 -8
- package/packages/installer/lib/commands/update.mjs +9 -4
- package/packages/installer/lib/commands/verify.mjs +13 -12
- package/packages/installer/lib/constants.mjs +13 -0
- package/packages/installer/lib/core/conflict.mjs +5 -4
- package/packages/installer/lib/core/fs-safe.mjs +146 -6
- package/packages/installer/lib/core/integrity.mjs +59 -0
- package/packages/installer/lib/core/lockfile.mjs +19 -3
- package/packages/installer/lib/core/plan.mjs +213 -0
- package/packages/installer/lib/core/qa-home.mjs +145 -0
- package/packages/installer/lib/core/scope.mjs +274 -0
- package/packages/installer/lib/core/validate-install.mjs +37 -12
- package/packages/installer/package.json +1 -1
- package/packages/installer/schemas/qa-lock.schema.json +119 -21
- package/shared/tooling/qa-tool.mjs +41 -5
- package/skills/qa-api/scripts/qa-tool.mjs +41 -5
- package/skills/qa-audit/scripts/qa-tool.mjs +41 -5
- package/skills/qa-debug/SKILL.md +3 -0
- package/skills/qa-debug/references/failure-handoff.md +44 -0
- package/skills/qa-debug/scripts/qa-tool.mjs +41 -5
- package/skills/qa-explore/SKILL.md +26 -10
- package/skills/qa-explore/contracts/explore-result.schema.json +517 -11
- package/skills/qa-explore/references/api-replay.md +40 -1
- package/skills/qa-explore/references/report-pipeline.md +265 -95
- package/skills/qa-explore/scripts/qa-tool.mjs +41 -5
- package/skills/qa-fix/scripts/qa-tool.mjs +41 -5
- package/skills/qa-flaky/scripts/qa-tool.mjs +41 -5
- package/skills/qa-init/scripts/qa-tool.mjs +41 -5
- package/skills/qa-report/scripts/qa-tool.mjs +41 -5
- package/skills/qa-run/README.md +7 -0
- package/skills/qa-run/SKILL.md +12 -7
- package/skills/qa-run/contracts/execution-result.schema.json +77 -0
- package/skills/qa-run/examples/execute-playwright.md +42 -10
- package/skills/qa-run/examples/plan-a-run.md +5 -3
- package/skills/qa-run/references/artifact-collector.md +2 -1
- package/skills/qa-run/references/command-builder.md +18 -1
- package/skills/qa-run/references/execution-strategy.md +2 -0
- package/skills/qa-run/references/failure-handoff.md +44 -0
- package/skills/qa-run/references/playwright-artifacts.md +3 -1
- package/skills/qa-run/references/playwright-execution.md +14 -1
- package/skills/qa-run/scripts/qa-tool.mjs +41 -5
|
@@ -20,10 +20,23 @@ Start from the package-manager invocation (`pnpm exec playwright test`, `npx pla
|
|
|
20
20
|
| Scope — project or browser | `--project=<name>` |
|
|
21
21
|
| Display | `--headed` only on explicit request; headless is the default (no flag) |
|
|
22
22
|
| Evidence — trace | `--trace=on-first-retry` by default, `on` when the strategy asks |
|
|
23
|
-
| Evidence —
|
|
23
|
+
| Evidence — screenshot | `--screenshot=only-on-failure` always, `on` when the strategy asks |
|
|
24
|
+
| Evidence — video | `--video=retain-on-failure` always, `on` when the strategy asks |
|
|
24
25
|
|
|
25
26
|
The full command is recorded verbatim. Secrets are never interpolated; environment variables are referenced by name and resolved at run time (see the environment-detection module).
|
|
26
27
|
|
|
28
|
+
### The failure floor in Playwright flags
|
|
29
|
+
|
|
30
|
+
The last three rows are the shared failure evidence floor (see the command-builder module) expressed in Playwright's own flags, and they are written on every command — including a one-spec run and a smoke run:
|
|
31
|
+
|
|
32
|
+
```text
|
|
33
|
+
--screenshot=only-on-failure --video=retain-on-failure --trace=on-first-retry
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- **`--screenshot=off`, `--video=off`, and `--trace=off` are never built.** Playwright's own defaults are `off`, and a project config may set `off` explicitly; the flags are passed anyway because a CLI flag overrides `use.screenshot` / `use.video` / `use.trace` in the config, which is exactly the point.
|
|
37
|
+
- **Retries interact with the floor.** `--trace=on-first-retry` produces a trace only when a retry happens, so a run with `--retries=0` fails without one. When a diagnosis needs the trace of a first, un-retried failure, the strategy raises tracing to `--trace=retain-on-failure` rather than lowering the floor.
|
|
38
|
+
- **The cost is paid only by failures.** `only-on-failure` and `retain-on-failure` write nothing for a passing test — a green run produces the same artifacts it always did, which is why the floor does not need a fast-mode exemption.
|
|
39
|
+
|
|
27
40
|
## Launch and run
|
|
28
41
|
|
|
29
42
|
The agent runs the built command through its shell, under the run's wall-clock timeout, following the browser lifecycle: headless by default, bounded test-level retries via `--retries` to observe flakiness, cleanup on every exit path, and honest handling of a hang or crash as `errored` rather than as a test failure.
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
// `node qa-tool.mjs …`, which needs none.
|
|
30
30
|
|
|
31
31
|
import fs from 'node:fs';
|
|
32
|
+
import os from 'node:os';
|
|
32
33
|
import path from 'node:path';
|
|
33
34
|
import { spawnSync } from 'node:child_process';
|
|
34
35
|
import { fileURLToPath } from 'node:url';
|
|
@@ -38,7 +39,7 @@ const here = path.dirname(fileURLToPath(import.meta.url));
|
|
|
38
39
|
const USAGE = `usage: node qa-tool.mjs <tool> <subcommand> [args]
|
|
39
40
|
|
|
40
41
|
analysis parse artifacts, classify errors, validate contracts, diff-guard,
|
|
41
|
-
|
|
42
|
+
read .qa/context.md, render an HTML report, print the footer
|
|
42
43
|
diagnostics root cause, timeline, priority, repair plans, release readiness
|
|
43
44
|
playwright normalize a Playwright report or summarize a trace
|
|
44
45
|
|
|
@@ -53,15 +54,26 @@ examples:
|
|
|
53
54
|
/**
|
|
54
55
|
* Where the engine is, and how we found it.
|
|
55
56
|
*
|
|
56
|
-
* Ordered by cost: a bundled copy needs no resolution and no network, a
|
|
57
|
-
* node_modules copy needs no network, and npx needs both
|
|
58
|
-
* *which* one answered matters when a skill degrades — "the
|
|
59
|
-
* "the engine is being fetched" are different problems.
|
|
57
|
+
* Ordered by cost: a bundled copy needs no resolution and no network, a shared copy
|
|
58
|
+
* needs one stat per ancestor, a node_modules copy needs no network, and npx needs both
|
|
59
|
+
* on first use. Reporting *which* one answered matters when a skill degrades — "the
|
|
60
|
+
* engine is missing" and "the engine is being fetched" are different problems.
|
|
61
|
+
*
|
|
62
|
+
* The shared lookup is what lets one skill directory serve a project, a workspace, and a
|
|
63
|
+
* machine-wide install without knowing which installed it. Walking up for
|
|
64
|
+
* `.qa-engineer/engine` finds a workspace install from a skill inside the repository,
|
|
65
|
+
* and finds a global install from a skill linked into an agent's user-level directory —
|
|
66
|
+
* `~/.claude/skills/qa-explore` walks up to `~`, where `~/.qa-engineer/engine` is.
|
|
60
67
|
*/
|
|
61
68
|
function resolveEngine() {
|
|
62
69
|
const bundled = path.join(here, 'lib', 'bin', 'qa-engine.mjs');
|
|
63
70
|
if (fs.existsSync(bundled)) return { kind: 'bundled', command: [process.execPath, bundled] };
|
|
64
71
|
|
|
72
|
+
for (const root of candidateSharedRoots()) {
|
|
73
|
+
const shared = path.join(root, 'bin', 'qa-engine.mjs');
|
|
74
|
+
if (fs.existsSync(shared)) return { kind: 'shared', command: [process.execPath, shared] };
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
for (const base of candidateModuleRoots()) {
|
|
66
78
|
const installed = path.join(base, 'qa-engineer', 'packages', 'engine', 'bin', 'qa-engine.mjs');
|
|
67
79
|
if (fs.existsSync(installed)) {
|
|
@@ -75,6 +87,30 @@ function resolveEngine() {
|
|
|
75
87
|
};
|
|
76
88
|
}
|
|
77
89
|
|
|
90
|
+
/** Shared engine directories worth checking, most specific first. */
|
|
91
|
+
function candidateSharedRoots() {
|
|
92
|
+
const roots = [];
|
|
93
|
+
|
|
94
|
+
// An explicit home wins over anything discovered, so a user who moved the install can
|
|
95
|
+
// rely on it rather than on whatever the walk happens to find first.
|
|
96
|
+
const override = process.env.QA_ENGINEER_HOME;
|
|
97
|
+
if (override && override.trim()) roots.push(path.join(path.resolve(override.trim()), 'engine'));
|
|
98
|
+
|
|
99
|
+
let dir = here;
|
|
100
|
+
for (let depth = 0; depth < 12; depth += 1) {
|
|
101
|
+
roots.push(path.join(dir, '.qa-engineer', 'engine'));
|
|
102
|
+
const parent = path.dirname(dir);
|
|
103
|
+
if (parent === dir) break;
|
|
104
|
+
dir = parent;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// The default machine home, for the case where the skill lives outside it entirely.
|
|
108
|
+
const home = os.homedir();
|
|
109
|
+
if (home) roots.push(path.join(home, '.qa-engineer', 'engine'));
|
|
110
|
+
|
|
111
|
+
return roots;
|
|
112
|
+
}
|
|
113
|
+
|
|
78
114
|
/** node_modules directories worth checking, nearest first. */
|
|
79
115
|
function candidateModuleRoots() {
|
|
80
116
|
const roots = [];
|