@voidzero-dev/vite-plus-test 0.0.0-0bfcc90f.20260209-0731

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 (318) hide show
  1. package/LICENSE.md +691 -0
  2. package/browser/context.d.ts +4 -0
  3. package/browser/context.js +20 -0
  4. package/config.d.ts +3 -0
  5. package/coverage.d.ts +1 -0
  6. package/dist/@vitest/browser/client/.vite/manifest.json +24 -0
  7. package/dist/@vitest/browser/client/__vitest__/assets/index-BUCFJtth.js +57 -0
  8. package/dist/@vitest/browser/client/__vitest__/assets/index-DlhE0rqZ.css +1 -0
  9. package/dist/@vitest/browser/client/__vitest__/bg.png +0 -0
  10. package/dist/@vitest/browser/client/__vitest__/favicon.ico +0 -0
  11. package/dist/@vitest/browser/client/__vitest__/favicon.svg +5 -0
  12. package/dist/@vitest/browser/client/__vitest__/index.html +32 -0
  13. package/dist/@vitest/browser/client/__vitest_browser__/orchestrator-S_3e_uzt.js +345 -0
  14. package/dist/@vitest/browser/client/__vitest_browser__/tester-k74mgIRa.js +2171 -0
  15. package/dist/@vitest/browser/client/__vitest_browser__/utils-uxqdqUz8.js +223 -0
  16. package/dist/@vitest/browser/client/error-catcher.js +82 -0
  17. package/dist/@vitest/browser/client/esm-client-injector.js +67 -0
  18. package/dist/@vitest/browser/client/favicon.svg +5 -0
  19. package/dist/@vitest/browser/client/orchestrator.html +35 -0
  20. package/dist/@vitest/browser/client/tester/tester.html +13 -0
  21. package/dist/@vitest/browser/client.js +456 -0
  22. package/dist/@vitest/browser/context.d.ts +792 -0
  23. package/dist/@vitest/browser/context.js +541 -0
  24. package/dist/@vitest/browser/expect-element.js +32 -0
  25. package/dist/@vitest/browser/index-D6m36C6U.js +11 -0
  26. package/dist/@vitest/browser/index.d.ts +73 -0
  27. package/dist/@vitest/browser/index.js +3513 -0
  28. package/dist/@vitest/browser/jest-dom.d.ts +724 -0
  29. package/dist/@vitest/browser/locators.d.ts +354 -0
  30. package/dist/@vitest/browser/locators.js +1 -0
  31. package/dist/@vitest/browser/matchers.d.ts +29 -0
  32. package/dist/@vitest/browser/shared/screenshotMatcher/types.d.ts +22 -0
  33. package/dist/@vitest/browser/state.js +280 -0
  34. package/dist/@vitest/browser/types.d.ts +69 -0
  35. package/dist/@vitest/browser-playwright/context.d.ts +1 -0
  36. package/dist/@vitest/browser-playwright/index.d.ts +106 -0
  37. package/dist/@vitest/browser-playwright/index.js +1111 -0
  38. package/dist/@vitest/browser-playwright/locators.js +114 -0
  39. package/dist/@vitest/browser-preview/context.d.ts +1 -0
  40. package/dist/@vitest/browser-preview/index.d.ts +19 -0
  41. package/dist/@vitest/browser-preview/index.js +148 -0
  42. package/dist/@vitest/browser-preview/locators.js +79 -0
  43. package/dist/@vitest/browser-webdriverio/context.d.ts +1 -0
  44. package/dist/@vitest/browser-webdriverio/index.d.ts +63 -0
  45. package/dist/@vitest/browser-webdriverio/index.js +600 -0
  46. package/dist/@vitest/browser-webdriverio/locators.js +163 -0
  47. package/dist/@vitest/expect/index.d.ts +807 -0
  48. package/dist/@vitest/expect/index.js +1875 -0
  49. package/dist/@vitest/mocker/auto-register.d.ts +2 -0
  50. package/dist/@vitest/mocker/auto-register.js +9 -0
  51. package/dist/@vitest/mocker/automock.d.ts +12 -0
  52. package/dist/@vitest/mocker/automock.js +1 -0
  53. package/dist/@vitest/mocker/browser.d.ts +53 -0
  54. package/dist/@vitest/mocker/browser.js +91 -0
  55. package/dist/@vitest/mocker/chunk-automock.js +354 -0
  56. package/dist/@vitest/mocker/chunk-interceptor-native.js +15 -0
  57. package/dist/@vitest/mocker/chunk-mocker.js +521 -0
  58. package/dist/@vitest/mocker/chunk-pathe.M-eThtNZ.js +174 -0
  59. package/dist/@vitest/mocker/chunk-registry.js +185 -0
  60. package/dist/@vitest/mocker/chunk-utils.js +16 -0
  61. package/dist/@vitest/mocker/index.d-C-sLYZi-.d.ts +25 -0
  62. package/dist/@vitest/mocker/index.d.ts +2 -0
  63. package/dist/@vitest/mocker/index.js +185 -0
  64. package/dist/@vitest/mocker/mocker.d-TnKRhz7N.d.ts +81 -0
  65. package/dist/@vitest/mocker/node.d.ts +800 -0
  66. package/dist/@vitest/mocker/node.js +966 -0
  67. package/dist/@vitest/mocker/redirect.d.ts +3 -0
  68. package/dist/@vitest/mocker/redirect.js +79 -0
  69. package/dist/@vitest/mocker/register.d.ts +9 -0
  70. package/dist/@vitest/mocker/register.js +41 -0
  71. package/dist/@vitest/mocker/types.d-B8CCKmHt.d.ts +107 -0
  72. package/dist/@vitest/pretty-format/index.d.ts +124 -0
  73. package/dist/@vitest/pretty-format/index.js +1022 -0
  74. package/dist/@vitest/runner/chunk-tasks.js +340 -0
  75. package/dist/@vitest/runner/index.d.ts +180 -0
  76. package/dist/@vitest/runner/index.js +2114 -0
  77. package/dist/@vitest/runner/tasks.d-C7UxawJ9.d.ts +834 -0
  78. package/dist/@vitest/runner/types.d.ts +183 -0
  79. package/dist/@vitest/runner/types.js +1 -0
  80. package/dist/@vitest/runner/utils.d.ts +45 -0
  81. package/dist/@vitest/runner/utils.js +5 -0
  82. package/dist/@vitest/snapshot/environment.d-DHdQ1Csl.d.ts +22 -0
  83. package/dist/@vitest/snapshot/environment.d.ts +16 -0
  84. package/dist/@vitest/snapshot/environment.js +40 -0
  85. package/dist/@vitest/snapshot/index.d.ts +130 -0
  86. package/dist/@vitest/snapshot/index.js +1437 -0
  87. package/dist/@vitest/snapshot/manager.d.ts +18 -0
  88. package/dist/@vitest/snapshot/manager.js +73 -0
  89. package/dist/@vitest/snapshot/rawSnapshot.d-lFsMJFUd.d.ts +61 -0
  90. package/dist/@vitest/spy/index.d.ts +384 -0
  91. package/dist/@vitest/spy/index.js +433 -0
  92. package/dist/@vitest/utils/chunk-_commonjsHelpers.js +5 -0
  93. package/dist/@vitest/utils/chunk-pathe.M-eThtNZ.js +156 -0
  94. package/dist/@vitest/utils/constants.d.ts +21 -0
  95. package/dist/@vitest/utils/constants.js +49 -0
  96. package/dist/@vitest/utils/diff.d.ts +93 -0
  97. package/dist/@vitest/utils/diff.js +2199 -0
  98. package/dist/@vitest/utils/display.d.ts +29 -0
  99. package/dist/@vitest/utils/display.js +742 -0
  100. package/dist/@vitest/utils/error.d.ts +7 -0
  101. package/dist/@vitest/utils/error.js +42 -0
  102. package/dist/@vitest/utils/helpers.d.ts +73 -0
  103. package/dist/@vitest/utils/helpers.js +295 -0
  104. package/dist/@vitest/utils/highlight.d.ts +9 -0
  105. package/dist/@vitest/utils/highlight.js +538 -0
  106. package/dist/@vitest/utils/index.d.ts +5 -0
  107. package/dist/@vitest/utils/index.js +1 -0
  108. package/dist/@vitest/utils/offset.d.ts +5 -0
  109. package/dist/@vitest/utils/offset.js +32 -0
  110. package/dist/@vitest/utils/resolver.d.ts +7 -0
  111. package/dist/@vitest/utils/resolver.js +71 -0
  112. package/dist/@vitest/utils/serialize.d.ts +3 -0
  113. package/dist/@vitest/utils/serialize.js +118 -0
  114. package/dist/@vitest/utils/source-map.d.ts +55 -0
  115. package/dist/@vitest/utils/source-map.js +478 -0
  116. package/dist/@vitest/utils/timers.d.ts +33 -0
  117. package/dist/@vitest/utils/timers.js +49 -0
  118. package/dist/@vitest/utils/types.d-BCElaP-c.d.ts +53 -0
  119. package/dist/@vitest/utils/types.d.ts +34 -0
  120. package/dist/@vitest/utils/types.js +1 -0
  121. package/dist/browser-compat.js +3 -0
  122. package/dist/browser.d.ts +46 -0
  123. package/dist/browser.js +20 -0
  124. package/dist/chunks/_commonjsHelpers.D26ty3Ew.js +6 -0
  125. package/dist/chunks/base.CJ0Y4ePK.js +165 -0
  126. package/dist/chunks/benchmark.B3N2zMcH.js +40 -0
  127. package/dist/chunks/benchmark.d.DAaHLpsq.d.ts +24 -0
  128. package/dist/chunks/browser.d.ChKACdzH.d.ts +59 -0
  129. package/dist/chunks/cac.DVeoLl0M.js +1409 -0
  130. package/dist/chunks/cli-api.B7PN_QUv.js +13672 -0
  131. package/dist/chunks/config.d.Cy95HiCx.d.ts +210 -0
  132. package/dist/chunks/console.Cf-YriPC.js +146 -0
  133. package/dist/chunks/constants.D_Q9UYh-.js +36 -0
  134. package/dist/chunks/coverage.AVPTjMgw.js +3292 -0
  135. package/dist/chunks/coverage.D_JHT54q.js +25 -0
  136. package/dist/chunks/coverage.d.BZtK59WP.d.ts +37 -0
  137. package/dist/chunks/creator.DAmOKTvJ.js +673 -0
  138. package/dist/chunks/date.Bq6ZW5rf.js +73 -0
  139. package/dist/chunks/defaults.BOqNVLsY.js +74 -0
  140. package/dist/chunks/env.D4Lgay0q.js +8 -0
  141. package/dist/chunks/environment.d.CrsxCzP1.d.ts +29 -0
  142. package/dist/chunks/evaluatedModules.Dg1zASAC.js +17 -0
  143. package/dist/chunks/evaluatedModules.d.BxJ5omdx.d.ts +7 -0
  144. package/dist/chunks/git.Bm2pzPAa.js +71 -0
  145. package/dist/chunks/global.d.B15mdLcR.d.ts +99 -0
  146. package/dist/chunks/globals.DOayXfHP.js +30 -0
  147. package/dist/chunks/index.6Qv1eEA6.js +109 -0
  148. package/dist/chunks/index.C5r1PdPD.js +231 -0
  149. package/dist/chunks/index.Chj8NDwU.js +206 -0
  150. package/dist/chunks/index.CyBMJtT7.js +727 -0
  151. package/dist/chunks/index.D3XRDfWc.js +213 -0
  152. package/dist/chunks/index.D4KonVSU.js +6343 -0
  153. package/dist/chunks/index.M8mOzt4Y.js +3839 -0
  154. package/dist/chunks/index.Z5E_ObnR.js +37 -0
  155. package/dist/chunks/init-forks._y3TW739.js +41 -0
  156. package/dist/chunks/init-threads.DBO2kn-p.js +18 -0
  157. package/dist/chunks/init.B6MLFIaN.js +334 -0
  158. package/dist/chunks/inspector.CvyFGlXm.js +53 -0
  159. package/dist/chunks/modules.BJuCwlRJ.js +36 -0
  160. package/dist/chunks/node.Ce0vMQM7.js +14 -0
  161. package/dist/chunks/plugin.d.CtqpEehP.d.ts +38 -0
  162. package/dist/chunks/reporters.d.CWXNI2jG.d.ts +3271 -0
  163. package/dist/chunks/rpc.BoxB0q7B.js +76 -0
  164. package/dist/chunks/rpc.d.RH3apGEf.d.ts +64 -0
  165. package/dist/chunks/setup-common.Cm-kSBVi.js +60 -0
  166. package/dist/chunks/startModuleRunner.DEj0jb3e.js +861 -0
  167. package/dist/chunks/suite.d.BJWk38HB.d.ts +10 -0
  168. package/dist/chunks/test.B8ej_ZHS.js +254 -0
  169. package/dist/chunks/traces.CCmnQaNT.js +217 -0
  170. package/dist/chunks/traces.d.402V_yFI.d.ts +18 -0
  171. package/dist/chunks/utils.DvEY5TfP.js +52 -0
  172. package/dist/chunks/vi.2VT5v0um.js +3919 -0
  173. package/dist/chunks/vm.D3epNOPZ.js +744 -0
  174. package/dist/chunks/worker.d.Dyxm8DEL.d.ts +255 -0
  175. package/dist/cli.js +28 -0
  176. package/dist/client/.vite/manifest.json +24 -0
  177. package/dist/client/__vitest__/assets/index-BUCFJtth.js +57 -0
  178. package/dist/client/__vitest__/assets/index-DlhE0rqZ.css +1 -0
  179. package/dist/client/__vitest__/bg.png +0 -0
  180. package/dist/client/__vitest__/favicon.ico +0 -0
  181. package/dist/client/__vitest__/favicon.svg +5 -0
  182. package/dist/client/__vitest__/index.html +32 -0
  183. package/dist/client/__vitest_browser__/orchestrator-S_3e_uzt.js +345 -0
  184. package/dist/client/__vitest_browser__/tester-k74mgIRa.js +2171 -0
  185. package/dist/client/__vitest_browser__/utils-uxqdqUz8.js +223 -0
  186. package/dist/client/error-catcher.js +82 -0
  187. package/dist/client/esm-client-injector.js +67 -0
  188. package/dist/client/favicon.svg +5 -0
  189. package/dist/client/orchestrator.html +35 -0
  190. package/dist/client/tester/tester.html +13 -0
  191. package/dist/client.js +456 -0
  192. package/dist/config.cjs +94 -0
  193. package/dist/config.d.ts +104 -0
  194. package/dist/config.js +15 -0
  195. package/dist/context.js +541 -0
  196. package/dist/coverage.d.ts +118 -0
  197. package/dist/coverage.js +23 -0
  198. package/dist/dummy.js +2 -0
  199. package/dist/environments.d.ts +22 -0
  200. package/dist/environments.js +3 -0
  201. package/dist/expect-element.js +27 -0
  202. package/dist/index-D6m36C6U.js +6 -0
  203. package/dist/index-node.js +7 -0
  204. package/dist/index.d.ts +510 -0
  205. package/dist/index.js +19 -0
  206. package/dist/locators.d.ts +354 -0
  207. package/dist/locators.js +1 -0
  208. package/dist/mocker.d.ts +1 -0
  209. package/dist/mocker.js +1 -0
  210. package/dist/module-evaluator.d.ts +124 -0
  211. package/dist/module-evaluator.js +343 -0
  212. package/dist/module-runner-stub.js +44 -0
  213. package/dist/module-runner.js +17 -0
  214. package/dist/node.d.ts +251 -0
  215. package/dist/node.js +98 -0
  216. package/dist/path.js +7 -0
  217. package/dist/plugins/browser-client.mjs +2 -0
  218. package/dist/plugins/browser-context.mjs +2 -0
  219. package/dist/plugins/browser-locators.mjs +2 -0
  220. package/dist/plugins/browser-playwright.mjs +2 -0
  221. package/dist/plugins/browser-preview.mjs +2 -0
  222. package/dist/plugins/browser-webdriverio.mjs +2 -0
  223. package/dist/plugins/browser.mjs +2 -0
  224. package/dist/plugins/expect.mjs +2 -0
  225. package/dist/plugins/mocker-automock.mjs +2 -0
  226. package/dist/plugins/mocker-browser.mjs +2 -0
  227. package/dist/plugins/mocker-node.mjs +2 -0
  228. package/dist/plugins/mocker-redirect.mjs +2 -0
  229. package/dist/plugins/mocker-register.mjs +2 -0
  230. package/dist/plugins/mocker.mjs +2 -0
  231. package/dist/plugins/pretty-format.mjs +2 -0
  232. package/dist/plugins/runner-types.mjs +2 -0
  233. package/dist/plugins/runner-utils.mjs +2 -0
  234. package/dist/plugins/runner.mjs +2 -0
  235. package/dist/plugins/snapshot-environment.mjs +2 -0
  236. package/dist/plugins/snapshot-manager.mjs +2 -0
  237. package/dist/plugins/snapshot.mjs +2 -0
  238. package/dist/plugins/spy.mjs +2 -0
  239. package/dist/plugins/utils-constants.mjs +2 -0
  240. package/dist/plugins/utils-diff.mjs +2 -0
  241. package/dist/plugins/utils-display.mjs +2 -0
  242. package/dist/plugins/utils-error.mjs +2 -0
  243. package/dist/plugins/utils-helpers.mjs +2 -0
  244. package/dist/plugins/utils-highlight.mjs +2 -0
  245. package/dist/plugins/utils-offset.mjs +2 -0
  246. package/dist/plugins/utils-resolver.mjs +2 -0
  247. package/dist/plugins/utils-serialize.mjs +2 -0
  248. package/dist/plugins/utils-source-map.mjs +2 -0
  249. package/dist/plugins/utils-timers.mjs +2 -0
  250. package/dist/plugins/utils.mjs +2 -0
  251. package/dist/reporters.d.ts +27 -0
  252. package/dist/reporters.js +24 -0
  253. package/dist/runners.d.ts +50 -0
  254. package/dist/runners.js +19 -0
  255. package/dist/shared/screenshotMatcher/types.d.ts +22 -0
  256. package/dist/snapshot.d.ts +9 -0
  257. package/dist/snapshot.js +4 -0
  258. package/dist/spy.js +1 -0
  259. package/dist/state.js +280 -0
  260. package/dist/suite.d.ts +5 -0
  261. package/dist/suite.js +6 -0
  262. package/dist/types.d.ts +69 -0
  263. package/dist/vendor/chai.d.mts +1 -0
  264. package/dist/vendor/chai.mjs +3577 -0
  265. package/dist/vendor/es-module-lexer.d.mts +193 -0
  266. package/dist/vendor/es-module-lexer.mjs +79 -0
  267. package/dist/vendor/estree-walker.d.mts +583 -0
  268. package/dist/vendor/estree-walker.mjs +339 -0
  269. package/dist/vendor/expect-type.d.mts +1574 -0
  270. package/dist/vendor/expect-type.mjs +214 -0
  271. package/dist/vendor/magic-string.d.mts +261 -0
  272. package/dist/vendor/magic-string.mjs +1700 -0
  273. package/dist/vendor/obug.d.mts +56 -0
  274. package/dist/vendor/obug.mjs +276 -0
  275. package/dist/vendor/pathe.d.mts +46 -0
  276. package/dist/vendor/pathe.mjs +496 -0
  277. package/dist/vendor/picomatch.d.mts +1 -0
  278. package/dist/vendor/picomatch.mjs +1855 -0
  279. package/dist/vendor/shared-3g9mwCWP.mjs +31 -0
  280. package/dist/vendor/std-env.d.mts +88 -0
  281. package/dist/vendor/std-env.mjs +159 -0
  282. package/dist/vendor/tinybench.d.mts +317 -0
  283. package/dist/vendor/tinybench.mjs +504 -0
  284. package/dist/vendor/tinyexec.d.mts +72 -0
  285. package/dist/vendor/tinyexec.mjs +637 -0
  286. package/dist/vendor/tinyglobby.d.mts +157 -0
  287. package/dist/vendor/tinyglobby.mjs +832 -0
  288. package/dist/vendor/tinyrainbow.d.mts +60 -0
  289. package/dist/vendor/tinyrainbow.mjs +93 -0
  290. package/dist/vendor/vitest_browser.mjs +2 -0
  291. package/dist/vendor/vitest_internal_browser.mjs +2 -0
  292. package/dist/vendor/vitest_runner.mjs +2 -0
  293. package/dist/vendor/vitest_runners.mjs +2 -0
  294. package/dist/worker.d.ts +32 -0
  295. package/dist/worker.js +48 -0
  296. package/dist/workers/forks.js +54 -0
  297. package/dist/workers/runVmTests.js +95 -0
  298. package/dist/workers/threads.js +55 -0
  299. package/dist/workers/vmForks.js +36 -0
  300. package/dist/workers/vmThreads.js +37 -0
  301. package/environments.d.ts +1 -0
  302. package/globals.d.ts +20 -0
  303. package/import-meta.d.ts +5 -0
  304. package/importMeta.d.ts +4 -0
  305. package/index.cjs +5 -0
  306. package/index.d.cts +1 -0
  307. package/jsdom.d.ts +6 -0
  308. package/mocker.d.ts +1 -0
  309. package/node.d.ts +1 -0
  310. package/optional-types.d.ts +7 -0
  311. package/package.json +335 -0
  312. package/reporters.d.ts +1 -0
  313. package/runners.d.ts +1 -0
  314. package/snapshot.d.ts +1 -0
  315. package/suite.d.ts +1 -0
  316. package/suppress-warnings.cjs +21 -0
  317. package/vitest.mjs +2 -0
  318. package/worker.d.ts +1 -0
@@ -0,0 +1,3513 @@
1
+ import { ManualMockedModule, RedirectedModule, AutomockedModule, AutospiedModule, MockerRegistry } from '../mocker/index.js';
2
+ import { dynamicImportPlugin, ServerMockResolver, interceptorPlugin } from '../mocker/node.js';
3
+ import c from '../../vendor/tinyrainbow.mjs';
4
+ import { isValidApiRequest, isFileServingAllowed, distDir, resolveApiServerConfig, resolveFsAllow, rolldownVersion, createDebugger, createViteLogger, createViteServer } from '../../node.js';
5
+ import { fileURLToPath } from 'node:url';
6
+ import fs, { readFileSync, lstatSync, createReadStream, promises, existsSync } from 'node:fs';
7
+ import { createRequire } from 'node:module';
8
+ import { slash as slash$1, toArray, deepMerge } from '../utils/helpers.js';
9
+ import MagicString from '../../vendor/magic-string.mjs';
10
+ import sirv from 'sirv';
11
+ import { coverageConfigDefaults } from '../../config.js';
12
+ import crypto from 'node:crypto';
13
+ import { readFile as readFile$1, mkdir, writeFile as writeFile$1 } from 'node:fs/promises';
14
+ import { parseErrorStacktrace, parseStacktrace } from '../utils/source-map.js';
15
+ import { resolve as resolve$1, basename as basename$1, dirname as dirname$1 } from 'node:path';
16
+ import { platform } from 'node:os';
17
+ import { PNG } from 'pngjs';
18
+ import pm from 'pixelmatch';
19
+ import { WebSocketServer } from 'ws';
20
+
21
+ var version = "4.0.18";
22
+
23
+ const _DRIVE_LETTER_START_RE = /^[A-Za-z]:\//;
24
+ function normalizeWindowsPath(input = "") {
25
+ if (!input) {
26
+ return input;
27
+ }
28
+ return input.replace(/\\/g, "/").replace(_DRIVE_LETTER_START_RE, (r) => r.toUpperCase());
29
+ }
30
+
31
+ const _UNC_REGEX = /^[/\\]{2}/;
32
+ const _IS_ABSOLUTE_RE = /^[/\\](?![/\\])|^[/\\]{2}(?!\.)|^[A-Za-z]:[/\\]/;
33
+ const _DRIVE_LETTER_RE = /^[A-Za-z]:$/;
34
+ const _ROOT_FOLDER_RE = /^\/([A-Za-z]:)?$/;
35
+ const _EXTNAME_RE = /.(\.[^./]+|\.)$/;
36
+ const normalize = function(path) {
37
+ if (path.length === 0) {
38
+ return ".";
39
+ }
40
+ path = normalizeWindowsPath(path);
41
+ const isUNCPath = path.match(_UNC_REGEX);
42
+ const isPathAbsolute = isAbsolute(path);
43
+ const trailingSeparator = path[path.length - 1] === "/";
44
+ path = normalizeString(path, !isPathAbsolute);
45
+ if (path.length === 0) {
46
+ if (isPathAbsolute) {
47
+ return "/";
48
+ }
49
+ return trailingSeparator ? "./" : ".";
50
+ }
51
+ if (trailingSeparator) {
52
+ path += "/";
53
+ }
54
+ if (_DRIVE_LETTER_RE.test(path)) {
55
+ path += "/";
56
+ }
57
+ if (isUNCPath) {
58
+ if (!isPathAbsolute) {
59
+ return `//./${path}`;
60
+ }
61
+ return `//${path}`;
62
+ }
63
+ return isPathAbsolute && !isAbsolute(path) ? `/${path}` : path;
64
+ };
65
+ const join = function(...segments) {
66
+ let path = "";
67
+ for (const seg of segments) {
68
+ if (!seg) {
69
+ continue;
70
+ }
71
+ if (path.length > 0) {
72
+ const pathTrailing = path[path.length - 1] === "/";
73
+ const segLeading = seg[0] === "/";
74
+ const both = pathTrailing && segLeading;
75
+ if (both) {
76
+ path += seg.slice(1);
77
+ } else {
78
+ path += pathTrailing || segLeading ? seg : `/${seg}`;
79
+ }
80
+ } else {
81
+ path += seg;
82
+ }
83
+ }
84
+ return normalize(path);
85
+ };
86
+ function cwd() {
87
+ if (typeof process !== "undefined" && typeof process.cwd === "function") {
88
+ return process.cwd().replace(/\\/g, "/");
89
+ }
90
+ return "/";
91
+ }
92
+ const resolve = function(...arguments_) {
93
+ arguments_ = arguments_.map((argument) => normalizeWindowsPath(argument));
94
+ let resolvedPath = "";
95
+ let resolvedAbsolute = false;
96
+ for (let index = arguments_.length - 1; index >= -1 && !resolvedAbsolute; index--) {
97
+ const path = index >= 0 ? arguments_[index] : cwd();
98
+ if (!path || path.length === 0) {
99
+ continue;
100
+ }
101
+ resolvedPath = `${path}/${resolvedPath}`;
102
+ resolvedAbsolute = isAbsolute(path);
103
+ }
104
+ resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute);
105
+ if (resolvedAbsolute && !isAbsolute(resolvedPath)) {
106
+ return `/${resolvedPath}`;
107
+ }
108
+ return resolvedPath.length > 0 ? resolvedPath : ".";
109
+ };
110
+ function normalizeString(path, allowAboveRoot) {
111
+ let res = "";
112
+ let lastSegmentLength = 0;
113
+ let lastSlash = -1;
114
+ let dots = 0;
115
+ let char = null;
116
+ for (let index = 0; index <= path.length; ++index) {
117
+ if (index < path.length) {
118
+ char = path[index];
119
+ } else if (char === "/") {
120
+ break;
121
+ } else {
122
+ char = "/";
123
+ }
124
+ if (char === "/") {
125
+ if (lastSlash === index - 1 || dots === 1) ; else if (dots === 2) {
126
+ if (res.length < 2 || lastSegmentLength !== 2 || res[res.length - 1] !== "." || res[res.length - 2] !== ".") {
127
+ if (res.length > 2) {
128
+ const lastSlashIndex = res.lastIndexOf("/");
129
+ if (lastSlashIndex === -1) {
130
+ res = "";
131
+ lastSegmentLength = 0;
132
+ } else {
133
+ res = res.slice(0, lastSlashIndex);
134
+ lastSegmentLength = res.length - 1 - res.lastIndexOf("/");
135
+ }
136
+ lastSlash = index;
137
+ dots = 0;
138
+ continue;
139
+ } else if (res.length > 0) {
140
+ res = "";
141
+ lastSegmentLength = 0;
142
+ lastSlash = index;
143
+ dots = 0;
144
+ continue;
145
+ }
146
+ }
147
+ if (allowAboveRoot) {
148
+ res += res.length > 0 ? "/.." : "..";
149
+ lastSegmentLength = 2;
150
+ }
151
+ } else {
152
+ if (res.length > 0) {
153
+ res += `/${path.slice(lastSlash + 1, index)}`;
154
+ } else {
155
+ res = path.slice(lastSlash + 1, index);
156
+ }
157
+ lastSegmentLength = index - lastSlash - 1;
158
+ }
159
+ lastSlash = index;
160
+ dots = 0;
161
+ } else if (char === "." && dots !== -1) {
162
+ ++dots;
163
+ } else {
164
+ dots = -1;
165
+ }
166
+ }
167
+ return res;
168
+ }
169
+ const isAbsolute = function(p) {
170
+ return _IS_ABSOLUTE_RE.test(p);
171
+ };
172
+ const extname = function(p) {
173
+ if (p === "..") return "";
174
+ const match = _EXTNAME_RE.exec(normalizeWindowsPath(p));
175
+ return match && match[1] || "";
176
+ };
177
+ const relative = function(from, to) {
178
+ const _from = resolve(from).replace(_ROOT_FOLDER_RE, "$1").split("/");
179
+ const _to = resolve(to).replace(_ROOT_FOLDER_RE, "$1").split("/");
180
+ if (_to[0][1] === ":" && _from[0][1] === ":" && _from[0] !== _to[0]) {
181
+ return _to.join("/");
182
+ }
183
+ const _fromCopy = [..._from];
184
+ for (const segment of _fromCopy) {
185
+ if (_to[0] !== segment) {
186
+ break;
187
+ }
188
+ _from.shift();
189
+ _to.shift();
190
+ }
191
+ return [..._from.map(() => ".."), ..._to].join("/");
192
+ };
193
+ const dirname = function(p) {
194
+ const segments = normalizeWindowsPath(p).replace(/\/$/, "").split("/").slice(0, -1);
195
+ if (segments.length === 1 && _DRIVE_LETTER_RE.test(segments[0])) {
196
+ segments[0] += "/";
197
+ }
198
+ return segments.join("/") || (isAbsolute(p) ? "/" : ".");
199
+ };
200
+ const basename = function(p, extension) {
201
+ const segments = normalizeWindowsPath(p).split("/");
202
+ let lastSegment = "";
203
+ for (let i = segments.length - 1; i >= 0; i--) {
204
+ const val = segments[i];
205
+ if (val) {
206
+ lastSegment = val;
207
+ break;
208
+ }
209
+ }
210
+ return extension && lastSegment.endsWith(extension) ? lastSegment.slice(0, -extension.length) : lastSegment;
211
+ };
212
+
213
+ const distRoot = dirname(fileURLToPath(import.meta.url));
214
+
215
+ /// <reference types="../types/index.d.ts" />
216
+
217
+ // (c) 2020-present Andrea Giammarchi
218
+
219
+ const {parse: $parse, stringify: $stringify} = JSON;
220
+ const {keys} = Object;
221
+
222
+ const Primitive = String; // it could be Number
223
+ const primitive = 'string'; // it could be 'number'
224
+
225
+ const ignore = {};
226
+ const object = 'object';
227
+
228
+ const noop = (_, value) => value;
229
+
230
+ const primitives = value => (
231
+ value instanceof Primitive ? Primitive(value) : value
232
+ );
233
+
234
+ const Primitives = (_, value) => (
235
+ typeof value === primitive ? new Primitive(value) : value
236
+ );
237
+
238
+ const revive = (input, parsed, output, $) => {
239
+ const lazy = [];
240
+ for (let ke = keys(output), {length} = ke, y = 0; y < length; y++) {
241
+ const k = ke[y];
242
+ const value = output[k];
243
+ if (value instanceof Primitive) {
244
+ const tmp = input[value];
245
+ if (typeof tmp === object && !parsed.has(tmp)) {
246
+ parsed.add(tmp);
247
+ output[k] = ignore;
248
+ lazy.push({k, a: [input, parsed, tmp, $]});
249
+ }
250
+ else
251
+ output[k] = $.call(output, k, tmp);
252
+ }
253
+ else if (output[k] !== ignore)
254
+ output[k] = $.call(output, k, value);
255
+ }
256
+ for (let {length} = lazy, i = 0; i < length; i++) {
257
+ const {k, a} = lazy[i];
258
+ output[k] = $.call(output, k, revive.apply(null, a));
259
+ }
260
+ return output;
261
+ };
262
+
263
+ const set = (known, input, value) => {
264
+ const index = Primitive(input.push(value) - 1);
265
+ known.set(value, index);
266
+ return index;
267
+ };
268
+
269
+ /**
270
+ * Converts a specialized flatted string into a JS value.
271
+ * @param {string} text
272
+ * @param {(this: any, key: string, value: any) => any} [reviver]
273
+ * @returns {any}
274
+ */
275
+ const parse = (text, reviver) => {
276
+ const input = $parse(text, Primitives).map(primitives);
277
+ const value = input[0];
278
+ const $ = reviver || noop;
279
+ const tmp = typeof value === object && value ?
280
+ revive(input, new Set, value, $) :
281
+ value;
282
+ return $.call({'': tmp}, '', tmp);
283
+ };
284
+
285
+ /**
286
+ * Converts a JS value into a specialized flatted string.
287
+ * @param {any} value
288
+ * @param {((this: any, key: string, value: any) => any) | (string | number)[] | null | undefined} [replacer]
289
+ * @param {string | number | undefined} [space]
290
+ * @returns {string}
291
+ */
292
+ const stringify = (value, replacer, space) => {
293
+ const $ = replacer && typeof replacer === object ?
294
+ (k, v) => (k === '' || -1 < replacer.indexOf(k) ? v : void 0) :
295
+ (replacer || noop);
296
+ const known = new Map;
297
+ const input = [];
298
+ const output = [];
299
+ let i = +set(known, input, $.call({'': value}, '', value));
300
+ let firstRun = !i;
301
+ while (i < input.length) {
302
+ firstRun = true;
303
+ output[i] = $stringify(input[i++], replace, space);
304
+ }
305
+ return '[' + output.join(',') + ']';
306
+ function replace(key, value) {
307
+ if (firstRun) {
308
+ firstRun = !firstRun;
309
+ return value;
310
+ }
311
+ const after = $.call(this, key, value);
312
+ switch (typeof after) {
313
+ case object:
314
+ if (after === null) return after;
315
+ case primitive:
316
+ return known.get(after) || set(known, input, after);
317
+ }
318
+ return after;
319
+ }
320
+ };
321
+
322
+ var DOM_KEY_LOCATION = /*#__PURE__*/ function(DOM_KEY_LOCATION) {
323
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["STANDARD"] = 0] = "STANDARD";
324
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["LEFT"] = 1] = "LEFT";
325
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["RIGHT"] = 2] = "RIGHT";
326
+ DOM_KEY_LOCATION[DOM_KEY_LOCATION["NUMPAD"] = 3] = "NUMPAD";
327
+ return DOM_KEY_LOCATION;
328
+ }({});
329
+
330
+ /**
331
+ * Mapping for a default US-104-QWERTY keyboard
332
+ */ const defaultKeyMap = [
333
+ // alphanumeric block - writing system
334
+ ...'0123456789'.split('').map((c)=>({
335
+ code: `Digit${c}`,
336
+ key: c
337
+ })),
338
+ ...')!@#$%^&*('.split('').map((c, i)=>({
339
+ code: `Digit${i}`,
340
+ key: c,
341
+ shiftKey: true
342
+ })),
343
+ ...'abcdefghijklmnopqrstuvwxyz'.split('').map((c)=>({
344
+ code: `Key${c.toUpperCase()}`,
345
+ key: c
346
+ })),
347
+ ...'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.split('').map((c)=>({
348
+ code: `Key${c}`,
349
+ key: c,
350
+ shiftKey: true
351
+ })),
352
+ {
353
+ code: 'BracketLeft',
354
+ key: '['
355
+ },
356
+ {
357
+ code: 'BracketLeft',
358
+ key: '{',
359
+ shiftKey: true
360
+ },
361
+ {
362
+ code: 'BracketRight',
363
+ key: ']'
364
+ },
365
+ {
366
+ code: 'BracketRight',
367
+ key: '}',
368
+ shiftKey: true
369
+ },
370
+ // alphanumeric block - functional
371
+ {
372
+ code: 'Space',
373
+ key: ' '
374
+ },
375
+ {
376
+ code: 'AltLeft',
377
+ key: 'Alt',
378
+ location: DOM_KEY_LOCATION.LEFT
379
+ },
380
+ {
381
+ code: 'AltRight',
382
+ key: 'Alt',
383
+ location: DOM_KEY_LOCATION.RIGHT
384
+ },
385
+ {
386
+ code: 'ShiftLeft',
387
+ key: 'Shift',
388
+ location: DOM_KEY_LOCATION.LEFT
389
+ },
390
+ {
391
+ code: 'ShiftRight',
392
+ key: 'Shift',
393
+ location: DOM_KEY_LOCATION.RIGHT
394
+ },
395
+ {
396
+ code: 'ControlLeft',
397
+ key: 'Control',
398
+ location: DOM_KEY_LOCATION.LEFT
399
+ },
400
+ {
401
+ code: 'ControlRight',
402
+ key: 'Control',
403
+ location: DOM_KEY_LOCATION.RIGHT
404
+ },
405
+ {
406
+ code: 'MetaLeft',
407
+ key: 'Meta',
408
+ location: DOM_KEY_LOCATION.LEFT
409
+ },
410
+ {
411
+ code: 'MetaRight',
412
+ key: 'Meta',
413
+ location: DOM_KEY_LOCATION.RIGHT
414
+ },
415
+ {
416
+ code: 'OSLeft',
417
+ key: 'OS',
418
+ location: DOM_KEY_LOCATION.LEFT
419
+ },
420
+ {
421
+ code: 'OSRight',
422
+ key: 'OS',
423
+ location: DOM_KEY_LOCATION.RIGHT
424
+ },
425
+ {
426
+ code: 'ContextMenu',
427
+ key: 'ContextMenu'
428
+ },
429
+ {
430
+ code: 'Tab',
431
+ key: 'Tab'
432
+ },
433
+ {
434
+ code: 'CapsLock',
435
+ key: 'CapsLock'
436
+ },
437
+ {
438
+ code: 'Backspace',
439
+ key: 'Backspace'
440
+ },
441
+ {
442
+ code: 'Enter',
443
+ key: 'Enter'
444
+ },
445
+ // function
446
+ {
447
+ code: 'Escape',
448
+ key: 'Escape'
449
+ },
450
+ // arrows
451
+ {
452
+ code: 'ArrowUp',
453
+ key: 'ArrowUp'
454
+ },
455
+ {
456
+ code: 'ArrowDown',
457
+ key: 'ArrowDown'
458
+ },
459
+ {
460
+ code: 'ArrowLeft',
461
+ key: 'ArrowLeft'
462
+ },
463
+ {
464
+ code: 'ArrowRight',
465
+ key: 'ArrowRight'
466
+ },
467
+ // control pad
468
+ {
469
+ code: 'Home',
470
+ key: 'Home'
471
+ },
472
+ {
473
+ code: 'End',
474
+ key: 'End'
475
+ },
476
+ {
477
+ code: 'Delete',
478
+ key: 'Delete'
479
+ },
480
+ {
481
+ code: 'PageUp',
482
+ key: 'PageUp'
483
+ },
484
+ {
485
+ code: 'PageDown',
486
+ key: 'PageDown'
487
+ },
488
+ // Special keys that are not part of a default US-layout but included for specific behavior
489
+ {
490
+ code: 'Fn',
491
+ key: 'Fn'
492
+ },
493
+ {
494
+ code: 'Symbol',
495
+ key: 'Symbol'
496
+ },
497
+ {
498
+ code: 'AltRight',
499
+ key: 'AltGraph'
500
+ }
501
+ ];
502
+
503
+ var bracketDict = /*#__PURE__*/ function(bracketDict) {
504
+ bracketDict["{"] = "}";
505
+ bracketDict["["] = "]";
506
+ return bracketDict;
507
+ }(bracketDict || {});
508
+ /**
509
+ * Read the next key definition from user input
510
+ *
511
+ * Describe key per `{descriptor}` or `[descriptor]`.
512
+ * Everything else will be interpreted as a single character as descriptor - e.g. `a`.
513
+ * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
514
+ * A previously pressed key can be released per `{/descriptor}`.
515
+ * Keeping the key pressed can be written as `{descriptor>}`.
516
+ * When keeping the key pressed you can choose how long the key is pressed `{descriptor>3}`.
517
+ * You can then release the key per `{descriptor>3/}` or keep it pressed and continue with the next key.
518
+ */ function readNextDescriptor(text, context) {
519
+ let pos = 0;
520
+ const startBracket = text[pos] in bracketDict ? text[pos] : '';
521
+ pos += startBracket.length;
522
+ const isEscapedChar = new RegExp(`^\\${startBracket}{2}`).test(text);
523
+ const type = isEscapedChar ? '' : startBracket;
524
+ return {
525
+ type,
526
+ ...type === '' ? readPrintableChar(text, pos) : readTag(text, pos, type)
527
+ };
528
+ }
529
+ function readPrintableChar(text, pos, context) {
530
+ const descriptor = text[pos];
531
+ assertDescriptor(descriptor, text, pos);
532
+ pos += descriptor.length;
533
+ return {
534
+ consumedLength: pos,
535
+ descriptor,
536
+ releasePrevious: false,
537
+ releaseSelf: true,
538
+ repeat: 1
539
+ };
540
+ }
541
+ function readTag(text, pos, startBracket, context) {
542
+ var _text_slice_match, _text_slice_match1;
543
+ const releasePreviousModifier = text[pos] === '/' ? '/' : '';
544
+ pos += releasePreviousModifier.length;
545
+ const escapedDescriptor = startBracket === '{' && text[pos] === '\\';
546
+ pos += Number(escapedDescriptor);
547
+ const descriptor = escapedDescriptor ? text[pos] : (_text_slice_match = text.slice(pos).match(startBracket === '{' ? /^\w+|^[^}>/]/ : /^\w+/)) === null || _text_slice_match === undefined ? undefined : _text_slice_match[0];
548
+ assertDescriptor(descriptor, text, pos);
549
+ pos += descriptor.length;
550
+ var _text_slice_match_;
551
+ const repeatModifier = (_text_slice_match_ = (_text_slice_match1 = text.slice(pos).match(/^>\d+/)) === null || _text_slice_match1 === undefined ? undefined : _text_slice_match1[0]) !== null && _text_slice_match_ !== undefined ? _text_slice_match_ : '';
552
+ pos += repeatModifier.length;
553
+ const releaseSelfModifier = text[pos] === '/' || !repeatModifier && text[pos] === '>' ? text[pos] : '';
554
+ pos += releaseSelfModifier.length;
555
+ const expectedEndBracket = bracketDict[startBracket];
556
+ const endBracket = text[pos] === expectedEndBracket ? expectedEndBracket : '';
557
+ if (!endBracket) {
558
+ throw new Error(getErrorMessage([
559
+ !repeatModifier && 'repeat modifier',
560
+ !releaseSelfModifier && 'release modifier',
561
+ `"${expectedEndBracket}"`
562
+ ].filter(Boolean).join(' or '), text[pos], text));
563
+ }
564
+ pos += endBracket.length;
565
+ return {
566
+ consumedLength: pos,
567
+ descriptor,
568
+ releasePrevious: !!releasePreviousModifier,
569
+ repeat: repeatModifier ? Math.max(Number(repeatModifier.substr(1)), 1) : 1,
570
+ releaseSelf: hasReleaseSelf(releaseSelfModifier, repeatModifier)
571
+ };
572
+ }
573
+ function assertDescriptor(descriptor, text, pos, context) {
574
+ if (!descriptor) {
575
+ throw new Error(getErrorMessage('key descriptor', text[pos], text));
576
+ }
577
+ }
578
+ function hasReleaseSelf(releaseSelfModifier, repeatModifier) {
579
+ if (releaseSelfModifier) {
580
+ return releaseSelfModifier === '/';
581
+ }
582
+ if (repeatModifier) {
583
+ return false;
584
+ }
585
+ }
586
+ function getErrorMessage(expected, found, text, context) {
587
+ return `Expected ${expected} but found "${found !== null && found !== undefined ? found : ''}" in "${text}"
588
+ See ${`https://testing-library.com/docs/user-event/keyboard`}
589
+ for more information about how userEvent parses your input.`;
590
+ }
591
+
592
+ /**
593
+ * Parse key definitions per `keyboardMap`
594
+ *
595
+ * Keys can be referenced by `{key}` or `{special}` as well as physical locations per `[code]`.
596
+ * Everything else will be interpreted as a typed character - e.g. `a`.
597
+ * Brackets `{` and `[` can be escaped by doubling - e.g. `foo[[bar` translates to `foo[bar`.
598
+ * Keeping the key pressed can be written as `{key>}`.
599
+ * When keeping the key pressed you can choose how long (how many keydown and keypress) the key is pressed `{key>3}`.
600
+ * You can then release the key per `{key>3/}` or keep it pressed and continue with the next key.
601
+ */ function parseKeyDef$1(keyboardMap, text) {
602
+ const defs = [];
603
+ do {
604
+ const { type, descriptor, consumedLength, releasePrevious, releaseSelf = true, repeat } = readNextDescriptor(text);
605
+ var _keyboardMap_find;
606
+ const keyDef = (_keyboardMap_find = keyboardMap.find((def)=>{
607
+ if (type === '[') {
608
+ var _def_code;
609
+ return ((_def_code = def.code) === null || _def_code === undefined ? undefined : _def_code.toLowerCase()) === descriptor.toLowerCase();
610
+ } else if (type === '{') {
611
+ var _def_key;
612
+ return ((_def_key = def.key) === null || _def_key === undefined ? undefined : _def_key.toLowerCase()) === descriptor.toLowerCase();
613
+ }
614
+ return def.key === descriptor;
615
+ })) !== null && _keyboardMap_find !== undefined ? _keyboardMap_find : {
616
+ key: 'Unknown',
617
+ code: 'Unknown',
618
+ [type === '[' ? 'code' : 'key']: descriptor
619
+ };
620
+ defs.push({
621
+ keyDef,
622
+ releasePrevious,
623
+ releaseSelf,
624
+ repeat
625
+ });
626
+ text = text.slice(consumedLength);
627
+ }while (text)
628
+ return defs;
629
+ }
630
+
631
+ function parseKeyDef(text) {
632
+ return parseKeyDef$1(defaultKeyMap, text);
633
+ }
634
+ function replacer(code, values) {
635
+ return code.replace(/\{\s*(\w+)\s*\}/g, (_, key) => values[key] ?? _);
636
+ }
637
+ function resolveScreenshotPath(testPath, name, config, customPath) {
638
+ if (customPath) {
639
+ return resolve(dirname(testPath), customPath);
640
+ }
641
+ const dir = dirname(testPath);
642
+ const base = basename(testPath);
643
+ if (config.browser.screenshotDirectory) {
644
+ return resolve(config.browser.screenshotDirectory, relative(config.root, dir), base, name);
645
+ }
646
+ return resolve(dir, "__screenshots__", base, name);
647
+ }
648
+ async function getBrowserProvider(options, project) {
649
+ const browser = project.config.browser.name;
650
+ const name = project.name ? `[${project.name}] ` : "";
651
+ if (!browser) {
652
+ throw new Error(`${name}Browser name is required. Please, set \`test.browser.instances[].browser\` option manually.`);
653
+ }
654
+ if (options.provider == null) {
655
+ throw new Error(`Browser Mode requires the "provider" to always be specified.`);
656
+ }
657
+ const supportedBrowsers = options.provider.supportedBrowser || [];
658
+ if (supportedBrowsers.length && !supportedBrowsers.includes(browser)) {
659
+ throw new Error(`${name}Browser "${browser}" is not supported by the browser provider "${options.provider.name}". Supported browsers: ${supportedBrowsers.join(", ")}.`);
660
+ }
661
+ if (typeof options.provider.providerFactory !== "function") {
662
+ throw new TypeError(`The "${name}" browser provider does not provide a "providerFactory" function. Received ${typeof options.provider.providerFactory}.`);
663
+ }
664
+ return options.provider.providerFactory(project);
665
+ }
666
+ function slash(path) {
667
+ return path.replace(/\\/g, "/").replace(/\/+/g, "/");
668
+ }
669
+
670
+ async function resolveOrchestrator(globalServer, url, res) {
671
+ let sessionId = url.searchParams.get("sessionId");
672
+ // it's possible to open the page without a context
673
+ if (!sessionId) {
674
+ const contexts = [...globalServer.children].flatMap((p) => [...p.state.orchestrators.keys()]);
675
+ sessionId = contexts.at(-1) ?? "none";
676
+ }
677
+ // it's ok to not have a session here, especially in the preview provider
678
+ // because the user could refresh the page which would remove the session id from the url
679
+ const session = globalServer.vitest._browserSessions.getSession(sessionId);
680
+ const browserProject = session?.project.browser || [...globalServer.children][0];
681
+ if (!browserProject) {
682
+ return;
683
+ }
684
+ // ignore unknown pages
685
+ if (sessionId && sessionId !== "none" && !globalServer.vitest._browserSessions.sessionIds.has(sessionId)) {
686
+ return;
687
+ }
688
+ const injectorJs = typeof globalServer.injectorJs === "string" ? globalServer.injectorJs : await globalServer.injectorJs;
689
+ const injector = replacer(injectorJs, {
690
+ __VITEST_PROVIDER__: JSON.stringify(browserProject.config.browser.provider?.name || "preview"),
691
+ __VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
692
+ __VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root }),
693
+ __VITEST_METHOD__: JSON.stringify("orchestrate"),
694
+ __VITEST_TYPE__: "\"orchestrator\"",
695
+ __VITEST_SESSION_ID__: JSON.stringify(sessionId),
696
+ __VITEST_TESTER_ID__: "\"none\"",
697
+ __VITEST_OTEL_CARRIER__: url.searchParams.get("otelCarrier") ?? "null",
698
+ __VITEST_PROVIDED_CONTEXT__: JSON.stringify(stringify(browserProject.project.getProvidedContext())),
699
+ __VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
700
+ });
701
+ // disable CSP for the orchestrator as we are the ones controlling it
702
+ res.removeHeader("Content-Security-Policy");
703
+ if (!globalServer.orchestratorScripts) {
704
+ globalServer.orchestratorScripts = (await globalServer.formatScripts(globalServer.config.browser.orchestratorScripts)).map((script) => {
705
+ let html = "<script ";
706
+ for (const attr in script.attrs || {}) {
707
+ html += `${attr}="${script.attrs[attr]}" `;
708
+ }
709
+ html += `>${script.children}<\/script>`;
710
+ return html;
711
+ }).join("\n");
712
+ }
713
+ let baseHtml = typeof globalServer.orchestratorHtml === "string" ? globalServer.orchestratorHtml : await globalServer.orchestratorHtml;
714
+ // if UI is enabled, use UI HTML and inject the orchestrator script
715
+ if (globalServer.config.browser.ui) {
716
+ const manifestContent = globalServer.manifest instanceof Promise ? await globalServer.manifest : globalServer.manifest;
717
+ const jsEntry = manifestContent["orchestrator.html"].file;
718
+ const base = browserProject.parent.vite.config.base || "/";
719
+ baseHtml = baseHtml.replace("href=\"./favicon.ico\"", `href="${base}__vitest__/favicon.ico"`).replace("href=\"./favicon.svg\"", `href="${base}__vitest__/favicon.svg"`).replaceAll("./assets/", `${base}__vitest__/assets/`).replace("<!-- !LOAD_METADATA! -->", [
720
+ "{__VITEST_INJECTOR__}",
721
+ "{__VITEST_ERROR_CATCHER__}",
722
+ "{__VITEST_SCRIPTS__}",
723
+ `<script type="module" crossorigin src="${base}${jsEntry}"><\/script>`
724
+ ].join("\n"));
725
+ }
726
+ return replacer(baseHtml, {
727
+ __VITEST_FAVICON__: globalServer.faviconUrl,
728
+ __VITEST_TITLE__: "Vitest Browser Runner",
729
+ __VITEST_SCRIPTS__: globalServer.orchestratorScripts,
730
+ __VITEST_INJECTOR__: `<script type="module">${injector}<\/script>`,
731
+ __VITEST_ERROR_CATCHER__: `<script type="module" src="${globalServer.errorCatcherUrl}"><\/script>`,
732
+ __VITEST_SESSION_ID__: JSON.stringify(sessionId)
733
+ });
734
+ }
735
+
736
+ function disableCache(res) {
737
+ res.setHeader("Cache-Control", "no-cache, max-age=0, must-revalidate");
738
+ res.setHeader("Content-Type", "text/html; charset=utf-8");
739
+ }
740
+ function allowIframes(res) {
741
+ // remove custom iframe related headers to allow the iframe to load
742
+ res.removeHeader("X-Frame-Options");
743
+ }
744
+
745
+ function createOrchestratorMiddleware(parentServer) {
746
+ return async function vitestOrchestratorMiddleware(req, res, next) {
747
+ if (!req.url) {
748
+ return next();
749
+ }
750
+ const url = new URL(req.url, "http://localhost");
751
+ if (url.pathname !== parentServer.prefixOrchestratorUrl) {
752
+ return next();
753
+ }
754
+ const html = await resolveOrchestrator(parentServer, url, res);
755
+ if (html) {
756
+ disableCache(res);
757
+ allowIframes(res);
758
+ res.write(html, "utf-8");
759
+ res.end();
760
+ }
761
+ };
762
+ }
763
+
764
+ async function resolveTester(globalServer, url, res, next) {
765
+ const csp = res.getHeader("Content-Security-Policy");
766
+ if (typeof csp === "string") {
767
+ // add frame-ancestors to allow the iframe to be loaded by Vitest,
768
+ // but keep the rest of the CSP
769
+ res.setHeader("Content-Security-Policy", csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *"));
770
+ }
771
+ const sessionId = url.searchParams.get("sessionId") || "none";
772
+ const session = globalServer.vitest._browserSessions.getSession(sessionId);
773
+ if (!session) {
774
+ res.statusCode = 400;
775
+ res.end("Invalid session ID");
776
+ return;
777
+ }
778
+ const project = globalServer.vitest.getProjectByName(session.project.name || "");
779
+ const browserProject = project.browser || [...globalServer.children][0];
780
+ if (!browserProject) {
781
+ res.statusCode = 400;
782
+ res.end("Invalid session ID");
783
+ return;
784
+ }
785
+ const injectorJs = typeof globalServer.injectorJs === "string" ? globalServer.injectorJs : await globalServer.injectorJs;
786
+ const injector = replacer(injectorJs, {
787
+ __VITEST_PROVIDER__: JSON.stringify(project.browser.provider.name),
788
+ __VITEST_CONFIG__: JSON.stringify(browserProject.wrapSerializedConfig()),
789
+ __VITEST_VITE_CONFIG__: JSON.stringify({ root: browserProject.vite.config.root }),
790
+ __VITEST_TYPE__: "\"tester\"",
791
+ __VITEST_METHOD__: JSON.stringify("none"),
792
+ __VITEST_SESSION_ID__: JSON.stringify(sessionId),
793
+ __VITEST_OTEL_CARRIER__: JSON.stringify(null),
794
+ __VITEST_TESTER_ID__: JSON.stringify(crypto.randomUUID()),
795
+ __VITEST_PROVIDED_CONTEXT__: "{}",
796
+ __VITEST_API_TOKEN__: JSON.stringify(globalServer.vitest.config.api.token)
797
+ });
798
+ const testerHtml = typeof browserProject.testerHtml === "string" ? browserProject.testerHtml : await browserProject.testerHtml;
799
+ try {
800
+ const url = join("/@fs/", browserProject.testerFilepath);
801
+ const indexhtml = await browserProject.vite.transformIndexHtml(url, testerHtml);
802
+ const html = replacer(indexhtml, {
803
+ __VITEST_FAVICON__: globalServer.faviconUrl,
804
+ __VITEST_INJECTOR__: injector
805
+ });
806
+ return html;
807
+ } catch (err) {
808
+ session.fail(err);
809
+ next(err);
810
+ }
811
+ }
812
+
813
+ function createTesterMiddleware(browserServer) {
814
+ return async function vitestTesterMiddleware(req, res, next) {
815
+ if (!req.url) {
816
+ return next();
817
+ }
818
+ const url = new URL(req.url, "http://localhost");
819
+ if (url.pathname !== browserServer.prefixTesterUrl || !url.searchParams.has("sessionId")) {
820
+ return next();
821
+ }
822
+ const html = await resolveTester(browserServer, url, res, next);
823
+ if (html) {
824
+ disableCache(res);
825
+ allowIframes(res);
826
+ res.write(html, "utf-8");
827
+ res.end();
828
+ }
829
+ };
830
+ }
831
+
832
+ const VIRTUAL_ID_CONTEXT = "\0vitest/browser";
833
+ const ID_CONTEXT = "vitest/browser";
834
+ // for libraries that use an older import but are not type checked
835
+ const DEPRECATED_ID_CONTEXT = "@vitest/browser/context";
836
+ const DEPRECATED_VIRTUAL_ID_UTILS = "\0@vitest/browser/utils";
837
+ const DEPRECATED_ID_UTILS = "@vitest/browser/utils";
838
+ const __dirname$1 = dirname(fileURLToPath(import.meta.url));
839
+ function BrowserContext(globalServer) {
840
+ return {
841
+ name: "vitest:browser:virtual-module:context",
842
+ enforce: "pre",
843
+ resolveId(id, importer) {
844
+ if (id === ID_CONTEXT || id === "@voidzero-dev/vite-plus-test/browser" || id === "vite-plus/test/browser") {
845
+ return VIRTUAL_ID_CONTEXT;
846
+ }
847
+ if (id === DEPRECATED_ID_CONTEXT) {
848
+ if (importer && !importer.includes("/node_modules/")) {
849
+ globalServer.vitest.logger.deprecate(`${importer} tries to load a deprecated "${id}" module. ` + `This import will stop working in the next major version. ` + `Please, use "vitest/browser" instead.`);
850
+ }
851
+ return VIRTUAL_ID_CONTEXT;
852
+ }
853
+ if (id === DEPRECATED_ID_UTILS) {
854
+ return DEPRECATED_VIRTUAL_ID_UTILS;
855
+ }
856
+ },
857
+ load(id) {
858
+ if (id === VIRTUAL_ID_CONTEXT) {
859
+ return generateContextFile.call(this, globalServer);
860
+ }
861
+ if (id === DEPRECATED_VIRTUAL_ID_UTILS) {
862
+ return `
863
+ import { utils } from 'vitest/browser'
864
+ export const getElementLocatorSelectors = utils.getElementLocatorSelectors
865
+ export const debug = utils.debug
866
+ export const prettyDOM = utils.prettyDOM
867
+ export const getElementError = utils.getElementError
868
+ `;
869
+ }
870
+ }
871
+ };
872
+ }
873
+ async function generateContextFile(globalServer) {
874
+ const commands = Object.keys(globalServer.commands);
875
+ const provider = [...globalServer.children][0].provider;
876
+ const providerName = provider?.name || "preview";
877
+ const commandsCode = commands.filter((command) => !command.startsWith("__vitest")).map((command) => {
878
+ return ` ["${command}"]: (...args) => __vitest_browser_runner__.commands.triggerCommand("${command}", args),`;
879
+ }).join("\n");
880
+ const userEventNonProviderImport = await getUserEventImport(provider, this.resolve.bind(this));
881
+ const distContextPath = slash$1(`/@fs/${resolve(__dirname$1, "context.js")}`);
882
+ return `
883
+ import { page, createUserEvent, cdp, locators, utils } from '${distContextPath}'
884
+ ${userEventNonProviderImport}
885
+
886
+ export const server = {
887
+ platform: ${JSON.stringify(process.platform)},
888
+ version: ${JSON.stringify(process.version)},
889
+ provider: ${JSON.stringify(providerName)},
890
+ browser: __vitest_browser_runner__.config.browser.name,
891
+ commands: {
892
+ ${commandsCode}
893
+ },
894
+ config: __vitest_browser_runner__.config,
895
+ }
896
+ export const commands = server.commands
897
+ export const userEvent = createUserEvent(_userEventSetup)
898
+ export { page, cdp, locators, utils }
899
+ `;
900
+ }
901
+ async function getUserEventImport(provider, resolve) {
902
+ if (!provider || provider.name !== "preview") {
903
+ return "const _userEventSetup = undefined";
904
+ }
905
+ const previewDistRoot = provider.distRoot;
906
+ const resolved = await resolve("@testing-library/user-event", previewDistRoot);
907
+ if (!resolved) {
908
+ throw new Error(`Failed to resolve user-event package from ${previewDistRoot}`);
909
+ }
910
+ return `\
911
+ import { userEvent as __vitest_user_event__ } from '${slash$1(`/@fs/${resolved.id}`)}'
912
+ const _userEventSetup = __vitest_user_event__
913
+ `;
914
+ }
915
+
916
+ const versionRegexp = /(?:\?|&)v=\w{8}/;
917
+ var BrowserPlugin = (parentServer, base = "/") => {
918
+ function isPackageExists(pkg, root) {
919
+ return parentServer.vitest.packageInstaller.isPackageExists?.(pkg, { paths: [root] });
920
+ }
921
+ return [
922
+ {
923
+ name: 'vitest:vendor-aliases',
924
+ enforce: 'pre',
925
+ resolveId(id) {
926
+ // distRoot is @vitest/browser/, packageRoot is the actual dist/ directory
927
+ const packageRoot = resolve(distRoot, '../..');
928
+ // Resolve module-runner to a browser-safe stub
929
+ // This is critical: module-runner contains Node.js-only code (process.platform, etc.)
930
+ // that causes browsers to hang when loaded
931
+ if (id === '@voidzero-dev/vite-plus-core/module-runner' || id === 'vite/module-runner') {
932
+ return resolve(packageRoot, 'module-runner-stub.js');
933
+ }
934
+ // Mark vite/core as external to prevent Node.js-only code from being bundled
935
+ // This prevents __vite__injectQuery duplication errors in browser tests
936
+ if (id === '@voidzero-dev/vite-plus-core' || id === 'vite') {
937
+ return { id, external: true };
938
+ }
939
+ // Handle vitest/browser and package aliases
940
+ // Return virtual module ID so BrowserContext plugin can load it
941
+ // Supports: vitest/browser, @voidzero-dev/vite-plus-test/browser, vite-plus/test/browser
942
+ if (id === 'vitest/browser' || id === '@voidzero-dev/vite-plus-test/browser' || id === 'vite-plus/test/browser') {
943
+ return '\0vitest/browser';
944
+ }
945
+ // Handle vitest/* subpaths (resolve to our dist files)
946
+ // Also handle @voidzero-dev package aliases that resolve to the same files
947
+ const vitestSubpathMap = {
948
+ 'vitest': resolve(packageRoot, 'index.js'),
949
+ '@voidzero-dev/vite-plus-test': resolve(packageRoot, 'index.js'),
950
+ 'vite-plus/test': resolve(packageRoot, 'index.js'),
951
+ 'vitest/node': resolve(packageRoot, 'node.js'),
952
+ 'vitest/config': resolve(packageRoot, 'config.js'),
953
+ 'vitest/internal/browser': resolve(packageRoot, 'browser.js'),
954
+ 'vitest/runners': resolve(packageRoot, 'runners.js'),
955
+ 'vitest/suite': resolve(packageRoot, 'suite.js'),
956
+ 'vitest/environments': resolve(packageRoot, 'environments.js'),
957
+ 'vitest/coverage': resolve(packageRoot, 'coverage.js'),
958
+ 'vitest/reporters': resolve(packageRoot, 'reporters.js'),
959
+ 'vitest/snapshot': resolve(packageRoot, 'snapshot.js'),
960
+ 'vitest/mocker': resolve(packageRoot, 'mocker.js'),
961
+ // Browser providers - resolve to our bundled @vitest/browser-* packages
962
+ 'vitest/browser-playwright': resolve(packageRoot, '@vitest/browser-playwright/index.js'),
963
+ 'vitest/browser-webdriverio': resolve(packageRoot, '@vitest/browser-webdriverio/index.js'),
964
+ 'vitest/browser-preview': resolve(packageRoot, '@vitest/browser-preview/index.js'),
965
+ };
966
+ if (vitestSubpathMap[id]) {
967
+ return vitestSubpathMap[id];
968
+ }
969
+ // Handle @voidzero-dev/vite-plus-test/* subpaths (same as vitest/*)
970
+ if (id.startsWith('@voidzero-dev/vite-plus-test/')) {
971
+ const subpath = id.slice('@voidzero-dev/vite-plus-test/'.length);
972
+ const vitestEquiv = 'vitest/' + subpath;
973
+ if (vitestSubpathMap[vitestEquiv]) {
974
+ return vitestSubpathMap[vitestEquiv];
975
+ }
976
+ }
977
+ // Handle vite-plus/test/* subpaths (CLI package paths, same as vitest/*)
978
+ if (id.startsWith('vite-plus/test/')) {
979
+ const subpath = id.slice('vite-plus/test/'.length);
980
+ const vitestEquiv = 'vitest/' + subpath;
981
+ if (vitestSubpathMap[vitestEquiv]) {
982
+ return vitestSubpathMap[vitestEquiv];
983
+ }
984
+ }
985
+ // Handle @vitest/* packages (resolve to our copied files)
986
+ const vendorMap = {
987
+ '@vitest/runner': resolve(distRoot, '@vitest/runner/index.js'),
988
+ '@vitest/runner/utils': resolve(distRoot, '@vitest/runner/utils.js'),
989
+ '@vitest/runner/types': resolve(distRoot, '@vitest/runner/types.js'),
990
+ '@vitest/utils': resolve(distRoot, '@vitest/utils/index.js'),
991
+ '@vitest/utils/source-map': resolve(distRoot, '@vitest/utils/source-map.js'),
992
+ '@vitest/utils/error': resolve(distRoot, '@vitest/utils/error.js'),
993
+ '@vitest/utils/helpers': resolve(distRoot, '@vitest/utils/helpers.js'),
994
+ '@vitest/utils/display': resolve(distRoot, '@vitest/utils/display.js'),
995
+ '@vitest/utils/timers': resolve(distRoot, '@vitest/utils/timers.js'),
996
+ '@vitest/utils/highlight': resolve(distRoot, '@vitest/utils/highlight.js'),
997
+ '@vitest/utils/offset': resolve(distRoot, '@vitest/utils/offset.js'),
998
+ '@vitest/utils/resolver': resolve(distRoot, '@vitest/utils/resolver.js'),
999
+ '@vitest/utils/serialize': resolve(distRoot, '@vitest/utils/serialize.js'),
1000
+ '@vitest/utils/constants': resolve(distRoot, '@vitest/utils/constants.js'),
1001
+ '@vitest/utils/diff': resolve(distRoot, '@vitest/utils/diff.js'),
1002
+ '@vitest/spy': resolve(distRoot, '@vitest/spy/index.js'),
1003
+ '@vitest/expect': resolve(distRoot, '@vitest/expect/index.js'),
1004
+ '@vitest/snapshot': resolve(distRoot, '@vitest/snapshot/index.js'),
1005
+ '@vitest/snapshot/environment': resolve(distRoot, '@vitest/snapshot/environment.js'),
1006
+ '@vitest/snapshot/manager': resolve(distRoot, '@vitest/snapshot/manager.js'),
1007
+ '@vitest/mocker': resolve(distRoot, '@vitest/mocker/index.js'),
1008
+ '@vitest/mocker/node': resolve(distRoot, '@vitest/mocker/node.js'),
1009
+ '@vitest/mocker/browser': resolve(distRoot, '@vitest/mocker/browser.js'),
1010
+ '@vitest/mocker/redirect': resolve(distRoot, '@vitest/mocker/redirect.js'),
1011
+ '@vitest/mocker/automock': resolve(distRoot, '@vitest/mocker/automock.js'),
1012
+ '@vitest/mocker/register': resolve(distRoot, '@vitest/mocker/register.js'),
1013
+ '@vitest/pretty-format': resolve(distRoot, '@vitest/pretty-format/index.js'),
1014
+ '@vitest/browser': resolve(distRoot, '@vitest/browser/index.js'),
1015
+ '@vitest/browser/context': resolve(distRoot, '@vitest/browser/context.js'),
1016
+ '@vitest/browser/client': resolve(distRoot, '@vitest/browser/client.js'),
1017
+ '@vitest/browser/locators': resolve(distRoot, '@vitest/browser/locators.js'),
1018
+ '@vitest/browser-playwright': resolve(distRoot, '@vitest/browser-playwright/index.js'),
1019
+ '@vitest/browser-webdriverio': resolve(distRoot, '@vitest/browser-webdriverio/index.js'),
1020
+ '@vitest/browser-preview': resolve(distRoot, '@vitest/browser-preview/index.js')
1021
+ };
1022
+ if (vendorMap[id]) {
1023
+ return vendorMap[id];
1024
+ }
1025
+ }
1026
+ },
1027
+ {
1028
+ enforce: "pre",
1029
+ name: "vitest:browser",
1030
+ async configureServer(server) {
1031
+ parentServer.setServer(server);
1032
+ // eslint-disable-next-line prefer-arrow-callback
1033
+ server.middlewares.use(function vitestHeaders(_req, res, next) {
1034
+ const headers = server.config.server.headers;
1035
+ if (headers) {
1036
+ for (const name in headers) {
1037
+ res.setHeader(name, headers[name]);
1038
+ }
1039
+ }
1040
+ next();
1041
+ });
1042
+ server.middlewares.use(createOrchestratorMiddleware(parentServer));
1043
+ server.middlewares.use(createTesterMiddleware(parentServer));
1044
+ server.middlewares.use(`${base}favicon.svg`, (_, res) => {
1045
+ const content = readFileSync(resolve(distRoot, "client/favicon.svg"));
1046
+ res.write(content, "utf-8");
1047
+ res.end();
1048
+ });
1049
+ const coverageFolder = resolveCoverageFolder(parentServer.vitest);
1050
+ const coveragePath = coverageFolder ? coverageFolder[1] : undefined;
1051
+ if (coveragePath && base === coveragePath) {
1052
+ throw new Error(`The ui base path and the coverage path cannot be the same: ${base}, change coverage.reportsDirectory`);
1053
+ }
1054
+ if (coverageFolder) {
1055
+ server.middlewares.use(coveragePath, sirv(coverageFolder[0], {
1056
+ single: true,
1057
+ dev: true,
1058
+ setHeaders: (res) => {
1059
+ const csp = res.getHeader("Content-Security-Policy");
1060
+ if (typeof csp === "string") {
1061
+ // add frame-ancestors to allow the iframe to be loaded by Vitest,
1062
+ // but keep the rest of the CSP
1063
+ res.setHeader("Content-Security-Policy", csp.replace(/frame-ancestors [^;]+/, "frame-ancestors *"));
1064
+ }
1065
+ res.setHeader("Cache-Control", "public,max-age=0,must-revalidate");
1066
+ }
1067
+ }));
1068
+ }
1069
+ const uiEnabled = parentServer.config.browser.ui;
1070
+ if (uiEnabled) {
1071
+ // eslint-disable-next-line prefer-arrow-callback
1072
+ server.middlewares.use(`${base}__screenshot-error`, function vitestBrowserScreenshotError(req, res) {
1073
+ if (!req.url) {
1074
+ res.statusCode = 404;
1075
+ res.end();
1076
+ return;
1077
+ }
1078
+ const url = new URL(req.url, "http://localhost");
1079
+ const id = url.searchParams.get("id");
1080
+ if (!id) {
1081
+ res.statusCode = 404;
1082
+ res.end();
1083
+ return;
1084
+ }
1085
+ const task = parentServer.vitest.state.idMap.get(id);
1086
+ const file = task?.meta.failScreenshotPath;
1087
+ if (!file) {
1088
+ res.statusCode = 404;
1089
+ res.end();
1090
+ return;
1091
+ }
1092
+ let stat;
1093
+ try {
1094
+ stat = lstatSync(file);
1095
+ } catch {}
1096
+ if (!stat?.isFile()) {
1097
+ res.statusCode = 404;
1098
+ res.end();
1099
+ return;
1100
+ }
1101
+ const ext = extname(file);
1102
+ const buffer = readFileSync(file);
1103
+ res.setHeader("Cache-Control", "public,max-age=0,must-revalidate");
1104
+ res.setHeader("Content-Length", buffer.length);
1105
+ res.setHeader("Content-Type", ext === "jpeg" || ext === "jpg" ? "image/jpeg" : ext === "webp" ? "image/webp" : "image/png");
1106
+ res.end(buffer);
1107
+ });
1108
+ }
1109
+ server.middlewares.use((req, res, next) => {
1110
+ // 9000 mega head move
1111
+ // Vite always caches optimized dependencies, but users might mock
1112
+ // them in _some_ tests, while keeping original modules in others
1113
+ // there is no way to configure that in Vite, so we patch it here
1114
+ // to always ignore the cache-control set by Vite in the next middleware
1115
+ if (req.url && versionRegexp.test(req.url) && !req.url.includes("chunk-")) {
1116
+ res.setHeader("Cache-Control", "no-cache");
1117
+ const setHeader = res.setHeader.bind(res);
1118
+ res.setHeader = function(name, value) {
1119
+ if (name === "Cache-Control") {
1120
+ return res;
1121
+ }
1122
+ return setHeader(name, value);
1123
+ };
1124
+ }
1125
+ next();
1126
+ });
1127
+ // handle attachments the same way as in packages/ui/node/index.ts
1128
+ server.middlewares.use((req, res, next) => {
1129
+ if (!req.url) {
1130
+ return next();
1131
+ }
1132
+ const url = new URL(req.url, "http://localhost");
1133
+ if (url.pathname !== "/__vitest_attachment__") {
1134
+ return next();
1135
+ }
1136
+ const path = url.searchParams.get("path");
1137
+ const contentType = url.searchParams.get("contentType");
1138
+ if (!isValidApiRequest(parentServer.config, req) || !contentType || !path) {
1139
+ return next();
1140
+ }
1141
+ const fsPath = decodeURIComponent(path);
1142
+ if (!isFileServingAllowed(parentServer.vite.config, fsPath)) {
1143
+ return next();
1144
+ }
1145
+ try {
1146
+ res.setHeader("content-type", contentType);
1147
+ return createReadStream(fsPath).pipe(res).on("close", () => res.end());
1148
+ } catch (err) {
1149
+ return next(err);
1150
+ }
1151
+ });
1152
+ }
1153
+ },
1154
+ {
1155
+ name: "vitest:browser:tests",
1156
+ enforce: "pre",
1157
+ async config() {
1158
+ // this plugin can be used in different projects, but all of them
1159
+ // have the same `include` pattern, so it doesn't matter which project we use
1160
+ const project = parentServer.project;
1161
+ const { testFiles: browserTestFiles } = await project.globTestFiles();
1162
+ const setupFiles = toArray(project.config.setupFiles);
1163
+ // replace env values - cannot be reassign at runtime
1164
+ const define = {};
1165
+ for (const env in project.config.env || {}) {
1166
+ const stringValue = JSON.stringify(project.config.env[env]);
1167
+ define[`import.meta.env.${env}`] = stringValue;
1168
+ }
1169
+ const entries = [
1170
+ ...browserTestFiles,
1171
+ ...setupFiles,
1172
+ resolve(distDir, "index.js"),
1173
+ resolve(distDir, "browser.js"),
1174
+ resolve(distDir, "runners.js"),
1175
+ resolve(distDir, "utils.js"),
1176
+ ...project.config.snapshotSerializers || []
1177
+ ];
1178
+ const exclude = [
1179
+ "@vitest/browser",
1180
+ "@vitest/ui",
1181
+ "@vitest/ui/reporter",
1182
+ "@vitest/mocker/node",
1183
+ "@voidzero-dev/vite-plus-test",
1184
+ "@voidzero-dev/vite-plus-test/browser",
1185
+ "@voidzero-dev/vite-plus-test/browser/context",
1186
+ "vite-plus/test",
1187
+ "vite-plus/test/browser",
1188
+ "vite-plus/test/browser/context",
1189
+ "vite",
1190
+ "@voidzero-dev/vite-plus-core",
1191
+ "@voidzero-dev/vite-plus-core/module-runner",
1192
+ "lightningcss",
1193
+ "@tailwindcss/oxide",
1194
+ "tailwindcss",
1195
+ "vitest",
1196
+ "vitest/browser",
1197
+ "vitest/internal/browser",
1198
+ "vitest/runners",
1199
+ "vite/module-runner",
1200
+ "@vitest/browser/utils",
1201
+ "@vitest/browser/context",
1202
+ "@vitest/browser/client",
1203
+ "@vitest/utils",
1204
+ "@vitest/utils/source-map",
1205
+ "@vitest/runner",
1206
+ "@vitest/spy",
1207
+ "@vitest/utils/error",
1208
+ "@vitest/snapshot",
1209
+ "@vitest/expect",
1210
+ "std-env",
1211
+ "tinybench",
1212
+ "tinyspy",
1213
+ "tinyrainbow",
1214
+ "pathe",
1215
+ "msw",
1216
+ "msw/browser"
1217
+ ];
1218
+ if (typeof project.config.diff === "string") {
1219
+ entries.push(project.config.diff);
1220
+ }
1221
+ if (parentServer.vitest.coverageProvider) {
1222
+ const coverage = parentServer.vitest._coverageOptions;
1223
+ const provider = coverage.provider;
1224
+ if (provider === "v8") {
1225
+ const path = tryResolve("@vitest/coverage-v8", [parentServer.config.root]);
1226
+ if (path) {
1227
+ entries.push(path);
1228
+ exclude.push("@vitest/coverage-v8/browser");
1229
+ }
1230
+ } else if (provider === "istanbul") {
1231
+ const path = tryResolve("@vitest/coverage-istanbul", [parentServer.config.root]);
1232
+ if (path) {
1233
+ entries.push(path);
1234
+ exclude.push("@vitest/coverage-istanbul");
1235
+ }
1236
+ } else if (provider === "custom" && coverage.customProviderModule) {
1237
+ entries.push(coverage.customProviderModule);
1238
+ }
1239
+ }
1240
+ const include = [
1241
+ ];
1242
+ const provider = parentServer.config.browser.provider || [...parentServer.children][0]?.provider;
1243
+ if (provider?.name === "preview") {
1244
+ include.push("@vitest/browser-preview > @testing-library/user-event", "@vitest/browser-preview > @testing-library/dom");
1245
+ }
1246
+ const fileRoot = browserTestFiles[0] ? dirname(browserTestFiles[0]) : project.config.root;
1247
+ const svelte = isPackageExists("vitest-browser-svelte", fileRoot);
1248
+ if (svelte) {
1249
+ exclude.push("vitest-browser-svelte");
1250
+ }
1251
+ // since we override the resolution in the esbuild plugin, Vite can no longer optimizer it
1252
+ const vue = isPackageExists("vitest-browser-vue", fileRoot);
1253
+ if (vue) {
1254
+ // we override them in the esbuild plugin so optimizer can no longer intercept it
1255
+ include.push("vitest-browser-vue", "vitest-browser-vue > @vue/test-utils", "vitest-browser-vue > @vue/test-utils > @vue/compiler-core");
1256
+ }
1257
+ const vueTestUtils = isPackageExists("@vue/test-utils", fileRoot);
1258
+ if (vueTestUtils) {
1259
+ include.push("@vue/test-utils");
1260
+ }
1261
+ const otelConfig = project.config.experimental.openTelemetry;
1262
+ if (otelConfig?.enabled && otelConfig.browserSdkPath) {
1263
+ entries.push(otelConfig.browserSdkPath);
1264
+ include.push("@opentelemetry/api");
1265
+ }
1266
+ return {
1267
+ define,
1268
+ resolve: { dedupe: ["vitest"] },
1269
+ optimizeDeps: {
1270
+ entries,
1271
+ exclude,
1272
+ include
1273
+ }
1274
+ };
1275
+ },
1276
+ async resolveId(id) {
1277
+ if (!/\?browserv=\w+$/.test(id)) {
1278
+ return;
1279
+ }
1280
+ let useId = id.slice(0, id.lastIndexOf("?"));
1281
+ if (useId.startsWith("/@fs/")) {
1282
+ useId = useId.slice(5);
1283
+ }
1284
+ if (/^\w:/.test(useId)) {
1285
+ useId = useId.replace(/\\/g, "/");
1286
+ }
1287
+ return useId;
1288
+ }
1289
+ },
1290
+ {
1291
+ name: "vitest:browser:resolve-virtual",
1292
+ async resolveId(rawId) {
1293
+ if (rawId === "/mockServiceWorker.js") {
1294
+ return this.resolve("msw/mockServiceWorker.js", distRoot, { skipSelf: true });
1295
+ }
1296
+ }
1297
+ },
1298
+ {
1299
+ name: "vitest:browser:assets",
1300
+ configureServer(server) {
1301
+ server.middlewares.use("/__vitest__", sirv(resolve(distRoot, "client/__vitest__")));
1302
+ },
1303
+ resolveId(id) {
1304
+ if (id.startsWith("/__vitest_browser__/")) {
1305
+ return resolve(distRoot, "client", id.slice(1));
1306
+ }
1307
+ },
1308
+ transform(code, id) {
1309
+ if (id.includes(parentServer.vite.config.cacheDir) && id.includes("loupe.js")) {
1310
+ // loupe bundle has a nastry require('util') call that leaves a warning in the console
1311
+ const utilRequire = "nodeUtil = require_util();";
1312
+ return code.replace(utilRequire, " ".repeat(utilRequire.length));
1313
+ }
1314
+ }
1315
+ },
1316
+ BrowserContext(parentServer),
1317
+ dynamicImportPlugin({
1318
+ globalThisAccessor: "\"__vitest_browser_runner__\"",
1319
+ filter(id) {
1320
+ if (id.includes(distRoot)) {
1321
+ return false;
1322
+ }
1323
+ return true;
1324
+ }
1325
+ }),
1326
+ {
1327
+ name: "vitest:browser:config",
1328
+ enforce: "post",
1329
+ async config(viteConfig) {
1330
+ // Enables using ignore hint for coverage providers with @preserve keyword
1331
+ if (viteConfig.esbuild !== false) {
1332
+ viteConfig.esbuild ||= {};
1333
+ viteConfig.esbuild.legalComments = "inline";
1334
+ }
1335
+ const defaultPort = parentServer.vitest.state._data.browserLastPort++;
1336
+ const api = resolveApiServerConfig(viteConfig.test?.browser || {}, defaultPort);
1337
+ viteConfig.server = {
1338
+ ...viteConfig.server,
1339
+ port: defaultPort,
1340
+ ...api,
1341
+ middlewareMode: false,
1342
+ open: false
1343
+ };
1344
+ viteConfig.server.fs ??= {};
1345
+ viteConfig.server.fs.allow = viteConfig.server.fs.allow || [];
1346
+ viteConfig.server.fs.allow.push(...resolveFsAllow(parentServer.vitest.config.root, parentServer.vitest.vite.config.configFile), distRoot);
1347
+ return { resolve: { alias: viteConfig.test?.alias } };
1348
+ }
1349
+ },
1350
+ {
1351
+ name: "vitest:browser:in-source-tests",
1352
+ transform: {
1353
+ filter: { code: /import\.meta\.vitest/ },
1354
+ handler(code, id) {
1355
+ const filename = cleanUrl(id);
1356
+ if (!code.includes("import.meta.vitest")) {
1357
+ return;
1358
+ }
1359
+ const s = new MagicString(code, { filename });
1360
+ s.prepend(`Object.defineProperty(import.meta, 'vitest', { get() { return typeof __vitest_worker__ !== 'undefined' && __vitest_worker__.filepath === "${filename.replace(/"/g, "\\\"")}" ? __vitest_index__ : undefined } });\n`);
1361
+ return {
1362
+ code: s.toString(),
1363
+ map: s.generateMap({ hires: true })
1364
+ };
1365
+ }
1366
+ }
1367
+ },
1368
+ {
1369
+ name: "vitest:browser:worker",
1370
+ transform(code, id, _options) {
1371
+ // https://github.com/vitejs/vite/blob/ba56cf43b5480f8519349f7d7fe60718e9af5f1a/packages/vite/src/node/plugins/worker.ts#L46
1372
+ if (/(?:\?|&)worker_file&type=\w+(?:&|$)/.test(id)) {
1373
+ const s = new MagicString(code);
1374
+ s.prepend("globalThis.__vitest_browser_runner__ = { wrapDynamicImport: f => f() };\n");
1375
+ return {
1376
+ code: s.toString(),
1377
+ map: s.generateMap({ hires: "boundary" })
1378
+ };
1379
+ }
1380
+ }
1381
+ },
1382
+ {
1383
+ name: "vitest:browser:transform-tester-html",
1384
+ enforce: "pre",
1385
+ async transformIndexHtml(html, ctx) {
1386
+ const projectBrowser = [...parentServer.children].find((server) => {
1387
+ return ctx.filename === server.testerFilepath;
1388
+ });
1389
+ if (!projectBrowser) {
1390
+ return;
1391
+ }
1392
+ const stateJs = typeof parentServer.stateJs === "string" ? parentServer.stateJs : await parentServer.stateJs;
1393
+ const testerTags = [];
1394
+ const isDefaultTemplate = resolve(distRoot, "client/tester/tester.html") === projectBrowser.testerFilepath;
1395
+ if (!isDefaultTemplate) {
1396
+ const manifestContent = parentServer.manifest instanceof Promise ? await parentServer.manifest : parentServer.manifest;
1397
+ const testerEntry = manifestContent["tester/tester.html"];
1398
+ testerTags.push({
1399
+ tag: "script",
1400
+ attrs: {
1401
+ type: "module",
1402
+ crossorigin: "",
1403
+ src: `${parentServer.base}${testerEntry.file}`
1404
+ },
1405
+ injectTo: "head"
1406
+ });
1407
+ for (const importName of testerEntry.imports || []) {
1408
+ const entryManifest = manifestContent[importName];
1409
+ if (entryManifest) {
1410
+ testerTags.push({
1411
+ tag: "link",
1412
+ attrs: {
1413
+ href: `${parentServer.base}${entryManifest.file}`,
1414
+ rel: "modulepreload",
1415
+ crossorigin: ""
1416
+ },
1417
+ injectTo: "head"
1418
+ });
1419
+ }
1420
+ }
1421
+ } else {
1422
+ // inject the reset style only in the default template,
1423
+ // allowing users to customize the style in their own template
1424
+ testerTags.push({
1425
+ tag: "style",
1426
+ children: `
1427
+ html {
1428
+ padding: 0;
1429
+ margin: 0;
1430
+ }
1431
+ body {
1432
+ padding: 0;
1433
+ margin: 0;
1434
+ min-height: 100vh;
1435
+ }`,
1436
+ injectTo: "head"
1437
+ });
1438
+ }
1439
+ return [
1440
+ {
1441
+ tag: "script",
1442
+ children: "{__VITEST_INJECTOR__}",
1443
+ injectTo: "head-prepend"
1444
+ },
1445
+ {
1446
+ tag: "script",
1447
+ children: stateJs,
1448
+ injectTo: "head-prepend"
1449
+ },
1450
+ {
1451
+ tag: "script",
1452
+ attrs: {
1453
+ type: "module",
1454
+ src: parentServer.errorCatcherUrl
1455
+ },
1456
+ injectTo: "head"
1457
+ },
1458
+ {
1459
+ tag: "script",
1460
+ attrs: {
1461
+ type: "module",
1462
+ src: parentServer.matchersUrl
1463
+ },
1464
+ injectTo: "head"
1465
+ },
1466
+ ...parentServer.initScripts.map((script) => ({
1467
+ tag: "script",
1468
+ attrs: {
1469
+ type: "module",
1470
+ src: join("/@fs/", script)
1471
+ },
1472
+ injectTo: "head"
1473
+ })),
1474
+ ...testerTags
1475
+ ].filter((s) => s != null);
1476
+ }
1477
+ },
1478
+ {
1479
+ name: "vitest:browser:support-testing-library",
1480
+ enforce: "pre",
1481
+ config() {
1482
+ const rolldownPlugin = {
1483
+ name: "vue-test-utils-rewrite",
1484
+ resolveId: {
1485
+ filter: { id: /^@vue\/(test-utils|compiler-core)$/ },
1486
+ handler(source, importer) {
1487
+ const resolved = getRequire().resolve(source, { paths: [importer] });
1488
+ return resolved;
1489
+ }
1490
+ }
1491
+ };
1492
+ const esbuildPlugin = {
1493
+ name: "test-utils-rewrite",
1494
+ setup(build) {
1495
+ // test-utils: resolve to CJS instead of the browser because the browser version expects a global Vue object
1496
+ // compiler-core: only CJS version allows slots as strings
1497
+ build.onResolve({ filter: /^@vue\/(test-utils|compiler-core)$/ }, (args) => {
1498
+ const resolved = getRequire().resolve(args.path, { paths: [args.importer] });
1499
+ return { path: resolved };
1500
+ });
1501
+ }
1502
+ };
1503
+ return { optimizeDeps: rolldownVersion ? { rolldownOptions: { plugins: [rolldownPlugin] } } : { esbuildOptions: { plugins: [esbuildPlugin] } } };
1504
+ }
1505
+ },
1506
+ {
1507
+ name: "vitest:browser:__vitest_browser_import_meta_env_init__",
1508
+ transform: { handler(code) {
1509
+ // this transform runs after `vitest:meta-env-replacer` so that
1510
+ // `import.meta.env` will be handled by Vite import analysis to match behavior.
1511
+ if (code.includes("__vitest_browser_import_meta_env_init__")) {
1512
+ return code.replace("__vitest_browser_import_meta_env_init__", "import.meta.env");
1513
+ }
1514
+ } }
1515
+ }
1516
+ ];
1517
+ };
1518
+ function tryResolve(path, paths) {
1519
+ try {
1520
+ const _require = getRequire();
1521
+ return _require.resolve(path, { paths });
1522
+ } catch {
1523
+ return undefined;
1524
+ }
1525
+ }
1526
+ let _require;
1527
+ function getRequire() {
1528
+ if (!_require) {
1529
+ _require = createRequire(import.meta.url);
1530
+ }
1531
+ return _require;
1532
+ }
1533
+ function resolveCoverageFolder(vitest) {
1534
+ const options = vitest.config;
1535
+ const coverageOptions = vitest._coverageOptions;
1536
+ const htmlReporter = coverageOptions?.enabled ? toArray(options.coverage.reporter).find((reporter) => {
1537
+ if (typeof reporter === "string") {
1538
+ return reporter === "html";
1539
+ }
1540
+ return reporter[0] === "html";
1541
+ }) : undefined;
1542
+ if (!htmlReporter) {
1543
+ return undefined;
1544
+ }
1545
+ // reportsDirectory not resolved yet
1546
+ const root = resolve(options.root || process.cwd(), coverageOptions.reportsDirectory || coverageConfigDefaults.reportsDirectory);
1547
+ const subdir = Array.isArray(htmlReporter) && htmlReporter.length > 1 && "subdir" in htmlReporter[1] ? htmlReporter[1].subdir : undefined;
1548
+ if (!subdir || typeof subdir !== "string") {
1549
+ return [root, `/${basename(root)}/`];
1550
+ }
1551
+ return [resolve(root, subdir), `/${basename(root)}/${subdir}/`];
1552
+ }
1553
+ const postfixRE = /[?#].*$/;
1554
+ function cleanUrl(url) {
1555
+ return url.replace(postfixRE, "");
1556
+ }
1557
+
1558
+ class BrowserServerCDPHandler {
1559
+ listenerIds = {};
1560
+ listeners = {};
1561
+ constructor(session, tester) {
1562
+ this.session = session;
1563
+ this.tester = tester;
1564
+ }
1565
+ send(method, params) {
1566
+ return this.session.send(method, params);
1567
+ }
1568
+ on(event, id, once = false) {
1569
+ if (!this.listenerIds[event]) {
1570
+ this.listenerIds[event] = [];
1571
+ }
1572
+ this.listenerIds[event].push(id);
1573
+ if (!this.listeners[event]) {
1574
+ this.listeners[event] = (payload) => {
1575
+ this.tester.cdpEvent(event, payload);
1576
+ if (once) {
1577
+ this.off(event, id);
1578
+ }
1579
+ };
1580
+ this.session.on(event, this.listeners[event]);
1581
+ }
1582
+ }
1583
+ off(event, id) {
1584
+ if (!this.listenerIds[event]) {
1585
+ this.listenerIds[event] = [];
1586
+ }
1587
+ this.listenerIds[event] = this.listenerIds[event].filter((l) => l !== id);
1588
+ if (!this.listenerIds[event].length) {
1589
+ this.session.off(event, this.listeners[event]);
1590
+ delete this.listeners[event];
1591
+ }
1592
+ }
1593
+ once(event, listener) {
1594
+ this.on(event, listener, true);
1595
+ }
1596
+ }
1597
+
1598
+ const types = {
1599
+ 'application/andrew-inset': ['ez'],
1600
+ 'application/appinstaller': ['appinstaller'],
1601
+ 'application/applixware': ['aw'],
1602
+ 'application/appx': ['appx'],
1603
+ 'application/appxbundle': ['appxbundle'],
1604
+ 'application/atom+xml': ['atom'],
1605
+ 'application/atomcat+xml': ['atomcat'],
1606
+ 'application/atomdeleted+xml': ['atomdeleted'],
1607
+ 'application/atomsvc+xml': ['atomsvc'],
1608
+ 'application/atsc-dwd+xml': ['dwd'],
1609
+ 'application/atsc-held+xml': ['held'],
1610
+ 'application/atsc-rsat+xml': ['rsat'],
1611
+ 'application/automationml-aml+xml': ['aml'],
1612
+ 'application/automationml-amlx+zip': ['amlx'],
1613
+ 'application/bdoc': ['bdoc'],
1614
+ 'application/calendar+xml': ['xcs'],
1615
+ 'application/ccxml+xml': ['ccxml'],
1616
+ 'application/cdfx+xml': ['cdfx'],
1617
+ 'application/cdmi-capability': ['cdmia'],
1618
+ 'application/cdmi-container': ['cdmic'],
1619
+ 'application/cdmi-domain': ['cdmid'],
1620
+ 'application/cdmi-object': ['cdmio'],
1621
+ 'application/cdmi-queue': ['cdmiq'],
1622
+ 'application/cpl+xml': ['cpl'],
1623
+ 'application/cu-seeme': ['cu'],
1624
+ 'application/cwl': ['cwl'],
1625
+ 'application/dash+xml': ['mpd'],
1626
+ 'application/dash-patch+xml': ['mpp'],
1627
+ 'application/davmount+xml': ['davmount'],
1628
+ 'application/dicom': ['dcm'],
1629
+ 'application/docbook+xml': ['dbk'],
1630
+ 'application/dssc+der': ['dssc'],
1631
+ 'application/dssc+xml': ['xdssc'],
1632
+ 'application/ecmascript': ['ecma'],
1633
+ 'application/emma+xml': ['emma'],
1634
+ 'application/emotionml+xml': ['emotionml'],
1635
+ 'application/epub+zip': ['epub'],
1636
+ 'application/exi': ['exi'],
1637
+ 'application/express': ['exp'],
1638
+ 'application/fdf': ['fdf'],
1639
+ 'application/fdt+xml': ['fdt'],
1640
+ 'application/font-tdpfr': ['pfr'],
1641
+ 'application/geo+json': ['geojson'],
1642
+ 'application/gml+xml': ['gml'],
1643
+ 'application/gpx+xml': ['gpx'],
1644
+ 'application/gxf': ['gxf'],
1645
+ 'application/gzip': ['gz'],
1646
+ 'application/hjson': ['hjson'],
1647
+ 'application/hyperstudio': ['stk'],
1648
+ 'application/inkml+xml': ['ink', 'inkml'],
1649
+ 'application/ipfix': ['ipfix'],
1650
+ 'application/its+xml': ['its'],
1651
+ 'application/java-archive': ['jar', 'war', 'ear'],
1652
+ 'application/java-serialized-object': ['ser'],
1653
+ 'application/java-vm': ['class'],
1654
+ 'application/javascript': ['*js'],
1655
+ 'application/json': ['json', 'map'],
1656
+ 'application/json5': ['json5'],
1657
+ 'application/jsonml+json': ['jsonml'],
1658
+ 'application/ld+json': ['jsonld'],
1659
+ 'application/lgr+xml': ['lgr'],
1660
+ 'application/lost+xml': ['lostxml'],
1661
+ 'application/mac-binhex40': ['hqx'],
1662
+ 'application/mac-compactpro': ['cpt'],
1663
+ 'application/mads+xml': ['mads'],
1664
+ 'application/manifest+json': ['webmanifest'],
1665
+ 'application/marc': ['mrc'],
1666
+ 'application/marcxml+xml': ['mrcx'],
1667
+ 'application/mathematica': ['ma', 'nb', 'mb'],
1668
+ 'application/mathml+xml': ['mathml'],
1669
+ 'application/mbox': ['mbox'],
1670
+ 'application/media-policy-dataset+xml': ['mpf'],
1671
+ 'application/mediaservercontrol+xml': ['mscml'],
1672
+ 'application/metalink+xml': ['metalink'],
1673
+ 'application/metalink4+xml': ['meta4'],
1674
+ 'application/mets+xml': ['mets'],
1675
+ 'application/mmt-aei+xml': ['maei'],
1676
+ 'application/mmt-usd+xml': ['musd'],
1677
+ 'application/mods+xml': ['mods'],
1678
+ 'application/mp21': ['m21', 'mp21'],
1679
+ 'application/mp4': ['*mp4', '*mpg4', 'mp4s', 'm4p'],
1680
+ 'application/msix': ['msix'],
1681
+ 'application/msixbundle': ['msixbundle'],
1682
+ 'application/msword': ['doc', 'dot'],
1683
+ 'application/mxf': ['mxf'],
1684
+ 'application/n-quads': ['nq'],
1685
+ 'application/n-triples': ['nt'],
1686
+ 'application/node': ['cjs'],
1687
+ 'application/octet-stream': [
1688
+ 'bin',
1689
+ 'dms',
1690
+ 'lrf',
1691
+ 'mar',
1692
+ 'so',
1693
+ 'dist',
1694
+ 'distz',
1695
+ 'pkg',
1696
+ 'bpk',
1697
+ 'dump',
1698
+ 'elc',
1699
+ 'deploy',
1700
+ 'exe',
1701
+ 'dll',
1702
+ 'deb',
1703
+ 'dmg',
1704
+ 'iso',
1705
+ 'img',
1706
+ 'msi',
1707
+ 'msp',
1708
+ 'msm',
1709
+ 'buffer',
1710
+ ],
1711
+ 'application/oda': ['oda'],
1712
+ 'application/oebps-package+xml': ['opf'],
1713
+ 'application/ogg': ['ogx'],
1714
+ 'application/omdoc+xml': ['omdoc'],
1715
+ 'application/onenote': [
1716
+ 'onetoc',
1717
+ 'onetoc2',
1718
+ 'onetmp',
1719
+ 'onepkg',
1720
+ 'one',
1721
+ 'onea',
1722
+ ],
1723
+ 'application/oxps': ['oxps'],
1724
+ 'application/p2p-overlay+xml': ['relo'],
1725
+ 'application/patch-ops-error+xml': ['xer'],
1726
+ 'application/pdf': ['pdf'],
1727
+ 'application/pgp-encrypted': ['pgp'],
1728
+ 'application/pgp-keys': ['asc'],
1729
+ 'application/pgp-signature': ['sig', '*asc'],
1730
+ 'application/pics-rules': ['prf'],
1731
+ 'application/pkcs10': ['p10'],
1732
+ 'application/pkcs7-mime': ['p7m', 'p7c'],
1733
+ 'application/pkcs7-signature': ['p7s'],
1734
+ 'application/pkcs8': ['p8'],
1735
+ 'application/pkix-attr-cert': ['ac'],
1736
+ 'application/pkix-cert': ['cer'],
1737
+ 'application/pkix-crl': ['crl'],
1738
+ 'application/pkix-pkipath': ['pkipath'],
1739
+ 'application/pkixcmp': ['pki'],
1740
+ 'application/pls+xml': ['pls'],
1741
+ 'application/postscript': ['ai', 'eps', 'ps'],
1742
+ 'application/provenance+xml': ['provx'],
1743
+ 'application/pskc+xml': ['pskcxml'],
1744
+ 'application/raml+yaml': ['raml'],
1745
+ 'application/rdf+xml': ['rdf', 'owl'],
1746
+ 'application/reginfo+xml': ['rif'],
1747
+ 'application/relax-ng-compact-syntax': ['rnc'],
1748
+ 'application/resource-lists+xml': ['rl'],
1749
+ 'application/resource-lists-diff+xml': ['rld'],
1750
+ 'application/rls-services+xml': ['rs'],
1751
+ 'application/route-apd+xml': ['rapd'],
1752
+ 'application/route-s-tsid+xml': ['sls'],
1753
+ 'application/route-usd+xml': ['rusd'],
1754
+ 'application/rpki-ghostbusters': ['gbr'],
1755
+ 'application/rpki-manifest': ['mft'],
1756
+ 'application/rpki-roa': ['roa'],
1757
+ 'application/rsd+xml': ['rsd'],
1758
+ 'application/rss+xml': ['rss'],
1759
+ 'application/rtf': ['rtf'],
1760
+ 'application/sbml+xml': ['sbml'],
1761
+ 'application/scvp-cv-request': ['scq'],
1762
+ 'application/scvp-cv-response': ['scs'],
1763
+ 'application/scvp-vp-request': ['spq'],
1764
+ 'application/scvp-vp-response': ['spp'],
1765
+ 'application/sdp': ['sdp'],
1766
+ 'application/senml+xml': ['senmlx'],
1767
+ 'application/sensml+xml': ['sensmlx'],
1768
+ 'application/set-payment-initiation': ['setpay'],
1769
+ 'application/set-registration-initiation': ['setreg'],
1770
+ 'application/shf+xml': ['shf'],
1771
+ 'application/sieve': ['siv', 'sieve'],
1772
+ 'application/smil+xml': ['smi', 'smil'],
1773
+ 'application/sparql-query': ['rq'],
1774
+ 'application/sparql-results+xml': ['srx'],
1775
+ 'application/sql': ['sql'],
1776
+ 'application/srgs': ['gram'],
1777
+ 'application/srgs+xml': ['grxml'],
1778
+ 'application/sru+xml': ['sru'],
1779
+ 'application/ssdl+xml': ['ssdl'],
1780
+ 'application/ssml+xml': ['ssml'],
1781
+ 'application/swid+xml': ['swidtag'],
1782
+ 'application/tei+xml': ['tei', 'teicorpus'],
1783
+ 'application/thraud+xml': ['tfi'],
1784
+ 'application/timestamped-data': ['tsd'],
1785
+ 'application/toml': ['toml'],
1786
+ 'application/trig': ['trig'],
1787
+ 'application/ttml+xml': ['ttml'],
1788
+ 'application/ubjson': ['ubj'],
1789
+ 'application/urc-ressheet+xml': ['rsheet'],
1790
+ 'application/urc-targetdesc+xml': ['td'],
1791
+ 'application/voicexml+xml': ['vxml'],
1792
+ 'application/wasm': ['wasm'],
1793
+ 'application/watcherinfo+xml': ['wif'],
1794
+ 'application/widget': ['wgt'],
1795
+ 'application/winhlp': ['hlp'],
1796
+ 'application/wsdl+xml': ['wsdl'],
1797
+ 'application/wspolicy+xml': ['wspolicy'],
1798
+ 'application/xaml+xml': ['xaml'],
1799
+ 'application/xcap-att+xml': ['xav'],
1800
+ 'application/xcap-caps+xml': ['xca'],
1801
+ 'application/xcap-diff+xml': ['xdf'],
1802
+ 'application/xcap-el+xml': ['xel'],
1803
+ 'application/xcap-ns+xml': ['xns'],
1804
+ 'application/xenc+xml': ['xenc'],
1805
+ 'application/xfdf': ['xfdf'],
1806
+ 'application/xhtml+xml': ['xhtml', 'xht'],
1807
+ 'application/xliff+xml': ['xlf'],
1808
+ 'application/xml': ['xml', 'xsl', 'xsd', 'rng'],
1809
+ 'application/xml-dtd': ['dtd'],
1810
+ 'application/xop+xml': ['xop'],
1811
+ 'application/xproc+xml': ['xpl'],
1812
+ 'application/xslt+xml': ['*xsl', 'xslt'],
1813
+ 'application/xspf+xml': ['xspf'],
1814
+ 'application/xv+xml': ['mxml', 'xhvml', 'xvml', 'xvm'],
1815
+ 'application/yang': ['yang'],
1816
+ 'application/yin+xml': ['yin'],
1817
+ 'application/zip': ['zip'],
1818
+ 'application/zip+dotlottie': ['lottie'],
1819
+ 'audio/3gpp': ['*3gpp'],
1820
+ 'audio/aac': ['adts', 'aac'],
1821
+ 'audio/adpcm': ['adp'],
1822
+ 'audio/amr': ['amr'],
1823
+ 'audio/basic': ['au', 'snd'],
1824
+ 'audio/midi': ['mid', 'midi', 'kar', 'rmi'],
1825
+ 'audio/mobile-xmf': ['mxmf'],
1826
+ 'audio/mp3': ['*mp3'],
1827
+ 'audio/mp4': ['m4a', 'mp4a', 'm4b'],
1828
+ 'audio/mpeg': ['mpga', 'mp2', 'mp2a', 'mp3', 'm2a', 'm3a'],
1829
+ 'audio/ogg': ['oga', 'ogg', 'spx', 'opus'],
1830
+ 'audio/s3m': ['s3m'],
1831
+ 'audio/silk': ['sil'],
1832
+ 'audio/wav': ['wav'],
1833
+ 'audio/wave': ['*wav'],
1834
+ 'audio/webm': ['weba'],
1835
+ 'audio/xm': ['xm'],
1836
+ 'font/collection': ['ttc'],
1837
+ 'font/otf': ['otf'],
1838
+ 'font/ttf': ['ttf'],
1839
+ 'font/woff': ['woff'],
1840
+ 'font/woff2': ['woff2'],
1841
+ 'image/aces': ['exr'],
1842
+ 'image/apng': ['apng'],
1843
+ 'image/avci': ['avci'],
1844
+ 'image/avcs': ['avcs'],
1845
+ 'image/avif': ['avif'],
1846
+ 'image/bmp': ['bmp', 'dib'],
1847
+ 'image/cgm': ['cgm'],
1848
+ 'image/dicom-rle': ['drle'],
1849
+ 'image/dpx': ['dpx'],
1850
+ 'image/emf': ['emf'],
1851
+ 'image/fits': ['fits'],
1852
+ 'image/g3fax': ['g3'],
1853
+ 'image/gif': ['gif'],
1854
+ 'image/heic': ['heic'],
1855
+ 'image/heic-sequence': ['heics'],
1856
+ 'image/heif': ['heif'],
1857
+ 'image/heif-sequence': ['heifs'],
1858
+ 'image/hej2k': ['hej2'],
1859
+ 'image/ief': ['ief'],
1860
+ 'image/jaii': ['jaii'],
1861
+ 'image/jais': ['jais'],
1862
+ 'image/jls': ['jls'],
1863
+ 'image/jp2': ['jp2', 'jpg2'],
1864
+ 'image/jpeg': ['jpg', 'jpeg', 'jpe'],
1865
+ 'image/jph': ['jph'],
1866
+ 'image/jphc': ['jhc'],
1867
+ 'image/jpm': ['jpm', 'jpgm'],
1868
+ 'image/jpx': ['jpx', 'jpf'],
1869
+ 'image/jxl': ['jxl'],
1870
+ 'image/jxr': ['jxr'],
1871
+ 'image/jxra': ['jxra'],
1872
+ 'image/jxrs': ['jxrs'],
1873
+ 'image/jxs': ['jxs'],
1874
+ 'image/jxsc': ['jxsc'],
1875
+ 'image/jxsi': ['jxsi'],
1876
+ 'image/jxss': ['jxss'],
1877
+ 'image/ktx': ['ktx'],
1878
+ 'image/ktx2': ['ktx2'],
1879
+ 'image/pjpeg': ['jfif'],
1880
+ 'image/png': ['png'],
1881
+ 'image/sgi': ['sgi'],
1882
+ 'image/svg+xml': ['svg', 'svgz'],
1883
+ 'image/t38': ['t38'],
1884
+ 'image/tiff': ['tif', 'tiff'],
1885
+ 'image/tiff-fx': ['tfx'],
1886
+ 'image/webp': ['webp'],
1887
+ 'image/wmf': ['wmf'],
1888
+ 'message/disposition-notification': ['disposition-notification'],
1889
+ 'message/global': ['u8msg'],
1890
+ 'message/global-delivery-status': ['u8dsn'],
1891
+ 'message/global-disposition-notification': ['u8mdn'],
1892
+ 'message/global-headers': ['u8hdr'],
1893
+ 'message/rfc822': ['eml', 'mime', 'mht', 'mhtml'],
1894
+ 'model/3mf': ['3mf'],
1895
+ 'model/gltf+json': ['gltf'],
1896
+ 'model/gltf-binary': ['glb'],
1897
+ 'model/iges': ['igs', 'iges'],
1898
+ 'model/jt': ['jt'],
1899
+ 'model/mesh': ['msh', 'mesh', 'silo'],
1900
+ 'model/mtl': ['mtl'],
1901
+ 'model/obj': ['obj'],
1902
+ 'model/prc': ['prc'],
1903
+ 'model/step': ['step', 'stp', 'stpnc', 'p21', '210'],
1904
+ 'model/step+xml': ['stpx'],
1905
+ 'model/step+zip': ['stpz'],
1906
+ 'model/step-xml+zip': ['stpxz'],
1907
+ 'model/stl': ['stl'],
1908
+ 'model/u3d': ['u3d'],
1909
+ 'model/vrml': ['wrl', 'vrml'],
1910
+ 'model/x3d+binary': ['*x3db', 'x3dbz'],
1911
+ 'model/x3d+fastinfoset': ['x3db'],
1912
+ 'model/x3d+vrml': ['*x3dv', 'x3dvz'],
1913
+ 'model/x3d+xml': ['x3d', 'x3dz'],
1914
+ 'model/x3d-vrml': ['x3dv'],
1915
+ 'text/cache-manifest': ['appcache', 'manifest'],
1916
+ 'text/calendar': ['ics', 'ifb'],
1917
+ 'text/coffeescript': ['coffee', 'litcoffee'],
1918
+ 'text/css': ['css'],
1919
+ 'text/csv': ['csv'],
1920
+ 'text/html': ['html', 'htm', 'shtml'],
1921
+ 'text/jade': ['jade'],
1922
+ 'text/javascript': ['js', 'mjs'],
1923
+ 'text/jsx': ['jsx'],
1924
+ 'text/less': ['less'],
1925
+ 'text/markdown': ['md', 'markdown'],
1926
+ 'text/mathml': ['mml'],
1927
+ 'text/mdx': ['mdx'],
1928
+ 'text/n3': ['n3'],
1929
+ 'text/plain': ['txt', 'text', 'conf', 'def', 'list', 'log', 'in', 'ini'],
1930
+ 'text/richtext': ['rtx'],
1931
+ 'text/rtf': ['*rtf'],
1932
+ 'text/sgml': ['sgml', 'sgm'],
1933
+ 'text/shex': ['shex'],
1934
+ 'text/slim': ['slim', 'slm'],
1935
+ 'text/spdx': ['spdx'],
1936
+ 'text/stylus': ['stylus', 'styl'],
1937
+ 'text/tab-separated-values': ['tsv'],
1938
+ 'text/troff': ['t', 'tr', 'roff', 'man', 'me', 'ms'],
1939
+ 'text/turtle': ['ttl'],
1940
+ 'text/uri-list': ['uri', 'uris', 'urls'],
1941
+ 'text/vcard': ['vcard'],
1942
+ 'text/vtt': ['vtt'],
1943
+ 'text/wgsl': ['wgsl'],
1944
+ 'text/xml': ['*xml'],
1945
+ 'text/yaml': ['yaml', 'yml'],
1946
+ 'video/3gpp': ['3gp', '3gpp'],
1947
+ 'video/3gpp2': ['3g2'],
1948
+ 'video/h261': ['h261'],
1949
+ 'video/h263': ['h263'],
1950
+ 'video/h264': ['h264'],
1951
+ 'video/iso.segment': ['m4s'],
1952
+ 'video/jpeg': ['jpgv'],
1953
+ 'video/jpm': ['*jpm', '*jpgm'],
1954
+ 'video/mj2': ['mj2', 'mjp2'],
1955
+ 'video/mp2t': ['ts', 'm2t', 'm2ts', 'mts'],
1956
+ 'video/mp4': ['mp4', 'mp4v', 'mpg4'],
1957
+ 'video/mpeg': ['mpeg', 'mpg', 'mpe', 'm1v', 'm2v'],
1958
+ 'video/ogg': ['ogv'],
1959
+ 'video/quicktime': ['qt', 'mov'],
1960
+ 'video/webm': ['webm'],
1961
+ };
1962
+ Object.freeze(types);
1963
+
1964
+ var __classPrivateFieldGet = (null && null.__classPrivateFieldGet) || function (receiver, state, kind, f) {
1965
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
1966
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
1967
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
1968
+ };
1969
+ var _Mime_extensionToType, _Mime_typeToExtension, _Mime_typeToExtensions;
1970
+ class Mime {
1971
+ constructor(...args) {
1972
+ _Mime_extensionToType.set(this, new Map());
1973
+ _Mime_typeToExtension.set(this, new Map());
1974
+ _Mime_typeToExtensions.set(this, new Map());
1975
+ for (const arg of args) {
1976
+ this.define(arg);
1977
+ }
1978
+ }
1979
+ define(typeMap, force = false) {
1980
+ for (let [type, extensions] of Object.entries(typeMap)) {
1981
+ type = type.toLowerCase();
1982
+ extensions = extensions.map((ext) => ext.toLowerCase());
1983
+ if (!__classPrivateFieldGet(this, _Mime_typeToExtensions, "f").has(type)) {
1984
+ __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").set(type, new Set());
1985
+ }
1986
+ const allExtensions = __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type);
1987
+ let first = true;
1988
+ for (let extension of extensions) {
1989
+ const starred = extension.startsWith('*');
1990
+ extension = starred ? extension.slice(1) : extension;
1991
+ allExtensions?.add(extension);
1992
+ if (first) {
1993
+ __classPrivateFieldGet(this, _Mime_typeToExtension, "f").set(type, extension);
1994
+ }
1995
+ first = false;
1996
+ if (starred)
1997
+ continue;
1998
+ const currentType = __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(extension);
1999
+ if (currentType && currentType != type && !force) {
2000
+ throw new Error(`"${type} -> ${extension}" conflicts with "${currentType} -> ${extension}". Pass \`force=true\` to override this definition.`);
2001
+ }
2002
+ __classPrivateFieldGet(this, _Mime_extensionToType, "f").set(extension, type);
2003
+ }
2004
+ }
2005
+ return this;
2006
+ }
2007
+ getType(path) {
2008
+ if (typeof path !== 'string')
2009
+ return null;
2010
+ const last = path.replace(/^.*[/\\]/s, '').toLowerCase();
2011
+ const ext = last.replace(/^.*\./s, '').toLowerCase();
2012
+ const hasPath = last.length < path.length;
2013
+ const hasDot = ext.length < last.length - 1;
2014
+ if (!hasDot && hasPath)
2015
+ return null;
2016
+ return __classPrivateFieldGet(this, _Mime_extensionToType, "f").get(ext) ?? null;
2017
+ }
2018
+ getExtension(type) {
2019
+ if (typeof type !== 'string')
2020
+ return null;
2021
+ type = type?.split?.(';')[0];
2022
+ return ((type && __classPrivateFieldGet(this, _Mime_typeToExtension, "f").get(type.trim().toLowerCase())) ?? null);
2023
+ }
2024
+ getAllExtensions(type) {
2025
+ if (typeof type !== 'string')
2026
+ return null;
2027
+ return __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").get(type.toLowerCase()) ?? null;
2028
+ }
2029
+ _freeze() {
2030
+ this.define = () => {
2031
+ throw new Error('define() not allowed for built-in Mime objects. See https://github.com/broofa/mime/blob/main/README.md#custom-mime-instances');
2032
+ };
2033
+ Object.freeze(this);
2034
+ for (const extensions of __classPrivateFieldGet(this, _Mime_typeToExtensions, "f").values()) {
2035
+ Object.freeze(extensions);
2036
+ }
2037
+ return this;
2038
+ }
2039
+ _getTestState() {
2040
+ return {
2041
+ types: __classPrivateFieldGet(this, _Mime_extensionToType, "f"),
2042
+ extensions: __classPrivateFieldGet(this, _Mime_typeToExtension, "f"),
2043
+ };
2044
+ }
2045
+ }
2046
+ _Mime_extensionToType = new WeakMap(), _Mime_typeToExtension = new WeakMap(), _Mime_typeToExtensions = new WeakMap();
2047
+
2048
+ var mime = new Mime(types)._freeze();
2049
+
2050
+ function assertFileAccess(path, project) {
2051
+ if (!isFileServingAllowed(path, project.vite) && !isFileServingAllowed(path, project.vitest.vite)) {
2052
+ throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
2053
+ }
2054
+ }
2055
+ const readFile = async ({ project }, path, options = {}) => {
2056
+ const filepath = resolve$1(project.config.root, path);
2057
+ assertFileAccess(filepath, project);
2058
+ // never return a Buffer
2059
+ if (typeof options === "object" && !options.encoding) {
2060
+ options.encoding = "utf-8";
2061
+ }
2062
+ return promises.readFile(filepath, options);
2063
+ };
2064
+ const writeFile = async ({ project }, path, data, options) => {
2065
+ const filepath = resolve$1(project.config.root, path);
2066
+ assertFileAccess(filepath, project);
2067
+ const dir = dirname$1(filepath);
2068
+ if (!fs.existsSync(dir)) {
2069
+ await promises.mkdir(dir, { recursive: true });
2070
+ }
2071
+ await promises.writeFile(filepath, data, options);
2072
+ };
2073
+ const removeFile = async ({ project }, path) => {
2074
+ const filepath = resolve$1(project.config.root, path);
2075
+ assertFileAccess(filepath, project);
2076
+ await promises.rm(filepath);
2077
+ };
2078
+ const _fileInfo = async ({ project }, path, encoding) => {
2079
+ const filepath = resolve$1(project.config.root, path);
2080
+ assertFileAccess(filepath, project);
2081
+ const content = await promises.readFile(filepath, encoding || "base64");
2082
+ return {
2083
+ content,
2084
+ basename: basename$1(filepath),
2085
+ mime: mime.getType(filepath)
2086
+ };
2087
+ };
2088
+
2089
+ const screenshot = async (context, name, options = {}) => {
2090
+ options.save ??= true;
2091
+ if (!options.save) {
2092
+ options.base64 = true;
2093
+ }
2094
+ const { buffer, path } = await context.triggerCommand("__vitest_takeScreenshot", name, options);
2095
+ return returnResult(options, path, buffer);
2096
+ };
2097
+ function returnResult(options, path, buffer) {
2098
+ if (!options.save) {
2099
+ return buffer.toString("base64");
2100
+ }
2101
+ if (options.base64) {
2102
+ return {
2103
+ path,
2104
+ base64: buffer.toString("base64")
2105
+ };
2106
+ }
2107
+ return path;
2108
+ }
2109
+
2110
+ const codec = {
2111
+ decode: (buffer, options) => {
2112
+ const { data, alpha, bpp, color, colorType, depth, height, interlace, palette, width } = PNG.sync.read(Buffer.isBuffer(buffer) ? buffer : Buffer.from(buffer), options);
2113
+ return {
2114
+ metadata: {
2115
+ alpha,
2116
+ bpp,
2117
+ color,
2118
+ colorType,
2119
+ depth,
2120
+ height,
2121
+ interlace,
2122
+ palette,
2123
+ width
2124
+ },
2125
+ data
2126
+ };
2127
+ },
2128
+ encode: ({ data, metadata: { height, width } }, options) => {
2129
+ const png = new PNG({
2130
+ height,
2131
+ width
2132
+ });
2133
+ png.data = Buffer.isBuffer(data) ? data : Buffer.from(data);
2134
+ return PNG.sync.write(png, options);
2135
+ }
2136
+ };
2137
+
2138
+ function getCodec(type) {
2139
+ switch (type) {
2140
+ case "png": return codec;
2141
+ default: throw new Error(`No codec found for type ${type}`);
2142
+ }
2143
+ }
2144
+
2145
+ const defaultOptions$1 = {
2146
+ allowedMismatchedPixelRatio: undefined,
2147
+ allowedMismatchedPixels: undefined,
2148
+ threshold: .1,
2149
+ includeAA: false,
2150
+ alpha: .1,
2151
+ aaColor: [
2152
+ 255,
2153
+ 255,
2154
+ 0
2155
+ ],
2156
+ diffColor: [
2157
+ 255,
2158
+ 0,
2159
+ 0
2160
+ ],
2161
+ diffColorAlt: undefined,
2162
+ diffMask: false
2163
+ };
2164
+ const pixelmatch = (reference, actual, { createDiff, ...options }) => {
2165
+ if (reference.metadata.height !== actual.metadata.height || reference.metadata.width !== actual.metadata.width) {
2166
+ return {
2167
+ pass: false,
2168
+ diff: null,
2169
+ message: `Expected image dimensions to be ${reference.metadata.width}×${reference.metadata.height}px, but received ${actual.metadata.width}×${actual.metadata.height}px.`
2170
+ };
2171
+ }
2172
+ const optionsWithDefaults = {
2173
+ ...defaultOptions$1,
2174
+ ...options
2175
+ };
2176
+ const diffBuffer = createDiff ? new Uint8Array(reference.data.length) : undefined;
2177
+ const mismatchedPixels = pm(reference.data, actual.data, diffBuffer, reference.metadata.width, reference.metadata.height, optionsWithDefaults);
2178
+ const imageArea = reference.metadata.width * reference.metadata.height;
2179
+ let allowedMismatchedPixels = Math.min(optionsWithDefaults.allowedMismatchedPixels ?? Number.POSITIVE_INFINITY, (optionsWithDefaults.allowedMismatchedPixelRatio ?? Number.POSITIVE_INFINITY) * imageArea);
2180
+ if (allowedMismatchedPixels === Number.POSITIVE_INFINITY) {
2181
+ allowedMismatchedPixels = 0;
2182
+ }
2183
+ const pass = mismatchedPixels <= allowedMismatchedPixels;
2184
+ return {
2185
+ pass,
2186
+ diff: diffBuffer ?? null,
2187
+ message: pass ? null : `${mismatchedPixels} pixels (ratio ${(Math.ceil(mismatchedPixels / imageArea * 100) / 100).toFixed(2)}) differ.`
2188
+ };
2189
+ };
2190
+
2191
+ const comparators = { pixelmatch };
2192
+ function getComparator(comparator, context) {
2193
+ if (comparator in comparators) {
2194
+ return comparators[comparator];
2195
+ }
2196
+ const customComparators = context.project.config.browser.expect?.toMatchScreenshot?.comparators;
2197
+ if (customComparators && comparator in customComparators) {
2198
+ return customComparators[comparator];
2199
+ }
2200
+ throw new Error(`Unrecognized comparator ${comparator}`);
2201
+ }
2202
+
2203
+ const defaultOptions = {
2204
+ comparatorName: "pixelmatch",
2205
+ comparatorOptions: {},
2206
+ screenshotOptions: {
2207
+ animations: "disabled",
2208
+ caret: "hide",
2209
+ fullPage: false,
2210
+ maskColor: "#ff00ff",
2211
+ omitBackground: false,
2212
+ scale: "device"
2213
+ },
2214
+ timeout: 5e3,
2215
+ resolveDiffPath: ({ arg, ext, root, attachmentsDir, browserName, platform, testFileDirectory, testFileName }) => resolve(root, attachmentsDir, testFileDirectory, testFileName, `${arg}-${browserName}-${platform}${ext}`),
2216
+ resolveScreenshotPath: ({ arg, ext, root, screenshotDirectory, testFileDirectory, testFileName, browserName }) => resolve(root, testFileDirectory, screenshotDirectory, testFileName, `${arg}-${browserName}-${platform}${ext}`)
2217
+ };
2218
+ const supportedExtensions = ["png"];
2219
+ function resolveOptions({ context, name, options, testName }) {
2220
+ if (context.testPath === undefined) {
2221
+ throw new Error("`resolveOptions` has to be used in a test file");
2222
+ }
2223
+ const resolvedOptions = deepMerge(Object.create(null), defaultOptions, context.project.config.browser.expect?.toMatchScreenshot ?? {}, options);
2224
+ const extensionFromName = extname(name);
2225
+ // technically the type is a lie, but we check beneath and reassign otherwise
2226
+ let extension = extensionFromName.replace(/^\./, "");
2227
+ // when `type` will be supported in `screenshotOptions`:
2228
+ // - `'png'` should end up in `defaultOptions.screenshotOptions.type`
2229
+ // - this condition should be switched around
2230
+ // - the assignment should be `resolvedOptions.screenshotOptions.type = extension`
2231
+ // - everything using `extension` should use `resolvedOptions.screenshotOptions.type`
2232
+ if (supportedExtensions.includes(extension) === false) {
2233
+ extension = "png";
2234
+ }
2235
+ const { root } = context.project.serializedConfig;
2236
+ const resolvePathData = {
2237
+ arg: sanitizeArg(
2238
+ // remove the extension only if it ends up being used
2239
+ extensionFromName.endsWith(extension) ? basename(name, extensionFromName) : name
2240
+ ),
2241
+ ext: `.${extension}`,
2242
+ platform: platform(),
2243
+ root,
2244
+ screenshotDirectory: relative(root, join(root, context.project.config.browser.screenshotDirectory ?? "__screenshots__")),
2245
+ attachmentsDir: relative(root, context.project.config.attachmentsDir),
2246
+ testFileDirectory: relative(root, dirname(context.testPath)),
2247
+ testFileName: basename(context.testPath),
2248
+ testName: sanitize(testName, false),
2249
+ browserName: context.project.config.browser.name
2250
+ };
2251
+ return {
2252
+ codec: getCodec(extension),
2253
+ comparator: getComparator(resolvedOptions.comparatorName, context),
2254
+ resolvedOptions,
2255
+ paths: {
2256
+ reference: resolvedOptions.resolveScreenshotPath(resolvePathData),
2257
+ get diffs() {
2258
+ const diffs = {
2259
+ reference: resolvedOptions.resolveDiffPath({
2260
+ ...resolvePathData,
2261
+ arg: `${resolvePathData.arg}-reference`
2262
+ }),
2263
+ actual: resolvedOptions.resolveDiffPath({
2264
+ ...resolvePathData,
2265
+ arg: `${resolvePathData.arg}-actual`
2266
+ }),
2267
+ diff: resolvedOptions.resolveDiffPath({
2268
+ ...resolvePathData,
2269
+ arg: `${resolvePathData.arg}-diff`
2270
+ })
2271
+ };
2272
+ Object.defineProperty(this, "diffs", { value: diffs });
2273
+ return diffs;
2274
+ }
2275
+ }
2276
+ };
2277
+ }
2278
+ /**
2279
+ * Sanitizes a string by removing or transforming characters to ensure it is
2280
+ * safe for use as a filename or path segment. It supports two modes:
2281
+ *
2282
+ * 1. Non-path mode (`keepPaths === false`):
2283
+ * - Replaces one or more whitespace characters (`\s+`) with a single hyphen (`-`).
2284
+ * - Removes any character that is not a word character (`\w`) or a hyphen (`-`).
2285
+ * - Collapses multiple consecutive hyphens (`-{2,}`) into a single hyphen.
2286
+ *
2287
+ * 2. Path-preserving mode (`keepPaths === true`):
2288
+ * - Splits the input string on the path separator.
2289
+ * - Sanitizes each path segment individually in non-path mode.
2290
+ * - Joins the sanitized segments back together.
2291
+ *
2292
+ * @param input - The raw string to sanitize.
2293
+ * @param keepPaths - If `false`, performs a flat sanitization (drops path segments).
2294
+ * If `true`, treats `input` as a path: each segment is sanitized independently,
2295
+ * preserving separators.
2296
+ */
2297
+ function sanitize(input, keepPaths) {
2298
+ if (keepPaths === false) {
2299
+ return input.replace(/\s+/g, "-").replace(/[^\w-]+/g, "").replace(/-{2,}/g, "-");
2300
+ }
2301
+ return input.split("/").map((path) => sanitize(path, false)).join("/");
2302
+ }
2303
+ /**
2304
+ * Takes a string, treats it as a potential path or filename, and ensures it cannot
2305
+ * escape the root directory or contain invalid characters. Internally, it:
2306
+ *
2307
+ * 1. Prepends the path separator to the raw input to form a path-like string.
2308
+ * 2. Uses {@linkcode relative|relative('/', <that-path>)} to compute a relative
2309
+ * path from the root, which effectively strips any leading separators and prevents
2310
+ * traversal above the root.
2311
+ * 3. Passes the resulting relative path into {@linkcode sanitize|sanitize(..., true)},
2312
+ * preserving any path separators but sanitizing each segment.
2313
+ *
2314
+ * @param input - The raw string to clean.
2315
+ */
2316
+ function sanitizeArg(input) {
2317
+ return sanitize(relative("/", join("/", input)), true);
2318
+ }
2319
+ /**
2320
+ * Takes a screenshot and decodes it using the provided codec.
2321
+ *
2322
+ * The screenshot is taken as a base64 string and then decoded into the format
2323
+ * expected by the comparator.
2324
+ *
2325
+ * @returns `Promise` resolving to the decoded screenshot data
2326
+ */
2327
+ function takeDecodedScreenshot({ codec, context, element, name, screenshotOptions }) {
2328
+ return context.triggerCommand("__vitest_takeScreenshot", name, {
2329
+ ...screenshotOptions,
2330
+ save: false,
2331
+ element
2332
+ }).then(({ buffer }) => codec.decode(buffer, {}));
2333
+ }
2334
+ /**
2335
+ * Creates a promise that resolves to `null` after the specified timeout.
2336
+ * If the timeout is `0`, the promise resolves immediately.
2337
+ *
2338
+ * @param timeout - The delay in milliseconds before the promise resolves
2339
+ * @returns `Promise` that resolves to `null` after the timeout
2340
+ */
2341
+ function asyncTimeout(timeout) {
2342
+ return new Promise((resolve) => {
2343
+ if (timeout === 0) {
2344
+ resolve(null);
2345
+ } else {
2346
+ setTimeout(() => resolve(null), timeout);
2347
+ }
2348
+ });
2349
+ }
2350
+
2351
+ /**
2352
+ * Browser command that compares a screenshot against a stored reference.
2353
+ *
2354
+ * The comparison workflow is organized as follows:
2355
+ *
2356
+ * 1. Load existing reference (if any)
2357
+ * 2. Capture a stable screenshot (retrying until the page stops changing)
2358
+ * 3. Determine the outcome based on capture results and update settings
2359
+ * 4. Write any necessary files (new references, diffs)
2360
+ * 5. Return result for the test runner
2361
+ */
2362
+ const screenshotMatcher = async (context, name, testName, options) => {
2363
+ if (!context.testPath) {
2364
+ throw new Error("Cannot compare screenshots without a test path");
2365
+ }
2366
+ const { element } = options;
2367
+ const { codec, comparator, paths, resolvedOptions: { comparatorOptions, screenshotOptions, timeout } } = resolveOptions({
2368
+ context,
2369
+ name,
2370
+ testName,
2371
+ options
2372
+ });
2373
+ const referenceFile = await readFile$1(paths.reference).catch(() => null);
2374
+ const reference = referenceFile && await codec.decode(referenceFile, {});
2375
+ const screenshotResult = await waitForStableScreenshot({
2376
+ codec,
2377
+ comparator,
2378
+ comparatorOptions,
2379
+ context,
2380
+ element,
2381
+ name: `${Date.now()}-${basename(paths.reference)}`,
2382
+ reference,
2383
+ screenshotOptions
2384
+ }, timeout);
2385
+ const outcome = await determineOutcome({
2386
+ reference,
2387
+ screenshot: screenshotResult && screenshotResult.actual,
2388
+ retries: screenshotResult?.retries ?? 0,
2389
+ updateSnapshot: context.project.serializedConfig.snapshotOptions.updateSnapshot,
2390
+ paths,
2391
+ comparator,
2392
+ comparatorOptions
2393
+ });
2394
+ await performSideEffects(outcome, codec);
2395
+ return buildOutput(outcome, timeout);
2396
+ };
2397
+ /**
2398
+ * Core comparison logic that produces a {@linkcode MatchOutcome}.
2399
+ *
2400
+ * All branching logic lives here. This is the single source of truth for "what happened".
2401
+ *
2402
+ * The outcome carries all data needed by {@linkcode performSideEffects} and {@linkcode buildOutput}.
2403
+ */
2404
+ async function determineOutcome({ comparator, comparatorOptions, paths, reference, retries, screenshot, updateSnapshot }) {
2405
+ if (screenshot === null) {
2406
+ return {
2407
+ type: "unstable-screenshot",
2408
+ reference: reference && {
2409
+ image: reference,
2410
+ path: paths.reference
2411
+ }
2412
+ };
2413
+ }
2414
+ // no reference to compare against - create one based on update settings
2415
+ if (reference === null || updateSnapshot === "all") {
2416
+ if (updateSnapshot === "all") {
2417
+ return {
2418
+ type: "update-reference",
2419
+ reference: {
2420
+ image: screenshot,
2421
+ path: paths.reference
2422
+ }
2423
+ };
2424
+ }
2425
+ const location = updateSnapshot === "none" ? "diffs" : "reference";
2426
+ return {
2427
+ type: "missing-reference",
2428
+ location,
2429
+ reference: {
2430
+ image: screenshot,
2431
+ path: location === "reference" ? paths.reference : paths.diffs.reference
2432
+ }
2433
+ };
2434
+ }
2435
+ // first capture matched reference (used as baseline) - no further comparison needed
2436
+ if (retries === 0) {
2437
+ return { type: "matched-immediately" };
2438
+ }
2439
+ const comparisonResult = await comparator(reference, screenshot, {
2440
+ createDiff: true,
2441
+ ...comparatorOptions
2442
+ });
2443
+ if (comparisonResult.pass) {
2444
+ return { type: "matched-after-comparison" };
2445
+ }
2446
+ return {
2447
+ type: "mismatch",
2448
+ reference: {
2449
+ image: reference,
2450
+ path: paths.reference
2451
+ },
2452
+ actual: {
2453
+ image: screenshot,
2454
+ path: paths.diffs.actual
2455
+ },
2456
+ diff: comparisonResult.diff && {
2457
+ image: {
2458
+ data: comparisonResult.diff,
2459
+ metadata: reference.metadata
2460
+ },
2461
+ path: paths.diffs.diff
2462
+ },
2463
+ message: comparisonResult.message
2464
+ };
2465
+ }
2466
+ /**
2467
+ * Writes files to disk based on the outcome.
2468
+ *
2469
+ * Only `missing-reference`, `update-reference`, and `mismatch` write files. Successful matches produce no side effects.
2470
+ */
2471
+ async function performSideEffects(outcome, codec) {
2472
+ switch (outcome.type) {
2473
+ case "missing-reference":
2474
+ case "update-reference": {
2475
+ await writeScreenshot(outcome.reference.path, await codec.encode(outcome.reference.image, {}));
2476
+ break;
2477
+ }
2478
+ case "mismatch": {
2479
+ await writeScreenshot(outcome.actual.path, await codec.encode(outcome.actual.image, {}));
2480
+ if (outcome.diff) {
2481
+ await writeScreenshot(outcome.diff.path, await codec.encode(outcome.diff.image, {}));
2482
+ }
2483
+ break;
2484
+ }
2485
+ }
2486
+ }
2487
+ /**
2488
+ * Transforms a {@linkcode MatchOutcome} into the output format expected by the test runner.
2489
+ *
2490
+ * Maps each outcome to a pass/fail result with metadata and error messages.
2491
+ */
2492
+ function buildOutput(outcome, timeout) {
2493
+ switch (outcome.type) {
2494
+ case "unstable-screenshot": return {
2495
+ pass: false,
2496
+ reference: outcome.reference && {
2497
+ path: outcome.reference.path,
2498
+ width: outcome.reference.image.metadata.width,
2499
+ height: outcome.reference.image.metadata.height
2500
+ },
2501
+ actual: null,
2502
+ diff: null,
2503
+ message: `Could not capture a stable screenshot within ${timeout}ms.`
2504
+ };
2505
+ case "missing-reference": {
2506
+ return {
2507
+ pass: false,
2508
+ reference: {
2509
+ path: outcome.reference.path,
2510
+ width: outcome.reference.image.metadata.width,
2511
+ height: outcome.reference.image.metadata.height
2512
+ },
2513
+ actual: null,
2514
+ diff: null,
2515
+ message: outcome.location === "reference" ? "No existing reference screenshot found; a new one was created. Review it before running tests again." : "No existing reference screenshot found."
2516
+ };
2517
+ }
2518
+ case "update-reference":
2519
+ case "matched-immediately":
2520
+ case "matched-after-comparison": return { pass: true };
2521
+ case "mismatch": return {
2522
+ pass: false,
2523
+ reference: {
2524
+ path: outcome.reference.path,
2525
+ width: outcome.reference.image.metadata.width,
2526
+ height: outcome.reference.image.metadata.height
2527
+ },
2528
+ actual: {
2529
+ path: outcome.actual.path,
2530
+ width: outcome.actual.image.metadata.width,
2531
+ height: outcome.actual.image.metadata.height
2532
+ },
2533
+ diff: outcome.diff && {
2534
+ path: outcome.diff.path,
2535
+ width: outcome.diff.image.metadata.width,
2536
+ height: outcome.diff.image.metadata.height
2537
+ },
2538
+ message: `Screenshot does not match the stored reference.${outcome.message ? `\n${outcome.message}` : ""}`
2539
+ };
2540
+ default: {
2541
+ return {
2542
+ pass: false,
2543
+ actual: null,
2544
+ reference: null,
2545
+ diff: null,
2546
+ message: `Outcome (${outcome.type}) not handled. This is a bug in Vitest. Please, open an issue with reproduction.`
2547
+ };
2548
+ }
2549
+ }
2550
+ }
2551
+ /**
2552
+ * Captures a stable screenshot with timeout handling.
2553
+ *
2554
+ * Wraps {@linkcode getStableScreenshot} with an abort controller that triggers when the timeout expires. Returns `null` if the page never stabilizes.
2555
+ */
2556
+ async function waitForStableScreenshot(options, timeout) {
2557
+ const abortController = new AbortController();
2558
+ const stableScreenshot = getStableScreenshot(options, abortController.signal);
2559
+ const result = await (timeout === 0 ? stableScreenshot : Promise.race([stableScreenshot, asyncTimeout(timeout).finally(() => abortController.abort())]));
2560
+ return result;
2561
+ }
2562
+ /**
2563
+ * Takes screenshots repeatedly until the page reaches a visually stable state.
2564
+ *
2565
+ * This function compares consecutive screenshots and continues taking new ones until two consecutive screenshots match according to the provided comparator.
2566
+ *
2567
+ * The process works as follows:
2568
+ *
2569
+ * 1. Uses as baseline an optional reference screenshot or takes a new screenshot
2570
+ * 2. Takes a screenshot and compares with baseline
2571
+ * 3. If they match, the page is considered stable and the function returns
2572
+ * 4. If they don't match, it continues with the newer screenshot as the baseline
2573
+ * 5. Repeats until stability is achieved or the operation is aborted
2574
+ *
2575
+ * @returns `Promise` resolving to an object containing the retry count and final screenshot
2576
+ */
2577
+ async function getStableScreenshot({ codec, context, comparator, comparatorOptions, element, name, reference, screenshotOptions }, signal) {
2578
+ const screenshotArgument = {
2579
+ codec,
2580
+ context,
2581
+ element,
2582
+ name,
2583
+ screenshotOptions
2584
+ };
2585
+ let retries = 0;
2586
+ let decodedBaseline = reference;
2587
+ while (signal.aborted === false) {
2588
+ if (decodedBaseline === null) {
2589
+ decodedBaseline = takeDecodedScreenshot(screenshotArgument);
2590
+ }
2591
+ const [image1, image2] = await Promise.all([decodedBaseline, takeDecodedScreenshot(screenshotArgument)]);
2592
+ const isStable = (await comparator(image1, image2, {
2593
+ ...comparatorOptions,
2594
+ createDiff: false
2595
+ })).pass;
2596
+ decodedBaseline = image2;
2597
+ if (isStable) {
2598
+ break;
2599
+ }
2600
+ retries += 1;
2601
+ }
2602
+ return {
2603
+ retries,
2604
+ actual: await decodedBaseline
2605
+ };
2606
+ }
2607
+ /** Writes encoded images to disk, creating parent directories as needed. */
2608
+ async function writeScreenshot(path, image) {
2609
+ try {
2610
+ await mkdir(dirname(path), { recursive: true });
2611
+ await writeFile$1(path, image);
2612
+ } catch (cause) {
2613
+ throw new Error("Couldn't write file to fs", { cause });
2614
+ }
2615
+ }
2616
+
2617
+ var builtinCommands = {
2618
+ readFile,
2619
+ removeFile,
2620
+ writeFile,
2621
+ __vitest_fileInfo: _fileInfo,
2622
+ __vitest_screenshot: screenshot,
2623
+ __vitest_screenshotMatcher: screenshotMatcher
2624
+ };
2625
+
2626
+ class BrowserServerState {
2627
+ orchestrators = new Map();
2628
+ testers = new Map();
2629
+ }
2630
+
2631
+ class ProjectBrowser {
2632
+ testerHtml;
2633
+ testerFilepath;
2634
+ provider;
2635
+ vitest;
2636
+ config;
2637
+ children = new Set();
2638
+ parent;
2639
+ state = new BrowserServerState();
2640
+ constructor(project, base) {
2641
+ this.project = project;
2642
+ this.base = base;
2643
+ this.vitest = project.vitest;
2644
+ this.config = project.config;
2645
+ const distRoot = dirname(fileURLToPath(import.meta.url));
2646
+ const testerHtmlPath = project.config.browser.testerHtmlPath ? resolve(project.config.root, project.config.browser.testerHtmlPath) : resolve(distRoot, "client/tester/tester.html");
2647
+ if (!existsSync(testerHtmlPath)) {
2648
+ throw new Error(`Tester HTML file "${testerHtmlPath}" doesn't exist.`);
2649
+ }
2650
+ this.testerFilepath = testerHtmlPath;
2651
+ this.testerHtml = readFile$1(testerHtmlPath, "utf8").then((html) => this.testerHtml = html);
2652
+ }
2653
+ get vite() {
2654
+ return this.parent.vite;
2655
+ }
2656
+ commands = {};
2657
+ registerCommand(name, cb) {
2658
+ if (!/^[a-z_$][\w$]*$/i.test(name)) {
2659
+ throw new Error(`Invalid command name "${name}". Only alphanumeric characters, $ and _ are allowed.`);
2660
+ }
2661
+ this.commands[name] = cb;
2662
+ }
2663
+ triggerCommand = ((name, context, ...args) => {
2664
+ if (name in this.commands) {
2665
+ return this.commands[name](context, ...args);
2666
+ }
2667
+ if (name in this.parent.commands) {
2668
+ return this.parent.commands[name](context, ...args);
2669
+ }
2670
+ throw new Error(`Provider ${this.provider.name} does not support command "${name}".`);
2671
+ });
2672
+ wrapSerializedConfig() {
2673
+ const config = wrapConfig(this.project.serializedConfig);
2674
+ config.env ??= {};
2675
+ config.env.VITEST_BROWSER_DEBUG = process.env.VITEST_BROWSER_DEBUG || "";
2676
+ return config;
2677
+ }
2678
+ async initBrowserProvider(project) {
2679
+ if (this.provider) {
2680
+ return;
2681
+ }
2682
+ this.provider = await getBrowserProvider(project.config.browser, project);
2683
+ if (this.provider.initScripts) {
2684
+ this.parent.initScripts = this.provider.initScripts;
2685
+ // make sure the script can be imported
2686
+ const allow = this.parent.vite.config.server.fs.allow;
2687
+ this.provider.initScripts.forEach((script) => {
2688
+ if (!allow.includes(script)) {
2689
+ allow.push(script);
2690
+ }
2691
+ });
2692
+ }
2693
+ }
2694
+ parseErrorStacktrace(e, options = {}) {
2695
+ return this.parent.parseErrorStacktrace(e, options);
2696
+ }
2697
+ parseStacktrace(trace, options = {}) {
2698
+ return this.parent.parseStacktrace(trace, options);
2699
+ }
2700
+ async close() {
2701
+ await this.parent.vite.close();
2702
+ }
2703
+ }
2704
+ function wrapConfig(config) {
2705
+ return {
2706
+ ...config,
2707
+ testNamePattern: config.testNamePattern ? config.testNamePattern.toString() : undefined
2708
+ };
2709
+ }
2710
+
2711
+ class ParentBrowserProject {
2712
+ orchestratorScripts;
2713
+ faviconUrl;
2714
+ prefixOrchestratorUrl;
2715
+ prefixTesterUrl;
2716
+ manifest;
2717
+ vite;
2718
+ stackTraceOptions;
2719
+ orchestratorHtml;
2720
+ injectorJs;
2721
+ errorCatcherUrl;
2722
+ locatorsUrl;
2723
+ matchersUrl;
2724
+ stateJs;
2725
+ initScripts = [];
2726
+ commands = {};
2727
+ children = new Set();
2728
+ vitest;
2729
+ config;
2730
+ // cache for non-vite source maps
2731
+ sourceMapCache = new Map();
2732
+ constructor(project, base) {
2733
+ this.project = project;
2734
+ this.base = base;
2735
+ this.vitest = project.vitest;
2736
+ this.config = project.config;
2737
+ this.stackTraceOptions = {
2738
+ frameFilter: project.config.onStackTrace,
2739
+ getSourceMap: (id) => {
2740
+ if (this.sourceMapCache.has(id)) {
2741
+ return this.sourceMapCache.get(id);
2742
+ }
2743
+ const result = this.vite.moduleGraph.getModuleById(id)?.transformResult;
2744
+ // this can happen for bundled dependencies in node_modules/.vite
2745
+ if (result && !result.map) {
2746
+ const sourceMapUrl = this.retrieveSourceMapURL(result.code);
2747
+ if (!sourceMapUrl) {
2748
+ return null;
2749
+ }
2750
+ const filepathDir = dirname(id);
2751
+ const sourceMapPath = resolve(filepathDir, sourceMapUrl);
2752
+ const map = JSON.parse(readFileSync(sourceMapPath, "utf-8"));
2753
+ this.sourceMapCache.set(id, map);
2754
+ return map;
2755
+ }
2756
+ return result?.map;
2757
+ },
2758
+ getUrlId: (id) => {
2759
+ const moduleGraph = this.vite.environments.client.moduleGraph;
2760
+ const mod = moduleGraph.getModuleById(id);
2761
+ if (mod) {
2762
+ return id;
2763
+ }
2764
+ const resolvedPath = resolve(this.vite.config.root, id.slice(1));
2765
+ const modUrl = moduleGraph.getModuleById(resolvedPath);
2766
+ if (modUrl) {
2767
+ return resolvedPath;
2768
+ }
2769
+ // some browsers (looking at you, safari) don't report queries in stack traces
2770
+ // the next best thing is to try the first id that this file resolves to
2771
+ const files = moduleGraph.getModulesByFile(resolvedPath);
2772
+ if (files && files.size) {
2773
+ return files.values().next().value.id;
2774
+ }
2775
+ return id;
2776
+ }
2777
+ };
2778
+ for (const [name, command] of Object.entries(builtinCommands)) {
2779
+ this.commands[name] ??= command;
2780
+ }
2781
+ // validate names because they can't be used as identifiers
2782
+ for (const command in project.config.browser.commands) {
2783
+ if (!/^[a-z_$][\w$]*$/i.test(command)) {
2784
+ throw new Error(`Invalid command name "${command}". Only alphanumeric characters, $ and _ are allowed.`);
2785
+ }
2786
+ this.commands[command] = project.config.browser.commands[command];
2787
+ }
2788
+ this.prefixTesterUrl = `${base || "/"}`;
2789
+ this.prefixOrchestratorUrl = `${base}__vitest_test__/`;
2790
+ this.faviconUrl = `${base}__vitest__/favicon.svg`;
2791
+ this.manifest = (async () => {
2792
+ return JSON.parse(await readFile$1(`${distRoot}/client/.vite/manifest.json`, "utf8"));
2793
+ })().then((manifest) => this.manifest = manifest);
2794
+ this.orchestratorHtml = (project.config.browser.ui ? readFile$1(resolve(distRoot, "client/__vitest__/index.html"), "utf8") : readFile$1(resolve(distRoot, "client/orchestrator.html"), "utf8")).then((html) => this.orchestratorHtml = html);
2795
+ this.injectorJs = readFile$1(resolve(distRoot, "client/esm-client-injector.js"), "utf8").then((js) => this.injectorJs = js);
2796
+ this.errorCatcherUrl = join("/@fs/", resolve(distRoot, "client/error-catcher.js"));
2797
+ this.matchersUrl = join("/@fs/", distRoot, "expect-element.js");
2798
+ this.stateJs = readFile$1(resolve(distRoot, "state.js"), "utf-8").then((js) => this.stateJs = js);
2799
+ }
2800
+ setServer(vite) {
2801
+ this.vite = vite;
2802
+ }
2803
+ spawn(project) {
2804
+ if (!this.vite) {
2805
+ throw new Error(`Cannot spawn child server without a parent dev server.`);
2806
+ }
2807
+ const clone = new ProjectBrowser(project, "/");
2808
+ clone.parent = this;
2809
+ this.children.add(clone);
2810
+ return clone;
2811
+ }
2812
+ parseErrorStacktrace(e, options = {}) {
2813
+ return parseErrorStacktrace(e, {
2814
+ ...this.stackTraceOptions,
2815
+ ...options
2816
+ });
2817
+ }
2818
+ parseStacktrace(trace, options = {}) {
2819
+ return parseStacktrace(trace, {
2820
+ ...this.stackTraceOptions,
2821
+ ...options
2822
+ });
2823
+ }
2824
+ cdps = new Map();
2825
+ cdpSessionsPromises = new Map();
2826
+ async ensureCDPHandler(sessionId, rpcId) {
2827
+ const cachedHandler = this.cdps.get(rpcId);
2828
+ if (cachedHandler) {
2829
+ return cachedHandler;
2830
+ }
2831
+ const browserSession = this.vitest._browserSessions.getSession(sessionId);
2832
+ if (!browserSession) {
2833
+ throw new Error(`Session "${sessionId}" not found.`);
2834
+ }
2835
+ const browser = browserSession.project.browser;
2836
+ const provider = browser.provider;
2837
+ if (!provider) {
2838
+ throw new Error(`Browser provider is not defined for the project "${browserSession.project.name}".`);
2839
+ }
2840
+ if (!provider.getCDPSession) {
2841
+ throw new Error(`CDP is not supported by the provider "${provider.name}".`);
2842
+ }
2843
+ const session = await this.cdpSessionsPromises.get(rpcId) ?? await (async () => {
2844
+ const promise = provider.getCDPSession(sessionId).finally(() => {
2845
+ this.cdpSessionsPromises.delete(rpcId);
2846
+ });
2847
+ this.cdpSessionsPromises.set(rpcId, promise);
2848
+ return promise;
2849
+ })();
2850
+ const rpc = browser.state.testers.get(rpcId);
2851
+ if (!rpc) {
2852
+ throw new Error(`Tester RPC "${rpcId}" was not established.`);
2853
+ }
2854
+ const handler = new BrowserServerCDPHandler(session, rpc);
2855
+ this.cdps.set(rpcId, handler);
2856
+ return handler;
2857
+ }
2858
+ removeCDPHandler(sessionId) {
2859
+ this.cdps.delete(sessionId);
2860
+ }
2861
+ async formatScripts(scripts) {
2862
+ if (!scripts?.length) {
2863
+ return [];
2864
+ }
2865
+ const server = this.vite;
2866
+ const promises = scripts.map(async ({ content, src, async, id, type = "module" }, index) => {
2867
+ const srcLink = (src ? (await server.pluginContainer.resolveId(src))?.id : undefined) || src;
2868
+ const transformId = srcLink || join(server.config.root, `virtual__${id || `injected-${index}.js`}`);
2869
+ await server.moduleGraph.ensureEntryFromUrl(transformId);
2870
+ const contentProcessed = content && type === "module" ? (await server.pluginContainer.transform(content, transformId)).code : content;
2871
+ return {
2872
+ tag: "script",
2873
+ attrs: {
2874
+ type,
2875
+ ...async ? { async: "" } : {},
2876
+ ...srcLink ? { src: srcLink.startsWith("http") ? srcLink : slash(`/@fs/${srcLink}`) } : {}
2877
+ },
2878
+ injectTo: "head",
2879
+ children: contentProcessed || ""
2880
+ };
2881
+ });
2882
+ return await Promise.all(promises);
2883
+ }
2884
+ resolveTesterUrl(pathname) {
2885
+ const [sessionId, testFile] = pathname.slice(this.prefixTesterUrl.length).split("/");
2886
+ const decodedTestFile = decodeURIComponent(testFile);
2887
+ return {
2888
+ sessionId,
2889
+ testFile: decodedTestFile
2890
+ };
2891
+ }
2892
+ retrieveSourceMapURL(source) {
2893
+ const re = /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm;
2894
+ // Keep executing the search to find the *last* sourceMappingURL to avoid
2895
+ // picking up sourceMappingURLs from comments, strings, etc.
2896
+ let lastMatch, match;
2897
+ // eslint-disable-next-line no-cond-assign
2898
+ while (match = re.exec(source)) {
2899
+ lastMatch = match;
2900
+ }
2901
+ if (!lastMatch) {
2902
+ return null;
2903
+ }
2904
+ return lastMatch[1];
2905
+ }
2906
+ }
2907
+
2908
+ //#region src/messages.ts
2909
+ const TYPE_REQUEST = "q";
2910
+ const TYPE_RESPONSE = "s";
2911
+
2912
+ //#endregion
2913
+ //#region src/utils.ts
2914
+ function createPromiseWithResolvers() {
2915
+ let resolve;
2916
+ let reject;
2917
+ return {
2918
+ promise: new Promise((res, rej) => {
2919
+ resolve = res;
2920
+ reject = rej;
2921
+ }),
2922
+ resolve,
2923
+ reject
2924
+ };
2925
+ }
2926
+ const random = Math.random.bind(Math);
2927
+ const urlAlphabet = "useandom-26T198340PX75pxJACKVERYMINDBUSHWOLF_GQZbfghjklqvwyzrict";
2928
+ function nanoid(size = 21) {
2929
+ let id = "";
2930
+ let i = size;
2931
+ while (i--) id += urlAlphabet[random() * 64 | 0];
2932
+ return id;
2933
+ }
2934
+
2935
+ //#endregion
2936
+ //#region src/main.ts
2937
+ const DEFAULT_TIMEOUT = 6e4;
2938
+ const defaultSerialize = (i) => i;
2939
+ const defaultDeserialize = defaultSerialize;
2940
+ const { clearTimeout, setTimeout: setTimeout$1 } = globalThis;
2941
+ function createBirpc($functions, options) {
2942
+ const { post, on, off = () => {}, eventNames = [], serialize = defaultSerialize, deserialize = defaultDeserialize, resolver, bind = "rpc", timeout = DEFAULT_TIMEOUT, proxify = true } = options;
2943
+ let $closed = false;
2944
+ const _rpcPromiseMap = /* @__PURE__ */ new Map();
2945
+ let _promiseInit;
2946
+ let rpc;
2947
+ async function _call(method, args, event, optional) {
2948
+ if ($closed) throw new Error(`[birpc] rpc is closed, cannot call "${method}"`);
2949
+ const req = {
2950
+ m: method,
2951
+ a: args,
2952
+ t: TYPE_REQUEST
2953
+ };
2954
+ if (optional) req.o = true;
2955
+ const send = async (_req) => post(serialize(_req));
2956
+ if (event) {
2957
+ await send(req);
2958
+ return;
2959
+ }
2960
+ if (_promiseInit) try {
2961
+ await _promiseInit;
2962
+ } finally {
2963
+ _promiseInit = void 0;
2964
+ }
2965
+ let { promise, resolve, reject } = createPromiseWithResolvers();
2966
+ const id = nanoid();
2967
+ req.i = id;
2968
+ let timeoutId;
2969
+ async function handler(newReq = req) {
2970
+ if (timeout >= 0) {
2971
+ timeoutId = setTimeout$1(() => {
2972
+ try {
2973
+ if (options.onTimeoutError?.call(rpc, method, args) !== true) throw new Error(`[birpc] timeout on calling "${method}"`);
2974
+ } catch (e) {
2975
+ reject(e);
2976
+ }
2977
+ _rpcPromiseMap.delete(id);
2978
+ }, timeout);
2979
+ if (typeof timeoutId === "object") timeoutId = timeoutId.unref?.();
2980
+ }
2981
+ _rpcPromiseMap.set(id, {
2982
+ resolve,
2983
+ reject,
2984
+ timeoutId,
2985
+ method
2986
+ });
2987
+ await send(newReq);
2988
+ return promise;
2989
+ }
2990
+ try {
2991
+ if (options.onRequest) await options.onRequest.call(rpc, req, handler, resolve);
2992
+ else await handler();
2993
+ } catch (e) {
2994
+ if (options.onGeneralError?.call(rpc, e) !== true) throw e;
2995
+ return;
2996
+ } finally {
2997
+ clearTimeout(timeoutId);
2998
+ _rpcPromiseMap.delete(id);
2999
+ }
3000
+ return promise;
3001
+ }
3002
+ const builtinMethods = {
3003
+ $call: (method, ...args) => _call(method, args, false),
3004
+ $callOptional: (method, ...args) => _call(method, args, false, true),
3005
+ $callEvent: (method, ...args) => _call(method, args, true),
3006
+ $callRaw: (options$1) => _call(options$1.method, options$1.args, options$1.event, options$1.optional),
3007
+ $rejectPendingCalls,
3008
+ get $closed() {
3009
+ return $closed;
3010
+ },
3011
+ get $meta() {
3012
+ return options.meta;
3013
+ },
3014
+ $close,
3015
+ $functions
3016
+ };
3017
+ if (proxify) rpc = new Proxy({}, { get(_, method) {
3018
+ if (Object.prototype.hasOwnProperty.call(builtinMethods, method)) return builtinMethods[method];
3019
+ if (method === "then" && !eventNames.includes("then") && !("then" in $functions)) return void 0;
3020
+ const sendEvent = (...args) => _call(method, args, true);
3021
+ if (eventNames.includes(method)) {
3022
+ sendEvent.asEvent = sendEvent;
3023
+ return sendEvent;
3024
+ }
3025
+ const sendCall = (...args) => _call(method, args, false);
3026
+ sendCall.asEvent = sendEvent;
3027
+ return sendCall;
3028
+ } });
3029
+ else rpc = builtinMethods;
3030
+ function $close(customError) {
3031
+ $closed = true;
3032
+ _rpcPromiseMap.forEach(({ reject, method }) => {
3033
+ const error = /* @__PURE__ */ new Error(`[birpc] rpc is closed, cannot call "${method}"`);
3034
+ if (customError) {
3035
+ customError.cause ??= error;
3036
+ return reject(customError);
3037
+ }
3038
+ reject(error);
3039
+ });
3040
+ _rpcPromiseMap.clear();
3041
+ off(onMessage);
3042
+ }
3043
+ function $rejectPendingCalls(handler) {
3044
+ const handlerResults = Array.from(_rpcPromiseMap.values()).map(({ method, reject }) => {
3045
+ if (!handler) return reject(/* @__PURE__ */ new Error(`[birpc]: rejected pending call "${method}".`));
3046
+ return handler({
3047
+ method,
3048
+ reject
3049
+ });
3050
+ });
3051
+ _rpcPromiseMap.clear();
3052
+ return handlerResults;
3053
+ }
3054
+ async function onMessage(data, ...extra) {
3055
+ let msg;
3056
+ try {
3057
+ msg = deserialize(data);
3058
+ } catch (e) {
3059
+ if (options.onGeneralError?.call(rpc, e) !== true) throw e;
3060
+ return;
3061
+ }
3062
+ if (msg.t === TYPE_REQUEST) {
3063
+ const { m: method, a: args, o: optional } = msg;
3064
+ let result, error;
3065
+ let fn = await (resolver ? resolver.call(rpc, method, $functions[method]) : $functions[method]);
3066
+ if (optional) fn ||= () => void 0;
3067
+ if (!fn) error = /* @__PURE__ */ new Error(`[birpc] function "${method}" not found`);
3068
+ else try {
3069
+ result = await fn.apply(bind === "rpc" ? rpc : $functions, args);
3070
+ } catch (e) {
3071
+ error = e;
3072
+ }
3073
+ if (msg.i) {
3074
+ if (error && options.onFunctionError) {
3075
+ if (options.onFunctionError.call(rpc, error, method, args) === true) return;
3076
+ }
3077
+ if (!error) try {
3078
+ await post(serialize({
3079
+ t: TYPE_RESPONSE,
3080
+ i: msg.i,
3081
+ r: result
3082
+ }), ...extra);
3083
+ return;
3084
+ } catch (e) {
3085
+ error = e;
3086
+ if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
3087
+ }
3088
+ try {
3089
+ await post(serialize({
3090
+ t: TYPE_RESPONSE,
3091
+ i: msg.i,
3092
+ e: error
3093
+ }), ...extra);
3094
+ } catch (e) {
3095
+ if (options.onGeneralError?.call(rpc, e, method, args) !== true) throw e;
3096
+ }
3097
+ }
3098
+ } else {
3099
+ const { i: ack, r: result, e: error } = msg;
3100
+ const promise = _rpcPromiseMap.get(ack);
3101
+ if (promise) {
3102
+ clearTimeout(promise.timeoutId);
3103
+ if (error) promise.reject(error);
3104
+ else promise.resolve(result);
3105
+ }
3106
+ _rpcPromiseMap.delete(ack);
3107
+ }
3108
+ }
3109
+ _promiseInit = on(onMessage);
3110
+ return rpc;
3111
+ }
3112
+
3113
+ const debug = createDebugger("vitest:browser:api");
3114
+ const BROWSER_API_PATH = "/__vitest_browser_api__";
3115
+ function setupBrowserRpc(globalServer, defaultMockerRegistry) {
3116
+ const vite = globalServer.vite;
3117
+ const vitest = globalServer.vitest;
3118
+ const wss = new WebSocketServer({ noServer: true });
3119
+ vite.httpServer?.on("upgrade", (request, socket, head) => {
3120
+ if (!request.url) {
3121
+ return;
3122
+ }
3123
+ const { pathname, searchParams } = new URL(request.url, "http://localhost");
3124
+ if (pathname !== BROWSER_API_PATH) {
3125
+ return;
3126
+ }
3127
+ if (!isValidApiRequest(vitest.config, request)) {
3128
+ socket.destroy();
3129
+ return;
3130
+ }
3131
+ const type = searchParams.get("type");
3132
+ const rpcId = searchParams.get("rpcId");
3133
+ const sessionId = searchParams.get("sessionId");
3134
+ const projectName = searchParams.get("projectName");
3135
+ if (type !== "tester" && type !== "orchestrator") {
3136
+ return error(new Error(`[vitest] Type query in ${request.url} is invalid. Type should be either "tester" or "orchestrator".`));
3137
+ }
3138
+ if (!sessionId || !rpcId || projectName == null) {
3139
+ return error(new Error(`[vitest] Invalid URL ${request.url}. "projectName", "sessionId" and "rpcId" queries are required.`));
3140
+ }
3141
+ const sessions = vitest._browserSessions;
3142
+ if (!sessions.sessionIds.has(sessionId)) {
3143
+ const ids = [...sessions.sessionIds].join(", ");
3144
+ return error(new Error(`[vitest] Unknown session id "${sessionId}". Expected one of ${ids}.`));
3145
+ }
3146
+ if (type === "orchestrator") {
3147
+ const session = sessions.getSession(sessionId);
3148
+ // it's possible the session was already resolved by the preview provider
3149
+ session?.connected();
3150
+ }
3151
+ const project = vitest.getProjectByName(projectName);
3152
+ if (!project) {
3153
+ return error(new Error(`[vitest] Project "${projectName}" not found.`));
3154
+ }
3155
+ wss.handleUpgrade(request, socket, head, (ws) => {
3156
+ wss.emit("connection", ws, request);
3157
+ const { rpc, offCancel } = setupClient(project, rpcId, ws);
3158
+ const state = project.browser.state;
3159
+ const clients = type === "tester" ? state.testers : state.orchestrators;
3160
+ clients.set(rpcId, rpc);
3161
+ debug?.("[%s] Browser API connected to %s", rpcId, type);
3162
+ ws.on("close", () => {
3163
+ debug?.("[%s] Browser API disconnected from %s", rpcId, type);
3164
+ offCancel();
3165
+ clients.delete(rpcId);
3166
+ globalServer.removeCDPHandler(rpcId);
3167
+ if (type === "orchestrator") {
3168
+ sessions.destroySession(sessionId);
3169
+ }
3170
+ // this will reject any hanging methods if there are any
3171
+ rpc.$close(new Error(`[vitest] Browser connection was closed while running tests. Was the page closed unexpectedly?`));
3172
+ });
3173
+ });
3174
+ });
3175
+ // we don't throw an error inside a stream because this can segfault the process
3176
+ function error(err) {
3177
+ console.error(err);
3178
+ vitest.state.catchError(err, "RPC Error");
3179
+ }
3180
+ function checkFileAccess(path) {
3181
+ if (!isFileServingAllowed(path, vite)) {
3182
+ throw new Error(`Access denied to "${path}". See Vite config documentation for "server.fs": https://vitejs.dev/config/server-options.html#server-fs-strict.`);
3183
+ }
3184
+ }
3185
+ function setupClient(project, rpcId, ws) {
3186
+ const mockResolver = new ServerMockResolver(globalServer.vite, { moduleDirectories: project.config?.deps?.moduleDirectories });
3187
+ const mocker = project.browser?.provider.mocker;
3188
+ const rpc = createBirpc({
3189
+ async onUnhandledError(error, type) {
3190
+ if (error && typeof error === "object") {
3191
+ const _error = error;
3192
+ _error.stacks = globalServer.parseErrorStacktrace(_error);
3193
+ }
3194
+ vitest.state.catchError(error, type);
3195
+ },
3196
+ async onQueued(method, file) {
3197
+ if (method === "collect") {
3198
+ vitest.state.collectFiles(project, [file]);
3199
+ } else {
3200
+ await vitest._testRun.enqueued(project, file);
3201
+ }
3202
+ },
3203
+ async onCollected(method, files) {
3204
+ if (method === "collect") {
3205
+ vitest.state.collectFiles(project, files);
3206
+ } else {
3207
+ await vitest._testRun.collected(project, files);
3208
+ }
3209
+ },
3210
+ async onTaskArtifactRecord(id, artifact) {
3211
+ return vitest._testRun.recordArtifact(id, artifact);
3212
+ },
3213
+ async onTaskUpdate(method, packs, events) {
3214
+ if (method === "collect") {
3215
+ vitest.state.updateTasks(packs);
3216
+ } else {
3217
+ await vitest._testRun.updated(packs, events);
3218
+ }
3219
+ },
3220
+ onAfterSuiteRun(meta) {
3221
+ vitest.coverageProvider?.onAfterSuiteRun(meta);
3222
+ },
3223
+ async sendLog(method, log) {
3224
+ if (method === "collect") {
3225
+ vitest.state.updateUserLog(log);
3226
+ } else {
3227
+ await vitest._testRun.log(log);
3228
+ }
3229
+ },
3230
+ resolveSnapshotPath(testPath) {
3231
+ return vitest.snapshot.resolvePath(testPath, { config: project.serializedConfig });
3232
+ },
3233
+ resolveSnapshotRawPath(testPath, rawPath) {
3234
+ return vitest.snapshot.resolveRawPath(testPath, rawPath);
3235
+ },
3236
+ snapshotSaved(snapshot) {
3237
+ vitest.snapshot.add(snapshot);
3238
+ },
3239
+ async readSnapshotFile(snapshotPath) {
3240
+ checkFileAccess(snapshotPath);
3241
+ if (!existsSync(snapshotPath)) {
3242
+ return null;
3243
+ }
3244
+ return promises.readFile(snapshotPath, "utf-8");
3245
+ },
3246
+ async saveSnapshotFile(id, content) {
3247
+ checkFileAccess(id);
3248
+ await promises.mkdir(dirname(id), { recursive: true });
3249
+ return promises.writeFile(id, content, "utf-8");
3250
+ },
3251
+ async removeSnapshotFile(id) {
3252
+ checkFileAccess(id);
3253
+ if (!existsSync(id)) {
3254
+ throw new Error(`Snapshot file "${id}" does not exist.`);
3255
+ }
3256
+ return promises.unlink(id);
3257
+ },
3258
+ getBrowserFileSourceMap(id) {
3259
+ const mod = globalServer.vite.moduleGraph.getModuleById(id);
3260
+ const result = mod?.transformResult;
3261
+ // this can happen for bundled dependencies in node_modules/.vite
3262
+ if (result && !result.map) {
3263
+ const sourceMapUrl = retrieveSourceMapURL(result.code);
3264
+ if (!sourceMapUrl) {
3265
+ return null;
3266
+ }
3267
+ const filepathDir = dirname(id);
3268
+ const sourceMapPath = resolve(filepathDir, sourceMapUrl);
3269
+ try {
3270
+ const map = JSON.parse(readFileSync(sourceMapPath, "utf-8"));
3271
+ return map;
3272
+ } catch {
3273
+ return null;
3274
+ }
3275
+ }
3276
+ return result?.map;
3277
+ },
3278
+ cancelCurrentRun(reason) {
3279
+ vitest.cancelCurrentRun(reason);
3280
+ },
3281
+ async resolveId(id, importer) {
3282
+ return mockResolver.resolveId(id, importer);
3283
+ },
3284
+ debug(...args) {
3285
+ vitest.logger.console.debug(...args);
3286
+ },
3287
+ getCountOfFailedTests() {
3288
+ return vitest.state.getCountOfFailedTests();
3289
+ },
3290
+ async wdioSwitchContext(direction) {
3291
+ const provider = project.browser.provider;
3292
+ if (!provider) {
3293
+ throw new Error("Commands are only available for browser tests.");
3294
+ }
3295
+ if (provider.name !== "webdriverio") {
3296
+ throw new Error("Switch context is only available for WebDriverIO provider.");
3297
+ }
3298
+ if (direction === "iframe") {
3299
+ await provider.switchToTestFrame();
3300
+ } else {
3301
+ await provider.switchToMainFrame();
3302
+ }
3303
+ },
3304
+ async triggerCommand(sessionId, command, testPath, payload) {
3305
+ debug?.("[%s] Triggering command \"%s\"", sessionId, command);
3306
+ const provider = project.browser.provider;
3307
+ if (!provider) {
3308
+ throw new Error("Commands are only available for browser tests.");
3309
+ }
3310
+ const context = Object.assign({
3311
+ testPath,
3312
+ project,
3313
+ provider,
3314
+ contextId: sessionId,
3315
+ sessionId,
3316
+ triggerCommand: (name, ...args) => {
3317
+ return project.browser.triggerCommand(name, context, ...args);
3318
+ }
3319
+ }, provider.getCommandsContext(sessionId));
3320
+ return await project.browser.triggerCommand(command, context, ...payload);
3321
+ },
3322
+ resolveMock(rawId, importer, options) {
3323
+ return mockResolver.resolveMock(rawId, importer, options);
3324
+ },
3325
+ invalidate(ids) {
3326
+ return mockResolver.invalidate(ids);
3327
+ },
3328
+ async registerMock(sessionId, module) {
3329
+ if (!mocker) {
3330
+ // make sure modules are not processed yet in case they were imported before
3331
+ // and were not mocked
3332
+ mockResolver.invalidate([module.id]);
3333
+ if (module.type === "manual") {
3334
+ const mock = ManualMockedModule.fromJSON(module, async () => {
3335
+ try {
3336
+ const { keys } = await rpc.resolveManualMock(module.url);
3337
+ return Object.fromEntries(keys.map((key) => [key, null]));
3338
+ } catch (err) {
3339
+ vitest.state.catchError(err, "Manual Mock Resolver Error");
3340
+ return {};
3341
+ }
3342
+ });
3343
+ defaultMockerRegistry.add(mock);
3344
+ } else {
3345
+ if (module.type === "redirect") {
3346
+ const redirectUrl = new URL(module.redirect);
3347
+ module.redirect = join(vite.config.root, redirectUrl.pathname);
3348
+ }
3349
+ defaultMockerRegistry.register(module);
3350
+ }
3351
+ return;
3352
+ }
3353
+ if (module.type === "manual") {
3354
+ const manualModule = ManualMockedModule.fromJSON(module, async () => {
3355
+ const { keys } = await rpc.resolveManualMock(module.url);
3356
+ return Object.fromEntries(keys.map((key) => [key, null]));
3357
+ });
3358
+ await mocker.register(sessionId, manualModule);
3359
+ } else if (module.type === "redirect") {
3360
+ await mocker.register(sessionId, RedirectedModule.fromJSON(module));
3361
+ } else if (module.type === "automock") {
3362
+ await mocker.register(sessionId, AutomockedModule.fromJSON(module));
3363
+ } else if (module.type === "autospy") {
3364
+ await mocker.register(sessionId, AutospiedModule.fromJSON(module));
3365
+ }
3366
+ },
3367
+ clearMocks(sessionId) {
3368
+ if (!mocker) {
3369
+ return defaultMockerRegistry.clear();
3370
+ }
3371
+ return mocker.clear(sessionId);
3372
+ },
3373
+ unregisterMock(sessionId, id) {
3374
+ if (!mocker) {
3375
+ return defaultMockerRegistry.delete(id);
3376
+ }
3377
+ return mocker.delete(sessionId, id);
3378
+ },
3379
+ async sendCdpEvent(sessionId, event, payload) {
3380
+ const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
3381
+ return cdp.send(event, payload);
3382
+ },
3383
+ async trackCdpEvent(sessionId, type, event, listenerId) {
3384
+ const cdp = await globalServer.ensureCDPHandler(sessionId, rpcId);
3385
+ cdp[type](event, listenerId);
3386
+ }
3387
+ }, {
3388
+ post: (msg) => ws.send(msg),
3389
+ on: (fn) => ws.on("message", fn),
3390
+ eventNames: ["onCancel", "cdpEvent"],
3391
+ serialize: (data) => stringify(data, stringifyReplace),
3392
+ deserialize: parse,
3393
+ timeout: -1
3394
+ });
3395
+ const offCancel = vitest.onCancel((reason) => rpc.onCancel(reason));
3396
+ return {
3397
+ rpc,
3398
+ offCancel
3399
+ };
3400
+ }
3401
+ }
3402
+ function retrieveSourceMapURL(source) {
3403
+ const re = /\/\/[@#]\s*sourceMappingURL=([^\s'"]+)\s*$|\/\*[@#]\s*sourceMappingURL=[^\s*'"]+\s*\*\/\s*$/gm;
3404
+ // keep executing the search to find the *last* sourceMappingURL to avoid
3405
+ // picking up sourceMappingURLs from comments, strings, etc.
3406
+ let lastMatch, match;
3407
+ // eslint-disable-next-line no-cond-assign
3408
+ while (match = re.exec(source)) {
3409
+ lastMatch = match;
3410
+ }
3411
+ if (!lastMatch) {
3412
+ return null;
3413
+ }
3414
+ return lastMatch[1];
3415
+ }
3416
+ // Serialization support utils.
3417
+ function cloneByOwnProperties(value) {
3418
+ // Clones the value's properties into a new Object. The simpler approach of
3419
+ // Object.assign() won't work in the case that properties are not enumerable.
3420
+ return Object.getOwnPropertyNames(value).reduce((clone, prop) => ({
3421
+ ...clone,
3422
+ [prop]: value[prop]
3423
+ }), {});
3424
+ }
3425
+ /**
3426
+ * Replacer function for serialization methods such as JS.stringify() or
3427
+ * flatted.stringify().
3428
+ */
3429
+ function stringifyReplace(key, value) {
3430
+ if (value instanceof Error) {
3431
+ const cloned = cloneByOwnProperties(value);
3432
+ return {
3433
+ name: value.name,
3434
+ message: value.message,
3435
+ stack: value.stack,
3436
+ ...cloned
3437
+ };
3438
+ } else {
3439
+ return value;
3440
+ }
3441
+ }
3442
+
3443
+ function defineBrowserCommand(fn) {
3444
+ return fn;
3445
+ }
3446
+ const createBrowserServer = async (options) => {
3447
+ const project = options.project;
3448
+ const configFile = project.vite.config.configFile;
3449
+ if (project.vitest.version !== version) {
3450
+ project.vitest.logger.warn(c.yellow(`Loaded ${c.inverse(c.yellow(` vitest@${project.vitest.version} `))} and ${c.inverse(c.yellow(` @vitest/browser@${version} `))}.` + "\nRunning mixed versions is not supported and may lead into bugs" + "\nUpdate your dependencies and make sure the versions match."));
3451
+ }
3452
+ const server = new ParentBrowserProject(project, "/");
3453
+ const configPath = typeof configFile === "string" ? configFile : false;
3454
+ const logLevel = process.env.VITEST_BROWSER_DEBUG ?? "info";
3455
+ const logger = createViteLogger(project.vitest.logger, logLevel, { allowClearScreen: false });
3456
+ const mockerRegistry = new MockerRegistry();
3457
+ let cacheDir;
3458
+ const vite = await createViteServer({
3459
+ ...project.options,
3460
+ define: project.config.viteDefine,
3461
+ base: "/",
3462
+ root: project.config.root,
3463
+ logLevel,
3464
+ customLogger: {
3465
+ ...logger,
3466
+ info(msg, options) {
3467
+ logger.info(msg, options);
3468
+ if (msg.includes("optimized dependencies changed. reloading")) {
3469
+ logger.warn([c.yellow(`\n${c.bold("[vitest]")} Vite unexpectedly reloaded a test. This may cause tests to fail, lead to flaky behaviour or duplicated test runs.\n`), c.yellow(`For a stable experience, please add mentioned dependencies to your config\'s ${c.bold("`optimizeDeps.include`")} field manually.\n\n`)].join(""));
3470
+ }
3471
+ }
3472
+ },
3473
+ mode: project.config.mode,
3474
+ configFile: configPath,
3475
+ configLoader: project.vite.config.inlineConfig.configLoader,
3476
+ server: {
3477
+ hmr: false,
3478
+ watch: null
3479
+ },
3480
+ cacheDir: project.vite.config.cacheDir,
3481
+ plugins: [
3482
+ {
3483
+ name: "vitest-internal:browser-cacheDir",
3484
+ configResolved(config) {
3485
+ cacheDir = config.cacheDir;
3486
+ }
3487
+ },
3488
+ ...options.mocksPlugins({ filter(id) {
3489
+ if (id.includes(distRoot) || id.includes(cacheDir)) {
3490
+ return false;
3491
+ }
3492
+ return true;
3493
+ } }),
3494
+ options.metaEnvReplacer(),
3495
+ ...project.options?.plugins || [],
3496
+ BrowserPlugin(server),
3497
+ interceptorPlugin({ registry: mockerRegistry }),
3498
+ options.coveragePlugin()
3499
+ ]
3500
+ });
3501
+ await vite.listen();
3502
+ setupBrowserRpc(server, mockerRegistry);
3503
+ return server;
3504
+ };
3505
+ function defineBrowserProvider(options) {
3506
+ return {
3507
+ ...options,
3508
+ options: options.options || {},
3509
+ serverFactory: createBrowserServer
3510
+ };
3511
+ }
3512
+
3513
+ export { createBrowserServer, defineBrowserCommand, defineBrowserProvider, parseKeyDef, resolveScreenshotPath };