@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/LICENSE.md +0 -200
- package/dist/0~130.js +7 -9
- package/dist/0~1472.js +8 -12
- package/dist/0~2173.js +114 -56
- package/dist/0~2255.js +2 -2
- package/dist/0~3919.js +1 -1
- package/dist/0~4403.js +1 -2
- package/dist/0~4809.js +3 -2
- package/dist/0~5835.js +2 -2
- package/dist/0~6588.js +3 -6
- package/dist/0~6923.js +2 -2
- package/dist/0~7583.js +7 -8
- package/dist/0~7882.js +37 -152
- package/dist/0~8426.js +5 -7
- package/dist/0~89.js +14 -16
- package/dist/0~9348.js +1 -2
- package/dist/0~9634.js +3 -6
- package/dist/1157.js +127 -41
- package/dist/1157.js.LICENSE.txt +21 -0
- package/dist/1294.js +1 -1
- package/dist/{2672.js → 3160.js} +311 -7
- package/dist/487.js +2 -2
- package/dist/5734.js +2 -5
- package/dist/6151.js +198 -3
- package/dist/6973.js +5 -7
- package/dist/7011.js +19 -0
- package/dist/9131.js +162 -81
- package/dist/browser-runtime/389.js +615 -13
- package/dist/browser-runtime/389.js.map +1 -1
- package/dist/browser-runtime/index.d.ts +217 -20
- package/dist/browser.d.ts +251 -22
- package/dist/browser.js +1 -7
- package/dist/globalSetupWorker.js +2 -4
- package/dist/index.d.ts +241 -21
- package/dist/mockRuntimeCode.js +92 -0
- package/dist/rslib-runtime.js +0 -27
- package/dist/worker.d.ts +54 -13
- package/dist/worker.js +3 -5
- package/package.json +5 -6
- package/dist/3278.js +0 -318
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,
|
|
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.
|
|
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 (
|
|
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.
|
|
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: ${
|
|
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
|
-
${
|
|
1307
|
-
if (options.project) errorMsg += `\n${
|
|
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
|
|
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 ?
|
|
1415
|
-
passed.length ?
|
|
1416
|
-
skipped.length ?
|
|
1417
|
-
todo.length ?
|
|
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(
|
|
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(
|
|
1424
|
-
if (snapshots.unmatched) summary.push(
|
|
1425
|
-
if (snapshots.updated) summary.push(
|
|
1426
|
-
if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(
|
|
1427
|
-
else summary.push(
|
|
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(`${
|
|
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(
|
|
1435
|
-
else summary.push(
|
|
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(`${
|
|
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(`${
|
|
1486
|
+
logger_logger.log(`${color.gray(title.padStart(12))} ${snapshot}`);
|
|
1444
1487
|
}
|
|
1445
1488
|
};
|
|
1446
|
-
const TestFileSummaryLabel =
|
|
1447
|
-
const TestSummaryLabel =
|
|
1448
|
-
const DurationLabel =
|
|
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)} ${
|
|
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(
|
|
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 ? `${
|
|
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 = ` ${
|
|
1632
|
-
if (runningTests.length > 1) caseLog +=
|
|
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)} ${
|
|
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:
|
|
1667
|
-
pass:
|
|
1668
|
-
todo:
|
|
1669
|
-
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 ?
|
|
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 ?
|
|
1678
|
-
const heap = result.heap ? ` ${
|
|
1679
|
-
logger_logger.log(` ${icon} ${nameStr}${
|
|
1680
|
-
if (result.errors) for (const error of result.errors)logger_logger.log(
|
|
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 = ` ${
|
|
1685
|
-
|
|
1686
|
-
title += ` ${
|
|
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 += ` ${
|
|
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
|
-
|
|
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}${
|
|
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 "${
|
|
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 "${
|
|
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(`${
|
|
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(`${
|
|
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(
|
|
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(
|
|
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) :
|
|
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
|
|
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.
|
|
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,
|
|
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
|
-
...
|
|
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.`);
|