@vitest/runner 3.2.0-beta.1 → 3.2.0-beta.3
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/chunk-tasks.js +24 -0
- package/dist/index.d.ts +2 -2
- package/dist/index.js +62 -1
- package/dist/{tasks.d-hzpC0pGR.d.ts → tasks.d-CItyFU3G.d.ts} +8 -0
- package/dist/types.d.ts +2 -2
- package/dist/utils.d.ts +2 -2
- package/package.json +2 -2
package/dist/chunk-tasks.js
CHANGED
@@ -38,9 +38,11 @@ function interpretTaskModes(file, namePattern, testLocations, onlyMode, parentIs
|
|
38
38
|
const traverseSuite = (suite, parentIsOnly, parentMatchedWithLocation) => {
|
39
39
|
const suiteIsOnly = parentIsOnly || suite.mode === "only";
|
40
40
|
suite.tasks.forEach((t) => {
|
41
|
+
// Check if either the parent suite or the task itself are marked as included
|
41
42
|
const includeTask = suiteIsOnly || t.mode === "only";
|
42
43
|
if (onlyMode) {
|
43
44
|
if (t.type === "suite" && (includeTask || someTasksAreOnly(t))) {
|
45
|
+
// Don't skip this suite
|
44
46
|
if (t.mode === "only") {
|
45
47
|
checkAllowOnly(t, allowOnly);
|
46
48
|
t.mode = "run";
|
@@ -53,6 +55,9 @@ function interpretTaskModes(file, namePattern, testLocations, onlyMode, parentIs
|
|
53
55
|
}
|
54
56
|
}
|
55
57
|
let hasLocationMatch = parentMatchedWithLocation;
|
58
|
+
// Match test location against provided locations, only run if present
|
59
|
+
// in `testLocations`. Note: if `includeTaskLocations` is not enabled,
|
60
|
+
// all test will be skipped.
|
56
61
|
if (testLocations !== undefined && testLocations.length !== 0) {
|
57
62
|
if (t.location && (testLocations === null || testLocations === void 0 ? void 0 : testLocations.includes(t.location.line))) {
|
58
63
|
t.mode = "run";
|
@@ -78,6 +83,7 @@ function interpretTaskModes(file, namePattern, testLocations, onlyMode, parentIs
|
|
78
83
|
}
|
79
84
|
}
|
80
85
|
});
|
86
|
+
// if all subtasks are skipped, mark as skip
|
81
87
|
if (suite.mode === "run" || suite.mode === "queued") {
|
82
88
|
if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run" && i.mode !== "queued")) {
|
83
89
|
suite.mode = "skip";
|
@@ -186,27 +192,45 @@ function generateFileHash(file, projectName) {
|
|
186
192
|
* Return a function for running multiple async operations with limited concurrency.
|
187
193
|
*/
|
188
194
|
function limitConcurrency(concurrency = Infinity) {
|
195
|
+
// The number of currently active + pending tasks.
|
189
196
|
let count = 0;
|
197
|
+
// The head and tail of the pending task queue, built using a singly linked list.
|
198
|
+
// Both head and tail are initially undefined, signifying an empty queue.
|
199
|
+
// They both become undefined again whenever there are no pending tasks.
|
190
200
|
let head;
|
191
201
|
let tail;
|
202
|
+
// A bookkeeping function executed whenever a task has been run to completion.
|
192
203
|
const finish = () => {
|
193
204
|
count--;
|
205
|
+
// Check if there are further pending tasks in the queue.
|
194
206
|
if (head) {
|
207
|
+
// Allow the next pending task to run and pop it from the queue.
|
195
208
|
head[0]();
|
196
209
|
head = head[1];
|
210
|
+
// The head may now be undefined if there are no further pending tasks.
|
211
|
+
// In that case, set tail to undefined as well.
|
197
212
|
tail = head && tail;
|
198
213
|
}
|
199
214
|
};
|
200
215
|
return (func, ...args) => {
|
216
|
+
// Create a promise chain that:
|
217
|
+
// 1. Waits for its turn in the task queue (if necessary).
|
218
|
+
// 2. Runs the task.
|
219
|
+
// 3. Allows the next pending task (if any) to run.
|
201
220
|
return new Promise((resolve) => {
|
202
221
|
if (count++ < concurrency) {
|
222
|
+
// No need to queue if fewer than maxConcurrency tasks are running.
|
203
223
|
resolve();
|
204
224
|
} else if (tail) {
|
225
|
+
// There are pending tasks, so append to the queue.
|
205
226
|
tail = tail[1] = [resolve];
|
206
227
|
} else {
|
228
|
+
// No other pending tasks, initialize the queue with a new tail and head.
|
207
229
|
head = tail = [resolve];
|
208
230
|
}
|
209
231
|
}).then(() => {
|
232
|
+
// Running func here ensures that even a non-thenable result or an
|
233
|
+
// immediately thrown error gets wrapped into a Promise.
|
210
234
|
return func(...args);
|
211
235
|
}).finally(finish);
|
212
236
|
};
|
package/dist/index.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { A as AfterAllListener, b as AfterEachListener, B as BeforeAllListener, d as BeforeEachListener, e as TaskHook, O as OnTestFailedHandler, f as OnTestFinishedHandler, a as Test, g as Custom, S as Suite, h as SuiteHooks, F as File, i as TaskUpdateEvent, T as Task, j as TestAPI, k as SuiteAPI, l as SuiteCollector } from './tasks.d-
|
2
|
-
export { D as DoneCallback, E as ExtendedContext, m as Fixture, n as FixtureFn, o as FixtureOptions, p as Fixtures, H as HookCleanupCallback, q as HookListener, I as InferFixturesTypes, R as RunMode, r as RuntimeContext, s as SequenceHooks, t as SequenceSetupFiles, u as SuiteFactory, v as TaskBase, w as TaskContext, x as TaskCustomOptions, y as TaskEventPack, z as TaskMeta, G as TaskPopulated, J as TaskResult, K as TaskResultPack, L as TaskState, M as TestContext, N as TestFunction, P as TestOptions, U as Use } from './tasks.d-
|
1
|
+
import { A as AfterAllListener, b as AfterEachListener, B as BeforeAllListener, d as BeforeEachListener, e as TaskHook, O as OnTestFailedHandler, f as OnTestFinishedHandler, a as Test, g as Custom, S as Suite, h as SuiteHooks, F as File, i as TaskUpdateEvent, T as Task, j as TestAPI, k as SuiteAPI, l as SuiteCollector } from './tasks.d-CItyFU3G.js';
|
2
|
+
export { D as DoneCallback, E as ExtendedContext, m as Fixture, n as FixtureFn, o as FixtureOptions, p as Fixtures, H as HookCleanupCallback, q as HookListener, I as InferFixturesTypes, R as RunMode, r as RuntimeContext, s as SequenceHooks, t as SequenceSetupFiles, u as SuiteFactory, v as TaskBase, w as TaskContext, x as TaskCustomOptions, y as TaskEventPack, z as TaskMeta, G as TaskPopulated, J as TaskResult, K as TaskResultPack, L as TaskState, M as TestContext, N as TestFunction, P as TestOptions, U as Use } from './tasks.d-CItyFU3G.js';
|
3
3
|
import { Awaitable } from '@vitest/utils';
|
4
4
|
import { FileSpecification, VitestRunner } from './types.js';
|
5
5
|
export { CancelReason, VitestRunnerConfig, VitestRunnerConstructor, VitestRunnerImportSource } from './types.js';
|
package/dist/index.js
CHANGED
@@ -24,6 +24,7 @@ class TestRunAbortError extends Error {
|
|
24
24
|
}
|
25
25
|
}
|
26
26
|
|
27
|
+
// use WeakMap here to make the Test and Suite object serializable
|
27
28
|
const fnMap = new WeakMap();
|
28
29
|
const testFixtureMap = new WeakMap();
|
29
30
|
const hooksMap = new WeakMap();
|
@@ -59,6 +60,8 @@ function mergeScopedFixtures(testFixtures, scopedFixtures) {
|
|
59
60
|
for (const fixtureKep in newFixtures) {
|
60
61
|
var _fixture$deps;
|
61
62
|
const fixture = newFixtures[fixtureKep];
|
63
|
+
// if the fixture was define before the scope, then its dep
|
64
|
+
// will reference the original fixture instead of the scope
|
62
65
|
fixture.deps = (_fixture$deps = fixture.deps) === null || _fixture$deps === void 0 ? void 0 : _fixture$deps.map((dep) => newFixtures[dep.prop]);
|
63
66
|
}
|
64
67
|
return Object.values(newFixtures);
|
@@ -68,6 +71,7 @@ function mergeContextFixtures(fixtures, context, inject) {
|
|
68
71
|
const fixtureArray = Object.entries(fixtures).map(([prop, value]) => {
|
69
72
|
const fixtureItem = { value };
|
70
73
|
if (Array.isArray(value) && value.length >= 2 && isObject(value[1]) && Object.keys(value[1]).some((key) => fixtureOptionKeys.includes(key))) {
|
74
|
+
// fixture with options
|
71
75
|
Object.assign(fixtureItem, value[1]);
|
72
76
|
const userValue = value[0];
|
73
77
|
fixtureItem.value = fixtureItem.injected ? inject(prop) ?? userValue : userValue;
|
@@ -81,6 +85,7 @@ function mergeContextFixtures(fixtures, context, inject) {
|
|
81
85
|
} else {
|
82
86
|
context.fixtures = fixtureArray;
|
83
87
|
}
|
88
|
+
// Update dependencies of fixture functions
|
84
89
|
fixtureArray.forEach((fixture) => {
|
85
90
|
if (fixture.isFn) {
|
86
91
|
const usedProps = getUsedProps(fixture.value);
|
@@ -130,6 +135,7 @@ function withFixtures(fn, testContext) {
|
|
130
135
|
}
|
131
136
|
async function resolveFixtures() {
|
132
137
|
for (const fixture of pendingFixtures) {
|
138
|
+
// fixture could be already initialized during "before" hook
|
133
139
|
if (fixtureValueMap.has(fixture)) {
|
134
140
|
continue;
|
135
141
|
}
|
@@ -145,22 +151,29 @@ function withFixtures(fn, testContext) {
|
|
145
151
|
};
|
146
152
|
}
|
147
153
|
async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
|
154
|
+
// wait for `use` call to extract fixture value
|
148
155
|
const useFnArgPromise = createDefer();
|
149
156
|
let isUseFnArgResolved = false;
|
150
157
|
const fixtureReturn = fixtureFn(context, async (useFnArg) => {
|
158
|
+
// extract `use` argument
|
151
159
|
isUseFnArgResolved = true;
|
152
160
|
useFnArgPromise.resolve(useFnArg);
|
161
|
+
// suspend fixture teardown by holding off `useReturnPromise` resolution until cleanup
|
153
162
|
const useReturnPromise = createDefer();
|
154
163
|
cleanupFnArray.push(async () => {
|
164
|
+
// start teardown by resolving `use` Promise
|
155
165
|
useReturnPromise.resolve();
|
166
|
+
// wait for finishing teardown
|
156
167
|
await fixtureReturn;
|
157
168
|
});
|
158
169
|
await useReturnPromise;
|
159
170
|
}).catch((e) => {
|
171
|
+
// treat fixture setup error as test failure
|
160
172
|
if (!isUseFnArgResolved) {
|
161
173
|
useFnArgPromise.reject(e);
|
162
174
|
return;
|
163
175
|
}
|
176
|
+
// otherwise re-throw to avoid silencing error during cleanup
|
164
177
|
throw e;
|
165
178
|
});
|
166
179
|
return useFnArgPromise;
|
@@ -186,6 +199,11 @@ function resolveDeps(fixtures, depSet = new Set(), pendingFixtures = []) {
|
|
186
199
|
}
|
187
200
|
function getUsedProps(fn) {
|
188
201
|
let fnString = fn.toString();
|
202
|
+
// match lowered async function and strip it off
|
203
|
+
// example code on esbuild-try https://esbuild.github.io/try/#YgAwLjI0LjAALS1zdXBwb3J0ZWQ6YXN5bmMtYXdhaXQ9ZmFsc2UAZQBlbnRyeS50cwBjb25zdCBvID0gewogIGYxOiBhc3luYyAoKSA9PiB7fSwKICBmMjogYXN5bmMgKGEpID0+IHt9LAogIGYzOiBhc3luYyAoYSwgYikgPT4ge30sCiAgZjQ6IGFzeW5jIGZ1bmN0aW9uKGEpIHt9LAogIGY1OiBhc3luYyBmdW5jdGlvbiBmZihhKSB7fSwKICBhc3luYyBmNihhKSB7fSwKCiAgZzE6IGFzeW5jICgpID0+IHt9LAogIGcyOiBhc3luYyAoeyBhIH0pID0+IHt9LAogIGczOiBhc3luYyAoeyBhIH0sIGIpID0+IHt9LAogIGc0OiBhc3luYyBmdW5jdGlvbiAoeyBhIH0pIHt9LAogIGc1OiBhc3luYyBmdW5jdGlvbiBnZyh7IGEgfSkge30sCiAgYXN5bmMgZzYoeyBhIH0pIHt9Cn0
|
204
|
+
// __async(this, null, function*
|
205
|
+
// __async(this, arguments, function*
|
206
|
+
// __async(this, [_0, _1], function*
|
189
207
|
if (/__async\((?:this|null), (?:null|arguments|\[[_0-9, ]*\]), function\*/.test(fnString)) {
|
190
208
|
fnString = fnString.split(/__async\((?:this|null),/)[1];
|
191
209
|
}
|
@@ -408,6 +426,7 @@ function getRunner() {
|
|
408
426
|
function createDefaultSuite(runner) {
|
409
427
|
const config = runner.config.sequence;
|
410
428
|
const collector = suite("", { concurrent: config.concurrent }, () => {});
|
429
|
+
// no parent suite for top-level tests
|
411
430
|
delete collector.suite;
|
412
431
|
return collector;
|
413
432
|
}
|
@@ -437,7 +456,9 @@ function createSuiteHooks() {
|
|
437
456
|
function parseArguments(optionsOrFn, optionsOrTest) {
|
438
457
|
let options = {};
|
439
458
|
let fn = () => {};
|
459
|
+
// it('', () => {}, { retry: 2 })
|
440
460
|
if (typeof optionsOrTest === "object") {
|
461
|
+
// it('', { retry: 2 }, { retry: 3 })
|
441
462
|
if (typeof optionsOrFn === "object") {
|
442
463
|
throw new TypeError("Cannot use two objects as arguments. Please provide options and a function callback in that order.");
|
443
464
|
}
|
@@ -461,6 +482,7 @@ function parseArguments(optionsOrFn, optionsOrTest) {
|
|
461
482
|
handler: fn
|
462
483
|
};
|
463
484
|
}
|
485
|
+
// implementations
|
464
486
|
function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions, parentCollectorFixtures) {
|
465
487
|
const tasks = [];
|
466
488
|
let suite;
|
@@ -489,11 +511,13 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
|
|
489
511
|
}
|
490
512
|
task.shuffle = suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.shuffle;
|
491
513
|
const context = createTestContext(task, runner);
|
514
|
+
// create test context
|
492
515
|
Object.defineProperty(task, "context", {
|
493
516
|
value: context,
|
494
517
|
enumerable: false
|
495
518
|
});
|
496
519
|
setTestFixture(context, options.fixtures);
|
520
|
+
// custom can be called from any place, let's assume the limit is 15 stacks
|
497
521
|
const limit = Error.stackTraceLimit;
|
498
522
|
Error.stackTraceLimit = 15;
|
499
523
|
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
@@ -513,9 +537,11 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
|
|
513
537
|
};
|
514
538
|
const test = createTest(function(name, optionsOrFn, optionsOrTest) {
|
515
539
|
let { options, handler } = parseArguments(optionsOrFn, optionsOrTest);
|
540
|
+
// inherit repeats, retry, timeout from suite
|
516
541
|
if (typeof suiteOptions === "object") {
|
517
542
|
options = Object.assign({}, suiteOptions, options);
|
518
543
|
}
|
544
|
+
// inherit concurrent / sequential from suite
|
519
545
|
options.concurrent = this.concurrent || !this.sequential && (options === null || options === void 0 ? void 0 : options.concurrent);
|
520
546
|
options.sequential = this.sequential || !this.concurrent && (options === null || options === void 0 ? void 0 : options.sequential);
|
521
547
|
const test = task(formatName(name), {
|
@@ -612,6 +638,7 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
|
|
612
638
|
function withAwaitAsyncAssertions(fn, task) {
|
613
639
|
return async (...args) => {
|
614
640
|
const fnResult = await fn(...args);
|
641
|
+
// some async expect will be added to this array, in case user forget to await them
|
615
642
|
if (task.promises) {
|
616
643
|
const result = await Promise.allSettled(task.promises);
|
617
644
|
const errors = result.map((r) => r.status === "rejected" ? r.reason : undefined).filter(Boolean);
|
@@ -630,11 +657,13 @@ function createSuite() {
|
|
630
657
|
let { options, handler: factory } = parseArguments(factoryOrOptions, optionsOrFactory);
|
631
658
|
const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false;
|
632
659
|
const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false;
|
660
|
+
// inherit options from current suite
|
633
661
|
options = {
|
634
662
|
...currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.options,
|
635
663
|
...options,
|
636
664
|
shuffle: this.shuffle ?? options.shuffle ?? (currentSuite === null || currentSuite === void 0 || (_currentSuite$options = currentSuite.options) === null || _currentSuite$options === void 0 ? void 0 : _currentSuite$options.shuffle) ?? (runner === null || runner === void 0 ? void 0 : runner.config.sequence.shuffle)
|
637
665
|
};
|
666
|
+
// inherit concurrent / sequential from suite
|
638
667
|
const isConcurrent = isConcurrentSpecified || options.concurrent && !isSequentialSpecified;
|
639
668
|
const isSequential = isSequentialSpecified || options.sequential && !isConcurrentSpecified;
|
640
669
|
options.concurrent = isConcurrent && !isSequential;
|
@@ -735,6 +764,7 @@ function createTaskCollector(fn, context) {
|
|
735
764
|
const _name = formatName(name);
|
736
765
|
const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
|
737
766
|
cases.forEach((item, idx) => {
|
767
|
+
// monkey-patch handler to allow parsing fixture
|
738
768
|
const handlerWrapper = (ctx) => handler(item, ctx);
|
739
769
|
handlerWrapper.__VITEST_FIXTURE_INDEX__ = 1;
|
740
770
|
handlerWrapper.toString = () => handler.toString();
|
@@ -788,6 +818,7 @@ function formatName(name) {
|
|
788
818
|
}
|
789
819
|
function formatTitle(template, items, idx) {
|
790
820
|
if (template.includes("%#") || template.includes("%$")) {
|
821
|
+
// '%#' match index of the test case
|
791
822
|
template = template.replace(/%%/g, "__vitest_escaped_%__").replace(/%#/g, `${idx}`).replace(/%\$/g, `${idx + 1}`).replace(/__vitest_escaped_%__/g, "%%");
|
792
823
|
}
|
793
824
|
const count = template.split("%").length - 1;
|
@@ -795,6 +826,7 @@ function formatTitle(template, items, idx) {
|
|
795
826
|
const placeholders = template.match(/%f/g) || [];
|
796
827
|
placeholders.forEach((_, i) => {
|
797
828
|
if (isNegativeNaN(items[i]) || Object.is(items[i], -0)) {
|
829
|
+
// Replace the i-th occurrence of '%f' with '-%f'
|
798
830
|
let occurrence = 0;
|
799
831
|
template = template.replace(/%f/g, (match) => {
|
800
832
|
occurrence++;
|
@@ -830,6 +862,7 @@ function formatTemplateString(cases, args) {
|
|
830
862
|
return res;
|
831
863
|
}
|
832
864
|
function findTestFileStackTrace(error, each) {
|
865
|
+
// first line is the error message
|
833
866
|
const lines = error.split("\n").slice(1);
|
834
867
|
for (const line of lines) {
|
835
868
|
const stack = parseSingleStack(line);
|
@@ -862,6 +895,7 @@ function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
862
895
|
return fn;
|
863
896
|
}
|
864
897
|
const { setTimeout, clearTimeout } = getSafeTimers();
|
898
|
+
// this function name is used to filter error in test/cli/test/fails.test.ts
|
865
899
|
return function runWithTimeout(...args) {
|
866
900
|
const startTime = now$2();
|
867
901
|
const runner = getRunner();
|
@@ -873,6 +907,7 @@ function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
873
907
|
clearTimeout(timer);
|
874
908
|
rejectTimeoutError();
|
875
909
|
}, timeout);
|
910
|
+
// `unref` might not exist in browser
|
876
911
|
(_timer$unref = timer.unref) === null || _timer$unref === void 0 ? void 0 : _timer$unref.call(timer);
|
877
912
|
function rejectTimeoutError() {
|
878
913
|
const error = makeTimeoutError(isHook, timeout, stackTraceError);
|
@@ -883,6 +918,9 @@ function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
883
918
|
runner._currentTaskStartTime = undefined;
|
884
919
|
runner._currentTaskTimeout = undefined;
|
885
920
|
clearTimeout(timer);
|
921
|
+
// if test/hook took too long in microtask, setTimeout won't be triggered,
|
922
|
+
// but we still need to fail the test, see
|
923
|
+
// https://github.com/vitest-dev/vitest/issues/2920
|
886
924
|
if (now$2() - startTime >= timeout) {
|
887
925
|
rejectTimeoutError();
|
888
926
|
return;
|
@@ -895,14 +933,19 @@ function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
895
933
|
clearTimeout(timer);
|
896
934
|
reject_(error);
|
897
935
|
}
|
936
|
+
// sync test/hook will be caught by try/catch
|
898
937
|
try {
|
899
938
|
const result = fn(...args);
|
939
|
+
// the result is a thenable, we don't wrap this in Promise.resolve
|
940
|
+
// to avoid creating new promises
|
900
941
|
if (typeof result === "object" && result != null && typeof result.then === "function") {
|
901
942
|
result.then(resolve, reject);
|
902
943
|
} else {
|
903
944
|
resolve(result);
|
904
945
|
}
|
905
|
-
}
|
946
|
+
}
|
947
|
+
// user sync test/hook throws an error
|
948
|
+
catch (error) {
|
906
949
|
reject(error);
|
907
950
|
}
|
908
951
|
});
|
@@ -932,6 +975,7 @@ function createTestContext(test, runner) {
|
|
932
975
|
context.task = test;
|
933
976
|
context.skip = (condition, note) => {
|
934
977
|
if (condition === false) {
|
978
|
+
// do nothing
|
935
979
|
return undefined;
|
936
980
|
}
|
937
981
|
test.result ?? (test.result = { state: "skip" });
|
@@ -1174,6 +1218,7 @@ async function collectTests(specs, runner) {
|
|
1174
1218
|
file.tasks.push(suite);
|
1175
1219
|
}
|
1176
1220
|
} else {
|
1221
|
+
// check that types are exhausted
|
1177
1222
|
c;
|
1178
1223
|
}
|
1179
1224
|
}
|
@@ -1267,6 +1312,7 @@ async function callTestHooks(runner, test, hooks, sequence) {
|
|
1267
1312
|
async function callSuiteHook(suite, currentTask, name, runner, args) {
|
1268
1313
|
const sequence = runner.config.sequence.hooks;
|
1269
1314
|
const callbacks = [];
|
1315
|
+
// stop at file level
|
1270
1316
|
const parentSuite = "filepath" in suite ? null : suite.suite || suite.file;
|
1271
1317
|
if (name === "beforeEach" && parentSuite) {
|
1272
1318
|
callbacks.push(...await callSuiteHook(parentSuite, currentTask, name, runner, args));
|
@@ -1309,6 +1355,8 @@ function sendTasksUpdate(runner) {
|
|
1309
1355
|
const p = (_runner$onTaskUpdate = runner.onTaskUpdate) === null || _runner$onTaskUpdate === void 0 ? void 0 : _runner$onTaskUpdate.call(runner, taskPacks, eventsPacks);
|
1310
1356
|
if (p) {
|
1311
1357
|
pendingTasksUpdates.push(p);
|
1358
|
+
// remove successful promise to not grow array indefnitely,
|
1359
|
+
// but keep rejections so finishSendTasksUpdate can handle them
|
1312
1360
|
p.then(() => pendingTasksUpdates.splice(pendingTasksUpdates.indexOf(p), 1), () => {});
|
1313
1361
|
}
|
1314
1362
|
eventsPacks.length = 0;
|
@@ -1329,6 +1377,7 @@ function throttle(fn, ms) {
|
|
1329
1377
|
}
|
1330
1378
|
};
|
1331
1379
|
}
|
1380
|
+
// throttle based on summary reporter's DURATION_UPDATE_INTERVAL_MS
|
1332
1381
|
const sendTasksUpdateThrottled = throttle(sendTasksUpdate, 100);
|
1333
1382
|
function updateTask(event, task, runner) {
|
1334
1383
|
eventsPacks.push([task.id, event]);
|
@@ -1365,6 +1414,9 @@ async function runTest(test, runner) {
|
|
1365
1414
|
return;
|
1366
1415
|
}
|
1367
1416
|
if (((_test$result = test.result) === null || _test$result === void 0 ? void 0 : _test$result.state) === "fail") {
|
1417
|
+
// should not be possible to get here, I think this is just copy pasted from suite
|
1418
|
+
// TODO: maybe someone fails tests in `beforeAll` hooks?
|
1419
|
+
// https://github.com/vitest-dev/vitest/pull/7069
|
1368
1420
|
updateTask("test-failed-early", test, runner);
|
1369
1421
|
return;
|
1370
1422
|
}
|
@@ -1434,6 +1486,7 @@ async function runTest(test, runner) {
|
|
1434
1486
|
}
|
1435
1487
|
test.onFailed = undefined;
|
1436
1488
|
test.onFinished = undefined;
|
1489
|
+
// skipped with new PendingError
|
1437
1490
|
if (((_test$result2 = test.result) === null || _test$result2 === void 0 ? void 0 : _test$result2.pending) || ((_test$result3 = test.result) === null || _test$result3 === void 0 ? void 0 : _test$result3.state) === "skip") {
|
1438
1491
|
var _test$result4;
|
1439
1492
|
test.mode = "skip";
|
@@ -1452,12 +1505,15 @@ async function runTest(test, runner) {
|
|
1452
1505
|
break;
|
1453
1506
|
}
|
1454
1507
|
if (retryCount < retry) {
|
1508
|
+
// reset state when retry test
|
1455
1509
|
test.result.state = "run";
|
1456
1510
|
test.result.retryCount = (test.result.retryCount ?? 0) + 1;
|
1457
1511
|
}
|
1512
|
+
// update retry info
|
1458
1513
|
updateTask("test-retried", test, runner);
|
1459
1514
|
}
|
1460
1515
|
}
|
1516
|
+
// if test is marked to be failed, flip the result
|
1461
1517
|
if (test.fails) {
|
1462
1518
|
if (test.result.state === "pass") {
|
1463
1519
|
const error = processError(new Error("Expect test to fail"));
|
@@ -1507,6 +1563,7 @@ async function runSuite(suite, runner) {
|
|
1507
1563
|
await ((_runner$onBeforeRunSu = runner.onBeforeRunSuite) === null || _runner$onBeforeRunSu === void 0 ? void 0 : _runner$onBeforeRunSu.call(runner, suite));
|
1508
1564
|
if (((_suite$result = suite.result) === null || _suite$result === void 0 ? void 0 : _suite$result.state) === "fail") {
|
1509
1565
|
markTasksAsSkipped(suite, runner);
|
1566
|
+
// failed during collection
|
1510
1567
|
updateTask("suite-failed-early", suite, runner);
|
1511
1568
|
return;
|
1512
1569
|
}
|
@@ -1542,6 +1599,7 @@ async function runSuite(suite, runner) {
|
|
1542
1599
|
} else {
|
1543
1600
|
const { sequence } = runner.config;
|
1544
1601
|
if (suite.shuffle) {
|
1602
|
+
// run describe block independently from tests
|
1545
1603
|
const suites = tasksGroup.filter((group) => group.type === "suite");
|
1546
1604
|
const tests = tasksGroup.filter((group) => group.type === "test");
|
1547
1605
|
const groups = shuffle([suites, tests], sequence.seed);
|
@@ -1608,7 +1666,10 @@ async function runFiles(files, runner) {
|
|
1608
1666
|
async function startTests(specs, runner) {
|
1609
1667
|
var _runner$cancel;
|
1610
1668
|
const cancel = (_runner$cancel = runner.cancel) === null || _runner$cancel === void 0 ? void 0 : _runner$cancel.bind(runner);
|
1669
|
+
// Ideally, we need to have an event listener for this, but only have a runner here.
|
1670
|
+
// Adding another onCancel felt wrong (maybe it needs to be refactored)
|
1611
1671
|
runner.cancel = (reason) => {
|
1672
|
+
// We intentionally create only one error since there is only one test run that can be cancelled
|
1612
1673
|
const error = new TestRunAbortError("The test run was aborted by the user.", reason);
|
1613
1674
|
getRunningTests().forEach((test) => abortContextSignal(test.context, error));
|
1614
1675
|
return cancel === null || cancel === void 0 ? void 0 : cancel(reason);
|
@@ -222,6 +222,7 @@ type Task = Test | Suite | File;
|
|
222
222
|
*/
|
223
223
|
type DoneCallback = (error?: any) => void;
|
224
224
|
type TestFunction<ExtraContext = object> = (context: TestContext & ExtraContext) => Awaitable<any> | void;
|
225
|
+
// jest's ExtractEachCallbackArgs
|
225
226
|
type ExtractEachCallbackArgs<T extends ReadonlyArray<any>> = {
|
226
227
|
1: [T[0]]
|
227
228
|
2: [T[0], T[1]]
|
@@ -257,7 +258,14 @@ interface TestForFunctionReturn<
|
|
257
258
|
(name: string | Function, options: TestCollectorOptions, fn: (args: Arg, context: Context) => Awaitable<void>): void;
|
258
259
|
}
|
259
260
|
interface TestForFunction<ExtraContext> {
|
261
|
+
// test.for([1, 2, 3])
|
262
|
+
// test.for([[1, 2], [3, 4, 5]])
|
260
263
|
<T>(cases: ReadonlyArray<T>): TestForFunctionReturn<T, TestContext & ExtraContext>;
|
264
|
+
// test.for`
|
265
|
+
// a | b
|
266
|
+
// {1} | {2}
|
267
|
+
// {3} | {4}
|
268
|
+
// `
|
261
269
|
(strings: TemplateStringsArray, ...values: any[]): TestForFunctionReturn<any, TestContext & ExtraContext>;
|
262
270
|
}
|
263
271
|
interface SuiteForFunction {
|
package/dist/types.d.ts
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
import { DiffOptions } from '@vitest/utils/diff';
|
2
|
-
import { F as File, T as Task, a as Test, S as Suite, K as TaskResultPack, y as TaskEventPack, M as TestContext, s as SequenceHooks, t as SequenceSetupFiles } from './tasks.d-
|
3
|
-
export { A as AfterAllListener, b as AfterEachListener, B as BeforeAllListener, d as BeforeEachListener, g as Custom, j as CustomAPI, D as DoneCallback, E as ExtendedContext, m as Fixture, n as FixtureFn, o as FixtureOptions, p as Fixtures, H as HookCleanupCallback, q as HookListener, I as InferFixturesTypes, O as OnTestFailedHandler, f as OnTestFinishedHandler, R as RunMode, r as RuntimeContext, k as SuiteAPI, l as SuiteCollector, u as SuiteFactory, h as SuiteHooks, v as TaskBase, w as TaskContext, x as TaskCustomOptions, e as TaskHook, z as TaskMeta, G as TaskPopulated, J as TaskResult, L as TaskState, i as TaskUpdateEvent, j as TestAPI, N as TestFunction, P as TestOptions, U as Use } from './tasks.d-
|
2
|
+
import { F as File, T as Task, a as Test, S as Suite, K as TaskResultPack, y as TaskEventPack, M as TestContext, s as SequenceHooks, t as SequenceSetupFiles } from './tasks.d-CItyFU3G.js';
|
3
|
+
export { A as AfterAllListener, b as AfterEachListener, B as BeforeAllListener, d as BeforeEachListener, g as Custom, j as CustomAPI, D as DoneCallback, E as ExtendedContext, m as Fixture, n as FixtureFn, o as FixtureOptions, p as Fixtures, H as HookCleanupCallback, q as HookListener, I as InferFixturesTypes, O as OnTestFailedHandler, f as OnTestFinishedHandler, R as RunMode, r as RuntimeContext, k as SuiteAPI, l as SuiteCollector, u as SuiteFactory, h as SuiteHooks, v as TaskBase, w as TaskContext, x as TaskCustomOptions, e as TaskHook, z as TaskMeta, G as TaskPopulated, J as TaskResult, L as TaskState, i as TaskUpdateEvent, j as TestAPI, N as TestFunction, P as TestOptions, U as Use } from './tasks.d-CItyFU3G.js';
|
4
4
|
import '@vitest/utils';
|
5
5
|
|
6
6
|
/**
|
package/dist/utils.d.ts
CHANGED
@@ -1,5 +1,5 @@
|
|
1
|
-
import { S as Suite, F as File, T as Task, a as Test } from './tasks.d-
|
2
|
-
export { C as ChainableFunction, c as createChainable } from './tasks.d-
|
1
|
+
import { S as Suite, F as File, T as Task, a as Test } from './tasks.d-CItyFU3G.js';
|
2
|
+
export { C as ChainableFunction, c as createChainable } from './tasks.d-CItyFU3G.js';
|
3
3
|
import { Arrayable } from '@vitest/utils';
|
4
4
|
|
5
5
|
/**
|
package/package.json
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
{
|
2
2
|
"name": "@vitest/runner",
|
3
3
|
"type": "module",
|
4
|
-
"version": "3.2.0-beta.
|
4
|
+
"version": "3.2.0-beta.3",
|
5
5
|
"description": "Vitest test runner",
|
6
6
|
"license": "MIT",
|
7
7
|
"funding": "https://opencollective.com/vitest",
|
@@ -39,7 +39,7 @@
|
|
39
39
|
],
|
40
40
|
"dependencies": {
|
41
41
|
"pathe": "^2.0.3",
|
42
|
-
"@vitest/utils": "3.2.0-beta.
|
42
|
+
"@vitest/utils": "3.2.0-beta.3"
|
43
43
|
},
|
44
44
|
"scripts": {
|
45
45
|
"build": "rimraf dist && rollup -c",
|