@aplaytest/runner-playwright 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.
Files changed (58) hide show
  1. package/dist/.tsbuildinfo +1 -0
  2. package/dist/api-fixtures.d.ts +71 -0
  3. package/dist/api-fixtures.d.ts.map +1 -0
  4. package/dist/api-fixtures.js +162 -0
  5. package/dist/api-fixtures.js.map +1 -0
  6. package/dist/assemble.d.ts +86 -0
  7. package/dist/assemble.d.ts.map +1 -0
  8. package/dist/assemble.js +176 -0
  9. package/dist/assemble.js.map +1 -0
  10. package/dist/bind.d.ts +43 -0
  11. package/dist/bind.d.ts.map +1 -0
  12. package/dist/bind.js +112 -0
  13. package/dist/bind.js.map +1 -0
  14. package/dist/fixtures.d.ts +54 -0
  15. package/dist/fixtures.d.ts.map +1 -0
  16. package/dist/fixtures.js +210 -0
  17. package/dist/fixtures.js.map +1 -0
  18. package/dist/index.d.ts +31 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +24 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/reporter.d.ts +79 -0
  23. package/dist/reporter.d.ts.map +1 -0
  24. package/dist/reporter.js +277 -0
  25. package/dist/reporter.js.map +1 -0
  26. package/dist/sidecar.d.ts +134 -0
  27. package/dist/sidecar.d.ts.map +1 -0
  28. package/dist/sidecar.js +111 -0
  29. package/dist/sidecar.js.map +1 -0
  30. package/dist/spawn.d.ts +57 -0
  31. package/dist/spawn.d.ts.map +1 -0
  32. package/dist/spawn.js +170 -0
  33. package/dist/spawn.js.map +1 -0
  34. package/dist/steps.d.ts +45 -0
  35. package/dist/steps.d.ts.map +1 -0
  36. package/dist/steps.js +106 -0
  37. package/dist/steps.js.map +1 -0
  38. package/dist-cjs/.tsbuildinfo +1 -0
  39. package/dist-cjs/api-fixtures.js +166 -0
  40. package/dist-cjs/api-fixtures.js.map +1 -0
  41. package/dist-cjs/assemble.js +181 -0
  42. package/dist-cjs/assemble.js.map +1 -0
  43. package/dist-cjs/bind.js +117 -0
  44. package/dist-cjs/bind.js.map +1 -0
  45. package/dist-cjs/fixtures.js +215 -0
  46. package/dist-cjs/fixtures.js.map +1 -0
  47. package/dist-cjs/index.js +64 -0
  48. package/dist-cjs/index.js.map +1 -0
  49. package/dist-cjs/package.json +3 -0
  50. package/dist-cjs/reporter.js +282 -0
  51. package/dist-cjs/reporter.js.map +1 -0
  52. package/dist-cjs/sidecar.js +116 -0
  53. package/dist-cjs/sidecar.js.map +1 -0
  54. package/dist-cjs/spawn.js +174 -0
  55. package/dist-cjs/spawn.js.map +1 -0
  56. package/dist-cjs/steps.js +112 -0
  57. package/dist-cjs/steps.js.map +1 -0
  58. package/package.json +51 -0
@@ -0,0 +1,282 @@
1
+ "use strict";
2
+ /**
3
+ * The atest Playwright reporter.
4
+ *
5
+ * INVARIANT: this reporter must never change a run's verdict. It reports no
6
+ * status from `onEnd`, swallows its own errors to stderr, and does its I/O
7
+ * after the run rather than inside it. If atest breaks, the suite still tells
8
+ * the truth about the application — that property is what makes adopting it a
9
+ * one-line, revertible change.
10
+ *
11
+ * Work is COLLECTED in `onTestEnd` (synchronous, cheap: a couple of object
12
+ * references) and FLUSHED in `onEnd` (asynchronous: attachment reads, JSON
13
+ * writes). Playwright's `onTestEnd` cannot await, and doing file I/O per test
14
+ * inside a parallel run is exactly the kind of overhead that would make people
15
+ * turn the tool off.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.ATEST_VERSION = void 0;
19
+ exports.runFileName = runFileName;
20
+ const promises_1 = require("node:fs/promises");
21
+ const promises_2 = require("node:fs/promises");
22
+ const node_path_1 = require("node:path");
23
+ const core_1 = require("@aplaytest/core");
24
+ Object.defineProperty(exports, "ATEST_VERSION", { enumerable: true, get: function () { return core_1.ATEST_VERSION; } });
25
+ const assemble_js_1 = require("./assemble.js");
26
+ const sidecar_js_1 = require("./sidecar.js");
27
+ const DEFAULTS = {
28
+ evidenceDir: '.atest/evidence',
29
+ runsDir: '.atest/runs',
30
+ retainRuns: 50,
31
+ redactKeys: ['password', 'token', 'authorization', 'cookie', 'secret', 'api-key'],
32
+ };
33
+ function toOutcome(status) {
34
+ switch (status) {
35
+ case 'passed':
36
+ return 'passed';
37
+ case 'failed':
38
+ return 'failed';
39
+ case 'timedOut':
40
+ return 'timedOut';
41
+ case 'skipped':
42
+ return 'skipped';
43
+ case 'interrupted':
44
+ return 'interrupted';
45
+ default:
46
+ return 'failed';
47
+ }
48
+ }
49
+ function warn(message) {
50
+ process.stderr.write(`[atest] ${message}\n`);
51
+ }
52
+ class AtestReporter {
53
+ options;
54
+ collected = [];
55
+ workerTests = new Map();
56
+ config;
57
+ runId = '';
58
+ startedAt = '';
59
+ enabled = true;
60
+ constructor(options = {}) {
61
+ this.options = options;
62
+ }
63
+ /** We add a short footer only; other reporters own the main output. */
64
+ printsToStdio() {
65
+ return false;
66
+ }
67
+ onBegin(config) {
68
+ this.config = config;
69
+ this.enabled = this.options.enabled ?? process.env['ATEST'] !== '0';
70
+ this.startedAt = new Date().toISOString();
71
+ const fromOptions = typeof this.options.runId === 'function' ? this.options.runId() : this.options.runId;
72
+ // Environment wins over config, deliberately. Every shard of a CI run must
73
+ // share one run id, and only the runtime knows it — a value baked into
74
+ // playwright.config.ts cannot vary per invocation, so config-wins would
75
+ // make sharded runs silently collapse into a single overwritten record.
76
+ this.runId =
77
+ process.env['ATEST_RUN_ID'] ??
78
+ fromOptions ??
79
+ `${this.startedAt.replace(/[:.]/g, '-')}-${process.pid}`;
80
+ }
81
+ onTestEnd(test, result) {
82
+ if (!this.enabled)
83
+ return;
84
+ // Cheap and synchronous by design — two references and a string push.
85
+ this.collected.push({ test, result });
86
+ const onWorker = this.workerTests.get(result.workerIndex) ?? [];
87
+ onWorker.push(test.id);
88
+ this.workerTests.set(result.workerIndex, onWorker);
89
+ }
90
+ async onEnd(_result) {
91
+ if (!this.enabled)
92
+ return;
93
+ try {
94
+ await this.flush();
95
+ }
96
+ catch (error) {
97
+ // A reporting failure must never become a test failure.
98
+ warn(`failed to write evidence: ${error instanceof Error ? error.message : String(error)}`);
99
+ }
100
+ }
101
+ async flush() {
102
+ const evidenceStore = new core_1.EvidenceStore({
103
+ dir: this.options.evidenceDir ?? DEFAULTS.evidenceDir,
104
+ redactKeys: this.options.redactKeys ?? DEFAULTS.redactKeys,
105
+ retainRuns: this.options.retainRuns ?? DEFAULTS.retainRuns,
106
+ });
107
+ const attempts = [];
108
+ let bundlesWritten = 0;
109
+ for (const { test, result } of this.collected) {
110
+ const outcome = toOutcome(result.status);
111
+ // Playwright names the implicit project '' when a config declares no
112
+ // projects. Empty string is a poor grouping key: it is part of the
113
+ // scoring key and the evidence id, and it reads as missing data in
114
+ // every report.
115
+ const projectName = test.parent.project()?.name;
116
+ const project = projectName === undefined || projectName === '' ? 'default' : projectName;
117
+ const traceId = this.options.traceId?.({ id: test.id }, result.retry) ?? null;
118
+ let failureKind = null;
119
+ let evidenceIdValue = null;
120
+ // Coverage is read for EVERY attempt, not only failures: what a passing
121
+ // test visited is precisely what future selection needs.
122
+ const coverage = await this.readCoverage(result);
123
+ if ((0, core_1.isFailure)(outcome)) {
124
+ try {
125
+ const sidecars = await this.readSidecars(result);
126
+ const bundle = (0, assemble_js_1.assembleBundle)({
127
+ test: {
128
+ id: test.id,
129
+ title: test.title,
130
+ titlePath: () => test.titlePath(),
131
+ location: { file: test.location.file, line: test.location.line },
132
+ tags: test.tags,
133
+ },
134
+ result: result,
135
+ sidecars,
136
+ context: {
137
+ runId: this.runId,
138
+ traceId,
139
+ project,
140
+ shard: this.config?.shard ?? null,
141
+ workers: this.config?.workers ?? 1,
142
+ appEnv: process.env['APP_ENV'] ?? 'unknown',
143
+ baseUrl: process.env['BASE_URL'] ?? '',
144
+ browser: project,
145
+ platform: process.platform,
146
+ commit: process.env['GITHUB_SHA'] ?? '',
147
+ changedPaths: [],
148
+ timeoutMs: test.timeout,
149
+ },
150
+ });
151
+ await evidenceStore.write(bundle);
152
+ bundlesWritten += 1;
153
+ failureKind = bundle.failure.kind;
154
+ evidenceIdValue = bundle.id;
155
+ }
156
+ catch (error) {
157
+ if (error instanceof sidecar_js_1.SidecarParseError) {
158
+ // Loud, named, and actionable — a drifted contract is a bug in the
159
+ // fixtures, not something to paper over with an empty bundle.
160
+ warn(error.message);
161
+ }
162
+ else {
163
+ warn(`could not build evidence for "${test.title}": ` +
164
+ `${error instanceof Error ? error.message : String(error)}`);
165
+ }
166
+ failureKind = (0, assemble_js_1.classifyResult)(result, EMPTY, test.timeout).kind;
167
+ }
168
+ }
169
+ attempts.push({
170
+ testId: test.id,
171
+ title: test.title,
172
+ titlePath: test.titlePath(),
173
+ file: test.location.file,
174
+ line: test.location.line,
175
+ project,
176
+ tags: test.tags,
177
+ retry: result.retry,
178
+ outcome,
179
+ failureKind,
180
+ durationMs: result.duration,
181
+ workerIndex: result.workerIndex,
182
+ shard: this.config?.shard ?? null,
183
+ traceId,
184
+ evidenceId: evidenceIdValue,
185
+ coScheduled: (this.workerTests.get(result.workerIndex) ?? []).filter(id => id !== test.id),
186
+ routes: coverage,
187
+ });
188
+ }
189
+ const run = {
190
+ schemaVersion: core_1.RUN_SCHEMA_VERSION,
191
+ runId: this.runId,
192
+ startedAt: this.startedAt,
193
+ finishedAt: new Date().toISOString(),
194
+ commit: process.env['GITHUB_SHA'] ?? null,
195
+ branch: process.env['GITHUB_REF_NAME'] ?? null,
196
+ appEnv: process.env['APP_ENV'] ?? null,
197
+ ci: process.env['CI'] === 'true' || process.env['CI'] === '1',
198
+ workers: this.config?.workers ?? 1,
199
+ shard: this.config?.shard ?? null,
200
+ atestVersion: core_1.ATEST_VERSION,
201
+ playwrightVersion: this.config?.version ?? null,
202
+ attempts,
203
+ };
204
+ const runPath = (0, node_path_1.join)(this.options.runsDir ?? DEFAULTS.runsDir, runFileName(this.runId, run.shard, attempts.map(a => a.project)));
205
+ await (0, promises_2.mkdir)((0, node_path_1.dirname)(runPath), { recursive: true });
206
+ await (0, promises_2.writeFile)(runPath, `${JSON.stringify(run, null, 2)}\n`, 'utf8');
207
+ await evidenceStore.prune();
208
+ const failures = attempts.filter(a => (0, core_1.isFailure)(a.outcome)).length;
209
+ if (failures > 0) {
210
+ warn(`${bundlesWritten}/${failures} failures captured → ${runPath}`);
211
+ }
212
+ }
213
+ async readAttachment(result, name) {
214
+ const attachment = result.attachments.find(a => a.name === name);
215
+ if (attachment === undefined)
216
+ return undefined;
217
+ if (attachment.body !== undefined)
218
+ return attachment.body.toString('utf8');
219
+ if (attachment.path !== undefined)
220
+ return (0, promises_1.readFile)(attachment.path, 'utf8').catch(() => undefined);
221
+ return undefined;
222
+ }
223
+ async readCoverage(result) {
224
+ try {
225
+ const parsed = (0, sidecar_js_1.parseSidecar)(sidecar_js_1.SIDECAR.coverage, await this.readAttachment(result, sidecar_js_1.SIDECAR.coverage));
226
+ return parsed?.routes ?? [];
227
+ }
228
+ catch (error) {
229
+ // A drifted coverage sidecar must not cost us the attempt record.
230
+ warn(error instanceof Error ? error.message : String(error));
231
+ return [];
232
+ }
233
+ }
234
+ async readSidecars(result) {
235
+ const read = (name) => this.readAttachment(result, name);
236
+ return {
237
+ page: (0, sidecar_js_1.parseSidecar)(sidecar_js_1.SIDECAR.page, await read(sidecar_js_1.SIDECAR.page)),
238
+ network: (0, sidecar_js_1.parseSidecar)(sidecar_js_1.SIDECAR.network, await read(sidecar_js_1.SIDECAR.network)),
239
+ console: (0, sidecar_js_1.parseSidecar)(sidecar_js_1.SIDECAR.console, await read(sidecar_js_1.SIDECAR.console)),
240
+ intent: (0, sidecar_js_1.parseSidecar)(sidecar_js_1.SIDECAR.intent, await read(sidecar_js_1.SIDECAR.intent)),
241
+ };
242
+ }
243
+ }
244
+ exports.default = AtestReporter;
245
+ const EMPTY = { page: null, network: null, console: null, intent: null };
246
+ /**
247
+ * One file per INVOCATION, not per run.
248
+ *
249
+ * Naming the file `<runId>.json` looks right until the suite is sharded, and
250
+ * then it silently destroys almost all of the data. Every shard shares one run
251
+ * id on purpose — that is how they get merged — so every shard wrote the same
252
+ * filename. Measured on a three-shard run: shards 1 and 2 recorded their
253
+ * attempts, shard 3 had no tests to run, and its empty record overwrote both.
254
+ * The surviving file reported `attempts: 0` for a run that passed four tests.
255
+ *
256
+ * It survives the upload too. CI downloads shard artifacts with
257
+ * `merge-multiple: true`, which flattens them into one directory — identical
258
+ * names collide there as well, so a 6-shard × 9-project matrix would keep one
259
+ * file in fifty-four.
260
+ *
261
+ * Shard and project both go in the name because their matrix varies both, and
262
+ * two jobs differing only by project would otherwise still collide.
263
+ */
264
+ function runFileName(runId, shard, projects) {
265
+ const parts = [runId];
266
+ if (shard !== null)
267
+ parts.push(`shard-${shard.current}of${shard.total}`);
268
+ const distinct = [...new Set(projects)].sort();
269
+ if (distinct.length === 1 && distinct[0] !== undefined)
270
+ parts.push(distinct[0]);
271
+ else if (distinct.length > 1)
272
+ parts.push(shortHash(distinct.join(',')));
273
+ return `${parts.join('-').replace(/[^A-Za-z0-9._-]/g, '_')}.json`;
274
+ }
275
+ /** djb2, base36. Deterministic — re-ingesting the same file must be idempotent. */
276
+ function shortHash(input) {
277
+ let hash = 5381;
278
+ for (let i = 0; i < input.length; i += 1)
279
+ hash = ((hash * 33) ^ input.charCodeAt(i)) >>> 0;
280
+ return hash.toString(36).padStart(7, '0').slice(0, 7);
281
+ }
282
+ //# sourceMappingURL=reporter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reporter.js","sourceRoot":"","sources":["../src/reporter.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ /**
3
+ * The fixture → reporter channel.
4
+ *
5
+ * A Playwright reporter runs in the main process and cannot reach into the
6
+ * browser. Anything only the worker knows — the ARIA snapshot, the request
7
+ * ledger, console output — has to travel as a test ATTACHMENT, which is the
8
+ * runner's own supported channel and needs no side files or IPC.
9
+ *
10
+ * Both ends import these schemas, and the reporter PARSES rather than casts.
11
+ * A fixture that drifts from the contract fails loudly with a named error
12
+ * instead of silently producing a bundle with an empty ARIA snapshot — the
13
+ * same reasoning behind validating mocked response bodies against wire
14
+ * schemas rather than trusting them.
15
+ */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.SidecarParseError = exports.CoverageSidecarSchema = exports.IntentSidecarSchema = exports.ConsoleSidecarSchema = exports.NetworkSidecarSchema = exports.PageSidecarSchema = exports.SIDECAR = void 0;
18
+ exports.parseSidecar = parseSidecar;
19
+ const zod_1 = require("zod");
20
+ exports.SIDECAR = {
21
+ page: 'atest:page',
22
+ network: 'atest:network',
23
+ console: 'atest:console',
24
+ intent: 'atest:intent',
25
+ coverage: 'atest:coverage',
26
+ };
27
+ exports.PageSidecarSchema = zod_1.z.object({
28
+ url: zod_1.z.string(),
29
+ title: zod_1.z.string(),
30
+ ariaSnapshot: zod_1.z.string(),
31
+ testIdsPresent: zod_1.z.array(zod_1.z.string()),
32
+ htmlDigest: zod_1.z.string().nullable().default(null),
33
+ });
34
+ const RequestRecordSchema = zod_1.z.object({
35
+ url: zod_1.z.string(),
36
+ method: zod_1.z.string(),
37
+ status: zod_1.z.number().nullable(),
38
+ durationMs: zod_1.z.number(),
39
+ failureText: zod_1.z.string().nullable().default(null),
40
+ schemaError: zod_1.z.string().nullable().default(null),
41
+ });
42
+ exports.NetworkSidecarSchema = zod_1.z.object({
43
+ failed: zod_1.z.array(RequestRecordSchema),
44
+ slow: zod_1.z.array(RequestRecordSchema),
45
+ statusCounts: zod_1.z.record(zod_1.z.string(), zod_1.z.number()),
46
+ });
47
+ exports.ConsoleSidecarSchema = zod_1.z.object({
48
+ errors: zod_1.z.array(zod_1.z.string()),
49
+ warnings: zod_1.z.array(zod_1.z.string()),
50
+ });
51
+ exports.IntentSidecarSchema = zod_1.z.object({
52
+ selector: zod_1.z.string().nullable().default(null),
53
+ selectorSource: zod_1.z
54
+ .object({
55
+ file: zod_1.z.string(),
56
+ line: zod_1.z.number(),
57
+ constantPath: zod_1.z.string(),
58
+ aliases: zod_1.z.array(zod_1.z.string()).default([]),
59
+ })
60
+ .nullable()
61
+ .default(null),
62
+ });
63
+ /**
64
+ * Routes a test actually visited.
65
+ *
66
+ * The one signal that survives a fixture barrel. Static imports say every spec
67
+ * depends on every feature (they compose one `test` object); what a test
68
+ * VISITED is independent of that, and is the only thing that can narrow
69
+ * selection in a suite built that way.
70
+ */
71
+ exports.CoverageSidecarSchema = zod_1.z.object({
72
+ routes: zod_1.z.array(zod_1.z.string()),
73
+ });
74
+ const SCHEMAS = {
75
+ [exports.SIDECAR.page]: exports.PageSidecarSchema,
76
+ [exports.SIDECAR.network]: exports.NetworkSidecarSchema,
77
+ [exports.SIDECAR.console]: exports.ConsoleSidecarSchema,
78
+ [exports.SIDECAR.intent]: exports.IntentSidecarSchema,
79
+ [exports.SIDECAR.coverage]: exports.CoverageSidecarSchema,
80
+ };
81
+ class SidecarParseError extends Error {
82
+ sidecar;
83
+ issues;
84
+ constructor(sidecar, issues) {
85
+ super(`Attachment "${sidecar}" does not match its schema — the atest fixtures and ` +
86
+ `reporter are out of sync.\n${issues}`);
87
+ this.sidecar = sidecar;
88
+ this.issues = issues;
89
+ this.name = 'SidecarParseError';
90
+ }
91
+ }
92
+ exports.SidecarParseError = SidecarParseError;
93
+ /**
94
+ * Parse a sidecar payload. Absent is fine (the fixtures are optional);
95
+ * PRESENT-BUT-WRONG is not, and throws.
96
+ */
97
+ function parseSidecar(name, raw) {
98
+ if (raw === undefined)
99
+ return null;
100
+ let json;
101
+ try {
102
+ json = JSON.parse(raw);
103
+ }
104
+ catch (error) {
105
+ throw new SidecarParseError(name, error instanceof Error ? error.message : String(error));
106
+ }
107
+ const result = SCHEMAS[name].safeParse(json);
108
+ if (!result.success) {
109
+ const issues = result.error.issues
110
+ .map(i => ` • ${i.path.join('.') || '(root)'}: ${i.message}`)
111
+ .join('\n');
112
+ throw new SidecarParseError(name, issues);
113
+ }
114
+ return result.data;
115
+ }
116
+ //# sourceMappingURL=sidecar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sidecar.js","sourceRoot":"","sources":["../src/sidecar.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,174 @@
1
+ "use strict";
2
+ /**
3
+ * Drive `playwright test` as a child process and read structured results.
4
+ *
5
+ * Used by bisect (re-run under controlled perturbations) and by heal
6
+ * validation (re-run with a candidate patch applied). Both need counts, not
7
+ * evidence, so this uses Playwright's own JSON reporter rather than ours —
8
+ * fewer moving parts, and it works against a consumer repo whether or not the
9
+ * atest reporter is configured.
10
+ */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.escapeForGrep = escapeForGrep;
13
+ exports.runPlaywright = runPlaywright;
14
+ const node_child_process_1 = require("node:child_process");
15
+ const promises_1 = require("node:fs/promises");
16
+ const node_os_1 = require("node:os");
17
+ const node_path_1 = require("node:path");
18
+ /** Playwright's `-g` takes a regular expression; titles are literal text. */
19
+ function escapeForGrep(title) {
20
+ return title.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
21
+ }
22
+ const RESULT_PASSED = 'passed';
23
+ const RESULT_FAILED = 'failed';
24
+ const RESULT_TIMED_OUT = 'timedOut';
25
+ const PLAYWRIGHT_JSON_OUTPUT = 'PLAYWRIGHT_JSON_OUTPUT_NAME';
26
+ function countResults(spec) {
27
+ let passed = 0;
28
+ let failed = 0;
29
+ let error = null;
30
+ for (const test of spec.tests ?? []) {
31
+ for (const result of test.results ?? []) {
32
+ if (result.status === RESULT_PASSED)
33
+ passed += 1;
34
+ else if (result.status === RESULT_FAILED || result.status === RESULT_TIMED_OUT) {
35
+ failed += 1;
36
+ error = result.error?.message ?? error;
37
+ }
38
+ }
39
+ }
40
+ return { passed, failed, error };
41
+ }
42
+ function collectSpecs(suites, file = '') {
43
+ const out = [];
44
+ for (const suite of suites ?? []) {
45
+ const suiteFile = suite.file ?? file;
46
+ for (const spec of suite.specs ?? []) {
47
+ out.push({ title: spec.title ?? '', file: suiteFile, ...countResults(spec) });
48
+ }
49
+ out.push(...collectSpecs(suite.suites, suiteFile));
50
+ }
51
+ return out;
52
+ }
53
+ function buildArgs(options) {
54
+ const args = ['playwright', 'test'];
55
+ if (options.config !== undefined)
56
+ args.push('--config', options.config);
57
+ if (options.file !== undefined)
58
+ args.push(options.file);
59
+ if (options.grepTitle !== undefined)
60
+ args.push('-g', escapeForGrep(options.grepTitle));
61
+ if (options.project !== undefined)
62
+ args.push('--project', options.project);
63
+ if (options.workers !== undefined)
64
+ args.push(`--workers=${options.workers}`);
65
+ if (options.repeatEach !== undefined)
66
+ args.push(`--repeat-each=${options.repeatEach}`);
67
+ if (options.maxFailures !== undefined)
68
+ args.push(`--max-failures=${options.maxFailures}`);
69
+ // Replaces the project's reporters for this invocation. Deliberate: bisect
70
+ // and validation want counts, and inheriting a consumer's HTML or blob
71
+ // reporter would write artifacts nobody asked for on every probe.
72
+ args.push('--reporter=json');
73
+ return args;
74
+ }
75
+ function isJsonReport(value) {
76
+ return typeof value === 'object' && value !== null;
77
+ }
78
+ function parseJsonReport(raw) {
79
+ try {
80
+ const parsed = JSON.parse(raw);
81
+ return isJsonReport(parsed) ? parsed : null;
82
+ }
83
+ catch {
84
+ return null;
85
+ }
86
+ }
87
+ function inconclusiveResult(exitCode, stderr) {
88
+ return {
89
+ ok: false,
90
+ passed: 0,
91
+ failed: 0,
92
+ flaky: 0,
93
+ skipped: 0,
94
+ durationMs: 0,
95
+ exitCode,
96
+ inconclusive: true,
97
+ stderr,
98
+ specs: [],
99
+ };
100
+ }
101
+ async function runPlaywright(options) {
102
+ const dir = await (0, promises_1.mkdtemp)((0, node_path_1.join)((0, node_os_1.tmpdir)(), 'atest-run-'));
103
+ const jsonPath = (0, node_path_1.join)(dir, 'report.json');
104
+ try {
105
+ const args = buildArgs(options);
106
+ const result = await new Promise(resolve => {
107
+ const child = (0, node_child_process_1.spawn)('npx', args, {
108
+ cwd: options.cwd,
109
+ env: {
110
+ ...process.env,
111
+ ...options.env,
112
+ [PLAYWRIGHT_JSON_OUTPUT]: jsonPath,
113
+ // The reporter is not wanted during a probe: bisect runs the same
114
+ // test dozens of times, and each run would otherwise write evidence
115
+ // bundles that pollute the history the analysis is reading.
116
+ ATEST: '0',
117
+ FORCE_COLOR: '0',
118
+ },
119
+ stdio: ['ignore', 'ignore', 'pipe'],
120
+ });
121
+ let stderr = '';
122
+ child.stderr.on('data', (chunk) => {
123
+ stderr += chunk.toString('utf8');
124
+ });
125
+ const timeout = options.timeoutMs === undefined
126
+ ? null
127
+ : setTimeout(() => child.kill('SIGTERM'), options.timeoutMs);
128
+ child.on('close', code => {
129
+ if (timeout !== null)
130
+ clearTimeout(timeout);
131
+ resolve({ code: code ?? 1, stderr });
132
+ });
133
+ child.on('error', error => {
134
+ if (timeout !== null)
135
+ clearTimeout(timeout);
136
+ resolve({ code: 1, stderr: error.message });
137
+ });
138
+ });
139
+ const raw = await (0, promises_1.readFile)(jsonPath, 'utf8').catch(() => null);
140
+ if (raw === null) {
141
+ // No report means the run never got as far as executing tests — a config
142
+ // error, a missing browser. That is INCONCLUSIVE, not a failing test;
143
+ // counting it as a failure would make bisect blame the code under test
144
+ // for a broken environment.
145
+ return inconclusiveResult(result.code, result.stderr);
146
+ }
147
+ const report = parseJsonReport(raw);
148
+ if (report === null) {
149
+ return inconclusiveResult(result.code, `unparseable JSON report\n${result.stderr}`);
150
+ }
151
+ const stats = report.stats ?? {};
152
+ const passed = stats.expected ?? 0;
153
+ const failed = stats.unexpected ?? 0;
154
+ const flaky = stats.flaky ?? 0;
155
+ return {
156
+ ok: failed === 0 && passed + flaky > 0,
157
+ passed,
158
+ failed,
159
+ flaky,
160
+ skipped: stats.skipped ?? 0,
161
+ durationMs: stats.duration ?? 0,
162
+ exitCode: result.code,
163
+ // A run that executed nothing tells us nothing — usually a grep that
164
+ // matched no test, which is a caller mistake worth surfacing.
165
+ inconclusive: passed + failed + flaky === 0,
166
+ stderr: result.stderr,
167
+ specs: collectSpecs(report.suites),
168
+ };
169
+ }
170
+ finally {
171
+ await (0, promises_1.rm)(dir, { recursive: true, force: true });
172
+ }
173
+ }
174
+ //# sourceMappingURL=spawn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn.js","sourceRoot":"","sources":["../src/spawn.ts"],"names":[],"mappings":"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"}