vitest 4.0.0-beta.13 → 4.0.0-beta.15

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 (31) hide show
  1. package/browser/context.d.ts +6 -0
  2. package/browser/context.js +20 -0
  3. package/dist/browser.d.ts +3 -3
  4. package/dist/browser.js +7 -0
  5. package/dist/chunks/{browser.d.D9YV3JvA.d.ts → browser.d.CM1rjKAX.d.ts} +1 -2
  6. package/dist/chunks/{cac.BBtYKH7y.js → cac.C4q82cVp.js} +7 -7
  7. package/dist/chunks/{cli-api.CeakdBUN.js → cli-api.B1q8b4Kf.js} +33 -64
  8. package/dist/chunks/{coverage.DabP7UTQ.js → coverage.3htTSxXZ.js} +201 -20
  9. package/dist/chunks/{creator.DfXDsUyL.js → creator.Daoa5_gR.js} +2 -28
  10. package/dist/chunks/environment.d.CrsxCzP1.d.ts +29 -0
  11. package/dist/chunks/{global.d.BcFPD2LN.d.ts → global.d.YxmikCHu.d.ts} +1 -1
  12. package/dist/chunks/{index.uLUz1RDt.js → index.01uBqPwR.js} +1 -1
  13. package/dist/chunks/{index.CHrBLuEH.js → index.DehVUBn4.js} +2 -109
  14. package/dist/chunks/{moduleRunner.d.CeYc7nZ0.d.ts → moduleRunner.d.CTZUg34g.d.ts} +1 -2
  15. package/dist/chunks/{plugin.d.COyglhiI.d.ts → plugin.d.DvkMsuLk.d.ts} +1 -1
  16. package/dist/chunks/{reporters.d.xGvTJYG3.d.ts → reporters.d.DhY37FSs.d.ts} +71 -37
  17. package/dist/chunks/{typechecker.BfOQ86_a.js → typechecker.DsKAhua5.js} +108 -1
  18. package/dist/chunks/{worker.d.buwuBpBt.d.ts → worker.d.B8jq7m7O.d.ts} +38 -2
  19. package/dist/cli.js +3 -3
  20. package/dist/config.d.ts +9 -8
  21. package/dist/coverage.d.ts +6 -5
  22. package/dist/coverage.js +6 -5
  23. package/dist/environments.d.ts +3 -2
  24. package/dist/index.d.ts +8 -8
  25. package/dist/module-evaluator.d.ts +4 -3
  26. package/dist/node.d.ts +9 -8
  27. package/dist/node.js +20 -20
  28. package/dist/reporters.d.ts +10 -9
  29. package/dist/reporters.js +4 -4
  30. package/package.json +26 -12
  31. package/dist/chunks/environment.d.BsToaxti.d.ts +0 -65
@@ -0,0 +1,6 @@
1
+ // @ts-ignore -- @vitest/browser-playwright might not be installed
2
+ export * from '@vitest/browser-playwright/context'
3
+ // @ts-ignore -- @vitest/browser-webdriverio might not be installed
4
+ export * from '@vitest/browser-webdriverio/context'
5
+ // @ts-ignore -- @vitest/browser-preview might not be installed
6
+ export * from '@vitest/browser-preview/context'
@@ -0,0 +1,20 @@
1
+ // Vitest resolves "vitest/browser" as a virtual module instead
2
+
3
+ // fake exports for static analysis
4
+ export const page = null
5
+ export const server = null
6
+ export const userEvent = null
7
+ export const cdp = null
8
+ export const commands = null
9
+ export const locators = null
10
+ export const utils = null
11
+
12
+ const pool = globalThis.__vitest_worker__?.ctx?.pool
13
+
14
+ throw new Error(
15
+ // eslint-disable-next-line prefer-template
16
+ 'vitest/browser can be imported only inside the Browser Mode. '
17
+ + (pool
18
+ ? `Your test is running in ${pool} pool. Make sure your regular tests are excluded from the "test.include" glob pattern.`
19
+ : 'Instead, it was imported outside of Vitest.'),
20
+ )
package/dist/browser.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { S as SerializedCoverageConfig, a as SerializedConfig } from './chunks/config.d.DGazh2r6.js';
2
2
  import { R as RuntimeCoverageModuleLoader } from './chunks/coverage.d.BZtK59WP.js';
3
3
  import { SerializedDiffOptions } from '@vitest/utils/diff';
4
- import { V as VitestModuleRunner } from './chunks/moduleRunner.d.CeYc7nZ0.js';
4
+ import { V as VitestModuleRunner } from './chunks/moduleRunner.d.CTZUg34g.js';
5
5
  export { collectTests, startTests } from '@vitest/runner';
6
6
  import * as _vitest_spy from '@vitest/spy';
7
7
  export { _vitest_spy as SpyModule };
@@ -15,8 +15,8 @@ import '@vitest/pretty-format';
15
15
  import '@vitest/snapshot';
16
16
  import 'node:vm';
17
17
  import 'vite/module-runner';
18
- import './chunks/worker.d.buwuBpBt.js';
19
- import './chunks/environment.d.BsToaxti.js';
18
+ import './chunks/worker.d.B8jq7m7O.js';
19
+ import './chunks/environment.d.CrsxCzP1.js';
20
20
  import '@vitest/mocker';
21
21
  import './chunks/mocker.d.BE_2ls6u.js';
22
22
 
package/dist/browser.js CHANGED
@@ -10,3 +10,10 @@ export { getSafeTimers, setSafeTimers } from '@vitest/utils/timers';
10
10
  import './chunks/coverage.D_JHT54q.js';
11
11
  import '@vitest/snapshot';
12
12
  import './chunks/utils.CG9h5ccR.js';
13
+
14
+ /**
15
+ * @internal
16
+ */
17
+ const __INTERNAL = { _extendedMethods: /* @__PURE__ */ new Set() };
18
+
19
+ export { __INTERNAL };
@@ -1,4 +1,4 @@
1
- import { T as TestExecutionMethod } from './worker.d.buwuBpBt.js';
1
+ import { T as TestExecutionMethod } from './worker.d.B8jq7m7O.js';
2
2
 
3
3
  type SerializedTestSpecification = [project: {
4
4
  name: string | undefined;
@@ -12,7 +12,6 @@ interface BrowserTesterOptions {
12
12
  method: TestExecutionMethod;
13
13
  files: string[];
14
14
  providedContext: string;
15
- startTime: number;
16
15
  }
17
16
 
18
17
  export type { BrowserTesterOptions as B, SerializedTestSpecification as S };
@@ -3,7 +3,7 @@ import { EventEmitter } from 'events';
3
3
  import { normalize } from 'pathe';
4
4
  import c from 'tinyrainbow';
5
5
  import { a as defaultPort, d as defaultBrowserPort } from './constants.D_Q9UYh-.js';
6
- import { R as ReportersMap } from './index.CHrBLuEH.js';
6
+ import { R as ReportersMap } from './index.DehVUBn4.js';
7
7
 
8
8
  function toArr(any) {
9
9
  return any == null ? [] : Array.isArray(any) ? any : [any];
@@ -619,7 +619,7 @@ class CAC extends EventEmitter {
619
619
 
620
620
  const cac = (name = "") => new CAC(name);
621
621
 
622
- var version = "4.0.0-beta.13";
622
+ var version = "4.0.0-beta.15";
623
623
 
624
624
  const apiConfig = (port) => ({
625
625
  port: {
@@ -1341,22 +1341,22 @@ function normalizeCliOptions(cliFilters, argv) {
1341
1341
  }
1342
1342
  async function start(mode, cliFilters, options) {
1343
1343
  try {
1344
- const { startVitest } = await import('./cli-api.CeakdBUN.js').then(function (n) { return n.j; }), ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(cliFilters, options));
1344
+ const { startVitest } = await import('./cli-api.B1q8b4Kf.js').then(function (n) { return n.h; }), ctx = await startVitest(mode, cliFilters.map(normalize), normalizeCliOptions(cliFilters, options));
1345
1345
  if (!ctx.shouldKeepServer()) await ctx.exit();
1346
1346
  } catch (e) {
1347
- const { errorBanner } = await import('./index.CHrBLuEH.js').then(function (n) { return n.u; });
1347
+ const { errorBanner } = await import('./index.DehVUBn4.js').then(function (n) { return n.u; });
1348
1348
  if (console.error(`\n${errorBanner("Startup Error")}`), console.error(e), console.error("\n\n"), process.exitCode == null) process.exitCode = 1;
1349
1349
  process.exit();
1350
1350
  }
1351
1351
  }
1352
1352
  async function init(project) {
1353
1353
  if (project !== "browser") console.error(/* @__PURE__ */ new Error("Only the \"browser\" project is supported. Use \"vitest init browser\" to create a new project.")), process.exit(1);
1354
- const { create } = await import('./creator.DfXDsUyL.js');
1354
+ const { create } = await import('./creator.Daoa5_gR.js');
1355
1355
  await create();
1356
1356
  }
1357
1357
  async function collect(mode, cliFilters, options) {
1358
1358
  try {
1359
- const { prepareVitest, processCollected, outputFileList } = await import('./cli-api.CeakdBUN.js').then(function (n) { return n.j; }), ctx = await prepareVitest(mode, {
1359
+ const { prepareVitest, processCollected, outputFileList } = await import('./cli-api.B1q8b4Kf.js').then(function (n) { return n.h; }), ctx = await prepareVitest(mode, {
1360
1360
  ...normalizeCliOptions(cliFilters, options),
1361
1361
  watch: false,
1362
1362
  run: true
@@ -1374,7 +1374,7 @@ async function collect(mode, cliFilters, options) {
1374
1374
  }
1375
1375
  await ctx.close();
1376
1376
  } catch (e) {
1377
- const { errorBanner } = await import('./index.CHrBLuEH.js').then(function (n) { return n.u; });
1377
+ const { errorBanner } = await import('./index.DehVUBn4.js').then(function (n) { return n.u; });
1378
1378
  if (console.error(`\n${errorBanner("Collect Error")}`), console.error(e), console.error("\n\n"), process.exitCode == null) process.exitCode = 1;
1379
1379
  process.exit();
1380
1380
  }
@@ -10,9 +10,9 @@ import { A as API_PATH, c as configFiles, d as defaultBrowserPort, a as defaultP
10
10
  import nodeos__default, { tmpdir } from 'node:os';
11
11
  import { generateHash as generateHash$1, calculateSuiteHash, someTasksAreOnly, interpretTaskModes, generateFileHash, limitConcurrency, createFileTask as createFileTask$1, hasFailed, getTasks, isTestCase } from '@vitest/runner/utils';
12
12
  import { SnapshotManager } from '@vitest/snapshot/manager';
13
- import { v as version$1 } from './cac.BBtYKH7y.js';
13
+ import { v as version$1 } from './cac.C4q82cVp.js';
14
14
  import { c as createBirpc } from './index.Bgo3tNWt.js';
15
- import { p as parse, d as stringify, e as printError, f as formatProjectName, w as withLabel, h as errorBanner, i as divider, j as generateCodeFrame, R as ReportersMap, B as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.CHrBLuEH.js';
15
+ import { p as parse, s as stringify, b as TraceMap, o as originalPositionFor, c as ancestor, d as createDefinesScript, e as convertTasksToEvents } from './typechecker.DsKAhua5.js';
16
16
  import require$$0$3 from 'events';
17
17
  import require$$1$1 from 'https';
18
18
  import require$$2 from 'http';
@@ -26,14 +26,13 @@ import require$$0$1 from 'buffer';
26
26
  import { g as getDefaultExportFromCjs } from './_commonjsHelpers.BFTU3MAI.js';
27
27
  import crypto, { createHash } from 'node:crypto';
28
28
  import { distDir, rootDir } from '../path.js';
29
- import { h as hash, d as createFetchModuleFunction, n as normalizeResolvedIdToUrl, R as RandomSequencer, i as isPackageExists, g as getFilePoolName, e as isBrowserEnabled, r as resolveConfig, f as groupBy, j as getCoverageProvider, k as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.DabP7UTQ.js';
30
- import { b as TraceMap, o as originalPositionFor, c as ancestor, d as createDefinesScript, e as convertTasksToEvents } from './typechecker.BfOQ86_a.js';
31
- import createDebug from 'debug';
29
+ import { d as createDebugger, h as hash, e as createFetchModuleFunction, n as normalizeResolvedIdToUrl, R as RandomSequencer, i as isPackageExists, g as getFilePoolName, f as isBrowserEnabled, r as resolveConfig, j as groupBy, k as getCoverageProvider, l as createPool, w as wildcardPatternToRegExp, a as resolveApiServerConfig, s as stdout } from './coverage.3htTSxXZ.js';
32
30
  import { VitestModuleEvaluator } from '#module-evaluator';
33
31
  import { ModuleRunner } from 'vite/module-runner';
34
32
  import { Console } from 'node:console';
35
33
  import c from 'tinyrainbow';
36
34
  import { highlight } from '@vitest/utils/highlight';
35
+ import { p as printError, f as formatProjectName, w as withLabel, e as errorBanner, d as divider, h as generateCodeFrame, R as ReportersMap, B as BlobReporter, r as readBlobs, H as HangingProcessReporter } from './index.DehVUBn4.js';
37
36
  import { createRequire, builtinModules, isBuiltin } from 'node:module';
38
37
  import url, { pathToFileURL } from 'node:url';
39
38
  import { i as isTTY, a as isWindows } from './env.D4Lgay0q.js';
@@ -46,7 +45,7 @@ import { c as configDefaults } from './defaults.CXFFjsi8.js';
46
45
  import { KNOWN_ASSET_RE } from '@vitest/utils/constants';
47
46
  import { findNearestPackageData } from '@vitest/utils/resolver';
48
47
  import * as esModuleLexer from 'es-module-lexer';
49
- import { a as BenchmarkReportsMap } from './index.uLUz1RDt.js';
48
+ import { a as BenchmarkReportsMap } from './index.01uBqPwR.js';
50
49
  import assert$1 from 'node:assert';
51
50
  import { serializeValue } from '@vitest/utils/serialize';
52
51
  import { parseErrorStacktrace } from '@vitest/utils/source-map';
@@ -5022,7 +5021,7 @@ var WebSocketServer = /*@__PURE__*/getDefaultExportFromCjs(websocketServerExport
5022
5021
  async function getModuleGraph(ctx, projectName, id, browser = false) {
5023
5022
  const graph = {}, externalized = /* @__PURE__ */ new Set(), inlined = /* @__PURE__ */ new Set(), project = ctx.getProjectByName(projectName);
5024
5023
  async function get(mod, seen = /* @__PURE__ */ new Map()) {
5025
- if (!mod || !mod.id || mod.id === "\0@vitest/browser/context") return;
5024
+ if (!mod || !mod.id || mod.id === "\0vitest/browser") return;
5026
5025
  if (seen.has(mod)) return seen.get(mod);
5027
5026
  let id = clearId(mod.id);
5028
5027
  seen.set(mod, id);
@@ -5230,11 +5229,6 @@ var setup$1 = /*#__PURE__*/Object.freeze({
5230
5229
  setup: setup
5231
5230
  });
5232
5231
 
5233
- function createDebugger(namespace) {
5234
- const debug = createDebug(namespace);
5235
- if (debug.enabled) return debug;
5236
- }
5237
-
5238
5232
  const debug = createDebugger("vitest:ast-collect-info"), verbose = createDebugger("vitest:ast-collect-verbose");
5239
5233
  function astParseFile(filepath, code) {
5240
5234
  const ast = parseAst(code);
@@ -5782,7 +5776,7 @@ class Logger {
5782
5776
  if (!project.browser) return;
5783
5777
  const resolvedUrls = project.browser.vite.resolvedUrls, origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
5784
5778
  if (!origin) return;
5785
- const output = project.isRootProject() ? "" : formatProjectName(project), provider = project.browser.provider.name, providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
5779
+ const output = project.isRootProject() ? "" : formatProjectName(project), provider = project.browser.provider?.name, providerString = provider === "preview" ? "" : ` by ${c.reset(c.bold(provider))}`;
5786
5780
  this.log(c.dim(`${output}Browser runner started${providerString} ${c.dim("at")} ${c.blue(new URL("/__vitest_test__/", origin))}\n`));
5787
5781
  }
5788
5782
  printUnhandledErrors(errors) {
@@ -7359,28 +7353,22 @@ class TestProject {
7359
7353
  /** @internal */
7360
7354
  _parent;
7361
7355
  /** @internal */
7362
- _initParentBrowser = deduped(async () => {
7356
+ _initParentBrowser = deduped(async (childProject) => {
7363
7357
  if (!this.isBrowserEnabled() || this._parentBrowser) return;
7364
- await this.vitest.packageInstaller.ensureInstalled("@vitest/browser", this.config.root, this.vitest.version);
7365
- const { createBrowserServer, distRoot } = await import('@vitest/browser');
7366
- let cacheDir;
7367
- const browser = await createBrowserServer(this, this.vite.config.configFile, [
7368
- {
7369
- name: "vitest:browser-cacheDir",
7370
- configResolved(config) {
7371
- cacheDir = config.cacheDir;
7372
- }
7373
- },
7374
- ...MocksPlugins({ filter(id) {
7375
- return !(id.includes(distRoot) || id.includes(cacheDir));
7376
- } }),
7377
- MetaEnvReplacerPlugin()
7378
- ], [CoverageTransform(this.vitest)]);
7358
+ const provider = this.config.browser.provider || childProject.config.browser.provider;
7359
+ if (provider == null) throw new Error(`Proider was not specified in the "browser.provider" setting. Please, pass down playwright(), webdriverio() or preview() from "@vitest/browser-playwright", "@vitest/browser-webdriverio" or "@vitest/browser-preview" package respectively.`);
7360
+ if (typeof provider.serverFactory !== "function") throw new TypeError(`The browser provider options do not return a "serverFactory" function. Are you using the latest "@vitest/browser-${provider.name}" package?`);
7361
+ const browser = await provider.serverFactory({
7362
+ project: this,
7363
+ mocksPlugins: (options) => MocksPlugins(options),
7364
+ metaEnvReplacer: () => MetaEnvReplacerPlugin(),
7365
+ coveragePlugin: () => CoverageTransform(this.vitest)
7366
+ });
7379
7367
  if (this._parentBrowser = browser, this.config.browser.ui) setup(this.vitest, browser.vite);
7380
7368
  });
7381
7369
  /** @internal */
7382
7370
  _initBrowserServer = deduped(async () => {
7383
- if (await this._parent?._initParentBrowser(), !this.browser && this._parent?._parentBrowser) this.browser = this._parent._parentBrowser.spawn(this), await this.vitest.report("onBrowserInit", this);
7371
+ if (await this._parent?._initParentBrowser(this), !this.browser && this._parent?._parentBrowser) this.browser = this._parent._parentBrowser.spawn(this), await this.vitest.report("onBrowserInit", this);
7384
7372
  });
7385
7373
  /**
7386
7374
  * Closes the project and all associated resources. This can only be called once; the closing promise is cached until the server restarts.
@@ -7584,19 +7572,13 @@ async function resolveProjects(vitest, cliOptions, workspaceConfigPath, projects
7584
7572
  }
7585
7573
  async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7586
7574
  const removeProjects = /* @__PURE__ */ new Set();
7587
- resolvedProjects.forEach((project) => {
7575
+ return resolvedProjects.forEach((project) => {
7588
7576
  if (!project.config.browser.enabled) return;
7589
- const instances = project.config.browser.instances || [], browser = project.config.browser.name;
7590
- if (instances.length === 0 && browser) instances.push({
7591
- browser,
7592
- name: project.name ? `${project.name} (${browser})` : browser
7593
- }), vitest.logger.warn(withLabel("yellow", "Vitest", [
7594
- `No browser "instances" were defined`,
7595
- project.name ? ` for the "${project.name}" project. ` : ". ",
7596
- `Running tests in "${project.config.browser.name}" browser. `,
7597
- "The \"browser.name\" field is deprecated since Vitest 3. ",
7598
- "Read more: https://vitest.dev/guide/browser/config#browser-instances"
7599
- ].filter(Boolean).join("")));
7577
+ const instances = project.config.browser.instances || [];
7578
+ if (instances.length === 0) {
7579
+ removeProjects.add(project);
7580
+ return;
7581
+ }
7600
7582
  const originalName = project.config.name, filteredInstances = vitest.matchesProjectFilter(originalName) ? instances : instances.filter((instance) => {
7601
7583
  const newName = instance.name;
7602
7584
  return vitest.matchesProjectFilter(newName);
@@ -7614,6 +7596,7 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7614
7596
  }
7615
7597
  const name = config.name;
7616
7598
  if (name == null) throw new Error(`The browser configuration must have a "name" property. This is a bug in Vitest. Please, open a new issue with reproduction`);
7599
+ if (config.provider?.name != null && project.config.browser.provider?.name != null && config.provider?.name !== project.config.browser.provider?.name) throw new Error(`The instance cannot have a different provider from its parent. The "${name}" instance specifies "${config.provider?.name}" provider, but its parent has a "${project.config.browser.provider?.name}" provider.`);
7617
7600
  if (names.has(name)) throw new Error([
7618
7601
  `Cannot define a nested project for a ${browser} browser. The project name "${name}" was already defined. `,
7619
7602
  "If you have multiple instances for the same browser, make sure to define a custom \"name\". ",
@@ -7625,26 +7608,7 @@ async function resolveBrowserProjects(vitest, names, resolvedProjects) {
7625
7608
  const clone = TestProject._cloneBrowserProject(project, clonedConfig);
7626
7609
  resolvedProjects.push(clone);
7627
7610
  }), removeProjects.add(project);
7628
- }), resolvedProjects = resolvedProjects.filter((project) => !removeProjects.has(project));
7629
- const headedBrowserProjects = resolvedProjects.filter((project) => {
7630
- return project.config.browser.enabled && !project.config.browser.headless;
7631
- });
7632
- if (headedBrowserProjects.length > 1) {
7633
- const message = [`Found multiple projects that run browser tests in headed mode: "${headedBrowserProjects.map((p) => p.name).join("\", \"")}".`, ` Vitest cannot run multiple headed browsers at the same time.`].join("");
7634
- if (!isTTY) throw new Error(`${message} Please, filter projects with --browser=name or --project=name flag or run tests with "headless: true" option.`);
7635
- const { projectName } = await (await import('./index.Dc3xnDvT.js').then(function (n) { return n.i; })).default({
7636
- type: "select",
7637
- name: "projectName",
7638
- choices: headedBrowserProjects.map((project) => ({
7639
- title: project.name,
7640
- value: project.name
7641
- })),
7642
- 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.`
7643
- });
7644
- if (!projectName) throw new Error("The test run was aborted.");
7645
- return resolvedProjects.filter((project) => project.name === projectName);
7646
- }
7647
- return resolvedProjects;
7611
+ }), resolvedProjects.filter((project) => !removeProjects.has(project));
7648
7612
  }
7649
7613
  function cloneConfig(project, { browser,...config }) {
7650
7614
  const { locators, viewport, testerHtmlPath, headless, screenshotDirectory, screenshotFailures, browser: _browser, name, provider,...overrideConfig } = config, currentConfig = project.config.browser, clonedConfig = deepClone(project.config);
@@ -9170,7 +9134,12 @@ class Vitest {
9170
9134
  }
9171
9135
  if (!this.projects.length) {
9172
9136
  const filter = toArray(resolved.project).join("\", \"");
9173
- throw filter ? new Error(`No projects matched the filter "${filter}".`) : new Error(`Vitest wasn't able to resolve any project.`);
9137
+ if (filter) throw new Error(`No projects matched the filter "${filter}".`);
9138
+ {
9139
+ let error = `Vitest wasn't able to resolve any project.`;
9140
+ if (this.config.browser.enabled && !this.config.browser.instances?.length) error += ` Please, check that you specified the "browser.instances" option.`;
9141
+ throw new Error(error);
9142
+ }
9174
9143
  }
9175
9144
  if (!this.coreWorkspaceProject) this.coreWorkspaceProject = TestProject._createBasicProject(this);
9176
9145
  if (this.config.testNamePattern) this.configOverride.testNamePattern = this.config.testNamePattern;
@@ -10277,4 +10246,4 @@ var cliApi = /*#__PURE__*/Object.freeze({
10277
10246
  startVitest: startVitest
10278
10247
  });
10279
10248
 
10280
- export { FilesNotFoundError as F, GitNotFoundError as G, Vitest as V, VitestPlugin as a, VitestPackageInstaller as b, createVitest as c, experimental_getRunnerTask as d, escapeTestName as e, registerConsoleShortcuts as f, createViteLogger as g, createDebugger as h, isValidApiRequest as i, cliApi as j, resolveFsAllow as r, startVitest as s };
10249
+ export { FilesNotFoundError as F, GitNotFoundError as G, Vitest as V, VitestPlugin as a, VitestPackageInstaller as b, createVitest as c, experimental_getRunnerTask as d, escapeTestName as e, registerConsoleShortcuts as f, createViteLogger as g, cliApi as h, isValidApiRequest as i, resolveFsAllow as r, startVitest as s };
@@ -1,6 +1,6 @@
1
1
  import fs, { statSync, realpathSync, promises as promises$1, mkdirSync, existsSync, readdirSync, writeFileSync } from 'node:fs';
2
2
  import path, { win32, dirname, join, resolve } from 'node:path';
3
- import { isExternalUrl, unwrapId, nanoid, withTrailingSlash as withTrailingSlash$1, cleanUrl, wrapId, createDefer, slash, shuffle, toArray } from '@vitest/utils/helpers';
3
+ import { createDefer, isExternalUrl, unwrapId, nanoid, withTrailingSlash as withTrailingSlash$1, cleanUrl, wrapId, slash, shuffle, toArray } from '@vitest/utils/helpers';
4
4
  import { isAbsolute, join as join$1, dirname as dirname$1, resolve as resolve$1, relative, normalize } from 'pathe';
5
5
  import pm from 'picomatch';
6
6
  import { glob } from 'tinyglobby';
@@ -21,10 +21,11 @@ import * as nodeos from 'node:os';
21
21
  import nodeos__default, { tmpdir } from 'node:os';
22
22
  import { isatty } from 'node:tty';
23
23
  import { rootDir } from '../path.js';
24
+ import { s as stringify, w as wrapSerializableConfig, a as Typechecker } from './typechecker.DsKAhua5.js';
25
+ import createDebug from 'debug';
24
26
  import EventEmitter from 'node:events';
25
27
  import { c as createBirpc } from './index.Bgo3tNWt.js';
26
28
  import Tinypool$1, { Tinypool } from 'tinypool';
27
- import { w as wrapSerializableConfig, a as Typechecker } from './typechecker.BfOQ86_a.js';
28
29
  import { MessageChannel } from 'node:worker_threads';
29
30
  import { hasFailed } from '@vitest/runner/utils';
30
31
  import { isCI, provider } from 'std-env';
@@ -53,6 +54,11 @@ function wildcardPatternToRegExp(pattern) {
53
54
  return new RegExp(`^${regexp}`, "i");
54
55
  }
55
56
 
57
+ function createDebugger(namespace) {
58
+ const debug = createDebug(namespace);
59
+ if (debug.enabled) return debug;
60
+ }
61
+
56
62
  const hash = crypto.hash ?? ((algorithm, data, outputEncoding) => crypto.createHash(algorithm).update(data).digest(outputEncoding));
57
63
 
58
64
  const JOIN_LEADING_SLASH_RE = /^\.?\//;
@@ -2386,6 +2392,182 @@ function getWorkersCountByPercentage(percent) {
2386
2392
  return Math.max(1, Math.min(maxWorkersCount, workersCountByPercentage));
2387
2393
  }
2388
2394
 
2395
+ const debug = createDebugger("vitest:browser:pool");
2396
+ function createBrowserPool(vitest) {
2397
+ const providers = /* @__PURE__ */ new Set(), numCpus = typeof nodeos.availableParallelism === "function" ? nodeos.availableParallelism() : nodeos.cpus().length, threadsCount = vitest.config.watch ? Math.max(Math.floor(numCpus / 2), 1) : Math.max(numCpus - 1, 1), projectPools = /* @__PURE__ */ new WeakMap(), ensurePool = (project) => {
2398
+ if (projectPools.has(project)) return projectPools.get(project);
2399
+ debug?.("creating pool for project %s", project.name);
2400
+ const resolvedUrls = project.browser.vite.resolvedUrls, origin = resolvedUrls?.local[0] ?? resolvedUrls?.network[0];
2401
+ if (!origin) throw new Error(`Can't find browser origin URL for project "${project.name}"`);
2402
+ const pool = new BrowserPool(project, {
2403
+ maxWorkers: getThreadsCount(project),
2404
+ origin
2405
+ });
2406
+ return projectPools.set(project, pool), vitest.onCancel(() => {
2407
+ pool.cancel();
2408
+ }), pool;
2409
+ }, runWorkspaceTests = async (method, specs) => {
2410
+ const groupedFiles = /* @__PURE__ */ new Map();
2411
+ for (const { project, moduleId } of specs) {
2412
+ const files = groupedFiles.get(project) || [];
2413
+ files.push(moduleId), groupedFiles.set(project, files);
2414
+ }
2415
+ let isCancelled = false;
2416
+ vitest.onCancel(() => {
2417
+ isCancelled = true;
2418
+ });
2419
+ const initialisedPools = await Promise.all([...groupedFiles.entries()].map(async ([project, files]) => {
2420
+ if (await project._initBrowserProvider(), !project.browser) throw new TypeError(`The browser server was not initialized${project.name ? ` for the "${project.name}" project` : ""}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
2421
+ if (isCancelled) return;
2422
+ debug?.("provider is ready for %s project", project.name);
2423
+ const pool = ensurePool(project);
2424
+ return vitest.state.clearFiles(project, files), providers.add(project.browser.provider), {
2425
+ pool,
2426
+ provider: project.browser.provider,
2427
+ runTests: () => pool.runTests(method, files)
2428
+ };
2429
+ }));
2430
+ if (isCancelled) return;
2431
+ const parallelPools = [], nonParallelPools = [];
2432
+ for (const pool of initialisedPools) {
2433
+ if (!pool)
2434
+ // this means it was cancelled
2435
+ return;
2436
+ if (pool.provider.mocker && pool.provider.supportsParallelism) parallelPools.push(pool.runTests);
2437
+ else nonParallelPools.push(pool.runTests);
2438
+ }
2439
+ await Promise.all(parallelPools.map((runTests) => runTests()));
2440
+ for (const runTests of nonParallelPools) {
2441
+ if (isCancelled) return;
2442
+ await runTests();
2443
+ }
2444
+ };
2445
+ function getThreadsCount(project) {
2446
+ const config = project.config.browser;
2447
+ return !config.headless || !config.fileParallelism || !project.browser.provider.supportsParallelism ? 1 : project.config.maxWorkers ? project.config.maxWorkers : threadsCount;
2448
+ }
2449
+ return {
2450
+ name: "browser",
2451
+ async close() {
2452
+ await Promise.all([...providers].map((provider) => provider.close())), vitest._browserSessions.sessionIds.clear(), providers.clear(), vitest.projects.forEach((project) => {
2453
+ project.browser?.state.orchestrators.forEach((orchestrator) => {
2454
+ orchestrator.$close();
2455
+ });
2456
+ }), debug?.("browser pool closed all providers");
2457
+ },
2458
+ runTests: (files) => runWorkspaceTests("run", files),
2459
+ collectTests: (files) => runWorkspaceTests("collect", files)
2460
+ };
2461
+ }
2462
+ function escapePathToRegexp(path) {
2463
+ return path.replace(/[/\\.?*()^${}|[\]+]/g, "\\$&");
2464
+ }
2465
+ class BrowserPool {
2466
+ _queue = [];
2467
+ _promise;
2468
+ _providedContext;
2469
+ readySessions = /* @__PURE__ */ new Set();
2470
+ constructor(project, options) {
2471
+ this.project = project, this.options = options;
2472
+ }
2473
+ cancel() {
2474
+ this._queue = [];
2475
+ }
2476
+ reject(error) {
2477
+ this._promise?.reject(error), this._promise = void 0, this.cancel();
2478
+ }
2479
+ get orchestrators() {
2480
+ return this.project.browser.state.orchestrators;
2481
+ }
2482
+ async runTests(method, files) {
2483
+ if (this._promise ??= createDefer(), !files.length) return debug?.("no tests found, finishing test run immediately"), this._promise.resolve(), this._promise;
2484
+ if (this._providedContext = stringify(this.project.getProvidedContext()), this._queue.push(...files), this.readySessions.forEach((sessionId) => {
2485
+ if (this._queue.length) this.readySessions.delete(sessionId), this.runNextTest(method, sessionId);
2486
+ }), this.orchestrators.size >= this.options.maxWorkers) return debug?.("all orchestrators are ready, not creating more"), this._promise;
2487
+ // open the minimum amount of tabs
2488
+ // if there is only 1 file running, we don't need 8 tabs running
2489
+ const workerCount = Math.min(this.options.maxWorkers - this.orchestrators.size, files.length), promises = [];
2490
+ for (let i = 0; i < workerCount; i++) {
2491
+ const sessionId = crypto.randomUUID();
2492
+ this.project.vitest._browserSessions.sessionIds.add(sessionId);
2493
+ const project = this.project.name;
2494
+ debug?.("[%s] creating session for %s", sessionId, project);
2495
+ const page = this.openPage(sessionId).then(() => {
2496
+ // start running tests on the page when it's ready
2497
+ this.runNextTest(method, sessionId);
2498
+ });
2499
+ promises.push(page);
2500
+ }
2501
+ return await Promise.all(promises), debug?.("all sessions are created"), this._promise;
2502
+ }
2503
+ async openPage(sessionId) {
2504
+ const sessionPromise = this.project.vitest._browserSessions.createSession(sessionId, this.project, this), browser = this.project.browser, url = new URL("/__vitest_test__/", this.options.origin);
2505
+ url.searchParams.set("sessionId", sessionId);
2506
+ const pagePromise = browser.provider.openPage(sessionId, url.toString());
2507
+ await Promise.all([sessionPromise, pagePromise]);
2508
+ }
2509
+ getOrchestrator(sessionId) {
2510
+ const orchestrator = this.orchestrators.get(sessionId);
2511
+ if (!orchestrator) throw new Error(`Orchestrator not found for session ${sessionId}. This is a bug in Vitest. Please, open a new issue with reproduction.`);
2512
+ return orchestrator;
2513
+ }
2514
+ finishSession(sessionId) {
2515
+ // the last worker finished running tests
2516
+ if (this.readySessions.add(sessionId), this.readySessions.size === this.orchestrators.size) this._promise?.resolve(), this._promise = void 0, debug?.("[%s] all tests finished running", sessionId);
2517
+ else debug?.(`did not finish sessions for ${sessionId}: |ready - %s| |overall - %s|`, [...this.readySessions].join(", "), [...this.orchestrators.keys()].join(", "));
2518
+ }
2519
+ runNextTest(method, sessionId) {
2520
+ const file = this._queue.shift();
2521
+ if (!file) {
2522
+ // we don't need to cleanup testers if isolation is enabled,
2523
+ // because cleanup is done at the end of every test
2524
+ if (debug?.("[%s] no more tests to run", sessionId), this.project.config.browser.isolate) {
2525
+ this.finishSession(sessionId);
2526
+ return;
2527
+ }
2528
+ this.getOrchestrator(sessionId).cleanupTesters().catch((error) => this.reject(error)).finally(() => this.finishSession(sessionId));
2529
+ return;
2530
+ }
2531
+ if (!this._promise) throw new Error(`Unexpected empty queue`);
2532
+ const orchestrator = this.getOrchestrator(sessionId);
2533
+ debug?.("[%s] run test %s", sessionId, file), this.setBreakpoint(sessionId, file).then(() => {
2534
+ // this starts running tests inside the orchestrator
2535
+ orchestrator.createTesters({
2536
+ method,
2537
+ files: [file],
2538
+ providedContext: this._providedContext || "[{}]"
2539
+ }).then(() => {
2540
+ debug?.("[%s] test %s finished running", sessionId, file), this.runNextTest(method, sessionId);
2541
+ }).catch((error) => {
2542
+ // if user cancels the test run manually, ignore the error and exit gracefully
2543
+ if (this.project.vitest.isCancelling && error instanceof Error && error.message.startsWith("Browser connection was closed while running tests")) {
2544
+ this.cancel(), this._promise?.resolve(), this._promise = void 0, debug?.("[%s] browser connection was closed", sessionId);
2545
+ return;
2546
+ }
2547
+ debug?.("[%s] error during %s test run: %s", sessionId, file, error), this.reject(error);
2548
+ });
2549
+ }).catch((err) => this.reject(err));
2550
+ }
2551
+ async setBreakpoint(sessionId, file) {
2552
+ if (!this.project.config.inspector.waitForDebugger) return;
2553
+ const provider = this.project.browser.provider, browser = this.project.config.browser.name;
2554
+ if (shouldIgnoreDebugger(provider.name, browser)) {
2555
+ debug?.("[$s] ignoring debugger in %s browser because it is not supported", sessionId, browser);
2556
+ return;
2557
+ }
2558
+ if (!provider.getCDPSession) throw new Error("Unable to set breakpoint, CDP not supported");
2559
+ debug?.("[%s] set breakpoint for %s", sessionId, file);
2560
+ const session = await provider.getCDPSession(sessionId);
2561
+ await session.send("Debugger.enable", {}), await session.send("Debugger.setBreakpointByUrl", {
2562
+ lineNumber: 0,
2563
+ urlRegex: escapePathToRegexp(file)
2564
+ });
2565
+ }
2566
+ }
2567
+ function shouldIgnoreDebugger(provider, browser) {
2568
+ return provider === "webdriverio" ? browser !== "chrome" && browser !== "edge" : browser !== "chromium";
2569
+ }
2570
+
2389
2571
  const envsOrder = [
2390
2572
  "node",
2391
2573
  "jsdom",
@@ -3271,18 +3453,13 @@ function createPool(ctx) {
3271
3453
  function getCustomPool(pool) {
3272
3454
  return getConcurrentPool(pool, () => resolveCustomPool(pool));
3273
3455
  }
3274
- function getBrowserPool() {
3275
- return getConcurrentPool("browser", async () => {
3276
- const { createBrowserPool } = await import('@vitest/browser');
3277
- return createBrowserPool(ctx);
3278
- });
3279
- }
3280
3456
  const groupedSpecifications = {}, groups = /* @__PURE__ */ new Set(), factories = {
3281
3457
  vmThreads: (specs) => createVmThreadsPool(ctx, options, specs),
3282
3458
  vmForks: (specs) => createVmForksPool(ctx, options, specs),
3283
3459
  threads: (specs) => createThreadsPool(ctx, options, specs),
3284
3460
  forks: (specs) => createForksPool(ctx, options, specs),
3285
- typescript: () => createTypecheckPool(ctx)
3461
+ typescript: () => createTypecheckPool(ctx),
3462
+ browser: () => createBrowserPool(ctx)
3286
3463
  };
3287
3464
  for (const spec of files) {
3288
3465
  const group = spec.project.config.sequence.groupOrder ?? 0;
@@ -3318,7 +3495,6 @@ function createPool(ctx) {
3318
3495
  const factory = factories[pool];
3319
3496
  return pools[pool] ??= factory(specs), pools[pool][method](specs, invalidate);
3320
3497
  }
3321
- if (pool === "browser") return pools.browser ??= await getBrowserPool(), pools.browser[method](specs, invalidate);
3322
3498
  const poolHandler = await getCustomPool(pool);
3323
3499
  return pools[poolHandler.name] ??= poolHandler, poolHandler[method](specs, invalidate);
3324
3500
  }));
@@ -3463,12 +3639,17 @@ function resolveConfig$1(vitest, options, viteConfig) {
3463
3639
  resolved.browser ??= {};
3464
3640
  const browser = resolved.browser;
3465
3641
  if (browser.enabled) {
3466
- if (!browser.name && !browser.instances) throw new Error(`Vitest Browser Mode requires "browser.name" (deprecated) or "browser.instances" options, none were set.`);
3467
3642
  const instances = browser.instances;
3468
- if (browser.name && browser.instances)
3469
- // --browser=chromium filters configs to a single one
3470
- browser.instances = browser.instances.filter((instance) => instance.browser === browser.name);
3471
- if (browser.instances && !browser.instances.length) throw new Error([`"browser.instances" was set in the config, but the array is empty. Define at least one browser config.`, browser.name && instances?.length ? ` The "browser.name" was set to "${browser.name}" which filtered all configs (${instances.map((c) => c.browser).join(", ")}). Did you mean to use another name?` : ""].join(""));
3643
+ if (!browser.instances) browser.instances = [];
3644
+ // use `chromium` by default when the preview provider is specified
3645
+ // for a smoother experience. if chromium is not available, it will
3646
+ // open the default browser anyway
3647
+ if (!browser.instances.length && browser.provider?.name === "preview") browser.instances = [{ browser: "chromium" }];
3648
+ if (browser.name && instances?.length) {
3649
+ // if `instances` were defined, but now they are empty,
3650
+ // let's throw an error because the filter is invalid
3651
+ if (browser.instances = browser.instances.filter((instance) => instance.browser === browser.name), !browser.instances.length) throw new Error([`"browser.instances" was set in the config, but the array is empty. Define at least one browser config.`, ` The "browser.name" was set to "${browser.name}" which filtered all configs (${instances.map((c) => c.browser).join(", ")}). Did you mean to use another name?`].join(""));
3652
+ }
3472
3653
  }
3473
3654
  const containsChromium = hasBrowserChromium(vitest, resolved), hasOnlyChromium = hasOnlyBrowserChromium(vitest, resolved);
3474
3655
  // Browser-mode "Chromium" only features:
@@ -3644,13 +3825,13 @@ function resolveConfig$1(vitest, options, viteConfig) {
3644
3825
  ...configDefaults.typecheck,
3645
3826
  ...resolved.typecheck
3646
3827
  }, resolved.typecheck ??= {}, resolved.typecheck.enabled ??= false, resolved.typecheck.enabled) logger.console.warn(c.yellow("Testing types with tsc and vue-tsc is an experimental feature.\nBreaking changes might not follow SemVer, please pin Vitest's version when using it."));
3647
- if (resolved.browser.enabled ??= false, resolved.browser.headless ??= isCI, resolved.browser.isolate ??= true, resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark", resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI, resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve$1(resolved.root, resolved.browser.screenshotDirectory);
3648
- if (resolved.browser.viewport ??= {}, resolved.browser.viewport.width ??= 414, resolved.browser.viewport.height ??= 896, resolved.browser.locators ??= {}, resolved.browser.locators.testIdAttribute ??= "data-testid", resolved.browser.enabled && provider === "stackblitz") resolved.browser.provider = void 0;
3649
- if (typeof resolved.browser.provider === "string") {
3650
- const source = `@vitest/browser/providers/${resolved.browser.provider}`;
3828
+ if (resolved.browser.enabled ??= false, resolved.browser.headless ??= isCI, resolved.browser.isolate ??= true, resolved.browser.fileParallelism ??= options.fileParallelism ?? mode !== "benchmark", resolved.browser.ui ??= resolved.browser.headless === true ? false : !isCI, resolved.browser.commands ??= {}, resolved.browser.screenshotDirectory) resolved.browser.screenshotDirectory = resolve$1(resolved.root, resolved.browser.screenshotDirectory);
3829
+ if (resolved.browser.viewport ??= {}, resolved.browser.viewport.width ??= 414, resolved.browser.viewport.height ??= 896, resolved.browser.locators ??= {}, resolved.browser.locators.testIdAttribute ??= "data-testid", typeof resolved.browser.provider === "string") {
3830
+ const source = `@vitest/browser-${resolved.browser.provider}`;
3651
3831
  throw new TypeError(`The \`browser.provider\` configuration was changed to accept a factory instead of a string. Add an import of "${resolved.browser.provider}" from "${source}" instead. See: https://vitest.dev/guide/browser/config#provider`);
3652
3832
  }
3653
3833
  const isPreview = resolved.browser.provider?.name === "preview";
3834
+ if (!isPreview && resolved.browser.enabled && provider === "stackblitz") throw new Error(`stackblitz environment does not support the ${resolved.browser.provider?.name} provider. Please, use "@vitest/browser-preview" instead.`);
3654
3835
  if (isPreview && resolved.browser.screenshotFailures === true) console.warn(c.yellow([
3655
3836
  `Browser provider "preview" doesn't support screenshots, `,
3656
3837
  `so "browser.screenshotFailures" option is forcefully disabled. `,
@@ -4097,4 +4278,4 @@ function resolveMergeConfig(mod) {
4097
4278
  }
4098
4279
  }
4099
4280
 
4100
- export { BaseCoverageProvider as B, RandomSequencer as R, resolveApiServerConfig as a, BaseSequencer as b, createMethodsRPC as c, createFetchModuleFunction as d, isBrowserEnabled as e, groupBy as f, getFilePoolName as g, hash as h, isPackageExists as i, getCoverageProvider as j, createPool as k, normalizeResolvedIdToUrl as n, resolveConfig$1 as r, stdout as s, wildcardPatternToRegExp as w };
4281
+ export { BaseCoverageProvider as B, RandomSequencer as R, resolveApiServerConfig as a, BaseSequencer as b, createMethodsRPC as c, createDebugger as d, createFetchModuleFunction as e, isBrowserEnabled as f, getFilePoolName as g, hash as h, isPackageExists as i, groupBy as j, getCoverageProvider as k, createPool as l, normalizeResolvedIdToUrl as n, resolveConfig$1 as r, stdout as s, wildcardPatternToRegExp as w };