vitest 3.0.0-beta.2 → 3.0.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 (34) hide show
  1. package/dist/browser.d.ts +2 -2
  2. package/dist/chunks/{RandomSequencer.gisBJ77r.js → RandomSequencer.C6x84bNN.js} +4 -3
  3. package/dist/chunks/{base.CUgXReRN.js → base.CQ2VEtuH.js} +1 -1
  4. package/dist/chunks/{cac.Xzv7eNWw.js → cac.e7qW4xLT.js} +19 -8
  5. package/dist/chunks/{cli-api.CETCDGgZ.js → cli-api.CWDlED-m.js} +244 -39
  6. package/dist/chunks/{creator.DcAcUhMD.js → creator.Ot9GlSGw.js} +16 -14
  7. package/dist/chunks/{index.DoV7W5gc.js → index.BBoOXW-l.js} +5 -0
  8. package/dist/chunks/{index.9ZEBV_TJ.js → index.CzkCSFCy.js} +37 -24
  9. package/dist/chunks/{reporters.DTtxC3KQ.d.ts → reporters.DCiyjXOg.d.ts} +131 -102
  10. package/dist/chunks/{resolveConfig.BA-_OKEx.js → resolveConfig.C1d7TK-U.js} +25 -3
  11. package/dist/chunks/{runBaseTests.D0dWpzZV.js → runBaseTests.qNWRkgHj.js} +1 -1
  12. package/dist/chunks/{utils.CMUTX-p8.js → utils.Coei4Wlj.js} +1 -1
  13. package/dist/chunks/{vite.CXaetSK3.d.ts → vite.CRSMFy31.d.ts} +1 -1
  14. package/dist/chunks/{worker.ClntunZp.d.ts → worker.R-PA7DpW.d.ts} +1 -1
  15. package/dist/chunks/{worker.o1PBoDdo.d.ts → worker.XbtCXEXv.d.ts} +1 -0
  16. package/dist/cli.js +1 -1
  17. package/dist/config.cjs +1 -0
  18. package/dist/config.d.ts +3 -3
  19. package/dist/config.js +1 -0
  20. package/dist/coverage.d.ts +1 -1
  21. package/dist/coverage.js +33 -8
  22. package/dist/execute.d.ts +1 -1
  23. package/dist/index.d.ts +5 -5
  24. package/dist/node.d.ts +5 -5
  25. package/dist/node.js +10 -10
  26. package/dist/reporters.d.ts +1 -1
  27. package/dist/reporters.js +3 -3
  28. package/dist/runners.js +2 -2
  29. package/dist/workers/forks.js +1 -1
  30. package/dist/workers/runVmTests.js +1 -1
  31. package/dist/workers/threads.js +1 -1
  32. package/dist/workers.d.ts +2 -2
  33. package/dist/workers.js +1 -1
  34. package/package.json +13 -13
package/dist/browser.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { b as CoverageProvider, c as CoverageProviderModule } from './chunks/reporters.DTtxC3KQ.js';
1
+ import { b as CoverageProvider, c as CoverageProviderModule } from './chunks/reporters.DCiyjXOg.js';
2
2
  import { a as SerializedCoverageConfig, S as SerializedConfig } from './chunks/config.BTPBhmK5.js';
3
3
  import * as spy$1 from '@vitest/spy';
4
4
  import * as _vitest_utils_diff from '@vitest/utils/diff';
@@ -20,7 +20,7 @@ import '@vitest/snapshot/manager';
20
20
  import 'node:fs';
21
21
  import '@vitest/snapshot/environment';
22
22
  import 'vite-node/client';
23
- import './chunks/worker.o1PBoDdo.js';
23
+ import './chunks/worker.XbtCXEXv.js';
24
24
  import 'node:vm';
25
25
  import '@vitest/mocker';
26
26
  import './chunks/mocker.cRtM890J.js';
@@ -1,4 +1,4 @@
1
- import 'std-env';
1
+ import { isCI } from 'std-env';
2
2
  import { writeFile, rm } from 'node:fs/promises';
3
3
  import { performance } from 'node:perf_hooks';
4
4
  import { generateHash, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, getTasks } from '@vitest/runner/utils';
@@ -21,6 +21,7 @@ const hash = crypto.hash ?? ((algorithm, data, outputEncoding) => crypto.createH
21
21
  const isNode = typeof process < "u" && typeof process.stdout < "u" && !process.versions?.deno && !globalThis.window;
22
22
  const isDeno = typeof process < "u" && typeof process.stdout < "u" && process.versions?.deno !== void 0;
23
23
  const isWindows = (isNode || isDeno) && process.platform === "win32";
24
+ const isTTY = (isNode || isDeno) && process.stdout?.isTTY && !isCI;
24
25
 
25
26
  const REGEXP_WRAP_PREFIX = "$$vitest:";
26
27
  function getOutputFile(config, reporter) {
@@ -709,7 +710,7 @@ class Typechecker {
709
710
  if ("tasks" in task) {
710
711
  markTasks(task.tasks);
711
712
  }
712
- if (!task.result?.state && task.mode === "run") {
713
+ if (!task.result?.state && (task.mode === "run" || task.mode === "queued")) {
713
714
  task.result = {
714
715
  state: "pass"
715
716
  };
@@ -977,4 +978,4 @@ class RandomSequencer extends BaseSequencer {
977
978
  }
978
979
  }
979
980
 
980
- export { BaseSequencer as B, RandomSequencer as R, Typechecker as T, TypeCheckError as a, isNode as b, isDeno as c, getOutputFile as g, hash as h, isWindows as i, wrapSerializableConfig as w };
981
+ export { BaseSequencer as B, RandomSequencer as R, Typechecker as T, TypeCheckError as a, isTTY as b, getOutputFile as g, hash as h, isWindows as i, wrapSerializableConfig as w };
@@ -26,7 +26,7 @@ async function runBaseTests(method, state) {
26
26
  ));
27
27
  const [executor, { run }] = await Promise.all([
28
28
  startViteNode({ state, requestStubs: getDefaultRequestStubs() }),
29
- import('./runBaseTests.D0dWpzZV.js')
29
+ import('./runBaseTests.qNWRkgHj.js')
30
30
  ]);
31
31
  const fileSpecs = ctx.files.map(
32
32
  (f) => typeof f === "string" ? { filepath: f, testLocations: void 0 } : f
@@ -618,7 +618,7 @@ class CAC extends EventEmitter {
618
618
 
619
619
  const cac = (name = "") => new CAC(name);
620
620
 
621
- var version = "3.0.0-beta.2";
621
+ var version = "3.0.0-beta.3";
622
622
 
623
623
  const apiConfig = (port) => ({
624
624
  port: {
@@ -919,7 +919,7 @@ const cliOptionsConfig = {
919
919
  description: "Run tests in the browser. Equivalent to `--browser.enabled` (default: `false`)"
920
920
  },
921
921
  name: {
922
- description: "Run all tests in a specific browser. Some browsers are only available for specific providers (see `--browser.provider`). Visit [`browser.name`](https://vitest.dev/config/#browser-name) for more information",
922
+ description: "Run all tests in a specific browser. Some browsers are only available for specific providers (see `--browser.provider`). Visit [`browser.name`](https://vitest.dev/guide/browser/config/#browser-name) for more information",
923
923
  argument: "<name>"
924
924
  },
925
925
  headless: {
@@ -951,6 +951,10 @@ const cliOptionsConfig = {
951
951
  fileParallelism: {
952
952
  description: "Should browser test files run in parallel. Use `--browser.fileParallelism=false` to disable (default: `true`)"
953
953
  },
954
+ connectTimeout: {
955
+ description: "If connection to the browser takes longer, the test suite will fail (default: `60_000`)",
956
+ argument: "<timeout>"
957
+ },
954
958
  orchestratorScripts: null,
955
959
  testerScripts: null,
956
960
  commands: null,
@@ -958,7 +962,8 @@ const cliOptionsConfig = {
958
962
  screenshotDirectory: null,
959
963
  screenshotFailures: null,
960
964
  locators: null,
961
- testerHtmlPath: null
965
+ testerHtmlPath: null,
966
+ instances: null
962
967
  }
963
968
  },
964
969
  pool: {
@@ -1548,6 +1553,12 @@ function normalizeCliOptions(cliFilters, argv) {
1548
1553
  if (cliFilters.some((filter) => filter.includes(":"))) {
1549
1554
  argv.includeTaskLocation ??= true;
1550
1555
  }
1556
+ if (typeof argv.browser === "object" && !("enabled" in argv.browser)) {
1557
+ argv.browser.enabled = true;
1558
+ }
1559
+ if (typeof argv.typecheck?.only === "boolean") {
1560
+ argv.typecheck.enabled ??= true;
1561
+ }
1551
1562
  return argv;
1552
1563
  }
1553
1564
  async function start(mode, cliFilters, options) {
@@ -1556,13 +1567,13 @@ async function start(mode, cliFilters, options) {
1556
1567
  } catch {
1557
1568
  }
1558
1569
  try {
1559
- const { startVitest } = await import('./cli-api.CETCDGgZ.js').then(function (n) { return n.f; });
1570
+ const { startVitest } = await import('./cli-api.CWDlED-m.js').then(function (n) { return n.f; });
1560
1571
  const ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(cliFilters, options));
1561
1572
  if (!ctx.shouldKeepServer()) {
1562
1573
  await ctx.exit();
1563
1574
  }
1564
1575
  } catch (e) {
1565
- const { divider } = await import('./utils.CMUTX-p8.js').then(function (n) { return n.u; });
1576
+ const { divider } = await import('./utils.Coei4Wlj.js').then(function (n) { return n.u; });
1566
1577
  console.error(`
1567
1578
  ${c.red(divider(c.bold(c.inverse(" Startup Error "))))}`);
1568
1579
  console.error(e);
@@ -1578,7 +1589,7 @@ async function init(project) {
1578
1589
  console.error(new Error('Only the "browser" project is supported. Use "vitest init browser" to create a new project.'));
1579
1590
  process.exit(1);
1580
1591
  }
1581
- const { create } = await import('./creator.DcAcUhMD.js');
1592
+ const { create } = await import('./creator.Ot9GlSGw.js');
1582
1593
  await create();
1583
1594
  }
1584
1595
  async function collect(mode, cliFilters, options) {
@@ -1587,7 +1598,7 @@ async function collect(mode, cliFilters, options) {
1587
1598
  } catch {
1588
1599
  }
1589
1600
  try {
1590
- const { prepareVitest, processCollected, outputFileList } = await import('./cli-api.CETCDGgZ.js').then(function (n) { return n.f; });
1601
+ const { prepareVitest, processCollected, outputFileList } = await import('./cli-api.CWDlED-m.js').then(function (n) { return n.f; });
1591
1602
  const ctx = await prepareVitest(mode, {
1592
1603
  ...normalizeCliOptions(cliFilters, options),
1593
1604
  watch: false,
@@ -1609,7 +1620,7 @@ async function collect(mode, cliFilters, options) {
1609
1620
  }
1610
1621
  await ctx.close();
1611
1622
  } catch (e) {
1612
- const { divider } = await import('./utils.CMUTX-p8.js').then(function (n) { return n.u; });
1623
+ const { divider } = await import('./utils.Coei4Wlj.js').then(function (n) { return n.u; });
1613
1624
  console.error(`
1614
1625
  ${c.red(divider(c.bold(c.inverse(" Collect Error "))))}`);
1615
1626
  console.error(e);
@@ -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.e7qW4xLT.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.CzkCSFCy.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.C1d7TK-U.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 { b as isTTY, h as hash, i as isWindows } from './RandomSequencer.C6x84bNN.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.Coei4Wlj.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) {
@@ -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)}?`)
@@ -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
  /**
@@ -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.
@@ -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);
@@ -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 = [];
@@ -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)) {
@@ -11545,8 +11752,9 @@ class Vitest {
11545
11752
  this.report("onWatcherRerun", files, trigger),
11546
11753
  ...this._onUserTestsRerun.map((fn) => fn(specifications))
11547
11754
  ]);
11548
- await this.runFiles(specifications, allTestsRun);
11755
+ const testResult = await this.runFiles(specifications, allTestsRun);
11549
11756
  await this.report("onWatcherStart", this.state.getFiles(files));
11757
+ return testResult;
11550
11758
  }
11551
11759
  /** @internal */
11552
11760
  async rerunTask(id) {
@@ -11599,7 +11807,10 @@ class Vitest {
11599
11807
  async rerunFailed() {
11600
11808
  await this.rerunFiles(this.state.getFailedFilepaths(), "rerun failed", false);
11601
11809
  }
11602
- /** @internal */
11810
+ /**
11811
+ * Update snapshots in specified files. If no files are provided, it will update files with failed tests and obsolete snapshots.
11812
+ * @param files The list of files on the file system
11813
+ */
11603
11814
  async updateSnapshot(files) {
11604
11815
  files = files || [
11605
11816
  ...this.state.getFailedFilepaths(),
@@ -11607,7 +11818,7 @@ class Vitest {
11607
11818
  ];
11608
11819
  this.enableSnapshotUpdate();
11609
11820
  try {
11610
- await this.rerunFiles(files, "update snapshot", false);
11821
+ return await this.rerunFiles(files, "update snapshot", false);
11611
11822
  } finally {
11612
11823
  this.resetSnapshotUpdate();
11613
11824
  }
@@ -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)) {