@rstest/core 0.9.6 → 0.9.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.
@@ -3,7 +3,50 @@ import { mkdirSync, writeFileSync } from "node:fs";
3
3
  import { dirname, isAbsolute, join, relative } from "node:path";
4
4
  import { prepareRsbuild, createPool, createRsbuildServer, runGlobalTeardown, runGlobalSetup } from "./0~8843.js";
5
5
  import { resolveShardedEntries, getTestEntries, prettyTestPath } from "./4411.js";
6
- import { getTaskNameWithPrefix, logger as logger_logger, color as logger_color, ROOT_SUITE_NAME, bgColor } from "./6830.js";
6
+ import { logger as logger_logger, color as logger_color, getTaskNameWithPrefix, ROOT_SUITE_NAME, bgColor } from "./6830.js";
7
+ const SummaryProjectLabel = logger_color.gray('Projects'.padStart(11));
8
+ const SummaryTestFileLabel = logger_color.gray('Test Files'.padStart(11));
9
+ const SummarySuiteLabel = logger_color.gray('Suites'.padStart(11));
10
+ const SummaryTestLabel = logger_color.gray('Tests'.padStart(11));
11
+ const getListSummaryCounts = (tests)=>{
12
+ const projects = new Set();
13
+ const files = new Set();
14
+ let suites = 0;
15
+ let testCases = 0;
16
+ for (const test of tests){
17
+ if (test.project) projects.add(test.project);
18
+ files.add(`${test.project ?? ''}\0${test.file}`);
19
+ if ('suite' === test.type) suites += 1;
20
+ if ('case' === test.type) testCases += 1;
21
+ }
22
+ return {
23
+ projects: projects.size,
24
+ files: files.size,
25
+ suites,
26
+ testCases
27
+ };
28
+ };
29
+ const printListSummary = ({ tests, filesOnly, includeSuites, showProject, write })=>{
30
+ const counts = getListSummaryCounts(tests);
31
+ write('');
32
+ if (showProject) write(`${SummaryProjectLabel} ${logger_color.bold(`${counts.projects} matched`)}`);
33
+ write(`${SummaryTestFileLabel} ${logger_color.bold(`${counts.files} matched`)}`);
34
+ if (filesOnly) return;
35
+ if (includeSuites) write(`${SummarySuiteLabel} ${logger_color.bold(`${counts.suites} matched`)}`);
36
+ write(`${SummaryTestLabel} ${logger_color.bold(`${counts.testCases} matched`)}`);
37
+ };
38
+ const createListSummaryPayload = ({ tests, filesOnly, includeSuites, showProject })=>{
39
+ const counts = getListSummaryCounts(tests);
40
+ const summary = {
41
+ files: counts.files
42
+ };
43
+ if (showProject) summary.projects = counts.projects;
44
+ if (!filesOnly) {
45
+ if (includeSuites) summary.suites = counts.suites;
46
+ summary.tests = counts.testCases;
47
+ }
48
+ return summary;
49
+ };
7
50
  const collectNodeTests = async ({ context, nodeProjects, globTestSourceEntries })=>{
8
51
  const { getSetupFiles } = await import("./255.js");
9
52
  if (0 === nodeProjects.length) return {
@@ -161,7 +204,7 @@ const collectAllTests = async ({ context, globTestSourceEntries, shardedEntries
161
204
  }
162
205
  };
163
206
  };
164
- async function listTests(context, { filesOnly, json, printLocation, includeSuites }) {
207
+ async function listTests(context, { filesOnly, json, printLocation, includeSuites, summary }) {
165
208
  const { rootPath } = context;
166
209
  const { shard } = context.normalizedConfig;
167
210
  const shardedEntries = await resolveShardedEntries(context);
@@ -255,7 +298,15 @@ async function listTests(context, { filesOnly, json, printLocation, includeSuite
255
298
  for (const test of file.tests)traverseTests(test);
256
299
  }
257
300
  if (json && 'false' !== json) {
258
- const content = JSON.stringify(tests, null, 2);
301
+ const content = JSON.stringify(summary ? {
302
+ items: tests,
303
+ summary: createListSummaryPayload({
304
+ tests,
305
+ filesOnly,
306
+ includeSuites,
307
+ showProject
308
+ })
309
+ } : tests, null, 2);
259
310
  if (true !== json && 'true' !== json) {
260
311
  const jsonPath = isAbsolute(json) ? json : join(rootPath, json);
261
312
  mkdirSync(dirname(jsonPath), {
@@ -263,10 +314,19 @@ async function listTests(context, { filesOnly, json, printLocation, includeSuite
263
314
  });
264
315
  writeFileSync(jsonPath, content);
265
316
  } else logger_logger.log(content);
266
- } else for (const test of tests){
267
- let shortPath = relative(rootPath, test.file);
268
- if (test.location && printLocation) shortPath = `${shortPath}:${test.location.line}:${test.location.column}`;
269
- logger_logger.log(test.name ? `${logger_color.dim(`${shortPath} > `)}${test.name}` : prettyTestPath(shortPath));
317
+ } else {
318
+ for (const test of tests){
319
+ let shortPath = relative(rootPath, test.file);
320
+ if (test.location && printLocation) shortPath = `${shortPath}:${test.location.line}:${test.location.column}`;
321
+ logger_logger.log(test.name ? `${logger_color.dim(`${shortPath} > `)}${test.name}` : prettyTestPath(shortPath));
322
+ }
323
+ if (summary) printListSummary({
324
+ tests,
325
+ filesOnly,
326
+ includeSuites,
327
+ showProject,
328
+ write: logger_logger.log
329
+ });
270
330
  }
271
331
  await close();
272
332
  return list;
package/dist/1255.js CHANGED
@@ -36,10 +36,10 @@ __webpack_require__.add({
36
36
  "../../node_modules/.pnpm/source-map-support@0.5.21/node_modules/source-map-support/source-map-support.js" (module, exports, __webpack_require__) {
37
37
  module = __webpack_require__.nmd(module);
38
38
  var SourceMapConsumer = __webpack_require__("../../node_modules/.pnpm/source-map@0.6.1/node_modules/source-map/source-map.js").YK;
39
- var path = __webpack_require__("path?37f5");
39
+ var path = __webpack_require__("path?435f");
40
40
  var fs;
41
41
  try {
42
- fs = __webpack_require__("fs?cdbd");
42
+ fs = __webpack_require__("fs?9592");
43
43
  if (!fs.existsSync || !fs.readFileSync) fs = null;
44
44
  } catch (err) {}
45
45
  var bufferFrom = __webpack_require__("../../node_modules/.pnpm/buffer-from@1.1.2/node_modules/buffer-from/index.js");
@@ -1732,11 +1732,11 @@ __webpack_require__.add({
1732
1732
  exports.YK = __webpack_require__("../../node_modules/.pnpm/source-map@0.6.1/node_modules/source-map/lib/source-map-consumer.js").YK;
1733
1733
  __webpack_require__("../../node_modules/.pnpm/source-map@0.6.1/node_modules/source-map/lib/source-node.js");
1734
1734
  },
1735
- "fs?cdbd" (module) {
1736
- module.exports = __rspack_createRequire_require("fs");
1735
+ "fs?9592" (module) {
1736
+ module.exports = __rspack_createRequire_require("node:fs");
1737
1737
  },
1738
- "path?37f5" (module) {
1739
- module.exports = __rspack_createRequire_require("path");
1738
+ "path?435f" (module) {
1739
+ module.exports = __rspack_createRequire_require("node:path");
1740
1740
  }
1741
1741
  });
1742
1742
  const gracefulExit = process.execArgv.some((execArg)=>execArg.startsWith('--perf') || execArg.startsWith('--prof') || execArg.startsWith('--cpu-prof') || execArg.startsWith('--heap-prof') || execArg.startsWith('--diagnostic-dir'));
package/dist/3145.js CHANGED
@@ -588,7 +588,7 @@ function prepareCli() {
588
588
  if (!npm_execpath || npm_execpath.includes('npx-cli.js') || npm_execpath.includes('.bun')) logger_logger.log();
589
589
  }
590
590
  function showRstest() {
591
- logger_logger.greet(" Rstest v0.9.6");
591
+ logger_logger.greet(" Rstest v0.9.7");
592
592
  logger_logger.log('');
593
593
  }
594
594
  const runtimeOptionDefinitions = [
@@ -821,6 +821,10 @@ const listCommandOptionDefinitions = [
821
821
  [
822
822
  '--printLocation',
823
823
  'print test case location'
824
+ ],
825
+ [
826
+ '--summary',
827
+ 'print a summary after the list'
824
828
  ]
825
829
  ];
826
830
  const applyOptions = (command, definitions)=>{
@@ -856,6 +860,7 @@ const resolveCliRuntime = async (options)=>{
856
860
  createRstest
857
861
  };
858
862
  };
863
+ const normalizeCliFilters = (filters)=>filters.map((filter)=>normalize(String(filter)));
859
864
  const runRest = async ({ options, filters, command })=>{
860
865
  let rstest;
861
866
  const unexpectedlyExitHandler = (err)=>{
@@ -867,7 +872,7 @@ const runRest = async ({ options, filters, command })=>{
867
872
  config,
868
873
  configFilePath,
869
874
  projects
870
- }, command, filters.map(normalize));
875
+ }, command, normalizeCliFilters(filters));
871
876
  process.on('uncaughtException', unexpectedlyExitHandler);
872
877
  process.on('unhandledRejection', unexpectedlyExitHandler);
873
878
  if ('watch' === command) {
@@ -900,7 +905,7 @@ function createCli() {
900
905
  return sections;
901
906
  }
902
907
  });
903
- cli.version("0.9.6");
908
+ cli.version("0.9.7");
904
909
  const defaultCommand = cli.command('[...filters]', 'run tests').option('-w, --watch', 'Run tests in watch mode');
905
910
  applyRuntimeCommandOptions(defaultCommand);
906
911
  defaultCommand.action(async (filters, options)=>{
@@ -947,12 +952,13 @@ function createCli() {
947
952
  config,
948
953
  configFilePath,
949
954
  projects
950
- }, 'list', filters.map(normalize));
955
+ }, 'list', normalizeCliFilters(filters));
951
956
  await rstest.listTests({
952
957
  filesOnly: options.filesOnly,
953
958
  json: options.json,
954
959
  includeSuites: options.includeSuites,
955
- printLocation: options.printLocation
960
+ printLocation: options.printLocation,
961
+ summary: options.summary
956
962
  });
957
963
  } catch (err) {
958
964
  logger_logger.error('Failed to run Rstest list.');
@@ -1516,6 +1522,77 @@ function addSnapshotResult(summary, result) {
1516
1522
  summary.updated += result.updated;
1517
1523
  summary.total += result.added + result.matched + result.unmatched + result.updated;
1518
1524
  }
1525
+ const REPORT_INTERVAL_MS = 30000;
1526
+ const SLOW_CASE_THRESHOLD_MS = 10000;
1527
+ const MAX_REPORT_COUNT = 20;
1528
+ class NonTTYProgressNotifier {
1529
+ rootPath;
1530
+ testState;
1531
+ reportTimeout;
1532
+ startTime;
1533
+ started = false;
1534
+ reportCount = 0;
1535
+ constructor(rootPath, testState){
1536
+ this.rootPath = rootPath;
1537
+ this.testState = testState;
1538
+ }
1539
+ start() {
1540
+ if (this.started) return;
1541
+ this.started = true;
1542
+ this.startTime ??= Date.now();
1543
+ this.scheduleReport();
1544
+ }
1545
+ notifyOutput() {
1546
+ if (this.started) this.scheduleReport();
1547
+ }
1548
+ stop() {
1549
+ this.started = false;
1550
+ if (this.reportTimeout) {
1551
+ clearTimeout(this.reportTimeout);
1552
+ this.reportTimeout = void 0;
1553
+ }
1554
+ }
1555
+ scheduleReport() {
1556
+ if (this.reportTimeout) clearTimeout(this.reportTimeout);
1557
+ this.reportTimeout = setTimeout(()=>{
1558
+ this.report();
1559
+ this.reportCount++;
1560
+ if (this.reportCount < MAX_REPORT_COUNT) this.scheduleReport();
1561
+ }, REPORT_INTERVAL_MS);
1562
+ this.reportTimeout.unref();
1563
+ }
1564
+ report() {
1565
+ const runningModules = this.testState.getRunningModules();
1566
+ const testModules = this.testState.getTestModules();
1567
+ const doneFiles = testModules.length;
1568
+ const allResults = testModules.flatMap((mod)=>mod.results).concat(Array.from(runningModules.values()).flatMap(({ results })=>results));
1569
+ const passed = allResults.filter((r)=>'pass' === r.status).length;
1570
+ const failed = allResults.filter((r)=>'fail' === r.status).length;
1571
+ const elapsed = prettyTime(Date.now() - this.startTime);
1572
+ const filePart = `test files: ${doneFiles} done${runningModules.size ? `, ${runningModules.size} running` : ''}`;
1573
+ const testParts = [
1574
+ passed ? `${passed} passed` : null,
1575
+ failed ? `${failed} failed` : null
1576
+ ].filter(Boolean);
1577
+ const parts = [
1578
+ filePart,
1579
+ testParts.length ? `tests: ${testParts.join(', ')}` : null,
1580
+ elapsed
1581
+ ].filter(Boolean);
1582
+ console.log(`[PROGRESS] ${parts.join(' | ')}`);
1583
+ if (runningModules.size > 0) {
1584
+ const now = Date.now();
1585
+ for (const [module, { runningTests }] of runningModules.entries()){
1586
+ const relativePath = relative(this.rootPath, module);
1587
+ const slowCases = runningTests.filter((t)=>t.startTime && now - t.startTime > SLOW_CASE_THRESHOLD_MS);
1588
+ if (slowCases.length > 0) {
1589
+ const caseNames = slowCases.map((t)=>`${getTaskNameWithPrefix(t)} ${prettyTime(now - t.startTime)}`).join(', ');
1590
+ console.log(` Running: ${relativePath} > ${caseNames}`);
1591
+ } else console.log(` Running: ${relativePath}`);
1592
+ }
1593
+ }
1594
+ }
1595
+ }
1519
1596
  const getSummaryStatusString = (tasks, name = 'tests', showTotal = true)=>{
1520
1597
  if (0 === tasks.length) return logger_color.dim(`no ${name}`);
1521
1598
  const passed = tasks.filter((result)=>'pass' === result.status);
@@ -1808,12 +1885,28 @@ const logFileTitle = (test, relativePath, alwaysShowTime = false, showProjectNam
1808
1885
  if (test.heap) title += ` ${logger_color.magenta(formatHeapUsed(test.heap))}`;
1809
1886
  logger_logger.log(title);
1810
1887
  };
1888
+ const logUserConsoleLog = (rootPath, log)=>{
1889
+ const titles = [];
1890
+ const testPath = relative(rootPath, log.testPath);
1891
+ if (log.trace) {
1892
+ const [frame] = stack_trace_parser_esm_parse(log.trace);
1893
+ const filePath = relative(rootPath, frame.file || '');
1894
+ if (filePath !== testPath) titles.push(testPath);
1895
+ titles.push(`${filePath}:${frame.lineNumber}:${frame.column}`);
1896
+ } else titles.push(testPath);
1897
+ const logOutput = 'stdout' === log.type ? logger_logger.log : logger_logger.stderr;
1898
+ logOutput('');
1899
+ logOutput(`${log.name}${logger_color.gray(logger_color.dim(` | ${titles.join(logger_color.gray(logger_color.dim(' | ')))}`))}`);
1900
+ logOutput(log.content);
1901
+ logOutput('');
1902
+ };
1811
1903
  class DefaultReporter {
1812
1904
  rootPath;
1813
1905
  config;
1814
1906
  projectConfigs;
1815
1907
  options = {};
1816
1908
  statusRenderer;
1909
+ nonTTYProgressNotifier;
1817
1910
  testState;
1818
1911
  constructor({ rootPath, options, config, testState, projectConfigs }){
1819
1912
  this.rootPath = rootPath;
@@ -1822,12 +1915,15 @@ class DefaultReporter {
1822
1915
  this.options = options;
1823
1916
  this.testState = testState;
1824
1917
  if (isTTY() || options.logger) this.statusRenderer = new StatusRenderer(rootPath, testState, options.logger);
1918
+ else this.nonTTYProgressNotifier = new NonTTYProgressNotifier(rootPath, testState);
1825
1919
  }
1826
1920
  onTestFileStart() {
1827
1921
  this.statusRenderer?.onTestFileStart();
1922
+ this.nonTTYProgressNotifier?.start();
1828
1923
  }
1829
1924
  onTestFileResult(test) {
1830
1925
  this.statusRenderer?.onTestFileResult();
1926
+ this.nonTTYProgressNotifier?.notifyOutput();
1831
1927
  const projectConfig = this.projectConfigs.get(test.project);
1832
1928
  const hideSkippedTestFiles = projectConfig?.hideSkippedTestFiles ?? this.config.hideSkippedTestFiles;
1833
1929
  if (hideSkippedTestFiles && 'skip' === test.status) return;
@@ -1850,25 +1946,16 @@ class DefaultReporter {
1850
1946
  onUserConsoleLog(log) {
1851
1947
  const shouldLog = this.config.onConsoleLog?.(log.content) ?? true;
1852
1948
  if (!shouldLog) return;
1853
- const titles = [];
1854
- const testPath = relative(this.rootPath, log.testPath);
1855
- if (log.trace) {
1856
- const [frame] = stack_trace_parser_esm_parse(log.trace);
1857
- const filePath = relative(this.rootPath, frame.file || '');
1858
- if (filePath !== testPath) titles.push(testPath);
1859
- titles.push(`${filePath}:${frame.lineNumber}:${frame.column}`);
1860
- } else titles.push(testPath);
1861
- const logOutput = 'stdout' === log.type ? logger_logger.log : logger_logger.stderr;
1862
- logOutput('');
1863
- logOutput(`${log.name}${logger_color.gray(logger_color.dim(` | ${titles.join(logger_color.gray(logger_color.dim(' | ')))}`))}`);
1864
- logOutput(log.content);
1865
- logOutput('');
1949
+ this.nonTTYProgressNotifier?.notifyOutput();
1950
+ logUserConsoleLog(this.rootPath, log);
1866
1951
  }
1867
1952
  onExit() {
1868
1953
  this.statusRenderer?.clear();
1954
+ this.nonTTYProgressNotifier?.stop();
1869
1955
  }
1870
1956
  async onTestRunEnd({ results, testResults, duration, getSourcemap, snapshotSummary, filterRerunTestPaths, unhandledErrors }) {
1871
1957
  this.statusRenderer?.clear();
1958
+ this.nonTTYProgressNotifier?.stop();
1872
1959
  if (false === this.options.summary) return;
1873
1960
  await printSummaryErrorLogs({
1874
1961
  testResults,
@@ -1903,7 +1990,7 @@ class BlobReporter {
1903
1990
  const shard = this.config.shard;
1904
1991
  const fileName = shard ? `blob-${shard.index}-${shard.count}.json` : 'blob.json';
1905
1992
  const blobData = {
1906
- version: "0.9.6",
1993
+ version: "0.9.7",
1907
1994
  shard: shard ? {
1908
1995
  index: shard.index,
1909
1996
  count: shard.count
@@ -1925,6 +2012,74 @@ class BlobReporter {
1925
2012
  writeFileSync(join(this.outputDir, fileName), JSON.stringify(blobData), 'utf-8');
1926
2013
  }
1927
2014
  }
2015
+ const DOT_BY_STATUS = {
2016
+ fail: 'x',
2017
+ pass: '·',
2018
+ skip: '-',
2019
+ todo: '*'
2020
+ };
2021
+ const COLOR_BY_STATUS = {
2022
+ fail: logger_color.red,
2023
+ pass: logger_color.green,
2024
+ skip: logger_color.yellow,
2025
+ todo: logger_color.gray
2026
+ };
2027
+ class DotReporter {
2028
+ rootPath;
2029
+ options;
2030
+ outputStream;
2031
+ getColumns;
2032
+ currentColumn = 0;
2033
+ constructor({ rootPath, options = {} }){
2034
+ this.rootPath = rootPath;
2035
+ this.options = options;
2036
+ this.outputStream = options.logger?.outputStream ?? process.stdout;
2037
+ this.getColumns = options.logger?.getColumns ?? (()=>'columns' in process.stdout ? process.stdout.columns || 80 : 80);
2038
+ }
2039
+ onTestCaseResult(result) {
2040
+ const marker = COLOR_BY_STATUS[result.status](DOT_BY_STATUS[result.status]);
2041
+ this.outputStream.write(marker);
2042
+ this.currentColumn += 1;
2043
+ if (this.currentColumn >= this.getColumnWidth()) {
2044
+ this.outputStream.write('\n');
2045
+ this.currentColumn = 0;
2046
+ }
2047
+ }
2048
+ onUserConsoleLog(log) {
2049
+ this.flushLine();
2050
+ logUserConsoleLog(this.rootPath, log);
2051
+ }
2052
+ onExit() {
2053
+ this.flushLine();
2054
+ }
2055
+ async onTestRunEnd({ results, testResults, duration, getSourcemap, snapshotSummary, filterRerunTestPaths, unhandledErrors }) {
2056
+ this.flushLine();
2057
+ if (false === this.options.summary) return;
2058
+ await printSummaryErrorLogs({
2059
+ testResults,
2060
+ results,
2061
+ unhandledErrors,
2062
+ rootPath: this.rootPath,
2063
+ getSourcemap,
2064
+ filterRerunTestPaths
2065
+ });
2066
+ printSummaryLog({
2067
+ results,
2068
+ testResults,
2069
+ duration,
2070
+ rootPath: this.rootPath,
2071
+ snapshotSummary
2072
+ });
2073
+ }
2074
+ flushLine() {
2075
+ if (0 === this.currentColumn) return;
2076
+ this.outputStream.write('\n');
2077
+ this.currentColumn = 0;
2078
+ }
2079
+ getColumnWidth() {
2080
+ return Math.max(1, this.getColumns() || 80);
2081
+ }
2082
+ }
1928
2083
  class GithubActionsReporter {
1929
2084
  onWritePath;
1930
2085
  rootPath;
@@ -2007,7 +2162,7 @@ class JsonReporter {
2007
2162
  const hasFailedStatus = failedTests.length > 0 || failedFiles.length > 0 || (unhandledErrors?.length ?? 0) > 0 || noTestsDiscovered && !this.config.passWithNoTests;
2008
2163
  return {
2009
2164
  tool: 'rstest',
2010
- version: "0.9.6",
2165
+ version: "0.9.7",
2011
2166
  status: hasFailedStatus ? 'fail' : 'pass',
2012
2167
  summary: {
2013
2168
  testFiles: results.length,
@@ -3660,7 +3815,7 @@ class MdReporter {
3660
3815
  }
3661
3816
  renderFrontMatter(lines) {
3662
3817
  const frontMatter = {
3663
- tool: "@rstest/core@0.9.6",
3818
+ tool: "@rstest/core@0.9.7",
3664
3819
  timestamp: new Date().toISOString()
3665
3820
  };
3666
3821
  if (this.options.header.env) frontMatter.runtime = {
@@ -3911,6 +4066,7 @@ class VerboseReporter extends DefaultReporter {
3911
4066
  }
3912
4067
  onTestFileResult(test) {
3913
4068
  this.statusRenderer?.onTestFileResult();
4069
+ this.nonTTYProgressNotifier?.notifyOutput();
3914
4070
  const projectConfig = this.projectConfigs.get(test.project);
3915
4071
  const hideSkippedTestFiles = projectConfig?.hideSkippedTestFiles ?? this.config.hideSkippedTestFiles;
3916
4072
  if (hideSkippedTestFiles && 'skip' === test.status) return;
@@ -4034,7 +4190,7 @@ class Rstest {
4034
4190
  updateSnapshot: rstestConfig.update ? 'all' : dist_m ? 'none' : 'new'
4035
4191
  });
4036
4192
  this.snapshotManager = snapshotManager;
4037
- this.version = "0.9.6";
4193
+ this.version = "0.9.7";
4038
4194
  this.rootPath = rootPath;
4039
4195
  this.originalConfig = userConfig;
4040
4196
  this.normalizedConfig = rstestConfig;
@@ -4111,6 +4267,7 @@ class Rstest {
4111
4267
  }
4112
4268
  const reportersMap = {
4113
4269
  default: DefaultReporter,
4270
+ dot: DotReporter,
4114
4271
  verbose: VerboseReporter,
4115
4272
  'github-actions': GithubActionsReporter,
4116
4273
  junit: JUnitReporter,