@vijaypjavvadi/bdd2pw 3.9.0 → 3.10.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/CHANGELOG.md +90 -0
- package/dist/cli.js +30 -0
- package/dist/cli.js.map +1 -1
- package/dist/reports/healStats.d.ts +132 -0
- package/dist/reports/healStats.js +303 -0
- package/dist/reports/healStats.js.map +1 -0
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,96 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
_Nothing yet._
|
|
11
11
|
|
|
12
|
+
## [3.10.0] — 2026-06-06
|
|
13
|
+
|
|
14
|
+
### Added — self-healing stats sidecar + `bdd2pw heal-stats` CLI
|
|
15
|
+
|
|
16
|
+
v1.1+ has shipped a runtime `healOrThrow` helper that emits
|
|
17
|
+
`register` / `heal_attempt` / `healed` / `heal_unavailable` events
|
|
18
|
+
to `<repo>/artefacts/heal-events.jsonl`. Until now those events
|
|
19
|
+
sat in a JSONL file with no offline analyzer — operators had to
|
|
20
|
+
grep the file themselves to see if healing was working.
|
|
21
|
+
|
|
22
|
+
v3.10.0 adds:
|
|
23
|
+
|
|
24
|
+
**1. `bdd2pw heal-stats <repo>` CLI** — reads
|
|
25
|
+
`<repo>/artefacts/heal-events.jsonl` and writes
|
|
26
|
+
`<repo>/artefacts/heal-stats.json` with:
|
|
27
|
+
|
|
28
|
+
{
|
|
29
|
+
"version": "3.10.0",
|
|
30
|
+
"generatedAt": "2026-06-06T...",
|
|
31
|
+
"source": "<events file>",
|
|
32
|
+
"totals": {
|
|
33
|
+
"registrations": 47,
|
|
34
|
+
"healAttempts": 6,
|
|
35
|
+
"healed": 4,
|
|
36
|
+
"healUnavailable": 2,
|
|
37
|
+
"healRate": 0.6667,
|
|
38
|
+
"uniqueFields": 23,
|
|
39
|
+
"uniquePages": 3
|
|
40
|
+
},
|
|
41
|
+
"topFailingFields": [
|
|
42
|
+
{ "page": "LoginPage", "name": "submitButton",
|
|
43
|
+
"attempts": 3, "healed": 2 },
|
|
44
|
+
...
|
|
45
|
+
],
|
|
46
|
+
"topErrors": [
|
|
47
|
+
{ "error": "timeout <n>ms exceeded", "count": 4 },
|
|
48
|
+
...
|
|
49
|
+
],
|
|
50
|
+
"topCandidates": [
|
|
51
|
+
{ "selector": "[data-testid='submit']", "promotions": 2,
|
|
52
|
+
"averageConfidence": 0.87 },
|
|
53
|
+
...
|
|
54
|
+
],
|
|
55
|
+
"retryLatencyMs": { "p50": 1240, "p95": 3010,
|
|
56
|
+
"min": 880, "max": 3010 },
|
|
57
|
+
"perScenario": [
|
|
58
|
+
{ "scenario": "User can log in", "attempts": 1, "healed": 1 },
|
|
59
|
+
...
|
|
60
|
+
]
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
Where v3.9.0's `llm-stats.json` measures the LLM batching ROI per
|
|
64
|
+
scaffold, v3.10.0's `heal-stats.json` measures the self-healing
|
|
65
|
+
ROI per TEST RUN.
|
|
66
|
+
|
|
67
|
+
**2. New module `src/reports/healStats.ts`** — pure
|
|
68
|
+
`aggregate(events, source, topN)` function plus the wrapper
|
|
69
|
+
`analyseHealStats(opts)` that resolves the input path
|
|
70
|
+
(file / repo-dir / artefacts-subdir) and writes the sidecar.
|
|
71
|
+
|
|
72
|
+
**3. Error normalisation.** The aggregator collapses error messages
|
|
73
|
+
into stable buckets (lowercase, strip "Error:"/"TimeoutError:"
|
|
74
|
+
prefixes, replace runs of 3+ digits with `<n>`) so transient
|
|
75
|
+
timestamps / IDs don't fragment the histogram.
|
|
76
|
+
|
|
77
|
+
**4. Retry latency derivation.** Pairs each `heal_attempt` with its
|
|
78
|
+
matching `healed` (by `page+name+method`) and reports the timestamp
|
|
79
|
+
delta as the retry latency. Computes p50 / p95 / min / max across
|
|
80
|
+
the run.
|
|
81
|
+
|
|
82
|
+
**5. Graceful empty-input behaviour.** Pointing the CLI at a repo
|
|
83
|
+
with no heal-events.jsonl writes a zero-event summary instead of
|
|
84
|
+
failing — `bdd2pw heal-stats` is now safe to run unconditionally
|
|
85
|
+
in CI after `npx playwright test`.
|
|
86
|
+
|
|
87
|
+
### Tests
|
|
88
|
+
|
|
89
|
+
`tests/unit/v3100HealStats.test.ts`:
|
|
90
|
+
- Empty input → all-zero summary.
|
|
91
|
+
- Mixed register / heal_attempt / healed / heal_unavailable sequence
|
|
92
|
+
aggregates correctly (counts, healRate, topFailingFields,
|
|
93
|
+
topErrors, topCandidates, p50/p95 latency, per-scenario rollup).
|
|
94
|
+
- `topN` honored — top-3 returns 3 entries from 5 distinct fields.
|
|
95
|
+
- End-to-end: write JSONL → `analyseHealStats(file)` → read JSON
|
|
96
|
+
back and verify shape + version stamp.
|
|
97
|
+
- Missing-file safety: pointing at a directory with no
|
|
98
|
+
heal-events.jsonl returns total=0 and writes an empty summary.
|
|
99
|
+
- Repo-path resolution: passing a directory finds
|
|
100
|
+
`artefacts/heal-events.jsonl` automatically.
|
|
101
|
+
|
|
12
102
|
## [3.9.0] — 2026-06-05
|
|
13
103
|
|
|
14
104
|
### Added — opt-in LLM telemetry sidecar
|
package/dist/cli.js
CHANGED
|
@@ -42,6 +42,7 @@ const commander_1 = require("commander");
|
|
|
42
42
|
const path = __importStar(require("path"));
|
|
43
43
|
const index_1 = require("./index");
|
|
44
44
|
const llm_1 = require("./llm");
|
|
45
|
+
const healStats_1 = require("./reports/healStats");
|
|
45
46
|
const logger_1 = require("./utils/logger");
|
|
46
47
|
const program = new commander_1.Command();
|
|
47
48
|
program
|
|
@@ -75,6 +76,7 @@ program
|
|
|
75
76
|
.option("--telemetry", "Enable local SQLite failure telemetry", false)
|
|
76
77
|
.option("--force", "Overwrite existing spec files", false)
|
|
77
78
|
.option("--self-healing", "Wrap emitted locators in healOrThrow() and generate lib/heal.ts + tsconfig path alias. Locator events are logged to artefacts/heal-events.jsonl for the offline self-heal pipeline. Action-time healing is v1.2.", false)
|
|
79
|
+
.option("--llm-stats", "v3.9.0 — write <repo>/artefacts/llm-stats.json with per-call latency, token counts, cache hit rate, and estimated cost.", false)
|
|
78
80
|
.action(async (feature, opts) => {
|
|
79
81
|
try {
|
|
80
82
|
// v2.0 — wire the actual LLM config when --llm is passed. The legacy
|
|
@@ -122,6 +124,8 @@ program
|
|
|
122
124
|
// Commander negates --no-discovery into opts.discovery=false
|
|
123
125
|
noDiscovery: opts.discovery === false,
|
|
124
126
|
selfHealing: opts.selfHealing,
|
|
127
|
+
// v3.9.0 — telemetry sidecar
|
|
128
|
+
llmStats: opts.llmStats === true,
|
|
125
129
|
});
|
|
126
130
|
logger_1.logger.info({ result }, "scaffold complete");
|
|
127
131
|
}
|
|
@@ -235,6 +239,32 @@ program
|
|
|
235
239
|
process.stdout.write(`\n${result.proposalsWritten} proposal(s) written to ${result.outputPath} ` +
|
|
236
240
|
`(from ${result.totalCandidates} candidate entries).\n`);
|
|
237
241
|
});
|
|
242
|
+
/**
|
|
243
|
+
* v3.10.0 — heal-stats subcommand. Reads `<repo>/artefacts/heal-events.jsonl`
|
|
244
|
+
* (produced by the runtime `healOrThrow` helper) and writes
|
|
245
|
+
* `<repo>/artefacts/heal-stats.json` summarising heal attempts,
|
|
246
|
+
* success rate, top failing fields, top error patterns, retry latency,
|
|
247
|
+
* and the candidate selectors that got promoted. Run after
|
|
248
|
+
* `npx playwright test` in CI to track self-healing ROI per test run.
|
|
249
|
+
*/
|
|
250
|
+
program
|
|
251
|
+
.command("heal-stats")
|
|
252
|
+
.description("Aggregate <repo>/artefacts/heal-events.jsonl into a heal-stats.json sidecar.")
|
|
253
|
+
.argument("<input>", "Path to heal-events.jsonl OR the test repo containing artefacts/heal-events.jsonl")
|
|
254
|
+
.option("--out <path>", "Where to write heal-stats.json. Defaults to next to the events file.")
|
|
255
|
+
.option("--top <n>", "How many entries to keep in each ranked list (failing fields, errors, candidates, scenarios). Default 10.", "10")
|
|
256
|
+
.action(async (input, raw) => {
|
|
257
|
+
const result = await (0, healStats_1.analyseHealStats)({
|
|
258
|
+
inputPath: input,
|
|
259
|
+
outputPath: raw.out,
|
|
260
|
+
topN: raw.top ? Number(raw.top) : undefined,
|
|
261
|
+
});
|
|
262
|
+
logger_1.logger.info({
|
|
263
|
+
outputPath: result.outputPath,
|
|
264
|
+
totalEvents: result.totalEvents,
|
|
265
|
+
}, "heal-stats: complete");
|
|
266
|
+
process.stdout.write(`\nheal-stats: wrote ${result.outputPath} (parsed ${result.totalEvents} event(s)).\n`);
|
|
267
|
+
});
|
|
238
268
|
program.parseAsync(process.argv).catch((err) => {
|
|
239
269
|
logger_1.logger.error({ err }, "fatal");
|
|
240
270
|
process.exit(1);
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* v3.10.0 — heal-events.jsonl → heal-stats.json analyzer.
|
|
3
|
+
*
|
|
4
|
+
* Same flavor as v3.9.0's `LLMTelemetry` sidecar but for the
|
|
5
|
+
* self-healing pipeline. Where v3.9.0 measures the LLM batching ROI
|
|
6
|
+
* per scaffold, v3.10.0 measures the self-healing ROI per TEST RUN.
|
|
7
|
+
*
|
|
8
|
+
* Input: `<repo>/artefacts/heal-events.jsonl` — one JSON event per
|
|
9
|
+
* line, written by the runtime `healOrThrow` helper from
|
|
10
|
+
* `templates/heal.ts.tmpl`. Event kinds:
|
|
11
|
+
* - `register` — locator constructed
|
|
12
|
+
* - `heal_attempt` — action failed; asking the heal
|
|
13
|
+
* service for a candidate
|
|
14
|
+
* - `healed` — candidate worked; subsequent
|
|
15
|
+
* calls use it transparently
|
|
16
|
+
* - `heal_unavailable` — no candidate / candidate failed
|
|
17
|
+
*
|
|
18
|
+
* Output: `<repo>/artefacts/heal-stats.json` — stable shape:
|
|
19
|
+
*
|
|
20
|
+
* {
|
|
21
|
+
* "version": "3.10.0",
|
|
22
|
+
* "generatedAt": "2026-...",
|
|
23
|
+
* "source": "<events file>",
|
|
24
|
+
* "totals": {
|
|
25
|
+
* "registrations": 47,
|
|
26
|
+
* "healAttempts": 6,
|
|
27
|
+
* "healed": 4,
|
|
28
|
+
* "healUnavailable": 2,
|
|
29
|
+
* "healRate": 0.6667, // healed / healAttempts
|
|
30
|
+
* "uniqueFields": 23,
|
|
31
|
+
* "uniquePages": 3
|
|
32
|
+
* },
|
|
33
|
+
* "topFailingFields": [
|
|
34
|
+
* { "page": "LoginPage", "name": "submitButton", "attempts": 3, "healed": 2 },
|
|
35
|
+
* ...
|
|
36
|
+
* ],
|
|
37
|
+
* "topErrors": [
|
|
38
|
+
* { "error": "Timeout 30000ms exceeded", "count": 4 },
|
|
39
|
+
* ...
|
|
40
|
+
* ],
|
|
41
|
+
* "topCandidates": [
|
|
42
|
+
* { "selector": "[data-testid='submit']", "promotions": 2,
|
|
43
|
+
* "averageConfidence": 0.87 },
|
|
44
|
+
* ...
|
|
45
|
+
* ],
|
|
46
|
+
* "retryLatencyMs": { "p50": 1240, "p95": 3010, "min": 880, "max": 3010 },
|
|
47
|
+
* "perScenario": [
|
|
48
|
+
* { "scenario": "User can log in", "attempts": 1, "healed": 1 },
|
|
49
|
+
* ...
|
|
50
|
+
* ]
|
|
51
|
+
* }
|
|
52
|
+
*
|
|
53
|
+
* Why a separate command (not part of scaffold): scaffold GENERATES the
|
|
54
|
+
* test repo; the heal events are produced when the user RUNS the
|
|
55
|
+
* generated tests. So this analyzer is a post-test command, run from
|
|
56
|
+
* CI after `npx playwright test`. Same pattern as `bdd2pw propose-rules`
|
|
57
|
+
* which runs after the LLM has populated `candidate-rules.jsonl`.
|
|
58
|
+
*/
|
|
59
|
+
export interface HealStatsOptions {
|
|
60
|
+
/** Path to heal-events.jsonl OR a repo dir containing it. */
|
|
61
|
+
inputPath: string;
|
|
62
|
+
/** Override output path. Default: <input dir>/heal-stats.json. */
|
|
63
|
+
outputPath?: string;
|
|
64
|
+
/** How many top entries to keep in each ranked list. Default 10. */
|
|
65
|
+
topN?: number;
|
|
66
|
+
}
|
|
67
|
+
export interface HealStatsResult {
|
|
68
|
+
outputPath: string;
|
|
69
|
+
totalEvents: number;
|
|
70
|
+
}
|
|
71
|
+
/** One event line in heal-events.jsonl. Shape tracks `templates/heal.ts.tmpl`. */
|
|
72
|
+
interface HealEvent {
|
|
73
|
+
ts: string;
|
|
74
|
+
event: "register" | "heal_attempt" | "healed" | "heal_unavailable";
|
|
75
|
+
page: string;
|
|
76
|
+
name: string;
|
|
77
|
+
scenario_name?: string | null;
|
|
78
|
+
method?: string;
|
|
79
|
+
original?: string | null;
|
|
80
|
+
healed?: string | null;
|
|
81
|
+
confidence?: number | null;
|
|
82
|
+
error?: string | null;
|
|
83
|
+
}
|
|
84
|
+
export interface HealStats {
|
|
85
|
+
version: string;
|
|
86
|
+
generatedAt: string;
|
|
87
|
+
source: string;
|
|
88
|
+
totals: {
|
|
89
|
+
registrations: number;
|
|
90
|
+
healAttempts: number;
|
|
91
|
+
healed: number;
|
|
92
|
+
healUnavailable: number;
|
|
93
|
+
healRate: number;
|
|
94
|
+
uniqueFields: number;
|
|
95
|
+
uniquePages: number;
|
|
96
|
+
};
|
|
97
|
+
topFailingFields: {
|
|
98
|
+
page: string;
|
|
99
|
+
name: string;
|
|
100
|
+
attempts: number;
|
|
101
|
+
healed: number;
|
|
102
|
+
}[];
|
|
103
|
+
topErrors: {
|
|
104
|
+
error: string;
|
|
105
|
+
count: number;
|
|
106
|
+
}[];
|
|
107
|
+
topCandidates: {
|
|
108
|
+
selector: string;
|
|
109
|
+
promotions: number;
|
|
110
|
+
averageConfidence: number | null;
|
|
111
|
+
}[];
|
|
112
|
+
retryLatencyMs: {
|
|
113
|
+
p50: number;
|
|
114
|
+
p95: number;
|
|
115
|
+
min: number;
|
|
116
|
+
max: number;
|
|
117
|
+
};
|
|
118
|
+
perScenario: {
|
|
119
|
+
scenario: string;
|
|
120
|
+
attempts: number;
|
|
121
|
+
healed: number;
|
|
122
|
+
}[];
|
|
123
|
+
}
|
|
124
|
+
/** Public entry point. */
|
|
125
|
+
export declare function analyseHealStats(opts: HealStatsOptions): Promise<HealStatsResult>;
|
|
126
|
+
/**
|
|
127
|
+
* Compute a HealStats summary from an in-memory event list. Pure
|
|
128
|
+
* function — no I/O. Used both by the public CLI entry and the unit
|
|
129
|
+
* tests.
|
|
130
|
+
*/
|
|
131
|
+
export declare function aggregate(events: HealEvent[], source: string, topN: number): HealStats;
|
|
132
|
+
export {};
|
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* v3.10.0 — heal-events.jsonl → heal-stats.json analyzer.
|
|
4
|
+
*
|
|
5
|
+
* Same flavor as v3.9.0's `LLMTelemetry` sidecar but for the
|
|
6
|
+
* self-healing pipeline. Where v3.9.0 measures the LLM batching ROI
|
|
7
|
+
* per scaffold, v3.10.0 measures the self-healing ROI per TEST RUN.
|
|
8
|
+
*
|
|
9
|
+
* Input: `<repo>/artefacts/heal-events.jsonl` — one JSON event per
|
|
10
|
+
* line, written by the runtime `healOrThrow` helper from
|
|
11
|
+
* `templates/heal.ts.tmpl`. Event kinds:
|
|
12
|
+
* - `register` — locator constructed
|
|
13
|
+
* - `heal_attempt` — action failed; asking the heal
|
|
14
|
+
* service for a candidate
|
|
15
|
+
* - `healed` — candidate worked; subsequent
|
|
16
|
+
* calls use it transparently
|
|
17
|
+
* - `heal_unavailable` — no candidate / candidate failed
|
|
18
|
+
*
|
|
19
|
+
* Output: `<repo>/artefacts/heal-stats.json` — stable shape:
|
|
20
|
+
*
|
|
21
|
+
* {
|
|
22
|
+
* "version": "3.10.0",
|
|
23
|
+
* "generatedAt": "2026-...",
|
|
24
|
+
* "source": "<events file>",
|
|
25
|
+
* "totals": {
|
|
26
|
+
* "registrations": 47,
|
|
27
|
+
* "healAttempts": 6,
|
|
28
|
+
* "healed": 4,
|
|
29
|
+
* "healUnavailable": 2,
|
|
30
|
+
* "healRate": 0.6667, // healed / healAttempts
|
|
31
|
+
* "uniqueFields": 23,
|
|
32
|
+
* "uniquePages": 3
|
|
33
|
+
* },
|
|
34
|
+
* "topFailingFields": [
|
|
35
|
+
* { "page": "LoginPage", "name": "submitButton", "attempts": 3, "healed": 2 },
|
|
36
|
+
* ...
|
|
37
|
+
* ],
|
|
38
|
+
* "topErrors": [
|
|
39
|
+
* { "error": "Timeout 30000ms exceeded", "count": 4 },
|
|
40
|
+
* ...
|
|
41
|
+
* ],
|
|
42
|
+
* "topCandidates": [
|
|
43
|
+
* { "selector": "[data-testid='submit']", "promotions": 2,
|
|
44
|
+
* "averageConfidence": 0.87 },
|
|
45
|
+
* ...
|
|
46
|
+
* ],
|
|
47
|
+
* "retryLatencyMs": { "p50": 1240, "p95": 3010, "min": 880, "max": 3010 },
|
|
48
|
+
* "perScenario": [
|
|
49
|
+
* { "scenario": "User can log in", "attempts": 1, "healed": 1 },
|
|
50
|
+
* ...
|
|
51
|
+
* ]
|
|
52
|
+
* }
|
|
53
|
+
*
|
|
54
|
+
* Why a separate command (not part of scaffold): scaffold GENERATES the
|
|
55
|
+
* test repo; the heal events are produced when the user RUNS the
|
|
56
|
+
* generated tests. So this analyzer is a post-test command, run from
|
|
57
|
+
* CI after `npx playwright test`. Same pattern as `bdd2pw propose-rules`
|
|
58
|
+
* which runs after the LLM has populated `candidate-rules.jsonl`.
|
|
59
|
+
*/
|
|
60
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
61
|
+
if (k2 === undefined) k2 = k;
|
|
62
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
63
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
64
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
65
|
+
}
|
|
66
|
+
Object.defineProperty(o, k2, desc);
|
|
67
|
+
}) : (function(o, m, k, k2) {
|
|
68
|
+
if (k2 === undefined) k2 = k;
|
|
69
|
+
o[k2] = m[k];
|
|
70
|
+
}));
|
|
71
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
72
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
73
|
+
}) : function(o, v) {
|
|
74
|
+
o["default"] = v;
|
|
75
|
+
});
|
|
76
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
77
|
+
var ownKeys = function(o) {
|
|
78
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
79
|
+
var ar = [];
|
|
80
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
81
|
+
return ar;
|
|
82
|
+
};
|
|
83
|
+
return ownKeys(o);
|
|
84
|
+
};
|
|
85
|
+
return function (mod) {
|
|
86
|
+
if (mod && mod.__esModule) return mod;
|
|
87
|
+
var result = {};
|
|
88
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
89
|
+
__setModuleDefault(result, mod);
|
|
90
|
+
return result;
|
|
91
|
+
};
|
|
92
|
+
})();
|
|
93
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
94
|
+
exports.analyseHealStats = analyseHealStats;
|
|
95
|
+
exports.aggregate = aggregate;
|
|
96
|
+
const fs = __importStar(require("fs-extra"));
|
|
97
|
+
const path = __importStar(require("path"));
|
|
98
|
+
const PACKAGE_VERSION = "3.10.0";
|
|
99
|
+
/** Public entry point. */
|
|
100
|
+
async function analyseHealStats(opts) {
|
|
101
|
+
const jsonlPath = await resolveJsonlPath(opts.inputPath);
|
|
102
|
+
const events = await readEvents(jsonlPath);
|
|
103
|
+
const summary = aggregate(events, jsonlPath, opts.topN ?? 10);
|
|
104
|
+
const outputPath = opts.outputPath ?? path.join(path.dirname(jsonlPath), "heal-stats.json");
|
|
105
|
+
await fs.ensureDir(path.dirname(outputPath));
|
|
106
|
+
await fs.writeFile(outputPath, JSON.stringify(summary, null, 2), "utf8");
|
|
107
|
+
return { outputPath, totalEvents: events.length };
|
|
108
|
+
}
|
|
109
|
+
/* ─── pure aggregation (testable without fs) ──────────────────────── */
|
|
110
|
+
/**
|
|
111
|
+
* Compute a HealStats summary from an in-memory event list. Pure
|
|
112
|
+
* function — no I/O. Used both by the public CLI entry and the unit
|
|
113
|
+
* tests.
|
|
114
|
+
*/
|
|
115
|
+
function aggregate(events, source, topN) {
|
|
116
|
+
const fieldKey = (e) => `${e.page}::${e.name}`;
|
|
117
|
+
const fieldAttempts = new Map();
|
|
118
|
+
const errorCounts = new Map();
|
|
119
|
+
const candidateStats = new Map();
|
|
120
|
+
const scenarioStats = new Map();
|
|
121
|
+
const uniquePages = new Set();
|
|
122
|
+
const uniqueFields = new Set();
|
|
123
|
+
const retryLatencies = [];
|
|
124
|
+
/**
|
|
125
|
+
* Match heal_attempt events with their matching `healed` /
|
|
126
|
+
* `heal_unavailable` outcome by (page, name, method). The keying
|
|
127
|
+
* assumes a heal attempt resolves before another one fires for the
|
|
128
|
+
* same field+method combo, which holds in practice — actions run
|
|
129
|
+
* serially per test.
|
|
130
|
+
*/
|
|
131
|
+
const openAttempts = new Map();
|
|
132
|
+
const attemptKey = (e) => `${e.page}::${e.name}::${e.method ?? ""}`;
|
|
133
|
+
let registrations = 0;
|
|
134
|
+
let healAttempts = 0;
|
|
135
|
+
let healed = 0;
|
|
136
|
+
let healUnavailable = 0;
|
|
137
|
+
for (const e of events) {
|
|
138
|
+
uniquePages.add(e.page);
|
|
139
|
+
uniqueFields.add(fieldKey(e));
|
|
140
|
+
if (e.event === "register") {
|
|
141
|
+
registrations += 1;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
if (e.event === "heal_attempt") {
|
|
145
|
+
healAttempts += 1;
|
|
146
|
+
const field = fieldAttempts.get(fieldKey(e)) ?? {
|
|
147
|
+
page: e.page,
|
|
148
|
+
name: e.name,
|
|
149
|
+
attempts: 0,
|
|
150
|
+
healed: 0,
|
|
151
|
+
};
|
|
152
|
+
field.attempts += 1;
|
|
153
|
+
fieldAttempts.set(fieldKey(e), field);
|
|
154
|
+
if (e.error) {
|
|
155
|
+
const msg = truncateError(e.error);
|
|
156
|
+
errorCounts.set(msg, (errorCounts.get(msg) ?? 0) + 1);
|
|
157
|
+
}
|
|
158
|
+
const scenario = e.scenario_name ?? "(no scenario context)";
|
|
159
|
+
const ss = scenarioStats.get(scenario) ?? { attempts: 0, healed: 0 };
|
|
160
|
+
ss.attempts += 1;
|
|
161
|
+
scenarioStats.set(scenario, ss);
|
|
162
|
+
openAttempts.set(attemptKey(e), Date.parse(e.ts));
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (e.event === "healed") {
|
|
166
|
+
healed += 1;
|
|
167
|
+
const field = fieldAttempts.get(fieldKey(e)) ?? {
|
|
168
|
+
page: e.page,
|
|
169
|
+
name: e.name,
|
|
170
|
+
attempts: 0,
|
|
171
|
+
healed: 0,
|
|
172
|
+
};
|
|
173
|
+
field.healed += 1;
|
|
174
|
+
fieldAttempts.set(fieldKey(e), field);
|
|
175
|
+
if (e.healed) {
|
|
176
|
+
const cs = candidateStats.get(e.healed) ?? {
|
|
177
|
+
promotions: 0,
|
|
178
|
+
confidenceSum: 0,
|
|
179
|
+
confidenceCount: 0,
|
|
180
|
+
};
|
|
181
|
+
cs.promotions += 1;
|
|
182
|
+
if (typeof e.confidence === "number") {
|
|
183
|
+
cs.confidenceSum += e.confidence;
|
|
184
|
+
cs.confidenceCount += 1;
|
|
185
|
+
}
|
|
186
|
+
candidateStats.set(e.healed, cs);
|
|
187
|
+
}
|
|
188
|
+
const scenario = e.scenario_name ?? "(no scenario context)";
|
|
189
|
+
const ss = scenarioStats.get(scenario) ?? { attempts: 0, healed: 0 };
|
|
190
|
+
ss.healed += 1;
|
|
191
|
+
scenarioStats.set(scenario, ss);
|
|
192
|
+
const startTs = openAttempts.get(attemptKey(e));
|
|
193
|
+
if (startTs !== undefined) {
|
|
194
|
+
retryLatencies.push(Date.parse(e.ts) - startTs);
|
|
195
|
+
openAttempts.delete(attemptKey(e));
|
|
196
|
+
}
|
|
197
|
+
continue;
|
|
198
|
+
}
|
|
199
|
+
if (e.event === "heal_unavailable") {
|
|
200
|
+
healUnavailable += 1;
|
|
201
|
+
openAttempts.delete(attemptKey(e));
|
|
202
|
+
continue;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const sortedFailingFields = Array.from(fieldAttempts.values())
|
|
206
|
+
.sort((a, b) => b.attempts - a.attempts)
|
|
207
|
+
.slice(0, topN);
|
|
208
|
+
const sortedErrors = Array.from(errorCounts.entries())
|
|
209
|
+
.map(([error, count]) => ({ error, count }))
|
|
210
|
+
.sort((a, b) => b.count - a.count)
|
|
211
|
+
.slice(0, topN);
|
|
212
|
+
const sortedCandidates = Array.from(candidateStats.entries())
|
|
213
|
+
.map(([selector, s]) => ({
|
|
214
|
+
selector,
|
|
215
|
+
promotions: s.promotions,
|
|
216
|
+
averageConfidence: s.confidenceCount > 0 ? s.confidenceSum / s.confidenceCount : null,
|
|
217
|
+
}))
|
|
218
|
+
.sort((a, b) => b.promotions - a.promotions)
|
|
219
|
+
.slice(0, topN);
|
|
220
|
+
const sortedScenarios = Array.from(scenarioStats.entries())
|
|
221
|
+
.map(([scenario, s]) => ({ scenario, ...s }))
|
|
222
|
+
.sort((a, b) => b.attempts - a.attempts)
|
|
223
|
+
.slice(0, topN);
|
|
224
|
+
const latenciesSorted = [...retryLatencies].sort((a, b) => a - b);
|
|
225
|
+
return {
|
|
226
|
+
version: PACKAGE_VERSION,
|
|
227
|
+
generatedAt: new Date().toISOString(),
|
|
228
|
+
source,
|
|
229
|
+
totals: {
|
|
230
|
+
registrations,
|
|
231
|
+
healAttempts,
|
|
232
|
+
healed,
|
|
233
|
+
healUnavailable,
|
|
234
|
+
healRate: healAttempts === 0 ? 0 : healed / healAttempts,
|
|
235
|
+
uniqueFields: uniqueFields.size,
|
|
236
|
+
uniquePages: uniquePages.size,
|
|
237
|
+
},
|
|
238
|
+
topFailingFields: sortedFailingFields,
|
|
239
|
+
topErrors: sortedErrors,
|
|
240
|
+
topCandidates: sortedCandidates,
|
|
241
|
+
retryLatencyMs: {
|
|
242
|
+
p50: percentile(latenciesSorted, 0.5),
|
|
243
|
+
p95: percentile(latenciesSorted, 0.95),
|
|
244
|
+
min: latenciesSorted.length ? latenciesSorted[0] : 0,
|
|
245
|
+
max: latenciesSorted.length
|
|
246
|
+
? latenciesSorted[latenciesSorted.length - 1]
|
|
247
|
+
: 0,
|
|
248
|
+
},
|
|
249
|
+
perScenario: sortedScenarios,
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
function percentile(sortedAsc, p) {
|
|
253
|
+
if (sortedAsc.length === 0)
|
|
254
|
+
return 0;
|
|
255
|
+
const idx = Math.ceil(sortedAsc.length * p) - 1;
|
|
256
|
+
return sortedAsc[Math.max(0, Math.min(idx, sortedAsc.length - 1))];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Normalise an error message so different timestamps / IDs collapse to
|
|
260
|
+
* the same bucket: lowercase, strip leading "Error: " / "TimeoutError",
|
|
261
|
+
* collapse runs of digits.
|
|
262
|
+
*/
|
|
263
|
+
function truncateError(s) {
|
|
264
|
+
return s
|
|
265
|
+
.toLowerCase()
|
|
266
|
+
.replace(/^(?:error|timeouterror|protocolerror):\s*/i, "")
|
|
267
|
+
.replace(/\b\d{3,}\b/g, "<n>")
|
|
268
|
+
.slice(0, 120);
|
|
269
|
+
}
|
|
270
|
+
/* ─── filesystem glue ─────────────────────────────────────────────── */
|
|
271
|
+
async function resolveJsonlPath(inputPath) {
|
|
272
|
+
const stat = await fs.stat(inputPath).catch(() => undefined);
|
|
273
|
+
if (stat?.isFile())
|
|
274
|
+
return inputPath;
|
|
275
|
+
if (stat?.isDirectory()) {
|
|
276
|
+
const direct = path.join(inputPath, "heal-events.jsonl");
|
|
277
|
+
if (await fs.pathExists(direct))
|
|
278
|
+
return direct;
|
|
279
|
+
const nested = path.join(inputPath, "artefacts", "heal-events.jsonl");
|
|
280
|
+
if (await fs.pathExists(nested))
|
|
281
|
+
return nested;
|
|
282
|
+
return nested; // analyser will handle 'no events' gracefully
|
|
283
|
+
}
|
|
284
|
+
return path.join(inputPath, "artefacts", "heal-events.jsonl");
|
|
285
|
+
}
|
|
286
|
+
async function readEvents(jsonlPath) {
|
|
287
|
+
if (!(await fs.pathExists(jsonlPath)))
|
|
288
|
+
return [];
|
|
289
|
+
const raw = await fs.readFile(jsonlPath, "utf8");
|
|
290
|
+
const out = [];
|
|
291
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
292
|
+
if (!line.trim())
|
|
293
|
+
continue;
|
|
294
|
+
try {
|
|
295
|
+
out.push(JSON.parse(line));
|
|
296
|
+
}
|
|
297
|
+
catch {
|
|
298
|
+
/* skip malformed line */
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
return out;
|
|
302
|
+
}
|
|
303
|
+
//# sourceMappingURL=healStats.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"healStats.js","sourceRoot":"","sources":["../../src/reports/healStats.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vijaypjavvadi/bdd2pw",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.0",
|
|
4
4
|
"description": "Scaffold runnable Playwright TypeScript tests from Gherkin .feature files. Detects existing Page Objects, scans live pages via Microsoft Playwright MCP, emits POMs + specs, ready for execution. CLI + HTTP service. Optional governed LLM fallback (Anthropic / OpenAI / Gemini).",
|
|
5
5
|
"bin": {
|
|
6
6
|
"bdd2pw": "dist/cli.js"
|