@empiricalrun/playwright-utils 0.58.1 → 0.58.3
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 +15 -0
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +12 -2
- package/dist/generated/api-worker-openapi.d.ts +1130 -699
- package/dist/generated/api-worker-openapi.d.ts.map +1 -1
- package/dist/reporter/harness.d.ts +80 -0
- package/dist/reporter/harness.d.ts.map +1 -1
- package/dist/reporter/harness.js +198 -34
- package/dist/reporter/ibr-utils.d.ts +6 -0
- package/dist/reporter/ibr-utils.d.ts.map +1 -1
- package/dist/reporter/ibr-utils.js +15 -1
- package/dist/reporter/incremental-blob-reporter.d.ts +13 -2
- package/dist/reporter/incremental-blob-reporter.d.ts.map +1 -1
- package/dist/reporter/incremental-blob-reporter.js +87 -33
- package/dist/reporter/lifecycle-events.d.ts +5 -3
- package/dist/reporter/lifecycle-events.d.ts.map +1 -1
- package/dist/reporter/lifecycle-events.js +54 -3
- package/dist/reporter/parity-utils.d.ts +63 -0
- package/dist/reporter/parity-utils.d.ts.map +1 -0
- package/dist/reporter/parity-utils.js +109 -0
- package/package.json +5 -5
- package/tsconfig.tsbuildinfo +1 -1
- package/upstream-oracle/README.md +48 -0
- package/upstream-oracle/ibr-adapter.js +72 -0
- package/upstream-oracle/intermediate-mode-ibr.patch +16 -0
- package/upstream-oracle/run.mjs +231 -0
|
@@ -28,9 +28,10 @@ class IncrementalBlobReporter {
|
|
|
28
28
|
_completedTestIds = new Set();
|
|
29
29
|
_completedResultIds = new Set();
|
|
30
30
|
_testRetries = new Map(); // testId → configured retries
|
|
31
|
-
_testResults = new Map(); // testId → retryIndex →
|
|
31
|
+
_testResults = new Map(); // testId → retryIndex → RecordedRetry
|
|
32
32
|
_startTime = Date.now();
|
|
33
33
|
_finalizePromise = null;
|
|
34
|
+
_rootDir = process.cwd();
|
|
34
35
|
constructor() {
|
|
35
36
|
if (process.env.SHARD_INDEX && process.env.TOTAL_SHARDS) {
|
|
36
37
|
this._shardIndex = Number.parseInt(process.env.SHARD_INDEX, 10);
|
|
@@ -101,51 +102,73 @@ class IncrementalBlobReporter {
|
|
|
101
102
|
if (this._uploader) {
|
|
102
103
|
await this._uploader.waitForUploads();
|
|
103
104
|
}
|
|
104
|
-
this.
|
|
105
|
-
await this._writeZip();
|
|
105
|
+
const lines = this._buildFinalReportLines(result, pruneIncompleteTests);
|
|
106
|
+
await this._writeZip(lines);
|
|
106
107
|
if (!upload) {
|
|
107
108
|
logFinalizeComplete();
|
|
108
109
|
return;
|
|
109
110
|
}
|
|
110
111
|
if (!this._uploader) {
|
|
111
|
-
console.warn("[
|
|
112
|
+
console.warn("[IncrementalBlobReporter] No uploader available, skipping upload");
|
|
112
113
|
logFinalizeComplete();
|
|
113
114
|
return;
|
|
114
115
|
}
|
|
115
116
|
await this._uploader.uploadFile(this._zipPath, `blobs/incremental-report-${this._shardIndex}.zip`);
|
|
116
117
|
await this._uploader.waitForUploads();
|
|
118
|
+
if (source === "onEnd") {
|
|
119
|
+
fs_1.default.writeFileSync(this._markerPath, JSON.stringify({ status: result.status, events: lines.length }), "utf8");
|
|
120
|
+
await this._uploader.uploadFile(this._markerPath, `blobs/incremental-report-${this._shardIndex}.zip.complete`);
|
|
121
|
+
await this._uploader.waitForUploads();
|
|
122
|
+
}
|
|
117
123
|
console.log(`[IncrementalBlobReporter] Finalize and upload complete from ${source}`);
|
|
118
124
|
if ((0, ibr_utils_1.isLocalTesting)()) {
|
|
119
125
|
await (0, local_test_1.mergeForLocalTest)(this._currentWorkingDir, this._outputDir);
|
|
120
126
|
}
|
|
121
127
|
logFinalizeComplete();
|
|
122
128
|
}
|
|
123
|
-
|
|
129
|
+
/**
|
|
130
|
+
* Computes the lines to write, without touching `_reportLines`: recording
|
|
131
|
+
* continues after the SIGINT salvage, and the onEnd pass re-finalizes from
|
|
132
|
+
* the full stream — mutating here would make the second pass work from the
|
|
133
|
+
* first pass's pruned leftovers.
|
|
134
|
+
*/
|
|
135
|
+
_buildFinalReportLines(result, pruneIncompleteTests) {
|
|
136
|
+
let lines = [...this._reportLines];
|
|
124
137
|
if (pruneIncompleteTests) {
|
|
125
138
|
// Determine which tests are fully complete (passed or all retries finished)
|
|
126
139
|
this._computeCompletedSets();
|
|
127
140
|
// Remove unrun tests from onProject suites
|
|
128
|
-
this._patchProjectSuites();
|
|
141
|
+
lines = this._patchProjectSuites(lines);
|
|
129
142
|
// Remove events (onTestBegin, onStepBegin, etc.) for incomplete tests
|
|
130
|
-
this._removeIncompleteTestArtifacts();
|
|
143
|
+
lines = this._removeIncompleteTestArtifacts(lines);
|
|
131
144
|
}
|
|
132
|
-
//
|
|
133
|
-
//
|
|
134
|
-
|
|
145
|
+
// The onEnd event belongs only in the written report, never in the
|
|
146
|
+
// in-memory stream — a later finalize pass appends its own.
|
|
147
|
+
lines.push(JSON.stringify((0, lifecycle_events_1.buildOnEndEvent)(result)));
|
|
148
|
+
return lines;
|
|
135
149
|
}
|
|
136
150
|
_computeCompletedSets() {
|
|
137
151
|
for (const [testId, results] of this._testResults) {
|
|
138
152
|
const maxRetries = this._testRetries.get(testId);
|
|
139
|
-
|
|
153
|
+
// Shutdown artifacts are not finished retries: an "interrupted" result
|
|
154
|
+
// is a test cut off mid-flight, and a post-signal "skipped" is a serial
|
|
155
|
+
// sibling Playwright marks off while shutting down. The resume shard
|
|
156
|
+
// re-runs both, so counting them as complete would put their results in
|
|
157
|
+
// the final zip alongside the resume shard's, duplicating tests in the
|
|
158
|
+
// merge. A post-signal passed/failed is real, though: the test finished
|
|
159
|
+
// its body and only its aftermath overlapped the shutdown.
|
|
160
|
+
const finished = new Map([...results].filter(([, r]) => r.status !== "interrupted" &&
|
|
161
|
+
!(r.postSignal && r.status === "skipped")));
|
|
162
|
+
if ((0, reporter_1.areRetriesComplete)(maxRetries, finished)) {
|
|
140
163
|
this._completedTestIds.add(testId);
|
|
141
|
-
for (const { resultId } of
|
|
164
|
+
for (const { resultId } of finished.values()) {
|
|
142
165
|
this._completedResultIds.add(resultId);
|
|
143
166
|
}
|
|
144
167
|
}
|
|
145
168
|
}
|
|
146
169
|
}
|
|
147
|
-
_removeIncompleteTestArtifacts() {
|
|
148
|
-
|
|
170
|
+
_removeIncompleteTestArtifacts(lines) {
|
|
171
|
+
return lines.filter((line) => {
|
|
149
172
|
const parsed = JSON.parse(line);
|
|
150
173
|
const resultId = parsed.params?.resultId ?? parsed.params?.result?.id;
|
|
151
174
|
if (!resultId) {
|
|
@@ -154,24 +177,26 @@ class IncrementalBlobReporter {
|
|
|
154
177
|
return this._completedResultIds.has(resultId);
|
|
155
178
|
});
|
|
156
179
|
}
|
|
157
|
-
_patchProjectSuites() {
|
|
158
|
-
|
|
159
|
-
const line = this._reportLines[i];
|
|
180
|
+
_patchProjectSuites(lines) {
|
|
181
|
+
return lines.map((line) => {
|
|
160
182
|
const parsed = JSON.parse(line);
|
|
161
183
|
if (parsed.method !== "onProject") {
|
|
162
|
-
|
|
184
|
+
return line;
|
|
163
185
|
}
|
|
164
186
|
const project = parsed.params.project;
|
|
165
187
|
const filteredSuites = project.suites
|
|
166
188
|
.map((suite) => (0, ibr_utils_1.filterSuiteByCompletedTests)(suite, this._completedTestIds))
|
|
167
189
|
.filter(Boolean);
|
|
168
190
|
project.suites = filteredSuites;
|
|
169
|
-
|
|
170
|
-
}
|
|
191
|
+
return JSON.stringify(parsed);
|
|
192
|
+
});
|
|
171
193
|
}
|
|
172
194
|
get _zipPath() {
|
|
173
195
|
return path_1.default.join(this._outputDir, `incremental-report-${this._shardIndex}.zip`);
|
|
174
196
|
}
|
|
197
|
+
get _markerPath() {
|
|
198
|
+
return `${this._zipPath}.complete`;
|
|
199
|
+
}
|
|
175
200
|
get _jsonlPath() {
|
|
176
201
|
return path_1.default.join(this._stagingDir, "report.jsonl");
|
|
177
202
|
}
|
|
@@ -183,9 +208,10 @@ class IncrementalBlobReporter {
|
|
|
183
208
|
fs_1.default.mkdirSync(this._outputDir, { recursive: true });
|
|
184
209
|
}
|
|
185
210
|
_appendEvent(event) {
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
211
|
+
// Deliberately not gated on isFinalized(): the SIGINT salvage seals a
|
|
212
|
+
// *snapshot*, not the stream. Playwright keeps running afterEach/afterAll
|
|
213
|
+
// teardown after the signal, and those events feed the second finalize
|
|
214
|
+
// pass in onEnd.
|
|
189
215
|
this._reportLines.push(JSON.stringify(event));
|
|
190
216
|
return true;
|
|
191
217
|
}
|
|
@@ -198,10 +224,10 @@ class IncrementalBlobReporter {
|
|
|
198
224
|
}
|
|
199
225
|
return allAppended;
|
|
200
226
|
}
|
|
201
|
-
async _writeZip() {
|
|
227
|
+
async _writeZip(lines) {
|
|
202
228
|
this._ensureDirs();
|
|
203
229
|
// Write report.jsonl to the output directory
|
|
204
|
-
const jsonlContent =
|
|
230
|
+
const jsonlContent = lines.join("\n") + "\n";
|
|
205
231
|
fs_1.default.writeFileSync(this._jsonlPath, jsonlContent, "utf8");
|
|
206
232
|
// Write _empirical_urls.json with attachment URL mappings
|
|
207
233
|
const urlsJson = (0, ibr_utils_1.buildUrlsJson)(this._attachmentUrlMap);
|
|
@@ -209,10 +235,18 @@ class IncrementalBlobReporter {
|
|
|
209
235
|
// Create zip from the staging directory
|
|
210
236
|
const zipBuffer = await (0, zip_1.createZipFromDirectory)(this._stagingDir);
|
|
211
237
|
fs_1.default.writeFileSync(this._zipPath, zipBuffer);
|
|
212
|
-
console.log(`[IncrementalBlobReporter] Zip updated: ${this._zipPath} (${
|
|
238
|
+
console.log(`[IncrementalBlobReporter] Zip updated: ${this._zipPath} (${lines.length} events, ${this._attachmentUrlMap.size} attachments)`);
|
|
213
239
|
}
|
|
214
240
|
onBegin(config, suite) {
|
|
215
241
|
this._ensureDirs();
|
|
242
|
+
this._rootDir = config.rootDir;
|
|
243
|
+
// The env vars win (production sets them), but a sharded run without
|
|
244
|
+
// them should still stamp the right shard into the blob metadata and
|
|
245
|
+
// the zip name instead of defaulting every shard to 1/1.
|
|
246
|
+
if (!process.env.SHARD_INDEX && config.shard) {
|
|
247
|
+
this._shardIndex = config.shard.current;
|
|
248
|
+
this._totalShards = config.shard.total;
|
|
249
|
+
}
|
|
216
250
|
this._appendEvents((0, lifecycle_events_1.buildOnBeginEvents)(config, suite, this._currentWorkingDir, this._shardIndex, this._totalShards));
|
|
217
251
|
console.log(`[IncrementalBlobReporter] Started with ${suite.allTests().length} tests`);
|
|
218
252
|
}
|
|
@@ -220,7 +254,7 @@ class IncrementalBlobReporter {
|
|
|
220
254
|
this._appendEvent((0, lifecycle_events_1.buildOnTestBeginEvent)(test, result, this._resultIdMap));
|
|
221
255
|
}
|
|
222
256
|
onTestEnd(test, result) {
|
|
223
|
-
const events = (0, lifecycle_events_1.buildOnTestEndEvents)(test, result, this._resultIdMap, this._stagingDir);
|
|
257
|
+
const events = (0, lifecycle_events_1.buildOnTestEndEvents)(test, result, this._resultIdMap, this._stagingDir, this._rootDir);
|
|
224
258
|
if (this._appendEvents(events)) {
|
|
225
259
|
if (!this._testRetries.has(test.id)) {
|
|
226
260
|
this._testRetries.set(test.id, test.retries);
|
|
@@ -234,21 +268,43 @@ class IncrementalBlobReporter {
|
|
|
234
268
|
resultId,
|
|
235
269
|
status: result.status,
|
|
236
270
|
expectedStatus: test.expectedStatus,
|
|
271
|
+
// The SIGINT salvage has started; this result arrived during
|
|
272
|
+
// interruption shutdown rather than the normal run.
|
|
273
|
+
postSignal: this._finalizePromise !== null,
|
|
237
274
|
});
|
|
238
275
|
}
|
|
239
276
|
}
|
|
240
277
|
}
|
|
278
|
+
onStdOut(chunk, test, result) {
|
|
279
|
+
this._appendEvent((0, lifecycle_events_1.buildOnStdIOEvent)("stdout", chunk, test, result, this._resultIdMap));
|
|
280
|
+
}
|
|
281
|
+
onStdErr(chunk, test, result) {
|
|
282
|
+
this._appendEvent((0, lifecycle_events_1.buildOnStdIOEvent)("stderr", chunk, test, result, this._resultIdMap));
|
|
283
|
+
}
|
|
284
|
+
onError(error, workerInfo) {
|
|
285
|
+
this._appendEvent((0, lifecycle_events_1.buildOnErrorEvent)(error, workerInfo));
|
|
286
|
+
}
|
|
241
287
|
onStepBegin(test, result, step) {
|
|
242
288
|
this._appendEvent((0, lifecycle_events_1.buildOnStepBeginEvent)(test, result, step, this._resultIdMap, this._stepIdMap));
|
|
243
289
|
}
|
|
244
290
|
onStepEnd(test, result, step) {
|
|
245
|
-
this._appendEvent((0, lifecycle_events_1.buildOnStepEndEvent)(test, result, step, this._resultIdMap, this._stepIdMap));
|
|
291
|
+
this._appendEvent((0, lifecycle_events_1.buildOnStepEndEvent)(test, result, step, this._resultIdMap, this._stepIdMap, this._rootDir));
|
|
246
292
|
}
|
|
247
293
|
async onEnd(result) {
|
|
248
294
|
this._removeSignalHandler();
|
|
249
|
-
//
|
|
250
|
-
//
|
|
295
|
+
// Second phase of an interrupted run: the SIGINT pass salvaged a snapshot
|
|
296
|
+
// within milliseconds (SIGKILL insurance); by the time Playwright calls
|
|
297
|
+
// onEnd the teardown chain has finished, so re-finalize from the full
|
|
298
|
+
// stream and overwrite the salvage blob with the post-teardown report.
|
|
251
299
|
if (this._finalizePromise) {
|
|
300
|
+
await this._finalizePromise;
|
|
301
|
+
this._finalizePromise = this._finalizeAndMaybeUpload({
|
|
302
|
+
result,
|
|
303
|
+
source: "onEnd",
|
|
304
|
+
markGlobalFinalized: false,
|
|
305
|
+
pruneIncompleteTests: true,
|
|
306
|
+
upload: true,
|
|
307
|
+
});
|
|
252
308
|
await this._finalizePromise;
|
|
253
309
|
return;
|
|
254
310
|
}
|
|
@@ -257,9 +313,7 @@ class IncrementalBlobReporter {
|
|
|
257
313
|
source: "onEnd",
|
|
258
314
|
markGlobalFinalized: false,
|
|
259
315
|
pruneIncompleteTests: false,
|
|
260
|
-
|
|
261
|
-
// Only the SIGINT path uploads this incremental fallback blob.
|
|
262
|
-
upload: false,
|
|
316
|
+
upload: true,
|
|
263
317
|
});
|
|
264
318
|
await this._finalizePromise;
|
|
265
319
|
console.log(`[IncrementalBlobReporter] Finished with status: ${result.status}`);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FullConfig, FullProject, FullResult, Suite, TestCase, TestResult, TestStep } from "@playwright/test/reporter";
|
|
1
|
+
import type { FullConfig, FullProject, FullResult, Suite, TestCase, TestError, TestResult, TestStep, WorkerInfo } from "@playwright/test/reporter";
|
|
2
2
|
export type JsonEvent = {
|
|
3
3
|
method: string;
|
|
4
4
|
params?: object;
|
|
@@ -6,8 +6,10 @@ export type JsonEvent = {
|
|
|
6
6
|
export declare function buildOnBeginEvents(config: FullConfig, suite: Suite, currentWorkingDir: string, shardIndex: number, totalShards: number): JsonEvent[];
|
|
7
7
|
export declare function buildOnProjectEvent(config: FullConfig, project: FullProject, projectSuite: Suite, currentWorkingDir: string): JsonEvent;
|
|
8
8
|
export declare function buildOnTestBeginEvent(test: TestCase, result: TestResult, resultIdMap: Map<string, string>): JsonEvent;
|
|
9
|
-
export declare function buildOnTestEndEvents(test: TestCase, result: TestResult, resultIdMap: Map<string, string>, stagingDir: string): JsonEvent[];
|
|
9
|
+
export declare function buildOnTestEndEvents(test: TestCase, result: TestResult, resultIdMap: Map<string, string>, stagingDir: string, rootDir: string): JsonEvent[];
|
|
10
|
+
export declare function buildOnStdIOEvent(type: "stdout" | "stderr", chunk: string | Buffer, test: TestCase | undefined, result: TestResult | undefined, resultIdMap: Map<string, string>): JsonEvent;
|
|
11
|
+
export declare function buildOnErrorEvent(error: TestError, workerInfo?: WorkerInfo): JsonEvent;
|
|
10
12
|
export declare function buildOnStepBeginEvent(test: TestCase, result: TestResult, step: TestStep, resultIdMap: Map<string, string>, stepIdMap: Map<TestStep, string>): JsonEvent;
|
|
11
|
-
export declare function buildOnStepEndEvent(test: TestCase, result: TestResult, step: TestStep, resultIdMap: Map<string, string>, stepIdMap: Map<TestStep, string
|
|
13
|
+
export declare function buildOnStepEndEvent(test: TestCase, result: TestResult, step: TestStep, resultIdMap: Map<string, string>, stepIdMap: Map<TestStep, string>, rootDir: string): JsonEvent;
|
|
12
14
|
export declare function buildOnEndEvent(result: FullResult): JsonEvent;
|
|
13
15
|
//# sourceMappingURL=lifecycle-events.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"lifecycle-events.d.ts","sourceRoot":"","sources":["../../src/reporter/lifecycle-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,KAAK,EACL,QAAQ,EACR,UAAU,EACV,QAAQ,
|
|
1
|
+
{"version":3,"file":"lifecycle-events.d.ts","sourceRoot":"","sources":["../../src/reporter/lifecycle-events.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,WAAW,EACX,UAAU,EACV,KAAK,EACL,QAAQ,EACR,SAAS,EACT,UAAU,EACV,QAAQ,EACR,UAAU,EACX,MAAM,2BAA2B,CAAC;AAiBnC,MAAM,MAAM,SAAS,GAAG;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,wBAAgB,kBAAkB,CAChC,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,KAAK,EACZ,iBAAiB,EAAE,MAAM,EACzB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,GAClB,SAAS,EAAE,CAqDb;AAED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,WAAW,EACpB,YAAY,EAAE,KAAK,EACnB,iBAAiB,EAAE,MAAM,GACxB,SAAS,CA+BX;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,SAAS,CAeX;AAED,wBAAgB,oBAAoB,CAClC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,UAAU,EAAE,MAAM,EAClB,OAAO,EAAE,MAAM,GACd,SAAS,EAAE,CA+Db;AAED,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,QAAQ,GAAG,QAAQ,EACzB,KAAK,EAAE,MAAM,GAAG,MAAM,EACtB,IAAI,EAAE,QAAQ,GAAG,SAAS,EAC1B,MAAM,EAAE,UAAU,GAAG,SAAS,EAC9B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,GAC/B,SAAS,CAgBX;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,SAAS,EAChB,UAAU,CAAC,EAAE,UAAU,GACtB,SAAS,CAcX;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,SAAS,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,GAC/B,SAAS,CAsBX;AAED,wBAAgB,mBAAmB,CACjC,IAAI,EAAE,QAAQ,EACd,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,QAAQ,EACd,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EAChC,SAAS,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,EAChC,OAAO,EAAE,MAAM,GACd,SAAS,CA+BX;AAED,wBAAgB,eAAe,CAAC,MAAM,EAAE,UAAU,GAAG,SAAS,CAW7D"}
|
|
@@ -7,6 +7,8 @@ exports.buildOnBeginEvents = buildOnBeginEvents;
|
|
|
7
7
|
exports.buildOnProjectEvent = buildOnProjectEvent;
|
|
8
8
|
exports.buildOnTestBeginEvent = buildOnTestBeginEvent;
|
|
9
9
|
exports.buildOnTestEndEvents = buildOnTestEndEvents;
|
|
10
|
+
exports.buildOnStdIOEvent = buildOnStdIOEvent;
|
|
11
|
+
exports.buildOnErrorEvent = buildOnErrorEvent;
|
|
10
12
|
exports.buildOnStepBeginEvent = buildOnStepBeginEvent;
|
|
11
13
|
exports.buildOnStepEndEvent = buildOnStepEndEvent;
|
|
12
14
|
exports.buildOnEndEvent = buildOnEndEvent;
|
|
@@ -108,7 +110,7 @@ function buildOnTestBeginEvent(test, result, resultIdMap) {
|
|
|
108
110
|
},
|
|
109
111
|
};
|
|
110
112
|
}
|
|
111
|
-
function buildOnTestEndEvents(test, result, resultIdMap, stagingDir) {
|
|
113
|
+
function buildOnTestEndEvents(test, result, resultIdMap, stagingDir, rootDir) {
|
|
112
114
|
const resultId = (0, ibr_utils_1.getOrCreateId)(resultIdMap, `${test.id}-${result.retry}`);
|
|
113
115
|
const events = [];
|
|
114
116
|
// onAttach for each attachment (matching Playwright's blob format)
|
|
@@ -131,6 +133,11 @@ function buildOnTestEndEvents(test, result, resultIdMap, stagingDir) {
|
|
|
131
133
|
name: attachment.name,
|
|
132
134
|
contentType: attachment.contentType,
|
|
133
135
|
path: attachmentPath,
|
|
136
|
+
// Body-based attachments travel inline, mirroring
|
|
137
|
+
// TeleReporterEmitter._serializeAttachments.
|
|
138
|
+
base64: attachment.body
|
|
139
|
+
? attachment.body.toString("base64")
|
|
140
|
+
: undefined,
|
|
134
141
|
},
|
|
135
142
|
],
|
|
136
143
|
},
|
|
@@ -143,7 +150,7 @@ function buildOnTestEndEvents(test, result, resultIdMap, stagingDir) {
|
|
|
143
150
|
testId: test.id,
|
|
144
151
|
expectedStatus: test.expectedStatus,
|
|
145
152
|
timeout: test.timeout,
|
|
146
|
-
annotations: test.annotations,
|
|
153
|
+
annotations: (0, ibr_utils_1.relativeAnnotationLocations)(test.annotations, rootDir),
|
|
147
154
|
},
|
|
148
155
|
result: {
|
|
149
156
|
id: resultId,
|
|
@@ -155,11 +162,47 @@ function buildOnTestEndEvents(test, result, resultIdMap, stagingDir) {
|
|
|
155
162
|
location: e.location,
|
|
156
163
|
snippet: e.snippet,
|
|
157
164
|
})),
|
|
165
|
+
// Blob v2 carries annotations on the result; the merge reads them
|
|
166
|
+
// from here (result-level), not from the test payload above.
|
|
167
|
+
annotations: result.annotations?.length
|
|
168
|
+
? (0, ibr_utils_1.relativeAnnotationLocations)(result.annotations, rootDir)
|
|
169
|
+
: undefined,
|
|
158
170
|
},
|
|
159
171
|
},
|
|
160
172
|
});
|
|
161
173
|
return events;
|
|
162
174
|
}
|
|
175
|
+
function buildOnStdIOEvent(type, chunk, test, result, resultIdMap) {
|
|
176
|
+
const isBase64 = typeof chunk !== "string";
|
|
177
|
+
const data = isBase64 ? chunk.toString("base64") : chunk;
|
|
178
|
+
return {
|
|
179
|
+
method: "onStdIO",
|
|
180
|
+
params: {
|
|
181
|
+
testId: test?.id,
|
|
182
|
+
resultId: test && result
|
|
183
|
+
? (0, ibr_utils_1.getOrCreateId)(resultIdMap, `${test.id}-${result.retry}`)
|
|
184
|
+
: undefined,
|
|
185
|
+
type,
|
|
186
|
+
data,
|
|
187
|
+
isBase64,
|
|
188
|
+
},
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
function buildOnErrorEvent(error, workerInfo) {
|
|
192
|
+
return {
|
|
193
|
+
method: "onError",
|
|
194
|
+
params: {
|
|
195
|
+
error,
|
|
196
|
+
workerInfo: workerInfo
|
|
197
|
+
? {
|
|
198
|
+
workerIndex: workerInfo.workerIndex,
|
|
199
|
+
parallelIndex: workerInfo.parallelIndex,
|
|
200
|
+
projectName: workerInfo.project.name,
|
|
201
|
+
}
|
|
202
|
+
: undefined,
|
|
203
|
+
},
|
|
204
|
+
};
|
|
205
|
+
}
|
|
163
206
|
function buildOnStepBeginEvent(test, result, step, resultIdMap, stepIdMap) {
|
|
164
207
|
const resultId = (0, ibr_utils_1.getOrCreateId)(resultIdMap, `${test.id}-${result.retry}`);
|
|
165
208
|
const stepId = (0, ibr_utils_1.getOrCreateStepId)(stepIdMap, step);
|
|
@@ -182,7 +225,7 @@ function buildOnStepBeginEvent(test, result, step, resultIdMap, stepIdMap) {
|
|
|
182
225
|
},
|
|
183
226
|
};
|
|
184
227
|
}
|
|
185
|
-
function buildOnStepEndEvent(test, result, step, resultIdMap, stepIdMap) {
|
|
228
|
+
function buildOnStepEndEvent(test, result, step, resultIdMap, stepIdMap, rootDir) {
|
|
186
229
|
const resultId = (0, ibr_utils_1.getOrCreateId)(resultIdMap, `${test.id}-${result.retry}`);
|
|
187
230
|
const stepId = (0, ibr_utils_1.getOrCreateStepId)(stepIdMap, step);
|
|
188
231
|
return {
|
|
@@ -201,6 +244,14 @@ function buildOnStepEndEvent(test, result, step, resultIdMap, stepIdMap) {
|
|
|
201
244
|
snippet: step.error.snippet,
|
|
202
245
|
}
|
|
203
246
|
: undefined,
|
|
247
|
+
// Indices into the result's attachments, matching
|
|
248
|
+
// TeleReporterEmitter._serializeStepEnd.
|
|
249
|
+
attachments: step.attachments.length
|
|
250
|
+
? step.attachments.map((a) => result.attachments.indexOf(a))
|
|
251
|
+
: undefined,
|
|
252
|
+
annotations: step.annotations.length
|
|
253
|
+
? (0, ibr_utils_1.relativeAnnotationLocations)(step.annotations, rootDir)
|
|
254
|
+
: undefined,
|
|
204
255
|
},
|
|
205
256
|
},
|
|
206
257
|
};
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import type { BlobEvent } from "./harness";
|
|
2
|
+
export interface NormalizedAttachment {
|
|
3
|
+
name: string;
|
|
4
|
+
contentType: string;
|
|
5
|
+
/**
|
|
6
|
+
* Whether the merged report still points at real content (path or inline
|
|
7
|
+
* body). Compared only where a scenario opts in: the empirical pipeline
|
|
8
|
+
* resolves attachment bytes through _empirical_urls.json, so name+type is
|
|
9
|
+
* the baseline parity contract.
|
|
10
|
+
*/
|
|
11
|
+
hasContent: boolean;
|
|
12
|
+
}
|
|
13
|
+
export interface NormalizedResult {
|
|
14
|
+
retry: number;
|
|
15
|
+
status: string;
|
|
16
|
+
errorMessages: string[];
|
|
17
|
+
stdout: string[];
|
|
18
|
+
stderr: string[];
|
|
19
|
+
attachments: NormalizedAttachment[];
|
|
20
|
+
}
|
|
21
|
+
export interface NormalizedTest {
|
|
22
|
+
projectName: string;
|
|
23
|
+
expectedStatus: string;
|
|
24
|
+
status: string;
|
|
25
|
+
annotations: {
|
|
26
|
+
type: string;
|
|
27
|
+
description?: string;
|
|
28
|
+
}[];
|
|
29
|
+
results: NormalizedResult[];
|
|
30
|
+
}
|
|
31
|
+
export interface NormalizedSpec {
|
|
32
|
+
title: string;
|
|
33
|
+
file: string;
|
|
34
|
+
ok: boolean;
|
|
35
|
+
tests: NormalizedTest[];
|
|
36
|
+
}
|
|
37
|
+
export interface NormalizedSuite {
|
|
38
|
+
title: string;
|
|
39
|
+
file: string;
|
|
40
|
+
specs: NormalizedSpec[];
|
|
41
|
+
suites: NormalizedSuite[];
|
|
42
|
+
}
|
|
43
|
+
export interface NormalizedReport {
|
|
44
|
+
suites: NormalizedSuite[];
|
|
45
|
+
errorMessages: string[];
|
|
46
|
+
stats: Record<string, unknown>;
|
|
47
|
+
}
|
|
48
|
+
export interface NormalizeOptions {
|
|
49
|
+
/** Absolute fixture dir, replaced with a stable token in messages. */
|
|
50
|
+
cwd: string;
|
|
51
|
+
/** Include attachment `hasContent` in the comparison (see above). */
|
|
52
|
+
compareAttachmentContent?: boolean;
|
|
53
|
+
}
|
|
54
|
+
export declare function normalizeJsonReport(report: Record<string, unknown>, opts: NormalizeOptions): NormalizedReport;
|
|
55
|
+
/**
|
|
56
|
+
* Per-result step signatures from a raw blob event stream, keyed by
|
|
57
|
+
* `testId:retry` (stable across both reporters — they see the same run).
|
|
58
|
+
* The merged JSON report drops steps, so step parity is checked here.
|
|
59
|
+
*/
|
|
60
|
+
export declare function stepSignatures(events: BlobEvent[]): Map<string, string[]>;
|
|
61
|
+
/** Event method names present in `reference` but absent from `candidate`. */
|
|
62
|
+
export declare function missingEventMethods(reference: BlobEvent[], candidate: BlobEvent[]): string[];
|
|
63
|
+
//# sourceMappingURL=parity-utils.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parity-utils.d.ts","sourceRoot":"","sources":["../../src/reporter/parity-utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAiD3C,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,UAAU,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,WAAW,EAAE,oBAAoB,EAAE,CAAC;CACrC;AAED,MAAM,WAAW,cAAc;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,WAAW,CAAC,EAAE,MAAM,CAAA;KAAE,EAAE,CAAC;IACtD,OAAO,EAAE,gBAAgB,EAAE,CAAC;CAC7B;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,KAAK,EAAE,cAAc,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,MAAM,EAAE,eAAe,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,eAAe,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CAChC;AAED,MAAM,WAAW,gBAAgB;IAC/B,sEAAsE;IACtE,GAAG,EAAE,MAAM,CAAC;IACZ,qEAAqE;IACrE,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC;AAwED,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,IAAI,EAAE,gBAAgB,GACrB,gBAAgB,CAclB;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CA+BzE;AAED,6EAA6E;AAC7E,wBAAgB,mBAAmB,CACjC,SAAS,EAAE,SAAS,EAAE,EACtB,SAAS,EAAE,SAAS,EAAE,GACrB,MAAM,EAAE,CASV"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.normalizeJsonReport = normalizeJsonReport;
|
|
4
|
+
exports.stepSignatures = stepSignatures;
|
|
5
|
+
exports.missingEventMethods = missingEventMethods;
|
|
6
|
+
const ANSI_RE = /\u001b\[[0-9;]*m/g;
|
|
7
|
+
function normalizeText(text, cwd) {
|
|
8
|
+
return text.replace(ANSI_RE, "").split(cwd).join("<cwd>").trimEnd();
|
|
9
|
+
}
|
|
10
|
+
function normalizeStdio(entries, cwd) {
|
|
11
|
+
return (entries ?? []).map((entry) => typeof entry === "string"
|
|
12
|
+
? normalizeText(entry, cwd)
|
|
13
|
+
: entry.text !== undefined
|
|
14
|
+
? normalizeText(entry.text, cwd)
|
|
15
|
+
: `<buffer:${entry.buffer?.length ?? 0}>`);
|
|
16
|
+
}
|
|
17
|
+
function normalizeResult(result, opts) {
|
|
18
|
+
return {
|
|
19
|
+
retry: result.retry,
|
|
20
|
+
status: result.status,
|
|
21
|
+
errorMessages: (result.errors ?? []).map((e) => normalizeText(e.message ?? "", opts.cwd)),
|
|
22
|
+
stdout: normalizeStdio(result.stdout, opts.cwd),
|
|
23
|
+
stderr: normalizeStdio(result.stderr, opts.cwd),
|
|
24
|
+
attachments: (result.attachments ?? []).map((a) => ({
|
|
25
|
+
name: a.name,
|
|
26
|
+
contentType: a.contentType,
|
|
27
|
+
hasContent: opts.compareAttachmentContent
|
|
28
|
+
? Boolean(a.path || a.body)
|
|
29
|
+
: true,
|
|
30
|
+
})),
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
function normalizeSuite(suite, opts) {
|
|
34
|
+
return {
|
|
35
|
+
title: suite.title,
|
|
36
|
+
file: suite.file,
|
|
37
|
+
specs: (suite.specs ?? [])
|
|
38
|
+
.map((spec) => ({
|
|
39
|
+
title: spec.title,
|
|
40
|
+
file: spec.file,
|
|
41
|
+
ok: spec.ok,
|
|
42
|
+
tests: (spec.tests ?? []).map((test) => ({
|
|
43
|
+
projectName: test.projectName,
|
|
44
|
+
expectedStatus: test.expectedStatus,
|
|
45
|
+
status: test.status,
|
|
46
|
+
annotations: test.annotations ?? [],
|
|
47
|
+
results: (test.results ?? [])
|
|
48
|
+
.map((r) => normalizeResult(r, opts))
|
|
49
|
+
.sort((a, b) => a.retry - b.retry),
|
|
50
|
+
})),
|
|
51
|
+
}))
|
|
52
|
+
.sort((a, b) => a.title.localeCompare(b.title)),
|
|
53
|
+
suites: (suite.suites ?? [])
|
|
54
|
+
.map((s) => normalizeSuite(s, opts))
|
|
55
|
+
.sort((a, b) => a.title.localeCompare(b.title)),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function normalizeJsonReport(report, opts) {
|
|
59
|
+
const typed = report;
|
|
60
|
+
const stats = { ...(typed.stats ?? {}) };
|
|
61
|
+
delete stats.startTime;
|
|
62
|
+
delete stats.duration;
|
|
63
|
+
return {
|
|
64
|
+
suites: (typed.suites ?? [])
|
|
65
|
+
.map((s) => normalizeSuite(s, opts))
|
|
66
|
+
.sort((a, b) => (a.file + a.title).localeCompare(b.file + b.title)),
|
|
67
|
+
errorMessages: (typed.errors ?? []).map((e) => normalizeText(e.message ?? "", opts.cwd)),
|
|
68
|
+
stats,
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Per-result step signatures from a raw blob event stream, keyed by
|
|
73
|
+
* `testId:retry` (stable across both reporters — they see the same run).
|
|
74
|
+
* The merged JSON report drops steps, so step parity is checked here.
|
|
75
|
+
*/
|
|
76
|
+
function stepSignatures(events) {
|
|
77
|
+
const resultKey = new Map(); // resultId -> testId:retry
|
|
78
|
+
for (const event of events) {
|
|
79
|
+
if (event.method !== "onTestBegin") {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
const params = event.params;
|
|
83
|
+
resultKey.set(params.result.id, `${params.testId}:${params.result.retry}`);
|
|
84
|
+
}
|
|
85
|
+
const signatures = new Map();
|
|
86
|
+
for (const event of events) {
|
|
87
|
+
if (event.method !== "onStepBegin") {
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
const params = event.params;
|
|
91
|
+
const key = resultKey.get(params.resultId);
|
|
92
|
+
if (!key) {
|
|
93
|
+
continue;
|
|
94
|
+
}
|
|
95
|
+
const list = signatures.get(key) ?? [];
|
|
96
|
+
list.push(`${params.step.category}:${params.step.title}`);
|
|
97
|
+
signatures.set(key, list);
|
|
98
|
+
}
|
|
99
|
+
return signatures;
|
|
100
|
+
}
|
|
101
|
+
/** Event method names present in `reference` but absent from `candidate`. */
|
|
102
|
+
function missingEventMethods(reference, candidate) {
|
|
103
|
+
const candidateMethods = new Set(candidate.map((e) => e.method));
|
|
104
|
+
return [
|
|
105
|
+
...new Set(reference
|
|
106
|
+
.map((e) => e.method)
|
|
107
|
+
.filter((method) => !candidateMethods.has(method))),
|
|
108
|
+
].sort();
|
|
109
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@empiricalrun/playwright-utils",
|
|
3
|
-
"version": "0.58.
|
|
3
|
+
"version": "0.58.3",
|
|
4
4
|
"publishConfig": {
|
|
5
5
|
"registry": "https://registry.npmjs.org/",
|
|
6
6
|
"access": "public"
|
|
@@ -30,16 +30,16 @@
|
|
|
30
30
|
},
|
|
31
31
|
"author": "Empirical Team <hey@empirical.run>",
|
|
32
32
|
"devDependencies": {
|
|
33
|
-
"@playwright/test": "1.
|
|
33
|
+
"@playwright/test": "1.62.1",
|
|
34
34
|
"@types/async-retry": "^1.4.8",
|
|
35
35
|
"@types/authenticator": "^1.1.4",
|
|
36
36
|
"@types/babel__code-frame": "^7.0.6",
|
|
37
37
|
"@types/console-log-level": "^1.4.5",
|
|
38
38
|
"@types/mime": "3.0.0",
|
|
39
|
-
"@types/node": "^
|
|
39
|
+
"@types/node": "^22",
|
|
40
40
|
"@types/serve-handler": "^6.1.4",
|
|
41
41
|
"openapi-typescript": "7.6.1",
|
|
42
|
-
"playwright-core": "1.
|
|
42
|
+
"playwright-core": "1.62.1",
|
|
43
43
|
"serve-handler": "^6.1.6",
|
|
44
44
|
"@empiricalrun/shared-types": "0.15.3"
|
|
45
45
|
},
|
|
@@ -54,8 +54,8 @@
|
|
|
54
54
|
"puppeteer-extra-plugin-recaptcha": "^3.6.8",
|
|
55
55
|
"rimraf": "^6.0.1",
|
|
56
56
|
"ts-morph": "^23.0.0",
|
|
57
|
-
"@empiricalrun/cua": "^0.4.1",
|
|
58
57
|
"@empiricalrun/llm": "^0.27.0",
|
|
58
|
+
"@empiricalrun/cua": "^0.4.1",
|
|
59
59
|
"@empiricalrun/r2-uploader": "^0.10.1",
|
|
60
60
|
"@empiricalrun/reporter": "^0.29.1"
|
|
61
61
|
},
|
package/tsconfig.tsbuildinfo
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"7.0.0-dev.20260623.1","root":["./src/api-worker-client.ts","./src/email.ts","./src/inbox-client.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/generated/api-worker-openapi.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/overlay-tests/visual.spec.ts","./src/reporter/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/reporter-state.ts","./src/reporter/test-case-sync-reporter.ts","./src/reporter/test-case-sync.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"]}
|
|
1
|
+
{"version":"7.0.0-dev.20260623.1","root":["./src/api-worker-client.ts","./src/email.ts","./src/inbox-client.ts","./src/index.ts","./src/kv.ts","./src/logger.ts","./src/mailosaur-client.ts","./src/playwright-extensions.ts","./src/telemetry.ts","./src/webhook.ts","./src/auth/google.ts","./src/auth/index.ts","./src/auth/types.ts","./src/captcha/index.ts","./src/config/index.ts","./src/config/proxy.ts","./src/config/devices/types.ts","./src/generated/api-worker-openapi.ts","./src/overlay-tests/cache.spec.ts","./src/overlay-tests/click.spec.ts","./src/overlay-tests/fixtures.ts","./src/overlay-tests/patch.spec.ts","./src/overlay-tests/visual.spec.ts","./src/reporter/attachment-cleanup-test-reporter.ts","./src/reporter/attachment-cleanup.ts","./src/reporter/blob-utils.ts","./src/reporter/empirical-reporter.ts","./src/reporter/failing-line.ts","./src/reporter/harness.ts","./src/reporter/ibr-utils.ts","./src/reporter/incremental-blob-reporter.ts","./src/reporter/lifecycle-events.ts","./src/reporter/local-test.ts","./src/reporter/parity-utils.ts","./src/reporter/reporter-state.ts","./src/reporter/test-case-sync-reporter.ts","./src/reporter/test-case-sync.ts","./src/reporter/uploader.ts","./src/reporter/util.ts","./src/test/constants.ts","./src/test/coverage.ts","./src/test/index.ts","./src/test/types.ts","./src/test/video-labels.ts","./src/test/expect/index.ts","./src/test/expect/types.ts","./src/test/expect/visual.ts","./src/test/expect/webhook.ts","./src/test/scripts/agent-capabilities.ts","./src/test/scripts/index.ts","./src/test/scripts/locator-highlights.ts","./src/test/scripts/locator-vision.ts","./src/test/scripts/mouse-pointer.ts","./src/test/scripts/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/cache.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/index.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/prompt.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/types.ts","./src/test/scripts/pw-locator-patch/dismiss-overlays/utils.ts","./src/test/scripts/pw-locator-patch/highlight/click.ts","./src/test/scripts/pw-locator-patch/highlight/expect.ts","./src/test/scripts/pw-locator-patch/highlight/hover.ts","./src/test/scripts/pw-locator-patch/highlight/inner-text.ts","./src/test/scripts/pw-locator-patch/highlight/input-value.ts","./src/test/scripts/pw-locator-patch/highlight/is-checked.ts","./src/test/scripts/pw-locator-patch/highlight/is-disabled.ts","./src/test/scripts/pw-locator-patch/highlight/is-editable.ts","./src/test/scripts/pw-locator-patch/highlight/text-content.ts","./src/test/scripts/pw-locator-patch/utils/index.ts","./src/test/scripts/pw-locator-patch/vision/query.ts"]}
|