vitest 3.0.0-beta.2 → 3.0.0-beta.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE.md +1 -315
  2. package/config.d.ts +2 -0
  3. package/dist/browser.d.ts +3 -3
  4. package/dist/browser.js +1 -1
  5. package/dist/chunks/{RandomSequencer.gisBJ77r.js → RandomSequencer.DB__To1b.js} +38 -8
  6. package/dist/chunks/{base.CUgXReRN.js → base.BJ8KO-VX.js} +2 -2
  7. package/dist/chunks/{cac.Xzv7eNWw.js → cac.BAYqQ2aM.js} +21 -10
  8. package/dist/chunks/{cli-api.CETCDGgZ.js → cli-api.Dhl34Trr.js} +263 -58
  9. package/dist/chunks/{config.BTPBhmK5.d.ts → config.BRtC-JeT.d.ts} +6 -0
  10. package/dist/chunks/{console.BYGVloWk.js → console.CN7AiMGV.js} +16 -7
  11. package/dist/chunks/{creator.DcAcUhMD.js → creator.Ot9GlSGw.js} +16 -14
  12. package/dist/chunks/{execute.2pr0rHgK.js → execute.BMOaRArH.js} +27 -16
  13. package/dist/chunks/{globals.BFncSRNA.js → globals.C5RQxaV3.js} +2 -2
  14. package/dist/chunks/{index.DoV7W5gc.js → index.B2M9nD1V.js} +6 -1
  15. package/dist/chunks/{index.CkWmZCXU.js → index.BQbxGbG9.js} +1 -1
  16. package/dist/chunks/index.CAueP3cK.js +3205 -0
  17. package/dist/chunks/{reporters.DTtxC3KQ.d.ts → reporters.Dcdq51WE.d.ts} +211 -257
  18. package/dist/chunks/{resolveConfig.BA-_OKEx.js → resolveConfig.kZFMjKCQ.js} +28 -6
  19. package/dist/chunks/{runBaseTests.D0dWpzZV.js → runBaseTests.URiUrnWK.js} +8 -6
  20. package/dist/chunks/{setup-common.Cp_bu5q3.js → setup-common.D0zLenuv.js} +1 -1
  21. package/dist/chunks/{utils.CMUTX-p8.js → utils.yHKcm4dz.js} +10 -21
  22. package/dist/chunks/{vi.S4Fq8wSo.js → vi.Da_PT3Vw.js} +554 -272
  23. package/dist/chunks/{vite.CXaetSK3.d.ts → vite.DzluO1Kj.d.ts} +1 -1
  24. package/dist/chunks/{vm.DGhTouO3.js → vm.DrFVeTXo.js} +4 -4
  25. package/dist/chunks/{worker.ClntunZp.d.ts → worker.BIVMnzXw.d.ts} +1 -1
  26. package/dist/chunks/{worker.o1PBoDdo.d.ts → worker.Hz_LAzfd.d.ts} +2 -1
  27. package/dist/cli.js +1 -1
  28. package/dist/config.cjs +1 -0
  29. package/dist/config.d.ts +4 -4
  30. package/dist/config.js +1 -0
  31. package/dist/coverage.d.ts +2 -2
  32. package/dist/coverage.js +33 -8
  33. package/dist/execute.d.ts +3 -3
  34. package/dist/execute.js +1 -1
  35. package/dist/index.d.ts +7 -7
  36. package/dist/index.js +2 -2
  37. package/dist/node.d.ts +6 -6
  38. package/dist/node.js +10 -11
  39. package/dist/reporters.d.ts +2 -2
  40. package/dist/reporters.js +3 -7
  41. package/dist/runners.d.ts +1 -1
  42. package/dist/runners.js +5 -12
  43. package/dist/workers/forks.js +2 -2
  44. package/dist/workers/runVmTests.js +7 -5
  45. package/dist/workers/threads.js +2 -2
  46. package/dist/workers/vmForks.js +3 -3
  47. package/dist/workers/vmThreads.js +3 -3
  48. package/dist/workers.d.ts +3 -3
  49. package/dist/workers.js +4 -4
  50. package/package.json +17 -19
  51. package/dist/chunks/index.9ZEBV_TJ.js +0 -5442
@@ -2,7 +2,7 @@ import { existsSync, promises, readFileSync, mkdirSync, writeFileSync } from 'no
2
2
  import { normalize, relative, dirname, resolve, join, basename, isAbsolute } from 'pathe';
3
3
  import { g as getCoverageProvider, C as CoverageProviderMap } from './coverage.BWeNbfBa.js';
4
4
  import a, { resolve as resolve$1 } from 'node:path';
5
- import { noop, isPrimitive, toArray, deepMerge, nanoid, slash, notNullish, createDefer } from '@vitest/utils';
5
+ import { noop, isPrimitive, createDefer, toArray, deepMerge, nanoid, slash, deepClone, notNullish } from '@vitest/utils';
6
6
  import { f as findUp, p as prompt } from './index.BJDntFik.js';
7
7
  import { searchForWorkspaceRoot, version, createServer, mergeConfig } from 'vite';
8
8
  import { A as API_PATH, c as configFiles, a as defaultBrowserPort, w as workspacesFiles, d as defaultPort } from './constants.fzPh7AOq.js';
@@ -10,9 +10,9 @@ import { createFileTask, limitConcurrency, getTasks, hasFailed, getTests } from
10
10
  import { SnapshotManager } from '@vitest/snapshot/manager';
11
11
  import { ViteNodeRunner } from 'vite-node/client';
12
12
  import { ViteNodeServer } from 'vite-node/server';
13
- import { v as version$1 } from './cac.Xzv7eNWw.js';
13
+ import { v as version$1 } from './cac.BAYqQ2aM.js';
14
14
  import { c as createBirpc } from './index.68735LiX.js';
15
- import { s as stringify, p as parse, i as generateCodeFrame, R as ReportersMap, h as BenchmarkReportsMap, f as TestModule, g as TestSuite, e as TestCase, L as Logger, j as BlobReporter, r as readBlobs } from './index.9ZEBV_TJ.js';
15
+ import { s as stringify, p as parse, i as generateCodeFrame, R as ReportersMap, h as BenchmarkReportsMap, f as TestModule, g as TestSuite, e as TestCase, L as Logger, j as BlobReporter, r as readBlobs } from './index.CAueP3cK.js';
16
16
  import require$$0$2 from 'stream';
17
17
  import require$$0 from 'zlib';
18
18
  import require$$0$1 from 'buffer';
@@ -26,12 +26,11 @@ import require$$7 from 'url';
26
26
  import { g as getDefaultExportFromCjs, c as commonjsGlobal } from './_commonjsHelpers.BFTU3MAI.js';
27
27
  import { parseErrorStacktrace } from '@vitest/utils/source-map';
28
28
  import { distDir, rootDir } from '../path.js';
29
- import { i as isPackageExists, e as requireMicromatch, V as VitestCache, f as configDefaults, g as getFilePoolName, h as isBrowserEnabled, m as mm, a as resolveConfig, j as groupBy, w as wildcardPatternToRegExp, k as createPool, b as resolveApiServerConfig, c as coverageConfigDefaults, s as stdout } from './resolveConfig.BA-_OKEx.js';
29
+ import { i as isPackageExists, e as requireMicromatch, V as VitestCache, f as configDefaults, g as getFilePoolName, h as isBrowserEnabled, m as mm, a as resolveConfig, j as groupBy, w as wildcardPatternToRegExp, k as createPool, b as resolveApiServerConfig, c as coverageConfigDefaults, s as stdout } from './resolveConfig.kZFMjKCQ.js';
30
30
  import { createRequire } from 'node:module';
31
31
  import url from 'node:url';
32
32
  import c from 'tinyrainbow';
33
- import { h as hash, i as isWindows } from './RandomSequencer.gisBJ77r.js';
34
- import { isCI } from 'std-env';
33
+ import { i as isTTY, h as hash, b as isWindows } from './RandomSequencer.DB__To1b.js';
35
34
  import { rm } from 'node:fs/promises';
36
35
  import nodeos__default, { tmpdir } from 'node:os';
37
36
  import require$$0$4 from 'os';
@@ -40,6 +39,7 @@ import require$$0$6 from 'fs';
40
39
  import { normalizeRequestId, cleanUrl } from 'vite-node/utils';
41
40
  import { hoistMocksPlugin, automockPlugin } from '@vitest/mocker/node';
42
41
  import MagicString from 'magic-string';
42
+ import { w as withLabel } from './utils.yHKcm4dz.js';
43
43
  import readline from 'node:readline';
44
44
  import { stripVTControlCharacters } from 'node:util';
45
45
 
@@ -4947,11 +4947,12 @@ function setup(ctx, _server) {
4947
4947
  async getModuleGraph(project, id, browser) {
4948
4948
  return getModuleGraph(ctx, project, id, browser);
4949
4949
  },
4950
- updateSnapshot(file) {
4950
+ async updateSnapshot(file) {
4951
4951
  if (!file) {
4952
- return ctx.updateSnapshot();
4952
+ await ctx.updateSnapshot();
4953
+ } else {
4954
+ await ctx.updateSnapshot([file.filepath]);
4953
4955
  }
4954
- return ctx.updateSnapshot([file.filepath]);
4955
4956
  },
4956
4957
  getUnhandledErrors() {
4957
4958
  return ctx.state.getUnhandledErrors();
@@ -5060,6 +5061,33 @@ var setup$1 = /*#__PURE__*/Object.freeze({
5060
5061
  setup: setup
5061
5062
  });
5062
5063
 
5064
+ class BrowserSessions {
5065
+ sessions = /* @__PURE__ */ new Map();
5066
+ getSession(sessionId) {
5067
+ return this.sessions.get(sessionId);
5068
+ }
5069
+ createAsyncSession(method, sessionId, files, project) {
5070
+ const defer = createDefer();
5071
+ const timeout = setTimeout(() => {
5072
+ defer.reject(new Error(`Failed to connect to the browser session "${sessionId}" within the timeout.`));
5073
+ }, project.vitest.config.browser.connectTimeout ?? 6e4).unref();
5074
+ this.sessions.set(sessionId, {
5075
+ files,
5076
+ method,
5077
+ project,
5078
+ connected: () => {
5079
+ clearTimeout(timeout);
5080
+ },
5081
+ resolve: () => {
5082
+ defer.resolve();
5083
+ this.sessions.delete(sessionId);
5084
+ },
5085
+ reject: defer.reject
5086
+ });
5087
+ return defer;
5088
+ }
5089
+ }
5090
+
5063
5091
  class FilesNotFoundError extends Error {
5064
5092
  code = "VITEST_FILES_NOT_FOUND";
5065
5093
  constructor(mode) {
@@ -5081,7 +5109,7 @@ class LocationFilterFileNotFoundError extends Error {
5081
5109
  class IncludeTaskLocationDisabledError extends Error {
5082
5110
  code = "VITEST_INCLUDE_TASK_LOCATION_DISABLED";
5083
5111
  constructor() {
5084
- super("Recieved line number filters while `includeTaskLocation` option is disabled");
5112
+ super("Received line number filters while `includeTaskLocation` option is disabled");
5085
5113
  }
5086
5114
  }
5087
5115
  class RangeLocationFilterProvidedError extends Error {
@@ -5111,7 +5139,6 @@ class VitestPackageInstaller {
5111
5139
  if (/* @__PURE__ */ isPackageExists(dependency, { paths: [root, __dirname] })) {
5112
5140
  return true;
5113
5141
  }
5114
- const promptInstall = !isCI && process.stdout.isTTY;
5115
5142
  process.stderr.write(
5116
5143
  c.red(
5117
5144
  `${c.inverse(
@@ -5121,11 +5148,11 @@ class VitestPackageInstaller {
5121
5148
  `
5122
5149
  )
5123
5150
  );
5124
- if (!promptInstall) {
5151
+ if (!isTTY) {
5125
5152
  return false;
5126
5153
  }
5127
5154
  const prompts = await import('./index.BJDntFik.js').then(function (n) { return n.i; });
5128
- const { install } = await prompts.prompt({
5155
+ const { install } = await prompts.default({
5129
5156
  type: "confirm",
5130
5157
  name: "install",
5131
5158
  message: c.reset(`Do you want to install ${c.green(dependency)}?`)
@@ -8289,7 +8316,7 @@ function requireOut () {
8289
8316
  }
8290
8317
  win32.convertPathToPattern = convertPathToPattern;
8291
8318
  })(FastGlob.win32 || (FastGlob.win32 = {}));
8292
- })(FastGlob || (FastGlob = {}));
8319
+ })(FastGlob);
8293
8320
  function getWorks(source, _Provider, options) {
8294
8321
  const patterns = [].concat(source);
8295
8322
  const settings = new settings_1.default(options);
@@ -9652,13 +9679,14 @@ class TestProject {
9652
9679
  vitenode;
9653
9680
  /** @internal */
9654
9681
  typechecker;
9682
+ /** @internal */
9683
+ _config;
9655
9684
  runner;
9656
9685
  closingPromise;
9657
9686
  testFilesList = null;
9658
9687
  typecheckFilesList = null;
9659
9688
  _globalSetups;
9660
9689
  _provided = {};
9661
- _config;
9662
9690
  _vite;
9663
9691
  // "provide" is a property, not a method to keep the context when destructed in the global setup,
9664
9692
  // making it a method would be a breaking change, and can be done in Vitest 3 at minimum
@@ -9716,6 +9744,11 @@ class TestProject {
9716
9744
  if (!this._vite) {
9717
9745
  throw new Error("The server was not set. It means that `project.vite` was called before the Vite server was established.");
9718
9746
  }
9747
+ Object.defineProperty(this, "vite", {
9748
+ configurable: true,
9749
+ writable: true,
9750
+ value: this._vite
9751
+ });
9719
9752
  return this._vite;
9720
9753
  }
9721
9754
  /**
@@ -9737,7 +9770,7 @@ class TestProject {
9737
9770
  * Serialized project configuration. This is the config that tests receive.
9738
9771
  */
9739
9772
  get serializedConfig() {
9740
- return this._serializeOverridenConfig();
9773
+ return this._serializeOverriddenConfig();
9741
9774
  }
9742
9775
  /** @deprecated use `vite` instead */
9743
9776
  get server() {
@@ -9881,19 +9914,19 @@ class TestProject {
9881
9914
  * Returns if the file is a test file. Requires `.globTestFiles()` to be called first.
9882
9915
  * @internal
9883
9916
  */
9884
- isCachedTestFile(testPath) {
9917
+ _isCachedTestFile(testPath) {
9885
9918
  return !!this.testFilesList && this.testFilesList.includes(testPath);
9886
9919
  }
9887
9920
  /**
9888
9921
  * Returns if the file is a typecheck test file. Requires `.globTestFiles()` to be called first.
9889
9922
  * @internal
9890
9923
  */
9891
- isCachedTypecheckFile(testPath) {
9924
+ _isCachedTypecheckFile(testPath) {
9892
9925
  return !!this.typecheckFilesList && this.typecheckFilesList.includes(testPath);
9893
9926
  }
9894
9927
  /** @deprecated use `serializedConfig` instead */
9895
9928
  getSerializableConfig() {
9896
- return this._serializeOverridenConfig();
9929
+ return this._serializeOverriddenConfig();
9897
9930
  }
9898
9931
  /** @internal */
9899
9932
  async globFiles(include, exclude, cwd) {
@@ -9909,7 +9942,7 @@ class TestProject {
9909
9942
  * Test if a file matches the test globs. This does the actual glob matching if the test is not cached, unlike `isCachedTestFile`.
9910
9943
  */
9911
9944
  matchesTestGlob(moduleId, source) {
9912
- if (this.isCachedTestFile(moduleId)) {
9945
+ if (this._isCachedTestFile(moduleId)) {
9913
9946
  return true;
9914
9947
  }
9915
9948
  const relativeId = relative(this.config.dir || this.config.root, moduleId);
@@ -9955,11 +9988,19 @@ class TestProject {
9955
9988
  return testFiles;
9956
9989
  }
9957
9990
  /** @internal */
9958
- async _initBrowserServer() {
9959
- if (!this.isBrowserEnabled() || this.browser) {
9991
+ _parentBrowser;
9992
+ /** @internal */
9993
+ _parent;
9994
+ /** @internal */
9995
+ _initParentBrowser = deduped(async () => {
9996
+ if (!this.isBrowserEnabled() || this._parentBrowser) {
9960
9997
  return;
9961
9998
  }
9962
- await this.vitest.packageInstaller.ensureInstalled("@vitest/browser", this.config.root, this.ctx.version);
9999
+ await this.vitest.packageInstaller.ensureInstalled(
10000
+ "@vitest/browser",
10001
+ this.config.root,
10002
+ this.vitest.version
10003
+ );
9963
10004
  const { createBrowserServer, distRoot } = await import('@vitest/browser');
9964
10005
  const browser = await createBrowserServer(
9965
10006
  this,
@@ -9974,13 +10015,20 @@ class TestProject {
9974
10015
  }
9975
10016
  })
9976
10017
  ],
9977
- [CoverageTransform(this.ctx)]
10018
+ [CoverageTransform(this.vitest)]
9978
10019
  );
9979
- this.browser = browser;
10020
+ this._parentBrowser = browser;
9980
10021
  if (this.config.browser.ui) {
9981
10022
  setup(this.vitest, browser.vite);
9982
10023
  }
9983
- }
10024
+ });
10025
+ /** @internal */
10026
+ _initBrowserServer = deduped(async () => {
10027
+ await this._parent?._initParentBrowser();
10028
+ if (!this.browser && this._parent?._parentBrowser) {
10029
+ this.browser = this._parent._parentBrowser.spawn(this);
10030
+ }
10031
+ });
9984
10032
  /**
9985
10033
  * Closes the project and all associated resources. This can only be called once; the closing promise is cached until the server restarts.
9986
10034
  * If the resources are needed again, create a new project.
@@ -10049,7 +10097,7 @@ class TestProject {
10049
10097
  }
10050
10098
  });
10051
10099
  }
10052
- _serializeOverridenConfig() {
10100
+ _serializeOverriddenConfig() {
10053
10101
  const config = serializeConfig(
10054
10102
  this.config,
10055
10103
  this.vitest.config,
@@ -10074,14 +10122,24 @@ class TestProject {
10074
10122
  return this._initBrowserProvider();
10075
10123
  }
10076
10124
  /** @internal */
10077
- async _initBrowserProvider() {
10125
+ _initBrowserProvider = deduped(async () => {
10078
10126
  if (!this.isBrowserEnabled() || this.browser?.provider) {
10079
10127
  return;
10080
10128
  }
10081
10129
  if (!this.browser) {
10082
10130
  await this._initBrowserServer();
10083
10131
  }
10084
- await this.browser?.initBrowserProvider();
10132
+ await this.browser?.initBrowserProvider(this);
10133
+ });
10134
+ /** @internal */
10135
+ _provideObject(context) {
10136
+ for (const _providedKey in context) {
10137
+ const providedKey = _providedKey;
10138
+ this.provide(
10139
+ providedKey,
10140
+ context[providedKey]
10141
+ );
10142
+ }
10085
10143
  }
10086
10144
  /** @internal */
10087
10145
  static _createBasicProject(vitest) {
@@ -10093,15 +10151,34 @@ class TestProject {
10093
10151
  project.runner = vitest.runner;
10094
10152
  project._vite = vitest.server;
10095
10153
  project._config = vitest.config;
10096
- for (const _providedKey in vitest.config.provide) {
10097
- const providedKey = _providedKey;
10098
- project.provide(
10099
- providedKey,
10100
- vitest.config.provide[providedKey]
10101
- );
10102
- }
10154
+ project._provideObject(vitest.config.provide);
10103
10155
  return project;
10104
10156
  }
10157
+ /** @internal */
10158
+ static _cloneBrowserProject(parent, config) {
10159
+ const clone = new TestProject(
10160
+ parent.path,
10161
+ parent.vitest
10162
+ );
10163
+ clone.vitenode = parent.vitenode;
10164
+ clone.runner = parent.runner;
10165
+ clone._vite = parent._vite;
10166
+ clone._config = config;
10167
+ clone._parent = parent;
10168
+ clone._provideObject(config.provide);
10169
+ return clone;
10170
+ }
10171
+ }
10172
+ function deduped(cb) {
10173
+ let _promise;
10174
+ return (...args) => {
10175
+ if (!_promise) {
10176
+ _promise = cb(...args).finally(() => {
10177
+ _promise = void 0;
10178
+ });
10179
+ }
10180
+ return _promise;
10181
+ };
10105
10182
  }
10106
10183
  async function initializeProject(workspacePath, ctx, options) {
10107
10184
  const project = new TestProject(workspacePath, ctx, options);
@@ -10233,10 +10310,10 @@ class VitestSpecifications {
10233
10310
  }
10234
10311
  const specs = [];
10235
10312
  for (const project of this.vitest.projects) {
10236
- if (project.isCachedTestFile(moduleId)) {
10313
+ if (project._isCachedTestFile(moduleId)) {
10237
10314
  specs.push(project.createSpecification(moduleId));
10238
10315
  }
10239
- if (project.isCachedTypecheckFile(moduleId)) {
10316
+ if (project._isCachedTypecheckFile(moduleId)) {
10240
10317
  specs.push(project.createSpecification(moduleId, [], "typescript"));
10241
10318
  }
10242
10319
  }
@@ -10653,7 +10730,7 @@ class VitestWatcher {
10653
10730
  return moduleGraph.getModulesByFile(filepath)?.size;
10654
10731
  });
10655
10732
  if (!projects.length) {
10656
- if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project.isCachedTestFile(filepath))) {
10733
+ if (this.vitest.state.filesMap.has(filepath) || this.vitest.projects.some((project) => project._isCachedTestFile(filepath))) {
10657
10734
  this.changedTests.add(filepath);
10658
10735
  return true;
10659
10736
  }
@@ -10666,7 +10743,7 @@ class VitestWatcher {
10666
10743
  continue;
10667
10744
  }
10668
10745
  this.invalidates.add(filepath);
10669
- if (this.vitest.state.filesMap.has(filepath) || project.isCachedTestFile(filepath)) {
10746
+ if (this.vitest.state.filesMap.has(filepath) || project._isCachedTestFile(filepath)) {
10670
10747
  this.changedTests.add(filepath);
10671
10748
  files.push(filepath);
10672
10749
  continue;
@@ -10787,7 +10864,7 @@ async function resolveWorkspace(vitest, cliOptions, workspaceConfigPath, workspa
10787
10864
  );
10788
10865
  }
10789
10866
  if (!projectPromises.length) {
10790
- return [vitest._ensureRootProject()];
10867
+ return resolveBrowserWorkspace(vitest, /* @__PURE__ */ new Set(), [vitest._ensureRootProject()]);
10791
10868
  }
10792
10869
  const resolvedProjects = await Promise.all(projectPromises);
10793
10870
  const names = /* @__PURE__ */ new Set();
@@ -10811,8 +10888,136 @@ async function resolveWorkspace(vitest, cliOptions, workspaceConfigPath, workspa
10811
10888
  }
10812
10889
  names.add(name);
10813
10890
  }
10891
+ return resolveBrowserWorkspace(vitest, names, resolvedProjects);
10892
+ }
10893
+ async function resolveBrowserWorkspace(vitest, names, resolvedProjects) {
10894
+ const filters = toArray(vitest.config.project).map((s) => wildcardPatternToRegExp(s));
10895
+ const removeProjects = /* @__PURE__ */ new Set();
10896
+ resolvedProjects.forEach((project) => {
10897
+ if (!project.config.browser.enabled) {
10898
+ return;
10899
+ }
10900
+ const configs = project.config.browser.instances || [];
10901
+ if (configs.length === 0) {
10902
+ configs.push({ browser: project.config.browser.name });
10903
+ console.warn(
10904
+ withLabel(
10905
+ "yellow",
10906
+ "Vitest",
10907
+ [
10908
+ `No browser "instances" were defined`,
10909
+ project.name ? ` for the "${project.name}" project. ` : ". ",
10910
+ `Running tests in "${project.config.browser.name}" browser. `,
10911
+ 'The "browser.name" field is deprecated since Vitest 3. ',
10912
+ "Read more: https://vitest.dev/guide/browser/config#browser-instances"
10913
+ ].filter(Boolean).join("")
10914
+ )
10915
+ );
10916
+ }
10917
+ const originalName = project.config.name;
10918
+ const filteredConfigs = !filters.length ? configs : configs.filter((config) => {
10919
+ const browser = config.browser;
10920
+ const newName = config.name || (originalName ? `${originalName} (${browser})` : browser);
10921
+ return filters.some((pattern) => pattern.test(newName));
10922
+ });
10923
+ if (!filteredConfigs.length) {
10924
+ return;
10925
+ }
10926
+ if (project.config.browser.providerOptions) {
10927
+ vitest.logger.warn(
10928
+ withLabel("yellow", "Vitest", `"providerOptions"${originalName ? ` in "${originalName}" project` : ""} is ignored because it's overriden by the configs. To hide this warning, remove the "providerOptions" property from the browser configuration.`)
10929
+ );
10930
+ }
10931
+ filteredConfigs.forEach((config, index) => {
10932
+ const browser = config.browser;
10933
+ if (!browser) {
10934
+ const nth = index + 1;
10935
+ const ending = nth === 2 ? "nd" : nth === 3 ? "rd" : "th";
10936
+ throw new Error(`The browser configuration must have a "browser" property. The ${nth}${ending} item in "browser.instances" doesn't have it. Make sure your${originalName ? ` "${originalName}"` : ""} configuration is correct.`);
10937
+ }
10938
+ const name = config.name;
10939
+ const newName = name || (originalName ? `${originalName} (${browser})` : browser);
10940
+ if (names.has(newName)) {
10941
+ throw new Error(
10942
+ [
10943
+ `Cannot define a nested project for a ${browser} browser. The project name "${newName}" was already defined. `,
10944
+ 'If you have multiple instances for the same browser, make sure to define a custom "name". ',
10945
+ "All projects in a workspace should have unique names. Make sure your configuration is correct."
10946
+ ].join("")
10947
+ );
10948
+ }
10949
+ names.add(newName);
10950
+ const clonedConfig = cloneConfig(project, config);
10951
+ clonedConfig.name = newName;
10952
+ const clone = TestProject._cloneBrowserProject(project, clonedConfig);
10953
+ resolvedProjects.push(clone);
10954
+ });
10955
+ removeProjects.add(project);
10956
+ });
10957
+ resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
10958
+ const headedBrowserProjects = resolvedProjects.filter((project) => {
10959
+ return project.config.browser.enabled && !project.config.browser.headless;
10960
+ });
10961
+ if (headedBrowserProjects.length > 1) {
10962
+ const message = [
10963
+ `Found multiple projects that run browser tests in headed mode: "${headedBrowserProjects.map((p) => p.name).join('", "')}".`,
10964
+ ` Vitest cannot run multiple headed browsers at the same time.`
10965
+ ].join("");
10966
+ if (!isTTY) {
10967
+ throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
10968
+ }
10969
+ const prompts = await import('./index.BJDntFik.js').then(function (n) { return n.i; });
10970
+ const { projectName } = await prompts.default({
10971
+ type: "select",
10972
+ name: "projectName",
10973
+ choices: headedBrowserProjects.map((project) => ({
10974
+ title: project.name,
10975
+ value: project.name
10976
+ })),
10977
+ message: `${message} Select a single project to run or cancel and run tests with "headless: true" option. Note that you can also start tests with --browser=name or --project=name flag.`
10978
+ });
10979
+ if (!projectName) {
10980
+ throw new Error("The test run was aborted.");
10981
+ }
10982
+ return resolvedProjects.filter((project) => project.name === projectName);
10983
+ }
10814
10984
  return resolvedProjects;
10815
10985
  }
10986
+ function cloneConfig(project, { browser, ...config }) {
10987
+ const {
10988
+ locators,
10989
+ viewport,
10990
+ testerHtmlPath,
10991
+ headless,
10992
+ screenshotDirectory,
10993
+ screenshotFailures,
10994
+ // @ts-expect-error remove just in case
10995
+ browser: _browser,
10996
+ name,
10997
+ ...overrideConfig
10998
+ } = config;
10999
+ const currentConfig = project.config.browser;
11000
+ return mergeConfig({
11001
+ ...deepClone(project.config),
11002
+ browser: {
11003
+ ...project.config.browser,
11004
+ locators: locators ? {
11005
+ testIdAttribute: locators.testIdAttribute ?? currentConfig.locators.testIdAttribute
11006
+ } : project.config.browser.locators,
11007
+ viewport: viewport ?? currentConfig.viewport,
11008
+ testerHtmlPath: testerHtmlPath ?? currentConfig.testerHtmlPath,
11009
+ screenshotDirectory: screenshotDirectory ?? currentConfig.screenshotDirectory,
11010
+ screenshotFailures: screenshotFailures ?? currentConfig.screenshotFailures,
11011
+ // TODO: test that CLI arg is preferred over the local config
11012
+ headless: project.vitest._options?.browser?.headless ?? headless ?? currentConfig.headless,
11013
+ name: browser,
11014
+ providerOptions: config,
11015
+ instances: void 0
11016
+ // projects cannot spawn more configs
11017
+ }
11018
+ // TODO: should resolve, not merge/override
11019
+ }, overrideConfig);
11020
+ }
10816
11021
  async function resolveTestProjectConfigs(vitest, workspaceConfigPath, workspaceDefinition) {
10817
11022
  const projectsOptions = [];
10818
11023
  const workspaceConfigFiles = [];
@@ -10824,8 +11029,8 @@ async function resolveTestProjectConfigs(vitest, workspaceConfigPath, workspaceD
10824
11029
  if (!isDynamicPattern(stringOption)) {
10825
11030
  const file = resolve(vitest.config.root, stringOption);
10826
11031
  if (!existsSync(file)) {
10827
- const relativeWorkpaceConfigPath = workspaceConfigPath ? relative(vitest.config.root, workspaceConfigPath) : void 0;
10828
- const note = workspaceConfigPath ? `Workspace config file "${relativeWorkpaceConfigPath}"` : "Inline workspace";
11032
+ const relativeWorkSpaceConfigPath = workspaceConfigPath ? relative(vitest.config.root, workspaceConfigPath) : void 0;
11033
+ const note = workspaceConfigPath ? `Workspace config file "${relativeWorkSpaceConfigPath}"` : "Inline workspace";
10829
11034
  throw new Error(`${note} references a non-existing file or a directory: ${file}`);
10830
11035
  }
10831
11036
  const stats = await promises.stat(file);
@@ -10964,6 +11169,8 @@ class Vitest {
10964
11169
  /** @internal */
10965
11170
  _browserLastPort = defaultBrowserPort;
10966
11171
  /** @internal */
11172
+ _browserSessions = new BrowserSessions();
11173
+ /** @internal */
10967
11174
  _options = {};
10968
11175
  /** @internal */
10969
11176
  reporters = void 0;
@@ -11204,7 +11411,7 @@ class Vitest {
11204
11411
  const workspaceConfigPath = await this.resolveWorkspaceConfigPath();
11205
11412
  this._workspaceConfigPath = workspaceConfigPath;
11206
11413
  if (!workspaceConfigPath) {
11207
- return [this._ensureRootProject()];
11414
+ return resolveBrowserWorkspace(this, /* @__PURE__ */ new Set(), [this._ensureRootProject()]);
11208
11415
  }
11209
11416
  const workspaceModule = await this.import(workspaceConfigPath);
11210
11417
  if (!workspaceModule.default || !Array.isArray(workspaceModule.default)) {
@@ -11380,7 +11587,7 @@ class Vitest {
11380
11587
  return this.getModuleSpecifications(file);
11381
11588
  }
11382
11589
  /**
11383
- * Get test specifications assosiated with the given module. If module is not a test file, an empty array is returned.
11590
+ * Get test specifications associated with the given module. If module is not a test file, an empty array is returned.
11384
11591
  *
11385
11592
  * **Note:** this method relies on a cache generated by `globTestSpecifications`. If the file was not processed yet, use `project.matchesGlobPattern` instead.
11386
11593
  * @param moduleId The module ID to get test specifications for.
@@ -11389,7 +11596,7 @@ class Vitest {
11389
11596
  return this.specifications.getModuleSpecifications(moduleId);
11390
11597
  }
11391
11598
  /**
11392
- * Vitest automatically caches test specifications for each file. This method clears the cache for the given file or the whole cache alltogether.
11599
+ * Vitest automatically caches test specifications for each file. This method clears the cache for the given file or the whole cache altogether.
11393
11600
  */
11394
11601
  clearSpecificationsCache(moduleId) {
11395
11602
  this.specifications.clearCache(moduleId);
@@ -11516,6 +11723,7 @@ class Vitest {
11516
11723
  async cancelCurrentRun(reason) {
11517
11724
  this.isCancelling = true;
11518
11725
  await Promise.all(this._onCancelListeners.splice(0).map((listener) => listener(reason)));
11726
+ await this.runningPromise;
11519
11727
  }
11520
11728
  /** @internal */
11521
11729
  async _initBrowserServers() {
@@ -11545,8 +11753,9 @@ class Vitest {
11545
11753
  this.report("onWatcherRerun", files, trigger),
11546
11754
  ...this._onUserTestsRerun.map((fn) => fn(specifications))
11547
11755
  ]);
11548
- await this.runFiles(specifications, allTestsRun);
11756
+ const testResult = await this.runFiles(specifications, allTestsRun);
11549
11757
  await this.report("onWatcherStart", this.state.getFiles(files));
11758
+ return testResult;
11550
11759
  }
11551
11760
  /** @internal */
11552
11761
  async rerunTask(id) {
@@ -11599,7 +11808,10 @@ class Vitest {
11599
11808
  async rerunFailed() {
11600
11809
  await this.rerunFiles(this.state.getFailedFilepaths(), "rerun failed", false);
11601
11810
  }
11602
- /** @internal */
11811
+ /**
11812
+ * Update snapshots in specified files. If no files are provided, it will update files with failed tests and obsolete snapshots.
11813
+ * @param files The list of files on the file system
11814
+ */
11603
11815
  async updateSnapshot(files) {
11604
11816
  files = files || [
11605
11817
  ...this.state.getFailedFilepaths(),
@@ -11607,7 +11819,7 @@ class Vitest {
11607
11819
  ];
11608
11820
  this.enableSnapshotUpdate();
11609
11821
  try {
11610
- await this.rerunFiles(files, "update snapshot", false);
11822
+ return await this.rerunFiles(files, "update snapshot", false);
11611
11823
  } finally {
11612
11824
  this.resetSnapshotUpdate();
11613
11825
  }
@@ -11625,12 +11837,14 @@ class Vitest {
11625
11837
  // environment is resolved inside a worker thread
11626
11838
  snapshotEnvironment: null
11627
11839
  };
11840
+ this.snapshot.options.updateSnapshot = "all";
11628
11841
  }
11629
11842
  /**
11630
11843
  * Disable the mode that allows updating snapshots when running tests.
11631
11844
  */
11632
11845
  resetSnapshotUpdate() {
11633
11846
  delete this.configOverride.snapshotOptions;
11847
+ this.snapshot.options.updateSnapshot = this.config.snapshotOptions.updateSnapshot;
11634
11848
  }
11635
11849
  /**
11636
11850
  * Set the global test name pattern to a regexp.
@@ -11764,7 +11978,6 @@ class Vitest {
11764
11978
  this.logger.error("error during close", r.reason);
11765
11979
  }
11766
11980
  });
11767
- this.logger.logUpdate.done();
11768
11981
  });
11769
11982
  })();
11770
11983
  }
@@ -12293,13 +12506,11 @@ function registerConsoleShortcuts(ctx, stdin = process.stdin, stdout2) {
12293
12506
  async function _keypressHandler(str, key) {
12294
12507
  if (str === "" || str === "\x1B" || key && key.ctrl && key.name === "c") {
12295
12508
  if (!ctx.isCancelling) {
12296
- ctx.logger.logUpdate.clear();
12297
12509
  ctx.logger.log(
12298
12510
  c.red("Cancelling test run. Press CTRL+c again to exit forcefully.\n")
12299
12511
  );
12300
12512
  process.exitCode = 130;
12301
12513
  await ctx.cancelCurrentRun("keyboard-input");
12302
- await ctx.runningPromise;
12303
12514
  }
12304
12515
  return ctx.exit(true);
12305
12516
  }
@@ -12514,12 +12725,6 @@ async function prepareVitest(mode, options = {}, viteOverrides, vitestOptions) {
12514
12725
  options.watch = false;
12515
12726
  }
12516
12727
  const root = resolve(options.root || process.cwd());
12517
- if (typeof options.browser === "object" && !("enabled" in options.browser)) {
12518
- options.browser.enabled = true;
12519
- }
12520
- if (typeof options.typecheck?.only === "boolean") {
12521
- options.typecheck.enabled ??= true;
12522
- }
12523
12728
  const ctx = await createVitest(mode, options, viteOverrides, vitestOptions);
12524
12729
  const environmentPackage = getEnvPackageName(ctx.config.environment);
12525
12730
  if (environmentPackage && !await ctx.packageInstaller.ensureInstalled(environmentPackage, root)) {
@@ -60,6 +60,12 @@ interface FakeTimerInstallOpts {
60
60
  * @default true
61
61
  */
62
62
  shouldClearNativeTimers?: boolean | undefined;
63
+
64
+ /**
65
+ * Don't throw error when asked to fake timers that are not present.
66
+ * @default false
67
+ */
68
+ ignoreMissingTimers?: boolean | undefined;
63
69
  }
64
70
 
65
71
  /**