@vitest/runner 0.30.1 → 0.31.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.
@@ -85,7 +85,7 @@ function normalizeErrorMessage(message) {
85
85
  }
86
86
  function processError(err, options = {}) {
87
87
  if (!err || typeof err !== "object")
88
- return err;
88
+ return { message: err };
89
89
  if (err.stack)
90
90
  err.stackStr = String(err.stack);
91
91
  if (err.name)
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- import { V as VitestRunner } from './runner-3b8473ea.js';
2
- export { a as VitestRunnerConfig, c as VitestRunnerConstructor, b as VitestRunnerImportSource } from './runner-3b8473ea.js';
3
- import { T as Task, F as File, S as SuiteAPI, a as TestAPI, b as SuiteCollector, c as SuiteHooks, O as OnTestFailedHandler, d as Test } from './tasks-c965d7f6.js';
4
- export { D as DoneCallback, m as HookCleanupCallback, H as HookListener, R as RunMode, o as RuntimeContext, q as SequenceHooks, r as SequenceSetupFiles, j as Suite, n as SuiteFactory, f as TaskBase, g as TaskCustom, h as TaskResult, i as TaskResultPack, e as TaskState, p as TestContext, k as TestFunction, l as TestOptions } from './tasks-c965d7f6.js';
1
+ import { V as VitestRunner } from './runner-a2cd0770.js';
2
+ export { C as CancelReason, a as VitestRunnerConfig, c as VitestRunnerConstructor, b as VitestRunnerImportSource } from './runner-a2cd0770.js';
3
+ import { T as Task, F as File, S as SuiteAPI, a as TestAPI, b as SuiteCollector, c as SuiteHooks, O as OnTestFailedHandler, d as Test } from './tasks-891047e7.js';
4
+ export { D as DoneCallback, m as HookCleanupCallback, H as HookListener, R as RunMode, o as RuntimeContext, q as SequenceHooks, r as SequenceSetupFiles, j as Suite, n as SuiteFactory, f as TaskBase, g as TaskCustom, h as TaskResult, i as TaskResultPack, e as TaskState, p as TestContext, k as TestFunction, l as TestOptions } from './tasks-891047e7.js';
5
5
  import { Awaitable } from '@vitest/utils';
6
6
 
7
7
  declare function updateTask(task: Task, runner: VitestRunner): void;
package/dist/index.js CHANGED
@@ -65,9 +65,29 @@ function makeTimeoutMsg(isHook, timeout) {
65
65
  If this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
66
66
  }
67
67
 
68
+ var version = "0.31.1";
69
+
70
+ function getVersion() {
71
+ return globalThis.__vitest_runner_version__ || version;
72
+ }
73
+ function markVersion() {
74
+ globalThis.__vitest_runner_version__ = version;
75
+ }
76
+ function checkVersion() {
77
+ const collectVersion = getVersion();
78
+ if (collectVersion !== version) {
79
+ const error = `Version mismatch: Vitest started as ${collectVersion}, but tests are collected with ${version} version.
80
+
81
+ - If you are using global Vitest, make sure your package has the same version.
82
+ - If you have a monorepo setup, make sure your main package has the same version as your test packages.`;
83
+ throw new Error(error);
84
+ }
85
+ }
86
+
68
87
  const suite = createSuite();
69
88
  const test = createTest(
70
89
  function(name, fn, options) {
90
+ checkVersion();
71
91
  getCurrentSuite().test.fn.call(this, name, fn, options);
72
92
  }
73
93
  );
@@ -106,10 +126,17 @@ function createSuiteCollector(name, factory = () => {
106
126
  const factoryQueue = [];
107
127
  let suite2;
108
128
  initSuite();
109
- const test2 = createTest(function(name2, fn = noop, options = suiteOptions) {
129
+ const test2 = createTest(function(name2, fn = noop, options) {
110
130
  const mode2 = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run";
111
131
  if (typeof options === "number")
112
132
  options = { timeout: options };
133
+ if (typeof suiteOptions === "object") {
134
+ options = {
135
+ repeats: suiteOptions.repeats,
136
+ retry: suiteOptions.retry,
137
+ ...options
138
+ };
139
+ }
113
140
  const test3 = {
114
141
  id: "",
115
142
  type: "test",
@@ -117,7 +144,8 @@ function createSuiteCollector(name, factory = () => {
117
144
  mode: mode2,
118
145
  suite: void 0,
119
146
  fails: this.fails,
120
- retry: options == null ? void 0 : options.retry
147
+ retry: options == null ? void 0 : options.retry,
148
+ repeats: options == null ? void 0 : options.repeats
121
149
  };
122
150
  if (this.concurrent || concurrent)
123
151
  test3.concurrent = true;
@@ -149,6 +177,7 @@ function createSuiteCollector(name, factory = () => {
149
177
  type: "collector",
150
178
  name,
151
179
  mode,
180
+ options: suiteOptions,
152
181
  test: test2,
153
182
  tasks,
154
183
  collect,
@@ -160,6 +189,8 @@ function createSuiteCollector(name, factory = () => {
160
189
  getHooks(suite2)[name2].push(...fn);
161
190
  }
162
191
  function initSuite() {
192
+ if (typeof suiteOptions === "number")
193
+ suiteOptions = { timeout: suiteOptions };
163
194
  suite2 = {
164
195
  id: "",
165
196
  type: "suite",
@@ -196,7 +227,15 @@ function createSuiteCollector(name, factory = () => {
196
227
  }
197
228
  function createSuite() {
198
229
  function suiteFn(name, factory, options) {
230
+ var _a;
231
+ checkVersion();
199
232
  const mode = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run";
233
+ const currentSuite = getCurrentSuite();
234
+ if (typeof options === "number")
235
+ options = { timeout: options };
236
+ if (currentSuite && typeof ((_a = currentSuite.options) == null ? void 0 : _a.repeats) === "number") {
237
+ options = { repeats: currentSuite.options.repeats, ...options };
238
+ }
200
239
  return createSuiteCollector(name, factory, mode, this.concurrent, this.shuffle, options);
201
240
  }
202
241
  suiteFn.each = function(cases, ...args) {
@@ -433,41 +472,48 @@ async function runTest(test, runner) {
433
472
  };
434
473
  updateTask(test, runner);
435
474
  setCurrentTest(test);
436
- const retry = test.retry || 1;
437
- for (let retryCount = 0; retryCount < retry; retryCount++) {
438
- let beforeEachCleanups = [];
439
- try {
440
- await ((_c = runner.onBeforeTryTest) == null ? void 0 : _c.call(runner, test, retryCount));
441
- beforeEachCleanups = await callSuiteHook(test.suite, test, "beforeEach", runner, [test.context, test.suite]);
442
- test.result.retryCount = retryCount;
443
- if (runner.runTest) {
444
- await runner.runTest(test);
445
- } else {
446
- const fn = getFn(test);
447
- if (!fn)
448
- throw new Error("Test function is not found. Did you add it using `setFn`?");
449
- await fn();
475
+ const repeats = typeof test.repeats === "number" ? test.repeats : 1;
476
+ for (let repeatCount = 0; repeatCount < repeats; repeatCount++) {
477
+ const retry = test.retry || 1;
478
+ for (let retryCount = 0; retryCount < retry; retryCount++) {
479
+ let beforeEachCleanups = [];
480
+ try {
481
+ await ((_c = runner.onBeforeTryTest) == null ? void 0 : _c.call(runner, test, { retry: retryCount, repeats: repeatCount }));
482
+ test.result.retryCount = retryCount;
483
+ test.result.repeatCount = repeatCount;
484
+ beforeEachCleanups = await callSuiteHook(test.suite, test, "beforeEach", runner, [test.context, test.suite]);
485
+ if (runner.runTest) {
486
+ await runner.runTest(test);
487
+ } else {
488
+ const fn = getFn(test);
489
+ if (!fn)
490
+ throw new Error("Test function is not found. Did you add it using `setFn`?");
491
+ await fn();
492
+ }
493
+ if (test.promises) {
494
+ const result = await Promise.allSettled(test.promises);
495
+ const errors = result.map((r) => r.status === "rejected" ? r.reason : void 0).filter(Boolean);
496
+ if (errors.length)
497
+ throw errors;
498
+ }
499
+ await ((_d = runner.onAfterTryTest) == null ? void 0 : _d.call(runner, test, { retry: retryCount, repeats: repeatCount }));
500
+ if (!test.repeats)
501
+ test.result.state = "pass";
502
+ else if (test.repeats && retry === retryCount)
503
+ test.result.state = "pass";
504
+ } catch (e) {
505
+ failTask(test.result, e);
450
506
  }
451
- if (test.promises) {
452
- const result = await Promise.allSettled(test.promises);
453
- const errors = result.map((r) => r.status === "rejected" ? r.reason : void 0).filter(Boolean);
454
- if (errors.length)
455
- throw errors;
507
+ try {
508
+ await callSuiteHook(test.suite, test, "afterEach", runner, [test.context, test.suite]);
509
+ await callCleanupHooks(beforeEachCleanups);
510
+ } catch (e) {
511
+ failTask(test.result, e);
456
512
  }
457
- await ((_d = runner.onAfterTryTest) == null ? void 0 : _d.call(runner, test, retryCount));
458
- test.result.state = "pass";
459
- } catch (e) {
460
- failTask(test.result, e);
461
- }
462
- try {
463
- await callSuiteHook(test.suite, test, "afterEach", runner, [test.context, test.suite]);
464
- await callCleanupHooks(beforeEachCleanups);
465
- } catch (e) {
466
- failTask(test.result, e);
513
+ if (test.result.state === "pass")
514
+ break;
515
+ updateTask(test, runner);
467
516
  }
468
- if (test.result.state === "pass")
469
- break;
470
- updateTask(test, runner);
471
517
  }
472
518
  if (test.result.state === "fail")
473
519
  await Promise.all(((_e = test.onFailed) == null ? void 0 : _e.map((fn) => fn(test.result))) || []);
@@ -558,24 +604,24 @@ async function runSuite(suite, runner) {
558
604
  } catch (e) {
559
605
  failTask(suite.result, e);
560
606
  }
561
- }
562
- suite.result.duration = now() - start;
563
- if (suite.mode === "run") {
564
- if (!hasTests(suite)) {
565
- suite.result.state = "fail";
566
- if (!suite.result.error) {
567
- const error = processError(new Error(`No test found in suite ${suite.name}`));
568
- suite.result.error = error;
569
- suite.result.errors = [error];
607
+ if (suite.mode === "run") {
608
+ if (!hasTests(suite)) {
609
+ suite.result.state = "fail";
610
+ if (!suite.result.error) {
611
+ const error = processError(new Error(`No test found in suite ${suite.name}`));
612
+ suite.result.error = error;
613
+ suite.result.errors = [error];
614
+ }
615
+ } else if (hasFailed(suite)) {
616
+ suite.result.state = "fail";
617
+ } else {
618
+ suite.result.state = "pass";
570
619
  }
571
- } else if (hasFailed(suite)) {
572
- suite.result.state = "fail";
573
- } else {
574
- suite.result.state = "pass";
575
620
  }
621
+ updateTask(suite, runner);
622
+ suite.result.duration = now() - start;
623
+ await ((_c = runner.onAfterRunSuite) == null ? void 0 : _c.call(runner, suite));
576
624
  }
577
- await ((_c = runner.onAfterRunSuite) == null ? void 0 : _c.call(runner, suite));
578
- updateTask(suite, runner);
579
625
  }
580
626
  async function runSuiteChild(c, runner) {
581
627
  if (c.type === "test")
@@ -601,6 +647,7 @@ async function runFiles(files, runner) {
601
647
  }
602
648
  async function startTests(paths, runner) {
603
649
  var _a, _b, _c, _d;
650
+ markVersion();
604
651
  await ((_a = runner.onBeforeCollect) == null ? void 0 : _a.call(runner, paths));
605
652
  const files = await collectTests(paths, runner);
606
653
  (_b = runner.onCollected) == null ? void 0 : _b.call(runner, files);
@@ -1,4 +1,4 @@
1
- import { q as SequenceHooks, r as SequenceSetupFiles, F as File, d as Test, j as Suite, h as TaskResult, p as TestContext } from './tasks-c965d7f6.js';
1
+ import { q as SequenceHooks, r as SequenceSetupFiles, F as File, d as Test, j as Suite, h as TaskResult, p as TestContext } from './tasks-891047e7.js';
2
2
 
3
3
  interface VitestRunnerConfig {
4
4
  root: string;
@@ -24,6 +24,7 @@ type VitestRunnerImportSource = 'collect' | 'setup';
24
24
  interface VitestRunnerConstructor {
25
25
  new (config: VitestRunnerConfig): VitestRunner;
26
26
  }
27
+ type CancelReason = 'keyboard-input' | 'test-failure' | string & {};
27
28
  interface VitestRunner {
28
29
  /**
29
30
  * First thing that's getting called before actually collecting and running tests.
@@ -33,6 +34,12 @@ interface VitestRunner {
33
34
  * Called after collecting tests and before "onBeforeRun".
34
35
  */
35
36
  onCollected?(files: File[]): unknown;
37
+ /**
38
+ * Called when test runner should cancel next test runs.
39
+ * Runner should listen for this method and mark tests and suites as skipped in
40
+ * "onBeforeRunSuite" and "onBeforeRunTest" when called.
41
+ */
42
+ onCancel?(reason: CancelReason): unknown;
36
43
  /**
37
44
  * Called before running a single test. Doesn't have "result" yet.
38
45
  */
@@ -40,7 +47,10 @@ interface VitestRunner {
40
47
  /**
41
48
  * Called before actually running the test function. Already has "result" with "state" and "startTime".
42
49
  */
43
- onBeforeTryTest?(test: Test, retryCount: number): unknown;
50
+ onBeforeTryTest?(test: Test, options: {
51
+ retry: number;
52
+ repeats: number;
53
+ }): unknown;
44
54
  /**
45
55
  * Called after result and state are set.
46
56
  */
@@ -48,7 +58,10 @@ interface VitestRunner {
48
58
  /**
49
59
  * Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws.
50
60
  */
51
- onAfterTryTest?(test: Test, retryCount: number): unknown;
61
+ onAfterTryTest?(test: Test, options: {
62
+ retry: number;
63
+ repeats: number;
64
+ }): unknown;
52
65
  /**
53
66
  * Called before running a single suite. Doesn't have "result" yet.
54
67
  */
@@ -94,4 +107,4 @@ interface VitestRunner {
94
107
  config: VitestRunnerConfig;
95
108
  }
96
109
 
97
- export { VitestRunner as V, VitestRunnerConfig as a, VitestRunnerImportSource as b, VitestRunnerConstructor as c };
110
+ export { CancelReason as C, VitestRunner as V, VitestRunnerConfig as a, VitestRunnerImportSource as b, VitestRunnerConstructor as c };
@@ -22,6 +22,7 @@ interface TaskBase {
22
22
  result?: TaskResult;
23
23
  retry?: number;
24
24
  meta?: any;
25
+ repeats?: number;
25
26
  }
26
27
  interface TaskCustom extends TaskBase {
27
28
  type: 'custom';
@@ -39,6 +40,7 @@ interface TaskResult {
39
40
  htmlError?: string;
40
41
  hooks?: Partial<Record<keyof SuiteHooks, TaskState>>;
41
42
  retryCount?: number;
43
+ repeatCount?: number;
42
44
  }
43
45
  type TaskResultPack = [id: string, result: TaskResult | undefined];
44
46
  interface Suite extends TaskBase {
@@ -111,6 +113,14 @@ interface TestOptions {
111
113
  * @default 1
112
114
  */
113
115
  retry?: number;
116
+ /**
117
+ * How many times the test will run.
118
+ * Only inner tests will repeat if set on `describe()`, nested `describe()` will inherit parent's repeat by default.
119
+ *
120
+ * @default 1
121
+ *
122
+ */
123
+ repeats?: number;
114
124
  }
115
125
  type TestAPI<ExtraContext = {}> = ChainableTestAPI<ExtraContext> & {
116
126
  each: TestEachFunction;
@@ -141,6 +151,7 @@ interface SuiteHooks<ExtraContext = {}> {
141
151
  interface SuiteCollector<ExtraContext = {}> {
142
152
  readonly name: string;
143
153
  readonly mode: RunMode;
154
+ options?: TestOptions;
144
155
  type: 'collector';
145
156
  test: TestAPI<ExtraContext>;
146
157
  tasks: (Suite | TaskCustom | Test | SuiteCollector<ExtraContext>)[];
package/dist/types.d.ts CHANGED
@@ -1,3 +1,3 @@
1
- export { D as DoneCallback, F as File, m as HookCleanupCallback, H as HookListener, O as OnTestFailedHandler, R as RunMode, o as RuntimeContext, q as SequenceHooks, r as SequenceSetupFiles, j as Suite, S as SuiteAPI, b as SuiteCollector, n as SuiteFactory, c as SuiteHooks, T as Task, f as TaskBase, g as TaskCustom, h as TaskResult, i as TaskResultPack, e as TaskState, d as Test, a as TestAPI, p as TestContext, k as TestFunction, l as TestOptions } from './tasks-c965d7f6.js';
2
- export { V as VitestRunner, a as VitestRunnerConfig, c as VitestRunnerConstructor, b as VitestRunnerImportSource } from './runner-3b8473ea.js';
1
+ export { D as DoneCallback, F as File, m as HookCleanupCallback, H as HookListener, O as OnTestFailedHandler, R as RunMode, o as RuntimeContext, q as SequenceHooks, r as SequenceSetupFiles, j as Suite, S as SuiteAPI, b as SuiteCollector, n as SuiteFactory, c as SuiteHooks, T as Task, f as TaskBase, g as TaskCustom, h as TaskResult, i as TaskResultPack, e as TaskState, d as Test, a as TestAPI, p as TestContext, k as TestFunction, l as TestOptions } from './tasks-891047e7.js';
2
+ export { C as CancelReason, V as VitestRunner, a as VitestRunnerConfig, c as VitestRunnerConstructor, b as VitestRunnerImportSource } from './runner-a2cd0770.js';
3
3
  import '@vitest/utils';
package/dist/utils.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { j as Suite, T as Task, d as Test, g as TaskCustom } from './tasks-c965d7f6.js';
2
- export { C as ChainableFunction, s as createChainable } from './tasks-c965d7f6.js';
1
+ import { j as Suite, T as Task, d as Test, g as TaskCustom } from './tasks-891047e7.js';
2
+ export { C as ChainableFunction, s as createChainable } from './tasks-891047e7.js';
3
3
  import { Arrayable } from '@vitest/utils';
4
4
  export { ErrorWithDiff, ParsedStack } from '@vitest/utils';
5
5
  import { DiffOptions } from '@vitest/utils/diff';
package/package.json CHANGED
@@ -1,14 +1,19 @@
1
1
  {
2
2
  "name": "@vitest/runner",
3
3
  "type": "module",
4
- "version": "0.30.1",
4
+ "version": "0.31.1",
5
5
  "description": "Vitest test runner",
6
6
  "license": "MIT",
7
+ "funding": "https://opencollective.com/vitest",
8
+ "homepage": "https://github.com/vitest-dev/vitest/tree/main/packages/runner#readme",
7
9
  "repository": {
8
10
  "type": "git",
9
11
  "url": "git+https://github.com/vitest-dev/vitest.git",
10
12
  "directory": "packages/runner"
11
13
  },
14
+ "bugs": {
15
+ "url": "https://github.com/vitest-dev/vitest/issues"
16
+ },
12
17
  "sideEffects": true,
13
18
  "exports": {
14
19
  ".": {
@@ -36,7 +41,7 @@
36
41
  "concordance": "^5.0.4",
37
42
  "p-limit": "^4.0.0",
38
43
  "pathe": "^1.1.0",
39
- "@vitest/utils": "0.30.1"
44
+ "@vitest/utils": "0.31.1"
40
45
  },
41
46
  "scripts": {
42
47
  "build": "rimraf dist && rollup -c",