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.
Files changed (108) hide show
  1. package/ThirdPartyNotices.txt +10 -1133
  2. package/lib/agents/generateAgents.js +2 -4
  3. package/lib/common/config.js +4 -5
  4. package/lib/common/expectBundleImpl.js +221 -221
  5. package/lib/common/process.js +1 -0
  6. package/lib/common/test.js +10 -1
  7. package/lib/common/testType.js +3 -0
  8. package/lib/common/validators.js +38 -38
  9. package/lib/errorContext.js +121 -0
  10. package/lib/index.js +98 -60
  11. package/lib/isomorphic/teleReceiver.js +26 -9
  12. package/lib/isomorphic/testServerConnection.js +3 -5
  13. package/lib/matchers/matchers.js +2 -0
  14. package/lib/matchers/toMatchAriaSnapshot.js +5 -1
  15. package/lib/matchers/toMatchSnapshot.js +42 -35
  16. package/lib/mcp/test/browserBackend.js +45 -22
  17. package/lib/mcp/test/generatorTools.js +9 -9
  18. package/lib/mcp/test/plannerTools.js +17 -17
  19. package/lib/mcp/test/testBackend.js +27 -27
  20. package/lib/mcp/test/testContext.js +6 -8
  21. package/lib/mcp/test/testTools.js +9 -9
  22. package/lib/plugins/webServerPlugin.js +2 -1
  23. package/lib/program.js +34 -212
  24. package/lib/reportActions.js +80 -0
  25. package/lib/reporters/base.js +4 -5
  26. package/lib/reporters/blob.js +2 -2
  27. package/lib/reporters/github.js +1 -2
  28. package/lib/reporters/html.js +64 -31
  29. package/lib/reporters/junit.js +104 -15
  30. package/lib/reporters/line.js +1 -1
  31. package/lib/reporters/list.js +2 -3
  32. package/lib/reporters/merge.js +47 -26
  33. package/lib/reporters/multiplexer.js +6 -2
  34. package/lib/reporters/teleEmitter.js +2 -0
  35. package/lib/runner/dispatcher.js +6 -14
  36. package/lib/runner/loadUtils.js +11 -5
  37. package/lib/runner/projectUtils.js +1 -1
  38. package/lib/runner/reporters.js +5 -0
  39. package/lib/runner/tasks.js +11 -8
  40. package/lib/runner/testRunner.js +2 -2
  41. package/lib/runner/workerHost.js +0 -3
  42. package/lib/testActions.js +220 -0
  43. package/lib/transform/babelBundle.js +0 -3
  44. package/lib/transform/babelBundleImpl.js +134 -134
  45. package/lib/transform/compilationCache.js +0 -2
  46. package/lib/transform/esmLoader.js +8 -6
  47. package/lib/transform/transform.js +3 -10
  48. package/lib/utilsBundle.js +0 -7
  49. package/lib/utilsBundleImpl.js +48 -51
  50. package/lib/worker/fixtureRunner.js +2 -2
  51. package/lib/worker/testInfo.js +10 -14
  52. package/lib/worker/testTracing.js +12 -6
  53. package/lib/worker/timeoutManager.js +14 -3
  54. package/lib/worker/workerMain.js +24 -21
  55. package/package.json +2 -6
  56. package/types/test.d.ts +83 -12
  57. package/lib/mcp/browser/browserContextFactory.js +0 -329
  58. package/lib/mcp/browser/browserServerBackend.js +0 -84
  59. package/lib/mcp/browser/config.js +0 -421
  60. package/lib/mcp/browser/context.js +0 -244
  61. package/lib/mcp/browser/response.js +0 -278
  62. package/lib/mcp/browser/sessionLog.js +0 -75
  63. package/lib/mcp/browser/tab.js +0 -343
  64. package/lib/mcp/browser/tools/common.js +0 -65
  65. package/lib/mcp/browser/tools/console.js +0 -46
  66. package/lib/mcp/browser/tools/dialogs.js +0 -60
  67. package/lib/mcp/browser/tools/evaluate.js +0 -61
  68. package/lib/mcp/browser/tools/files.js +0 -58
  69. package/lib/mcp/browser/tools/form.js +0 -63
  70. package/lib/mcp/browser/tools/install.js +0 -72
  71. package/lib/mcp/browser/tools/keyboard.js +0 -107
  72. package/lib/mcp/browser/tools/mouse.js +0 -107
  73. package/lib/mcp/browser/tools/navigate.js +0 -71
  74. package/lib/mcp/browser/tools/network.js +0 -63
  75. package/lib/mcp/browser/tools/open.js +0 -57
  76. package/lib/mcp/browser/tools/pdf.js +0 -49
  77. package/lib/mcp/browser/tools/runCode.js +0 -78
  78. package/lib/mcp/browser/tools/screenshot.js +0 -93
  79. package/lib/mcp/browser/tools/snapshot.js +0 -173
  80. package/lib/mcp/browser/tools/tabs.js +0 -67
  81. package/lib/mcp/browser/tools/tool.js +0 -47
  82. package/lib/mcp/browser/tools/tracing.js +0 -74
  83. package/lib/mcp/browser/tools/utils.js +0 -94
  84. package/lib/mcp/browser/tools/verify.js +0 -143
  85. package/lib/mcp/browser/tools/wait.js +0 -63
  86. package/lib/mcp/browser/tools.js +0 -84
  87. package/lib/mcp/browser/watchdog.js +0 -44
  88. package/lib/mcp/config.d.js +0 -16
  89. package/lib/mcp/extension/cdpRelay.js +0 -351
  90. package/lib/mcp/extension/extensionContextFactory.js +0 -76
  91. package/lib/mcp/extension/protocol.js +0 -28
  92. package/lib/mcp/index.js +0 -61
  93. package/lib/mcp/log.js +0 -35
  94. package/lib/mcp/program.js +0 -111
  95. package/lib/mcp/sdk/exports.js +0 -28
  96. package/lib/mcp/sdk/http.js +0 -152
  97. package/lib/mcp/sdk/inProcessTransport.js +0 -71
  98. package/lib/mcp/sdk/server.js +0 -223
  99. package/lib/mcp/sdk/tool.js +0 -47
  100. package/lib/mcp/terminal/cli.js +0 -296
  101. package/lib/mcp/terminal/command.js +0 -56
  102. package/lib/mcp/terminal/commands.js +0 -333
  103. package/lib/mcp/terminal/daemon.js +0 -129
  104. package/lib/mcp/terminal/help.json +0 -32
  105. package/lib/mcp/terminal/helpGenerator.js +0 -88
  106. package/lib/mcp/terminal/socketConnection.js +0 -80
  107. package/lib/runner/storage.js +0 -91
  108. package/lib/transform/md.js +0 -221
@@ -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
- onMachineEnd(result) {
109
- this._machines.push(result);
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
- let noSnippets;
115
- if (process.env.PLAYWRIGHT_HTML_NO_SNIPPETS === "false" || process.env.PLAYWRIGHT_HTML_NO_SNIPPETS === "0")
116
- noSnippets = false;
117
- else if (process.env.PLAYWRIGHT_HTML_NO_SNIPPETS)
118
- noSnippets = true;
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 shouldOpen = !!process.stdin.isTTY && (this._open === "always" || !ok && this._open === "on-failure");
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((s) => ({
268
- duration: s.duration,
269
- startTime: s.startTime.getTime(),
270
- tag: s.tag,
271
- shardIndex: s.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 appFolder = import_path.default.join(require.resolve("patchright-bun-core"), "..", "lib", "vite", "htmlReport");
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(import_path.default.join(this._reportFolder, "index.html"));
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, '<script id="playwrightReportBase64" type="application/zip">data:application/zip;base64,');
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, "</script>");
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 { attachments: result.attachments.map((a) => ({ name: a.name, contentType: a.contentType, path: a.path })) };
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) {
@@ -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: 0,
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: 0
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
- if (!test.ok()) {
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: "failure",
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 test.results) {
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 = [];
@@ -102,7 +102,7 @@ class LineReporter extends import_base.TerminalReporter {
102
102
  }
103
103
  }
104
104
  _updateLine(test, result, step) {
105
- const retriesPrefix = this.totalTestCount < this._current ? ` (retries)` : ``;
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;
@@ -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, import_utilsBundle.ms)(step.duration)})`);
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, import_utilsBundle.ms)(result.duration)})`);
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
  }
@@ -77,22 +77,28 @@ async function createMergedReport(config, dir, reporterDescriptions, rootDirOver
77
77
  }
78
78
  };
79
79
  await dispatchEvents(eventData.prologue);
80
- for (const { reportFile, eventPatchers, metadata, tags, startTime, duration } of eventData.reports) {
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.length)
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.onMachineEnd({
92
- startTime: new Date(startTime),
93
- duration,
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 fileName = import_path.default.join(dir, entryName);
139
+ let reportFile = import_path.default.join(dir, entryName);
134
140
  const content = await zipFile.read(entryName);
135
141
  if (entryName.endsWith(".jsonl")) {
136
- fileName = reportNames.makeUnique(fileName);
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
- file,
143
- localPath: fileName,
148
+ zipFile: absolutePath,
149
+ reportFile,
144
150
  metadata,
145
151
  parsedEvents
146
152
  });
147
153
  }
148
- await import_fs.default.promises.writeFile(fileName, content);
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.file.localeCompare(b.file);
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, localPath } = blobs[i];
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 tags = [];
195
- let startTime = 0;
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
- tags = event.params.config.tags || [];
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
- endEvents.push({ event, metadata, tags });
205
- startTime = event.params.result.startTime;
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: localPath,
215
+ reportFile,
216
+ zipFile,
212
217
  metadata,
213
- tags,
214
- startTime,
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
- onMachineEnd(result) {
59
+ onReportConfigure(params) {
60
60
  for (const reporter of this._reporters)
61
- wrap(() => reporter.onMachineEnd?.(result));
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;