@midscene/core 1.12.10-beta-20260920031616.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/es/agent/agent.mjs +103 -6
- package/dist/es/agent/agent.mjs.map +1 -1
- package/dist/es/agent/index.mjs +4 -3
- package/dist/es/agent/tasks.mjs +2 -2
- package/dist/es/agent/tasks.mjs.map +1 -1
- package/dist/es/agent/test-runner-nodes.mjs +291 -40
- package/dist/es/agent/test-runner-nodes.mjs.map +1 -1
- package/dist/es/agent/utils.mjs +1 -1
- package/dist/es/device/index.mjs +15 -2
- package/dist/es/device/index.mjs.map +1 -1
- package/dist/es/dump/html-utils.mjs +37 -4
- package/dist/es/dump/html-utils.mjs.map +1 -1
- package/dist/es/dump/screenshot-store.mjs +8 -1
- package/dist/es/dump/screenshot-store.mjs.map +1 -1
- package/dist/es/report-cli.mjs +9 -4
- package/dist/es/report-cli.mjs.map +1 -1
- package/dist/es/report-generator.mjs +117 -4
- package/dist/es/report-generator.mjs.map +1 -1
- package/dist/es/report-html-template.mjs +1 -1
- package/dist/es/report.mjs +190 -41
- package/dist/es/report.mjs.map +1 -1
- package/dist/es/test-run-report.mjs.map +1 -1
- package/dist/es/test-runner/engine/case-runner.mjs +52 -0
- package/dist/es/test-runner/engine/case-runner.mjs.map +1 -0
- package/dist/es/test-runner/engine/document-runtime.mjs +273 -0
- package/dist/es/test-runner/engine/document-runtime.mjs.map +1 -0
- package/dist/es/test-runner/engine/execute-step.mjs +177 -0
- package/dist/es/test-runner/engine/execute-step.mjs.map +1 -0
- package/dist/es/test-runner/engine/execution-failure.mjs +39 -0
- package/dist/es/test-runner/engine/execution-failure.mjs.map +1 -0
- package/dist/es/test-runner/engine/index.mjs +12 -0
- package/dist/es/test-runner/engine/project-runtime.mjs +142 -0
- package/dist/es/test-runner/engine/project-runtime.mjs.map +1 -0
- package/dist/es/test-runner/engine/registry.mjs +61 -0
- package/dist/es/test-runner/engine/registry.mjs.map +1 -0
- package/dist/es/test-runner/engine/resource-operations.mjs +211 -0
- package/dist/es/test-runner/engine/resource-operations.mjs.map +1 -0
- package/dist/es/test-runner/engine/run-collected-case.mjs +193 -0
- package/dist/es/test-runner/engine/run-collected-case.mjs.map +1 -0
- package/dist/es/test-runner/engine/run-workflow-document.mjs +155 -0
- package/dist/es/test-runner/engine/run-workflow-document.mjs.map +1 -0
- package/dist/es/test-runner/engine/scheduler.mjs +27 -0
- package/dist/es/test-runner/engine/scheduler.mjs.map +1 -0
- package/dist/es/test-runner/engine/scope-teardown.mjs +16 -0
- package/dist/es/test-runner/engine/scope-teardown.mjs.map +1 -0
- package/dist/es/test-runner/engine/types.mjs +0 -0
- package/dist/es/test-runner/errors.mjs +201 -0
- package/dist/es/test-runner/errors.mjs.map +1 -0
- package/dist/es/test-runner/execution-record.mjs +83 -0
- package/dist/es/test-runner/execution-record.mjs.map +1 -0
- package/dist/es/test-runner/index.mjs +12 -0
- package/dist/es/test-runner/node/define-node.mjs +44 -0
- package/dist/es/test-runner/node/define-node.mjs.map +1 -0
- package/dist/es/test-runner/node/index.mjs +2 -0
- package/dist/es/test-runner/node/types.mjs +0 -0
- package/dist/es/test-runner/parser/document-format.mjs +21 -0
- package/dist/es/test-runner/parser/document-format.mjs.map +1 -0
- package/dist/es/test-runner/parser/normalize.mjs +87 -0
- package/dist/es/test-runner/parser/normalize.mjs.map +1 -0
- package/dist/es/test-runner/parser/types.mjs +0 -0
- package/dist/es/test-runner/project.mjs +0 -0
- package/dist/es/test-runner/reporting/agent-report.mjs +23 -0
- package/dist/es/test-runner/reporting/agent-report.mjs.map +1 -0
- package/dist/es/test-runner/reporting/execution-record.mjs +73 -0
- package/dist/es/test-runner/reporting/execution-record.mjs.map +1 -0
- package/dist/es/test-runner/reporting/merge-report.mjs +231 -0
- package/dist/es/test-runner/reporting/merge-report.mjs.map +1 -0
- package/dist/es/test-runner/reporting/sanitize.mjs +162 -0
- package/dist/es/test-runner/reporting/sanitize.mjs.map +1 -0
- package/dist/es/test-runner/reporting/test-run-report.mjs +327 -0
- package/dist/es/test-runner/reporting/test-run-report.mjs.map +1 -0
- package/dist/es/test-runner/reporting/types.mjs +0 -0
- package/dist/es/utils.mjs +1 -1
- package/dist/es/yaml/execution-context.node.mjs +6 -0
- package/dist/es/yaml/execution-context.node.mjs.map +1 -0
- package/dist/es/yaml/execution-context.stub.mjs +8 -0
- package/dist/es/yaml/execution-context.stub.mjs.map +1 -0
- package/dist/es/yaml/execution-session.mjs +90 -0
- package/dist/es/yaml/execution-session.mjs.map +1 -0
- package/dist/es/yaml/index.mjs +2 -1
- package/dist/es/yaml/legacy-yaml-execution-record.mjs +47 -0
- package/dist/es/yaml/legacy-yaml-execution-record.mjs.map +1 -0
- package/dist/es/yaml/legacy-yaml-report.mjs +52 -0
- package/dist/es/yaml/legacy-yaml-report.mjs.map +1 -0
- package/dist/es/yaml/legacy-yaml-runtime.mjs +119 -0
- package/dist/es/yaml/legacy-yaml-runtime.mjs.map +1 -0
- package/dist/es/yaml/player-state.mjs +15 -0
- package/dist/es/yaml/player-state.mjs.map +1 -0
- package/dist/es/yaml/player.mjs +214 -348
- package/dist/es/yaml/player.mjs.map +1 -1
- package/dist/es/yaml/report-policy.mjs +7 -0
- package/dist/es/yaml/report-policy.mjs.map +1 -0
- package/dist/es/yaml/test-runner-compat.mjs +301 -0
- package/dist/es/yaml/test-runner-compat.mjs.map +1 -0
- package/dist/es/yaml/test-runner-nodes.mjs +147 -0
- package/dist/es/yaml/test-runner-nodes.mjs.map +1 -0
- package/dist/es/yaml/utils.mjs +12 -1
- package/dist/es/yaml/utils.mjs.map +1 -1
- package/dist/es/yaml-runtime.mjs +7 -0
- package/dist/es/yaml.mjs.map +1 -1
- package/dist/lib/agent/agent.js +103 -6
- package/dist/lib/agent/agent.js.map +1 -1
- package/dist/lib/agent/index.js +37 -3
- package/dist/lib/agent/tasks.js +2 -2
- package/dist/lib/agent/tasks.js.map +1 -1
- package/dist/lib/agent/test-runner-nodes.js +329 -45
- package/dist/lib/agent/test-runner-nodes.js.map +1 -1
- package/dist/lib/agent/utils.js +1 -1
- package/dist/lib/device/index.js +15 -2
- package/dist/lib/device/index.js.map +1 -1
- package/dist/lib/dump/html-utils.js +43 -4
- package/dist/lib/dump/html-utils.js.map +1 -1
- package/dist/lib/dump/screenshot-store.js +8 -1
- package/dist/lib/dump/screenshot-store.js.map +1 -1
- package/dist/lib/report-cli.js +8 -3
- package/dist/lib/report-cli.js.map +1 -1
- package/dist/lib/report-generator.js +114 -1
- package/dist/lib/report-generator.js.map +1 -1
- package/dist/lib/report-html-template.js +1 -1
- package/dist/lib/report.js +187 -38
- package/dist/lib/report.js.map +1 -1
- package/dist/lib/test-run-report.js.map +1 -1
- package/dist/lib/test-runner/engine/case-runner.js +89 -0
- package/dist/lib/test-runner/engine/case-runner.js.map +1 -0
- package/dist/lib/test-runner/engine/document-runtime.js +307 -0
- package/dist/lib/test-runner/engine/document-runtime.js.map +1 -0
- package/dist/lib/test-runner/engine/execute-step.js +211 -0
- package/dist/lib/test-runner/engine/execute-step.js.map +1 -0
- package/dist/lib/test-runner/engine/execution-failure.js +79 -0
- package/dist/lib/test-runner/engine/execution-failure.js.map +1 -0
- package/dist/lib/test-runner/engine/index.js +137 -0
- package/dist/lib/test-runner/engine/index.js.map +1 -0
- package/dist/lib/test-runner/engine/project-runtime.js +176 -0
- package/dist/lib/test-runner/engine/project-runtime.js.map +1 -0
- package/dist/lib/test-runner/engine/registry.js +95 -0
- package/dist/lib/test-runner/engine/registry.js.map +1 -0
- package/dist/lib/test-runner/engine/resource-operations.js +272 -0
- package/dist/lib/test-runner/engine/resource-operations.js.map +1 -0
- package/dist/lib/test-runner/engine/run-collected-case.js +227 -0
- package/dist/lib/test-runner/engine/run-collected-case.js.map +1 -0
- package/dist/lib/test-runner/engine/run-workflow-document.js +189 -0
- package/dist/lib/test-runner/engine/run-workflow-document.js.map +1 -0
- package/dist/lib/test-runner/engine/scheduler.js +64 -0
- package/dist/lib/test-runner/engine/scheduler.js.map +1 -0
- package/dist/lib/test-runner/engine/scope-teardown.js +50 -0
- package/dist/lib/test-runner/engine/scope-teardown.js.map +1 -0
- package/dist/lib/test-runner/engine/types.js +20 -0
- package/dist/lib/test-runner/engine/types.js.map +1 -0
- package/dist/lib/test-runner/errors.js +283 -0
- package/dist/lib/test-runner/errors.js.map +1 -0
- package/dist/lib/test-runner/execution-record.js +123 -0
- package/dist/lib/test-runner/execution-record.js.map +1 -0
- package/dist/lib/test-runner/index.js +137 -0
- package/dist/lib/test-runner/index.js.map +1 -0
- package/dist/lib/test-runner/node/define-node.js +78 -0
- package/dist/lib/test-runner/node/define-node.js.map +1 -0
- package/dist/lib/test-runner/node/index.js +67 -0
- package/dist/lib/test-runner/node/index.js.map +1 -0
- package/dist/lib/test-runner/node/types.js +20 -0
- package/dist/lib/test-runner/node/types.js.map +1 -0
- package/dist/lib/test-runner/parser/document-format.js +58 -0
- package/dist/lib/test-runner/parser/document-format.js.map +1 -0
- package/dist/lib/test-runner/parser/normalize.js +124 -0
- package/dist/lib/test-runner/parser/normalize.js.map +1 -0
- package/dist/lib/test-runner/parser/types.js +20 -0
- package/dist/lib/test-runner/parser/types.js.map +1 -0
- package/dist/lib/test-runner/project.js +20 -0
- package/dist/lib/test-runner/project.js.map +1 -0
- package/dist/lib/test-runner/reporting/agent-report.js +57 -0
- package/dist/lib/test-runner/reporting/agent-report.js.map +1 -0
- package/dist/lib/test-runner/reporting/execution-record.js +107 -0
- package/dist/lib/test-runner/reporting/execution-record.js.map +1 -0
- package/dist/lib/test-runner/reporting/merge-report.js +265 -0
- package/dist/lib/test-runner/reporting/merge-report.js.map +1 -0
- package/dist/lib/test-runner/reporting/sanitize.js +202 -0
- package/dist/lib/test-runner/reporting/sanitize.js.map +1 -0
- package/dist/lib/test-runner/reporting/test-run-report.js +364 -0
- package/dist/lib/test-runner/reporting/test-run-report.js.map +1 -0
- package/dist/lib/test-runner/reporting/types.js +20 -0
- package/dist/lib/test-runner/reporting/types.js.map +1 -0
- package/dist/lib/utils.js +1 -1
- package/dist/lib/yaml/execution-context.node.js +43 -0
- package/dist/lib/yaml/execution-context.node.js.map +1 -0
- package/dist/lib/yaml/execution-context.stub.js +45 -0
- package/dist/lib/yaml/execution-context.stub.js.map +1 -0
- package/dist/lib/yaml/execution-session.js +136 -0
- package/dist/lib/yaml/execution-session.js.map +1 -0
- package/dist/lib/yaml/index.js +49 -7
- package/dist/lib/yaml/legacy-yaml-execution-record.js +81 -0
- package/dist/lib/yaml/legacy-yaml-execution-record.js.map +1 -0
- package/dist/lib/yaml/legacy-yaml-report.js +86 -0
- package/dist/lib/yaml/legacy-yaml-report.js.map +1 -0
- package/dist/lib/yaml/legacy-yaml-runtime.js +156 -0
- package/dist/lib/yaml/legacy-yaml-runtime.js.map +1 -0
- package/dist/lib/yaml/player-state.js +52 -0
- package/dist/lib/yaml/player-state.js.map +1 -0
- package/dist/lib/yaml/player.js +213 -347
- package/dist/lib/yaml/player.js.map +1 -1
- package/dist/lib/yaml/report-policy.js +41 -0
- package/dist/lib/yaml/report-policy.js.map +1 -0
- package/dist/lib/yaml/test-runner-compat.js +344 -0
- package/dist/lib/yaml/test-runner-compat.js.map +1 -0
- package/dist/lib/yaml/test-runner-nodes.js +184 -0
- package/dist/lib/yaml/test-runner-nodes.js.map +1 -0
- package/dist/lib/yaml/utils.js +18 -1
- package/dist/lib/yaml/utils.js.map +1 -1
- package/dist/lib/yaml-runtime.js +67 -0
- package/dist/lib/yaml-runtime.js.map +1 -0
- package/dist/lib/yaml.js.map +1 -1
- package/dist/types/agent/agent.d.ts +14 -1
- package/dist/types/agent/agent.d.ts.map +1 -1
- package/dist/types/agent/index.d.ts +4 -3
- package/dist/types/agent/index.d.ts.map +1 -1
- package/dist/types/agent/tasks.d.ts +1 -1
- package/dist/types/agent/tasks.d.ts.map +1 -1
- package/dist/types/agent/test-runner-nodes.d.ts +226 -0
- package/dist/types/agent/test-runner-nodes.d.ts.map +1 -1
- package/dist/types/device/index.d.ts +1 -1
- package/dist/types/device/index.d.ts.map +1 -1
- package/dist/types/dump/html-utils.d.ts +4 -0
- package/dist/types/dump/html-utils.d.ts.map +1 -1
- package/dist/types/dump/screenshot-store.d.ts +2 -1
- package/dist/types/dump/screenshot-store.d.ts.map +1 -1
- package/dist/types/report-cli.d.ts.map +1 -1
- package/dist/types/report-generator.d.ts +10 -0
- package/dist/types/report-generator.d.ts.map +1 -1
- package/dist/types/report.d.ts +3 -1
- package/dist/types/report.d.ts.map +1 -1
- package/dist/types/test-run-report.d.ts +18 -2
- package/dist/types/test-run-report.d.ts.map +1 -1
- package/dist/types/test-runner/engine/case-runner.d.ts +13 -0
- package/dist/types/test-runner/engine/case-runner.d.ts.map +1 -0
- package/dist/types/test-runner/engine/document-runtime.d.ts +4 -0
- package/dist/types/test-runner/engine/document-runtime.d.ts.map +1 -0
- package/dist/types/test-runner/engine/execute-step.d.ts +17 -0
- package/dist/types/test-runner/engine/execute-step.d.ts.map +1 -0
- package/dist/types/test-runner/engine/execution-failure.d.ts +13 -0
- package/dist/types/test-runner/engine/execution-failure.d.ts.map +1 -0
- package/dist/types/test-runner/engine/index.d.ts +13 -0
- package/dist/types/test-runner/engine/index.d.ts.map +1 -0
- package/dist/types/test-runner/engine/project-runtime.d.ts +3 -0
- package/dist/types/test-runner/engine/project-runtime.d.ts.map +1 -0
- package/dist/types/test-runner/engine/registry.d.ts +12 -0
- package/dist/types/test-runner/engine/registry.d.ts.map +1 -0
- package/dist/types/test-runner/engine/resource-operations.d.ts +32 -0
- package/dist/types/test-runner/engine/resource-operations.d.ts.map +1 -0
- package/dist/types/test-runner/engine/run-collected-case.d.ts +4 -0
- package/dist/types/test-runner/engine/run-collected-case.d.ts.map +1 -0
- package/dist/types/test-runner/engine/run-workflow-document.d.ts +4 -0
- package/dist/types/test-runner/engine/run-workflow-document.d.ts.map +1 -0
- package/dist/types/test-runner/engine/scheduler.d.ts +12 -0
- package/dist/types/test-runner/engine/scheduler.d.ts.map +1 -0
- package/dist/types/test-runner/engine/scope-teardown.d.ts +3 -0
- package/dist/types/test-runner/engine/scope-teardown.d.ts.map +1 -0
- package/dist/types/test-runner/engine/types.d.ts +226 -0
- package/dist/types/test-runner/engine/types.d.ts.map +1 -0
- package/dist/types/test-runner/errors.d.ts +79 -0
- package/dist/types/test-runner/errors.d.ts.map +1 -0
- package/dist/types/test-runner/execution-record.d.ts +43 -0
- package/dist/types/test-runner/execution-record.d.ts.map +1 -0
- package/dist/types/test-runner/index.d.ts +20 -0
- package/dist/types/test-runner/index.d.ts.map +1 -0
- package/dist/types/test-runner/node/define-node.d.ts +4 -0
- package/dist/types/test-runner/node/define-node.d.ts.map +1 -0
- package/dist/types/test-runner/node/index.d.ts +3 -0
- package/dist/types/test-runner/node/index.d.ts.map +1 -0
- package/dist/types/test-runner/node/types.d.ts +66 -0
- package/dist/types/test-runner/node/types.d.ts.map +1 -0
- package/dist/types/test-runner/parser/document-format.d.ts +8 -0
- package/dist/types/test-runner/parser/document-format.d.ts.map +1 -0
- package/dist/types/test-runner/parser/normalize.d.ts +9 -0
- package/dist/types/test-runner/parser/normalize.d.ts.map +1 -0
- package/dist/types/test-runner/parser/types.d.ts +69 -0
- package/dist/types/test-runner/parser/types.d.ts.map +1 -0
- package/dist/types/test-runner/project.d.ts +75 -0
- package/dist/types/test-runner/project.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/agent-report.d.ts +9 -0
- package/dist/types/test-runner/reporting/agent-report.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/execution-record.d.ts +9 -0
- package/dist/types/test-runner/reporting/execution-record.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/merge-report.d.ts +15 -0
- package/dist/types/test-runner/reporting/merge-report.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/sanitize.d.ts +6 -0
- package/dist/types/test-runner/reporting/sanitize.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/test-run-report.d.ts +5 -0
- package/dist/types/test-runner/reporting/test-run-report.d.ts.map +1 -0
- package/dist/types/test-runner/reporting/types.d.ts +34 -0
- package/dist/types/test-runner/reporting/types.d.ts.map +1 -0
- package/dist/types/yaml/execution-context.node.d.ts +4 -0
- package/dist/types/yaml/execution-context.node.d.ts.map +1 -0
- package/dist/types/yaml/execution-context.stub.d.ts +6 -0
- package/dist/types/yaml/execution-context.stub.d.ts.map +1 -0
- package/dist/types/yaml/execution-session.d.ts +17 -0
- package/dist/types/yaml/execution-session.d.ts.map +1 -0
- package/dist/types/yaml/index.d.ts +1 -1
- package/dist/types/yaml/index.d.ts.map +1 -1
- package/dist/types/yaml/legacy-yaml-execution-record.d.ts +24 -0
- package/dist/types/yaml/legacy-yaml-execution-record.d.ts.map +1 -0
- package/dist/types/yaml/legacy-yaml-report.d.ts +19 -0
- package/dist/types/yaml/legacy-yaml-report.d.ts.map +1 -0
- package/dist/types/yaml/legacy-yaml-runtime.d.ts +23 -0
- package/dist/types/yaml/legacy-yaml-runtime.d.ts.map +1 -0
- package/dist/types/yaml/player-state.d.ts +11 -0
- package/dist/types/yaml/player-state.d.ts.map +1 -0
- package/dist/types/yaml/player.d.ts +11 -3
- package/dist/types/yaml/player.d.ts.map +1 -1
- package/dist/types/yaml/report-policy.d.ts +4 -0
- package/dist/types/yaml/report-policy.d.ts.map +1 -0
- package/dist/types/yaml/test-runner-compat.d.ts +17 -0
- package/dist/types/yaml/test-runner-compat.d.ts.map +1 -0
- package/dist/types/yaml/test-runner-nodes.d.ts +6 -0
- package/dist/types/yaml/test-runner-nodes.d.ts.map +1 -0
- package/dist/types/yaml/utils.d.ts +9 -0
- package/dist/types/yaml/utils.d.ts.map +1 -1
- package/dist/types/yaml-runtime.d.ts +13 -0
- package/dist/types/yaml-runtime.d.ts.map +1 -0
- package/dist/types/yaml.d.ts +25 -7
- package/dist/types/yaml.d.ts.map +1 -1
- package/package.json +28 -2
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { executionRecordToResult } from "../execution-record.mjs";
|
|
2
|
+
function executionRecordsToReportInput(records, options) {
|
|
3
|
+
if (0 === records.length) throw new Error('A report requires at least one execution record.');
|
|
4
|
+
const grouped = new Map();
|
|
5
|
+
for (const record of records){
|
|
6
|
+
const key = record.document.projectId;
|
|
7
|
+
const group = grouped.get(key) ?? [];
|
|
8
|
+
group.push(record);
|
|
9
|
+
grouped.set(key, group);
|
|
10
|
+
}
|
|
11
|
+
const summary = {
|
|
12
|
+
total: 0,
|
|
13
|
+
passed: 0,
|
|
14
|
+
failed: 0,
|
|
15
|
+
notRun: 0,
|
|
16
|
+
filtered: 0,
|
|
17
|
+
collectionErrors: 0,
|
|
18
|
+
documentFailures: 0,
|
|
19
|
+
projectFailures: 0
|
|
20
|
+
};
|
|
21
|
+
const projects = [];
|
|
22
|
+
for (const [projectId, group] of grouped){
|
|
23
|
+
const platform = group[0].platform;
|
|
24
|
+
const name = group[0].projectName ?? platform;
|
|
25
|
+
const latest = new Map();
|
|
26
|
+
const currentInvocation = new Map();
|
|
27
|
+
for (const record of group){
|
|
28
|
+
const documentId = record.document.documentId;
|
|
29
|
+
if (0 === record.attemptIndex || !currentInvocation.has(documentId)) currentInvocation.set(documentId, record.runId);
|
|
30
|
+
latest.set(currentInvocation.get(documentId), record);
|
|
31
|
+
}
|
|
32
|
+
for (const record of latest.values()){
|
|
33
|
+
const outcomes = record.execution?.cases;
|
|
34
|
+
summary.total += record.document.cases.length;
|
|
35
|
+
summary.passed += outcomes?.filter((item)=>'success' === item.status).length ?? 0;
|
|
36
|
+
summary.failed += outcomes?.filter((item)=>'failed' === item.status).length ?? 0;
|
|
37
|
+
summary.notRun += outcomes?.filter((item)=>'not-run' === item.status).length ?? record.document.cases.length;
|
|
38
|
+
if (void 0 !== record.setupError || void 0 !== record.executionError || void 0 !== record.reportError || (record.publicationErrors?.length ?? 0) > 0 || (record.observerErrors?.length ?? 0) > 0 || record.cleanupErrors.length || record.execution?.document.status === 'failed') summary.documentFailures += 1;
|
|
39
|
+
}
|
|
40
|
+
const results = group.map(executionRecordToResult);
|
|
41
|
+
const documents = results.map((result)=>result.document);
|
|
42
|
+
projects.push({
|
|
43
|
+
projectId,
|
|
44
|
+
name,
|
|
45
|
+
platform,
|
|
46
|
+
status: [
|
|
47
|
+
...latest.values()
|
|
48
|
+
].some((record)=>'failed' === record.status) ? 'failed' : 'success',
|
|
49
|
+
retry: Math.max(...group.map((record)=>record.attemptIndex)),
|
|
50
|
+
documents,
|
|
51
|
+
collectionErrors: [],
|
|
52
|
+
cases: results.flatMap((result)=>result.cases.map((outcome)=>({
|
|
53
|
+
...outcome,
|
|
54
|
+
documentId: result.document.documentId,
|
|
55
|
+
documentRunId: result.document.documentRunId
|
|
56
|
+
})))
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const startedAt = options.startedAt ?? records.map((record)=>record.startedAt).sort()[0];
|
|
60
|
+
const endedAt = options.endedAt ?? records.map((record)=>record.endedAt).sort().at(-1);
|
|
61
|
+
return {
|
|
62
|
+
runId: options.runId,
|
|
63
|
+
startedAt,
|
|
64
|
+
endedAt,
|
|
65
|
+
durationMs: Math.max(0, Date.parse(endedAt) - Date.parse(startedAt)),
|
|
66
|
+
status: projects.some((project)=>'failed' === project.status) ? 'failed' : 'success',
|
|
67
|
+
summary,
|
|
68
|
+
projects
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
export { executionRecordsToReportInput };
|
|
72
|
+
|
|
73
|
+
//# sourceMappingURL=execution-record.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-runner/reporting/execution-record.mjs","sources":["../../../../src/test-runner/reporting/execution-record.ts"],"sourcesContent":["import {\n type WorkflowExecutionRecord,\n executionRecordToResult,\n} from '../execution-record';\nimport type { RunReportInput, RunReportProjectInput } from './types';\n\n/** Project complete invocations without changing their legacy/API state. */\nexport function executionRecordsToReportInput(\n records: readonly WorkflowExecutionRecord[],\n options: { runId: string; startedAt?: string; endedAt?: string },\n): RunReportInput {\n if (records.length === 0)\n throw new Error('A report requires at least one execution record.');\n const grouped = new Map<string, WorkflowExecutionRecord[]>();\n for (const record of records) {\n const key = record.document.projectId;\n const group = grouped.get(key) ?? [];\n group.push(record);\n grouped.set(key, group);\n }\n const summary = {\n total: 0,\n passed: 0,\n failed: 0,\n notRun: 0,\n filtered: 0,\n collectionErrors: 0,\n documentFailures: 0,\n projectFailures: 0,\n };\n const projects: RunReportProjectInput[] = [];\n for (const [projectId, group] of grouped) {\n const platform = group[0].platform;\n const name = group[0].projectName ?? platform;\n const latest = new Map<string, WorkflowExecutionRecord>();\n const currentInvocation = new Map<string, string>();\n for (const record of group) {\n const documentId = record.document.documentId;\n if (record.attemptIndex === 0 || !currentInvocation.has(documentId))\n currentInvocation.set(documentId, record.runId);\n latest.set(currentInvocation.get(documentId)!, record);\n }\n for (const record of latest.values()) {\n const outcomes = record.execution?.cases;\n summary.total += record.document.cases.length;\n summary.passed +=\n outcomes?.filter((item) => item.status === 'success').length ?? 0;\n summary.failed +=\n outcomes?.filter((item) => item.status === 'failed').length ?? 0;\n summary.notRun +=\n outcomes?.filter((item) => item.status === 'not-run').length ??\n record.document.cases.length;\n if (\n record.setupError !== undefined ||\n record.executionError !== undefined ||\n record.reportError !== undefined ||\n (record.publicationErrors?.length ?? 0) > 0 ||\n (record.observerErrors?.length ?? 0) > 0 ||\n record.cleanupErrors.length ||\n record.execution?.document.status === 'failed'\n )\n summary.documentFailures += 1;\n }\n const results = group.map(executionRecordToResult);\n const documents = results.map((result) => result.document);\n projects.push({\n projectId,\n name,\n platform,\n status: [...latest.values()].some((record) => record.status === 'failed')\n ? 'failed'\n : 'success',\n retry: Math.max(...group.map((record) => record.attemptIndex)),\n documents,\n collectionErrors: [],\n cases: results.flatMap((result) =>\n result.cases.map((outcome) => ({\n ...outcome,\n documentId: result.document.documentId,\n documentRunId: result.document.documentRunId,\n })),\n ),\n });\n }\n const startedAt =\n options.startedAt ?? records.map((record) => record.startedAt).sort()[0];\n const endedAt =\n options.endedAt ??\n records\n .map((record) => record.endedAt)\n .sort()\n .at(-1)!;\n return {\n runId: options.runId,\n startedAt,\n endedAt,\n durationMs: Math.max(0, Date.parse(endedAt) - Date.parse(startedAt)),\n status: projects.some((project) => project.status === 'failed')\n ? 'failed'\n : 'success',\n summary,\n projects,\n };\n}\n"],"names":["executionRecordsToReportInput","records","options","Error","grouped","Map","record","key","group","summary","projects","projectId","platform","name","latest","currentInvocation","documentId","outcomes","item","undefined","results","executionRecordToResult","documents","result","Math","outcome","startedAt","endedAt","Date","project"],"mappings":"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"}
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
function agentReportAsTestRun(source) {
|
|
2
|
+
const { attributes, reportId } = source;
|
|
3
|
+
const status = 'skipped' === attributes.testStatus ? 'not-run' : 'passed' === attributes.testStatus ? 'success' : 'failed';
|
|
4
|
+
const failed = 'failed' === status;
|
|
5
|
+
return {
|
|
6
|
+
status: failed ? 'failed' : 'success',
|
|
7
|
+
...source.timing,
|
|
8
|
+
summary: {
|
|
9
|
+
total: 1,
|
|
10
|
+
passed: 'success' === status ? 1 : 0,
|
|
11
|
+
failed: failed ? 1 : 0,
|
|
12
|
+
notRun: 'not-run' === status ? 1 : 0,
|
|
13
|
+
filtered: 0,
|
|
14
|
+
collectionErrors: 0,
|
|
15
|
+
documentFailures: 0,
|
|
16
|
+
projectFailures: 0
|
|
17
|
+
},
|
|
18
|
+
projects: [
|
|
19
|
+
{
|
|
20
|
+
projectId: 'agent-report',
|
|
21
|
+
name: attributes.testTitle,
|
|
22
|
+
platform: 'agent',
|
|
23
|
+
status: failed ? 'failed' : 'success',
|
|
24
|
+
retry: 0,
|
|
25
|
+
collectionErrors: [],
|
|
26
|
+
documents: [
|
|
27
|
+
{
|
|
28
|
+
documentId: 'agent-report',
|
|
29
|
+
sourcePath: source.sourcePath,
|
|
30
|
+
status: failed ? 'failed' : 'success',
|
|
31
|
+
beforeAll: [],
|
|
32
|
+
afterAll: [],
|
|
33
|
+
scopeReportIds: [
|
|
34
|
+
reportId
|
|
35
|
+
],
|
|
36
|
+
cases: [
|
|
37
|
+
{
|
|
38
|
+
caseId: attributes.testId,
|
|
39
|
+
name: attributes.testTitle,
|
|
40
|
+
caseIndex: 0,
|
|
41
|
+
status,
|
|
42
|
+
attempts: 'not-run' !== status && source.timing ? [
|
|
43
|
+
{
|
|
44
|
+
attemptId: 'agent-report',
|
|
45
|
+
attemptIndex: 0,
|
|
46
|
+
status,
|
|
47
|
+
...source.timing,
|
|
48
|
+
beforeEach: [],
|
|
49
|
+
steps: [],
|
|
50
|
+
afterEach: [],
|
|
51
|
+
scopeReportIds: [
|
|
52
|
+
reportId
|
|
53
|
+
]
|
|
54
|
+
}
|
|
55
|
+
] : []
|
|
56
|
+
}
|
|
57
|
+
]
|
|
58
|
+
}
|
|
59
|
+
]
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function retainHostFailure(source) {
|
|
65
|
+
const dump = source.dump;
|
|
66
|
+
if (!dump || 'failed' === dump.status || ![
|
|
67
|
+
'failed',
|
|
68
|
+
'timedOut',
|
|
69
|
+
'interrupted'
|
|
70
|
+
].includes(source.attributes.testStatus)) return dump;
|
|
71
|
+
const message = `Host test "${source.attributes.testTitle}" finished with status "${source.attributes.testStatus}".`;
|
|
72
|
+
const projectIndex = dump.projects.length - 1;
|
|
73
|
+
const project = dump.projects[projectIndex];
|
|
74
|
+
const documentIndex = (project?.documents.length ?? 0) - 1;
|
|
75
|
+
const document = project?.documents[documentIndex];
|
|
76
|
+
const afterHost = (value)=>({
|
|
77
|
+
...value,
|
|
78
|
+
status: 'failed',
|
|
79
|
+
afterAll: [
|
|
80
|
+
...value.afterAll,
|
|
81
|
+
{
|
|
82
|
+
id: `${value.documentRunId ?? value.documentId}:host-result`,
|
|
83
|
+
phase: 'afterAll',
|
|
84
|
+
stepIndex: value.afterAll.length,
|
|
85
|
+
node: 'runner:host-result',
|
|
86
|
+
status: 'failed',
|
|
87
|
+
continuedAfterError: false,
|
|
88
|
+
startedAt: value.endedAt ?? dump.endedAt,
|
|
89
|
+
endedAt: value.endedAt ?? dump.endedAt,
|
|
90
|
+
durationMs: 0,
|
|
91
|
+
error: {
|
|
92
|
+
name: 'HostTestError',
|
|
93
|
+
message
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
});
|
|
98
|
+
return {
|
|
99
|
+
...dump,
|
|
100
|
+
status: 'failed',
|
|
101
|
+
summary: {
|
|
102
|
+
...dump.summary,
|
|
103
|
+
...document ? {
|
|
104
|
+
documentFailures: dump.summary.documentFailures + 1
|
|
105
|
+
} : {
|
|
106
|
+
projectFailures: dump.summary.projectFailures + 1
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
projects: dump.projects.map((item, index)=>index !== projectIndex ? item : {
|
|
110
|
+
...item,
|
|
111
|
+
status: 'failed',
|
|
112
|
+
documents: item.documents.map((value, index)=>index !== documentIndex ? value : {
|
|
113
|
+
...afterHost(value),
|
|
114
|
+
...value.attempts ? {
|
|
115
|
+
attempts: value.attempts.map((attempt, index)=>index === value.attempts.length - 1 ? afterHost(attempt) : attempt)
|
|
116
|
+
} : {}
|
|
117
|
+
})
|
|
118
|
+
}),
|
|
119
|
+
diagnostics: [
|
|
120
|
+
...dump.diagnostics ?? [],
|
|
121
|
+
{
|
|
122
|
+
level: 'error',
|
|
123
|
+
code: 'run-infrastructure-error',
|
|
124
|
+
message
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
function mergeTestRunReportDumps(sources, metrics, runId) {
|
|
130
|
+
const dumps = sources.map((source)=>{
|
|
131
|
+
if (source.dump && (!Number.isFinite(Date.parse(source.dump.startedAt)) || !Number.isFinite(Date.parse(source.dump.endedAt)) || Date.parse(source.dump.endedAt) < Date.parse(source.dump.startedAt))) throw new Error(`Invalid execution timestamps in Midscene Test report: ${source.sourcePath}`);
|
|
132
|
+
const dump = retainHostFailure(source) ?? agentReportAsTestRun(source);
|
|
133
|
+
const id = (value)=>`${source.reportId}:${value}`;
|
|
134
|
+
const scopeReports = (reportIds)=>reportIds?.length ? [
|
|
135
|
+
source.reportId
|
|
136
|
+
] : reportIds;
|
|
137
|
+
const step = (value)=>({
|
|
138
|
+
...value,
|
|
139
|
+
id: id(value.id),
|
|
140
|
+
...value.agentDetails ? {
|
|
141
|
+
agentDetails: value.agentDetails.map((detail)=>({
|
|
142
|
+
...detail,
|
|
143
|
+
reportId: source.reportId
|
|
144
|
+
}))
|
|
145
|
+
} : {}
|
|
146
|
+
});
|
|
147
|
+
const documentScope = (value)=>({
|
|
148
|
+
...value,
|
|
149
|
+
documentId: id(value.documentId),
|
|
150
|
+
...value.logicalDocumentId ? {
|
|
151
|
+
logicalDocumentId: id(value.logicalDocumentId)
|
|
152
|
+
} : {},
|
|
153
|
+
...value.documentRunId ? {
|
|
154
|
+
documentRunId: id(value.documentRunId)
|
|
155
|
+
} : {},
|
|
156
|
+
beforeAll: value.beforeAll.map(step),
|
|
157
|
+
afterAll: value.afterAll.map(step),
|
|
158
|
+
scopeReportIds: scopeReports(value.scopeReportIds)
|
|
159
|
+
});
|
|
160
|
+
return {
|
|
161
|
+
...dump,
|
|
162
|
+
projects: dump.projects.map((project)=>({
|
|
163
|
+
...project,
|
|
164
|
+
projectId: id(project.projectId),
|
|
165
|
+
documents: project.documents.map((document)=>({
|
|
166
|
+
...documentScope(document),
|
|
167
|
+
...document.attempts ? {
|
|
168
|
+
attempts: document.attempts.map(documentScope)
|
|
169
|
+
} : {},
|
|
170
|
+
cases: document.cases.map((testCase)=>({
|
|
171
|
+
...testCase,
|
|
172
|
+
caseId: id(testCase.caseId),
|
|
173
|
+
attempts: testCase.attempts.map((attempt)=>({
|
|
174
|
+
...attempt,
|
|
175
|
+
attemptId: id(attempt.attemptId),
|
|
176
|
+
beforeEach: attempt.beforeEach.map(step),
|
|
177
|
+
steps: attempt.steps.map(step),
|
|
178
|
+
afterEach: attempt.afterEach.map(step),
|
|
179
|
+
scopeReportIds: scopeReports(attempt.scopeReportIds)
|
|
180
|
+
}))
|
|
181
|
+
}))
|
|
182
|
+
}))
|
|
183
|
+
})),
|
|
184
|
+
diagnostics: dump.diagnostics?.map((diagnostic)=>({
|
|
185
|
+
...diagnostic,
|
|
186
|
+
...diagnostic.scopeId ? {
|
|
187
|
+
scopeId: id(diagnostic.scopeId)
|
|
188
|
+
} : {}
|
|
189
|
+
}))
|
|
190
|
+
};
|
|
191
|
+
});
|
|
192
|
+
const recordedTimes = dumps.flatMap((dump)=>dump.startedAt && dump.endedAt ? [
|
|
193
|
+
{
|
|
194
|
+
start: Date.parse(dump.startedAt),
|
|
195
|
+
end: Date.parse(dump.endedAt)
|
|
196
|
+
}
|
|
197
|
+
] : []);
|
|
198
|
+
if (!recordedTimes.length) throw new Error('Cannot merge Midscene Test reports without recorded execution timestamps.');
|
|
199
|
+
const startedAt = new Date(Math.min(...recordedTimes.map((time)=>time.start))).toISOString();
|
|
200
|
+
const endedAt = new Date(Math.max(...recordedTimes.map((time)=>time.end))).toISOString();
|
|
201
|
+
const summary = {
|
|
202
|
+
total: 0,
|
|
203
|
+
passed: 0,
|
|
204
|
+
failed: 0,
|
|
205
|
+
notRun: 0,
|
|
206
|
+
filtered: 0,
|
|
207
|
+
collectionErrors: 0,
|
|
208
|
+
documentFailures: 0,
|
|
209
|
+
projectFailures: 0
|
|
210
|
+
};
|
|
211
|
+
for (const dump of dumps)for (const key of Object.keys(summary))summary[key] += dump.summary[key];
|
|
212
|
+
const diagnostics = dumps.flatMap((dump)=>dump.diagnostics ?? []);
|
|
213
|
+
return {
|
|
214
|
+
schemaVersion: 1,
|
|
215
|
+
kind: 'test-runner',
|
|
216
|
+
runId,
|
|
217
|
+
status: dumps.some((dump)=>'failed' === dump.status) ? 'failed' : 'success',
|
|
218
|
+
startedAt,
|
|
219
|
+
endedAt,
|
|
220
|
+
durationMs: Math.max(0, Date.parse(endedAt) - Date.parse(startedAt)),
|
|
221
|
+
summary,
|
|
222
|
+
metrics,
|
|
223
|
+
projects: dumps.flatMap((dump)=>dump.projects),
|
|
224
|
+
...diagnostics.length ? {
|
|
225
|
+
diagnostics
|
|
226
|
+
} : {}
|
|
227
|
+
};
|
|
228
|
+
}
|
|
229
|
+
export { mergeTestRunReportDumps };
|
|
230
|
+
|
|
231
|
+
//# sourceMappingURL=merge-report.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-runner/reporting/merge-report.mjs","sources":["../../../../src/test-runner/reporting/merge-report.ts"],"sourcesContent":["import type {\n TestRunReportDocument,\n TestRunReportDump,\n TestRunReportMetrics,\n TestRunReportStep,\n} from '../../test-run-report';\nimport type { ReportFileAttributes } from '../../types';\n\nexport interface MergeTestRunReportSource {\n reportId: string;\n sourcePath: string;\n attributes: ReportFileAttributes;\n dump?: TestRunReportDump;\n timing?: { startedAt: string; endedAt: string; durationMs: number };\n}\n\nfunction agentReportAsTestRun(\n source: MergeTestRunReportSource,\n): Pick<TestRunReportDump, 'status' | 'summary' | 'projects'> &\n Partial<Pick<TestRunReportDump, 'startedAt' | 'endedAt' | 'diagnostics'>> {\n const { attributes, reportId } = source;\n const status =\n attributes.testStatus === 'skipped'\n ? 'not-run'\n : attributes.testStatus === 'passed'\n ? 'success'\n : 'failed';\n const failed = status === 'failed';\n return {\n status: failed ? 'failed' : 'success',\n ...source.timing,\n summary: {\n total: 1,\n passed: status === 'success' ? 1 : 0,\n failed: failed ? 1 : 0,\n notRun: status === 'not-run' ? 1 : 0,\n filtered: 0,\n collectionErrors: 0,\n documentFailures: 0,\n projectFailures: 0,\n },\n projects: [\n {\n projectId: 'agent-report',\n name: attributes.testTitle,\n platform: 'agent',\n status: failed ? 'failed' : 'success',\n retry: 0,\n collectionErrors: [],\n documents: [\n {\n documentId: 'agent-report',\n sourcePath: source.sourcePath,\n status: failed ? 'failed' : 'success',\n beforeAll: [],\n afterAll: [],\n scopeReportIds: [reportId],\n cases: [\n {\n caseId: attributes.testId,\n name: attributes.testTitle,\n caseIndex: 0,\n status,\n attempts:\n status === 'not-run' || !source.timing\n ? []\n : [\n {\n attemptId: 'agent-report',\n attemptIndex: 0,\n status,\n ...source.timing,\n beforeEach: [],\n steps: [],\n afterEach: [],\n scopeReportIds: [reportId],\n },\n ],\n },\n ],\n },\n ],\n },\n ],\n };\n}\n\nfunction retainHostFailure(\n source: MergeTestRunReportSource,\n): TestRunReportDump | undefined {\n const dump = source.dump;\n if (\n !dump ||\n dump.status === 'failed' ||\n !['failed', 'timedOut', 'interrupted'].includes(\n source.attributes.testStatus,\n )\n )\n return dump;\n\n const message = `Host test \"${source.attributes.testTitle}\" finished with status \"${source.attributes.testStatus}\".`;\n const projectIndex = dump.projects.length - 1;\n const project = dump.projects[projectIndex];\n const documentIndex = (project?.documents.length ?? 0) - 1;\n const document = project?.documents[documentIndex];\n const afterHost = <\n T extends Omit<TestRunReportDocument, 'cases' | 'attempts'>,\n >(\n value: T,\n ): T => ({\n ...value,\n status: 'failed',\n afterAll: [\n ...value.afterAll,\n {\n id: `${value.documentRunId ?? value.documentId}:host-result`,\n phase: 'afterAll',\n stepIndex: value.afterAll.length,\n node: 'runner:host-result',\n status: 'failed',\n continuedAfterError: false,\n startedAt: value.endedAt ?? dump.endedAt,\n endedAt: value.endedAt ?? dump.endedAt,\n durationMs: 0,\n error: { name: 'HostTestError', message },\n },\n ],\n });\n // The enclosing test can fail after YAML succeeds. Record that outcome at\n // the file boundary instead of changing successful actions or Case attempts.\n return {\n ...dump,\n status: 'failed',\n summary: {\n ...dump.summary,\n ...(document\n ? { documentFailures: dump.summary.documentFailures + 1 }\n : { projectFailures: dump.summary.projectFailures + 1 }),\n },\n projects: dump.projects.map((item, index) =>\n index !== projectIndex\n ? item\n : {\n ...item,\n status: 'failed',\n documents: item.documents.map((value, index) =>\n index !== documentIndex\n ? value\n : {\n ...afterHost(value),\n ...(value.attempts\n ? {\n attempts: value.attempts.map((attempt, index) =>\n index === value.attempts!.length - 1\n ? afterHost(attempt)\n : attempt,\n ),\n }\n : {}),\n },\n ),\n },\n ),\n diagnostics: [\n ...(dump.diagnostics ?? []),\n {\n level: 'error',\n code: 'run-infrastructure-error',\n message,\n },\n ],\n };\n}\n\nexport function mergeTestRunReportDumps(\n sources: readonly MergeTestRunReportSource[],\n metrics: TestRunReportMetrics,\n runId: string,\n): TestRunReportDump {\n const dumps = sources.map((source) => {\n if (\n source.dump &&\n (!Number.isFinite(Date.parse(source.dump.startedAt)) ||\n !Number.isFinite(Date.parse(source.dump.endedAt)) ||\n Date.parse(source.dump.endedAt) < Date.parse(source.dump.startedAt))\n )\n throw new Error(\n `Invalid execution timestamps in Midscene Test report: ${source.sourcePath}`,\n );\n const dump = retainHostFailure(source) ?? agentReportAsTestRun(source);\n const id = (value: string) => `${source.reportId}:${value}`;\n const scopeReports = (reportIds: string[] | undefined) =>\n reportIds?.length ? [source.reportId] : reportIds;\n const step = (value: TestRunReportStep): TestRunReportStep => ({\n ...value,\n id: id(value.id),\n ...(value.agentDetails\n ? {\n agentDetails: value.agentDetails.map((detail) => ({\n ...detail,\n reportId: source.reportId,\n })),\n }\n : {}),\n });\n const documentScope = <\n T extends Omit<TestRunReportDocument, 'cases' | 'attempts'>,\n >(\n value: T,\n ): T => ({\n ...value,\n documentId: id(value.documentId),\n ...(value.logicalDocumentId\n ? { logicalDocumentId: id(value.logicalDocumentId) }\n : {}),\n ...(value.documentRunId\n ? { documentRunId: id(value.documentRunId) }\n : {}),\n beforeAll: value.beforeAll.map(step),\n afterAll: value.afterAll.map(step),\n scopeReportIds: scopeReports(value.scopeReportIds),\n });\n // A file can already contain a merged run. Namespace hierarchy IDs while\n // keeping execution IDs stable: the latter also deduplicate shared snapshots.\n return {\n ...dump,\n projects: dump.projects.map((project) => ({\n ...project,\n projectId: id(project.projectId),\n documents: project.documents.map((document) => ({\n ...documentScope(document),\n ...(document.attempts\n ? { attempts: document.attempts.map(documentScope) }\n : {}),\n cases: document.cases.map((testCase) => ({\n ...testCase,\n caseId: id(testCase.caseId),\n attempts: testCase.attempts.map((attempt) => ({\n ...attempt,\n attemptId: id(attempt.attemptId),\n beforeEach: attempt.beforeEach.map(step),\n steps: attempt.steps.map(step),\n afterEach: attempt.afterEach.map(step),\n scopeReportIds: scopeReports(attempt.scopeReportIds),\n })),\n })),\n })),\n })),\n diagnostics: dump.diagnostics?.map((diagnostic) => ({\n ...diagnostic,\n ...(diagnostic.scopeId ? { scopeId: id(diagnostic.scopeId) } : {}),\n })),\n };\n });\n // A report without recorded timestamps contributes outcomes, not the time\n // at which an unrelated merge command happened to run.\n const recordedTimes = dumps.flatMap((dump) =>\n dump.startedAt && dump.endedAt\n ? [{ start: Date.parse(dump.startedAt), end: Date.parse(dump.endedAt) }]\n : [],\n );\n if (!recordedTimes.length)\n throw new Error(\n 'Cannot merge Midscene Test reports without recorded execution timestamps.',\n );\n const startedAt = new Date(\n Math.min(...recordedTimes.map((time) => time.start)),\n ).toISOString();\n const endedAt = new Date(\n Math.max(...recordedTimes.map((time) => time.end)),\n ).toISOString();\n const summary = {\n total: 0,\n passed: 0,\n failed: 0,\n notRun: 0,\n filtered: 0,\n collectionErrors: 0,\n documentFailures: 0,\n projectFailures: 0,\n };\n for (const dump of dumps) {\n for (const key of Object.keys(summary) as (keyof typeof summary)[])\n summary[key] += dump.summary[key];\n }\n const diagnostics = dumps.flatMap((dump) => dump.diagnostics ?? []);\n return {\n schemaVersion: 1,\n kind: 'test-runner',\n runId,\n status: dumps.some((dump) => dump.status === 'failed')\n ? 'failed'\n : 'success',\n startedAt,\n endedAt,\n durationMs: Math.max(0, Date.parse(endedAt) - Date.parse(startedAt)),\n summary,\n metrics,\n projects: dumps.flatMap((dump) => dump.projects),\n ...(diagnostics.length ? { diagnostics } : {}),\n };\n}\n"],"names":["agentReportAsTestRun","source","attributes","reportId","status","failed","retainHostFailure","dump","message","projectIndex","project","documentIndex","document","afterHost","value","item","index","attempt","mergeTestRunReportDumps","sources","metrics","runId","dumps","Number","Date","Error","id","scopeReports","reportIds","step","detail","documentScope","testCase","diagnostic","recordedTimes","startedAt","Math","time","endedAt","summary","key","Object","diagnostics"],"mappings":"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"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
const MAX_DEPTH = 6;
|
|
2
|
+
const MAX_STRING_LENGTH = 2000;
|
|
3
|
+
const MAX_ARRAY_ITEMS = 50;
|
|
4
|
+
const MAX_OBJECT_KEYS = 50;
|
|
5
|
+
const MAX_SERIALIZED_BYTES = 65536;
|
|
6
|
+
const TRUNCATED_VALUE = '[Truncated: display value exceeded 64 KiB]';
|
|
7
|
+
const normalizedSecretKeys = [
|
|
8
|
+
'password',
|
|
9
|
+
'passwd',
|
|
10
|
+
'secret',
|
|
11
|
+
'token',
|
|
12
|
+
'authorization',
|
|
13
|
+
'cookie',
|
|
14
|
+
'apikey'
|
|
15
|
+
];
|
|
16
|
+
const normalizedKey = (key)=>key.toLowerCase().replace(/[^a-z0-9]/g, '');
|
|
17
|
+
const isSecretKey = (key)=>{
|
|
18
|
+
const normalized = normalizedKey(key);
|
|
19
|
+
return normalizedSecretKeys.some((secret)=>normalized.includes(secret));
|
|
20
|
+
};
|
|
21
|
+
const objectPath = (parent, key)=>/^[A-Za-z_$][\w$]*$/.test(key) ? `${parent}.${key}` : `${parent}[${JSON.stringify(key)}]`;
|
|
22
|
+
const stringByteLength = (value)=>new TextEncoder().encode(value).byteLength;
|
|
23
|
+
function sanitizeReportText(value, maximumLength = MAX_STRING_LENGTH) {
|
|
24
|
+
return value.length <= maximumLength ? value : `${value.slice(0, maximumLength)}… [truncated]`;
|
|
25
|
+
}
|
|
26
|
+
function sanitizeReportValue(value) {
|
|
27
|
+
const truncatedPaths = new Set();
|
|
28
|
+
const redactedPaths = new Set();
|
|
29
|
+
const ancestors = new WeakSet();
|
|
30
|
+
let remainingBytes = MAX_SERIALIZED_BYTES;
|
|
31
|
+
const consume = (path, text)=>{
|
|
32
|
+
remainingBytes -= stringByteLength(text);
|
|
33
|
+
if (remainingBytes >= 0) return true;
|
|
34
|
+
truncatedPaths.add(path);
|
|
35
|
+
return false;
|
|
36
|
+
};
|
|
37
|
+
const visit = (current, path, depth)=>{
|
|
38
|
+
if (remainingBytes <= 0) {
|
|
39
|
+
truncatedPaths.add(path);
|
|
40
|
+
return '[Truncated: size limit]';
|
|
41
|
+
}
|
|
42
|
+
if (null === current || 'boolean' == typeof current) {
|
|
43
|
+
consume(path, String(current));
|
|
44
|
+
return current;
|
|
45
|
+
}
|
|
46
|
+
if ('string' == typeof current) {
|
|
47
|
+
if (current.length > MAX_STRING_LENGTH) {
|
|
48
|
+
truncatedPaths.add(path);
|
|
49
|
+
const shortened = `${current.slice(0, MAX_STRING_LENGTH)}… [truncated]`;
|
|
50
|
+
consume(path, shortened);
|
|
51
|
+
return shortened;
|
|
52
|
+
}
|
|
53
|
+
consume(path, current);
|
|
54
|
+
return current;
|
|
55
|
+
}
|
|
56
|
+
if ('number' == typeof current) {
|
|
57
|
+
if (Number.isFinite(current)) {
|
|
58
|
+
consume(path, String(current));
|
|
59
|
+
return current;
|
|
60
|
+
}
|
|
61
|
+
truncatedPaths.add(path);
|
|
62
|
+
return `[Unsupported number: ${String(current)}]`;
|
|
63
|
+
}
|
|
64
|
+
if ('bigint' == typeof current) {
|
|
65
|
+
truncatedPaths.add(path);
|
|
66
|
+
return `[Unsupported bigint: ${String(current)}]`;
|
|
67
|
+
}
|
|
68
|
+
if (void 0 === current || 'function' == typeof current || 'symbol' == typeof current) {
|
|
69
|
+
truncatedPaths.add(path);
|
|
70
|
+
return `[Unsupported ${typeof current}]`;
|
|
71
|
+
}
|
|
72
|
+
if (depth >= MAX_DEPTH) {
|
|
73
|
+
truncatedPaths.add(path);
|
|
74
|
+
return '[Truncated: maximum depth]';
|
|
75
|
+
}
|
|
76
|
+
if (current instanceof Date) {
|
|
77
|
+
const date = Number.isNaN(current.getTime()) ? '[Invalid Date]' : current.toISOString();
|
|
78
|
+
consume(path, date);
|
|
79
|
+
return date;
|
|
80
|
+
}
|
|
81
|
+
if (ancestors.has(current)) {
|
|
82
|
+
truncatedPaths.add(path);
|
|
83
|
+
return '[Unsupported circular value]';
|
|
84
|
+
}
|
|
85
|
+
if (Array.isArray(current)) {
|
|
86
|
+
ancestors.add(current);
|
|
87
|
+
const output = [];
|
|
88
|
+
const itemCount = Math.min(current.length, MAX_ARRAY_ITEMS);
|
|
89
|
+
for(let index = 0; index < itemCount; index += 1){
|
|
90
|
+
if (remainingBytes <= 0) break;
|
|
91
|
+
output.push(visit(current[index], `${path}[${index}]`, depth + 1));
|
|
92
|
+
}
|
|
93
|
+
if (current.length > output.length) truncatedPaths.add(path);
|
|
94
|
+
ancestors.delete(current);
|
|
95
|
+
return output;
|
|
96
|
+
}
|
|
97
|
+
const prototype = Object.getPrototypeOf(current);
|
|
98
|
+
if (prototype !== Object.prototype && null !== prototype) {
|
|
99
|
+
truncatedPaths.add(path);
|
|
100
|
+
const constructorName = current.constructor?.name ?? 'object';
|
|
101
|
+
return `[Unsupported object: ${constructorName}]`;
|
|
102
|
+
}
|
|
103
|
+
ancestors.add(current);
|
|
104
|
+
const output = {};
|
|
105
|
+
const keys = Object.keys(current);
|
|
106
|
+
for (const key of keys.slice(0, MAX_OBJECT_KEYS)){
|
|
107
|
+
const childPath = objectPath(path, key);
|
|
108
|
+
if (!consume(childPath, key)) break;
|
|
109
|
+
if (isSecretKey(key)) {
|
|
110
|
+
output[key] = '[REDACTED]';
|
|
111
|
+
redactedPaths.add(childPath);
|
|
112
|
+
} else output[key] = visit(current[key], childPath, depth + 1);
|
|
113
|
+
if (remainingBytes <= 0) break;
|
|
114
|
+
}
|
|
115
|
+
if (keys.length > Object.keys(output).length) truncatedPaths.add(path);
|
|
116
|
+
ancestors.delete(current);
|
|
117
|
+
return output;
|
|
118
|
+
};
|
|
119
|
+
const sanitized = visit(value, '$', 0);
|
|
120
|
+
const serialized = JSON.stringify(sanitized);
|
|
121
|
+
if (void 0 === serialized || stringByteLength(serialized) > MAX_SERIALIZED_BYTES) return {
|
|
122
|
+
value: TRUNCATED_VALUE,
|
|
123
|
+
truncatedPaths: [
|
|
124
|
+
'$'
|
|
125
|
+
]
|
|
126
|
+
};
|
|
127
|
+
return {
|
|
128
|
+
value: sanitized,
|
|
129
|
+
...truncatedPaths.size > 0 ? {
|
|
130
|
+
truncatedPaths: [
|
|
131
|
+
...truncatedPaths
|
|
132
|
+
]
|
|
133
|
+
} : {},
|
|
134
|
+
...redactedPaths.size > 0 ? {
|
|
135
|
+
redactedPaths: [
|
|
136
|
+
...redactedPaths
|
|
137
|
+
]
|
|
138
|
+
} : {}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function sanitizeReportError(error) {
|
|
142
|
+
if (error instanceof Error || 'object' == typeof error && null !== error && 'string' == typeof error.message) {
|
|
143
|
+
const workflowLike = error;
|
|
144
|
+
return {
|
|
145
|
+
name: sanitizeReportText(workflowLike.name || 'Error', 200),
|
|
146
|
+
message: sanitizeReportText(workflowLike.message || String(error)),
|
|
147
|
+
...'string' == typeof workflowLike.code ? {
|
|
148
|
+
code: sanitizeReportText(workflowLike.code, 200)
|
|
149
|
+
} : {},
|
|
150
|
+
...void 0 === workflowLike.details ? {} : {
|
|
151
|
+
details: sanitizeReportValue(workflowLike.details)
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
name: 'Error',
|
|
157
|
+
message: sanitizeReportText(String(error))
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
export { sanitizeReportError, sanitizeReportText, sanitizeReportValue };
|
|
161
|
+
|
|
162
|
+
//# sourceMappingURL=sanitize.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"test-runner/reporting/sanitize.mjs","sources":["../../../../src/test-runner/reporting/sanitize.ts"],"sourcesContent":["import type {\n TestRunReportError,\n TestRunReportValue,\n} from '../../test-run-report';\n\nconst MAX_DEPTH = 6;\nconst MAX_STRING_LENGTH = 2_000;\nconst MAX_ARRAY_ITEMS = 50;\nconst MAX_OBJECT_KEYS = 50;\nconst MAX_SERIALIZED_BYTES = 64 * 1024;\nconst TRUNCATED_VALUE = '[Truncated: display value exceeded 64 KiB]';\n\nconst normalizedSecretKeys = [\n 'password',\n 'passwd',\n 'secret',\n 'token',\n 'authorization',\n 'cookie',\n 'apikey',\n];\n\nconst normalizedKey = (key: string): string =>\n key.toLowerCase().replace(/[^a-z0-9]/g, '');\n\nconst isSecretKey = (key: string): boolean => {\n const normalized = normalizedKey(key);\n return normalizedSecretKeys.some((secret) => normalized.includes(secret));\n};\n\nconst objectPath = (parent: string, key: string): string =>\n /^[A-Za-z_$][\\w$]*$/.test(key)\n ? `${parent}.${key}`\n : `${parent}[${JSON.stringify(key)}]`;\n\nconst stringByteLength = (value: string): number =>\n new TextEncoder().encode(value).byteLength;\n\nexport function sanitizeReportText(\n value: string,\n maximumLength = MAX_STRING_LENGTH,\n): string {\n return value.length <= maximumLength\n ? value\n : `${value.slice(0, maximumLength)}… [truncated]`;\n}\n\n/** Convert arbitrary Node input/output to a bounded, JSON-safe display value. */\nexport function sanitizeReportValue(value: unknown): TestRunReportValue {\n const truncatedPaths = new Set<string>();\n const redactedPaths = new Set<string>();\n const ancestors = new WeakSet<object>();\n let remainingBytes = MAX_SERIALIZED_BYTES;\n\n const consume = (path: string, text: string): boolean => {\n remainingBytes -= stringByteLength(text);\n if (remainingBytes >= 0) return true;\n truncatedPaths.add(path);\n return false;\n };\n\n const visit = (current: unknown, path: string, depth: number): unknown => {\n if (remainingBytes <= 0) {\n truncatedPaths.add(path);\n return '[Truncated: size limit]';\n }\n if (current === null || typeof current === 'boolean') {\n consume(path, String(current));\n return current;\n }\n if (typeof current === 'string') {\n if (current.length > MAX_STRING_LENGTH) {\n truncatedPaths.add(path);\n const shortened = `${current.slice(0, MAX_STRING_LENGTH)}… [truncated]`;\n consume(path, shortened);\n return shortened;\n }\n consume(path, current);\n return current;\n }\n if (typeof current === 'number') {\n if (Number.isFinite(current)) {\n consume(path, String(current));\n return current;\n }\n truncatedPaths.add(path);\n return `[Unsupported number: ${String(current)}]`;\n }\n if (typeof current === 'bigint') {\n truncatedPaths.add(path);\n return `[Unsupported bigint: ${String(current)}]`;\n }\n if (\n current === undefined ||\n typeof current === 'function' ||\n typeof current === 'symbol'\n ) {\n truncatedPaths.add(path);\n return `[Unsupported ${typeof current}]`;\n }\n if (depth >= MAX_DEPTH) {\n truncatedPaths.add(path);\n return '[Truncated: maximum depth]';\n }\n\n if (current instanceof Date) {\n const date = Number.isNaN(current.getTime())\n ? '[Invalid Date]'\n : current.toISOString();\n consume(path, date);\n return date;\n }\n if (ancestors.has(current)) {\n truncatedPaths.add(path);\n return '[Unsupported circular value]';\n }\n\n if (Array.isArray(current)) {\n ancestors.add(current);\n const output: unknown[] = [];\n const itemCount = Math.min(current.length, MAX_ARRAY_ITEMS);\n for (let index = 0; index < itemCount; index += 1) {\n if (remainingBytes <= 0) break;\n output.push(visit(current[index], `${path}[${index}]`, depth + 1));\n }\n if (current.length > output.length) truncatedPaths.add(path);\n ancestors.delete(current);\n return output;\n }\n\n const prototype = Object.getPrototypeOf(current);\n if (prototype !== Object.prototype && prototype !== null) {\n truncatedPaths.add(path);\n const constructorName =\n (current as { constructor?: { name?: string } }).constructor?.name ??\n 'object';\n return `[Unsupported object: ${constructorName}]`;\n }\n\n ancestors.add(current);\n const output: Record<string, unknown> = {};\n const keys = Object.keys(current as Record<string, unknown>);\n for (const key of keys.slice(0, MAX_OBJECT_KEYS)) {\n const childPath = objectPath(path, key);\n if (!consume(childPath, key)) break;\n if (isSecretKey(key)) {\n output[key] = '[REDACTED]';\n redactedPaths.add(childPath);\n } else {\n output[key] = visit(\n (current as Record<string, unknown>)[key],\n childPath,\n depth + 1,\n );\n }\n if (remainingBytes <= 0) break;\n }\n if (keys.length > Object.keys(output).length) truncatedPaths.add(path);\n ancestors.delete(current);\n return output;\n };\n\n const sanitized = visit(value, '$', 0);\n const serialized = JSON.stringify(sanitized);\n if (\n serialized === undefined ||\n stringByteLength(serialized) > MAX_SERIALIZED_BYTES\n ) {\n return { value: TRUNCATED_VALUE, truncatedPaths: ['$'] };\n }\n\n return {\n value: sanitized,\n ...(truncatedPaths.size > 0 ? { truncatedPaths: [...truncatedPaths] } : {}),\n ...(redactedPaths.size > 0 ? { redactedPaths: [...redactedPaths] } : {}),\n };\n}\n\nexport function sanitizeReportError(error: unknown): TestRunReportError {\n if (\n error instanceof Error ||\n (typeof error === 'object' &&\n error !== null &&\n typeof (error as { message?: unknown }).message === 'string')\n ) {\n const workflowLike = error as {\n name?: string;\n message: string;\n code?: unknown;\n details?: unknown;\n };\n return {\n name: sanitizeReportText(workflowLike.name || 'Error', 200),\n message: sanitizeReportText(workflowLike.message || String(error)),\n ...(typeof workflowLike.code === 'string'\n ? { code: sanitizeReportText(workflowLike.code, 200) }\n : {}),\n ...(workflowLike.details === undefined\n ? {}\n : { details: sanitizeReportValue(workflowLike.details) }),\n };\n }\n\n return {\n name: 'Error',\n message: sanitizeReportText(String(error)),\n };\n}\n"],"names":["MAX_DEPTH","MAX_STRING_LENGTH","MAX_ARRAY_ITEMS","MAX_OBJECT_KEYS","MAX_SERIALIZED_BYTES","TRUNCATED_VALUE","normalizedSecretKeys","normalizedKey","key","isSecretKey","normalized","secret","objectPath","parent","JSON","stringByteLength","value","TextEncoder","sanitizeReportText","maximumLength","sanitizeReportValue","truncatedPaths","Set","redactedPaths","ancestors","WeakSet","remainingBytes","consume","path","text","visit","current","depth","String","shortened","Number","undefined","Date","date","Array","output","itemCount","Math","index","prototype","Object","constructorName","keys","childPath","sanitized","serialized","sanitizeReportError","error","Error","workflowLike"],"mappings":"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"}
|