vitest 5.0.0-beta.2 → 5.0.0-beta.4

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.
Files changed (44) hide show
  1. package/dist/browser.d.ts +1 -1
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{base.Opc_YHkk.js → base.BEGVMQrS.js} +6 -6
  4. package/dist/chunks/{browser.d.BUhkKcDl.d.ts → browser.d.BGxB4Xum.d.ts} +5 -26
  5. package/dist/chunks/{cac.8N4bOkkB.js → cac.CyXAEMkE.js} +26 -33
  6. package/dist/chunks/{cli-api.B0RFke2g.js → cli-api.DJMXq34b.js} +640 -615
  7. package/dist/chunks/{config.d.D91DHYaD.d.ts → config.d.DXq1aBpy.d.ts} +93 -89
  8. package/dist/chunks/{creator.BqL2U_x4.js → creator.D66cVXYh.js} +2 -2
  9. package/dist/chunks/{defaults.szbHWQun.js → defaults.CUUnbOrq.js} +6 -4
  10. package/dist/chunks/{env.D4Lgay0q.js → env.BKKtU2WC.js} +2 -1
  11. package/dist/chunks/global.d.BtKPuz2X.d.ts +194 -0
  12. package/dist/chunks/{globals.EHmmu0nC.js → globals.BuY-yD0m.js} +2 -1
  13. package/dist/chunks/{index.D_7-4CaB.js → index.CE58PZNH.js} +1660 -701
  14. package/dist/chunks/{index.CViWo__T.js → index.CcluKS59.js} +4 -4
  15. package/dist/chunks/{index.CbgUM9E5.js → index.nQFVd50u.js} +2 -1
  16. package/dist/chunks/{init-forks.DMge3WTt.js → init-forks.Ce3vGWgL.js} +1 -1
  17. package/dist/chunks/{init-threads.eIoyCTon.js → init-threads.8e1OLv5v.js} +1 -1
  18. package/dist/chunks/{init.BVd7SaCA.js → init.6qx-LaHs.js} +31 -3
  19. package/dist/chunks/{nativeModuleMocker.DKpFw0pk.js → nativeModuleMocker.DDZfQXLs.js} +1 -1
  20. package/dist/chunks/{plugin.d.cIKZEZ16.d.ts → plugin.d.B7MTG_Fe.d.ts} +103 -88
  21. package/dist/chunks/{rpc.d.7JZuxZ8u.d.ts → rpc.d.OQ_EZi1Z.d.ts} +18 -2
  22. package/dist/chunks/{setup-common.Hpq30zVk.js → setup-common.DdEF_hkE.js} +1 -1
  23. package/dist/chunks/{vm.2okbRRME.js → vm.Bu7mmcZq.js} +2 -2
  24. package/dist/chunks/{worker.d.Bu1kXGw4.d.ts → worker.d.yR22cs6X.d.ts} +3 -2
  25. package/dist/cli.js +2 -2
  26. package/dist/config.cjs +1 -1
  27. package/dist/config.d.ts +7 -10
  28. package/dist/config.js +2 -2
  29. package/dist/index.d.ts +24 -41
  30. package/dist/index.js +2 -1
  31. package/dist/module-evaluator.d.ts +4 -1
  32. package/dist/module-evaluator.js +15 -20
  33. package/dist/node.d.ts +20 -13
  34. package/dist/node.js +6 -6
  35. package/dist/runtime.js +2 -2
  36. package/dist/worker.d.ts +3 -3
  37. package/dist/worker.js +7 -6
  38. package/dist/workers/forks.js +8 -7
  39. package/dist/workers/runVmTests.js +4 -3
  40. package/dist/workers/threads.js +8 -7
  41. package/dist/workers/vmForks.js +4 -4
  42. package/dist/workers/vmThreads.js +4 -4
  43. package/package.json +20 -21
  44. package/dist/chunks/global.d.DhbKSQoV.d.ts +0 -99
@@ -1,6 +1,5 @@
1
1
  import * as fs from 'node:fs';
2
2
  import fs__default, { promises, existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync, statSync } from 'node:fs';
3
- import * as pathe from 'pathe';
4
3
  import { join, dirname, basename, isAbsolute, resolve as resolve$1, relative, normalize, extname } from 'pathe';
5
4
  import { r as resolveCoverageProviderModule, C as CoverageProviderMap } from './coverage.CTzCuANN.js';
6
5
  import path, { resolve as resolve$2 } from 'node:path';
@@ -8,13 +7,13 @@ import { unique, createDefer, slash, shuffle, toArray, cleanUrl, withTrailingSla
8
7
  import { a as any, p as prompt } from './index.og1WyBLx.js';
9
8
  import { r as resolveModule, i as isPackageExists, N as NativeModuleRunner } from './nativeModuleRunner.BOeMnHl4.js';
10
9
  import * as vite from 'vite';
11
- import { parseAst, searchForWorkspaceRoot, mergeConfig, fetchModule, parseAstAsync, version, createServer, isFileLoadingAllowed, normalizePath as normalizePath$1, isRunnableDevEnvironment } from 'vite';
10
+ import { parseAst, searchForWorkspaceRoot, mergeConfig, fetchModule, version, createServer, isFileLoadingAllowed, normalizePath as normalizePath$1, isRunnableDevEnvironment } from 'vite';
12
11
  import { c as configFiles, d as defaultBrowserPort, b as defaultInspectPort, a as defaultPort, A as API_PATH } from './constants.-juJ8b_4.js';
13
12
  import * as nodeos from 'node:os';
14
13
  import nodeos__default, { tmpdir, hostname } from 'node:os';
15
- import { getTests, createFileTask as createFileTask$1, createTaskName, validateTags, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, hasFailed, generateFileHash, limitConcurrency, getTestName, getSuites, getTasks, getFullName, createTagsFilter, isTestCase } from '@vitest/runner/utils';
14
+ import { getTests, createFileTask as createFileTask$1, createTaskName, validateTags, calculateSuiteHash, hasFailed, generateFileHash, limitConcurrency, getTestName, getSuites, getTasks, getFullName, createTagsFilter, someTasksAreOnly, interpretTaskModes, isTestCase } from '@vitest/runner/utils';
16
15
  import { serializeValue } from '@vitest/utils/serialize';
17
- import { v as version$1 } from './cac.8N4bOkkB.js';
16
+ import { v as version$1 } from './cac.CyXAEMkE.js';
18
17
  import { distDir, rootDir } from '../path.js';
19
18
  import { Traces } from '../traces.js';
20
19
  import { createDebug } from 'obug';
@@ -23,8 +22,8 @@ import { rm, readFile, writeFile, rename, stat, unlink, mkdir, readdir, copyFile
23
22
  import c from 'tinyrainbow';
24
23
  import url, { pathToFileURL, fileURLToPath } from 'node:url';
25
24
  import { isDynamicPattern, glob } from 'tinyglobby';
26
- import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.szbHWQun.js';
27
- import { i as isTTY, a as isWindows } from './env.D4Lgay0q.js';
25
+ import { c as configDefaults, e as benchmarkConfigDefaults, a as coverageConfigDefaults } from './defaults.CUUnbOrq.js';
26
+ import { a as isTTY, b as isWindows, i as isForceColor } from './env.BKKtU2WC.js';
28
27
  import { w as withLabel, t as truncateString, e as errorBanner, F as F_POINTER, d as divider, f as formatProjectName, a as formatTimeString, b as taskFail, s as separator, c as F_CHECK, g as F_DOWN_RIGHT, h as getStateSymbol, 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.DKODp04v.js';
29
28
  import { isAgent, isCI, provider } from 'std-env';
30
29
  import module$1, { createRequire, builtinModules, isBuiltin as isBuiltin$1 } from 'node:module';
@@ -1222,6 +1221,15 @@ function detectCodeBlock(content) {
1222
1221
 
1223
1222
  const debug$1 = createDebugger("vitest:ast-collect-info");
1224
1223
  const verbose = createDebugger("vitest:ast-collect-verbose");
1224
+ const INTERMEDIATE_CALL_PROPERTIES = new Set([
1225
+ "each",
1226
+ "for",
1227
+ "skipIf",
1228
+ "runIf",
1229
+ "extend",
1230
+ "scoped",
1231
+ "override"
1232
+ ]);
1225
1233
  function isTestFunctionName(name) {
1226
1234
  return name === "it" || name === "test" || name.startsWith("test") || name.endsWith("Test");
1227
1235
  }
@@ -1274,15 +1282,7 @@ function astParseFile(filepath, code) {
1274
1282
  const properties = getProperties(callee);
1275
1283
  const property = callee?.property?.name;
1276
1284
  // intermediate calls like .each(), .for() will be picked up in the next iteration
1277
- if (property && [
1278
- "each",
1279
- "for",
1280
- "skipIf",
1281
- "runIf",
1282
- "extend",
1283
- "scoped",
1284
- "override"
1285
- ].includes(property)) return;
1285
+ if (property && INTERMEDIATE_CALL_PROPERTIES.has(property)) return;
1286
1286
  // skip properties on return values of calls - e.g., test('name', fn).skip()
1287
1287
  if (callee.type === "MemberExpression" && callee.object?.type === "CallExpression") return;
1288
1288
  // derive mode from the full chain (handles any order like .skip.concurrent or .concurrent.skip)
@@ -1349,11 +1349,12 @@ function astParseFile(filepath, code) {
1349
1349
  definitions
1350
1350
  };
1351
1351
  }
1352
- function createFailedFileTask(project, filepath, error) {
1352
+ function createFailedFileTask(project, filepath, error, options) {
1353
1353
  const config = project.serializedConfig;
1354
+ const pool = options?.pool ?? config.pool;
1354
1355
  const file = {
1355
- ...createFileTask$1(filepath, config.root, config.name, config.pool, void 0, {
1356
- typecheck: config.pool === "typescript",
1356
+ ...createFileTask$1(filepath, config.root, config.name, pool, void 0, {
1357
+ typecheck: pool === "typescript",
1357
1358
  __vitest_label__: config.mergeReportsLabel
1358
1359
  }),
1359
1360
  mode: "run",
@@ -1381,12 +1382,13 @@ function serializeError(ctx, error) {
1381
1382
  message: error.message
1382
1383
  }];
1383
1384
  }
1384
- function createFileTask(project, testFilepath, code, requestMap, filepath, fileTags) {
1385
+ function createFileTask(project, testFilepath, code, requestMap, filepath, fileTags, options) {
1385
1386
  const { definitions, ast } = astParseFile(testFilepath, code);
1386
1387
  const config = project.serializedConfig;
1388
+ const pool = options?.pool ?? config.pool;
1387
1389
  const file = {
1388
- ...createFileTask$1(filepath, config.root, config.name, config.pool, void 0, {
1389
- typecheck: config.pool === "typescript",
1390
+ ...createFileTask$1(filepath, config.root, config.name, pool, void 0, {
1391
+ typecheck: pool === "typescript",
1390
1392
  __vitest_label__: config.mergeReportsLabel
1391
1393
  }),
1392
1394
  mode: "run",
@@ -1416,10 +1418,10 @@ function createFileTask(project, testFilepath, code, requestMap, filepath, fileT
1416
1418
  column: processedLocation.column
1417
1419
  });
1418
1420
  if (originalLocation.column != null) {
1419
- verbose?.(`Found location for`, definition.type, definition.name, `${processedLocation.line}:${processedLocation.column}`, "->", `${originalLocation.line}:${originalLocation.column}`);
1421
+ verbose?.(`Found location for`, definition.type, definition.name, `${processedLocation.line}:${processedLocation.column + 1}`, "->", `${originalLocation.line}:${originalLocation.column + 1}`);
1420
1422
  location = {
1421
1423
  line: originalLocation.line,
1422
- column: originalLocation.column
1424
+ column: originalLocation.column + 1
1423
1425
  };
1424
1426
  } else debug$1?.("Cannot find original location for", definition.type, definition.name, `${processedLocation.column}:${processedLocation.line}`);
1425
1427
  } else debug$1?.("Cannot find original location for", definition.type, definition.name, `${definition.start}`);
@@ -1471,6 +1473,7 @@ function createFileTask(project, testFilepath, code, requestMap, filepath, fileT
1471
1473
  timeout: 0,
1472
1474
  annotations: [],
1473
1475
  artifacts: [],
1476
+ benchmarks: [],
1474
1477
  tags: taskTags
1475
1478
  };
1476
1479
  definition.task = task;
@@ -1485,16 +1488,35 @@ function createFileTask(project, testFilepath, code, requestMap, filepath, fileT
1485
1488
  message: `No test suite found in file ${filepath}`
1486
1489
  }]
1487
1490
  };
1488
- return file;
1491
+ return {
1492
+ file,
1493
+ definitions
1494
+ };
1489
1495
  }
1490
1496
  async function astCollectTests(project, filepath) {
1497
+ return (await astCollectFileInformation(project, filepath)).file;
1498
+ }
1499
+ async function astCollectFileInformation(project, filepath, options) {
1491
1500
  const request = await transformSSR(project, filepath);
1492
1501
  const testFilepath = relative(project.config.root, filepath);
1493
1502
  if (!request) {
1494
1503
  debug$1?.("Cannot parse", testFilepath, "(vite didn't return anything)");
1495
- return createFailedFileTask(project, filepath, /* @__PURE__ */ new Error(`Failed to parse ${testFilepath}. Vite didn't return anything.`));
1504
+ return {
1505
+ file: createFailedFileTask(project, filepath, /* @__PURE__ */ new Error(`Failed to parse ${testFilepath}. Vite didn't return anything.`), options),
1506
+ filepath,
1507
+ parsed: "",
1508
+ map: null,
1509
+ definitions: []
1510
+ };
1496
1511
  }
1497
- return createFileTask(project, testFilepath, request.code, request.map, filepath, request.fileTags);
1512
+ const { file, definitions } = createFileTask(project, testFilepath, request.code, request.map, filepath, request.fileTags, options);
1513
+ return {
1514
+ file,
1515
+ filepath,
1516
+ parsed: request.code,
1517
+ map: request.map,
1518
+ definitions
1519
+ };
1498
1520
  }
1499
1521
  async function transformSSR(project, filepath) {
1500
1522
  const { env: pragmaEnv, tags: fileTags } = detectCodeBlock(await promises.readFile(filepath, "utf-8").catch(() => ""));
@@ -1544,7 +1566,7 @@ class BrowserSessions {
1544
1566
  destroySession(sessionId) {
1545
1567
  this.sessions.delete(sessionId);
1546
1568
  }
1547
- createSession(sessionId, project, pool) {
1569
+ createSession(sessionId, project, pool, options) {
1548
1570
  // this promise only waits for the WS connection with the orchestrator to be established
1549
1571
  const defer = createDefer();
1550
1572
  const timeout = setTimeout(() => {
@@ -1552,6 +1574,7 @@ class BrowserSessions {
1552
1574
  }, project.vitest.config.browser.connectTimeout ?? 6e4).unref();
1553
1575
  this.sessions.set(sessionId, {
1554
1576
  project,
1577
+ otelCarrier: options?.otelCarrier,
1555
1578
  connected: () => {
1556
1579
  defer.resolve();
1557
1580
  clearTimeout(timeout);
@@ -2260,8 +2283,10 @@ function resolveInlineWorkerOption(value) {
2260
2283
  if (typeof value === "string" && value.trim().endsWith("%")) return getWorkersCountByPercentage(value);
2261
2284
  else return Number(value);
2262
2285
  }
2286
+ // warn only once, check one PER PROCESS, not per instance,
2287
+ // that's why it's on a module-level
2288
+ let warnedTypeCheck = false;
2263
2289
  function resolveConfig$1(vitest, options, viteConfig) {
2264
- const mode = vitest.mode;
2265
2290
  const logger = vitest.logger;
2266
2291
  if (options.dom) {
2267
2292
  if (viteConfig.test?.environment != null && viteConfig.test.environment !== "happy-dom") logger.console.warn(withLabel("yellow", "Vitest", `Your config.test.environment ("${viteConfig.test.environment}") conflicts with --dom flag ("happy-dom"), ignoring "${viteConfig.test.environment}"`));
@@ -2270,9 +2295,9 @@ function resolveConfig$1(vitest, options, viteConfig) {
2270
2295
  const resolved = {
2271
2296
  ...configDefaults,
2272
2297
  ...options,
2273
- root: viteConfig.root,
2274
- mode
2298
+ root: viteConfig.root
2275
2299
  };
2300
+ resolved.mode ??= viteConfig.mode ?? "test";
2276
2301
  if (resolved.retry && typeof resolved.retry === "object" && typeof resolved.retry.condition === "function") {
2277
2302
  logger.console.warn(c.yellow("Warning: retry.condition function cannot be used inside a config file. Use a RegExp pattern instead, or define the function in your test file."));
2278
2303
  resolved.retry = {
@@ -2304,6 +2329,10 @@ function resolveConfig$1(vitest, options, viteConfig) {
2304
2329
  resolved.name = typeof options.name === "string" ? options.name : options.name?.label || "";
2305
2330
  resolved.color = typeof options.name !== "string" ? options.name?.color : void 0;
2306
2331
  if (resolved.environment === "browser") throw new Error(`Looks like you set "test.environment" to "browser". To enable Browser Mode, use "test.browser.enabled" instead.`);
2332
+ resolved.benchmark = {
2333
+ ...benchmarkConfigDefaults,
2334
+ ...resolved.benchmark
2335
+ };
2307
2336
  const inspector = resolved.inspect || resolved.inspectBrk;
2308
2337
  resolved.inspector = {
2309
2338
  ...resolved.inspector,
@@ -2328,7 +2357,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
2328
2357
  if (resolved.standalone && !resolved.watch) throw new Error(`Vitest standalone mode requires --watch`);
2329
2358
  if (resolved.mergeReports && resolved.watch) throw new Error(`Cannot merge reports with --watch enabled`);
2330
2359
  if (resolved.maxWorkers) resolved.maxWorkers = resolveInlineWorkerOption(resolved.maxWorkers);
2331
- if (!(options.fileParallelism ?? mode !== "benchmark"))
2360
+ if (!(options.fileParallelism ?? true))
2332
2361
  // ignore user config, parallelism cannot be implemented without limiting workers
2333
2362
  resolved.maxWorkers = 1;
2334
2363
  if (resolved.maxConcurrency === 0) {
@@ -2457,7 +2486,7 @@ function resolveConfig$1(vitest, options, viteConfig) {
2457
2486
  resolved.coverage.exclude = [
2458
2487
  ...resolved.coverage.exclude,
2459
2488
  ...resolved.setupFiles.map((file) => `${resolved.coverage.allowExternal ? "**/" : ""}${relative(resolved.root, file)}`),
2460
- ...resolved.include,
2489
+ ...resolved.include.filter((pattern) => !pattern.startsWith("!")),
2461
2490
  resolved.config && slash(resolved.config),
2462
2491
  ...configFiles,
2463
2492
  "**/virtual:*",
@@ -2491,25 +2520,6 @@ function resolveConfig$1(vitest, options, viteConfig) {
2491
2520
  resolved.pool ??= "threads";
2492
2521
  if (resolved.pool === "vmForks" || resolved.pool === "vmThreads" || resolved.pool === "typescript") resolved.isolate = false;
2493
2522
  if (process.env.VITEST_MAX_WORKERS) resolved.maxWorkers = Number.parseInt(process.env.VITEST_MAX_WORKERS);
2494
- if (mode === "benchmark") {
2495
- resolved.benchmark = {
2496
- ...benchmarkConfigDefaults,
2497
- ...resolved.benchmark
2498
- };
2499
- // override test config
2500
- resolved.coverage.enabled = false;
2501
- resolved.typecheck.enabled = false;
2502
- resolved.include = resolved.benchmark.include;
2503
- resolved.exclude = resolved.benchmark.exclude;
2504
- resolved.includeSource = resolved.benchmark.includeSource;
2505
- const reporters = Array.from(new Set([...toArray(resolved.benchmark.reporters), ...toArray(options.reporter)])).filter(Boolean);
2506
- if (reporters.length) resolved.benchmark.reporters = reporters;
2507
- else resolved.benchmark.reporters = ["default"];
2508
- if (options.outputFile) resolved.benchmark.outputFile = options.outputFile;
2509
- // --compare from cli
2510
- if (options.compare) resolved.benchmark.compare = options.compare;
2511
- if (options.outputJson) resolved.benchmark.outputJson = options.outputJson;
2512
- }
2513
2523
  if (typeof resolved.diff === "string") {
2514
2524
  resolved.diff = resolvePath(resolved.diff, resolved.root);
2515
2525
  resolved.forceRerunTriggers.push(resolved.diff);
@@ -2545,25 +2555,23 @@ function resolveConfig$1(vitest, options, viteConfig) {
2545
2555
  // Inline reporter, e.g. { reporter: [{ onFinish() { method() } }] }
2546
2556
  resolved.reporters.push(reporter);
2547
2557
  }
2548
- if (mode !== "benchmark") {
2549
- // @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
2550
- // it is passed down as "vitest --reporter ../reporter.js"
2551
- const reportersFromCLI = resolved.reporter;
2552
- const cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
2553
- // ./reporter.js || ../reporter.js, but not .reporters/reporter.js
2554
- if (/^\.\.?\//.test(reporter)) return resolve$1(process.cwd(), reporter);
2555
- return reporter;
2556
- });
2557
- if (cliReporters.length) {
2558
- // When CLI reporters are specified, preserve options from config file
2559
- const configReportersMap = /* @__PURE__ */ new Map();
2560
- // Build a map of reporter names to their options from the config
2561
- for (const reporter of resolved.reporters) if (Array.isArray(reporter)) {
2562
- const [reporterName, reporterOptions] = reporter;
2563
- if (typeof reporterName === "string") configReportersMap.set(reporterName, reporterOptions);
2564
- }
2565
- resolved.reporters = Array.from(new Set(toArray(cliReporters))).filter(Boolean).map((reporter) => [reporter, configReportersMap.get(reporter) || {}]);
2558
+ // @ts-expect-error "reporter" is from CLI, should be absolute to the running directory
2559
+ // it is passed down as "vitest --reporter ../reporter.js"
2560
+ const reportersFromCLI = resolved.reporter;
2561
+ const cliReporters = toArray(reportersFromCLI || []).map((reporter) => {
2562
+ // ./reporter.js || ../reporter.js, but not .reporters/reporter.js
2563
+ if (/^\.\.?\//.test(reporter)) return resolve$1(process.cwd(), reporter);
2564
+ return reporter;
2565
+ });
2566
+ if (cliReporters.length) {
2567
+ // When CLI reporters are specified, preserve options from config file
2568
+ const configReportersMap = /* @__PURE__ */ new Map();
2569
+ // Build a map of reporter names to their options from the config
2570
+ for (const reporter of resolved.reporters) if (Array.isArray(reporter)) {
2571
+ const [reporterName, reporterOptions] = reporter;
2572
+ if (typeof reporterName === "string") configReportersMap.set(reporterName, reporterOptions);
2566
2573
  }
2574
+ resolved.reporters = Array.from(new Set(toArray(cliReporters))).filter(Boolean).map((reporter) => [reporter, configReportersMap.get(reporter) || {}]);
2567
2575
  }
2568
2576
  resolved.mergeReportsLabel = process.env.VITEST_BLOB_LABEL;
2569
2577
  for (const reporter of resolved.reporters) if (Array.isArray(reporter) && reporter[0] === "blob") {
@@ -2599,12 +2607,15 @@ function resolveConfig$1(vitest, options, viteConfig) {
2599
2607
  };
2600
2608
  resolved.typecheck ??= {};
2601
2609
  resolved.typecheck.enabled ??= false;
2602
- if (resolved.typecheck.enabled) logger.console.warn(c.yellow("Testing types with tsc and vue-tsc is an experimental feature.\nBreaking changes might not follow SemVer, please pin Vitest's version when using it."));
2610
+ if (resolved.typecheck.enabled && !warnedTypeCheck) {
2611
+ warnedTypeCheck = true;
2612
+ logger.console.warn(c.yellow("Testing types with tsc and vue-tsc is an experimental feature.\nBreaking changes might not follow SemVer, please pin Vitest's version when using it."));
2613
+ }
2603
2614
  resolved.browser.enabled ??= false;
2604
2615
  resolved.browser.headless ??= isCI;
2605
2616
  if (resolved.browser.isolate) logger.console.warn(c.yellow("`browser.isolate` is deprecated. Use top-level `isolate` instead."));
2606
2617
  resolved.browser.isolate ??= resolved.isolate ?? true;
2607
- resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark";
2618
+ resolved.browser.fileParallelism ??= options.fileParallelism ?? true;
2608
2619
  // disable in headless mode by default, and if CI is detected
2609
2620
  resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI;
2610
2621
  resolved.browser.commands ??= {};
@@ -2616,7 +2627,8 @@ function resolveConfig$1(vitest, options, viteConfig) {
2616
2627
  resolved.browser.viewport.height ??= 896;
2617
2628
  resolved.browser.locators ??= {};
2618
2629
  resolved.browser.locators.testIdAttribute ??= "data-testid";
2619
- resolved.browser.locators.exact ??= false;
2630
+ resolved.browser.locators.exact ??= true;
2631
+ resolved.browser.locators.errorFormat ??= "all";
2620
2632
  if (typeof resolved.browser.provider === "string") {
2621
2633
  const source = `@vitest/browser-${resolved.browser.provider}`;
2622
2634
  throw new TypeError(`The \`browser.provider\` configuration was changed to accept a factory instead of a string. Add an import of "${resolved.browser.provider}" from "${source}" instead. See: https://vitest.dev/config/browser/provider`);
@@ -3567,8 +3579,8 @@ class ServerModuleRunner extends ModuleRunner {
3567
3579
 
3568
3580
  class FilesNotFoundError extends Error {
3569
3581
  code = "VITEST_FILES_NOT_FOUND";
3570
- constructor(mode) {
3571
- super(`No ${mode} files found`);
3582
+ constructor() {
3583
+ super(`No test files found`);
3572
3584
  }
3573
3585
  }
3574
3586
  class GitNotFoundError extends Error {
@@ -4146,144 +4158,6 @@ function getDefs(c) {
4146
4158
  };
4147
4159
  }
4148
4160
 
4149
- async function collectTests(ctx, filepath) {
4150
- const request = await ctx.vite.environments.ssr.transformRequest(filepath);
4151
- if (!request) return null;
4152
- const ast = await parseAstAsync(request.code);
4153
- const projectName = ctx.name;
4154
- const file = {
4155
- ...createFileTask$1(filepath, ctx.config.root, projectName, void 0, void 0, { typecheck: true }),
4156
- start: ast.start,
4157
- end: ast.end,
4158
- mode: "run"
4159
- };
4160
- file.file = file;
4161
- const definitions = [];
4162
- const getName = (callee) => {
4163
- if (!callee) return null;
4164
- if (callee.type === "Identifier") return callee.name;
4165
- if (callee.type === "CallExpression") return getName(callee.callee);
4166
- if (callee.type === "TaggedTemplateExpression") return getName(callee.tag);
4167
- if (callee.type === "MemberExpression") {
4168
- if (callee.object?.type === "Identifier" && [
4169
- "it",
4170
- "test",
4171
- "describe",
4172
- "suite"
4173
- ].includes(callee.object.name)) return callee.object?.name;
4174
- // direct call as `__vite_ssr_exports_0__.test()`
4175
- if (callee.object?.name?.startsWith("__vite_ssr_")) return getName(callee.property);
4176
- // call as `__vite_ssr__.test.skip()`
4177
- return getName(callee.object?.property);
4178
- }
4179
- // unwrap (0, ...)
4180
- if (callee.type === "SequenceExpression" && callee.expressions.length === 2) {
4181
- const [e0, e1] = callee.expressions;
4182
- if (e0.type === "Literal" && e0.value === 0) return getName(e1);
4183
- }
4184
- return null;
4185
- };
4186
- ancestor(ast, { CallExpression(node) {
4187
- const { callee } = node;
4188
- const name = getName(callee);
4189
- if (!name) return;
4190
- if (![
4191
- "it",
4192
- "test",
4193
- "describe",
4194
- "suite"
4195
- ].includes(name)) return;
4196
- const property = callee?.property?.name;
4197
- let mode = !property || property === name ? "run" : property;
4198
- // they will be picked up in the next iteration
4199
- if ([
4200
- "each",
4201
- "for",
4202
- "skipIf",
4203
- "runIf"
4204
- ].includes(mode)) return;
4205
- let start;
4206
- const end = node.end;
4207
- // .each
4208
- if (callee.type === "CallExpression") start = callee.end;
4209
- else if (callee.type === "TaggedTemplateExpression") start = callee.end + 1;
4210
- else start = node.start;
4211
- const { arguments: [messageNode] } = node;
4212
- const message = messageNode?.type === "Literal" || messageNode?.type === "TemplateLiteral" ? request.code.slice(messageNode.start + 1, messageNode.end - 1) : request.code.slice(messageNode.start, messageNode.end);
4213
- // cannot statically analyze, so we always skip it
4214
- if (mode === "skipIf" || mode === "runIf") mode = "skip";
4215
- definitions.push({
4216
- start,
4217
- end,
4218
- name: message,
4219
- type: name === "it" || name === "test" ? "test" : "suite",
4220
- mode,
4221
- task: null
4222
- });
4223
- } });
4224
- let lastSuite = file;
4225
- const updateLatestSuite = (index) => {
4226
- while (lastSuite.suite && lastSuite.end < index) lastSuite = lastSuite.suite;
4227
- return lastSuite;
4228
- };
4229
- definitions.sort((a, b) => a.start - b.start).forEach((definition) => {
4230
- const latestSuite = updateLatestSuite(definition.start);
4231
- let mode = definition.mode;
4232
- if (latestSuite.mode !== "run")
4233
- // inherit suite mode, if it's set
4234
- mode = latestSuite.mode;
4235
- if (definition.type === "suite") {
4236
- const task = {
4237
- type: definition.type,
4238
- id: "",
4239
- suite: latestSuite,
4240
- file,
4241
- tasks: [],
4242
- mode,
4243
- name: definition.name,
4244
- fullName: createTaskName([lastSuite.fullName, definition.name]),
4245
- fullTestName: createTaskName([lastSuite.fullTestName, definition.name]),
4246
- end: definition.end,
4247
- start: definition.start,
4248
- meta: { typecheck: true }
4249
- };
4250
- definition.task = task;
4251
- latestSuite.tasks.push(task);
4252
- lastSuite = task;
4253
- return;
4254
- }
4255
- const task = {
4256
- type: definition.type,
4257
- id: "",
4258
- suite: latestSuite,
4259
- file,
4260
- mode,
4261
- timeout: 0,
4262
- context: {},
4263
- name: definition.name,
4264
- fullName: createTaskName([lastSuite.fullName, definition.name]),
4265
- fullTestName: createTaskName([lastSuite.fullTestName, definition.name]),
4266
- end: definition.end,
4267
- start: definition.start,
4268
- annotations: [],
4269
- artifacts: [],
4270
- meta: { typecheck: true }
4271
- };
4272
- definition.task = task;
4273
- latestSuite.tasks.push(task);
4274
- });
4275
- calculateSuiteHash(file);
4276
- const hasOnly = someTasksAreOnly(file);
4277
- interpretTaskModes(file, ctx.config.testNamePattern, void 0, void 0, void 0, hasOnly, false, ctx.config.allowOnly);
4278
- return {
4279
- file,
4280
- parsed: request.code,
4281
- filepath,
4282
- map: request.map,
4283
- definitions
4284
- };
4285
- }
4286
-
4287
4161
  const newLineRegExp = /\r?\n/;
4288
4162
  const errCodeRegExp = /error TS(?<errCode>\d+)/;
4289
4163
  async function makeTscErrorInfo(errInfo) {
@@ -4364,7 +4238,7 @@ class Typechecker {
4364
4238
  this._onWatcherRerun = fn;
4365
4239
  }
4366
4240
  async collectFileTests(filepath) {
4367
- return collectTests(this.project, filepath);
4241
+ return astCollectFileInformation(this.project, filepath, { pool: "typescript" });
4368
4242
  }
4369
4243
  getFiles() {
4370
4244
  return this.files;
@@ -4504,18 +4378,16 @@ Possible solutions:
4504
4378
  }
4505
4379
  async spawn() {
4506
4380
  const { root, watch, typecheck } = this.project.config;
4507
- const args = [
4508
- "--noEmit",
4509
- "--pretty",
4510
- "false",
4511
- "--incremental",
4512
- "--tsBuildInfoFile",
4513
- join(process.versions.pnp ? join(nodeos__default.tmpdir(), this.project.hash) : distDir, "tsconfig.tmp.tsbuildinfo")
4514
- ];
4381
+ const args = ["--pretty", "false"];
4382
+ if (typecheck.build) args.unshift("--build");
4383
+ else args.push("--noEmit", "--incremental", "--tsBuildInfoFile", join(process.versions.pnp ? join(nodeos__default.tmpdir(), this.project.hash) : distDir, "tsconfig.tmp.tsbuildinfo"));
4515
4384
  // use builtin watcher because it's faster
4516
4385
  if (watch) args.push("--watch");
4517
4386
  if (typecheck.allowJs) args.push("--allowJs", "--checkJs");
4518
- if (typecheck.tsconfig) args.push("-p", resolve$1(root, typecheck.tsconfig));
4387
+ if (typecheck.tsconfig) {
4388
+ if (!typecheck.build) args.push("-p");
4389
+ args.push(resolve$1(root, typecheck.tsconfig));
4390
+ }
4519
4391
  this._output = "";
4520
4392
  this._startTime = performance$1.now();
4521
4393
  const child = x(typecheck.checker, args, {
@@ -4755,9 +4627,9 @@ function printErrorInner(error, project, options) {
4755
4627
  const testName = e.VITEST_TEST_NAME;
4756
4628
  // testName has testPath inside
4757
4629
  if (testPath) logger.error(c.red(`This error originated in "${c.bold(relative(project.config.root, testPath))}" test file. It doesn't mean the error was thrown inside the file itself, but while it was running.`));
4758
- if (testName) logger.error(c.red(`The latest test that might've caused the error is "${c.bold(testName)}". It might mean one of the following:
4759
- - The error was thrown, while Vitest was running this test.
4760
- - If the error occurred after the test had been completed, this was the last documented test before it was thrown.`));
4630
+ if (testName) logger.error(c.red(`The last test to run before this error was "${c.bold(testName)}". This means either:
4631
+ - the error was thrown while Vitest was running this test, or
4632
+ - the error was thrown after the test completed, and this was the most recent test at that point.`));
4761
4633
  if (typeof e.cause === "object" && e.cause && "name" in e.cause) {
4762
4634
  e.cause.name = `Caused by: ${e.cause.name}`;
4763
4635
  printErrorInner(e.cause, project, {
@@ -5011,10 +4883,10 @@ class Logger {
5011
4883
  }
5012
4884
  printNoTestFound(filters) {
5013
4885
  const config = this.ctx.config;
5014
- if (config.watch && (config.changed || config.related?.length)) this.log(`No affected ${config.mode} files found\n`);
5015
- else if (config.watch) this.log(c.red(`No ${config.mode} files found. You can change the file name pattern by pressing "p"\n`));
5016
- else if (config.passWithNoTests) this.log(`No ${config.mode} files found, exiting with code 0\n`);
5017
- else this.error(c.red(`No ${config.mode} files found, exiting with code 1\n`));
4886
+ if (config.watch && (config.changed || config.related?.length)) this.log(`No affected test files found\n`);
4887
+ else if (config.watch) this.log(c.red(`No test files found. You can change the file name pattern by pressing "p"\n`));
4888
+ else if (config.passWithNoTests) this.log(`No test files found, exiting with code 0\n`);
4889
+ else this.error(c.red(`No test files found, exiting with code 1\n`));
5018
4890
  const comma = c.dim(", ");
5019
4891
  if (filters?.length) this.console.error(c.dim("filter: ") + c.yellow(filters.join(comma)));
5020
4892
  const projectsFilter = toArray(config.project);
@@ -5775,6 +5647,15 @@ function createMethodsRPC(project, methodsOptions = {}) {
5775
5647
  onAfterSuiteRun(meta) {
5776
5648
  vitest.coverageProvider?.onAfterSuiteRun(meta);
5777
5649
  },
5650
+ async onTestBenchmark(testId, benchmark) {
5651
+ return vitest._testRun.recordBenchmark(testId, benchmark);
5652
+ },
5653
+ async readBenchmarkResult(relativePath) {
5654
+ return project.benchmark.readResult(relativePath);
5655
+ },
5656
+ async writeBenchmarkResult(relativePath, data) {
5657
+ return project.benchmark.writeResult(relativePath, data);
5658
+ },
5778
5659
  async onTaskArtifactRecord(testId, artifact) {
5779
5660
  return vitest._testRun.recordArtifact(testId, artifact);
5780
5661
  },
@@ -6067,7 +5948,7 @@ class PoolRunner {
6067
5948
  }
6068
5949
  };
6069
5950
  emitUnexpectedExit = () => {
6070
- const error = /* @__PURE__ */ new Error("Worker exited unexpectedly");
5951
+ const error = /* @__PURE__ */ new Error(`Worker exited unexpectedly during ${this._state} state`);
6071
5952
  this._eventEmitter.emit("error", error);
6072
5953
  };
6073
5954
  waitForStart() {
@@ -6638,7 +6519,7 @@ function deepEqual$1(obj1, obj2) {
6638
6519
  const keys1 = Object.keys(obj1);
6639
6520
  const keys2 = Object.keys(obj2);
6640
6521
  if (keys1.length !== keys2.length) return false;
6641
- for (const key of keys1) if (!keys2.includes(key) || !deepEqual$1(obj1[key], obj2[key])) return false;
6522
+ for (const key of keys1) if (!Object.prototype.hasOwnProperty.call(obj2, key) || !deepEqual$1(obj1[key], obj2[key])) return false;
6642
6523
  return true;
6643
6524
  }
6644
6525
 
@@ -11902,11 +11783,12 @@ function getTestFileEnvironment(project, testFile, browser = false) {
11902
11783
  }
11903
11784
  }
11904
11785
 
11905
- async function getModuleGraph(ctx, projectName, testFilePath, browser = false) {
11786
+ async function getModuleGraph(ctx, projectName, testFilePath) {
11906
11787
  const graph = {};
11907
11788
  const externalized = /* @__PURE__ */ new Set();
11908
11789
  const inlined = /* @__PURE__ */ new Set();
11909
11790
  const project = ctx.getProjectByName(projectName);
11791
+ const browser = project.config.browser.enabled;
11910
11792
  const environment = project.config.experimental.viteModuleRunner === false ? project.vite.environments.__vitest__ : getTestFileEnvironment(project, testFilePath, browser);
11911
11793
  if (!environment) throw new Error(`Cannot find environment for ${testFilePath}`);
11912
11794
  const seen = /* @__PURE__ */ new Map();
@@ -12048,10 +11930,11 @@ function setup(ctx, _server) {
12048
11930
  } catch {}
12049
11931
  return result;
12050
11932
  },
12051
- async getTransformResult(projectName, moduleId, testFileTaskId, browser = false) {
11933
+ async getTransformResult(projectName, moduleId, testFileTaskId) {
12052
11934
  const project = ctx.getProjectByName(projectName);
12053
11935
  const testModule = ctx.state.getReportedEntityById(testFileTaskId);
12054
11936
  if (!testModule || !isFileServingAllowed(project.vite.config, moduleId)) return;
11937
+ const browser = !!project.config.browser.enabled;
12055
11938
  const environment = getTestFileEnvironment(project, testModule.moduleId, browser);
12056
11939
  const moduleNode = environment?.moduleGraph.getModuleById(moduleId);
12057
11940
  if (!environment || !moduleNode?.transformResult) return;
@@ -12069,8 +11952,8 @@ function setup(ctx, _server) {
12069
11952
  } catch {}
12070
11953
  return result;
12071
11954
  },
12072
- async getModuleGraph(project, id, browser) {
12073
- return getModuleGraph(ctx, project, id, browser);
11955
+ async getModuleGraph(project, id) {
11956
+ return getModuleGraph(ctx, project, id);
12074
11957
  },
12075
11958
  async updateSnapshot(file) {
12076
11959
  // silently ignore exec/write attempts if not allowed
@@ -12242,13 +12125,39 @@ function handleDefineValue(value) {
12242
12125
  return JSON.stringify(value);
12243
12126
  }
12244
12127
 
12128
+ class BenchmarkManager {
12129
+ constructor(project) {
12130
+ this.project = project;
12131
+ }
12132
+ // Resolve a user-supplied path against the project root. Reject paths that
12133
+ // escape the project root: `bench.from()` accepts arbitrary input, and we
12134
+ // never want a benchmark file to be able to read or clobber files outside
12135
+ // the workspace.
12136
+ resolve(relativePath) {
12137
+ const root = this.project.config.root;
12138
+ const absolute = isAbsolute(relativePath) ? resolve$1(relativePath) : resolve$1(root, relativePath);
12139
+ const rootWithSep = root.endsWith("/") ? root : `${root}/`;
12140
+ if (absolute !== root && !absolute.startsWith(rootWithSep)) throw new Error(`Benchmark artifact path "${relativePath}" resolves outside the project root (${root}). Paths passed to \`writeResult\` and \`bench.from()\` must point inside the project.`);
12141
+ return absolute;
12142
+ }
12143
+ async readResult(relativePath) {
12144
+ const path = this.resolve(relativePath);
12145
+ if (!existsSync(path)) return null;
12146
+ return JSON.parse(await readFile(path, "utf-8"));
12147
+ }
12148
+ async writeResult(relativePath, data) {
12149
+ const absolute = this.resolve(relativePath);
12150
+ await mkdir(dirname(absolute), { recursive: true });
12151
+ await writeFile(absolute, `${JSON.stringify(data, null, 2)}\n`, "utf-8");
12152
+ }
12153
+ }
12154
+
12245
12155
  function serializeConfig(project) {
12246
12156
  const { config, globalConfig } = project;
12247
12157
  const viteConfig = project._vite?.config;
12248
12158
  const optimizer = config.deps?.optimizer || {};
12249
12159
  return {
12250
12160
  environmentOptions: config.environmentOptions,
12251
- mode: config.mode,
12252
12161
  isolate: config.isolate,
12253
12162
  maxWorkers: config.maxWorkers,
12254
12163
  base: config.base,
@@ -12343,7 +12252,8 @@ function serializeConfig(project) {
12343
12252
  screenshotFailures: browser.screenshotFailures,
12344
12253
  locators: {
12345
12254
  testIdAttribute: browser.locators.testIdAttribute,
12346
- exact: browser.locators.exact
12255
+ exact: browser.locators.exact,
12256
+ errorFormat: browser.locators.errorFormat
12347
12257
  },
12348
12258
  providerOptions: provider?.name === "playwright" ? { actionTimeout: provider?.options?.actionTimeout } : {},
12349
12259
  trackUnhandledErrors: browser.trackUnhandledErrors ?? true,
@@ -12353,7 +12263,12 @@ function serializeConfig(project) {
12353
12263
  })(config.browser),
12354
12264
  standalone: config.standalone,
12355
12265
  printConsoleTrace: config.printConsoleTrace ?? globalConfig.printConsoleTrace,
12356
- benchmark: config.benchmark && { includeSamples: config.benchmark.includeSamples },
12266
+ benchmark: {
12267
+ enabled: config.benchmark.enabled,
12268
+ retainSamples: config.benchmark.retainSamples,
12269
+ suppressExportGetterWarnings: config.benchmark.suppressExportGetterWarnings,
12270
+ projectName: config.benchmark.projectName
12271
+ },
12357
12272
  serializedDefines: config.browser.enabled ? "" : project._serializedDefines || "",
12358
12273
  experimental: {
12359
12274
  fsModuleCache: config.experimental.fsModuleCache ?? false,
@@ -12367,7 +12282,7 @@ function serializeConfig(project) {
12367
12282
  strictTags: config.strictTags ?? true,
12368
12283
  mergeReportsLabel: config.mergeReportsLabel,
12369
12284
  slowTestThreshold: config.slowTestThreshold ?? globalConfig.slowTestThreshold ?? configDefaults.slowTestThreshold,
12370
- isAgent
12285
+ disableColors: isAgent && !isForceColor()
12371
12286
  };
12372
12287
  }
12373
12288
 
@@ -13364,7 +13279,8 @@ function WorkspaceVitestPlugin(project, options) {
13364
13279
  name: "vitest:project:name",
13365
13280
  enforce: "post",
13366
13281
  config(viteConfig) {
13367
- const testConfig = viteConfig.test || {};
13282
+ viteConfig.test ??= {};
13283
+ const testConfig = viteConfig.test;
13368
13284
  let { label: name, color } = typeof testConfig.name === "string" ? { label: testConfig.name } : {
13369
13285
  label: "",
13370
13286
  ...testConfig.name
@@ -13376,6 +13292,10 @@ function WorkspaceVitestPlugin(project, options) {
13376
13292
  if (existsSync(pkgJsonPath)) name = JSON.parse(readFileSync(pkgJsonPath, "utf-8")).name;
13377
13293
  if (typeof name !== "string" || !name) name = basename(dir);
13378
13294
  } else name = options.workspacePath.toString();
13295
+ if (project.vitest._cliOptions.benchmarkOnly) {
13296
+ viteConfig.test.benchmark ??= {};
13297
+ viteConfig.test.benchmark.enabled = true;
13298
+ }
13379
13299
  const isBrowserEnabled = viteConfig.test?.browser?.enabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled);
13380
13300
  // keep project names to potentially filter it out
13381
13301
  const workspaceNames = [name];
@@ -13388,6 +13308,7 @@ function WorkspaceVitestPlugin(project, options) {
13388
13308
  instance.name ??= name ? `${name} (${instance.browser})` : instance.browser;
13389
13309
  if (isBrowserEnabled) workspaceNames.push(instance.name);
13390
13310
  });
13311
+ if (viteConfig.test?.benchmark?.enabled) workspaceNames.push(name ? `${name} (bench)` : "bench");
13391
13312
  // if there is `--project=...` filter, check if any of the potential projects match
13392
13313
  // if projects don't match, we ignore the test project altogether
13393
13314
  // if some of them match, they will later be filtered again by `resolveWorkspace`
@@ -13660,9 +13581,9 @@ class TestSpecification {
13660
13581
  * This class represents a test suite for a test module within a single project.
13661
13582
  * @internal
13662
13583
  */
13663
- constructor(project, moduleId, pool, testLinesOrOptions, metaOverride) {
13584
+ constructor(project, moduleId, pool, testLinesOrOptions, taskIdOverride) {
13664
13585
  const projectName = project.config.name;
13665
- this.taskId = generateFileHash(relative(project.config.root, moduleId), projectName, metaOverride ?? {
13586
+ this.taskId = taskIdOverride ?? generateFileHash(relative(project.config.root, moduleId), projectName, {
13666
13587
  typecheck: pool === "typescript",
13667
13588
  __vitest_label__: project.config.mergeReportsLabel
13668
13589
  });
@@ -13720,6 +13641,7 @@ class TestProject {
13720
13641
  * Temporary directory for the project. This is unique for each project. Vitest stores transformed content here.
13721
13642
  */
13722
13643
  tmpDir;
13644
+ benchmark = new BenchmarkManager(this);
13723
13645
  /** @internal */ typechecker;
13724
13646
  /** @internal */ _config;
13725
13647
  /** @internal */ _vite;
@@ -13779,8 +13701,8 @@ class TestProject {
13779
13701
  * Creates a new test specification. Specifications describe how to run tests.
13780
13702
  * @param moduleId The file path
13781
13703
  */
13782
- createSpecification(moduleId, locationsOrOptions, pool, metaOverride) {
13783
- return new TestSpecification(this, moduleId, pool || getFilePoolName(this), locationsOrOptions, metaOverride);
13704
+ createSpecification(moduleId, locationsOrOptions, pool, taskIdOverride) {
13705
+ return new TestSpecification(this, moduleId, pool || getFilePoolName(this), locationsOrOptions, taskIdOverride);
13784
13706
  }
13785
13707
  toJSON() {
13786
13708
  return {
@@ -14029,7 +13951,8 @@ class TestProject {
14029
13951
  async _configureServer(options, server) {
14030
13952
  this._config = resolveConfig$1(this.vitest, {
14031
13953
  ...options,
14032
- coverage: this.vitest.config.coverage
13954
+ coverage: this.vitest.config.coverage,
13955
+ attachmentsDir: this.vitest.config.attachmentsDir
14033
13956
  }, server.config);
14034
13957
  this._config.api.token = this.vitest.config.api.token;
14035
13958
  this._config.mergeReportsLabel = this.vitest.config.mergeReportsLabel;
@@ -14069,9 +13992,8 @@ class TestProject {
14069
13992
  const url = new URL("/__vitest_test__/", origin);
14070
13993
  url.searchParams.set("sessionId", sessionId);
14071
13994
  const otelCarrier = this.vitest._traces.getContextCarrier();
14072
- if (otelCarrier) url.searchParams.set("otelCarrier", JSON.stringify(otelCarrier));
14073
13995
  this.vitest._browserSessions.sessionIds.add(sessionId);
14074
- const sessionPromise = this.vitest._browserSessions.createSession(sessionId, this, pool);
13996
+ const sessionPromise = this.vitest._browserSessions.createSession(sessionId, this, pool, { otelCarrier });
14075
13997
  const pagePromise = this.browser.provider.openPage(sessionId, url.toString(), { parallel: pool.parallel ?? false });
14076
13998
  await Promise.all([sessionPromise, pagePromise]);
14077
13999
  }
@@ -14125,7 +14047,7 @@ class TestProject {
14125
14047
  return project;
14126
14048
  }
14127
14049
  /** @internal */
14128
- static _cloneBrowserProject(parent, config) {
14050
+ static _cloneTestProject(parent, config) {
14129
14051
  const clone = new TestProject(parent.vitest, void 0, parent.tmpDir);
14130
14052
  clone.runner = parent.runner;
14131
14053
  clone._vite = parent._vite;
@@ -14287,7 +14209,58 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
14287
14209
  }
14288
14210
  names.add(name);
14289
14211
  }
14290
- return resolveBrowserProjects(vitest, names, resolvedProjects);
14212
+ return resolveDefaultProjects(vitest, names, resolvedProjects);
14213
+ }
14214
+ async function resolveDefaultProjects(vitest, names, resolvedProjects) {
14215
+ const newProjects = await resolveBrowserProjects(vitest, names, resolvedProjects);
14216
+ let lastGroupOrder = Math.max(0, ...newProjects.map((p) => p.config.sequence.groupOrder));
14217
+ newProjects.forEach((project) => {
14218
+ const benchmark = project.config.benchmark;
14219
+ if (!benchmark.enabled) return;
14220
+ if (vitest.isExcludedByProjectFilter(project.config.name)) {
14221
+ benchmark.enabled = false;
14222
+ return;
14223
+ }
14224
+ const name = project.config.name ? `${project.config.name} (bench)` : "bench";
14225
+ if (!vitest.matchesProjectFilter(name)) {
14226
+ benchmark.enabled = false;
14227
+ return;
14228
+ }
14229
+ if (names.has(name)) throw new Error(`Cannot create a benchmark project because the name "${name}" is already in use.`);
14230
+ names.add(name);
14231
+ const benchmarkProject = TestProject._cloneTestProject(project, {
14232
+ ...project.config,
14233
+ name,
14234
+ include: benchmark.include,
14235
+ exclude: benchmark.exclude,
14236
+ includeSource: benchmark.includeSource,
14237
+ coverage: {
14238
+ ...project.config.coverage,
14239
+ enabled: false
14240
+ },
14241
+ maxWorkers: 1,
14242
+ maxConcurrency: 1,
14243
+ testTimeout: project.config.testTimeout < 6e4 ? 6e4 : project.config.testTimeout,
14244
+ hookTimeout: project.config.hookTimeout < 12e4 ? 12e4 : project.config.hookTimeout,
14245
+ benchmark: {
14246
+ ...benchmark,
14247
+ projectName: project.config.name ?? ""
14248
+ },
14249
+ sequence: {
14250
+ ...project.config.sequence,
14251
+ concurrent: false,
14252
+ groupOrder: ++lastGroupOrder
14253
+ },
14254
+ typecheck: {
14255
+ ...project.config.typecheck,
14256
+ enabled: false
14257
+ }
14258
+ });
14259
+ // disable benchmark in the original project
14260
+ benchmark.enabled = false;
14261
+ newProjects.push(benchmarkProject);
14262
+ });
14263
+ return newProjects;
14291
14264
  }
14292
14265
  async function resolveBrowserProjects(vitest, names, resolvedProjects) {
14293
14266
  const removeProjects = /* @__PURE__ */ new Set();
@@ -14328,7 +14301,7 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
14328
14301
  names.add(name);
14329
14302
  const clonedConfig = cloneConfig(project, config);
14330
14303
  clonedConfig.name = name;
14331
- const clone = TestProject._cloneBrowserProject(project, clonedConfig);
14304
+ const clone = TestProject._cloneTestProject(project, clonedConfig);
14332
14305
  resolvedProjects.push(clone);
14333
14306
  });
14334
14307
  removeProjects.add(project);
@@ -14345,7 +14318,8 @@ function cloneConfig(project, { browser, ...config }) {
14345
14318
  ...project.config.browser,
14346
14319
  locators: locators ? {
14347
14320
  testIdAttribute: locators.testIdAttribute ?? currentConfig.locators.testIdAttribute,
14348
- exact: locators.exact ?? currentConfig.locators.exact
14321
+ exact: locators.exact ?? currentConfig.locators.exact,
14322
+ errorFormat: locators.errorFormat ?? currentConfig.locators.errorFormat
14349
14323
  } : project.config.browser.locators,
14350
14324
  viewport: viewport ?? currentConfig.viewport,
14351
14325
  testerHtmlPath: testerHtmlPath ?? currentConfig.testerHtmlPath,
@@ -14452,8 +14426,10 @@ function getDefaultTestProject(vitest) {
14452
14426
  }
14453
14427
  function getPotentialProjectNames(project) {
14454
14428
  const names = [project.name];
14429
+ // TODO: include benchmarks in browsers
14455
14430
  if (project.config.browser.instances) names.push(...project.config.browser.instances.map((i) => i.name));
14456
14431
  else if (project.config.browser.name) names.push(project.config.browser.name);
14432
+ if (project.config.benchmark.enabled) names.push(project.name ? `${project.name} (bench)` : "bench");
14457
14433
  return names;
14458
14434
  }
14459
14435
 
@@ -14512,8 +14488,8 @@ class BlobReporter {
14512
14488
  outputFile = [
14513
14489
  "blob",
14514
14490
  this.ctx.config.mergeReportsLabel,
14515
- shard ? `-${shard.index}-${shard.count}` : ""
14516
- ].join("");
14491
+ shard ? `${shard.index}-${shard.count}` : ""
14492
+ ].filter(Boolean).join("-");
14517
14493
  outputFile = `${sanitizeFilePath(outputFile)}.json`;
14518
14494
  await report.writeFile(outputFile, content, "utf-8");
14519
14495
  outputFile = resolve$1(report.root, outputFile);
@@ -14587,9 +14563,11 @@ function serializeEnvironmentModuleGraph(environment) {
14587
14563
  };
14588
14564
  const modules = [];
14589
14565
  for (const [id, mod] of environment.moduleGraph.idToModuleMap.entries()) {
14590
- if (!mod.file) continue;
14566
+ // Vite can generate module with `file = ""` for module id "#..."
14567
+ // when the actual module doesn't exist (e.g. resolve failure or mocked module)
14568
+ if (mod.file == null) continue;
14591
14569
  const importedIds = [];
14592
- for (const importedNode of mod.importedModules) if (importedNode.id) importedIds.push(getIdIndex(importedNode.id));
14570
+ for (const importedNode of mod.importedModules) if (importedNode.id !== null) importedIds.push(getIdIndex(importedNode.id));
14593
14571
  modules.push([
14594
14572
  getIdIndex(id),
14595
14573
  getIdIndex(mod.file),
@@ -14608,6 +14586,10 @@ function deserializeEnvironmentModuleGraph(environment, serialized) {
14608
14586
  const moduleId = serialized.idTable[id];
14609
14587
  const filePath = serialized.idTable[file];
14610
14588
  const urlPath = serialized.idTable[url];
14589
+ // `createFileOnlyEntry('')` normalizes the file to ".". This keeps
14590
+ // the graph usable, but doesn't perfectly round-trip Vite's `file = ""`
14591
+ // nodes for ids like "#...".
14592
+ // We may just do moduleNode.file = filePath in the future.
14611
14593
  const moduleNode = environment.moduleGraph.createFileOnlyEntry(filePath);
14612
14594
  moduleNode.url = urlPath;
14613
14595
  moduleNode.id = moduleId;
@@ -14679,6 +14661,66 @@ function createReport(ctx, scope) {
14679
14661
  };
14680
14662
  }
14681
14663
 
14664
+ const BENCH_TABLE_HEAD = [
14665
+ "hz",
14666
+ "min",
14667
+ "max",
14668
+ "mean",
14669
+ "p75",
14670
+ "p99",
14671
+ "p995",
14672
+ "p999",
14673
+ "rme",
14674
+ "samples"
14675
+ ];
14676
+ function formatBenchNumber(number) {
14677
+ const res = String(number.toFixed(number < 100 ? 4 : 2)).split(".");
14678
+ return res[0].replace(/(?=(?:\d{3})+$)\B/g, ",") + (res[1] ? `.${res[1]}` : "");
14679
+ }
14680
+ // Plain-text rendering of the benchmark table (no ANSI colors, no indent).
14681
+ // Used by the junit reporter to embed benchmark data in <system-out>.
14682
+ function renderBenchmarkTableText(benchmarks, columnName = "name") {
14683
+ const lines = [];
14684
+ for (const benchmark of benchmarks) {
14685
+ const { tasks } = benchmark;
14686
+ if (tasks.length === 0) continue;
14687
+ if (lines.length > 0) lines.push("");
14688
+ const rows = tasks.map(renderBenchmarkRow);
14689
+ const head = [columnName, ...BENCH_TABLE_HEAD];
14690
+ const widths = computeBenchColumnWidths(head, rows);
14691
+ lines.push(padBenchRow(head, widths).join(" "));
14692
+ for (const task of tasks) {
14693
+ let row = padBenchRow(renderBenchmarkRow(task), widths).join(" ");
14694
+ if (task.rank === 1 && tasks.length > 1) row += " fastest";
14695
+ if (task.rank === tasks.length && tasks.length > 2) row += " slowest";
14696
+ lines.push(row);
14697
+ }
14698
+ }
14699
+ return lines.join("\n");
14700
+ }
14701
+ function renderBenchmarkRow(task) {
14702
+ return [
14703
+ task.name,
14704
+ formatBenchNumber(task.throughput.mean || 0),
14705
+ formatBenchNumber(task.latency.min || 0),
14706
+ formatBenchNumber(task.latency.max || 0),
14707
+ formatBenchNumber(task.latency.mean || 0),
14708
+ formatBenchNumber(task.latency.p75 || 0),
14709
+ formatBenchNumber(task.latency.p99 || 0),
14710
+ formatBenchNumber(task.latency.p995 || 0),
14711
+ formatBenchNumber(task.latency.p999 || 0),
14712
+ `\u00B1${(task.latency.rme || 0).toFixed(2)}%`,
14713
+ String(task.latency.samplesCount || 0)
14714
+ ];
14715
+ }
14716
+ function computeBenchColumnWidths(header, rows) {
14717
+ const allRows = [header, ...rows];
14718
+ return Array.from(header, (_, i) => Math.max(...allRows.map((row) => stripVTControlCharacters(row[i]).length)));
14719
+ }
14720
+ function padBenchRow(row, widths) {
14721
+ return row.map((v, i) => i === 0 ? v.padEnd(widths[i]) : v.padStart(widths[i]));
14722
+ }
14723
+
14682
14724
  const BADGE_PADDING = " ";
14683
14725
  class BaseReporter {
14684
14726
  start = 0;
@@ -14692,6 +14734,7 @@ class BaseReporter {
14692
14734
  silent;
14693
14735
  _filesInWatchMode = /* @__PURE__ */ new Map();
14694
14736
  _timeStart = formatTimeString(/* @__PURE__ */ new Date());
14737
+ _perProjectBenchmarks = /* @__PURE__ */ new Map();
14695
14738
  constructor(options = {}) {
14696
14739
  this.isTTY = options.isTTY ?? isTTY;
14697
14740
  this.silent = options.silent;
@@ -14713,17 +14756,34 @@ class BaseReporter {
14713
14756
  onTestRunStart(_specifications) {
14714
14757
  this.start = performance$1.now();
14715
14758
  this._timeStart = formatTimeString(/* @__PURE__ */ new Date());
14759
+ this._perProjectBenchmarks.clear();
14716
14760
  }
14717
14761
  onTestRunEnd(testModules, unhandledErrors, _reason) {
14718
14762
  const files = testModules.map((testModule) => testModule.task);
14719
14763
  const errors = [...unhandledErrors];
14720
14764
  this.end = performance$1.now();
14721
14765
  if (!files.length && !errors.length) this.ctx.logger.printNoTestFound(this.ctx.filenamePattern);
14722
- else this.reportSummary(files, errors);
14766
+ else {
14767
+ this.printPerProjectBenchmarks();
14768
+ this.reportSummary(files, errors);
14769
+ }
14723
14770
  }
14724
14771
  onTestCaseResult(testCase) {
14725
14772
  if (testCase.result().state === "failed") this.logFailedTask(testCase.task);
14726
14773
  }
14774
+ onTestCaseBenchmark(testCase, benchmark) {
14775
+ const projectName = testCase.project.name || "";
14776
+ for (const task of benchmark.tasks) {
14777
+ if (!task.perProject) continue;
14778
+ const benchKey = `${testCase.module.relativeModuleId} > ${testCase.fullName} > ${task.name}`;
14779
+ let projectMap = this._perProjectBenchmarks.get(benchKey);
14780
+ if (!projectMap) {
14781
+ projectMap = /* @__PURE__ */ new Map();
14782
+ this._perProjectBenchmarks.set(benchKey, projectMap);
14783
+ }
14784
+ projectMap.set(projectName, task);
14785
+ }
14786
+ }
14727
14787
  onTestSuiteResult(testSuite) {
14728
14788
  if (testSuite.state() === "failed") this.logFailedTask(testSuite.task);
14729
14789
  }
@@ -14782,9 +14842,13 @@ class BaseReporter {
14782
14842
  const { duration = 0 } = test.diagnostic() || {};
14783
14843
  const padding = this.getTestIndentation(test.task);
14784
14844
  const suffix = this.getTestCaseSuffix(test);
14845
+ // perProject tasks still appear in the inline table — they're additionally
14846
+ // aggregated in the cross-project section at the end of the run
14847
+ const inlineBenchmarks = test.benchmarks().filter((b) => b.tasks.length > 0);
14785
14848
  if (testResult.state === "failed") this.log(c.red(` ${padding}${taskFail} ${this.getTestName(test.task, separator)}`) + suffix);
14786
- else if (duration > this.ctx.config.slowTestThreshold) this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, separator)} ${suffix}`);
14787
- else if (this.ctx.config.hideSkippedTests && testResult.state === "skipped" && test.options.mode !== "todo") ; else if (this.renderSucceed || moduleState === "failed") this.log(` ${padding}${this.getStateSymbol(test)} ${this.getTestName(test.task, separator)}${suffix}`);
14849
+ else if (duration > this.ctx.config.slowTestThreshold) this.log(` ${padding}${c.yellow(c.dim(F_CHECK))} ${this.getTestName(test.task, separator)}${suffix}`);
14850
+ else if (this.ctx.config.hideSkippedTests && testResult.state === "skipped" && test.options.mode !== "todo") ; else if (this.renderSucceed || moduleState === "failed" || inlineBenchmarks.length) this.log(` ${padding}${this.getStateSymbol(test)} ${this.getTestName(test.task, separator)}${suffix}`);
14851
+ if (inlineBenchmarks.length > 0) this.printBenchmarkTable(inlineBenchmarks, padding);
14788
14852
  }
14789
14853
  getModuleLog(testModule, counts) {
14790
14854
  let state = c.dim(`${counts.tests} test${counts.tests > 1 ? "s" : ""}`);
@@ -14938,8 +15002,7 @@ class BaseReporter {
14938
15002
  reportSummary(files, errors) {
14939
15003
  this.printErrorsSummary(files, errors);
14940
15004
  const leakCount = this.printLeaksSummary();
14941
- if (this.ctx.config.mode === "benchmark") this.reportBenchmarkSummary(files);
14942
- else this.reportTestSummary(files, errors, leakCount);
15005
+ this.reportTestSummary(files, errors, leakCount);
14943
15006
  }
14944
15007
  reportTestSummary(files, errors, leakCount) {
14945
15008
  this.log();
@@ -15004,19 +15067,23 @@ class BaseReporter {
15004
15067
  }
15005
15068
  }
15006
15069
  if (allImports.length === 0) return;
15007
- const dangerImports = allImports.filter((imp) => imp.totalTime >= thresholds.danger);
15008
- const warnImports = allImports.filter((imp) => imp.totalTime >= thresholds.warn);
15009
- const hasDangerImports = dangerImports.length > 0;
15010
- const hasWarnImports = warnImports.length > 0;
15070
+ let dangerImportsCount = 0;
15071
+ let hasWarnImports = false;
15072
+ let totalSelfTime = 0;
15073
+ let totalTotalTime = 0;
15074
+ for (const imp of allImports) {
15075
+ if (imp.totalTime >= thresholds.danger) dangerImportsCount++;
15076
+ if (imp.totalTime >= thresholds.warn) hasWarnImports = true;
15077
+ totalSelfTime += imp.selfTime;
15078
+ totalTotalTime += imp.totalTime;
15079
+ }
15011
15080
  // Determine if we should print
15012
- const shouldFail = failOnDanger && hasDangerImports;
15081
+ const shouldFail = failOnDanger && dangerImportsCount > 0;
15013
15082
  if (!(print === true || print === "on-warn" && hasWarnImports || shouldFail)) return;
15014
15083
  const sortedImports = allImports.sort((a, b) => b.totalTime - a.totalTime);
15015
15084
  const maxTotalTime = sortedImports[0].totalTime;
15016
15085
  const limit = this.ctx.config.experimental.importDurations.limit;
15017
15086
  const topImports = sortedImports.slice(0, limit);
15018
- const totalSelfTime = allImports.reduce((sum, imp) => sum + imp.selfTime, 0);
15019
- const totalTotalTime = allImports.reduce((sum, imp) => sum + imp.totalTime, 0);
15020
15087
  const slowestImport = sortedImports[0];
15021
15088
  this.log();
15022
15089
  this.log(c.bold("Import Duration Breakdown") + c.dim(` (Top ${limit})`));
@@ -15056,7 +15123,7 @@ class BaseReporter {
15056
15123
  // Fail if danger threshold exceeded
15057
15124
  if (shouldFail) {
15058
15125
  this.log();
15059
- this.ctx.logger.error(`ERROR: ${dangerImports.length} import(s) exceeded the danger threshold of ${thresholds.danger}ms`);
15126
+ this.ctx.logger.error(`ERROR: ${dangerImportsCount} import(s) exceeded the danger threshold of ${thresholds.danger}ms`);
15060
15127
  process.exitCode = 1;
15061
15128
  }
15062
15129
  }
@@ -15123,21 +15190,63 @@ class BaseReporter {
15123
15190
  }
15124
15191
  return leakWithStacks.size;
15125
15192
  }
15126
- reportBenchmarkSummary(files) {
15127
- const topBenches = getTests(files).filter((i) => i.result?.benchmark?.rank === 1);
15128
- this.log(`\n${withLabel("cyan", "BENCH", "Summary\n")}`);
15129
- for (const bench of topBenches) {
15130
- const group = bench.suite || bench.file;
15131
- if (!group) continue;
15132
- const groupName = this.getFullName(group, separator);
15133
- const project = this.ctx.projects.find((p) => p.name === bench.file.projectName);
15134
- this.log(` ${formatProjectName(project)}${bench.name}${c.dim(` - ${groupName}`)}`);
15135
- const siblings = group.tasks.filter((i) => i.meta.benchmark && i.result?.benchmark && i !== bench).sort((a, b) => a.result.benchmark.rank - b.result.benchmark.rank);
15136
- for (const sibling of siblings) {
15137
- const number = (sibling.result.benchmark.mean / bench.result.benchmark.mean).toFixed(2);
15138
- this.log(c.green(` ${number}x `) + c.gray("faster than ") + sibling.name);
15139
- }
15193
+ printPerProjectBenchmarks() {
15194
+ if (this._perProjectBenchmarks.size === 0) return;
15195
+ let hasComparable = false;
15196
+ for (const projectMap of this._perProjectBenchmarks.values()) if (projectMap.size > 1) {
15197
+ hasComparable = true;
15198
+ break;
15199
+ }
15200
+ if (!hasComparable) return;
15201
+ this.log("");
15202
+ this.log(divider(c.bold(c.bgBlue(` Cross-Project Benchmark Comparison `)), null, null, c.blue));
15203
+ for (const [benchName, projectMap] of this._perProjectBenchmarks) {
15204
+ const tasks = [...projectMap.entries()].sort((a, b) => a[1].latency.mean - b[1].latency.mean).map(([projectName, task], index) => ({
15205
+ ...task,
15206
+ name: projectName,
15207
+ rank: index + 1
15208
+ }));
15209
+ if (tasks.length <= 1) continue;
15140
15210
  this.log("");
15211
+ this.log(` ${c.dim(benchName)}`);
15212
+ this.printBenchmarkTable([{
15213
+ name: benchName,
15214
+ tasks
15215
+ }], "", "project");
15216
+ }
15217
+ this.log("");
15218
+ }
15219
+ printBenchmarkTable(benchmarks, basePadding, columnName = "name") {
15220
+ let printedCount = 0;
15221
+ for (const benchmark of benchmarks) {
15222
+ const { tasks } = benchmark;
15223
+ if (tasks.length === 0) continue;
15224
+ if (printedCount > 0) this.log("");
15225
+ const rows = tasks.map((t) => renderBenchmarkRow(t));
15226
+ const tableHead = [columnName, ...BENCH_TABLE_HEAD];
15227
+ const widths = computeBenchColumnWidths(tableHead, rows);
15228
+ const indent = ` ${basePadding} `;
15229
+ this.log(`${indent}${padBenchRow(tableHead, widths).map(c.bold).join(" ")}`);
15230
+ printedCount++;
15231
+ for (const task of tasks) {
15232
+ const padded = padBenchRow(renderBenchmarkRow(task), widths);
15233
+ let row = [
15234
+ padded[0],
15235
+ c.blue(padded[1]),
15236
+ c.cyan(padded[2]),
15237
+ c.cyan(padded[3]),
15238
+ c.cyan(padded[4]),
15239
+ c.cyan(padded[5]),
15240
+ c.cyan(padded[6]),
15241
+ c.cyan(padded[7]),
15242
+ c.cyan(padded[8]),
15243
+ c.dim(padded[9]),
15244
+ c.dim(padded[10])
15245
+ ].join(" ");
15246
+ if (task.rank === 1 && tasks.length > 1) row += c.bold(c.green(" fastest"));
15247
+ if (task.rank === tasks.length && tasks.length > 2) row += c.bold(c.gray(" slowest"));
15248
+ this.log(`${indent}${row}`);
15249
+ }
15141
15250
  }
15142
15251
  }
15143
15252
  printTaskErrors(tasks, errorDivider) {
@@ -15195,7 +15304,7 @@ function deepEqual(a, b) {
15195
15304
  const keysA = Object.keys(a);
15196
15305
  const keysB = Object.keys(b);
15197
15306
  if (keysA.length !== keysB.length) return false;
15198
- for (const key of keysA) if (!keysB.includes(key) || !deepEqual(a[key], b[key])) return false;
15307
+ for (const key of keysA) if (!Object.prototype.hasOwnProperty.call(b, key) || !deepEqual(a[key], b[key])) return false;
15199
15308
  return true;
15200
15309
  }
15201
15310
  function sum(items, cb) {
@@ -15208,6 +15317,9 @@ function getIndentation(suite, level = 1) {
15208
15317
  return level;
15209
15318
  }
15210
15319
 
15320
+ /** Minimum time between two renders, no matter how many scheduled renderes were called */
15321
+ const DEFAULT_RENDER_THRESHOLD_MS = 100;
15322
+ /** Interval between automatic renders. If no test state changes happened, this will increase just duration field */
15211
15323
  const DEFAULT_RENDER_INTERVAL_MS = 1e3;
15212
15324
  const ESC$1 = "\x1B[";
15213
15325
  const CLEAR_LINE = `${ESC$1}K`;
@@ -15230,14 +15342,19 @@ class WindowRenderer {
15230
15342
  cleanups = [];
15231
15343
  constructor(options) {
15232
15344
  this.options = {
15233
- interval: DEFAULT_RENDER_INTERVAL_MS,
15234
- ...options
15345
+ ...options,
15346
+ threshold: options.threshold ?? DEFAULT_RENDER_THRESHOLD_MS,
15347
+ interval: options.interval ?? DEFAULT_RENDER_INTERVAL_MS
15235
15348
  };
15349
+ // Capture the original write methods early, before intercepting these
15236
15350
  this.streams = {
15237
15351
  output: options.logger.outputStream.write.bind(options.logger.outputStream),
15238
15352
  error: options.logger.errorStream.write.bind(options.logger.errorStream)
15239
15353
  };
15240
15354
  this.cleanups.push(this.interceptStream(process.stdout, "output"), this.interceptStream(process.stderr, "error"));
15355
+ // Intercept calls to custom VitestOptions.stdout and stderr streams
15356
+ if (options.logger.outputStream !== process.stdout) this.cleanups.push(this.interceptStream(options.logger.outputStream, "output"));
15357
+ if (options.logger.errorStream !== process.stderr) this.cleanups.push(this.interceptStream(options.logger.errorStream, "error"));
15241
15358
  // Write buffered content on unexpected exits, e.g. direct `process.exit()` calls
15242
15359
  this.options.logger.onTerminalCleanup(() => {
15243
15360
  this.flushBuffer();
@@ -15269,9 +15386,10 @@ class WindowRenderer {
15269
15386
  if (!this.renderScheduled) {
15270
15387
  this.renderScheduled = true;
15271
15388
  this.flushBuffer();
15272
- setTimeout(() => {
15389
+ if (this.options.threshold) setTimeout(() => {
15273
15390
  this.renderScheduled = false;
15274
- }, 100).unref();
15391
+ }, this.options.threshold).unref();
15392
+ else this.renderScheduled = false;
15275
15393
  }
15276
15394
  }
15277
15395
  flushBuffer() {
@@ -15293,15 +15411,16 @@ class WindowRenderer {
15293
15411
  if (current) this.render(current?.message, current?.type);
15294
15412
  }
15295
15413
  render(message, type = "output") {
15414
+ this.write(SYNC_START);
15296
15415
  if (this.finished) {
15297
15416
  this.clearWindow();
15298
- return this.write(message || "", type);
15417
+ this.write(message || "", type);
15418
+ return this.write(SYNC_END);
15299
15419
  }
15300
15420
  const windowContent = this.options.getWindow();
15301
15421
  const rowCount = getRenderedRowCount(windowContent, this.options.logger.getColumns());
15302
15422
  let padding = this.windowHeight - rowCount;
15303
15423
  if (padding > 0 && message) padding -= getRenderedRowCount([message], this.options.logger.getColumns());
15304
- this.write(SYNC_START);
15305
15424
  this.clearWindow();
15306
15425
  if (message) this.write(message, type);
15307
15426
  if (padding > 0) this.write("\n".repeat(padding));
@@ -15373,7 +15492,9 @@ class SummaryReporter {
15373
15492
  };
15374
15493
  this.renderer = new WindowRenderer({
15375
15494
  logger: ctx.logger,
15376
- getWindow: () => this.createSummary()
15495
+ getWindow: () => this.createSummary(),
15496
+ interval: this.options.interval,
15497
+ threshold: this.options.threshold
15377
15498
  });
15378
15499
  this.ctx.onClose(() => {
15379
15500
  clearInterval(this.durationInterval);
@@ -15416,27 +15537,30 @@ class SummaryReporter {
15416
15537
  this.maxParallelTests = Math.max(this.maxParallelTests, this.runningModules.size);
15417
15538
  this.renderer.schedule();
15418
15539
  }
15419
- onHookStart(options) {
15420
- const stats = this.getHookStats(options);
15421
- if (!stats) return;
15422
- const hook = {
15423
- name: options.name,
15540
+ startStep(stats, name) {
15541
+ const step = {
15542
+ name,
15424
15543
  visible: false,
15425
15544
  startTime: performance.now(),
15426
15545
  onFinish: () => {}
15427
15546
  };
15428
- stats.hook?.onFinish?.();
15429
- stats.hook = hook;
15547
+ stats.step?.onFinish?.();
15548
+ stats.step = step;
15549
+ if (!Number.isFinite(this.ctx.config.slowTestThreshold)) return;
15430
15550
  const timeout = setTimeout(() => {
15431
- hook.visible = true;
15551
+ step.visible = true;
15432
15552
  }, this.ctx.config.slowTestThreshold).unref();
15433
- hook.onFinish = () => clearTimeout(timeout);
15553
+ step.onFinish = () => clearTimeout(timeout);
15554
+ }
15555
+ onHookStart(options) {
15556
+ const stats = this.getStepStats(options.entity);
15557
+ if (stats) this.startStep(stats, options.name);
15434
15558
  }
15435
15559
  onHookEnd(options) {
15436
- const stats = this.getHookStats(options);
15437
- if (stats?.hook?.name !== options.name) return;
15438
- stats.hook.onFinish();
15439
- stats.hook.visible = false;
15560
+ const stats = this.getStepStats(options.entity);
15561
+ if (stats?.step?.name !== options.name) return;
15562
+ stats.step.onFinish();
15563
+ stats.step.visible = false;
15440
15564
  }
15441
15565
  onTestCaseReady(test) {
15442
15566
  // Track slow running tests only on verbose mode
@@ -15449,11 +15573,11 @@ class SummaryReporter {
15449
15573
  startTime: performance.now(),
15450
15574
  onFinish: () => {}
15451
15575
  };
15452
- const timeout = setTimeout(() => {
15576
+ const timeout = Number.isFinite(this.ctx.config.slowTestThreshold) ? setTimeout(() => {
15453
15577
  slowTest.visible = true;
15454
- }, this.ctx.config.slowTestThreshold).unref();
15578
+ }, this.ctx.config.slowTestThreshold).unref() : void 0;
15455
15579
  slowTest.onFinish = () => {
15456
- slowTest.hook?.onFinish();
15580
+ slowTest.step?.onFinish();
15457
15581
  clearTimeout(timeout);
15458
15582
  };
15459
15583
  stats.tests.set(test.id, slowTest);
@@ -15493,7 +15617,7 @@ class SummaryReporter {
15493
15617
  this.removeTestModule(module.id);
15494
15618
  this.renderer.schedule();
15495
15619
  }
15496
- getHookStats({ entity }) {
15620
+ getStepStats(entity) {
15497
15621
  // Track slow running hooks only on verbose mode
15498
15622
  if (!this.options.verbose) return;
15499
15623
  const module = entity.type === "module" ? entity : entity.module;
@@ -15510,12 +15634,12 @@ class SummaryReporter {
15510
15634
  name: testFile.projectName,
15511
15635
  color: testFile.projectColor
15512
15636
  }) + typecheck + label + testFile.filename + c.dim(!testFile.completed && !testFile.total ? " [queued]" : ` ${testFile.completed}/${testFile.total}`));
15513
- const slowTasks = [testFile.hook, ...testFile.tests.values()].filter((t) => t != null && t.visible);
15637
+ const slowTasks = [testFile.step, ...testFile.tests.values()].filter((t) => t != null && t.visible);
15514
15638
  for (const [index, task] of slowTasks.entries()) {
15515
15639
  const elapsed = this.currentTime - task.startTime;
15516
15640
  const icon = index === slowTasks.length - 1 ? F_TREE_NODE_END : F_TREE_NODE_MIDDLE;
15517
15641
  summary.push(c.bold(c.yellow(` ${icon} `)) + task.name + c.bold(c.yellow(` ${formatTime(Math.max(0, elapsed))}`)));
15518
- if (task.hook?.visible) summary.push(c.bold(c.yellow(` ${F_TREE_NODE_END} `)) + task.hook.name);
15642
+ if (task.step?.visible) summary.push(c.bold(c.yellow(` ${F_TREE_NODE_END} `)) + task.step.name);
15519
15643
  }
15520
15644
  }
15521
15645
  if (this.runningModules.size > 0) summary.push("");
@@ -15537,7 +15661,7 @@ class SummaryReporter {
15537
15661
  removeTestModule(id) {
15538
15662
  if (!id) return;
15539
15663
  const testFile = this.runningModules.get(id);
15540
- testFile?.hook?.onFinish();
15664
+ testFile?.step?.onFinish();
15541
15665
  testFile?.tests?.forEach((test) => test.onFinish());
15542
15666
  this.runningModules.delete(id);
15543
15667
  clearTimeout(this.finishedModules.get(id));
@@ -15630,7 +15754,10 @@ class DefaultReporter extends BaseReporter {
15630
15754
  }
15631
15755
  onInit(ctx) {
15632
15756
  super.onInit(ctx);
15633
- this.summary?.onInit(ctx, { verbose: this.verbose });
15757
+ this.summary?.onInit(ctx, {
15758
+ verbose: this.verbose,
15759
+ ...this.options.summaryOptions
15760
+ });
15634
15761
  }
15635
15762
  }
15636
15763
 
@@ -16075,7 +16202,8 @@ class JsonReporter {
16075
16202
  }
16076
16203
  return filtered;
16077
16204
  })() : t.meta,
16078
- tags: t.tags || []
16205
+ tags: t.tags || [],
16206
+ benchmarks: t.benchmarks
16079
16207
  };
16080
16208
  });
16081
16209
  if (tests.some((t) => t.result?.state === "run" || t.result?.state === "queued")) this.ctx.logger.warn("WARNING: Some tests are still running when generating the JSON report.This is likely an internal bug in Vitest.Please report it to https://github.com/vitest-dev/vitest/issues");
@@ -16235,6 +16363,18 @@ class JUnitReporter {
16235
16363
  for (const log of logs) await this.baseLog(escapeXML(log.content));
16236
16364
  });
16237
16365
  }
16366
+ async writeSystemOut(task) {
16367
+ const logs = this.options.includeConsoleOutput && task.logs ? task.logs.filter((log) => log.type === "stdout") : [];
16368
+ const benchmarks = task.type === "test" ? task.benchmarks : [];
16369
+ if (logs.length === 0 && benchmarks.length === 0) return;
16370
+ await this.writeElement("system-out", {}, async () => {
16371
+ for (const log of logs) await this.baseLog(escapeXML(log.content));
16372
+ if (benchmarks.length > 0) {
16373
+ if (logs.length > 0) await this.baseLog("");
16374
+ await this.baseLog(escapeXML(renderBenchmarkTableText(benchmarks)));
16375
+ }
16376
+ });
16377
+ }
16238
16378
  applyTemplate(template, vars) {
16239
16379
  if (typeof template === "function") return template(vars);
16240
16380
  return template.replace(/\{filename\}/g, () => vars.filename).replace(/\{filepath\}/g, () => vars.filepath).replace(/\{basename\}/g, () => vars.basename).replace(/\{classname\}/g, () => vars.classname).replace(/\{title\}/g, () => vars.title).replace(/\{suitename\}/g, () => vars.suitename).replace(/\{displayName\}/g, () => vars.displayName);
@@ -16260,10 +16400,8 @@ class JUnitReporter {
16260
16400
  name: testcaseName,
16261
16401
  time: getDuration(task)
16262
16402
  }, async () => {
16263
- if (this.options.includeConsoleOutput) {
16264
- await this.writeLogs(task, "out");
16265
- await this.writeLogs(task, "err");
16266
- }
16403
+ await this.writeSystemOut(task);
16404
+ if (this.options.includeConsoleOutput) await this.writeLogs(task, "err");
16267
16405
  if (task.mode === "skip" || task.mode === "todo") await this.logger.log("<skipped/>");
16268
16406
  if (task.type === "test" && task.annotations.length) {
16269
16407
  await this.logger.log("<properties>");
@@ -16277,14 +16415,7 @@ class JUnitReporter {
16277
16415
  }
16278
16416
  if (task.result?.state === "fail") {
16279
16417
  const errors = task.result.errors || [];
16280
- for (const error of errors) await this.writeElement("failure", {
16281
- message: error?.message,
16282
- type: error?.name
16283
- }, async () => {
16284
- if (!error || !this.options.stackTrace) return;
16285
- const result = this.ctx.logger.formatError(error, { project: this.ctx.getProjectByName(task.file?.projectName ?? "") });
16286
- await this.baseLog(escapeXML(stripVTControlCharacters(result.output.trim())));
16287
- });
16418
+ for (const error of errors) await this.writeErrorElement("failure", error, { project: this.ctx.getProjectByName(task.file?.projectName ?? "") });
16288
16419
  }
16289
16420
  });
16290
16421
  }
@@ -16303,7 +16434,52 @@ class JUnitReporter {
16303
16434
  if (typeof this.options.suiteNameTemplate === "function") return this.options.suiteNameTemplate(vars);
16304
16435
  return this.options.suiteNameTemplate.replace(/\{filepath\}/g, () => vars.filepath).replace(/\{filename\}/g, () => vars.filename).replace(/\{basename\}/g, () => vars.basename).replace(/\{displayName\}/g, () => vars.displayName).replace(/\{title\}/g, () => vars.title);
16305
16436
  }
16306
- async onTestRunEnd(testModules) {
16437
+ async writeErrorElement(elementName, error, errorOptions) {
16438
+ await this.writeElement(elementName, {
16439
+ message: error?.message,
16440
+ type: error?.name
16441
+ }, async () => {
16442
+ if (!error || !this.options.stackTrace) return;
16443
+ const result = this.ctx.logger.formatError(error, errorOptions);
16444
+ await this.baseLog(escapeXML(stripVTControlCharacters(result.output.trim())));
16445
+ });
16446
+ }
16447
+ async writeUnhandledErrorsTestsuite(unhandledErrors, testModules) {
16448
+ await this.writeElement("testsuite", {
16449
+ name: "vitest unhandled errors",
16450
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16451
+ hostname: this.options.hostname || hostname(),
16452
+ tests: unhandledErrors.length,
16453
+ failures: 0,
16454
+ errors: unhandledErrors.length,
16455
+ skipped: 0,
16456
+ time: "0"
16457
+ }, async () => {
16458
+ // Stable order across runs — workers/projects report errors concurrently.
16459
+ const sortedErrors = [...unhandledErrors].sort((a, b) => {
16460
+ const ka = `${a.VITEST_TEST_PATH ?? ""}\0${a.type ?? ""}\0${a.name ?? ""}\0${a.message ?? ""}`;
16461
+ const kb = `${b.VITEST_TEST_PATH ?? ""}\0${b.type ?? ""}\0${b.name ?? ""}\0${b.message ?? ""}`;
16462
+ return ka < kb ? -1 : ka > kb ? 1 : 0;
16463
+ });
16464
+ for (const error of sortedErrors) {
16465
+ const errorTitle = error.type || error.name || "Unhandled Error";
16466
+ // Only attribute when the path resolves to exactly one module — when
16467
+ // multiple projects share a file, errors lack a project identifier and
16468
+ // we can't disambiguate without one (tracked for follow-up).
16469
+ const matches = error.VITEST_TEST_PATH ? testModules.filter((m) => m.task.filepath === error.VITEST_TEST_PATH) : [];
16470
+ const owningModule = matches.length === 1 ? matches[0] : void 0;
16471
+ await this.writeElement("testcase", {
16472
+ classname: "vitest unhandled errors",
16473
+ file: this.options.addFileAttribute && error.VITEST_TEST_PATH ? relative(this.ctx.config.root, error.VITEST_TEST_PATH) : void 0,
16474
+ name: error.message ? `${errorTitle}: ${error.message}` : errorTitle,
16475
+ time: "0"
16476
+ }, async () => {
16477
+ await this.writeErrorElement("error", error, { project: owningModule?.project });
16478
+ });
16479
+ }
16480
+ });
16481
+ }
16482
+ async onTestRunEnd(testModules, unhandledErrors = []) {
16307
16483
  const files = testModules.map((testModule) => testModule.task);
16308
16484
  const separator = this.options.ancestorSeparator ?? " > ";
16309
16485
  await this.logger.log("<?xml version=\"1.0\" encoding=\"UTF-8\" ?>");
@@ -16343,7 +16519,8 @@ class JUnitReporter {
16343
16519
  suite: null,
16344
16520
  file: null,
16345
16521
  annotations: [],
16346
- artifacts: []
16522
+ artifacts: [],
16523
+ benchmarks: []
16347
16524
  });
16348
16525
  }
16349
16526
  return {
@@ -16361,32 +16538,36 @@ class JUnitReporter {
16361
16538
  name: this.options.suiteName || "vitest tests",
16362
16539
  tests: 0,
16363
16540
  failures: 0,
16364
- errors: 0,
16541
+ errors: unhandledErrors.length,
16365
16542
  time: 0
16366
16543
  });
16544
+ stats.tests += unhandledErrors.length;
16545
+ // Plain byte compare (not localeCompare) so output is identical across machines and ICU versions.
16546
+ const orderedSuites = transformed.map((file, i) => {
16547
+ const filename = relative(this.ctx.config.root, file.filepath);
16548
+ return {
16549
+ file,
16550
+ filename,
16551
+ suiteName: this.resolveSuiteNameTemplate(files[i], filename)
16552
+ };
16553
+ }).sort((a, b) => a.suiteName < b.suiteName ? -1 : a.suiteName > b.suiteName ? 1 : 0);
16367
16554
  await this.writeElement("testsuites", {
16368
16555
  ...stats,
16369
16556
  time: executionTime(stats.time)
16370
16557
  }, async () => {
16371
- for (let i = 0; i < transformed.length; i++) {
16372
- const file = transformed[i];
16373
- const filename = relative(this.ctx.config.root, file.filepath);
16374
- // resolveSuiteNameTemplate needs the original file (before task flattening) to
16375
- // search for top-level describe blocks, so pass files[i] directly.
16376
- const suiteName = this.resolveSuiteNameTemplate(files[i], filename);
16377
- await this.writeElement("testsuite", {
16378
- name: suiteName,
16379
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16380
- hostname: this.options.hostname || hostname(),
16381
- tests: file.tasks.length,
16382
- failures: file.stats.failures,
16383
- errors: 0,
16384
- skipped: file.stats.skipped,
16385
- time: getDuration(file)
16386
- }, async () => {
16387
- await this.writeTasks(file.tasks, filename, file.filepath);
16388
- });
16389
- }
16558
+ for (const { file, filename, suiteName } of orderedSuites) await this.writeElement("testsuite", {
16559
+ name: suiteName,
16560
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
16561
+ hostname: this.options.hostname || hostname(),
16562
+ tests: file.tasks.length,
16563
+ failures: file.stats.failures,
16564
+ errors: 0,
16565
+ skipped: file.stats.skipped,
16566
+ time: getDuration(file)
16567
+ }, async () => {
16568
+ await this.writeTasks(file.tasks, filename, file.filepath);
16569
+ });
16570
+ if (unhandledErrors.length) await this.writeUnhandledErrorsTestsuite(unhandledErrors, testModules);
16390
16571
  });
16391
16572
  if (this.reportFile) this.ctx.logger.log(`JUNIT report written to ${this.reportFile}`);
16392
16573
  await this.fileFd?.close();
@@ -16541,231 +16722,11 @@ class VerboseReporter extends DefaultReporter {
16541
16722
  this.printAnnotations(test, "log", 3);
16542
16723
  this.log();
16543
16724
  }
16725
+ const inlineBenchmarks = test.benchmarks().filter((b) => b.tasks.length > 0);
16726
+ if (inlineBenchmarks.length > 0) this.printBenchmarkTable(inlineBenchmarks, "");
16544
16727
  }
16545
16728
  }
16546
16729
 
16547
- function createBenchmarkJsonReport(files) {
16548
- const report = { files: [] };
16549
- for (const file of files) {
16550
- const groups = [];
16551
- for (const task of getTasks(file)) if (task?.type === "suite") {
16552
- const benchmarks = [];
16553
- for (const t of task.tasks) {
16554
- const benchmark = t.meta.benchmark && t.result?.benchmark;
16555
- if (benchmark) benchmarks.push({
16556
- id: t.id,
16557
- ...benchmark,
16558
- samples: []
16559
- });
16560
- }
16561
- if (benchmarks.length) groups.push({
16562
- fullName: getFullName(task, " > "),
16563
- benchmarks
16564
- });
16565
- }
16566
- report.files.push({
16567
- filepath: file.filepath,
16568
- groups
16569
- });
16570
- }
16571
- return report;
16572
- }
16573
- function flattenFormattedBenchmarkReport(report) {
16574
- const flat = {};
16575
- for (const file of report.files) for (const group of file.groups) for (const t of group.benchmarks) flat[t.id] = t;
16576
- return flat;
16577
- }
16578
-
16579
- const outputMap = /* @__PURE__ */ new WeakMap();
16580
- function formatNumber(number) {
16581
- const res = String(number.toFixed(number < 100 ? 4 : 2)).split(".");
16582
- return res[0].replace(/(?=(?:\d{3})+$)\B/g, ",") + (res[1] ? `.${res[1]}` : "");
16583
- }
16584
- const tableHead = [
16585
- "name",
16586
- "hz",
16587
- "min",
16588
- "max",
16589
- "mean",
16590
- "p75",
16591
- "p99",
16592
- "p995",
16593
- "p999",
16594
- "rme",
16595
- "samples"
16596
- ];
16597
- function renderBenchmarkItems(result) {
16598
- return [
16599
- result.name,
16600
- formatNumber(result.hz || 0),
16601
- formatNumber(result.min || 0),
16602
- formatNumber(result.max || 0),
16603
- formatNumber(result.mean || 0),
16604
- formatNumber(result.p75 || 0),
16605
- formatNumber(result.p99 || 0),
16606
- formatNumber(result.p995 || 0),
16607
- formatNumber(result.p999 || 0),
16608
- `±${(result.rme || 0).toFixed(2)}%`,
16609
- (result.sampleCount || 0).toString()
16610
- ];
16611
- }
16612
- function computeColumnWidths(results) {
16613
- const rows = [tableHead, ...results.map((v) => renderBenchmarkItems(v))];
16614
- return Array.from(tableHead, (_, i) => Math.max(...rows.map((row) => stripVTControlCharacters(row[i]).length)));
16615
- }
16616
- function padRow(row, widths) {
16617
- return row.map((v, i) => i ? v.padStart(widths[i], " ") : v.padEnd(widths[i], " "));
16618
- }
16619
- function renderTableHead(widths) {
16620
- return " ".repeat(3) + padRow(tableHead, widths).map(c.bold).join(" ");
16621
- }
16622
- function renderBenchmark(result, widths) {
16623
- const padded = padRow(renderBenchmarkItems(result), widths);
16624
- return [
16625
- padded[0],
16626
- c.blue(padded[1]),
16627
- c.cyan(padded[2]),
16628
- c.cyan(padded[3]),
16629
- c.cyan(padded[4]),
16630
- c.cyan(padded[5]),
16631
- c.cyan(padded[6]),
16632
- c.cyan(padded[7]),
16633
- c.cyan(padded[8]),
16634
- c.dim(padded[9]),
16635
- c.dim(padded[10])
16636
- ].join(" ");
16637
- }
16638
- function renderTable(options) {
16639
- const output = [];
16640
- const benchMap = {};
16641
- for (const task of options.tasks) if (task.meta.benchmark && task.result?.benchmark) benchMap[task.id] = {
16642
- current: task.result.benchmark,
16643
- baseline: options.compare?.[task.id]
16644
- };
16645
- const benchCount = Object.entries(benchMap).length;
16646
- const columnWidths = computeColumnWidths(Object.values(benchMap).flatMap((v) => [v.current, v.baseline]).filter(notNullish));
16647
- let idx = 0;
16648
- const padding = " ".repeat(1 );
16649
- for (const task of options.tasks) {
16650
- const duration = task.result?.duration;
16651
- const bench = benchMap[task.id];
16652
- let prefix = "";
16653
- if (idx === 0 && task.meta?.benchmark) prefix += `${renderTableHead(columnWidths)}\n${padding}`;
16654
- prefix += ` ${getStateSymbol(task)} `;
16655
- let suffix = "";
16656
- if (task.type === "suite") suffix += c.dim(` (${getTests(task).length})`);
16657
- if (task.mode === "skip" || task.mode === "todo") suffix += c.dim(c.gray(" [skipped]"));
16658
- if (duration != null) {
16659
- const color = duration > options.slowTestThreshold ? c.yellow : c.green;
16660
- suffix += color(` ${Math.round(duration)}${c.dim("ms")}`);
16661
- }
16662
- if (options.showHeap && task.result?.heap != null) suffix += c.magenta(` ${Math.floor(task.result.heap / 1024 / 1024)} MB heap used`);
16663
- if (bench) {
16664
- let body = renderBenchmark(bench.current, columnWidths);
16665
- if (options.compare && bench.baseline) {
16666
- if (bench.current.hz) {
16667
- const diff = bench.current.hz / bench.baseline.hz;
16668
- const diffFixed = diff.toFixed(2);
16669
- if (diffFixed === "1.0.0") body += c.gray(` [${diffFixed}x]`);
16670
- if (diff > 1) body += c.blue(` [${diffFixed}x] ⇑`);
16671
- else body += c.red(` [${diffFixed}x] ⇓`);
16672
- }
16673
- output.push(padding + prefix + body + suffix);
16674
- const bodyBaseline = renderBenchmark(bench.baseline, columnWidths);
16675
- output.push(`${padding} ${bodyBaseline} ${c.dim("(baseline)")}`);
16676
- } else {
16677
- if (bench.current.rank === 1 && benchCount > 1) body += c.bold(c.green(" fastest"));
16678
- if (bench.current.rank === benchCount && benchCount > 2) body += c.bold(c.gray(" slowest"));
16679
- output.push(padding + prefix + body + suffix);
16680
- }
16681
- } else output.push(padding + prefix + task.name + suffix);
16682
- if (task.result?.state !== "pass" && outputMap.get(task) != null) {
16683
- let data = outputMap.get(task);
16684
- if (typeof data === "string") {
16685
- data = stripVTControlCharacters(data.trim().split("\n").filter(Boolean).pop());
16686
- if (data === "") data = void 0;
16687
- }
16688
- if (data != null) {
16689
- const out = ` ${" ".repeat(options.level)}${F_RIGHT} ${data}`;
16690
- output.push(c.gray(truncateString(out, options.columns)));
16691
- }
16692
- }
16693
- idx++;
16694
- }
16695
- return output.filter(Boolean).join("\n");
16696
- }
16697
-
16698
- class BenchmarkReporter extends DefaultReporter {
16699
- compare;
16700
- async onInit(ctx) {
16701
- super.onInit(ctx);
16702
- if (this.ctx.config.benchmark?.compare) {
16703
- const compareFile = pathe.resolve(this.ctx.config.root, this.ctx.config.benchmark?.compare);
16704
- try {
16705
- this.compare = flattenFormattedBenchmarkReport(JSON.parse(await fs__default.promises.readFile(compareFile, "utf-8")));
16706
- } catch (e) {
16707
- this.error(`Failed to read '${compareFile}'`, e);
16708
- }
16709
- }
16710
- }
16711
- onTaskUpdate(packs) {
16712
- for (const pack of packs) {
16713
- const task = this.ctx.state.idMap.get(pack[0]);
16714
- 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) => {
16715
- bench.result.benchmark.rank = Number(idx) + 1;
16716
- });
16717
- }
16718
- }
16719
- onTestSuiteResult(testSuite) {
16720
- super.onTestSuiteResult(testSuite);
16721
- this.printSuiteTable(testSuite);
16722
- }
16723
- printTestModule(testModule) {
16724
- this.printSuiteTable(testModule);
16725
- }
16726
- printSuiteTable(testTask) {
16727
- const state = testTask.state();
16728
- if (state === "pending" || state === "queued") return;
16729
- const benches = testTask.task.tasks.filter((t) => t.meta.benchmark);
16730
- const duration = testTask.task.result?.duration || 0;
16731
- if (benches.length > 0 && benches.every((t) => t.result?.state !== "run" && t.result?.state !== "queued")) {
16732
- let title = `\n ${getStateSymbol(testTask.task)} ${formatProjectName(testTask.project)}${getFullName(testTask.task, separator)}`;
16733
- if (duration != null && duration > this.ctx.config.slowTestThreshold) title += c.yellow(` ${Math.round(duration)}${c.dim("ms")}`);
16734
- this.log(title);
16735
- this.log(renderTable({
16736
- tasks: benches,
16737
- level: 1,
16738
- columns: this.ctx.logger.getColumns(),
16739
- compare: this.compare,
16740
- showHeap: this.ctx.config.logHeapUsage,
16741
- slowTestThreshold: this.ctx.config.slowTestThreshold
16742
- }));
16743
- }
16744
- }
16745
- async onTestRunEnd(testModules, unhandledErrors, reason) {
16746
- super.onTestRunEnd(testModules, unhandledErrors, reason);
16747
- // write output for future comparison
16748
- let outputFile = this.ctx.config.benchmark?.outputJson;
16749
- if (outputFile) {
16750
- outputFile = pathe.resolve(this.ctx.config.root, outputFile);
16751
- const outputDirectory = pathe.dirname(outputFile);
16752
- if (!fs__default.existsSync(outputDirectory)) await fs__default.promises.mkdir(outputDirectory, { recursive: true });
16753
- const output = createBenchmarkJsonReport(testModules.map((t) => t.task.file));
16754
- await fs__default.promises.writeFile(outputFile, JSON.stringify(output, null, 2));
16755
- this.log(`Benchmark report written to ${outputFile}`);
16756
- }
16757
- }
16758
- }
16759
-
16760
- class VerboseBenchmarkReporter extends BenchmarkReporter {
16761
- verbose = true;
16762
- }
16763
-
16764
- const BenchmarkReportsMap = {
16765
- default: BenchmarkReporter,
16766
- verbose: VerboseBenchmarkReporter
16767
- };
16768
-
16769
16730
  const ReportersMap = {
16770
16731
  "default": DefaultReporter,
16771
16732
  "agent": MinimalReporter,
@@ -16809,16 +16770,6 @@ function createReporters(reporterReferences, ctx) {
16809
16770
  });
16810
16771
  return Promise.all(promisedReporters);
16811
16772
  }
16812
- function createBenchmarkReporters(reporterReferences, runner) {
16813
- const promisedReporters = reporterReferences.map(async (referenceOrInstance) => {
16814
- if (typeof referenceOrInstance === "string") if (referenceOrInstance in BenchmarkReportsMap) {
16815
- const BuiltinReporter = BenchmarkReportsMap[referenceOrInstance];
16816
- return new BuiltinReporter();
16817
- } else return new (await (loadCustomReporterModule(referenceOrInstance, runner)))();
16818
- return referenceOrInstance;
16819
- });
16820
- return Promise.all(promisedReporters);
16821
- }
16822
16773
 
16823
16774
  function parseFilter(filter) {
16824
16775
  const colonIndex = filter.lastIndexOf(":");
@@ -16994,6 +16945,12 @@ class ReportedTaskImplementation {
16994
16945
  return this.task.meta;
16995
16946
  }
16996
16947
  /**
16948
+ * Console logs recorded during the test execution.
16949
+ */
16950
+ logs() {
16951
+ return [...this.task.logs || []];
16952
+ }
16953
+ /**
16997
16954
  * Creates a new reported task instance and stores it in the project's state for future use.
16998
16955
  * @internal
16999
16956
  */
@@ -17094,6 +17051,14 @@ class TestCase extends ReportedTaskImplementation {
17094
17051
  return [...this.task.artifacts];
17095
17052
  }
17096
17053
  /**
17054
+ * @experimental
17055
+ *
17056
+ * A list of benchmarks performed during the test.
17057
+ */
17058
+ benchmarks() {
17059
+ return [...this.task.benchmarks];
17060
+ }
17061
+ /**
17097
17062
  * Useful information about the test like duration, memory usage, etc.
17098
17063
  * Diagnostic is only available after the test has finished.
17099
17064
  */
@@ -17998,21 +17963,23 @@ class TestRun {
17998
17963
  this.vitest.state.updateUserLog(log);
17999
17964
  await this.vitest.report("onUserConsoleLog", log);
18000
17965
  }
17966
+ async recordBenchmark(testId, benchmark) {
17967
+ const testCase = this.getTestCaseById(testId, "Benchmark");
17968
+ testCase.task.benchmarks.push(benchmark);
17969
+ await this.vitest.report("onTestCaseBenchmark", testCase, benchmark);
17970
+ }
18001
17971
  async recordArtifact(testId, artifact) {
18002
- const task = this.vitest.state.idMap.get(testId);
18003
- const entity = task && this.vitest.state.getReportedEntity(task);
18004
- assert$1(task && entity, `Entity must be found for task ${task?.name || testId}`);
18005
- assert$1(entity.type === "test", `Artifacts can only be recorded on a test, instead got ${entity.type}`);
17972
+ const testCase = this.getTestCaseById(testId, "Artifact");
18006
17973
  // annotations won't resolve as artifacts for backwards compatibility until next major
18007
17974
  if (artifact.type === "internal:annotation") {
18008
- await this.resolveTestAttachment(entity, artifact.annotation.attachment, artifact.annotation.message);
18009
- entity.task.annotations.push(artifact.annotation);
18010
- await this.vitest.report("onTestCaseAnnotate", entity, artifact.annotation);
17975
+ await this.resolveTestAttachment(testCase, artifact.annotation.attachment, artifact.annotation.message);
17976
+ testCase.task.annotations.push(artifact.annotation);
17977
+ await this.vitest.report("onTestCaseAnnotate", testCase, artifact.annotation);
18011
17978
  return artifact;
18012
17979
  }
18013
- if (Array.isArray(artifact.attachments)) await Promise.all(artifact.attachments.map((attachment) => this.resolveTestAttachment(entity, attachment)));
18014
- entity.task.artifacts.push(artifact);
18015
- await this.vitest.report("onTestCaseArtifactRecord", entity, artifact);
17980
+ if (Array.isArray(artifact.attachments)) await Promise.all(artifact.attachments.map((attachment) => this.resolveTestAttachment(testCase, attachment)));
17981
+ testCase.task.artifacts.push(artifact);
17982
+ await this.vitest.report("onTestCaseArtifactRecord", testCase, artifact);
18016
17983
  return artifact;
18017
17984
  }
18018
17985
  async updated(update, events) {
@@ -18026,6 +17993,13 @@ class TestRun {
18026
17993
  // TODO: error handling - what happens if custom reporter throws an error?
18027
17994
  await this.vitest.report("onTaskUpdate", update, events);
18028
17995
  }
17996
+ getTestCaseById(testId, recordType) {
17997
+ const task = this.vitest.state.idMap.get(testId);
17998
+ const entity = task && this.vitest.state.getReportedEntity(task);
17999
+ assert$1(task && entity, `Entity must be found for task ${task?.name || testId}`);
18000
+ assert$1(entity.type === "test", `${recordType} can only be recorded on a test, instead got ${entity.type}`);
18001
+ return entity;
18002
+ }
18029
18003
  async end(specifications, errors, coverage) {
18030
18004
  if (coverage) await this.vitest.report("onCoverage", coverage);
18031
18005
  // specification won't have the File task if they were filtered by the --shard command
@@ -18390,7 +18364,7 @@ class Vitest {
18390
18364
  * The logger instance used to log messages. It's recommended to use this logger instead of `console`.
18391
18365
  * It's possible to override stdout and stderr streams when initiating Vitest.
18392
18366
  * @example
18393
- * new Vitest('test', {
18367
+ * new Vitest({
18394
18368
  * stdout: new Writable(),
18395
18369
  * })
18396
18370
  */
@@ -18452,8 +18426,20 @@ class Vitest {
18452
18426
  _cache;
18453
18427
  _snapshot;
18454
18428
  _coverageProvider;
18455
- constructor(mode, cliOptions, options = {}) {
18456
- this.mode = mode;
18429
+ /**
18430
+ * @deprecated Do not rely on this property, it's always `test`. Scheduled to be removed in the next major.
18431
+ */
18432
+ mode = "test";
18433
+ constructor(modeOrCliOptions, cliOptionsOrOptions, maybeOptions) {
18434
+ let cliOptions;
18435
+ let options;
18436
+ if (typeof modeOrCliOptions === "string") {
18437
+ cliOptions = cliOptionsOrOptions;
18438
+ options = maybeOptions ?? {};
18439
+ } else {
18440
+ cliOptions = modeOrCliOptions;
18441
+ options = cliOptionsOrOptions ?? {};
18442
+ }
18457
18443
  this._cliOptions = cliOptions;
18458
18444
  this.logger = new Logger(this, options.stdout, options.stderr);
18459
18445
  this.packageInstaller = options.packageInstaller || new VitestPackageInstaller();
@@ -18571,9 +18557,9 @@ class Vitest {
18571
18557
  try {
18572
18558
  await this.cache.results.readFromCache();
18573
18559
  } catch {}
18574
- const projects = await this.resolveProjects(this._cliOptions);
18575
- this.projects = projects;
18576
- await Promise.all(projects.flatMap((project) => {
18560
+ this.projects = await this.resolveProjects(this._cliOptions);
18561
+ if (this._cliOptions.benchmarkOnly) this.projects = this.projects.filter((c) => c.config.benchmark.enabled);
18562
+ await Promise.all(this.projects.flatMap((project) => {
18577
18563
  return project.vite.config.getSortedPluginHooks("configureVitest").map((hook) => hook({
18578
18564
  project,
18579
18565
  vitest: this,
@@ -18598,7 +18584,7 @@ class Vitest {
18598
18584
  // populate will merge all configs into every project,
18599
18585
  // we don't want that when just listing tags
18600
18586
  if (!this.config.listTags) populateProjectsTags(this.coreWorkspaceProject, this.projects);
18601
- this.reporters = resolved.mode === "benchmark" ? await createBenchmarkReporters(toArray(resolved.benchmark?.reporters), this.runner) : await createReporters(resolved.reporters, this);
18587
+ this.reporters = await createReporters(resolved.reporters, this);
18602
18588
  await this._fsCache.ensureCacheIntegrity();
18603
18589
  await Promise.all([...this._onSetServer.map((fn) => fn()), this._traces.waitInit()]);
18604
18590
  }
@@ -18725,7 +18711,7 @@ class Vitest {
18725
18711
  // returns the project only if it matches the filter
18726
18712
  const project = getDefaultTestProject(this);
18727
18713
  if (!project) return [];
18728
- return resolveBrowserProjects(this, new Set([project.name]), [project]);
18714
+ return resolveDefaultProjects(this, new Set([project.name]), [project]);
18729
18715
  }
18730
18716
  /**
18731
18717
  * Glob test files in every project and create a TestSpecification for each file and pool.
@@ -18767,7 +18753,7 @@ class Vitest {
18767
18753
  await this.report("onInit", this);
18768
18754
  const specifications = [];
18769
18755
  for (const file of files) {
18770
- const specification = this.getProjectByName(file.projectName || "").createSpecification(file.filepath, void 0, file.pool, file.meta);
18756
+ const specification = this.getProjectByName(file.projectName || "").createSpecification(file.filepath, void 0, file.pool, file.id);
18771
18757
  specifications.push(specification);
18772
18758
  }
18773
18759
  await this._testRun.start(specifications);
@@ -18891,7 +18877,7 @@ class Vitest {
18891
18877
  // Report coverage for uncovered files
18892
18878
  await this.reportCoverage(coverage, true);
18893
18879
  });
18894
- if (!this.config.watch || !(this.config.changed || this.config.related?.length)) throw new FilesNotFoundError(this.mode);
18880
+ if (!this.config.watch || !(this.config.changed || this.config.related?.length)) throw new FilesNotFoundError();
18895
18881
  }
18896
18882
  let testModules = {
18897
18883
  testModules: [],
@@ -19075,7 +19061,6 @@ class Vitest {
19075
19061
  }));
19076
19062
  }
19077
19063
  async experimental_parseSpecifications(specifications, options) {
19078
- if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecifications\` does not support "${this.mode}" mode.`);
19079
19064
  const limit = limitConcurrency(options?.concurrency ?? (typeof nodeos__default.availableParallelism === "function" ? nodeos__default.availableParallelism() : nodeos__default.cpus().length));
19080
19065
  // Phase 1: parse all files in parallel (without mode interpretation)
19081
19066
  const results = await Promise.all(specifications.map((specification) => limit(async () => {
@@ -19097,7 +19082,6 @@ class Vitest {
19097
19082
  return results.map(({ file }) => this.state.getReportedEntity(file));
19098
19083
  }
19099
19084
  async experimental_parseSpecification(specification) {
19100
- if (this.mode !== "test") throw new Error(`The \`experimental_parseSpecification\` does not support "${this.mode}" mode.`);
19101
19085
  const file = await astCollectTests(specification.project, specification.moduleId).catch((error) => {
19102
19086
  return createFailedFileTask(specification.project, specification.moduleId, error);
19103
19087
  });
@@ -19493,7 +19477,7 @@ function assert(condition, property, name = property) {
19493
19477
  if (!condition) throw new Error(`The ${name} was not set. It means that \`vitest.${property}\` was called before the Vite server was established. Await the Vitest promise before accessing \`vitest.${property}\`.`);
19494
19478
  }
19495
19479
 
19496
- async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(options))) {
19480
+ async function VitestPlugin(options = {}, vitest = new Vitest(deepClone(options))) {
19497
19481
  const userConfig = deepMerge({}, options);
19498
19482
  async function UIPlugin() {
19499
19483
  await vitest.packageInstaller.ensureInstalled("@vitest/ui", resolve$1(options.root || process.cwd()), vitest.version);
@@ -19566,6 +19550,10 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
19566
19550
  legalComments: "inline"
19567
19551
  }
19568
19552
  };
19553
+ if (vitest._cliOptions.benchmarkOnly) {
19554
+ config.test.benchmark ??= {};
19555
+ config.test.benchmark.enabled = true;
19556
+ }
19569
19557
  // inherit so it's available in VitestOptimizer
19570
19558
  // I cannot wait to rewrite all of this in Vitest 4
19571
19559
  if (options.cache != null) config.test.cache = options.cache;
@@ -19635,8 +19623,20 @@ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(
19635
19623
  ].filter(notNullish);
19636
19624
  }
19637
19625
 
19638
- async function createVitest(mode, options, viteOverrides = {}, vitestOptions = {}) {
19639
- const ctx = new Vitest(mode, deepClone(options), vitestOptions);
19626
+ async function createVitest(modeOrOptions, optionsOrViteOverrides = {}, viteOverridesOrVitestOptions = {}, maybeVitestOptions = {}) {
19627
+ let options;
19628
+ let viteOverrides;
19629
+ let vitestOptions;
19630
+ if (typeof modeOrOptions === "string") {
19631
+ options = optionsOrViteOverrides;
19632
+ viteOverrides = viteOverridesOrVitestOptions;
19633
+ vitestOptions = maybeVitestOptions;
19634
+ } else {
19635
+ options = modeOrOptions;
19636
+ viteOverrides = optionsOrViteOverrides;
19637
+ vitestOptions = viteOverridesOrVitestOptions;
19638
+ }
19639
+ const ctx = new Vitest(deepClone(options), vitestOptions);
19640
19640
  const root = slash(resolve$2(options.root || process.cwd()));
19641
19641
  const configPath = options.config === false ? false : options.config ? resolveModule(options.config, { paths: [root] }) ?? resolve$2(root, options.config) : any(configFiles, { cwd: root });
19642
19642
  options.config = configPath;
@@ -19644,7 +19644,7 @@ async function createVitest(mode, options, viteOverrides = {}, vitestOptions = {
19644
19644
  const config = {
19645
19645
  configFile: configPath,
19646
19646
  configLoader: options.configLoader,
19647
- mode: options.mode || mode,
19647
+ mode: options.mode || "test",
19648
19648
  plugins: await VitestPlugin(restOptions, ctx)
19649
19649
  };
19650
19650
  try {
@@ -19951,15 +19951,25 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
19951
19951
  };
19952
19952
  }
19953
19953
 
19954
- /**
19955
- * Start Vitest programmatically
19956
- *
19957
- * Returns a Vitest instance if initialized successfully.
19958
- */
19959
- async function startVitest(mode, cliFilters = [], options = {}, viteOverrides, vitestOptions) {
19954
+ async function startVitest(modeOrCliFilters, cliFiltersOrOptions, optionsOrViteOverrides, viteOverridesOrVitestOptions, maybeVitestOptions) {
19955
+ let cliFilters;
19956
+ let options;
19957
+ let viteOverrides;
19958
+ let vitestOptions;
19959
+ if (typeof modeOrCliFilters === "string") {
19960
+ cliFilters = cliFiltersOrOptions ?? [];
19961
+ options = optionsOrViteOverrides ?? {};
19962
+ viteOverrides = viteOverridesOrVitestOptions;
19963
+ vitestOptions = maybeVitestOptions;
19964
+ } else {
19965
+ cliFilters = modeOrCliFilters ?? [];
19966
+ options = cliFiltersOrOptions ?? {};
19967
+ viteOverrides = optionsOrViteOverrides;
19968
+ vitestOptions = viteOverridesOrVitestOptions;
19969
+ }
19960
19970
  const root = resolve$1(options.root || process.cwd());
19961
- const ctx = await prepareVitest(mode, options, viteOverrides, vitestOptions, cliFilters);
19962
- if (mode === "test" && ctx._coverageOptions.enabled) {
19971
+ const ctx = await prepareVitest(options, viteOverrides, vitestOptions, cliFilters);
19972
+ if (ctx._coverageOptions.enabled) {
19963
19973
  const requiredPackages = CoverageProviderMap[ctx._coverageOptions.provider || "v8"];
19964
19974
  if (requiredPackages) {
19965
19975
  if (!await ctx.packageInstaller.ensureInstalled(requiredPackages, root, ctx.version)) {
@@ -20007,7 +20017,22 @@ async function startVitest(mode, cliFilters = [], options = {}, viteOverrides, v
20007
20017
  }
20008
20018
  }
20009
20019
  }
20010
- async function prepareVitest(mode, options = {}, viteOverrides, vitestOptions, cliFilters) {
20020
+ async function prepareVitest(modeOrOptions, optionsOrViteOverrides, viteOverridesOrVitestOptions, vitestOptionsOrCliFilters, maybeCliFilters) {
20021
+ let options;
20022
+ let viteOverrides;
20023
+ let vitestOptions;
20024
+ let cliFilters;
20025
+ if (typeof modeOrOptions === "string") {
20026
+ options = optionsOrViteOverrides ?? {};
20027
+ viteOverrides = viteOverridesOrVitestOptions;
20028
+ vitestOptions = vitestOptionsOrCliFilters;
20029
+ cliFilters = maybeCliFilters;
20030
+ } else {
20031
+ options = modeOrOptions ?? {};
20032
+ viteOverrides = optionsOrViteOverrides;
20033
+ vitestOptions = viteOverridesOrVitestOptions;
20034
+ cliFilters = vitestOptionsOrCliFilters;
20035
+ }
20011
20036
  process.env.TEST = "true";
20012
20037
  process.env.VITEST = "true";
20013
20038
  process.env.NODE_ENV ??= "test";
@@ -20015,7 +20040,7 @@ async function prepareVitest(mode, options = {}, viteOverrides, vitestOptions, c
20015
20040
  if (options.standalone && (cliFilters?.length || 0) > 0) options.standalone = false;
20016
20041
  // this shouldn't affect _application root_ that can be changed inside config
20017
20042
  const root = resolve$1(options.root || process.cwd());
20018
- const ctx = await createVitest(mode, options, viteOverrides, vitestOptions);
20043
+ const ctx = await createVitest(options, viteOverrides, vitestOptions);
20019
20044
  const environmentPackage = getEnvPackageName(ctx.config.environment);
20020
20045
  if (environmentPackage && !await ctx.packageInstaller.ensureInstalled(environmentPackage, root)) {
20021
20046
  process.exitCode = 1;
@@ -20124,4 +20149,4 @@ var cliApi = /*#__PURE__*/Object.freeze({
20124
20149
  startVitest: startVitest
20125
20150
  });
20126
20151
 
20127
- export { registerConsoleShortcuts as A, BaseCoverageProvider as B, resolveApiServerConfig as C, DefaultReporter as D, resolveFsAllow as E, ForksPoolWorker as F, GitNotFoundError as G, HangingProcessReporter as H, startVitest as I, JUnitReporter as J, cliApi as K, MinimalReporter as M, ReportersMap as R, TapFlatReporter as T, Vitest as V, VitestPlugin as a, BaseSequencer as b, BenchmarkReporter as c, BenchmarkReportsMap as d, DotReporter as e, GithubActionsReporter as f, JsonReporter as g, TapReporter as h, FilesNotFoundError as i, ThreadsPoolWorker as j, TypecheckPoolWorker as k, VerboseBenchmarkReporter as l, VerboseReporter as m, VitestPackageInstaller as n, VmForksPoolWorker as o, VmThreadsPoolWorker as p, createDebugger as q, resolveConfig$1 as r, createMethodsRPC as s, createViteLogger as t, createVitest as u, escapeTestName as v, experimental_getRunnerTask as w, getFilePoolName as x, isFileServingAllowed as y, isValidApiRequest as z };
20152
+ export { startVitest as A, BaseCoverageProvider as B, cliApi as C, DefaultReporter as D, ForksPoolWorker as F, GitNotFoundError as G, HangingProcessReporter as H, JUnitReporter as J, MinimalReporter as M, ReportersMap as R, TapFlatReporter as T, Vitest as V, VitestPlugin as a, BaseSequencer as b, DotReporter as c, GithubActionsReporter as d, JsonReporter as e, TapReporter as f, FilesNotFoundError as g, ThreadsPoolWorker as h, TypecheckPoolWorker as i, VerboseReporter as j, VitestPackageInstaller as k, VmForksPoolWorker as l, VmThreadsPoolWorker as m, createDebugger as n, createMethodsRPC as o, createViteLogger as p, createVitest as q, resolveConfig$1 as r, escapeTestName as s, experimental_getRunnerTask as t, getFilePoolName as u, isFileServingAllowed as v, isValidApiRequest as w, registerConsoleShortcuts as x, resolveApiServerConfig as y, resolveFsAllow as z };