@aplaytest/core 0.1.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/dist/.tsbuildinfo +1 -0
- package/dist/config/schema.d.ts +247 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +279 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/evidence/id.d.ts +17 -0
- package/dist/evidence/id.d.ts.map +1 -0
- package/dist/evidence/id.js +20 -0
- package/dist/evidence/id.js.map +1 -0
- package/dist/evidence/redact.d.ts +22 -0
- package/dist/evidence/redact.d.ts.map +1 -0
- package/dist/evidence/redact.js +97 -0
- package/dist/evidence/redact.js.map +1 -0
- package/dist/evidence/store.d.ts +51 -0
- package/dist/evidence/store.d.ts.map +1 -0
- package/dist/evidence/store.js +148 -0
- package/dist/evidence/store.js.map +1 -0
- package/dist/evidence/types.d.ts +183 -0
- package/dist/evidence/types.d.ts.map +1 -0
- package/dist/evidence/types.js +21 -0
- package/dist/evidence/types.js.map +1 -0
- package/dist/history/ingest-playwright.d.ts +26 -0
- package/dist/history/ingest-playwright.d.ts.map +1 -0
- package/dist/history/ingest-playwright.js +73 -0
- package/dist/history/ingest-playwright.js.map +1 -0
- package/dist/history/ingest.d.ts +29 -0
- package/dist/history/ingest.d.ts.map +1 -0
- package/dist/history/ingest.js +86 -0
- package/dist/history/ingest.js.map +1 -0
- package/dist/history/memory-index.d.ts +88 -0
- package/dist/history/memory-index.d.ts.map +1 -0
- package/dist/history/memory-index.js +236 -0
- package/dist/history/memory-index.js.map +1 -0
- package/dist/history/playwright-report.d.ts +102 -0
- package/dist/history/playwright-report.d.ts.map +1 -0
- package/dist/history/playwright-report.js +274 -0
- package/dist/history/playwright-report.js.map +1 -0
- package/dist/history/store.d.ts +73 -0
- package/dist/history/store.d.ts.map +1 -0
- package/dist/history/store.js +243 -0
- package/dist/history/store.js.map +1 -0
- package/dist/history/types.d.ts +69 -0
- package/dist/history/types.d.ts.map +1 -0
- package/dist/history/types.js +18 -0
- package/dist/history/types.js.map +1 -0
- package/dist/history/url.d.ts +79 -0
- package/dist/history/url.d.ts.map +1 -0
- package/dist/history/url.js +121 -0
- package/dist/history/url.js.map +1 -0
- package/dist/index.d.ts +39 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +31 -0
- package/dist/index.js.map +1 -0
- package/dist/locator/stability.d.ts +63 -0
- package/dist/locator/stability.d.ts.map +1 -0
- package/dist/locator/stability.js +127 -0
- package/dist/locator/stability.js.map +1 -0
- package/dist/taxonomy/classify.d.ts +63 -0
- package/dist/taxonomy/classify.d.ts.map +1 -0
- package/dist/taxonomy/classify.js +344 -0
- package/dist/taxonomy/classify.js.map +1 -0
- package/dist/taxonomy/kinds.d.ts +49 -0
- package/dist/taxonomy/kinds.d.ts.map +1 -0
- package/dist/taxonomy/kinds.js +139 -0
- package/dist/taxonomy/kinds.js.map +1 -0
- package/dist/taxonomy/parse-error.d.ts +85 -0
- package/dist/taxonomy/parse-error.d.ts.map +1 -0
- package/dist/taxonomy/parse-error.js +179 -0
- package/dist/taxonomy/parse-error.js.map +1 -0
- package/dist/version.d.ts +10 -0
- package/dist/version.d.ts.map +1 -0
- package/dist/version.js +10 -0
- package/dist/version.js.map +1 -0
- package/dist-cjs/.tsbuildinfo +1 -0
- package/dist-cjs/config/schema.js +283 -0
- package/dist-cjs/config/schema.js.map +1 -0
- package/dist-cjs/evidence/id.js +24 -0
- package/dist-cjs/evidence/id.js.map +1 -0
- package/dist-cjs/evidence/redact.js +103 -0
- package/dist-cjs/evidence/redact.js.map +1 -0
- package/dist-cjs/evidence/store.js +156 -0
- package/dist-cjs/evidence/store.js.map +1 -0
- package/dist-cjs/evidence/types.js +25 -0
- package/dist-cjs/evidence/types.js.map +1 -0
- package/dist-cjs/history/ingest-playwright.js +76 -0
- package/dist-cjs/history/ingest-playwright.js.map +1 -0
- package/dist-cjs/history/ingest.js +89 -0
- package/dist-cjs/history/ingest.js.map +1 -0
- package/dist-cjs/history/memory-index.js +242 -0
- package/dist-cjs/history/memory-index.js.map +1 -0
- package/dist-cjs/history/playwright-report.js +277 -0
- package/dist-cjs/history/playwright-report.js.map +1 -0
- package/dist-cjs/history/store.js +247 -0
- package/dist-cjs/history/store.js.map +1 -0
- package/dist-cjs/history/types.js +23 -0
- package/dist-cjs/history/types.js.map +1 -0
- package/dist-cjs/history/url.js +127 -0
- package/dist-cjs/history/url.js.map +1 -0
- package/dist-cjs/index.js +88 -0
- package/dist-cjs/index.js.map +1 -0
- package/dist-cjs/locator/stability.js +134 -0
- package/dist-cjs/locator/stability.js.map +1 -0
- package/dist-cjs/package.json +3 -0
- package/dist-cjs/taxonomy/classify.js +348 -0
- package/dist-cjs/taxonomy/classify.js.map +1 -0
- package/dist-cjs/taxonomy/kinds.js +145 -0
- package/dist-cjs/taxonomy/kinds.js.map +1 -0
- package/dist-cjs/taxonomy/parse-error.js +186 -0
- package/dist-cjs/taxonomy/parse-error.js.map +1 -0
- package/dist-cjs/version.js +13 -0
- package/dist-cjs/version.js.map +1 -0
- package/package.json +39 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The evidence store: plain JSON on disk, one directory per run.
|
|
3
|
+
*
|
|
4
|
+
* <dir>/<runId>/<evidenceId>.json
|
|
5
|
+
*
|
|
6
|
+
* Deliberately not a binary format and not a database. A human with `jq` must
|
|
7
|
+
* be able to read a bundle, and a CI job must be able to upload the directory
|
|
8
|
+
* as an artifact with no export step. Every bundle carries `schemaVersion` so
|
|
9
|
+
* a reader can refuse politely rather than mis-parse.
|
|
10
|
+
*/
|
|
11
|
+
import { mkdir, readdir, readFile, rm, stat, writeFile } from 'node:fs/promises';
|
|
12
|
+
import { dirname, join } from 'node:path';
|
|
13
|
+
import { redact } from './redact.js';
|
|
14
|
+
import { EVIDENCE_SCHEMA_VERSION } from './types.js';
|
|
15
|
+
const JSON_EXTENSION = '.json';
|
|
16
|
+
function isRecord(value) {
|
|
17
|
+
return typeof value === 'object' && value !== null;
|
|
18
|
+
}
|
|
19
|
+
export function isEvidenceBundle(value) {
|
|
20
|
+
if (!isRecord(value))
|
|
21
|
+
return false;
|
|
22
|
+
if (value['schemaVersion'] !== EVIDENCE_SCHEMA_VERSION)
|
|
23
|
+
return false;
|
|
24
|
+
if (typeof value['id'] !== 'string')
|
|
25
|
+
return false;
|
|
26
|
+
if (!isRecord(value['test']) || typeof value['test']['title'] !== 'string')
|
|
27
|
+
return false;
|
|
28
|
+
if (!isRecord(value['failure']) || typeof value['failure']['kind'] !== 'string')
|
|
29
|
+
return false;
|
|
30
|
+
return true;
|
|
31
|
+
}
|
|
32
|
+
export function parseEvidenceBundle(value) {
|
|
33
|
+
return isEvidenceBundle(value) ? value : null;
|
|
34
|
+
}
|
|
35
|
+
function schemaVersionOf(value) {
|
|
36
|
+
return isRecord(value) ? value['schemaVersion'] : undefined;
|
|
37
|
+
}
|
|
38
|
+
async function latestRunDirectory(evidenceDir) {
|
|
39
|
+
const entries = await readdir(evidenceDir, { withFileTypes: true }).catch(() => []);
|
|
40
|
+
const dirs = entries.filter(entry => entry.isDirectory()).map(entry => entry.name).sort();
|
|
41
|
+
return dirs[dirs.length - 1] ?? null;
|
|
42
|
+
}
|
|
43
|
+
export async function loadRunBundles(evidenceDir, runId) {
|
|
44
|
+
const resolvedRunId = runId ?? (await latestRunDirectory(evidenceDir));
|
|
45
|
+
if (resolvedRunId === null)
|
|
46
|
+
return { runId: null, bundles: [], skipped: [] };
|
|
47
|
+
const dir = join(evidenceDir, resolvedRunId);
|
|
48
|
+
const files = (await readdir(dir).catch(() => [])).filter(file => file.endsWith(JSON_EXTENSION));
|
|
49
|
+
const bundles = [];
|
|
50
|
+
const skipped = [];
|
|
51
|
+
for (const file of files) {
|
|
52
|
+
const raw = await readFile(join(dir, file), 'utf8').catch(() => null);
|
|
53
|
+
if (raw === null) {
|
|
54
|
+
skipped.push({ file, reason: 'unreadable' });
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
let parsed;
|
|
58
|
+
try {
|
|
59
|
+
parsed = JSON.parse(raw);
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
skipped.push({ file, reason: 'unreadable' });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
const bundle = parseEvidenceBundle(parsed);
|
|
66
|
+
if (bundle === null) {
|
|
67
|
+
const version = schemaVersionOf(parsed);
|
|
68
|
+
skipped.push({
|
|
69
|
+
file,
|
|
70
|
+
reason: typeof version === 'number'
|
|
71
|
+
? `schemaVersion ${version}, this build reads ${EVIDENCE_SCHEMA_VERSION}`
|
|
72
|
+
: 'malformed',
|
|
73
|
+
});
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
bundles.push(bundle);
|
|
77
|
+
}
|
|
78
|
+
return { runId: resolvedRunId, bundles, skipped };
|
|
79
|
+
}
|
|
80
|
+
export class SchemaVersionError extends Error {
|
|
81
|
+
found;
|
|
82
|
+
expected;
|
|
83
|
+
path;
|
|
84
|
+
constructor(found, expected, path) {
|
|
85
|
+
super(`Evidence bundle at ${path} is schemaVersion ${found}, this build reads ${expected}. ` +
|
|
86
|
+
`Re-run the suite to regenerate, or use a matching atest version.`);
|
|
87
|
+
this.found = found;
|
|
88
|
+
this.expected = expected;
|
|
89
|
+
this.path = path;
|
|
90
|
+
this.name = 'SchemaVersionError';
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
export class EvidenceStore {
|
|
94
|
+
options;
|
|
95
|
+
constructor(options) {
|
|
96
|
+
this.options = options;
|
|
97
|
+
}
|
|
98
|
+
bundlePath(runId, id) {
|
|
99
|
+
return join(this.options.dir, runId, `${id}.json`);
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Redaction happens HERE, on the write path, so there is exactly one place
|
|
103
|
+
* that can forget to do it.
|
|
104
|
+
*/
|
|
105
|
+
async write(bundle) {
|
|
106
|
+
const safe = redact(bundle, this.options.redactKeys);
|
|
107
|
+
const path = this.bundlePath(bundle.runId, bundle.id);
|
|
108
|
+
await mkdir(dirname(path), { recursive: true });
|
|
109
|
+
await writeFile(path, `${JSON.stringify(safe, null, 2)}\n`, 'utf8');
|
|
110
|
+
return path;
|
|
111
|
+
}
|
|
112
|
+
async read(runId, id) {
|
|
113
|
+
const path = this.bundlePath(runId, id);
|
|
114
|
+
const raw = await readFile(path, 'utf8');
|
|
115
|
+
const parsed = JSON.parse(raw);
|
|
116
|
+
const bundle = parseEvidenceBundle(parsed);
|
|
117
|
+
if (bundle === null) {
|
|
118
|
+
throw new SchemaVersionError(Number(schemaVersionOf(parsed)), EVIDENCE_SCHEMA_VERSION, path);
|
|
119
|
+
}
|
|
120
|
+
return bundle;
|
|
121
|
+
}
|
|
122
|
+
/** Run ids, newest first by directory mtime. */
|
|
123
|
+
async listRuns() {
|
|
124
|
+
const entries = await readdir(this.options.dir, { withFileTypes: true }).catch(() => []);
|
|
125
|
+
const dirs = entries.filter(e => e.isDirectory()).map(e => e.name);
|
|
126
|
+
const withTime = await Promise.all(dirs.map(async (name) => ({
|
|
127
|
+
name,
|
|
128
|
+
mtime: await stat(join(this.options.dir, name))
|
|
129
|
+
.then(s => s.mtimeMs)
|
|
130
|
+
.catch(() => 0),
|
|
131
|
+
})));
|
|
132
|
+
return withTime.sort((a, b) => b.mtime - a.mtime).map(e => e.name);
|
|
133
|
+
}
|
|
134
|
+
async listBundles(runId) {
|
|
135
|
+
const entries = await readdir(join(this.options.dir, runId)).catch(() => []);
|
|
136
|
+
return entries
|
|
137
|
+
.filter(file => file.endsWith(JSON_EXTENSION))
|
|
138
|
+
.map(file => file.slice(0, -JSON_EXTENSION.length));
|
|
139
|
+
}
|
|
140
|
+
/** Drop the oldest runs beyond the retention limit. Returns what it removed. */
|
|
141
|
+
async prune() {
|
|
142
|
+
const runs = await this.listRuns();
|
|
143
|
+
const doomed = runs.slice(this.options.retainRuns);
|
|
144
|
+
await Promise.all(doomed.map(r => rm(join(this.options.dir, r), { recursive: true, force: true })));
|
|
145
|
+
return doomed;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
//# sourceMappingURL=store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"store.js","sourceRoot":"","sources":["../../src/evidence/store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Evidence Bundle — the central data structure of the system.
|
|
3
|
+
*
|
|
4
|
+
* Everything downstream (healing, flaky classification, MCP resources, report
|
|
5
|
+
* insights) consumes this and only this. Engines never re-open a browser to
|
|
6
|
+
* "go look"; if a field is missing, the fix is to capture it at source, not to
|
|
7
|
+
* re-run the test.
|
|
8
|
+
*
|
|
9
|
+
* DESIGN NOTE — nullable, not optional. Fields that may be absent are typed
|
|
10
|
+
* `T | null` rather than `field?: T`. Bundles are written to disk as JSON and
|
|
11
|
+
* read back by other processes (and by humans with `jq`); `undefined`
|
|
12
|
+
* disappears on serialisation, `null` round-trips. Every bundle therefore has
|
|
13
|
+
* the same key set regardless of what was captured.
|
|
14
|
+
*/
|
|
15
|
+
import type { FailureKind } from '../taxonomy/kinds.js';
|
|
16
|
+
import type { LocatorStrategy } from '../locator/stability.js';
|
|
17
|
+
/** sha256(runId, testId, project, retry) — stable and reproducible. */
|
|
18
|
+
export type EvidenceId = string & {
|
|
19
|
+
readonly __brand: 'EvidenceId';
|
|
20
|
+
};
|
|
21
|
+
export declare const EVIDENCE_SCHEMA_VERSION: 1;
|
|
22
|
+
export interface Rect {
|
|
23
|
+
readonly x: number;
|
|
24
|
+
readonly y: number;
|
|
25
|
+
readonly width: number;
|
|
26
|
+
readonly height: number;
|
|
27
|
+
}
|
|
28
|
+
/** One page-object call, captured by the bindPage wrapper. */
|
|
29
|
+
export interface StepRecord {
|
|
30
|
+
/** e.g. "gymsPage" */
|
|
31
|
+
readonly pageObject: string;
|
|
32
|
+
/** e.g. "expectCardData" */
|
|
33
|
+
readonly method: string;
|
|
34
|
+
/** Domain arguments, JSON-safe and redacted. */
|
|
35
|
+
readonly args: readonly unknown[];
|
|
36
|
+
readonly startedAt: string;
|
|
37
|
+
readonly durationMs: number;
|
|
38
|
+
readonly failed: boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare function formatFailingStep(step: StepRecord | null): string | null;
|
|
41
|
+
/** Where a selector literal lives in source — the target of a heal patch. */
|
|
42
|
+
export interface SelectorSource {
|
|
43
|
+
readonly file: string;
|
|
44
|
+
readonly line: number;
|
|
45
|
+
/** e.g. "TEST_IDS.cardName" */
|
|
46
|
+
readonly constantPath: string;
|
|
47
|
+
/** Other constants in the same file holding the identical literal. */
|
|
48
|
+
readonly aliases: readonly string[];
|
|
49
|
+
}
|
|
50
|
+
export interface LocatorCandidate {
|
|
51
|
+
readonly strategy: LocatorStrategy;
|
|
52
|
+
/** e.g. "getByRole('option', { name: 'Cork' })" */
|
|
53
|
+
readonly expression: string;
|
|
54
|
+
/** Must be exactly 1 to be eligible — ambiguity is disqualifying. */
|
|
55
|
+
readonly matchCount: number;
|
|
56
|
+
readonly visible: boolean;
|
|
57
|
+
readonly enabled: boolean;
|
|
58
|
+
readonly accessibleName: string | null;
|
|
59
|
+
readonly boundingBox: Rect | null;
|
|
60
|
+
/** 0 = means the same thing, 1 = unrelated. Tier-0 heuristic. */
|
|
61
|
+
readonly semanticDistance: number;
|
|
62
|
+
/** 0 = testid … 6 = xpath. See locator/stability. */
|
|
63
|
+
readonly stabilityRank: number;
|
|
64
|
+
}
|
|
65
|
+
export interface RequestRecord {
|
|
66
|
+
readonly url: string;
|
|
67
|
+
readonly method: string;
|
|
68
|
+
readonly status: number | null;
|
|
69
|
+
readonly durationMs: number;
|
|
70
|
+
readonly failureText: string | null;
|
|
71
|
+
/** Set when the body was parsed against a wire schema and failed. */
|
|
72
|
+
readonly schemaError: string | null;
|
|
73
|
+
}
|
|
74
|
+
export interface AppSpanRecord {
|
|
75
|
+
readonly spanId: string;
|
|
76
|
+
readonly name: string;
|
|
77
|
+
readonly durationMs: number;
|
|
78
|
+
readonly status: 'ok' | 'error' | 'unset';
|
|
79
|
+
}
|
|
80
|
+
export interface EvidenceBundle {
|
|
81
|
+
readonly schemaVersion: typeof EVIDENCE_SCHEMA_VERSION;
|
|
82
|
+
readonly id: EvidenceId;
|
|
83
|
+
readonly runId: string;
|
|
84
|
+
/** Joins to OpenTelemetry / Tempo. Derived, not random. */
|
|
85
|
+
readonly traceId: string;
|
|
86
|
+
readonly capturedAt: string;
|
|
87
|
+
readonly test: {
|
|
88
|
+
/** Playwright's stable test id — survives a title rename. */
|
|
89
|
+
readonly id: string;
|
|
90
|
+
readonly title: string;
|
|
91
|
+
readonly titlePath: readonly string[];
|
|
92
|
+
readonly file: string;
|
|
93
|
+
readonly line: number;
|
|
94
|
+
readonly project: string;
|
|
95
|
+
readonly tags: readonly string[];
|
|
96
|
+
readonly retry: number;
|
|
97
|
+
readonly workerIndex: number;
|
|
98
|
+
readonly shard: {
|
|
99
|
+
readonly current: number;
|
|
100
|
+
readonly total: number;
|
|
101
|
+
} | null;
|
|
102
|
+
};
|
|
103
|
+
readonly failure: {
|
|
104
|
+
readonly kind: FailureKind;
|
|
105
|
+
readonly message: string;
|
|
106
|
+
readonly stack: string;
|
|
107
|
+
/** e.g. "toBeVisible" | "toHaveText" */
|
|
108
|
+
readonly matcher: string | null;
|
|
109
|
+
readonly expected: string | null;
|
|
110
|
+
readonly actual: string | null;
|
|
111
|
+
readonly timedOut: boolean;
|
|
112
|
+
};
|
|
113
|
+
/** What the test was trying to do, in domain terms. */
|
|
114
|
+
readonly intent: {
|
|
115
|
+
readonly steps: readonly StepRecord[];
|
|
116
|
+
readonly failingStep: StepRecord | null;
|
|
117
|
+
readonly selector: string | null;
|
|
118
|
+
readonly selectorSource: SelectorSource | null;
|
|
119
|
+
};
|
|
120
|
+
/** Primary model-facing page representation. ARIA first: cheap and semantic. */
|
|
121
|
+
readonly page: {
|
|
122
|
+
readonly url: string;
|
|
123
|
+
readonly title: string;
|
|
124
|
+
readonly ariaSnapshot: string;
|
|
125
|
+
readonly candidates: readonly LocatorCandidate[];
|
|
126
|
+
/** Structural skeleton only, sent when ARIA is insufficient. */
|
|
127
|
+
readonly htmlDigest: string | null;
|
|
128
|
+
/** Every data-testid present. The cheapest answer to "was it renamed?" */
|
|
129
|
+
readonly testIdsPresent: readonly string[];
|
|
130
|
+
};
|
|
131
|
+
readonly visual: {
|
|
132
|
+
readonly screenshotPath: string | null;
|
|
133
|
+
readonly diffPath: string | null;
|
|
134
|
+
readonly diffPixelRatio: number | null;
|
|
135
|
+
};
|
|
136
|
+
readonly network: {
|
|
137
|
+
readonly failed: readonly RequestRecord[];
|
|
138
|
+
readonly slow: readonly RequestRecord[];
|
|
139
|
+
readonly statusCounts: Readonly<Record<string, number>>;
|
|
140
|
+
};
|
|
141
|
+
readonly console: {
|
|
142
|
+
readonly errors: readonly string[];
|
|
143
|
+
readonly warnings: readonly string[];
|
|
144
|
+
};
|
|
145
|
+
readonly timing: {
|
|
146
|
+
readonly testMs: number;
|
|
147
|
+
readonly failingActionMs: number | null;
|
|
148
|
+
readonly navigationMs: number | null;
|
|
149
|
+
/** testMs / configured timeout. Near 1.0 implies a timing failure. */
|
|
150
|
+
readonly budgetUsedRatio: number;
|
|
151
|
+
};
|
|
152
|
+
readonly env: {
|
|
153
|
+
readonly appEnv: string;
|
|
154
|
+
readonly baseUrl: string;
|
|
155
|
+
readonly browser: string;
|
|
156
|
+
readonly platform: string;
|
|
157
|
+
readonly workers: number;
|
|
158
|
+
readonly commit: string;
|
|
159
|
+
readonly changedPaths: readonly string[];
|
|
160
|
+
};
|
|
161
|
+
/** Populated when an OTel backend is reachable; joined on traceId. */
|
|
162
|
+
readonly appSpans: readonly AppSpanRecord[] | null;
|
|
163
|
+
readonly artifacts: {
|
|
164
|
+
readonly tracePath: string | null;
|
|
165
|
+
readonly videoPath: string | null;
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* The subset of a bundle the classifier needs. Keeping this narrow means
|
|
170
|
+
* classification can be unit-tested against small fixtures instead of
|
|
171
|
+
* requiring a full bundle, and makes the classifier reusable at capture time
|
|
172
|
+
* before the rest of the bundle is assembled.
|
|
173
|
+
*/
|
|
174
|
+
export interface ClassifiableFailure {
|
|
175
|
+
readonly message: string;
|
|
176
|
+
readonly stack: string;
|
|
177
|
+
readonly matcher: string | null;
|
|
178
|
+
readonly timedOut: boolean;
|
|
179
|
+
readonly consoleErrors: readonly string[];
|
|
180
|
+
readonly failedRequests: readonly RequestRecord[];
|
|
181
|
+
readonly budgetUsedRatio: number;
|
|
182
|
+
}
|
|
183
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/evidence/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The Evidence Bundle — the central data structure of the system.
|
|
3
|
+
*
|
|
4
|
+
* Everything downstream (healing, flaky classification, MCP resources, report
|
|
5
|
+
* insights) consumes this and only this. Engines never re-open a browser to
|
|
6
|
+
* "go look"; if a field is missing, the fix is to capture it at source, not to
|
|
7
|
+
* re-run the test.
|
|
8
|
+
*
|
|
9
|
+
* DESIGN NOTE — nullable, not optional. Fields that may be absent are typed
|
|
10
|
+
* `T | null` rather than `field?: T`. Bundles are written to disk as JSON and
|
|
11
|
+
* read back by other processes (and by humans with `jq`); `undefined`
|
|
12
|
+
* disappears on serialisation, `null` round-trips. Every bundle therefore has
|
|
13
|
+
* the same key set regardless of what was captured.
|
|
14
|
+
*/
|
|
15
|
+
export const EVIDENCE_SCHEMA_VERSION = 1;
|
|
16
|
+
export function formatFailingStep(step) {
|
|
17
|
+
if (step === null)
|
|
18
|
+
return null;
|
|
19
|
+
return `${step.pageObject}.${step.method}(${step.args.join(', ')})`;
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/evidence/types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAQH,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAU,CAAC;AAsBlD,MAAM,UAAU,iBAAiB,CAAC,IAAuB;IACvD,IAAI,IAAI,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IAC/B,OAAO,GAAG,IAAI,CAAC,UAAU,IAAI,IAAI,CAAC,MAAM,IAAI,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;AACtE,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingest a Playwright JSON report into the history store.
|
|
3
|
+
*
|
|
4
|
+
* The I/O half of the adapter: read, parse, hand to `mapPlaywrightReport`,
|
|
5
|
+
* store. All of the mapping decisions — identity, timestamps, classification —
|
|
6
|
+
* live in `playwright-report.ts` and are unit-tested without a database.
|
|
7
|
+
*
|
|
8
|
+
* Like `ingestDirectory`, this NEVER throws. A bad report is reported through
|
|
9
|
+
* `IngestResult.skipped` so that one unreadable artifact among many cannot cost
|
|
10
|
+
* the others, and so a CI analyze job degrades to "nothing ingested" rather
|
|
11
|
+
* than to a red build.
|
|
12
|
+
*/
|
|
13
|
+
import { type RunIdentity } from './playwright-report.js';
|
|
14
|
+
import type { HistoryStore } from './store.js';
|
|
15
|
+
import type { IngestResult } from './ingest.js';
|
|
16
|
+
export interface IngestPlaywrightJsonOptions {
|
|
17
|
+
/**
|
|
18
|
+
* Run-level facts the report may not carry. Defaults read the GitHub Actions
|
|
19
|
+
* environment, matching the reporter's own convention so both paths label a
|
|
20
|
+
* run the same way. The report still wins wherever it has an answer.
|
|
21
|
+
*/
|
|
22
|
+
readonly identity?: Partial<RunIdentity>;
|
|
23
|
+
readonly now?: () => number;
|
|
24
|
+
}
|
|
25
|
+
export declare function ingestPlaywrightJson(store: HistoryStore, path: string, options?: IngestPlaywrightJsonOptions): Promise<IngestResult>;
|
|
26
|
+
//# sourceMappingURL=ingest-playwright.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-playwright.d.ts","sourceRoot":"","sources":["../../src/history/ingest-playwright.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAIH,OAAO,EAAwC,KAAK,WAAW,EAAE,MAAM,wBAAwB,CAAC;AAChG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,MAAM,WAAW,2BAA2B;IAC1C;;;;OAIG;IACH,QAAQ,CAAC,QAAQ,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAC;IACzC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;CAC7B;AA0BD,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,YAAY,EACnB,IAAI,EAAE,MAAM,EACZ,OAAO,GAAE,2BAAgC,GACxC,OAAO,CAAC,YAAY,CAAC,CAyCvB"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingest a Playwright JSON report into the history store.
|
|
3
|
+
*
|
|
4
|
+
* The I/O half of the adapter: read, parse, hand to `mapPlaywrightReport`,
|
|
5
|
+
* store. All of the mapping decisions — identity, timestamps, classification —
|
|
6
|
+
* live in `playwright-report.ts` and are unit-tested without a database.
|
|
7
|
+
*
|
|
8
|
+
* Like `ingestDirectory`, this NEVER throws. A bad report is reported through
|
|
9
|
+
* `IngestResult.skipped` so that one unreadable artifact among many cannot cost
|
|
10
|
+
* the others, and so a CI analyze job degrades to "nothing ingested" rather
|
|
11
|
+
* than to a red build.
|
|
12
|
+
*/
|
|
13
|
+
import { readFile } from 'node:fs/promises';
|
|
14
|
+
import { mapPlaywrightReport } from './playwright-report.js';
|
|
15
|
+
function isRecord(value) {
|
|
16
|
+
return typeof value === 'object' && value !== null;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The same environment variables the reporter reads. Kept identical on purpose:
|
|
20
|
+
* a run ingested through the JSON path and one written by the reporter should
|
|
21
|
+
* be labelled by the same rules, or comparing them later means comparing the
|
|
22
|
+
* conventions of two different code paths.
|
|
23
|
+
*/
|
|
24
|
+
function identityFromEnv(overrides) {
|
|
25
|
+
const env = process.env;
|
|
26
|
+
return {
|
|
27
|
+
commit: overrides?.commit ?? env['GITHUB_SHA'] ?? null,
|
|
28
|
+
branch: overrides?.branch ?? env['GITHUB_REF_NAME'] ?? null,
|
|
29
|
+
appEnv: overrides?.appEnv ?? env['APP_ENV'] ?? null,
|
|
30
|
+
ci: overrides?.ci ?? (env['CI'] === 'true' || env['CI'] === '1'),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function failed(path, reason) {
|
|
34
|
+
return { filesRead: 1, runsIngested: 0, attemptsIngested: 0, skipped: [{ file: path, reason }] };
|
|
35
|
+
}
|
|
36
|
+
export async function ingestPlaywrightJson(store, path, options = {}) {
|
|
37
|
+
const raw = await readFile(path, 'utf8').catch(() => null);
|
|
38
|
+
if (raw === null) {
|
|
39
|
+
return { filesRead: 0, runsIngested: 0, attemptsIngested: 0, skipped: [{ file: path, reason: 'unreadable' }] };
|
|
40
|
+
}
|
|
41
|
+
let parsed;
|
|
42
|
+
try {
|
|
43
|
+
parsed = JSON.parse(raw);
|
|
44
|
+
}
|
|
45
|
+
catch {
|
|
46
|
+
return failed(path, 'not valid JSON');
|
|
47
|
+
}
|
|
48
|
+
if (!isRecord(parsed))
|
|
49
|
+
return failed(path, 'not a Playwright JSON report');
|
|
50
|
+
const { run, collapsed } = mapPlaywrightReport(parsed, {
|
|
51
|
+
identity: identityFromEnv(options.identity),
|
|
52
|
+
...(options.now === undefined ? {} : { now: options.now }),
|
|
53
|
+
});
|
|
54
|
+
try {
|
|
55
|
+
await store.ingest(run);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
return failed(path, `could not be stored: ${error instanceof Error ? error.message : String(error)}`);
|
|
59
|
+
}
|
|
60
|
+
return {
|
|
61
|
+
filesRead: 1,
|
|
62
|
+
runsIngested: 1,
|
|
63
|
+
attemptsIngested: run.attempts.length,
|
|
64
|
+
// Surfaced through the channel the CLI already prints as warnings. A
|
|
65
|
+
// collapsed duplicate is a real loss of an attempt and has to be visible;
|
|
66
|
+
// it is not a reason to reject the other hundred-odd rows.
|
|
67
|
+
skipped: collapsed.map(what => ({
|
|
68
|
+
file: path,
|
|
69
|
+
reason: `duplicate attempt collapsed: ${what}`,
|
|
70
|
+
})),
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=ingest-playwright.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest-playwright.js","sourceRoot":"","sources":["../../src/history/ingest-playwright.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingest run records written by the reporter into the history store.
|
|
3
|
+
*
|
|
4
|
+
* The reporter writes plain JSON; this is the seam where those files become
|
|
5
|
+
* queryable history. Keeping it separate means a CI job can download shard
|
|
6
|
+
* artifacts from anywhere and ingest them in one pass, and a developer can
|
|
7
|
+
* replay months of archived runs to seed history without waiting for it to
|
|
8
|
+
* accumulate.
|
|
9
|
+
*/
|
|
10
|
+
import type { HistoryStore } from './store.js';
|
|
11
|
+
export interface IngestResult {
|
|
12
|
+
readonly filesRead: number;
|
|
13
|
+
readonly runsIngested: number;
|
|
14
|
+
readonly attemptsIngested: number;
|
|
15
|
+
/** Files that could not be ingested, with the reason. Never thrown. */
|
|
16
|
+
readonly skipped: readonly {
|
|
17
|
+
readonly file: string;
|
|
18
|
+
readonly reason: string;
|
|
19
|
+
}[];
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Ingest every `*.json` in a directory.
|
|
23
|
+
*
|
|
24
|
+
* A malformed or version-mismatched file is SKIPPED and reported, never
|
|
25
|
+
* thrown: one bad artifact among fifty shards must not cost you the other
|
|
26
|
+
* forty-nine, and a partial history is far more useful than none.
|
|
27
|
+
*/
|
|
28
|
+
export declare function ingestDirectory(store: HistoryStore, dir: string): Promise<IngestResult>;
|
|
29
|
+
//# sourceMappingURL=ingest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../src/history/ingest.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAMH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE/C,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,uEAAuE;IACvE,QAAQ,CAAC,OAAO,EAAE,SAAS;QAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;CACjF;AAuBD;;;;;;GAMG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,YAAY,EACnB,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,YAAY,CAAC,CA4CvB"}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ingest run records written by the reporter into the history store.
|
|
3
|
+
*
|
|
4
|
+
* The reporter writes plain JSON; this is the seam where those files become
|
|
5
|
+
* queryable history. Keeping it separate means a CI job can download shard
|
|
6
|
+
* artifacts from anywhere and ingest them in one pass, and a developer can
|
|
7
|
+
* replay months of archived runs to seed history without waiting for it to
|
|
8
|
+
* accumulate.
|
|
9
|
+
*/
|
|
10
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { RUN_SCHEMA_VERSION } from './types.js';
|
|
13
|
+
function isRecord(value) {
|
|
14
|
+
return typeof value === 'object' && value !== null;
|
|
15
|
+
}
|
|
16
|
+
function isRunRecord(value) {
|
|
17
|
+
if (!isRecord(value))
|
|
18
|
+
return false;
|
|
19
|
+
if (value['schemaVersion'] !== RUN_SCHEMA_VERSION)
|
|
20
|
+
return false;
|
|
21
|
+
if (typeof value['runId'] !== 'string' || value['runId'] === '')
|
|
22
|
+
return false;
|
|
23
|
+
return Array.isArray(value['attempts']);
|
|
24
|
+
}
|
|
25
|
+
function validate(parsed) {
|
|
26
|
+
if (!isRecord(parsed))
|
|
27
|
+
return 'not an object';
|
|
28
|
+
if (parsed['schemaVersion'] !== RUN_SCHEMA_VERSION) {
|
|
29
|
+
return `schemaVersion ${String(parsed['schemaVersion'])} — this build reads ${RUN_SCHEMA_VERSION}`;
|
|
30
|
+
}
|
|
31
|
+
if (typeof parsed['runId'] !== 'string' || parsed['runId'] === '')
|
|
32
|
+
return 'missing runId';
|
|
33
|
+
if (!Array.isArray(parsed['attempts']))
|
|
34
|
+
return 'missing attempts';
|
|
35
|
+
return isRunRecord(parsed) ? parsed : 'not an object';
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Ingest every `*.json` in a directory.
|
|
39
|
+
*
|
|
40
|
+
* A malformed or version-mismatched file is SKIPPED and reported, never
|
|
41
|
+
* thrown: one bad artifact among fifty shards must not cost you the other
|
|
42
|
+
* forty-nine, and a partial history is far more useful than none.
|
|
43
|
+
*/
|
|
44
|
+
export async function ingestDirectory(store, dir) {
|
|
45
|
+
const entries = await readdir(dir).catch(() => []);
|
|
46
|
+
const files = entries.filter(name => name.endsWith('.json'));
|
|
47
|
+
const skipped = [];
|
|
48
|
+
let runsIngested = 0;
|
|
49
|
+
let attemptsIngested = 0;
|
|
50
|
+
for (const name of files) {
|
|
51
|
+
const path = join(dir, name);
|
|
52
|
+
let parsed;
|
|
53
|
+
try {
|
|
54
|
+
parsed = JSON.parse(await readFile(path, 'utf8'));
|
|
55
|
+
}
|
|
56
|
+
catch (error) {
|
|
57
|
+
skipped.push({ file: name, reason: error instanceof Error ? error.message : 'unreadable' });
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
const validated = validate(parsed);
|
|
61
|
+
if (typeof validated === 'string') {
|
|
62
|
+
skipped.push({ file: name, reason: validated });
|
|
63
|
+
continue;
|
|
64
|
+
}
|
|
65
|
+
// The doc comment above promises a bad file is skipped, never thrown —
|
|
66
|
+
// but only the JSON parse and the shape check were guarded. A record that
|
|
67
|
+
// passes both and then fails at the database (an unbindable value, a
|
|
68
|
+
// constraint) threw straight out of here and rolled back the transaction,
|
|
69
|
+
// costing every other file in the batch. In CI that reads as history
|
|
70
|
+
// simply never accumulating.
|
|
71
|
+
try {
|
|
72
|
+
await store.ingest(validated);
|
|
73
|
+
}
|
|
74
|
+
catch (error) {
|
|
75
|
+
skipped.push({
|
|
76
|
+
file: name,
|
|
77
|
+
reason: `could not be stored: ${error instanceof Error ? error.message : String(error)}`,
|
|
78
|
+
});
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
runsIngested += 1;
|
|
82
|
+
attemptsIngested += validated.attempts.length;
|
|
83
|
+
}
|
|
84
|
+
return { filesRead: files.length, runsIngested, attemptsIngested, skipped };
|
|
85
|
+
}
|
|
86
|
+
//# sourceMappingURL=ingest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.js","sourceRoot":"","sources":["../../src/history/ingest.ts"],"names":[],"mappings":"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"}
|