@midscene/core 1.17.4 → 1.17.5

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 (86) hide show
  1. package/dist/es/agent/agent-report-manager.mjs +28 -5
  2. package/dist/es/agent/agent-report-manager.mjs.map +1 -1
  3. package/dist/es/agent/agent.mjs +2 -2
  4. package/dist/es/agent/agent.mjs.map +1 -1
  5. package/dist/es/agent/utils.mjs +1 -1
  6. package/dist/es/dump/html-utils.mjs +51 -68
  7. package/dist/es/dump/html-utils.mjs.map +1 -1
  8. package/dist/es/dump/report-artifact-publication.mjs +52 -0
  9. package/dist/es/dump/report-artifact-publication.mjs.map +1 -0
  10. package/dist/es/dump/report-artifact-store.mjs +2 -2
  11. package/dist/es/dump/report-artifact-store.mjs.map +1 -1
  12. package/dist/es/dump/report-artifact-writer.mjs +129 -0
  13. package/dist/es/dump/report-artifact-writer.mjs.map +1 -0
  14. package/dist/es/dump/report-session.mjs +229 -0
  15. package/dist/es/dump/report-session.mjs.map +1 -0
  16. package/dist/es/dump/test-report-images.mjs +68 -42
  17. package/dist/es/dump/test-report-images.mjs.map +1 -1
  18. package/dist/es/index.mjs +2 -2
  19. package/dist/es/index.mjs.map +1 -1
  20. package/dist/es/report-cli.mjs +38 -11
  21. package/dist/es/report-cli.mjs.map +1 -1
  22. package/dist/es/report-generator.mjs +2 -1
  23. package/dist/es/report-generator.mjs.map +1 -1
  24. package/dist/es/report-html-template.mjs +1 -1
  25. package/dist/es/report.mjs +152 -159
  26. package/dist/es/report.mjs.map +1 -1
  27. package/dist/es/test-run-report.mjs.map +1 -1
  28. package/dist/es/utils.mjs +1 -1
  29. package/dist/es/yaml/player.mjs +1 -1
  30. package/dist/es/yaml/player.mjs.map +1 -1
  31. package/dist/lib/agent/agent-report-manager.js +27 -4
  32. package/dist/lib/agent/agent-report-manager.js.map +1 -1
  33. package/dist/lib/agent/agent.js +2 -2
  34. package/dist/lib/agent/agent.js.map +1 -1
  35. package/dist/lib/agent/utils.js +1 -1
  36. package/dist/lib/dump/html-utils.js +59 -70
  37. package/dist/lib/dump/html-utils.js.map +1 -1
  38. package/dist/lib/dump/report-artifact-publication.js +89 -0
  39. package/dist/lib/dump/report-artifact-publication.js.map +1 -0
  40. package/dist/lib/dump/report-artifact-store.js +2 -2
  41. package/dist/lib/dump/report-artifact-store.js.map +1 -1
  42. package/dist/lib/dump/report-artifact-writer.js +163 -0
  43. package/dist/lib/dump/report-artifact-writer.js.map +1 -0
  44. package/dist/lib/dump/report-session.js +266 -0
  45. package/dist/lib/dump/report-session.js.map +1 -0
  46. package/dist/lib/dump/test-report-images.js +67 -41
  47. package/dist/lib/dump/test-report-images.js.map +1 -1
  48. package/dist/lib/index.js +3 -0
  49. package/dist/lib/index.js.map +1 -1
  50. package/dist/lib/report-cli.js +37 -10
  51. package/dist/lib/report-cli.js.map +1 -1
  52. package/dist/lib/report-generator.js +2 -1
  53. package/dist/lib/report-generator.js.map +1 -1
  54. package/dist/lib/report-html-template.js +1 -1
  55. package/dist/lib/report.js +152 -153
  56. package/dist/lib/report.js.map +1 -1
  57. package/dist/lib/test-run-report.js.map +1 -1
  58. package/dist/lib/utils.js +1 -1
  59. package/dist/lib/yaml/player.js +1 -1
  60. package/dist/lib/yaml/player.js.map +1 -1
  61. package/dist/types/agent/agent-report-manager.d.ts +4 -1
  62. package/dist/types/agent/agent-report-manager.d.ts.map +1 -1
  63. package/dist/types/agent/agent.d.ts +1 -1
  64. package/dist/types/agent/agent.d.ts.map +1 -1
  65. package/dist/types/dump/html-utils.d.ts +7 -1
  66. package/dist/types/dump/html-utils.d.ts.map +1 -1
  67. package/dist/types/dump/report-artifact-publication.d.ts +5 -0
  68. package/dist/types/dump/report-artifact-publication.d.ts.map +1 -0
  69. package/dist/types/dump/report-artifact-store.d.ts +1 -1
  70. package/dist/types/dump/report-artifact-store.d.ts.map +1 -1
  71. package/dist/types/dump/report-artifact-writer.d.ts +21 -0
  72. package/dist/types/dump/report-artifact-writer.d.ts.map +1 -0
  73. package/dist/types/dump/report-session.d.ts +48 -0
  74. package/dist/types/dump/report-session.d.ts.map +1 -0
  75. package/dist/types/dump/test-report-images.d.ts +5 -2
  76. package/dist/types/dump/test-report-images.d.ts.map +1 -1
  77. package/dist/types/index.d.ts +1 -1
  78. package/dist/types/index.d.ts.map +1 -1
  79. package/dist/types/report-cli.d.ts +2 -1
  80. package/dist/types/report-cli.d.ts.map +1 -1
  81. package/dist/types/report-generator.d.ts.map +1 -1
  82. package/dist/types/report.d.ts +11 -0
  83. package/dist/types/report.d.ts.map +1 -1
  84. package/dist/types/test-run-report.d.ts +2 -2
  85. package/dist/types/test-run-report.d.ts.map +1 -1
  86. package/package.json +2 -2
@@ -27,8 +27,10 @@ __webpack_require__.d(__webpack_exports__, {
27
27
  readReportArtifactManifestAsync: ()=>report_artifact_reader_js_namespaceObject.readReportArtifactManifestAsync,
28
28
  readReportDumpScripts: ()=>report_artifact_reader_js_namespaceObject.readReportDumpScripts,
29
29
  ReportMergingTool: ()=>ReportMergingTool,
30
+ ReportSession: ()=>report_session_js_namespaceObject.ReportSession,
30
31
  TestRunReportAssembler: ()=>TestRunReportAssembler,
31
32
  collectDedupedExecutions: ()=>collectDedupedExecutions,
33
+ exportReportArtifact: ()=>exportReportArtifact,
32
34
  splitReportHtmlByExecution: ()=>splitReportHtmlByExecution,
33
35
  streamReportDumpScripts: ()=>report_artifact_reader_js_namespaceObject.streamReportDumpScripts,
34
36
  isDirectoryModeReport: ()=>isDirectoryModeReport,
@@ -54,7 +56,9 @@ const report_artifact_reader_js_namespaceObject = require("./dump/report-artifac
54
56
  const screenshot_store_js_namespaceObject = require("./dump/screenshot-store.js");
55
57
  const execution_recorder_js_namespaceObject = require("./dump/execution-recorder.js");
56
58
  const report_artifact_store_js_namespaceObject = require("./dump/report-artifact-store.js");
57
- const test_report_images_js_namespaceObject = require("./dump/test-report-images.js");
59
+ const report_session_js_namespaceObject = require("./dump/report-session.js");
60
+ const report_artifact_publication_js_namespaceObject = require("./dump/report-artifact-publication.js");
61
+ const report_artifact_writer_js_namespaceObject = require("./dump/report-artifact-writer.js");
58
62
  const external_report_stats_js_namespaceObject = require("./report-stats.js");
59
63
  const merge_report_js_namespaceObject = require("./test-runner/reporting/merge-report.js");
60
64
  const metrics_js_namespaceObject = require("./test-runner/reporting/metrics.js");
@@ -71,6 +75,64 @@ function _define_property(obj, key, value) {
71
75
  else obj[key] = value;
72
76
  return obj;
73
77
  }
78
+ async function exportReportArtifact(options) {
79
+ validateTestRunReportFileName(options.reportFileName);
80
+ const input = external_node_path_namespaceObject.resolve(options.reportPath);
81
+ const output = external_node_path_namespaceObject.resolve(options.outputDir);
82
+ const external = 'html-and-external-assets' === options.outputFormat;
83
+ const artifact = external_node_path_namespaceObject.join(output, options.reportFileName);
84
+ const destination = external ? external_node_path_namespaceObject.join(artifact, 'index.html') : `${artifact}.html`;
85
+ if (input === destination || external && input.startsWith(`${artifact}${external_node_path_namespaceObject.sep}`)) throw new Error('Export destination must differ from the input report.');
86
+ if (!options.overwrite && (0, external_node_fs_namespaceObject.existsSync)(external ? artifact : destination)) throw new Error(`Report artifact already exists: ${artifact}`);
87
+ const manifest = (0, report_artifact_reader_js_namespaceObject.readReportArtifactManifest)(input);
88
+ const runner = (0, report_artifact_reader_js_namespaceObject.readReportRunnerDump)(input);
89
+ const groups = new Map();
90
+ for (const source of (0, report_artifact_reader_js_namespaceObject.readReportDumpScripts)(input)){
91
+ const attributes = Object.fromEntries([
92
+ ...source.openTag.matchAll(/([^\s=]+)="([^"]*)"/g)
93
+ ].filter(([, key])=>'type' !== key).map(([, key, value])=>[
94
+ key,
95
+ decodeURIComponent(value)
96
+ ]));
97
+ const id = attributes['data-group-id'];
98
+ if (!id) throw new Error('Report execution group is missing its ID.');
99
+ const dump = external_types_js_namespaceObject.ReportActionDump.fromSerializedString(manifest ? source.content : (0, utils_namespaceObject.antiEscapeScriptTag)(source.content));
100
+ const previous = groups.get(id);
101
+ dump.executions = dedupeExecutionsKeepLatest([
102
+ ...previous?.dump.executions ?? [],
103
+ ...dump.executions
104
+ ]);
105
+ groups.set(id, {
106
+ dump,
107
+ attributes
108
+ });
109
+ }
110
+ if (!groups.size && !runner) throw new Error('No report execution data was found in the input.');
111
+ const hint = [
112
+ ...groups.values()
113
+ ][0]?.attributes['data-report-kind'];
114
+ const kind = manifest?.kind ?? ('agent' === hint || 'test' === hint ? hint : runner ? 'test' : 'agent');
115
+ await promises_namespaceObject.mkdir(output, {
116
+ recursive: true
117
+ });
118
+ const temporary = await promises_namespaceObject.mkdtemp(external_node_path_namespaceObject.join(output, '.report-export-'));
119
+ const entry = external_node_path_namespaceObject.join(temporary, 'index.html');
120
+ try {
121
+ const writer = new report_artifact_writer_js_namespaceObject.ReportArtifactWriter(entry, external, kind, manifest?.reportId ?? runner?.runId);
122
+ await writer.initialize();
123
+ for (const [id, group] of groups)await writer.addGroup(id, group.dump.serialize(), input, group.attributes);
124
+ await writer.finish(runner, mergedAgentReportComment([
125
+ ...groups.values()
126
+ ].map((group)=>group.dump)));
127
+ await (0, report_artifact_publication_js_namespaceObject.publishReportArtifact)(external ? temporary : entry, external ? artifact : destination, external, options.overwrite);
128
+ return destination;
129
+ } finally{
130
+ await promises_namespaceObject.rm(temporary, {
131
+ recursive: true,
132
+ force: true
133
+ });
134
+ }
135
+ }
74
136
  const screenshotModeAttrRegExp = new RegExp(`${html_utils_js_namespaceObject.DATA_SCREENSHOT_MODE_ATTR}="(inline|directory)"`);
75
137
  function readDeclaredScreenshotMode(reportFilePath) {
76
138
  let mode;
@@ -174,8 +236,8 @@ class ReportMergingTool {
174
236
  clear() {
175
237
  this.reportInfos = [];
176
238
  }
177
- mergeDumpScripts(contents) {
178
- const unescaped = contents.map((c)=>(0, utils_namespaceObject.antiEscapeScriptTag)(c)).filter((c)=>c.length > 0);
239
+ mergeDumpScripts(contents, external) {
240
+ const unescaped = contents.map((c)=>external ? c : (0, utils_namespaceObject.antiEscapeScriptTag)(c)).filter((c)=>c.length > 0);
179
241
  if (0 === unescaped.length) return '';
180
242
  if (1 === unescaped.length) return unescaped[0];
181
243
  const base = external_types_js_namespaceObject.ReportActionDump.fromSerializedString(unescaped[0]);
@@ -189,99 +251,67 @@ class ReportMergingTool {
189
251
  base.executions = dedupeExecutionsKeepLatest(allExecutions);
190
252
  return base.serialize();
191
253
  }
192
- mergeReports(reportFileName = 'AUTO', opts) {
193
- const { rmOriginalReports = false, overwrite = false, outputDir } = opts ?? {};
194
- if (0 === this.reportInfos.length) {
254
+ mergeReports(reportFileName = 'AUTO', opts = {}) {
255
+ const { rmOriginalReports = false, overwrite = false, outputDir, outputFormat } = opts;
256
+ if (!this.reportInfos.length) {
195
257
  (0, utils_namespaceObject.logMsg)('No reports to merge');
196
258
  return null;
197
259
  }
198
260
  const runnerDumps = this.reportInfos.map((info)=>info.reportFilePath ? (0, report_artifact_reader_js_namespaceObject.readReportRunnerDump)(info.reportFilePath) : void 0);
199
261
  const hasRunnerDump = runnerDumps.some(Boolean);
262
+ const external = 'html-and-external-assets' === outputFormat;
263
+ const name = 'AUTO' === reportFileName ? (0, report_file_name_js_namespaceObject.getReportFileName)('merged-report') : reportFileName;
264
+ validateTestRunReportFileName(name);
200
265
  const targetDir = outputDir ? external_node_path_namespaceObject.resolve(outputDir) : (0, common_namespaceObject.getMidsceneRunSubDir)('report');
201
- if (outputDir) (0, external_node_fs_namespaceObject.mkdirSync)(targetDir, {
202
- recursive: true
203
- });
204
- const isDirModeByIndex = this.reportInfos.map((info)=>Boolean(info.reportFilePath && isDirectoryModeReport(info.reportFilePath)));
205
- const hasDirectoryModeReport = isDirModeByIndex.some(Boolean);
206
- const resolvedName = 'AUTO' === reportFileName ? (0, report_file_name_js_namespaceObject.getReportFileName)('merged-report') : reportFileName;
207
- const outputFilePath = hasDirectoryModeReport ? external_node_path_namespaceObject.resolve(targetDir, resolvedName, 'index.html') : external_node_path_namespaceObject.resolve(targetDir, `${resolvedName}.html`);
208
- if ('AUTO' !== reportFileName && (0, external_node_fs_namespaceObject.existsSync)(outputFilePath)) {
209
- if (!overwrite) throw new Error(`Report file already exists: ${outputFilePath}\nSet overwrite to true to overwrite this file.`);
210
- if (hasDirectoryModeReport) (0, external_node_fs_namespaceObject.rmSync)(external_node_path_namespaceObject.dirname(outputFilePath), {
211
- recursive: true,
212
- force: true
213
- });
214
- else (0, external_node_fs_namespaceObject.unlinkSync)(outputFilePath);
215
- }
216
- if (hasDirectoryModeReport) (0, external_node_fs_namespaceObject.mkdirSync)(external_node_path_namespaceObject.dirname(outputFilePath), {
266
+ (0, external_node_fs_namespaceObject.mkdirSync)(targetDir, {
217
267
  recursive: true
218
268
  });
219
- (0, utils_namespaceObject.logMsg)(`Start merging ${this.reportInfos.length} reports...\nCreating template file...`);
269
+ const artifact = external_node_path_namespaceObject.join(targetDir, name);
270
+ const outputFilePath = external ? external_node_path_namespaceObject.join(artifact, 'index.html') : `${artifact}.html`;
271
+ if (!overwrite && (0, external_node_fs_namespaceObject.existsSync)(external ? artifact : outputFilePath)) throw new Error(`Report file already exists: ${outputFilePath}\nSet overwrite to true to overwrite this file.`);
272
+ if (rmOriginalReports && this.reportInfos.some((info)=>info.reportFilePath && isDirectoryBasedReport(info.reportFilePath) && outputFilePath.startsWith(`${external_node_path_namespaceObject.dirname(external_node_path_namespaceObject.resolve(info.reportFilePath))}${external_node_path_namespaceObject.sep}`))) throw new Error('Merged output must be outside source report directories when removing originals.');
273
+ const temporary = (0, external_node_fs_namespaceObject.mkdtempSync)(external_node_path_namespaceObject.join(targetDir, '.merged-report-'));
274
+ const entry = external_node_path_namespaceObject.join(temporary, 'index.html');
220
275
  try {
221
- const htmlEndTag = '</html>';
222
- const tpl = (0, external_utils_js_namespaceObject.getReportTpl)();
223
- const htmlEndIdx = tpl.lastIndexOf(htmlEndTag);
224
- const tplWithoutClose = -1 !== htmlEndIdx ? tpl.slice(0, htmlEndIdx) : tpl;
225
- (0, external_node_fs_namespaceObject.appendFileSync)(outputFilePath, tplWithoutClose);
226
- if (hasDirectoryModeReport) (0, external_node_fs_namespaceObject.appendFileSync)(outputFilePath, (0, html_utils_js_namespaceObject.getBaseUrlFixScript)());
276
+ const writer = new report_artifact_writer_js_namespaceObject.ReportArtifactWriter(entry, external, hasRunnerDump ? 'test' : 'agent');
277
+ writer.initializeSync();
227
278
  const agentReports = [];
228
279
  const runnerSources = [];
229
- const writtenInlineImageIds = new Set();
230
- for(let i = 0; i < this.reportInfos.length; i++){
231
- const reportInfo = this.reportInfos[i];
232
- (0, utils_namespaceObject.logMsg)(`Processing report ${i + 1}/${this.reportInfos.length}`);
233
- const { reportAttributes } = reportInfo;
234
- let dumpString = this.createEmptyDumpString(reportAttributes.testTitle, reportAttributes.testDescription);
235
- let mergedGroupId = `merged-group-${i}`;
236
- if (reportInfo.reportFilePath) {
237
- if (isDirModeByIndex[i]) {
238
- const reportDir = external_node_path_namespaceObject.dirname(reportInfo.reportFilePath);
239
- const screenshotsDir = external_node_path_namespaceObject.join(reportDir, 'screenshots');
240
- if ((0, external_node_fs_namespaceObject.existsSync)(screenshotsDir)) {
241
- const mergedScreenshotsDir = external_node_path_namespaceObject.join(external_node_path_namespaceObject.dirname(outputFilePath), 'screenshots');
242
- (0, external_node_fs_namespaceObject.mkdirSync)(mergedScreenshotsDir, {
243
- recursive: true
244
- });
245
- for (const file of (0, external_node_fs_namespaceObject.readdirSync)(screenshotsDir)){
246
- const src = external_node_path_namespaceObject.join(screenshotsDir, file);
247
- const dest = external_node_path_namespaceObject.join(mergedScreenshotsDir, file);
248
- (0, external_node_fs_namespaceObject.copyFileSync)(src, dest);
249
- }
250
- }
251
- } else (0, html_utils_js_namespaceObject.streamImageScriptsToFile)(reportInfo.reportFilePath, outputFilePath, writtenInlineImageIds);
252
- const allDumps = (0, report_artifact_reader_js_namespaceObject.readReportDumpScripts)(reportInfo.reportFilePath).filter((d)=>d.openTag.includes('data-group-id'));
253
- const groupIdMatch = allDumps[0]?.openTag.match(/data-group-id="([^"]+)"/);
254
- if (groupIdMatch && !hasRunnerDump) mergedGroupId = decodeURIComponent(groupIdMatch[1]);
255
- const extractedDumpString = allDumps.length > 0 ? this.mergeDumpScripts(allDumps.map((d)=>d.content)) : (0, html_utils_js_namespaceObject.extractLastDumpScriptSync)(reportInfo.reportFilePath);
256
- if (extractedDumpString) dumpString = extractedDumpString;
280
+ const groupIds = new Set();
281
+ for (const [index, info] of this.reportInfos.entries()){
282
+ const attributes = info.reportAttributes;
283
+ let serialized = this.createEmptyDumpString(attributes.testTitle, attributes.testDescription);
284
+ let groupId = `merged-group-${index}`;
285
+ if (info.reportFilePath) {
286
+ const dumps = (0, report_artifact_reader_js_namespaceObject.readReportDumpScripts)(info.reportFilePath);
287
+ const originalId = dumps[0]?.openTag.match(/data-group-id="([^"]+)"/)?.[1];
288
+ if (originalId && !hasRunnerDump && !groupIds.has(decodeURIComponent(originalId))) groupId = decodeURIComponent(originalId);
289
+ serialized = dumps.length ? this.mergeDumpScripts(dumps.map((dump)=>dump.content), Boolean((0, report_artifact_reader_js_namespaceObject.readReportArtifactManifest)(info.reportFilePath))) : (0, html_utils_js_namespaceObject.extractLastDumpScriptSync)(info.reportFilePath) ?? serialized;
257
290
  }
258
- const agentReport = tryParseAgentReportDump(dumpString);
291
+ if (groupIds.has(groupId)) groupId = `merged-group-${(0, utils_namespaceObject.uuid)()}`;
292
+ groupIds.add(groupId);
293
+ const agentReport = tryParseAgentReportDump(serialized);
259
294
  if (agentReport) agentReports.push(agentReport);
260
295
  if (hasRunnerDump) runnerSources.push({
261
- reportId: mergedGroupId,
262
- sourcePath: reportInfo.reportFilePath ?? '<skipped-agent-report>',
263
- attributes: reportAttributes,
264
- dump: runnerDumps[i],
265
- timing: agentReport ? agentReportTiming(agentReport, reportAttributes.testDuration) : void 0
296
+ reportId: groupId,
297
+ sourcePath: info.reportFilePath ?? '<skipped-agent-report>',
298
+ attributes,
299
+ dump: runnerDumps[index],
300
+ timing: agentReport ? agentReportTiming(agentReport, attributes.testDuration) : void 0
301
+ });
302
+ writer.addGroupSync(groupId, serialized, info.reportFilePath ?? entry, {
303
+ ...hasRunnerDump ? {
304
+ 'data-report-id': groupId
305
+ } : {},
306
+ playwright_test_duration: attributes.testDuration,
307
+ playwright_test_status: attributes.testStatus,
308
+ playwright_test_title: attributes.testTitle,
309
+ playwright_test_id: attributes.testId,
310
+ playwright_test_description: attributes.testDescription,
311
+ is_merged: true
266
312
  });
267
- const reportHtmlStr = `${(0, external_utils_js_namespaceObject.reportHTMLContent)({
268
- dumpString,
269
- attributes: {
270
- 'data-group-id': mergedGroupId,
271
- ...hasRunnerDump ? {
272
- 'data-report-id': mergedGroupId
273
- } : {},
274
- [html_utils_js_namespaceObject.DATA_SCREENSHOT_MODE_ATTR]: hasDirectoryModeReport ? 'directory' : 'inline',
275
- playwright_test_duration: reportAttributes.testDuration,
276
- playwright_test_status: reportAttributes.testStatus,
277
- playwright_test_title: reportAttributes.testTitle,
278
- playwright_test_id: reportAttributes.testId,
279
- playwright_test_description: reportAttributes.testDescription,
280
- is_merged: true
281
- }
282
- }, void 0, void 0, false)}\n`;
283
- (0, external_node_fs_namespaceObject.appendFileSync)(outputFilePath, reportHtmlStr);
284
313
  }
314
+ let runner;
285
315
  if (hasRunnerDump) {
286
316
  const executions = dedupeExecutionsKeepLatest(agentReports.flatMap((report)=>report.executions));
287
317
  const metrics = (0, metrics_js_namespaceObject.collectTestRunReportMetrics)({
@@ -297,30 +327,22 @@ class ReportMergingTool {
297
327
  })
298
328
  ]
299
329
  ] : []));
300
- (0, external_node_fs_namespaceObject.appendFileSync)(outputFilePath, (0, html_utils_js_namespaceObject.generateTestRunReportScriptTag)((0, merge_report_js_namespaceObject.mergeTestRunReportDumps)(runnerSources, metrics, (0, utils_namespaceObject.uuid)(), executionMetrics)));
330
+ runner = (0, merge_report_js_namespaceObject.mergeTestRunReportDumps)(runnerSources, metrics, (0, utils_namespaceObject.uuid)(), executionMetrics);
301
331
  }
302
- const agentComment = mergedAgentReportComment(agentReports);
303
- if (agentComment) (0, external_node_fs_namespaceObject.appendFileSync)(outputFilePath, agentComment);
304
- (0, external_node_fs_namespaceObject.appendFileSync)(outputFilePath, `${htmlEndTag}\n`);
305
- (0, utils_namespaceObject.logMsg)(`Successfully merged new report: ${outputFilePath}`);
332
+ writer.finishSync(runner, mergedAgentReportComment(agentReports));
333
+ (0, report_artifact_publication_js_namespaceObject.publishReportArtifactSync)(external ? temporary : entry, external ? artifact : outputFilePath, external, overwrite);
306
334
  if (rmOriginalReports) {
307
- let removedReportCount = 0;
308
- let reportCountWithFilePath = 0;
309
- for (const info of this.reportInfos)if (info.reportFilePath) {
310
- reportCountWithFilePath += 1;
311
- try {
312
- removeReportArtifact(info.reportFilePath);
313
- removedReportCount += 1;
314
- } catch (error) {
315
- (0, utils_namespaceObject.logMsg)(`Error deleting report ${info.reportFilePath}: ${error}`);
316
- }
317
- }
318
- (0, utils_namespaceObject.logMsg)(`Removed ${removedReportCount}/${reportCountWithFilePath} original reports`);
335
+ for (const sourcePath of new Set(this.reportInfos.flatMap((info)=>info.reportFilePath ? [
336
+ external_node_path_namespaceObject.resolve(info.reportFilePath)
337
+ ] : [])))if (sourcePath !== outputFilePath) removeReportArtifact(sourcePath);
319
338
  }
339
+ (0, utils_namespaceObject.logMsg)(`Successfully merged new report: ${outputFilePath}`);
320
340
  return outputFilePath;
321
- } catch (error) {
322
- (0, utils_namespaceObject.logMsg)(`Error in mergeReports: ${error}`);
323
- throw error;
341
+ } finally{
342
+ (0, external_node_fs_namespaceObject.rmSync)(temporary, {
343
+ recursive: true,
344
+ force: true
345
+ });
324
346
  }
325
347
  }
326
348
  constructor(){
@@ -395,8 +417,15 @@ const prepareTestRunReportSources = (sources, loadedDumps)=>{
395
417
  continue;
396
418
  }
397
419
  const sourceKey = source.sourcePath;
420
+ const imageOrigins = new Map();
421
+ JSON.parse(baseDump.serialize(), (_key, value)=>{
422
+ const ref = (0, image_reference_js_namespaceObject.normalizeStoredImageRef)(value);
423
+ if (ref) imageOrigins.set(ref.id, 'file' === ref.storage ? source.dumpPath ?? source.sourcePath : source.sourcePath);
424
+ return value;
425
+ });
398
426
  const shared = preparedByPath.get(sourceKey);
399
427
  if (shared) {
428
+ for (const [id, origin] of imageOrigins)shared.imageOrigins.set(id, origin);
400
429
  if (source.dumpPath) shared.dump.executions = dedupeExecutionsKeepLatest([
401
430
  ...shared.dump.executions,
402
431
  ...baseDump.executions
@@ -456,6 +485,7 @@ const prepareTestRunReportSources = (sources, loadedDumps)=>{
456
485
  executionIds,
457
486
  collection: source.collection,
458
487
  dump,
488
+ imageOrigins,
459
489
  ...source.dumpPath ? {
460
490
  dumpPath: source.dumpPath
461
491
  } : {}
@@ -530,41 +560,25 @@ class TestRunReportAssembler {
530
560
  });
531
561
  const artifact = external_node_path_namespaceObject.join(outputDir, options.reportFileName);
532
562
  const finalPath = directoryMode ? external_node_path_namespaceObject.join(artifact, 'index.html') : `${artifact}.html`;
533
- if (!options.overwrite && ((0, external_node_fs_namespaceObject.existsSync)(artifact) || (0, external_node_fs_namespaceObject.existsSync)(`${artifact}.html`))) throw new Error(`Report artifact already exists: ${artifact}`);
563
+ if (!options.overwrite && (0, external_node_fs_namespaceObject.existsSync)(directoryMode ? artifact : finalPath)) throw new Error(`Report artifact already exists: ${artifact}`);
534
564
  const temporary = await promises_namespaceObject.mkdtemp(external_node_path_namespaceObject.join(outputDir, '.test-run-report-'));
535
565
  const htmlPath = external_node_path_namespaceObject.join(temporary, 'index.html');
536
566
  try {
537
- await promises_namespaceObject.writeFile(htmlPath, (0, external_utils_js_namespaceObject.getReportTpl)());
538
- if (directoryMode) await promises_namespaceObject.appendFile(htmlPath, (0, html_utils_js_namespaceObject.getBaseUrlFixScript)());
539
- const images = new test_report_images_js_namespaceObject.TestReportImages(htmlPath, directoryMode);
567
+ const writer = new report_artifact_writer_js_namespaceObject.ReportArtifactWriter(htmlPath, directoryMode, 'test', dump.runId);
568
+ await writer.initialize();
540
569
  const reports = [];
541
570
  for (const source of new Map(prepared.sources.map((source)=>[
542
571
  source.reportId,
543
572
  source
544
573
  ])).values()){
545
- const serialized = await images.rewriteAsync(source.dump.serialize(), source.dumpPath ?? source.sourcePath);
546
574
  reports.push(source.dump);
547
- await promises_namespaceObject.appendFile(htmlPath, (0, external_utils_js_namespaceObject.reportHTMLContent)({
548
- dumpString: serialized,
549
- attributes: {
550
- 'data-group-id': source.reportId,
551
- 'data-report-id': source.reportId,
552
- 'data-runner-scope-id': source.scopeId,
553
- [html_utils_js_namespaceObject.DATA_SCREENSHOT_MODE_ATTR]: directoryMode ? 'directory' : 'inline'
554
- }
555
- }, void 0, void 0, false));
556
- }
557
- await promises_namespaceObject.appendFile(htmlPath, (0, html_utils_js_namespaceObject.generateTestRunReportScriptTag)(dump) + mergedAgentReportComment(reports));
558
- if (options.overwrite) {
559
- await promises_namespaceObject.rm(artifact, {
560
- recursive: true,
561
- force: true
562
- });
563
- await promises_namespaceObject.rm(`${artifact}.html`, {
564
- force: true
565
- });
575
+ await writer.addGroup(source.reportId, source.dump.serialize(), source.dumpPath ?? source.sourcePath, {
576
+ 'data-report-id': source.reportId,
577
+ 'data-runner-scope-id': source.scopeId
578
+ }, source.imageOrigins);
566
579
  }
567
- await promises_namespaceObject.rename(directoryMode ? temporary : htmlPath, directoryMode ? artifact : finalPath);
580
+ await writer.finish(dump, mergedAgentReportComment(reports));
581
+ await (0, report_artifact_publication_js_namespaceObject.publishReportArtifact)(directoryMode ? temporary : htmlPath, directoryMode ? artifact : finalPath, directoryMode, options.overwrite);
568
582
  return finalPath;
569
583
  } finally{
570
584
  await promises_namespaceObject.rm(temporary, {
@@ -602,8 +616,7 @@ class TestRunReportAssembler {
602
616
  const standaloneReportPath = `${finalArtifactPath}.html`;
603
617
  const finalReportPath = directoryMode ? directoryReportPath : standaloneReportPath;
604
618
  const existingArtifacts = [
605
- finalArtifactPath,
606
- standaloneReportPath
619
+ directoryMode ? finalArtifactPath : standaloneReportPath
607
620
  ].filter(external_node_fs_namespaceObject.existsSync);
608
621
  if (existingArtifacts.length > 0 && !options.overwrite) throw new Error(`Report artifact already exists: ${existingArtifacts[0]}\nSet overwrite to true to overwrite this report.`);
609
622
  const temporaryRoot = (0, external_node_fs_namespaceObject.mkdtempSync)(external_node_path_namespaceObject.join(outputDir, '.test-run-report-'));
@@ -613,39 +626,21 @@ class TestRunReportAssembler {
613
626
  if (directoryMode) (0, external_node_fs_namespaceObject.mkdirSync)(temporaryArtifactPath, {
614
627
  recursive: true
615
628
  });
616
- const htmlEndTag = '</html>';
617
- const template = (0, external_utils_js_namespaceObject.getReportTpl)();
618
- const htmlEndIndex = template.lastIndexOf(htmlEndTag);
619
- (0, external_node_fs_namespaceObject.appendFileSync)(temporaryReportPath, -1 === htmlEndIndex ? template : template.slice(0, htmlEndIndex));
620
- if (directoryMode) (0, external_node_fs_namespaceObject.appendFileSync)(temporaryReportPath, (0, html_utils_js_namespaceObject.getBaseUrlFixScript)());
621
- const images = new test_report_images_js_namespaceObject.TestReportImages(temporaryReportPath, directoryMode);
629
+ const writer = new report_artifact_writer_js_namespaceObject.ReportArtifactWriter(temporaryReportPath, directoryMode, 'test', runnerDump.runId);
630
+ writer.initializeSync();
622
631
  const agentReports = [];
623
632
  for (const source of new Map(prepared.sources.map((source)=>[
624
633
  source.reportId,
625
634
  source
626
635
  ])).values()){
627
- const serialized = images.rewriteSync(source.dump.serialize(), source.dumpPath ?? source.sourcePath);
628
636
  agentReports.push(source.dump);
629
- (0, external_node_fs_namespaceObject.appendFileSync)(temporaryReportPath, `${(0, external_utils_js_namespaceObject.reportHTMLContent)({
630
- dumpString: serialized,
631
- attributes: {
632
- 'data-group-id': source.reportId,
633
- 'data-report-id': source.reportId,
634
- 'data-runner-scope-id': source.scopeId,
635
- [html_utils_js_namespaceObject.DATA_SCREENSHOT_MODE_ATTR]: directoryMode ? 'directory' : 'inline'
636
- }
637
- }, void 0, void 0, false)}\n`);
637
+ writer.addGroupSync(source.reportId, source.dump.serialize(), source.dumpPath ?? source.sourcePath, {
638
+ 'data-report-id': source.reportId,
639
+ 'data-runner-scope-id': source.scopeId
640
+ }, source.imageOrigins);
638
641
  }
639
- (0, external_node_fs_namespaceObject.appendFileSync)(temporaryReportPath, (0, html_utils_js_namespaceObject.generateTestRunReportScriptTag)(runnerDump));
640
- const agentComment = mergedAgentReportComment(agentReports);
641
- if (agentComment) (0, external_node_fs_namespaceObject.appendFileSync)(temporaryReportPath, agentComment);
642
- (0, external_node_fs_namespaceObject.appendFileSync)(temporaryReportPath, `${htmlEndTag}\n`);
643
- if ((0, external_node_fs_namespaceObject.existsSync)(finalArtifactPath)) (0, external_node_fs_namespaceObject.rmSync)(finalArtifactPath, {
644
- recursive: true,
645
- force: true
646
- });
647
- if ((0, external_node_fs_namespaceObject.existsSync)(standaloneReportPath)) (0, external_node_fs_namespaceObject.unlinkSync)(standaloneReportPath);
648
- (0, external_node_fs_namespaceObject.renameSync)(directoryMode ? temporaryArtifactPath : temporaryReportPath, directoryMode ? finalArtifactPath : standaloneReportPath);
642
+ writer.finishSync(runnerDump, mergedAgentReportComment(agentReports));
643
+ (0, report_artifact_publication_js_namespaceObject.publishReportArtifactSync)(directoryMode ? temporaryArtifactPath : temporaryReportPath, directoryMode ? finalArtifactPath : standaloneReportPath, directoryMode, options.overwrite);
649
644
  (0, external_node_fs_namespaceObject.rmSync)(temporaryRoot, {
650
645
  recursive: true,
651
646
  force: true
@@ -764,11 +759,13 @@ function splitReportHtmlByExecution(options) {
764
759
  exports.ExecutionRecorder = __webpack_exports__.ExecutionRecorder;
765
760
  exports.ReportArtifactStore = __webpack_exports__.ReportArtifactStore;
766
761
  exports.ReportMergingTool = __webpack_exports__.ReportMergingTool;
762
+ exports.ReportSession = __webpack_exports__.ReportSession;
767
763
  exports.TestRunReportAssembler = __webpack_exports__.TestRunReportAssembler;
768
764
  exports.calculateTestRunHealth = __webpack_exports__.calculateTestRunHealth;
769
765
  exports.classifyTestRunCase = __webpack_exports__.classifyTestRunCase;
770
766
  exports.collectDedupedExecutions = __webpack_exports__.collectDedupedExecutions;
771
767
  exports.dedupeExecutionsKeepLatest = __webpack_exports__.dedupeExecutionsKeepLatest;
768
+ exports.exportReportArtifact = __webpack_exports__.exportReportArtifact;
772
769
  exports.isDirectoryModeReport = __webpack_exports__.isDirectoryModeReport;
773
770
  exports.readReportActionDump = __webpack_exports__.readReportActionDump;
774
771
  exports.readReportArtifactManifest = __webpack_exports__.readReportArtifactManifest;
@@ -782,11 +779,13 @@ for(var __rspack_i in __webpack_exports__)if (-1 === [
782
779
  "ExecutionRecorder",
783
780
  "ReportArtifactStore",
784
781
  "ReportMergingTool",
782
+ "ReportSession",
785
783
  "TestRunReportAssembler",
786
784
  "calculateTestRunHealth",
787
785
  "classifyTestRunCase",
788
786
  "collectDedupedExecutions",
789
787
  "dedupeExecutionsKeepLatest",
788
+ "exportReportArtifact",
790
789
  "isDirectoryModeReport",
791
790
  "readReportActionDump",
792
791
  "readReportArtifactManifest",