patchright-bun 1.58.2 → 1.59.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/ThirdPartyNotices.txt +10 -1133
- package/lib/agents/generateAgents.js +2 -4
- package/lib/common/config.js +4 -5
- package/lib/common/expectBundleImpl.js +221 -221
- package/lib/common/process.js +1 -0
- package/lib/common/test.js +10 -1
- package/lib/common/testType.js +3 -0
- package/lib/common/validators.js +38 -38
- package/lib/errorContext.js +121 -0
- package/lib/index.js +98 -60
- package/lib/isomorphic/teleReceiver.js +26 -9
- package/lib/isomorphic/testServerConnection.js +3 -5
- package/lib/matchers/matchers.js +2 -0
- package/lib/matchers/toMatchAriaSnapshot.js +5 -1
- package/lib/matchers/toMatchSnapshot.js +42 -35
- package/lib/mcp/test/browserBackend.js +45 -22
- package/lib/mcp/test/generatorTools.js +9 -9
- package/lib/mcp/test/plannerTools.js +17 -17
- package/lib/mcp/test/testBackend.js +27 -27
- package/lib/mcp/test/testContext.js +6 -8
- package/lib/mcp/test/testTools.js +9 -9
- package/lib/plugins/webServerPlugin.js +2 -1
- package/lib/program.js +34 -212
- package/lib/reportActions.js +80 -0
- package/lib/reporters/base.js +4 -5
- package/lib/reporters/blob.js +2 -2
- package/lib/reporters/github.js +1 -2
- package/lib/reporters/html.js +64 -31
- package/lib/reporters/junit.js +104 -15
- package/lib/reporters/line.js +1 -1
- package/lib/reporters/list.js +2 -3
- package/lib/reporters/merge.js +47 -26
- package/lib/reporters/multiplexer.js +6 -2
- package/lib/reporters/teleEmitter.js +2 -0
- package/lib/runner/dispatcher.js +6 -14
- package/lib/runner/loadUtils.js +11 -5
- package/lib/runner/projectUtils.js +1 -1
- package/lib/runner/reporters.js +5 -0
- package/lib/runner/tasks.js +11 -8
- package/lib/runner/testRunner.js +2 -2
- package/lib/runner/workerHost.js +0 -3
- package/lib/testActions.js +220 -0
- package/lib/transform/babelBundle.js +0 -3
- package/lib/transform/babelBundleImpl.js +134 -134
- package/lib/transform/compilationCache.js +0 -2
- package/lib/transform/esmLoader.js +8 -6
- package/lib/transform/transform.js +3 -10
- package/lib/utilsBundle.js +0 -7
- package/lib/utilsBundleImpl.js +48 -51
- package/lib/worker/fixtureRunner.js +2 -2
- package/lib/worker/testInfo.js +10 -14
- package/lib/worker/testTracing.js +12 -6
- package/lib/worker/timeoutManager.js +14 -3
- package/lib/worker/workerMain.js +24 -21
- package/package.json +2 -6
- package/types/test.d.ts +83 -12
- package/lib/mcp/browser/browserContextFactory.js +0 -329
- package/lib/mcp/browser/browserServerBackend.js +0 -84
- package/lib/mcp/browser/config.js +0 -421
- package/lib/mcp/browser/context.js +0 -244
- package/lib/mcp/browser/response.js +0 -278
- package/lib/mcp/browser/sessionLog.js +0 -75
- package/lib/mcp/browser/tab.js +0 -343
- package/lib/mcp/browser/tools/common.js +0 -65
- package/lib/mcp/browser/tools/console.js +0 -46
- package/lib/mcp/browser/tools/dialogs.js +0 -60
- package/lib/mcp/browser/tools/evaluate.js +0 -61
- package/lib/mcp/browser/tools/files.js +0 -58
- package/lib/mcp/browser/tools/form.js +0 -63
- package/lib/mcp/browser/tools/install.js +0 -72
- package/lib/mcp/browser/tools/keyboard.js +0 -107
- package/lib/mcp/browser/tools/mouse.js +0 -107
- package/lib/mcp/browser/tools/navigate.js +0 -71
- package/lib/mcp/browser/tools/network.js +0 -63
- package/lib/mcp/browser/tools/open.js +0 -57
- package/lib/mcp/browser/tools/pdf.js +0 -49
- package/lib/mcp/browser/tools/runCode.js +0 -78
- package/lib/mcp/browser/tools/screenshot.js +0 -93
- package/lib/mcp/browser/tools/snapshot.js +0 -173
- package/lib/mcp/browser/tools/tabs.js +0 -67
- package/lib/mcp/browser/tools/tool.js +0 -47
- package/lib/mcp/browser/tools/tracing.js +0 -74
- package/lib/mcp/browser/tools/utils.js +0 -94
- package/lib/mcp/browser/tools/verify.js +0 -143
- package/lib/mcp/browser/tools/wait.js +0 -63
- package/lib/mcp/browser/tools.js +0 -84
- package/lib/mcp/browser/watchdog.js +0 -44
- package/lib/mcp/config.d.js +0 -16
- package/lib/mcp/extension/cdpRelay.js +0 -351
- package/lib/mcp/extension/extensionContextFactory.js +0 -76
- package/lib/mcp/extension/protocol.js +0 -28
- package/lib/mcp/index.js +0 -61
- package/lib/mcp/log.js +0 -35
- package/lib/mcp/program.js +0 -111
- package/lib/mcp/sdk/exports.js +0 -28
- package/lib/mcp/sdk/http.js +0 -152
- package/lib/mcp/sdk/inProcessTransport.js +0 -71
- package/lib/mcp/sdk/server.js +0 -223
- package/lib/mcp/sdk/tool.js +0 -47
- package/lib/mcp/terminal/cli.js +0 -296
- package/lib/mcp/terminal/command.js +0 -56
- package/lib/mcp/terminal/commands.js +0 -333
- package/lib/mcp/terminal/daemon.js +0 -129
- package/lib/mcp/terminal/help.json +0 -32
- package/lib/mcp/terminal/helpGenerator.js +0 -88
- package/lib/mcp/terminal/socketConnection.js +0 -80
- package/lib/runner/storage.js +0 -91
- package/lib/transform/md.js +0 -221
package/lib/reporters/html.js
CHANGED
|
@@ -40,7 +40,6 @@ var import_utils = require("patchright-bun-core/lib/utils");
|
|
|
40
40
|
var import_utils2 = require("patchright-bun-core/lib/utils");
|
|
41
41
|
var import_utilsBundle = require("patchright-bun-core/lib/utilsBundle");
|
|
42
42
|
var import_utilsBundle2 = require("patchright-bun-core/lib/utilsBundle");
|
|
43
|
-
var import_zipBundle = require("patchright-bun-core/lib/zipBundle");
|
|
44
43
|
var import_base = require("./base");
|
|
45
44
|
var import_babelBundle = require("../transform/babelBundle");
|
|
46
45
|
var import_util = require("../util");
|
|
@@ -51,6 +50,7 @@ const isHtmlReportOption = (type) => {
|
|
|
51
50
|
class HtmlReporter {
|
|
52
51
|
constructor(options) {
|
|
53
52
|
this._topLevelErrors = [];
|
|
53
|
+
this._reportConfigs = /* @__PURE__ */ new Map();
|
|
54
54
|
this._machines = [];
|
|
55
55
|
this._options = options;
|
|
56
56
|
}
|
|
@@ -105,25 +105,22 @@ class HtmlReporter {
|
|
|
105
105
|
onError(error) {
|
|
106
106
|
this._topLevelErrors.push(error);
|
|
107
107
|
}
|
|
108
|
-
|
|
109
|
-
this.
|
|
108
|
+
onReportConfigure(params) {
|
|
109
|
+
this._reportConfigs.set(params.reportPath, params.config);
|
|
110
|
+
}
|
|
111
|
+
onReportEnd(params) {
|
|
112
|
+
const config = this._reportConfigs.get(params.reportPath);
|
|
113
|
+
if (config)
|
|
114
|
+
this._machines.push({ config, result: params.result, reportPath: params.reportPath });
|
|
110
115
|
}
|
|
111
116
|
async onEnd(result) {
|
|
112
117
|
const projectSuites = this.suite.suites;
|
|
113
118
|
await (0, import_utils.removeFolders)([this._outputFolder]);
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
noSnippets = noSnippets || this._options.noSnippets;
|
|
120
|
-
let noCopyPrompt;
|
|
121
|
-
if (process.env.PLAYWRIGHT_HTML_NO_COPY_PROMPT === "false" || process.env.PLAYWRIGHT_HTML_NO_COPY_PROMPT === "0")
|
|
122
|
-
noCopyPrompt = false;
|
|
123
|
-
else if (process.env.PLAYWRIGHT_HTML_NO_COPY_PROMPT)
|
|
124
|
-
noCopyPrompt = true;
|
|
125
|
-
noCopyPrompt = noCopyPrompt || this._options.noCopyPrompt;
|
|
126
|
-
const builder = new HtmlBuilder(this.config, this._outputFolder, this._attachmentsBaseURL, {
|
|
119
|
+
const noSnippets = parseBooleanEnvVar("PLAYWRIGHT_HTML_NO_SNIPPETS") ?? this._options.noSnippets;
|
|
120
|
+
const noCopyPrompt = parseBooleanEnvVar("PLAYWRIGHT_HTML_NO_COPY_PROMPT") ?? this._options.noCopyPrompt;
|
|
121
|
+
const doNotInlineAssets = parseBooleanEnvVar("PLAYWRIGHT_HTML_DO_NOT_INLINE_ASSETS") ?? this._options.doNotInlineAssets ?? false;
|
|
122
|
+
const { yazl } = await import("playwright-core/lib/zipBundle");
|
|
123
|
+
const builder = new HtmlBuilder(yazl, this.config, this._outputFolder, this._attachmentsBaseURL, doNotInlineAssets, {
|
|
127
124
|
title: process.env.PLAYWRIGHT_HTML_TITLE || this._options.title,
|
|
128
125
|
noSnippets,
|
|
129
126
|
noCopyPrompt
|
|
@@ -134,7 +131,8 @@ class HtmlReporter {
|
|
|
134
131
|
if (process.env.CI || !this._buildResult)
|
|
135
132
|
return;
|
|
136
133
|
const { ok, singleTestId } = this._buildResult;
|
|
137
|
-
const
|
|
134
|
+
const isCodingAgent = !!process.env.CLAUDECODE || !!process.env.COPILOT_CLI;
|
|
135
|
+
const shouldOpen = !isCodingAgent && !!process.stdin.isTTY && (this._open === "always" || !ok && this._open === "on-failure");
|
|
138
136
|
if (shouldOpen) {
|
|
139
137
|
await showHTMLReport(this._outputFolder, this._host, this._port, singleTestId);
|
|
140
138
|
} else if (this._options._mode === "test" && !!process.stdin.isTTY) {
|
|
@@ -164,6 +162,14 @@ function getHtmlReportOptionProcessEnv() {
|
|
|
164
162
|
}
|
|
165
163
|
return htmlOpenEnv;
|
|
166
164
|
}
|
|
165
|
+
function parseBooleanEnvVar(name) {
|
|
166
|
+
const value = process.env[name];
|
|
167
|
+
if (value === "false" || value === "0")
|
|
168
|
+
return false;
|
|
169
|
+
if (value)
|
|
170
|
+
return true;
|
|
171
|
+
return void 0;
|
|
172
|
+
}
|
|
167
173
|
function standaloneDefaultFolder() {
|
|
168
174
|
return reportFolderFromEnv() ?? (0, import_util.resolveReporterOutputPath)("playwright-report", process.cwd(), void 0);
|
|
169
175
|
}
|
|
@@ -209,14 +215,15 @@ function startHtmlReportServer(folder) {
|
|
|
209
215
|
return server;
|
|
210
216
|
}
|
|
211
217
|
class HtmlBuilder {
|
|
212
|
-
constructor(config, outputDir, attachmentsBaseURL, options) {
|
|
218
|
+
constructor(yazl, config, outputDir, attachmentsBaseURL, doNotInlineAssets, options) {
|
|
213
219
|
this._stepsInFile = new import_utils.MultiMap();
|
|
214
220
|
this._hasTraces = false;
|
|
221
|
+
this._dataZipFile = new yazl.ZipFile();
|
|
215
222
|
this._config = config;
|
|
216
223
|
this._reportFolder = outputDir;
|
|
217
224
|
this._options = options;
|
|
225
|
+
this._doNotInlineAssets = doNotInlineAssets;
|
|
218
226
|
import_fs.default.mkdirSync(this._reportFolder, { recursive: true });
|
|
219
|
-
this._dataZipFile = new import_zipBundle.yazl.ZipFile();
|
|
220
227
|
this._attachmentsBaseURL = attachmentsBaseURL;
|
|
221
228
|
}
|
|
222
229
|
async build(metadata, projectSuites, result, topLevelErrors, machines) {
|
|
@@ -264,11 +271,11 @@ class HtmlBuilder {
|
|
|
264
271
|
stats: { ...[...data.values()].reduce((a, e) => addStats(a, e.testFileSummary.stats), emptyStats()) },
|
|
265
272
|
errors: topLevelErrors.map((error) => (0, import_base.formatError)(import_base.internalScreen, error).message),
|
|
266
273
|
options: this._options,
|
|
267
|
-
machines: machines.map((
|
|
268
|
-
duration:
|
|
269
|
-
startTime:
|
|
270
|
-
tag:
|
|
271
|
-
shardIndex:
|
|
274
|
+
machines: machines.map((machine) => ({
|
|
275
|
+
duration: machine.result.duration,
|
|
276
|
+
startTime: machine.result.startTime.getTime(),
|
|
277
|
+
tag: machine.config.tags,
|
|
278
|
+
shardIndex: machine.config.shard?.current
|
|
272
279
|
}))
|
|
273
280
|
};
|
|
274
281
|
htmlReport.files.sort((f1, f2) => {
|
|
@@ -282,8 +289,7 @@ class HtmlBuilder {
|
|
|
282
289
|
const testFile = data.values().next().value.testFile;
|
|
283
290
|
singleTestId = testFile.tests[0].testId;
|
|
284
291
|
}
|
|
285
|
-
const
|
|
286
|
-
await (0, import_utils.copyFileAndMakeWritable)(import_path.default.join(appFolder, "index.html"), import_path.default.join(this._reportFolder, "index.html"));
|
|
292
|
+
const reportIndexFile = await this._writeStaticAssets();
|
|
287
293
|
if (this._hasTraces) {
|
|
288
294
|
const traceViewerFolder = import_path.default.join(require.resolve("patchright-bun-core"), "..", "lib", "vite", "traceViewer");
|
|
289
295
|
const traceViewerTargetFolder = import_path.default.join(this._reportFolder, "trace");
|
|
@@ -300,17 +306,39 @@ class HtmlBuilder {
|
|
|
300
306
|
await (0, import_utils.copyFileAndMakeWritable)(import_path.default.join(traceViewerFolder, "assets", file), import_path.default.join(traceViewerAssetsTargetFolder, file));
|
|
301
307
|
}
|
|
302
308
|
}
|
|
303
|
-
await this._writeReportData(
|
|
309
|
+
await this._writeReportData(reportIndexFile);
|
|
304
310
|
return { ok, singleTestId };
|
|
305
311
|
}
|
|
312
|
+
async _writeStaticAssets() {
|
|
313
|
+
const appFolder = import_path.default.join(require.resolve("patchright-bun-core"), "..", "lib", "vite", "htmlReport");
|
|
314
|
+
const reportIndexFile = import_path.default.join(this._reportFolder, "index.html");
|
|
315
|
+
if (this._doNotInlineAssets) {
|
|
316
|
+
const html = await import_fs.default.promises.readFile(import_path.default.join(appFolder, "index.html"), "utf-8");
|
|
317
|
+
await Promise.all([
|
|
318
|
+
import_fs.default.promises.writeFile(reportIndexFile, html),
|
|
319
|
+
import_fs.default.promises.copyFile(import_path.default.join(appFolder, "report.js"), import_path.default.join(this._reportFolder, "report.js")),
|
|
320
|
+
import_fs.default.promises.copyFile(import_path.default.join(appFolder, "report.css"), import_path.default.join(this._reportFolder, "report.css"))
|
|
321
|
+
]);
|
|
322
|
+
} else {
|
|
323
|
+
let html = await import_fs.default.promises.readFile(import_path.default.join(appFolder, "index.html"), "utf-8");
|
|
324
|
+
const [js, css] = await Promise.all([
|
|
325
|
+
import_fs.default.promises.readFile(import_path.default.join(appFolder, "report.js"), "utf-8"),
|
|
326
|
+
import_fs.default.promises.readFile(import_path.default.join(appFolder, "report.css"), "utf-8")
|
|
327
|
+
]);
|
|
328
|
+
html = html.replace(/<script type="module"[^>]*><\/script>/, () => `<script type="module">${js}</script>`);
|
|
329
|
+
html = html.replace(/<link rel="stylesheet"[^>]*>/, () => `<style type='text/css'>${css}</style>`);
|
|
330
|
+
await import_fs.default.promises.writeFile(reportIndexFile, html);
|
|
331
|
+
}
|
|
332
|
+
return reportIndexFile;
|
|
333
|
+
}
|
|
306
334
|
async _writeReportData(filePath) {
|
|
307
|
-
import_fs.default.appendFileSync(filePath, '<
|
|
335
|
+
import_fs.default.appendFileSync(filePath, '<template id="playwrightReportBase64">data:application/zip;base64,');
|
|
308
336
|
await new Promise((f) => {
|
|
309
337
|
this._dataZipFile.end(void 0, () => {
|
|
310
338
|
this._dataZipFile.outputStream.pipe(new Base64Encoder()).pipe(import_fs.default.createWriteStream(filePath, { flags: "a" })).on("close", f);
|
|
311
339
|
});
|
|
312
340
|
});
|
|
313
|
-
import_fs.default.appendFileSync(filePath, "</
|
|
341
|
+
import_fs.default.appendFileSync(filePath, "</template>");
|
|
314
342
|
}
|
|
315
343
|
_addDataFile(fileName, data) {
|
|
316
344
|
this._dataZipFile.addBuffer(Buffer.from(JSON.stringify(data)), fileName);
|
|
@@ -373,7 +401,11 @@ class HtmlBuilder {
|
|
|
373
401
|
path: path2,
|
|
374
402
|
ok: test.outcome() === "expected" || test.outcome() === "flaky",
|
|
375
403
|
results: results.map((result) => {
|
|
376
|
-
return {
|
|
404
|
+
return {
|
|
405
|
+
attachments: result.attachments.map((a) => ({ name: a.name, contentType: a.contentType, path: a.path })),
|
|
406
|
+
startTime: result.startTime,
|
|
407
|
+
workerIndex: result.workerIndex
|
|
408
|
+
};
|
|
377
409
|
})
|
|
378
410
|
}
|
|
379
411
|
};
|
|
@@ -468,7 +500,8 @@ class HtmlBuilder {
|
|
|
468
500
|
...result.attachments,
|
|
469
501
|
...result.stdout.map((m) => stdioAttachment(m, "stdout")),
|
|
470
502
|
...result.stderr.map((m) => stdioAttachment(m, "stderr"))
|
|
471
|
-
])
|
|
503
|
+
]),
|
|
504
|
+
workerIndex: result.workerIndex
|
|
472
505
|
};
|
|
473
506
|
}
|
|
474
507
|
_createTestStep(dedupedStep, result) {
|
package/lib/reporters/junit.js
CHANGED
|
@@ -40,11 +40,14 @@ class JUnitReporter {
|
|
|
40
40
|
constructor(options) {
|
|
41
41
|
this.totalTests = 0;
|
|
42
42
|
this.totalFailures = 0;
|
|
43
|
+
this.totalErrors = 0;
|
|
43
44
|
this.totalSkipped = 0;
|
|
44
45
|
this.stripANSIControlSequences = false;
|
|
45
46
|
this.includeProjectInTestName = false;
|
|
47
|
+
this.includeRetries = false;
|
|
46
48
|
this.stripANSIControlSequences = (0, import_utils.getAsBooleanFromENV)("PLAYWRIGHT_JUNIT_STRIP_ANSI", !!options.stripANSIControlSequences);
|
|
47
49
|
this.includeProjectInTestName = (0, import_utils.getAsBooleanFromENV)("PLAYWRIGHT_JUNIT_INCLUDE_PROJECT_IN_TEST_NAME", !!options.includeProjectInTestName);
|
|
50
|
+
this.includeRetries = (0, import_utils.getAsBooleanFromENV)("PLAYWRIGHT_JUNIT_INCLUDE_RETRIES", !!options.includeRetries);
|
|
48
51
|
this.configDir = options.configDir;
|
|
49
52
|
this.resolvedOutputFile = (0, import_base.resolveOutputFile)("JUNIT", options)?.outputFile;
|
|
50
53
|
}
|
|
@@ -77,7 +80,7 @@ class JUnitReporter {
|
|
|
77
80
|
tests: self.totalTests,
|
|
78
81
|
failures: self.totalFailures,
|
|
79
82
|
skipped: self.totalSkipped,
|
|
80
|
-
errors:
|
|
83
|
+
errors: self.totalErrors,
|
|
81
84
|
time: result.duration / 1e3
|
|
82
85
|
},
|
|
83
86
|
children
|
|
@@ -95,6 +98,7 @@ class JUnitReporter {
|
|
|
95
98
|
let tests = 0;
|
|
96
99
|
let skipped = 0;
|
|
97
100
|
let failures = 0;
|
|
101
|
+
let errors = 0;
|
|
98
102
|
let duration = 0;
|
|
99
103
|
const children = [];
|
|
100
104
|
const testCaseNamePrefix = projectName && this.includeProjectInTestName ? `[${projectName}] ` : "";
|
|
@@ -102,15 +106,18 @@ class JUnitReporter {
|
|
|
102
106
|
++tests;
|
|
103
107
|
if (test.outcome() === "skipped")
|
|
104
108
|
++skipped;
|
|
105
|
-
if (!test.ok())
|
|
106
|
-
++failures;
|
|
107
109
|
for (const result of test.results)
|
|
108
110
|
duration += result.duration;
|
|
109
|
-
await this._addTestCase(suite.title, testCaseNamePrefix, test, children);
|
|
111
|
+
const classification = await this._addTestCase(suite.title, testCaseNamePrefix, test, children);
|
|
112
|
+
if (classification === "error")
|
|
113
|
+
++errors;
|
|
114
|
+
else if (classification === "failure")
|
|
115
|
+
++failures;
|
|
110
116
|
}
|
|
111
117
|
this.totalTests += tests;
|
|
112
118
|
this.totalSkipped += skipped;
|
|
113
119
|
this.totalFailures += failures;
|
|
120
|
+
this.totalErrors += errors;
|
|
114
121
|
const entry = {
|
|
115
122
|
name: "testsuite",
|
|
116
123
|
attributes: {
|
|
@@ -121,7 +128,7 @@ class JUnitReporter {
|
|
|
121
128
|
failures,
|
|
122
129
|
skipped,
|
|
123
130
|
time: duration / 1e3,
|
|
124
|
-
errors
|
|
131
|
+
errors
|
|
125
132
|
},
|
|
126
133
|
children
|
|
127
134
|
};
|
|
@@ -134,8 +141,7 @@ class JUnitReporter {
|
|
|
134
141
|
// Skip root, project, file
|
|
135
142
|
name: namePrefix + test.titlePath().slice(3).join(" \u203A "),
|
|
136
143
|
// filename
|
|
137
|
-
classname: suiteName
|
|
138
|
-
time: test.results.reduce((acc, value) => acc + value.duration, 0) / 1e3
|
|
144
|
+
classname: suiteName
|
|
139
145
|
},
|
|
140
146
|
children: []
|
|
141
147
|
};
|
|
@@ -158,21 +164,60 @@ class JUnitReporter {
|
|
|
158
164
|
entry.children.push(properties);
|
|
159
165
|
if (test.outcome() === "skipped") {
|
|
160
166
|
entry.children.push({ name: "skipped" });
|
|
161
|
-
return;
|
|
167
|
+
return null;
|
|
168
|
+
}
|
|
169
|
+
if (this.includeRetries && test.ok()) {
|
|
170
|
+
const passResult = test.results[test.results.length - 1];
|
|
171
|
+
entry.attributes.time = passResult.duration / 1e3;
|
|
172
|
+
await this._appendStdIO(entry, [passResult]);
|
|
173
|
+
for (let i = 0; i < test.results.length - 1; i++) {
|
|
174
|
+
const result = test.results[i];
|
|
175
|
+
if (result.status === "passed" || result.status === "skipped")
|
|
176
|
+
continue;
|
|
177
|
+
entry.children.push(await this._buildRetryEntry(result, "flaky"));
|
|
178
|
+
}
|
|
179
|
+
return null;
|
|
180
|
+
}
|
|
181
|
+
if (this.includeRetries) {
|
|
182
|
+
entry.attributes.time = test.results[0].duration / 1e3;
|
|
183
|
+
await this._appendStdIO(entry, [test.results[0]]);
|
|
184
|
+
for (let i = 1; i < test.results.length; i++) {
|
|
185
|
+
const result = test.results[i];
|
|
186
|
+
if (result.status === "passed" || result.status === "skipped")
|
|
187
|
+
continue;
|
|
188
|
+
entry.children.push(await this._buildRetryEntry(result, "rerun"));
|
|
189
|
+
}
|
|
190
|
+
return this._addFailureEntry(test, classifyResultError(test.results[0]), entry);
|
|
162
191
|
}
|
|
163
|
-
|
|
192
|
+
entry.attributes.time = test.results.reduce((acc, value) => acc + value.duration, 0) / 1e3;
|
|
193
|
+
await this._appendStdIO(entry, test.results);
|
|
194
|
+
if (test.ok())
|
|
195
|
+
return null;
|
|
196
|
+
return this._addFailureEntry(test, classifyTestError(test), entry);
|
|
197
|
+
}
|
|
198
|
+
_addFailureEntry(test, errorInfo, entry) {
|
|
199
|
+
if (errorInfo) {
|
|
164
200
|
entry.children.push({
|
|
165
|
-
name:
|
|
166
|
-
attributes: {
|
|
167
|
-
message: `${import_path.default.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`,
|
|
168
|
-
type: "FAILURE"
|
|
169
|
-
},
|
|
201
|
+
name: errorInfo.elementName,
|
|
202
|
+
attributes: { message: errorInfo.message, type: errorInfo.type },
|
|
170
203
|
text: (0, import_util.stripAnsiEscapes)((0, import_base.formatFailure)(import_base.nonTerminalScreen, this.config, test))
|
|
171
204
|
});
|
|
205
|
+
return errorInfo.elementName;
|
|
172
206
|
}
|
|
207
|
+
entry.children.push({
|
|
208
|
+
name: "failure",
|
|
209
|
+
attributes: {
|
|
210
|
+
message: `${import_path.default.basename(test.location.file)}:${test.location.line}:${test.location.column} ${test.title}`,
|
|
211
|
+
type: "FAILURE"
|
|
212
|
+
},
|
|
213
|
+
text: (0, import_util.stripAnsiEscapes)((0, import_base.formatFailure)(import_base.nonTerminalScreen, this.config, test))
|
|
214
|
+
});
|
|
215
|
+
return "failure";
|
|
216
|
+
}
|
|
217
|
+
async _appendStdIO(entry, results) {
|
|
173
218
|
const systemOut = [];
|
|
174
219
|
const systemErr = [];
|
|
175
|
-
for (const result of
|
|
220
|
+
for (const result of results) {
|
|
176
221
|
for (const item of result.stdout)
|
|
177
222
|
systemOut.push(item.toString());
|
|
178
223
|
for (const item of result.stderr)
|
|
@@ -204,6 +249,50 @@ Warning: attachment ${attachmentPath} is missing`);
|
|
|
204
249
|
if (systemErr.length)
|
|
205
250
|
entry.children.push({ name: "system-err", text: systemErr.join("") });
|
|
206
251
|
}
|
|
252
|
+
async _buildRetryEntry(result, prefix) {
|
|
253
|
+
const errorInfo = classifyResultError(result);
|
|
254
|
+
const entry = {
|
|
255
|
+
name: `${prefix}${errorInfo?.elementName === "error" ? "Error" : "Failure"}`,
|
|
256
|
+
attributes: { message: errorInfo?.message || "", type: errorInfo?.type || "FAILURE", time: result.duration / 1e3 },
|
|
257
|
+
children: []
|
|
258
|
+
};
|
|
259
|
+
const stackTrace = result.error?.stack || result.error?.message || result.error?.value || "";
|
|
260
|
+
entry.children.push({ name: "stackTrace", text: (0, import_util.stripAnsiEscapes)(stackTrace) });
|
|
261
|
+
await this._appendStdIO(entry, [result]);
|
|
262
|
+
return entry;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
function classifyResultError(result) {
|
|
266
|
+
const error = result.error;
|
|
267
|
+
if (!error)
|
|
268
|
+
return null;
|
|
269
|
+
const rawMessage = (0, import_util.stripAnsiEscapes)(error.message || error.value || "");
|
|
270
|
+
const nameMatch = rawMessage.match(/^(\w+): /);
|
|
271
|
+
const errorName = nameMatch ? nameMatch[1] : "";
|
|
272
|
+
const messageBody = nameMatch ? rawMessage.slice(nameMatch[0].length) : rawMessage;
|
|
273
|
+
const firstLine = messageBody.split("\n")[0].trim();
|
|
274
|
+
const matcherMatch = rawMessage.match(/expect\(.*?\)\.(not\.)?(\w+)/);
|
|
275
|
+
if (matcherMatch) {
|
|
276
|
+
const matcherName = `expect.${matcherMatch[1] || ""}${matcherMatch[2]}`;
|
|
277
|
+
return {
|
|
278
|
+
elementName: "failure",
|
|
279
|
+
type: matcherName,
|
|
280
|
+
message: firstLine
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
return {
|
|
284
|
+
elementName: "error",
|
|
285
|
+
type: errorName || "Error",
|
|
286
|
+
message: firstLine
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
function classifyTestError(test) {
|
|
290
|
+
for (const result of test.results) {
|
|
291
|
+
const info = classifyResultError(result);
|
|
292
|
+
if (info)
|
|
293
|
+
return info;
|
|
294
|
+
}
|
|
295
|
+
return null;
|
|
207
296
|
}
|
|
208
297
|
function serializeXML(entry, tokens, stripANSIControlSequences) {
|
|
209
298
|
const attrs = [];
|
package/lib/reporters/line.js
CHANGED
|
@@ -102,7 +102,7 @@ class LineReporter extends import_base.TerminalReporter {
|
|
|
102
102
|
}
|
|
103
103
|
}
|
|
104
104
|
_updateLine(test, result, step) {
|
|
105
|
-
const retriesPrefix =
|
|
105
|
+
const retriesPrefix = result.retry ? ` (retries)` : ``;
|
|
106
106
|
const prefix = `[${this._current}/${this.totalTestCount}]${retriesPrefix} `;
|
|
107
107
|
const currentRetrySuffix = result.retry ? this.screen.colors.yellow(` (retry #${result.retry})`) : "";
|
|
108
108
|
const title = this.formatTestTitle(test, step) + currentRetrySuffix;
|
package/lib/reporters/list.js
CHANGED
|
@@ -22,7 +22,6 @@ __export(list_exports, {
|
|
|
22
22
|
});
|
|
23
23
|
module.exports = __toCommonJS(list_exports);
|
|
24
24
|
var import_utils = require("patchright-bun-core/lib/utils");
|
|
25
|
-
var import_utilsBundle = require("patchright-bun-core/lib/utilsBundle");
|
|
26
25
|
var import_base = require("./base");
|
|
27
26
|
var import_util = require("../util");
|
|
28
27
|
const DOES_NOT_SUPPORT_UTF8_IN_TERMINAL = process.platform === "win32" && process.env.TERM_PROGRAM !== "vscode" && !process.env.WT_SESSION;
|
|
@@ -110,7 +109,7 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
110
109
|
text = this.screen.colors.red(title);
|
|
111
110
|
else
|
|
112
111
|
text = title;
|
|
113
|
-
text += this.screen.colors.dim(` (${(0,
|
|
112
|
+
text += this.screen.colors.dim(` (${(0, import_utils.msToString)(step.duration)})`);
|
|
114
113
|
this._updateOrAppendLine(this._stepRows, step, text, prefix);
|
|
115
114
|
}
|
|
116
115
|
_maybeWriteNewLine() {
|
|
@@ -188,7 +187,7 @@ class ListReporter extends import_base.TerminalReporter {
|
|
|
188
187
|
prefix = this._testPrefix(index, this.screen.colors.red(statusMark));
|
|
189
188
|
text = this.screen.colors.red(title);
|
|
190
189
|
}
|
|
191
|
-
text += this._retrySuffix(result) + this.screen.colors.dim(` (${(0,
|
|
190
|
+
text += this._retrySuffix(result) + this.screen.colors.dim(` (${(0, import_utils.msToString)(result.duration)})`);
|
|
192
191
|
}
|
|
193
192
|
this._updateOrAppendLine(this._testRows, test, text, prefix);
|
|
194
193
|
}
|
package/lib/reporters/merge.js
CHANGED
|
@@ -77,22 +77,28 @@ async function createMergedReport(config, dir, reporterDescriptions, rootDirOver
|
|
|
77
77
|
}
|
|
78
78
|
};
|
|
79
79
|
await dispatchEvents(eventData.prologue);
|
|
80
|
-
|
|
80
|
+
let usedWorkers = 0;
|
|
81
|
+
for (const { reportFile, zipFile, eventPatchers, metadata, config: config2, fullResult } of eventData.reports) {
|
|
82
|
+
multiplexer.onReportConfigure({
|
|
83
|
+
reportPath: zipFile,
|
|
84
|
+
config: (0, import_teleReceiver.asFullConfig)(config2)
|
|
85
|
+
});
|
|
81
86
|
const reportJsonl = await import_fs.default.promises.readFile(reportFile);
|
|
82
87
|
const events = parseTestEvents(reportJsonl);
|
|
83
88
|
new import_stringInternPool.JsonStringInternalizer(stringPool).traverse(events);
|
|
84
89
|
eventPatchers.patchers.push(new AttachmentPathPatcher(dir));
|
|
85
90
|
if (metadata.name)
|
|
86
91
|
eventPatchers.patchers.push(new GlobalErrorPatcher(metadata.name));
|
|
87
|
-
if (tags
|
|
88
|
-
eventPatchers.patchers.push(new GlobalErrorPatcher(tags.join(" ")));
|
|
92
|
+
if (config2?.tags?.length)
|
|
93
|
+
eventPatchers.patchers.push(new GlobalErrorPatcher(config2.tags.join(" ")));
|
|
94
|
+
const workerIndexPatcher = new WorkerIndexPatcher(usedWorkers);
|
|
95
|
+
eventPatchers.patchers.push(workerIndexPatcher);
|
|
89
96
|
eventPatchers.patchEvents(events);
|
|
97
|
+
usedWorkers += workerIndexPatcher.usedWorkers();
|
|
90
98
|
await dispatchEvents(events);
|
|
91
|
-
multiplexer.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
tag: tags,
|
|
95
|
-
shardIndex: metadata.shard?.current
|
|
99
|
+
multiplexer.onReportEnd({
|
|
100
|
+
reportPath: zipFile,
|
|
101
|
+
result: (0, import_teleReceiver.asFullResult)(fullResult)
|
|
96
102
|
});
|
|
97
103
|
}
|
|
98
104
|
await dispatchEvents(eventData.epilogue);
|
|
@@ -130,22 +136,22 @@ async function extractAndParseReports(dir, shardFiles, internalizer, printStatus
|
|
|
130
136
|
const zipFile = new import_utils.ZipFile(absolutePath);
|
|
131
137
|
const entryNames = await zipFile.entries();
|
|
132
138
|
for (const entryName of entryNames.sort()) {
|
|
133
|
-
let
|
|
139
|
+
let reportFile = import_path.default.join(dir, entryName);
|
|
134
140
|
const content = await zipFile.read(entryName);
|
|
135
141
|
if (entryName.endsWith(".jsonl")) {
|
|
136
|
-
|
|
142
|
+
reportFile = reportNames.makeUnique(reportFile);
|
|
137
143
|
let parsedEvents = parseCommonEvents(content);
|
|
138
144
|
internalizer.traverse(parsedEvents);
|
|
139
145
|
const metadata = findMetadata(parsedEvents, file);
|
|
140
146
|
parsedEvents = modernizer.modernize(metadata.version, parsedEvents);
|
|
141
147
|
shardEvents.push({
|
|
142
|
-
|
|
143
|
-
|
|
148
|
+
zipFile: absolutePath,
|
|
149
|
+
reportFile,
|
|
144
150
|
metadata,
|
|
145
151
|
parsedEvents
|
|
146
152
|
});
|
|
147
153
|
}
|
|
148
|
-
await import_fs.default.promises.writeFile(
|
|
154
|
+
await import_fs.default.promises.writeFile(reportFile, content);
|
|
149
155
|
}
|
|
150
156
|
zipFile.close();
|
|
151
157
|
}
|
|
@@ -174,13 +180,13 @@ async function mergeEvents(dir, shardReportFiles, stringPool, printStatus, rootD
|
|
|
174
180
|
const shardB = b.metadata.shard?.current ?? 0;
|
|
175
181
|
if (shardA !== shardB)
|
|
176
182
|
return shardA - shardB;
|
|
177
|
-
return a.
|
|
183
|
+
return a.zipFile.localeCompare(b.zipFile);
|
|
178
184
|
});
|
|
179
185
|
printStatus(`merging events`);
|
|
180
186
|
const reports = [];
|
|
181
187
|
const globalTestIdSet = /* @__PURE__ */ new Set();
|
|
182
188
|
for (let i = 0; i < blobs.length; ++i) {
|
|
183
|
-
const { parsedEvents, metadata,
|
|
189
|
+
const { parsedEvents, metadata, reportFile, zipFile } = blobs[i];
|
|
184
190
|
const eventPatchers = new JsonEventPatchers();
|
|
185
191
|
eventPatchers.patchers.push(new IdsPatcher(
|
|
186
192
|
stringPool,
|
|
@@ -191,28 +197,26 @@ async function mergeEvents(dir, shardReportFiles, stringPool, printStatus, rootD
|
|
|
191
197
|
if (rootDirOverride)
|
|
192
198
|
eventPatchers.patchers.push(new PathSeparatorPatcher(metadata.pathSeparator));
|
|
193
199
|
eventPatchers.patchEvents(parsedEvents);
|
|
194
|
-
let
|
|
195
|
-
let
|
|
196
|
-
let duration = 0;
|
|
200
|
+
let config;
|
|
201
|
+
let fullResult;
|
|
197
202
|
for (const event of parsedEvents) {
|
|
198
203
|
if (event.method === "onConfigure") {
|
|
199
204
|
configureEvents.push(event);
|
|
200
|
-
|
|
205
|
+
config = event.params.config;
|
|
201
206
|
} else if (event.method === "onProject") {
|
|
202
207
|
projectEvents.push(event);
|
|
203
208
|
} else if (event.method === "onEnd") {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
duration = event.params.result.duration;
|
|
209
|
+
fullResult = event.params.result;
|
|
210
|
+
endEvents.push({ event, metadata });
|
|
207
211
|
}
|
|
208
212
|
}
|
|
209
213
|
reports.push({
|
|
210
214
|
eventPatchers,
|
|
211
|
-
reportFile
|
|
215
|
+
reportFile,
|
|
216
|
+
zipFile,
|
|
212
217
|
metadata,
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
duration
|
|
218
|
+
config,
|
|
219
|
+
fullResult
|
|
216
220
|
});
|
|
217
221
|
}
|
|
218
222
|
return {
|
|
@@ -237,6 +241,7 @@ function mergeConfigureEvents(configureEvents, rootDirOverride) {
|
|
|
237
241
|
globalTimeout: 0,
|
|
238
242
|
maxFailures: 0,
|
|
239
243
|
metadata: {},
|
|
244
|
+
shard: null,
|
|
240
245
|
rootDir: "",
|
|
241
246
|
version: "",
|
|
242
247
|
workers: 0,
|
|
@@ -281,6 +286,7 @@ function mergeConfigs(to, from) {
|
|
|
281
286
|
...from.metadata,
|
|
282
287
|
actualWorkers: (to.metadata.actualWorkers || 0) + (from.metadata.actualWorkers || 0)
|
|
283
288
|
},
|
|
289
|
+
shard: null,
|
|
284
290
|
workers: to.workers + from.workers
|
|
285
291
|
};
|
|
286
292
|
}
|
|
@@ -512,6 +518,21 @@ class GlobalErrorPatcher {
|
|
|
512
518
|
error.stack = this._prefix + error.stack;
|
|
513
519
|
}
|
|
514
520
|
}
|
|
521
|
+
class WorkerIndexPatcher {
|
|
522
|
+
constructor(baseWorkerIndex) {
|
|
523
|
+
this._maxWorkerIndex = 0;
|
|
524
|
+
this._baseWorkerIndex = baseWorkerIndex;
|
|
525
|
+
}
|
|
526
|
+
patchEvent(event) {
|
|
527
|
+
if (event.method === "onTestBegin") {
|
|
528
|
+
this._maxWorkerIndex = Math.max(this._maxWorkerIndex, event.params.result.workerIndex);
|
|
529
|
+
event.params.result.workerIndex += this._baseWorkerIndex;
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
usedWorkers() {
|
|
533
|
+
return this._maxWorkerIndex + 1;
|
|
534
|
+
}
|
|
535
|
+
}
|
|
515
536
|
class JsonEventPatchers {
|
|
516
537
|
constructor() {
|
|
517
538
|
this.patchers = [];
|
|
@@ -56,9 +56,13 @@ class Multiplexer {
|
|
|
56
56
|
for (const reporter of this._reporters)
|
|
57
57
|
wrap(() => reporter.onTestEnd?.(test, result));
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
onReportConfigure(params) {
|
|
60
60
|
for (const reporter of this._reporters)
|
|
61
|
-
wrap(() => reporter.
|
|
61
|
+
wrap(() => reporter.onReportConfigure?.(params));
|
|
62
|
+
}
|
|
63
|
+
onReportEnd(params) {
|
|
64
|
+
for (const reporter of this._reporters)
|
|
65
|
+
wrap(() => reporter.onReportEnd?.(params));
|
|
62
66
|
}
|
|
63
67
|
async onEnd(result) {
|
|
64
68
|
for (const reporter of this._reporters) {
|
|
@@ -168,6 +168,7 @@ class TeleReporterEmitter {
|
|
|
168
168
|
maxFailures: config.maxFailures,
|
|
169
169
|
metadata: config.metadata,
|
|
170
170
|
rootDir: config.rootDir,
|
|
171
|
+
shard: config.shard,
|
|
171
172
|
version: config.version,
|
|
172
173
|
workers: config.workers,
|
|
173
174
|
globalSetup: config.globalSetup,
|
|
@@ -196,6 +197,7 @@ class TeleReporterEmitter {
|
|
|
196
197
|
dependencies: project.dependencies,
|
|
197
198
|
snapshotDir: this._relativePath(project.snapshotDir),
|
|
198
199
|
teardown: project.teardown,
|
|
200
|
+
ignoreSnapshots: project.ignoreSnapshots ? true : void 0,
|
|
199
201
|
use: this._serializeProjectUseOptions(project.use)
|
|
200
202
|
};
|
|
201
203
|
return report;
|