@vitest/runner 4.0.9 → 4.0.11
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/index.d.ts +39 -3
- package/dist/index.js +568 -451
- package/dist/{hooks.d-C0RE9A6t.d.ts → tasks.d-r9p5YKu0.d.ts} +244 -111
- package/dist/types.d.ts +11 -3
- package/dist/utils.d.ts +2 -2
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { isObject, createDefer, toArray, isNegativeNaN, objectAttr, shuffle, assertTypes } from '@vitest/utils/helpers';
|
|
2
|
-
import { getSafeTimers } from '@vitest/utils/timers';
|
|
3
1
|
import { processError } from '@vitest/utils/error';
|
|
2
|
+
import { isObject, createDefer, assertTypes, toArray, isNegativeNaN, objectAttr, shuffle } from '@vitest/utils/helpers';
|
|
3
|
+
import { getSafeTimers } from '@vitest/utils/timers';
|
|
4
4
|
import { format, formatRegExp, objDisplay } from '@vitest/utils/display';
|
|
5
5
|
import { c as createChainable, f as findTestFileStackTrace, b as createFileTask, a as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, l as limitConcurrency, p as partitionSuiteChildren, q as hasTests, o as hasFailed } from './chunk-tasks.js';
|
|
6
6
|
import '@vitest/utils/source-map';
|
|
@@ -48,18 +48,6 @@ function getHooks(key) {
|
|
|
48
48
|
return hooksMap.get(key);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
-
async function runSetupFiles(config, files, runner) {
|
|
52
|
-
if (config.sequence.setupFiles === "parallel") {
|
|
53
|
-
await Promise.all(files.map(async (fsPath) => {
|
|
54
|
-
await runner.importFile(fsPath, "setup");
|
|
55
|
-
}));
|
|
56
|
-
} else {
|
|
57
|
-
for (const fsPath of files) {
|
|
58
|
-
await runner.importFile(fsPath, "setup");
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
|
|
63
51
|
function mergeScopedFixtures(testFixtures, scopedFixtures) {
|
|
64
52
|
const scopedFixturesMap = scopedFixtures.reduce((map, fixture) => {
|
|
65
53
|
map[fixture.prop] = fixture;
|
|
@@ -390,6 +378,176 @@ function getRunningTests() {
|
|
|
390
378
|
return tests;
|
|
391
379
|
}
|
|
392
380
|
|
|
381
|
+
function getDefaultHookTimeout() {
|
|
382
|
+
return getRunner().config.hookTimeout;
|
|
383
|
+
}
|
|
384
|
+
const CLEANUP_TIMEOUT_KEY = Symbol.for("VITEST_CLEANUP_TIMEOUT");
|
|
385
|
+
const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
386
|
+
function getBeforeHookCleanupCallback(hook, result, context) {
|
|
387
|
+
if (typeof result === "function") {
|
|
388
|
+
const timeout = CLEANUP_TIMEOUT_KEY in hook && typeof hook[CLEANUP_TIMEOUT_KEY] === "number" ? hook[CLEANUP_TIMEOUT_KEY] : getDefaultHookTimeout();
|
|
389
|
+
const stackTraceError = CLEANUP_STACK_TRACE_KEY in hook && hook[CLEANUP_STACK_TRACE_KEY] instanceof Error ? hook[CLEANUP_STACK_TRACE_KEY] : undefined;
|
|
390
|
+
return withTimeout(result, timeout, true, stackTraceError, (_, error) => {
|
|
391
|
+
if (context) {
|
|
392
|
+
abortContextSignal(context, error);
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* Registers a callback function to be executed once before all tests within the current suite.
|
|
399
|
+
* This hook is useful for scenarios where you need to perform setup operations that are common to all tests in a suite, such as initializing a database connection or setting up a test environment.
|
|
400
|
+
*
|
|
401
|
+
* **Note:** The `beforeAll` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
402
|
+
*
|
|
403
|
+
* @param {Function} fn - The callback function to be executed before all tests.
|
|
404
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
405
|
+
* @returns {void}
|
|
406
|
+
* @example
|
|
407
|
+
* ```ts
|
|
408
|
+
* // Example of using beforeAll to set up a database connection
|
|
409
|
+
* beforeAll(async () => {
|
|
410
|
+
* await database.connect();
|
|
411
|
+
* });
|
|
412
|
+
* ```
|
|
413
|
+
*/
|
|
414
|
+
function beforeAll(fn, timeout = getDefaultHookTimeout()) {
|
|
415
|
+
assertTypes(fn, "\"beforeAll\" callback", ["function"]);
|
|
416
|
+
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
417
|
+
return getCurrentSuite().on("beforeAll", Object.assign(withTimeout(fn, timeout, true, stackTraceError), {
|
|
418
|
+
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
419
|
+
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
420
|
+
}));
|
|
421
|
+
}
|
|
422
|
+
/**
|
|
423
|
+
* Registers a callback function to be executed once after all tests within the current suite have completed.
|
|
424
|
+
* This hook is useful for scenarios where you need to perform cleanup operations after all tests in a suite have run, such as closing database connections or cleaning up temporary files.
|
|
425
|
+
*
|
|
426
|
+
* **Note:** The `afterAll` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
427
|
+
*
|
|
428
|
+
* @param {Function} fn - The callback function to be executed after all tests.
|
|
429
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
430
|
+
* @returns {void}
|
|
431
|
+
* @example
|
|
432
|
+
* ```ts
|
|
433
|
+
* // Example of using afterAll to close a database connection
|
|
434
|
+
* afterAll(async () => {
|
|
435
|
+
* await database.disconnect();
|
|
436
|
+
* });
|
|
437
|
+
* ```
|
|
438
|
+
*/
|
|
439
|
+
function afterAll(fn, timeout) {
|
|
440
|
+
assertTypes(fn, "\"afterAll\" callback", ["function"]);
|
|
441
|
+
return getCurrentSuite().on("afterAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR")));
|
|
442
|
+
}
|
|
443
|
+
/**
|
|
444
|
+
* Registers a callback function to be executed before each test within the current suite.
|
|
445
|
+
* This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables.
|
|
446
|
+
*
|
|
447
|
+
* **Note:** The `beforeEach` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
448
|
+
*
|
|
449
|
+
* @param {Function} fn - The callback function to be executed before each test. This function receives an `TestContext` parameter if additional test context is needed.
|
|
450
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
451
|
+
* @returns {void}
|
|
452
|
+
* @example
|
|
453
|
+
* ```ts
|
|
454
|
+
* // Example of using beforeEach to reset a database state
|
|
455
|
+
* beforeEach(async () => {
|
|
456
|
+
* await database.reset();
|
|
457
|
+
* });
|
|
458
|
+
* ```
|
|
459
|
+
*/
|
|
460
|
+
function beforeEach(fn, timeout = getDefaultHookTimeout()) {
|
|
461
|
+
assertTypes(fn, "\"beforeEach\" callback", ["function"]);
|
|
462
|
+
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
463
|
+
const runner = getRunner();
|
|
464
|
+
return getCurrentSuite().on("beforeEach", Object.assign(withTimeout(withFixtures(runner, fn), timeout ?? getDefaultHookTimeout(), true, stackTraceError, abortIfTimeout), {
|
|
465
|
+
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
466
|
+
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
467
|
+
}));
|
|
468
|
+
}
|
|
469
|
+
/**
|
|
470
|
+
* Registers a callback function to be executed after each test within the current suite has completed.
|
|
471
|
+
* This hook is useful for scenarios where you need to clean up or reset the test environment after each test runs, such as deleting temporary files, clearing test-specific database entries, or resetting mocked functions.
|
|
472
|
+
*
|
|
473
|
+
* **Note:** The `afterEach` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
474
|
+
*
|
|
475
|
+
* @param {Function} fn - The callback function to be executed after each test. This function receives an `TestContext` parameter if additional test context is needed.
|
|
476
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
477
|
+
* @returns {void}
|
|
478
|
+
* @example
|
|
479
|
+
* ```ts
|
|
480
|
+
* // Example of using afterEach to delete temporary files created during a test
|
|
481
|
+
* afterEach(async () => {
|
|
482
|
+
* await fileSystem.deleteTempFiles();
|
|
483
|
+
* });
|
|
484
|
+
* ```
|
|
485
|
+
*/
|
|
486
|
+
function afterEach(fn, timeout) {
|
|
487
|
+
assertTypes(fn, "\"afterEach\" callback", ["function"]);
|
|
488
|
+
const runner = getRunner();
|
|
489
|
+
return getCurrentSuite().on("afterEach", withTimeout(withFixtures(runner, fn), timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
492
|
+
* Registers a callback function to be executed when a test fails within the current suite.
|
|
493
|
+
* This function allows for custom actions to be performed in response to test failures, such as logging, cleanup, or additional diagnostics.
|
|
494
|
+
*
|
|
495
|
+
* **Note:** The `onTestFailed` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
496
|
+
*
|
|
497
|
+
* @param {Function} fn - The callback function to be executed upon a test failure. The function receives the test result (including errors).
|
|
498
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
499
|
+
* @throws {Error} Throws an error if the function is not called within a test.
|
|
500
|
+
* @returns {void}
|
|
501
|
+
* @example
|
|
502
|
+
* ```ts
|
|
503
|
+
* // Example of using onTestFailed to log failure details
|
|
504
|
+
* onTestFailed(({ errors }) => {
|
|
505
|
+
* console.log(`Test failed: ${test.name}`, errors);
|
|
506
|
+
* });
|
|
507
|
+
* ```
|
|
508
|
+
*/
|
|
509
|
+
const onTestFailed = createTestHook("onTestFailed", (test, handler, timeout) => {
|
|
510
|
+
test.onFailed || (test.onFailed = []);
|
|
511
|
+
test.onFailed.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
512
|
+
});
|
|
513
|
+
/**
|
|
514
|
+
* Registers a callback function to be executed when the current test finishes, regardless of the outcome (pass or fail).
|
|
515
|
+
* This function is ideal for performing actions that should occur after every test execution, such as cleanup, logging, or resetting shared resources.
|
|
516
|
+
*
|
|
517
|
+
* This hook is useful if you have access to a resource in the test itself and you want to clean it up after the test finishes. It is a more compact way to clean up resources than using the combination of `beforeEach` and `afterEach`.
|
|
518
|
+
*
|
|
519
|
+
* **Note:** The `onTestFinished` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
520
|
+
*
|
|
521
|
+
* **Note:** The `onTestFinished` hook is not called if the test is canceled with a dynamic `ctx.skip()` call.
|
|
522
|
+
*
|
|
523
|
+
* @param {Function} fn - The callback function to be executed after a test finishes. The function can receive parameters providing details about the completed test, including its success or failure status.
|
|
524
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
525
|
+
* @throws {Error} Throws an error if the function is not called within a test.
|
|
526
|
+
* @returns {void}
|
|
527
|
+
* @example
|
|
528
|
+
* ```ts
|
|
529
|
+
* // Example of using onTestFinished for cleanup
|
|
530
|
+
* const db = await connectToDatabase();
|
|
531
|
+
* onTestFinished(async () => {
|
|
532
|
+
* await db.disconnect();
|
|
533
|
+
* });
|
|
534
|
+
* ```
|
|
535
|
+
*/
|
|
536
|
+
const onTestFinished = createTestHook("onTestFinished", (test, handler, timeout) => {
|
|
537
|
+
test.onFinished || (test.onFinished = []);
|
|
538
|
+
test.onFinished.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
539
|
+
});
|
|
540
|
+
function createTestHook(name, handler) {
|
|
541
|
+
return (fn, timeout) => {
|
|
542
|
+
assertTypes(fn, `"${name}" callback`, ["function"]);
|
|
543
|
+
const current = getCurrentTest();
|
|
544
|
+
if (!current) {
|
|
545
|
+
throw new Error(`Hook ${name}() can only be called inside a test`);
|
|
546
|
+
}
|
|
547
|
+
return handler(current, fn, timeout);
|
|
548
|
+
};
|
|
549
|
+
}
|
|
550
|
+
|
|
393
551
|
/**
|
|
394
552
|
* Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
|
|
395
553
|
* Suites can contain both tests and other suites, enabling complex test structures.
|
|
@@ -610,7 +768,8 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
|
|
|
610
768
|
repeats: options.repeats,
|
|
611
769
|
mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run",
|
|
612
770
|
meta: options.meta ?? Object.create(null),
|
|
613
|
-
annotations: []
|
|
771
|
+
annotations: [],
|
|
772
|
+
artifacts: []
|
|
614
773
|
};
|
|
615
774
|
const handler = options.handler;
|
|
616
775
|
if (task.mode === "run" && !handler) {
|
|
@@ -1015,86 +1174,264 @@ function formatTemplateString(cases, args) {
|
|
|
1015
1174
|
return res;
|
|
1016
1175
|
}
|
|
1017
1176
|
|
|
1018
|
-
const now$2 =
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1177
|
+
const now$2 = Date.now;
|
|
1178
|
+
const collectorContext = {
|
|
1179
|
+
tasks: [],
|
|
1180
|
+
currentSuite: null
|
|
1181
|
+
};
|
|
1182
|
+
function collectTask(task) {
|
|
1183
|
+
var _collectorContext$cur;
|
|
1184
|
+
(_collectorContext$cur = collectorContext.currentSuite) === null || _collectorContext$cur === void 0 ? void 0 : _collectorContext$cur.tasks.push(task);
|
|
1185
|
+
}
|
|
1186
|
+
async function runWithSuite(suite, fn) {
|
|
1187
|
+
const prev = collectorContext.currentSuite;
|
|
1188
|
+
collectorContext.currentSuite = suite;
|
|
1189
|
+
await fn();
|
|
1190
|
+
collectorContext.currentSuite = prev;
|
|
1191
|
+
}
|
|
1192
|
+
function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
1193
|
+
if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY) {
|
|
1194
|
+
return fn;
|
|
1195
|
+
}
|
|
1196
|
+
const { setTimeout, clearTimeout } = getSafeTimers();
|
|
1197
|
+
// this function name is used to filter error in test/cli/test/fails.test.ts
|
|
1198
|
+
return (function runWithTimeout(...args) {
|
|
1199
|
+
const startTime = now$2();
|
|
1200
|
+
const runner = getRunner();
|
|
1201
|
+
runner._currentTaskStartTime = startTime;
|
|
1202
|
+
runner._currentTaskTimeout = timeout;
|
|
1203
|
+
return new Promise((resolve_, reject_) => {
|
|
1204
|
+
var _timer$unref;
|
|
1205
|
+
const timer = setTimeout(() => {
|
|
1206
|
+
clearTimeout(timer);
|
|
1207
|
+
rejectTimeoutError();
|
|
1208
|
+
}, timeout);
|
|
1209
|
+
// `unref` might not exist in browser
|
|
1210
|
+
(_timer$unref = timer.unref) === null || _timer$unref === void 0 ? void 0 : _timer$unref.call(timer);
|
|
1211
|
+
function rejectTimeoutError() {
|
|
1212
|
+
const error = makeTimeoutError(isHook, timeout, stackTraceError);
|
|
1213
|
+
onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout(args, error);
|
|
1214
|
+
reject_(error);
|
|
1047
1215
|
}
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
if
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
file.tasks.push(suite);
|
|
1059
|
-
}
|
|
1060
|
-
} else {
|
|
1061
|
-
// check that types are exhausted
|
|
1062
|
-
c;
|
|
1216
|
+
function resolve(result) {
|
|
1217
|
+
runner._currentTaskStartTime = undefined;
|
|
1218
|
+
runner._currentTaskTimeout = undefined;
|
|
1219
|
+
clearTimeout(timer);
|
|
1220
|
+
// if test/hook took too long in microtask, setTimeout won't be triggered,
|
|
1221
|
+
// but we still need to fail the test, see
|
|
1222
|
+
// https://github.com/vitest-dev/vitest/issues/2920
|
|
1223
|
+
if (now$2() - startTime >= timeout) {
|
|
1224
|
+
rejectTimeoutError();
|
|
1225
|
+
return;
|
|
1063
1226
|
}
|
|
1227
|
+
resolve_(result);
|
|
1064
1228
|
}
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
file.result = {
|
|
1071
|
-
state: "fail",
|
|
1072
|
-
errors: [error]
|
|
1073
|
-
};
|
|
1074
|
-
const durations = (_runner$getImportDura2 = runner.getImportDurations) === null || _runner$getImportDura2 === void 0 ? void 0 : _runner$getImportDura2.call(runner);
|
|
1075
|
-
if (durations) {
|
|
1076
|
-
file.importDurations = durations;
|
|
1229
|
+
function reject(error) {
|
|
1230
|
+
runner._currentTaskStartTime = undefined;
|
|
1231
|
+
runner._currentTaskTimeout = undefined;
|
|
1232
|
+
clearTimeout(timer);
|
|
1233
|
+
reject_(error);
|
|
1077
1234
|
}
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1235
|
+
// sync test/hook will be caught by try/catch
|
|
1236
|
+
try {
|
|
1237
|
+
const result = fn(...args);
|
|
1238
|
+
// the result is a thenable, we don't wrap this in Promise.resolve
|
|
1239
|
+
// to avoid creating new promises
|
|
1240
|
+
if (typeof result === "object" && result != null && typeof result.then === "function") {
|
|
1241
|
+
result.then(resolve, reject);
|
|
1242
|
+
} else {
|
|
1243
|
+
resolve(result);
|
|
1244
|
+
}
|
|
1245
|
+
}
|
|
1246
|
+
// user sync test/hook throws an error
|
|
1247
|
+
catch (error) {
|
|
1248
|
+
reject(error);
|
|
1249
|
+
}
|
|
1250
|
+
});
|
|
1251
|
+
});
|
|
1252
|
+
}
|
|
1253
|
+
const abortControllers = new WeakMap();
|
|
1254
|
+
function abortIfTimeout([context], error) {
|
|
1255
|
+
if (context) {
|
|
1256
|
+
abortContextSignal(context, error);
|
|
1257
|
+
}
|
|
1258
|
+
}
|
|
1259
|
+
function abortContextSignal(context, error) {
|
|
1260
|
+
const abortController = abortControllers.get(context);
|
|
1261
|
+
abortController === null || abortController === void 0 ? void 0 : abortController.abort(error);
|
|
1262
|
+
}
|
|
1263
|
+
function createTestContext(test, runner) {
|
|
1264
|
+
var _runner$extendTaskCon;
|
|
1265
|
+
const context = function() {
|
|
1266
|
+
throw new Error("done() callback is deprecated, use promise instead");
|
|
1267
|
+
};
|
|
1268
|
+
let abortController = abortControllers.get(context);
|
|
1269
|
+
if (!abortController) {
|
|
1270
|
+
abortController = new AbortController();
|
|
1271
|
+
abortControllers.set(context, abortController);
|
|
1272
|
+
}
|
|
1273
|
+
context.signal = abortController.signal;
|
|
1274
|
+
context.task = test;
|
|
1275
|
+
context.skip = (condition, note) => {
|
|
1276
|
+
if (condition === false) {
|
|
1277
|
+
// do nothing
|
|
1278
|
+
return undefined;
|
|
1279
|
+
}
|
|
1280
|
+
test.result ?? (test.result = { state: "skip" });
|
|
1281
|
+
test.result.pending = true;
|
|
1282
|
+
throw new PendingError("test is skipped; abort execution", test, typeof condition === "string" ? condition : note);
|
|
1283
|
+
};
|
|
1284
|
+
context.annotate = ((message, type, attachment) => {
|
|
1285
|
+
if (test.result && test.result.state !== "run") {
|
|
1286
|
+
throw new Error(`Cannot annotate tests outside of the test run. The test "${test.name}" finished running with the "${test.result.state}" state already.`);
|
|
1287
|
+
}
|
|
1288
|
+
const annotation = {
|
|
1289
|
+
message,
|
|
1290
|
+
type: typeof type === "object" || type === undefined ? "notice" : type
|
|
1291
|
+
};
|
|
1292
|
+
const annotationAttachment = typeof type === "object" ? type : attachment;
|
|
1293
|
+
if (annotationAttachment) {
|
|
1294
|
+
annotation.attachment = annotationAttachment;
|
|
1295
|
+
manageArtifactAttachment(annotation.attachment);
|
|
1296
|
+
}
|
|
1297
|
+
return recordAsyncOperation(test, recordArtifact(test, {
|
|
1298
|
+
type: "internal:annotation",
|
|
1299
|
+
annotation
|
|
1300
|
+
}).then(async ({ annotation }) => {
|
|
1301
|
+
if (!runner.onTestAnnotate) {
|
|
1302
|
+
throw new Error(`Test runner doesn't support test annotations.`);
|
|
1303
|
+
}
|
|
1304
|
+
await finishSendTasksUpdate(runner);
|
|
1305
|
+
const resolvedAnnotation = await runner.onTestAnnotate(test, annotation);
|
|
1306
|
+
test.annotations.push(resolvedAnnotation);
|
|
1307
|
+
return resolvedAnnotation;
|
|
1308
|
+
}));
|
|
1309
|
+
});
|
|
1310
|
+
context.onTestFailed = (handler, timeout) => {
|
|
1311
|
+
test.onFailed || (test.onFailed = []);
|
|
1312
|
+
test.onFailed.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
|
|
1313
|
+
};
|
|
1314
|
+
context.onTestFinished = (handler, timeout) => {
|
|
1315
|
+
test.onFinished || (test.onFinished = []);
|
|
1316
|
+
test.onFinished.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
|
|
1317
|
+
};
|
|
1318
|
+
return ((_runner$extendTaskCon = runner.extendTaskContext) === null || _runner$extendTaskCon === void 0 ? void 0 : _runner$extendTaskCon.call(runner, context)) || context;
|
|
1319
|
+
}
|
|
1320
|
+
function makeTimeoutError(isHook, timeout, stackTraceError) {
|
|
1321
|
+
const message = `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.\nIf this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
|
|
1322
|
+
const error = new Error(message);
|
|
1323
|
+
if (stackTraceError === null || stackTraceError === void 0 ? void 0 : stackTraceError.stack) {
|
|
1324
|
+
error.stack = stackTraceError.stack.replace(error.message, stackTraceError.message);
|
|
1325
|
+
}
|
|
1326
|
+
return error;
|
|
1327
|
+
}
|
|
1328
|
+
const fileContexts = new WeakMap();
|
|
1329
|
+
function getFileContext(file) {
|
|
1330
|
+
const context = fileContexts.get(file);
|
|
1331
|
+
if (!context) {
|
|
1332
|
+
throw new Error(`Cannot find file context for ${file.name}`);
|
|
1333
|
+
}
|
|
1334
|
+
return context;
|
|
1335
|
+
}
|
|
1336
|
+
function setFileContext(file, context) {
|
|
1337
|
+
fileContexts.set(file, context);
|
|
1338
|
+
}
|
|
1339
|
+
|
|
1340
|
+
async function runSetupFiles(config, files, runner) {
|
|
1341
|
+
if (config.sequence.setupFiles === "parallel") {
|
|
1342
|
+
await Promise.all(files.map(async (fsPath) => {
|
|
1343
|
+
await runner.importFile(fsPath, "setup");
|
|
1344
|
+
}));
|
|
1345
|
+
} else {
|
|
1346
|
+
for (const fsPath of files) {
|
|
1347
|
+
await runner.importFile(fsPath, "setup");
|
|
1348
|
+
}
|
|
1349
|
+
}
|
|
1095
1350
|
}
|
|
1096
1351
|
|
|
1097
1352
|
const now$1 = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
1353
|
+
async function collectTests(specs, runner) {
|
|
1354
|
+
const files = [];
|
|
1355
|
+
const config = runner.config;
|
|
1356
|
+
const $ = runner.trace;
|
|
1357
|
+
for (const spec of specs) {
|
|
1358
|
+
const filepath = typeof spec === "string" ? spec : spec.filepath;
|
|
1359
|
+
await $("collect_spec", { "code.file.path": filepath }, async () => {
|
|
1360
|
+
var _runner$onCollectStar;
|
|
1361
|
+
const testLocations = typeof spec === "string" ? undefined : spec.testLocations;
|
|
1362
|
+
const file = createFileTask(filepath, config.root, config.name, runner.pool);
|
|
1363
|
+
setFileContext(file, Object.create(null));
|
|
1364
|
+
file.shuffle = config.sequence.shuffle;
|
|
1365
|
+
(_runner$onCollectStar = runner.onCollectStart) === null || _runner$onCollectStar === void 0 ? void 0 : _runner$onCollectStar.call(runner, file);
|
|
1366
|
+
clearCollectorContext(filepath, runner);
|
|
1367
|
+
try {
|
|
1368
|
+
var _runner$getImportDura;
|
|
1369
|
+
const setupFiles = toArray(config.setupFiles);
|
|
1370
|
+
if (setupFiles.length) {
|
|
1371
|
+
const setupStart = now$1();
|
|
1372
|
+
await runSetupFiles(config, setupFiles, runner);
|
|
1373
|
+
const setupEnd = now$1();
|
|
1374
|
+
file.setupDuration = setupEnd - setupStart;
|
|
1375
|
+
} else {
|
|
1376
|
+
file.setupDuration = 0;
|
|
1377
|
+
}
|
|
1378
|
+
const collectStart = now$1();
|
|
1379
|
+
await runner.importFile(filepath, "collect");
|
|
1380
|
+
const durations = (_runner$getImportDura = runner.getImportDurations) === null || _runner$getImportDura === void 0 ? void 0 : _runner$getImportDura.call(runner);
|
|
1381
|
+
if (durations) {
|
|
1382
|
+
file.importDurations = durations;
|
|
1383
|
+
}
|
|
1384
|
+
const defaultTasks = await getDefaultSuite().collect(file);
|
|
1385
|
+
const fileHooks = createSuiteHooks();
|
|
1386
|
+
mergeHooks(fileHooks, getHooks(defaultTasks));
|
|
1387
|
+
for (const c of [...defaultTasks.tasks, ...collectorContext.tasks]) {
|
|
1388
|
+
if (c.type === "test" || c.type === "suite") {
|
|
1389
|
+
file.tasks.push(c);
|
|
1390
|
+
} else if (c.type === "collector") {
|
|
1391
|
+
const suite = await c.collect(file);
|
|
1392
|
+
if (suite.name || suite.tasks.length) {
|
|
1393
|
+
mergeHooks(fileHooks, getHooks(suite));
|
|
1394
|
+
file.tasks.push(suite);
|
|
1395
|
+
}
|
|
1396
|
+
} else {
|
|
1397
|
+
// check that types are exhausted
|
|
1398
|
+
c;
|
|
1399
|
+
}
|
|
1400
|
+
}
|
|
1401
|
+
setHooks(file, fileHooks);
|
|
1402
|
+
file.collectDuration = now$1() - collectStart;
|
|
1403
|
+
} catch (e) {
|
|
1404
|
+
var _runner$getImportDura2;
|
|
1405
|
+
const error = processError(e);
|
|
1406
|
+
file.result = {
|
|
1407
|
+
state: "fail",
|
|
1408
|
+
errors: [error]
|
|
1409
|
+
};
|
|
1410
|
+
const durations = (_runner$getImportDura2 = runner.getImportDurations) === null || _runner$getImportDura2 === void 0 ? void 0 : _runner$getImportDura2.call(runner);
|
|
1411
|
+
if (durations) {
|
|
1412
|
+
file.importDurations = durations;
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
calculateSuiteHash(file);
|
|
1416
|
+
const hasOnlyTasks = someTasksAreOnly(file);
|
|
1417
|
+
interpretTaskModes(file, config.testNamePattern, testLocations, hasOnlyTasks, false, config.allowOnly);
|
|
1418
|
+
if (file.mode === "queued") {
|
|
1419
|
+
file.mode = "run";
|
|
1420
|
+
}
|
|
1421
|
+
files.push(file);
|
|
1422
|
+
});
|
|
1423
|
+
}
|
|
1424
|
+
return files;
|
|
1425
|
+
}
|
|
1426
|
+
function mergeHooks(baseHooks, hooks) {
|
|
1427
|
+
for (const _key in hooks) {
|
|
1428
|
+
const key = _key;
|
|
1429
|
+
baseHooks[key].push(...hooks[key]);
|
|
1430
|
+
}
|
|
1431
|
+
return baseHooks;
|
|
1432
|
+
}
|
|
1433
|
+
|
|
1434
|
+
const now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
1098
1435
|
const unixNow = Date.now;
|
|
1099
1436
|
const { clearTimeout, setTimeout } = getSafeTimers();
|
|
1100
1437
|
function updateSuiteHookState(task, name, state, runner) {
|
|
@@ -1275,7 +1612,7 @@ async function runTest(test, runner) {
|
|
|
1275
1612
|
updateTask("test-failed-early", test, runner);
|
|
1276
1613
|
return;
|
|
1277
1614
|
}
|
|
1278
|
-
const start = now
|
|
1615
|
+
const start = now();
|
|
1279
1616
|
test.result = {
|
|
1280
1617
|
state: "run",
|
|
1281
1618
|
startTime: unixNow(),
|
|
@@ -1285,11 +1622,12 @@ async function runTest(test, runner) {
|
|
|
1285
1622
|
const cleanupRunningTest = addRunningTest(test);
|
|
1286
1623
|
setCurrentTest(test);
|
|
1287
1624
|
const suite = test.suite || test.file;
|
|
1625
|
+
const $ = runner.trace;
|
|
1288
1626
|
const repeats = test.repeats ?? 0;
|
|
1289
1627
|
for (let repeatCount = 0; repeatCount <= repeats; repeatCount++) {
|
|
1290
1628
|
const retry = test.retry ?? 0;
|
|
1291
1629
|
for (let retryCount = 0; retryCount <= retry; retryCount++) {
|
|
1292
|
-
var _runner$onAfterRetryT, _test$result2, _test$result3;
|
|
1630
|
+
var _test$onFinished, _test$onFailed, _runner$onAfterRetryT, _test$result2, _test$result3;
|
|
1293
1631
|
let beforeEachCleanups = [];
|
|
1294
1632
|
try {
|
|
1295
1633
|
var _runner$onBeforeTryTa, _runner$onAfterTryTas;
|
|
@@ -1298,15 +1636,15 @@ async function runTest(test, runner) {
|
|
|
1298
1636
|
repeats: repeatCount
|
|
1299
1637
|
}));
|
|
1300
1638
|
test.result.repeatCount = repeatCount;
|
|
1301
|
-
beforeEachCleanups = await callSuiteHook(suite, test, "beforeEach", runner, [test.context, suite]);
|
|
1639
|
+
beforeEachCleanups = await $("test.beforeEach", () => callSuiteHook(suite, test, "beforeEach", runner, [test.context, suite]));
|
|
1302
1640
|
if (runner.runTask) {
|
|
1303
|
-
await runner.runTask(test);
|
|
1641
|
+
await $("test.callback", () => runner.runTask(test));
|
|
1304
1642
|
} else {
|
|
1305
1643
|
const fn = getFn(test);
|
|
1306
1644
|
if (!fn) {
|
|
1307
1645
|
throw new Error("Test function is not found. Did you add it using `setFn`?");
|
|
1308
1646
|
}
|
|
1309
|
-
await fn();
|
|
1647
|
+
await $("test.callback", () => fn());
|
|
1310
1648
|
}
|
|
1311
1649
|
await ((_runner$onAfterTryTas = runner.onAfterTryTask) === null || _runner$onAfterTryTas === void 0 ? void 0 : _runner$onAfterTryTas.call(runner, test, {
|
|
1312
1650
|
retry: retryCount,
|
|
@@ -1329,15 +1667,19 @@ async function runTest(test, runner) {
|
|
|
1329
1667
|
failTask(test.result, e, runner.config.diffOptions);
|
|
1330
1668
|
}
|
|
1331
1669
|
try {
|
|
1332
|
-
await callSuiteHook(suite, test, "afterEach", runner, [test.context, suite]);
|
|
1333
|
-
|
|
1670
|
+
await $("test.afterEach", () => callSuiteHook(suite, test, "afterEach", runner, [test.context, suite]));
|
|
1671
|
+
if (beforeEachCleanups.length) {
|
|
1672
|
+
await $("test.cleanup", () => callCleanupHooks(runner, beforeEachCleanups));
|
|
1673
|
+
}
|
|
1334
1674
|
await callFixtureCleanup(test.context);
|
|
1335
1675
|
} catch (e) {
|
|
1336
1676
|
failTask(test.result, e, runner.config.diffOptions);
|
|
1337
1677
|
}
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1678
|
+
if ((_test$onFinished = test.onFinished) === null || _test$onFinished === void 0 ? void 0 : _test$onFinished.length) {
|
|
1679
|
+
await $("test.onFinished", () => callTestHooks(runner, test, test.onFinished, "stack"));
|
|
1680
|
+
}
|
|
1681
|
+
if (test.result.state === "fail" && ((_test$onFailed = test.onFailed) === null || _test$onFailed === void 0 ? void 0 : _test$onFailed.length)) {
|
|
1682
|
+
await $("test.onFailed", () => callTestHooks(runner, test, test.onFailed, runner.config.sequence.hooks));
|
|
1341
1683
|
}
|
|
1342
1684
|
test.onFailed = undefined;
|
|
1343
1685
|
test.onFinished = undefined;
|
|
@@ -1353,7 +1695,7 @@ async function runTest(test, runner) {
|
|
|
1353
1695
|
state: "skip",
|
|
1354
1696
|
note: (_test$result4 = test.result) === null || _test$result4 === void 0 ? void 0 : _test$result4.note,
|
|
1355
1697
|
pending: true,
|
|
1356
|
-
duration: now
|
|
1698
|
+
duration: now() - start
|
|
1357
1699
|
};
|
|
1358
1700
|
updateTask("test-finished", test, runner);
|
|
1359
1701
|
setCurrentTest(undefined);
|
|
@@ -1385,7 +1727,7 @@ async function runTest(test, runner) {
|
|
|
1385
1727
|
}
|
|
1386
1728
|
cleanupRunningTest();
|
|
1387
1729
|
setCurrentTest(undefined);
|
|
1388
|
-
test.result.duration = now
|
|
1730
|
+
test.result.duration = now() - start;
|
|
1389
1731
|
await ((_runner$onAfterRunTas = runner.onAfterRunTask) === null || _runner$onAfterRunTas === void 0 ? void 0 : _runner$onAfterRunTas.call(runner, test));
|
|
1390
1732
|
updateTask("test-finished", test, runner);
|
|
1391
1733
|
}
|
|
@@ -1426,12 +1768,13 @@ async function runSuite(suite, runner) {
|
|
|
1426
1768
|
updateTask("suite-failed-early", suite, runner);
|
|
1427
1769
|
return;
|
|
1428
1770
|
}
|
|
1429
|
-
const start = now
|
|
1771
|
+
const start = now();
|
|
1430
1772
|
const mode = suite.mode;
|
|
1431
1773
|
suite.result = {
|
|
1432
1774
|
state: mode === "skip" || mode === "todo" ? mode : "run",
|
|
1433
1775
|
startTime: unixNow()
|
|
1434
1776
|
};
|
|
1777
|
+
const $ = runner.trace;
|
|
1435
1778
|
updateTask("suite-prepare", suite, runner);
|
|
1436
1779
|
let beforeAllCleanups = [];
|
|
1437
1780
|
if (suite.mode === "skip") {
|
|
@@ -1444,7 +1787,7 @@ async function runSuite(suite, runner) {
|
|
|
1444
1787
|
var _runner$onAfterRunSui;
|
|
1445
1788
|
try {
|
|
1446
1789
|
try {
|
|
1447
|
-
beforeAllCleanups = await callSuiteHook(suite, suite, "beforeAll", runner, [suite]);
|
|
1790
|
+
beforeAllCleanups = await $("suite.beforeAll", () => callSuiteHook(suite, suite, "beforeAll", runner, [suite]));
|
|
1448
1791
|
} catch (e) {
|
|
1449
1792
|
markTasksAsSkipped(suite, runner);
|
|
1450
1793
|
throw e;
|
|
@@ -1474,8 +1817,10 @@ async function runSuite(suite, runner) {
|
|
|
1474
1817
|
failTask(suite.result, e, runner.config.diffOptions);
|
|
1475
1818
|
}
|
|
1476
1819
|
try {
|
|
1477
|
-
await callSuiteHook(suite, suite, "afterAll", runner, [suite]);
|
|
1478
|
-
|
|
1820
|
+
await $("suite.afterAll", () => callSuiteHook(suite, suite, "afterAll", runner, [suite]));
|
|
1821
|
+
if (beforeAllCleanups.length) {
|
|
1822
|
+
await $("suite.cleanup", () => callCleanupHooks(runner, beforeAllCleanups));
|
|
1823
|
+
}
|
|
1479
1824
|
if (suite.file === suite) {
|
|
1480
1825
|
const context = getFileContext(suite);
|
|
1481
1826
|
await callFixtureCleanup(context);
|
|
@@ -1497,17 +1842,37 @@ async function runSuite(suite, runner) {
|
|
|
1497
1842
|
suite.result.state = "pass";
|
|
1498
1843
|
}
|
|
1499
1844
|
}
|
|
1500
|
-
suite.result.duration = now
|
|
1845
|
+
suite.result.duration = now() - start;
|
|
1501
1846
|
await ((_runner$onAfterRunSui = runner.onAfterRunSuite) === null || _runner$onAfterRunSui === void 0 ? void 0 : _runner$onAfterRunSui.call(runner, suite));
|
|
1502
1847
|
updateTask("suite-finished", suite, runner);
|
|
1503
1848
|
}
|
|
1504
1849
|
}
|
|
1505
1850
|
let limitMaxConcurrency;
|
|
1506
1851
|
async function runSuiteChild(c, runner) {
|
|
1852
|
+
const $ = runner.trace;
|
|
1507
1853
|
if (c.type === "test") {
|
|
1508
|
-
return limitMaxConcurrency(() =>
|
|
1854
|
+
return limitMaxConcurrency(() => {
|
|
1855
|
+
var _c$location, _c$location2;
|
|
1856
|
+
return $("run.test", {
|
|
1857
|
+
"vitest.test.id": c.id,
|
|
1858
|
+
"vitest.test.name": c.name,
|
|
1859
|
+
"vitest.test.mode": c.mode,
|
|
1860
|
+
"vitest.test.timeout": c.timeout,
|
|
1861
|
+
"code.file.path": c.file.filepath,
|
|
1862
|
+
"code.line.number": (_c$location = c.location) === null || _c$location === void 0 ? void 0 : _c$location.line,
|
|
1863
|
+
"code.column.number": (_c$location2 = c.location) === null || _c$location2 === void 0 ? void 0 : _c$location2.column
|
|
1864
|
+
}, () => runTest(c, runner));
|
|
1865
|
+
});
|
|
1509
1866
|
} else if (c.type === "suite") {
|
|
1510
|
-
|
|
1867
|
+
var _c$location3, _c$location4;
|
|
1868
|
+
return $("run.suite", {
|
|
1869
|
+
"vitest.suite.id": c.id,
|
|
1870
|
+
"vitest.suite.name": c.name,
|
|
1871
|
+
"vitest.suite.mode": c.mode,
|
|
1872
|
+
"code.file.path": c.file.filepath,
|
|
1873
|
+
"code.line.number": (_c$location3 = c.location) === null || _c$location3 === void 0 ? void 0 : _c$location3.line,
|
|
1874
|
+
"code.column.number": (_c$location4 = c.location) === null || _c$location4 === void 0 ? void 0 : _c$location4.column
|
|
1875
|
+
}, () => runSuite(c, runner));
|
|
1511
1876
|
}
|
|
1512
1877
|
}
|
|
1513
1878
|
async function runFiles(files, runner) {
|
|
@@ -1523,12 +1888,22 @@ async function runFiles(files, runner) {
|
|
|
1523
1888
|
};
|
|
1524
1889
|
}
|
|
1525
1890
|
}
|
|
1526
|
-
await
|
|
1891
|
+
await runner.trace("run.spec", {
|
|
1892
|
+
"code.file.path": file.filepath,
|
|
1893
|
+
"vitest.suite.tasks.length": file.tasks.length
|
|
1894
|
+
}, () => runSuite(file, runner));
|
|
1527
1895
|
}
|
|
1528
1896
|
}
|
|
1529
1897
|
const workerRunners = new WeakSet();
|
|
1898
|
+
function defaultTrace(_, attributes, cb) {
|
|
1899
|
+
if (typeof attributes === "function") {
|
|
1900
|
+
return attributes();
|
|
1901
|
+
}
|
|
1902
|
+
return cb();
|
|
1903
|
+
}
|
|
1530
1904
|
async function startTests(specs, runner) {
|
|
1531
1905
|
var _runner$cancel;
|
|
1906
|
+
runner.trace ?? (runner.trace = defaultTrace);
|
|
1532
1907
|
const cancel = (_runner$cancel = runner.cancel) === null || _runner$cancel === void 0 ? void 0 : _runner$cancel.bind(runner);
|
|
1533
1908
|
// Ideally, we need to have an event listener for this, but only have a runner here.
|
|
1534
1909
|
// Adding another onCancel felt wrong (maybe it needs to be refactored)
|
|
@@ -1566,6 +1941,7 @@ async function startTests(specs, runner) {
|
|
|
1566
1941
|
}
|
|
1567
1942
|
async function publicCollect(specs, runner) {
|
|
1568
1943
|
var _runner$onBeforeColle2, _runner$onCollected2;
|
|
1944
|
+
runner.trace ?? (runner.trace = defaultTrace);
|
|
1569
1945
|
const paths = specs.map((f) => typeof f === "string" ? f : f.filepath);
|
|
1570
1946
|
await ((_runner$onBeforeColle2 = runner.onBeforeCollect) === null || _runner$onBeforeColle2 === void 0 ? void 0 : _runner$onBeforeColle2.call(runner, paths));
|
|
1571
1947
|
const files = await collectTests(specs, runner);
|
|
@@ -1573,189 +1949,72 @@ async function publicCollect(specs, runner) {
|
|
|
1573
1949
|
return files;
|
|
1574
1950
|
}
|
|
1575
1951
|
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
// https://github.com/vitest-dev/vitest/issues/2920
|
|
1622
|
-
if (now() - startTime >= timeout) {
|
|
1623
|
-
rejectTimeoutError();
|
|
1624
|
-
return;
|
|
1625
|
-
}
|
|
1626
|
-
resolve_(result);
|
|
1627
|
-
}
|
|
1628
|
-
function reject(error) {
|
|
1629
|
-
runner._currentTaskStartTime = undefined;
|
|
1630
|
-
runner._currentTaskTimeout = undefined;
|
|
1631
|
-
clearTimeout(timer);
|
|
1632
|
-
reject_(error);
|
|
1633
|
-
}
|
|
1634
|
-
// sync test/hook will be caught by try/catch
|
|
1635
|
-
try {
|
|
1636
|
-
const result = fn(...args);
|
|
1637
|
-
// the result is a thenable, we don't wrap this in Promise.resolve
|
|
1638
|
-
// to avoid creating new promises
|
|
1639
|
-
if (typeof result === "object" && result != null && typeof result.then === "function") {
|
|
1640
|
-
result.then(resolve, reject);
|
|
1641
|
-
} else {
|
|
1642
|
-
resolve(result);
|
|
1643
|
-
}
|
|
1644
|
-
}
|
|
1645
|
-
// user sync test/hook throws an error
|
|
1646
|
-
catch (error) {
|
|
1647
|
-
reject(error);
|
|
1648
|
-
}
|
|
1649
|
-
});
|
|
1650
|
-
});
|
|
1651
|
-
}
|
|
1652
|
-
const abortControllers = new WeakMap();
|
|
1653
|
-
function abortIfTimeout([context], error) {
|
|
1654
|
-
if (context) {
|
|
1655
|
-
abortContextSignal(context, error);
|
|
1656
|
-
}
|
|
1657
|
-
}
|
|
1658
|
-
function abortContextSignal(context, error) {
|
|
1659
|
-
const abortController = abortControllers.get(context);
|
|
1660
|
-
abortController === null || abortController === void 0 ? void 0 : abortController.abort(error);
|
|
1661
|
-
}
|
|
1662
|
-
function createTestContext(test, runner) {
|
|
1663
|
-
var _runner$extendTaskCon;
|
|
1664
|
-
const context = function() {
|
|
1665
|
-
throw new Error("done() callback is deprecated, use promise instead");
|
|
1666
|
-
};
|
|
1667
|
-
let abortController = abortControllers.get(context);
|
|
1668
|
-
if (!abortController) {
|
|
1669
|
-
abortController = new AbortController();
|
|
1670
|
-
abortControllers.set(context, abortController);
|
|
1671
|
-
}
|
|
1672
|
-
context.signal = abortController.signal;
|
|
1673
|
-
context.task = test;
|
|
1674
|
-
context.skip = (condition, note) => {
|
|
1675
|
-
if (condition === false) {
|
|
1676
|
-
// do nothing
|
|
1677
|
-
return undefined;
|
|
1678
|
-
}
|
|
1679
|
-
test.result ?? (test.result = { state: "skip" });
|
|
1680
|
-
test.result.pending = true;
|
|
1681
|
-
throw new PendingError("test is skipped; abort execution", test, typeof condition === "string" ? condition : note);
|
|
1682
|
-
};
|
|
1683
|
-
async function annotate(message, location, type, attachment) {
|
|
1684
|
-
const annotation = {
|
|
1685
|
-
message,
|
|
1686
|
-
type: type || "notice"
|
|
1952
|
+
/**
|
|
1953
|
+
* @experimental
|
|
1954
|
+
* @advanced
|
|
1955
|
+
*
|
|
1956
|
+
* Records a custom test artifact during test execution.
|
|
1957
|
+
*
|
|
1958
|
+
* This function allows you to attach structured data, files, or metadata to a test.
|
|
1959
|
+
*
|
|
1960
|
+
* Vitest automatically injects the source location where the artifact was created and manages any attachments you include.
|
|
1961
|
+
*
|
|
1962
|
+
* @param task - The test task context, typically accessed via `this.task` in custom matchers or `context.task` in tests
|
|
1963
|
+
* @param artifact - The artifact to record. Must extend {@linkcode TestArtifactBase}
|
|
1964
|
+
*
|
|
1965
|
+
* @returns A promise that resolves to the recorded artifact with location injected
|
|
1966
|
+
*
|
|
1967
|
+
* @throws {Error} If called after the test has finished running
|
|
1968
|
+
* @throws {Error} If the test runner doesn't support artifacts
|
|
1969
|
+
*
|
|
1970
|
+
* @example
|
|
1971
|
+
* ```ts
|
|
1972
|
+
* // In a custom assertion
|
|
1973
|
+
* async function toHaveValidSchema(this: MatcherState, actual: unknown) {
|
|
1974
|
+
* const validation = validateSchema(actual)
|
|
1975
|
+
*
|
|
1976
|
+
* await recordArtifact(this.task, {
|
|
1977
|
+
* type: 'my-plugin:schema-validation',
|
|
1978
|
+
* passed: validation.valid,
|
|
1979
|
+
* errors: validation.errors,
|
|
1980
|
+
* })
|
|
1981
|
+
*
|
|
1982
|
+
* return { pass: validation.valid, message: () => '...' }
|
|
1983
|
+
* }
|
|
1984
|
+
* ```
|
|
1985
|
+
*/
|
|
1986
|
+
async function recordArtifact(task, artifact) {
|
|
1987
|
+
const runner = getRunner();
|
|
1988
|
+
if (task.result && task.result.state !== "run") {
|
|
1989
|
+
throw new Error(`Cannot record a test artifact outside of the test run. The test "${task.name}" finished running with the "${task.result.state}" state already.`);
|
|
1990
|
+
}
|
|
1991
|
+
const stack = findTestFileStackTrace(task.file.filepath, new Error("STACK_TRACE").stack);
|
|
1992
|
+
if (stack) {
|
|
1993
|
+
artifact.location = {
|
|
1994
|
+
file: stack.file,
|
|
1995
|
+
line: stack.line,
|
|
1996
|
+
column: stack.column
|
|
1687
1997
|
};
|
|
1688
|
-
if (
|
|
1689
|
-
|
|
1690
|
-
throw new TypeError(`Test attachment requires "body" or "path" to be set. Both are missing.`);
|
|
1691
|
-
}
|
|
1692
|
-
if (attachment.body && attachment.path) {
|
|
1693
|
-
throw new TypeError(`Test attachment requires only one of "body" or "path" to be set. Both are specified.`);
|
|
1694
|
-
}
|
|
1695
|
-
annotation.attachment = attachment;
|
|
1696
|
-
// convert to a string so it's easier to serialise
|
|
1697
|
-
if (attachment.body instanceof Uint8Array) {
|
|
1698
|
-
attachment.body = encodeUint8Array(attachment.body);
|
|
1699
|
-
}
|
|
1700
|
-
}
|
|
1701
|
-
if (location) {
|
|
1702
|
-
annotation.location = location;
|
|
1703
|
-
}
|
|
1704
|
-
if (!runner.onTestAnnotate) {
|
|
1705
|
-
throw new Error(`Test runner doesn't support test annotations.`);
|
|
1998
|
+
if (artifact.type === "internal:annotation") {
|
|
1999
|
+
artifact.annotation.location = artifact.location;
|
|
1706
2000
|
}
|
|
1707
|
-
await finishSendTasksUpdate(runner);
|
|
1708
|
-
const resolvedAnnotation = await runner.onTestAnnotate(test, annotation);
|
|
1709
|
-
test.annotations.push(resolvedAnnotation);
|
|
1710
|
-
return resolvedAnnotation;
|
|
1711
2001
|
}
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
2002
|
+
if (Array.isArray(artifact.attachments)) {
|
|
2003
|
+
for (const attachment of artifact.attachments) {
|
|
2004
|
+
manageArtifactAttachment(attachment);
|
|
1715
2005
|
}
|
|
1716
|
-
const stack = findTestFileStackTrace(test.file.filepath, new Error("STACK_TRACE").stack);
|
|
1717
|
-
let location;
|
|
1718
|
-
if (stack) {
|
|
1719
|
-
location = {
|
|
1720
|
-
file: stack.file,
|
|
1721
|
-
line: stack.line,
|
|
1722
|
-
column: stack.column
|
|
1723
|
-
};
|
|
1724
|
-
}
|
|
1725
|
-
if (typeof type === "object") {
|
|
1726
|
-
return recordAsyncAnnotation(test, annotate(message, location, undefined, type));
|
|
1727
|
-
} else {
|
|
1728
|
-
return recordAsyncAnnotation(test, annotate(message, location, type, attachment));
|
|
1729
|
-
}
|
|
1730
|
-
});
|
|
1731
|
-
context.onTestFailed = (handler, timeout) => {
|
|
1732
|
-
test.onFailed || (test.onFailed = []);
|
|
1733
|
-
test.onFailed.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
|
|
1734
|
-
};
|
|
1735
|
-
context.onTestFinished = (handler, timeout) => {
|
|
1736
|
-
test.onFinished || (test.onFinished = []);
|
|
1737
|
-
test.onFinished.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
|
|
1738
|
-
};
|
|
1739
|
-
return ((_runner$extendTaskCon = runner.extendTaskContext) === null || _runner$extendTaskCon === void 0 ? void 0 : _runner$extendTaskCon.call(runner, context)) || context;
|
|
1740
|
-
}
|
|
1741
|
-
function makeTimeoutError(isHook, timeout, stackTraceError) {
|
|
1742
|
-
const message = `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.\nIf this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
|
|
1743
|
-
const error = new Error(message);
|
|
1744
|
-
if (stackTraceError === null || stackTraceError === void 0 ? void 0 : stackTraceError.stack) {
|
|
1745
|
-
error.stack = stackTraceError.stack.replace(error.message, stackTraceError.message);
|
|
1746
2006
|
}
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
function getFileContext(file) {
|
|
1751
|
-
const context = fileContexts.get(file);
|
|
1752
|
-
if (!context) {
|
|
1753
|
-
throw new Error(`Cannot find file context for ${file.name}`);
|
|
2007
|
+
// annotations won't resolve as artifacts for backwards compatibility until next major
|
|
2008
|
+
if (artifact.type === "internal:annotation") {
|
|
2009
|
+
return artifact;
|
|
1754
2010
|
}
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
2011
|
+
if (!runner.onTestArtifactRecord) {
|
|
2012
|
+
throw new Error(`Test runner doesn't support test artifacts.`);
|
|
2013
|
+
}
|
|
2014
|
+
await finishSendTasksUpdate(runner);
|
|
2015
|
+
const resolvedArtifact = await runner.onTestArtifactRecord(task, artifact);
|
|
2016
|
+
task.artifacts.push(resolvedArtifact);
|
|
2017
|
+
return resolvedArtifact;
|
|
1759
2018
|
}
|
|
1760
2019
|
const table = [];
|
|
1761
2020
|
for (let i = 65; i < 91; i++) {
|
|
@@ -1799,7 +2058,13 @@ function encodeUint8Array(bytes) {
|
|
|
1799
2058
|
}
|
|
1800
2059
|
return base64;
|
|
1801
2060
|
}
|
|
1802
|
-
|
|
2061
|
+
/**
|
|
2062
|
+
* Records an async operation associated with a test task.
|
|
2063
|
+
*
|
|
2064
|
+
* This function tracks promises that should be awaited before a test completes.
|
|
2065
|
+
* The promise is automatically removed from the test's promise list once it settles.
|
|
2066
|
+
*/
|
|
2067
|
+
function recordAsyncOperation(test, promise) {
|
|
1803
2068
|
// if promise is explicitly awaited, remove it from the list
|
|
1804
2069
|
promise = promise.finally(() => {
|
|
1805
2070
|
if (!test.promises) {
|
|
@@ -1817,175 +2082,27 @@ function recordAsyncAnnotation(test, promise) {
|
|
|
1817
2082
|
test.promises.push(promise);
|
|
1818
2083
|
return promise;
|
|
1819
2084
|
}
|
|
1820
|
-
|
|
1821
|
-
function getDefaultHookTimeout() {
|
|
1822
|
-
return getRunner().config.hookTimeout;
|
|
1823
|
-
}
|
|
1824
|
-
const CLEANUP_TIMEOUT_KEY = Symbol.for("VITEST_CLEANUP_TIMEOUT");
|
|
1825
|
-
const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
1826
|
-
function getBeforeHookCleanupCallback(hook, result, context) {
|
|
1827
|
-
if (typeof result === "function") {
|
|
1828
|
-
const timeout = CLEANUP_TIMEOUT_KEY in hook && typeof hook[CLEANUP_TIMEOUT_KEY] === "number" ? hook[CLEANUP_TIMEOUT_KEY] : getDefaultHookTimeout();
|
|
1829
|
-
const stackTraceError = CLEANUP_STACK_TRACE_KEY in hook && hook[CLEANUP_STACK_TRACE_KEY] instanceof Error ? hook[CLEANUP_STACK_TRACE_KEY] : undefined;
|
|
1830
|
-
return withTimeout(result, timeout, true, stackTraceError, (_, error) => {
|
|
1831
|
-
if (context) {
|
|
1832
|
-
abortContextSignal(context, error);
|
|
1833
|
-
}
|
|
1834
|
-
});
|
|
1835
|
-
}
|
|
1836
|
-
}
|
|
1837
|
-
/**
|
|
1838
|
-
* Registers a callback function to be executed once before all tests within the current suite.
|
|
1839
|
-
* This hook is useful for scenarios where you need to perform setup operations that are common to all tests in a suite, such as initializing a database connection or setting up a test environment.
|
|
1840
|
-
*
|
|
1841
|
-
* **Note:** The `beforeAll` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
1842
|
-
*
|
|
1843
|
-
* @param {Function} fn - The callback function to be executed before all tests.
|
|
1844
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
1845
|
-
* @returns {void}
|
|
1846
|
-
* @example
|
|
1847
|
-
* ```ts
|
|
1848
|
-
* // Example of using beforeAll to set up a database connection
|
|
1849
|
-
* beforeAll(async () => {
|
|
1850
|
-
* await database.connect();
|
|
1851
|
-
* });
|
|
1852
|
-
* ```
|
|
1853
|
-
*/
|
|
1854
|
-
function beforeAll(fn, timeout = getDefaultHookTimeout()) {
|
|
1855
|
-
assertTypes(fn, "\"beforeAll\" callback", ["function"]);
|
|
1856
|
-
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
1857
|
-
return getCurrentSuite().on("beforeAll", Object.assign(withTimeout(fn, timeout, true, stackTraceError), {
|
|
1858
|
-
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
1859
|
-
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
1860
|
-
}));
|
|
1861
|
-
}
|
|
1862
|
-
/**
|
|
1863
|
-
* Registers a callback function to be executed once after all tests within the current suite have completed.
|
|
1864
|
-
* This hook is useful for scenarios where you need to perform cleanup operations after all tests in a suite have run, such as closing database connections or cleaning up temporary files.
|
|
1865
|
-
*
|
|
1866
|
-
* **Note:** The `afterAll` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
1867
|
-
*
|
|
1868
|
-
* @param {Function} fn - The callback function to be executed after all tests.
|
|
1869
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
1870
|
-
* @returns {void}
|
|
1871
|
-
* @example
|
|
1872
|
-
* ```ts
|
|
1873
|
-
* // Example of using afterAll to close a database connection
|
|
1874
|
-
* afterAll(async () => {
|
|
1875
|
-
* await database.disconnect();
|
|
1876
|
-
* });
|
|
1877
|
-
* ```
|
|
1878
|
-
*/
|
|
1879
|
-
function afterAll(fn, timeout) {
|
|
1880
|
-
assertTypes(fn, "\"afterAll\" callback", ["function"]);
|
|
1881
|
-
return getCurrentSuite().on("afterAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR")));
|
|
1882
|
-
}
|
|
1883
2085
|
/**
|
|
1884
|
-
*
|
|
1885
|
-
* This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables.
|
|
2086
|
+
* Validates and prepares a test attachment for serialization.
|
|
1886
2087
|
*
|
|
1887
|
-
*
|
|
2088
|
+
* This function ensures attachments have either `body` or `path` set (but not both), and converts `Uint8Array` bodies to base64-encoded strings for easier serialization.
|
|
1888
2089
|
*
|
|
1889
|
-
* @param
|
|
1890
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
1891
|
-
* @returns {void}
|
|
1892
|
-
* @example
|
|
1893
|
-
* ```ts
|
|
1894
|
-
* // Example of using beforeEach to reset a database state
|
|
1895
|
-
* beforeEach(async () => {
|
|
1896
|
-
* await database.reset();
|
|
1897
|
-
* });
|
|
1898
|
-
* ```
|
|
1899
|
-
*/
|
|
1900
|
-
function beforeEach(fn, timeout = getDefaultHookTimeout()) {
|
|
1901
|
-
assertTypes(fn, "\"beforeEach\" callback", ["function"]);
|
|
1902
|
-
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
1903
|
-
const runner = getRunner();
|
|
1904
|
-
return getCurrentSuite().on("beforeEach", Object.assign(withTimeout(withFixtures(runner, fn), timeout ?? getDefaultHookTimeout(), true, stackTraceError, abortIfTimeout), {
|
|
1905
|
-
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
1906
|
-
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
1907
|
-
}));
|
|
1908
|
-
}
|
|
1909
|
-
/**
|
|
1910
|
-
* Registers a callback function to be executed after each test within the current suite has completed.
|
|
1911
|
-
* This hook is useful for scenarios where you need to clean up or reset the test environment after each test runs, such as deleting temporary files, clearing test-specific database entries, or resetting mocked functions.
|
|
1912
|
-
*
|
|
1913
|
-
* **Note:** The `afterEach` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
2090
|
+
* @param attachment - The attachment to validate and prepare
|
|
1914
2091
|
*
|
|
1915
|
-
* @
|
|
1916
|
-
* @
|
|
1917
|
-
* @returns {void}
|
|
1918
|
-
* @example
|
|
1919
|
-
* ```ts
|
|
1920
|
-
* // Example of using afterEach to delete temporary files created during a test
|
|
1921
|
-
* afterEach(async () => {
|
|
1922
|
-
* await fileSystem.deleteTempFiles();
|
|
1923
|
-
* });
|
|
1924
|
-
* ```
|
|
2092
|
+
* @throws {TypeError} If neither `body` nor `path` is provided
|
|
2093
|
+
* @throws {TypeError} If both `body` and `path` are provided
|
|
1925
2094
|
*/
|
|
1926
|
-
function
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
* @param {Function} fn - The callback function to be executed upon a test failure. The function receives the test result (including errors).
|
|
1938
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
1939
|
-
* @throws {Error} Throws an error if the function is not called within a test.
|
|
1940
|
-
* @returns {void}
|
|
1941
|
-
* @example
|
|
1942
|
-
* ```ts
|
|
1943
|
-
* // Example of using onTestFailed to log failure details
|
|
1944
|
-
* onTestFailed(({ errors }) => {
|
|
1945
|
-
* console.log(`Test failed: ${test.name}`, errors);
|
|
1946
|
-
* });
|
|
1947
|
-
* ```
|
|
1948
|
-
*/
|
|
1949
|
-
const onTestFailed = createTestHook("onTestFailed", (test, handler, timeout) => {
|
|
1950
|
-
test.onFailed || (test.onFailed = []);
|
|
1951
|
-
test.onFailed.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
1952
|
-
});
|
|
1953
|
-
/**
|
|
1954
|
-
* Registers a callback function to be executed when the current test finishes, regardless of the outcome (pass or fail).
|
|
1955
|
-
* This function is ideal for performing actions that should occur after every test execution, such as cleanup, logging, or resetting shared resources.
|
|
1956
|
-
*
|
|
1957
|
-
* This hook is useful if you have access to a resource in the test itself and you want to clean it up after the test finishes. It is a more compact way to clean up resources than using the combination of `beforeEach` and `afterEach`.
|
|
1958
|
-
*
|
|
1959
|
-
* **Note:** The `onTestFinished` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
|
|
1960
|
-
*
|
|
1961
|
-
* **Note:** The `onTestFinished` hook is not called if the test is canceled with a dynamic `ctx.skip()` call.
|
|
1962
|
-
*
|
|
1963
|
-
* @param {Function} fn - The callback function to be executed after a test finishes. The function can receive parameters providing details about the completed test, including its success or failure status.
|
|
1964
|
-
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
1965
|
-
* @throws {Error} Throws an error if the function is not called within a test.
|
|
1966
|
-
* @returns {void}
|
|
1967
|
-
* @example
|
|
1968
|
-
* ```ts
|
|
1969
|
-
* // Example of using onTestFinished for cleanup
|
|
1970
|
-
* const db = await connectToDatabase();
|
|
1971
|
-
* onTestFinished(async () => {
|
|
1972
|
-
* await db.disconnect();
|
|
1973
|
-
* });
|
|
1974
|
-
* ```
|
|
1975
|
-
*/
|
|
1976
|
-
const onTestFinished = createTestHook("onTestFinished", (test, handler, timeout) => {
|
|
1977
|
-
test.onFinished || (test.onFinished = []);
|
|
1978
|
-
test.onFinished.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
1979
|
-
});
|
|
1980
|
-
function createTestHook(name, handler) {
|
|
1981
|
-
return (fn, timeout) => {
|
|
1982
|
-
assertTypes(fn, `"${name}" callback`, ["function"]);
|
|
1983
|
-
const current = getCurrentTest();
|
|
1984
|
-
if (!current) {
|
|
1985
|
-
throw new Error(`Hook ${name}() can only be called inside a test`);
|
|
1986
|
-
}
|
|
1987
|
-
return handler(current, fn, timeout);
|
|
1988
|
-
};
|
|
2095
|
+
function manageArtifactAttachment(attachment) {
|
|
2096
|
+
if (attachment.body == null && !attachment.path) {
|
|
2097
|
+
throw new TypeError(`Test attachment requires "body" or "path" to be set. Both are missing.`);
|
|
2098
|
+
}
|
|
2099
|
+
if (attachment.body && attachment.path) {
|
|
2100
|
+
throw new TypeError(`Test attachment requires only one of "body" or "path" to be set. Both are specified.`);
|
|
2101
|
+
}
|
|
2102
|
+
// convert to a string so it's easier to serialise
|
|
2103
|
+
if (attachment.body instanceof Uint8Array) {
|
|
2104
|
+
attachment.body = encodeUint8Array(attachment.body);
|
|
2105
|
+
}
|
|
1989
2106
|
}
|
|
1990
2107
|
|
|
1991
|
-
export { afterAll, afterEach, beforeAll, beforeEach, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, setFn, setHooks, startTests, suite, test, updateTask };
|
|
2108
|
+
export { afterAll, afterEach, beforeAll, beforeEach, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, recordArtifact, setFn, setHooks, startTests, suite, test, updateTask };
|