@vitest/runner 4.1.0-beta.1 → 4.1.0-beta.2

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.
@@ -34,7 +34,7 @@ function createChainable(keys, fn) {
34
34
  /**
35
35
  * If any tasks been marked as `only`, mark all other tasks as `skip`.
36
36
  */
37
- function interpretTaskModes(file, namePattern, testLocations, testIds, onlyMode, parentIsOnly, allowOnly) {
37
+ function interpretTaskModes(file, namePattern, testLocations, testIds, testTagsFilter, onlyMode, parentIsOnly, allowOnly) {
38
38
  const matchedLocations = [];
39
39
  const traverseSuite = (suite, parentIsOnly, parentMatchedWithLocation) => {
40
40
  const suiteIsOnly = parentIsOnly || suite.mode === "only";
@@ -80,6 +80,9 @@ function interpretTaskModes(file, namePattern, testLocations, testIds, onlyMode,
80
80
  if (testIds && !testIds.includes(t.id)) {
81
81
  t.mode = "skip";
82
82
  }
83
+ if (testTagsFilter && !testTagsFilter(t.tags || [])) {
84
+ t.mode = "skip";
85
+ }
83
86
  } else if (t.type === "suite") {
84
87
  if (t.mode === "skip") {
85
88
  skipAllTasks(t);
@@ -277,6 +280,241 @@ function partitionSuiteChildren(suite) {
277
280
  return tasksGroups;
278
281
  }
279
282
 
283
+ function validateTags(config, tags) {
284
+ if (!config.strictTags) {
285
+ return;
286
+ }
287
+ const availableTags = new Set(config.tags.map((tag) => tag.name));
288
+ for (const tag of tags) {
289
+ if (!availableTags.has(tag)) {
290
+ throw createNoTagsError(config.tags, tag);
291
+ }
292
+ }
293
+ }
294
+ function createNoTagsError(availableTags, tag, prefix = "tag") {
295
+ if (!availableTags.length) {
296
+ throw new Error(`The Vitest config does't define any "tags", cannot apply "${tag}" ${prefix} for this test. See: https://vitest.dev/guide/test-tags`);
297
+ }
298
+ throw new Error(`The ${prefix} "${tag}" is not defined in the configuration. Available tags are:\n${availableTags.map((t) => `- ${t.name}${t.description ? `: ${t.description}` : ""}`).join("\n")}`);
299
+ }
300
+ function createTagsFilter(tagsExpr, availableTags) {
301
+ const matchers = tagsExpr.map((expr) => parseTagsExpression(expr, availableTags));
302
+ return (testTags) => {
303
+ return matchers.every((matcher) => matcher(testTags));
304
+ };
305
+ }
306
+ function parseTagsExpression(expr, availableTags) {
307
+ const tokens = tokenize(expr);
308
+ const stream = new TokenStream(tokens, expr);
309
+ const ast = parseOrExpression(stream, availableTags);
310
+ if (stream.peek().type !== "EOF") {
311
+ throw new Error(`Invalid tags expression: unexpected "${formatToken(stream.peek())}" in "${expr}"`);
312
+ }
313
+ return (tags) => evaluateNode(ast, tags);
314
+ }
315
+ function formatToken(token) {
316
+ switch (token.type) {
317
+ case "TAG": return token.value;
318
+ default: return formatTokenType(token.type);
319
+ }
320
+ }
321
+ function tokenize(expr) {
322
+ const tokens = [];
323
+ let i = 0;
324
+ while (i < expr.length) {
325
+ if (expr[i] === " " || expr[i] === " ") {
326
+ i++;
327
+ continue;
328
+ }
329
+ if (expr[i] === "(") {
330
+ tokens.push({ type: "LPAREN" });
331
+ i++;
332
+ continue;
333
+ }
334
+ if (expr[i] === ")") {
335
+ tokens.push({ type: "RPAREN" });
336
+ i++;
337
+ continue;
338
+ }
339
+ if (expr[i] === "!") {
340
+ tokens.push({ type: "NOT" });
341
+ i++;
342
+ continue;
343
+ }
344
+ if (expr.slice(i, i + 2) === "&&") {
345
+ tokens.push({ type: "AND" });
346
+ i += 2;
347
+ continue;
348
+ }
349
+ if (expr.slice(i, i + 2) === "||") {
350
+ tokens.push({ type: "OR" });
351
+ i += 2;
352
+ continue;
353
+ }
354
+ if (/^and(?:\s|\)|$)/i.test(expr.slice(i))) {
355
+ tokens.push({ type: "AND" });
356
+ i += 3;
357
+ continue;
358
+ }
359
+ if (/^or(?:\s|\)|$)/i.test(expr.slice(i))) {
360
+ tokens.push({ type: "OR" });
361
+ i += 2;
362
+ continue;
363
+ }
364
+ if (/^not\s/i.test(expr.slice(i))) {
365
+ tokens.push({ type: "NOT" });
366
+ i += 3;
367
+ continue;
368
+ }
369
+ let tag = "";
370
+ while (i < expr.length && expr[i] !== " " && expr[i] !== " " && expr[i] !== "(" && expr[i] !== ")" && expr[i] !== "!" && expr[i] !== "&" && expr[i] !== "|") {
371
+ const remaining = expr.slice(i);
372
+ // Only treat and/or/not as operators if we're at the start of a tag (after whitespace)
373
+ // This allows tags like "demand", "editor", "cannot" to work correctly
374
+ if (tag === "" && (/^and(?:\s|\)|$)/i.test(remaining) || /^or(?:\s|\)|$)/i.test(remaining) || /^not\s/i.test(remaining))) {
375
+ break;
376
+ }
377
+ tag += expr[i];
378
+ i++;
379
+ }
380
+ if (tag) {
381
+ tokens.push({
382
+ type: "TAG",
383
+ value: tag
384
+ });
385
+ }
386
+ }
387
+ tokens.push({ type: "EOF" });
388
+ return tokens;
389
+ }
390
+ class TokenStream {
391
+ pos = 0;
392
+ constructor(tokens, expr) {
393
+ this.tokens = tokens;
394
+ this.expr = expr;
395
+ }
396
+ peek() {
397
+ return this.tokens[this.pos];
398
+ }
399
+ next() {
400
+ return this.tokens[this.pos++];
401
+ }
402
+ expect(type) {
403
+ const token = this.next();
404
+ if (token.type !== type) {
405
+ if (type === "RPAREN" && token.type === "EOF") {
406
+ throw new Error(`Invalid tags expression: missing closing ")" in "${this.expr}"`);
407
+ }
408
+ throw new Error(`Invalid tags expression: expected "${formatTokenType(type)}" but got "${formatToken(token)}" in "${this.expr}"`);
409
+ }
410
+ return token;
411
+ }
412
+ unexpectedToken() {
413
+ const token = this.peek();
414
+ if (token.type === "EOF") {
415
+ throw new Error(`Invalid tags expression: unexpected end of expression in "${this.expr}"`);
416
+ }
417
+ throw new Error(`Invalid tags expression: unexpected "${formatToken(token)}" in "${this.expr}"`);
418
+ }
419
+ }
420
+ function formatTokenType(type) {
421
+ switch (type) {
422
+ case "TAG": return "tag";
423
+ case "AND": return "and";
424
+ case "OR": return "or";
425
+ case "NOT": return "not";
426
+ case "LPAREN": return "(";
427
+ case "RPAREN": return ")";
428
+ case "EOF": return "end of expression";
429
+ }
430
+ }
431
+ function parseOrExpression(stream, availableTags) {
432
+ let left = parseAndExpression(stream, availableTags);
433
+ while (stream.peek().type === "OR") {
434
+ stream.next();
435
+ const right = parseAndExpression(stream, availableTags);
436
+ left = {
437
+ type: "or",
438
+ left,
439
+ right
440
+ };
441
+ }
442
+ return left;
443
+ }
444
+ function parseAndExpression(stream, availableTags) {
445
+ let left = parseUnaryExpression(stream, availableTags);
446
+ while (stream.peek().type === "AND") {
447
+ stream.next();
448
+ const right = parseUnaryExpression(stream, availableTags);
449
+ left = {
450
+ type: "and",
451
+ left,
452
+ right
453
+ };
454
+ }
455
+ return left;
456
+ }
457
+ function parseUnaryExpression(stream, availableTags) {
458
+ if (stream.peek().type === "NOT") {
459
+ stream.next();
460
+ const operand = parseUnaryExpression(stream, availableTags);
461
+ return {
462
+ type: "not",
463
+ operand
464
+ };
465
+ }
466
+ return parsePrimaryExpression(stream, availableTags);
467
+ }
468
+ function parsePrimaryExpression(stream, availableTags) {
469
+ const token = stream.peek();
470
+ if (token.type === "LPAREN") {
471
+ stream.next();
472
+ const expr = parseOrExpression(stream, availableTags);
473
+ stream.expect("RPAREN");
474
+ return expr;
475
+ }
476
+ if (token.type === "TAG") {
477
+ stream.next();
478
+ const tagValue = token.value;
479
+ const pattern = resolveTagPattern(tagValue, availableTags);
480
+ return {
481
+ type: "tag",
482
+ value: tagValue,
483
+ pattern
484
+ };
485
+ }
486
+ stream.unexpectedToken();
487
+ }
488
+ function createWildcardRegex(pattern) {
489
+ return new RegExp(`^${pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*")}$`);
490
+ }
491
+ function resolveTagPattern(tagPattern, availableTags) {
492
+ if (tagPattern.includes("*")) {
493
+ const regex = createWildcardRegex(tagPattern);
494
+ const hasMatch = availableTags.some((tag) => regex.test(tag.name));
495
+ if (!hasMatch) {
496
+ throw createNoTagsError(availableTags, tagPattern, "tag pattern");
497
+ }
498
+ return regex;
499
+ }
500
+ if (!availableTags.length || !availableTags.some((tag) => tag.name === tagPattern)) {
501
+ throw createNoTagsError(availableTags, tagPattern, "tag pattern");
502
+ }
503
+ return null;
504
+ }
505
+ function evaluateNode(node, tags) {
506
+ switch (node.type) {
507
+ case "tag":
508
+ if (node.pattern) {
509
+ return tags.some((tag) => node.pattern.test(tag));
510
+ }
511
+ return tags.includes(node.value);
512
+ case "not": return !evaluateNode(node.operand, tags);
513
+ case "and": return evaluateNode(node.left, tags) && evaluateNode(node.right, tags);
514
+ case "or": return evaluateNode(node.left, tags) || evaluateNode(node.right, tags);
515
+ }
516
+ }
517
+
280
518
  function isTestCase(s) {
281
519
  return s.type === "test";
282
520
  }
@@ -340,4 +578,4 @@ function createTaskName(names, separator = " > ") {
340
578
  return names.filter((name) => name !== undefined).join(separator);
341
579
  }
342
580
 
343
- export { calculateSuiteHash as a, createFileTask as b, createChainable as c, generateHash as d, createTaskName as e, findTestFileStackTrace as f, generateFileHash as g, getFullName as h, interpretTaskModes as i, getNames as j, getSuites as k, limitConcurrency as l, getTasks as m, getTestName as n, getTests as o, partitionSuiteChildren as p, hasFailed as q, hasTests as r, someTasksAreOnly as s, isTestCase as t };
581
+ export { calculateSuiteHash as a, createFileTask as b, createChainable as c, generateHash as d, createTagsFilter as e, findTestFileStackTrace as f, generateFileHash as g, createTaskName as h, interpretTaskModes as i, getFullName as j, getNames as k, limitConcurrency as l, getSuites as m, getTasks as n, getTestName as o, partitionSuiteChildren as p, getTests as q, hasFailed as r, someTasksAreOnly as s, hasTests as t, isTestCase as u, validateTags as v, createNoTagsError as w };
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
- import { b as TestArtifact, a as Test, S as Suite, d as SuiteHooks, F as File, e as TaskUpdateEvent, T as Task, f as TestAPI, g as SuiteAPI, h as SuiteCollector } from './tasks.d-hZ73xajr.js';
2
- export { A as AfterAllListener, n as AfterEachListener, B as BeforeAllListener, p as BeforeEachListener, q as Fixture, r as FixtureFn, s as FixtureOptions, t as Fixtures, I as ImportDuration, u as InferFixturesTypes, O as OnTestFailedHandler, v as OnTestFinishedHandler, R as Retry, w as RunMode, x as RuntimeContext, y as SequenceHooks, z as SequenceSetupFiles, D as SerializableRetry, E as SuiteFactory, G as TaskBase, H as TaskCustomOptions, J as TaskEventPack, K as TaskHook, L as TaskMeta, M as TaskPopulated, N as TaskResult, P as TaskResultPack, Q as TaskState, U as TestAnnotation, V as TestAnnotationArtifact, W as TestAnnotationLocation, X as TestArtifactBase, Y as TestArtifactLocation, Z as TestArtifactRegistry, _ as TestAttachment, $ as TestContext, a0 as TestFunction, a1 as TestOptions, a2 as Use, a3 as VisualRegressionArtifact, i as afterAll, j as afterEach, k as beforeAll, l as beforeEach, o as onTestFailed, m as onTestFinished } from './tasks.d-hZ73xajr.js';
1
+ import { T as TestArtifact, a as Test, S as Suite, b as SuiteHooks, F as FileSpecification, V as VitestRunner, c as File, d as TaskUpdateEvent, e as Task, f as TestAPI, g as SuiteAPI, h as SuiteCollector } from './tasks.d-CLPU8HE4.js';
2
+ export { A as AfterAllListener, s as AfterEachListener, B as BeforeAllListener, t as BeforeEachListener, C as CancelReason, u as Fixture, v as FixtureFn, w as FixtureOptions, x as Fixtures, I as ImportDuration, y as InferFixturesTypes, O as OnTestFailedHandler, z as OnTestFinishedHandler, R as Retry, D as RunMode, E as RuntimeContext, G as SequenceHooks, H as SequenceSetupFiles, J as SerializableRetry, K as SuiteFactory, L as TaskBase, M as TaskCustomOptions, N as TaskEventPack, P as TaskHook, Q as TaskMeta, U as TaskPopulated, W as TaskResult, X as TaskResultPack, Y as TaskState, Z as TestAnnotation, _ as TestAnnotationArtifact, $ as TestAnnotationLocation, a0 as TestArtifactBase, a1 as TestArtifactLocation, a2 as TestArtifactRegistry, a3 as TestAttachment, a4 as TestContext, a5 as TestFunction, a6 as TestOptions, n as TestTagDefinition, a7 as TestTags, a8 as Use, a9 as VisualRegressionArtifact, p as VitestRunnerConfig, q as VitestRunnerConstructor, r as VitestRunnerImportSource, i as afterAll, j as afterEach, k as beforeAll, l as beforeEach, o as onTestFailed, m as onTestFinished } from './tasks.d-CLPU8HE4.js';
3
3
  import { Awaitable } from '@vitest/utils';
4
- import { FileSpecification, VitestRunner } from './types.js';
5
- export { CancelReason, VitestRunnerConfig, VitestRunnerConstructor, VitestRunnerImportSource } from './types.js';
6
4
  import '@vitest/utils/diff';
7
5
 
8
6
  /**
package/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { processError } from '@vitest/utils/error';
2
- import { isObject, createDefer, assertTypes, toArray, isNegativeNaN, objectAttr, shuffle } from '@vitest/utils/helpers';
2
+ import { isObject, filterOutComments, createDefer, assertTypes, toArray, isNegativeNaN, unique, objectAttr, shuffle } from '@vitest/utils/helpers';
3
3
  import { getSafeTimers } from '@vitest/utils/timers';
4
4
  import { format, formatRegExp, objDisplay } from '@vitest/utils/display';
5
- import { c as createChainable, e as createTaskName, f as findTestFileStackTrace, b as createFileTask, a as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, l as limitConcurrency, p as partitionSuiteChildren, r as hasTests, q as hasFailed } from './chunk-tasks.js';
5
+ import { c as createChainable, v as validateTags, h as createTaskName, w as createNoTagsError, f as findTestFileStackTrace, e as createTagsFilter, b as createFileTask, a as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, l as limitConcurrency, p as partitionSuiteChildren, t as hasTests, r as hasFailed } from './chunk-tasks.js';
6
6
  import '@vitest/utils/source-map';
7
7
  import 'pathe';
8
8
 
@@ -311,31 +311,6 @@ function getUsedProps(fn) {
311
311
  }
312
312
  return props;
313
313
  }
314
- function filterOutComments(s) {
315
- const result = [];
316
- let commentState = "none";
317
- for (let i = 0; i < s.length; ++i) {
318
- if (commentState === "singleline") {
319
- if (s[i] === "\n") {
320
- commentState = "none";
321
- }
322
- } else if (commentState === "multiline") {
323
- if (s[i - 1] === "*" && s[i] === "/") {
324
- commentState = "none";
325
- }
326
- } else if (commentState === "none") {
327
- if (s[i] === "/" && s[i + 1] === "/") {
328
- commentState = "singleline";
329
- } else if (s[i] === "/" && s[i + 1] === "*") {
330
- commentState = "multiline";
331
- i += 2;
332
- } else {
333
- result.push(s[i]);
334
- }
335
- }
336
- }
337
- return result.join("");
338
- }
339
314
  function splitByComma(s) {
340
315
  const result = [];
341
316
  const stack = [];
@@ -693,18 +668,22 @@ function getRunner() {
693
668
  }
694
669
  function createDefaultSuite(runner) {
695
670
  const config = runner.config.sequence;
696
- const collector = suite("", { concurrent: config.concurrent }, () => {});
671
+ const options = {};
672
+ if (config.concurrent != null) {
673
+ options.concurrent = config.concurrent;
674
+ }
675
+ const collector = suite("", options, () => {});
697
676
  // no parent suite for top-level tests
698
677
  delete collector.suite;
699
678
  return collector;
700
679
  }
701
680
  function clearCollectorContext(file, currentRunner) {
681
+ currentTestFilepath = file.filepath;
682
+ runner = currentRunner;
702
683
  if (!defaultSuite) {
703
684
  defaultSuite = createDefaultSuite(currentRunner);
704
685
  }
705
686
  defaultSuite.file = file;
706
- runner = currentRunner;
707
- currentTestFilepath = file.filepath;
708
687
  collectorContext.tasks.length = 0;
709
688
  defaultSuite.clear();
710
689
  collectorContext.currentSuite = defaultSuite;
@@ -722,6 +701,7 @@ function createSuiteHooks() {
722
701
  afterEach: []
723
702
  };
724
703
  }
704
+ const POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
725
705
  function parseArguments(optionsOrFn, timeoutOrTest) {
726
706
  if (timeoutOrTest != null && typeof timeoutOrTest === "object") {
727
707
  throw new TypeError(`Signature "test(name, fn, { ... })" was deprecated in Vitest 3 and removed in Vitest 4. Please, provide options as a second argument instead.`);
@@ -753,27 +733,47 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
753
733
  let suite;
754
734
  initSuite(true);
755
735
  const task = function(name = "", options = {}) {
756
- var _collectorContext$cur, _collectorContext$cur2, _collectorContext$cur3;
757
- const timeout = (options === null || options === void 0 ? void 0 : options.timeout) ?? runner.config.testTimeout;
736
+ var _collectorContext$cur, _collectorContext$cur2, _collectorContext$cur3, _collectorContext$cur4;
758
737
  const currentSuite = (_collectorContext$cur = collectorContext.currentSuite) === null || _collectorContext$cur === void 0 ? void 0 : _collectorContext$cur.suite;
738
+ const parentTask = currentSuite ?? ((_collectorContext$cur2 = collectorContext.currentSuite) === null || _collectorContext$cur2 === void 0 ? void 0 : _collectorContext$cur2.file);
739
+ const parentTags = (parentTask === null || parentTask === void 0 ? void 0 : parentTask.tags) || [];
740
+ const testTags = unique([...parentTags, ...toArray(options.tags)]);
741
+ const tagsOptions = testTags.map((tag) => {
742
+ var _runner$config$tags;
743
+ const tagDefinition = (_runner$config$tags = runner.config.tags) === null || _runner$config$tags === void 0 ? void 0 : _runner$config$tags.find((t) => t.name === tag);
744
+ if (!tagDefinition && runner.config.strictTags) {
745
+ throw createNoTagsError(runner.config.tags, tag);
746
+ }
747
+ return tagDefinition;
748
+ }).filter((r) => r != null).sort((tag1, tag2) => (tag2.priority ?? POSITIVE_INFINITY) - (tag1.priority ?? POSITIVE_INFINITY)).reduce((acc, tag) => {
749
+ const { name, description, priority, ...options } = tag;
750
+ Object.assign(acc, options);
751
+ return acc;
752
+ }, {});
753
+ options = {
754
+ ...tagsOptions,
755
+ ...options
756
+ };
757
+ const timeout = options.timeout ?? runner.config.testTimeout;
759
758
  const task = {
760
759
  id: "",
761
760
  name,
762
- fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur2 = collectorContext.currentSuite) === null || _collectorContext$cur2 === void 0 || (_collectorContext$cur2 = _collectorContext$cur2.file) === null || _collectorContext$cur2 === void 0 ? void 0 : _collectorContext$cur2.fullName), name]),
761
+ fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur3 = collectorContext.currentSuite) === null || _collectorContext$cur3 === void 0 || (_collectorContext$cur3 = _collectorContext$cur3.file) === null || _collectorContext$cur3 === void 0 ? void 0 : _collectorContext$cur3.fullName), name]),
763
762
  fullTestName: createTaskName([currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullTestName, name]),
764
763
  suite: currentSuite,
765
764
  each: options.each,
766
765
  fails: options.fails,
767
766
  context: undefined,
768
767
  type: "test",
769
- file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur3 = collectorContext.currentSuite) === null || _collectorContext$cur3 === void 0 ? void 0 : _collectorContext$cur3.file),
768
+ file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur4 = collectorContext.currentSuite) === null || _collectorContext$cur4 === void 0 ? void 0 : _collectorContext$cur4.file),
770
769
  timeout,
771
770
  retry: options.retry ?? runner.config.retry,
772
771
  repeats: options.repeats,
773
772
  mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run",
774
773
  meta: options.meta ?? Object.create(null),
775
774
  annotations: [],
776
- artifacts: []
775
+ artifacts: [],
776
+ tags: testTags
777
777
  };
778
778
  const handler = options.handler;
779
779
  if (task.mode === "run" && !handler) {
@@ -818,8 +818,14 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
818
818
  options = Object.assign({}, suiteOptions, options);
819
819
  }
820
820
  // inherit concurrent / sequential from suite
821
- options.concurrent = this.concurrent || !this.sequential && (options === null || options === void 0 ? void 0 : options.concurrent);
822
- options.sequential = this.sequential || !this.concurrent && (options === null || options === void 0 ? void 0 : options.sequential);
821
+ const concurrent = this.concurrent ?? (!this.sequential && (options === null || options === void 0 ? void 0 : options.concurrent));
822
+ if (options.concurrent != null && concurrent != null) {
823
+ options.concurrent = concurrent;
824
+ }
825
+ const sequential = this.sequential ?? (!this.concurrent && (options === null || options === void 0 ? void 0 : options.sequential));
826
+ if (options.sequential != null && sequential != null) {
827
+ options.sequential = sequential;
828
+ }
823
829
  const test = task(formatName(name), {
824
830
  ...this,
825
831
  ...options,
@@ -854,25 +860,29 @@ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions
854
860
  getHooks(suite)[name].push(...fn);
855
861
  }
856
862
  function initSuite(includeLocation) {
857
- var _collectorContext$cur4, _collectorContext$cur5, _collectorContext$cur6;
863
+ var _collectorContext$cur5, _collectorContext$cur6, _collectorContext$cur7, _collectorContext$cur8;
858
864
  if (typeof suiteOptions === "number") {
859
865
  suiteOptions = { timeout: suiteOptions };
860
866
  }
861
- const currentSuite = (_collectorContext$cur4 = collectorContext.currentSuite) === null || _collectorContext$cur4 === void 0 ? void 0 : _collectorContext$cur4.suite;
867
+ const currentSuite = (_collectorContext$cur5 = collectorContext.currentSuite) === null || _collectorContext$cur5 === void 0 ? void 0 : _collectorContext$cur5.suite;
868
+ const parentTask = currentSuite ?? ((_collectorContext$cur6 = collectorContext.currentSuite) === null || _collectorContext$cur6 === void 0 ? void 0 : _collectorContext$cur6.file);
869
+ const suiteTags = toArray(suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.tags);
870
+ validateTags(runner.config, suiteTags);
862
871
  suite = {
863
872
  id: "",
864
873
  type: "suite",
865
874
  name,
866
- fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur5 = collectorContext.currentSuite) === null || _collectorContext$cur5 === void 0 || (_collectorContext$cur5 = _collectorContext$cur5.file) === null || _collectorContext$cur5 === void 0 ? void 0 : _collectorContext$cur5.fullName), name]),
875
+ fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur7 = collectorContext.currentSuite) === null || _collectorContext$cur7 === void 0 || (_collectorContext$cur7 = _collectorContext$cur7.file) === null || _collectorContext$cur7 === void 0 ? void 0 : _collectorContext$cur7.fullName), name]),
867
876
  fullTestName: createTaskName([currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullTestName, name]),
868
877
  suite: currentSuite,
869
878
  mode,
870
879
  each,
871
- file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur6 = collectorContext.currentSuite) === null || _collectorContext$cur6 === void 0 ? void 0 : _collectorContext$cur6.file),
880
+ file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur8 = collectorContext.currentSuite) === null || _collectorContext$cur8 === void 0 ? void 0 : _collectorContext$cur8.file),
872
881
  shuffle: suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.shuffle,
873
882
  tasks: [],
874
883
  meta: Object.create(null),
875
- concurrent: suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.concurrent
884
+ concurrent: suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.concurrent,
885
+ tags: unique([...(parentTask === null || parentTask === void 0 ? void 0 : parentTask.tags) || [], ...suiteTags])
876
886
  };
877
887
  if (runner && includeLocation && runner.config.includeTaskLocation) {
878
888
  const limit = Error.stackTraceLimit;
@@ -930,25 +940,33 @@ function createSuite() {
930
940
  if (getCurrentTest()) {
931
941
  throw new Error("Calling the suite function inside test function is not allowed. It can be only called at the top level or inside another suite function.");
932
942
  }
933
- let mode = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run";
934
943
  const currentSuite = collectorContext.currentSuite || defaultSuite;
935
944
  let { options, handler: factory } = parseArguments(factoryOrOptions, optionsOrFactory);
936
- if (mode === "run" && !factory) {
937
- mode = "todo";
938
- }
939
945
  const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false;
940
946
  const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false;
941
947
  // inherit options from current suite
942
948
  options = {
943
949
  ...currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.options,
944
- ...options,
945
- 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)
950
+ ...options
946
951
  };
952
+ const 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);
953
+ if (shuffle != null) {
954
+ options.shuffle = shuffle;
955
+ }
956
+ let mode = this.only ?? options.only ? "only" : this.skip ?? options.skip ? "skip" : this.todo ?? options.todo ? "todo" : "run";
957
+ // passed as test(name), assume it's a "todo"
958
+ if (mode === "run" && !factory) {
959
+ mode = "todo";
960
+ }
947
961
  // inherit concurrent / sequential from suite
948
962
  const isConcurrent = isConcurrentSpecified || options.concurrent && !isSequentialSpecified;
949
963
  const isSequential = isSequentialSpecified || options.sequential && !isConcurrentSpecified;
950
- options.concurrent = isConcurrent && !isSequential;
951
- options.sequential = isSequential && !isConcurrent;
964
+ if (isConcurrent != null) {
965
+ options.concurrent = isConcurrent && !isSequential;
966
+ }
967
+ if (isSequential != null) {
968
+ options.sequential = isSequential && !isConcurrent;
969
+ }
952
970
  return createSuiteCollector(formatName(name), factory, mode, this.each, options, currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fixtures());
953
971
  }
954
972
  suiteFn.each = function(cases, ...args) {
@@ -1360,20 +1378,24 @@ async function collectTests(specs, runner) {
1360
1378
  const files = [];
1361
1379
  const config = runner.config;
1362
1380
  const $ = runner.trace;
1381
+ let defaultTagsFilter;
1363
1382
  for (const spec of specs) {
1364
1383
  const filepath = typeof spec === "string" ? spec : spec.filepath;
1365
1384
  await $("collect_spec", { "code.file.path": filepath }, async () => {
1366
- var _runner$onCollectStar;
1367
1385
  const testLocations = typeof spec === "string" ? undefined : spec.testLocations;
1368
1386
  const testNamePattern = typeof spec === "string" ? undefined : spec.testNamePattern;
1369
1387
  const testIds = typeof spec === "string" ? undefined : spec.testIds;
1388
+ const testTagsFilter = typeof spec === "object" && spec.testTagsFilter ? createTagsFilter(spec.testTagsFilter, config.tags) : undefined;
1389
+ const fileTags = typeof spec === "string" ? [] : spec.fileTags || [];
1370
1390
  const file = createFileTask(filepath, config.root, config.name, runner.pool, runner.viteEnvironment);
1371
1391
  setFileContext(file, Object.create(null));
1392
+ file.tags = fileTags;
1372
1393
  file.shuffle = config.sequence.shuffle;
1373
- (_runner$onCollectStar = runner.onCollectStart) === null || _runner$onCollectStar === void 0 ? void 0 : _runner$onCollectStar.call(runner, file);
1374
- clearCollectorContext(file, runner);
1375
1394
  try {
1376
- var _runner$getImportDura;
1395
+ var _runner$onCollectStar, _runner$getImportDura;
1396
+ validateTags(runner.config, fileTags);
1397
+ (_runner$onCollectStar = runner.onCollectStart) === null || _runner$onCollectStar === void 0 ? void 0 : _runner$onCollectStar.call(runner, file);
1398
+ clearCollectorContext(file, runner);
1377
1399
  const setupFiles = toArray(config.setupFiles);
1378
1400
  if (setupFiles.length) {
1379
1401
  const setupStart = now$1();
@@ -1422,7 +1444,10 @@ async function collectTests(specs, runner) {
1422
1444
  }
1423
1445
  calculateSuiteHash(file);
1424
1446
  const hasOnlyTasks = someTasksAreOnly(file);
1425
- interpretTaskModes(file, testNamePattern ?? config.testNamePattern, testLocations, testIds, hasOnlyTasks, false, config.allowOnly);
1447
+ if (!testTagsFilter && !defaultTagsFilter && config.tagsFilter) {
1448
+ defaultTagsFilter = createTagsFilter(config.tagsFilter, config.tags);
1449
+ }
1450
+ interpretTaskModes(file, testNamePattern ?? config.testNamePattern, testLocations, testIds, testTagsFilter ?? defaultTagsFilter, hasOnlyTasks, false, config.allowOnly);
1426
1451
  if (file.mode === "queued") {
1427
1452
  file.mode = "run";
1428
1453
  }
@@ -1,5 +1,205 @@
1
+ import { DiffOptions } from '@vitest/utils/diff';
1
2
  import { TestError, Awaitable } from '@vitest/utils';
2
3
 
4
+ /**
5
+ * This is a subset of Vitest config that's required for the runner to work.
6
+ */
7
+ interface VitestRunnerConfig {
8
+ root: string;
9
+ setupFiles: string[];
10
+ name?: string;
11
+ passWithNoTests: boolean;
12
+ testNamePattern?: RegExp;
13
+ allowOnly?: boolean;
14
+ sequence: {
15
+ shuffle?: boolean;
16
+ concurrent?: boolean;
17
+ seed: number;
18
+ hooks: SequenceHooks;
19
+ setupFiles: SequenceSetupFiles;
20
+ };
21
+ chaiConfig?: {
22
+ truncateThreshold?: number;
23
+ };
24
+ maxConcurrency: number;
25
+ testTimeout: number;
26
+ hookTimeout: number;
27
+ retry: SerializableRetry;
28
+ includeTaskLocation?: boolean;
29
+ diffOptions?: DiffOptions;
30
+ tags: TestTagDefinition[];
31
+ tagsFilter?: string[];
32
+ strictTags: boolean;
33
+ }
34
+ /**
35
+ * Possible options to run a single file in a test.
36
+ */
37
+ interface FileSpecification {
38
+ filepath: string;
39
+ fileTags?: string[];
40
+ testLocations: number[] | undefined;
41
+ testNamePattern: RegExp | undefined;
42
+ testTagsFilter: string[] | undefined;
43
+ testIds: string[] | undefined;
44
+ }
45
+ interface TestTagDefinition extends Omit<TestOptions, "tags" | "shuffle"> {
46
+ /**
47
+ * The name of the tag. This is what you use in the `tags` array in tests.
48
+ */
49
+ name: keyof TestTags extends never ? string : TestTags[keyof TestTags];
50
+ /**
51
+ * A description for the tag. This will be shown in the CLI help and UI.
52
+ */
53
+ description?: string;
54
+ /**
55
+ * Priority for merging options when multiple tags with the same options are applied to a test.
56
+ *
57
+ * Lower number means higher priority. E.g., priority 1 takes precedence over priority 3.
58
+ */
59
+ priority?: number;
60
+ }
61
+ type VitestRunnerImportSource = "collect" | "setup";
62
+ interface VitestRunnerConstructor {
63
+ new (config: VitestRunnerConfig): VitestRunner;
64
+ }
65
+ type CancelReason = "keyboard-input" | "test-failure" | (string & Record<string, never>);
66
+ interface VitestRunner {
67
+ /**
68
+ * First thing that's getting called before actually collecting and running tests.
69
+ */
70
+ onBeforeCollect?: (paths: string[]) => unknown;
71
+ /**
72
+ * Called after the file task was created but not collected yet.
73
+ */
74
+ onCollectStart?: (file: File) => unknown;
75
+ /**
76
+ * Called after collecting tests and before "onBeforeRun".
77
+ */
78
+ onCollected?: (files: File[]) => unknown;
79
+ /**
80
+ * Called when test runner should cancel next test runs.
81
+ * Runner should listen for this method and mark tests and suites as skipped in
82
+ * "onBeforeRunSuite" and "onBeforeRunTask" when called.
83
+ */
84
+ cancel?: (reason: CancelReason) => unknown;
85
+ /**
86
+ * Called before running a single test. Doesn't have "result" yet.
87
+ */
88
+ onBeforeRunTask?: (test: Test) => unknown;
89
+ /**
90
+ * Called before actually running the test function. Already has "result" with "state" and "startTime".
91
+ */
92
+ onBeforeTryTask?: (test: Test, options: {
93
+ retry: number;
94
+ repeats: number;
95
+ }) => unknown;
96
+ /**
97
+ * When the task has finished running, but before cleanup hooks are called
98
+ */
99
+ onTaskFinished?: (test: Test) => unknown;
100
+ /**
101
+ * Called after result and state are set.
102
+ */
103
+ onAfterRunTask?: (test: Test) => unknown;
104
+ /**
105
+ * Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws.
106
+ */
107
+ onAfterTryTask?: (test: Test, options: {
108
+ retry: number;
109
+ repeats: number;
110
+ }) => unknown;
111
+ /**
112
+ * Called after the retry resolution happend. Unlike `onAfterTryTask`, the test now has a new state.
113
+ * All `after` hooks were also called by this point.
114
+ */
115
+ onAfterRetryTask?: (test: Test, options: {
116
+ retry: number;
117
+ repeats: number;
118
+ }) => unknown;
119
+ /**
120
+ * Called before running a single suite. Doesn't have "result" yet.
121
+ */
122
+ onBeforeRunSuite?: (suite: Suite) => unknown;
123
+ /**
124
+ * Called after running a single suite. Has state and result.
125
+ */
126
+ onAfterRunSuite?: (suite: Suite) => unknown;
127
+ /**
128
+ * If defined, will be called instead of usual Vitest suite partition and handling.
129
+ * "before" and "after" hooks will not be ignored.
130
+ */
131
+ runSuite?: (suite: Suite) => Promise<void>;
132
+ /**
133
+ * If defined, will be called instead of usual Vitest handling. Useful, if you have your custom test function.
134
+ * "before" and "after" hooks will not be ignored.
135
+ */
136
+ runTask?: (test: Test) => Promise<void>;
137
+ /**
138
+ * Called, when a task is updated. The same as "onTaskUpdate" in a reporter, but this is running in the same thread as tests.
139
+ */
140
+ onTaskUpdate?: (task: TaskResultPack[], events: TaskEventPack[]) => Promise<void>;
141
+ /**
142
+ * Called when annotation is added via the `context.annotate` method.
143
+ */
144
+ onTestAnnotate?: (test: Test, annotation: TestAnnotation) => Promise<TestAnnotation>;
145
+ /**
146
+ * @experimental
147
+ *
148
+ * Called when artifacts are recorded on tests via the `recordArtifact` utility.
149
+ */
150
+ onTestArtifactRecord?: <Artifact extends TestArtifact>(test: Test, artifact: Artifact) => Promise<Artifact>;
151
+ /**
152
+ * Called before running all tests in collected paths.
153
+ */
154
+ onBeforeRunFiles?: (files: File[]) => unknown;
155
+ /**
156
+ * Called right after running all tests in collected paths.
157
+ */
158
+ onAfterRunFiles?: (files: File[]) => unknown;
159
+ /**
160
+ * Called when new context for a test is defined. Useful if you want to add custom properties to the context.
161
+ * If you only want to define custom context, consider using "beforeAll" in "setupFiles" instead.
162
+ *
163
+ * @see https://vitest.dev/advanced/runner#your-task-function
164
+ */
165
+ extendTaskContext?: (context: TestContext) => TestContext;
166
+ /**
167
+ * Called when test and setup files are imported. Can be called in two situations: when collecting tests and when importing setup files.
168
+ */
169
+ importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
170
+ /**
171
+ * Function that is called when the runner attempts to get the value when `test.extend` is used with `{ injected: true }`
172
+ */
173
+ injectValue?: (key: string) => unknown;
174
+ /**
175
+ * Gets the time spent importing each individual non-externalized file that Vitest collected.
176
+ */
177
+ getImportDurations?: () => Record<string, ImportDuration>;
178
+ /**
179
+ * Publicly available configuration.
180
+ */
181
+ config: VitestRunnerConfig;
182
+ /**
183
+ * The name of the current pool. Can affect how stack trace is inferred on the server side.
184
+ */
185
+ pool?: string;
186
+ /**
187
+ * The current Vite environment that processes the files on the server.
188
+ */
189
+ viteEnvironment?: string;
190
+ /**
191
+ * Return the worker context for fixtures specified with `scope: 'worker'`
192
+ */
193
+ getWorkerContext?: () => Record<string, unknown>;
194
+ onCleanupWorkerContext?: (cleanup: () => unknown) => void;
195
+ trace?<T>(name: string, cb: () => T): T;
196
+ trace?<T>(name: string, attributes: Record<string, any>, cb: () => T): T;
197
+ /** @private */
198
+ _currentTaskStartTime?: number;
199
+ /** @private */
200
+ _currentTaskTimeout?: number;
201
+ }
202
+
3
203
  interface FixtureItem extends FixtureOptions {
4
204
  prop: string;
5
205
  value: any;
@@ -253,6 +453,10 @@ interface TaskBase {
253
453
  * @experimental
254
454
  */
255
455
  dynamic?: boolean;
456
+ /**
457
+ * Custom tags of the task. Useful for filtering tasks.
458
+ */
459
+ tags?: string[];
256
460
  }
257
461
  interface TaskPopulated extends TaskBase {
258
462
  /**
@@ -530,10 +734,6 @@ interface TestOptions {
530
734
  */
531
735
  sequential?: boolean;
532
736
  /**
533
- * Whether the tasks of the suite run in a random order.
534
- */
535
- shuffle?: boolean;
536
- /**
537
737
  * Whether the test should be skipped.
538
738
  */
539
739
  skip?: boolean;
@@ -549,6 +749,17 @@ interface TestOptions {
549
749
  * Whether the test is expected to fail. If it does, the test will pass, otherwise it will fail.
550
750
  */
551
751
  fails?: boolean;
752
+ /**
753
+ * Custom tags of the test. Useful for filtering tests.
754
+ */
755
+ tags?: keyof TestTags extends never ? string[] | string : TestTags[keyof TestTags] | TestTags[keyof TestTags][];
756
+ }
757
+ interface TestTags {}
758
+ interface SuiteOptions extends TestOptions {
759
+ /**
760
+ * Whether the tasks of the suite run in a random order.
761
+ */
762
+ shuffle?: boolean;
552
763
  }
553
764
  interface ExtendedAPI<ExtraContext> {
554
765
  skipIf: (condition: any) => ChainableTestAPI<ExtraContext>;
@@ -603,7 +814,7 @@ type Fixtures<
603
814
  type InferFixturesTypes<T> = T extends TestAPI<infer C> ? C : T;
604
815
  interface SuiteCollectorCallable<ExtraContext = object> {
605
816
  <OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, fn?: SuiteFactory<OverrideExtraContext>, options?: number): SuiteCollector<OverrideExtraContext>;
606
- <OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, options: TestOptions, fn?: SuiteFactory<OverrideExtraContext>): SuiteCollector<OverrideExtraContext>;
817
+ <OverrideExtraContext extends ExtraContext = ExtraContext>(name: string | Function, options: SuiteOptions, fn?: SuiteFactory<OverrideExtraContext>): SuiteCollector<OverrideExtraContext>;
607
818
  }
608
819
  type ChainableSuiteAPI<ExtraContext = object> = ChainableFunction<"concurrent" | "sequential" | "only" | "skip" | "todo" | "shuffle", SuiteCollectorCallable<ExtraContext>, {
609
820
  each: TestEachFunction;
@@ -654,7 +865,7 @@ interface TaskCustomOptions extends TestOptions {
654
865
  interface SuiteCollector<ExtraContext = object> {
655
866
  readonly name: string;
656
867
  readonly mode: RunMode;
657
- options?: TestOptions;
868
+ options?: SuiteOptions;
658
869
  type: "collector";
659
870
  test: TestAPI<ExtraContext>;
660
871
  tasks: (Suite | Test<ExtraContext> | SuiteCollector<ExtraContext>)[];
@@ -882,5 +1093,5 @@ interface TestArtifactRegistry {}
882
1093
  */
883
1094
  type TestArtifact = TestAnnotationArtifact | VisualRegressionArtifact | TestArtifactRegistry[keyof TestArtifactRegistry];
884
1095
 
885
- export { createChainable as c, afterAll as i, afterEach as j, beforeAll as k, beforeEach as l, onTestFinished as m, onTestFailed as o };
886
- export type { TestContext as $, AfterAllListener as A, BeforeAllListener as B, ChainableFunction as C, SerializableRetry as D, SuiteFactory as E, File as F, TaskBase as G, TaskCustomOptions as H, ImportDuration as I, TaskEventPack as J, TaskHook as K, TaskMeta as L, TaskPopulated as M, TaskResult as N, OnTestFailedHandler as O, TaskResultPack as P, TaskState as Q, Retry as R, Suite as S, Task as T, TestAnnotation as U, TestAnnotationArtifact as V, TestAnnotationLocation as W, TestArtifactBase as X, TestArtifactLocation as Y, TestArtifactRegistry as Z, TestAttachment as _, Test as a, TestFunction as a0, TestOptions as a1, Use as a2, VisualRegressionArtifact as a3, TestArtifact as b, SuiteHooks as d, TaskUpdateEvent as e, TestAPI as f, SuiteAPI as g, SuiteCollector as h, AfterEachListener as n, BeforeEachListener as p, Fixture as q, FixtureFn as r, FixtureOptions as s, Fixtures as t, InferFixturesTypes as u, OnTestFinishedHandler as v, RunMode as w, RuntimeContext as x, SequenceHooks as y, SequenceSetupFiles as z };
1096
+ export { createChainable as ab, afterAll as i, afterEach as j, beforeAll as k, beforeEach as l, onTestFinished as m, onTestFailed as o };
1097
+ export type { TestAnnotationLocation as $, AfterAllListener as A, BeforeAllListener as B, CancelReason as C, RunMode as D, RuntimeContext as E, FileSpecification as F, SequenceHooks as G, SequenceSetupFiles as H, ImportDuration as I, SerializableRetry as J, SuiteFactory as K, TaskBase as L, TaskCustomOptions as M, TaskEventPack as N, OnTestFailedHandler as O, TaskHook as P, TaskMeta as Q, Retry as R, Suite as S, TestArtifact as T, TaskPopulated as U, VitestRunner as V, TaskResult as W, TaskResultPack as X, TaskState as Y, TestAnnotation as Z, TestAnnotationArtifact as _, Test as a, TestArtifactBase as a0, TestArtifactLocation as a1, TestArtifactRegistry as a2, TestAttachment as a3, TestContext as a4, TestFunction as a5, TestOptions as a6, TestTags as a7, Use as a8, VisualRegressionArtifact as a9, ChainableFunction as aa, SuiteHooks as b, File as c, TaskUpdateEvent as d, Task as e, TestAPI as f, SuiteAPI as g, SuiteCollector as h, TestTagDefinition as n, VitestRunnerConfig as p, VitestRunnerConstructor as q, VitestRunnerImportSource as r, AfterEachListener as s, BeforeEachListener as t, Fixture as u, FixtureFn as v, FixtureOptions as w, Fixtures as x, InferFixturesTypes as y, OnTestFinishedHandler as z };
package/dist/types.d.ts CHANGED
@@ -1,185 +1,3 @@
1
- import { DiffOptions } from '@vitest/utils/diff';
2
- import { F as File, a as Test, S as Suite, P as TaskResultPack, J as TaskEventPack, U as TestAnnotation, b as TestArtifact, $ as TestContext, I as ImportDuration, y as SequenceHooks, z as SequenceSetupFiles, D as SerializableRetry } from './tasks.d-hZ73xajr.js';
3
- export { A as AfterAllListener, n as AfterEachListener, B as BeforeAllListener, p as BeforeEachListener, q as Fixture, r as FixtureFn, s as FixtureOptions, t as Fixtures, u as InferFixturesTypes, O as OnTestFailedHandler, v as OnTestFinishedHandler, R as Retry, w as RunMode, x as RuntimeContext, g as SuiteAPI, h as SuiteCollector, E as SuiteFactory, d as SuiteHooks, T as Task, G as TaskBase, H as TaskCustomOptions, K as TaskHook, L as TaskMeta, M as TaskPopulated, N as TaskResult, Q as TaskState, e as TaskUpdateEvent, f as TestAPI, V as TestAnnotationArtifact, W as TestAnnotationLocation, X as TestArtifactBase, Y as TestArtifactLocation, Z as TestArtifactRegistry, _ as TestAttachment, a0 as TestFunction, a1 as TestOptions, a2 as Use, a3 as VisualRegressionArtifact } from './tasks.d-hZ73xajr.js';
1
+ export { A as AfterAllListener, s as AfterEachListener, B as BeforeAllListener, t as BeforeEachListener, C as CancelReason, c as File, F as FileSpecification, u as Fixture, v as FixtureFn, w as FixtureOptions, x as Fixtures, I as ImportDuration, y as InferFixturesTypes, O as OnTestFailedHandler, z as OnTestFinishedHandler, R as Retry, D as RunMode, E as RuntimeContext, G as SequenceHooks, H as SequenceSetupFiles, J as SerializableRetry, S as Suite, g as SuiteAPI, h as SuiteCollector, K as SuiteFactory, b as SuiteHooks, e as Task, L as TaskBase, M as TaskCustomOptions, N as TaskEventPack, P as TaskHook, Q as TaskMeta, U as TaskPopulated, W as TaskResult, X as TaskResultPack, Y as TaskState, d as TaskUpdateEvent, a as Test, f as TestAPI, Z as TestAnnotation, _ as TestAnnotationArtifact, $ as TestAnnotationLocation, T as TestArtifact, a0 as TestArtifactBase, a1 as TestArtifactLocation, a2 as TestArtifactRegistry, a3 as TestAttachment, a4 as TestContext, a5 as TestFunction, a6 as TestOptions, n as TestTagDefinition, a7 as TestTags, a8 as Use, a9 as VisualRegressionArtifact, V as VitestRunner, p as VitestRunnerConfig, q as VitestRunnerConstructor, r as VitestRunnerImportSource } from './tasks.d-CLPU8HE4.js';
2
+ import '@vitest/utils/diff';
4
3
  import '@vitest/utils';
5
-
6
- /**
7
- * This is a subset of Vitest config that's required for the runner to work.
8
- */
9
- interface VitestRunnerConfig {
10
- root: string;
11
- setupFiles: string[];
12
- name?: string;
13
- passWithNoTests: boolean;
14
- testNamePattern?: RegExp;
15
- allowOnly?: boolean;
16
- sequence: {
17
- shuffle?: boolean;
18
- concurrent?: boolean;
19
- seed: number;
20
- hooks: SequenceHooks;
21
- setupFiles: SequenceSetupFiles;
22
- };
23
- chaiConfig?: {
24
- truncateThreshold?: number;
25
- };
26
- maxConcurrency: number;
27
- testTimeout: number;
28
- hookTimeout: number;
29
- retry: SerializableRetry;
30
- includeTaskLocation?: boolean;
31
- diffOptions?: DiffOptions;
32
- }
33
- /**
34
- * Possible options to run a single file in a test.
35
- */
36
- interface FileSpecification {
37
- filepath: string;
38
- testLocations: number[] | undefined;
39
- testNamePattern: RegExp | undefined;
40
- testIds: string[] | undefined;
41
- }
42
- type VitestRunnerImportSource = "collect" | "setup";
43
- interface VitestRunnerConstructor {
44
- new (config: VitestRunnerConfig): VitestRunner;
45
- }
46
- type CancelReason = "keyboard-input" | "test-failure" | (string & Record<string, never>);
47
- interface VitestRunner {
48
- /**
49
- * First thing that's getting called before actually collecting and running tests.
50
- */
51
- onBeforeCollect?: (paths: string[]) => unknown;
52
- /**
53
- * Called after the file task was created but not collected yet.
54
- */
55
- onCollectStart?: (file: File) => unknown;
56
- /**
57
- * Called after collecting tests and before "onBeforeRun".
58
- */
59
- onCollected?: (files: File[]) => unknown;
60
- /**
61
- * Called when test runner should cancel next test runs.
62
- * Runner should listen for this method and mark tests and suites as skipped in
63
- * "onBeforeRunSuite" and "onBeforeRunTask" when called.
64
- */
65
- cancel?: (reason: CancelReason) => unknown;
66
- /**
67
- * Called before running a single test. Doesn't have "result" yet.
68
- */
69
- onBeforeRunTask?: (test: Test) => unknown;
70
- /**
71
- * Called before actually running the test function. Already has "result" with "state" and "startTime".
72
- */
73
- onBeforeTryTask?: (test: Test, options: {
74
- retry: number;
75
- repeats: number;
76
- }) => unknown;
77
- /**
78
- * When the task has finished running, but before cleanup hooks are called
79
- */
80
- onTaskFinished?: (test: Test) => unknown;
81
- /**
82
- * Called after result and state are set.
83
- */
84
- onAfterRunTask?: (test: Test) => unknown;
85
- /**
86
- * Called right after running the test function. Doesn't have new state yet. Will not be called, if the test function throws.
87
- */
88
- onAfterTryTask?: (test: Test, options: {
89
- retry: number;
90
- repeats: number;
91
- }) => unknown;
92
- /**
93
- * Called after the retry resolution happend. Unlike `onAfterTryTask`, the test now has a new state.
94
- * All `after` hooks were also called by this point.
95
- */
96
- onAfterRetryTask?: (test: Test, options: {
97
- retry: number;
98
- repeats: number;
99
- }) => unknown;
100
- /**
101
- * Called before running a single suite. Doesn't have "result" yet.
102
- */
103
- onBeforeRunSuite?: (suite: Suite) => unknown;
104
- /**
105
- * Called after running a single suite. Has state and result.
106
- */
107
- onAfterRunSuite?: (suite: Suite) => unknown;
108
- /**
109
- * If defined, will be called instead of usual Vitest suite partition and handling.
110
- * "before" and "after" hooks will not be ignored.
111
- */
112
- runSuite?: (suite: Suite) => Promise<void>;
113
- /**
114
- * If defined, will be called instead of usual Vitest handling. Useful, if you have your custom test function.
115
- * "before" and "after" hooks will not be ignored.
116
- */
117
- runTask?: (test: Test) => Promise<void>;
118
- /**
119
- * Called, when a task is updated. The same as "onTaskUpdate" in a reporter, but this is running in the same thread as tests.
120
- */
121
- onTaskUpdate?: (task: TaskResultPack[], events: TaskEventPack[]) => Promise<void>;
122
- /**
123
- * Called when annotation is added via the `context.annotate` method.
124
- */
125
- onTestAnnotate?: (test: Test, annotation: TestAnnotation) => Promise<TestAnnotation>;
126
- /**
127
- * @experimental
128
- *
129
- * Called when artifacts are recorded on tests via the `recordArtifact` utility.
130
- */
131
- onTestArtifactRecord?: <Artifact extends TestArtifact>(test: Test, artifact: Artifact) => Promise<Artifact>;
132
- /**
133
- * Called before running all tests in collected paths.
134
- */
135
- onBeforeRunFiles?: (files: File[]) => unknown;
136
- /**
137
- * Called right after running all tests in collected paths.
138
- */
139
- onAfterRunFiles?: (files: File[]) => unknown;
140
- /**
141
- * Called when new context for a test is defined. Useful if you want to add custom properties to the context.
142
- * If you only want to define custom context, consider using "beforeAll" in "setupFiles" instead.
143
- *
144
- * @see https://vitest.dev/advanced/runner#your-task-function
145
- */
146
- extendTaskContext?: (context: TestContext) => TestContext;
147
- /**
148
- * Called when test and setup files are imported. Can be called in two situations: when collecting tests and when importing setup files.
149
- */
150
- importFile: (filepath: string, source: VitestRunnerImportSource) => unknown;
151
- /**
152
- * Function that is called when the runner attempts to get the value when `test.extend` is used with `{ injected: true }`
153
- */
154
- injectValue?: (key: string) => unknown;
155
- /**
156
- * Gets the time spent importing each individual non-externalized file that Vitest collected.
157
- */
158
- getImportDurations?: () => Record<string, ImportDuration>;
159
- /**
160
- * Publicly available configuration.
161
- */
162
- config: VitestRunnerConfig;
163
- /**
164
- * The name of the current pool. Can affect how stack trace is inferred on the server side.
165
- */
166
- pool?: string;
167
- /**
168
- * The current Vite environment that processes the files on the server.
169
- */
170
- viteEnvironment?: string;
171
- /**
172
- * Return the worker context for fixtures specified with `scope: 'worker'`
173
- */
174
- getWorkerContext?: () => Record<string, unknown>;
175
- onCleanupWorkerContext?: (cleanup: () => unknown) => void;
176
- trace?<T>(name: string, cb: () => T): T;
177
- trace?<T>(name: string, attributes: Record<string, any>, cb: () => T): T;
178
- /** @private */
179
- _currentTaskStartTime?: number;
180
- /** @private */
181
- _currentTaskTimeout?: number;
182
- }
183
-
184
- export { File, ImportDuration, SequenceHooks, SequenceSetupFiles, SerializableRetry, Suite, TaskEventPack, TaskResultPack, Test, TestAnnotation, TestArtifact, TestContext };
185
- export type { CancelReason, FileSpecification, VitestRunner, VitestRunnerConfig, VitestRunnerConstructor, VitestRunnerImportSource };
package/dist/utils.d.ts CHANGED
@@ -1,11 +1,12 @@
1
- import { S as Suite, F as File, T as Task, a as Test } from './tasks.d-hZ73xajr.js';
2
- export { C as ChainableFunction, c as createChainable } from './tasks.d-hZ73xajr.js';
1
+ import { S as Suite, c as File, e as Task, n as TestTagDefinition, p as VitestRunnerConfig, a as Test } from './tasks.d-CLPU8HE4.js';
2
+ export { aa as ChainableFunction, ab as createChainable } from './tasks.d-CLPU8HE4.js';
3
3
  import { ParsedStack, Arrayable } from '@vitest/utils';
4
+ import '@vitest/utils/diff';
4
5
 
5
6
  /**
6
7
  * If any tasks been marked as `only`, mark all other tasks as `skip`.
7
8
  */
8
- declare function interpretTaskModes(file: Suite, namePattern?: string | RegExp, testLocations?: number[] | undefined, testIds?: string[] | undefined, onlyMode?: boolean, parentIsOnly?: boolean, allowOnly?: boolean): void;
9
+ declare function interpretTaskModes(file: Suite, namePattern?: string | RegExp, testLocations?: number[] | undefined, testIds?: string[] | undefined, testTagsFilter?: ((testTags: string[]) => boolean) | undefined, onlyMode?: boolean, parentIsOnly?: boolean, allowOnly?: boolean): void;
9
10
  declare function someTasksAreOnly(suite: Suite): boolean;
10
11
  declare function generateHash(str: string): string;
11
12
  declare function calculateSuiteHash(parent: Suite): void;
@@ -31,6 +32,9 @@ declare function limitConcurrency(concurrency?: number): <
31
32
  */
32
33
  declare function partitionSuiteChildren(suite: Suite): Task[][];
33
34
 
35
+ declare function validateTags(config: VitestRunnerConfig, tags: string[]): void;
36
+ declare function createTagsFilter(tagsExpr: string[], availableTags: TestTagDefinition[]): (testTags: string[]) => boolean;
37
+
34
38
  declare function isTestCase(s: Task): s is Test;
35
39
  declare function getTests(suite: Arrayable<Task>): Test[];
36
40
  declare function getTasks(tasks?: Arrayable<Task>): Task[];
@@ -42,4 +46,4 @@ declare function getFullName(task: Task, separator?: string): string;
42
46
  declare function getTestName(task: Task, separator?: string): string;
43
47
  declare function createTaskName(names: readonly (string | undefined)[], separator?: string): string;
44
48
 
45
- export { calculateSuiteHash, createFileTask, createTaskName, findTestFileStackTrace, generateFileHash, generateHash, getFullName, getNames, getSuites, getTasks, getTestName, getTests, hasFailed, hasTests, interpretTaskModes, isTestCase, limitConcurrency, partitionSuiteChildren, someTasksAreOnly };
49
+ export { calculateSuiteHash, createFileTask, createTagsFilter, createTaskName, findTestFileStackTrace, generateFileHash, generateHash, getFullName, getNames, getSuites, getTasks, getTestName, getTests, hasFailed, hasTests, interpretTaskModes, isTestCase, limitConcurrency, partitionSuiteChildren, someTasksAreOnly, validateTags };
package/dist/utils.js CHANGED
@@ -1,4 +1,4 @@
1
- export { a as calculateSuiteHash, c as createChainable, b as createFileTask, e as createTaskName, f as findTestFileStackTrace, g as generateFileHash, d as generateHash, h as getFullName, j as getNames, k as getSuites, m as getTasks, n as getTestName, o as getTests, q as hasFailed, r as hasTests, i as interpretTaskModes, t as isTestCase, l as limitConcurrency, p as partitionSuiteChildren, s as someTasksAreOnly } from './chunk-tasks.js';
1
+ export { a as calculateSuiteHash, c as createChainable, b as createFileTask, e as createTagsFilter, h as createTaskName, f as findTestFileStackTrace, g as generateFileHash, d as generateHash, j as getFullName, k as getNames, m as getSuites, n as getTasks, o as getTestName, q as getTests, r as hasFailed, t as hasTests, i as interpretTaskModes, u as isTestCase, l as limitConcurrency, p as partitionSuiteChildren, s as someTasksAreOnly, v as validateTags } from './chunk-tasks.js';
2
2
  import '@vitest/utils/error';
3
3
  import '@vitest/utils/source-map';
4
4
  import 'pathe';
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@vitest/runner",
3
3
  "type": "module",
4
- "version": "4.1.0-beta.1",
4
+ "version": "4.1.0-beta.2",
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": "4.1.0-beta.1"
42
+ "@vitest/utils": "4.1.0-beta.2"
43
43
  },
44
44
  "scripts": {
45
45
  "build": "premove dist && rollup -c",