@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.
- package/dist/es/agent/agent-report-manager.mjs +28 -5
- package/dist/es/agent/agent-report-manager.mjs.map +1 -1
- package/dist/es/agent/agent.mjs +2 -2
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/dump/html-utils.mjs +51 -68
- package/dist/es/dump/html-utils.mjs.map +1 -1
- package/dist/es/dump/report-artifact-publication.mjs +52 -0
- package/dist/es/dump/report-artifact-publication.mjs.map +1 -0
- package/dist/es/dump/report-artifact-store.mjs +2 -2
- package/dist/es/dump/report-artifact-store.mjs.map +1 -1
- package/dist/es/dump/report-artifact-writer.mjs +129 -0
- package/dist/es/dump/report-artifact-writer.mjs.map +1 -0
- package/dist/es/dump/report-session.mjs +229 -0
- package/dist/es/dump/report-session.mjs.map +1 -0
- package/dist/es/dump/test-report-images.mjs +68 -42
- package/dist/es/dump/test-report-images.mjs.map +1 -1
- package/dist/es/index.mjs +2 -2
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/report-cli.mjs +38 -11
- package/dist/es/report-cli.mjs.map +1 -1
- package/dist/es/report-generator.mjs +2 -1
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/report.mjs +152 -159
- package/dist/es/report.mjs.map +1 -1
- package/dist/es/test-run-report.mjs.map +1 -1
- package/dist/es/utils.mjs +1 -1
- package/dist/es/yaml/player.mjs +1 -1
- package/dist/es/yaml/player.mjs.map +1 -1
- package/dist/lib/agent/agent-report-manager.js +27 -4
- package/dist/lib/agent/agent-report-manager.js.map +1 -1
- package/dist/lib/agent/agent.js +2 -2
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/dump/html-utils.js +59 -70
- package/dist/lib/dump/html-utils.js.map +1 -1
- package/dist/lib/dump/report-artifact-publication.js +89 -0
- package/dist/lib/dump/report-artifact-publication.js.map +1 -0
- package/dist/lib/dump/report-artifact-store.js +2 -2
- package/dist/lib/dump/report-artifact-store.js.map +1 -1
- package/dist/lib/dump/report-artifact-writer.js +163 -0
- package/dist/lib/dump/report-artifact-writer.js.map +1 -0
- package/dist/lib/dump/report-session.js +266 -0
- package/dist/lib/dump/report-session.js.map +1 -0
- package/dist/lib/dump/test-report-images.js +67 -41
- package/dist/lib/dump/test-report-images.js.map +1 -1
- package/dist/lib/index.js +3 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/report-cli.js +37 -10
- package/dist/lib/report-cli.js.map +1 -1
- package/dist/lib/report-generator.js +2 -1
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/report.js +152 -153
- package/dist/lib/report.js.map +1 -1
- package/dist/lib/test-run-report.js.map +1 -1
- package/dist/lib/utils.js +1 -1
- package/dist/lib/yaml/player.js +1 -1
- package/dist/lib/yaml/player.js.map +1 -1
- package/dist/types/agent/agent-report-manager.d.ts +4 -1
- package/dist/types/agent/agent-report-manager.d.ts.map +1 -1
- package/dist/types/agent/agent.d.ts +1 -1
- package/dist/types/agent/agent.d.ts.map +1 -1
- package/dist/types/dump/html-utils.d.ts +7 -1
- package/dist/types/dump/html-utils.d.ts.map +1 -1
- package/dist/types/dump/report-artifact-publication.d.ts +5 -0
- package/dist/types/dump/report-artifact-publication.d.ts.map +1 -0
- package/dist/types/dump/report-artifact-store.d.ts +1 -1
- package/dist/types/dump/report-artifact-store.d.ts.map +1 -1
- package/dist/types/dump/report-artifact-writer.d.ts +21 -0
- package/dist/types/dump/report-artifact-writer.d.ts.map +1 -0
- package/dist/types/dump/report-session.d.ts +48 -0
- package/dist/types/dump/report-session.d.ts.map +1 -0
- package/dist/types/dump/test-report-images.d.ts +5 -2
- package/dist/types/dump/test-report-images.d.ts.map +1 -1
- package/dist/types/index.d.ts +1 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/report-cli.d.ts +2 -1
- package/dist/types/report-cli.d.ts.map +1 -1
- package/dist/types/report-generator.d.ts.map +1 -1
- package/dist/types/report.d.ts +11 -0
- package/dist/types/report.d.ts.map +1 -1
- package/dist/types/test-run-report.d.ts +2 -2
- package/dist/types/test-run-report.d.ts.map +1 -1
- package/package.json +2 -2
package/dist/es/report.mjs
CHANGED
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import { basename, dirname, join, resolve } from "node:path";
|
|
1
|
+
import { copyFileSync, existsSync, mkdirSync, mkdtempSync, readFileSync, rmSync, unlinkSync, writeFileSync } from "node:fs";
|
|
2
|
+
import { mkdir, mkdtemp, readFile, rm } from "node:fs/promises";
|
|
3
|
+
import { basename, dirname, join, resolve, sep } from "node:path";
|
|
4
4
|
import { getMidsceneRunSubDir } from "@midscene/shared/common";
|
|
5
5
|
import { antiEscapeScriptTag, logMsg, uuid } from "@midscene/shared/utils";
|
|
6
6
|
import { getReportFileName } from "./agent/report-file-name.mjs";
|
|
7
|
-
import { DATA_SCREENSHOT_MODE_ATTR, extractLastDumpScriptSync, generateAgentReportComment,
|
|
7
|
+
import { DATA_SCREENSHOT_MODE_ATTR, extractLastDumpScriptSync, generateAgentReportComment, streamDumpScriptsSync } from "./dump/html-utils.mjs";
|
|
8
8
|
import { imageRefFileExtension, normalizeStoredImageRef } from "./dump/image-reference.mjs";
|
|
9
9
|
import { readReportActionDump, readReportArtifactManifest, readReportArtifactManifestAsync, readReportDumpScripts, readReportRunnerDump, streamReportDumpScripts } from "./dump/report-artifact-reader.mjs";
|
|
10
10
|
import { parseBase64ImageDataUrl, resolveImageSource } from "./dump/screenshot-store.mjs";
|
|
11
11
|
import { ExecutionRecorder } from "./dump/execution-recorder.mjs";
|
|
12
12
|
import { ReportArtifactStore } from "./dump/report-artifact-store.mjs";
|
|
13
|
-
import {
|
|
13
|
+
import { ReportSession } from "./dump/report-session.mjs";
|
|
14
|
+
import { publishReportArtifact, publishReportArtifactSync } from "./dump/report-artifact-publication.mjs";
|
|
15
|
+
import { ReportArtifactWriter } from "./dump/report-artifact-writer.mjs";
|
|
14
16
|
import { collectReportSummary } from "./report-stats.mjs";
|
|
15
17
|
import { mergeTestRunReportDumps } from "./test-runner/reporting/merge-report.mjs";
|
|
16
18
|
import { collectTestRunReportMetrics } from "./test-runner/reporting/metrics.mjs";
|
|
17
19
|
import { ReportActionDump } from "./types.mjs";
|
|
18
|
-
import {
|
|
20
|
+
import { getVersion } from "./utils.mjs";
|
|
19
21
|
import { calculateTestRunHealth, classifyTestRunCase } from "./test-run-health.mjs";
|
|
20
22
|
function _define_property(obj, key, value) {
|
|
21
23
|
if (key in obj) Object.defineProperty(obj, key, {
|
|
@@ -27,6 +29,64 @@ function _define_property(obj, key, value) {
|
|
|
27
29
|
else obj[key] = value;
|
|
28
30
|
return obj;
|
|
29
31
|
}
|
|
32
|
+
async function exportReportArtifact(options) {
|
|
33
|
+
validateTestRunReportFileName(options.reportFileName);
|
|
34
|
+
const input = resolve(options.reportPath);
|
|
35
|
+
const output = resolve(options.outputDir);
|
|
36
|
+
const external = 'html-and-external-assets' === options.outputFormat;
|
|
37
|
+
const artifact = join(output, options.reportFileName);
|
|
38
|
+
const destination = external ? join(artifact, 'index.html') : `${artifact}.html`;
|
|
39
|
+
if (input === destination || external && input.startsWith(`${artifact}${sep}`)) throw new Error('Export destination must differ from the input report.');
|
|
40
|
+
if (!options.overwrite && existsSync(external ? artifact : destination)) throw new Error(`Report artifact already exists: ${artifact}`);
|
|
41
|
+
const manifest = readReportArtifactManifest(input);
|
|
42
|
+
const runner = readReportRunnerDump(input);
|
|
43
|
+
const groups = new Map();
|
|
44
|
+
for (const source of readReportDumpScripts(input)){
|
|
45
|
+
const attributes = Object.fromEntries([
|
|
46
|
+
...source.openTag.matchAll(/([^\s=]+)="([^"]*)"/g)
|
|
47
|
+
].filter(([, key])=>'type' !== key).map(([, key, value])=>[
|
|
48
|
+
key,
|
|
49
|
+
decodeURIComponent(value)
|
|
50
|
+
]));
|
|
51
|
+
const id = attributes['data-group-id'];
|
|
52
|
+
if (!id) throw new Error('Report execution group is missing its ID.');
|
|
53
|
+
const dump = ReportActionDump.fromSerializedString(manifest ? source.content : antiEscapeScriptTag(source.content));
|
|
54
|
+
const previous = groups.get(id);
|
|
55
|
+
dump.executions = dedupeExecutionsKeepLatest([
|
|
56
|
+
...previous?.dump.executions ?? [],
|
|
57
|
+
...dump.executions
|
|
58
|
+
]);
|
|
59
|
+
groups.set(id, {
|
|
60
|
+
dump,
|
|
61
|
+
attributes
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
if (!groups.size && !runner) throw new Error('No report execution data was found in the input.');
|
|
65
|
+
const hint = [
|
|
66
|
+
...groups.values()
|
|
67
|
+
][0]?.attributes['data-report-kind'];
|
|
68
|
+
const kind = manifest?.kind ?? ('agent' === hint || 'test' === hint ? hint : runner ? 'test' : 'agent');
|
|
69
|
+
await mkdir(output, {
|
|
70
|
+
recursive: true
|
|
71
|
+
});
|
|
72
|
+
const temporary = await mkdtemp(join(output, '.report-export-'));
|
|
73
|
+
const entry = join(temporary, 'index.html');
|
|
74
|
+
try {
|
|
75
|
+
const writer = new ReportArtifactWriter(entry, external, kind, manifest?.reportId ?? runner?.runId);
|
|
76
|
+
await writer.initialize();
|
|
77
|
+
for (const [id, group] of groups)await writer.addGroup(id, group.dump.serialize(), input, group.attributes);
|
|
78
|
+
await writer.finish(runner, mergedAgentReportComment([
|
|
79
|
+
...groups.values()
|
|
80
|
+
].map((group)=>group.dump)));
|
|
81
|
+
await publishReportArtifact(external ? temporary : entry, external ? artifact : destination, external, options.overwrite);
|
|
82
|
+
return destination;
|
|
83
|
+
} finally{
|
|
84
|
+
await rm(temporary, {
|
|
85
|
+
recursive: true,
|
|
86
|
+
force: true
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
30
90
|
const screenshotModeAttrRegExp = new RegExp(`${DATA_SCREENSHOT_MODE_ATTR}="(inline|directory)"`);
|
|
31
91
|
function readDeclaredScreenshotMode(reportFilePath) {
|
|
32
92
|
let mode;
|
|
@@ -130,8 +190,8 @@ class ReportMergingTool {
|
|
|
130
190
|
clear() {
|
|
131
191
|
this.reportInfos = [];
|
|
132
192
|
}
|
|
133
|
-
mergeDumpScripts(contents) {
|
|
134
|
-
const unescaped = contents.map((c)=>antiEscapeScriptTag(c)).filter((c)=>c.length > 0);
|
|
193
|
+
mergeDumpScripts(contents, external) {
|
|
194
|
+
const unescaped = contents.map((c)=>external ? c : antiEscapeScriptTag(c)).filter((c)=>c.length > 0);
|
|
135
195
|
if (0 === unescaped.length) return '';
|
|
136
196
|
if (1 === unescaped.length) return unescaped[0];
|
|
137
197
|
const base = ReportActionDump.fromSerializedString(unescaped[0]);
|
|
@@ -145,99 +205,67 @@ class ReportMergingTool {
|
|
|
145
205
|
base.executions = dedupeExecutionsKeepLatest(allExecutions);
|
|
146
206
|
return base.serialize();
|
|
147
207
|
}
|
|
148
|
-
mergeReports(reportFileName = 'AUTO', opts) {
|
|
149
|
-
const { rmOriginalReports = false, overwrite = false, outputDir } = opts
|
|
150
|
-
if (
|
|
208
|
+
mergeReports(reportFileName = 'AUTO', opts = {}) {
|
|
209
|
+
const { rmOriginalReports = false, overwrite = false, outputDir, outputFormat } = opts;
|
|
210
|
+
if (!this.reportInfos.length) {
|
|
151
211
|
logMsg('No reports to merge');
|
|
152
212
|
return null;
|
|
153
213
|
}
|
|
154
214
|
const runnerDumps = this.reportInfos.map((info)=>info.reportFilePath ? readReportRunnerDump(info.reportFilePath) : void 0);
|
|
155
215
|
const hasRunnerDump = runnerDumps.some(Boolean);
|
|
216
|
+
const external = 'html-and-external-assets' === outputFormat;
|
|
217
|
+
const name = 'AUTO' === reportFileName ? getReportFileName('merged-report') : reportFileName;
|
|
218
|
+
validateTestRunReportFileName(name);
|
|
156
219
|
const targetDir = outputDir ? resolve(outputDir) : getMidsceneRunSubDir('report');
|
|
157
|
-
|
|
158
|
-
recursive: true
|
|
159
|
-
});
|
|
160
|
-
const isDirModeByIndex = this.reportInfos.map((info)=>Boolean(info.reportFilePath && isDirectoryModeReport(info.reportFilePath)));
|
|
161
|
-
const hasDirectoryModeReport = isDirModeByIndex.some(Boolean);
|
|
162
|
-
const resolvedName = 'AUTO' === reportFileName ? getReportFileName('merged-report') : reportFileName;
|
|
163
|
-
const outputFilePath = hasDirectoryModeReport ? resolve(targetDir, resolvedName, 'index.html') : resolve(targetDir, `${resolvedName}.html`);
|
|
164
|
-
if ('AUTO' !== reportFileName && existsSync(outputFilePath)) {
|
|
165
|
-
if (!overwrite) throw new Error(`Report file already exists: ${outputFilePath}\nSet overwrite to true to overwrite this file.`);
|
|
166
|
-
if (hasDirectoryModeReport) rmSync(dirname(outputFilePath), {
|
|
167
|
-
recursive: true,
|
|
168
|
-
force: true
|
|
169
|
-
});
|
|
170
|
-
else unlinkSync(outputFilePath);
|
|
171
|
-
}
|
|
172
|
-
if (hasDirectoryModeReport) mkdirSync(dirname(outputFilePath), {
|
|
220
|
+
mkdirSync(targetDir, {
|
|
173
221
|
recursive: true
|
|
174
222
|
});
|
|
175
|
-
|
|
223
|
+
const artifact = join(targetDir, name);
|
|
224
|
+
const outputFilePath = external ? join(artifact, 'index.html') : `${artifact}.html`;
|
|
225
|
+
if (!overwrite && existsSync(external ? artifact : outputFilePath)) throw new Error(`Report file already exists: ${outputFilePath}\nSet overwrite to true to overwrite this file.`);
|
|
226
|
+
if (rmOriginalReports && this.reportInfos.some((info)=>info.reportFilePath && isDirectoryBasedReport(info.reportFilePath) && outputFilePath.startsWith(`${dirname(resolve(info.reportFilePath))}${sep}`))) throw new Error('Merged output must be outside source report directories when removing originals.');
|
|
227
|
+
const temporary = mkdtempSync(join(targetDir, '.merged-report-'));
|
|
228
|
+
const entry = join(temporary, 'index.html');
|
|
176
229
|
try {
|
|
177
|
-
const
|
|
178
|
-
|
|
179
|
-
const htmlEndIdx = tpl.lastIndexOf(htmlEndTag);
|
|
180
|
-
const tplWithoutClose = -1 !== htmlEndIdx ? tpl.slice(0, htmlEndIdx) : tpl;
|
|
181
|
-
appendFileSync(outputFilePath, tplWithoutClose);
|
|
182
|
-
if (hasDirectoryModeReport) appendFileSync(outputFilePath, getBaseUrlFixScript());
|
|
230
|
+
const writer = new ReportArtifactWriter(entry, external, hasRunnerDump ? 'test' : 'agent');
|
|
231
|
+
writer.initializeSync();
|
|
183
232
|
const agentReports = [];
|
|
184
233
|
const runnerSources = [];
|
|
185
|
-
const
|
|
186
|
-
for(
|
|
187
|
-
const
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
if (
|
|
194
|
-
|
|
195
|
-
const screenshotsDir = join(reportDir, 'screenshots');
|
|
196
|
-
if (existsSync(screenshotsDir)) {
|
|
197
|
-
const mergedScreenshotsDir = join(dirname(outputFilePath), 'screenshots');
|
|
198
|
-
mkdirSync(mergedScreenshotsDir, {
|
|
199
|
-
recursive: true
|
|
200
|
-
});
|
|
201
|
-
for (const file of readdirSync(screenshotsDir)){
|
|
202
|
-
const src = join(screenshotsDir, file);
|
|
203
|
-
const dest = join(mergedScreenshotsDir, file);
|
|
204
|
-
copyFileSync(src, dest);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
} else streamImageScriptsToFile(reportInfo.reportFilePath, outputFilePath, writtenInlineImageIds);
|
|
208
|
-
const allDumps = readReportDumpScripts(reportInfo.reportFilePath).filter((d)=>d.openTag.includes('data-group-id'));
|
|
209
|
-
const groupIdMatch = allDumps[0]?.openTag.match(/data-group-id="([^"]+)"/);
|
|
210
|
-
if (groupIdMatch && !hasRunnerDump) mergedGroupId = decodeURIComponent(groupIdMatch[1]);
|
|
211
|
-
const extractedDumpString = allDumps.length > 0 ? this.mergeDumpScripts(allDumps.map((d)=>d.content)) : extractLastDumpScriptSync(reportInfo.reportFilePath);
|
|
212
|
-
if (extractedDumpString) dumpString = extractedDumpString;
|
|
234
|
+
const groupIds = new Set();
|
|
235
|
+
for (const [index, info] of this.reportInfos.entries()){
|
|
236
|
+
const attributes = info.reportAttributes;
|
|
237
|
+
let serialized = this.createEmptyDumpString(attributes.testTitle, attributes.testDescription);
|
|
238
|
+
let groupId = `merged-group-${index}`;
|
|
239
|
+
if (info.reportFilePath) {
|
|
240
|
+
const dumps = readReportDumpScripts(info.reportFilePath);
|
|
241
|
+
const originalId = dumps[0]?.openTag.match(/data-group-id="([^"]+)"/)?.[1];
|
|
242
|
+
if (originalId && !hasRunnerDump && !groupIds.has(decodeURIComponent(originalId))) groupId = decodeURIComponent(originalId);
|
|
243
|
+
serialized = dumps.length ? this.mergeDumpScripts(dumps.map((dump)=>dump.content), Boolean(readReportArtifactManifest(info.reportFilePath))) : extractLastDumpScriptSync(info.reportFilePath) ?? serialized;
|
|
213
244
|
}
|
|
214
|
-
|
|
245
|
+
if (groupIds.has(groupId)) groupId = `merged-group-${uuid()}`;
|
|
246
|
+
groupIds.add(groupId);
|
|
247
|
+
const agentReport = tryParseAgentReportDump(serialized);
|
|
215
248
|
if (agentReport) agentReports.push(agentReport);
|
|
216
249
|
if (hasRunnerDump) runnerSources.push({
|
|
217
|
-
reportId:
|
|
218
|
-
sourcePath:
|
|
219
|
-
attributes
|
|
220
|
-
dump: runnerDumps[
|
|
221
|
-
timing: agentReport ? agentReportTiming(agentReport,
|
|
250
|
+
reportId: groupId,
|
|
251
|
+
sourcePath: info.reportFilePath ?? '<skipped-agent-report>',
|
|
252
|
+
attributes,
|
|
253
|
+
dump: runnerDumps[index],
|
|
254
|
+
timing: agentReport ? agentReportTiming(agentReport, attributes.testDuration) : void 0
|
|
255
|
+
});
|
|
256
|
+
writer.addGroupSync(groupId, serialized, info.reportFilePath ?? entry, {
|
|
257
|
+
...hasRunnerDump ? {
|
|
258
|
+
'data-report-id': groupId
|
|
259
|
+
} : {},
|
|
260
|
+
playwright_test_duration: attributes.testDuration,
|
|
261
|
+
playwright_test_status: attributes.testStatus,
|
|
262
|
+
playwright_test_title: attributes.testTitle,
|
|
263
|
+
playwright_test_id: attributes.testId,
|
|
264
|
+
playwright_test_description: attributes.testDescription,
|
|
265
|
+
is_merged: true
|
|
222
266
|
});
|
|
223
|
-
const reportHtmlStr = `${reportHTMLContent({
|
|
224
|
-
dumpString,
|
|
225
|
-
attributes: {
|
|
226
|
-
'data-group-id': mergedGroupId,
|
|
227
|
-
...hasRunnerDump ? {
|
|
228
|
-
'data-report-id': mergedGroupId
|
|
229
|
-
} : {},
|
|
230
|
-
[DATA_SCREENSHOT_MODE_ATTR]: hasDirectoryModeReport ? 'directory' : 'inline',
|
|
231
|
-
playwright_test_duration: reportAttributes.testDuration,
|
|
232
|
-
playwright_test_status: reportAttributes.testStatus,
|
|
233
|
-
playwright_test_title: reportAttributes.testTitle,
|
|
234
|
-
playwright_test_id: reportAttributes.testId,
|
|
235
|
-
playwright_test_description: reportAttributes.testDescription,
|
|
236
|
-
is_merged: true
|
|
237
|
-
}
|
|
238
|
-
}, void 0, void 0, false)}\n`;
|
|
239
|
-
appendFileSync(outputFilePath, reportHtmlStr);
|
|
240
267
|
}
|
|
268
|
+
let runner;
|
|
241
269
|
if (hasRunnerDump) {
|
|
242
270
|
const executions = dedupeExecutionsKeepLatest(agentReports.flatMap((report)=>report.executions));
|
|
243
271
|
const metrics = collectTestRunReportMetrics({
|
|
@@ -253,30 +281,22 @@ class ReportMergingTool {
|
|
|
253
281
|
})
|
|
254
282
|
]
|
|
255
283
|
] : []));
|
|
256
|
-
|
|
284
|
+
runner = mergeTestRunReportDumps(runnerSources, metrics, uuid(), executionMetrics);
|
|
257
285
|
}
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
appendFileSync(outputFilePath, `${htmlEndTag}\n`);
|
|
261
|
-
logMsg(`Successfully merged new report: ${outputFilePath}`);
|
|
286
|
+
writer.finishSync(runner, mergedAgentReportComment(agentReports));
|
|
287
|
+
publishReportArtifactSync(external ? temporary : entry, external ? artifact : outputFilePath, external, overwrite);
|
|
262
288
|
if (rmOriginalReports) {
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
reportCountWithFilePath += 1;
|
|
267
|
-
try {
|
|
268
|
-
removeReportArtifact(info.reportFilePath);
|
|
269
|
-
removedReportCount += 1;
|
|
270
|
-
} catch (error) {
|
|
271
|
-
logMsg(`Error deleting report ${info.reportFilePath}: ${error}`);
|
|
272
|
-
}
|
|
273
|
-
}
|
|
274
|
-
logMsg(`Removed ${removedReportCount}/${reportCountWithFilePath} original reports`);
|
|
289
|
+
for (const sourcePath of new Set(this.reportInfos.flatMap((info)=>info.reportFilePath ? [
|
|
290
|
+
resolve(info.reportFilePath)
|
|
291
|
+
] : [])))if (sourcePath !== outputFilePath) removeReportArtifact(sourcePath);
|
|
275
292
|
}
|
|
293
|
+
logMsg(`Successfully merged new report: ${outputFilePath}`);
|
|
276
294
|
return outputFilePath;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
|
|
295
|
+
} finally{
|
|
296
|
+
rmSync(temporary, {
|
|
297
|
+
recursive: true,
|
|
298
|
+
force: true
|
|
299
|
+
});
|
|
280
300
|
}
|
|
281
301
|
}
|
|
282
302
|
constructor(){
|
|
@@ -351,8 +371,15 @@ const prepareTestRunReportSources = (sources, loadedDumps)=>{
|
|
|
351
371
|
continue;
|
|
352
372
|
}
|
|
353
373
|
const sourceKey = source.sourcePath;
|
|
374
|
+
const imageOrigins = new Map();
|
|
375
|
+
JSON.parse(baseDump.serialize(), (_key, value)=>{
|
|
376
|
+
const ref = normalizeStoredImageRef(value);
|
|
377
|
+
if (ref) imageOrigins.set(ref.id, 'file' === ref.storage ? source.dumpPath ?? source.sourcePath : source.sourcePath);
|
|
378
|
+
return value;
|
|
379
|
+
});
|
|
354
380
|
const shared = preparedByPath.get(sourceKey);
|
|
355
381
|
if (shared) {
|
|
382
|
+
for (const [id, origin] of imageOrigins)shared.imageOrigins.set(id, origin);
|
|
356
383
|
if (source.dumpPath) shared.dump.executions = dedupeExecutionsKeepLatest([
|
|
357
384
|
...shared.dump.executions,
|
|
358
385
|
...baseDump.executions
|
|
@@ -412,6 +439,7 @@ const prepareTestRunReportSources = (sources, loadedDumps)=>{
|
|
|
412
439
|
executionIds,
|
|
413
440
|
collection: source.collection,
|
|
414
441
|
dump,
|
|
442
|
+
imageOrigins,
|
|
415
443
|
...source.dumpPath ? {
|
|
416
444
|
dumpPath: source.dumpPath
|
|
417
445
|
} : {}
|
|
@@ -486,41 +514,25 @@ class TestRunReportAssembler {
|
|
|
486
514
|
});
|
|
487
515
|
const artifact = join(outputDir, options.reportFileName);
|
|
488
516
|
const finalPath = directoryMode ? join(artifact, 'index.html') : `${artifact}.html`;
|
|
489
|
-
if (!options.overwrite &&
|
|
517
|
+
if (!options.overwrite && existsSync(directoryMode ? artifact : finalPath)) throw new Error(`Report artifact already exists: ${artifact}`);
|
|
490
518
|
const temporary = await mkdtemp(join(outputDir, '.test-run-report-'));
|
|
491
519
|
const htmlPath = join(temporary, 'index.html');
|
|
492
520
|
try {
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
const images = new TestReportImages(htmlPath, directoryMode);
|
|
521
|
+
const writer = new ReportArtifactWriter(htmlPath, directoryMode, 'test', dump.runId);
|
|
522
|
+
await writer.initialize();
|
|
496
523
|
const reports = [];
|
|
497
524
|
for (const source of new Map(prepared.sources.map((source)=>[
|
|
498
525
|
source.reportId,
|
|
499
526
|
source
|
|
500
527
|
])).values()){
|
|
501
|
-
const serialized = await images.rewriteAsync(source.dump.serialize(), source.dumpPath ?? source.sourcePath);
|
|
502
528
|
reports.push(source.dump);
|
|
503
|
-
await
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
'data-report-id': source.reportId,
|
|
508
|
-
'data-runner-scope-id': source.scopeId,
|
|
509
|
-
[DATA_SCREENSHOT_MODE_ATTR]: directoryMode ? 'directory' : 'inline'
|
|
510
|
-
}
|
|
511
|
-
}, void 0, void 0, false));
|
|
512
|
-
}
|
|
513
|
-
await appendFile(htmlPath, generateTestRunReportScriptTag(dump) + mergedAgentReportComment(reports));
|
|
514
|
-
if (options.overwrite) {
|
|
515
|
-
await rm(artifact, {
|
|
516
|
-
recursive: true,
|
|
517
|
-
force: true
|
|
518
|
-
});
|
|
519
|
-
await rm(`${artifact}.html`, {
|
|
520
|
-
force: true
|
|
521
|
-
});
|
|
529
|
+
await writer.addGroup(source.reportId, source.dump.serialize(), source.dumpPath ?? source.sourcePath, {
|
|
530
|
+
'data-report-id': source.reportId,
|
|
531
|
+
'data-runner-scope-id': source.scopeId
|
|
532
|
+
}, source.imageOrigins);
|
|
522
533
|
}
|
|
523
|
-
await
|
|
534
|
+
await writer.finish(dump, mergedAgentReportComment(reports));
|
|
535
|
+
await publishReportArtifact(directoryMode ? temporary : htmlPath, directoryMode ? artifact : finalPath, directoryMode, options.overwrite);
|
|
524
536
|
return finalPath;
|
|
525
537
|
} finally{
|
|
526
538
|
await rm(temporary, {
|
|
@@ -558,8 +570,7 @@ class TestRunReportAssembler {
|
|
|
558
570
|
const standaloneReportPath = `${finalArtifactPath}.html`;
|
|
559
571
|
const finalReportPath = directoryMode ? directoryReportPath : standaloneReportPath;
|
|
560
572
|
const existingArtifacts = [
|
|
561
|
-
finalArtifactPath
|
|
562
|
-
standaloneReportPath
|
|
573
|
+
directoryMode ? finalArtifactPath : standaloneReportPath
|
|
563
574
|
].filter(existsSync);
|
|
564
575
|
if (existingArtifacts.length > 0 && !options.overwrite) throw new Error(`Report artifact already exists: ${existingArtifacts[0]}\nSet overwrite to true to overwrite this report.`);
|
|
565
576
|
const temporaryRoot = mkdtempSync(join(outputDir, '.test-run-report-'));
|
|
@@ -569,39 +580,21 @@ class TestRunReportAssembler {
|
|
|
569
580
|
if (directoryMode) mkdirSync(temporaryArtifactPath, {
|
|
570
581
|
recursive: true
|
|
571
582
|
});
|
|
572
|
-
const
|
|
573
|
-
|
|
574
|
-
const htmlEndIndex = template.lastIndexOf(htmlEndTag);
|
|
575
|
-
appendFileSync(temporaryReportPath, -1 === htmlEndIndex ? template : template.slice(0, htmlEndIndex));
|
|
576
|
-
if (directoryMode) appendFileSync(temporaryReportPath, getBaseUrlFixScript());
|
|
577
|
-
const images = new TestReportImages(temporaryReportPath, directoryMode);
|
|
583
|
+
const writer = new ReportArtifactWriter(temporaryReportPath, directoryMode, 'test', runnerDump.runId);
|
|
584
|
+
writer.initializeSync();
|
|
578
585
|
const agentReports = [];
|
|
579
586
|
for (const source of new Map(prepared.sources.map((source)=>[
|
|
580
587
|
source.reportId,
|
|
581
588
|
source
|
|
582
589
|
])).values()){
|
|
583
|
-
const serialized = images.rewriteSync(source.dump.serialize(), source.dumpPath ?? source.sourcePath);
|
|
584
590
|
agentReports.push(source.dump);
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
'data-report-id': source.reportId,
|
|
590
|
-
'data-runner-scope-id': source.scopeId,
|
|
591
|
-
[DATA_SCREENSHOT_MODE_ATTR]: directoryMode ? 'directory' : 'inline'
|
|
592
|
-
}
|
|
593
|
-
}, void 0, void 0, false)}\n`);
|
|
591
|
+
writer.addGroupSync(source.reportId, source.dump.serialize(), source.dumpPath ?? source.sourcePath, {
|
|
592
|
+
'data-report-id': source.reportId,
|
|
593
|
+
'data-runner-scope-id': source.scopeId
|
|
594
|
+
}, source.imageOrigins);
|
|
594
595
|
}
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
if (agentComment) appendFileSync(temporaryReportPath, agentComment);
|
|
598
|
-
appendFileSync(temporaryReportPath, `${htmlEndTag}\n`);
|
|
599
|
-
if (existsSync(finalArtifactPath)) rmSync(finalArtifactPath, {
|
|
600
|
-
recursive: true,
|
|
601
|
-
force: true
|
|
602
|
-
});
|
|
603
|
-
if (existsSync(standaloneReportPath)) unlinkSync(standaloneReportPath);
|
|
604
|
-
renameSync(directoryMode ? temporaryArtifactPath : temporaryReportPath, directoryMode ? finalArtifactPath : standaloneReportPath);
|
|
596
|
+
writer.finishSync(runnerDump, mergedAgentReportComment(agentReports));
|
|
597
|
+
publishReportArtifactSync(directoryMode ? temporaryArtifactPath : temporaryReportPath, directoryMode ? finalArtifactPath : standaloneReportPath, directoryMode, options.overwrite);
|
|
605
598
|
rmSync(temporaryRoot, {
|
|
606
599
|
recursive: true,
|
|
607
600
|
force: true
|
|
@@ -717,6 +710,6 @@ function splitReportHtmlByExecution(options) {
|
|
|
717
710
|
screenshotFiles: Array.from(writtenScreenshotFiles).sort().map((fileName)=>join(screenshotsDir, fileName))
|
|
718
711
|
};
|
|
719
712
|
}
|
|
720
|
-
export { ExecutionRecorder, ReportArtifactStore, ReportMergingTool, TestRunReportAssembler, calculateTestRunHealth, classifyTestRunCase, collectDedupedExecutions, dedupeExecutionsKeepLatest, isDirectoryModeReport, readReportActionDump, readReportArtifactManifest, readReportArtifactManifestAsync, readReportDumpScripts, readReportRunnerDump, removeReportArtifact, splitReportHtmlByExecution, streamReportDumpScripts };
|
|
713
|
+
export { ExecutionRecorder, ReportArtifactStore, ReportMergingTool, ReportSession, TestRunReportAssembler, calculateTestRunHealth, classifyTestRunCase, collectDedupedExecutions, dedupeExecutionsKeepLatest, exportReportArtifact, isDirectoryModeReport, readReportActionDump, readReportArtifactManifest, readReportArtifactManifestAsync, readReportDumpScripts, readReportRunnerDump, removeReportArtifact, splitReportHtmlByExecution, streamReportDumpScripts };
|
|
721
714
|
|
|
722
715
|
//# sourceMappingURL=report.mjs.map
|