@midscene/test 1.17.5 → 1.17.7
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/cli/index.mjs +73 -39
- package/dist/es/cli/index.mjs.map +1 -1
- package/dist/es/index.mjs +5 -0
- package/dist/es/index.mjs.map +1 -1
- package/dist/es/test-cli.mjs +76 -40
- package/dist/es/test-cli.mjs.map +1 -1
- package/dist/lib/cli/index.js +73 -39
- package/dist/lib/cli/index.js.map +1 -1
- package/dist/lib/index.js +5 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/test-cli.js +76 -40
- package/dist/lib/test-cli.js.map +1 -1
- package/dist/templates/test/android/README.md +1 -1
- package/dist/templates/test/android/README.zh.md +1 -1
- package/dist/templates/test/android/gitignore +0 -1
- package/dist/templates/test/computer/README.md +1 -1
- package/dist/templates/test/computer/README.zh.md +1 -1
- package/dist/templates/test/computer/gitignore +0 -1
- package/dist/templates/test/harmony/README.md +1 -1
- package/dist/templates/test/harmony/README.zh.md +1 -1
- package/dist/templates/test/harmony/gitignore +0 -1
- package/dist/templates/test/ios/README.md +1 -1
- package/dist/templates/test/ios/README.zh.md +1 -1
- package/dist/templates/test/ios/gitignore +0 -1
- package/dist/templates/test/web/README.md +1 -1
- package/dist/templates/test/web/README.zh.md +1 -1
- package/dist/templates/test/web/gitignore +0 -1
- package/dist/types/cli/index.d.ts +2 -1
- package/package.json +13 -13
package/dist/es/cli/index.mjs
CHANGED
|
@@ -7,6 +7,7 @@ import { globSync } from "tinyglobby";
|
|
|
7
7
|
import { createHash, randomUUID } from "node:crypto";
|
|
8
8
|
import { JSON_SCHEMA, load } from "js-yaml";
|
|
9
9
|
import { mkdir, writeFile } from "node:fs/promises";
|
|
10
|
+
import { resolveMidsceneRunDir, runWithMidsceneRunDir } from "@midscene/shared/common";
|
|
10
11
|
import { setMaxListeners } from "node:events";
|
|
11
12
|
import { ReportSession, TestRunReportAssembler, calculateTestRunHealth } from "@midscene/core/report";
|
|
12
13
|
import { getDebug, runWithConsoleLogScope } from "@midscene/shared/logger";
|
|
@@ -249,8 +250,7 @@ const validateTestOptions = (value, hasExplicitProjects)=>{
|
|
|
249
250
|
testTimeout: validatePositiveInteger(candidate.testTimeout, 120000, 'test.testTimeout')
|
|
250
251
|
});
|
|
251
252
|
};
|
|
252
|
-
const validateOutputPath = (value,
|
|
253
|
-
if (void 0 === value) return fallback;
|
|
253
|
+
const validateOutputPath = (value, label)=>{
|
|
254
254
|
if ('string' != typeof value || 0 === value.trim().length) throw new TypeError(`Midscene config ${label} must be non-empty.`);
|
|
255
255
|
return value;
|
|
256
256
|
};
|
|
@@ -264,7 +264,9 @@ const validateOutput = (value)=>{
|
|
|
264
264
|
if (void 0 !== candidate.reportFormat && 'single-html' !== candidate.reportFormat && 'html-and-external-assets' !== candidate.reportFormat) throw new TypeError('Midscene config output.reportFormat must be single-html or html-and-external-assets.');
|
|
265
265
|
return Object.freeze({
|
|
266
266
|
reportFormat: candidate.reportFormat ?? 'single-html',
|
|
267
|
-
|
|
267
|
+
...void 0 === candidate.reportDir ? {} : {
|
|
268
|
+
reportDir: validateOutputPath(candidate.reportDir, 'output.reportDir')
|
|
269
|
+
}
|
|
268
270
|
});
|
|
269
271
|
};
|
|
270
272
|
const validateTestProjectDefinition = (definition)=>{
|
|
@@ -852,8 +854,16 @@ const DEFAULT_TEST_FILE_SELECTION = {
|
|
|
852
854
|
]
|
|
853
855
|
};
|
|
854
856
|
const project_preparation_toPosix = (value)=>value.split(sep).join('/');
|
|
855
|
-
const discoverResolvedTestFiles = (projectRoot, selection)=>{
|
|
857
|
+
const discoverResolvedTestFiles = (projectRoot, selection, excludedDirectories = [])=>{
|
|
856
858
|
const root = external_node_path_resolve(projectRoot);
|
|
859
|
+
const outputPatterns = excludedDirectories.flatMap((directory)=>{
|
|
860
|
+
const withinRoot = relative(root, directory);
|
|
861
|
+
if (!withinRoot || isAbsolute(withinRoot) || '..' === withinRoot || withinRoot.startsWith(`..${sep}`)) return [];
|
|
862
|
+
const literal = project_preparation_toPosix(withinRoot).replace(/([*?{}[\]()!])/g, '\\$1');
|
|
863
|
+
return [
|
|
864
|
+
`${literal}/**`
|
|
865
|
+
];
|
|
866
|
+
});
|
|
857
867
|
const match = (patterns)=>globSync(patterns, {
|
|
858
868
|
absolute: true,
|
|
859
869
|
caseSensitiveMatch: false,
|
|
@@ -863,6 +873,7 @@ const discoverResolvedTestFiles = (projectRoot, selection)=>{
|
|
|
863
873
|
followSymbolicLinks: false,
|
|
864
874
|
ignore: [
|
|
865
875
|
...ALWAYS_IGNORED_PATTERNS,
|
|
876
|
+
...outputPatterns,
|
|
866
877
|
...selection.exclude ?? []
|
|
867
878
|
],
|
|
868
879
|
onlyFiles: true
|
|
@@ -937,7 +948,7 @@ async function prepareProject(project, projectRoot, runDir, defaultTimeoutMs, op
|
|
|
937
948
|
const platform = options.platform?.trim();
|
|
938
949
|
const fileSelection = project.files ?? DEFAULT_TEST_FILE_SELECTION;
|
|
939
950
|
const prerequisiteFile = options.prerequisiteFile ? external_node_path_resolve(options.prerequisiteFile) : void 0;
|
|
940
|
-
const mainFiles = options.files ?? discoverResolvedTestFiles(projectRoot, fileSelection);
|
|
951
|
+
const mainFiles = options.files ?? discoverResolvedTestFiles(projectRoot, fileSelection, options.excludedDirectories);
|
|
941
952
|
const files = [
|
|
942
953
|
...prerequisiteFile ? [
|
|
943
954
|
prerequisiteFile
|
|
@@ -1102,40 +1113,61 @@ async function prepareTestRun(options) {
|
|
|
1102
1113
|
if (options.configPath && /\.ya?ml$/i.test(configPath)) throw new Error(`midscene-test --config only accepts a native TypeScript or JavaScript project config: ${configPath}. Run legacy batch YAML with \`midscene --config ${options.configPath}\`.`);
|
|
1103
1114
|
if (options.configPath && !existsSync(configPath)) throw new Error(`Midscene config does not exist: ${configPath}`);
|
|
1104
1115
|
const definition = await loadTestProject(configPath);
|
|
1105
|
-
const
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1116
|
+
const artifactDir = resolveMidsceneRunDir(input.projectRoot);
|
|
1117
|
+
return runWithMidsceneRunDir(artifactDir, async ()=>{
|
|
1118
|
+
const resultDir = options.resultDir ? external_node_path_resolve(input.cwd, options.resultDir) : join(artifactDir, 'test-results');
|
|
1119
|
+
const runDir = join(resultDir, runId);
|
|
1120
|
+
const summaryPath = join(runDir, 'summary.json');
|
|
1121
|
+
const reportDir = definition.output.reportDir ? external_node_path_resolve(input.projectRoot, definition.output.reportDir) : join(artifactDir, 'report');
|
|
1122
|
+
mkdirSync(resultDir, {
|
|
1123
|
+
recursive: true
|
|
1124
|
+
});
|
|
1125
|
+
mkdirSync(runDir);
|
|
1126
|
+
const projects = [];
|
|
1127
|
+
for (const project of selectProjects(definition.projects, options.projectNames))projects.push(await prepareProject(project, input.projectRoot, runDir, definition.test.testTimeout, {
|
|
1128
|
+
...input.singleFile ? {
|
|
1129
|
+
files: [
|
|
1130
|
+
input.singleFile
|
|
1131
|
+
]
|
|
1132
|
+
} : {},
|
|
1133
|
+
excludedDirectories: [
|
|
1134
|
+
artifactDir,
|
|
1135
|
+
resultDir,
|
|
1136
|
+
reportDir,
|
|
1137
|
+
...[
|
|
1138
|
+
'cache',
|
|
1139
|
+
'log',
|
|
1140
|
+
'output',
|
|
1141
|
+
'tmp',
|
|
1142
|
+
'dump'
|
|
1143
|
+
].map((name)=>join(artifactDir, name))
|
|
1144
|
+
]
|
|
1145
|
+
}));
|
|
1146
|
+
const hasFormatMismatch = projects.some((project)=>project.collectionErrors.some(({ error })=>error instanceof NativeWorkflowFormatError));
|
|
1147
|
+
return {
|
|
1148
|
+
startedAt,
|
|
1149
|
+
runId,
|
|
1150
|
+
projectRoot: input.projectRoot,
|
|
1151
|
+
artifactDir,
|
|
1152
|
+
...configPath ? {
|
|
1153
|
+
configPath
|
|
1154
|
+
} : {},
|
|
1155
|
+
resultDir,
|
|
1156
|
+
runDir,
|
|
1157
|
+
summaryPath,
|
|
1158
|
+
reportDir,
|
|
1159
|
+
definition,
|
|
1160
|
+
projects,
|
|
1161
|
+
preflightScope: hasFormatMismatch ? 'run' : 'project',
|
|
1162
|
+
reportEnabled: true,
|
|
1163
|
+
publications: [],
|
|
1164
|
+
onProgress: options.onProgress ?? (()=>{}),
|
|
1165
|
+
onEvent: options.onEvent
|
|
1166
|
+
};
|
|
1111
1167
|
});
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
files: [
|
|
1116
|
-
input.singleFile
|
|
1117
|
-
]
|
|
1118
|
-
} : void 0));
|
|
1119
|
-
const hasFormatMismatch = projects.some((project)=>project.collectionErrors.some(({ error })=>error instanceof NativeWorkflowFormatError));
|
|
1120
|
-
return {
|
|
1121
|
-
startedAt,
|
|
1122
|
-
runId,
|
|
1123
|
-
projectRoot: input.projectRoot,
|
|
1124
|
-
...configPath ? {
|
|
1125
|
-
configPath
|
|
1126
|
-
} : {},
|
|
1127
|
-
resultDir,
|
|
1128
|
-
runDir,
|
|
1129
|
-
summaryPath,
|
|
1130
|
-
reportDir,
|
|
1131
|
-
definition,
|
|
1132
|
-
projects,
|
|
1133
|
-
preflightScope: hasFormatMismatch ? 'run' : 'project',
|
|
1134
|
-
reportEnabled: true,
|
|
1135
|
-
publications: [],
|
|
1136
|
-
onProgress: options.onProgress ?? (()=>{}),
|
|
1137
|
-
onEvent: options.onEvent
|
|
1138
|
-
};
|
|
1168
|
+
}
|
|
1169
|
+
function renderTestBadge(summary) {
|
|
1170
|
+
return ``;
|
|
1139
1171
|
}
|
|
1140
1172
|
const result_markdown_text = (value)=>value.replaceAll('&', '&').replaceAll('<', '<').replaceAll('>', '>').replaceAll('"', '"').replaceAll('|', '|').replaceAll('[', '[').replaceAll(']', ']').replaceAll('*', '*').replaceAll('_', '_').replaceAll('`', '`').replaceAll('\\', '\').replace(/[\r\n]+/g, ' ');
|
|
1141
1173
|
const duration = (ms)=>{
|
|
@@ -1149,6 +1181,8 @@ function renderTestResultMarkdown(dump, options = {}) {
|
|
|
1149
1181
|
const lines = [
|
|
1150
1182
|
'## Midscene Test',
|
|
1151
1183
|
'',
|
|
1184
|
+
renderTestBadge(summary),
|
|
1185
|
+
'',
|
|
1152
1186
|
`${outcome} · ${duration(dump.durationMs)}`,
|
|
1153
1187
|
''
|
|
1154
1188
|
];
|
|
@@ -1881,7 +1915,7 @@ const latestById = (items, id)=>[
|
|
|
1881
1915
|
const notRunSuite = (prepared, reason)=>prepared.documents.flatMap((document)=>document.cases.map((item)=>asNotRun(document.documentId, item, prepared.project.name, reason)));
|
|
1882
1916
|
async function runPreparedTestProject(plan) {
|
|
1883
1917
|
const reports = new ReportSession(join(plan.runDir, 'records'));
|
|
1884
|
-
return reports.run(()=>runWithReportSession(plan, reports));
|
|
1918
|
+
return runWithMidsceneRunDir(plan.artifactDir, ()=>reports.run(()=>runWithReportSession(plan, reports)));
|
|
1885
1919
|
}
|
|
1886
1920
|
async function runWithReportSession(plan, reports) {
|
|
1887
1921
|
const { startedAt, runId, projectRoot, configPath, resultDir, runDir, summaryPath, reportDir, definition, projects: preparedProjects } = plan;
|