vitest 5.0.4 → 5.1.0-beta.1

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 (53) hide show
  1. package/LICENSE.md +1 -1
  2. package/dist/browser.d.ts +4 -4
  3. package/dist/browser.js +3 -3
  4. package/dist/chunks/{base.CYbb9pQW.js → base.PJyN9N0u.js} +13 -11
  5. package/dist/chunks/{browser.d.FGQ9_rK6.d.ts → browser.d.D7a15z82.d.ts} +4 -3
  6. package/dist/chunks/{cac.DTXWs6Ni.js → cac.Ccf5Je_F.js} +21 -9
  7. package/dist/chunks/{cli-api.Bx28WXzL.js → cli-api.DE22LIzq.js} +4584 -4369
  8. package/dist/chunks/{config.d.QBNYCgFU.d.ts → config.d.BgktgHfG.d.ts} +134 -93
  9. package/dist/chunks/{console.DtI4yxfN.js → console.DrMPSuFo.js} +5 -2
  10. package/dist/chunks/{constants.-juJ8b_4.js → constants.DLYM6cZY.js} +1 -1
  11. package/dist/chunks/{creator.CLL8QFMS.js → creator.BQ3onxbh.js} +5 -5
  12. package/dist/chunks/{doctor.Bqc8NDDb.js → doctor.CPlMs7-y.js} +1 -1
  13. package/dist/chunks/{environment.d.BY56MoGs.d.ts → environment.d.CbTLsZSj.d.ts} +1 -1
  14. package/dist/chunks/{globals.CTWMSIPL.js → globals.TipXUFcZ.js} +5 -5
  15. package/dist/chunks/{index.GYMZ12wH.js → index.B03wg4V0.js} +5 -3
  16. package/dist/chunks/{index.CPNMO3h-.js → index.BFXLpwll.js} +119 -43
  17. package/dist/chunks/{index.uwTEEBsB.js → index.BXttT2gP.js} +37 -18
  18. package/dist/chunks/{index.B8VfM7Mf.js → index.D14IAvnE.js} +149 -96
  19. package/dist/chunks/{init-forks.CbYYa5xq.js → init-forks.BL1E3__4.js} +1 -1
  20. package/dist/chunks/{init-threads.DtjdvxPl.js → init-threads.CYXAGzKQ.js} +1 -1
  21. package/dist/chunks/{init.DrHtV5zd.js → init.DVorxUyL.js} +3 -3
  22. package/dist/chunks/{nativeModuleMocker.D8f4cW4A.js → nativeModuleMocker.3uBcOd84.js} +26 -4
  23. package/dist/chunks/{plugin.d.C8mmycim.d.ts → plugin.d.olEn8IL9.d.ts} +108 -20
  24. package/dist/chunks/{rpc.DpkUyY1N.js → rpc.BV0XpJ5q.js} +1 -1
  25. package/dist/chunks/{rpc.d.Cft1biqy.d.ts → rpc.d.DGFP7vWr.d.ts} +1 -1
  26. package/dist/chunks/{run.Cm2sd7-x.js → run.BfrMbGVD.js} +59 -32
  27. package/dist/chunks/{setup-common.BABL3z3m.js → setup-common.llxJkJaD.js} +2 -2
  28. package/dist/chunks/{source-map.DdKI5IHB.js → source-map.BjWcsIL9.js} +32 -24
  29. package/dist/chunks/{source-map.d.kzFtk_dj.d.ts → source-map.d.C5A_Tr6V.d.ts} +2 -1
  30. package/dist/chunks/{task-utils.d.PFh2BV6Q.d.ts → task-utils.d.BUh7ylUT.d.ts} +2 -1
  31. package/dist/chunks/{vm.Uo_C9Uld.js → vm.Cwj01UFE.js} +16 -5
  32. package/dist/chunks/{worker.d.BH-r1bPu.d.ts → worker.d.gsA26mmR.d.ts} +3 -3
  33. package/dist/cli.js +2 -2
  34. package/dist/config.cjs +1 -1
  35. package/dist/config.d.ts +11 -11
  36. package/dist/config.js +1 -1
  37. package/dist/index.d.ts +14 -13
  38. package/dist/index.js +4 -4
  39. package/dist/module-evaluator.d.ts +3 -2
  40. package/dist/module-evaluator.js +16 -12
  41. package/dist/node.d.ts +29 -11
  42. package/dist/node.js +7 -7
  43. package/dist/runtime.d.ts +4 -4
  44. package/dist/runtime.js +3 -2
  45. package/dist/task-utils.js +3 -1
  46. package/dist/worker.d.ts +4 -4
  47. package/dist/worker.js +9 -9
  48. package/dist/workers/forks.js +10 -10
  49. package/dist/workers/runVmTests.js +10 -8
  50. package/dist/workers/threads.js +10 -10
  51. package/dist/workers/vmForks.js +9 -8
  52. package/dist/workers/vmThreads.js +9 -8
  53. package/package.json +14 -14
@@ -1,4 +1,4 @@
1
- import { promises, existsSync, mkdirSync, readFileSync, writeFileSync, linkSync, renameSync, rmSync, chmodSync, statfsSync, realpathSync, statSync, readdirSync } from 'node:fs';
1
+ import { promises, existsSync, mkdirSync, writeFileSync, linkSync, renameSync, rmSync, chmodSync, readFileSync, statfsSync, realpathSync, statSync, readdirSync } from 'node:fs';
2
2
  import url, { pathToFileURL, fileURLToPath } from 'node:url';
3
3
  import { c as cleanUrl, r as resolve, f as extname, d as dirname, j as join, h as KNOWN_ASSET_RE, H as withTrailingSlash, I as wrapId, i as isAbsolute, J as isExternalUrl, L as unwrapId, a as relative, z as unique, s as slash, t as toArray, b as basename, m as isPrimitive, n as normalize, G as deepMerge, e as createDefer, p as deepClone } from './pathe.M-eThtNZ.Ba-7h1rE.js';
4
4
  import { N as NativeModuleRunner, i as isPackageExists, r as resolveModule } from './nativeModuleRunner.xi64Afb2.js';
@@ -6,7 +6,7 @@ import { glob, isDynamicPattern } from 'tinyglobby';
6
6
  import { $ } from './tinyrainbow.Do8I1XRG.js';
7
7
  import * as vite from 'vite';
8
8
  import { createServer, normalizePath, isFileLoadingAllowed, fetchModule, parseAst, searchForWorkspaceRoot, version, isRunnableDevEnvironment, mergeConfig, resolveConfig as resolveConfig$1 } from 'vite';
9
- import { a as defaultPort, c as configFiles, d as defaultBrowserPort, b as defaultInspectPort } from './constants.-juJ8b_4.js';
9
+ import { a as defaultPort, c as configFiles, d as defaultBrowserPort, b as defaultInspectPort } from './constants.DLYM6cZY.js';
10
10
  import { c as configDefaults, e as benchmarkConfigDefaults } from './defaults.B1FCaFfh.js';
11
11
  import { b as isWindows, i as isForceColor, a as isTTY } from './env.BKKtU2WC.js';
12
12
  import * as os from 'node:os';
@@ -22,9 +22,9 @@ import { Writable } from 'node:stream';
22
22
  import { stripVTControlCharacters } from 'node:util';
23
23
  import { i as inspect } from './display.BlTmubjj.js';
24
24
  import { performance as performance$1 } from 'node:perf_hooks';
25
- import { T as TraceMap, o as originalPositionFor, i as generatedPositionFor, j as eachMapping, k as stackIgnorePatterns, e as parseErrorStacktrace } from './source-map.DdKI5IHB.js';
25
+ import { T as TraceMap, o as originalPositionFor, i as generatedPositionFor, j as eachMapping, k as stackIgnorePatterns, e as parseErrorStacktrace } from './source-map.BjWcsIL9.js';
26
26
  import { x } from 'tinyexec';
27
- import { B as validateTags, z as limitConcurrency, C as shuffle } from './run.Cm2sd7-x.js';
27
+ import { B as validateTags, z as limitConcurrency, C as shuffle } from './run.BfrMbGVD.js';
28
28
  import { createDebug } from 'obug';
29
29
  import { createFileTask as createFileTask$1, createTaskName, calculateSuiteHash, convertTasksToEvents, hasFailed, generateFileHash } from '../task-utils.js';
30
30
  import { readFile, mkdir, writeFile, rm, stat } from 'node:fs/promises';
@@ -45,7 +45,7 @@ import EventEmitter$1, { EventEmitter } from 'node:events';
45
45
  import { c as createBirpc } from './index.DmDMHCg8.js';
46
46
  import { fork } from 'node:child_process';
47
47
  import { Worker } from 'node:worker_threads';
48
- import { v as version$1 } from './cac.DTXWs6Ni.js';
48
+ import { v as version$1 } from './cac.Ccf5Je_F.js';
49
49
  import { g as getDefaultExportFromCjs } from './index.D0heQG4v.js';
50
50
  import require$$0 from 'readline';
51
51
  import require$$0$1 from 'events';
@@ -915,14 +915,16 @@ class ModuleTransformService {
915
915
  */
916
916
  async transform(id, environment) {
917
917
  await this.traces.waitInit();
918
- const result = await this.traces.$("vitest.module.transform", (span) => this.fetchInSpan(span, id, void 0, environment));
918
+ // the module graph keeps the url of the first request, so it has to be the url that workers import
919
+ const url = normalizeResolvedIdToUrl(environment, id);
920
+ const result = await this.traces.$("vitest.module.transform", (span) => this.fetchInSpan(span, url, void 0, environment));
919
921
  // externalized modules are loaded by Node without a transform, like in a normal run
920
922
  if ("externalize" in result) return null;
921
923
  if ("id" in result) {
922
924
  const transformResult = environment.moduleGraph.getModuleById(result.id)?.transformResult;
923
925
  if (transformResult) return transformResult;
924
926
  }
925
- return environment.transformRequest(id);
927
+ return environment.transformRequest(url);
926
928
  }
927
929
  async fetchInSpan(trace, url, importer, environment, makeTmpCopies, options) {
928
930
  if (url.startsWith("data:")) {
@@ -1101,7 +1103,7 @@ class ModuleTransformService {
1101
1103
  file: cachedModule.file,
1102
1104
  id: cachedModule.id,
1103
1105
  tmp: cachePath,
1104
- url: cachedModule.url,
1106
+ url: moduleGraphModule.url,
1105
1107
  invalidate: false,
1106
1108
  moduleType
1107
1109
  };
@@ -1422,6 +1424,7 @@ function createFailedFileTask(project, filepath, error, options) {
1422
1424
  return file;
1423
1425
  }
1424
1426
  function serializeError(error) {
1427
+ if (error?.__vitest_rollup_error__) return [error];
1425
1428
  return [toRollupError(error) ?? {
1426
1429
  name: error.name,
1427
1430
  stack: error.stack,
@@ -1574,7 +1577,8 @@ async function transformSSR(project, filepath) {
1574
1577
  const { env: pragmaEnv, tags: fileTags } = detectCodeBlock(originalCode);
1575
1578
  // Use environment from pragma if defined, otherwise fall back to config
1576
1579
  const environment = pragmaEnv || project.config.environment;
1577
- const transformResult = await (environment === "jsdom" || environment === "happy-dom" ? project.vite.environments.client : project.vite.environments.ssr).transformRequest(filepath);
1580
+ const env = environment === "jsdom" || environment === "happy-dom" ? project.vite.environments.client : project.vite.environments.ssr;
1581
+ const transformResult = await project._transformService.transform(filepath, env);
1578
1582
  return transformResult ? {
1579
1583
  ...transformResult,
1580
1584
  fileTags
@@ -1865,7 +1869,12 @@ async function createClusterServer(vitest, viteConfig, config, children) {
1865
1869
  // listen on startup only for `api`/`ui`, otherwise on the first browser launch
1866
1870
  // (collect-only processes never listen, so Vite never starts a dependency scan)
1867
1871
  if (config._apiRequested) await server.listen(config.api.port);
1868
- else deferredListen.set(server, { port: config.api.port });
1872
+ else {
1873
+ deferredListen.set(server, { port: config.api.port });
1874
+ // Vite runs plugin buildStart hooks on listen; global setup and --changed
1875
+ // transform modules before the first browser launch
1876
+ await server.environments.client.pluginContainer.buildStart();
1877
+ }
1869
1878
  contribution.setupRpc(parent);
1870
1879
  return {
1871
1880
  server,
@@ -1883,6 +1892,70 @@ function generateScopedClassName(strategy, name, filename) {
1883
1892
  return `_${name}_${generateCssFilenameHash(filename)}`;
1884
1893
  }
1885
1894
 
1895
+ const API_TOKEN_FILE = ".vitest-secret-token";
1896
+ // Follows env-paths' user data directory conventions:
1897
+ // https://github.com/sindresorhus/env-paths/blob/v4.0.0/index.js
1898
+ function getUserDataDir() {
1899
+ if (process.platform === "win32") return process.env.LOCALAPPDATA || join(homedir(), "AppData/Local");
1900
+ if (process.platform === "darwin") return join(homedir(), "Library/Application Support");
1901
+ return process.env.XDG_DATA_HOME || join(homedir(), ".local/share");
1902
+ }
1903
+ function readPublishedToken(tokenPath) {
1904
+ try {
1905
+ return readFileSync(tokenPath, "utf-8").trim() || void 0;
1906
+ } catch {
1907
+ return;
1908
+ }
1909
+ }
1910
+ function resolveTokenFromPath(tokenPath) {
1911
+ const published = readPublishedToken(tokenPath);
1912
+ if (published) return {
1913
+ token: published,
1914
+ tokenCreated: false
1915
+ };
1916
+ const dir = dirname(tokenPath);
1917
+ mkdirSync(dir, {
1918
+ recursive: true,
1919
+ mode: 448
1920
+ });
1921
+ const token = crypto.randomUUID();
1922
+ const pendingPath = join(dir, `${API_TOKEN_FILE}.${process.pid}.${crypto.randomUUID()}`);
1923
+ writeFileSync(pendingPath, `${token}\n`, { mode: 384 });
1924
+ let tokenCreated = true;
1925
+ try {
1926
+ // linking fails when another process already published, which keeps a single winner
1927
+ linkSync(pendingPath, tokenPath);
1928
+ } catch (error) {
1929
+ if (error.code === "EEXIST") tokenCreated = false;
1930
+ else renameSync(pendingPath, tokenPath);
1931
+ } finally {
1932
+ rmSync(pendingPath, { force: true });
1933
+ }
1934
+ const winner = readPublishedToken(tokenPath);
1935
+ if (!tokenCreated && !winner) {
1936
+ writeFileSync(tokenPath, `${token}\n`, { mode: 384 });
1937
+ tokenCreated = true;
1938
+ }
1939
+ try {
1940
+ chmodSync(dir, 448);
1941
+ chmodSync(tokenPath, 384);
1942
+ } catch {}
1943
+ return {
1944
+ token: (tokenCreated ? token : winner) ?? token,
1945
+ tokenCreated
1946
+ };
1947
+ }
1948
+ function resolveApiToken(root) {
1949
+ const tokenPaths = [join(getUserDataDir(), "vitest", API_TOKEN_FILE), join(searchForWorkspaceRoot(root), "node_modules/.vitest", API_TOKEN_FILE)];
1950
+ for (const tokenPath of tokenPaths) try {
1951
+ return {
1952
+ ...resolveTokenFromPath(tokenPath),
1953
+ tokenPath
1954
+ };
1955
+ } catch {}
1956
+ throw new Error(`Failed to create Vitest API token at ${tokenPaths.join(" or ")}`);
1957
+ }
1958
+
1886
1959
  function resolveTestCacheDir(root, testConfig, viteCacheDir) {
1887
1960
  const name = testConfig.name;
1888
1961
  const label = typeof name === "string" ? name : name?.label || "";
@@ -1976,6 +2049,23 @@ function deleteDefineConfig(viteConfig) {
1976
2049
  scriptDefines
1977
2050
  };
1978
2051
  }
2052
+ // TODO: use Vite's own defaults once they are exported (request upstream)
2053
+ // copied from Vite 8.3 https://github.com/vitejs/vite/blob/v8.3.4/packages/vite/src/node/server/index.ts#L1254-L1261
2054
+ const VITE_DEFAULT_FS_DENY = [
2055
+ ".env",
2056
+ ".env.*",
2057
+ "*.{crt,pem,key,p12,pfx,cer,der}",
2058
+ ".npmrc",
2059
+ ".yarnrc.yml",
2060
+ "**/.git/**"
2061
+ ];
2062
+ // Vite drops its default `server.fs.deny` once any value is set,
2063
+ // so the defaults are kept explicitly when the user didn't set one.
2064
+ function appendApiTokenFsDeny(viteConfig) {
2065
+ viteConfig.server ??= {};
2066
+ viteConfig.server.fs ??= {};
2067
+ viteConfig.server.fs.deny = [...viteConfig.server.fs.deny ?? VITE_DEFAULT_FS_DENY, API_TOKEN_FILE];
2068
+ }
1979
2069
  function resolveFsAllow(projectRoot, rootConfigFile) {
1980
2070
  if (!rootConfigFile) return [searchForWorkspaceRoot(projectRoot), rootDir];
1981
2071
  return [
@@ -3558,6 +3648,7 @@ function VitestCorePlugin(harness) {
3558
3648
  order: "post",
3559
3649
  handler(viteConfig) {
3560
3650
  const root = resolve(viteConfig.test?.root || viteConfig.root || process.cwd());
3651
+ appendApiTokenFsDeny(viteConfig);
3561
3652
  return {
3562
3653
  base: "/",
3563
3654
  root,
@@ -3783,70 +3874,6 @@ function processWildcard(dep, moduleDirectories) {
3783
3874
  return dep;
3784
3875
  }
3785
3876
 
3786
- const API_TOKEN_FILE = ".vitest-secret-token";
3787
- // Follows env-paths' user data directory conventions:
3788
- // https://github.com/sindresorhus/env-paths/blob/v4.0.0/index.js
3789
- function getUserDataDir() {
3790
- if (process.platform === "win32") return process.env.LOCALAPPDATA || join(homedir(), "AppData/Local");
3791
- if (process.platform === "darwin") return join(homedir(), "Library/Application Support");
3792
- return process.env.XDG_DATA_HOME || join(homedir(), ".local/share");
3793
- }
3794
- function readPublishedToken(tokenPath) {
3795
- try {
3796
- return readFileSync(tokenPath, "utf-8").trim() || void 0;
3797
- } catch {
3798
- return;
3799
- }
3800
- }
3801
- function resolveTokenFromPath(tokenPath) {
3802
- const published = readPublishedToken(tokenPath);
3803
- if (published) return {
3804
- token: published,
3805
- tokenCreated: false
3806
- };
3807
- const dir = dirname(tokenPath);
3808
- mkdirSync(dir, {
3809
- recursive: true,
3810
- mode: 448
3811
- });
3812
- const token = crypto.randomUUID();
3813
- const pendingPath = join(dir, `${API_TOKEN_FILE}.${process.pid}.${crypto.randomUUID()}`);
3814
- writeFileSync(pendingPath, `${token}\n`, { mode: 384 });
3815
- let tokenCreated = true;
3816
- try {
3817
- // linking fails when another process already published, which keeps a single winner
3818
- linkSync(pendingPath, tokenPath);
3819
- } catch (error) {
3820
- if (error.code === "EEXIST") tokenCreated = false;
3821
- else renameSync(pendingPath, tokenPath);
3822
- } finally {
3823
- rmSync(pendingPath, { force: true });
3824
- }
3825
- const winner = readPublishedToken(tokenPath);
3826
- if (!tokenCreated && !winner) {
3827
- writeFileSync(tokenPath, `${token}\n`, { mode: 384 });
3828
- tokenCreated = true;
3829
- }
3830
- try {
3831
- chmodSync(dir, 448);
3832
- chmodSync(tokenPath, 384);
3833
- } catch {}
3834
- return {
3835
- token: (tokenCreated ? token : winner) ?? token,
3836
- tokenCreated
3837
- };
3838
- }
3839
- function resolveApiToken(root) {
3840
- const tokenPaths = [join(getUserDataDir(), "vitest", API_TOKEN_FILE), join(searchForWorkspaceRoot(root), "node_modules/.vitest", API_TOKEN_FILE)];
3841
- for (const tokenPath of tokenPaths) try {
3842
- return {
3843
- ...resolveTokenFromPath(tokenPath),
3844
- tokenPath
3845
- };
3846
- } catch {}
3847
- throw new Error(`Failed to create Vitest API token at ${tokenPaths.join(" or ")}`);
3848
- }
3849
-
3850
3877
  function ProjectRootPlugin(projectRoot) {
3851
3878
  return {
3852
3879
  name: "vitest:project-root",
@@ -3872,16 +3899,14 @@ function WorkspaceVitestPlugin(harness, globalViteConfig) {
3872
3899
  config(viteConfig) {
3873
3900
  const testConfig = viteConfig.test || {};
3874
3901
  const root = testConfig.root || viteConfig.root;
3902
+ appendApiTokenFsDeny(viteConfig);
3875
3903
  return {
3876
3904
  base: "/",
3877
3905
  root,
3878
3906
  cacheDir: resolveTestCacheDir(resolve(root || process.cwd()), testConfig, viteConfig.cacheDir),
3879
3907
  server: {
3880
3908
  open: false,
3881
- fs: {
3882
- allow: globalViteConfig.server.fs.allow,
3883
- deny: [API_TOKEN_FILE]
3884
- }
3909
+ fs: { allow: globalViteConfig.server.fs.allow }
3885
3910
  }
3886
3911
  };
3887
3912
  },
@@ -4100,6 +4125,7 @@ function serializeConfig(project) {
4100
4125
  },
4101
4126
  providerOptions: provider?.name === "playwright" ? { actionTimeout: provider?.options?.actionTimeout } : {},
4102
4127
  trackUnhandledErrors: browser.trackUnhandledErrors ?? true,
4128
+ importMapMocks: browser.importMapMocks,
4103
4129
  trace: browser.trace.mode,
4104
4130
  traceView: browser.traceView
4105
4131
  };
@@ -4272,7 +4298,7 @@ class RangeLocationFilterProvidedError extends Error {
4272
4298
  }
4273
4299
 
4274
4300
  const debug$1 = createDebugger("vitest:browser:pool");
4275
- const PROVIDER_CLOSE_TIMEOUT = 1e4;
4301
+ const PROVIDER_CLOSE_TIMEOUT = 3e4;
4276
4302
  function createBrowserPool(vitest) {
4277
4303
  // every browser instance is a worker; at most `maxWorkers` of them are open
4278
4304
  const workers = /* @__PURE__ */ new Map();
@@ -4392,7 +4418,7 @@ function createBrowserPool(vitest) {
4392
4418
  project,
4393
4419
  files,
4394
4420
  method,
4395
- exclusive: !provider.mocker || !provider.supportsParallelism,
4421
+ exclusive: !provider.supportsParallelism || !provider.mocker && project.config.browser.importMapMocks !== true,
4396
4422
  result: createDefer()
4397
4423
  };
4398
4424
  }));
@@ -4420,7 +4446,9 @@ function createBrowserPool(vitest) {
4420
4446
  await Promise.all([...stopping, ...exitPromises]);
4421
4447
  // pages can be opened outside of the pool (`vitest.standalone`)
4422
4448
  await Promise.all(vitest.projects.map((project) => closeProvider(project)));
4423
- vitest._browserSessions.sessionIds.clear();
4449
+ vitest._browserSessions.sessionIds.forEach((sessionId) => {
4450
+ vitest._browserSessions.close(sessionId);
4451
+ });
4424
4452
  vitest.projects.forEach((project) => {
4425
4453
  project.browser?.state.orchestrators.forEach((orchestrator) => {
4426
4454
  orchestrator.$close();
@@ -4439,12 +4467,13 @@ async function groupSpecifications(specs) {
4439
4467
  const code = /* @__PURE__ */ new Map();
4440
4468
  const docblocks = await Promise.all(specs.map((spec) => getSpecificationDocblock(spec, code)));
4441
4469
  specs.forEach((spec, index) => {
4442
- const { project, moduleId, testLines, testIds, testNamePattern, testTagsFilter } = spec;
4470
+ const { project, moduleId, testLines, testIds, testNames, testNamePattern, testTagsFilter } = spec;
4443
4471
  const files = groupedFiles.get(project) || [];
4444
4472
  files.push({
4445
4473
  filepath: moduleId,
4446
4474
  testLocations: testLines,
4447
4475
  testIds,
4476
+ testNames,
4448
4477
  testNamePattern,
4449
4478
  testTagsFilter,
4450
4479
  fileTags: docblocks[index].tags
@@ -4638,8 +4667,7 @@ class BrowserWorker {
4638
4667
  this.busySessions.clear();
4639
4668
  this.readySessions.clear();
4640
4669
  await closeProvider(this.project, options);
4641
- // the pages are closed now, so a late reconnect is not reported as an unknown session
4642
- sessionIds.forEach((sessionId) => sessions.sessionIds.delete(sessionId));
4670
+ sessionIds.forEach((sessionId) => sessions.close(sessionId));
4643
4671
  orchestrators.forEach((orchestrator) => orchestrator?.$close());
4644
4672
  }
4645
4673
  async openPage(sessionId, options) {
@@ -6120,6 +6148,7 @@ function createPool(ctx) {
6120
6148
  testLocations: spec.testLines,
6121
6149
  testNamePattern: spec.testNamePattern,
6122
6150
  testIds: spec.testIds,
6151
+ testNames: spec.testNames,
6123
6152
  testTagsFilter: spec.testTagsFilter
6124
6153
  })),
6125
6154
  invalidates,
@@ -6356,6 +6385,10 @@ class TestSpecification {
6356
6385
  */
6357
6386
  testIds;
6358
6387
  /**
6388
+ * The full names of the tests inside of this specification to run, including the parent suites separated with `>`.
6389
+ */
6390
+ testNames;
6391
+ /**
6359
6392
  * The tags of tests to run.
6360
6393
  */
6361
6394
  testTagsFilter;
@@ -6379,6 +6412,7 @@ class TestSpecification {
6379
6412
  this.testLines = testLinesOrOptions.testLines;
6380
6413
  this.testNamePattern = testLinesOrOptions.testNamePattern;
6381
6414
  this.testIds = testLinesOrOptions.testIds;
6415
+ this.testNames = testLinesOrOptions.testNames;
6382
6416
  this.testTagsFilter = testLinesOrOptions.testTagsFilter;
6383
6417
  }
6384
6418
  }
@@ -6401,6 +6435,7 @@ class TestSpecification {
6401
6435
  pool: this.pool,
6402
6436
  testLines: this.testLines,
6403
6437
  testIds: this.testIds,
6438
+ testNames: this.testNames,
6404
6439
  testNamePattern: this.testNamePattern,
6405
6440
  testTagsFilter: this.testTagsFilter
6406
6441
  }
@@ -6478,10 +6513,8 @@ class TestProject {
6478
6513
  */
6479
6514
  getProvidedContext() {
6480
6515
  if (this.isRootProject()) return this._provided;
6481
- // globalSetup can run even if core workspace is not part of the test run
6482
- // so we need to inherit its provided context
6483
6516
  return {
6484
- ...this.vitest.getRootProject().getProvidedContext(),
6517
+ ...(this._instanceParent ?? this.vitest.getRootProject()).getProvidedContext(),
6485
6518
  ...this._provided
6486
6519
  };
6487
6520
  }
@@ -6534,10 +6567,14 @@ class TestProject {
6534
6567
  /** @internal */
6535
6568
  async _initializeGlobalSetup() {
6536
6569
  if (this._globalSetups) return;
6570
+ // the project that declares browser instances runs their inherited global setup once
6571
+ const parent = this._instanceParent;
6572
+ await parent?._initializeGlobalSetup();
6573
+ const globalSetup = parent ? toArray(this.config.globalSetup).filter((file) => !parent.config.globalSetup.includes(file)) : this.config.globalSetup;
6537
6574
  const native = this.runner instanceof NativeModuleRunner;
6538
6575
  // global setup is imported by this process, so its imports are only known to a resolve hook
6539
6576
  const canRecord = !native || !this.vitest.config.experimental.recordDependencies || !this.config.globalSetup.length || trackNativeImports();
6540
- this._globalSetups = await loadGlobalSetupFiles(this.runner, this.config.globalSetup);
6577
+ this._globalSetups = await loadGlobalSetupFiles(this.runner, globalSetup);
6541
6578
  if (canRecord) this.vitest.cache._dependencies.recordGlobalSetup(this, collectDependencies(native ? nativeImports.getImports : getEvaluatedImports(this.runner.evaluatedModules), this.config.globalSetup));
6542
6579
  for (const globalSetupFile of this._globalSetups) {
6543
6580
  const teardown = await globalSetupFile.setup?.(this);
@@ -6655,6 +6692,10 @@ class TestProject {
6655
6692
  _parentBrowser;
6656
6693
  /** @internal */
6657
6694
  _parent;
6695
+ /** @internal */
6696
+ get _instanceParent() {
6697
+ return this._parent?._parentBrowser ? this._parent : void 0;
6698
+ }
6658
6699
  /**
6659
6700
  * Closes the project and all associated resources. This can only be called once; the closing promise is cached until the server restarts.
6660
6701
  * If the resources are needed again, create a new project.
@@ -8465,12 +8506,14 @@ class Logger {
8465
8506
  const config = this.ctx.config;
8466
8507
  // test files were found, but none of them depend on the changed files
8467
8508
  const noAffectedTests = !!this.ctx._sourceFilterResult?.total;
8468
- const message = !noAffectedTests ? "No test files found" : config.related?.length ? "No affected test files found" : "No changed files found";
8469
- if (config.watch && noAffectedTests) this.log(`${message}\n`);
8509
+ // test files were found, but none of them failed in the last run
8510
+ const noFailedTests = !!this.ctx._failedFilterResult?.total;
8511
+ const message = noFailedTests ? "No failed tests found" : !noAffectedTests ? "No test files found" : config.related?.length ? "No affected test files found" : "No changed files found";
8512
+ if (config.watch && (noAffectedTests || noFailedTests)) this.log(`${message}\n`);
8470
8513
  else if (config.watch) this.log($.red(`${message}. You can change the file name pattern by pressing "p"\n`));
8471
8514
  else if (config.passWithNoTests) this.log(`${message}, exiting with code 0\n`);
8472
8515
  else this.error($.red(`${message}, exiting with code 1\n`));
8473
- if (noAffectedTests) return;
8516
+ if (noAffectedTests || noFailedTests) return;
8474
8517
  const comma = $.dim(", ");
8475
8518
  if (filters?.length) this.console.error($.dim("filter: ") + $.yellow(filters.join(comma)));
8476
8519
  const projectsFilter = toArray(config.project);
@@ -8640,6 +8683,7 @@ class PluginHarness {
8640
8683
  }
8641
8684
  }
8642
8685
 
8686
+ const builtinVCSProviders = /* @__PURE__ */ new Set(["git", "mtime"]);
8643
8687
  function resolvePath(path, root) {
8644
8688
  // local-pkg (mlly)'s resolveModule("./file", { paths: ["/some/root"] }) tries
8645
8689
  // /some/file
@@ -8891,6 +8935,14 @@ function resolveTestConfig(logger, options, viteConfig, globalConfig) {
8891
8935
  if (!textSummary) resolved.coverage.reporter.push(["text-summary", {}]);
8892
8936
  }
8893
8937
  }
8938
+ // CLI options are merged into the test config
8939
+ if (options.sinceLastRun) {
8940
+ if (resolved.cache === false) throw new Error("The \"--since-last-run\" flag requires the \"cache\" option to store the dependencies of the test files.");
8941
+ resolved.changed ||= true;
8942
+ resolved.experimental ??= {};
8943
+ resolved.experimental.recordDependencies = true;
8944
+ resolved.experimental.vcsProvider = "mtime";
8945
+ }
8894
8946
  if (resolved.coverage.changed === void 0 && resolved.changed !== void 0) resolved.coverage.changed = resolved.changed;
8895
8947
  if (resolved.coverage.enabled && resolved.coverage.reportsDirectory) {
8896
8948
  const reportsDirectory = resolve(resolved.root, resolved.coverage.reportsDirectory);
@@ -9112,11 +9164,13 @@ function resolveTestConfig(logger, options, viteConfig, globalConfig) {
9112
9164
  resolved.browser.traceView = typeof traceView === "object" ? {
9113
9165
  enabled: traceView.enabled ?? false,
9114
9166
  recordCanvas: traceView.recordCanvas ?? false,
9115
- inlineImages: traceView.inlineImages ?? false
9167
+ inlineImages: traceView.inlineImages ?? false,
9168
+ ariaSnapshot: traceView.ariaSnapshot ?? true
9116
9169
  } : {
9117
9170
  enabled: traceView ?? false,
9118
9171
  recordCanvas: false,
9119
- inlineImages: false
9172
+ inlineImages: false,
9173
+ ariaSnapshot: true
9120
9174
  };
9121
9175
  if (resolved.browser.enabled && resolved.browser.traceView.enabled) resolved.browser.detailsPanelPosition = "bottom";
9122
9176
  if (resolved.browser.trace.tracesDir != null) resolved.browser.trace.tracesDir = resolvePath(resolved.browser.trace.tracesDir, resolved.root);
@@ -9165,7 +9219,7 @@ function resolveTestConfig(logger, options, viteConfig, globalConfig) {
9165
9219
  import: diagnostics.import ?? true,
9166
9220
  transform: diagnostics.transform ?? true
9167
9221
  };
9168
- if (typeof resolved.experimental.vcsProvider === "string" && resolved.experimental.vcsProvider !== "git") resolved.experimental.vcsProvider = resolvePath(resolved.experimental.vcsProvider, resolved.root);
9222
+ if (typeof resolved.experimental.vcsProvider === "string" && !builtinVCSProviders.has(resolved.experimental.vcsProvider)) resolved.experimental.vcsProvider = resolvePath(resolved.experimental.vcsProvider, resolved.root);
9169
9223
  // `experimental.fsModuleCache` / `experimental.fsModuleCachePath` were promoted to
9170
9224
  // the top-level `fsModuleCache` / `fsModuleCachePath` options.
9171
9225
  const legacyExperimental = options.experimental;
@@ -9242,7 +9296,6 @@ async function resolveConfig(options = {}, viteOverrides = {}, pluginsHarness =
9242
9296
  const rootConfig = resolveTestConfig(pluginsHarness.logger, rootViteConfig.test || {}, rootViteConfig);
9243
9297
  rootViteConfig.test = rootConfig;
9244
9298
  rootViteConfig.server.fs.allow.push(...resolveFsAllow(rootViteConfig.root, rootViteConfig.configFile));
9245
- rootViteConfig.server.fs.deny.push(API_TOKEN_FILE);
9246
9299
  // the server has been created, we don't need to override vite.server options
9247
9300
  const { token, tokenCreated } = resolveApiToken(rootViteConfig.root);
9248
9301
  rootConfig.api.token = token;
@@ -15603,4 +15656,4 @@ var index = /*#__PURE__*/_mergeNamespaces({
15603
15656
  default: prompt
15604
15657
  }, [promptsExports]);
15605
15658
 
15606
- export { resolveStaticMocks as A, BaseSequencer as B, getStaticMocks as C, LocationFilterFileNotFoundError as D, GitCommandError as E, ForksPoolWorker as F, GitNotFoundError as G, VitestResolver as H, IncludeTaskLocationDisabledError as I, createClusterServer as J, attachProjectsFromEntries as K, Logger as L, ModuleTransformService as M, TestProject as N, resolveAndAttachProjects as O, PluginHarness as P, RandomSequencer as Q, RangeLocationFilterProvidedError as R, ServerModuleRunner as S, ThreadsPoolWorker as T, createPool as U, VitestPackageInstaller as V, astCollectTests as W, createFailedFileTask as X, matchesProjectFilter as Y, nanoid as Z, stdout as _, BrowserConnectionError as a, FilesNotFoundError as b, TypecheckPoolWorker as c, VmForksPoolWorker as d, VmThreadsPoolWorker as e, createDebugger as f, createMethodsRPC as g, createViteLogger as h, escapeTestName as i, getFilePoolName as j, isFileServingAllowed as k, resolveConfig as l, resolveFsAllow as m, findConfigFile as n, parse as o, prompt as p, hash as q, resolveApiServerConfig as r, stringify as s, resolveCoverageReporters as t, createIndexLocationsMap as u, getOutputFile as v, groupBy as w, generateCodeFrame as x, printStack as y, getSpecificationDocblock as z };
15659
+ export { generateCodeFrame as A, BaseSequencer as B, printStack as C, LocationFilterFileNotFoundError as D, GitCommandError as E, ForksPoolWorker as F, GitNotFoundError as G, VitestResolver as H, IncludeTaskLocationDisabledError as I, createClusterServer as J, attachProjectsFromEntries as K, Logger as L, ModuleTransformService as M, TestProject as N, resolveAndAttachProjects as O, PluginHarness as P, RandomSequencer as Q, RangeLocationFilterProvidedError as R, ServerModuleRunner as S, ThreadsPoolWorker as T, createPool as U, VitestPackageInstaller as V, astCollectTests as W, createFailedFileTask as X, matchesProjectFilter as Y, nanoid as Z, stdout as _, BrowserConnectionError as a, FilesNotFoundError as b, TypecheckPoolWorker as c, VmForksPoolWorker as d, VmThreadsPoolWorker as e, createDebugger as f, createMethodsRPC as g, createViteLogger as h, escapeTestName as i, getFilePoolName as j, isFileServingAllowed as k, resolveConfig as l, resolveFsAllow as m, findConfigFile as n, parse as o, prompt as p, getSpecificationDocblock as q, resolveApiServerConfig as r, stringify as s, resolveStaticMocks as t, getStaticMocks as u, hash as v, resolveCoverageReporters as w, createIndexLocationsMap as x, getOutputFile as y, groupBy as z };
@@ -1,4 +1,4 @@
1
- import { i as init } from './init.DrHtV5zd.js';
1
+ import { i as init } from './init.DVorxUyL.js';
2
2
 
3
3
  if (!process.send) throw new Error("Expected worker to be run in node:child_process");
4
4
  // Store globals in case tests overwrite them
@@ -1,5 +1,5 @@
1
1
  import { isMainThread, parentPort } from 'node:worker_threads';
2
- import { i as init } from './init.DrHtV5zd.js';
2
+ import { i as init } from './init.DVorxUyL.js';
3
3
 
4
4
  if (isMainThread || !parentPort) throw new Error("Expected worker to be run in node:worker_threads");
5
5
  function workerInit(options) {
@@ -2,12 +2,12 @@ import { readFileSync } from 'node:fs';
2
2
  import module$1, { isBuiltin } from 'node:module';
3
3
  import { pathToFileURL } from 'node:url';
4
4
  import { EvaluatedModules, ModuleRunner } from 'vite/module-runner';
5
- import { e as environments, b as VitestTransport } from './index.CPNMO3h-.js';
5
+ import { e as environments, b as VitestTransport } from './index.BFXLpwll.js';
6
6
  import { r as resolve } from './pathe.M-eThtNZ.Ba-7h1rE.js';
7
- import { b as serializeValue, c as createStackString, p as parseStacktrace } from './source-map.DdKI5IHB.js';
7
+ import { b as serializeValue, c as createStackString, p as parseStacktrace } from './source-map.BjWcsIL9.js';
8
8
  import { w } from './tinyrainbow.Do8I1XRG.js';
9
9
  import { Traces } from '../traces.js';
10
- import { o as onCancel, V as VitestEvaluatedModules, a as rpcDone, c as createRuntimeRpc } from './rpc.DpkUyY1N.js';
10
+ import { o as onCancel, V as VitestEvaluatedModules, a as rpcDone, c as createRuntimeRpc } from './rpc.BV0XpJ5q.js';
11
11
  import { s as setupInspect } from './inspector.CvyFGlXm.js';
12
12
  import { E as EnvironmentTeardownError } from './utils.DYj33du9.js';
13
13
 
@@ -4,7 +4,7 @@ import { automockModule, createManualModuleSource, collectModuleExports } from '
4
4
  import { c as cleanUrl, e as createDefer, i as isAbsolute } from './pathe.M-eThtNZ.Ba-7h1rE.js';
5
5
  import { p as parse } from './acorn.C1kjbUFw.js';
6
6
  import { t as toBuiltin } from './modules.BJuCwlRJ.js';
7
- import { B as BareModuleMocker, n as normalizeModuleId } from './index.CPNMO3h-.js';
7
+ import { B as BareModuleMocker, n as normalizeModuleId } from './index.BFXLpwll.js';
8
8
  import 'node:fs';
9
9
  import './utils.DYj33du9.js';
10
10
  import '../path.js';
@@ -13,18 +13,22 @@ import '../module-evaluator.js';
13
13
  import 'node:vm';
14
14
  import 'vite/module-runner';
15
15
  import '../traces.js';
16
+ import 'node:async_hooks';
16
17
  import '@vitest/mocker';
17
18
  import '@vitest/mocker/redirect';
18
19
  import 'node:console';
19
20
 
20
21
  class NativeModuleMocker extends BareModuleMocker {
21
22
  wrapDynamicImport(moduleFactory) {
22
- if (typeof moduleFactory === "function") return new Promise((resolve, reject) => {
23
+ if (typeof moduleFactory !== "function") return moduleFactory;
24
+ // Node.js can evaluate the module outside of the importer's async chain,
25
+ // so the stack of an import error would not point at the import
26
+ const importer = /* @__PURE__ */ new Error("import location");
27
+ return new Promise((resolve, reject) => {
23
28
  this.resolveMocks().finally(() => {
24
- moduleFactory().then(resolve, reject);
29
+ moduleFactory().then(resolve, (error) => reject(addImporterFrame(error, importer)));
25
30
  });
26
31
  });
27
- return moduleFactory;
28
32
  }
29
33
  resolveMockedModule(url, parentURL) {
30
34
  // don't mock modules inside of packages because there is
@@ -209,5 +213,23 @@ function transformCode(code, format, filename) {
209
213
  }
210
214
  return code;
211
215
  }
216
+ function addImporterFrame(error, importer) {
217
+ const lines = importer.stack?.split("\n") || [];
218
+ const index = lines.findIndex((line) => line.includes("wrapDynamicImport"));
219
+ const frame = index === -1 ? void 0 : lines[index + 1];
220
+ const file = frame?.match(/\(?([^()\s]+):\d+:\d+\)?$/)?.[1];
221
+ if (!frame || !file || frame.includes("node:")) return error;
222
+ const asyncFrame = frame.replace(/^(\s*)at /, "$1at async ");
223
+ const seen = /* @__PURE__ */ new Set();
224
+ let current = error;
225
+ while (current instanceof Error && !seen.has(current)) {
226
+ seen.add(current);
227
+ const stack = current.stack;
228
+ // older Node.js versions keep the async frame of the import themselves
229
+ if (typeof stack === "string" && !stack.split("\n").some((line) => line.includes("at async ") && line.includes(file))) current.stack = `${stack}\n${asyncFrame}`;
230
+ current = current.cause;
231
+ }
232
+ return error;
233
+ }
212
234
 
213
235
  export { NativeModuleMocker };