@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.
- package/dist/chunk-tasks.js +240 -2
- package/dist/index.d.ts +2 -4
- package/dist/index.js +80 -55
- package/dist/{tasks.d-hZ73xajr.d.ts → tasks.d-CLPU8HE4.d.ts} +219 -8
- package/dist/types.d.ts +2 -184
- package/dist/utils.d.ts +8 -4
- package/dist/utils.js +1 -1
- package/package.json +2 -2
package/dist/chunk-tasks.js
CHANGED
|
@@ -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,
|
|
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 {
|
|
2
|
-
export { A as AfterAllListener,
|
|
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,
|
|
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
|
|
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$
|
|
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$
|
|
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
|
-
|
|
822
|
-
options.
|
|
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$
|
|
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$
|
|
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$
|
|
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$
|
|
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
|
-
|
|
951
|
-
|
|
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
|
-
|
|
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:
|
|
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?:
|
|
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
|
|
886
|
-
export type {
|
|
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
|
-
|
|
2
|
-
import
|
|
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,
|
|
2
|
-
export {
|
|
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,
|
|
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.
|
|
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.
|
|
42
|
+
"@vitest/utils": "4.1.0-beta.2"
|
|
43
43
|
},
|
|
44
44
|
"scripts": {
|
|
45
45
|
"build": "premove dist && rollup -c",
|