@vitest/runner 3.2.0-beta.2 → 3.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,6 +3,7 @@ import { ErrorWithDiff, Awaitable } from '@vitest/utils';
3
3
  interface FixtureItem extends FixtureOptions {
4
4
  prop: string;
5
5
  value: any;
6
+ scope: "test" | "file" | "worker";
6
7
  /**
7
8
  * Indicates whether the fixture is a function
8
9
  */
@@ -156,16 +157,24 @@ interface TaskResult {
156
157
  * `repeats` option is set. This number also contains `retryCount`.
157
158
  */
158
159
  repeatCount?: number;
159
- /** @private */
160
- note?: string;
160
+ }
161
+ /** The time spent importing & executing a non-externalized file. */
162
+ interface ImportDuration {
163
+ /** The time spent importing & executing the file itself, not counting all non-externalized imports that the file does. */
164
+ selfTime: number;
165
+ /** The time spent importing & executing the file and all its imports. */
166
+ totalTime: number;
161
167
  }
162
168
  /**
163
169
  * The tuple representing a single task update.
164
170
  * Usually reported after the task finishes.
165
171
  */
166
172
  type TaskResultPack = [id: string, result: TaskResult | undefined, meta: TaskMeta];
167
- type TaskEventPack = [id: string, event: TaskUpdateEvent];
168
- type TaskUpdateEvent = "test-failed-early" | "suite-failed-early" | "test-prepare" | "test-finished" | "test-retried" | "suite-prepare" | "suite-finished" | "before-hook-start" | "before-hook-end" | "after-hook-start" | "after-hook-end";
173
+ interface TaskEventData {
174
+ annotation?: TestAnnotation | undefined;
175
+ }
176
+ type TaskEventPack = [id: string, event: TaskUpdateEvent, data: TaskEventData | undefined];
177
+ type TaskUpdateEvent = "test-failed-early" | "suite-failed-early" | "test-prepare" | "test-finished" | "test-retried" | "suite-prepare" | "suite-finished" | "before-hook-start" | "before-hook-end" | "after-hook-start" | "after-hook-end" | "test-annotation";
169
178
  interface Suite extends TaskBase {
170
179
  type: "suite";
171
180
  /**
@@ -200,6 +209,8 @@ interface File extends Suite {
200
209
  * The time it took to import the setup file.
201
210
  */
202
211
  setupDuration?: number;
212
+ /** The time spent importing every non-externalized dependency that Vitest has processed. */
213
+ importDurations?: Record<string, ImportDuration>;
203
214
  }
204
215
  interface Test<ExtraContext = object> extends TaskPopulated {
205
216
  type: "test";
@@ -211,6 +222,26 @@ interface Test<ExtraContext = object> extends TaskPopulated {
211
222
  * The test timeout in milliseconds.
212
223
  */
213
224
  timeout: number;
225
+ /**
226
+ * An array of custom annotations.
227
+ */
228
+ annotations: TestAnnotation[];
229
+ }
230
+ interface TestAttachment {
231
+ contentType?: string;
232
+ path?: string;
233
+ body?: string | Uint8Array;
234
+ }
235
+ interface TestAnnotationLocation {
236
+ line: number;
237
+ column: number;
238
+ file: string;
239
+ }
240
+ interface TestAnnotation {
241
+ message: string;
242
+ type: string;
243
+ location?: TestAnnotationLocation;
244
+ attachment?: TestAttachment;
214
245
  }
215
246
  /**
216
247
  * @deprecated Use `Test` instead. `type: 'custom'` is not used since 2.2
@@ -222,6 +253,7 @@ type Task = Test | Suite | File;
222
253
  */
223
254
  type DoneCallback = (error?: any) => void;
224
255
  type TestFunction<ExtraContext = object> = (context: TestContext & ExtraContext) => Awaitable<any> | void;
256
+ // jest's ExtractEachCallbackArgs
225
257
  type ExtractEachCallbackArgs<T extends ReadonlyArray<any>> = {
226
258
  1: [T[0]]
227
259
  2: [T[0], T[1]]
@@ -257,7 +289,14 @@ interface TestForFunctionReturn<
257
289
  (name: string | Function, options: TestCollectorOptions, fn: (args: Arg, context: Context) => Awaitable<void>): void;
258
290
  }
259
291
  interface TestForFunction<ExtraContext> {
292
+ // test.for([1, 2, 3])
293
+ // test.for([[1, 2], [3, 4, 5]])
260
294
  <T>(cases: ReadonlyArray<T>): TestForFunctionReturn<T, TestContext & ExtraContext>;
295
+ // test.for`
296
+ // a | b
297
+ // {1} | {2}
298
+ // {3} | {4}
299
+ // `
261
300
  (strings: TemplateStringsArray, ...values: any[]): TestForFunctionReturn<any, TestContext & ExtraContext>;
262
301
  }
263
302
  interface SuiteForFunction {
@@ -339,12 +378,23 @@ type TestAPI<ExtraContext = object> = ChainableTestAPI<ExtraContext> & ExtendedA
339
378
  interface FixtureOptions {
340
379
  /**
341
380
  * Whether to automatically set up current fixture, even though it's not being used in tests.
381
+ * @default false
342
382
  */
343
383
  auto?: boolean;
344
384
  /**
345
385
  * Indicated if the injected value from the config should be preferred over the fixture value
346
386
  */
347
387
  injected?: boolean;
388
+ /**
389
+ * When should the fixture be set up.
390
+ * - **test**: fixture will be set up before every test
391
+ * - **worker**: fixture will be set up once per worker
392
+ * - **file**: fixture will be set up once per file
393
+ *
394
+ * **Warning:** The `vmThreads` and `vmForks` pools initiate worker fixtures once per test file.
395
+ * @default 'test'
396
+ */
397
+ scope?: "test" | "worker" | "file";
348
398
  }
349
399
  type Use<T> = (value: T) => Promise<void>;
350
400
  type FixtureFn<
@@ -458,24 +508,36 @@ interface TestContext {
458
508
  /**
459
509
  * An [`AbortSignal`](https://developer.mozilla.org/en-US/docs/Web/API/AbortSignal) that will be aborted if the test times out or
460
510
  * the test run was cancelled.
511
+ * @see {@link https://vitest.dev/guide/test-context#signal}
461
512
  */
462
513
  readonly signal: AbortSignal;
463
514
  /**
464
515
  * Extract hooks on test failed
516
+ * @see {@link https://vitest.dev/guide/test-context#ontestfailed}
465
517
  */
466
518
  readonly onTestFailed: (fn: OnTestFailedHandler, timeout?: number) => void;
467
519
  /**
468
520
  * Extract hooks on test failed
521
+ * @see {@link https://vitest.dev/guide/test-context#ontestfinished}
469
522
  */
470
523
  readonly onTestFinished: (fn: OnTestFinishedHandler, timeout?: number) => void;
471
524
  /**
472
525
  * Mark tests as skipped. All execution after this call will be skipped.
473
526
  * This function throws an error, so make sure you are not catching it accidentally.
527
+ * @see {@link https://vitest.dev/guide/test-context#skip}
474
528
  */
475
529
  readonly skip: {
476
530
  (note?: string): never
477
531
  (condition: boolean, note?: string): void
478
532
  };
533
+ /**
534
+ * Add a test annotation that will be displayed by your reporter.
535
+ * @see {@link https://vitest.dev/guide/test-context#annotate}
536
+ */
537
+ readonly annotate: {
538
+ (message: string, type?: string, attachment?: TestAttachment): Promise<TestAnnotation>
539
+ (message: string, attachment?: TestAttachment): Promise<TestAnnotation>
540
+ };
479
541
  }
480
542
  /**
481
543
  * Context that's always available in the test function.
@@ -493,4 +555,4 @@ type SequenceHooks = "stack" | "list" | "parallel";
493
555
  type SequenceSetupFiles = "list" | "parallel";
494
556
 
495
557
  export { createChainable as c };
496
- export type { AfterAllListener as A, BeforeAllListener as B, ChainableFunction as C, DoneCallback as D, ExtendedContext as E, File as F, TaskPopulated as G, HookCleanupCallback as H, InferFixturesTypes as I, TaskResult as J, TaskResultPack as K, TaskState as L, TestContext as M, TestFunction as N, OnTestFailedHandler as O, TestOptions as P, RunMode as R, Suite as S, Task as T, Use as U, Test as a, AfterEachListener as b, BeforeEachListener as d, TaskHook as e, OnTestFinishedHandler as f, Custom as g, SuiteHooks as h, TaskUpdateEvent as i, TestAPI as j, SuiteAPI as k, SuiteCollector as l, Fixture as m, FixtureFn as n, FixtureOptions as o, Fixtures as p, HookListener as q, RuntimeContext as r, SequenceHooks as s, SequenceSetupFiles as t, SuiteFactory as u, TaskBase as v, TaskContext as w, TaskCustomOptions as x, TaskEventPack as y, TaskMeta as z };
558
+ export type { AfterAllListener as A, BeforeAllListener as B, ChainableFunction as C, DoneCallback as D, ExtendedContext as E, File as F, TaskMeta as G, HookCleanupCallback as H, ImportDuration as I, TaskPopulated as J, TaskResult as K, TaskResultPack as L, TaskState as M, TestAnnotation as N, OnTestFailedHandler as O, TestAnnotationLocation as P, TestAttachment as Q, RunMode as R, Suite as S, Task as T, TestContext as U, TestFunction as V, TestOptions as W, Use as X, Test as a, AfterEachListener as b, BeforeEachListener as d, TaskHook as e, OnTestFinishedHandler as f, Custom as g, SuiteHooks as h, TaskUpdateEvent as i, TestAPI as j, SuiteAPI as k, SuiteCollector as l, Fixture as m, FixtureFn as n, FixtureOptions as o, Fixtures as p, HookListener as q, InferFixturesTypes as r, RuntimeContext as s, SequenceHooks as t, SequenceSetupFiles as u, SuiteFactory as v, TaskBase as w, TaskContext as x, TaskCustomOptions as y, TaskEventPack as z };
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-hzpC0pGR.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-hzpC0pGR.js';
2
+ import { F as File, a as Test, S as Suite, L as TaskResultPack, z as TaskEventPack, N as TestAnnotation, U as TestContext, I as ImportDuration, t as SequenceHooks, u as SequenceSetupFiles } from './tasks.d-CkscK4of.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, r as InferFixturesTypes, O as OnTestFailedHandler, f as OnTestFinishedHandler, R as RunMode, s as RuntimeContext, k as SuiteAPI, l as SuiteCollector, v as SuiteFactory, h as SuiteHooks, T as Task, w as TaskBase, x as TaskContext, y as TaskCustomOptions, e as TaskHook, G as TaskMeta, J as TaskPopulated, K as TaskResult, M as TaskState, i as TaskUpdateEvent, j as TestAPI, P as TestAnnotationLocation, Q as TestAttachment, V as TestFunction, W as TestOptions, X as Use } from './tasks.d-CkscK4of.js';
4
4
  import '@vitest/utils';
5
5
 
6
6
  /**
@@ -64,11 +64,11 @@ interface VitestRunner {
64
64
  /**
65
65
  * Called before running a single test. Doesn't have "result" yet.
66
66
  */
67
- onBeforeRunTask?: (test: Task) => unknown;
67
+ onBeforeRunTask?: (test: Test) => unknown;
68
68
  /**
69
69
  * Called before actually running the test function. Already has "result" with "state" and "startTime".
70
70
  */
71
- onBeforeTryTask?: (test: Task, options: {
71
+ onBeforeTryTask?: (test: Test, options: {
72
72
  retry: number
73
73
  repeats: number
74
74
  }) => unknown;
@@ -79,11 +79,11 @@ interface VitestRunner {
79
79
  /**
80
80
  * Called after result and state are set.
81
81
  */
82
- onAfterRunTask?: (test: Task) => unknown;
82
+ onAfterRunTask?: (test: Test) => unknown;
83
83
  /**
84
84
  * Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws.
85
85
  */
86
- onAfterTryTask?: (test: Task, options: {
86
+ onAfterTryTask?: (test: Test, options: {
87
87
  retry: number
88
88
  repeats: number
89
89
  }) => unknown;
@@ -104,12 +104,16 @@ interface VitestRunner {
104
104
  * If defined, will be called instead of usual Vitest handling. Useful, if you have your custom test function.
105
105
  * "before" and "after" hooks will not be ignored.
106
106
  */
107
- runTask?: (test: Task) => Promise<void>;
107
+ runTask?: (test: Test) => Promise<void>;
108
108
  /**
109
109
  * Called, when a task is updated. The same as "onTaskUpdate" in a reporter, but this is running in the same thread as tests.
110
110
  */
111
111
  onTaskUpdate?: (task: TaskResultPack[], events: TaskEventPack[]) => Promise<void>;
112
112
  /**
113
+ * Called when annotation is added via the `context.annotate` method.
114
+ */
115
+ onTestAnnotate?: (test: Test, annotation: TestAnnotation) => Promise<TestAnnotation>;
116
+ /**
113
117
  * Called before running all tests in collected paths.
114
118
  */
115
119
  onBeforeRunFiles?: (files: File[]) => unknown;
@@ -133,6 +137,10 @@ interface VitestRunner {
133
137
  */
134
138
  injectValue?: (key: string) => unknown;
135
139
  /**
140
+ * Gets the time spent importing each individual non-externalized file that Vitest collected.
141
+ */
142
+ getImportDurations?: () => Record<string, ImportDuration>;
143
+ /**
136
144
  * Publicly available configuration.
137
145
  */
138
146
  config: VitestRunnerConfig;
@@ -140,11 +148,16 @@ interface VitestRunner {
140
148
  * The name of the current pool. Can affect how stack trace is inferred on the server side.
141
149
  */
142
150
  pool?: string;
151
+ /**
152
+ * Return the worker context for fixtures specified with `scope: 'worker'`
153
+ */
154
+ getWorkerContext?: () => Record<string, unknown>;
155
+ onCleanupWorkerContext?: (cleanup: () => unknown) => void;
143
156
  /** @private */
144
157
  _currentTaskStartTime?: number;
145
158
  /** @private */
146
159
  _currentTaskTimeout?: number;
147
160
  }
148
161
 
149
- export { File, SequenceHooks, SequenceSetupFiles, Suite, Task, TaskEventPack, TaskResultPack, Test, TestContext };
162
+ export { File, ImportDuration, SequenceHooks, SequenceSetupFiles, Suite, TaskEventPack, TaskResultPack, Test, TestAnnotation, TestContext };
150
163
  export type { CancelReason, FileSpecification, VitestRunner, VitestRunnerConfig, VitestRunnerConstructor, VitestRunnerImportSource };
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-hzpC0pGR.js';
2
- export { C as ChainableFunction, c as createChainable } from './tasks.d-hzpC0pGR.js';
1
+ import { S as Suite, F as File, T as Task, a as Test } from './tasks.d-CkscK4of.js';
2
+ export { C as ChainableFunction, c as createChainable } from './tasks.d-CkscK4of.js';
3
3
  import { Arrayable } from '@vitest/utils';
4
4
 
5
5
  /**
package/dist/utils.js CHANGED
@@ -1,4 +1,5 @@
1
- export { a as calculateSuiteHash, c as createChainable, b as createFileTask, g as generateFileHash, d as generateHash, e as getFullName, f as getNames, h as getSuites, j as getTasks, k as getTestName, m as getTests, n as hasFailed, o as hasTests, i as interpretTaskModes, q as isAtomTest, r as isTestCase, l as limitConcurrency, p as partitionSuiteChildren, s as someTasksAreOnly } from './chunk-tasks.js';
1
+ export { v as calculateSuiteHash, r as createChainable, w as createFileTask, x as generateFileHash, y as generateHash, D as getFullName, E as getNames, F as getSuites, G as getTasks, H as getTestName, I as getTests, J as hasFailed, K as hasTests, z as interpretTaskModes, L as isAtomTest, M as isTestCase, B as limitConcurrency, C as partitionSuiteChildren, A as someTasksAreOnly } from './chunk-hooks.js';
2
+ import '@vitest/utils';
3
+ import '@vitest/utils/source-map';
2
4
  import '@vitest/utils/error';
3
5
  import 'pathe';
4
- import '@vitest/utils';
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.2",
4
+ "version": "3.2.0",
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.2"
42
+ "@vitest/utils": "3.2.0"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "rimraf dist && rollup -c",
@@ -1,301 +0,0 @@
1
- import { processError } from '@vitest/utils/error';
2
- import { relative } from 'pathe';
3
- import { toArray } from '@vitest/utils';
4
-
5
- function createChainable(keys, fn) {
6
- function create(context) {
7
- const chain = function(...args) {
8
- return fn.apply(context, args);
9
- };
10
- Object.assign(chain, fn);
11
- chain.withContext = () => chain.bind(context);
12
- chain.setContext = (key, value) => {
13
- context[key] = value;
14
- };
15
- chain.mergeContext = (ctx) => {
16
- Object.assign(context, ctx);
17
- };
18
- for (const key of keys) {
19
- Object.defineProperty(chain, key, { get() {
20
- return create({
21
- ...context,
22
- [key]: true
23
- });
24
- } });
25
- }
26
- return chain;
27
- }
28
- const chain = create({});
29
- chain.fn = fn;
30
- return chain;
31
- }
32
-
33
- /**
34
- * If any tasks been marked as `only`, mark all other tasks as `skip`.
35
- */
36
- function interpretTaskModes(file, namePattern, testLocations, onlyMode, parentIsOnly, allowOnly) {
37
- const matchedLocations = [];
38
- const traverseSuite = (suite, parentIsOnly, parentMatchedWithLocation) => {
39
- const suiteIsOnly = parentIsOnly || suite.mode === "only";
40
- suite.tasks.forEach((t) => {
41
- const includeTask = suiteIsOnly || t.mode === "only";
42
- if (onlyMode) {
43
- if (t.type === "suite" && (includeTask || someTasksAreOnly(t))) {
44
- if (t.mode === "only") {
45
- checkAllowOnly(t, allowOnly);
46
- t.mode = "run";
47
- }
48
- } else if (t.mode === "run" && !includeTask) {
49
- t.mode = "skip";
50
- } else if (t.mode === "only") {
51
- checkAllowOnly(t, allowOnly);
52
- t.mode = "run";
53
- }
54
- }
55
- let hasLocationMatch = parentMatchedWithLocation;
56
- if (testLocations !== undefined && testLocations.length !== 0) {
57
- if (t.location && (testLocations === null || testLocations === void 0 ? void 0 : testLocations.includes(t.location.line))) {
58
- t.mode = "run";
59
- matchedLocations.push(t.location.line);
60
- hasLocationMatch = true;
61
- } else if (parentMatchedWithLocation) {
62
- t.mode = "run";
63
- } else if (t.type === "test") {
64
- t.mode = "skip";
65
- }
66
- }
67
- if (t.type === "test") {
68
- if (namePattern && !getTaskFullName(t).match(namePattern)) {
69
- t.mode = "skip";
70
- }
71
- } else if (t.type === "suite") {
72
- if (t.mode === "skip") {
73
- skipAllTasks(t);
74
- } else if (t.mode === "todo") {
75
- todoAllTasks(t);
76
- } else {
77
- traverseSuite(t, includeTask, hasLocationMatch);
78
- }
79
- }
80
- });
81
- if (suite.mode === "run" || suite.mode === "queued") {
82
- if (suite.tasks.length && suite.tasks.every((i) => i.mode !== "run" && i.mode !== "queued")) {
83
- suite.mode = "skip";
84
- }
85
- }
86
- };
87
- traverseSuite(file, parentIsOnly, false);
88
- const nonMatching = testLocations === null || testLocations === void 0 ? void 0 : testLocations.filter((loc) => !matchedLocations.includes(loc));
89
- if (nonMatching && nonMatching.length !== 0) {
90
- const message = nonMatching.length === 1 ? `line ${nonMatching[0]}` : `lines ${nonMatching.join(", ")}`;
91
- if (file.result === undefined) {
92
- file.result = {
93
- state: "fail",
94
- errors: []
95
- };
96
- }
97
- if (file.result.errors === undefined) {
98
- file.result.errors = [];
99
- }
100
- file.result.errors.push(processError(new Error(`No test found in ${file.name} in ${message}`)));
101
- }
102
- }
103
- function getTaskFullName(task) {
104
- return `${task.suite ? `${getTaskFullName(task.suite)} ` : ""}${task.name}`;
105
- }
106
- function someTasksAreOnly(suite) {
107
- return suite.tasks.some((t) => t.mode === "only" || t.type === "suite" && someTasksAreOnly(t));
108
- }
109
- function skipAllTasks(suite) {
110
- suite.tasks.forEach((t) => {
111
- if (t.mode === "run" || t.mode === "queued") {
112
- t.mode = "skip";
113
- if (t.type === "suite") {
114
- skipAllTasks(t);
115
- }
116
- }
117
- });
118
- }
119
- function todoAllTasks(suite) {
120
- suite.tasks.forEach((t) => {
121
- if (t.mode === "run" || t.mode === "queued") {
122
- t.mode = "todo";
123
- if (t.type === "suite") {
124
- todoAllTasks(t);
125
- }
126
- }
127
- });
128
- }
129
- function checkAllowOnly(task, allowOnly) {
130
- if (allowOnly) {
131
- return;
132
- }
133
- const error = processError(new Error("[Vitest] Unexpected .only modifier. Remove it or pass --allowOnly argument to bypass this error"));
134
- task.result = {
135
- state: "fail",
136
- errors: [error]
137
- };
138
- }
139
- function generateHash(str) {
140
- let hash = 0;
141
- if (str.length === 0) {
142
- return `${hash}`;
143
- }
144
- for (let i = 0; i < str.length; i++) {
145
- const char = str.charCodeAt(i);
146
- hash = (hash << 5) - hash + char;
147
- hash = hash & hash;
148
- }
149
- return `${hash}`;
150
- }
151
- function calculateSuiteHash(parent) {
152
- parent.tasks.forEach((t, idx) => {
153
- t.id = `${parent.id}_${idx}`;
154
- if (t.type === "suite") {
155
- calculateSuiteHash(t);
156
- }
157
- });
158
- }
159
- function createFileTask(filepath, root, projectName, pool) {
160
- const path = relative(root, filepath);
161
- const file = {
162
- id: generateFileHash(path, projectName),
163
- name: path,
164
- type: "suite",
165
- mode: "queued",
166
- filepath,
167
- tasks: [],
168
- meta: Object.create(null),
169
- projectName,
170
- file: undefined,
171
- pool
172
- };
173
- file.file = file;
174
- return file;
175
- }
176
- /**
177
- * Generate a unique ID for a file based on its path and project name
178
- * @param file File relative to the root of the project to keep ID the same between different machines
179
- * @param projectName The name of the test project
180
- */
181
- function generateFileHash(file, projectName) {
182
- return generateHash(`${file}${projectName || ""}`);
183
- }
184
-
185
- /**
186
- * Return a function for running multiple async operations with limited concurrency.
187
- */
188
- function limitConcurrency(concurrency = Infinity) {
189
- let count = 0;
190
- let head;
191
- let tail;
192
- const finish = () => {
193
- count--;
194
- if (head) {
195
- head[0]();
196
- head = head[1];
197
- tail = head && tail;
198
- }
199
- };
200
- return (func, ...args) => {
201
- return new Promise((resolve) => {
202
- if (count++ < concurrency) {
203
- resolve();
204
- } else if (tail) {
205
- tail = tail[1] = [resolve];
206
- } else {
207
- head = tail = [resolve];
208
- }
209
- }).then(() => {
210
- return func(...args);
211
- }).finally(finish);
212
- };
213
- }
214
-
215
- /**
216
- * Partition in tasks groups by consecutive concurrent
217
- */
218
- function partitionSuiteChildren(suite) {
219
- let tasksGroup = [];
220
- const tasksGroups = [];
221
- for (const c of suite.tasks) {
222
- if (tasksGroup.length === 0 || c.concurrent === tasksGroup[0].concurrent) {
223
- tasksGroup.push(c);
224
- } else {
225
- tasksGroups.push(tasksGroup);
226
- tasksGroup = [c];
227
- }
228
- }
229
- if (tasksGroup.length > 0) {
230
- tasksGroups.push(tasksGroup);
231
- }
232
- return tasksGroups;
233
- }
234
-
235
- /**
236
- * @deprecated use `isTestCase` instead
237
- */
238
- function isAtomTest(s) {
239
- return isTestCase(s);
240
- }
241
- function isTestCase(s) {
242
- return s.type === "test";
243
- }
244
- function getTests(suite) {
245
- const tests = [];
246
- const arraySuites = toArray(suite);
247
- for (const s of arraySuites) {
248
- if (isTestCase(s)) {
249
- tests.push(s);
250
- } else {
251
- for (const task of s.tasks) {
252
- if (isTestCase(task)) {
253
- tests.push(task);
254
- } else {
255
- const taskTests = getTests(task);
256
- for (const test of taskTests) {
257
- tests.push(test);
258
- }
259
- }
260
- }
261
- }
262
- }
263
- return tests;
264
- }
265
- function getTasks(tasks = []) {
266
- return toArray(tasks).flatMap((s) => isTestCase(s) ? [s] : [s, ...getTasks(s.tasks)]);
267
- }
268
- function getSuites(suite) {
269
- return toArray(suite).flatMap((s) => s.type === "suite" ? [s, ...getSuites(s.tasks)] : []);
270
- }
271
- function hasTests(suite) {
272
- return toArray(suite).some((s) => s.tasks.some((c) => isTestCase(c) || hasTests(c)));
273
- }
274
- function hasFailed(suite) {
275
- return toArray(suite).some((s) => {
276
- var _s$result;
277
- return ((_s$result = s.result) === null || _s$result === void 0 ? void 0 : _s$result.state) === "fail" || s.type === "suite" && hasFailed(s.tasks);
278
- });
279
- }
280
- function getNames(task) {
281
- const names = [task.name];
282
- let current = task;
283
- while (current === null || current === void 0 ? void 0 : current.suite) {
284
- current = current.suite;
285
- if (current === null || current === void 0 ? void 0 : current.name) {
286
- names.unshift(current.name);
287
- }
288
- }
289
- if (current !== task.file) {
290
- names.unshift(task.file.name);
291
- }
292
- return names;
293
- }
294
- function getFullName(task, separator = " > ") {
295
- return getNames(task).join(separator);
296
- }
297
- function getTestName(task, separator = " > ") {
298
- return getNames(task).slice(1).join(separator);
299
- }
300
-
301
- export { calculateSuiteHash as a, createFileTask as b, createChainable as c, generateHash as d, getFullName as e, getNames as f, generateFileHash as g, getSuites as h, interpretTaskModes as i, getTasks as j, getTestName as k, limitConcurrency as l, getTests as m, hasFailed as n, hasTests as o, partitionSuiteChildren as p, isAtomTest as q, isTestCase as r, someTasksAreOnly as s };