@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.
@@ -276,8 +276,7 @@ const validateTestOptions = (value, hasExplicitProjects)=>{
276
276
  testTimeout: validatePositiveInteger(candidate.testTimeout, 120000, 'test.testTimeout')
277
277
  });
278
278
  };
279
- const validateOutputPath = (value, fallback, label)=>{
280
- if (void 0 === value) return fallback;
279
+ const validateOutputPath = (value, label)=>{
281
280
  if ('string' != typeof value || 0 === value.trim().length) throw new TypeError(`Midscene config ${label} must be non-empty.`);
282
281
  return value;
283
282
  };
@@ -291,7 +290,9 @@ const validateOutput = (value)=>{
291
290
  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.');
292
291
  return Object.freeze({
293
292
  reportFormat: candidate.reportFormat ?? 'single-html',
294
- reportDir: validateOutputPath(candidate.reportDir, './midscene_run/report', 'output.reportDir')
293
+ ...void 0 === candidate.reportDir ? {} : {
294
+ reportDir: validateOutputPath(candidate.reportDir, 'output.reportDir')
295
+ }
295
296
  });
296
297
  };
297
298
  const validateTestProjectDefinition = (definition)=>{
@@ -884,8 +885,16 @@ const DEFAULT_TEST_FILE_SELECTION = {
884
885
  ]
885
886
  };
886
887
  const project_preparation_toPosix = (value)=>value.split(external_node_path_namespaceObject.sep).join('/');
887
- const discoverResolvedTestFiles = (projectRoot, selection)=>{
888
+ const discoverResolvedTestFiles = (projectRoot, selection, excludedDirectories = [])=>{
888
889
  const root = (0, external_node_path_namespaceObject.resolve)(projectRoot);
890
+ const outputPatterns = excludedDirectories.flatMap((directory)=>{
891
+ const withinRoot = (0, external_node_path_namespaceObject.relative)(root, directory);
892
+ if (!withinRoot || (0, external_node_path_namespaceObject.isAbsolute)(withinRoot) || '..' === withinRoot || withinRoot.startsWith(`..${external_node_path_namespaceObject.sep}`)) return [];
893
+ const literal = project_preparation_toPosix(withinRoot).replace(/([*?{}[\]()!])/g, '\\$1');
894
+ return [
895
+ `${literal}/**`
896
+ ];
897
+ });
889
898
  const match = (patterns)=>(0, external_tinyglobby_namespaceObject.globSync)(patterns, {
890
899
  absolute: true,
891
900
  caseSensitiveMatch: false,
@@ -895,6 +904,7 @@ const discoverResolvedTestFiles = (projectRoot, selection)=>{
895
904
  followSymbolicLinks: false,
896
905
  ignore: [
897
906
  ...ALWAYS_IGNORED_PATTERNS,
907
+ ...outputPatterns,
898
908
  ...selection.exclude ?? []
899
909
  ],
900
910
  onlyFiles: true
@@ -969,7 +979,7 @@ async function prepareProject(project, projectRoot, runDir, defaultTimeoutMs, op
969
979
  const platform = options.platform?.trim();
970
980
  const fileSelection = project.files ?? DEFAULT_TEST_FILE_SELECTION;
971
981
  const prerequisiteFile = options.prerequisiteFile ? (0, external_node_path_namespaceObject.resolve)(options.prerequisiteFile) : void 0;
972
- const mainFiles = options.files ?? discoverResolvedTestFiles(projectRoot, fileSelection);
982
+ const mainFiles = options.files ?? discoverResolvedTestFiles(projectRoot, fileSelection, options.excludedDirectories);
973
983
  const files = [
974
984
  ...prerequisiteFile ? [
975
985
  prerequisiteFile
@@ -1093,6 +1103,7 @@ async function prepareProject(project, projectRoot, runDir, defaultTimeoutMs, op
1093
1103
  filteredCaseCount
1094
1104
  };
1095
1105
  }
1106
+ const common_namespaceObject = require("@midscene/shared/common");
1096
1107
  const padDatePart = (value)=>String(value).padStart(2, '0');
1097
1108
  const createTestRunId = (date = new Date(), uuid = (0, external_node_crypto_namespaceObject.randomUUID)())=>{
1098
1109
  const timestamp = [
@@ -1134,44 +1145,65 @@ async function prepareTestRun(options) {
1134
1145
  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}\`.`);
1135
1146
  if (options.configPath && !(0, external_node_fs_namespaceObject.existsSync)(configPath)) throw new Error(`Midscene config does not exist: ${configPath}`);
1136
1147
  const definition = await loadTestProject(configPath);
1137
- const resultDir = options.resultDir ? (0, external_node_path_namespaceObject.resolve)(input.cwd, options.resultDir) : (0, external_node_path_namespaceObject.join)(input.projectRoot, '.midscene', 'test-results');
1138
- const runDir = (0, external_node_path_namespaceObject.join)(resultDir, runId);
1139
- const summaryPath = (0, external_node_path_namespaceObject.join)(runDir, 'summary.json');
1140
- const reportDir = (0, external_node_path_namespaceObject.resolve)(input.projectRoot, definition.output.reportDir);
1141
- (0, external_node_fs_namespaceObject.mkdirSync)(resultDir, {
1142
- recursive: true
1148
+ const artifactDir = (0, common_namespaceObject.resolveMidsceneRunDir)(input.projectRoot);
1149
+ return (0, common_namespaceObject.runWithMidsceneRunDir)(artifactDir, async ()=>{
1150
+ const resultDir = options.resultDir ? (0, external_node_path_namespaceObject.resolve)(input.cwd, options.resultDir) : (0, external_node_path_namespaceObject.join)(artifactDir, 'test-results');
1151
+ const runDir = (0, external_node_path_namespaceObject.join)(resultDir, runId);
1152
+ const summaryPath = (0, external_node_path_namespaceObject.join)(runDir, 'summary.json');
1153
+ const reportDir = definition.output.reportDir ? (0, external_node_path_namespaceObject.resolve)(input.projectRoot, definition.output.reportDir) : (0, external_node_path_namespaceObject.join)(artifactDir, 'report');
1154
+ (0, external_node_fs_namespaceObject.mkdirSync)(resultDir, {
1155
+ recursive: true
1156
+ });
1157
+ (0, external_node_fs_namespaceObject.mkdirSync)(runDir);
1158
+ const projects = [];
1159
+ for (const project of selectProjects(definition.projects, options.projectNames))projects.push(await prepareProject(project, input.projectRoot, runDir, definition.test.testTimeout, {
1160
+ ...input.singleFile ? {
1161
+ files: [
1162
+ input.singleFile
1163
+ ]
1164
+ } : {},
1165
+ excludedDirectories: [
1166
+ artifactDir,
1167
+ resultDir,
1168
+ reportDir,
1169
+ ...[
1170
+ 'cache',
1171
+ 'log',
1172
+ 'output',
1173
+ 'tmp',
1174
+ 'dump'
1175
+ ].map((name)=>(0, external_node_path_namespaceObject.join)(artifactDir, name))
1176
+ ]
1177
+ }));
1178
+ const hasFormatMismatch = projects.some((project)=>project.collectionErrors.some(({ error })=>error instanceof NativeWorkflowFormatError));
1179
+ return {
1180
+ startedAt,
1181
+ runId,
1182
+ projectRoot: input.projectRoot,
1183
+ artifactDir,
1184
+ ...configPath ? {
1185
+ configPath
1186
+ } : {},
1187
+ resultDir,
1188
+ runDir,
1189
+ summaryPath,
1190
+ reportDir,
1191
+ definition,
1192
+ projects,
1193
+ preflightScope: hasFormatMismatch ? 'run' : 'project',
1194
+ reportEnabled: true,
1195
+ publications: [],
1196
+ onProgress: options.onProgress ?? (()=>{}),
1197
+ onEvent: options.onEvent
1198
+ };
1143
1199
  });
1144
- (0, external_node_fs_namespaceObject.mkdirSync)(runDir);
1145
- const projects = [];
1146
- for (const project of selectProjects(definition.projects, options.projectNames))projects.push(await prepareProject(project, input.projectRoot, runDir, definition.test.testTimeout, input.singleFile ? {
1147
- files: [
1148
- input.singleFile
1149
- ]
1150
- } : void 0));
1151
- const hasFormatMismatch = projects.some((project)=>project.collectionErrors.some(({ error })=>error instanceof NativeWorkflowFormatError));
1152
- return {
1153
- startedAt,
1154
- runId,
1155
- projectRoot: input.projectRoot,
1156
- ...configPath ? {
1157
- configPath
1158
- } : {},
1159
- resultDir,
1160
- runDir,
1161
- summaryPath,
1162
- reportDir,
1163
- definition,
1164
- projects,
1165
- preflightScope: hasFormatMismatch ? 'run' : 'project',
1166
- reportEnabled: true,
1167
- publications: [],
1168
- onProgress: options.onProgress ?? (()=>{}),
1169
- onEvent: options.onEvent
1170
- };
1171
1200
  }
1172
1201
  const external_node_events_namespaceObject = require("node:events");
1173
1202
  const report_namespaceObject = require("@midscene/core/report");
1174
1203
  const logger_namespaceObject = require("@midscene/shared/logger");
1204
+ function renderTestBadge(summary) {
1205
+ return `![Midscene Test: ${summary.passed} passed, ${summary.failed} failed](https://badge.midscenejs.com/e2e?pass=${summary.passed}&fail=${summary.failed}&style=large)`;
1206
+ }
1175
1207
  const result_markdown_text = (value)=>value.replaceAll('&', '&amp;').replaceAll('<', '&lt;').replaceAll('>', '&gt;').replaceAll('"', '&quot;').replaceAll('|', '&#124;').replaceAll('[', '&#91;').replaceAll(']', '&#93;').replaceAll('*', '&#42;').replaceAll('_', '&#95;').replaceAll('`', '&#96;').replaceAll('\\', '&#92;').replace(/[\r\n]+/g, ' ');
1176
1208
  const duration = (ms)=>{
1177
1209
  const seconds = Math.round(ms / 1000);
@@ -1184,6 +1216,8 @@ function renderTestResultMarkdown(dump, options = {}) {
1184
1216
  const lines = [
1185
1217
  '## Midscene Test',
1186
1218
  '',
1219
+ renderTestBadge(summary),
1220
+ '',
1187
1221
  `${outcome} · ${duration(dump.durationMs)}`,
1188
1222
  ''
1189
1223
  ];
@@ -1918,7 +1952,7 @@ const latestById = (items, id)=>[
1918
1952
  const notRunSuite = (prepared, reason)=>prepared.documents.flatMap((document)=>document.cases.map((item)=>asNotRun(document.documentId, item, prepared.project.name, reason)));
1919
1953
  async function runPreparedTestProject(plan) {
1920
1954
  const reports = new report_namespaceObject.ReportSession((0, external_node_path_namespaceObject.join)(plan.runDir, 'records'));
1921
- return reports.run(()=>runWithReportSession(plan, reports));
1955
+ return (0, common_namespaceObject.runWithMidsceneRunDir)(plan.artifactDir, ()=>reports.run(()=>runWithReportSession(plan, reports)));
1922
1956
  }
1923
1957
  async function runWithReportSession(plan, reports) {
1924
1958
  const { startedAt, runId, projectRoot, configPath, resultDir, runDir, summaryPath, reportDir, definition, projects: preparedProjects } = plan;