@midscene/test 1.17.8-beta-20261106101231.0 → 1.17.8
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/154.mjs +2 -0
- package/dist/es/164.mjs +1469 -0
- package/dist/es/164.mjs.map +1 -0
- package/dist/es/183.mjs +295 -0
- package/dist/es/183.mjs.map +1 -0
- package/dist/es/1~112.mjs +372 -0
- package/dist/es/1~112.mjs.map +1 -0
- package/dist/es/209.mjs +1 -0
- package/dist/es/299.mjs +1886 -0
- package/dist/es/299.mjs.map +1 -0
- package/dist/es/398.mjs +5 -0
- package/dist/es/398.mjs.map +1 -0
- package/dist/es/431.mjs +375 -0
- package/dist/es/431.mjs.map +1 -0
- package/dist/es/470.mjs +1 -0
- package/dist/es/487.mjs +1 -0
- package/dist/es/629.mjs +1 -0
- package/dist/es/793.mjs +1 -0
- package/dist/es/822.mjs +1 -0
- package/dist/es/946.mjs +1 -0
- package/dist/es/cli/index.mjs +3 -2196
- package/dist/es/cli/index.mjs.map +1 -1
- package/dist/es/cli/model-command.mjs +1 -296
- package/dist/es/index.mjs +10 -409
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/midscene/index.mjs +9 -9
- package/dist/es/midscene/index.mjs.map +1 -1
- package/dist/es/runtime/index.mjs +4 -5
- package/dist/es/runtime/index.mjs.map +1 -1
- package/dist/es/test-cli.mjs +1 -3786
- package/dist/lib/0~703.js.map +1 -1
- package/dist/lib/cli/index.js +45 -58
- package/dist/lib/cli/index.js.map +1 -1
- package/dist/lib/cli/model-command.js +1 -1
- package/dist/lib/cli/model-command.js.map +1 -1
- package/dist/lib/index.js +8 -37
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/midscene/index.js +1 -1
- package/dist/lib/midscene/index.js.map +1 -1
- package/dist/lib/runtime/index.js +1 -1
- package/dist/lib/runtime/index.js.map +1 -1
- package/dist/lib/test-cli.js +884 -877
- package/dist/lib/test-cli.js.map +1 -1
- package/dist/types/cli/index.d.ts +4 -0
- package/package.json +20 -21
- package/dist/es/1~4.mjs +0 -383
- package/dist/es/1~4.mjs.map +0 -1
- package/dist/es/1~731.mjs +0 -306
- package/dist/es/1~731.mjs.map +0 -1
- package/dist/es/cli/model-command.mjs.map +0 -1
- package/dist/es/test-cli.mjs.map +0 -1
package/dist/es/431.mjs
ADDED
|
@@ -0,0 +1,375 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { JSON_SCHEMA, load } from "js-yaml";
|
|
3
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { NodeRegistry as test_runner_NodeRegistry, normalizeStep as test_runner_normalizeStep, sanitizeReportText, classifyWorkflowDocumentFormat, buildTestRunReportDump, normalizeSteps as test_runner_normalizeSteps, WorkflowParseError } from "./793.mjs";
|
|
5
|
+
import { readFileSync, sep, relative, dirname, join } from "./154.mjs";
|
|
6
|
+
const NodeRegistry = test_runner_NodeRegistry;
|
|
7
|
+
const createCaseInvocationId = (projectId, sourcePath, caseIndex, invocationIndex = 0)=>createHash('sha256').update(JSON.stringify([
|
|
8
|
+
projectId,
|
|
9
|
+
sourcePath,
|
|
10
|
+
caseIndex,
|
|
11
|
+
...invocationIndex ? [
|
|
12
|
+
invocationIndex
|
|
13
|
+
] : []
|
|
14
|
+
])).digest('hex');
|
|
15
|
+
const createDocumentInvocationId = (projectId, sourcePath, invocationIndex = 0)=>createHash('sha256').update(JSON.stringify([
|
|
16
|
+
projectId,
|
|
17
|
+
sourcePath,
|
|
18
|
+
...invocationIndex ? [
|
|
19
|
+
invocationIndex
|
|
20
|
+
] : []
|
|
21
|
+
])).digest('hex');
|
|
22
|
+
const normalizeStep = test_runner_normalizeStep;
|
|
23
|
+
const normalizeSteps = test_runner_normalizeSteps;
|
|
24
|
+
function parseWorkflowSource(source) {
|
|
25
|
+
const stack = [];
|
|
26
|
+
let root;
|
|
27
|
+
const sourceOffset = 0xfeff === source.charCodeAt(0) ? 1 : 0;
|
|
28
|
+
const parsed = load(source, {
|
|
29
|
+
schema: JSON_SCHEMA,
|
|
30
|
+
listener (event, state) {
|
|
31
|
+
if ('open' === event) return void stack.push({
|
|
32
|
+
start: state.position + sourceOffset,
|
|
33
|
+
end: 0,
|
|
34
|
+
value: null,
|
|
35
|
+
children: [],
|
|
36
|
+
kind: null
|
|
37
|
+
});
|
|
38
|
+
const node = stack.pop();
|
|
39
|
+
node.end = state.position + sourceOffset;
|
|
40
|
+
node.value = state.result;
|
|
41
|
+
node.kind = state.kind;
|
|
42
|
+
const parent = stack.at(-1);
|
|
43
|
+
if (parent) parent.children.push(node);
|
|
44
|
+
else root = node;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
const value = sanitizeReportText(source, 65536);
|
|
48
|
+
return {
|
|
49
|
+
parsed,
|
|
50
|
+
locate (path) {
|
|
51
|
+
let node = root;
|
|
52
|
+
for (const part of path){
|
|
53
|
+
if (!node) return;
|
|
54
|
+
while(1 === node.children.length && node.children[0].value === node.value)node = node.children[0];
|
|
55
|
+
if ('number' == typeof part && 'sequence' === node.kind) node = node.children[part];
|
|
56
|
+
else {
|
|
57
|
+
if ('string' != typeof part || 'mapping' !== node.kind) return;
|
|
58
|
+
const children = node.children;
|
|
59
|
+
node = void 0;
|
|
60
|
+
for(let index = 0; index < children.length; index += 2)if (children[index].value === part) {
|
|
61
|
+
node = children[index + 1];
|
|
62
|
+
break;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
if (!node) return;
|
|
67
|
+
let start = node.start;
|
|
68
|
+
while(start < node.end)if (/\s/.test(source[start])) start++;
|
|
69
|
+
else if ('#' === source[start]) {
|
|
70
|
+
const end = source.indexOf('\n', start);
|
|
71
|
+
if (end < 0) return;
|
|
72
|
+
start = end + 1;
|
|
73
|
+
} else break;
|
|
74
|
+
const prefix = source.slice(0, start).split(/\r\n|\r|\n/);
|
|
75
|
+
return {
|
|
76
|
+
line: prefix.length,
|
|
77
|
+
column: prefix.at(-1).length + 1
|
|
78
|
+
};
|
|
79
|
+
},
|
|
80
|
+
sourceYaml: {
|
|
81
|
+
value,
|
|
82
|
+
...value !== source ? {
|
|
83
|
+
truncatedPaths: [
|
|
84
|
+
'$'
|
|
85
|
+
]
|
|
86
|
+
} : {}
|
|
87
|
+
}
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const interpolationExpression = /\$\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}|\$\{([^{}]+)\}/g;
|
|
91
|
+
const exactEnvExpression = /^\$\{\{([A-Za-z_][A-Za-z0-9_]*)\}\}$/;
|
|
92
|
+
const exactProjectExpression = /^\$\{([^{}]+)\}$/;
|
|
93
|
+
const missingVariable = (kind, name, options)=>{
|
|
94
|
+
throw new WorkflowParseError(`Undefined ${kind} variable "${name}" in ${options.location.sourcePath}.`, {
|
|
95
|
+
...options.location,
|
|
96
|
+
variable: name,
|
|
97
|
+
variableKind: kind
|
|
98
|
+
});
|
|
99
|
+
};
|
|
100
|
+
const projectVariable = (name, options)=>{
|
|
101
|
+
if (!Object.hasOwn(options.variables ?? {}, name)) return missingVariable('project', name, options);
|
|
102
|
+
return options.variables[name];
|
|
103
|
+
};
|
|
104
|
+
const environmentVariable = (name, options)=>{
|
|
105
|
+
const value = options.env?.[name];
|
|
106
|
+
if (void 0 === value) return missingVariable('environment', name, options);
|
|
107
|
+
return value;
|
|
108
|
+
};
|
|
109
|
+
const interpolationValue = (name, value, options)=>{
|
|
110
|
+
if ('object' == typeof value && null !== value) throw new WorkflowParseError(`Project variable "${name}" must be a primitive when embedded in a string.`, {
|
|
111
|
+
...options.location,
|
|
112
|
+
variable: name,
|
|
113
|
+
variableKind: 'project'
|
|
114
|
+
});
|
|
115
|
+
return String(value);
|
|
116
|
+
};
|
|
117
|
+
const resolveString = (value, options)=>{
|
|
118
|
+
const exactEnv = exactEnvExpression.exec(value);
|
|
119
|
+
if (exactEnv) return environmentVariable(exactEnv[1], options);
|
|
120
|
+
const exactProject = exactProjectExpression.exec(value);
|
|
121
|
+
if (exactProject) return projectVariable(exactProject[1], options);
|
|
122
|
+
return value.replace(interpolationExpression, (_match, environmentName, projectName)=>environmentName ? environmentVariable(environmentName, options) : interpolationValue(projectName, projectVariable(projectName, options), options));
|
|
123
|
+
};
|
|
124
|
+
const resolveWorkflowVariables = (value, options)=>{
|
|
125
|
+
if ('string' == typeof value) return resolveString(value, options);
|
|
126
|
+
if (Array.isArray(value)) return value.map((child)=>resolveWorkflowVariables(child, options));
|
|
127
|
+
if ('object' == typeof value && null !== value) return Object.fromEntries(Object.entries(value).map(([key, child])=>[
|
|
128
|
+
key,
|
|
129
|
+
resolveWorkflowVariables(child, options)
|
|
130
|
+
]));
|
|
131
|
+
return value;
|
|
132
|
+
};
|
|
133
|
+
function _define_property(obj, key, value) {
|
|
134
|
+
if (key in obj) Object.defineProperty(obj, key, {
|
|
135
|
+
value: value,
|
|
136
|
+
enumerable: true,
|
|
137
|
+
configurable: true,
|
|
138
|
+
writable: true
|
|
139
|
+
});
|
|
140
|
+
else obj[key] = value;
|
|
141
|
+
return obj;
|
|
142
|
+
}
|
|
143
|
+
const isMapping = (value)=>'object' == typeof value && null !== value && !Array.isArray(value);
|
|
144
|
+
class NativeWorkflowFormatError extends WorkflowParseError {
|
|
145
|
+
constructor(actualFormat, sourcePath){
|
|
146
|
+
super('legacy' === actualFormat ? `Legacy tasks/flow YAML is not supported by midscene-test: ${sourcePath}. Run it with \`midscene ${sourcePath}\`, or migrate the file to cases/steps.` : 'legacy-batch' === actualFormat ? `Legacy YAML batch configuration is not a midscene-test workflow document: ${sourcePath}. Run it with \`midscene --config ${sourcePath}\`, or migrate it to a native project config.` : `A workflow file cannot mix legacy tasks/flow with native cases/steps or lifecycle hooks: ${sourcePath}. Keep the formats in separate files and run each file with its matching command.`, {
|
|
147
|
+
sourcePath,
|
|
148
|
+
actualFormat,
|
|
149
|
+
expectedFormat: 'native'
|
|
150
|
+
}), _define_property(this, "actualFormat", void 0);
|
|
151
|
+
this.actualFormat = actualFormat;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
const rejectUnknownKeys = (value, allowed, label)=>{
|
|
155
|
+
const allowedSet = new Set(allowed);
|
|
156
|
+
const unknown = Object.keys(value).filter((key)=>!allowedSet.has(key));
|
|
157
|
+
if (unknown.length) throw new WorkflowParseError(`${label} has unsupported field "${unknown[0]}".`, {
|
|
158
|
+
field: unknown[0]
|
|
159
|
+
});
|
|
160
|
+
};
|
|
161
|
+
const createCaseId = (projectId, sourcePath, caseIndex)=>createCaseInvocationId(projectId, sourcePath, caseIndex);
|
|
162
|
+
const createWorkflowDocumentId = (projectId, sourcePath)=>createDocumentInvocationId(projectId, sourcePath);
|
|
163
|
+
function collectWorkflowDocument(source, options) {
|
|
164
|
+
let snapshot;
|
|
165
|
+
try {
|
|
166
|
+
snapshot = parseWorkflowSource(readFileSync(source.absolutePath, 'utf8'));
|
|
167
|
+
} catch (error) {
|
|
168
|
+
throw new WorkflowParseError(`Failed to parse workflow document "${source.sourcePath}".`, {
|
|
169
|
+
sourcePath: source.sourcePath
|
|
170
|
+
}, error);
|
|
171
|
+
}
|
|
172
|
+
const { parsed, sourceYaml } = snapshot;
|
|
173
|
+
if (!isMapping(parsed)) throw new WorkflowParseError('Workflow document must be a mapping.');
|
|
174
|
+
const format = classifyWorkflowDocumentFormat(parsed);
|
|
175
|
+
if ('legacy' === format || 'legacy-batch' === format || 'mixed' === format) throw new NativeWorkflowFormatError(format, source.sourcePath);
|
|
176
|
+
rejectUnknownKeys(parsed, [
|
|
177
|
+
'beforeAll',
|
|
178
|
+
'beforeEach',
|
|
179
|
+
'cases',
|
|
180
|
+
'afterEach',
|
|
181
|
+
'afterAll'
|
|
182
|
+
], 'Workflow document');
|
|
183
|
+
if (!Array.isArray(parsed.cases) || 0 === parsed.cases.length) throw new WorkflowParseError('Workflow document must contain a non-empty cases array.');
|
|
184
|
+
const sourcePath = source.sourcePath.split(sep).join('/');
|
|
185
|
+
const resolveStepVariables = (step, phase, stepIndex, caseIndex)=>({
|
|
186
|
+
...step,
|
|
187
|
+
meta: {
|
|
188
|
+
...step.meta,
|
|
189
|
+
sourceLocation: snapshot.locate('steps' === phase ? [
|
|
190
|
+
'cases',
|
|
191
|
+
caseIndex,
|
|
192
|
+
'steps',
|
|
193
|
+
stepIndex
|
|
194
|
+
] : [
|
|
195
|
+
phase,
|
|
196
|
+
stepIndex
|
|
197
|
+
])
|
|
198
|
+
},
|
|
199
|
+
templateInput: step.input,
|
|
200
|
+
input: resolveWorkflowVariables(step.input, {
|
|
201
|
+
variables: options.variables,
|
|
202
|
+
env: options.env ?? process.env,
|
|
203
|
+
location: {
|
|
204
|
+
projectName: source.projectName ?? source.projectId,
|
|
205
|
+
sourcePath,
|
|
206
|
+
phase,
|
|
207
|
+
stepIndex,
|
|
208
|
+
...void 0 === caseIndex ? {} : {
|
|
209
|
+
caseIndex
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
})
|
|
213
|
+
});
|
|
214
|
+
const normalizeLifecycle = (phase)=>{
|
|
215
|
+
const value = parsed[phase];
|
|
216
|
+
if (void 0 === value) return [];
|
|
217
|
+
if (!Array.isArray(value)) throw new WorkflowParseError(`Workflow document ${phase} must be an array.`, {
|
|
218
|
+
phase
|
|
219
|
+
});
|
|
220
|
+
return normalizeSteps(value, options.resolveNode).map((normalized, stepIndex)=>{
|
|
221
|
+
const resolved = options.resolveNode(normalized.node);
|
|
222
|
+
if (!resolved) throw new WorkflowParseError(`Workflow document ${phase} step ${stepIndex + 1} references unknown node "${normalized.node}".`, {
|
|
223
|
+
phase,
|
|
224
|
+
stepIndex,
|
|
225
|
+
node: normalized.node
|
|
226
|
+
});
|
|
227
|
+
return resolveStepVariables(normalized, phase, stepIndex);
|
|
228
|
+
});
|
|
229
|
+
};
|
|
230
|
+
const lifecycle = {
|
|
231
|
+
beforeAll: normalizeLifecycle('beforeAll'),
|
|
232
|
+
beforeEach: normalizeLifecycle('beforeEach'),
|
|
233
|
+
afterEach: normalizeLifecycle('afterEach'),
|
|
234
|
+
afterAll: normalizeLifecycle('afterAll')
|
|
235
|
+
};
|
|
236
|
+
const ids = new Set();
|
|
237
|
+
const cases = parsed.cases.map((definition, caseIndex)=>{
|
|
238
|
+
if (!isMapping(definition)) throw new WorkflowParseError(`Case ${caseIndex + 1} must be a mapping.`, {
|
|
239
|
+
caseIndex
|
|
240
|
+
});
|
|
241
|
+
rejectUnknownKeys(definition, [
|
|
242
|
+
'name',
|
|
243
|
+
'tags',
|
|
244
|
+
'steps'
|
|
245
|
+
], `Case ${caseIndex + 1}`);
|
|
246
|
+
if ('string' != typeof definition.name || 0 === definition.name.trim().length) throw new WorkflowParseError(`Case ${caseIndex + 1} name must be a non-empty string.`, {
|
|
247
|
+
caseIndex
|
|
248
|
+
});
|
|
249
|
+
if (!Array.isArray(definition.steps) || 0 === definition.steps.length) throw new WorkflowParseError(`Case ${caseIndex + 1} steps must be a non-empty array.`, {
|
|
250
|
+
caseIndex
|
|
251
|
+
});
|
|
252
|
+
const tags = definition.tags ?? [];
|
|
253
|
+
if (!Array.isArray(tags) || tags.some((tag)=>'string' != typeof tag || 0 === tag.trim().length)) throw new WorkflowParseError(`Case ${caseIndex + 1} tags must be an array of non-empty strings.`, {
|
|
254
|
+
caseIndex
|
|
255
|
+
});
|
|
256
|
+
const steps = normalizeSteps(definition.steps, options.resolveNode).map((normalized, stepIndex)=>{
|
|
257
|
+
if (!options.resolveNode(normalized.node)) throw new WorkflowParseError(`Case ${caseIndex + 1} step ${stepIndex + 1} references unknown node "${normalized.node}".`, {
|
|
258
|
+
caseIndex,
|
|
259
|
+
stepIndex,
|
|
260
|
+
node: normalized.node
|
|
261
|
+
});
|
|
262
|
+
return resolveStepVariables(normalized, 'steps', stepIndex, caseIndex);
|
|
263
|
+
});
|
|
264
|
+
const caseId = createCaseId(source.projectId, sourcePath, caseIndex);
|
|
265
|
+
if (ids.has(caseId)) throw new WorkflowParseError(`Case id collision: ${caseId}.`, {
|
|
266
|
+
caseId
|
|
267
|
+
});
|
|
268
|
+
ids.add(caseId);
|
|
269
|
+
return {
|
|
270
|
+
caseId,
|
|
271
|
+
projectId: source.projectId,
|
|
272
|
+
sourcePath,
|
|
273
|
+
caseIndex,
|
|
274
|
+
definition: {
|
|
275
|
+
name: definition.name,
|
|
276
|
+
tags: tags,
|
|
277
|
+
steps
|
|
278
|
+
}
|
|
279
|
+
};
|
|
280
|
+
});
|
|
281
|
+
return {
|
|
282
|
+
documentId: createWorkflowDocumentId(source.projectId, sourcePath),
|
|
283
|
+
projectId: source.projectId,
|
|
284
|
+
sourcePath,
|
|
285
|
+
sourceYaml,
|
|
286
|
+
lifecycle,
|
|
287
|
+
cases
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
function renderTestBadgeLines({ status, summary }) {
|
|
291
|
+
if ('success' !== status && 0 === summary.failed) return [];
|
|
292
|
+
return [
|
|
293
|
+
``,
|
|
294
|
+
''
|
|
295
|
+
];
|
|
296
|
+
}
|
|
297
|
+
const result_markdown_text = (value)=>value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll('|', '|').replaceAll('[', '[').replaceAll(']', ']').replaceAll('*', '*').replaceAll('_', '_').replaceAll('`', '`').replaceAll('\\', '\').replace(/[\r\n]+/g, ' ');
|
|
298
|
+
const duration = (ms)=>{
|
|
299
|
+
const seconds = Math.round(ms / 1000);
|
|
300
|
+
return seconds < 60 ? `${seconds}s` : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;
|
|
301
|
+
};
|
|
302
|
+
const href = (from, target)=>relative(dirname(from), target).split(sep).map(encodeURIComponent).join('/');
|
|
303
|
+
function renderTestResultMarkdown(dump, options = {}) {
|
|
304
|
+
const { summary } = dump;
|
|
305
|
+
const outcome = 'success' === dump.status ? `✅ **${summary.passed}/${summary.total} passed**` : `❌ **${summary.failed} failed** · ${summary.passed} passed · ${summary.notRun} not run`;
|
|
306
|
+
const lines = [
|
|
307
|
+
'## Midscene Test',
|
|
308
|
+
'',
|
|
309
|
+
...renderTestBadgeLines(dump),
|
|
310
|
+
`${outcome} · ${duration(dump.durationMs)}`,
|
|
311
|
+
''
|
|
312
|
+
];
|
|
313
|
+
if (summary.filtered) lines.push(`**Filtered:** ${summary.filtered}`, '');
|
|
314
|
+
const infrastructure = [
|
|
315
|
+
summary.collectionErrors ? `${summary.collectionErrors} collection errors` : '',
|
|
316
|
+
summary.documentFailures ? `${summary.documentFailures} document failures` : '',
|
|
317
|
+
summary.projectFailures ? `${summary.projectFailures} project failures` : ''
|
|
318
|
+
].filter(Boolean);
|
|
319
|
+
if (infrastructure.length) lines.push(`**Infrastructure:** ${infrastructure.join(' · ')}`, '');
|
|
320
|
+
const groups = [];
|
|
321
|
+
const errors = [];
|
|
322
|
+
const recordSteps = (label, steps)=>{
|
|
323
|
+
for (const step of steps)if ('failed' === step.status) errors.push(`${label} / ${step.node}: ${step.error?.message ?? 'Step failed'}`);
|
|
324
|
+
};
|
|
325
|
+
for (const project of dump.projects){
|
|
326
|
+
if (project.lifecycle?.setupError) errors.push(`${project.name}: ${project.lifecycle.setupError.message}`);
|
|
327
|
+
for (const error of project.lifecycle?.teardownErrors ?? [])errors.push(`${project.name}: ${error.message}`);
|
|
328
|
+
for (const error of project.collectionErrors)errors.push(`${project.name} / ${error.sourcePath}: ${error.error.message}`);
|
|
329
|
+
for (const document of project.documents){
|
|
330
|
+
const name = document.sourcePath.replace(/\\/g, '/').replace(/\.(?:yaml|yml)$/, '');
|
|
331
|
+
const group = {
|
|
332
|
+
name: dump.projects.length > 1 ? `${project.name} / ${name}` : name,
|
|
333
|
+
rows: []
|
|
334
|
+
};
|
|
335
|
+
groups.push(group);
|
|
336
|
+
recordSteps(`${project.name} / ${document.sourcePath}`, [
|
|
337
|
+
...document.beforeAll,
|
|
338
|
+
...document.afterAll
|
|
339
|
+
]);
|
|
340
|
+
for (const error of document.teardownErrors ?? [])errors.push(`${document.sourcePath}: ${error.message}`);
|
|
341
|
+
for (const failure of document.hostErrors ?? [])errors.push(`${document.sourcePath} / ${failure.phase}: ${failure.error.message}`);
|
|
342
|
+
for (const testCase of document.cases)group.rows.push(` - ${'success' === testCase.status ? '✅' : 'not-run' === testCase.status ? '⏭️' : '❌'} ${result_markdown_text(testCase.name)}`);
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
for (const diagnostic of dump.diagnostics ?? [])if ('error' === diagnostic.level) errors.push(diagnostic.message);
|
|
346
|
+
if (errors.length) lines.push('### Execution errors', '', ...errors.map((error)=>`- ${result_markdown_text(error)}`), '');
|
|
347
|
+
for (const group of groups)if (group.rows.length) lines.push(`- ${result_markdown_text(group.name)}`, ...group.rows);
|
|
348
|
+
if (!groups.some((group)=>group.rows.length)) lines.push('No cases were reported.');
|
|
349
|
+
if (options.reportHref) lines.push('', `[Open the Midscene Test report ↗](<${options.reportHref}>)`);
|
|
350
|
+
return `${lines.join('\n')}\n`;
|
|
351
|
+
}
|
|
352
|
+
const emptyIndex = ()=>({
|
|
353
|
+
sources: [],
|
|
354
|
+
metrics: {
|
|
355
|
+
modelCallCount: 0,
|
|
356
|
+
modelTimeMs: 0,
|
|
357
|
+
promptTokens: 0,
|
|
358
|
+
cachedInputTokens: 0,
|
|
359
|
+
completionTokens: 0,
|
|
360
|
+
totalTokens: 0
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
async function writeTestResultMarkdown(result, index = emptyIndex()) {
|
|
364
|
+
const output = join(dirname(result.summaryPath), 'result.md');
|
|
365
|
+
const dump = buildTestRunReportDump(result, index);
|
|
366
|
+
await writeFile(output, renderTestResultMarkdown(dump, {
|
|
367
|
+
...result.reportPath ? {
|
|
368
|
+
reportHref: href(output, result.reportPath)
|
|
369
|
+
} : {}
|
|
370
|
+
}));
|
|
371
|
+
return output;
|
|
372
|
+
}
|
|
373
|
+
export { NativeWorkflowFormatError, NodeRegistry, collectWorkflowDocument, createCaseId, createHash, createWorkflowDocumentId, mkdir, normalizeStep, normalizeSteps, randomUUID, renderTestBadgeLines, renderTestResultMarkdown, resolveWorkflowVariables, writeFile, writeTestResultMarkdown };
|
|
374
|
+
|
|
375
|
+
//# sourceMappingURL=431.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"431.mjs","sources":["../../src/engine/registry.ts","../../src/parser/identifiers.ts","../../src/parser/normalize.ts","../../src/parser/source.ts","../../src/parser/variables.ts","../../src/parser/collect.ts","../../src/report/badge.ts","../../src/report/result-markdown.ts"],"sourcesContent":["import { NodeRegistry as CoreNodeRegistry } from '@midscene/core/internal/test-runner';\nimport type { NodeDefinition } from '../node/types';\n\nexport type NodeRegistry = Omit<\n CoreNodeRegistry,\n 'get' | 'require' | 'register' | 'definitions'\n> & {\n get(name: string): NodeDefinition<any, any> | undefined;\n require(name: string): NodeDefinition<any, any>;\n register<TInput, TData>(node: NodeDefinition<TInput, TData>): NodeRegistry;\n definitions(): readonly NodeDefinition<any, any, any>[];\n};\nexport const NodeRegistry: {\n new (nodes?: readonly NodeDefinition<any, any>[]): NodeRegistry;\n} = CoreNodeRegistry;\n","import { createHash } from 'node:crypto';\n\n/** Internal invocation identity keeps repeated legacy files distinct. */\nexport const createCaseInvocationId = (\n projectId: string,\n sourcePath: string,\n caseIndex: number,\n invocationIndex = 0,\n): string =>\n createHash('sha256')\n .update(\n JSON.stringify([\n projectId,\n sourcePath,\n caseIndex,\n ...(invocationIndex ? [invocationIndex] : []),\n ]),\n )\n .digest('hex');\n\nexport const createDocumentInvocationId = (\n projectId: string,\n sourcePath: string,\n invocationIndex = 0,\n): string =>\n createHash('sha256')\n .update(\n JSON.stringify([\n projectId,\n sourcePath,\n ...(invocationIndex ? [invocationIndex] : []),\n ]),\n )\n .digest('hex');\n","import {\n type ResolveNodeForNormalization,\n normalizeStep as coreNormalizeStep,\n normalizeSteps as coreNormalizeSteps,\n} from '@midscene/core/internal/test-runner';\nimport type { NormalizedStep } from './types';\nexport type { ResolveNodeForNormalization } from '@midscene/core/internal/test-runner';\n\nexport const normalizeStep: (\n value: unknown,\n index?: number,\n resolveNode?: ResolveNodeForNormalization,\n) => NormalizedStep = coreNormalizeStep;\nexport const normalizeSteps: (\n values: unknown,\n resolveNode?: ResolveNodeForNormalization,\n) => NormalizedStep[] = coreNormalizeSteps;\n","import type { TestRunReportValue } from '@midscene/core';\nimport { sanitizeReportText } from '@midscene/core/internal/test-runner';\nimport { JSON_SCHEMA, load } from 'js-yaml';\n\ninterface SourceNode {\n start: number;\n end: number;\n value: unknown;\n children: SourceNode[];\n kind: string | null;\n}\n\n/** Capture author formatting and locate steps in the original source. */\nexport function parseWorkflowSource(source: string): {\n parsed: unknown;\n sourceYaml: TestRunReportValue;\n locate(\n path: readonly (string | number)[],\n ): { line: number; column: number } | undefined;\n} {\n const stack: SourceNode[] = [];\n let root: SourceNode | undefined;\n // js-yaml removes the BOM before reporting source positions.\n const sourceOffset = source.charCodeAt(0) === 0xfeff ? 1 : 0;\n const parsed = load(source, {\n schema: JSON_SCHEMA,\n listener(event, state) {\n if (event === 'open') {\n stack.push({\n start: state.position + sourceOffset,\n end: 0,\n value: null,\n children: [],\n kind: null,\n });\n return;\n }\n const node = stack.pop()!;\n node.end = state.position + sourceOffset;\n node.value = state.result;\n node.kind = state.kind;\n const parent = stack.at(-1);\n if (parent) parent.children.push(node);\n else root = node;\n },\n });\n const value = sanitizeReportText(source, 64 * 1024);\n return {\n parsed,\n locate(path) {\n let node = root;\n for (const part of path) {\n if (!node) return undefined;\n // js-yaml can emit a wrapper around a block collection.\n while (\n node.children.length === 1 &&\n node.children[0].value === node.value\n )\n node = node.children[0];\n if (typeof part === 'number' && node.kind === 'sequence')\n node = node.children[part];\n else if (typeof part === 'string' && node.kind === 'mapping') {\n const children = node.children;\n node = undefined;\n for (let index = 0; index < children.length; index += 2)\n if (children[index].value === part) {\n node = children[index + 1];\n break;\n }\n } else return undefined;\n }\n if (!node) return undefined;\n let start = node.start;\n while (start < node.end) {\n if (/\\s/.test(source[start])) start++;\n else if (source[start] === '#') {\n const end = source.indexOf('\\n', start);\n if (end < 0) return undefined;\n start = end + 1;\n } else break;\n }\n const prefix = source.slice(0, start).split(/\\r\\n|\\r|\\n/);\n return { line: prefix.length, column: prefix.at(-1)!.length + 1 };\n },\n sourceYaml: {\n value,\n ...(value !== source ? { truncatedPaths: ['$'] } : {}),\n },\n };\n}\n","import type { JsonValue } from '../cli/test-project';\nimport { WorkflowParseError } from '../errors';\n\nexport interface VariableResolutionLocation {\n projectName: string;\n sourcePath: string;\n phase: 'beforeAll' | 'beforeEach' | 'steps' | 'afterEach' | 'afterAll';\n stepIndex: number;\n caseIndex?: number;\n}\n\nexport interface ResolveWorkflowVariablesOptions {\n variables?: Readonly<Record<string, JsonValue>>;\n env?: Readonly<NodeJS.ProcessEnv>;\n location: VariableResolutionLocation;\n}\n\nconst interpolationExpression =\n /\\$\\{\\{([A-Za-z_][A-Za-z0-9_]*)\\}\\}|\\$\\{([^{}]+)\\}/g;\nconst exactEnvExpression = /^\\$\\{\\{([A-Za-z_][A-Za-z0-9_]*)\\}\\}$/;\nconst exactProjectExpression = /^\\$\\{([^{}]+)\\}$/;\n\nconst missingVariable = (\n kind: 'environment' | 'project',\n name: string,\n options: ResolveWorkflowVariablesOptions,\n): never => {\n throw new WorkflowParseError(\n `Undefined ${kind} variable \"${name}\" in ${options.location.sourcePath}.`,\n { ...options.location, variable: name, variableKind: kind },\n );\n};\n\nconst projectVariable = (\n name: string,\n options: ResolveWorkflowVariablesOptions,\n): JsonValue => {\n if (!Object.hasOwn(options.variables ?? {}, name)) {\n return missingVariable('project', name, options);\n }\n return options.variables![name];\n};\n\nconst environmentVariable = (\n name: string,\n options: ResolveWorkflowVariablesOptions,\n): string => {\n const value = options.env?.[name];\n if (value === undefined) return missingVariable('environment', name, options);\n return value;\n};\n\nconst interpolationValue = (\n name: string,\n value: JsonValue,\n options: ResolveWorkflowVariablesOptions,\n): string => {\n if (typeof value === 'object' && value !== null) {\n throw new WorkflowParseError(\n `Project variable \"${name}\" must be a primitive when embedded in a string.`,\n {\n ...options.location,\n variable: name,\n variableKind: 'project',\n },\n );\n }\n return String(value);\n};\n\nconst resolveString = (\n value: string,\n options: ResolveWorkflowVariablesOptions,\n): JsonValue => {\n const exactEnv = exactEnvExpression.exec(value);\n if (exactEnv) return environmentVariable(exactEnv[1], options);\n const exactProject = exactProjectExpression.exec(value);\n if (exactProject) return projectVariable(exactProject[1], options);\n\n return value.replace(\n interpolationExpression,\n (_match, environmentName: string | undefined, projectName: string) =>\n environmentName\n ? environmentVariable(environmentName, options)\n : interpolationValue(\n projectName,\n projectVariable(projectName, options),\n options,\n ),\n );\n};\n\nexport const resolveWorkflowVariables = (\n value: unknown,\n options: ResolveWorkflowVariablesOptions,\n): unknown => {\n if (typeof value === 'string') return resolveString(value, options);\n if (Array.isArray(value)) {\n return value.map((child) => resolveWorkflowVariables(child, options));\n }\n if (typeof value === 'object' && value !== null) {\n return Object.fromEntries(\n Object.entries(value).map(([key, child]) => [\n key,\n resolveWorkflowVariables(child, options),\n ]),\n );\n }\n return value;\n};\n","import { readFileSync } from 'node:fs';\nimport { sep } from 'node:path';\nimport { classifyWorkflowDocumentFormat } from '@midscene/core/internal/test-runner';\nimport type { JsonValue } from '../cli/test-project';\nimport { WorkflowParseError } from '../errors';\nimport type { NodeDefinition } from '../node/types';\nimport {\n createCaseInvocationId,\n createDocumentInvocationId,\n} from './identifiers';\nimport { normalizeSteps } from './normalize';\nimport { parseWorkflowSource } from './source';\nimport type {\n CollectedCase,\n CollectedWorkflowDocument,\n WorkflowDocumentSource,\n} from './types';\nimport { resolveWorkflowVariables } from './variables';\n\nexport interface CollectWorkflowDocumentOptions {\n resolveNode(name: string): NodeDefinition<any, any> | undefined;\n variables?: Readonly<Record<string, JsonValue>>;\n env?: Readonly<NodeJS.ProcessEnv>;\n}\n\nconst isMapping = (value: unknown): value is Record<string, unknown> =>\n typeof value === 'object' && value !== null && !Array.isArray(value);\n\nexport class NativeWorkflowFormatError extends WorkflowParseError {\n readonly actualFormat: 'legacy' | 'legacy-batch' | 'mixed';\n\n constructor(\n actualFormat: 'legacy' | 'legacy-batch' | 'mixed',\n sourcePath: string,\n ) {\n super(\n actualFormat === 'legacy'\n ? `Legacy tasks/flow YAML is not supported by midscene-test: ${sourcePath}. Run it with \\`midscene ${sourcePath}\\`, or migrate the file to cases/steps.`\n : actualFormat === 'legacy-batch'\n ? `Legacy YAML batch configuration is not a midscene-test workflow document: ${sourcePath}. Run it with \\`midscene --config ${sourcePath}\\`, or migrate it to a native project config.`\n : `A workflow file cannot mix legacy tasks/flow with native cases/steps or lifecycle hooks: ${sourcePath}. Keep the formats in separate files and run each file with its matching command.`,\n { sourcePath, actualFormat, expectedFormat: 'native' },\n );\n this.actualFormat = actualFormat;\n }\n}\n\nconst rejectUnknownKeys = (\n value: Record<string, unknown>,\n allowed: readonly string[],\n label: string,\n) => {\n const allowedSet = new Set(allowed);\n const unknown = Object.keys(value).filter((key) => !allowedSet.has(key));\n if (unknown.length) {\n throw new WorkflowParseError(\n `${label} has unsupported field \"${unknown[0]}\".`,\n { field: unknown[0] },\n );\n }\n};\n\nexport const createCaseId = (\n projectId: string,\n sourcePath: string,\n caseIndex: number,\n): string => createCaseInvocationId(projectId, sourcePath, caseIndex);\n\nexport const createWorkflowDocumentId = (\n projectId: string,\n sourcePath: string,\n): string => createDocumentInvocationId(projectId, sourcePath);\n\nexport function collectWorkflowDocument(\n source: WorkflowDocumentSource,\n options: CollectWorkflowDocumentOptions,\n): CollectedWorkflowDocument {\n let snapshot: ReturnType<typeof parseWorkflowSource>;\n try {\n snapshot = parseWorkflowSource(readFileSync(source.absolutePath, 'utf8'));\n } catch (error) {\n throw new WorkflowParseError(\n `Failed to parse workflow document \"${source.sourcePath}\".`,\n { sourcePath: source.sourcePath },\n error,\n );\n }\n const { parsed, sourceYaml } = snapshot;\n\n if (!isMapping(parsed)) {\n throw new WorkflowParseError('Workflow document must be a mapping.');\n }\n const format = classifyWorkflowDocumentFormat(parsed);\n if (format === 'legacy' || format === 'legacy-batch' || format === 'mixed') {\n throw new NativeWorkflowFormatError(format, source.sourcePath);\n }\n rejectUnknownKeys(\n parsed,\n ['beforeAll', 'beforeEach', 'cases', 'afterEach', 'afterAll'],\n 'Workflow document',\n );\n if (!Array.isArray(parsed.cases) || parsed.cases.length === 0) {\n throw new WorkflowParseError(\n 'Workflow document must contain a non-empty cases array.',\n );\n }\n\n const sourcePath = source.sourcePath.split(sep).join('/');\n const resolveStepVariables = (\n step: ReturnType<typeof normalizeSteps>[number],\n phase: 'beforeAll' | 'beforeEach' | 'steps' | 'afterEach' | 'afterAll',\n stepIndex: number,\n caseIndex?: number,\n ) => ({\n ...step,\n meta: {\n ...step.meta,\n sourceLocation: snapshot.locate(\n phase === 'steps'\n ? ['cases', caseIndex!, 'steps', stepIndex]\n : [phase, stepIndex],\n ),\n },\n templateInput: step.input,\n input: resolveWorkflowVariables(step.input, {\n variables: options.variables,\n env: options.env ?? process.env,\n location: {\n projectName: source.projectName ?? source.projectId,\n sourcePath,\n phase,\n stepIndex,\n ...(caseIndex === undefined ? {} : { caseIndex }),\n },\n }) as typeof step.input,\n });\n const normalizeLifecycle = (\n phase: 'beforeAll' | 'beforeEach' | 'afterEach' | 'afterAll',\n ) => {\n const value = parsed[phase];\n if (value === undefined) return [];\n if (!Array.isArray(value)) {\n throw new WorkflowParseError(\n `Workflow document ${phase} must be an array.`,\n { phase },\n );\n }\n return normalizeSteps(value, options.resolveNode).map(\n (normalized, stepIndex) => {\n const resolved = options.resolveNode(normalized.node);\n if (!resolved) {\n throw new WorkflowParseError(\n `Workflow document ${phase} step ${stepIndex + 1} references unknown node \"${normalized.node}\".`,\n { phase, stepIndex, node: normalized.node },\n );\n }\n return resolveStepVariables(normalized, phase, stepIndex);\n },\n );\n };\n const lifecycle = {\n beforeAll: normalizeLifecycle('beforeAll'),\n beforeEach: normalizeLifecycle('beforeEach'),\n afterEach: normalizeLifecycle('afterEach'),\n afterAll: normalizeLifecycle('afterAll'),\n };\n const ids = new Set<string>();\n const cases: CollectedCase[] = parsed.cases.map((definition, caseIndex) => {\n if (!isMapping(definition)) {\n throw new WorkflowParseError(`Case ${caseIndex + 1} must be a mapping.`, {\n caseIndex,\n });\n }\n rejectUnknownKeys(\n definition,\n ['name', 'tags', 'steps'],\n `Case ${caseIndex + 1}`,\n );\n if (\n typeof definition.name !== 'string' ||\n definition.name.trim().length === 0\n ) {\n throw new WorkflowParseError(\n `Case ${caseIndex + 1} name must be a non-empty string.`,\n { caseIndex },\n );\n }\n if (!Array.isArray(definition.steps) || definition.steps.length === 0) {\n throw new WorkflowParseError(\n `Case ${caseIndex + 1} steps must be a non-empty array.`,\n { caseIndex },\n );\n }\n const tags = definition.tags ?? [];\n if (\n !Array.isArray(tags) ||\n tags.some((tag) => typeof tag !== 'string' || tag.trim().length === 0)\n ) {\n throw new WorkflowParseError(\n `Case ${caseIndex + 1} tags must be an array of non-empty strings.`,\n { caseIndex },\n );\n }\n\n const steps = normalizeSteps(definition.steps, options.resolveNode).map(\n (normalized, stepIndex) => {\n if (!options.resolveNode(normalized.node)) {\n throw new WorkflowParseError(\n `Case ${caseIndex + 1} step ${stepIndex + 1} references unknown node \"${normalized.node}\".`,\n { caseIndex, stepIndex, node: normalized.node },\n );\n }\n return resolveStepVariables(normalized, 'steps', stepIndex, caseIndex);\n },\n );\n const caseId = createCaseId(source.projectId, sourcePath, caseIndex);\n if (ids.has(caseId)) {\n throw new WorkflowParseError(`Case id collision: ${caseId}.`, {\n caseId,\n });\n }\n ids.add(caseId);\n\n return {\n caseId,\n projectId: source.projectId,\n sourcePath,\n caseIndex,\n definition: {\n name: definition.name,\n tags: tags as string[],\n steps,\n },\n };\n });\n\n return {\n documentId: createWorkflowDocumentId(source.projectId, sourcePath),\n projectId: source.projectId,\n sourcePath,\n sourceYaml,\n lifecycle,\n cases,\n } satisfies CollectedWorkflowDocument;\n}\n","import type { TestRunReportDump } from '@midscene/core';\n\n/** Omit case-count badges when they cannot represent the failed run. */\nexport function renderTestBadgeLines({\n status,\n summary,\n}: Pick<TestRunReportDump, 'status' | 'summary'>): string[] {\n if (status !== 'success' && summary.failed === 0) return [];\n return [\n ``,\n '',\n ];\n}\n","import { writeFile } from 'node:fs/promises';\nimport { dirname, join, relative, sep } from 'node:path';\nimport type {\n TestRunReportDump,\n TestRunReportSourceIndex,\n TestRunReportStep,\n} from '@midscene/core';\nimport type { TestProjectRunResult } from '../cli/types';\nimport { renderTestBadgeLines } from './badge';\nimport { buildTestRunReportDump } from './test-run-report';\n\nconst text = (value: string): string =>\n value\n .replaceAll('&', '&')\n .replaceAll('<', '<')\n .replaceAll('>', '>')\n .replaceAll('\"', '"')\n .replaceAll('|', '|')\n .replaceAll('[', '[')\n .replaceAll(']', ']')\n .replaceAll('*', '*')\n .replaceAll('_', '_')\n .replaceAll('`', '`')\n .replaceAll('\\\\', '\')\n .replace(/[\\r\\n]+/g, ' ');\n\nconst duration = (ms: number): string => {\n const seconds = Math.round(ms / 1000);\n return seconds < 60\n ? `${seconds}s`\n : `${Math.floor(seconds / 60)}m ${seconds % 60}s`;\n};\nconst href = (from: string, target: string): string =>\n relative(dirname(from), target).split(sep).map(encodeURIComponent).join('/');\n\nexport interface TestResultMarkdownOptions {\n /** Relative or hosted URL of the HTML report. */\n reportHref?: string;\n}\n\n/** Render the same sanitized execution facts used by the HTML report. */\nexport function renderTestResultMarkdown(\n dump: TestRunReportDump,\n options: TestResultMarkdownOptions = {},\n): string {\n const { summary } = dump;\n const outcome =\n dump.status === 'success'\n ? `✅ **${summary.passed}/${summary.total} passed**`\n : `❌ **${summary.failed} failed** · ${summary.passed} passed · ${summary.notRun} not run`;\n const lines = [\n '## Midscene Test',\n '',\n ...renderTestBadgeLines(dump),\n `${outcome} · ${duration(dump.durationMs)}`,\n '',\n ];\n if (summary.filtered) lines.push(`**Filtered:** ${summary.filtered}`, '');\n const infrastructure = [\n summary.collectionErrors\n ? `${summary.collectionErrors} collection errors`\n : '',\n summary.documentFailures\n ? `${summary.documentFailures} document failures`\n : '',\n summary.projectFailures\n ? `${summary.projectFailures} project failures`\n : '',\n ].filter(Boolean);\n if (infrastructure.length)\n lines.push(`**Infrastructure:** ${infrastructure.join(' · ')}`, '');\n const groups: { name: string; rows: string[] }[] = [];\n const errors: string[] = [];\n const recordSteps = (label: string, steps: readonly TestRunReportStep[]) => {\n for (const step of steps) {\n if (step.status === 'failed')\n errors.push(\n `${label} / ${step.node}: ${step.error?.message ?? 'Step failed'}`,\n );\n }\n };\n for (const project of dump.projects) {\n if (project.lifecycle?.setupError)\n errors.push(`${project.name}: ${project.lifecycle.setupError.message}`);\n for (const error of project.lifecycle?.teardownErrors ?? [])\n errors.push(`${project.name}: ${error.message}`);\n for (const error of project.collectionErrors)\n errors.push(\n `${project.name} / ${error.sourcePath}: ${error.error.message}`,\n );\n for (const document of project.documents) {\n const name = document.sourcePath\n .replace(/\\\\/g, '/')\n .replace(/\\.(?:yaml|yml)$/, '');\n const group = {\n name: dump.projects.length > 1 ? `${project.name} / ${name}` : name,\n rows: [] as string[],\n };\n groups.push(group);\n recordSteps(`${project.name} / ${document.sourcePath}`, [\n ...document.beforeAll,\n ...document.afterAll,\n ]);\n for (const error of document.teardownErrors ?? [])\n errors.push(`${document.sourcePath}: ${error.message}`);\n for (const failure of document.hostErrors ?? [])\n errors.push(\n `${document.sourcePath} / ${failure.phase}: ${failure.error.message}`,\n );\n for (const testCase of document.cases) {\n group.rows.push(\n ` - ${testCase.status === 'success' ? '✅' : testCase.status === 'not-run' ? '⏭️' : '❌'} ${text(testCase.name)}`,\n );\n }\n }\n }\n for (const diagnostic of dump.diagnostics ?? []) {\n if (diagnostic.level === 'error') errors.push(diagnostic.message);\n }\n if (errors.length)\n lines.push(\n '### Execution errors',\n '',\n ...errors.map((error) => `- ${text(error)}`),\n '',\n );\n for (const group of groups) {\n if (group.rows.length) lines.push(`- ${text(group.name)}`, ...group.rows);\n }\n if (!groups.some((group) => group.rows.length))\n lines.push('No cases were reported.');\n if (options.reportHref)\n lines.push(\n '',\n `[Open the Midscene Test report ↗](<${options.reportHref}>)`,\n );\n return `${lines.join('\\n')}\\n`;\n}\n\nconst emptyIndex = (): TestRunReportSourceIndex => ({\n sources: [],\n metrics: {\n modelCallCount: 0,\n modelTimeMs: 0,\n promptTokens: 0,\n cachedInputTokens: 0,\n completionTokens: 0,\n totalTokens: 0,\n },\n});\n\n/** Write a per-run Markdown artifact even when HTML publication fails or is disabled. */\nexport async function writeTestResultMarkdown(\n result: TestProjectRunResult,\n index: TestRunReportSourceIndex = emptyIndex(),\n): Promise<string> {\n const output = join(dirname(result.summaryPath), 'result.md');\n const dump = buildTestRunReportDump(result, index);\n await writeFile(\n output,\n renderTestResultMarkdown(dump, {\n ...(result.reportPath\n ? { reportHref: href(output, result.reportPath) }\n : {}),\n }),\n );\n return output;\n}\n"],"names":["NodeRegistry","CoreNodeRegistry","createCaseInvocationId","projectId","sourcePath","caseIndex","invocationIndex","createHash","JSON","createDocumentInvocationId","normalizeStep","coreNormalizeStep","normalizeSteps","coreNormalizeSteps","parseWorkflowSource","source","stack","root","sourceOffset","parsed","load","JSON_SCHEMA","event","state","node","parent","value","sanitizeReportText","path","part","children","undefined","index","start","end","prefix","interpolationExpression","exactEnvExpression","exactProjectExpression","missingVariable","kind","name","options","WorkflowParseError","projectVariable","Object","environmentVariable","interpolationValue","String","resolveString","exactEnv","exactProject","_match","environmentName","projectName","resolveWorkflowVariables","Array","child","key","isMapping","NativeWorkflowFormatError","actualFormat","rejectUnknownKeys","allowed","label","allowedSet","Set","unknown","createCaseId","createWorkflowDocumentId","collectWorkflowDocument","snapshot","readFileSync","error","sourceYaml","format","classifyWorkflowDocumentFormat","sep","resolveStepVariables","step","phase","stepIndex","process","normalizeLifecycle","normalized","resolved","lifecycle","ids","cases","definition","tags","tag","steps","caseId","renderTestBadgeLines","status","summary","text","duration","ms","seconds","Math","href","from","target","relative","dirname","encodeURIComponent","renderTestResultMarkdown","dump","outcome","lines","infrastructure","Boolean","groups","errors","recordSteps","project","document","group","failure","testCase","diagnostic","emptyIndex","writeTestResultMarkdown","result","output","join","buildTestRunReportDump","writeFile"],"mappings":"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"}
|
package/dist/es/470.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { getDebug, runWithConsoleLogHandler, runWithConsoleLogScope } from "@midscene/shared/logger";
|
package/dist/es/487.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { z } from "zod/v4";
|
package/dist/es/629.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as dotenv } from "dotenv";
|
package/dist/es/793.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { CaseExecutionError, CaseRunner, DuplicateNodeError, FatalDeviceError, NodeDefinitionError, NodeExecutionError, NodeInputValidationError, NodeNotFoundError, NodeRegistry, NodeScopeTeardownError, ProjectSetupError, ProjectTeardownError, StepTimeoutError, WorkflowDocumentExecutionError, WorkflowError, WorkflowExecutionFailure, WorkflowLifecycleError, WorkflowParseError, WorkflowPublicationError, admitResourceOperation, asExecutionError, buildTestRunReportDump, buildTestRunReportPlan, classifyWorkflowDocumentFormat, collectTestRunReportMetrics, collectTestRunReportSources, createCaseRunner, createDocumentRuntime, createProjectRuntime, decideRetry, defineNode, getPendingScopeOperations, getResourceCleanupCompletion, hasPendingResourceOperation, isFatalDeviceError, isResourceRecoveryError, normalizeNodeExecutionError, normalizeStep, normalizeSteps, observeResourceOperations, retainResourceForScope, runDocumentAttempts, runWorkflowDocument, sanitizeReportError, sanitizeReportText, sanitizeReportValue, sumTestRunReportMetrics, trackResourceOperation, waitForScopeOperations } from "@midscene/core/internal/test-runner";
|
package/dist/es/822.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { enterYamlAction, enterYamlExecution, getLegacyYamlPlayerState, runInYamlExecutionContext } from "@midscene/core/internal/yaml-runtime";
|
package/dist/es/946.mjs
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as chalk } from "chalk";
|