vitest 3.2.0-beta.2 → 3.2.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/browser.d.ts +3 -3
  2. package/dist/browser.js +1 -1
  3. package/dist/chunks/{base.DwtwORaC.js → base.D4119yLM.js} +4 -3
  4. package/dist/chunks/{benchmark.BoF7jW0Q.js → benchmark.Cf_PACH1.js} +1 -1
  5. package/dist/chunks/{cac.I9MLYfT-.js → cac.DWaWHIIE.js} +18 -15
  6. package/dist/chunks/{cli-api.d6IK1pnk.js → cli-api.CnmEXkxs.js} +250 -49
  7. package/dist/chunks/{config.d.UqE-KR0o.d.ts → config.d.D2ROskhv.d.ts} +2 -0
  8. package/dist/chunks/{console.K1NMVOSc.js → console.Cwr-MFPV.js} +3 -2
  9. package/dist/chunks/{constants.BZZyIeIE.js → constants.DnKduX2e.js} +1 -0
  10. package/dist/chunks/{coverage.OGU09Jbh.js → coverage.C73DaDgS.js} +116 -12
  11. package/dist/chunks/{creator.DGAdZ4Hj.js → creator.C8WKy2eW.js} +10 -7
  12. package/dist/chunks/{date.CDOsz-HY.js → date.ByMsSlOr.js} +25 -0
  13. package/dist/chunks/{defaults.DSxsTG0h.js → defaults.DpVH7vbg.js} +1 -0
  14. package/dist/chunks/{environment.d.D8YDy2v5.d.ts → environment.d.cL3nLXbE.d.ts} +1 -0
  15. package/dist/chunks/{execute.JlGHLJZT.js → execute.B3q-2LPV.js} +25 -0
  16. package/dist/chunks/{global.d.BPa1eL3O.d.ts → global.d.BNLIi6yo.d.ts} +3 -1
  17. package/dist/chunks/{globals.CpxW8ccg.js → globals.CI21aWXF.js} +7 -6
  18. package/dist/chunks/{index.DFXFpH3w.js → index.2jgTs_Q5.js} +19 -1
  19. package/dist/chunks/{index.CV36oG_L.js → index.Bter3jj9.js} +83 -16
  20. package/dist/chunks/{index.DswW_LEs.js → index.CbT4iuwc.js} +7 -4
  21. package/dist/chunks/index.D3XRDfWc.js +213 -0
  22. package/dist/chunks/{index.CfXMNXHg.js → index.DNgLEKsQ.js} +4 -2
  23. package/dist/chunks/{index.CmC5OK9L.js → index.JOzufsrU.js} +2 -1
  24. package/dist/chunks/{inspector.DbDkSkFn.js → inspector.BFsh5KO0.js} +3 -0
  25. package/dist/chunks/{node.3xsWotC9.js → node.Be-ntJnD.js} +1 -1
  26. package/dist/chunks/{reporters.d.CLC9rhKy.d.ts → reporters.d.Bt4IGtsa.d.ts} +24 -6
  27. package/dist/chunks/{rpc.D9_013TY.js → rpc.BKExFSRG.js} +2 -1
  28. package/dist/chunks/{runBaseTests.Dn2vyej_.js → runBaseTests.B_M1TTsK.js} +19 -10
  29. package/dist/chunks/{setup-common.CYo3Y0dD.js → setup-common.CF-O-dZX.js} +2 -1
  30. package/dist/chunks/{typechecker.DnTrplSJ.js → typechecker.BgzF-6iO.js} +78 -21
  31. package/dist/chunks/{utils.CgTj3MsC.js → utils.BlI4TC7Y.js} +1 -0
  32. package/dist/chunks/{utils.BfxieIyZ.js → utils.DPCq3gzW.js} +3 -0
  33. package/dist/chunks/{vi.BFR5YIgu.js → vi.pkoYCV6A.js} +25 -2
  34. package/dist/chunks/{vite.d.CBZ3M_ru.d.ts → vite.d.B-Kx3KCF.d.ts} +3 -1
  35. package/dist/chunks/{vm.C1HHjtNS.js → vm.DPYem2so.js} +72 -4
  36. package/dist/chunks/{worker.d.CoCI7hzP.d.ts → worker.d.BKbBp2ga.d.ts} +2 -2
  37. package/dist/chunks/{worker.d.D5Xdi-Zr.d.ts → worker.d.Bl1O4kuf.d.ts} +1 -1
  38. package/dist/cli.js +4 -4
  39. package/dist/config.cjs +2 -0
  40. package/dist/config.d.ts +7 -6
  41. package/dist/config.js +2 -2
  42. package/dist/coverage.d.ts +4 -4
  43. package/dist/coverage.js +5 -5
  44. package/dist/environments.d.ts +6 -2
  45. package/dist/environments.js +1 -1
  46. package/dist/execute.d.ts +9 -3
  47. package/dist/execute.js +1 -1
  48. package/dist/index.d.ts +24 -12
  49. package/dist/index.js +5 -5
  50. package/dist/node.d.ts +18 -10
  51. package/dist/node.js +14 -12
  52. package/dist/reporters.d.ts +4 -4
  53. package/dist/reporters.js +3 -3
  54. package/dist/runners.d.ts +1 -1
  55. package/dist/runners.js +13 -5
  56. package/dist/snapshot.js +2 -2
  57. package/dist/suite.js +2 -2
  58. package/dist/worker.js +9 -5
  59. package/dist/workers/forks.js +6 -4
  60. package/dist/workers/runVmTests.js +14 -9
  61. package/dist/workers/threads.js +4 -4
  62. package/dist/workers/vmForks.js +6 -6
  63. package/dist/workers/vmThreads.js +6 -6
  64. package/dist/workers.d.ts +4 -4
  65. package/dist/workers.js +10 -10
  66. package/package.json +18 -18
  67. package/dist/chunks/index.CK1YOQaa.js +0 -143
@@ -5,15 +5,15 @@ import path, { resolve as resolve$1 } from 'node:path';
5
5
  import { noop, isPrimitive, createDefer, highlight, toArray, deepMerge, nanoid, slash, deepClone, notNullish } from '@vitest/utils';
6
6
  import { f as findUp, p as prompt } from './index.X0nbfr6-.js';
7
7
  import * as vite from 'vite';
8
- import { searchForWorkspaceRoot, version, mergeConfig, createServer } from 'vite';
9
- import { A as API_PATH, c as configFiles, d as defaultBrowserPort, w as workspacesFiles, a as defaultPort } from './constants.BZZyIeIE.js';
8
+ import { searchForWorkspaceRoot, version, createServer, mergeConfig } from 'vite';
9
+ import { A as API_PATH, c as configFiles, d as defaultBrowserPort, w as workspacesFiles, a as defaultPort } from './constants.DnKduX2e.js';
10
10
  import { generateFileHash, limitConcurrency, createFileTask, hasFailed, getTasks, getTests } from '@vitest/runner/utils';
11
11
  import { SnapshotManager } from '@vitest/snapshot/manager';
12
12
  import { ViteNodeRunner } from 'vite-node/client';
13
13
  import { ViteNodeServer } from 'vite-node/server';
14
- import { v as version$1 } from './cac.I9MLYfT-.js';
14
+ import { v as version$1 } from './cac.DWaWHIIE.js';
15
15
  import { c as createBirpc } from './index.CJ0plNrh.js';
16
- import { p as parse, s as stringify, d as printError, f as formatProjectName, w as withLabel, e as errorBanner, h as divider, i as generateCodeFrame, R as ReportersMap, j as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.CV36oG_L.js';
16
+ import { p as parse, s as stringify, d as printError, f as formatProjectName, w as withLabel, e as errorBanner, h as divider, i as generateCodeFrame, R as ReportersMap, j as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.Bter3jj9.js';
17
17
  import require$$0$3 from 'events';
18
18
  import require$$1$1 from 'https';
19
19
  import require$$2 from 'http';
@@ -28,8 +28,8 @@ import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
28
28
  import { parseErrorStacktrace } from '@vitest/utils/source-map';
29
29
  import crypto from 'node:crypto';
30
30
  import { distDir, rootDir } from '../path.js';
31
- import { R as RandomSequencer, i as isPackageExists, h as hash, V as VitestCache, g as getFilePoolName, d as isBrowserEnabled, r as resolveConfig, e as groupBy, f as getCoverageProvider, j as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.OGU09Jbh.js';
32
- import { c as convertTasksToEvents } from './typechecker.DnTrplSJ.js';
31
+ import { R as RandomSequencer, i as isPackageExists, h as hash, V as VitestCache, g as getFilePoolName, d as isBrowserEnabled, r as resolveConfig, e as groupBy, f as getCoverageProvider, j as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.C73DaDgS.js';
32
+ import { c as convertTasksToEvents } from './typechecker.BgzF-6iO.js';
33
33
  import { Console } from 'node:console';
34
34
  import c from 'tinyrainbow';
35
35
  import { createRequire } from 'node:module';
@@ -41,9 +41,9 @@ import pm from 'picomatch';
41
41
  import { glob, isDynamicPattern } from 'tinyglobby';
42
42
  import { normalizeRequestId, cleanUrl } from 'vite-node/utils';
43
43
  import { hoistMocksPlugin, automockPlugin } from '@vitest/mocker/node';
44
- import { c as configDefaults } from './defaults.DSxsTG0h.js';
44
+ import { c as configDefaults } from './defaults.DpVH7vbg.js';
45
45
  import MagicString from 'magic-string';
46
- import { a as BenchmarkReportsMap } from './index.CmC5OK9L.js';
46
+ import { a as BenchmarkReportsMap } from './index.JOzufsrU.js';
47
47
  import assert$1 from 'node:assert';
48
48
  import { serializeError } from '@vitest/utils/error';
49
49
  import readline from 'node:readline';
@@ -779,6 +779,14 @@ function requirePermessageDeflate () {
779
779
  this[kError].code = 'WS_ERR_UNSUPPORTED_MESSAGE_LENGTH';
780
780
  this[kError][kStatusCode] = 1009;
781
781
  this.removeListener('data', inflateOnData);
782
+
783
+ //
784
+ // The choice to employ `zlib.reset()` over `zlib.close()` is dictated by the
785
+ // fact that in Node.js versions prior to 13.10.0, the callback for
786
+ // `zlib.flush()` is not called if `zlib.close()` is used. Utilizing
787
+ // `zlib.reset()` ensures that either the callback is invoked or an error is
788
+ // emitted.
789
+ //
782
790
  this.reset();
783
791
  }
784
792
 
@@ -794,6 +802,12 @@ function requirePermessageDeflate () {
794
802
  // closed when an error is emitted.
795
803
  //
796
804
  this[kPerMessageDeflate]._inflate = null;
805
+
806
+ if (this[kError]) {
807
+ this[kCallback](this[kError]);
808
+ return;
809
+ }
810
+
797
811
  err[kStatusCode] = 1007;
798
812
  this[kCallback](err);
799
813
  }
@@ -3511,7 +3525,7 @@ function requireWebsocket () {
3511
3525
  if (parsedUrl.protocol !== 'ws:' && !isSecure && !isIpcUrl) {
3512
3526
  invalidUrlMessage =
3513
3527
  'The URL\'s protocol must be one of "ws:", "wss:", ' +
3514
- '"http:", "https", or "ws+unix:"';
3528
+ '"http:", "https:", or "ws+unix:"';
3515
3529
  } else if (isIpcUrl && !parsedUrl.pathname) {
3516
3530
  invalidUrlMessage = "The URL's pathname is empty";
3517
3531
  } else if (parsedUrl.hash) {
@@ -5035,7 +5049,10 @@ function clearId(id) {
5035
5049
  return id?.replace(/\?v=\w+$/, "") || "";
5036
5050
  }
5037
5051
 
5052
+ // Serialization support utils.
5038
5053
  function cloneByOwnProperties(value) {
5054
+ // Clones the value's properties into a new Object. The simpler approach of
5055
+ // Object.assign() won't work in the case that properties are not enumerable.
5039
5056
  return Object.getOwnPropertyNames(value).reduce((clone, prop) => ({
5040
5057
  ...clone,
5041
5058
  [prop]: value[prop]
@@ -5061,12 +5078,15 @@ function stringifyReplace(key, value) {
5061
5078
 
5062
5079
  function isValidApiRequest(config, req) {
5063
5080
  const url = new URL(req.url ?? "", "http://localhost");
5081
+ // validate token. token is injected in ui/tester/orchestrator html, which is cross origin protected.
5064
5082
  try {
5065
5083
  const token = url.searchParams.get("token");
5066
5084
  if (token && crypto.timingSafeEqual(Buffer.from(token), Buffer.from(config.api.token))) {
5067
5085
  return true;
5068
5086
  }
5069
- } catch {}
5087
+ }
5088
+ // an error is thrown when the length is incorrect
5089
+ catch {}
5070
5090
  return false;
5071
5091
  }
5072
5092
 
@@ -5267,6 +5287,7 @@ class BrowserSessions {
5267
5287
  this.sessions.delete(sessionId);
5268
5288
  }
5269
5289
  createSession(sessionId, project, pool) {
5290
+ // this promise only waits for the WS connection with the orhcestrator to be established
5270
5291
  const defer = createDefer();
5271
5292
  const timeout = setTimeout(() => {
5272
5293
  defer.reject(new Error(`Failed to connect to the browser session "${sessionId}" [${project.name}] within the timeout.`));
@@ -5489,6 +5510,7 @@ class Logger {
5489
5510
  this.log(PAD + c.dim(c.green(`UI started at http://${host}:${c.bold(port)}${base}`)));
5490
5511
  } else if (this.ctx.config.api?.port) {
5491
5512
  const resolvedUrls = this.ctx.server.resolvedUrls;
5513
+ // workaround for https://github.com/vitejs/vite/issues/15438, it was fixed in vite 5.1
5492
5514
  const fallbackUrl = `http://${this.ctx.config.api.host || "localhost"}:${this.ctx.config.api.port}`;
5493
5515
  const origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0] ?? fallbackUrl;
5494
5516
  this.log(PAD + c.dim(c.green(`API started at ${new URL("/", origin)}`)));
@@ -5552,6 +5574,8 @@ class Logger {
5552
5574
  };
5553
5575
  const onExit = (signal, exitCode) => {
5554
5576
  cleanup();
5577
+ // Interrupted signals don't set exit code automatically.
5578
+ // Use same exit code as node: https://nodejs.org/api/process.html#signal-events
5555
5579
  if (process.exitCode === undefined) {
5556
5580
  process.exitCode = exitCode !== undefined ? 128 + exitCode : Number(signal);
5557
5581
  }
@@ -5615,7 +5639,8 @@ class VitestPackageInstaller {
5615
5639
  });
5616
5640
  if (install) {
5617
5641
  const packageName = version ? `${dependency}@${version}` : dependency;
5618
- await (await import('./index.CK1YOQaa.js')).installPackage(packageName, { dev: true });
5642
+ await (await import('./index.D3XRDfWc.js')).installPackage(packageName, { dev: true });
5643
+ // TODO: somehow it fails to load the package after installation, remove this when it's fixed
5619
5644
  process.stderr.write(c.yellow(`\nPackage ${packageName} installed, re-run the command to start.\n`));
5620
5645
  process.exit();
5621
5646
  return true;
@@ -5627,6 +5652,7 @@ class VitestPackageInstaller {
5627
5652
  function serializeConfig(config, coreConfig, viteConfig) {
5628
5653
  const optimizer = config.deps?.optimizer;
5629
5654
  const poolOptions = config.poolOptions;
5655
+ // Resolve from server.config to avoid comparing against default value
5630
5656
  const isolate = viteConfig?.test?.isolate;
5631
5657
  return {
5632
5658
  environmentOptions: config.environmentOptions,
@@ -5799,6 +5825,7 @@ function generateCssFilenameHash(filepath) {
5799
5825
  return hash("md5", filepath, "hex").slice(0, 6);
5800
5826
  }
5801
5827
  function generateScopedClassName(strategy, name, filename) {
5828
+ // should be configured by Vite defaults
5802
5829
  if (strategy === "scoped") {
5803
5830
  return null;
5804
5831
  }
@@ -5823,6 +5850,8 @@ function clearScreen(logger) {
5823
5850
  let lastType;
5824
5851
  let lastMsg;
5825
5852
  let sameCount = 0;
5853
+ // Only initialize the timeFormatter when the timestamp option is used, and
5854
+ // reuse it across all loggers
5826
5855
  let timeFormatter;
5827
5856
  function getTimeFormatter() {
5828
5857
  timeFormatter ??= new Intl.DateTimeFormat(undefined, {
@@ -5832,6 +5861,10 @@ function getTimeFormatter() {
5832
5861
  });
5833
5862
  return timeFormatter;
5834
5863
  }
5864
+ // This is copy-pasted and needs to be synced from time to time. Ideally, Vite's `createLogger` should accept a custom `console`
5865
+ // https://github.com/vitejs/vite/blob/main/packages/vite/src/node/logger.ts?rgh-link-date=2024-10-16T23%3A29%3A19Z
5866
+ // When Vitest supports only Vite 6 and above, we can use Vite's `createLogger({ console })`
5867
+ // https://github.com/vitejs/vite/pull/18379
5835
5868
  function createViteLogger(console, level = "info", options = {}) {
5836
5869
  const loggedErrors = new WeakSet();
5837
5870
  const { prefix = "[vite]", allowClearScreen = true } = options;
@@ -5912,6 +5945,7 @@ function createViteLogger(console, level = "info", options = {}) {
5912
5945
  };
5913
5946
  return logger;
5914
5947
  }
5948
+ // silence warning by Vite for statically not analyzable dynamic import
5915
5949
  function silenceImportViteIgnoreWarning(logger) {
5916
5950
  return {
5917
5951
  ...logger,
@@ -5934,6 +5968,8 @@ function isCSS(id) {
5934
5968
  function isCSSModule(id) {
5935
5969
  return cssModuleRE.test(id);
5936
5970
  }
5971
+ // inline css requests are expected to just return the
5972
+ // string content directly and not the proxy module
5937
5973
  function isInline(id) {
5938
5974
  return cssInlineRE.test(id);
5939
5975
  }
@@ -5965,6 +6001,8 @@ function CSSEnablerPlugin(ctx) {
5965
6001
  if (!isCSS(id)) {
5966
6002
  return;
5967
6003
  }
6004
+ // css plugin inside Vite won't do anything if the code is empty
6005
+ // but it will put __vite__updateStyle anyway
5968
6006
  if (!shouldProcessCSS(id)) {
5969
6007
  return { code: "" };
5970
6008
  }
@@ -5977,6 +6015,9 @@ function CSSEnablerPlugin(ctx) {
5977
6015
  return;
5978
6016
  }
5979
6017
  if (isCSSModule(id) && !isInline(id)) {
6018
+ // return proxy for css modules, so that imported module has names:
6019
+ // styles.foo returns a "foo" instead of "undefined"
6020
+ // we don't use code content to generate hash for "scoped", because it's empty
5980
6021
  const scopeStrategy = typeof ctx.config.css !== "boolean" && ctx.config.css.modules?.classNameStrategy || "stable";
5981
6022
  const proxyReturn = getCSSModuleProxyReturn(scopeStrategy, relative(ctx.config.root, id));
5982
6023
  const code = `export default new Proxy(Object.create(null), {
@@ -6477,6 +6518,10 @@ function stripLiteralDetailed(code, options) {
6477
6518
 
6478
6519
  const metaUrlLength = "import.meta.url".length;
6479
6520
  const locationString = "self.location".padEnd(metaUrlLength, " ");
6521
+ // Vite transforms new URL('./path', import.meta.url) to new URL('/path.js', import.meta.url)
6522
+ // This makes "href" equal to "http://localhost:3000/path.js" in the browser, but if we keep it like this,
6523
+ // then in tests the URL will become "file:///path.js".
6524
+ // To battle this, we replace "import.meta.url" with "self.location" in the code to keep the browser behavior.
6480
6525
  function NormalizeURLPlugin() {
6481
6526
  return {
6482
6527
  name: "vitest:normalize-url",
@@ -6490,6 +6535,7 @@ function NormalizeURLPlugin() {
6490
6535
  const assetImportMetaUrlRE = /\bnew\s+URL\s*\(\s*(?:'[^']+'|"[^"]+"|`[^`]+`)\s*,\s*(?:'' \+ )?import\.meta\.url\s*(?:,\s*)?\)/g;
6491
6536
  let updatedCode = code;
6492
6537
  let match;
6538
+ // eslint-disable-next-line no-cond-assign
6493
6539
  while (match = assetImportMetaUrlRE.exec(cleanString)) {
6494
6540
  const { 0: exp, index } = match;
6495
6541
  const metaUrlIndex = index + exp.indexOf("import.meta.url");
@@ -6543,6 +6589,8 @@ function resolveOptimizerConfig(_testOptions, viteOptions, testConfig, viteCache
6543
6589
  include
6544
6590
  };
6545
6591
  }
6592
+ // `optimizeDeps.disabled` is deprecated since v5.1.0-beta.1
6593
+ // https://github.com/vitejs/vite/pull/15184
6546
6594
  if (major >= 5 && minor >= 1 || major >= 6) {
6547
6595
  if (newConfig.optimizeDeps.disabled) {
6548
6596
  newConfig.optimizeDeps.noDiscovery = true;
@@ -6566,6 +6614,8 @@ function deleteDefineConfig(viteConfig) {
6566
6614
  try {
6567
6615
  replacement = typeof val === "string" ? JSON.parse(val) : val;
6568
6616
  } catch {
6617
+ // probably means it contains reference to some variable,
6618
+ // like this: "__VAR__": "process.env.VAR"
6569
6619
  continue;
6570
6620
  }
6571
6621
  if (key.startsWith("import.meta.env.")) {
@@ -6626,6 +6676,8 @@ function VitestOptimizer() {
6626
6676
  };
6627
6677
  }
6628
6678
 
6679
+ // so people can reassign envs at runtime
6680
+ // import.meta.env.VITE_NAME = 'app' -> process.env.VITE_NAME = 'app'
6629
6681
  function SsrReplacerPlugin() {
6630
6682
  return {
6631
6683
  name: "vitest:ssr-replacer",
@@ -6662,6 +6714,8 @@ function VitestProjectResolver(ctx) {
6662
6714
  enforce: "pre",
6663
6715
  async resolveId(id, _, { ssr }) {
6664
6716
  if (id === "vitest" || id.startsWith("@vitest/") || id.startsWith("vitest/")) {
6717
+ // always redirect the request to the root vitest plugin since
6718
+ // it will be the one used to run Vitest
6665
6719
  const resolved = await ctx.server.pluginContainer.resolveId(id, undefined, {
6666
6720
  skip: new Set([plugin]),
6667
6721
  ssr
@@ -6681,6 +6735,7 @@ function VitestCoreResolver(ctx) {
6681
6735
  return resolve(distDir, "index.js");
6682
6736
  }
6683
6737
  if (id.startsWith("@vitest/") || id.startsWith("vitest/")) {
6738
+ // ignore actual importer, we want it to be resolved relative to the root
6684
6739
  return this.resolve(id, join(ctx.config.root, "index.html"), { skipSelf: true });
6685
6740
  }
6686
6741
  }
@@ -6705,6 +6760,7 @@ function WorkspaceVitestPlugin(project, options) {
6705
6760
  };
6706
6761
  if (!name) {
6707
6762
  if (typeof options.workspacePath === "string") {
6763
+ // if there is a package.json, read the name from it
6708
6764
  const dir = options.workspacePath.endsWith("/") ? options.workspacePath.slice(0, -1) : dirname(options.workspacePath);
6709
6765
  const pkgJsonPath = resolve(dir, "package.json");
6710
6766
  if (existsSync(pkgJsonPath)) {
@@ -6745,22 +6801,27 @@ function WorkspaceVitestPlugin(project, options) {
6745
6801
  color
6746
6802
  } }
6747
6803
  };
6748
- if (project.vitest._options.browser && viteConfig.test?.browser) {
6749
- viteConfig.test.browser = mergeConfig(viteConfig.test.browser, project.vitest._options.browser);
6750
- }
6751
6804
  config.test.defines = defines;
6805
+ const isUserBrowserEnabled = viteConfig.test?.browser?.enabled;
6806
+ const isBrowserEnabled = isUserBrowserEnabled ?? (viteConfig.test?.browser && project.vitest._cliOptions.browser?.enabled);
6807
+ // keep project names to potentially filter it out
6752
6808
  const workspaceNames = [name];
6753
- if (viteConfig.test?.browser?.enabled) {
6754
- if (viteConfig.test.browser.name && !viteConfig.test.browser.instances?.length) {
6755
- const browser = viteConfig.test.browser.name;
6756
- workspaceNames.push(name ? `${name} (${browser})` : browser);
6757
- }
6758
- viteConfig.test.browser.instances?.forEach((instance) => {
6759
- instance.name ??= name ? `${name} (${instance.browser})` : instance.browser;
6760
- workspaceNames.push(instance.name);
6761
- });
6809
+ const browser = viteConfig.test.browser || {};
6810
+ if (isBrowserEnabled && browser.name && !browser.instances?.length) {
6811
+ // vitest injects `instances` in this case later on
6812
+ workspaceNames.push(name ? `${name} (${browser.name})` : browser.name);
6762
6813
  }
6814
+ viteConfig.test?.browser?.instances?.forEach((instance) => {
6815
+ // every instance is a potential project
6816
+ instance.name ??= name ? `${name} (${instance.browser})` : instance.browser;
6817
+ if (isBrowserEnabled) {
6818
+ workspaceNames.push(instance.name);
6819
+ }
6820
+ });
6763
6821
  const filters = project.vitest.config.project;
6822
+ // if there is `--project=...` filter, check if any of the potential projects match
6823
+ // if projects don't match, we ignore the test project altogether
6824
+ // if some of them match, they will later be filtered again by `resolveWorkspace`
6764
6825
  if (filters.length) {
6765
6826
  const hasProject = workspaceNames.some((name) => {
6766
6827
  return project.vitest.matchesProjectFilter(name);
@@ -6881,6 +6942,8 @@ class TestSpecification {
6881
6942
  }
6882
6943
 
6883
6944
  async function createViteServer(inlineConfig) {
6945
+ // Vite prints an error (https://github.com/vitejs/vite/issues/14328)
6946
+ // But Vitest works correctly either way
6884
6947
  const error = console.error;
6885
6948
  console.error = (...args) => {
6886
6949
  if (typeof args[0] === "string" && args[0].includes("WebSocket server error:")) {
@@ -6942,6 +7005,8 @@ class TestProject {
6942
7005
  }
6943
7006
  return this._hash;
6944
7007
  }
7008
+ // "provide" is a property, not a method to keep the context when destructed in the global setup,
7009
+ // making it a method would be a breaking change, and can be done in Vitest 3 at minimum
6945
7010
  /**
6946
7011
  * Provide a value to the test context. This value will be available to all tests with `inject`.
6947
7012
  */
@@ -6951,6 +7016,7 @@ class TestProject {
6951
7016
  } catch (err) {
6952
7017
  throw new Error(`Cannot provide "${key}" because it's not serializable.`, { cause: err });
6953
7018
  }
7019
+ // casting `any` because the default type is `never` since `ProvidedContext` is empty
6954
7020
  this._provided[key] = value;
6955
7021
  };
6956
7022
  /**
@@ -6960,6 +7026,8 @@ class TestProject {
6960
7026
  if (this.isRootProject()) {
6961
7027
  return this._provided;
6962
7028
  }
7029
+ // globalSetup can run even if core workspace is not part of the test run
7030
+ // so we need to inherit its provided context
6963
7031
  return {
6964
7032
  ...this.vitest.getRootProject().getProvidedContext(),
6965
7033
  ...this._provided
@@ -6986,6 +7054,7 @@ class TestProject {
6986
7054
  if (!this._vite) {
6987
7055
  throw new Error("The server was not set. It means that `project.vite` was called before the Vite server was established.");
6988
7056
  }
7057
+ // checking it once should be enough
6989
7058
  Object.defineProperty(this, "vite", {
6990
7059
  configurable: true,
6991
7060
  writable: true,
@@ -7000,6 +7069,12 @@ class TestProject {
7000
7069
  if (!this._config) {
7001
7070
  throw new Error("The config was not set. It means that `project.config` was called before the Vite server was established.");
7002
7071
  }
7072
+ // checking it once should be enough
7073
+ // Object.defineProperty(this, 'config', {
7074
+ // configurable: true,
7075
+ // writable: true,
7076
+ // value: this._config,
7077
+ // })
7003
7078
  return this._config;
7004
7079
  }
7005
7080
  /**
@@ -7079,6 +7154,7 @@ class TestProject {
7079
7154
  get logger() {
7080
7155
  return this.vitest.logger;
7081
7156
  }
7157
+ // it's possible that file path was imported with different queries (?raw, ?url, etc)
7082
7158
  /** @deprecated use `.vite` or `.browser.vite` directly */
7083
7159
  getModulesByFilepath(file) {
7084
7160
  const set = this.server.moduleGraph.getModulesByFile(file) || this.browser?.vite.moduleGraph.getModulesByFile(file);
@@ -7172,6 +7248,9 @@ class TestProject {
7172
7248
  expandDirectories: false
7173
7249
  };
7174
7250
  const files = await glob(include, globOptions);
7251
+ // keep the slashes consistent with Vite
7252
+ // we are not using the pathe here because it normalizes the drive letter on Windows
7253
+ // and we want to keep it the same as working dir
7175
7254
  return files.map((file) => slash(path.resolve(cwd, file)));
7176
7255
  }
7177
7256
  /**
@@ -7213,6 +7292,7 @@ class TestProject {
7213
7292
  return testFiles.filter((t) => {
7214
7293
  const testFile = relative(dir, t).toLocaleLowerCase();
7215
7294
  return filters.some((f) => {
7295
+ // if filter is a full file path, we should include it if it's in the same folder
7216
7296
  if (isAbsolute(f) && t.startsWith(f)) {
7217
7297
  return true;
7218
7298
  }
@@ -7303,6 +7383,7 @@ class TestProject {
7303
7383
  this._setHash();
7304
7384
  for (const _providedKey in this.config.provide) {
7305
7385
  const providedKey = _providedKey;
7386
+ // type is very strict here, so we cast it to any
7306
7387
  this.provide(providedKey, this.config.provide[providedKey]);
7307
7388
  }
7308
7389
  this.closingPromise = undefined;
@@ -7321,6 +7402,7 @@ class TestProject {
7321
7402
  });
7322
7403
  }
7323
7404
  _serializeOverriddenConfig() {
7405
+ // TODO: serialize the config _once_ or when needed
7324
7406
  const config = serializeConfig(this.config, this.vitest.config, this.vite.config);
7325
7407
  if (!this.vitest.configOverride) {
7326
7408
  return config;
@@ -7350,6 +7432,7 @@ class TestProject {
7350
7432
  _provideObject(context) {
7351
7433
  for (const _providedKey in context) {
7352
7434
  const providedKey = _providedKey;
7435
+ // type is very strict here, so we cast it to any
7353
7436
  this.provide(providedKey, context[providedKey]);
7354
7437
  }
7355
7438
  }
@@ -7419,6 +7502,8 @@ function generateHash(str) {
7419
7502
 
7420
7503
  async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projectsDefinition, names) {
7421
7504
  const { configFiles, projectConfigs, nonConfigDirectories } = await resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition);
7505
+ // cli options that affect the project config,
7506
+ // not all options are allowed to be overridden
7422
7507
  const overridesOptions = [
7423
7508
  "logHeapUsage",
7424
7509
  "allowOnly",
@@ -7450,7 +7535,10 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7450
7535
  const concurrent = limitConcurrency(nodeos__default.availableParallelism?.() || nodeos__default.cpus().length || 5);
7451
7536
  projectConfigs.forEach((options, index) => {
7452
7537
  const configRoot = workspaceConfigPath ? dirname(workspaceConfigPath) : vitest.config.root;
7538
+ // if extends a config file, resolve the file path
7453
7539
  const configFile = typeof options.extends === "string" ? resolve(configRoot, options.extends) : options.extends === true ? vitest.vite.config.configFile || false : false;
7540
+ // if `root` is configured, resolve it relative to the workspace file or vite root (like other options)
7541
+ // if `root` is not specified, inline configs use the same root as the root project
7454
7542
  const root = options.root ? resolve(configRoot, options.root) : vitest.config.root;
7455
7543
  projectPromises.push(concurrent(() => initializeProject(index, vitest, {
7456
7544
  ...options,
@@ -7463,6 +7551,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7463
7551
  })));
7464
7552
  });
7465
7553
  for (const path of fileProjects) {
7554
+ // if file leads to the root config, then we can just reuse it because we already initialized it
7466
7555
  if (vitest.vite.config.configFile === path) {
7467
7556
  const project = getDefaultTestProject(vitest);
7468
7557
  if (project) {
@@ -7478,6 +7567,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7478
7567
  test: cliOverrides
7479
7568
  })));
7480
7569
  }
7570
+ // pretty rare case - the glob didn't match anything and there are no inline configs
7481
7571
  if (!projectPromises.length) {
7482
7572
  throw new Error([
7483
7573
  "No projects were found. Make sure your configuration is correct. ",
@@ -7491,6 +7581,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7491
7581
  for (const result of resolvedProjectsPromises) {
7492
7582
  if (result.status === "rejected") {
7493
7583
  if (result.reason instanceof VitestFilteredOutProjectError) {
7584
+ // filter out filtered out projects
7494
7585
  continue;
7495
7586
  }
7496
7587
  errors.push(result.reason);
@@ -7501,6 +7592,7 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7501
7592
  if (errors.length) {
7502
7593
  throw new AggregateError(errors, "Failed to initialize projects. There were errors during projects setup. See below for more details.");
7503
7594
  }
7595
+ // project names are guaranteed to be unique
7504
7596
  for (const project of resolvedProjects) {
7505
7597
  const name = project.name;
7506
7598
  if (names.has(name)) {
@@ -7545,10 +7637,12 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7545
7637
  ].filter(Boolean).join("")));
7546
7638
  }
7547
7639
  const originalName = project.config.name;
7640
+ // if original name is in the --project=name filter, keep all instances
7548
7641
  const filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
7549
7642
  const newName = instance.name;
7550
7643
  return vitest.matchesProjectFilter(newName);
7551
7644
  });
7645
+ // every project was filtered out
7552
7646
  if (!filteredInstances.length) {
7553
7647
  removeProjects.add(project);
7554
7648
  return;
@@ -7628,13 +7722,19 @@ function cloneConfig(project, { browser,...config }) {
7628
7722
  }, overrideConfig);
7629
7723
  }
7630
7724
  async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDefinition) {
7725
+ // project configurations that were specified directly
7631
7726
  const projectsOptions = [];
7727
+ // custom config files that were specified directly or resolved from a directory
7632
7728
  const projectsConfigFiles = [];
7729
+ // custom glob matches that should be resolved as directories or config files
7633
7730
  const projectsGlobMatches = [];
7731
+ // directories that don't have a config file inside, but should be treated as projects
7634
7732
  const nonConfigProjectDirectories = [];
7635
7733
  for (const definition of projectsDefinition) {
7636
7734
  if (typeof definition === "string") {
7637
7735
  const stringOption = definition.replace("<rootDir>", vitest.config.root);
7736
+ // if the string doesn't contain a glob, we can resolve it directly
7737
+ // ['./vitest.config.js']
7638
7738
  if (!isDynamicPattern(stringOption)) {
7639
7739
  const file = resolve(vitest.config.root, stringOption);
7640
7740
  if (!existsSync(file)) {
@@ -7643,6 +7743,7 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7643
7743
  throw new Error(`${note} references a non-existing file or a directory: ${file}`);
7644
7744
  }
7645
7745
  const stats = await promises.stat(file);
7746
+ // user can specify a config file directly
7646
7747
  if (stats.isFile()) {
7647
7748
  projectsConfigFiles.push(file);
7648
7749
  } else if (stats.isDirectory()) {
@@ -7654,6 +7755,7 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7654
7755
  nonConfigProjectDirectories.push(directory);
7655
7756
  }
7656
7757
  } else {
7758
+ // should never happen
7657
7759
  throw new TypeError(`Unexpected file type: ${file}`);
7658
7760
  }
7659
7761
  } else {
@@ -7685,6 +7787,8 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7685
7787
  };
7686
7788
  const projectsFs = await glob(projectsGlobMatches, globOptions);
7687
7789
  await Promise.all(projectsFs.map(async (path) => {
7790
+ // directories are allowed with a glob like `packages/*`
7791
+ // in this case every directory is treated as a project
7688
7792
  if (path.endsWith("/")) {
7689
7793
  const configFile = await resolveDirectoryConfig(path);
7690
7794
  if (configFile) {
@@ -7706,6 +7810,8 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, projectsDe
7706
7810
  }
7707
7811
  async function resolveDirectoryConfig(directory) {
7708
7812
  const files = new Set(await promises.readdir(directory));
7813
+ // default resolution looks for vitest.config.* or vite.config.* files
7814
+ // this simulates how `findUp` works in packages/vitest/src/node/create.ts:29
7709
7815
  const configFile = configFiles.find((file) => files.has(file));
7710
7816
  if (configFile) {
7711
7817
  return resolve(directory, configFile);
@@ -7718,6 +7824,7 @@ function getDefaultTestProject(vitest) {
7718
7824
  if (!filter.length) {
7719
7825
  return project;
7720
7826
  }
7827
+ // check for the project name and browser names
7721
7828
  const hasProjects = getPotentialProjectNames(project).some((p) => vitest.matchesProjectFilter(p));
7722
7829
  if (hasProjects) {
7723
7830
  return project;
@@ -7839,6 +7946,7 @@ class VitestSpecifications {
7839
7946
  const files = [];
7840
7947
  const dir = process.cwd();
7841
7948
  const parsedFilters = filters.map((f) => parseFilter(f));
7949
+ // Require includeTaskLocation when a location filter is passed
7842
7950
  if (!this.vitest.config.includeTaskLocation && parsedFilters.some((f) => f.lineNumber !== undefined)) {
7843
7951
  throw new IncludeTaskLocationDisabledError();
7844
7952
  }
@@ -7846,6 +7954,7 @@ class VitestSpecifications {
7846
7954
  ...f,
7847
7955
  filename: resolve(dir, f.filename)
7848
7956
  })));
7957
+ // Key is file and val specifies whether we have matched this file with testLocation
7849
7958
  const testLocHasMatch = {};
7850
7959
  await Promise.all(this.vitest.projects.map(async (project) => {
7851
7960
  const { testFiles, typecheckTestFiles } = await project.globTestFiles(parsedFilters.map((f) => f.filename));
@@ -7913,6 +8022,8 @@ class VitestSpecifications {
7913
8022
  if (matcher && related.some((file) => matcher(file))) {
7914
8023
  return specs;
7915
8024
  }
8025
+ // don't run anything if no related sources are found
8026
+ // if we are in watch mode, we want to process all tests
7916
8027
  if (!this.vitest.config.watch && !related.length) {
7917
8028
  return [];
7918
8029
  }
@@ -7922,6 +8033,7 @@ class VitestSpecifications {
7922
8033
  }));
7923
8034
  const runningTests = [];
7924
8035
  for (const [specification, deps] of testGraphs) {
8036
+ // if deps or the test itself were changed
7925
8037
  if (related.some((path) => path === specification.moduleId || deps.has(path))) {
7926
8038
  runningTests.push(specification);
7927
8039
  }
@@ -8097,6 +8209,7 @@ class TestCase extends ReportedTaskImplementation {
8097
8209
  */
8098
8210
  diagnostic() {
8099
8211
  const result = this.task.result;
8212
+ // startTime should always be available if the test has properly finished
8100
8213
  if (!result || !result.startTime) {
8101
8214
  return undefined;
8102
8215
  }
@@ -8418,6 +8531,7 @@ class StateManager {
8418
8531
  return keys.map((key) => this.filesMap.get(key)).flat().filter((file) => file && !file.local);
8419
8532
  }
8420
8533
  return Array.from(this.filesMap.values()).flat().filter((file) => !file.local).sort((f1, f2) => {
8534
+ // print typecheck files first
8421
8535
  if (f1.meta?.typecheck && f2.meta?.typecheck) {
8422
8536
  return 0;
8423
8537
  }
@@ -8446,6 +8560,8 @@ class StateManager {
8446
8560
  const existing = this.filesMap.get(file.filepath) || [];
8447
8561
  const otherFiles = existing.filter((i) => i.projectName !== file.projectName || i.meta.typecheck !== file.meta.typecheck);
8448
8562
  const currentFile = existing.find((i) => i.projectName === file.projectName);
8563
+ // keep logs for the previous file because it should always be initiated before the collections phase
8564
+ // which means that all logs are collected during the collection and not inside tests
8449
8565
  if (currentFile) {
8450
8566
  file.logs = currentFile.logs;
8451
8567
  }
@@ -8466,6 +8582,7 @@ class StateManager {
8466
8582
  return;
8467
8583
  }
8468
8584
  const filtered = files.filter((file) => file.projectName !== project.config.name);
8585
+ // always keep a File task, so we can associate logs with it
8469
8586
  if (!filtered.length) {
8470
8587
  this.filesMap.set(path, [fileTask]);
8471
8588
  } else {
@@ -8500,6 +8617,7 @@ class StateManager {
8500
8617
  if (task) {
8501
8618
  task.result = result;
8502
8619
  task.meta = meta;
8620
+ // skipped with new PendingError
8503
8621
  if (result?.state === "skip") {
8504
8622
  task.mode = "skip";
8505
8623
  }
@@ -8552,6 +8670,9 @@ class TestRun {
8552
8670
  }
8553
8671
  async updated(update, events) {
8554
8672
  this.vitest.state.updateTasks(update);
8673
+ // TODO: what is the order or reports here?
8674
+ // "onTaskUpdate" in parallel with others or before all or after all?
8675
+ // TODO: error handling - what happens if custom reporter throws an error?
8555
8676
  await this.vitest.report("onTaskUpdate", update);
8556
8677
  for (const [id, event] of events) {
8557
8678
  await this.reportEvent(id, event).catch((error) => {
@@ -8560,6 +8681,7 @@ class TestRun {
8560
8681
  }
8561
8682
  }
8562
8683
  async end(specifications, errors, coverage) {
8684
+ // specification won't have the File task if they were filtered by the --shard command
8563
8685
  const modules = specifications.map((spec) => spec.testModule).filter((s) => s != null);
8564
8686
  const files = modules.map((m) => m.task);
8565
8687
  const state = this.vitest.isCancelling ? "interrupted" : process.exitCode ? "failed" : "passed";
@@ -8584,6 +8706,9 @@ class TestRun {
8584
8706
  if (event === "suite-finished") {
8585
8707
  assert$1(entity.type === "suite" || entity.type === "module", "Entity type must be suite or module");
8586
8708
  if (entity.state() === "skipped") {
8709
+ // everything inside suite or a module is skipped,
8710
+ // so we won't get any children events
8711
+ // we need to report everything manually
8587
8712
  await this.reportChildren(entity.children);
8588
8713
  }
8589
8714
  if (entity.type === "module") {
@@ -8738,6 +8863,7 @@ class VitestWatcher {
8738
8863
  this.changedTests.add(id);
8739
8864
  this.scheduleRerun(id);
8740
8865
  } else {
8866
+ // it's possible that file was already there but watcher triggered "add" event instead
8741
8867
  const needsRerun = this.handleFileChanged(id);
8742
8868
  if (needsRerun) {
8743
8869
  this.scheduleRerun(id);
@@ -8760,6 +8886,8 @@ class VitestWatcher {
8760
8886
  return moduleGraph.getModulesByFile(filepath)?.size;
8761
8887
  });
8762
8888
  if (!projects.length) {
8889
+ // if there are no modules it's possible that server was restarted
8890
+ // we don't have information about importers anymore, so let's check if the file is a test file at least
8763
8891
  if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath))) {
8764
8892
  this.changedTests.add(filepath);
8765
8893
  return true;
@@ -8773,6 +8901,7 @@ class VitestWatcher {
8773
8901
  continue;
8774
8902
  }
8775
8903
  this.invalidates.add(filepath);
8904
+ // one of test files that we already run, or one of test files that we can run
8776
8905
  if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
8777
8906
  this.changedTests.add(filepath);
8778
8907
  files.push(filepath);
@@ -8844,7 +8973,7 @@ class Vitest {
8844
8973
  resolvedProjects = [];
8845
8974
  /** @internal */ _browserLastPort = defaultBrowserPort;
8846
8975
  /** @internal */ _browserSessions = new BrowserSessions();
8847
- /** @internal */ _options = {};
8976
+ /** @internal */ _cliOptions = {};
8848
8977
  /** @internal */ reporters = [];
8849
8978
  /** @internal */ vitenode = undefined;
8850
8979
  /** @internal */ runner = undefined;
@@ -8860,8 +8989,9 @@ class Vitest {
8860
8989
  _cache;
8861
8990
  _snapshot;
8862
8991
  _workspaceConfigPath;
8863
- constructor(mode, options = {}) {
8992
+ constructor(mode, cliOptions, options = {}) {
8864
8993
  this.mode = mode;
8994
+ this._cliOptions = cliOptions;
8865
8995
  this.logger = new Logger(this, options.stdout, options.stderr);
8866
8996
  this.packageInstaller = options.packageInstaller || new VitestPackageInstaller();
8867
8997
  this.specifications = new VitestSpecifications(this);
@@ -8922,12 +9052,11 @@ class Vitest {
8922
9052
  return this._cache;
8923
9053
  }
8924
9054
  /** @deprecated internal */
8925
- setServer(options, server, cliOptions) {
8926
- return this._setServer(options, server, cliOptions);
9055
+ setServer(options, server) {
9056
+ return this._setServer(options, server);
8927
9057
  }
8928
9058
  /** @internal */
8929
- async _setServer(options, server, cliOptions) {
8930
- this._options = options;
9059
+ async _setServer(options, server) {
8931
9060
  this.watcher.unregisterWatcher();
8932
9061
  clearTimeout(this._rerunTimer);
8933
9062
  this.restartsCount += 1;
@@ -8966,6 +9095,7 @@ class Vitest {
8966
9095
  }
8967
9096
  });
8968
9097
  if (this.config.watch) {
9098
+ // hijack server restart
8969
9099
  const serverRestart = server.restart;
8970
9100
  server.restart = async (...args) => {
8971
9101
  await Promise.all(this._onRestartListeners.map((fn) => fn()));
@@ -8973,6 +9103,7 @@ class Vitest {
8973
9103
  await this.close();
8974
9104
  await serverRestart(...args);
8975
9105
  };
9106
+ // since we set `server.hmr: false`, Vite does not auto restart itself
8976
9107
  server.watcher.on("change", async (file) => {
8977
9108
  file = normalize(file);
8978
9109
  const isConfig = file === server.config.configFile || this.projects.some((p) => p.vite.config.configFile === file) || file === this._workspaceConfigPath;
@@ -8988,7 +9119,7 @@ class Vitest {
8988
9119
  try {
8989
9120
  await this.cache.results.readFromCache();
8990
9121
  } catch {}
8991
- const projects = await this.resolveProjects(cliOptions);
9122
+ const projects = await this.resolveProjects(this._cliOptions);
8992
9123
  this.resolvedProjects = projects;
8993
9124
  this.projects = projects;
8994
9125
  await Promise.all(projects.flatMap((project) => {
@@ -8999,7 +9130,7 @@ class Vitest {
8999
9130
  injectTestProjects: this.injectTestProject
9000
9131
  }));
9001
9132
  }));
9002
- if (options.browser?.enabled) {
9133
+ if (this._cliOptions.browser?.enabled) {
9003
9134
  const browserProjects = this.projects.filter((p) => p.config.browser.enabled);
9004
9135
  if (!browserProjects.length) {
9005
9136
  throw new Error(`Vitest received --browser flag, but no project had a browser configuration.`);
@@ -9029,7 +9160,7 @@ class Vitest {
9029
9160
  */
9030
9161
  injectTestProject = async (config) => {
9031
9162
  const currentNames = new Set(this.projects.map((p) => p.name));
9032
- const projects = await resolveProjects(this, this._options, undefined, Array.isArray(config) ? config : [config], currentNames);
9163
+ const projects = await resolveProjects(this, this._cliOptions, undefined, Array.isArray(config) ? config : [config], currentNames);
9033
9164
  this.projects.push(...projects);
9034
9165
  return projects;
9035
9166
  };
@@ -9116,7 +9247,10 @@ class Vitest {
9116
9247
  }
9117
9248
  const workspaceConfigPath = await this.resolveWorkspaceConfigPath();
9118
9249
  this._workspaceConfigPath = workspaceConfigPath;
9250
+ // user doesn't have a workspace config, return default project
9119
9251
  if (!workspaceConfigPath) {
9252
+ // user can filter projects with --project flag, `getDefaultTestProject`
9253
+ // returns the project only if it matches the filter
9120
9254
  const project = getDefaultTestProject(this);
9121
9255
  if (!project) {
9122
9256
  return [];
@@ -9207,6 +9341,7 @@ class Vitest {
9207
9341
  }
9208
9342
  async collect(filters) {
9209
9343
  const files = await this.specifications.getRelevantTestSpecifications(filters);
9344
+ // if run with --changed, don't exit if no tests are found
9210
9345
  if (!files.length) {
9211
9346
  return {
9212
9347
  testModules: [],
@@ -9243,15 +9378,18 @@ class Vitest {
9243
9378
  }
9244
9379
  this.filenamePattern = filters && filters?.length > 0 ? filters : undefined;
9245
9380
  const files = await this.specifications.getRelevantTestSpecifications(filters);
9381
+ // if run with --changed, don't exit if no tests are found
9246
9382
  if (!files.length) {
9247
9383
  const throwAnError = !this.config.watch || !(this.config.changed || this.config.related?.length);
9248
9384
  await this._testRun.start([]);
9249
9385
  const coverage = await this.coverageProvider?.generateCoverage?.({ allTestsRun: true });
9386
+ // set exit code before calling `onTestRunEnd` so the lifecycle is consistent
9250
9387
  if (throwAnError) {
9251
9388
  const exitCode = this.config.passWithNoTests ? 0 : 1;
9252
9389
  process.exitCode = exitCode;
9253
9390
  }
9254
9391
  await this._testRun.end([], [], coverage);
9392
+ // Report coverage for uncovered files
9255
9393
  await this.reportCoverage(coverage, true);
9256
9394
  if (throwAnError) {
9257
9395
  throw new FilesNotFoundError(this.mode);
@@ -9262,6 +9400,7 @@ class Vitest {
9262
9400
  unhandledErrors: []
9263
9401
  };
9264
9402
  if (files.length) {
9403
+ // populate once, update cache on watch
9265
9404
  await this.cache.stats.populateStats(this.config.root, files);
9266
9405
  testModules = await this.runFiles(files, true);
9267
9406
  }
@@ -9281,6 +9420,7 @@ class Vitest {
9281
9420
  } finally {
9282
9421
  await this.report("onInit", this);
9283
9422
  }
9423
+ // populate test files cache so watch mode can trigger a file rerun
9284
9424
  await this.globTestSpecifications();
9285
9425
  if (this.config.watch) {
9286
9426
  await this.report("onWatcherStart");
@@ -9335,9 +9475,11 @@ class Vitest {
9335
9475
  }
9336
9476
  async runFiles(specs, allTestsRun) {
9337
9477
  await this._testRun.start(specs);
9478
+ // previous run
9338
9479
  await this.runningPromise;
9339
9480
  this._onCancelListeners = [];
9340
9481
  this.isCancelling = false;
9482
+ // schedule the new run
9341
9483
  this.runningPromise = (async () => {
9342
9484
  try {
9343
9485
  if (!this.pool) {
@@ -9369,6 +9511,7 @@ class Vitest {
9369
9511
  unhandledErrors: this.state.getUnhandledErrors()
9370
9512
  };
9371
9513
  } finally {
9514
+ // TODO: wait for coverage only if `onFinished` is defined
9372
9515
  const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun });
9373
9516
  const errors = this.state.getUnhandledErrors();
9374
9517
  this._checkUnhandledErrors(errors);
@@ -9378,6 +9521,7 @@ class Vitest {
9378
9521
  })().finally(() => {
9379
9522
  this.runningPromise = undefined;
9380
9523
  this.isFirstRun = false;
9524
+ // all subsequent runs will treat this as a fresh run
9381
9525
  this.config.changed = false;
9382
9526
  this.config.related = undefined;
9383
9527
  });
@@ -9390,9 +9534,11 @@ class Vitest {
9390
9534
  async collectTests(specifications) {
9391
9535
  const filepaths = specifications.map((spec) => spec.moduleId);
9392
9536
  this.state.collectPaths(filepaths);
9537
+ // previous run
9393
9538
  await this.runningPromise;
9394
9539
  this._onCancelListeners = [];
9395
9540
  this.isCancelling = false;
9541
+ // schedule the new run
9396
9542
  this.runningPromise = (async () => {
9397
9543
  if (!this.pool) {
9398
9544
  this.pool = createPool(this);
@@ -9408,6 +9554,8 @@ class Vitest {
9408
9554
  this.state.catchError(err, "Unhandled Error");
9409
9555
  }
9410
9556
  const files = this.state.getFiles();
9557
+ // can only happen if there was a syntax error in describe block
9558
+ // or there was an error importing a file
9411
9559
  if (hasFailed(files)) {
9412
9560
  process.exitCode = 1;
9413
9561
  }
@@ -9417,6 +9565,7 @@ class Vitest {
9417
9565
  };
9418
9566
  })().finally(() => {
9419
9567
  this.runningPromise = undefined;
9568
+ // all subsequent runs will treat this as a fresh run
9420
9569
  this.config.changed = false;
9421
9570
  this.config.related = undefined;
9422
9571
  });
@@ -9479,11 +9628,13 @@ class Vitest {
9479
9628
  }
9480
9629
  /** @internal */
9481
9630
  async changeNamePattern(pattern, files = this.state.getFilepaths(), trigger) {
9631
+ // Empty test name pattern should reset filename pattern as well
9482
9632
  if (pattern === "") {
9483
9633
  this.filenamePattern = undefined;
9484
9634
  }
9485
9635
  const testNamePattern = pattern ? new RegExp(pattern) : undefined;
9486
9636
  this.configOverride.testNamePattern = testNamePattern;
9637
+ // filter only test files that have tests matching the pattern
9487
9638
  if (testNamePattern) {
9488
9639
  files = files.filter((filepath) => {
9489
9640
  const files = this.state.getFiles([filepath]);
@@ -9510,6 +9661,7 @@ class Vitest {
9510
9661
  * @param files The list of files on the file system
9511
9662
  */
9512
9663
  async updateSnapshot(files) {
9664
+ // default to failed files
9513
9665
  files = files || [...this.state.getFailedFilepaths(), ...this.snapshot.summary.uncheckedKeysByFile.map((s) => s.filePath)];
9514
9666
  this.enableSnapshotUpdate();
9515
9667
  try {
@@ -9557,11 +9709,13 @@ class Vitest {
9557
9709
  this.configOverride.testNamePattern = undefined;
9558
9710
  }
9559
9711
  _rerunTimer;
9712
+ // we can't use a single `triggerId` yet because vscode extension relies on this
9560
9713
  async scheduleRerun(triggerId) {
9561
9714
  const currentCount = this.restartsCount;
9562
9715
  clearTimeout(this._rerunTimer);
9563
9716
  await this.runningPromise;
9564
9717
  clearTimeout(this._rerunTimer);
9718
+ // server restarted
9565
9719
  if (this.restartsCount !== currentCount) {
9566
9720
  return;
9567
9721
  }
@@ -9570,6 +9724,7 @@ class Vitest {
9570
9724
  this.watcher.invalidates.clear();
9571
9725
  return;
9572
9726
  }
9727
+ // server restarted
9573
9728
  if (this.restartsCount !== currentCount) {
9574
9729
  return;
9575
9730
  }
@@ -9579,6 +9734,7 @@ class Vitest {
9579
9734
  if (this.filenamePattern) {
9580
9735
  const filteredFiles = await this.globTestSpecifications(this.filenamePattern);
9581
9736
  files = files.filter((file) => filteredFiles.some((f) => f.moduleId === file));
9737
+ // A file that does not match the current filename pattern was changed
9582
9738
  if (files.length === 0) {
9583
9739
  return;
9584
9740
  }
@@ -9586,6 +9742,7 @@ class Vitest {
9586
9742
  this.watcher.changedTests.clear();
9587
9743
  const triggerIds = new Set(triggerId.map((id) => relative(this.config.root, id)));
9588
9744
  const triggerLabel = Array.from(triggerIds).join(", ");
9745
+ // get file specifications and filter them if needed
9589
9746
  const specifications = files.flatMap((file) => this.getModuleSpecifications(file)).filter((specification) => {
9590
9747
  if (this._onFilterWatchedSpecification.length === 0) {
9591
9748
  return true;
@@ -9629,6 +9786,7 @@ class Vitest {
9629
9786
  }
9630
9787
  if (this.coverageProvider) {
9631
9788
  await this.coverageProvider.reportCoverage(coverage, { allTestsRun });
9789
+ // notify coverage iframe reload
9632
9790
  for (const reporter of this.reporters) {
9633
9791
  if (reporter instanceof WebSocketReporter) {
9634
9792
  reporter.onFinishedReportCoverage();
@@ -9647,10 +9805,13 @@ class Vitest {
9647
9805
  if (this.coreWorkspaceProject && !teardownProjects.includes(this.coreWorkspaceProject)) {
9648
9806
  teardownProjects.push(this.coreWorkspaceProject);
9649
9807
  }
9808
+ // do teardown before closing the server
9650
9809
  for (const project of teardownProjects.reverse()) {
9651
9810
  await project._teardownGlobalSetup();
9652
9811
  }
9653
9812
  const closePromises = this.projects.map((w) => w.close());
9813
+ // close the core workspace server only once
9814
+ // it's possible that it's not initialized at all because it's not running any tests
9654
9815
  if (this.coreWorkspaceProject && !this.projects.includes(this.coreWorkspaceProject)) {
9655
9816
  closePromises.push(this.coreWorkspaceProject.close().then(() => this._vite = undefined));
9656
9817
  }
@@ -9707,7 +9868,7 @@ class Vitest {
9707
9868
  await Promise.all(this.reporters.map((r) => r[name]?.(
9708
9869
  // @ts-expect-error let me go
9709
9870
  ...args
9710
- )));
9871
+ )));
9711
9872
  }
9712
9873
  /** @internal */
9713
9874
  async _globTestFilepaths() {
@@ -9730,6 +9891,7 @@ class Vitest {
9730
9891
  getModuleProjects(filepath) {
9731
9892
  return this.projects.filter((project) => {
9732
9893
  return project.getModulesByFilepath(filepath).size;
9894
+ // TODO: reevaluate || project.browser?.moduleGraph.getModulesByFile(id)?.size
9733
9895
  });
9734
9896
  }
9735
9897
  /**
@@ -9780,7 +9942,8 @@ class Vitest {
9780
9942
  * Check if the project with a given name should be included.
9781
9943
  */
9782
9944
  matchesProjectFilter(name) {
9783
- const projects = this._config?.project || this._options?.project;
9945
+ const projects = this._config?.project || this._cliOptions?.project;
9946
+ // no filters applied, any project can be included
9784
9947
  if (!projects || !projects.length) {
9785
9948
  return true;
9786
9949
  }
@@ -9796,11 +9959,11 @@ function assert(condition, property, name = property) {
9796
9959
  }
9797
9960
  }
9798
9961
 
9799
- async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9962
+ async function VitestPlugin(options = {}, vitest = new Vitest("test", deepClone(options))) {
9800
9963
  const userConfig = deepMerge({}, options);
9801
9964
  async function UIPlugin() {
9802
- await ctx.packageInstaller.ensureInstalled("@vitest/ui", options.root || process.cwd(), ctx.version);
9803
- return (await import('@vitest/ui')).default(ctx);
9965
+ await vitest.packageInstaller.ensureInstalled("@vitest/ui", options.root || process.cwd(), vitest.version);
9966
+ return (await import('@vitest/ui')).default(vitest);
9804
9967
  }
9805
9968
  return [
9806
9969
  {
@@ -9811,10 +9974,17 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9811
9974
  },
9812
9975
  async config(viteConfig) {
9813
9976
  if (options.watch) {
9977
+ // Earlier runs have overwritten values of the `options`.
9978
+ // Reset it back to initial user config before setting up the server again.
9814
9979
  options = deepMerge({}, userConfig);
9815
9980
  }
9981
+ // preliminary merge of options to be able to create server options for vite
9982
+ // however to allow vitest plugins to modify vitest config values
9983
+ // this is repeated in configResolved where the config is final
9816
9984
  const testConfig = deepMerge({}, configDefaults, removeUndefinedValues(viteConfig.test ?? {}), options);
9817
9985
  testConfig.api = resolveApiServerConfig(testConfig, defaultPort);
9986
+ // store defines for globalThis to make them
9987
+ // reassignable when running in worker in src/runtime/setup.ts
9818
9988
  const defines = deleteDefineConfig(viteConfig);
9819
9989
  options.defines = defines;
9820
9990
  let open = false;
@@ -9856,11 +10026,13 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9856
10026
  deps: testConfig.deps ?? viteConfig.test?.deps
9857
10027
  }
9858
10028
  };
9859
- if (ctx.configOverride.project) {
9860
- options.project = ctx.configOverride.project;
10029
+ if (vitest.configOverride.project) {
10030
+ // project filter was set by the user, so we need to filter the project
10031
+ options.project = vitest.configOverride.project;
9861
10032
  }
9862
- config.customLogger = createViteLogger(ctx.logger, viteConfig.logLevel || "warn", { allowClearScreen: false });
10033
+ config.customLogger = createViteLogger(vitest.logger, viteConfig.logLevel || "warn", { allowClearScreen: false });
9863
10034
  config.customLogger = silenceImportViteIgnoreWarning(config.customLogger);
10035
+ // we want inline dependencies to be resolved by analyser plugin so module graph is populated correctly
9864
10036
  if (viteConfig.ssr?.noExternal !== true) {
9865
10037
  const inline = testConfig.server?.deps?.inline;
9866
10038
  if (inline === true) {
@@ -9868,13 +10040,17 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9868
10040
  } else {
9869
10041
  const noExternal = viteConfig.ssr?.noExternal;
9870
10042
  const noExternalArray = typeof noExternal !== "undefined" ? toArray(noExternal) : undefined;
10043
+ // filter the same packages
9871
10044
  const uniqueInline = inline && noExternalArray ? inline.filter((dep) => !noExternalArray.includes(dep)) : inline;
9872
10045
  config.ssr = { noExternal: uniqueInline };
9873
10046
  }
9874
10047
  }
10048
+ // chokidar fsevents is unstable on macos when emitting "ready" event
9875
10049
  if (process.platform === "darwin" && process.env.VITE_TEST_WATCHER_DEBUG) {
9876
10050
  const watch = config.server.watch;
9877
10051
  if (watch) {
10052
+ // eslint-disable-next-line ts/ban-ts-comment
10053
+ // @ts-ignore Vite 6 compat
9878
10054
  watch.useFsEvents = false;
9879
10055
  watch.usePolling = false;
9880
10056
  }
@@ -9885,7 +10061,7 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9885
10061
  config.css.modules ??= {};
9886
10062
  if (config.css.modules) {
9887
10063
  config.css.modules.generateScopedName = (name, filename) => {
9888
- const root = ctx.config.root || options.root || process.cwd();
10064
+ const root = vitest.config.root || options.root || process.cwd();
9889
10065
  return generateScopedClassName(classNameStrategy, name, relative(root, filename));
9890
10066
  };
9891
10067
  }
@@ -9900,14 +10076,20 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9900
10076
  if ("alias" in viteConfigTest) {
9901
10077
  delete viteConfigTest.alias;
9902
10078
  }
10079
+ // viteConfig.test is final now, merge it for real
9903
10080
  options = deepMerge({}, configDefaults, viteConfigTest, options);
9904
10081
  options.api = resolveApiServerConfig(options, defaultPort);
10082
+ // we replace every "import.meta.env" with "process.env"
10083
+ // to allow reassigning, so we need to put all envs on process.env
9905
10084
  const { PROD, DEV,...envs } = viteConfig.env;
10085
+ // process.env can have only string values and will cast string on it if we pass other type,
10086
+ // so we are making them truthy
9906
10087
  process.env.PROD ??= PROD ? "1" : "";
9907
10088
  process.env.DEV ??= DEV ? "1" : "";
9908
10089
  for (const name in envs) {
9909
10090
  process.env[name] ??= envs[name];
9910
10091
  }
10092
+ // don't watch files in run mode
9911
10093
  if (!options.watch) {
9912
10094
  viteConfig.server.watch = null;
9913
10095
  }
@@ -9917,7 +10099,7 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9917
10099
  configurable: true
9918
10100
  });
9919
10101
  const originalName = options.name;
9920
- if (options.browser?.enabled && options.browser?.instances) {
10102
+ if (options.browser?.instances) {
9921
10103
  options.browser.instances.forEach((instance) => {
9922
10104
  instance.name ??= originalName ? `${originalName} (${instance.browser})` : instance.browser;
9923
10105
  });
@@ -9928,13 +10110,15 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9928
10110
  async handler(server) {
9929
10111
  if (options.watch && process.env.VITE_TEST_WATCHER_DEBUG) {
9930
10112
  server.watcher.on("ready", () => {
10113
+ // eslint-disable-next-line no-console
9931
10114
  console.log("[debug] watcher is ready");
9932
10115
  });
9933
10116
  }
9934
- await ctx._setServer(options, server, userConfig);
10117
+ await vitest._setServer(options, server);
9935
10118
  if (options.api && options.watch) {
9936
- (await Promise.resolve().then(function () { return setup$1; })).setup(ctx);
10119
+ (await Promise.resolve().then(function () { return setup$1; })).setup(vitest);
9937
10120
  }
10121
+ // #415, in run mode we don't need the watcher, close it would improve the performance
9938
10122
  if (!options.watch) {
9939
10123
  await server.watcher.close();
9940
10124
  }
@@ -9942,9 +10126,9 @@ async function VitestPlugin(options = {}, ctx = new Vitest("test")) {
9942
10126
  }
9943
10127
  },
9944
10128
  SsrReplacerPlugin(),
9945
- ...CSSEnablerPlugin(ctx),
9946
- CoverageTransform(ctx),
9947
- VitestCoreResolver(ctx),
10129
+ ...CSSEnablerPlugin(vitest),
10130
+ CoverageTransform(vitest),
10131
+ VitestCoreResolver(vitest),
9948
10132
  options.ui ? await UIPlugin() : null,
9949
10133
  ...MocksPlugins(),
9950
10134
  VitestOptimizer(),
@@ -9961,15 +10145,16 @@ function removeUndefinedValues(obj) {
9961
10145
  }
9962
10146
 
9963
10147
  async function createVitest(mode, options, viteOverrides = {}, vitestOptions = {}) {
9964
- const ctx = new Vitest(mode, vitestOptions);
10148
+ const ctx = new Vitest(mode, deepClone(options), vitestOptions);
9965
10149
  const root = slash(resolve$1(options.root || process.cwd()));
9966
10150
  const configPath = options.config === false ? false : options.config ? resolve$1(root, options.config) : await findUp(configFiles, { cwd: root });
9967
10151
  options.config = configPath;
10152
+ const { browser: _removeBrowser,...restOptions } = options;
9968
10153
  const config = {
9969
10154
  configFile: configPath,
9970
10155
  configLoader: options.configLoader,
9971
10156
  mode: options.mode || mode,
9972
- plugins: await VitestPlugin(options, ctx)
10157
+ plugins: await VitestPlugin(restOptions, ctx)
9973
10158
  };
9974
10159
  const server = await createViteServer(mergeConfig(config, mergeConfig(viteOverrides, { root: options.root })));
9975
10160
  if (ctx.config.api?.port) {
@@ -10100,6 +10285,7 @@ class WatchFilter {
10100
10285
  const lines = str.split(/\r?\n/);
10101
10286
  for (const line of lines) {
10102
10287
  const columns = "columns" in this.stdout ? this.stdout.columns : 80;
10288
+ // We have to take care of screen width in case of long lines
10103
10289
  rows += 1 + Math.floor(Math.max(stripVTControlCharacters(line).length - 1, 0) / columns);
10104
10290
  }
10105
10291
  this.write(`${ESC}1G`);
@@ -10154,6 +10340,8 @@ ${keys.map((i) => c.dim(" press ") + c.reset([i[0]].flat().map(c.bold).join(",
10154
10340
  function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10155
10341
  let latestFilename = "";
10156
10342
  async function _keypressHandler(str, key) {
10343
+ // Cancel run and exit when ctrl-c or esc is pressed.
10344
+ // If cancelling takes long and key is pressed multiple times, exit forcefully.
10157
10345
  if (str === "" || str === "\x1B" || key && key.ctrl && key.name === "c") {
10158
10346
  if (!ctx.isCancelling) {
10159
10347
  ctx.logger.log(c.red("Cancelling test run. Press CTRL+c again to exit forcefully.\n"));
@@ -10162,6 +10350,7 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10162
10350
  }
10163
10351
  return ctx.exit(true);
10164
10352
  }
10353
+ // window not support suspend
10165
10354
  if (!isWindows && key && key.ctrl && key.name === "z") {
10166
10355
  process.kill(process.ppid, "SIGTSTP");
10167
10356
  process.kill(process.pid, "SIGTSTP");
@@ -10174,31 +10363,40 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10174
10363
  }
10175
10364
  return;
10176
10365
  }
10366
+ // quit
10177
10367
  if (name === "q") {
10178
10368
  return ctx.exit(true);
10179
10369
  }
10370
+ // help
10180
10371
  if (name === "h") {
10181
10372
  return printShortcutsHelp();
10182
10373
  }
10374
+ // update snapshot
10183
10375
  if (name === "u") {
10184
10376
  return ctx.updateSnapshot();
10185
10377
  }
10378
+ // rerun all tests
10186
10379
  if (name === "a" || name === "return") {
10187
10380
  const files = await ctx._globTestFilepaths();
10188
10381
  return ctx.changeNamePattern("", files, "rerun all tests");
10189
10382
  }
10383
+ // rerun current pattern tests
10190
10384
  if (name === "r") {
10191
10385
  return ctx.rerunFiles();
10192
10386
  }
10387
+ // rerun only failed tests
10193
10388
  if (name === "f") {
10194
10389
  return ctx.rerunFailed();
10195
10390
  }
10391
+ // change project filter
10196
10392
  if (name === "w") {
10197
10393
  return inputProjectName();
10198
10394
  }
10395
+ // change testNamePattern
10199
10396
  if (name === "t") {
10200
10397
  return inputNamePattern();
10201
10398
  }
10399
+ // change fileNamePattern
10202
10400
  if (name === "p") {
10203
10401
  return inputFilePattern();
10204
10402
  }
@@ -10224,6 +10422,7 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10224
10422
  const reg = new RegExp(str);
10225
10423
  return tests.map((test) => test.name).filter((testName) => testName.match(reg));
10226
10424
  } catch {
10425
+ // `new RegExp` may throw error when input is invalid regexp
10227
10426
  return [];
10228
10427
  }
10229
10428
  });
@@ -10232,6 +10431,7 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout) {
10232
10431
  return;
10233
10432
  }
10234
10433
  const files = ctx.state.getFilepaths();
10434
+ // if running in standalone mode, Vitest instance doesn't know about any test file
10235
10435
  const cliFiles = ctx.config.standalone && !files.length ? await ctx._globTestFilepaths() : undefined;
10236
10436
  await ctx.changeNamePattern(filter?.trim() || "", cliFiles, "change pattern");
10237
10437
  }
@@ -10361,6 +10561,7 @@ async function prepareVitest(mode, options = {}, viteOverrides, vitestOptions) {
10361
10561
  if (options.run) {
10362
10562
  options.watch = false;
10363
10563
  }
10564
+ // this shouldn't affect _application root_ that can be changed inside config
10364
10565
  const root = resolve(options.root || process.cwd());
10365
10566
  const ctx = await createVitest(mode, options, viteOverrides, vitestOptions);
10366
10567
  const environmentPackage = getEnvPackageName(ctx.config.environment);