@rstest/core 0.9.2 → 0.9.4

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/3145.js CHANGED
@@ -1,13 +1,13 @@
1
1
  import "node:module";
2
2
  import { __webpack_require__ } from "./rslib-runtime.js";
3
- import node_fs, { existsSync, readFileSync, statSync } from "node:fs";
3
+ import node_fs, { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
4
4
  import { loadConfig, mergeRsbuildConfig } from "@rsbuild/core";
5
5
  import { stripVTControlCharacters } from "node:util";
6
6
  import promises from "node:fs/promises";
7
7
  import node_path, { dirname as external_node_path_dirname, resolve as external_node_path_resolve } from "node:path";
8
8
  import { createRequire } from "node:module";
9
9
  import node_process from "node:process";
10
- import { basename, isTTY, dirname as pathe_M_eThtNZ_dirname, resolve as pathe_M_eThtNZ_resolve, relative, getAbsolutePath, join, bgColor, formatRootStr, determineAgent, logger as logger_logger, castArray, dist_m, prettyTime, isDebug, color, isAbsolute, getTaskNameWithPrefix, normalize, formatError } from "./6830.js";
10
+ import { basename, isTTY, dirname as pathe_M_eThtNZ_dirname, resolve as pathe_M_eThtNZ_resolve, relative, getAbsolutePath, join, bgColor, formatRootStr, determineAgent, logger as logger_logger, castArray, dist_m, prettyTime, isDebug, color as logger_color, isAbsolute, getTaskNameWithPrefix, normalize, formatError } from "./6830.js";
11
11
  import { isDynamicPattern, glob, DEFAULT_CONFIG_NAME, prettyTestPath, globalApis, TEMP_RSTEST_OUTPUT_DIR_GLOB, formatTestPath, filterProjects, DEFAULT_CONFIG_EXTENSIONS, TS_CONFIG_FILE } from "./4411.js";
12
12
  import { posix } from "./7011.js";
13
13
  import { parse as stack_trace_parser_esm_parse } from "./1672.js";
@@ -16,12 +16,14 @@ import "./5040.js";
16
16
  var init_namespaceObject = {};
17
17
  __webpack_require__.r(init_namespaceObject);
18
18
  __webpack_require__.d(init_namespaceObject, {
19
- initCli: ()=>init_initCli
19
+ initCli: ()=>init_initCli,
20
+ resolveProjects: ()=>resolveProjects
20
21
  });
21
- var src_core_namespaceObject = {};
22
- __webpack_require__.r(src_core_namespaceObject);
23
- __webpack_require__.d(src_core_namespaceObject, {
24
- createRstest: ()=>core_createRstest
22
+ var core_namespaceObject = {};
23
+ __webpack_require__.r(core_namespaceObject);
24
+ __webpack_require__.d(core_namespaceObject, {
25
+ createRstest: ()=>core_createRstest,
26
+ initCli: ()=>init_initCli
25
27
  });
26
28
  var error_namespaceObject = {};
27
29
  __webpack_require__.r(error_namespaceObject);
@@ -587,7 +589,7 @@ function prepareCli() {
587
589
  if (!npm_execpath || npm_execpath.includes('npx-cli.js') || npm_execpath.includes('.bun')) logger_logger.log();
588
590
  }
589
591
  function showRstest() {
590
- logger_logger.greet(" Rstest v0.9.2");
592
+ logger_logger.greet(" Rstest v0.9.4");
591
593
  logger_logger.log('');
592
594
  }
593
595
  const applyCommonOptions = (cli)=>{
@@ -602,15 +604,26 @@ const handleUnexpectedExit = (rstest, err)=>{
602
604
  logger_logger.error(formatError(err));
603
605
  process.exit(1);
604
606
  };
607
+ const resolveCliRuntime = async (options)=>{
608
+ const [{ initCli }, { createRstest }] = await Promise.all([
609
+ Promise.resolve(init_namespaceObject),
610
+ Promise.resolve(core_namespaceObject)
611
+ ]);
612
+ const { config, configFilePath, projects } = await initCli(options);
613
+ return {
614
+ config,
615
+ configFilePath,
616
+ projects,
617
+ createRstest
618
+ };
619
+ };
605
620
  const runRest = async ({ options, filters, command })=>{
606
621
  let rstest;
607
622
  const unexpectedlyExitHandler = (err)=>{
608
623
  handleUnexpectedExit(rstest, err);
609
624
  };
610
625
  try {
611
- const { initCli } = await Promise.resolve(init_namespaceObject);
612
- const { config, configFilePath, projects } = await initCli(options);
613
- const { createRstest } = await Promise.resolve(src_core_namespaceObject);
626
+ const { config, configFilePath, projects, createRstest } = await resolveCliRuntime(options);
614
627
  rstest = createRstest({
615
628
  config,
616
629
  configFilePath,
@@ -638,7 +651,7 @@ const runRest = async ({ options, filters, command })=>{
638
651
  function setupCommands() {
639
652
  const cli = cac('rstest');
640
653
  cli.help();
641
- cli.version("0.9.2");
654
+ cli.version("0.9.4");
642
655
  applyCommonOptions(cli);
643
656
  cli.command('[...filters]', 'run tests').option('-w, --watch', 'Run tests in watch mode').action(async (filters, options)=>{
644
657
  if (!determineAgent().isAgent) showRstest();
@@ -671,10 +684,8 @@ function setupCommands() {
671
684
  });
672
685
  cli.command('list [...filters]', 'lists all test files that Rstest will run').option('--filesOnly', 'only list the test files').option('--json [boolean/path]', 'print tests as JSON or write to a file').option('--includeSuites', 'include suites in output').option('--printLocation', 'print test case location').action(async (filters, options)=>{
673
686
  try {
674
- const { initCli } = await Promise.resolve(init_namespaceObject);
675
- const { config, configFilePath, projects } = await initCli(options);
687
+ const { config, configFilePath, projects, createRstest } = await resolveCliRuntime(options);
676
688
  if (options.printLocation) config.includeTaskLocation = true;
677
- const { createRstest } = await Promise.resolve(src_core_namespaceObject);
678
689
  const rstest = createRstest({
679
690
  config,
680
691
  configFilePath,
@@ -692,6 +703,25 @@ function setupCommands() {
692
703
  process.exit(1);
693
704
  }
694
705
  });
706
+ cli.command('merge-reports [path]', 'Merge blob reports from multiple shards into a unified report').option('--cleanup', 'Remove blob reports directory after merging').action(async (path, options)=>{
707
+ if (!determineAgent().isAgent) showRstest();
708
+ try {
709
+ const { config, configFilePath, projects, createRstest } = await resolveCliRuntime(options);
710
+ const rstest = createRstest({
711
+ config,
712
+ configFilePath,
713
+ projects
714
+ }, 'merge-reports', []);
715
+ await rstest.mergeReports({
716
+ path,
717
+ cleanup: options.cleanup
718
+ });
719
+ } catch (err) {
720
+ logger_logger.error('Failed to merge reports.');
721
+ logger_logger.error(formatError(err));
722
+ process.exit(1);
723
+ }
724
+ });
695
725
  cli.command('init [project]', 'Initialize rstest configuration').option('--yes', 'Use default options (non-interactive)').action(async (project, options)=>{
696
726
  try {
697
727
  let selectedProject = project;
@@ -709,7 +739,7 @@ function setupCommands() {
709
739
  ]
710
740
  });
711
741
  if (isCancel(selected)) {
712
- console.log(color.yellow('Operation cancelled.'));
742
+ console.log(logger_color.yellow('Operation cancelled.'));
713
743
  process.exit(0);
714
744
  }
715
745
  selectedProject = selected;
@@ -736,7 +766,7 @@ const resolveConfigPath = (root, customConfig)=>{
736
766
  if (customConfig) {
737
767
  const customConfigPath = isAbsolute(customConfig) ? customConfig : join(root, customConfig);
738
768
  if (node_fs.existsSync(customConfigPath)) return customConfigPath;
739
- throw `Cannot find config file: ${color.dim(customConfigPath)}`;
769
+ throw `Cannot find config file: ${logger_color.dim(customConfigPath)}`;
740
770
  }
741
771
  const configFilePath = findConfig(join(root, DEFAULT_CONFIG_NAME));
742
772
  if (configFilePath) return configFilePath;
@@ -758,18 +788,29 @@ async function config_loadConfig({ cwd = process.cwd(), path, envMode, configLoa
758
788
  loader: configLoader
759
789
  });
760
790
  let config = content;
761
- if (config.extends) {
762
- const extendsConfig = 'function' == typeof config.extends ? await config.extends(Object.freeze({
763
- ...config
764
- })) : config.extends;
765
- delete extendsConfig.projects;
766
- config = mergeRstestConfig(extendsConfig, config);
767
- }
791
+ config = await resolveExtends(config);
768
792
  return {
769
793
  content: config,
770
794
  filePath: configFilePath
771
795
  };
772
796
  }
797
+ const resolveExtendEntry = async (entry, userConfig)=>{
798
+ const resolved = 'function' == typeof entry ? await entry(userConfig) : entry;
799
+ if ('projects' in resolved) {
800
+ const { projects: _projects, ...rest } = resolved;
801
+ return rest;
802
+ }
803
+ return resolved;
804
+ };
805
+ const resolveExtends = async (config)=>{
806
+ if (!config.extends) return config;
807
+ const userConfig = Object.freeze({
808
+ ...config
809
+ });
810
+ const extendsEntries = castArray(config.extends);
811
+ const resolvedExtends = await Promise.all(extendsEntries.map((entry)=>resolveExtendEntry(entry, userConfig)));
812
+ return mergeRstestConfig(...resolvedExtends, config);
813
+ };
773
814
  const mergeProjectConfig = (...configs)=>mergeRstestConfig(...configs);
774
815
  const mergeRstestConfig = (...configs)=>configs.reduce((result, config)=>{
775
816
  const merged = mergeRsbuildConfig(result, {
@@ -848,7 +889,8 @@ const createDefaultConfig = ()=>({
848
889
  provider: 'playwright',
849
890
  browser: 'chromium',
850
891
  headless: dist_m,
851
- strictPort: false
892
+ strictPort: false,
893
+ providerOptions: {}
852
894
  },
853
895
  coverage: {
854
896
  exclude: [
@@ -872,7 +914,8 @@ const createDefaultConfig = ()=>({
872
914
  ],
873
915
  reportsDirectory: './coverage',
874
916
  clean: true,
875
- reportOnFailure: false
917
+ reportOnFailure: false,
918
+ allowExternal: false
876
919
  }
877
920
  });
878
921
  const withDefaultConfig = (config)=>{
@@ -895,7 +938,8 @@ const withDefaultConfig = (config)=>{
895
938
  headless: merged.browser?.headless ?? dist_m,
896
939
  port: merged.browser?.port,
897
940
  strictPort: merged.browser?.strictPort ?? false,
898
- viewport: merged.browser?.viewport
941
+ viewport: merged.browser?.viewport,
942
+ providerOptions: merged.browser?.providerOptions ?? {}
899
943
  };
900
944
  return {
901
945
  ...merged,
@@ -947,7 +991,11 @@ function mergeWithCLIOptions(config, options) {
947
991
  if (void 0 !== options.bail && ('number' == typeof options.bail || 'boolean' == typeof options.bail)) config.bail = Number(options.bail);
948
992
  if (void 0 !== options.coverage) {
949
993
  config.coverage ??= {};
950
- config.coverage.enabled = options.coverage;
994
+ if ('boolean' == typeof options.coverage) config.coverage.enabled = options.coverage;
995
+ else {
996
+ if (void 0 !== options.coverage.enabled) config.coverage.enabled = options.coverage.enabled;
997
+ if (void 0 !== options.coverage.allowExternal) config.coverage.allowExternal = options.coverage.allowExternal;
998
+ }
951
999
  }
952
1000
  if (options.exclude) config.exclude = castArray(options.exclude);
953
1001
  if (options.include) config.include = castArray(options.include);
@@ -1028,29 +1076,23 @@ async function resolveProjects({ config, root, options }) {
1028
1076
  const getProjects = async (rstestConfig, root)=>{
1029
1077
  const projectPaths = [];
1030
1078
  const projectPatterns = [];
1031
- const projectConfigs = [];
1032
- await Promise.all((rstestConfig.projects || []).map(async (p)=>{
1079
+ const inlineProjectConfigPromises = [];
1080
+ for (const p of rstestConfig.projects || []){
1033
1081
  if ('object' == typeof p) {
1034
1082
  const projectRoot = p.root ? formatRootStr(p.root, root) : root;
1035
- let projectConfig = {
1083
+ inlineProjectConfigPromises.push(resolveExtends({
1036
1084
  ...p
1037
- };
1038
- if (projectConfig.extends) {
1039
- const extendsConfig = 'function' == typeof projectConfig.extends ? await projectConfig.extends(Object.freeze({
1040
- ...projectConfig
1041
- })) : projectConfig.extends;
1042
- delete extendsConfig.projects;
1043
- projectConfig = mergeRstestConfig(extendsConfig, projectConfig);
1044
- }
1045
- projectConfigs.push({
1046
- config: mergeWithCLIOptions({
1047
- root: projectRoot,
1048
- ...projectConfig,
1049
- name: p.name ? p.name : getDefaultProjectName(projectRoot)
1050
- }, options),
1051
- configFilePath: void 0
1052
- });
1053
- return;
1085
+ }).then((projectConfig)=>({
1086
+ config: mergeWithCLIOptions({
1087
+ root: projectRoot,
1088
+ ...projectConfig,
1089
+ name: p.name ? p.name : getDefaultProjectName(projectRoot)
1090
+ }, options),
1091
+ configFilePath: void 0
1092
+ }), (error)=>({
1093
+ error
1094
+ })));
1095
+ continue;
1054
1096
  }
1055
1097
  const projectStr = formatRootStr(p, root);
1056
1098
  if (isDynamicPattern(projectStr)) projectPatterns.push(projectStr);
@@ -1059,8 +1101,16 @@ async function resolveProjects({ config, root, options }) {
1059
1101
  if (!existsSync(absolutePath)) throw `Can't resolve project "${p}", please make sure "${p}" is a existing file or a directory.`;
1060
1102
  projectPaths.push(absolutePath);
1061
1103
  }
1062
- }));
1063
- projectPaths.push(...await globProjects(projectPatterns, root));
1104
+ }
1105
+ const [inlineProjectConfigResults, globbedProjectPaths] = await Promise.all([
1106
+ Promise.all(inlineProjectConfigPromises),
1107
+ globProjects(projectPatterns, root)
1108
+ ]);
1109
+ const projectConfigs = inlineProjectConfigResults.map((result)=>{
1110
+ if ('error' in result) throw result.error;
1111
+ return result;
1112
+ });
1113
+ projectPaths.push(...globbedProjectPaths);
1064
1114
  const projects = [];
1065
1115
  await Promise.all(projectPaths.map(async (project)=>{
1066
1116
  const isDirectory = statSync(project).isDirectory();
@@ -1088,8 +1138,8 @@ async function resolveProjects({ config, root, options }) {
1088
1138
  const projects = await getProjects(config, root).then((p)=>filterProjects(p, options));
1089
1139
  if (!projects.length) {
1090
1140
  let errorMsg = `No projects found, please make sure you have at least one valid project.
1091
- ${color.gray('projects:')} ${JSON.stringify(config.projects, null, 2)}`;
1092
- if (options.project) errorMsg += `\n${color.gray('projectName filter:')} ${JSON.stringify(options.project, null, 2)}`;
1141
+ ${logger_color.gray('projects:')} ${JSON.stringify(config.projects, null, 2)}`;
1142
+ if (options.project) errorMsg += `\n${logger_color.gray('projectName filter:')} ${JSON.stringify(options.project, null, 2)}`;
1093
1143
  throw errorMsg;
1094
1144
  }
1095
1145
  const names = new Set();
@@ -1195,53 +1245,53 @@ function addSnapshotResult(summary, result) {
1195
1245
  summary.total += result.added + result.matched + result.unmatched + result.updated;
1196
1246
  }
1197
1247
  const getSummaryStatusString = (tasks, name = 'tests', showTotal = true)=>{
1198
- if (0 === tasks.length) return color.dim(`no ${name}`);
1248
+ if (0 === tasks.length) return logger_color.dim(`no ${name}`);
1199
1249
  const passed = tasks.filter((result)=>'pass' === result.status);
1200
1250
  const failed = tasks.filter((result)=>'fail' === result.status);
1201
1251
  const skipped = tasks.filter((result)=>'skip' === result.status);
1202
1252
  const todo = tasks.filter((result)=>'todo' === result.status);
1203
1253
  const status = [
1204
- failed.length ? color.bold(color.red(`${failed.length} failed`)) : null,
1205
- passed.length ? color.bold(color.green(`${passed.length} passed`)) : null,
1206
- skipped.length ? color.yellow(`${skipped.length} skipped`) : null,
1207
- todo.length ? color.gray(`${todo.length} todo`) : null
1254
+ failed.length ? logger_color.bold(logger_color.red(`${failed.length} failed`)) : null,
1255
+ passed.length ? logger_color.bold(logger_color.green(`${passed.length} passed`)) : null,
1256
+ skipped.length ? logger_color.yellow(`${skipped.length} skipped`) : null,
1257
+ todo.length ? logger_color.gray(`${todo.length} todo`) : null
1208
1258
  ].filter(Boolean);
1209
- return status.join(color.dim(' | ')) + (showTotal && status.length > 1 ? color.gray(` (${tasks.length})`) : '');
1259
+ return status.join(logger_color.dim(' | ')) + (showTotal && status.length > 1 ? logger_color.gray(` (${tasks.length})`) : '');
1210
1260
  };
1211
1261
  const printSnapshotSummaryLog = (snapshots, rootDir)=>{
1212
1262
  const summary = [];
1213
- if (snapshots.added) summary.push(color.bold(color.green(`${snapshots.added} written`)));
1214
- if (snapshots.unmatched) summary.push(color.bold(color.red(`${snapshots.unmatched} failed`)));
1215
- if (snapshots.updated) summary.push(color.bold(color.green(`${snapshots.updated} updated `)));
1216
- if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(color.bold(color.green(`${snapshots.filesRemoved} files removed `)));
1217
- else summary.push(color.bold(color.yellow(`${snapshots.filesRemoved} files obsolete `)));
1263
+ if (snapshots.added) summary.push(logger_color.bold(logger_color.green(`${snapshots.added} written`)));
1264
+ if (snapshots.unmatched) summary.push(logger_color.bold(logger_color.red(`${snapshots.unmatched} failed`)));
1265
+ if (snapshots.updated) summary.push(logger_color.bold(logger_color.green(`${snapshots.updated} updated `)));
1266
+ if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(logger_color.bold(logger_color.green(`${snapshots.filesRemoved} files removed `)));
1267
+ else summary.push(logger_color.bold(logger_color.yellow(`${snapshots.filesRemoved} files obsolete `)));
1218
1268
  if (snapshots.filesRemovedList?.length) {
1219
1269
  const [head, ...tail] = snapshots.filesRemovedList;
1220
- summary.push(`${color.gray("➜")} ${formatTestPath(rootDir, head)}`);
1270
+ summary.push(`${logger_color.gray("➜")} ${formatTestPath(rootDir, head)}`);
1221
1271
  for (const key of tail)summary.push(` ${formatTestPath(rootDir, key)}`);
1222
1272
  }
1223
1273
  if (snapshots.unchecked) {
1224
- if (snapshots.didUpdate) summary.push(color.bold(color.green(`${snapshots.unchecked} removed`)));
1225
- else summary.push(color.bold(color.yellow(`${snapshots.unchecked} obsolete`)));
1274
+ if (snapshots.didUpdate) summary.push(logger_color.bold(logger_color.green(`${snapshots.unchecked} removed`)));
1275
+ else summary.push(logger_color.bold(logger_color.yellow(`${snapshots.unchecked} obsolete`)));
1226
1276
  for (const uncheckedFile of snapshots.uncheckedKeysByFile){
1227
- summary.push(`${color.gray("➜")} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
1277
+ summary.push(`${logger_color.gray("➜")} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
1228
1278
  for (const key of uncheckedFile.keys)summary.push(` ${key}`);
1229
1279
  }
1230
1280
  }
1231
1281
  for (const [index, snapshot] of summary.entries()){
1232
1282
  const title = 0 === index ? 'Snapshots' : '';
1233
- logger_logger.log(`${color.gray(title.padStart(12))} ${snapshot}`);
1283
+ logger_logger.log(`${logger_color.gray(title.padStart(12))} ${snapshot}`);
1234
1284
  }
1235
1285
  };
1236
- const TestFileSummaryLabel = color.gray('Test Files'.padStart(11));
1237
- const TestSummaryLabel = color.gray('Tests'.padStart(11));
1238
- const DurationLabel = color.gray('Duration'.padStart(11));
1286
+ const TestFileSummaryLabel = logger_color.gray('Test Files'.padStart(11));
1287
+ const TestSummaryLabel = logger_color.gray('Tests'.padStart(11));
1288
+ const DurationLabel = logger_color.gray('Duration'.padStart(11));
1239
1289
  const printSummaryLog = ({ results, testResults, snapshotSummary, duration, rootPath })=>{
1240
1290
  logger_logger.log('');
1241
1291
  printSnapshotSummaryLog(snapshotSummary, rootPath);
1242
1292
  logger_logger.log(`${TestFileSummaryLabel} ${getSummaryStatusString(results)}`);
1243
1293
  logger_logger.log(`${TestSummaryLabel} ${getSummaryStatusString(testResults)}`);
1244
- logger_logger.log(`${DurationLabel} ${prettyTime(duration.totalTime)} ${color.gray(`(build ${prettyTime(duration.buildTime)}, tests ${prettyTime(duration.testTime)})`)}`);
1294
+ logger_logger.log(`${DurationLabel} ${prettyTime(duration.totalTime)} ${logger_color.gray(`(build ${prettyTime(duration.buildTime)}, tests ${prettyTime(duration.testTime)})`)}`);
1245
1295
  logger_logger.log('');
1246
1296
  };
1247
1297
  const printSummaryErrorLogs = async ({ testResults, results, rootPath, unhandledErrors, getSourcemap, filterRerunTestPaths })=>{
@@ -1251,7 +1301,7 @@ const printSummaryErrorLogs = async ({ testResults, results, rootPath, unhandled
1251
1301
  ];
1252
1302
  if (0 === failedTests.length && !unhandledErrors?.length) return;
1253
1303
  logger_logger.stderr('');
1254
- logger_logger.stderr(color.bold('Summary of all failing tests:'));
1304
+ logger_logger.stderr(logger_color.bold('Summary of all failing tests:'));
1255
1305
  logger_logger.stderr('');
1256
1306
  const { printError } = await Promise.resolve(error_namespaceObject);
1257
1307
  for (const error of unhandledErrors || []){
@@ -1261,7 +1311,7 @@ const printSummaryErrorLogs = async ({ testResults, results, rootPath, unhandled
1261
1311
  for (const test of failedTests){
1262
1312
  const relativePath = posix.relative(rootPath, test.testPath);
1263
1313
  const nameStr = getTaskNameWithPrefix(test);
1264
- logger_logger.stderr(`${bgColor('bgRed', ' FAIL ')} ${prettyTestPath(relativePath)} ${nameStr.length ? `${color.dim(">")} ${nameStr}` : ''}`);
1314
+ logger_logger.stderr(`${bgColor('bgRed', ' FAIL ')} ${prettyTestPath(relativePath)} ${nameStr.length ? `${logger_color.dim(">")} ${nameStr}` : ''}`);
1265
1315
  if (test.errors) {
1266
1316
  const { printError } = await Promise.resolve(error_namespaceObject);
1267
1317
  for (const error of test.errors)await printError(error, getSourcemap, rootPath);
@@ -1418,14 +1468,14 @@ class StatusRenderer {
1418
1468
  const relativePath = relative(this.rootPath, module);
1419
1469
  summary.push(`${bgColor('bgYellow', ' RUNS ')} ${prettyTestPath(relativePath)}`);
1420
1470
  if (runningTests.length && shouldDisplayRunningTests(runningTests)) {
1421
- let caseLog = ` ${color.gray("➜")} ${getTaskNameWithPrefix(runningTests[0])} ${color.magenta(prettyTime(now - runningTests[0].startTime))}`;
1422
- if (runningTests.length > 1) caseLog += color.gray(` and ${runningTests.length - 1} more cases`);
1471
+ let caseLog = ` ${logger_color.gray("➜")} ${getTaskNameWithPrefix(runningTests[0])} ${logger_color.magenta(prettyTime(now - runningTests[0].startTime))}`;
1472
+ if (runningTests.length > 1) caseLog += logger_color.gray(` and ${runningTests.length - 1} more cases`);
1423
1473
  summary.push(caseLog);
1424
1474
  }
1425
1475
  }
1426
1476
  summary.push('');
1427
1477
  if (0 === testModules.length) summary.push(`${TestFileSummaryLabel} ${runningModules.size} total`);
1428
- else summary.push(`${TestFileSummaryLabel} ${getSummaryStatusString(testModules, '', false)} ${color.dim('|')} ${runningModules.size + testModules.length} total`);
1478
+ else summary.push(`${TestFileSummaryLabel} ${getSummaryStatusString(testModules, '', false)} ${logger_color.dim('|')} ${runningModules.size + testModules.length} total`);
1429
1479
  const testResults = Array.from(runningModules.values()).flatMap(({ results })=>results).concat(testModules.flatMap((mod)=>mod.results));
1430
1480
  if (testResults.length) summary.push(`${TestSummaryLabel} ${getSummaryStatusString(testResults, '', false)}`);
1431
1481
  summary.push(`${DurationLabel} ${prettyTime(Date.now() - this.startTime)}`);
@@ -1453,10 +1503,10 @@ const statusStr = {
1453
1503
  skip: '-'
1454
1504
  };
1455
1505
  const statusColor = {
1456
- fail: color.red,
1457
- pass: color.green,
1458
- todo: color.gray,
1459
- skip: color.gray
1506
+ fail: logger_color.red,
1507
+ pass: logger_color.green,
1508
+ todo: logger_color.gray,
1509
+ skip: logger_color.gray
1460
1510
  };
1461
1511
  const statusColorfulStr = {
1462
1512
  fail: statusColor.fail(statusStr.fail),
@@ -1467,23 +1517,23 @@ const statusColorfulStr = {
1467
1517
  const logCase = (result, options)=>{
1468
1518
  const isSlowCase = (result.duration || 0) > options.slowTestThreshold;
1469
1519
  if (options.hideSkippedTests && 'skip' === result.status) return;
1470
- const icon = isSlowCase && 'pass' === result.status ? color.yellow(statusStr[result.status]) : statusColorfulStr[result.status];
1520
+ const icon = isSlowCase && 'pass' === result.status ? logger_color.yellow(statusStr[result.status]) : statusColorfulStr[result.status];
1471
1521
  const nameStr = getTaskNameWithPrefix(result);
1472
1522
  const duration = void 0 !== result.duration ? ` (${prettyTime(result.duration)})` : '';
1473
- const retry = result.retryCount ? color.yellow(` (retry x${result.retryCount})`) : '';
1474
- const heap = result.heap ? ` ${color.magenta(formatHeapUsed(result.heap))}` : '';
1475
- logger_logger.log(` ${icon} ${nameStr}${color.gray(duration)}${retry}${heap}`);
1476
- if (result.errors) for (const error of result.errors)logger_logger.log(color.red(` ${error.message}`));
1523
+ const retry = result.retryCount ? logger_color.yellow(` (retry x${result.retryCount})`) : '';
1524
+ const heap = result.heap ? ` ${logger_color.magenta(formatHeapUsed(result.heap))}` : '';
1525
+ logger_logger.log(` ${icon} ${nameStr}${logger_color.gray(duration)}${retry}${heap}`);
1526
+ if (result.errors) for (const error of result.errors)logger_logger.log(logger_color.red(` ${error.message}`));
1477
1527
  };
1478
1528
  const formatHeapUsed = (heap)=>`${Math.floor(heap / 1024 / 1024)} MB heap used`;
1479
1529
  const logFileTitle = (test, relativePath, alwaysShowTime = false, showProjectName = false)=>{
1480
- let title = ` ${color.bold(statusColorfulStr[test.status])}`;
1530
+ let title = ` ${logger_color.bold(statusColorfulStr[test.status])}`;
1481
1531
  if (showProjectName && test.project) title += ` ${statusColor[test.status](`[${test.project}]`)}`;
1482
1532
  title += ` ${prettyTestPath(relativePath)}`;
1483
- const formatDuration = (duration)=>color.green(prettyTime(duration));
1484
- title += ` ${color.gray(`(${test.results.length})`)}`;
1533
+ const formatDuration = (duration)=>logger_color.green(prettyTime(duration));
1534
+ title += ` ${logger_color.gray(`(${test.results.length})`)}`;
1485
1535
  if (alwaysShowTime) title += ` ${formatDuration(test.duration)}`;
1486
- if (test.heap) title += ` ${color.magenta(formatHeapUsed(test.heap))}`;
1536
+ if (test.heap) title += ` ${logger_color.magenta(formatHeapUsed(test.heap))}`;
1487
1537
  logger_logger.log(title);
1488
1538
  };
1489
1539
  class DefaultReporter {
@@ -1538,7 +1588,7 @@ class DefaultReporter {
1538
1588
  } else titles.push(testPath);
1539
1589
  const logOutput = 'stdout' === log.type ? logger_logger.log : logger_logger.stderr;
1540
1590
  logOutput('');
1541
- logOutput(`${log.name}${color.gray(color.dim(` | ${titles.join(color.gray(color.dim(' | ')))}`))}`);
1591
+ logOutput(`${log.name}${logger_color.gray(logger_color.dim(` | ${titles.join(logger_color.gray(logger_color.dim(' | ')))}`))}`);
1542
1592
  logOutput(log.content);
1543
1593
  logOutput('');
1544
1594
  }
@@ -1565,6 +1615,44 @@ class DefaultReporter {
1565
1615
  });
1566
1616
  }
1567
1617
  }
1618
+ const DEFAULT_OUTPUT_DIR = '.rstest-reports';
1619
+ class BlobReporter {
1620
+ config;
1621
+ outputDir;
1622
+ consoleLogs = [];
1623
+ constructor({ rootPath, config, options }){
1624
+ this.config = config;
1625
+ this.outputDir = options?.outputDir ? join(rootPath, options.outputDir) : join(rootPath, DEFAULT_OUTPUT_DIR);
1626
+ }
1627
+ onUserConsoleLog(log) {
1628
+ this.consoleLogs.push(log);
1629
+ }
1630
+ async onTestRunEnd({ results, testResults, duration, snapshotSummary, unhandledErrors }) {
1631
+ const shard = this.config.shard;
1632
+ const fileName = shard ? `blob-${shard.index}-${shard.count}.json` : 'blob.json';
1633
+ const blobData = {
1634
+ version: "0.9.4",
1635
+ shard: shard ? {
1636
+ index: shard.index,
1637
+ count: shard.count
1638
+ } : void 0,
1639
+ results,
1640
+ testResults,
1641
+ duration,
1642
+ snapshotSummary,
1643
+ unhandledErrors: unhandledErrors?.map((e)=>({
1644
+ message: e.message,
1645
+ stack: e.stack,
1646
+ name: e.name
1647
+ })),
1648
+ consoleLogs: this.consoleLogs.length > 0 ? this.consoleLogs : void 0
1649
+ };
1650
+ mkdirSync(this.outputDir, {
1651
+ recursive: true
1652
+ });
1653
+ writeFileSync(join(this.outputDir, fileName), JSON.stringify(blobData), 'utf-8');
1654
+ }
1655
+ }
1568
1656
  class GithubActionsReporter {
1569
1657
  onWritePath;
1570
1658
  rootPath;
@@ -1619,15 +1707,15 @@ function escapeData(s) {
1619
1707
  }
1620
1708
  function ansiRegex({ onlyFirst = false } = {}) {
1621
1709
  const ST = '(?:\\u0007|\\u001B\\u005C|\\u009C)';
1622
- const pattern = [
1623
- `[\\u001B\\u009B][[\\]()#;?]*(?:(?:(?:(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]+)*|[a-zA-Z\\d]+(?:;[-a-zA-Z\\d\\/#&.:=?%@~_]*)*)?${ST})`,
1624
- '(?:(?:\\d{1,4}(?:;\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]))'
1625
- ].join('|');
1710
+ const osc = `(?:\\u001B\\][\\s\\S]*?${ST})`;
1711
+ const csi = '[\\u001B\\u009B][[\\]()#;?]*(?:\\d{1,4}(?:[;:]\\d{0,4})*)?[\\dA-PR-TZcf-nq-uy=><~]';
1712
+ const pattern = `${osc}|${csi}`;
1626
1713
  return new RegExp(pattern, onlyFirst ? void 0 : 'g');
1627
1714
  }
1628
1715
  const regex = ansiRegex();
1629
1716
  function stripAnsi(string) {
1630
1717
  if ('string' != typeof string) throw new TypeError(`Expected a \`string\`, got \`${typeof string}\``);
1718
+ if (!string.includes('\u001B') && !string.includes('\u009B')) return string;
1631
1719
  return string.replace(regex, '');
1632
1720
  }
1633
1721
  const schemeRegex = /^[\w+.-]+:\/\//;
@@ -1941,7 +2029,7 @@ const hintNotDefinedError = (message)=>{
1941
2029
  'jest',
1942
2030
  'vitest'
1943
2031
  ].includes(varName)) return message.replace(`${varName} is not defined`, `${varName} is not defined. Did you mean rstest?`);
1944
- if ('React' === varName) return message.replace(`${varName} is not defined`, `${varName} is not defined. Did you forget to install "${color.yellow('@rsbuild/plugin-react')}" plugin?`);
2032
+ if ('React' === varName) return message.replace(`${varName} is not defined`, `${varName} is not defined. Did you forget to install "${logger_color.yellow('@rsbuild/plugin-react')}" plugin?`);
1945
2033
  }
1946
2034
  return message;
1947
2035
  };
@@ -1951,14 +2039,14 @@ async function error_printError(error, getSourcemap, rootPath) {
1951
2039
  const tips = [
1952
2040
  'Error: not support import `vitest` in Rstest test environment.\n',
1953
2041
  'Solution:',
1954
- ` - Update your code to use imports from "${color.yellow('@rstest/core')}" instead of "${color.yellow('vitest')}".`,
2042
+ ` - Update your code to use imports from "${logger_color.yellow('@rstest/core')}" instead of "${logger_color.yellow('vitest')}".`,
1955
2043
  ' - Enable `globals` configuration and use global API.'
1956
2044
  ];
1957
- logger_logger.stderr(`${color.red(tips.join('\n'))}\n`);
2045
+ logger_logger.stderr(`${logger_color.red(tips.join('\n'))}\n`);
1958
2046
  return;
1959
2047
  }
1960
2048
  if (error.message.includes('is not defined')) error.message = hintNotDefinedError(error.message);
1961
- logger_logger.stderr(`${color.red(color.bold(errorName))}${color.red(`: ${error.message}`)}\n`);
2049
+ logger_logger.stderr(`${logger_color.red(logger_color.bold(errorName))}${logger_color.red(`: ${error.message}`)}\n`);
1962
2050
  if (error.diff) {
1963
2051
  logger_logger.stderr(error.diff);
1964
2052
  logger_logger.stderr('');
@@ -1969,7 +2057,7 @@ async function error_printError(error, getSourcemap, rootPath) {
1969
2057
  fullStack: error.fullStack,
1970
2058
  getSourcemap
1971
2059
  });
1972
- if (!stackFrames.length && !(error.fullStack || isDebug()) && !error.stack.endsWith(error.message)) logger_logger.stderr(color.gray("No error stack found, set 'DEBUG=rstest' to show fullStack."));
2060
+ if (!stackFrames.length && !(error.fullStack || isDebug()) && !error.stack.endsWith(error.message)) logger_logger.stderr(logger_color.gray("No error stack found, set 'DEBUG=rstest' to show fullStack."));
1973
2061
  if (stackFrames[0]) await printCodeFrame(stackFrames[0]);
1974
2062
  printStack(stackFrames, rootPath);
1975
2063
  }
@@ -1996,7 +2084,7 @@ function formatStack(frame, rootPath) {
1996
2084
  return '<unknown>' !== frame.methodName ? `at ${frame.methodName} (${formatTestPath(rootPath, frame.file)}:${frame.lineNumber}:${frame.column})` : `at ${formatTestPath(rootPath, frame.file)}:${frame.lineNumber}:${frame.column}`;
1997
2085
  }
1998
2086
  function printStack(stackFrames, rootPath) {
1999
- for (const frame of stackFrames)logger_logger.stderr(color.gray(` ${formatStack(frame, rootPath)}`));
2087
+ for (const frame of stackFrames)logger_logger.stderr(logger_color.gray(` ${formatStack(frame, rootPath)}`));
2000
2088
  stackFrames.length && logger_logger.stderr('');
2001
2089
  }
2002
2090
  const stackIgnores = [
@@ -3201,7 +3289,7 @@ class MdReporter {
3201
3289
  }
3202
3290
  renderFrontMatter(lines) {
3203
3291
  const frontMatter = {
3204
- tool: "@rstest/core@0.9.2",
3292
+ tool: "@rstest/core@0.9.4",
3205
3293
  timestamp: new Date().toISOString()
3206
3294
  };
3207
3295
  if (this.options.header.env) frontMatter.runtime = {
@@ -3575,7 +3663,7 @@ class Rstest {
3575
3663
  updateSnapshot: rstestConfig.update ? 'all' : dist_m ? 'none' : 'new'
3576
3664
  });
3577
3665
  this.snapshotManager = snapshotManager;
3578
- this.version = "0.9.2";
3666
+ this.version = "0.9.4";
3579
3667
  this.rootPath = rootPath;
3580
3668
  this.originalConfig = userConfig;
3581
3669
  this.normalizedConfig = rstestConfig;
@@ -3628,6 +3716,9 @@ class Rstest {
3628
3716
  options: {
3629
3717
  showProjectName: projects.length > 1
3630
3718
  }
3719
+ }).filter((r)=>{
3720
+ if ('merge-reports' === command && r instanceof BlobReporter) return false;
3721
+ return true;
3631
3722
  }) : [];
3632
3723
  this.reporters = reporters;
3633
3724
  }
@@ -3652,7 +3743,8 @@ const reportersMap = {
3652
3743
  verbose: VerboseReporter,
3653
3744
  'github-actions': GithubActionsReporter,
3654
3745
  junit: JUnitReporter,
3655
- md: MdReporter
3746
+ md: MdReporter,
3747
+ blob: BlobReporter
3656
3748
  };
3657
3749
  function createReporters(reporters, initConfig = {}) {
3658
3750
  const result = castArray(reporters).map((reporter)=>{
@@ -3693,10 +3785,15 @@ function core_createRstest({ config, projects, configFilePath }, command, fileFi
3693
3785
  const { listTests } = await import("./0~listTests.js");
3694
3786
  return listTests(context, options);
3695
3787
  };
3788
+ const mergeReports = async (options)=>{
3789
+ const { mergeReports } = await import("./0~mergeReports.js");
3790
+ await mergeReports(context, options);
3791
+ };
3696
3792
  return {
3697
3793
  context,
3698
3794
  runTests,
3699
- listTests
3795
+ listTests,
3796
+ mergeReports
3700
3797
  };
3701
3798
  }
3702
3799
  function defineConfig(config) {
@@ -3705,4 +3802,4 @@ function defineConfig(config) {
3705
3802
  function defineProject(config) {
3706
3803
  return config;
3707
3804
  }
3708
- export { config_loadConfig as loadConfig, core_createRstest as createRstest, defineConfig, defineProject, detect, error_parseErrorStacktrace as parseErrorStacktrace, error_printError as printError, formatStack, init_initCli as initCli, mergeProjectConfig, mergeRstestConfig, resolveCommand, runCLI, runRest };
3805
+ export { config_loadConfig as loadConfig, core_createRstest as createRstest, core_namespaceObject, defineConfig, defineProject, detect, error_parseErrorStacktrace as parseErrorStacktrace, error_printError as printError, formatStack, init_initCli as initCli, init_namespaceObject, mergeProjectConfig, mergeRstestConfig, resolveCommand, runCLI, runRest };
package/dist/4411.js CHANGED
@@ -5,7 +5,7 @@ import node_fs, * as __rspack_external_node_fs_5ea92f0c from "node:fs";
5
5
  import node_path, { basename, dirname, normalize, posix, relative as external_node_path_relative, resolve, sep } from "node:path";
6
6
  import { fileURLToPath } from "url";
7
7
  import { createRequire } from "module";
8
- import { logger as logger_logger, parsePosix, color, castArray } from "./6830.js";
8
+ import { logger as logger_logger, parsePosix, color as logger_color, castArray } from "./6830.js";
9
9
  import { posix as dist_posix } from "./7011.js";
10
10
  __webpack_require__.add({
11
11
  "../../node_modules/.pnpm/picomatch@4.0.3/node_modules/picomatch/index.js" (module, __unused_rspack_exports, __webpack_require__) {
@@ -2266,7 +2266,7 @@ const getTestEntries = async ({ include, exclude, rootPath, projectRoot, fileFil
2266
2266
  };
2267
2267
  const prettyTestPath = (testPath)=>{
2268
2268
  const { dir, base } = parsePosix(testPath);
2269
- return `${'.' !== dir ? color.gray(`${dir}/`) : ''}${color.cyan(base)}`;
2269
+ return `${'.' !== dir ? logger_color.gray(`${dir}/`) : ''}${logger_color.cyan(base)}`;
2270
2270
  };
2271
2271
  const formatTestPath = (root, testFilePath)=>{
2272
2272
  let testPath = testFilePath;
@@ -2304,7 +2304,7 @@ async function resolveShardedEntries(context) {
2304
2304
  const shardedEntries = getShardedFiles(allTestEntriesBeforeSharding, shard);
2305
2305
  const totalTestFileCount = allTestEntriesBeforeSharding.length;
2306
2306
  const testFilesInShardCount = shardedEntries.length;
2307
- logger_logger.log(color.green(`Running shard ${shard.index} of ${shard.count} (${testFilesInShardCount} of ${totalTestFileCount} test files)\n`));
2307
+ logger_logger.log(logger_color.green(`Running shard ${shard.index} of ${shard.count} (${testFilesInShardCount} of ${totalTestFileCount} test files)\n`));
2308
2308
  const shardedEntriesByProject = new Map();
2309
2309
  for (const { project, alias, testPath } of shardedEntries){
2310
2310
  if (!shardedEntriesByProject.has(project)) shardedEntriesByProject.set(project, {});