vitest 4.1.1 → 4.1.3
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/browser.d.ts +1 -1
- package/dist/browser.js +1 -1
- package/dist/chunks/{base.C9_VThnT.js → base.C3wvLFNM.js} +7 -7
- package/dist/chunks/{benchmark.D0SlKNbZ.js → benchmark.CX_oY03V.js} +1 -1
- package/dist/chunks/{browser.d.X3SXoOCV.d.ts → browser.d.C0zGu1u9.d.ts} +1 -1
- package/dist/chunks/{cac.CHfKU_gf.js → cac.Bb7YBzMA.js} +24 -10
- package/dist/chunks/{cli-api.BUXBO6jS.js → cli-api.CaPRsymo.js} +47 -14
- package/dist/chunks/{config.d.EJLVE3es.d.ts → config.d.ChUh6-ad.d.ts} +2 -0
- package/dist/chunks/{coverage.CJ2HXVIG.js → coverage.CwUlQe0s.js} +13 -3
- package/dist/chunks/{defaults.CdU2lD-q.js → defaults.9aQKnqFk.js} +13 -5
- package/dist/chunks/{global.d.x-ILCfAE.d.ts → global.d.D74z04P1.d.ts} +2 -0
- package/dist/chunks/{globals.8mibwXRO.js → globals.7B-4LHAF.js} +4 -4
- package/dist/chunks/{index.Dkvtd-Ku.js → index.4L3g53iW.js} +3 -2
- package/dist/chunks/{index.6q6giCZO.js → index.Cj9kDiDi.js} +2 -2
- package/dist/chunks/{index.DXMFO5MJ.js → index.DICur-LY.js} +628 -586
- package/dist/chunks/{init-forks.B4YYSIj4.js → init-forks.UV3ZQGQH.js} +1 -1
- package/dist/chunks/{init-threads.Bd2Hsaex.js → init-threads.D3eCsY76.js} +1 -1
- package/dist/chunks/{init.BVxhC4nR.js → init.D98-gwRW.js} +3 -1
- package/dist/chunks/{nativeModuleMocker.Dd1Q1VIw.js → nativeModuleMocker.BRN2oBJd.js} +1 -1
- package/dist/chunks/{plugin.d.Dx0ozo6e.d.ts → plugin.d.BssAumYw.d.ts} +1 -1
- package/dist/chunks/{reporters.d.CZ5E0GCT.d.ts → reporters.d.yJ2fBir5.d.ts} +17 -8
- package/dist/chunks/{setup-common.CB31_KSV.js → setup-common.NdrZGMhw.js} +1 -1
- package/dist/chunks/{startVitestModuleRunner.C3FXk5Gv.js → startVitestModuleRunner.bRl2_oI_.js} +16 -2
- package/dist/chunks/{test.CBQUpOM3.js → test.BmQO5GaM.js} +402 -336
- package/dist/chunks/utils.DK8FXp4T.js +189 -0
- package/dist/chunks/{vm.DId8XBJa.js → vm.DVLYObm9.js} +7 -6
- package/dist/chunks/{worker.d.B84sVRy0.d.ts → worker.d.CckNUvI5.d.ts} +1 -1
- package/dist/cli.js +2 -20
- package/dist/config.cjs +13 -5
- package/dist/config.d.ts +8 -8
- package/dist/config.js +1 -1
- package/dist/coverage.d.ts +4 -4
- package/dist/coverage.js +2 -2
- package/dist/index.d.ts +79 -9
- package/dist/index.js +4 -4
- package/dist/node.d.ts +8 -8
- package/dist/node.js +8 -8
- package/dist/reporters.d.ts +4 -4
- package/dist/reporters.js +6 -6
- package/dist/runners.d.ts +1 -1
- package/dist/runners.js +3 -3
- package/dist/runtime.js +1 -1
- package/dist/suite.js +1 -1
- package/dist/worker.d.ts +2 -2
- package/dist/worker.js +9 -8
- package/dist/workers/forks.js +10 -9
- package/dist/workers/runVmTests.js +5 -5
- package/dist/workers/threads.js +10 -9
- package/dist/workers/vmForks.js +5 -5
- package/dist/workers/vmThreads.js +5 -5
- package/package.json +22 -14
- package/dist/chunks/index.CEzQDJGb.js +0 -231
|
@@ -1,21 +1,23 @@
|
|
|
1
|
-
import { existsSync, readFileSync, writeFileSync, promises } from 'node:fs';
|
|
2
|
-
import { performance as performance$1 } from 'node:perf_hooks';
|
|
1
|
+
import fs, { existsSync, readFileSync, writeFileSync, promises } from 'node:fs';
|
|
3
2
|
import { getTests, generateHash, createTaskName, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, getTestName, hasFailed, getSuites, getTasks, getFullName } from '@vitest/runner/utils';
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import { relative, basename, resolve as resolve$1, join, isAbsolute, dirname, normalize } from 'pathe';
|
|
3
|
+
import * as pathe from 'pathe';
|
|
4
|
+
import { relative, basename, resolve as resolve$1, join, normalize, dirname } from 'pathe';
|
|
7
5
|
import c from 'tinyrainbow';
|
|
6
|
+
import { t as truncateString, e as errorBanner, F as F_POINTER, d as divider, f as formatTimeString, a as taskFail, s as separator, b as F_CHECK, c as F_DOWN_RIGHT, g as formatProjectName, h as getStateSymbol, w as withLabel, r as renderSnapshotSummary, p as padSummaryTitle, i as getStateString$1, j as formatTime, k as countTestErrors, l as F_TREE_NODE_END, m as F_TREE_NODE_MIDDLE, n as noun, o as F_RIGHT } from './utils.DK8FXp4T.js';
|
|
7
|
+
import { stripVTControlCharacters } from 'node:util';
|
|
8
|
+
import { isPrimitive, toArray, deepMerge, notNullish } from '@vitest/utils/helpers';
|
|
9
|
+
import { performance as performance$1 } from 'node:perf_hooks';
|
|
10
|
+
import { defaultStackIgnorePatterns, parseErrorStacktrace, parseStacktrace } from '@vitest/utils/source-map';
|
|
8
11
|
import { i as isTTY } from './env.D4Lgay0q.js';
|
|
9
12
|
import { Console } from 'node:console';
|
|
10
13
|
import { Writable } from 'node:stream';
|
|
11
|
-
import { stripVTControlCharacters } from 'node:util';
|
|
12
14
|
import { inspect } from '@vitest/utils/display';
|
|
13
15
|
import nodeos__default, { hostname } from 'node:os';
|
|
14
16
|
import { x } from 'tinyexec';
|
|
15
17
|
import { distDir } from '../path.js';
|
|
16
18
|
import { parseAstAsync } from 'vite';
|
|
17
19
|
import { positionToOffset, lineSplitRE } from '@vitest/utils/offset';
|
|
18
|
-
import {
|
|
20
|
+
import { readdir, stat, readFile, mkdir, writeFile } from 'node:fs/promises';
|
|
19
21
|
import { createRequire } from 'node:module';
|
|
20
22
|
|
|
21
23
|
function groupBy(collection, iteratee) {
|
|
@@ -1063,6 +1065,120 @@ base.MethodDefinition = base.PropertyDefinition = base.Property = function (node
|
|
|
1063
1065
|
if (node.value) { c(node.value, st, "Expression"); }
|
|
1064
1066
|
};
|
|
1065
1067
|
|
|
1068
|
+
/// <reference types="../types/index.d.ts" />
|
|
1069
|
+
|
|
1070
|
+
// (c) 2020-present Andrea Giammarchi
|
|
1071
|
+
|
|
1072
|
+
const {parse: $parse, stringify: $stringify} = JSON;
|
|
1073
|
+
const {keys} = Object;
|
|
1074
|
+
|
|
1075
|
+
const Primitive = String; // it could be Number
|
|
1076
|
+
const primitive = 'string'; // it could be 'number'
|
|
1077
|
+
|
|
1078
|
+
const ignore = {};
|
|
1079
|
+
const object = 'object';
|
|
1080
|
+
|
|
1081
|
+
const noop = (_, value) => value;
|
|
1082
|
+
|
|
1083
|
+
const primitives = value => (
|
|
1084
|
+
value instanceof Primitive ? Primitive(value) : value
|
|
1085
|
+
);
|
|
1086
|
+
|
|
1087
|
+
const Primitives = (_, value) => (
|
|
1088
|
+
typeof value === primitive ? new Primitive(value) : value
|
|
1089
|
+
);
|
|
1090
|
+
|
|
1091
|
+
const resolver = (input, lazy, parsed, $) => output => {
|
|
1092
|
+
for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
|
|
1093
|
+
const k = ke[y];
|
|
1094
|
+
const value = output[k];
|
|
1095
|
+
if (value instanceof Primitive) {
|
|
1096
|
+
const tmp = input[+value];
|
|
1097
|
+
if (typeof tmp === object && !parsed.has(tmp)) {
|
|
1098
|
+
parsed.add(tmp);
|
|
1099
|
+
output[k] = ignore;
|
|
1100
|
+
lazy.push({ o: output, k, r: tmp });
|
|
1101
|
+
}
|
|
1102
|
+
else
|
|
1103
|
+
output[k] = $.call(output, k, tmp);
|
|
1104
|
+
}
|
|
1105
|
+
else if (output[k] !== ignore)
|
|
1106
|
+
output[k] = $.call(output, k, value);
|
|
1107
|
+
}
|
|
1108
|
+
return output;
|
|
1109
|
+
};
|
|
1110
|
+
|
|
1111
|
+
const set = (known, input, value) => {
|
|
1112
|
+
const index = Primitive(input.push(value) - 1);
|
|
1113
|
+
known.set(value, index);
|
|
1114
|
+
return index;
|
|
1115
|
+
};
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* Converts a specialized flatted string into a JS value.
|
|
1119
|
+
* @param {string} text
|
|
1120
|
+
* @param {(this: any, key: string, value: any) => any} [reviver]
|
|
1121
|
+
* @returns {any}
|
|
1122
|
+
*/
|
|
1123
|
+
const parse = (text, reviver) => {
|
|
1124
|
+
const input = $parse(text, Primitives).map(primitives);
|
|
1125
|
+
const $ = reviver || noop;
|
|
1126
|
+
|
|
1127
|
+
let value = input[0];
|
|
1128
|
+
|
|
1129
|
+
if (typeof value === object && value) {
|
|
1130
|
+
const lazy = [];
|
|
1131
|
+
const revive = resolver(input, lazy, new Set, $);
|
|
1132
|
+
value = revive(value);
|
|
1133
|
+
|
|
1134
|
+
let i = 0;
|
|
1135
|
+
while (i < lazy.length) {
|
|
1136
|
+
// it could be a lazy.shift() but that's costly
|
|
1137
|
+
const {o, k, r} = lazy[i++];
|
|
1138
|
+
o[k] = $.call(o, k, revive(r));
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
1141
|
+
|
|
1142
|
+
return $.call({'': value}, '', value);
|
|
1143
|
+
};
|
|
1144
|
+
|
|
1145
|
+
/**
|
|
1146
|
+
* Converts a JS value into a specialized flatted string.
|
|
1147
|
+
* @param {any} value
|
|
1148
|
+
* @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
|
|
1149
|
+
* @param {string | number | undefined} [space]
|
|
1150
|
+
* @returns {string}
|
|
1151
|
+
*/
|
|
1152
|
+
const stringify = (value, replacer, space) => {
|
|
1153
|
+
const $ = replacer && typeof replacer === object ?
|
|
1154
|
+
(k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
|
|
1155
|
+
(replacer || noop);
|
|
1156
|
+
const known = new Map;
|
|
1157
|
+
const input = [];
|
|
1158
|
+
const output = [];
|
|
1159
|
+
let i = +set(known, input, $.call({'': value}, '', value));
|
|
1160
|
+
let firstRun = !i;
|
|
1161
|
+
while (i < input.length) {
|
|
1162
|
+
firstRun = true;
|
|
1163
|
+
output[i] = $stringify(input[i++], replace, space);
|
|
1164
|
+
}
|
|
1165
|
+
return '[' + output.join(',') + ']';
|
|
1166
|
+
function replace(key, value) {
|
|
1167
|
+
if (firstRun) {
|
|
1168
|
+
firstRun = !firstRun;
|
|
1169
|
+
return value;
|
|
1170
|
+
}
|
|
1171
|
+
const after = $.call(this, key, value);
|
|
1172
|
+
switch (typeof after) {
|
|
1173
|
+
case object:
|
|
1174
|
+
if (after === null) return after;
|
|
1175
|
+
case primitive:
|
|
1176
|
+
return known.get(after) || set(known, input, after);
|
|
1177
|
+
}
|
|
1178
|
+
return after;
|
|
1179
|
+
}
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1066
1182
|
async function collectTests(ctx, filepath) {
|
|
1067
1183
|
const request = await ctx.vite.environments.ssr.transformRequest(filepath);
|
|
1068
1184
|
if (!request) return null;
|
|
@@ -1572,189 +1688,6 @@ function findGeneratedPosition(traceMap, { line, column, source }) {
|
|
|
1572
1688
|
};
|
|
1573
1689
|
}
|
|
1574
1690
|
|
|
1575
|
-
const F_RIGHT = "→";
|
|
1576
|
-
const F_DOWN = "↓";
|
|
1577
|
-
const F_DOWN_RIGHT = "↳";
|
|
1578
|
-
const F_POINTER = "❯";
|
|
1579
|
-
const F_DOT = "·";
|
|
1580
|
-
const F_CHECK = "✓";
|
|
1581
|
-
const F_CROSS = "×";
|
|
1582
|
-
const F_LONG_DASH = "⎯";
|
|
1583
|
-
const F_TODO = "□";
|
|
1584
|
-
const F_TREE_NODE_MIDDLE = "├──";
|
|
1585
|
-
const F_TREE_NODE_END = "└──";
|
|
1586
|
-
|
|
1587
|
-
const pointer = c.yellow(F_POINTER);
|
|
1588
|
-
const skipped = c.dim(c.gray(F_DOWN));
|
|
1589
|
-
const todo = c.dim(c.gray(F_TODO));
|
|
1590
|
-
const benchmarkPass = c.green(F_DOT);
|
|
1591
|
-
const testPass = c.green(F_CHECK);
|
|
1592
|
-
const taskFail = c.red(F_CROSS);
|
|
1593
|
-
const suiteFail = c.red(F_POINTER);
|
|
1594
|
-
const pending$1 = c.gray("·");
|
|
1595
|
-
const separator = c.dim(" > ");
|
|
1596
|
-
const labelDefaultColors = [
|
|
1597
|
-
c.bgYellow,
|
|
1598
|
-
c.bgCyan,
|
|
1599
|
-
c.bgGreen,
|
|
1600
|
-
c.bgMagenta
|
|
1601
|
-
];
|
|
1602
|
-
function getCols(delta = 0) {
|
|
1603
|
-
let length = process.stdout?.columns;
|
|
1604
|
-
if (!length || Number.isNaN(length)) length = 30;
|
|
1605
|
-
return Math.max(length + delta, 0);
|
|
1606
|
-
}
|
|
1607
|
-
function errorBanner(message) {
|
|
1608
|
-
return divider(c.bold(c.bgRed(` ${message} `)), null, null, c.red);
|
|
1609
|
-
}
|
|
1610
|
-
function divider(text, left, right, color) {
|
|
1611
|
-
const cols = getCols();
|
|
1612
|
-
const c = color || ((text) => text);
|
|
1613
|
-
if (text) {
|
|
1614
|
-
const textLength = stripVTControlCharacters(text).length;
|
|
1615
|
-
if (left == null && right != null) left = cols - textLength - right;
|
|
1616
|
-
else {
|
|
1617
|
-
left = left ?? Math.floor((cols - textLength) / 2);
|
|
1618
|
-
right = cols - textLength - left;
|
|
1619
|
-
}
|
|
1620
|
-
left = Math.max(0, left);
|
|
1621
|
-
right = Math.max(0, right);
|
|
1622
|
-
return `${c(F_LONG_DASH.repeat(left))}${text}${c(F_LONG_DASH.repeat(right))}`;
|
|
1623
|
-
}
|
|
1624
|
-
return F_LONG_DASH.repeat(cols);
|
|
1625
|
-
}
|
|
1626
|
-
function formatTestPath(root, path) {
|
|
1627
|
-
if (isAbsolute(path)) path = relative(root, path);
|
|
1628
|
-
const dir = dirname(path);
|
|
1629
|
-
const ext = path.match(/(\.(spec|test)\.[cm]?[tj]sx?)$/)?.[0] || "";
|
|
1630
|
-
const base = basename(path, ext);
|
|
1631
|
-
return slash(c.dim(`${dir}/`) + c.bold(base)) + c.dim(ext);
|
|
1632
|
-
}
|
|
1633
|
-
function renderSnapshotSummary(rootDir, snapshots) {
|
|
1634
|
-
const summary = [];
|
|
1635
|
-
if (snapshots.added) summary.push(c.bold(c.green(`${snapshots.added} written`)));
|
|
1636
|
-
if (snapshots.unmatched) summary.push(c.bold(c.red(`${snapshots.unmatched} failed`)));
|
|
1637
|
-
if (snapshots.updated) summary.push(c.bold(c.green(`${snapshots.updated} updated `)));
|
|
1638
|
-
if (snapshots.filesRemoved) if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.filesRemoved} files removed `)));
|
|
1639
|
-
else summary.push(c.bold(c.yellow(`${snapshots.filesRemoved} files obsolete `)));
|
|
1640
|
-
if (snapshots.filesRemovedList && snapshots.filesRemovedList.length) {
|
|
1641
|
-
const [head, ...tail] = snapshots.filesRemovedList;
|
|
1642
|
-
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, head)}`);
|
|
1643
|
-
tail.forEach((key) => {
|
|
1644
|
-
summary.push(` ${c.gray(F_DOT)} ${formatTestPath(rootDir, key)}`);
|
|
1645
|
-
});
|
|
1646
|
-
}
|
|
1647
|
-
if (snapshots.unchecked) {
|
|
1648
|
-
if (snapshots.didUpdate) summary.push(c.bold(c.green(`${snapshots.unchecked} removed`)));
|
|
1649
|
-
else summary.push(c.bold(c.yellow(`${snapshots.unchecked} obsolete`)));
|
|
1650
|
-
snapshots.uncheckedKeysByFile.forEach((uncheckedFile) => {
|
|
1651
|
-
summary.push(`${c.gray(F_DOWN_RIGHT)} ${formatTestPath(rootDir, uncheckedFile.filePath)}`);
|
|
1652
|
-
uncheckedFile.keys.forEach((key) => summary.push(` ${c.gray(F_DOT)} ${key}`));
|
|
1653
|
-
});
|
|
1654
|
-
}
|
|
1655
|
-
return summary;
|
|
1656
|
-
}
|
|
1657
|
-
function countTestErrors(tasks) {
|
|
1658
|
-
return tasks.reduce((c, i) => c + (i.result?.errors?.length || 0), 0);
|
|
1659
|
-
}
|
|
1660
|
-
function getStateString$1(tasks, name = "tests", showTotal = true) {
|
|
1661
|
-
if (tasks.length === 0) return c.dim(`no ${name}`);
|
|
1662
|
-
const passed = tasks.reduce((acc, i) => {
|
|
1663
|
-
// Exclude expected failures from passed count
|
|
1664
|
-
if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc;
|
|
1665
|
-
return i.result?.state === "pass" ? acc + 1 : acc;
|
|
1666
|
-
}, 0);
|
|
1667
|
-
const failed = tasks.reduce((acc, i) => i.result?.state === "fail" ? acc + 1 : acc, 0);
|
|
1668
|
-
const skipped = tasks.reduce((acc, i) => i.mode === "skip" ? acc + 1 : acc, 0);
|
|
1669
|
-
const todo = tasks.reduce((acc, i) => i.mode === "todo" ? acc + 1 : acc, 0);
|
|
1670
|
-
const expectedFail = tasks.reduce((acc, i) => {
|
|
1671
|
-
// Count tests that are marked as .fails and passed (which means they failed as expected)
|
|
1672
|
-
if (i.result?.state === "pass" && i.type === "test" && i.fails) return acc + 1;
|
|
1673
|
-
return acc;
|
|
1674
|
-
}, 0);
|
|
1675
|
-
return [
|
|
1676
|
-
failed ? c.bold(c.red(`${failed} failed`)) : null,
|
|
1677
|
-
passed ? c.bold(c.green(`${passed} passed`)) : null,
|
|
1678
|
-
expectedFail ? c.cyan(`${expectedFail} expected fail`) : null,
|
|
1679
|
-
skipped ? c.yellow(`${skipped} skipped`) : null,
|
|
1680
|
-
todo ? c.gray(`${todo} todo`) : null
|
|
1681
|
-
].filter(Boolean).join(c.dim(" | ")) + (showTotal ? c.gray(` (${tasks.length})`) : "");
|
|
1682
|
-
}
|
|
1683
|
-
function getStateSymbol(task) {
|
|
1684
|
-
if (task.mode === "todo") return todo;
|
|
1685
|
-
if (task.mode === "skip") return skipped;
|
|
1686
|
-
if (!task.result) return pending$1;
|
|
1687
|
-
if (task.result.state === "run" || task.result.state === "queued") {
|
|
1688
|
-
if (task.type === "suite") return pointer;
|
|
1689
|
-
}
|
|
1690
|
-
if (task.result.state === "pass") return task.meta?.benchmark ? benchmarkPass : testPass;
|
|
1691
|
-
if (task.result.state === "fail") return task.type === "suite" ? suiteFail : taskFail;
|
|
1692
|
-
return " ";
|
|
1693
|
-
}
|
|
1694
|
-
function formatTimeString(date) {
|
|
1695
|
-
return date.toTimeString().split(" ")[0];
|
|
1696
|
-
}
|
|
1697
|
-
function formatTime(time) {
|
|
1698
|
-
if (time > 1e3) return `${(time / 1e3).toFixed(2)}s`;
|
|
1699
|
-
return `${Math.round(time)}ms`;
|
|
1700
|
-
}
|
|
1701
|
-
function formatProjectName(project, suffix = " ") {
|
|
1702
|
-
if (!project?.name) return "";
|
|
1703
|
-
if (!c.isColorSupported) return `|${project.name}|${suffix}`;
|
|
1704
|
-
let background = project.color && c[`bg${capitalize(project.color)}`];
|
|
1705
|
-
if (!background) background = labelDefaultColors[project.name.split("").reduce((acc, v, idx) => acc + v.charCodeAt(0) + idx, 0) % labelDefaultColors.length];
|
|
1706
|
-
return c.black(background(` ${project.name} `)) + suffix;
|
|
1707
|
-
}
|
|
1708
|
-
function withLabel(color, label, message) {
|
|
1709
|
-
const bgColor = `bg${color.charAt(0).toUpperCase()}${color.slice(1)}`;
|
|
1710
|
-
return `${c.bold(c[bgColor](` ${label} `))} ${message ? c[color](message) : ""}`;
|
|
1711
|
-
}
|
|
1712
|
-
function padSummaryTitle(str) {
|
|
1713
|
-
return c.dim(`${str.padStart(11)} `);
|
|
1714
|
-
}
|
|
1715
|
-
function truncateString(text, maxLength) {
|
|
1716
|
-
const plainText = stripVTControlCharacters(text);
|
|
1717
|
-
if (plainText.length <= maxLength) return text;
|
|
1718
|
-
return `${plainText.slice(0, maxLength - 1)}…`;
|
|
1719
|
-
}
|
|
1720
|
-
function capitalize(text) {
|
|
1721
|
-
return `${text[0].toUpperCase()}${text.slice(1)}`;
|
|
1722
|
-
}
|
|
1723
|
-
/**
|
|
1724
|
-
* Returns the singular or plural form of a word based on the count.
|
|
1725
|
-
*/
|
|
1726
|
-
function noun(count, singular, plural) {
|
|
1727
|
-
if (count === 1) return singular;
|
|
1728
|
-
return plural;
|
|
1729
|
-
}
|
|
1730
|
-
|
|
1731
|
-
var utils = /*#__PURE__*/Object.freeze({
|
|
1732
|
-
__proto__: null,
|
|
1733
|
-
benchmarkPass: benchmarkPass,
|
|
1734
|
-
countTestErrors: countTestErrors,
|
|
1735
|
-
divider: divider,
|
|
1736
|
-
errorBanner: errorBanner,
|
|
1737
|
-
formatProjectName: formatProjectName,
|
|
1738
|
-
formatTestPath: formatTestPath,
|
|
1739
|
-
formatTime: formatTime,
|
|
1740
|
-
formatTimeString: formatTimeString,
|
|
1741
|
-
getStateString: getStateString$1,
|
|
1742
|
-
getStateSymbol: getStateSymbol,
|
|
1743
|
-
noun: noun,
|
|
1744
|
-
padSummaryTitle: padSummaryTitle,
|
|
1745
|
-
pending: pending$1,
|
|
1746
|
-
pointer: pointer,
|
|
1747
|
-
renderSnapshotSummary: renderSnapshotSummary,
|
|
1748
|
-
separator: separator,
|
|
1749
|
-
skipped: skipped,
|
|
1750
|
-
suiteFail: suiteFail,
|
|
1751
|
-
taskFail: taskFail,
|
|
1752
|
-
testPass: testPass,
|
|
1753
|
-
todo: todo,
|
|
1754
|
-
truncateString: truncateString,
|
|
1755
|
-
withLabel: withLabel
|
|
1756
|
-
});
|
|
1757
|
-
|
|
1758
1691
|
// use Logger with custom Console to capture entire error printing
|
|
1759
1692
|
function capturePrintError(error, ctx, options) {
|
|
1760
1693
|
let output = "";
|
|
@@ -1903,6 +1836,7 @@ const skipErrorProperties = new Set([
|
|
|
1903
1836
|
"VITEST_TEST_NAME",
|
|
1904
1837
|
"VITEST_TEST_PATH",
|
|
1905
1838
|
"__vitest_rollup_error__",
|
|
1839
|
+
"__vitest_error_context__",
|
|
1906
1840
|
...Object.getOwnPropertyNames(Error.prototype),
|
|
1907
1841
|
...Object.getOwnPropertyNames(Object.prototype)
|
|
1908
1842
|
]);
|
|
@@ -2018,64 +1952,295 @@ function lineNo(no = "") {
|
|
|
2018
1952
|
return c.gray(`${String(no).padStart(3, " ")}|`);
|
|
2019
1953
|
}
|
|
2020
1954
|
|
|
2021
|
-
|
|
2022
|
-
|
|
1955
|
+
function getOutputFile(config, reporter) {
|
|
1956
|
+
if (!config?.outputFile) return;
|
|
1957
|
+
if (typeof config.outputFile === "string") return config.outputFile;
|
|
1958
|
+
return config.outputFile[reporter];
|
|
1959
|
+
}
|
|
1960
|
+
function createDefinesScript(define) {
|
|
1961
|
+
if (!define) return "";
|
|
1962
|
+
if (serializeDefine(define) === "{}") return "";
|
|
1963
|
+
return `
|
|
1964
|
+
const defines = ${serializeDefine(define)}
|
|
1965
|
+
Object.keys(defines).forEach((key) => {
|
|
1966
|
+
const segments = key.split('.')
|
|
1967
|
+
let target = globalThis
|
|
1968
|
+
for (let i = 0; i < segments.length; i++) {
|
|
1969
|
+
const segment = segments[i]
|
|
1970
|
+
if (i === segments.length - 1) {
|
|
1971
|
+
target[segment] = defines[key]
|
|
1972
|
+
} else {
|
|
1973
|
+
target = target[segment] || (target[segment] = {})
|
|
1974
|
+
}
|
|
1975
|
+
}
|
|
1976
|
+
})
|
|
1977
|
+
`;
|
|
1978
|
+
}
|
|
1979
|
+
/**
|
|
1980
|
+
* Like `JSON.stringify` but keeps raw string values as a literal
|
|
1981
|
+
* in the generated code. For example: `"window"` would refer to
|
|
1982
|
+
* the global `window` object directly.
|
|
1983
|
+
*/
|
|
1984
|
+
function serializeDefine(define) {
|
|
1985
|
+
const userDefine = {};
|
|
1986
|
+
for (const key in define) {
|
|
1987
|
+
// vitest sets this to avoid vite:client-inject plugin
|
|
1988
|
+
if (key === "process.env.NODE_ENV" && define[key] === "process.env.NODE_ENV") continue;
|
|
1989
|
+
// import.meta.env.* is handled in `importAnalysis` plugin
|
|
1990
|
+
if (!key.startsWith("import.meta.env.")) userDefine[key] = define[key];
|
|
1991
|
+
}
|
|
1992
|
+
let res = `{`;
|
|
1993
|
+
const keys = Object.keys(userDefine).sort();
|
|
1994
|
+
for (let i = 0; i < keys.length; i++) {
|
|
1995
|
+
const key = keys[i];
|
|
1996
|
+
const val = userDefine[key];
|
|
1997
|
+
res += `${JSON.stringify(key)}: ${handleDefineValue(val)}`;
|
|
1998
|
+
if (i !== keys.length - 1) res += `, `;
|
|
1999
|
+
}
|
|
2000
|
+
return `${res}}`;
|
|
2001
|
+
}
|
|
2002
|
+
function handleDefineValue(value) {
|
|
2003
|
+
if (typeof value === "undefined") return "undefined";
|
|
2004
|
+
if (typeof value === "string") return value;
|
|
2005
|
+
return JSON.stringify(value);
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
class BlobReporter {
|
|
2023
2009
|
start = 0;
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
renderSucceed = false;
|
|
2030
|
-
verbose = false;
|
|
2031
|
-
silent;
|
|
2032
|
-
_filesInWatchMode = /* @__PURE__ */ new Map();
|
|
2033
|
-
_timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
2034
|
-
constructor(options = {}) {
|
|
2035
|
-
this.isTTY = options.isTTY ?? isTTY;
|
|
2036
|
-
this.silent = options.silent;
|
|
2010
|
+
ctx;
|
|
2011
|
+
options;
|
|
2012
|
+
coverage;
|
|
2013
|
+
constructor(options) {
|
|
2014
|
+
this.options = options;
|
|
2037
2015
|
}
|
|
2038
2016
|
onInit(ctx) {
|
|
2017
|
+
if (ctx.config.watch) throw new Error("Blob reporter is not supported in watch mode");
|
|
2039
2018
|
this.ctx = ctx;
|
|
2040
|
-
this.
|
|
2041
|
-
this.
|
|
2042
|
-
}
|
|
2043
|
-
log(...messages) {
|
|
2044
|
-
this.ctx.logger.log(...messages);
|
|
2045
|
-
}
|
|
2046
|
-
error(...messages) {
|
|
2047
|
-
this.ctx.logger.error(...messages);
|
|
2048
|
-
}
|
|
2049
|
-
relative(path) {
|
|
2050
|
-
return relative(this.ctx.config.root, path);
|
|
2019
|
+
this.start = performance.now();
|
|
2020
|
+
this.coverage = void 0;
|
|
2051
2021
|
}
|
|
2052
|
-
|
|
2053
|
-
this.
|
|
2054
|
-
this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
2022
|
+
onCoverage(coverage) {
|
|
2023
|
+
this.coverage = coverage;
|
|
2055
2024
|
}
|
|
2056
|
-
onTestRunEnd(testModules, unhandledErrors
|
|
2025
|
+
async onTestRunEnd(testModules, unhandledErrors) {
|
|
2026
|
+
const executionTime = performance.now() - this.start;
|
|
2057
2027
|
const files = testModules.map((testModule) => testModule.task);
|
|
2058
2028
|
const errors = [...unhandledErrors];
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2029
|
+
const coverage = this.coverage;
|
|
2030
|
+
let outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "blob");
|
|
2031
|
+
if (!outputFile) {
|
|
2032
|
+
const shard = this.ctx.config.shard;
|
|
2033
|
+
outputFile = shard ? `.vitest-reports/blob-${shard.index}-${shard.count}.json` : ".vitest-reports/blob.json";
|
|
2034
|
+
}
|
|
2035
|
+
const environmentModules = {};
|
|
2036
|
+
this.ctx.projects.forEach((project) => {
|
|
2037
|
+
const serializedProject = {
|
|
2038
|
+
environments: {},
|
|
2039
|
+
external: []
|
|
2040
|
+
};
|
|
2041
|
+
Object.entries(project.vite.environments).forEach(([environmentName, environment]) => {
|
|
2042
|
+
serializedProject.environments[environmentName] = serializeEnvironmentModuleGraph(environment);
|
|
2043
|
+
});
|
|
2044
|
+
if (project.browser?.vite.environments.client) serializedProject.browser = serializeEnvironmentModuleGraph(project.browser.vite.environments.client);
|
|
2045
|
+
for (const [id, value] of project._resolver.externalizeCache.entries()) if (typeof value === "string") serializedProject.external.push([id, value]);
|
|
2046
|
+
environmentModules[project.name] = serializedProject;
|
|
2047
|
+
});
|
|
2048
|
+
const report = [
|
|
2049
|
+
this.ctx.version,
|
|
2050
|
+
files,
|
|
2051
|
+
errors,
|
|
2052
|
+
coverage,
|
|
2053
|
+
executionTime,
|
|
2054
|
+
environmentModules
|
|
2055
|
+
];
|
|
2056
|
+
const reportFile = resolve$1(this.ctx.config.root, outputFile);
|
|
2057
|
+
await writeBlob(report, reportFile);
|
|
2058
|
+
this.ctx.logger.log("blob report written to", reportFile);
|
|
2059
|
+
}
|
|
2060
|
+
}
|
|
2061
|
+
async function writeBlob(content, filename) {
|
|
2062
|
+
const report = stringify(content);
|
|
2063
|
+
const dir = dirname(filename);
|
|
2064
|
+
if (!existsSync(dir)) await mkdir(dir, { recursive: true });
|
|
2065
|
+
await writeFile(filename, report, "utf-8");
|
|
2066
|
+
}
|
|
2067
|
+
async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
|
|
2068
|
+
// using process.cwd() because --merge-reports can only be used in CLI
|
|
2069
|
+
const resolvedDir = resolve$1(process.cwd(), blobsDirectory);
|
|
2070
|
+
const promises = (await readdir(resolvedDir)).map(async (filename) => {
|
|
2071
|
+
const fullPath = resolve$1(resolvedDir, filename);
|
|
2072
|
+
if (!(await stat(fullPath)).isFile()) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`);
|
|
2073
|
+
const [version, files, errors, coverage, executionTime, environmentModules] = parse(await readFile(fullPath, "utf-8"));
|
|
2074
|
+
if (!version) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`);
|
|
2075
|
+
return {
|
|
2076
|
+
version,
|
|
2077
|
+
files,
|
|
2078
|
+
errors,
|
|
2079
|
+
coverage,
|
|
2080
|
+
file: filename,
|
|
2081
|
+
executionTime,
|
|
2082
|
+
environmentModules
|
|
2083
|
+
};
|
|
2084
|
+
});
|
|
2085
|
+
const blobs = await Promise.all(promises);
|
|
2086
|
+
if (!blobs.length) throw new Error(`vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`);
|
|
2087
|
+
const versions = new Set(blobs.map((blob) => blob.version));
|
|
2088
|
+
if (versions.size > 1) throw new Error(`vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received\n\n${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`);
|
|
2089
|
+
if (!versions.has(currentVersion)) throw new Error(`the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`);
|
|
2090
|
+
// Restore module graph
|
|
2091
|
+
const projects = Object.fromEntries(projectsArray.map((p) => [p.name, p]));
|
|
2092
|
+
for (const project of projectsArray) if (project.isBrowserEnabled()) await project._initBrowserServer();
|
|
2093
|
+
blobs.forEach((blob) => {
|
|
2094
|
+
Object.entries(blob.environmentModules).forEach(([projectName, modulesByProject]) => {
|
|
2095
|
+
const project = projects[projectName];
|
|
2096
|
+
if (!project) return;
|
|
2097
|
+
modulesByProject.external.forEach(([id, externalized]) => {
|
|
2098
|
+
project._resolver.externalizeCache.set(id, externalized);
|
|
2099
|
+
});
|
|
2100
|
+
Object.entries(modulesByProject.environments).forEach(([environmentName, moduleGraph]) => {
|
|
2101
|
+
const environment = project.vite.environments[environmentName];
|
|
2102
|
+
deserializeEnvironmentModuleGraph(environment, moduleGraph);
|
|
2103
|
+
});
|
|
2104
|
+
const browserModuleGraph = modulesByProject.browser;
|
|
2105
|
+
if (browserModuleGraph) {
|
|
2106
|
+
const browserEnvironment = project.browser.vite.environments.client;
|
|
2107
|
+
deserializeEnvironmentModuleGraph(browserEnvironment, browserModuleGraph);
|
|
2108
|
+
}
|
|
2109
|
+
});
|
|
2110
|
+
});
|
|
2111
|
+
return {
|
|
2112
|
+
files: blobs.flatMap((blob) => blob.files).sort((f1, f2) => {
|
|
2113
|
+
return (f1.result?.startTime || 0) - (f2.result?.startTime || 0);
|
|
2114
|
+
}),
|
|
2115
|
+
errors: blobs.flatMap((blob) => blob.errors),
|
|
2116
|
+
coverages: blobs.map((blob) => blob.coverage),
|
|
2117
|
+
executionTimes: blobs.map((blob) => blob.executionTime)
|
|
2118
|
+
};
|
|
2119
|
+
}
|
|
2120
|
+
function serializeEnvironmentModuleGraph(environment) {
|
|
2121
|
+
const idTable = [];
|
|
2122
|
+
const idMap = /* @__PURE__ */ new Map();
|
|
2123
|
+
const getIdIndex = (id) => {
|
|
2124
|
+
const existing = idMap.get(id);
|
|
2125
|
+
if (existing != null) return existing;
|
|
2126
|
+
const next = idTable.length;
|
|
2127
|
+
idMap.set(id, next);
|
|
2128
|
+
idTable.push(id);
|
|
2129
|
+
return next;
|
|
2130
|
+
};
|
|
2131
|
+
const modules = [];
|
|
2132
|
+
for (const [id, mod] of environment.moduleGraph.idToModuleMap.entries()) {
|
|
2133
|
+
if (!mod.file) continue;
|
|
2134
|
+
const importedIds = [];
|
|
2135
|
+
for (const importedNode of mod.importedModules) if (importedNode.id) importedIds.push(getIdIndex(importedNode.id));
|
|
2136
|
+
modules.push([
|
|
2137
|
+
getIdIndex(id),
|
|
2138
|
+
getIdIndex(mod.file),
|
|
2139
|
+
getIdIndex(mod.url),
|
|
2140
|
+
importedIds
|
|
2141
|
+
]);
|
|
2142
|
+
}
|
|
2143
|
+
return {
|
|
2144
|
+
idTable,
|
|
2145
|
+
modules
|
|
2146
|
+
};
|
|
2147
|
+
}
|
|
2148
|
+
function deserializeEnvironmentModuleGraph(environment, serialized) {
|
|
2149
|
+
const nodesById = /* @__PURE__ */ new Map();
|
|
2150
|
+
serialized.modules.forEach(([id, file, url]) => {
|
|
2151
|
+
const moduleId = serialized.idTable[id];
|
|
2152
|
+
const filePath = serialized.idTable[file];
|
|
2153
|
+
const urlPath = serialized.idTable[url];
|
|
2154
|
+
const moduleNode = environment.moduleGraph.createFileOnlyEntry(filePath);
|
|
2155
|
+
moduleNode.url = urlPath;
|
|
2156
|
+
moduleNode.id = moduleId;
|
|
2157
|
+
moduleNode.transformResult = {
|
|
2158
|
+
code: " ",
|
|
2159
|
+
map: null
|
|
2160
|
+
};
|
|
2161
|
+
environment.moduleGraph.idToModuleMap.set(moduleId, moduleNode);
|
|
2162
|
+
nodesById.set(moduleId, moduleNode);
|
|
2163
|
+
});
|
|
2164
|
+
serialized.modules.forEach(([id, _file, _url, importedIds]) => {
|
|
2165
|
+
const moduleId = serialized.idTable[id];
|
|
2166
|
+
const moduleNode = nodesById.get(moduleId);
|
|
2167
|
+
importedIds.forEach((importedIdIndex) => {
|
|
2168
|
+
const importedId = serialized.idTable[importedIdIndex];
|
|
2169
|
+
const importedNode = nodesById.get(importedId);
|
|
2170
|
+
moduleNode.importedModules.add(importedNode);
|
|
2171
|
+
importedNode.importers.add(moduleNode);
|
|
2172
|
+
});
|
|
2173
|
+
});
|
|
2174
|
+
}
|
|
2175
|
+
|
|
2176
|
+
class HangingProcessReporter {
|
|
2177
|
+
whyRunning;
|
|
2178
|
+
onInit() {
|
|
2179
|
+
this.whyRunning = createRequire(import.meta.url)("why-is-node-running");
|
|
2180
|
+
}
|
|
2181
|
+
onProcessTimeout() {
|
|
2182
|
+
this.whyRunning?.();
|
|
2183
|
+
}
|
|
2184
|
+
}
|
|
2185
|
+
|
|
2186
|
+
const BADGE_PADDING = " ";
|
|
2187
|
+
class BaseReporter {
|
|
2188
|
+
start = 0;
|
|
2189
|
+
end = 0;
|
|
2190
|
+
watchFilters;
|
|
2191
|
+
failedUnwatchedFiles = [];
|
|
2192
|
+
isTTY;
|
|
2193
|
+
ctx = void 0;
|
|
2194
|
+
renderSucceed = false;
|
|
2195
|
+
verbose = false;
|
|
2196
|
+
silent;
|
|
2197
|
+
_filesInWatchMode = /* @__PURE__ */ new Map();
|
|
2198
|
+
_timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
2199
|
+
constructor(options = {}) {
|
|
2200
|
+
this.isTTY = options.isTTY ?? isTTY;
|
|
2201
|
+
this.silent = options.silent;
|
|
2202
|
+
}
|
|
2203
|
+
onInit(ctx) {
|
|
2204
|
+
this.ctx = ctx;
|
|
2205
|
+
this.silent ??= this.ctx.config.silent;
|
|
2206
|
+
this.ctx.logger.printBanner();
|
|
2207
|
+
}
|
|
2208
|
+
log(...messages) {
|
|
2209
|
+
this.ctx.logger.log(...messages);
|
|
2210
|
+
}
|
|
2211
|
+
error(...messages) {
|
|
2212
|
+
this.ctx.logger.error(...messages);
|
|
2213
|
+
}
|
|
2214
|
+
relative(path) {
|
|
2215
|
+
return relative(this.ctx.config.root, path);
|
|
2216
|
+
}
|
|
2217
|
+
onTestRunStart(_specifications) {
|
|
2218
|
+
this.start = performance$1.now();
|
|
2219
|
+
this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
|
|
2220
|
+
}
|
|
2221
|
+
onTestRunEnd(testModules, unhandledErrors, _reason) {
|
|
2222
|
+
const files = testModules.map((testModule) => testModule.task);
|
|
2223
|
+
const errors = [...unhandledErrors];
|
|
2224
|
+
this.end = performance$1.now();
|
|
2225
|
+
if (!files.length && !errors.length) this.ctx.logger.printNoTestFound(this.ctx.filenamePattern);
|
|
2226
|
+
else this.reportSummary(files, errors);
|
|
2227
|
+
}
|
|
2228
|
+
onTestCaseResult(testCase) {
|
|
2229
|
+
if (testCase.result().state === "failed") this.logFailedTask(testCase.task);
|
|
2230
|
+
}
|
|
2231
|
+
onTestSuiteResult(testSuite) {
|
|
2232
|
+
if (testSuite.state() === "failed") this.logFailedTask(testSuite.task);
|
|
2233
|
+
}
|
|
2234
|
+
onTestModuleEnd(testModule) {
|
|
2235
|
+
if (testModule.state() === "failed") this.logFailedTask(testModule.task);
|
|
2236
|
+
this.printTestModule(testModule);
|
|
2237
|
+
}
|
|
2238
|
+
logFailedTask(task) {
|
|
2239
|
+
if (this.silent === "passed-only") for (const log of task.logs || []) this.onUserConsoleLog(log, "failed");
|
|
2240
|
+
}
|
|
2241
|
+
printTestModule(testModule) {
|
|
2242
|
+
const moduleState = testModule.state();
|
|
2243
|
+
if (moduleState === "queued" || moduleState === "pending") return;
|
|
2079
2244
|
let testsCount = 0;
|
|
2080
2245
|
let failedCount = 0;
|
|
2081
2246
|
let skippedCount = 0;
|
|
@@ -2992,341 +3157,6 @@ class AgentReporter extends DefaultReporter {
|
|
|
2992
3157
|
}
|
|
2993
3158
|
}
|
|
2994
3159
|
|
|
2995
|
-
/// <reference types="../types/index.d.ts" />
|
|
2996
|
-
|
|
2997
|
-
// (c) 2020-present Andrea Giammarchi
|
|
2998
|
-
|
|
2999
|
-
const {parse: $parse, stringify: $stringify} = JSON;
|
|
3000
|
-
const {keys} = Object;
|
|
3001
|
-
|
|
3002
|
-
const Primitive = String; // it could be Number
|
|
3003
|
-
const primitive = 'string'; // it could be 'number'
|
|
3004
|
-
|
|
3005
|
-
const ignore = {};
|
|
3006
|
-
const object = 'object';
|
|
3007
|
-
|
|
3008
|
-
const noop = (_, value) => value;
|
|
3009
|
-
|
|
3010
|
-
const primitives = value => (
|
|
3011
|
-
value instanceof Primitive ? Primitive(value) : value
|
|
3012
|
-
);
|
|
3013
|
-
|
|
3014
|
-
const Primitives = (_, value) => (
|
|
3015
|
-
typeof value === primitive ? new Primitive(value) : value
|
|
3016
|
-
);
|
|
3017
|
-
|
|
3018
|
-
const resolver = (input, lazy, parsed, $) => output => {
|
|
3019
|
-
for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
|
|
3020
|
-
const k = ke[y];
|
|
3021
|
-
const value = output[k];
|
|
3022
|
-
if (value instanceof Primitive) {
|
|
3023
|
-
const tmp = input[value];
|
|
3024
|
-
if (typeof tmp === object && !parsed.has(tmp)) {
|
|
3025
|
-
parsed.add(tmp);
|
|
3026
|
-
output[k] = ignore;
|
|
3027
|
-
lazy.push({ o: output, k, r: tmp });
|
|
3028
|
-
}
|
|
3029
|
-
else
|
|
3030
|
-
output[k] = $.call(output, k, tmp);
|
|
3031
|
-
}
|
|
3032
|
-
else if (output[k] !== ignore)
|
|
3033
|
-
output[k] = $.call(output, k, value);
|
|
3034
|
-
}
|
|
3035
|
-
return output;
|
|
3036
|
-
};
|
|
3037
|
-
|
|
3038
|
-
const set = (known, input, value) => {
|
|
3039
|
-
const index = Primitive(input.push(value) - 1);
|
|
3040
|
-
known.set(value, index);
|
|
3041
|
-
return index;
|
|
3042
|
-
};
|
|
3043
|
-
|
|
3044
|
-
/**
|
|
3045
|
-
* Converts a specialized flatted string into a JS value.
|
|
3046
|
-
* @param {string} text
|
|
3047
|
-
* @param {(this: any, key: string, value: any) => any} [reviver]
|
|
3048
|
-
* @returns {any}
|
|
3049
|
-
*/
|
|
3050
|
-
const parse = (text, reviver) => {
|
|
3051
|
-
const input = $parse(text, Primitives).map(primitives);
|
|
3052
|
-
const $ = reviver || noop;
|
|
3053
|
-
|
|
3054
|
-
let value = input[0];
|
|
3055
|
-
|
|
3056
|
-
if (typeof value === object && value) {
|
|
3057
|
-
const lazy = [];
|
|
3058
|
-
const revive = resolver(input, lazy, new Set, $);
|
|
3059
|
-
value = revive(value);
|
|
3060
|
-
|
|
3061
|
-
let i = 0;
|
|
3062
|
-
while (i < lazy.length) {
|
|
3063
|
-
// it could be a lazy.shift() but that's costly
|
|
3064
|
-
const {o, k, r} = lazy[i++];
|
|
3065
|
-
o[k] = $.call(o, k, revive(r));
|
|
3066
|
-
}
|
|
3067
|
-
}
|
|
3068
|
-
|
|
3069
|
-
return $.call({'': value}, '', value);
|
|
3070
|
-
};
|
|
3071
|
-
|
|
3072
|
-
/**
|
|
3073
|
-
* Converts a JS value into a specialized flatted string.
|
|
3074
|
-
* @param {any} value
|
|
3075
|
-
* @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
|
|
3076
|
-
* @param {string | number | undefined} [space]
|
|
3077
|
-
* @returns {string}
|
|
3078
|
-
*/
|
|
3079
|
-
const stringify = (value, replacer, space) => {
|
|
3080
|
-
const $ = replacer && typeof replacer === object ?
|
|
3081
|
-
(k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
|
|
3082
|
-
(replacer || noop);
|
|
3083
|
-
const known = new Map;
|
|
3084
|
-
const input = [];
|
|
3085
|
-
const output = [];
|
|
3086
|
-
let i = +set(known, input, $.call({'': value}, '', value));
|
|
3087
|
-
let firstRun = !i;
|
|
3088
|
-
while (i < input.length) {
|
|
3089
|
-
firstRun = true;
|
|
3090
|
-
output[i] = $stringify(input[i++], replace, space);
|
|
3091
|
-
}
|
|
3092
|
-
return '[' + output.join(',') + ']';
|
|
3093
|
-
function replace(key, value) {
|
|
3094
|
-
if (firstRun) {
|
|
3095
|
-
firstRun = !firstRun;
|
|
3096
|
-
return value;
|
|
3097
|
-
}
|
|
3098
|
-
const after = $.call(this, key, value);
|
|
3099
|
-
switch (typeof after) {
|
|
3100
|
-
case object:
|
|
3101
|
-
if (after === null) return after;
|
|
3102
|
-
case primitive:
|
|
3103
|
-
return known.get(after) || set(known, input, after);
|
|
3104
|
-
}
|
|
3105
|
-
return after;
|
|
3106
|
-
}
|
|
3107
|
-
};
|
|
3108
|
-
|
|
3109
|
-
function getOutputFile(config, reporter) {
|
|
3110
|
-
if (!config?.outputFile) return;
|
|
3111
|
-
if (typeof config.outputFile === "string") return config.outputFile;
|
|
3112
|
-
return config.outputFile[reporter];
|
|
3113
|
-
}
|
|
3114
|
-
function createDefinesScript(define) {
|
|
3115
|
-
if (!define) return "";
|
|
3116
|
-
if (serializeDefine(define) === "{}") return "";
|
|
3117
|
-
return `
|
|
3118
|
-
const defines = ${serializeDefine(define)}
|
|
3119
|
-
Object.keys(defines).forEach((key) => {
|
|
3120
|
-
const segments = key.split('.')
|
|
3121
|
-
let target = globalThis
|
|
3122
|
-
for (let i = 0; i < segments.length; i++) {
|
|
3123
|
-
const segment = segments[i]
|
|
3124
|
-
if (i === segments.length - 1) {
|
|
3125
|
-
target[segment] = defines[key]
|
|
3126
|
-
} else {
|
|
3127
|
-
target = target[segment] || (target[segment] = {})
|
|
3128
|
-
}
|
|
3129
|
-
}
|
|
3130
|
-
})
|
|
3131
|
-
`;
|
|
3132
|
-
}
|
|
3133
|
-
/**
|
|
3134
|
-
* Like `JSON.stringify` but keeps raw string values as a literal
|
|
3135
|
-
* in the generated code. For example: `"window"` would refer to
|
|
3136
|
-
* the global `window` object directly.
|
|
3137
|
-
*/
|
|
3138
|
-
function serializeDefine(define) {
|
|
3139
|
-
const userDefine = {};
|
|
3140
|
-
for (const key in define) {
|
|
3141
|
-
// vitest sets this to avoid vite:client-inject plugin
|
|
3142
|
-
if (key === "process.env.NODE_ENV" && define[key] === "process.env.NODE_ENV") continue;
|
|
3143
|
-
// import.meta.env.* is handled in `importAnalysis` plugin
|
|
3144
|
-
if (!key.startsWith("import.meta.env.")) userDefine[key] = define[key];
|
|
3145
|
-
}
|
|
3146
|
-
let res = `{`;
|
|
3147
|
-
const keys = Object.keys(userDefine).sort();
|
|
3148
|
-
for (let i = 0; i < keys.length; i++) {
|
|
3149
|
-
const key = keys[i];
|
|
3150
|
-
const val = userDefine[key];
|
|
3151
|
-
res += `${JSON.stringify(key)}: ${handleDefineValue(val)}`;
|
|
3152
|
-
if (i !== keys.length - 1) res += `, `;
|
|
3153
|
-
}
|
|
3154
|
-
return `${res}}`;
|
|
3155
|
-
}
|
|
3156
|
-
function handleDefineValue(value) {
|
|
3157
|
-
if (typeof value === "undefined") return "undefined";
|
|
3158
|
-
if (typeof value === "string") return value;
|
|
3159
|
-
return JSON.stringify(value);
|
|
3160
|
-
}
|
|
3161
|
-
|
|
3162
|
-
class BlobReporter {
|
|
3163
|
-
start = 0;
|
|
3164
|
-
ctx;
|
|
3165
|
-
options;
|
|
3166
|
-
coverage;
|
|
3167
|
-
constructor(options) {
|
|
3168
|
-
this.options = options;
|
|
3169
|
-
}
|
|
3170
|
-
onInit(ctx) {
|
|
3171
|
-
if (ctx.config.watch) throw new Error("Blob reporter is not supported in watch mode");
|
|
3172
|
-
this.ctx = ctx;
|
|
3173
|
-
this.start = performance.now();
|
|
3174
|
-
this.coverage = void 0;
|
|
3175
|
-
}
|
|
3176
|
-
onCoverage(coverage) {
|
|
3177
|
-
this.coverage = coverage;
|
|
3178
|
-
}
|
|
3179
|
-
async onTestRunEnd(testModules, unhandledErrors) {
|
|
3180
|
-
const executionTime = performance.now() - this.start;
|
|
3181
|
-
const files = testModules.map((testModule) => testModule.task);
|
|
3182
|
-
const errors = [...unhandledErrors];
|
|
3183
|
-
const coverage = this.coverage;
|
|
3184
|
-
let outputFile = this.options.outputFile ?? getOutputFile(this.ctx.config, "blob");
|
|
3185
|
-
if (!outputFile) {
|
|
3186
|
-
const shard = this.ctx.config.shard;
|
|
3187
|
-
outputFile = shard ? `.vitest-reports/blob-${shard.index}-${shard.count}.json` : ".vitest-reports/blob.json";
|
|
3188
|
-
}
|
|
3189
|
-
const environmentModules = {};
|
|
3190
|
-
this.ctx.projects.forEach((project) => {
|
|
3191
|
-
const serializedProject = {
|
|
3192
|
-
environments: {},
|
|
3193
|
-
external: []
|
|
3194
|
-
};
|
|
3195
|
-
Object.entries(project.vite.environments).forEach(([environmentName, environment]) => {
|
|
3196
|
-
serializedProject.environments[environmentName] = serializeEnvironmentModuleGraph(environment);
|
|
3197
|
-
});
|
|
3198
|
-
if (project.browser?.vite.environments.client) serializedProject.browser = serializeEnvironmentModuleGraph(project.browser.vite.environments.client);
|
|
3199
|
-
for (const [id, value] of project._resolver.externalizeCache.entries()) if (typeof value === "string") serializedProject.external.push([id, value]);
|
|
3200
|
-
environmentModules[project.name] = serializedProject;
|
|
3201
|
-
});
|
|
3202
|
-
const report = [
|
|
3203
|
-
this.ctx.version,
|
|
3204
|
-
files,
|
|
3205
|
-
errors,
|
|
3206
|
-
coverage,
|
|
3207
|
-
executionTime,
|
|
3208
|
-
environmentModules
|
|
3209
|
-
];
|
|
3210
|
-
const reportFile = resolve$1(this.ctx.config.root, outputFile);
|
|
3211
|
-
await writeBlob(report, reportFile);
|
|
3212
|
-
this.ctx.logger.log("blob report written to", reportFile);
|
|
3213
|
-
}
|
|
3214
|
-
}
|
|
3215
|
-
async function writeBlob(content, filename) {
|
|
3216
|
-
const report = stringify(content);
|
|
3217
|
-
const dir = dirname(filename);
|
|
3218
|
-
if (!existsSync(dir)) await mkdir(dir, { recursive: true });
|
|
3219
|
-
await writeFile(filename, report, "utf-8");
|
|
3220
|
-
}
|
|
3221
|
-
async function readBlobs(currentVersion, blobsDirectory, projectsArray) {
|
|
3222
|
-
// using process.cwd() because --merge-reports can only be used in CLI
|
|
3223
|
-
const resolvedDir = resolve$1(process.cwd(), blobsDirectory);
|
|
3224
|
-
const promises = (await readdir(resolvedDir)).map(async (filename) => {
|
|
3225
|
-
const fullPath = resolve$1(resolvedDir, filename);
|
|
3226
|
-
if (!(await stat(fullPath)).isFile()) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a file`);
|
|
3227
|
-
const [version, files, errors, coverage, executionTime, environmentModules] = parse(await readFile(fullPath, "utf-8"));
|
|
3228
|
-
if (!version) throw new TypeError(`vitest.mergeReports() expects all paths in "${blobsDirectory}" to be files generated by the blob reporter, but "${filename}" is not a valid blob file`);
|
|
3229
|
-
return {
|
|
3230
|
-
version,
|
|
3231
|
-
files,
|
|
3232
|
-
errors,
|
|
3233
|
-
coverage,
|
|
3234
|
-
file: filename,
|
|
3235
|
-
executionTime,
|
|
3236
|
-
environmentModules
|
|
3237
|
-
};
|
|
3238
|
-
});
|
|
3239
|
-
const blobs = await Promise.all(promises);
|
|
3240
|
-
if (!blobs.length) throw new Error(`vitest.mergeReports() requires at least one blob file in "${blobsDirectory}" directory, but none were found`);
|
|
3241
|
-
const versions = new Set(blobs.map((blob) => blob.version));
|
|
3242
|
-
if (versions.size > 1) throw new Error(`vitest.mergeReports() requires all blob files to be generated by the same Vitest version, received\n\n${blobs.map((b) => `- "${b.file}" uses v${b.version}`).join("\n")}`);
|
|
3243
|
-
if (!versions.has(currentVersion)) throw new Error(`the blobs in "${blobsDirectory}" were generated by a different version of Vitest. Expected v${currentVersion}, but received v${blobs[0].version}`);
|
|
3244
|
-
// Restore module graph
|
|
3245
|
-
const projects = Object.fromEntries(projectsArray.map((p) => [p.name, p]));
|
|
3246
|
-
for (const project of projectsArray) if (project.isBrowserEnabled()) await project._initBrowserServer();
|
|
3247
|
-
blobs.forEach((blob) => {
|
|
3248
|
-
Object.entries(blob.environmentModules).forEach(([projectName, modulesByProject]) => {
|
|
3249
|
-
const project = projects[projectName];
|
|
3250
|
-
if (!project) return;
|
|
3251
|
-
modulesByProject.external.forEach(([id, externalized]) => {
|
|
3252
|
-
project._resolver.externalizeCache.set(id, externalized);
|
|
3253
|
-
});
|
|
3254
|
-
Object.entries(modulesByProject.environments).forEach(([environmentName, moduleGraph]) => {
|
|
3255
|
-
const environment = project.vite.environments[environmentName];
|
|
3256
|
-
deserializeEnvironmentModuleGraph(environment, moduleGraph);
|
|
3257
|
-
});
|
|
3258
|
-
const browserModuleGraph = modulesByProject.browser;
|
|
3259
|
-
if (browserModuleGraph) {
|
|
3260
|
-
const browserEnvironment = project.browser.vite.environments.client;
|
|
3261
|
-
deserializeEnvironmentModuleGraph(browserEnvironment, browserModuleGraph);
|
|
3262
|
-
}
|
|
3263
|
-
});
|
|
3264
|
-
});
|
|
3265
|
-
return {
|
|
3266
|
-
files: blobs.flatMap((blob) => blob.files).sort((f1, f2) => {
|
|
3267
|
-
return (f1.result?.startTime || 0) - (f2.result?.startTime || 0);
|
|
3268
|
-
}),
|
|
3269
|
-
errors: blobs.flatMap((blob) => blob.errors),
|
|
3270
|
-
coverages: blobs.map((blob) => blob.coverage),
|
|
3271
|
-
executionTimes: blobs.map((blob) => blob.executionTime)
|
|
3272
|
-
};
|
|
3273
|
-
}
|
|
3274
|
-
function serializeEnvironmentModuleGraph(environment) {
|
|
3275
|
-
const idTable = [];
|
|
3276
|
-
const idMap = /* @__PURE__ */ new Map();
|
|
3277
|
-
const getIdIndex = (id) => {
|
|
3278
|
-
const existing = idMap.get(id);
|
|
3279
|
-
if (existing != null) return existing;
|
|
3280
|
-
const next = idTable.length;
|
|
3281
|
-
idMap.set(id, next);
|
|
3282
|
-
idTable.push(id);
|
|
3283
|
-
return next;
|
|
3284
|
-
};
|
|
3285
|
-
const modules = [];
|
|
3286
|
-
for (const [id, mod] of environment.moduleGraph.idToModuleMap.entries()) {
|
|
3287
|
-
if (!mod.file) continue;
|
|
3288
|
-
const importedIds = [];
|
|
3289
|
-
for (const importedNode of mod.importedModules) if (importedNode.id) importedIds.push(getIdIndex(importedNode.id));
|
|
3290
|
-
modules.push([
|
|
3291
|
-
getIdIndex(id),
|
|
3292
|
-
getIdIndex(mod.file),
|
|
3293
|
-
getIdIndex(mod.url),
|
|
3294
|
-
importedIds
|
|
3295
|
-
]);
|
|
3296
|
-
}
|
|
3297
|
-
return {
|
|
3298
|
-
idTable,
|
|
3299
|
-
modules
|
|
3300
|
-
};
|
|
3301
|
-
}
|
|
3302
|
-
function deserializeEnvironmentModuleGraph(environment, serialized) {
|
|
3303
|
-
const nodesById = /* @__PURE__ */ new Map();
|
|
3304
|
-
serialized.modules.forEach(([id, file, url]) => {
|
|
3305
|
-
const moduleId = serialized.idTable[id];
|
|
3306
|
-
const filePath = serialized.idTable[file];
|
|
3307
|
-
const urlPath = serialized.idTable[url];
|
|
3308
|
-
const moduleNode = environment.moduleGraph.createFileOnlyEntry(filePath);
|
|
3309
|
-
moduleNode.url = urlPath;
|
|
3310
|
-
moduleNode.id = moduleId;
|
|
3311
|
-
moduleNode.transformResult = {
|
|
3312
|
-
code: " ",
|
|
3313
|
-
map: null
|
|
3314
|
-
};
|
|
3315
|
-
environment.moduleGraph.idToModuleMap.set(moduleId, moduleNode);
|
|
3316
|
-
nodesById.set(moduleId, moduleNode);
|
|
3317
|
-
});
|
|
3318
|
-
serialized.modules.forEach(([id, _file, _url, importedIds]) => {
|
|
3319
|
-
const moduleId = serialized.idTable[id];
|
|
3320
|
-
const moduleNode = nodesById.get(moduleId);
|
|
3321
|
-
importedIds.forEach((importedIdIndex) => {
|
|
3322
|
-
const importedId = serialized.idTable[importedIdIndex];
|
|
3323
|
-
const importedNode = nodesById.get(importedId);
|
|
3324
|
-
moduleNode.importedModules.add(importedNode);
|
|
3325
|
-
importedNode.importers.add(moduleNode);
|
|
3326
|
-
});
|
|
3327
|
-
});
|
|
3328
|
-
}
|
|
3329
|
-
|
|
3330
3160
|
class DotReporter extends BaseReporter {
|
|
3331
3161
|
renderer;
|
|
3332
3162
|
tests = /* @__PURE__ */ new Map();
|
|
@@ -3702,16 +3532,6 @@ function renderSummary(summaryData, fileLinks) {
|
|
|
3702
3532
|
return summary;
|
|
3703
3533
|
}
|
|
3704
3534
|
|
|
3705
|
-
class HangingProcessReporter {
|
|
3706
|
-
whyRunning;
|
|
3707
|
-
onInit() {
|
|
3708
|
-
this.whyRunning = createRequire(import.meta.url)("why-is-node-running");
|
|
3709
|
-
}
|
|
3710
|
-
onProcessTimeout() {
|
|
3711
|
-
this.whyRunning?.();
|
|
3712
|
-
}
|
|
3713
|
-
}
|
|
3714
|
-
|
|
3715
3535
|
const StatusMap = {
|
|
3716
3536
|
fail: "failed",
|
|
3717
3537
|
only: "pending",
|
|
@@ -4186,6 +4006,228 @@ class VerboseReporter extends DefaultReporter {
|
|
|
4186
4006
|
}
|
|
4187
4007
|
}
|
|
4188
4008
|
|
|
4009
|
+
function createBenchmarkJsonReport(files) {
|
|
4010
|
+
const report = { files: [] };
|
|
4011
|
+
for (const file of files) {
|
|
4012
|
+
const groups = [];
|
|
4013
|
+
for (const task of getTasks(file)) if (task?.type === "suite") {
|
|
4014
|
+
const benchmarks = [];
|
|
4015
|
+
for (const t of task.tasks) {
|
|
4016
|
+
const benchmark = t.meta.benchmark && t.result?.benchmark;
|
|
4017
|
+
if (benchmark) benchmarks.push({
|
|
4018
|
+
id: t.id,
|
|
4019
|
+
...benchmark,
|
|
4020
|
+
samples: []
|
|
4021
|
+
});
|
|
4022
|
+
}
|
|
4023
|
+
if (benchmarks.length) groups.push({
|
|
4024
|
+
fullName: getFullName(task, " > "),
|
|
4025
|
+
benchmarks
|
|
4026
|
+
});
|
|
4027
|
+
}
|
|
4028
|
+
report.files.push({
|
|
4029
|
+
filepath: file.filepath,
|
|
4030
|
+
groups
|
|
4031
|
+
});
|
|
4032
|
+
}
|
|
4033
|
+
return report;
|
|
4034
|
+
}
|
|
4035
|
+
function flattenFormattedBenchmarkReport(report) {
|
|
4036
|
+
const flat = {};
|
|
4037
|
+
for (const file of report.files) for (const group of file.groups) for (const t of group.benchmarks) flat[t.id] = t;
|
|
4038
|
+
return flat;
|
|
4039
|
+
}
|
|
4040
|
+
|
|
4041
|
+
const outputMap = /* @__PURE__ */ new WeakMap();
|
|
4042
|
+
function formatNumber(number) {
|
|
4043
|
+
const res = String(number.toFixed(number < 100 ? 4 : 2)).split(".");
|
|
4044
|
+
return res[0].replace(/(?=(?:\d{3})+$)\B/g, ",") + (res[1] ? `.${res[1]}` : "");
|
|
4045
|
+
}
|
|
4046
|
+
const tableHead = [
|
|
4047
|
+
"name",
|
|
4048
|
+
"hz",
|
|
4049
|
+
"min",
|
|
4050
|
+
"max",
|
|
4051
|
+
"mean",
|
|
4052
|
+
"p75",
|
|
4053
|
+
"p99",
|
|
4054
|
+
"p995",
|
|
4055
|
+
"p999",
|
|
4056
|
+
"rme",
|
|
4057
|
+
"samples"
|
|
4058
|
+
];
|
|
4059
|
+
function renderBenchmarkItems(result) {
|
|
4060
|
+
return [
|
|
4061
|
+
result.name,
|
|
4062
|
+
formatNumber(result.hz || 0),
|
|
4063
|
+
formatNumber(result.min || 0),
|
|
4064
|
+
formatNumber(result.max || 0),
|
|
4065
|
+
formatNumber(result.mean || 0),
|
|
4066
|
+
formatNumber(result.p75 || 0),
|
|
4067
|
+
formatNumber(result.p99 || 0),
|
|
4068
|
+
formatNumber(result.p995 || 0),
|
|
4069
|
+
formatNumber(result.p999 || 0),
|
|
4070
|
+
`±${(result.rme || 0).toFixed(2)}%`,
|
|
4071
|
+
(result.sampleCount || 0).toString()
|
|
4072
|
+
];
|
|
4073
|
+
}
|
|
4074
|
+
function computeColumnWidths(results) {
|
|
4075
|
+
const rows = [tableHead, ...results.map((v) => renderBenchmarkItems(v))];
|
|
4076
|
+
return Array.from(tableHead, (_, i) => Math.max(...rows.map((row) => stripVTControlCharacters(row[i]).length)));
|
|
4077
|
+
}
|
|
4078
|
+
function padRow(row, widths) {
|
|
4079
|
+
return row.map((v, i) => i ? v.padStart(widths[i], " ") : v.padEnd(widths[i], " "));
|
|
4080
|
+
}
|
|
4081
|
+
function renderTableHead(widths) {
|
|
4082
|
+
return " ".repeat(3) + padRow(tableHead, widths).map(c.bold).join(" ");
|
|
4083
|
+
}
|
|
4084
|
+
function renderBenchmark(result, widths) {
|
|
4085
|
+
const padded = padRow(renderBenchmarkItems(result), widths);
|
|
4086
|
+
return [
|
|
4087
|
+
padded[0],
|
|
4088
|
+
c.blue(padded[1]),
|
|
4089
|
+
c.cyan(padded[2]),
|
|
4090
|
+
c.cyan(padded[3]),
|
|
4091
|
+
c.cyan(padded[4]),
|
|
4092
|
+
c.cyan(padded[5]),
|
|
4093
|
+
c.cyan(padded[6]),
|
|
4094
|
+
c.cyan(padded[7]),
|
|
4095
|
+
c.cyan(padded[8]),
|
|
4096
|
+
c.dim(padded[9]),
|
|
4097
|
+
c.dim(padded[10])
|
|
4098
|
+
].join(" ");
|
|
4099
|
+
}
|
|
4100
|
+
function renderTable(options) {
|
|
4101
|
+
const output = [];
|
|
4102
|
+
const benchMap = {};
|
|
4103
|
+
for (const task of options.tasks) if (task.meta.benchmark && task.result?.benchmark) benchMap[task.id] = {
|
|
4104
|
+
current: task.result.benchmark,
|
|
4105
|
+
baseline: options.compare?.[task.id]
|
|
4106
|
+
};
|
|
4107
|
+
const benchCount = Object.entries(benchMap).length;
|
|
4108
|
+
const columnWidths = computeColumnWidths(Object.values(benchMap).flatMap((v) => [v.current, v.baseline]).filter(notNullish));
|
|
4109
|
+
let idx = 0;
|
|
4110
|
+
const padding = " ".repeat(1 );
|
|
4111
|
+
for (const task of options.tasks) {
|
|
4112
|
+
const duration = task.result?.duration;
|
|
4113
|
+
const bench = benchMap[task.id];
|
|
4114
|
+
let prefix = "";
|
|
4115
|
+
if (idx === 0 && task.meta?.benchmark) prefix += `${renderTableHead(columnWidths)}\n${padding}`;
|
|
4116
|
+
prefix += ` ${getStateSymbol(task)} `;
|
|
4117
|
+
let suffix = "";
|
|
4118
|
+
if (task.type === "suite") suffix += c.dim(` (${getTests(task).length})`);
|
|
4119
|
+
if (task.mode === "skip" || task.mode === "todo") suffix += c.dim(c.gray(" [skipped]"));
|
|
4120
|
+
if (duration != null) {
|
|
4121
|
+
const color = duration > options.slowTestThreshold ? c.yellow : c.green;
|
|
4122
|
+
suffix += color(` ${Math.round(duration)}${c.dim("ms")}`);
|
|
4123
|
+
}
|
|
4124
|
+
if (options.showHeap && task.result?.heap != null) suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
|
|
4125
|
+
if (bench) {
|
|
4126
|
+
let body = renderBenchmark(bench.current, columnWidths);
|
|
4127
|
+
if (options.compare && bench.baseline) {
|
|
4128
|
+
if (bench.current.hz) {
|
|
4129
|
+
const diff = bench.current.hz / bench.baseline.hz;
|
|
4130
|
+
const diffFixed = diff.toFixed(2);
|
|
4131
|
+
if (diffFixed === "1.0.0") body += c.gray(` [${diffFixed}x]`);
|
|
4132
|
+
if (diff > 1) body += c.blue(` [${diffFixed}x] ⇑`);
|
|
4133
|
+
else body += c.red(` [${diffFixed}x] ⇓`);
|
|
4134
|
+
}
|
|
4135
|
+
output.push(padding + prefix + body + suffix);
|
|
4136
|
+
const bodyBaseline = renderBenchmark(bench.baseline, columnWidths);
|
|
4137
|
+
output.push(`${padding} ${bodyBaseline} ${c.dim("(baseline)")}`);
|
|
4138
|
+
} else {
|
|
4139
|
+
if (bench.current.rank === 1 && benchCount > 1) body += c.bold(c.green(" fastest"));
|
|
4140
|
+
if (bench.current.rank === benchCount && benchCount > 2) body += c.bold(c.gray(" slowest"));
|
|
4141
|
+
output.push(padding + prefix + body + suffix);
|
|
4142
|
+
}
|
|
4143
|
+
} else output.push(padding + prefix + task.name + suffix);
|
|
4144
|
+
if (task.result?.state !== "pass" && outputMap.get(task) != null) {
|
|
4145
|
+
let data = outputMap.get(task);
|
|
4146
|
+
if (typeof data === "string") {
|
|
4147
|
+
data = stripVTControlCharacters(data.trim().split("\n").filter(Boolean).pop());
|
|
4148
|
+
if (data === "") data = void 0;
|
|
4149
|
+
}
|
|
4150
|
+
if (data != null) {
|
|
4151
|
+
const out = ` ${" ".repeat(options.level)}${F_RIGHT} ${data}`;
|
|
4152
|
+
output.push(c.gray(truncateString(out, options.columns)));
|
|
4153
|
+
}
|
|
4154
|
+
}
|
|
4155
|
+
idx++;
|
|
4156
|
+
}
|
|
4157
|
+
return output.filter(Boolean).join("\n");
|
|
4158
|
+
}
|
|
4159
|
+
|
|
4160
|
+
class BenchmarkReporter extends DefaultReporter {
|
|
4161
|
+
compare;
|
|
4162
|
+
async onInit(ctx) {
|
|
4163
|
+
super.onInit(ctx);
|
|
4164
|
+
if (this.ctx.config.benchmark?.compare) {
|
|
4165
|
+
const compareFile = pathe.resolve(this.ctx.config.root, this.ctx.config.benchmark?.compare);
|
|
4166
|
+
try {
|
|
4167
|
+
this.compare = flattenFormattedBenchmarkReport(JSON.parse(await fs.promises.readFile(compareFile, "utf-8")));
|
|
4168
|
+
} catch (e) {
|
|
4169
|
+
this.error(`Failed to read '${compareFile}'`, e);
|
|
4170
|
+
}
|
|
4171
|
+
}
|
|
4172
|
+
}
|
|
4173
|
+
onTaskUpdate(packs) {
|
|
4174
|
+
for (const pack of packs) {
|
|
4175
|
+
const task = this.ctx.state.idMap.get(pack[0]);
|
|
4176
|
+
if (task?.type === "suite" && task.result?.state !== "run") task.tasks.filter((task) => task.result?.benchmark).sort((benchA, benchB) => benchA.result.benchmark.mean - benchB.result.benchmark.mean).forEach((bench, idx) => {
|
|
4177
|
+
bench.result.benchmark.rank = Number(idx) + 1;
|
|
4178
|
+
});
|
|
4179
|
+
}
|
|
4180
|
+
}
|
|
4181
|
+
onTestSuiteResult(testSuite) {
|
|
4182
|
+
super.onTestSuiteResult(testSuite);
|
|
4183
|
+
this.printSuiteTable(testSuite);
|
|
4184
|
+
}
|
|
4185
|
+
printTestModule(testModule) {
|
|
4186
|
+
this.printSuiteTable(testModule);
|
|
4187
|
+
}
|
|
4188
|
+
printSuiteTable(testTask) {
|
|
4189
|
+
const state = testTask.state();
|
|
4190
|
+
if (state === "pending" || state === "queued") return;
|
|
4191
|
+
const benches = testTask.task.tasks.filter((t) => t.meta.benchmark);
|
|
4192
|
+
const duration = testTask.task.result?.duration || 0;
|
|
4193
|
+
if (benches.length > 0 && benches.every((t) => t.result?.state !== "run" && t.result?.state !== "queued")) {
|
|
4194
|
+
let title = `\n ${getStateSymbol(testTask.task)} ${formatProjectName(testTask.project)}${getFullName(testTask.task, separator)}`;
|
|
4195
|
+
if (duration != null && duration > this.ctx.config.slowTestThreshold) title += c.yellow(` ${Math.round(duration)}${c.dim("ms")}`);
|
|
4196
|
+
this.log(title);
|
|
4197
|
+
this.log(renderTable({
|
|
4198
|
+
tasks: benches,
|
|
4199
|
+
level: 1,
|
|
4200
|
+
columns: this.ctx.logger.getColumns(),
|
|
4201
|
+
compare: this.compare,
|
|
4202
|
+
showHeap: this.ctx.config.logHeapUsage,
|
|
4203
|
+
slowTestThreshold: this.ctx.config.slowTestThreshold
|
|
4204
|
+
}));
|
|
4205
|
+
}
|
|
4206
|
+
}
|
|
4207
|
+
async onTestRunEnd(testModules, unhandledErrors, reason) {
|
|
4208
|
+
super.onTestRunEnd(testModules, unhandledErrors, reason);
|
|
4209
|
+
// write output for future comparison
|
|
4210
|
+
let outputFile = this.ctx.config.benchmark?.outputJson;
|
|
4211
|
+
if (outputFile) {
|
|
4212
|
+
outputFile = pathe.resolve(this.ctx.config.root, outputFile);
|
|
4213
|
+
const outputDirectory = pathe.dirname(outputFile);
|
|
4214
|
+
if (!fs.existsSync(outputDirectory)) await fs.promises.mkdir(outputDirectory, { recursive: true });
|
|
4215
|
+
const output = createBenchmarkJsonReport(testModules.map((t) => t.task.file));
|
|
4216
|
+
await fs.promises.writeFile(outputFile, JSON.stringify(output, null, 2));
|
|
4217
|
+
this.log(`Benchmark report written to ${outputFile}`);
|
|
4218
|
+
}
|
|
4219
|
+
}
|
|
4220
|
+
}
|
|
4221
|
+
|
|
4222
|
+
class VerboseBenchmarkReporter extends BenchmarkReporter {
|
|
4223
|
+
verbose = true;
|
|
4224
|
+
}
|
|
4225
|
+
|
|
4226
|
+
const BenchmarkReportsMap = {
|
|
4227
|
+
default: BenchmarkReporter,
|
|
4228
|
+
verbose: VerboseBenchmarkReporter
|
|
4229
|
+
};
|
|
4230
|
+
|
|
4189
4231
|
const ReportersMap = {
|
|
4190
4232
|
"default": DefaultReporter,
|
|
4191
4233
|
"agent": AgentReporter,
|
|
@@ -4201,4 +4243,4 @@ const ReportersMap = {
|
|
|
4201
4243
|
"github-actions": GithubActionsReporter
|
|
4202
4244
|
};
|
|
4203
4245
|
|
|
4204
|
-
export { AgentReporter as A,
|
|
4246
|
+
export { AgentReporter as A, BenchmarkReporter as B, DefaultReporter as D, GithubActionsReporter as G, HangingProcessReporter as H, JUnitReporter as J, ReportersMap as R, TapFlatReporter as T, VerboseBenchmarkReporter as V, BenchmarkReportsMap as a, DotReporter as b, JsonReporter as c, TapReporter as d, VerboseReporter as e, createIndexLocationsMap as f, TraceMap as g, ancestor as h, printError as i, Typechecker as j, generateCodeFrame as k, escapeRegExp as l, createDefinesScript as m, groupBy as n, originalPositionFor as o, parse as p, BlobReporter as q, readBlobs as r, stringify as s, convertTasksToEvents as t, stdout as u, wildcardPatternToRegExp as w };
|