@sungen/driver-ui 3.3.0-alpha.7 → 3.3.0-alpha.9
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/trace-identity/trace-run-record-reporter.d.ts +9 -0
- package/dist/trace-identity/trace-run-record-reporter.d.ts.map +1 -1
- package/dist/trace-identity/trace-run-record-reporter.js +35 -2
- package/dist/trace-identity/trace-run-record-reporter.js.map +1 -1
- package/package.json +2 -2
- package/src/trace-identity/trace-run-record-reporter.ts +28 -2
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* runId SUNGEN_RUN_ID when set (one id across CI shards), else a fresh UUID
|
|
9
9
|
* specs sha256 of every test file at run START (the code that ran)
|
|
10
|
+
* contract sha256 of every `.feature` and `contracts.lock.yaml` under `qa/` at run START (schema 2,
|
|
11
|
+
* #967): the contract the run saw. A trace proves a contract only while those bytes still
|
|
12
|
+
* hold — compared by content, so a touch, a checkout or a fresh clone stales nothing
|
|
10
13
|
* attempts per test attempt: testId, retry, status, window, and the sha256 of its trace.zip
|
|
11
14
|
* report where the JSON reporter wrote its report — traces are mapped THROUGH that report
|
|
12
15
|
* (merge-reports renames trace files; their content, and so the sha256, stays)
|
|
@@ -22,12 +25,18 @@
|
|
|
22
25
|
* use: { trace: 'on' },
|
|
23
26
|
*/
|
|
24
27
|
import type { FullConfig, Reporter, Suite, TestCase, TestResult } from '@playwright/test/reporter';
|
|
28
|
+
/**
|
|
29
|
+
* sha256 of every contract file under `<root>/qa` — each `*.feature` and `contracts.lock.yaml` — keyed
|
|
30
|
+
* by its posix path from `root`, sorted. Dot-directories, node_modules and symlinks are not followed.
|
|
31
|
+
*/
|
|
32
|
+
export declare function contractContent(root: string): Record<string, string>;
|
|
25
33
|
declare class TraceRunRecordReporter implements Reporter {
|
|
26
34
|
private config?;
|
|
27
35
|
private root;
|
|
28
36
|
private runId;
|
|
29
37
|
private startedAt;
|
|
30
38
|
private specs;
|
|
39
|
+
private contract;
|
|
31
40
|
private results;
|
|
32
41
|
printsToStdio(): boolean;
|
|
33
42
|
onBegin(config: FullConfig, suite: Suite): void;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-run-record-reporter.d.ts","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"trace-run-record-reporter.d.ts","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAgBnG;;;GAGG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAcpE;AAED,cAAM,sBAAuB,YAAW,QAAQ;IAC9C,OAAO,CAAC,MAAM,CAAC,CAAa;IAC5B,OAAO,CAAC,IAAI,CAAiB;IAC7B,OAAO,CAAC,KAAK,CAA6C;IAC1D,OAAO,CAAC,SAAS,CAA4B;IAC7C,OAAO,CAAC,KAAK,CAA8B;IAC3C,OAAO,CAAC,QAAQ,CAA8B;IAC9C,OAAO,CAAC,OAAO,CAAqD;IAEpE,aAAa,IAAI,OAAO;IAExB,OAAO,CAAC,MAAM,EAAE,UAAU,EAAE,KAAK,EAAE,KAAK,GAAG,IAAI;IAU/C,SAAS,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,UAAU,GAAG,IAAI;IAI7C,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA2B5B,8FAA8F;IAC9F,OAAO,CAAC,cAAc;IAStB,OAAO,CAAC,GAAG;CAIZ;AAED,eAAe,sBAAsB,CAAC"}
|
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.contractContent = contractContent;
|
|
36
37
|
const node_crypto_1 = require("node:crypto");
|
|
37
38
|
const fs = __importStar(require("node:fs"));
|
|
38
39
|
const path = __importStar(require("node:path"));
|
|
@@ -45,12 +46,43 @@ const sha256File = (file) => {
|
|
|
45
46
|
}
|
|
46
47
|
};
|
|
47
48
|
const posix = (p) => p.split(path.sep).join('/');
|
|
49
|
+
const isContractFile = (name) => name.endsWith('.feature') || name === 'contracts.lock.yaml';
|
|
50
|
+
/**
|
|
51
|
+
* sha256 of every contract file under `<root>/qa` — each `*.feature` and `contracts.lock.yaml` — keyed
|
|
52
|
+
* by its posix path from `root`, sorted. Dot-directories, node_modules and symlinks are not followed.
|
|
53
|
+
*/
|
|
54
|
+
function contractContent(root) {
|
|
55
|
+
const found = [];
|
|
56
|
+
const walk = (dir) => {
|
|
57
|
+
let entries;
|
|
58
|
+
try {
|
|
59
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
for (const e of entries) {
|
|
65
|
+
const full = path.join(dir, e.name);
|
|
66
|
+
if (e.isDirectory()) {
|
|
67
|
+
if (!e.name.startsWith('.') && e.name !== 'node_modules')
|
|
68
|
+
walk(full);
|
|
69
|
+
continue;
|
|
70
|
+
}
|
|
71
|
+
const sha = e.isFile() && isContractFile(e.name) ? sha256File(full) : null;
|
|
72
|
+
if (sha)
|
|
73
|
+
found.push([posix(path.relative(root, full)), sha]);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
walk(path.join(root, 'qa'));
|
|
77
|
+
return Object.fromEntries(found.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
78
|
+
}
|
|
48
79
|
class TraceRunRecordReporter {
|
|
49
80
|
constructor() {
|
|
50
81
|
this.root = process.cwd();
|
|
51
82
|
this.runId = process.env.SUNGEN_RUN_ID || (0, node_crypto_1.randomUUID)();
|
|
52
83
|
this.startedAt = new Date().toISOString();
|
|
53
84
|
this.specs = {};
|
|
85
|
+
this.contract = {};
|
|
54
86
|
this.results = [];
|
|
55
87
|
}
|
|
56
88
|
printsToStdio() { return false; }
|
|
@@ -58,6 +90,7 @@ class TraceRunRecordReporter {
|
|
|
58
90
|
this.config = config;
|
|
59
91
|
if (config.configFile)
|
|
60
92
|
this.root = path.dirname(config.configFile);
|
|
93
|
+
this.contract = contractContent(this.root);
|
|
61
94
|
for (const test of suite.allTests()) {
|
|
62
95
|
const rel = this.rel(test.location.file);
|
|
63
96
|
if (this.specs[rel] === undefined)
|
|
@@ -80,9 +113,9 @@ class TraceRunRecordReporter {
|
|
|
80
113
|
});
|
|
81
114
|
const shard = this.config.shard;
|
|
82
115
|
const record = {
|
|
83
|
-
schema:
|
|
116
|
+
schema: 2, tool: 'sungen-trace-run-record', runId: this.runId, startedAt: this.startedAt,
|
|
84
117
|
playwrightVersion: this.config.version, shard: shard ? `${shard.current}/${shard.total}` : null,
|
|
85
|
-
report: this.jsonReportPath(), specs: this.specs, attempts,
|
|
118
|
+
report: this.jsonReportPath(), specs: this.specs, contract: this.contract, attempts,
|
|
86
119
|
};
|
|
87
120
|
const outDir = this.config.projects[0]?.outputDir ?? path.join(this.root, 'test-results');
|
|
88
121
|
const name = shard ? `sungen-run-record.shard-${shard.current}-of-${shard.total}.json` : 'sungen-run-record.json';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"trace-run-record-reporter.js","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"trace-run-record-reporter.js","sourceRoot":"","sources":["../../src/trace-identity/trace-run-record-reporter.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sungen/driver-ui",
|
|
3
|
-
"version": "3.3.0-alpha.
|
|
3
|
+
"version": "3.3.0-alpha.9",
|
|
4
4
|
"description": "Sungen UI capability (Playwright) — step patterns, viewpoint gate, and phase hooks. Plugs into @sun-asterisk/sungen via the capability SPI.",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"types": "src/index.ts",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"author": "eqe team (engineer & quality) — Sun Asterisk",
|
|
15
15
|
"license": "MIT",
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@sun-asterisk/sungen": "3.3.0-alpha.
|
|
17
|
+
"@sun-asterisk/sungen": "3.3.0-alpha.9"
|
|
18
18
|
},
|
|
19
19
|
"peerDependencies": {
|
|
20
20
|
"@playwright/test": "^1.57.0"
|
|
@@ -7,6 +7,9 @@
|
|
|
7
7
|
*
|
|
8
8
|
* runId SUNGEN_RUN_ID when set (one id across CI shards), else a fresh UUID
|
|
9
9
|
* specs sha256 of every test file at run START (the code that ran)
|
|
10
|
+
* contract sha256 of every `.feature` and `contracts.lock.yaml` under `qa/` at run START (schema 2,
|
|
11
|
+
* #967): the contract the run saw. A trace proves a contract only while those bytes still
|
|
12
|
+
* hold — compared by content, so a touch, a checkout or a fresh clone stales nothing
|
|
10
13
|
* attempts per test attempt: testId, retry, status, window, and the sha256 of its trace.zip
|
|
11
14
|
* report where the JSON reporter wrote its report — traces are mapped THROUGH that report
|
|
12
15
|
* (merge-reports renames trace files; their content, and so the sha256, stays)
|
|
@@ -35,6 +38,27 @@ const sha256File = (file: string): string | null => {
|
|
|
35
38
|
try { return createHash('sha256').update(fs.readFileSync(file)).digest('hex'); } catch { return null; }
|
|
36
39
|
};
|
|
37
40
|
const posix = (p: string): string => p.split(path.sep).join('/');
|
|
41
|
+
const isContractFile = (name: string): boolean => name.endsWith('.feature') || name === 'contracts.lock.yaml';
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* sha256 of every contract file under `<root>/qa` — each `*.feature` and `contracts.lock.yaml` — keyed
|
|
45
|
+
* by its posix path from `root`, sorted. Dot-directories, node_modules and symlinks are not followed.
|
|
46
|
+
*/
|
|
47
|
+
export function contractContent(root: string): Record<string, string> {
|
|
48
|
+
const found: Array<[string, string]> = [];
|
|
49
|
+
const walk = (dir: string): void => {
|
|
50
|
+
let entries: fs.Dirent[];
|
|
51
|
+
try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
|
|
52
|
+
for (const e of entries) {
|
|
53
|
+
const full = path.join(dir, e.name);
|
|
54
|
+
if (e.isDirectory()) { if (!e.name.startsWith('.') && e.name !== 'node_modules') walk(full); continue; }
|
|
55
|
+
const sha = e.isFile() && isContractFile(e.name) ? sha256File(full) : null;
|
|
56
|
+
if (sha) found.push([posix(path.relative(root, full)), sha]);
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
walk(path.join(root, 'qa'));
|
|
60
|
+
return Object.fromEntries(found.sort(([a], [b]) => (a < b ? -1 : a > b ? 1 : 0)));
|
|
61
|
+
}
|
|
38
62
|
|
|
39
63
|
class TraceRunRecordReporter implements Reporter {
|
|
40
64
|
private config?: FullConfig;
|
|
@@ -42,6 +66,7 @@ class TraceRunRecordReporter implements Reporter {
|
|
|
42
66
|
private runId = process.env.SUNGEN_RUN_ID || randomUUID();
|
|
43
67
|
private startedAt = new Date().toISOString();
|
|
44
68
|
private specs: Record<string, string> = {};
|
|
69
|
+
private contract: Record<string, string> = {};
|
|
45
70
|
private results: Array<{ test: TestCase; result: TestResult }> = [];
|
|
46
71
|
|
|
47
72
|
printsToStdio(): boolean { return false; }
|
|
@@ -49,6 +74,7 @@ class TraceRunRecordReporter implements Reporter {
|
|
|
49
74
|
onBegin(config: FullConfig, suite: Suite): void {
|
|
50
75
|
this.config = config;
|
|
51
76
|
if (config.configFile) this.root = path.dirname(config.configFile);
|
|
77
|
+
this.contract = contractContent(this.root);
|
|
52
78
|
for (const test of suite.allTests()) {
|
|
53
79
|
const rel = this.rel(test.location.file);
|
|
54
80
|
if (this.specs[rel] === undefined) this.specs[rel] = sha256File(test.location.file) ?? '';
|
|
@@ -71,9 +97,9 @@ class TraceRunRecordReporter implements Reporter {
|
|
|
71
97
|
});
|
|
72
98
|
const shard = this.config.shard;
|
|
73
99
|
const record = {
|
|
74
|
-
schema:
|
|
100
|
+
schema: 2, tool: 'sungen-trace-run-record', runId: this.runId, startedAt: this.startedAt,
|
|
75
101
|
playwrightVersion: this.config.version, shard: shard ? `${shard.current}/${shard.total}` : null,
|
|
76
|
-
report: this.jsonReportPath(), specs: this.specs, attempts,
|
|
102
|
+
report: this.jsonReportPath(), specs: this.specs, contract: this.contract, attempts,
|
|
77
103
|
};
|
|
78
104
|
const outDir = this.config.projects[0]?.outputDir ?? path.join(this.root, 'test-results');
|
|
79
105
|
const name = shard ? `sungen-run-record.shard-${shard.current}-of-${shard.total}.json` : 'sungen-run-record.json';
|