uql-orm 0.3.0 → 0.3.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/CHANGELOG.md +16 -2
- package/README.md +1 -1
- package/dist/browser/uql-browser.min.js +880 -314
- package/dist/browser/uql-browser.min.js.map +1 -1
- package/dist/maria/mariadbQuerier.d.ts.map +1 -1
- package/dist/maria/mariadbQuerier.js +3 -1
- package/dist/maria/mariadbQuerier.js.map +1 -1
- package/dist/mongo/mongoDialect.d.ts +17 -1
- package/dist/mongo/mongoDialect.d.ts.map +1 -1
- package/dist/mongo/mongoDialect.js +55 -1
- package/dist/mongo/mongoDialect.js.map +1 -1
- package/dist/mongo/mongodbQuerier.d.ts +8 -0
- package/dist/mongo/mongodbQuerier.d.ts.map +1 -1
- package/dist/mongo/mongodbQuerier.js +69 -27
- package/dist/mongo/mongodbQuerier.js.map +1 -1
- package/dist/mysql/mysql2Querier.d.ts +1 -5
- package/dist/mysql/mysql2Querier.d.ts.map +1 -1
- package/dist/mysql/mysql2Querier.js +2 -1
- package/dist/mysql/mysql2Querier.js.map +1 -1
- package/dist/postgres/postgresDialect.d.ts.map +1 -1
- package/dist/postgres/postgresDialect.js +2 -1
- package/dist/postgres/postgresDialect.js.map +1 -1
- package/dist/schema/types.d.ts +1 -1
- package/dist/schema/types.d.ts.map +1 -1
- package/dist/type/query.d.ts +5 -0
- package/dist/type/query.d.ts.map +1 -1
- package/dist/type/query.js.map +1 -1
- package/dist/util/sql.util.d.ts +1 -1
- package/dist/util/sql.util.d.ts.map +1 -1
- package/dist/util/sql.util.js +7 -2
- package/dist/util/sql.util.js.map +1 -1
- package/package.json +2 -2
|
@@ -1477,7 +1477,25 @@ function nodeIsComment(node) {
|
|
|
1477
1477
|
function nodeIsFragment(node) {
|
|
1478
1478
|
return node.nodeType === FRAGMENT_NODE;
|
|
1479
1479
|
}
|
|
1480
|
-
|
|
1480
|
+
function filterChildren(children, filterNode) {
|
|
1481
|
+
// Filter out text nodes that only contain whitespace to prevent empty lines
|
|
1482
|
+
// This is done regardless of whether a filterNode is provided
|
|
1483
|
+
let filtered = children.filter((node)=>{
|
|
1484
|
+
// Filter out text nodes that are only whitespace
|
|
1485
|
+
if (node.nodeType === TEXT_NODE) {
|
|
1486
|
+
const text = node.data || "";
|
|
1487
|
+
// Keep text nodes that have non-whitespace content
|
|
1488
|
+
return text.trim().length > 0;
|
|
1489
|
+
}
|
|
1490
|
+
return true;
|
|
1491
|
+
});
|
|
1492
|
+
// Apply additional user-provided filter if specified
|
|
1493
|
+
if (filterNode) {
|
|
1494
|
+
filtered = filtered.filter(filterNode);
|
|
1495
|
+
}
|
|
1496
|
+
return filtered;
|
|
1497
|
+
}
|
|
1498
|
+
function serializeDOM(node, config, indentation, depth, refs, printer, filterNode) {
|
|
1481
1499
|
if (nodeIsText(node)) {
|
|
1482
1500
|
return printText(node.data, config);
|
|
1483
1501
|
}
|
|
@@ -1488,13 +1506,24 @@ const serialize$3 = (node, config, indentation, depth, refs, printer)=>{
|
|
|
1488
1506
|
if (++depth > config.maxDepth) {
|
|
1489
1507
|
return printElementAsLeaf(type, config);
|
|
1490
1508
|
}
|
|
1509
|
+
const children = Array.prototype.slice.call(node.childNodes || node.children);
|
|
1510
|
+
const shadowChildren = nodeIsFragment(node) || !node.shadowRoot ? [] : Array.prototype.slice.call(node.shadowRoot.children);
|
|
1511
|
+
const resolvedChildren = filterNode ? filterChildren(children, filterNode) : children;
|
|
1512
|
+
const resolvedShadowChildren = filterNode ? filterChildren(shadowChildren, filterNode) : shadowChildren;
|
|
1491
1513
|
return printElement(type, printProps(nodeIsFragment(node) ? [] : Array.from(node.attributes, (attr)=>attr.name).sort(), nodeIsFragment(node) ? {} : [
|
|
1492
1514
|
...node.attributes
|
|
1493
1515
|
].reduce((props, attribute)=>{
|
|
1494
1516
|
props[attribute.name] = attribute.value;
|
|
1495
1517
|
return props;
|
|
1496
|
-
}, {}), config, indentation + config.indent, depth, refs, printer), (
|
|
1497
|
-
}
|
|
1518
|
+
}, {}), config, indentation + config.indent, depth, refs, printer), (resolvedShadowChildren.length > 0 ? printShadowRoot(resolvedShadowChildren, config, indentation + config.indent, depth, refs, printer) : "") + printChildren(resolvedChildren, config, indentation + config.indent, depth, refs, printer), config, indentation);
|
|
1519
|
+
}
|
|
1520
|
+
const serialize$3 = (node, config, indentation, depth, refs, printer)=>serializeDOM(node, config, indentation, depth, refs, printer);
|
|
1521
|
+
function createDOMElementFilter(filterNode) {
|
|
1522
|
+
return {
|
|
1523
|
+
test: test$3,
|
|
1524
|
+
serialize: (node, config, indentation, depth, refs, printer)=>serializeDOM(node, config, indentation, depth, refs, printer, filterNode)
|
|
1525
|
+
};
|
|
1526
|
+
}
|
|
1498
1527
|
const plugin$3 = {
|
|
1499
1528
|
serialize: serialize$3,
|
|
1500
1529
|
test: test$3
|
|
@@ -2861,14 +2890,24 @@ const PLUGINS = [
|
|
|
2861
2890
|
Immutable,
|
|
2862
2891
|
AsymmetricMatcher
|
|
2863
2892
|
];
|
|
2864
|
-
function stringify(object, maxDepth = 10, { maxLength, ...options } = {}) {
|
|
2893
|
+
function stringify(object, maxDepth = 10, { maxLength, filterNode, ...options } = {}) {
|
|
2865
2894
|
const MAX_LENGTH = maxLength ?? 1e4;
|
|
2866
2895
|
let result;
|
|
2896
|
+
// Convert string selector to filter function
|
|
2897
|
+
const filterFn = typeof filterNode === "string" ? createNodeFilterFromSelector(filterNode) : filterNode;
|
|
2898
|
+
const plugins = filterFn ? [
|
|
2899
|
+
ReactTestComponent,
|
|
2900
|
+
ReactElement,
|
|
2901
|
+
createDOMElementFilter(filterFn),
|
|
2902
|
+
DOMCollection,
|
|
2903
|
+
Immutable,
|
|
2904
|
+
AsymmetricMatcher
|
|
2905
|
+
] : PLUGINS;
|
|
2867
2906
|
try {
|
|
2868
2907
|
result = format$1(object, {
|
|
2869
2908
|
maxDepth,
|
|
2870
2909
|
escapeString: false,
|
|
2871
|
-
plugins
|
|
2910
|
+
plugins,
|
|
2872
2911
|
...options
|
|
2873
2912
|
});
|
|
2874
2913
|
} catch {
|
|
@@ -2876,16 +2915,36 @@ function stringify(object, maxDepth = 10, { maxLength, ...options } = {}) {
|
|
|
2876
2915
|
callToJSON: false,
|
|
2877
2916
|
maxDepth,
|
|
2878
2917
|
escapeString: false,
|
|
2879
|
-
plugins
|
|
2918
|
+
plugins,
|
|
2880
2919
|
...options
|
|
2881
2920
|
});
|
|
2882
2921
|
}
|
|
2883
2922
|
// Prevents infinite loop https://github.com/vitest-dev/vitest/issues/7249
|
|
2884
2923
|
return result.length >= MAX_LENGTH && maxDepth > 1 ? stringify(object, Math.floor(Math.min(maxDepth, Number.MAX_SAFE_INTEGER) / 2), {
|
|
2885
2924
|
maxLength,
|
|
2925
|
+
filterNode,
|
|
2886
2926
|
...options
|
|
2887
2927
|
}) : result;
|
|
2888
2928
|
}
|
|
2929
|
+
function createNodeFilterFromSelector(selector) {
|
|
2930
|
+
const ELEMENT_NODE = 1;
|
|
2931
|
+
const COMMENT_NODE = 8;
|
|
2932
|
+
return (node)=>{
|
|
2933
|
+
// Filter out comments
|
|
2934
|
+
if (node.nodeType === COMMENT_NODE) {
|
|
2935
|
+
return false;
|
|
2936
|
+
}
|
|
2937
|
+
// Filter out elements matching the selector
|
|
2938
|
+
if (node.nodeType === ELEMENT_NODE && node.matches) {
|
|
2939
|
+
try {
|
|
2940
|
+
return !node.matches(selector);
|
|
2941
|
+
} catch {
|
|
2942
|
+
return true;
|
|
2943
|
+
}
|
|
2944
|
+
}
|
|
2945
|
+
return true;
|
|
2946
|
+
};
|
|
2947
|
+
}
|
|
2889
2948
|
const formatRegExp = /%[sdjifoOc%]/g;
|
|
2890
2949
|
function baseFormat(args, options = {}) {
|
|
2891
2950
|
const formatArg = (item, inspecOptions)=>{
|
|
@@ -2944,6 +3003,9 @@ function baseFormat(args, options = {}) {
|
|
|
2944
3003
|
if (typeof value === "bigint") {
|
|
2945
3004
|
return `${value.toString()}n`;
|
|
2946
3005
|
}
|
|
3006
|
+
if (typeof value === "symbol") {
|
|
3007
|
+
return "NaN";
|
|
3008
|
+
}
|
|
2947
3009
|
return Number(value).toString();
|
|
2948
3010
|
}
|
|
2949
3011
|
case "%i":
|
|
@@ -2984,7 +3046,7 @@ function baseFormat(args, options = {}) {
|
|
|
2984
3046
|
});
|
|
2985
3047
|
for(let x = args[i]; i < len; x = args[++i]){
|
|
2986
3048
|
if (x === null || typeof x !== "object") {
|
|
2987
|
-
str += ` ${x}`;
|
|
3049
|
+
str += ` ${typeof x === "symbol" ? x.toString() : x}`;
|
|
2988
3050
|
} else {
|
|
2989
3051
|
str += ` ${formatArg(x)}`;
|
|
2990
3052
|
}
|
|
@@ -3030,6 +3092,31 @@ function assertTypes(value, name, types) {
|
|
|
3030
3092
|
throw new TypeError(`${name} value must be ${types.join(" or ")}, received "${receivedType}"`);
|
|
3031
3093
|
}
|
|
3032
3094
|
}
|
|
3095
|
+
function filterOutComments(s) {
|
|
3096
|
+
const result = [];
|
|
3097
|
+
let commentState = "none";
|
|
3098
|
+
for(let i = 0; i < s.length; ++i){
|
|
3099
|
+
if (commentState === "singleline") {
|
|
3100
|
+
if (s[i] === "\n") {
|
|
3101
|
+
commentState = "none";
|
|
3102
|
+
}
|
|
3103
|
+
} else if (commentState === "multiline") {
|
|
3104
|
+
if (s[i - 1] === "*" && s[i] === "/") {
|
|
3105
|
+
commentState = "none";
|
|
3106
|
+
}
|
|
3107
|
+
} else if (commentState === "none") {
|
|
3108
|
+
if (s[i] === "/" && s[i + 1] === "/") {
|
|
3109
|
+
commentState = "singleline";
|
|
3110
|
+
} else if (s[i] === "/" && s[i + 1] === "*") {
|
|
3111
|
+
commentState = "multiline";
|
|
3112
|
+
i += 2;
|
|
3113
|
+
} else {
|
|
3114
|
+
result.push(s[i]);
|
|
3115
|
+
}
|
|
3116
|
+
}
|
|
3117
|
+
}
|
|
3118
|
+
return result.join("");
|
|
3119
|
+
}
|
|
3033
3120
|
function toArray(array) {
|
|
3034
3121
|
if (array === null || array === undefined) {
|
|
3035
3122
|
array = [];
|
|
@@ -3077,6 +3164,23 @@ function isNegativeNaN(val) {
|
|
|
3077
3164
|
const isNegative = u32[1] >>> 31 === 1;
|
|
3078
3165
|
return isNegative;
|
|
3079
3166
|
}
|
|
3167
|
+
function ordinal(i) {
|
|
3168
|
+
const j = i % 10;
|
|
3169
|
+
const k = i % 100;
|
|
3170
|
+
if (j === 1 && k !== 11) {
|
|
3171
|
+
return `${i}st`;
|
|
3172
|
+
}
|
|
3173
|
+
if (j === 2 && k !== 12) {
|
|
3174
|
+
return `${i}nd`;
|
|
3175
|
+
}
|
|
3176
|
+
if (j === 3 && k !== 13) {
|
|
3177
|
+
return `${i}rd`;
|
|
3178
|
+
}
|
|
3179
|
+
return `${i}th`;
|
|
3180
|
+
}
|
|
3181
|
+
function unique(array) {
|
|
3182
|
+
return Array.from(new Set(array));
|
|
3183
|
+
}
|
|
3080
3184
|
|
|
3081
3185
|
const SAFE_TIMERS_SYMBOL = Symbol("vitest:SAFE_TIMERS");
|
|
3082
3186
|
function getSafeTimers() {
|
|
@@ -3200,6 +3304,8 @@ for(let i = 0; i < chars.length; i++){
|
|
|
3200
3304
|
}
|
|
3201
3305
|
const CHROME_IE_STACK_REGEXP = /^\s*at .*(?:\S:\d+|\(native\))/m;
|
|
3202
3306
|
const SAFARI_NATIVE_CODE_REGEXP = /^(?:eval@)?(?:\[native code\])?$/;
|
|
3307
|
+
const NOW_LENGTH = Date.now().toString().length;
|
|
3308
|
+
const REGEXP_VITEST = new RegExp(`vitest=\\d{${NOW_LENGTH}}`);
|
|
3203
3309
|
function extractLocation(urlLike) {
|
|
3204
3310
|
// Fail-fast but return locations like "(native)"
|
|
3205
3311
|
if (!urlLike.includes(":")) {
|
|
@@ -3228,6 +3334,9 @@ function extractLocation(urlLike) {
|
|
|
3228
3334
|
const isWindows = /^\/@fs\/[a-zA-Z]:\//.test(url);
|
|
3229
3335
|
url = url.slice(isWindows ? 5 : 4);
|
|
3230
3336
|
}
|
|
3337
|
+
if (url.includes("vitest=")) {
|
|
3338
|
+
url = url.replace(REGEXP_VITEST, "").replace(/[?&]$/, "");
|
|
3339
|
+
}
|
|
3231
3340
|
return [
|
|
3232
3341
|
url,
|
|
3233
3342
|
parts[2] || undefined,
|
|
@@ -3324,7 +3433,7 @@ function parseSingleV8Stack(raw) {
|
|
|
3324
3433
|
// normalize Windows path (\ -> /)
|
|
3325
3434
|
file = file.startsWith("node:") || file.startsWith("internal:") ? file : resolve(file);
|
|
3326
3435
|
if (method) {
|
|
3327
|
-
method = method.replace(
|
|
3436
|
+
method = method.replace(/\(0\s?,\s?__vite_ssr_import_\d+__.(\w+)\)/g, "$1").replace(/__(vite_ssr_import|vi_import)_\d+__\./g, "").replace(/(Object\.)?__vite_ssr_export_default__\s?/g, "");
|
|
3328
3437
|
}
|
|
3329
3438
|
return {
|
|
3330
3439
|
method,
|
|
@@ -3334,19 +3443,29 @@ function parseSingleV8Stack(raw) {
|
|
|
3334
3443
|
};
|
|
3335
3444
|
}
|
|
3336
3445
|
|
|
3337
|
-
|
|
3446
|
+
const kChainableContext = Symbol("kChainableContext");
|
|
3447
|
+
function getChainableContext(chainable) {
|
|
3448
|
+
return chainable?.[kChainableContext];
|
|
3449
|
+
}
|
|
3450
|
+
function createChainable(keys, fn, context) {
|
|
3338
3451
|
function create(context) {
|
|
3339
3452
|
const chain = function(...args) {
|
|
3340
3453
|
return fn.apply(context, args);
|
|
3341
3454
|
};
|
|
3342
3455
|
Object.assign(chain, fn);
|
|
3343
|
-
chain
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
|
|
3347
|
-
|
|
3348
|
-
|
|
3349
|
-
|
|
3456
|
+
Object.defineProperty(chain, kChainableContext, {
|
|
3457
|
+
value: {
|
|
3458
|
+
withContext: ()=>chain.bind(context),
|
|
3459
|
+
getFixtures: ()=>context.fixtures,
|
|
3460
|
+
setContext: (key, value)=>{
|
|
3461
|
+
context[key] = value;
|
|
3462
|
+
},
|
|
3463
|
+
mergeContext: (ctx)=>{
|
|
3464
|
+
Object.assign(context, ctx);
|
|
3465
|
+
}
|
|
3466
|
+
},
|
|
3467
|
+
enumerable: false
|
|
3468
|
+
});
|
|
3350
3469
|
for (const key of keys){
|
|
3351
3470
|
Object.defineProperty(chain, key, {
|
|
3352
3471
|
get () {
|
|
@@ -3359,8 +3478,11 @@ function createChainable(keys, fn) {
|
|
|
3359
3478
|
}
|
|
3360
3479
|
return chain;
|
|
3361
3480
|
}
|
|
3362
|
-
const chain = create({});
|
|
3363
|
-
chain
|
|
3481
|
+
const chain = create(context ?? {});
|
|
3482
|
+
Object.defineProperty(chain, "fn", {
|
|
3483
|
+
value: fn,
|
|
3484
|
+
enumerable: false
|
|
3485
|
+
});
|
|
3364
3486
|
return chain;
|
|
3365
3487
|
}
|
|
3366
3488
|
function findTestFileStackTrace(testFilePath, error) {
|
|
@@ -3373,6 +3495,23 @@ function findTestFileStackTrace(testFilePath, error) {
|
|
|
3373
3495
|
}
|
|
3374
3496
|
}
|
|
3375
3497
|
}
|
|
3498
|
+
function validateTags(config, tags) {
|
|
3499
|
+
if (!config.strictTags) {
|
|
3500
|
+
return;
|
|
3501
|
+
}
|
|
3502
|
+
const availableTags = new Set(config.tags.map((tag)=>tag.name));
|
|
3503
|
+
for (const tag of tags){
|
|
3504
|
+
if (!availableTags.has(tag)) {
|
|
3505
|
+
throw createNoTagsError(config.tags, tag);
|
|
3506
|
+
}
|
|
3507
|
+
}
|
|
3508
|
+
}
|
|
3509
|
+
function createNoTagsError(availableTags, tag, prefix = "tag") {
|
|
3510
|
+
if (!availableTags.length) {
|
|
3511
|
+
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`);
|
|
3512
|
+
}
|
|
3513
|
+
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")}`);
|
|
3514
|
+
}
|
|
3376
3515
|
function createTaskName(names, separator = " > ") {
|
|
3377
3516
|
return names.filter((name)=>name !== undefined).join(separator);
|
|
3378
3517
|
}
|
|
@@ -3385,6 +3524,21 @@ class PendingError extends Error {
|
|
|
3385
3524
|
this.taskId = task.id;
|
|
3386
3525
|
}
|
|
3387
3526
|
}
|
|
3527
|
+
class FixtureDependencyError extends Error {
|
|
3528
|
+
constructor(...args){
|
|
3529
|
+
super(...args), this.name = "FixtureDependencyError";
|
|
3530
|
+
}
|
|
3531
|
+
}
|
|
3532
|
+
class FixtureAccessError extends Error {
|
|
3533
|
+
constructor(...args){
|
|
3534
|
+
super(...args), this.name = "FixtureAccessError";
|
|
3535
|
+
}
|
|
3536
|
+
}
|
|
3537
|
+
class FixtureParseError extends Error {
|
|
3538
|
+
constructor(...args){
|
|
3539
|
+
super(...args), this.name = "FixtureParseError";
|
|
3540
|
+
}
|
|
3541
|
+
}
|
|
3388
3542
|
// use WeakMap here to make the Test and Suite object serializable
|
|
3389
3543
|
const fnMap = new WeakMap();
|
|
3390
3544
|
const testFixtureMap = new WeakMap();
|
|
@@ -3395,7 +3549,7 @@ function setFn(key, fn) {
|
|
|
3395
3549
|
function setTestFixture(key, fixture) {
|
|
3396
3550
|
testFixtureMap.set(key, fixture);
|
|
3397
3551
|
}
|
|
3398
|
-
function
|
|
3552
|
+
function getTestFixtures(key) {
|
|
3399
3553
|
return testFixtureMap.get(key);
|
|
3400
3554
|
}
|
|
3401
3555
|
function setHooks(key, hooks) {
|
|
@@ -3404,179 +3558,312 @@ function setHooks(key, hooks) {
|
|
|
3404
3558
|
function getHooks(key) {
|
|
3405
3559
|
return hooksMap.get(key);
|
|
3406
3560
|
}
|
|
3407
|
-
|
|
3408
|
-
|
|
3409
|
-
|
|
3410
|
-
|
|
3411
|
-
|
|
3412
|
-
|
|
3413
|
-
|
|
3414
|
-
|
|
3415
|
-
|
|
3416
|
-
|
|
3417
|
-
|
|
3418
|
-
|
|
3419
|
-
|
|
3420
|
-
|
|
3421
|
-
|
|
3422
|
-
|
|
3423
|
-
|
|
3424
|
-
|
|
3425
|
-
|
|
3426
|
-
|
|
3427
|
-
}
|
|
3428
|
-
|
|
3429
|
-
|
|
3430
|
-
|
|
3431
|
-
|
|
3432
|
-
|
|
3433
|
-
|
|
3434
|
-
|
|
3435
|
-
|
|
3436
|
-
|
|
3561
|
+
class TestFixtures {
|
|
3562
|
+
static{
|
|
3563
|
+
this._definitions = [];
|
|
3564
|
+
}
|
|
3565
|
+
static{
|
|
3566
|
+
this._builtinFixtures = [
|
|
3567
|
+
"task",
|
|
3568
|
+
"signal",
|
|
3569
|
+
"onTestFailed",
|
|
3570
|
+
"onTestFinished",
|
|
3571
|
+
"skip",
|
|
3572
|
+
"annotate"
|
|
3573
|
+
];
|
|
3574
|
+
}
|
|
3575
|
+
static{
|
|
3576
|
+
this._fixtureOptionKeys = [
|
|
3577
|
+
"auto",
|
|
3578
|
+
"injected",
|
|
3579
|
+
"scope"
|
|
3580
|
+
];
|
|
3581
|
+
}
|
|
3582
|
+
static{
|
|
3583
|
+
this._fixtureScopes = [
|
|
3584
|
+
"test",
|
|
3585
|
+
"file",
|
|
3586
|
+
"worker"
|
|
3587
|
+
];
|
|
3588
|
+
}
|
|
3589
|
+
static{
|
|
3590
|
+
this._workerContextSuite = {
|
|
3591
|
+
type: "worker"
|
|
3437
3592
|
};
|
|
3438
|
-
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3593
|
+
}
|
|
3594
|
+
static clearDefinitions() {
|
|
3595
|
+
TestFixtures._definitions.length = 0;
|
|
3596
|
+
}
|
|
3597
|
+
static getWorkerContexts() {
|
|
3598
|
+
return TestFixtures._definitions.map((f)=>f.getWorkerContext());
|
|
3599
|
+
}
|
|
3600
|
+
static getFileContexts(file) {
|
|
3601
|
+
return TestFixtures._definitions.map((f)=>f.getFileContext(file));
|
|
3602
|
+
}
|
|
3603
|
+
constructor(registrations){
|
|
3604
|
+
this._overrides = new WeakMap();
|
|
3605
|
+
this._registrations = registrations ?? new Map();
|
|
3606
|
+
this._suiteContexts = new WeakMap();
|
|
3607
|
+
TestFixtures._definitions.push(this);
|
|
3608
|
+
}
|
|
3609
|
+
extend(runner, userFixtures) {
|
|
3610
|
+
const { suite } = getCurrentSuite();
|
|
3611
|
+
const isTopLevel = !suite || suite.file === suite;
|
|
3612
|
+
const registrations = this.parseUserFixtures(runner, userFixtures, isTopLevel);
|
|
3613
|
+
return new TestFixtures(registrations);
|
|
3614
|
+
}
|
|
3615
|
+
get(suite) {
|
|
3616
|
+
let currentSuite = suite;
|
|
3617
|
+
while(currentSuite){
|
|
3618
|
+
const overrides = this._overrides.get(currentSuite);
|
|
3619
|
+
// return the closest override
|
|
3620
|
+
if (overrides) {
|
|
3621
|
+
return overrides;
|
|
3622
|
+
}
|
|
3623
|
+
if (currentSuite === currentSuite.file) {
|
|
3624
|
+
break;
|
|
3625
|
+
}
|
|
3626
|
+
currentSuite = currentSuite.suite || currentSuite.file;
|
|
3444
3627
|
}
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3628
|
+
return this._registrations;
|
|
3629
|
+
}
|
|
3630
|
+
override(runner, userFixtures) {
|
|
3631
|
+
const { suite: currentSuite, file } = getCurrentSuite();
|
|
3632
|
+
const suite = currentSuite || file;
|
|
3633
|
+
const isTopLevel = !currentSuite || currentSuite.file === currentSuite;
|
|
3634
|
+
// Create a copy of the closest parent's registrations to avoid modifying them
|
|
3635
|
+
// For chained calls, this.get(suite) returns this suite's overrides; for first call, returns parent's
|
|
3636
|
+
const suiteRegistrations = new Map(this.get(suite));
|
|
3637
|
+
const registrations = this.parseUserFixtures(runner, userFixtures, isTopLevel, suiteRegistrations);
|
|
3638
|
+
// If defined in top-level, just override all registrations
|
|
3639
|
+
// We don't support overriding suite-level fixtures anyway (it will throw an error)
|
|
3640
|
+
if (isTopLevel) {
|
|
3641
|
+
this._registrations = registrations;
|
|
3642
|
+
} else {
|
|
3643
|
+
this._overrides.set(suite, registrations);
|
|
3448
3644
|
}
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
}
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3645
|
+
}
|
|
3646
|
+
getFileContext(file) {
|
|
3647
|
+
if (!this._suiteContexts.has(file)) {
|
|
3648
|
+
this._suiteContexts.set(file, Object.create(null));
|
|
3649
|
+
}
|
|
3650
|
+
return this._suiteContexts.get(file);
|
|
3651
|
+
}
|
|
3652
|
+
getWorkerContext() {
|
|
3653
|
+
if (!this._suiteContexts.has(TestFixtures._workerContextSuite)) {
|
|
3654
|
+
this._suiteContexts.set(TestFixtures._workerContextSuite, Object.create(null));
|
|
3655
|
+
}
|
|
3656
|
+
return this._suiteContexts.get(TestFixtures._workerContextSuite);
|
|
3657
|
+
}
|
|
3658
|
+
parseUserFixtures(runner, userFixtures, supportNonTest, registrations = new Map(this._registrations)) {
|
|
3659
|
+
const errors = [];
|
|
3660
|
+
Object.entries(userFixtures).forEach(([name, fn])=>{
|
|
3661
|
+
let options;
|
|
3662
|
+
let value;
|
|
3663
|
+
let _options;
|
|
3664
|
+
if (Array.isArray(fn) && fn.length >= 2 && isObject(fn[1]) && Object.keys(fn[1]).some((key)=>TestFixtures._fixtureOptionKeys.includes(key))) {
|
|
3665
|
+
_options = fn[1];
|
|
3666
|
+
options = {
|
|
3667
|
+
auto: _options.auto ?? false,
|
|
3668
|
+
scope: _options.scope ?? "test",
|
|
3669
|
+
injected: _options.injected ?? false
|
|
3670
|
+
};
|
|
3671
|
+
value = options.injected ? runner.injectValue?.(name) ?? fn[0] : fn[0];
|
|
3672
|
+
} else {
|
|
3673
|
+
value = fn;
|
|
3464
3674
|
}
|
|
3465
|
-
|
|
3466
|
-
if (
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3474
|
-
|
|
3475
|
-
|
|
3476
|
-
|
|
3477
|
-
|
|
3478
|
-
|
|
3479
|
-
|
|
3480
|
-
|
|
3481
|
-
|
|
3482
|
-
|
|
3675
|
+
const parent = registrations.get(name);
|
|
3676
|
+
if (parent && options) {
|
|
3677
|
+
if (parent.scope !== options.scope) {
|
|
3678
|
+
errors.push(new FixtureDependencyError(`The "${name}" fixture was already registered with a "${options.scope}" scope.`));
|
|
3679
|
+
}
|
|
3680
|
+
if (parent.auto !== options.auto) {
|
|
3681
|
+
errors.push(new FixtureDependencyError(`The "${name}" fixture was already registered as { auto: ${options.auto} }.`));
|
|
3682
|
+
}
|
|
3683
|
+
} else if (parent) {
|
|
3684
|
+
options = {
|
|
3685
|
+
auto: parent.auto,
|
|
3686
|
+
scope: parent.scope,
|
|
3687
|
+
injected: parent.injected
|
|
3688
|
+
};
|
|
3689
|
+
} else if (!options) {
|
|
3690
|
+
options = {
|
|
3691
|
+
auto: false,
|
|
3692
|
+
injected: false,
|
|
3693
|
+
scope: "test"
|
|
3694
|
+
};
|
|
3695
|
+
}
|
|
3696
|
+
if (options.scope && !TestFixtures._fixtureScopes.includes(options.scope)) {
|
|
3697
|
+
errors.push(new FixtureDependencyError(`The "${name}" fixture has unknown scope "${options.scope}".`));
|
|
3698
|
+
}
|
|
3699
|
+
if (!supportNonTest && options.scope !== "test") {
|
|
3700
|
+
errors.push(new FixtureDependencyError(`The "${name}" fixture cannot be defined with a ${options.scope} scope${!_options?.scope && parent?.scope ? " (inherited from the base fixture)" : ""} inside the describe block. Define it at the top level of the file instead.`));
|
|
3701
|
+
}
|
|
3702
|
+
const deps = isFixtureFunction(value) ? getUsedProps(value) : new Set();
|
|
3703
|
+
const item = {
|
|
3704
|
+
name,
|
|
3705
|
+
value,
|
|
3706
|
+
auto: options.auto ?? false,
|
|
3707
|
+
injected: options.injected ?? false,
|
|
3708
|
+
scope: options.scope ?? "test",
|
|
3709
|
+
deps,
|
|
3710
|
+
parent
|
|
3711
|
+
};
|
|
3712
|
+
registrations.set(name, item);
|
|
3713
|
+
if (item.scope === "worker" && (runner.pool === "vmThreads" || runner.pool === "vmForks")) {
|
|
3714
|
+
item.scope = "file";
|
|
3715
|
+
}
|
|
3716
|
+
});
|
|
3717
|
+
// validate fixture dependency scopes
|
|
3718
|
+
for (const fixture of registrations.values()){
|
|
3719
|
+
for (const depName of fixture.deps){
|
|
3720
|
+
if (TestFixtures._builtinFixtures.includes(depName)) {
|
|
3721
|
+
continue;
|
|
3722
|
+
}
|
|
3723
|
+
const dep = registrations.get(depName);
|
|
3724
|
+
if (!dep) {
|
|
3725
|
+
errors.push(new FixtureDependencyError(`The "${fixture.name}" fixture depends on unknown fixture "${depName}".`));
|
|
3726
|
+
continue;
|
|
3727
|
+
}
|
|
3728
|
+
if (depName === fixture.name && !fixture.parent) {
|
|
3729
|
+
errors.push(new FixtureDependencyError(`The "${fixture.name}" fixture depends on itself, but does not have a base implementation.`));
|
|
3730
|
+
continue;
|
|
3731
|
+
}
|
|
3732
|
+
if (TestFixtures._fixtureScopes.indexOf(fixture.scope) > TestFixtures._fixtureScopes.indexOf(dep.scope)) {
|
|
3733
|
+
errors.push(new FixtureDependencyError(`The ${fixture.scope} "${fixture.name}" fixture cannot depend on a ${dep.scope} fixture "${dep.name}".`));
|
|
3734
|
+
continue;
|
|
3735
|
+
}
|
|
3483
3736
|
}
|
|
3484
3737
|
}
|
|
3485
|
-
|
|
3486
|
-
|
|
3738
|
+
if (errors.length === 1) {
|
|
3739
|
+
throw errors[0];
|
|
3740
|
+
} else if (errors.length > 1) {
|
|
3741
|
+
throw new AggregateError(errors, "Cannot resolve user fixtures. See errors for more information.");
|
|
3742
|
+
}
|
|
3743
|
+
return registrations;
|
|
3744
|
+
}
|
|
3487
3745
|
}
|
|
3488
|
-
const
|
|
3489
|
-
const
|
|
3490
|
-
function withFixtures(
|
|
3491
|
-
|
|
3492
|
-
|
|
3746
|
+
const cleanupFnArrayMap = new WeakMap();
|
|
3747
|
+
const contextHasFixturesCache = new WeakMap();
|
|
3748
|
+
function withFixtures(fn, options) {
|
|
3749
|
+
const collector = getCurrentSuite();
|
|
3750
|
+
const suite = options?.suite || collector.suite || collector.file;
|
|
3751
|
+
return async (hookContext)=>{
|
|
3752
|
+
const context = hookContext || options?.context;
|
|
3493
3753
|
if (!context) {
|
|
3754
|
+
if (options?.suiteHook) {
|
|
3755
|
+
validateSuiteHook(fn, options.suiteHook, options.stackTraceError);
|
|
3756
|
+
}
|
|
3494
3757
|
return fn({});
|
|
3495
3758
|
}
|
|
3496
|
-
const fixtures =
|
|
3497
|
-
if (!
|
|
3759
|
+
const fixtures = options?.fixtures || getTestFixtures(context);
|
|
3760
|
+
if (!fixtures) {
|
|
3498
3761
|
return fn(context);
|
|
3499
3762
|
}
|
|
3500
|
-
const
|
|
3501
|
-
|
|
3502
|
-
if (!usedProps.length && !hasAutoFixture) {
|
|
3763
|
+
const registrations = fixtures.get(suite);
|
|
3764
|
+
if (!registrations.size) {
|
|
3503
3765
|
return fn(context);
|
|
3504
3766
|
}
|
|
3505
|
-
|
|
3506
|
-
|
|
3767
|
+
const usedFixtures = [];
|
|
3768
|
+
const usedProps = getUsedProps(fn);
|
|
3769
|
+
for (const fixture of registrations.values()){
|
|
3770
|
+
if (fixture.auto || usedProps.has(fixture.name)) {
|
|
3771
|
+
usedFixtures.push(fixture);
|
|
3772
|
+
}
|
|
3773
|
+
}
|
|
3774
|
+
if (!usedFixtures.length) {
|
|
3775
|
+
return fn(context);
|
|
3507
3776
|
}
|
|
3508
|
-
const fixtureValueMap = fixtureValueMaps.get(context);
|
|
3509
3777
|
if (!cleanupFnArrayMap.has(context)) {
|
|
3510
3778
|
cleanupFnArrayMap.set(context, []);
|
|
3511
3779
|
}
|
|
3512
3780
|
const cleanupFnArray = cleanupFnArrayMap.get(context);
|
|
3513
|
-
const
|
|
3514
|
-
const pendingFixtures = resolveDeps(usedFixtures);
|
|
3781
|
+
const pendingFixtures = resolveDeps(usedFixtures, registrations);
|
|
3515
3782
|
if (!pendingFixtures.length) {
|
|
3516
3783
|
return fn(context);
|
|
3517
3784
|
}
|
|
3518
|
-
|
|
3519
|
-
|
|
3785
|
+
// Check if suite-level hook is trying to access test-scoped fixtures
|
|
3786
|
+
// Suite hooks (beforeAll/afterAll/aroundAll) can only access file/worker scoped fixtures
|
|
3787
|
+
if (options?.suiteHook) {
|
|
3788
|
+
const testScopedFixtures = pendingFixtures.filter((f)=>f.scope === "test");
|
|
3789
|
+
if (testScopedFixtures.length > 0) {
|
|
3790
|
+
const fixtureNames = testScopedFixtures.map((f)=>`"${f.name}"`).join(", ");
|
|
3791
|
+
const alternativeHook = {
|
|
3792
|
+
aroundAll: "aroundEach",
|
|
3793
|
+
beforeAll: "beforeEach",
|
|
3794
|
+
afterAll: "afterEach"
|
|
3795
|
+
};
|
|
3796
|
+
const error = new FixtureDependencyError(`Test-scoped fixtures cannot be used inside ${options.suiteHook} hook. ` + `The following fixtures are test-scoped: ${fixtureNames}. ` + `Use { scope: 'file' } or { scope: 'worker' } fixtures instead, or move the logic to ${alternativeHook[options.suiteHook]} hook.`);
|
|
3797
|
+
// Use stack trace from hook registration for better error location
|
|
3798
|
+
if (options.stackTraceError?.stack) {
|
|
3799
|
+
error.stack = error.message + options.stackTraceError.stack.replace(options.stackTraceError.message, "");
|
|
3800
|
+
}
|
|
3801
|
+
throw error;
|
|
3802
|
+
}
|
|
3803
|
+
}
|
|
3804
|
+
if (!contextHasFixturesCache.has(context)) {
|
|
3805
|
+
contextHasFixturesCache.set(context, new WeakSet());
|
|
3806
|
+
}
|
|
3807
|
+
const cachedFixtures = contextHasFixturesCache.get(context);
|
|
3808
|
+
for (const fixture of pendingFixtures){
|
|
3809
|
+
if (fixture.scope === "test") {
|
|
3520
3810
|
// fixture could be already initialized during "before" hook
|
|
3521
|
-
|
|
3811
|
+
// we can't check "fixture.name" in context because context may
|
|
3812
|
+
// access the parent fixture ({ a: ({ a }) => {} })
|
|
3813
|
+
if (cachedFixtures.has(fixture)) {
|
|
3522
3814
|
continue;
|
|
3523
3815
|
}
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3816
|
+
cachedFixtures.add(fixture);
|
|
3817
|
+
const resolvedValue = await resolveTestFixtureValue(fixture, context, cleanupFnArray);
|
|
3818
|
+
context[fixture.name] = resolvedValue;
|
|
3819
|
+
cleanupFnArray.push(()=>{
|
|
3820
|
+
cachedFixtures.delete(fixture);
|
|
3821
|
+
});
|
|
3822
|
+
} else {
|
|
3823
|
+
const resolvedValue = await resolveScopeFixtureValue(fixtures, suite, fixture);
|
|
3824
|
+
context[fixture.name] = resolvedValue;
|
|
3532
3825
|
}
|
|
3533
3826
|
}
|
|
3534
|
-
return
|
|
3827
|
+
return fn(context);
|
|
3535
3828
|
};
|
|
3536
3829
|
}
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
if (!fixture.isFn) {
|
|
3543
|
-
var _fixture$prop;
|
|
3544
|
-
fileContext[_fixture$prop = fixture.prop] ?? (fileContext[_fixture$prop] = fixture.value);
|
|
3545
|
-
if (workerContext) {
|
|
3546
|
-
var _fixture$prop2;
|
|
3547
|
-
workerContext[_fixture$prop2 = fixture.prop] ?? (workerContext[_fixture$prop2] = fixture.value);
|
|
3548
|
-
}
|
|
3830
|
+
function isFixtureFunction(value) {
|
|
3831
|
+
return typeof value === "function";
|
|
3832
|
+
}
|
|
3833
|
+
function resolveTestFixtureValue(fixture, context, cleanupFnArray) {
|
|
3834
|
+
if (!isFixtureFunction(fixture.value)) {
|
|
3549
3835
|
return fixture.value;
|
|
3550
3836
|
}
|
|
3551
|
-
|
|
3552
|
-
|
|
3553
|
-
|
|
3554
|
-
|
|
3555
|
-
|
|
3556
|
-
|
|
3837
|
+
return resolveFixtureFunction(fixture.value, context, cleanupFnArray);
|
|
3838
|
+
}
|
|
3839
|
+
const scopedFixturePromiseCache = new WeakMap();
|
|
3840
|
+
async function resolveScopeFixtureValue(fixtures, suite, fixture) {
|
|
3841
|
+
const workerContext = fixtures.getWorkerContext();
|
|
3842
|
+
const fileContext = fixtures.getFileContext(suite.file);
|
|
3843
|
+
const fixtureContext = fixture.scope === "worker" ? workerContext : fileContext;
|
|
3844
|
+
if (!isFixtureFunction(fixture.value)) {
|
|
3845
|
+
fixtureContext[fixture.name] = fixture.value;
|
|
3846
|
+
return fixture.value;
|
|
3557
3847
|
}
|
|
3558
|
-
|
|
3559
|
-
|
|
3560
|
-
if (!workerContext) {
|
|
3561
|
-
throw new TypeError("[@vitest/runner] The worker context is not available in the current test runner. Please, provide the `getWorkerContext` method when initiating the runner.");
|
|
3562
|
-
}
|
|
3563
|
-
fixtureContext = workerContext;
|
|
3564
|
-
} else {
|
|
3565
|
-
fixtureContext = fileContext;
|
|
3848
|
+
if (fixture.name in fixtureContext) {
|
|
3849
|
+
return fixtureContext[fixture.name];
|
|
3566
3850
|
}
|
|
3567
|
-
if (fixture
|
|
3568
|
-
return
|
|
3851
|
+
if (scopedFixturePromiseCache.has(fixture)) {
|
|
3852
|
+
return scopedFixturePromiseCache.get(fixture);
|
|
3569
3853
|
}
|
|
3570
3854
|
if (!cleanupFnArrayMap.has(fixtureContext)) {
|
|
3571
3855
|
cleanupFnArrayMap.set(fixtureContext, []);
|
|
3572
3856
|
}
|
|
3573
3857
|
const cleanupFnFileArray = cleanupFnArrayMap.get(fixtureContext);
|
|
3574
|
-
const promise = resolveFixtureFunction(fixture.value,
|
|
3575
|
-
|
|
3576
|
-
|
|
3858
|
+
const promise = resolveFixtureFunction(fixture.value, fixture.scope === "file" ? {
|
|
3859
|
+
...workerContext,
|
|
3860
|
+
...fileContext
|
|
3861
|
+
} : fixtureContext, cleanupFnFileArray).then((value)=>{
|
|
3862
|
+
fixtureContext[fixture.name] = value;
|
|
3863
|
+
scopedFixturePromiseCache.delete(fixture);
|
|
3577
3864
|
return value;
|
|
3578
3865
|
});
|
|
3579
|
-
|
|
3866
|
+
scopedFixturePromiseCache.set(fixture, promise);
|
|
3580
3867
|
return promise;
|
|
3581
3868
|
}
|
|
3582
3869
|
async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
|
|
@@ -3607,29 +3894,66 @@ async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
|
|
|
3607
3894
|
});
|
|
3608
3895
|
return useFnArgPromise;
|
|
3609
3896
|
}
|
|
3610
|
-
function resolveDeps(
|
|
3611
|
-
|
|
3897
|
+
function resolveDeps(usedFixtures, registrations, depSet = new Set(), pendingFixtures = []) {
|
|
3898
|
+
usedFixtures.forEach((fixture)=>{
|
|
3612
3899
|
if (pendingFixtures.includes(fixture)) {
|
|
3613
3900
|
return;
|
|
3614
3901
|
}
|
|
3615
|
-
if (!fixture.
|
|
3902
|
+
if (!isFixtureFunction(fixture.value) || !fixture.deps) {
|
|
3616
3903
|
pendingFixtures.push(fixture);
|
|
3617
3904
|
return;
|
|
3618
3905
|
}
|
|
3619
3906
|
if (depSet.has(fixture)) {
|
|
3620
|
-
|
|
3621
|
-
|
|
3622
|
-
|
|
3907
|
+
if (fixture.parent) {
|
|
3908
|
+
fixture = fixture.parent;
|
|
3909
|
+
} else {
|
|
3910
|
+
throw new Error(`Circular fixture dependency detected: ${fixture.name} <- ${[
|
|
3911
|
+
...depSet
|
|
3912
|
+
].reverse().map((d)=>d.name).join(" <- ")}`);
|
|
3913
|
+
}
|
|
3623
3914
|
}
|
|
3624
3915
|
depSet.add(fixture);
|
|
3625
|
-
resolveDeps(
|
|
3916
|
+
resolveDeps([
|
|
3917
|
+
...fixture.deps
|
|
3918
|
+
].map((n)=>n === fixture.name ? fixture.parent : registrations.get(n)).filter((n)=>!!n), registrations, depSet, pendingFixtures);
|
|
3626
3919
|
pendingFixtures.push(fixture);
|
|
3627
3920
|
depSet.clear();
|
|
3628
3921
|
});
|
|
3629
3922
|
return pendingFixtures;
|
|
3630
3923
|
}
|
|
3631
|
-
function
|
|
3632
|
-
|
|
3924
|
+
function validateSuiteHook(fn, hook, suiteError) {
|
|
3925
|
+
const usedProps = getUsedProps(fn, {
|
|
3926
|
+
sourceError: suiteError,
|
|
3927
|
+
suiteHook: hook
|
|
3928
|
+
});
|
|
3929
|
+
if (usedProps.size) {
|
|
3930
|
+
const error = new FixtureAccessError(`The ${hook} hook uses fixtures "${[
|
|
3931
|
+
...usedProps
|
|
3932
|
+
].join("\", \"")}", but has no access to context. ` + `Did you forget to call it as "test.${hook}()" instead of "${hook}()"?\n` + `If you used internal "suite" task as the first argument previously, access it in the second argument instead. ` + `See https://vitest.dev/guide/test-context#suite-level-hooks`);
|
|
3933
|
+
if (suiteError) {
|
|
3934
|
+
error.stack = suiteError.stack?.replace(suiteError.message, error.message);
|
|
3935
|
+
}
|
|
3936
|
+
throw error;
|
|
3937
|
+
}
|
|
3938
|
+
}
|
|
3939
|
+
const kPropsSymbol = Symbol("$vitest:fixture-props");
|
|
3940
|
+
const kPropNamesSymbol = Symbol("$vitest:fixture-prop-names");
|
|
3941
|
+
function configureProps(fn, options) {
|
|
3942
|
+
Object.defineProperty(fn, kPropsSymbol, {
|
|
3943
|
+
value: options,
|
|
3944
|
+
enumerable: false
|
|
3945
|
+
});
|
|
3946
|
+
}
|
|
3947
|
+
function memoProps(fn, props) {
|
|
3948
|
+
fn[kPropNamesSymbol] = props;
|
|
3949
|
+
return props;
|
|
3950
|
+
}
|
|
3951
|
+
function getUsedProps(fn, { sourceError, suiteHook } = {}) {
|
|
3952
|
+
if (kPropNamesSymbol in fn) {
|
|
3953
|
+
return fn[kPropNamesSymbol];
|
|
3954
|
+
}
|
|
3955
|
+
const { index: fixturesIndex = 0, original: implementation = fn } = kPropsSymbol in fn ? fn[kPropsSymbol] : {};
|
|
3956
|
+
let fnString = filterOutComments(implementation.toString());
|
|
3633
3957
|
// match lowered async function and strip it off
|
|
3634
3958
|
// example code on esbuild-try https://esbuild.github.io/try/#YgAwLjI0LjAALS1zdXBwb3J0ZWQ6YXN5bmMtYXdhaXQ9ZmFsc2UAZQBlbnRyeS50cwBjb25zdCBvID0gewogIGYxOiBhc3luYyAoKSA9PiB7fSwKICBmMjogYXN5bmMgKGEpID0+IHt9LAogIGYzOiBhc3luYyAoYSwgYikgPT4ge30sCiAgZjQ6IGFzeW5jIGZ1bmN0aW9uKGEpIHt9LAogIGY1OiBhc3luYyBmdW5jdGlvbiBmZihhKSB7fSwKICBhc3luYyBmNihhKSB7fSwKCiAgZzE6IGFzeW5jICgpID0+IHt9LAogIGcyOiBhc3luYyAoeyBhIH0pID0+IHt9LAogIGczOiBhc3luYyAoeyBhIH0sIGIpID0+IHt9LAogIGc0OiBhc3luYyBmdW5jdGlvbiAoeyBhIH0pIHt9LAogIGc1OiBhc3luYyBmdW5jdGlvbiBnZyh7IGEgfSkge30sCiAgYXN5bmMgZzYoeyBhIH0pIHt9LAoKICBoMTogYXN5bmMgKCkgPT4ge30sCiAgLy8gY29tbWVudCBiZXR3ZWVuCiAgaDI6IGFzeW5jIChhKSA9PiB7fSwKfQ
|
|
3635
3959
|
// __async(this, null, function*
|
|
@@ -3640,56 +3964,37 @@ function getUsedProps(fn) {
|
|
|
3640
3964
|
}
|
|
3641
3965
|
const match = fnString.match(/[^(]*\(([^)]*)/);
|
|
3642
3966
|
if (!match) {
|
|
3643
|
-
return
|
|
3967
|
+
return memoProps(fn, new Set());
|
|
3644
3968
|
}
|
|
3645
3969
|
const args = splitByComma(match[1]);
|
|
3646
3970
|
if (!args.length) {
|
|
3647
|
-
return
|
|
3971
|
+
return memoProps(fn, new Set());
|
|
3648
3972
|
}
|
|
3649
|
-
|
|
3650
|
-
if (
|
|
3651
|
-
|
|
3652
|
-
if (!first) {
|
|
3653
|
-
return [];
|
|
3654
|
-
}
|
|
3973
|
+
const fixturesArgument = args[fixturesIndex];
|
|
3974
|
+
if (!fixturesArgument) {
|
|
3975
|
+
return memoProps(fn, new Set());
|
|
3655
3976
|
}
|
|
3656
|
-
if (!(
|
|
3657
|
-
|
|
3977
|
+
if (!(fixturesArgument[0] === "{" && fixturesArgument.endsWith("}"))) {
|
|
3978
|
+
const ordinalArgument = ordinal(fixturesIndex + 1);
|
|
3979
|
+
const error = new FixtureParseError(`The ${ordinalArgument} argument inside a fixture must use object destructuring pattern, e.g. ({ task } => {}). ` + `Instead, received "${fixturesArgument}".` + `${suiteHook ? ` If you used internal "suite" task as the ${ordinalArgument} argument previously, access it in the ${ordinal(fixturesIndex + 2)} argument instead.` : ""}`);
|
|
3980
|
+
if (sourceError) {
|
|
3981
|
+
error.stack = sourceError.stack?.replace(sourceError.message, error.message);
|
|
3982
|
+
}
|
|
3983
|
+
throw error;
|
|
3658
3984
|
}
|
|
3659
|
-
const _first =
|
|
3985
|
+
const _first = fixturesArgument.slice(1, -1).replace(/\s/g, "");
|
|
3660
3986
|
const props = splitByComma(_first).map((prop)=>{
|
|
3661
3987
|
return prop.replace(/:.*|=.*/g, "");
|
|
3662
3988
|
});
|
|
3663
3989
|
const last = props.at(-1);
|
|
3664
3990
|
if (last && last.startsWith("...")) {
|
|
3665
|
-
|
|
3666
|
-
|
|
3667
|
-
|
|
3668
|
-
}
|
|
3669
|
-
function filterOutComments(s) {
|
|
3670
|
-
const result = [];
|
|
3671
|
-
let commentState = "none";
|
|
3672
|
-
for(let i = 0; i < s.length; ++i){
|
|
3673
|
-
if (commentState === "singleline") {
|
|
3674
|
-
if (s[i] === "\n") {
|
|
3675
|
-
commentState = "none";
|
|
3676
|
-
}
|
|
3677
|
-
} else if (commentState === "multiline") {
|
|
3678
|
-
if (s[i - 1] === "*" && s[i] === "/") {
|
|
3679
|
-
commentState = "none";
|
|
3680
|
-
}
|
|
3681
|
-
} else if (commentState === "none") {
|
|
3682
|
-
if (s[i] === "/" && s[i + 1] === "/") {
|
|
3683
|
-
commentState = "singleline";
|
|
3684
|
-
} else if (s[i] === "/" && s[i + 1] === "*") {
|
|
3685
|
-
commentState = "multiline";
|
|
3686
|
-
i += 2;
|
|
3687
|
-
} else {
|
|
3688
|
-
result.push(s[i]);
|
|
3689
|
-
}
|
|
3991
|
+
const error = new FixtureParseError(`Rest parameters are not supported in fixtures, received "${last}".`);
|
|
3992
|
+
if (sourceError) {
|
|
3993
|
+
error.stack = sourceError.stack?.replace(sourceError.message, error.message);
|
|
3690
3994
|
}
|
|
3995
|
+
throw error;
|
|
3691
3996
|
}
|
|
3692
|
-
return
|
|
3997
|
+
return memoProps(fn, new Set(props));
|
|
3693
3998
|
}
|
|
3694
3999
|
function splitByComma(s) {
|
|
3695
4000
|
const result = [];
|
|
@@ -3719,6 +4024,8 @@ function getDefaultHookTimeout() {
|
|
|
3719
4024
|
}
|
|
3720
4025
|
const CLEANUP_TIMEOUT_KEY = Symbol.for("VITEST_CLEANUP_TIMEOUT");
|
|
3721
4026
|
const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
4027
|
+
const AROUND_TIMEOUT_KEY = Symbol.for("VITEST_AROUND_TIMEOUT");
|
|
4028
|
+
const AROUND_STACK_TRACE_KEY = Symbol.for("VITEST_AROUND_STACK_TRACE");
|
|
3722
4029
|
/**
|
|
3723
4030
|
* Registers a callback function to be executed once before all tests within the current suite.
|
|
3724
4031
|
* This hook is useful for scenarios where you need to perform setup operations that are common to all tests in a suite, such as initializing a database connection or setting up a test environment.
|
|
@@ -3740,7 +4047,8 @@ const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
|
3740
4047
|
"function"
|
|
3741
4048
|
]);
|
|
3742
4049
|
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
3743
|
-
|
|
4050
|
+
const context = getChainableContext(this);
|
|
4051
|
+
return getCurrentSuite().on("beforeAll", Object.assign(withTimeout(withSuiteFixtures("beforeAll", fn, context, stackTraceError), timeout, true, stackTraceError), {
|
|
3744
4052
|
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
3745
4053
|
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
3746
4054
|
}));
|
|
@@ -3765,7 +4073,9 @@ const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
|
3765
4073
|
assertTypes(fn, "\"afterAll\" callback", [
|
|
3766
4074
|
"function"
|
|
3767
4075
|
]);
|
|
3768
|
-
|
|
4076
|
+
const context = getChainableContext(this);
|
|
4077
|
+
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
4078
|
+
return getCurrentSuite().on("afterAll", withTimeout(withSuiteFixtures("afterAll", fn, context, stackTraceError), timeout ?? getDefaultHookTimeout(), true, stackTraceError));
|
|
3769
4079
|
}
|
|
3770
4080
|
/**
|
|
3771
4081
|
* Registers a callback function to be executed before each test within the current suite.
|
|
@@ -3788,8 +4098,13 @@ const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
|
3788
4098
|
"function"
|
|
3789
4099
|
]);
|
|
3790
4100
|
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
3791
|
-
const
|
|
3792
|
-
|
|
4101
|
+
const wrapper = (context, suite)=>{
|
|
4102
|
+
const fixtureResolver = withFixtures(fn, {
|
|
4103
|
+
suite
|
|
4104
|
+
});
|
|
4105
|
+
return fixtureResolver(context);
|
|
4106
|
+
};
|
|
4107
|
+
return getCurrentSuite().on("beforeEach", Object.assign(withTimeout(wrapper, timeout ?? getDefaultHookTimeout(), true, stackTraceError, abortIfTimeout), {
|
|
3793
4108
|
[CLEANUP_TIMEOUT_KEY]: timeout,
|
|
3794
4109
|
[CLEANUP_STACK_TRACE_KEY]: stackTraceError
|
|
3795
4110
|
}));
|
|
@@ -3814,8 +4129,118 @@ const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
|
|
|
3814
4129
|
assertTypes(fn, "\"afterEach\" callback", [
|
|
3815
4130
|
"function"
|
|
3816
4131
|
]);
|
|
3817
|
-
const
|
|
3818
|
-
|
|
4132
|
+
const wrapper = (context, suite)=>{
|
|
4133
|
+
const fixtureResolver = withFixtures(fn, {
|
|
4134
|
+
suite
|
|
4135
|
+
});
|
|
4136
|
+
return fixtureResolver(context);
|
|
4137
|
+
};
|
|
4138
|
+
return getCurrentSuite().on("afterEach", withTimeout(wrapper, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
|
|
4139
|
+
}
|
|
4140
|
+
/**
|
|
4141
|
+
* Registers a callback function that wraps around all tests within the current suite.
|
|
4142
|
+
* The callback receives a `runSuite` function that must be called to run the suite's tests.
|
|
4143
|
+
* This hook is useful for scenarios where you need to wrap an entire suite in a context
|
|
4144
|
+
* (e.g., starting a server, opening a database connection that all tests share).
|
|
4145
|
+
*
|
|
4146
|
+
* **Note:** When multiple `aroundAll` hooks are registered, they are nested inside each other.
|
|
4147
|
+
* The first registered hook is the outermost wrapper.
|
|
4148
|
+
*
|
|
4149
|
+
* @param {Function} fn - The callback function that wraps the suite. Must call `runSuite()` to run the tests.
|
|
4150
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
4151
|
+
* @returns {void}
|
|
4152
|
+
* @example
|
|
4153
|
+
* ```ts
|
|
4154
|
+
* // Example of using aroundAll to wrap suite in a tracing span
|
|
4155
|
+
* aroundAll(async (runSuite) => {
|
|
4156
|
+
* await tracer.trace('test-suite', runSuite);
|
|
4157
|
+
* });
|
|
4158
|
+
* ```
|
|
4159
|
+
* @example
|
|
4160
|
+
* ```ts
|
|
4161
|
+
* // Example of using aroundAll with fixtures
|
|
4162
|
+
* aroundAll(async (runSuite, { db }) => {
|
|
4163
|
+
* await db.transaction(() => runSuite());
|
|
4164
|
+
* });
|
|
4165
|
+
* ```
|
|
4166
|
+
*/ function aroundAll(fn, timeout) {
|
|
4167
|
+
assertTypes(fn, "\"aroundAll\" callback", [
|
|
4168
|
+
"function"
|
|
4169
|
+
]);
|
|
4170
|
+
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
4171
|
+
const resolvedTimeout = timeout ?? getDefaultHookTimeout();
|
|
4172
|
+
const context = getChainableContext(this);
|
|
4173
|
+
return getCurrentSuite().on("aroundAll", Object.assign(withSuiteFixtures("aroundAll", fn, context, stackTraceError, 1), {
|
|
4174
|
+
[AROUND_TIMEOUT_KEY]: resolvedTimeout,
|
|
4175
|
+
[AROUND_STACK_TRACE_KEY]: stackTraceError
|
|
4176
|
+
}));
|
|
4177
|
+
}
|
|
4178
|
+
/**
|
|
4179
|
+
* Registers a callback function that wraps around each test within the current suite.
|
|
4180
|
+
* The callback receives a `runTest` function that must be called to run the test.
|
|
4181
|
+
* This hook is useful for scenarios where you need to wrap tests in a context (e.g., database transactions).
|
|
4182
|
+
*
|
|
4183
|
+
* **Note:** When multiple `aroundEach` hooks are registered, they are nested inside each other.
|
|
4184
|
+
* The first registered hook is the outermost wrapper.
|
|
4185
|
+
*
|
|
4186
|
+
* @param {Function} fn - The callback function that wraps the test. Must call `runTest()` to run the test.
|
|
4187
|
+
* @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
|
|
4188
|
+
* @returns {void}
|
|
4189
|
+
* @example
|
|
4190
|
+
* ```ts
|
|
4191
|
+
* // Example of using aroundEach to wrap tests in a database transaction
|
|
4192
|
+
* aroundEach(async (runTest) => {
|
|
4193
|
+
* await database.transaction(() => runTest());
|
|
4194
|
+
* });
|
|
4195
|
+
* ```
|
|
4196
|
+
* @example
|
|
4197
|
+
* ```ts
|
|
4198
|
+
* // Example of using aroundEach with fixtures
|
|
4199
|
+
* aroundEach(async (runTest, { db }) => {
|
|
4200
|
+
* await db.transaction(() => runTest());
|
|
4201
|
+
* });
|
|
4202
|
+
* ```
|
|
4203
|
+
*/ function aroundEach(fn, timeout) {
|
|
4204
|
+
assertTypes(fn, "\"aroundEach\" callback", [
|
|
4205
|
+
"function"
|
|
4206
|
+
]);
|
|
4207
|
+
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
4208
|
+
const resolvedTimeout = timeout ?? getDefaultHookTimeout();
|
|
4209
|
+
const wrapper = (runTest, context, suite)=>{
|
|
4210
|
+
const innerFn = (ctx)=>fn(runTest, ctx, suite);
|
|
4211
|
+
configureProps(innerFn, {
|
|
4212
|
+
index: 1,
|
|
4213
|
+
original: fn
|
|
4214
|
+
});
|
|
4215
|
+
const fixtureResolver = withFixtures(innerFn, {
|
|
4216
|
+
suite
|
|
4217
|
+
});
|
|
4218
|
+
return fixtureResolver(context);
|
|
4219
|
+
};
|
|
4220
|
+
return getCurrentSuite().on("aroundEach", Object.assign(wrapper, {
|
|
4221
|
+
[AROUND_TIMEOUT_KEY]: resolvedTimeout,
|
|
4222
|
+
[AROUND_STACK_TRACE_KEY]: stackTraceError
|
|
4223
|
+
}));
|
|
4224
|
+
}
|
|
4225
|
+
function withSuiteFixtures(suiteHook, fn, context, stackTraceError, contextIndex = 0) {
|
|
4226
|
+
return (...args)=>{
|
|
4227
|
+
const suite = args.at(-1);
|
|
4228
|
+
const prefix = args.slice(0, -1);
|
|
4229
|
+
const wrapper = (ctx)=>fn(...prefix, ctx, suite);
|
|
4230
|
+
configureProps(wrapper, {
|
|
4231
|
+
index: contextIndex,
|
|
4232
|
+
original: fn
|
|
4233
|
+
});
|
|
4234
|
+
const fixtures = context?.getFixtures();
|
|
4235
|
+
const fileContext = fixtures?.getFileContext(suite.file);
|
|
4236
|
+
const fixtured = withFixtures(wrapper, {
|
|
4237
|
+
suiteHook,
|
|
4238
|
+
fixtures,
|
|
4239
|
+
context: fileContext,
|
|
4240
|
+
stackTraceError
|
|
4241
|
+
});
|
|
4242
|
+
return fixtured();
|
|
4243
|
+
};
|
|
3819
4244
|
}
|
|
3820
4245
|
/**
|
|
3821
4246
|
* Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
|
|
@@ -3959,9 +4384,12 @@ function createSuiteHooks() {
|
|
|
3959
4384
|
beforeAll: [],
|
|
3960
4385
|
afterAll: [],
|
|
3961
4386
|
beforeEach: [],
|
|
3962
|
-
afterEach: []
|
|
4387
|
+
afterEach: [],
|
|
4388
|
+
aroundEach: [],
|
|
4389
|
+
aroundAll: []
|
|
3963
4390
|
};
|
|
3964
4391
|
}
|
|
4392
|
+
const POSITIVE_INFINITY = Number.POSITIVE_INFINITY;
|
|
3965
4393
|
function parseArguments(optionsOrFn, timeoutOrTest) {
|
|
3966
4394
|
if (timeoutOrTest != null && typeof timeoutOrTest === "object") {
|
|
3967
4395
|
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.`);
|
|
@@ -3990,23 +4418,59 @@ function parseArguments(optionsOrFn, timeoutOrTest) {
|
|
|
3990
4418
|
};
|
|
3991
4419
|
}
|
|
3992
4420
|
// implementations
|
|
3993
|
-
function createSuiteCollector(name, factory = ()=>{}, mode, each, suiteOptions
|
|
4421
|
+
function createSuiteCollector(name, factory = ()=>{}, mode, each, suiteOptions) {
|
|
3994
4422
|
const tasks = [];
|
|
3995
4423
|
let suite;
|
|
3996
4424
|
initSuite();
|
|
3997
4425
|
const task = function(name = "", options = {}) {
|
|
3998
|
-
|
|
3999
|
-
const
|
|
4000
|
-
const
|
|
4426
|
+
const currentSuite = collectorContext.currentSuite?.suite;
|
|
4427
|
+
const parentTask = currentSuite ?? collectorContext.currentSuite?.file;
|
|
4428
|
+
const parentTags = parentTask?.tags || [];
|
|
4429
|
+
const testTags = unique([
|
|
4430
|
+
...parentTags,
|
|
4431
|
+
...toArray(options.tags)
|
|
4432
|
+
]);
|
|
4433
|
+
const tagsOptions = testTags.map((tag)=>{
|
|
4434
|
+
const tagDefinition = runner.config.tags?.find((t)=>t.name === tag);
|
|
4435
|
+
if (!tagDefinition && runner.config.strictTags) {
|
|
4436
|
+
throw createNoTagsError(runner.config.tags, tag);
|
|
4437
|
+
}
|
|
4438
|
+
return tagDefinition;
|
|
4439
|
+
}).filter((r)=>r != null).sort((tag1, tag2)=>(tag2.priority ?? POSITIVE_INFINITY) - (tag1.priority ?? POSITIVE_INFINITY)).reduce((acc, tag)=>{
|
|
4440
|
+
const { name, description, priority, meta, ...options } = tag;
|
|
4441
|
+
Object.assign(acc, options);
|
|
4442
|
+
if (meta) {
|
|
4443
|
+
acc.meta = Object.assign(acc.meta ?? Object.create(null), meta);
|
|
4444
|
+
}
|
|
4445
|
+
return acc;
|
|
4446
|
+
}, {});
|
|
4447
|
+
const testOwnMeta = options.meta;
|
|
4448
|
+
options = {
|
|
4449
|
+
...tagsOptions,
|
|
4450
|
+
...options
|
|
4451
|
+
};
|
|
4452
|
+
const timeout = options.timeout ?? runner.config.testTimeout;
|
|
4453
|
+
const parentMeta = currentSuite?.meta;
|
|
4454
|
+
const tagMeta = tagsOptions.meta;
|
|
4455
|
+
const testMeta = Object.create(null);
|
|
4456
|
+
if (tagMeta) {
|
|
4457
|
+
Object.assign(testMeta, tagMeta);
|
|
4458
|
+
}
|
|
4459
|
+
if (parentMeta) {
|
|
4460
|
+
Object.assign(testMeta, parentMeta);
|
|
4461
|
+
}
|
|
4462
|
+
if (testOwnMeta) {
|
|
4463
|
+
Object.assign(testMeta, testOwnMeta);
|
|
4464
|
+
}
|
|
4001
4465
|
const task = {
|
|
4002
4466
|
id: "",
|
|
4003
4467
|
name,
|
|
4004
4468
|
fullName: createTaskName([
|
|
4005
|
-
|
|
4469
|
+
currentSuite?.fullName ?? collectorContext.currentSuite?.file?.fullName,
|
|
4006
4470
|
name
|
|
4007
4471
|
]),
|
|
4008
4472
|
fullTestName: createTaskName([
|
|
4009
|
-
currentSuite
|
|
4473
|
+
currentSuite?.fullTestName,
|
|
4010
4474
|
name
|
|
4011
4475
|
]),
|
|
4012
4476
|
suite: currentSuite,
|
|
@@ -4014,14 +4478,15 @@ function createSuiteCollector(name, factory = ()=>{}, mode, each, suiteOptions,
|
|
|
4014
4478
|
fails: options.fails,
|
|
4015
4479
|
context: undefined,
|
|
4016
4480
|
type: "test",
|
|
4017
|
-
file:
|
|
4481
|
+
file: currentSuite?.file ?? collectorContext.currentSuite?.file,
|
|
4018
4482
|
timeout,
|
|
4019
4483
|
retry: options.retry ?? runner.config.retry,
|
|
4020
4484
|
repeats: options.repeats,
|
|
4021
4485
|
mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run",
|
|
4022
|
-
meta:
|
|
4486
|
+
meta: testMeta,
|
|
4023
4487
|
annotations: [],
|
|
4024
|
-
artifacts: []
|
|
4488
|
+
artifacts: [],
|
|
4489
|
+
tags: testTags
|
|
4025
4490
|
};
|
|
4026
4491
|
const handler = options.handler;
|
|
4027
4492
|
if (task.mode === "run" && !handler) {
|
|
@@ -4030,21 +4495,22 @@ function createSuiteCollector(name, factory = ()=>{}, mode, each, suiteOptions,
|
|
|
4030
4495
|
if (options.concurrent || !options.sequential && runner.config.sequence.concurrent) {
|
|
4031
4496
|
task.concurrent = true;
|
|
4032
4497
|
}
|
|
4033
|
-
task.shuffle = suiteOptions
|
|
4498
|
+
task.shuffle = suiteOptions?.shuffle;
|
|
4034
4499
|
const context = createTestContext(task, runner);
|
|
4035
4500
|
// create test context
|
|
4036
4501
|
Object.defineProperty(task, "context", {
|
|
4037
4502
|
value: context,
|
|
4038
4503
|
enumerable: false
|
|
4039
4504
|
});
|
|
4040
|
-
setTestFixture(context, options.fixtures);
|
|
4041
|
-
// custom can be called from any place, let's assume the limit is 15 stacks
|
|
4505
|
+
setTestFixture(context, options.fixtures ?? new TestFixtures());
|
|
4042
4506
|
const limit = Error.stackTraceLimit;
|
|
4043
|
-
Error.stackTraceLimit =
|
|
4507
|
+
Error.stackTraceLimit = 10;
|
|
4044
4508
|
const stackTraceError = new Error("STACK_TRACE_ERROR");
|
|
4045
4509
|
Error.stackTraceLimit = limit;
|
|
4046
4510
|
if (handler) {
|
|
4047
|
-
setFn(task, withTimeout(withAwaitAsyncAssertions(withFixtures(
|
|
4511
|
+
setFn(task, withTimeout(withCancel(withAwaitAsyncAssertions(withFixtures(handler, {
|
|
4512
|
+
context
|
|
4513
|
+
}), task), task.context.signal), timeout, false, stackTraceError, (_, error)=>abortIfTimeout([
|
|
4048
4514
|
context
|
|
4049
4515
|
], error)));
|
|
4050
4516
|
}
|
|
@@ -4068,8 +4534,14 @@ function createSuiteCollector(name, factory = ()=>{}, mode, each, suiteOptions,
|
|
|
4068
4534
|
options = Object.assign({}, suiteOptions, options);
|
|
4069
4535
|
}
|
|
4070
4536
|
// inherit concurrent / sequential from suite
|
|
4071
|
-
|
|
4072
|
-
options.
|
|
4537
|
+
const concurrent = this.concurrent ?? (!this.sequential && options?.concurrent);
|
|
4538
|
+
if (options.concurrent != null && concurrent != null) {
|
|
4539
|
+
options.concurrent = concurrent;
|
|
4540
|
+
}
|
|
4541
|
+
const sequential = this.sequential ?? (!this.concurrent && options?.sequential);
|
|
4542
|
+
if (options.sequential != null && sequential != null) {
|
|
4543
|
+
options.sequential = sequential;
|
|
4544
|
+
}
|
|
4073
4545
|
const test = task(formatName(name), {
|
|
4074
4546
|
...this,
|
|
4075
4547
|
...options,
|
|
@@ -4077,7 +4549,6 @@ function createSuiteCollector(name, factory = ()=>{}, mode, each, suiteOptions,
|
|
|
4077
4549
|
});
|
|
4078
4550
|
test.type = "test";
|
|
4079
4551
|
});
|
|
4080
|
-
let collectorFixtures = parentCollectorFixtures;
|
|
4081
4552
|
const collector = {
|
|
4082
4553
|
type: "collector",
|
|
4083
4554
|
name,
|
|
@@ -4085,54 +4556,50 @@ function createSuiteCollector(name, factory = ()=>{}, mode, each, suiteOptions,
|
|
|
4085
4556
|
suite,
|
|
4086
4557
|
options: suiteOptions,
|
|
4087
4558
|
test,
|
|
4559
|
+
file: suite.file,
|
|
4088
4560
|
tasks,
|
|
4089
4561
|
collect,
|
|
4090
4562
|
task,
|
|
4091
4563
|
clear,
|
|
4092
|
-
on: addHook
|
|
4093
|
-
fixtures () {
|
|
4094
|
-
return collectorFixtures;
|
|
4095
|
-
},
|
|
4096
|
-
scoped (fixtures) {
|
|
4097
|
-
const parsed = mergeContextFixtures(fixtures, {
|
|
4098
|
-
fixtures: collectorFixtures
|
|
4099
|
-
}, runner);
|
|
4100
|
-
if (parsed.fixtures) {
|
|
4101
|
-
collectorFixtures = parsed.fixtures;
|
|
4102
|
-
}
|
|
4103
|
-
}
|
|
4564
|
+
on: addHook
|
|
4104
4565
|
};
|
|
4105
4566
|
function addHook(name, ...fn) {
|
|
4106
4567
|
getHooks(suite)[name].push(...fn);
|
|
4107
4568
|
}
|
|
4108
4569
|
function initSuite(includeLocation) {
|
|
4109
|
-
var _collectorContext$cur4, _collectorContext$cur5, _collectorContext$cur6;
|
|
4110
4570
|
if (typeof suiteOptions === "number") {
|
|
4111
4571
|
suiteOptions = {
|
|
4112
4572
|
timeout: suiteOptions
|
|
4113
4573
|
};
|
|
4114
4574
|
}
|
|
4115
|
-
const currentSuite =
|
|
4575
|
+
const currentSuite = collectorContext.currentSuite?.suite;
|
|
4576
|
+
const parentTask = currentSuite ?? collectorContext.currentSuite?.file;
|
|
4577
|
+
const suiteTags = toArray(suiteOptions?.tags);
|
|
4578
|
+
validateTags(runner.config, suiteTags);
|
|
4116
4579
|
suite = {
|
|
4117
4580
|
id: "",
|
|
4118
4581
|
type: "suite",
|
|
4119
4582
|
name,
|
|
4120
4583
|
fullName: createTaskName([
|
|
4121
|
-
|
|
4584
|
+
currentSuite?.fullName ?? collectorContext.currentSuite?.file?.fullName,
|
|
4122
4585
|
name
|
|
4123
4586
|
]),
|
|
4124
4587
|
fullTestName: createTaskName([
|
|
4125
|
-
currentSuite
|
|
4588
|
+
currentSuite?.fullTestName,
|
|
4126
4589
|
name
|
|
4127
4590
|
]),
|
|
4128
4591
|
suite: currentSuite,
|
|
4129
4592
|
mode,
|
|
4130
4593
|
each,
|
|
4131
|
-
file:
|
|
4132
|
-
shuffle: suiteOptions
|
|
4594
|
+
file: currentSuite?.file ?? collectorContext.currentSuite?.file,
|
|
4595
|
+
shuffle: suiteOptions?.shuffle,
|
|
4133
4596
|
tasks: [],
|
|
4134
|
-
meta: Object.create(null),
|
|
4135
|
-
concurrent: suiteOptions
|
|
4597
|
+
meta: suiteOptions?.meta ?? Object.create(null),
|
|
4598
|
+
concurrent: suiteOptions?.concurrent,
|
|
4599
|
+
tags: unique([
|
|
4600
|
+
...parentTask?.tags || [],
|
|
4601
|
+
...suiteTags
|
|
4602
|
+
])
|
|
4136
4603
|
};
|
|
4137
4604
|
setHooks(suite, createSuiteHooks());
|
|
4138
4605
|
}
|
|
@@ -4173,31 +4640,43 @@ function withAwaitAsyncAssertions(fn, task) {
|
|
|
4173
4640
|
}
|
|
4174
4641
|
function createSuite() {
|
|
4175
4642
|
function suiteFn(name, factoryOrOptions, optionsOrFactory) {
|
|
4176
|
-
var _currentSuite$options;
|
|
4177
|
-
let mode = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run";
|
|
4178
4643
|
const currentSuite = collectorContext.currentSuite || defaultSuite;
|
|
4179
4644
|
let { options, handler: factory } = parseArguments(factoryOrOptions, optionsOrFactory);
|
|
4180
|
-
if (mode === "run" && !factory) {
|
|
4181
|
-
mode = "todo";
|
|
4182
|
-
}
|
|
4183
4645
|
const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false;
|
|
4184
4646
|
const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false;
|
|
4647
|
+
const { meta: parentMeta, ...parentOptions } = currentSuite?.options || {};
|
|
4185
4648
|
// inherit options from current suite
|
|
4186
4649
|
options = {
|
|
4187
|
-
...
|
|
4188
|
-
...options
|
|
4189
|
-
shuffle: this.shuffle ?? options.shuffle ?? (currentSuite === null || currentSuite === void 0 || (_currentSuite$options = currentSuite.options) === null || _currentSuite$options === void 0 ? void 0 : _currentSuite$options.shuffle) ?? (void 0 )
|
|
4650
|
+
...parentOptions,
|
|
4651
|
+
...options
|
|
4190
4652
|
};
|
|
4653
|
+
const shuffle = this.shuffle ?? options.shuffle ?? currentSuite?.options?.shuffle ?? runner?.config.sequence.shuffle;
|
|
4654
|
+
if (shuffle != null) {
|
|
4655
|
+
options.shuffle = shuffle;
|
|
4656
|
+
}
|
|
4657
|
+
let mode = this.only ?? options.only ? "only" : this.skip ?? options.skip ? "skip" : this.todo ?? options.todo ? "todo" : "run";
|
|
4658
|
+
// passed as test(name), assume it's a "todo"
|
|
4659
|
+
if (mode === "run" && !factory) {
|
|
4660
|
+
mode = "todo";
|
|
4661
|
+
}
|
|
4191
4662
|
// inherit concurrent / sequential from suite
|
|
4192
4663
|
const isConcurrent = isConcurrentSpecified || options.concurrent && !isSequentialSpecified;
|
|
4193
4664
|
const isSequential = isSequentialSpecified || options.sequential && !isConcurrentSpecified;
|
|
4194
|
-
|
|
4195
|
-
|
|
4196
|
-
|
|
4665
|
+
if (isConcurrent != null) {
|
|
4666
|
+
options.concurrent = isConcurrent && !isSequential;
|
|
4667
|
+
}
|
|
4668
|
+
if (isSequential != null) {
|
|
4669
|
+
options.sequential = isSequential && !isConcurrent;
|
|
4670
|
+
}
|
|
4671
|
+
if (parentMeta) {
|
|
4672
|
+
options.meta = Object.assign(Object.create(null), parentMeta, options.meta);
|
|
4673
|
+
}
|
|
4674
|
+
return createSuiteCollector(formatName(name), factory, mode, this.each, options);
|
|
4197
4675
|
}
|
|
4198
4676
|
suiteFn.each = function(cases, ...args) {
|
|
4199
|
-
const
|
|
4200
|
-
|
|
4677
|
+
const context = getChainableContext(this);
|
|
4678
|
+
const suite = context.withContext();
|
|
4679
|
+
context.setContext("each", true);
|
|
4201
4680
|
if (Array.isArray(cases) && args.length) {
|
|
4202
4681
|
cases = formatTemplateString(cases, args);
|
|
4203
4682
|
}
|
|
@@ -4224,7 +4703,7 @@ function createSuite() {
|
|
|
4224
4703
|
}
|
|
4225
4704
|
}
|
|
4226
4705
|
});
|
|
4227
|
-
|
|
4706
|
+
context.setContext("each", undefined);
|
|
4228
4707
|
};
|
|
4229
4708
|
};
|
|
4230
4709
|
suiteFn.for = function(cases, ...args) {
|
|
@@ -4250,11 +4729,12 @@ function createSuite() {
|
|
|
4250
4729
|
"todo"
|
|
4251
4730
|
], suiteFn);
|
|
4252
4731
|
}
|
|
4253
|
-
function createTaskCollector(fn
|
|
4732
|
+
function createTaskCollector(fn) {
|
|
4254
4733
|
const taskFn = fn;
|
|
4255
4734
|
taskFn.each = function(cases, ...args) {
|
|
4256
|
-
const
|
|
4257
|
-
|
|
4735
|
+
const context = getChainableContext(this);
|
|
4736
|
+
const test = context.withContext();
|
|
4737
|
+
context.setContext("each", true);
|
|
4258
4738
|
if (Array.isArray(cases) && args.length) {
|
|
4259
4739
|
cases = formatTemplateString(cases, args);
|
|
4260
4740
|
}
|
|
@@ -4281,11 +4761,12 @@ function createTaskCollector(fn, context) {
|
|
|
4281
4761
|
}
|
|
4282
4762
|
}
|
|
4283
4763
|
});
|
|
4284
|
-
|
|
4764
|
+
context.setContext("each", undefined);
|
|
4285
4765
|
};
|
|
4286
4766
|
};
|
|
4287
4767
|
taskFn.for = function(cases, ...args) {
|
|
4288
|
-
const
|
|
4768
|
+
const context = getChainableContext(this);
|
|
4769
|
+
const test = context.withContext();
|
|
4289
4770
|
if (Array.isArray(cases) && args.length) {
|
|
4290
4771
|
cases = formatTemplateString(cases, args);
|
|
4291
4772
|
}
|
|
@@ -4296,8 +4777,10 @@ function createTaskCollector(fn, context) {
|
|
|
4296
4777
|
// monkey-patch handler to allow parsing fixture
|
|
4297
4778
|
const handlerWrapper = handler ? (ctx)=>handler(item, ctx) : undefined;
|
|
4298
4779
|
if (handlerWrapper) {
|
|
4299
|
-
handlerWrapper
|
|
4300
|
-
|
|
4780
|
+
configureProps(handlerWrapper, {
|
|
4781
|
+
index: 1,
|
|
4782
|
+
original: handler
|
|
4783
|
+
});
|
|
4301
4784
|
}
|
|
4302
4785
|
test(formatTitle(_name, toArray(item), idx), options, handlerWrapper);
|
|
4303
4786
|
});
|
|
@@ -4309,29 +4792,110 @@ function createTaskCollector(fn, context) {
|
|
|
4309
4792
|
taskFn.runIf = function(condition) {
|
|
4310
4793
|
return condition ? this : this.skip;
|
|
4311
4794
|
};
|
|
4312
|
-
|
|
4313
|
-
|
|
4314
|
-
|
|
4315
|
-
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
|
|
4323
|
-
|
|
4795
|
+
/**
|
|
4796
|
+
* Parse builder pattern arguments into a fixtures object.
|
|
4797
|
+
* Handles both builder pattern (name, options?, value) and object syntax.
|
|
4798
|
+
*/ function parseBuilderFixtures(fixturesOrName, optionsOrFn, maybeFn) {
|
|
4799
|
+
// Object syntax: just return as-is
|
|
4800
|
+
if (typeof fixturesOrName !== "string") {
|
|
4801
|
+
return fixturesOrName;
|
|
4802
|
+
}
|
|
4803
|
+
const fixtureName = fixturesOrName;
|
|
4804
|
+
let fixtureOptions;
|
|
4805
|
+
let fixtureValue;
|
|
4806
|
+
if (maybeFn !== undefined) {
|
|
4807
|
+
// (name, options, value) or (name, options, fn)
|
|
4808
|
+
fixtureOptions = optionsOrFn;
|
|
4809
|
+
fixtureValue = maybeFn;
|
|
4810
|
+
} else {
|
|
4811
|
+
// (name, value) or (name, fn)
|
|
4812
|
+
// Check if optionsOrFn looks like fixture options (has scope or auto)
|
|
4813
|
+
if (optionsOrFn !== null && typeof optionsOrFn === "object" && !Array.isArray(optionsOrFn) && ("scope" in optionsOrFn || "auto" in optionsOrFn)) {
|
|
4814
|
+
// (name, options) with no value - treat as empty object fixture
|
|
4815
|
+
fixtureOptions = optionsOrFn;
|
|
4816
|
+
fixtureValue = {};
|
|
4817
|
+
} else {
|
|
4818
|
+
// (name, value) or (name, fn)
|
|
4819
|
+
fixtureOptions = undefined;
|
|
4820
|
+
fixtureValue = optionsOrFn;
|
|
4821
|
+
}
|
|
4822
|
+
}
|
|
4823
|
+
// Function value: wrap with onCleanup pattern
|
|
4824
|
+
if (typeof fixtureValue === "function") {
|
|
4825
|
+
const builderFn = fixtureValue;
|
|
4826
|
+
// Wrap builder pattern function (returns value) to use() pattern
|
|
4827
|
+
const fixture = async (ctx, use)=>{
|
|
4828
|
+
let cleanup;
|
|
4829
|
+
const onCleanup = (fn)=>{
|
|
4830
|
+
if (cleanup !== undefined) {
|
|
4831
|
+
throw new Error(`onCleanup can only be called once per fixture. ` + `Define separate fixtures if you need multiple cleanup functions.`);
|
|
4832
|
+
}
|
|
4833
|
+
cleanup = fn;
|
|
4834
|
+
};
|
|
4835
|
+
const value = await builderFn(ctx, {
|
|
4836
|
+
onCleanup
|
|
4837
|
+
});
|
|
4838
|
+
await use(value);
|
|
4839
|
+
if (cleanup) {
|
|
4840
|
+
await cleanup();
|
|
4841
|
+
}
|
|
4324
4842
|
};
|
|
4325
|
-
|
|
4326
|
-
|
|
4843
|
+
configureProps(fixture, {
|
|
4844
|
+
original: builderFn
|
|
4845
|
+
});
|
|
4846
|
+
if (fixtureOptions) {
|
|
4847
|
+
return {
|
|
4848
|
+
[fixtureName]: [
|
|
4849
|
+
fixture,
|
|
4850
|
+
fixtureOptions
|
|
4851
|
+
]
|
|
4852
|
+
};
|
|
4327
4853
|
}
|
|
4328
|
-
|
|
4329
|
-
|
|
4854
|
+
return {
|
|
4855
|
+
[fixtureName]: fixture
|
|
4856
|
+
};
|
|
4857
|
+
}
|
|
4858
|
+
// Non-function value: use directly
|
|
4859
|
+
if (fixtureOptions) {
|
|
4860
|
+
return {
|
|
4861
|
+
[fixtureName]: [
|
|
4862
|
+
fixtureValue,
|
|
4863
|
+
fixtureOptions
|
|
4864
|
+
]
|
|
4865
|
+
};
|
|
4866
|
+
}
|
|
4867
|
+
return {
|
|
4868
|
+
[fixtureName]: fixtureValue
|
|
4869
|
+
};
|
|
4870
|
+
}
|
|
4871
|
+
taskFn.override = function(fixturesOrName, optionsOrFn, maybeFn) {
|
|
4872
|
+
const userFixtures = parseBuilderFixtures(fixturesOrName, optionsOrFn, maybeFn);
|
|
4873
|
+
getChainableContext(this).getFixtures().override(runner, userFixtures);
|
|
4874
|
+
return this;
|
|
4875
|
+
};
|
|
4876
|
+
taskFn.scoped = function(fixtures) {
|
|
4877
|
+
console.warn(`test.scoped() is deprecated and will be removed in future versions. Please use test.override() instead.`);
|
|
4878
|
+
return this.override(fixtures);
|
|
4330
4879
|
};
|
|
4880
|
+
taskFn.extend = function(fixturesOrName, optionsOrFn, maybeFn) {
|
|
4881
|
+
const userFixtures = parseBuilderFixtures(fixturesOrName, optionsOrFn, maybeFn);
|
|
4882
|
+
const fixtures = getChainableContext(this).getFixtures().extend(runner, userFixtures);
|
|
4883
|
+
const _test = createTest(function(name, optionsOrFn, optionsOrTest) {
|
|
4884
|
+
fn.call(this, formatName(name), optionsOrFn, optionsOrTest);
|
|
4885
|
+
});
|
|
4886
|
+
getChainableContext(_test).mergeContext({
|
|
4887
|
+
fixtures
|
|
4888
|
+
});
|
|
4889
|
+
return _test;
|
|
4890
|
+
};
|
|
4891
|
+
taskFn.describe = suite;
|
|
4892
|
+
taskFn.suite = suite;
|
|
4331
4893
|
taskFn.beforeEach = beforeEach;
|
|
4332
4894
|
taskFn.afterEach = afterEach;
|
|
4333
4895
|
taskFn.beforeAll = beforeAll;
|
|
4334
4896
|
taskFn.afterAll = afterAll;
|
|
4897
|
+
taskFn.aroundEach = aroundEach;
|
|
4898
|
+
taskFn.aroundAll = aroundAll;
|
|
4335
4899
|
const _test = createChainable([
|
|
4336
4900
|
"concurrent",
|
|
4337
4901
|
"sequential",
|
|
@@ -4339,14 +4903,13 @@ function createTaskCollector(fn, context) {
|
|
|
4339
4903
|
"only",
|
|
4340
4904
|
"todo",
|
|
4341
4905
|
"fails"
|
|
4342
|
-
], taskFn
|
|
4343
|
-
|
|
4344
|
-
|
|
4345
|
-
}
|
|
4906
|
+
], taskFn, {
|
|
4907
|
+
fixtures: new TestFixtures()
|
|
4908
|
+
});
|
|
4346
4909
|
return _test;
|
|
4347
4910
|
}
|
|
4348
|
-
function createTest(fn
|
|
4349
|
-
return createTaskCollector(fn
|
|
4911
|
+
function createTest(fn) {
|
|
4912
|
+
return createTaskCollector(fn);
|
|
4350
4913
|
}
|
|
4351
4914
|
function formatName(name) {
|
|
4352
4915
|
return typeof name === "string" ? name : typeof name === "function" ? name.name || "<anonymous>" : String(name);
|
|
@@ -4380,7 +4943,7 @@ function formatTitle(template, items, idx) {
|
|
|
4380
4943
|
const arrayElement = isArrayKey ? objectAttr(items, key) : undefined;
|
|
4381
4944
|
const value = isObjectItem ? objectAttr(items[0], key, arrayElement) : arrayElement;
|
|
4382
4945
|
return objDisplay(value, {
|
|
4383
|
-
truncate:
|
|
4946
|
+
truncate: runner?.config?.chaiConfig?.truncateThreshold
|
|
4384
4947
|
});
|
|
4385
4948
|
});
|
|
4386
4949
|
}
|
|
@@ -4425,13 +4988,12 @@ function formatTemplateString(cases, args) {
|
|
|
4425
4988
|
}
|
|
4426
4989
|
return res;
|
|
4427
4990
|
}
|
|
4428
|
-
const now$2 = Date.now;
|
|
4991
|
+
const now$2 = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
4429
4992
|
const collectorContext = {
|
|
4430
4993
|
currentSuite: null
|
|
4431
4994
|
};
|
|
4432
4995
|
function collectTask(task) {
|
|
4433
|
-
|
|
4434
|
-
(_collectorContext$cur = collectorContext.currentSuite) === null || _collectorContext$cur === void 0 ? void 0 : _collectorContext$cur.tasks.push(task);
|
|
4996
|
+
collectorContext.currentSuite?.tasks.push(task);
|
|
4435
4997
|
}
|
|
4436
4998
|
async function runWithSuite(suite, fn) {
|
|
4437
4999
|
const prev = collectorContext.currentSuite;
|
|
@@ -4451,16 +5013,15 @@ function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
|
4451
5013
|
runner._currentTaskStartTime = startTime;
|
|
4452
5014
|
runner._currentTaskTimeout = timeout;
|
|
4453
5015
|
return new Promise((resolve_, reject_)=>{
|
|
4454
|
-
var _timer$unref;
|
|
4455
5016
|
const timer = setTimeout(()=>{
|
|
4456
5017
|
clearTimeout(timer);
|
|
4457
5018
|
rejectTimeoutError();
|
|
4458
5019
|
}, timeout);
|
|
4459
5020
|
// `unref` might not exist in browser
|
|
4460
|
-
|
|
5021
|
+
timer.unref?.();
|
|
4461
5022
|
function rejectTimeoutError() {
|
|
4462
5023
|
const error = makeTimeoutError(isHook, timeout, stackTraceError);
|
|
4463
|
-
onTimeout
|
|
5024
|
+
onTimeout?.(args, error);
|
|
4464
5025
|
reject_(error);
|
|
4465
5026
|
}
|
|
4466
5027
|
function resolve(result) {
|
|
@@ -4499,6 +5060,23 @@ function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
|
|
|
4499
5060
|
});
|
|
4500
5061
|
};
|
|
4501
5062
|
}
|
|
5063
|
+
function withCancel(fn, signal) {
|
|
5064
|
+
return function runWithCancel(...args) {
|
|
5065
|
+
return new Promise((resolve, reject)=>{
|
|
5066
|
+
signal.addEventListener("abort", ()=>reject(signal.reason));
|
|
5067
|
+
try {
|
|
5068
|
+
const result = fn(...args);
|
|
5069
|
+
if (typeof result === "object" && result != null && typeof result.then === "function") {
|
|
5070
|
+
result.then(resolve, reject);
|
|
5071
|
+
} else {
|
|
5072
|
+
resolve(result);
|
|
5073
|
+
}
|
|
5074
|
+
} catch (error) {
|
|
5075
|
+
reject(error);
|
|
5076
|
+
}
|
|
5077
|
+
});
|
|
5078
|
+
};
|
|
5079
|
+
}
|
|
4502
5080
|
const abortControllers = new WeakMap();
|
|
4503
5081
|
function abortIfTimeout([context], error) {
|
|
4504
5082
|
if (context) {
|
|
@@ -4507,10 +5085,9 @@ function abortIfTimeout([context], error) {
|
|
|
4507
5085
|
}
|
|
4508
5086
|
function abortContextSignal(context, error) {
|
|
4509
5087
|
const abortController = abortControllers.get(context);
|
|
4510
|
-
abortController
|
|
5088
|
+
abortController?.abort(error);
|
|
4511
5089
|
}
|
|
4512
5090
|
function createTestContext(test, runner) {
|
|
4513
|
-
var _runner$extendTaskCon;
|
|
4514
5091
|
const context = function() {
|
|
4515
5092
|
throw new Error("done() callback is deprecated, use promise instead");
|
|
4516
5093
|
};
|
|
@@ -4526,9 +5103,9 @@ function createTestContext(test, runner) {
|
|
|
4526
5103
|
// do nothing
|
|
4527
5104
|
return undefined;
|
|
4528
5105
|
}
|
|
4529
|
-
test.result
|
|
5106
|
+
test.result ??= {
|
|
4530
5107
|
state: "skip"
|
|
4531
|
-
}
|
|
5108
|
+
};
|
|
4532
5109
|
test.result.pending = true;
|
|
4533
5110
|
throw new PendingError("test is skipped; abort execution", test, typeof condition === "string" ? condition : note);
|
|
4534
5111
|
};
|
|
@@ -4559,31 +5136,23 @@ function createTestContext(test, runner) {
|
|
|
4559
5136
|
}));
|
|
4560
5137
|
};
|
|
4561
5138
|
context.onTestFailed = (handler, timeout)=>{
|
|
4562
|
-
test.onFailed
|
|
5139
|
+
test.onFailed ||= [];
|
|
4563
5140
|
test.onFailed.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error)=>abortController.abort(error)));
|
|
4564
5141
|
};
|
|
4565
5142
|
context.onTestFinished = (handler, timeout)=>{
|
|
4566
|
-
test.onFinished
|
|
5143
|
+
test.onFinished ||= [];
|
|
4567
5144
|
test.onFinished.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error)=>abortController.abort(error)));
|
|
4568
5145
|
};
|
|
4569
|
-
return
|
|
5146
|
+
return runner.extendTaskContext?.(context) || context;
|
|
4570
5147
|
}
|
|
4571
5148
|
function makeTimeoutError(isHook, timeout, stackTraceError) {
|
|
4572
5149
|
const message = `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.\nIf this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
|
|
4573
5150
|
const error = new Error(message);
|
|
4574
|
-
if (stackTraceError
|
|
5151
|
+
if (stackTraceError?.stack) {
|
|
4575
5152
|
error.stack = stackTraceError.stack.replace(error.message, stackTraceError.message);
|
|
4576
5153
|
}
|
|
4577
5154
|
return error;
|
|
4578
5155
|
}
|
|
4579
|
-
const fileContexts = new WeakMap();
|
|
4580
|
-
function getFileContext(file) {
|
|
4581
|
-
const context = fileContexts.get(file);
|
|
4582
|
-
if (!context) {
|
|
4583
|
-
throw new Error(`Cannot find file context for ${file.name}`);
|
|
4584
|
-
}
|
|
4585
|
-
return context;
|
|
4586
|
-
}
|
|
4587
5156
|
globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
4588
5157
|
globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
|
|
4589
5158
|
getSafeTimers();
|
|
@@ -4592,7 +5161,6 @@ const eventsPacks = [];
|
|
|
4592
5161
|
const pendingTasksUpdates = [];
|
|
4593
5162
|
function sendTasksUpdate(runner) {
|
|
4594
5163
|
if (packs.size) {
|
|
4595
|
-
var _runner$onTaskUpdate;
|
|
4596
5164
|
const taskPacks = Array.from(packs).map(([id, task])=>{
|
|
4597
5165
|
return [
|
|
4598
5166
|
id,
|
|
@@ -4600,7 +5168,7 @@ function sendTasksUpdate(runner) {
|
|
|
4600
5168
|
task[1]
|
|
4601
5169
|
];
|
|
4602
5170
|
});
|
|
4603
|
-
const p =
|
|
5171
|
+
const p = runner.onTaskUpdate?.(taskPacks, eventsPacks);
|
|
4604
5172
|
if (p) {
|
|
4605
5173
|
pendingTasksUpdates.push(p);
|
|
4606
5174
|
// remove successful promise to not grow array indefnitely,
|
|
@@ -4625,12 +5193,13 @@ async function finishSendTasksUpdate(runner) {
|
|
|
4625
5193
|
*
|
|
4626
5194
|
* Vitest automatically injects the source location where the artifact was created and manages any attachments you include.
|
|
4627
5195
|
*
|
|
5196
|
+
* **Note:** artifacts must be recorded before the task is reported. Any artifacts recorded after that will not be included in the task.
|
|
5197
|
+
*
|
|
4628
5198
|
* @param task - The test task context, typically accessed via `this.task` in custom matchers or `context.task` in tests
|
|
4629
5199
|
* @param artifact - The artifact to record. Must extend {@linkcode TestArtifactBase}
|
|
4630
5200
|
*
|
|
4631
5201
|
* @returns A promise that resolves to the recorded artifact with location injected
|
|
4632
5202
|
*
|
|
4633
|
-
* @throws {Error} If called after the test has finished running
|
|
4634
5203
|
* @throws {Error} If the test runner doesn't support artifacts
|
|
4635
5204
|
*
|
|
4636
5205
|
* @example
|
|
@@ -4650,9 +5219,6 @@ async function finishSendTasksUpdate(runner) {
|
|
|
4650
5219
|
* ```
|
|
4651
5220
|
*/ async function recordArtifact(task, artifact) {
|
|
4652
5221
|
const runner = getRunner();
|
|
4653
|
-
if (task.result && task.result.state !== "run") {
|
|
4654
|
-
throw new Error(`Cannot record a test artifact outside of the test run. The test "${task.name}" finished running with the "${task.result.state}" state already.`);
|
|
4655
|
-
}
|
|
4656
5222
|
const stack = findTestFileStackTrace(task.file.filepath, new Error("STACK_TRACE").stack);
|
|
4657
5223
|
if (stack) {
|
|
4658
5224
|
artifact.location = {
|