@rstest/core 0.7.9 → 0.8.1

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/9131.js CHANGED
@@ -2,11 +2,11 @@ import 'module';
2
2
  /*#__PURE__*/ import.meta.url;
3
3
  import { __webpack_require__ } from "./rslib-runtime.js";
4
4
  import { EventEmitter } from "events";
5
- import { basename, isTTY, dirname, resolve as pathe_M_eThtNZ_resolve, relative, getAbsolutePath, join, bgColor, formatRootStr, castArray, prettyTime, getTaskNameWithPrefix, isAbsolute, formatError, normalize } from "./2672.js";
5
+ import { basename, isTTY, dirname, resolve as pathe_M_eThtNZ_resolve, relative, getAbsolutePath, join, bgColor, formatRootStr, logger as logger_logger, castArray, prettyTime, getTaskNameWithPrefix, isDebug, color, isAbsolute, normalize, formatError } from "./3160.js";
6
6
  import "./721.js";
7
- import { isDynamicPattern, glob, DEFAULT_CONFIG_NAME, prettyTestPath, globalApis, TEMP_RSTEST_OUTPUT_DIR_GLOB, formatTestPath, filterProjects, DEFAULT_CONFIG_EXTENSIONS, TS_CONFIG_FILE } from "./1157.js";
8
- import { posix, logger as logger_logger, isDebug } from "./3278.js";
7
+ import { isDynamicPattern, glob, DEFAULT_CONFIG_NAME, prettyTestPath, globalApis, writeFile, TEMP_RSTEST_OUTPUT_DIR_GLOB, formatTestPath, filterProjects, DEFAULT_CONFIG_EXTENSIONS, TS_CONFIG_FILE } from "./1157.js";
9
8
  import { rsbuild as __rspack_external__rsbuild_core_1b356efc } from "./4484.js";
9
+ import { posix } from "./7011.js";
10
10
  import { parse as stack_trace_parser_esm_parse } from "./1672.js";
11
11
  import { decode } from "./4397.js";
12
12
  function toArr(any) {
@@ -504,13 +504,14 @@ function prepareCli() {
504
504
  if (!npm_execpath || npm_execpath.includes('npx-cli.js') || npm_execpath.includes('.bun')) logger_logger.log();
505
505
  }
506
506
  function showRstest() {
507
- logger_logger.greet(" Rstest v0.7.9");
507
+ logger_logger.greet(" Rstest v0.8.1");
508
508
  logger_logger.log('');
509
509
  }
510
510
  const applyCommonOptions = (cli)=>{
511
511
  cli.option('-c, --config <config>', 'Specify the configuration file, can be a relative or absolute path').option('--config-loader <loader>', 'Specify the loader to load the config file, can be `jiti` or `native`', {
512
512
  default: 'jiti'
513
- }).option('-r, --root <root>', 'Specify the project root directory, can be an absolute path or a path relative to cwd').option('--globals', 'Provide global APIs').option('--isolate', 'Run tests in an isolated environment').option('--include <include>', 'Match test files').option('--exclude <exclude>', 'Exclude files from test').option('-u, --update', 'Update snapshot files').option('--coverage', 'Enable code coverage collection').option('--project <name>', 'Run only projects that match the name, can be a full name or wildcards pattern').option('--passWithNoTests', 'Allows the test suite to pass when no files are found').option('--printConsoleTrace', 'Print console traces when calling any console method').option('--disableConsoleIntercept', 'Disable console intercept').option('--logHeapUsage', 'Log heap usage after each test').option('--slowTestThreshold <value>', 'The number of milliseconds after which a test or suite is considered slow').option('--reporter <reporter>', 'Specify the reporter to use').option('-t, --testNamePattern <value>', 'Run only tests with a name that matches the regex').option('--testEnvironment <name>', 'The environment that will be used for testing').option('--testTimeout <value>', 'Timeout of a test in milliseconds').option('--hookTimeout <value>', 'Timeout of hook in milliseconds').option('--hideSkippedTests', 'Hide skipped tests from the output').option('--retry <retry>', 'Number of times to retry a test if it fails').option('--bail [number]', 'Stop running tests after n failures. Set to 0 to run all tests regardless of failures').option('--maxConcurrency <value>', 'Maximum number of concurrent tests').option('--clearMocks', 'Automatically clear mock calls, instances, contexts and results before every test').option('--resetMocks', 'Automatically reset mock state before every test').option('--restoreMocks', 'Automatically restore mock state and implementation before every test').option('--browser', 'Run tests in browser mode (Chromium)').option('--unstubGlobals', 'Restores all global variables that were changed with `rstest.stubGlobal` before every test').option('--unstubEnvs', 'Restores all `process.env` values that were changed with `rstest.stubEnv` before every test').option('--includeTaskLocation', 'Collect test and suite locations. This might increase the running time.');
513
+ }).option('-r, --root <root>', 'Specify the project root directory, can be an absolute path or a path relative to cwd').option('--globals', 'Provide global APIs').option('--isolate', 'Run tests in an isolated environment').option('--include <include>', 'Match test files').option('--exclude <exclude>', 'Exclude files from test').option('-u, --update', 'Update snapshot files').option('--coverage', 'Enable code coverage collection').option('--project <name>', 'Run only projects that match the name, can be a full name or wildcards pattern').option('--passWithNoTests', 'Allows the test suite to pass when no files are found').option('--printConsoleTrace', 'Print console traces when calling any console method').option('--disableConsoleIntercept', 'Disable console intercept').option('--logHeapUsage', 'Log heap usage after each test').option('--slowTestThreshold <value>', 'The number of milliseconds after which a test or suite is considered slow').option('--reporter <reporter>', 'Specify the reporter to use').option('-t, --testNamePattern <value>', 'Run only tests with a name that matches the regex').option('--testEnvironment <name>', 'The environment that will be used for testing').option('--testTimeout <value>', 'Timeout of a test in milliseconds').option('--hookTimeout <value>', 'Timeout of hook in milliseconds').option('--hideSkippedTests', 'Hide skipped tests from the output').option('--hideSkippedTestFiles', 'Hide skipped test files from the output').option('--retry <retry>', 'Number of times to retry a test if it fails').option('--bail [number]', 'Stop running tests after n failures. Set to 0 to run all tests regardless of failures').option('--maxConcurrency <value>', 'Maximum number of concurrent tests').option('--clearMocks', 'Automatically clear mock calls, instances, contexts and results before every test').option('--resetMocks', 'Automatically reset mock state before every test').option('--restoreMocks', 'Automatically restore mock state and implementation before every test').option('--browser', 'Run tests in browser mode').option('--browser.enabled', 'Run tests in browser mode').option('--browser.name <name>', 'Browser to use: chromium, firefox, webkit (default: chromium)').option('--browser.headless', 'Run browser in headless mode (default: true in CI)').option('--browser.port <port>', 'Port for the browser mode dev server').option('--browser.strictPort', 'Exit if the specified port is already in use').option('--unstubGlobals', 'Restores all global variables that were changed with `rstest.stubGlobal` before every test').option('--unstubEnvs', 'Restores all `process.env` values that were changed with `rstest.stubEnv` before every test').option('--includeTaskLocation', 'Collect test and suite locations. This might increase the running time.');
514
+ cli.option('--pool <type>', 'Shorthand for --pool.type').option('--pool.type <type>', 'Specify the test pool type (e.g. forks)').option('--pool.maxWorkers <value>', 'Maximum number or percentage of workers (e.g. 4 or 50%)').option('--pool.minWorkers <value>', 'Minimum number or percentage of workers (e.g. 1 or 25%)').option('--pool.execArgv <arg>', 'Additional Node.js execArgv passed to worker processes (can be specified multiple times)');
514
515
  };
515
516
  const handleUnexpectedExit = (rstest, err)=>{
516
517
  for (const reporter of rstest?.context.reporters || [])reporter.onExit?.();
@@ -561,7 +562,7 @@ const runRest = async ({ options, filters, command })=>{
561
562
  function setupCommands() {
562
563
  const cli = dist('rstest');
563
564
  cli.help();
564
- cli.version("0.7.9");
565
+ cli.version("0.8.1");
565
566
  applyCommonOptions(cli);
566
567
  cli.command('[...filters]', 'run tests').option('-w, --watch', 'Run tests in watch mode').action(async (filters, options)=>{
567
568
  showRstest();
@@ -627,7 +628,6 @@ function setupCommands() {
627
628
  select: mod.ve,
628
629
  isCancel: mod.pD
629
630
  }));
630
- const color = (await import("./2672.js").then(__webpack_require__.t.bind(__webpack_require__, "../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js", 23))).default;
631
631
  console.log();
632
632
  const selected = await select({
633
633
  message: 'What would you like to initialize?',
@@ -987,15 +987,13 @@ function G() {
987
987
  }
988
988
  const P = G();
989
989
  P?.name;
990
- const external_node_fs_ = __webpack_require__("fs");
991
- const picocolors = __webpack_require__("../../node_modules/.pnpm/picocolors@1.1.1/node_modules/picocolors/picocolors.js");
992
- var picocolors_default = /*#__PURE__*/ __webpack_require__.n(picocolors);
990
+ const external_node_fs_ = __webpack_require__("node:fs");
993
991
  const findConfig = (basePath)=>DEFAULT_CONFIG_EXTENSIONS.map((ext)=>basePath + ext).find(external_node_fs_["default"].existsSync);
994
992
  const resolveConfigPath = (root, customConfig)=>{
995
993
  if (customConfig) {
996
994
  const customConfigPath = isAbsolute(customConfig) ? customConfig : join(root, customConfig);
997
995
  if (external_node_fs_["default"].existsSync(customConfigPath)) return customConfigPath;
998
- throw `Cannot find config file: ${picocolors_default().dim(customConfigPath)}`;
996
+ throw `Cannot find config file: ${color.dim(customConfigPath)}`;
999
997
  }
1000
998
  const configFilePath = findConfig(join(root, DEFAULT_CONFIG_NAME));
1001
999
  if (configFilePath) return configFilePath;
@@ -1098,6 +1096,7 @@ const createDefaultConfig = ()=>({
1098
1096
  snapshotFormat: {},
1099
1097
  env: {},
1100
1098
  hideSkippedTests: false,
1099
+ hideSkippedTestFiles: false,
1101
1100
  logHeapUsage: false,
1102
1101
  bail: 0,
1103
1102
  includeTaskLocation: false,
@@ -1105,7 +1104,8 @@ const createDefaultConfig = ()=>({
1105
1104
  enabled: false,
1106
1105
  provider: 'playwright',
1107
1106
  browser: 'chromium',
1108
- headless: T
1107
+ headless: T,
1108
+ strictPort: false
1109
1109
  },
1110
1110
  coverage: {
1111
1111
  exclude: [
@@ -1134,6 +1134,13 @@ const createDefaultConfig = ()=>({
1134
1134
  }
1135
1135
  });
1136
1136
  const withDefaultConfig = (config)=>{
1137
+ if (config.browser?.enabled === true) {
1138
+ if (!config.browser.provider) throw new Error('browser.provider is required when browser.enabled is true.');
1139
+ const supportedProviders = [
1140
+ 'playwright'
1141
+ ];
1142
+ if (!supportedProviders.includes(config.browser.provider)) throw new Error(`browser.provider must be one of: ${supportedProviders.join(', ')}.`);
1143
+ }
1137
1144
  const merged = mergeRstestConfig(createDefaultConfig(), config);
1138
1145
  merged.setupFiles = castArray(merged.setupFiles);
1139
1146
  merged.globalSetup = castArray(merged.globalSetup);
@@ -1151,7 +1158,8 @@ const withDefaultConfig = (config)=>{
1151
1158
  provider: merged.browser?.provider ?? 'playwright',
1152
1159
  browser: merged.browser?.browser ?? 'chromium',
1153
1160
  headless: merged.browser?.headless ?? T,
1154
- port: merged.browser?.port
1161
+ port: merged.browser?.port,
1162
+ strictPort: merged.browser?.strictPort ?? false
1155
1163
  };
1156
1164
  return {
1157
1165
  ...merged,
@@ -1187,6 +1195,7 @@ function mergeWithCLIOptions(config, options) {
1187
1195
  'disableConsoleIntercept',
1188
1196
  'testEnvironment',
1189
1197
  'hideSkippedTests',
1198
+ 'hideSkippedTestFiles',
1190
1199
  'logHeapUsage'
1191
1200
  ];
1192
1201
  for (const key of keys)if (void 0 !== options[key]) config[key] = options[key];
@@ -1198,6 +1207,42 @@ function mergeWithCLIOptions(config, options) {
1198
1207
  }
1199
1208
  if (options.exclude) config.exclude = castArray(options.exclude);
1200
1209
  if (options.include) config.include = castArray(options.include);
1210
+ if (void 0 !== options.browser) {
1211
+ config.browser ??= {
1212
+ provider: 'playwright'
1213
+ };
1214
+ if ('boolean' == typeof options.browser) config.browser.enabled = options.browser;
1215
+ else {
1216
+ if (void 0 !== options.browser.enabled) config.browser.enabled = options.browser.enabled;
1217
+ if (void 0 !== options.browser.name) config.browser.browser = options.browser.name;
1218
+ if (void 0 !== options.browser.headless) config.browser.headless = options.browser.headless;
1219
+ if (void 0 !== options.browser.port) config.browser.port = Number(options.browser.port);
1220
+ if (void 0 !== options.browser.strictPort) config.browser.strictPort = options.browser.strictPort;
1221
+ }
1222
+ }
1223
+ if (void 0 !== options.pool) {
1224
+ const poolFromCli = options.pool;
1225
+ if ('string' == typeof poolFromCli) {
1226
+ if ('string' == typeof config.pool) config.pool = {
1227
+ type: config.pool
1228
+ };
1229
+ config.pool ??= {};
1230
+ if ('object' != typeof config.pool) config.pool = {};
1231
+ const pool = config.pool;
1232
+ pool.type = poolFromCli;
1233
+ } else {
1234
+ if ('string' == typeof config.pool) config.pool = {
1235
+ type: config.pool
1236
+ };
1237
+ config.pool ??= {};
1238
+ if ('object' != typeof config.pool) config.pool = {};
1239
+ const pool = config.pool;
1240
+ if (void 0 !== poolFromCli.type) pool.type = poolFromCli.type;
1241
+ if (void 0 !== poolFromCli.maxWorkers) pool.maxWorkers = poolFromCli.maxWorkers;
1242
+ if (void 0 !== poolFromCli.minWorkers) pool.minWorkers = poolFromCli.minWorkers;
1243
+ if (void 0 !== poolFromCli.execArgv) pool.execArgv = castArray(poolFromCli.execArgv);
1244
+ }
1245
+ }
1201
1246
  return config;
1202
1247
  }
1203
1248
  async function resolveConfig(options) {
@@ -1208,10 +1253,6 @@ async function resolveConfig(options) {
1208
1253
  });
1209
1254
  const mergedConfig = mergeWithCLIOptions(config, options);
1210
1255
  if (!mergedConfig.root) mergedConfig.root = options.cwd;
1211
- if (void 0 !== options.browser) {
1212
- config.browser ??= {};
1213
- config.browser.enabled = options.browser;
1214
- }
1215
1256
  return {
1216
1257
  config: mergedConfig,
1217
1258
  configFilePath: configFilePath ?? void 0
@@ -1303,8 +1344,8 @@ async function resolveProjects({ config, root, options }) {
1303
1344
  const projects = await getProjects(config, root).then((p)=>filterProjects(p, options));
1304
1345
  if (!projects.length) {
1305
1346
  let errorMsg = `No projects found, please make sure you have at least one valid project.
1306
- ${picocolors_default().gray('projects:')} ${JSON.stringify(config.projects, null, 2)}`;
1307
- if (options.project) errorMsg += `\n${picocolors_default().gray('projectName filter:')} ${JSON.stringify(options.project, null, 2)}`;
1347
+ ${color.gray('projects:')} ${JSON.stringify(config.projects, null, 2)}`;
1348
+ if (options.project) errorMsg += `\n${color.gray('projectName filter:')} ${JSON.stringify(options.project, null, 2)}`;
1308
1349
  throw errorMsg;
1309
1350
  }
1310
1351
  const names = new Set();
@@ -1339,12 +1380,14 @@ async function init_initCli(options) {
1339
1380
  };
1340
1381
  }
1341
1382
  async function runCLI() {
1383
+ process.title = 'rstest-node';
1342
1384
  prepareCli();
1343
1385
  try {
1344
1386
  setupCommands();
1345
1387
  } catch (err) {
1346
1388
  logger_logger.error('Failed to start Rstest CLI.');
1347
1389
  logger_logger.error(err);
1390
+ process.exit(1);
1348
1391
  }
1349
1392
  }
1350
1393
  class SnapshotManager {
@@ -1405,53 +1448,53 @@ function addSnapshotResult(summary, result) {
1405
1448
  summary.total += result.added + result.matched + result.unmatched + result.updated;
1406
1449
  }
1407
1450
  const getSummaryStatusString = (tasks, name = 'tests', showTotal = true)=>{
1408
- if (0 === tasks.length) return picocolors_default().dim(`no ${name}`);
1451
+ if (0 === tasks.length) return color.dim(`no ${name}`);
1409
1452
  const passed = tasks.filter((result)=>'pass' === result.status);
1410
1453
  const failed = tasks.filter((result)=>'fail' === result.status);
1411
1454
  const skipped = tasks.filter((result)=>'skip' === result.status);
1412
1455
  const todo = tasks.filter((result)=>'todo' === result.status);
1413
1456
  const status = [
1414
- failed.length ? picocolors_default().bold(picocolors_default().red(`${failed.length} failed`)) : null,
1415
- passed.length ? picocolors_default().bold(picocolors_default().green(`${passed.length} passed`)) : null,
1416
- skipped.length ? picocolors_default().yellow(`${skipped.length} skipped`) : null,
1417
- todo.length ? picocolors_default().gray(`${todo.length} todo`) : null
1457
+ failed.length ? color.bold(color.red(`${failed.length} failed`)) : null,
1458
+ passed.length ? color.bold(color.green(`${passed.length} passed`)) : null,
1459
+ skipped.length ? color.yellow(`${skipped.length} skipped`) : null,
1460
+ todo.length ? color.gray(`${todo.length} todo`) : null
1418
1461
  ].filter(Boolean);
1419
- return status.join(picocolors_default().dim(' | ')) + (showTotal && status.length > 1 ? picocolors_default().gray(` (${tasks.length})`) : '');
1462
+ return status.join(color.dim(' | ')) + (showTotal && status.length > 1 ? color.gray(` (${tasks.length})`) : '');
1420
1463
  };
1421
1464
  const printSnapshotSummaryLog = (snapshots, rootDir)=>{
1422
1465
  const summary = [];
1423
- if (snapshots.added) summary.push(picocolors_default().bold(picocolors_default().green(`${snapshots.added} written`)));
1424
- if (snapshots.unmatched) summary.push(picocolors_default().bold(picocolors_default().red(`${snapshots.unmatched} failed`)));
1425
- if (snapshots.updated) summary.push(picocolors_default().bold(picocolors_default().green(`${snapshots.updated} updated `)));
1426
- if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(picocolors_default().bold(picocolors_default().green(`${snapshots.filesRemoved} files removed `)));
1427
- else summary.push(picocolors_default().bold(picocolors_default().yellow(`${snapshots.filesRemoved} files obsolete `)));
1466
+ if (snapshots.added) summary.push(color.bold(color.green(`${snapshots.added} written`)));
1467
+ if (snapshots.unmatched) summary.push(color.bold(color.red(`${snapshots.unmatched} failed`)));
1468
+ if (snapshots.updated) summary.push(color.bold(color.green(`${snapshots.updated} updated `)));
1469
+ if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(color.bold(color.green(`${snapshots.filesRemoved} files removed `)));
1470
+ else summary.push(color.bold(color.yellow(`${snapshots.filesRemoved} files obsolete `)));
1428
1471
  if (snapshots.filesRemovedList?.length) {
1429
1472
  const [head, ...tail] = snapshots.filesRemovedList;
1430
- summary.push(`${picocolors_default().gray("➜")} ${formatTestPath(rootDir, head)}`);
1473
+ summary.push(`${color.gray("➜")} ${formatTestPath(rootDir, head)}`);
1431
1474
  for (const key of tail)summary.push(` ${formatTestPath(rootDir, key)}`);
1432
1475
  }
1433
1476
  if (snapshots.unchecked) {
1434
- if (snapshots.didUpdate) summary.push(picocolors_default().bold(picocolors_default().green(`${snapshots.unchecked} removed`)));
1435
- else summary.push(picocolors_default().bold(picocolors_default().yellow(`${snapshots.unchecked} obsolete`)));
1477
+ if (snapshots.didUpdate) summary.push(color.bold(color.green(`${snapshots.unchecked} removed`)));
1478
+ else summary.push(color.bold(color.yellow(`${snapshots.unchecked} obsolete`)));
1436
1479
  for (const uncheckedFile of snapshots.uncheckedKeysByFile){
1437
- summary.push(`${picocolors_default().gray("➜")} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
1480
+ summary.push(`${color.gray("➜")} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
1438
1481
  for (const key of uncheckedFile.keys)summary.push(` ${key}`);
1439
1482
  }
1440
1483
  }
1441
1484
  for (const [index, snapshot] of summary.entries()){
1442
1485
  const title = 0 === index ? 'Snapshots' : '';
1443
- logger_logger.log(`${picocolors_default().gray(title.padStart(12))} ${snapshot}`);
1486
+ logger_logger.log(`${color.gray(title.padStart(12))} ${snapshot}`);
1444
1487
  }
1445
1488
  };
1446
- const TestFileSummaryLabel = picocolors_default().gray('Test Files'.padStart(11));
1447
- const TestSummaryLabel = picocolors_default().gray('Tests'.padStart(11));
1448
- const DurationLabel = picocolors_default().gray('Duration'.padStart(11));
1489
+ const TestFileSummaryLabel = color.gray('Test Files'.padStart(11));
1490
+ const TestSummaryLabel = color.gray('Tests'.padStart(11));
1491
+ const DurationLabel = color.gray('Duration'.padStart(11));
1449
1492
  const printSummaryLog = ({ results, testResults, snapshotSummary, duration, rootPath })=>{
1450
1493
  logger_logger.log('');
1451
1494
  printSnapshotSummaryLog(snapshotSummary, rootPath);
1452
1495
  logger_logger.log(`${TestFileSummaryLabel} ${getSummaryStatusString(results)}`);
1453
1496
  logger_logger.log(`${TestSummaryLabel} ${getSummaryStatusString(testResults)}`);
1454
- logger_logger.log(`${DurationLabel} ${prettyTime(duration.totalTime)} ${picocolors_default().gray(`(build ${prettyTime(duration.buildTime)}, tests ${prettyTime(duration.testTime)})`)}`);
1497
+ logger_logger.log(`${DurationLabel} ${prettyTime(duration.totalTime)} ${color.gray(`(build ${prettyTime(duration.buildTime)}, tests ${prettyTime(duration.testTime)})`)}`);
1455
1498
  logger_logger.log('');
1456
1499
  };
1457
1500
  const printSummaryErrorLogs = async ({ testResults, results, rootPath, unhandledErrors, getSourcemap, filterRerunTestPaths })=>{
@@ -1461,7 +1504,7 @@ const printSummaryErrorLogs = async ({ testResults, results, rootPath, unhandled
1461
1504
  ];
1462
1505
  if (0 === failedTests.length && !unhandledErrors?.length) return;
1463
1506
  logger_logger.stderr('');
1464
- logger_logger.stderr(picocolors_default().bold('Summary of all failing tests:'));
1507
+ logger_logger.stderr(color.bold('Summary of all failing tests:'));
1465
1508
  logger_logger.stderr('');
1466
1509
  const { printError } = await Promise.resolve().then(()=>({
1467
1510
  printError: error_printError
@@ -1473,7 +1516,7 @@ const printSummaryErrorLogs = async ({ testResults, results, rootPath, unhandled
1473
1516
  for (const test of failedTests){
1474
1517
  const relativePath = posix.relative(rootPath, test.testPath);
1475
1518
  const nameStr = getTaskNameWithPrefix(test);
1476
- logger_logger.stderr(`${bgColor('bgRed', ' FAIL ')} ${prettyTestPath(relativePath)} ${nameStr.length ? `${picocolors_default().dim(">")} ${nameStr}` : ''}`);
1519
+ logger_logger.stderr(`${bgColor('bgRed', ' FAIL ')} ${prettyTestPath(relativePath)} ${nameStr.length ? `${color.dim(">")} ${nameStr}` : ''}`);
1477
1520
  if (test.errors) {
1478
1521
  const { printError } = await Promise.resolve().then(()=>({
1479
1522
  printError: error_printError
@@ -1628,14 +1671,14 @@ class StatusRenderer {
1628
1671
  const relativePath = relative(this.rootPath, module);
1629
1672
  summary.push(`${bgColor('bgYellow', ' RUNS ')} ${prettyTestPath(relativePath)}`);
1630
1673
  if (runningTests.length && shouldDisplayRunningTests(runningTests)) {
1631
- let caseLog = ` ${picocolors_default().gray("➜")} ${getTaskNameWithPrefix(runningTests[0])} ${picocolors_default().magenta(prettyTime(now - runningTests[0].startTime))}`;
1632
- if (runningTests.length > 1) caseLog += picocolors_default().gray(` and ${runningTests.length - 1} more cases`);
1674
+ let caseLog = ` ${color.gray("➜")} ${getTaskNameWithPrefix(runningTests[0])} ${color.magenta(prettyTime(now - runningTests[0].startTime))}`;
1675
+ if (runningTests.length > 1) caseLog += color.gray(` and ${runningTests.length - 1} more cases`);
1633
1676
  summary.push(caseLog);
1634
1677
  }
1635
1678
  }
1636
1679
  summary.push('');
1637
1680
  if (0 === testModules.length) summary.push(`${TestFileSummaryLabel} ${runningModules.size} total`);
1638
- else summary.push(`${TestFileSummaryLabel} ${getSummaryStatusString(testModules, '', false)} ${picocolors_default().dim('|')} ${runningModules.size + testModules.length} total`);
1681
+ else summary.push(`${TestFileSummaryLabel} ${getSummaryStatusString(testModules, '', false)} ${color.dim('|')} ${runningModules.size + testModules.length} total`);
1639
1682
  const testResults = Array.from(runningModules.values()).flatMap(({ results })=>results).concat(testModules.flatMap((mod)=>mod.results));
1640
1683
  if (testResults.length) summary.push(`${TestSummaryLabel} ${getSummaryStatusString(testResults, '', false)}`);
1641
1684
  summary.push(`${DurationLabel} ${prettyTime(Date.now() - this.startTime)}`);
@@ -1662,30 +1705,38 @@ const statusStr = {
1662
1705
  todo: '-',
1663
1706
  skip: '-'
1664
1707
  };
1708
+ const statusColor = {
1709
+ fail: color.red,
1710
+ pass: color.green,
1711
+ todo: color.gray,
1712
+ skip: color.gray
1713
+ };
1665
1714
  const statusColorfulStr = {
1666
- fail: picocolors_default().red(statusStr.fail),
1667
- pass: picocolors_default().green(statusStr.pass),
1668
- todo: picocolors_default().gray(statusStr.todo),
1669
- skip: picocolors_default().gray(statusStr.skip)
1715
+ fail: statusColor.fail(statusStr.fail),
1716
+ pass: statusColor.pass(statusStr.pass),
1717
+ todo: statusColor.todo(statusStr.todo),
1718
+ skip: statusColor.skip(statusStr.skip)
1670
1719
  };
1671
1720
  const logCase = (result, options)=>{
1672
1721
  const isSlowCase = (result.duration || 0) > options.slowTestThreshold;
1673
1722
  if (options.hideSkippedTests && 'skip' === result.status) return;
1674
- const icon = isSlowCase && 'pass' === result.status ? picocolors_default().yellow(statusStr[result.status]) : statusColorfulStr[result.status];
1723
+ const icon = isSlowCase && 'pass' === result.status ? color.yellow(statusStr[result.status]) : statusColorfulStr[result.status];
1675
1724
  const nameStr = getTaskNameWithPrefix(result);
1676
1725
  const duration = void 0 !== result.duration ? ` (${prettyTime(result.duration)})` : '';
1677
- const retry = result.retryCount ? picocolors_default().yellow(` (retry x${result.retryCount})`) : '';
1678
- const heap = result.heap ? ` ${picocolors_default().magenta(formatHeapUsed(result.heap))}` : '';
1679
- logger_logger.log(` ${icon} ${nameStr}${picocolors_default().gray(duration)}${retry}${heap}`);
1680
- if (result.errors) for (const error of result.errors)logger_logger.log(picocolors_default().red(` ${error.message}`));
1726
+ const retry = result.retryCount ? color.yellow(` (retry x${result.retryCount})`) : '';
1727
+ const heap = result.heap ? ` ${color.magenta(formatHeapUsed(result.heap))}` : '';
1728
+ logger_logger.log(` ${icon} ${nameStr}${color.gray(duration)}${retry}${heap}`);
1729
+ if (result.errors) for (const error of result.errors)logger_logger.log(color.red(` ${error.message}`));
1681
1730
  };
1682
1731
  const formatHeapUsed = (heap)=>`${Math.floor(heap / 1024 / 1024)} MB heap used`;
1683
- const logFileTitle = (test, relativePath, alwaysShowTime = false)=>{
1684
- let title = ` ${picocolors_default().bold(statusColorfulStr[test.status])} ${prettyTestPath(relativePath)}`;
1685
- const formatDuration = (duration)=>picocolors_default().green(prettyTime(duration));
1686
- title += ` ${picocolors_default().gray(`(${test.results.length})`)}`;
1732
+ const logFileTitle = (test, relativePath, alwaysShowTime = false, showProjectName = false)=>{
1733
+ let title = ` ${color.bold(statusColorfulStr[test.status])}`;
1734
+ if (showProjectName && test.project) title += ` ${statusColor[test.status](`[${test.project}]`)}`;
1735
+ title += ` ${prettyTestPath(relativePath)}`;
1736
+ const formatDuration = (duration)=>color.green(prettyTime(duration));
1737
+ title += ` ${color.gray(`(${test.results.length})`)}`;
1687
1738
  if (alwaysShowTime) title += ` ${formatDuration(test.duration)}`;
1688
- if (test.heap) title += ` ${picocolors_default().magenta(formatHeapUsed(test.heap))}`;
1739
+ if (test.heap) title += ` ${color.magenta(formatHeapUsed(test.heap))}`;
1689
1740
  logger_logger.log(title);
1690
1741
  };
1691
1742
  class DefaultReporter {
@@ -1699,16 +1750,21 @@ class DefaultReporter {
1699
1750
  this.config = config;
1700
1751
  this.options = options;
1701
1752
  this.testState = testState;
1702
- if (isTTY() || options.logger) this.statusRenderer = new StatusRenderer(rootPath, testState, options.logger);
1753
+ }
1754
+ ensureStatusRenderer() {
1755
+ if (this.statusRenderer) return;
1756
+ if (isTTY() || this.options.logger) this.statusRenderer = new StatusRenderer(this.rootPath, this.testState, this.options.logger);
1703
1757
  }
1704
1758
  onTestFileStart() {
1759
+ this.ensureStatusRenderer();
1705
1760
  this.statusRenderer?.onTestFileStart();
1706
1761
  }
1707
1762
  onTestFileResult(test) {
1708
1763
  this.statusRenderer?.onTestFileResult();
1764
+ if (this.config.hideSkippedTestFiles && 'skip' === test.status) return;
1709
1765
  const relativePath = relative(this.rootPath, test.testPath);
1710
1766
  const { slowTestThreshold } = this.config;
1711
- logFileTitle(test, relativePath);
1767
+ logFileTitle(test, relativePath, false, this.options.showProjectName);
1712
1768
  const showAllCases = this.testState.getTestFiles()?.length === 1;
1713
1769
  for (const result of test.results){
1714
1770
  const isDisplayed = showAllCases || 'fail' === result.status || (result.duration ?? 0) > slowTestThreshold || (result.retryCount ?? 0) > 0;
@@ -1734,7 +1790,7 @@ class DefaultReporter {
1734
1790
  } else titles.push(testPath);
1735
1791
  const logOutput = 'stdout' === log.type ? logger_logger.log : logger_logger.stderr;
1736
1792
  logOutput('');
1737
- logOutput(`${log.name}${picocolors_default().gray(picocolors_default().dim(` | ${titles.join(picocolors_default().gray(picocolors_default().dim(' | ')))}`))}`);
1793
+ logOutput(`${log.name}${color.gray(color.dim(` | ${titles.join(color.gray(color.dim(' | ')))}`))}`);
1738
1794
  logOutput(log.content);
1739
1795
  logOutput('');
1740
1796
  }
@@ -2139,7 +2195,7 @@ const hintNotDefinedError = (message)=>{
2139
2195
  'jest',
2140
2196
  'vitest'
2141
2197
  ].includes(varName)) return message.replace(`${varName} is not defined`, `${varName} is not defined. Did you mean rstest?`);
2142
- if ('React' === varName) return message.replace(`${varName} is not defined`, `${varName} is not defined. Did you forget to install "${picocolors_default().yellow('@rsbuild/plugin-react')}" plugin?`);
2198
+ 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?`);
2143
2199
  }
2144
2200
  return message;
2145
2201
  };
@@ -2149,14 +2205,14 @@ async function error_printError(error, getSourcemap, rootPath) {
2149
2205
  const tips = [
2150
2206
  'Error: not support import `vitest` in Rstest test environment.\n',
2151
2207
  'Solution:',
2152
- ` - Update your code to use imports from "${picocolors_default().yellow('@rstest/core')}" instead of "${picocolors_default().yellow('vitest')}".`,
2208
+ ` - Update your code to use imports from "${color.yellow('@rstest/core')}" instead of "${color.yellow('vitest')}".`,
2153
2209
  ' - Enable `globals` configuration and use global API.'
2154
2210
  ];
2155
- logger_logger.stderr(`${picocolors_default().red(tips.join('\n'))}\n`);
2211
+ logger_logger.stderr(`${color.red(tips.join('\n'))}\n`);
2156
2212
  return;
2157
2213
  }
2158
2214
  if (error.message.includes('is not defined')) error.message = hintNotDefinedError(error.message);
2159
- logger_logger.stderr(`${picocolors_default().red(picocolors_default().bold(errorName))}${picocolors_default().red(`: ${error.message}`)}\n`);
2215
+ logger_logger.stderr(`${color.red(color.bold(errorName))}${color.red(`: ${error.message}`)}\n`);
2160
2216
  if (error.diff) {
2161
2217
  logger_logger.stderr(error.diff);
2162
2218
  logger_logger.stderr('');
@@ -2167,7 +2223,7 @@ async function error_printError(error, getSourcemap, rootPath) {
2167
2223
  fullStack: error.fullStack,
2168
2224
  getSourcemap
2169
2225
  });
2170
- if (!stackFrames.length && !(error.fullStack || isDebug()) && !error.stack.endsWith(error.message)) logger_logger.stderr(picocolors_default().gray("No error stack found, set 'DEBUG=rstest' to show fullStack."));
2226
+ 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."));
2171
2227
  if (stackFrames[0]) await printCodeFrame(stackFrames[0]);
2172
2228
  printStack(stackFrames, rootPath);
2173
2229
  }
@@ -2194,7 +2250,7 @@ function formatStack(frame, rootPath) {
2194
2250
  return '<unknown>' !== frame.methodName ? `at ${frame.methodName} (${formatTestPath(rootPath, frame.file)}:${frame.lineNumber}:${frame.column})` : `at ${formatTestPath(rootPath, frame.file)}:${frame.lineNumber}:${frame.column}`;
2195
2251
  }
2196
2252
  function printStack(stackFrames, rootPath) {
2197
- for (const frame of stackFrames)logger_logger.stderr(picocolors_default().gray(` ${formatStack(frame, rootPath)}`));
2253
+ for (const frame of stackFrames)logger_logger.stderr(color.gray(` ${formatStack(frame, rootPath)}`));
2198
2254
  stackFrames.length && logger_logger.stderr('');
2199
2255
  }
2200
2256
  const stackIgnores = [
@@ -2222,7 +2278,13 @@ async function error_parseErrorStacktrace({ stack, getSourcemap, fullStack = isD
2222
2278
  if (!source) return null;
2223
2279
  return {
2224
2280
  ...frame,
2225
- file: isRelativePath(source) ? (0, external_node_path_.resolve)(frame.file, '../', source) : new URL(source).pathname,
2281
+ file: isRelativePath(source) ? (0, external_node_path_.resolve)(frame.file, '../', source) : (()=>{
2282
+ try {
2283
+ return new URL(source).pathname;
2284
+ } catch {
2285
+ return source;
2286
+ }
2287
+ })(),
2226
2288
  lineNumber: line,
2227
2289
  name,
2228
2290
  column
@@ -2232,7 +2294,6 @@ async function error_parseErrorStacktrace({ stack, getSourcemap, fullStack = isD
2232
2294
  })).then((frames)=>frames.filter((frame)=>null !== frame));
2233
2295
  return stackFrames;
2234
2296
  }
2235
- const promises_ = __webpack_require__("node:fs/promises");
2236
2297
  class JUnitReporter {
2237
2298
  rootPath;
2238
2299
  outputPath;
@@ -2342,7 +2403,7 @@ class JUnitReporter {
2342
2403
  };
2343
2404
  const xmlContent = this.generateJUnitXml(report);
2344
2405
  if (this.outputPath) try {
2345
- await (0, promises_.writeFile)(this.outputPath, xmlContent, 'utf-8');
2406
+ await writeFile(this.outputPath, xmlContent, 'utf-8');
2346
2407
  logger_logger.log(`JUnit XML report written to: ${this.outputPath}`);
2347
2408
  } catch (error) {
2348
2409
  logger_logger.stderr(`Failed to write JUnit XML report to ${this.outputPath}:`, error);
@@ -2353,11 +2414,25 @@ class JUnitReporter {
2353
2414
  }
2354
2415
  }
2355
2416
  class VerboseReporter extends DefaultReporter {
2417
+ verboseOptions = {};
2418
+ constructor({ rootPath, options, config, testState }){
2419
+ super({
2420
+ rootPath,
2421
+ options: {
2422
+ ...options,
2423
+ summary: true
2424
+ },
2425
+ config,
2426
+ testState
2427
+ });
2428
+ this.verboseOptions = options;
2429
+ }
2356
2430
  onTestFileResult(test) {
2357
2431
  this.statusRenderer?.onTestFileResult();
2432
+ if (this.config.hideSkippedTestFiles && 'skip' === test.status) return;
2358
2433
  const relativePath = relative(this.rootPath, test.testPath);
2359
2434
  const { slowTestThreshold } = this.config;
2360
- logFileTitle(test, relativePath, true);
2435
+ logFileTitle(test, relativePath, true, this.verboseOptions.showProjectName);
2361
2436
  for (const result of test.results)logCase(result, {
2362
2437
  slowTestThreshold,
2363
2438
  hideSkippedTests: this.config.hideSkippedTests
@@ -2462,17 +2537,11 @@ class Rstest {
2462
2537
  ...userConfig,
2463
2538
  root: rootPath
2464
2539
  });
2465
- const reporters = 'list' !== command ? createReporters(rstestConfig.reporters, {
2466
- rootPath,
2467
- config: rstestConfig,
2468
- testState: this.testState
2469
- }) : [];
2470
2540
  const snapshotManager = new SnapshotManager({
2471
2541
  updateSnapshot: rstestConfig.update ? 'all' : T ? 'none' : 'new'
2472
2542
  });
2473
- this.reporters = reporters;
2474
2543
  this.snapshotManager = snapshotManager;
2475
- this.version = "0.7.9";
2544
+ this.version = "0.8.1";
2476
2545
  this.rootPath = rootPath;
2477
2546
  this.originalConfig = userConfig;
2478
2547
  this.normalizedConfig = rstestConfig;
@@ -2508,6 +2577,15 @@ class Rstest {
2508
2577
  normalizedConfig: rstestConfig
2509
2578
  }
2510
2579
  ];
2580
+ const reporters = 'list' !== command ? createReporters(rstestConfig.reporters, {
2581
+ rootPath,
2582
+ config: rstestConfig,
2583
+ testState: this.testState,
2584
+ options: {
2585
+ showProjectName: projects.length > 1
2586
+ }
2587
+ }) : [];
2588
+ this.reporters = reporters;
2511
2589
  }
2512
2590
  updateReporterResultState(results, testResults, deletedEntries = []) {
2513
2591
  results.forEach((item)=>{
@@ -2531,7 +2609,7 @@ const reportersMap = {
2531
2609
  'github-actions': GithubActionsReporter,
2532
2610
  junit: JUnitReporter
2533
2611
  };
2534
- function createReporters(reporters, initOptions = {}) {
2612
+ function createReporters(reporters, initConfig = {}) {
2535
2613
  const result = castArray(reporters).map((reporter)=>{
2536
2614
  if ('string' == typeof reporter || Array.isArray(reporter)) {
2537
2615
  const [name, options = {}] = 'string' == typeof reporter ? [
@@ -2541,8 +2619,11 @@ function createReporters(reporters, initOptions = {}) {
2541
2619
  if (name in reportersMap) {
2542
2620
  const Reporter = reportersMap[name];
2543
2621
  return new Reporter({
2544
- ...initOptions,
2545
- options
2622
+ ...initConfig,
2623
+ options: {
2624
+ ...initConfig.options || {},
2625
+ ...options
2626
+ }
2546
2627
  });
2547
2628
  }
2548
2629
  throw new Error(`Reporter ${reporter} not found. Please install it or use a built-in reporter.`);