@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,2114 @@
1
+ import { processError } from '../utils/error.js';
2
+ import { isObject, createDefer, assertTypes, toArray, isNegativeNaN, objectAttr, shuffle } from '../utils/helpers.js';
3
+ import { getSafeTimers } from '../utils/timers.js';
4
+ import { format, formatRegExp, objDisplay } from '../utils/display.js';
5
+ import { c as createChainable, e as createTaskName, f as findTestFileStackTrace, b as createFileTask, a as calculateSuiteHash, s as someTasksAreOnly, i as interpretTaskModes, l as limitConcurrency, p as partitionSuiteChildren, r as hasTests, q as hasFailed } from './chunk-tasks.js';
6
+ import '../utils/source-map.js';
7
+ import '../../vendor/pathe.mjs';
8
+
9
+ class PendingError extends Error {
10
+ code = "VITEST_PENDING";
11
+ taskId;
12
+ constructor(message, task, note) {
13
+ super(message);
14
+ this.message = message;
15
+ this.note = note;
16
+ this.taskId = task.id;
17
+ }
18
+ }
19
+ class TestRunAbortError extends Error {
20
+ name = "TestRunAbortError";
21
+ reason;
22
+ constructor(message, reason) {
23
+ super(message);
24
+ this.reason = reason;
25
+ }
26
+ }
27
+
28
+ // use WeakMap here to make the Test and Suite object serializable
29
+ const fnMap = new WeakMap();
30
+ const testFixtureMap = new WeakMap();
31
+ const hooksMap = new WeakMap();
32
+ function setFn(key, fn) {
33
+ fnMap.set(key, fn);
34
+ }
35
+ function getFn(key) {
36
+ return fnMap.get(key);
37
+ }
38
+ function setTestFixture(key, fixture) {
39
+ testFixtureMap.set(key, fixture);
40
+ }
41
+ function getTestFixture(key) {
42
+ return testFixtureMap.get(key);
43
+ }
44
+ function setHooks(key, hooks) {
45
+ hooksMap.set(key, hooks);
46
+ }
47
+ function getHooks(key) {
48
+ return hooksMap.get(key);
49
+ }
50
+
51
+ function mergeScopedFixtures(testFixtures, scopedFixtures) {
52
+ const scopedFixturesMap = scopedFixtures.reduce((map, fixture) => {
53
+ map[fixture.prop] = fixture;
54
+ return map;
55
+ }, {});
56
+ const newFixtures = {};
57
+ testFixtures.forEach((fixture) => {
58
+ const useFixture = scopedFixturesMap[fixture.prop] || { ...fixture };
59
+ newFixtures[useFixture.prop] = useFixture;
60
+ });
61
+ for (const fixtureKep in newFixtures) {
62
+ var _fixture$deps;
63
+ const fixture = newFixtures[fixtureKep];
64
+ // if the fixture was define before the scope, then its dep
65
+ // will reference the original fixture instead of the scope
66
+ fixture.deps = (_fixture$deps = fixture.deps) === null || _fixture$deps === void 0 ? void 0 : _fixture$deps.map((dep) => newFixtures[dep.prop]);
67
+ }
68
+ return Object.values(newFixtures);
69
+ }
70
+ function mergeContextFixtures(fixtures, context, runner) {
71
+ const fixtureOptionKeys = [
72
+ "auto",
73
+ "injected",
74
+ "scope"
75
+ ];
76
+ const fixtureArray = Object.entries(fixtures).map(([prop, value]) => {
77
+ const fixtureItem = { value };
78
+ if (Array.isArray(value) && value.length >= 2 && isObject(value[1]) && Object.keys(value[1]).some((key) => fixtureOptionKeys.includes(key))) {
79
+ var _runner$injectValue;
80
+ // fixture with options
81
+ Object.assign(fixtureItem, value[1]);
82
+ const userValue = value[0];
83
+ fixtureItem.value = fixtureItem.injected ? ((_runner$injectValue = runner.injectValue) === null || _runner$injectValue === void 0 ? void 0 : _runner$injectValue.call(runner, prop)) ?? userValue : userValue;
84
+ }
85
+ fixtureItem.scope = fixtureItem.scope || "test";
86
+ if (fixtureItem.scope === "worker" && !runner.getWorkerContext) {
87
+ fixtureItem.scope = "file";
88
+ }
89
+ fixtureItem.prop = prop;
90
+ fixtureItem.isFn = typeof fixtureItem.value === "function";
91
+ return fixtureItem;
92
+ });
93
+ if (Array.isArray(context.fixtures)) {
94
+ context.fixtures = context.fixtures.concat(fixtureArray);
95
+ } else {
96
+ context.fixtures = fixtureArray;
97
+ }
98
+ // Update dependencies of fixture functions
99
+ fixtureArray.forEach((fixture) => {
100
+ if (fixture.isFn) {
101
+ const usedProps = getUsedProps(fixture.value);
102
+ if (usedProps.length) {
103
+ fixture.deps = context.fixtures.filter(({ prop }) => prop !== fixture.prop && usedProps.includes(prop));
104
+ }
105
+ // test can access anything, so we ignore it
106
+ if (fixture.scope !== "test") {
107
+ var _fixture$deps2;
108
+ (_fixture$deps2 = fixture.deps) === null || _fixture$deps2 === void 0 ? void 0 : _fixture$deps2.forEach((dep) => {
109
+ if (!dep.isFn) {
110
+ // non fn fixtures are always resolved and available to anyone
111
+ return;
112
+ }
113
+ // worker scope can only import from worker scope
114
+ if (fixture.scope === "worker" && dep.scope === "worker") {
115
+ return;
116
+ }
117
+ // file scope an import from file and worker scopes
118
+ if (fixture.scope === "file" && dep.scope !== "test") {
119
+ return;
120
+ }
121
+ throw new SyntaxError(`cannot use the ${dep.scope} fixture "${dep.prop}" inside the ${fixture.scope} fixture "${fixture.prop}"`);
122
+ });
123
+ }
124
+ }
125
+ });
126
+ return context;
127
+ }
128
+ const fixtureValueMaps = new Map();
129
+ const cleanupFnArrayMap = new Map();
130
+ async function callFixtureCleanup(context) {
131
+ const cleanupFnArray = cleanupFnArrayMap.get(context) ?? [];
132
+ for (const cleanup of cleanupFnArray.reverse()) {
133
+ await cleanup();
134
+ }
135
+ cleanupFnArrayMap.delete(context);
136
+ }
137
+ function withFixtures(runner, fn, testContext) {
138
+ return (hookContext) => {
139
+ const context = hookContext || testContext;
140
+ if (!context) {
141
+ return fn({});
142
+ }
143
+ const fixtures = getTestFixture(context);
144
+ if (!(fixtures === null || fixtures === void 0 ? void 0 : fixtures.length)) {
145
+ return fn(context);
146
+ }
147
+ const usedProps = getUsedProps(fn);
148
+ const hasAutoFixture = fixtures.some(({ auto }) => auto);
149
+ if (!usedProps.length && !hasAutoFixture) {
150
+ return fn(context);
151
+ }
152
+ if (!fixtureValueMaps.get(context)) {
153
+ fixtureValueMaps.set(context, new Map());
154
+ }
155
+ const fixtureValueMap = fixtureValueMaps.get(context);
156
+ if (!cleanupFnArrayMap.has(context)) {
157
+ cleanupFnArrayMap.set(context, []);
158
+ }
159
+ const cleanupFnArray = cleanupFnArrayMap.get(context);
160
+ const usedFixtures = fixtures.filter(({ prop, auto }) => auto || usedProps.includes(prop));
161
+ const pendingFixtures = resolveDeps(usedFixtures);
162
+ if (!pendingFixtures.length) {
163
+ return fn(context);
164
+ }
165
+ async function resolveFixtures() {
166
+ for (const fixture of pendingFixtures) {
167
+ // fixture could be already initialized during "before" hook
168
+ if (fixtureValueMap.has(fixture)) {
169
+ continue;
170
+ }
171
+ const resolvedValue = await resolveFixtureValue(runner, fixture, context, cleanupFnArray);
172
+ context[fixture.prop] = resolvedValue;
173
+ fixtureValueMap.set(fixture, resolvedValue);
174
+ if (fixture.scope === "test") {
175
+ cleanupFnArray.unshift(() => {
176
+ fixtureValueMap.delete(fixture);
177
+ });
178
+ }
179
+ }
180
+ }
181
+ return resolveFixtures().then(() => fn(context));
182
+ };
183
+ }
184
+ const globalFixturePromise = new WeakMap();
185
+ function resolveFixtureValue(runner, fixture, context, cleanupFnArray) {
186
+ var _runner$getWorkerCont;
187
+ const fileContext = getFileContext(context.task.file);
188
+ const workerContext = (_runner$getWorkerCont = runner.getWorkerContext) === null || _runner$getWorkerCont === void 0 ? void 0 : _runner$getWorkerCont.call(runner);
189
+ if (!fixture.isFn) {
190
+ var _fixture$prop;
191
+ fileContext[_fixture$prop = fixture.prop] ?? (fileContext[_fixture$prop] = fixture.value);
192
+ if (workerContext) {
193
+ var _fixture$prop2;
194
+ workerContext[_fixture$prop2 = fixture.prop] ?? (workerContext[_fixture$prop2] = fixture.value);
195
+ }
196
+ return fixture.value;
197
+ }
198
+ if (fixture.scope === "test") {
199
+ return resolveFixtureFunction(fixture.value, context, cleanupFnArray);
200
+ }
201
+ // in case the test runs in parallel
202
+ if (globalFixturePromise.has(fixture)) {
203
+ return globalFixturePromise.get(fixture);
204
+ }
205
+ let fixtureContext;
206
+ if (fixture.scope === "worker") {
207
+ if (!workerContext) {
208
+ throw new TypeError("[@vitest/runner] The worker context is not available in the current test runner. Please, provide the `getWorkerContext` method when initiating the runner.");
209
+ }
210
+ fixtureContext = workerContext;
211
+ } else {
212
+ fixtureContext = fileContext;
213
+ }
214
+ if (fixture.prop in fixtureContext) {
215
+ return fixtureContext[fixture.prop];
216
+ }
217
+ if (!cleanupFnArrayMap.has(fixtureContext)) {
218
+ cleanupFnArrayMap.set(fixtureContext, []);
219
+ }
220
+ const cleanupFnFileArray = cleanupFnArrayMap.get(fixtureContext);
221
+ const promise = resolveFixtureFunction(fixture.value, fixtureContext, cleanupFnFileArray).then((value) => {
222
+ fixtureContext[fixture.prop] = value;
223
+ globalFixturePromise.delete(fixture);
224
+ return value;
225
+ });
226
+ globalFixturePromise.set(fixture, promise);
227
+ return promise;
228
+ }
229
+ async function resolveFixtureFunction(fixtureFn, context, cleanupFnArray) {
230
+ // wait for `use` call to extract fixture value
231
+ const useFnArgPromise = createDefer();
232
+ let isUseFnArgResolved = false;
233
+ const fixtureReturn = fixtureFn(context, async (useFnArg) => {
234
+ // extract `use` argument
235
+ isUseFnArgResolved = true;
236
+ useFnArgPromise.resolve(useFnArg);
237
+ // suspend fixture teardown by holding off `useReturnPromise` resolution until cleanup
238
+ const useReturnPromise = createDefer();
239
+ cleanupFnArray.push(async () => {
240
+ // start teardown by resolving `use` Promise
241
+ useReturnPromise.resolve();
242
+ // wait for finishing teardown
243
+ await fixtureReturn;
244
+ });
245
+ await useReturnPromise;
246
+ }).catch((e) => {
247
+ // treat fixture setup error as test failure
248
+ if (!isUseFnArgResolved) {
249
+ useFnArgPromise.reject(e);
250
+ return;
251
+ }
252
+ // otherwise re-throw to avoid silencing error during cleanup
253
+ throw e;
254
+ });
255
+ return useFnArgPromise;
256
+ }
257
+ function resolveDeps(fixtures, depSet = new Set(), pendingFixtures = []) {
258
+ fixtures.forEach((fixture) => {
259
+ if (pendingFixtures.includes(fixture)) {
260
+ return;
261
+ }
262
+ if (!fixture.isFn || !fixture.deps) {
263
+ pendingFixtures.push(fixture);
264
+ return;
265
+ }
266
+ if (depSet.has(fixture)) {
267
+ throw new Error(`Circular fixture dependency detected: ${fixture.prop} <- ${[...depSet].reverse().map((d) => d.prop).join(" <- ")}`);
268
+ }
269
+ depSet.add(fixture);
270
+ resolveDeps(fixture.deps, depSet, pendingFixtures);
271
+ pendingFixtures.push(fixture);
272
+ depSet.clear();
273
+ });
274
+ return pendingFixtures;
275
+ }
276
+ function getUsedProps(fn) {
277
+ let fnString = filterOutComments(fn.toString());
278
+ // match lowered async function and strip it off
279
+ // example code on esbuild-try https://esbuild.github.io/try/#YgAwLjI0LjAALS1zdXBwb3J0ZWQ6YXN5bmMtYXdhaXQ9ZmFsc2UAZQBlbnRyeS50cwBjb25zdCBvID0gewogIGYxOiBhc3luYyAoKSA9PiB7fSwKICBmMjogYXN5bmMgKGEpID0+IHt9LAogIGYzOiBhc3luYyAoYSwgYikgPT4ge30sCiAgZjQ6IGFzeW5jIGZ1bmN0aW9uKGEpIHt9LAogIGY1OiBhc3luYyBmdW5jdGlvbiBmZihhKSB7fSwKICBhc3luYyBmNihhKSB7fSwKCiAgZzE6IGFzeW5jICgpID0+IHt9LAogIGcyOiBhc3luYyAoeyBhIH0pID0+IHt9LAogIGczOiBhc3luYyAoeyBhIH0sIGIpID0+IHt9LAogIGc0OiBhc3luYyBmdW5jdGlvbiAoeyBhIH0pIHt9LAogIGc1OiBhc3luYyBmdW5jdGlvbiBnZyh7IGEgfSkge30sCiAgYXN5bmMgZzYoeyBhIH0pIHt9LAoKICBoMTogYXN5bmMgKCkgPT4ge30sCiAgLy8gY29tbWVudCBiZXR3ZWVuCiAgaDI6IGFzeW5jIChhKSA9PiB7fSwKfQ
280
+ // __async(this, null, function*
281
+ // __async(this, arguments, function*
282
+ // __async(this, [_0, _1], function*
283
+ if (/__async\((?:this|null), (?:null|arguments|\[[_0-9, ]*\]), function\*/.test(fnString)) {
284
+ fnString = fnString.split(/__async\((?:this|null),/)[1];
285
+ }
286
+ const match = fnString.match(/[^(]*\(([^)]*)/);
287
+ if (!match) {
288
+ return [];
289
+ }
290
+ const args = splitByComma(match[1]);
291
+ if (!args.length) {
292
+ return [];
293
+ }
294
+ let first = args[0];
295
+ if ("__VITEST_FIXTURE_INDEX__" in fn) {
296
+ first = args[fn.__VITEST_FIXTURE_INDEX__];
297
+ if (!first) {
298
+ return [];
299
+ }
300
+ }
301
+ if (!(first[0] === "{" && first.endsWith("}"))) {
302
+ throw new Error(`The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "${first}".`);
303
+ }
304
+ const _first = first.slice(1, -1).replace(/\s/g, "");
305
+ const props = splitByComma(_first).map((prop) => {
306
+ return prop.replace(/:.*|=.*/g, "");
307
+ });
308
+ const last = props.at(-1);
309
+ if (last && last.startsWith("...")) {
310
+ throw new Error(`Rest parameters are not supported in fixtures, received "${last}".`);
311
+ }
312
+ return props;
313
+ }
314
+ function filterOutComments(s) {
315
+ const result = [];
316
+ let commentState = "none";
317
+ for (let i = 0; i < s.length; ++i) {
318
+ if (commentState === "singleline") {
319
+ if (s[i] === "\n") {
320
+ commentState = "none";
321
+ }
322
+ } else if (commentState === "multiline") {
323
+ if (s[i - 1] === "*" && s[i] === "/") {
324
+ commentState = "none";
325
+ }
326
+ } else if (commentState === "none") {
327
+ if (s[i] === "/" && s[i + 1] === "/") {
328
+ commentState = "singleline";
329
+ } else if (s[i] === "/" && s[i + 1] === "*") {
330
+ commentState = "multiline";
331
+ i += 2;
332
+ } else {
333
+ result.push(s[i]);
334
+ }
335
+ }
336
+ }
337
+ return result.join("");
338
+ }
339
+ function splitByComma(s) {
340
+ const result = [];
341
+ const stack = [];
342
+ let start = 0;
343
+ for (let i = 0; i < s.length; i++) {
344
+ if (s[i] === "{" || s[i] === "[") {
345
+ stack.push(s[i] === "{" ? "}" : "]");
346
+ } else if (s[i] === stack.at(-1)) {
347
+ stack.pop();
348
+ } else if (!stack.length && s[i] === ",") {
349
+ const token = s.substring(start, i).trim();
350
+ if (token) {
351
+ result.push(token);
352
+ }
353
+ start = i + 1;
354
+ }
355
+ }
356
+ const lastToken = s.substring(start).trim();
357
+ if (lastToken) {
358
+ result.push(lastToken);
359
+ }
360
+ return result;
361
+ }
362
+
363
+ let _test;
364
+ function setCurrentTest(test) {
365
+ _test = test;
366
+ }
367
+ function getCurrentTest() {
368
+ return _test;
369
+ }
370
+ const tests = [];
371
+ function addRunningTest(test) {
372
+ tests.push(test);
373
+ return () => {
374
+ tests.splice(tests.indexOf(test));
375
+ };
376
+ }
377
+ function getRunningTests() {
378
+ return tests;
379
+ }
380
+
381
+ function getDefaultHookTimeout() {
382
+ return getRunner().config.hookTimeout;
383
+ }
384
+ const CLEANUP_TIMEOUT_KEY = Symbol.for("VITEST_CLEANUP_TIMEOUT");
385
+ const CLEANUP_STACK_TRACE_KEY = Symbol.for("VITEST_CLEANUP_STACK_TRACE");
386
+ function getBeforeHookCleanupCallback(hook, result, context) {
387
+ if (typeof result === "function") {
388
+ const timeout = CLEANUP_TIMEOUT_KEY in hook && typeof hook[CLEANUP_TIMEOUT_KEY] === "number" ? hook[CLEANUP_TIMEOUT_KEY] : getDefaultHookTimeout();
389
+ const stackTraceError = CLEANUP_STACK_TRACE_KEY in hook && hook[CLEANUP_STACK_TRACE_KEY] instanceof Error ? hook[CLEANUP_STACK_TRACE_KEY] : undefined;
390
+ return withTimeout(result, timeout, true, stackTraceError, (_, error) => {
391
+ if (context) {
392
+ abortContextSignal(context, error);
393
+ }
394
+ });
395
+ }
396
+ }
397
+ /**
398
+ * Registers a callback function to be executed once before all tests within the current suite.
399
+ * This hook is useful for scenarios where you need to perform setup operations that are common to all tests in a suite, such as initializing a database connection or setting up a test environment.
400
+ *
401
+ * **Note:** The `beforeAll` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
402
+ *
403
+ * @param {Function} fn - The callback function to be executed before all tests.
404
+ * @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
405
+ * @returns {void}
406
+ * @example
407
+ * ```ts
408
+ * // Example of using beforeAll to set up a database connection
409
+ * beforeAll(async () => {
410
+ * await database.connect();
411
+ * });
412
+ * ```
413
+ */
414
+ function beforeAll(fn, timeout = getDefaultHookTimeout()) {
415
+ assertTypes(fn, "\"beforeAll\" callback", ["function"]);
416
+ const stackTraceError = new Error("STACK_TRACE_ERROR");
417
+ return getCurrentSuite().on("beforeAll", Object.assign(withTimeout(fn, timeout, true, stackTraceError), {
418
+ [CLEANUP_TIMEOUT_KEY]: timeout,
419
+ [CLEANUP_STACK_TRACE_KEY]: stackTraceError
420
+ }));
421
+ }
422
+ /**
423
+ * Registers a callback function to be executed once after all tests within the current suite have completed.
424
+ * This hook is useful for scenarios where you need to perform cleanup operations after all tests in a suite have run, such as closing database connections or cleaning up temporary files.
425
+ *
426
+ * **Note:** The `afterAll` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
427
+ *
428
+ * @param {Function} fn - The callback function to be executed after all tests.
429
+ * @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
430
+ * @returns {void}
431
+ * @example
432
+ * ```ts
433
+ * // Example of using afterAll to close a database connection
434
+ * afterAll(async () => {
435
+ * await database.disconnect();
436
+ * });
437
+ * ```
438
+ */
439
+ function afterAll(fn, timeout) {
440
+ assertTypes(fn, "\"afterAll\" callback", ["function"]);
441
+ return getCurrentSuite().on("afterAll", withTimeout(fn, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR")));
442
+ }
443
+ /**
444
+ * Registers a callback function to be executed before each test within the current suite.
445
+ * This hook is useful for scenarios where you need to reset or reinitialize the test environment before each test runs, such as resetting database states, clearing caches, or reinitializing variables.
446
+ *
447
+ * **Note:** The `beforeEach` hooks are executed in the order they are defined one after another. You can configure this by changing the `sequence.hooks` option in the config file.
448
+ *
449
+ * @param {Function} fn - The callback function to be executed before each test. This function receives an `TestContext` parameter if additional test context is needed.
450
+ * @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
451
+ * @returns {void}
452
+ * @example
453
+ * ```ts
454
+ * // Example of using beforeEach to reset a database state
455
+ * beforeEach(async () => {
456
+ * await database.reset();
457
+ * });
458
+ * ```
459
+ */
460
+ function beforeEach(fn, timeout = getDefaultHookTimeout()) {
461
+ assertTypes(fn, "\"beforeEach\" callback", ["function"]);
462
+ const stackTraceError = new Error("STACK_TRACE_ERROR");
463
+ const runner = getRunner();
464
+ return getCurrentSuite().on("beforeEach", Object.assign(withTimeout(withFixtures(runner, fn), timeout ?? getDefaultHookTimeout(), true, stackTraceError, abortIfTimeout), {
465
+ [CLEANUP_TIMEOUT_KEY]: timeout,
466
+ [CLEANUP_STACK_TRACE_KEY]: stackTraceError
467
+ }));
468
+ }
469
+ /**
470
+ * Registers a callback function to be executed after each test within the current suite has completed.
471
+ * This hook is useful for scenarios where you need to clean up or reset the test environment after each test runs, such as deleting temporary files, clearing test-specific database entries, or resetting mocked functions.
472
+ *
473
+ * **Note:** The `afterEach` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
474
+ *
475
+ * @param {Function} fn - The callback function to be executed after each test. This function receives an `TestContext` parameter if additional test context is needed.
476
+ * @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
477
+ * @returns {void}
478
+ * @example
479
+ * ```ts
480
+ * // Example of using afterEach to delete temporary files created during a test
481
+ * afterEach(async () => {
482
+ * await fileSystem.deleteTempFiles();
483
+ * });
484
+ * ```
485
+ */
486
+ function afterEach(fn, timeout) {
487
+ assertTypes(fn, "\"afterEach\" callback", ["function"]);
488
+ const runner = getRunner();
489
+ return getCurrentSuite().on("afterEach", withTimeout(withFixtures(runner, fn), timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
490
+ }
491
+ /**
492
+ * Registers a callback function to be executed when a test fails within the current suite.
493
+ * This function allows for custom actions to be performed in response to test failures, such as logging, cleanup, or additional diagnostics.
494
+ *
495
+ * **Note:** The `onTestFailed` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
496
+ *
497
+ * @param {Function} fn - The callback function to be executed upon a test failure. The function receives the test result (including errors).
498
+ * @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
499
+ * @throws {Error} Throws an error if the function is not called within a test.
500
+ * @returns {void}
501
+ * @example
502
+ * ```ts
503
+ * // Example of using onTestFailed to log failure details
504
+ * onTestFailed(({ errors }) => {
505
+ * console.log(`Test failed: ${test.name}`, errors);
506
+ * });
507
+ * ```
508
+ */
509
+ const onTestFailed = createTestHook("onTestFailed", (test, handler, timeout) => {
510
+ test.onFailed || (test.onFailed = []);
511
+ test.onFailed.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
512
+ });
513
+ /**
514
+ * Registers a callback function to be executed when the current test finishes, regardless of the outcome (pass or fail).
515
+ * This function is ideal for performing actions that should occur after every test execution, such as cleanup, logging, or resetting shared resources.
516
+ *
517
+ * This hook is useful if you have access to a resource in the test itself and you want to clean it up after the test finishes. It is a more compact way to clean up resources than using the combination of `beforeEach` and `afterEach`.
518
+ *
519
+ * **Note:** The `onTestFinished` hooks are running in reverse order of their registration. You can configure this by changing the `sequence.hooks` option in the config file.
520
+ *
521
+ * **Note:** The `onTestFinished` hook is not called if the test is canceled with a dynamic `ctx.skip()` call.
522
+ *
523
+ * @param {Function} fn - The callback function to be executed after a test finishes. The function can receive parameters providing details about the completed test, including its success or failure status.
524
+ * @param {number} [timeout] - Optional timeout in milliseconds for the hook. If not provided, the default hook timeout from the runner's configuration is used.
525
+ * @throws {Error} Throws an error if the function is not called within a test.
526
+ * @returns {void}
527
+ * @example
528
+ * ```ts
529
+ * // Example of using onTestFinished for cleanup
530
+ * const db = await connectToDatabase();
531
+ * onTestFinished(async () => {
532
+ * await db.disconnect();
533
+ * });
534
+ * ```
535
+ */
536
+ const onTestFinished = createTestHook("onTestFinished", (test, handler, timeout) => {
537
+ test.onFinished || (test.onFinished = []);
538
+ test.onFinished.push(withTimeout(handler, timeout ?? getDefaultHookTimeout(), true, new Error("STACK_TRACE_ERROR"), abortIfTimeout));
539
+ });
540
+ function createTestHook(name, handler) {
541
+ return (fn, timeout) => {
542
+ assertTypes(fn, `"${name}" callback`, ["function"]);
543
+ const current = getCurrentTest();
544
+ if (!current) {
545
+ throw new Error(`Hook ${name}() can only be called inside a test`);
546
+ }
547
+ return handler(current, fn, timeout);
548
+ };
549
+ }
550
+
551
+ /**
552
+ * Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
553
+ * Suites can contain both tests and other suites, enabling complex test structures.
554
+ *
555
+ * @param {string} name - The name of the suite, used for identification and reporting.
556
+ * @param {Function} fn - A function that defines the tests and suites within this suite.
557
+ * @example
558
+ * ```ts
559
+ * // Define a suite with two tests
560
+ * suite('Math operations', () => {
561
+ * test('should add two numbers', () => {
562
+ * expect(add(1, 2)).toBe(3);
563
+ * });
564
+ *
565
+ * test('should subtract two numbers', () => {
566
+ * expect(subtract(5, 2)).toBe(3);
567
+ * });
568
+ * });
569
+ * ```
570
+ * @example
571
+ * ```ts
572
+ * // Define nested suites
573
+ * suite('String operations', () => {
574
+ * suite('Trimming', () => {
575
+ * test('should trim whitespace from start and end', () => {
576
+ * expect(' hello '.trim()).toBe('hello');
577
+ * });
578
+ * });
579
+ *
580
+ * suite('Concatenation', () => {
581
+ * test('should concatenate two strings', () => {
582
+ * expect('hello' + ' ' + 'world').toBe('hello world');
583
+ * });
584
+ * });
585
+ * });
586
+ * ```
587
+ */
588
+ const suite = createSuite();
589
+ /**
590
+ * Defines a test case with a given name and test function. The test function can optionally be configured with test options.
591
+ *
592
+ * @param {string | Function} name - The name of the test or a function that will be used as a test name.
593
+ * @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided.
594
+ * @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters.
595
+ * @throws {Error} If called inside another test function.
596
+ * @example
597
+ * ```ts
598
+ * // Define a simple test
599
+ * test('should add two numbers', () => {
600
+ * expect(add(1, 2)).toBe(3);
601
+ * });
602
+ * ```
603
+ * @example
604
+ * ```ts
605
+ * // Define a test with options
606
+ * test('should subtract two numbers', { retry: 3 }, () => {
607
+ * expect(subtract(5, 2)).toBe(3);
608
+ * });
609
+ * ```
610
+ */
611
+ const test = createTest(function(name, optionsOrFn, optionsOrTest) {
612
+ if (getCurrentTest()) {
613
+ throw new Error("Calling the test function inside another test function is not allowed. Please put it inside \"describe\" or \"suite\" so it can be properly collected.");
614
+ }
615
+ getCurrentSuite().test.fn.call(this, formatName(name), optionsOrFn, optionsOrTest);
616
+ });
617
+ /**
618
+ * Creates a suite of tests, allowing for grouping and hierarchical organization of tests.
619
+ * Suites can contain both tests and other suites, enabling complex test structures.
620
+ *
621
+ * @param {string} name - The name of the suite, used for identification and reporting.
622
+ * @param {Function} fn - A function that defines the tests and suites within this suite.
623
+ * @example
624
+ * ```ts
625
+ * // Define a suite with two tests
626
+ * describe('Math operations', () => {
627
+ * test('should add two numbers', () => {
628
+ * expect(add(1, 2)).toBe(3);
629
+ * });
630
+ *
631
+ * test('should subtract two numbers', () => {
632
+ * expect(subtract(5, 2)).toBe(3);
633
+ * });
634
+ * });
635
+ * ```
636
+ * @example
637
+ * ```ts
638
+ * // Define nested suites
639
+ * describe('String operations', () => {
640
+ * describe('Trimming', () => {
641
+ * test('should trim whitespace from start and end', () => {
642
+ * expect(' hello '.trim()).toBe('hello');
643
+ * });
644
+ * });
645
+ *
646
+ * describe('Concatenation', () => {
647
+ * test('should concatenate two strings', () => {
648
+ * expect('hello' + ' ' + 'world').toBe('hello world');
649
+ * });
650
+ * });
651
+ * });
652
+ * ```
653
+ */
654
+ const describe = suite;
655
+ /**
656
+ * Defines a test case with a given name and test function. The test function can optionally be configured with test options.
657
+ *
658
+ * @param {string | Function} name - The name of the test or a function that will be used as a test name.
659
+ * @param {TestOptions | TestFunction} [optionsOrFn] - Optional. The test options or the test function if no explicit name is provided.
660
+ * @param {number | TestOptions | TestFunction} [optionsOrTest] - Optional. The test function or options, depending on the previous parameters.
661
+ * @throws {Error} If called inside another test function.
662
+ * @example
663
+ * ```ts
664
+ * // Define a simple test
665
+ * it('adds two numbers', () => {
666
+ * expect(add(1, 2)).toBe(3);
667
+ * });
668
+ * ```
669
+ * @example
670
+ * ```ts
671
+ * // Define a test with options
672
+ * it('subtracts two numbers', { retry: 3 }, () => {
673
+ * expect(subtract(5, 2)).toBe(3);
674
+ * });
675
+ * ```
676
+ */
677
+ const it = test;
678
+ let runner;
679
+ let defaultSuite;
680
+ let currentTestFilepath;
681
+ function assert(condition, message) {
682
+ if (!condition) {
683
+ throw new Error(`Vitest failed to find ${message}. One of the following is possible:` + "\n- \"vitest\" is imported directly without running \"vitest\" command" + "\n- \"vitest\" is imported inside \"globalSetup\" (to fix this, use \"setupFiles\" instead, because \"globalSetup\" runs in a different context)" + "\n- \"vitest\" is imported inside Vite / Vitest config file" + "\n- Otherwise, it might be a Vitest bug. Please report it to https://github.com/vitest-dev/vitest/issues\n");
684
+ }
685
+ }
686
+ function getDefaultSuite() {
687
+ assert(defaultSuite, "the default suite");
688
+ return defaultSuite;
689
+ }
690
+ function getRunner() {
691
+ assert(runner, "the runner");
692
+ return runner;
693
+ }
694
+ function createDefaultSuite(runner) {
695
+ const config = runner.config.sequence;
696
+ const collector = suite("", { concurrent: config.concurrent }, () => {});
697
+ // no parent suite for top-level tests
698
+ delete collector.suite;
699
+ return collector;
700
+ }
701
+ function clearCollectorContext(file, currentRunner) {
702
+ if (!defaultSuite) {
703
+ defaultSuite = createDefaultSuite(currentRunner);
704
+ }
705
+ defaultSuite.file = file;
706
+ runner = currentRunner;
707
+ currentTestFilepath = file.filepath;
708
+ collectorContext.tasks.length = 0;
709
+ defaultSuite.clear();
710
+ collectorContext.currentSuite = defaultSuite;
711
+ }
712
+ function getCurrentSuite() {
713
+ const currentSuite = collectorContext.currentSuite || defaultSuite;
714
+ assert(currentSuite, "the current suite");
715
+ return currentSuite;
716
+ }
717
+ function createSuiteHooks() {
718
+ return {
719
+ beforeAll: [],
720
+ afterAll: [],
721
+ beforeEach: [],
722
+ afterEach: []
723
+ };
724
+ }
725
+ function parseArguments(optionsOrFn, timeoutOrTest) {
726
+ if (timeoutOrTest != null && typeof timeoutOrTest === "object") {
727
+ throw new TypeError(`Signature "test(name, fn, { ... })" was deprecated in Vitest 3 and removed in Vitest 4. Please, provide options as a second argument instead.`);
728
+ }
729
+ let options = {};
730
+ let fn;
731
+ // it('', () => {}, 1000)
732
+ if (typeof timeoutOrTest === "number") {
733
+ options = { timeout: timeoutOrTest };
734
+ } else if (typeof optionsOrFn === "object") {
735
+ options = optionsOrFn;
736
+ }
737
+ if (typeof optionsOrFn === "function") {
738
+ if (typeof timeoutOrTest === "function") {
739
+ throw new TypeError("Cannot use two functions as arguments. Please use the second argument for options.");
740
+ }
741
+ fn = optionsOrFn;
742
+ } else if (typeof timeoutOrTest === "function") {
743
+ fn = timeoutOrTest;
744
+ }
745
+ return {
746
+ options,
747
+ handler: fn
748
+ };
749
+ }
750
+ // implementations
751
+ function createSuiteCollector(name, factory = () => {}, mode, each, suiteOptions, parentCollectorFixtures) {
752
+ const tasks = [];
753
+ let suite;
754
+ initSuite(true);
755
+ const task = function(name = "", options = {}) {
756
+ var _collectorContext$cur, _collectorContext$cur2, _collectorContext$cur3;
757
+ const timeout = (options === null || options === void 0 ? void 0 : options.timeout) ?? runner.config.testTimeout;
758
+ const currentSuite = (_collectorContext$cur = collectorContext.currentSuite) === null || _collectorContext$cur === void 0 ? void 0 : _collectorContext$cur.suite;
759
+ const task = {
760
+ id: "",
761
+ name,
762
+ fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur2 = collectorContext.currentSuite) === null || _collectorContext$cur2 === void 0 || (_collectorContext$cur2 = _collectorContext$cur2.file) === null || _collectorContext$cur2 === void 0 ? void 0 : _collectorContext$cur2.fullName), name]),
763
+ fullTestName: createTaskName([currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullTestName, name]),
764
+ suite: currentSuite,
765
+ each: options.each,
766
+ fails: options.fails,
767
+ context: undefined,
768
+ type: "test",
769
+ file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur3 = collectorContext.currentSuite) === null || _collectorContext$cur3 === void 0 ? void 0 : _collectorContext$cur3.file),
770
+ timeout,
771
+ retry: options.retry ?? runner.config.retry,
772
+ repeats: options.repeats,
773
+ mode: options.only ? "only" : options.skip ? "skip" : options.todo ? "todo" : "run",
774
+ meta: options.meta ?? Object.create(null),
775
+ annotations: [],
776
+ artifacts: []
777
+ };
778
+ const handler = options.handler;
779
+ if (task.mode === "run" && !handler) {
780
+ task.mode = "todo";
781
+ }
782
+ if (options.concurrent || !options.sequential && runner.config.sequence.concurrent) {
783
+ task.concurrent = true;
784
+ }
785
+ task.shuffle = suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.shuffle;
786
+ const context = createTestContext(task, runner);
787
+ // create test context
788
+ Object.defineProperty(task, "context", {
789
+ value: context,
790
+ enumerable: false
791
+ });
792
+ setTestFixture(context, options.fixtures);
793
+ // custom can be called from any place, let's assume the limit is 15 stacks
794
+ const limit = Error.stackTraceLimit;
795
+ Error.stackTraceLimit = 15;
796
+ const stackTraceError = new Error("STACK_TRACE_ERROR");
797
+ Error.stackTraceLimit = limit;
798
+ if (handler) {
799
+ setFn(task, withTimeout(withAwaitAsyncAssertions(withFixtures(runner, handler, context), task), timeout, false, stackTraceError, (_, error) => abortIfTimeout([context], error)));
800
+ }
801
+ if (runner.config.includeTaskLocation) {
802
+ const error = stackTraceError.stack;
803
+ const stack = findTestFileStackTrace(currentTestFilepath, error);
804
+ if (stack) {
805
+ task.location = {
806
+ line: stack.line,
807
+ column: stack.column
808
+ };
809
+ }
810
+ }
811
+ tasks.push(task);
812
+ return task;
813
+ };
814
+ const test = createTest(function(name, optionsOrFn, timeoutOrTest) {
815
+ let { options, handler } = parseArguments(optionsOrFn, timeoutOrTest);
816
+ // inherit repeats, retry, timeout from suite
817
+ if (typeof suiteOptions === "object") {
818
+ options = Object.assign({}, suiteOptions, options);
819
+ }
820
+ // inherit concurrent / sequential from suite
821
+ options.concurrent = this.concurrent || !this.sequential && (options === null || options === void 0 ? void 0 : options.concurrent);
822
+ options.sequential = this.sequential || !this.concurrent && (options === null || options === void 0 ? void 0 : options.sequential);
823
+ const test = task(formatName(name), {
824
+ ...this,
825
+ ...options,
826
+ handler
827
+ });
828
+ test.type = "test";
829
+ });
830
+ let collectorFixtures = parentCollectorFixtures;
831
+ const collector = {
832
+ type: "collector",
833
+ name,
834
+ mode,
835
+ suite,
836
+ options: suiteOptions,
837
+ test,
838
+ tasks,
839
+ collect,
840
+ task,
841
+ clear,
842
+ on: addHook,
843
+ fixtures() {
844
+ return collectorFixtures;
845
+ },
846
+ scoped(fixtures) {
847
+ const parsed = mergeContextFixtures(fixtures, { fixtures: collectorFixtures }, runner);
848
+ if (parsed.fixtures) {
849
+ collectorFixtures = parsed.fixtures;
850
+ }
851
+ }
852
+ };
853
+ function addHook(name, ...fn) {
854
+ getHooks(suite)[name].push(...fn);
855
+ }
856
+ function initSuite(includeLocation) {
857
+ var _collectorContext$cur4, _collectorContext$cur5, _collectorContext$cur6;
858
+ if (typeof suiteOptions === "number") {
859
+ suiteOptions = { timeout: suiteOptions };
860
+ }
861
+ const currentSuite = (_collectorContext$cur4 = collectorContext.currentSuite) === null || _collectorContext$cur4 === void 0 ? void 0 : _collectorContext$cur4.suite;
862
+ suite = {
863
+ id: "",
864
+ type: "suite",
865
+ name,
866
+ fullName: createTaskName([(currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullName) ?? ((_collectorContext$cur5 = collectorContext.currentSuite) === null || _collectorContext$cur5 === void 0 || (_collectorContext$cur5 = _collectorContext$cur5.file) === null || _collectorContext$cur5 === void 0 ? void 0 : _collectorContext$cur5.fullName), name]),
867
+ fullTestName: createTaskName([currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fullTestName, name]),
868
+ suite: currentSuite,
869
+ mode,
870
+ each,
871
+ file: (currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.file) ?? ((_collectorContext$cur6 = collectorContext.currentSuite) === null || _collectorContext$cur6 === void 0 ? void 0 : _collectorContext$cur6.file),
872
+ shuffle: suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.shuffle,
873
+ tasks: [],
874
+ meta: Object.create(null),
875
+ concurrent: suiteOptions === null || suiteOptions === void 0 ? void 0 : suiteOptions.concurrent
876
+ };
877
+ if (runner && includeLocation && runner.config.includeTaskLocation) {
878
+ const limit = Error.stackTraceLimit;
879
+ Error.stackTraceLimit = 15;
880
+ const error = new Error("stacktrace").stack;
881
+ Error.stackTraceLimit = limit;
882
+ const stack = findTestFileStackTrace(currentTestFilepath, error);
883
+ if (stack) {
884
+ suite.location = {
885
+ line: stack.line,
886
+ column: stack.column
887
+ };
888
+ }
889
+ }
890
+ setHooks(suite, createSuiteHooks());
891
+ }
892
+ function clear() {
893
+ tasks.length = 0;
894
+ initSuite(false);
895
+ }
896
+ async function collect(file) {
897
+ if (!file) {
898
+ throw new TypeError("File is required to collect tasks.");
899
+ }
900
+ if (factory) {
901
+ await runWithSuite(collector, () => factory(test));
902
+ }
903
+ const allChildren = [];
904
+ for (const i of tasks) {
905
+ allChildren.push(i.type === "collector" ? await i.collect(file) : i);
906
+ }
907
+ suite.tasks = allChildren;
908
+ return suite;
909
+ }
910
+ collectTask(collector);
911
+ return collector;
912
+ }
913
+ function withAwaitAsyncAssertions(fn, task) {
914
+ return (async (...args) => {
915
+ const fnResult = await fn(...args);
916
+ // some async expect will be added to this array, in case user forget to await them
917
+ if (task.promises) {
918
+ const result = await Promise.allSettled(task.promises);
919
+ const errors = result.map((r) => r.status === "rejected" ? r.reason : undefined).filter(Boolean);
920
+ if (errors.length) {
921
+ throw errors;
922
+ }
923
+ }
924
+ return fnResult;
925
+ });
926
+ }
927
+ function createSuite() {
928
+ function suiteFn(name, factoryOrOptions, optionsOrFactory) {
929
+ var _currentSuite$options;
930
+ if (getCurrentTest()) {
931
+ throw new Error("Calling the suite function inside test function is not allowed. It can be only called at the top level or inside another suite function.");
932
+ }
933
+ let mode = this.only ? "only" : this.skip ? "skip" : this.todo ? "todo" : "run";
934
+ const currentSuite = collectorContext.currentSuite || defaultSuite;
935
+ let { options, handler: factory } = parseArguments(factoryOrOptions, optionsOrFactory);
936
+ if (mode === "run" && !factory) {
937
+ mode = "todo";
938
+ }
939
+ const isConcurrentSpecified = options.concurrent || this.concurrent || options.sequential === false;
940
+ const isSequentialSpecified = options.sequential || this.sequential || options.concurrent === false;
941
+ // inherit options from current suite
942
+ options = {
943
+ ...currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.options,
944
+ ...options,
945
+ shuffle: this.shuffle ?? options.shuffle ?? (currentSuite === null || currentSuite === void 0 || (_currentSuite$options = currentSuite.options) === null || _currentSuite$options === void 0 ? void 0 : _currentSuite$options.shuffle) ?? (runner === null || runner === void 0 ? void 0 : runner.config.sequence.shuffle)
946
+ };
947
+ // inherit concurrent / sequential from suite
948
+ const isConcurrent = isConcurrentSpecified || options.concurrent && !isSequentialSpecified;
949
+ const isSequential = isSequentialSpecified || options.sequential && !isConcurrentSpecified;
950
+ options.concurrent = isConcurrent && !isSequential;
951
+ options.sequential = isSequential && !isConcurrent;
952
+ return createSuiteCollector(formatName(name), factory, mode, this.each, options, currentSuite === null || currentSuite === void 0 ? void 0 : currentSuite.fixtures());
953
+ }
954
+ suiteFn.each = function(cases, ...args) {
955
+ const suite = this.withContext();
956
+ this.setContext("each", true);
957
+ if (Array.isArray(cases) && args.length) {
958
+ cases = formatTemplateString(cases, args);
959
+ }
960
+ return (name, optionsOrFn, fnOrOptions) => {
961
+ const _name = formatName(name);
962
+ const arrayOnlyCases = cases.every(Array.isArray);
963
+ const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
964
+ const fnFirst = typeof optionsOrFn === "function";
965
+ cases.forEach((i, idx) => {
966
+ const items = Array.isArray(i) ? i : [i];
967
+ if (fnFirst) {
968
+ if (arrayOnlyCases) {
969
+ suite(formatTitle(_name, items, idx), handler ? () => handler(...items) : undefined, options.timeout);
970
+ } else {
971
+ suite(formatTitle(_name, items, idx), handler ? () => handler(i) : undefined, options.timeout);
972
+ }
973
+ } else {
974
+ if (arrayOnlyCases) {
975
+ suite(formatTitle(_name, items, idx), options, handler ? () => handler(...items) : undefined);
976
+ } else {
977
+ suite(formatTitle(_name, items, idx), options, handler ? () => handler(i) : undefined);
978
+ }
979
+ }
980
+ });
981
+ this.setContext("each", undefined);
982
+ };
983
+ };
984
+ suiteFn.for = function(cases, ...args) {
985
+ if (Array.isArray(cases) && args.length) {
986
+ cases = formatTemplateString(cases, args);
987
+ }
988
+ return (name, optionsOrFn, fnOrOptions) => {
989
+ const name_ = formatName(name);
990
+ const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
991
+ cases.forEach((item, idx) => {
992
+ suite(formatTitle(name_, toArray(item), idx), options, handler ? () => handler(item) : undefined);
993
+ });
994
+ };
995
+ };
996
+ suiteFn.skipIf = (condition) => condition ? suite.skip : suite;
997
+ suiteFn.runIf = (condition) => condition ? suite : suite.skip;
998
+ return createChainable([
999
+ "concurrent",
1000
+ "sequential",
1001
+ "shuffle",
1002
+ "skip",
1003
+ "only",
1004
+ "todo"
1005
+ ], suiteFn);
1006
+ }
1007
+ function createTaskCollector(fn, context) {
1008
+ const taskFn = fn;
1009
+ taskFn.each = function(cases, ...args) {
1010
+ const test = this.withContext();
1011
+ this.setContext("each", true);
1012
+ if (Array.isArray(cases) && args.length) {
1013
+ cases = formatTemplateString(cases, args);
1014
+ }
1015
+ return (name, optionsOrFn, fnOrOptions) => {
1016
+ const _name = formatName(name);
1017
+ const arrayOnlyCases = cases.every(Array.isArray);
1018
+ const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
1019
+ const fnFirst = typeof optionsOrFn === "function";
1020
+ cases.forEach((i, idx) => {
1021
+ const items = Array.isArray(i) ? i : [i];
1022
+ if (fnFirst) {
1023
+ if (arrayOnlyCases) {
1024
+ test(formatTitle(_name, items, idx), handler ? () => handler(...items) : undefined, options.timeout);
1025
+ } else {
1026
+ test(formatTitle(_name, items, idx), handler ? () => handler(i) : undefined, options.timeout);
1027
+ }
1028
+ } else {
1029
+ if (arrayOnlyCases) {
1030
+ test(formatTitle(_name, items, idx), options, handler ? () => handler(...items) : undefined);
1031
+ } else {
1032
+ test(formatTitle(_name, items, idx), options, handler ? () => handler(i) : undefined);
1033
+ }
1034
+ }
1035
+ });
1036
+ this.setContext("each", undefined);
1037
+ };
1038
+ };
1039
+ taskFn.for = function(cases, ...args) {
1040
+ const test = this.withContext();
1041
+ if (Array.isArray(cases) && args.length) {
1042
+ cases = formatTemplateString(cases, args);
1043
+ }
1044
+ return (name, optionsOrFn, fnOrOptions) => {
1045
+ const _name = formatName(name);
1046
+ const { options, handler } = parseArguments(optionsOrFn, fnOrOptions);
1047
+ cases.forEach((item, idx) => {
1048
+ // monkey-patch handler to allow parsing fixture
1049
+ const handlerWrapper = handler ? (ctx) => handler(item, ctx) : undefined;
1050
+ if (handlerWrapper) {
1051
+ handlerWrapper.__VITEST_FIXTURE_INDEX__ = 1;
1052
+ handlerWrapper.toString = () => handler.toString();
1053
+ }
1054
+ test(formatTitle(_name, toArray(item), idx), options, handlerWrapper);
1055
+ });
1056
+ };
1057
+ };
1058
+ taskFn.skipIf = function(condition) {
1059
+ return condition ? this.skip : this;
1060
+ };
1061
+ taskFn.runIf = function(condition) {
1062
+ return condition ? this : this.skip;
1063
+ };
1064
+ taskFn.scoped = function(fixtures) {
1065
+ const collector = getCurrentSuite();
1066
+ collector.scoped(fixtures);
1067
+ };
1068
+ taskFn.extend = function(fixtures) {
1069
+ const _context = mergeContextFixtures(fixtures, context || {}, runner);
1070
+ const originalWrapper = fn;
1071
+ return createTest(function(name, optionsOrFn, optionsOrTest) {
1072
+ const collector = getCurrentSuite();
1073
+ const scopedFixtures = collector.fixtures();
1074
+ const context = { ...this };
1075
+ if (scopedFixtures) {
1076
+ context.fixtures = mergeScopedFixtures(context.fixtures || [], scopedFixtures);
1077
+ }
1078
+ originalWrapper.call(context, formatName(name), optionsOrFn, optionsOrTest);
1079
+ }, _context);
1080
+ };
1081
+ taskFn.beforeEach = beforeEach;
1082
+ taskFn.afterEach = afterEach;
1083
+ taskFn.beforeAll = beforeAll;
1084
+ taskFn.afterAll = afterAll;
1085
+ const _test = createChainable([
1086
+ "concurrent",
1087
+ "sequential",
1088
+ "skip",
1089
+ "only",
1090
+ "todo",
1091
+ "fails"
1092
+ ], taskFn);
1093
+ if (context) {
1094
+ _test.mergeContext(context);
1095
+ }
1096
+ return _test;
1097
+ }
1098
+ function createTest(fn, context) {
1099
+ return createTaskCollector(fn, context);
1100
+ }
1101
+ function formatName(name) {
1102
+ return typeof name === "string" ? name : typeof name === "function" ? name.name || "<anonymous>" : String(name);
1103
+ }
1104
+ function formatTitle(template, items, idx) {
1105
+ if (template.includes("%#") || template.includes("%$")) {
1106
+ // '%#' match index of the test case
1107
+ template = template.replace(/%%/g, "__vitest_escaped_%__").replace(/%#/g, `${idx}`).replace(/%\$/g, `${idx + 1}`).replace(/__vitest_escaped_%__/g, "%%");
1108
+ }
1109
+ const count = template.split("%").length - 1;
1110
+ if (template.includes("%f")) {
1111
+ const placeholders = template.match(/%f/g) || [];
1112
+ placeholders.forEach((_, i) => {
1113
+ if (isNegativeNaN(items[i]) || Object.is(items[i], -0)) {
1114
+ // Replace the i-th occurrence of '%f' with '-%f'
1115
+ let occurrence = 0;
1116
+ template = template.replace(/%f/g, (match) => {
1117
+ occurrence++;
1118
+ return occurrence === i + 1 ? "-%f" : match;
1119
+ });
1120
+ }
1121
+ });
1122
+ }
1123
+ const isObjectItem = isObject(items[0]);
1124
+ function formatAttribute(s) {
1125
+ return s.replace(/\$([$\w.]+)/g, (_, key) => {
1126
+ var _runner$config;
1127
+ const isArrayKey = /^\d+$/.test(key);
1128
+ if (!isObjectItem && !isArrayKey) {
1129
+ return `$${key}`;
1130
+ }
1131
+ const arrayElement = isArrayKey ? objectAttr(items, key) : undefined;
1132
+ const value = isObjectItem ? objectAttr(items[0], key, arrayElement) : arrayElement;
1133
+ return objDisplay(value, { truncate: runner === null || runner === void 0 || (_runner$config = runner.config) === null || _runner$config === void 0 || (_runner$config = _runner$config.chaiConfig) === null || _runner$config === void 0 ? void 0 : _runner$config.truncateThreshold });
1134
+ });
1135
+ }
1136
+ let output = "";
1137
+ let i = 0;
1138
+ handleRegexMatch(
1139
+ template,
1140
+ formatRegExp,
1141
+ // format "%"
1142
+ (match) => {
1143
+ if (i < count) {
1144
+ output += format(match[0], items[i++]);
1145
+ } else {
1146
+ output += match[0];
1147
+ }
1148
+ },
1149
+ // format "$"
1150
+ (nonMatch) => {
1151
+ output += formatAttribute(nonMatch);
1152
+ }
1153
+ );
1154
+ return output;
1155
+ }
1156
+ // based on https://github.com/unocss/unocss/blob/2e74b31625bbe3b9c8351570749aa2d3f799d919/packages/autocomplete/src/parse.ts#L11
1157
+ function handleRegexMatch(input, regex, onMatch, onNonMatch) {
1158
+ let lastIndex = 0;
1159
+ for (const m of input.matchAll(regex)) {
1160
+ if (lastIndex < m.index) {
1161
+ onNonMatch(input.slice(lastIndex, m.index));
1162
+ }
1163
+ onMatch(m);
1164
+ lastIndex = m.index + m[0].length;
1165
+ }
1166
+ if (lastIndex < input.length) {
1167
+ onNonMatch(input.slice(lastIndex));
1168
+ }
1169
+ }
1170
+ function formatTemplateString(cases, args) {
1171
+ const header = cases.join("").trim().replace(/ /g, "").split("\n").map((i) => i.split("|"))[0];
1172
+ const res = [];
1173
+ for (let i = 0; i < Math.floor(args.length / header.length); i++) {
1174
+ const oneCase = {};
1175
+ for (let j = 0; j < header.length; j++) {
1176
+ oneCase[header[j]] = args[i * header.length + j];
1177
+ }
1178
+ res.push(oneCase);
1179
+ }
1180
+ return res;
1181
+ }
1182
+
1183
+ const now$2 = Date.now;
1184
+ const collectorContext = {
1185
+ tasks: [],
1186
+ currentSuite: null
1187
+ };
1188
+ function collectTask(task) {
1189
+ var _collectorContext$cur;
1190
+ (_collectorContext$cur = collectorContext.currentSuite) === null || _collectorContext$cur === void 0 ? void 0 : _collectorContext$cur.tasks.push(task);
1191
+ }
1192
+ async function runWithSuite(suite, fn) {
1193
+ const prev = collectorContext.currentSuite;
1194
+ collectorContext.currentSuite = suite;
1195
+ await fn();
1196
+ collectorContext.currentSuite = prev;
1197
+ }
1198
+ function withTimeout(fn, timeout, isHook = false, stackTraceError, onTimeout) {
1199
+ if (timeout <= 0 || timeout === Number.POSITIVE_INFINITY) {
1200
+ return fn;
1201
+ }
1202
+ const { setTimeout, clearTimeout } = getSafeTimers();
1203
+ // this function name is used to filter error in test/cli/test/fails.test.ts
1204
+ return (function runWithTimeout(...args) {
1205
+ const startTime = now$2();
1206
+ const runner = getRunner();
1207
+ runner._currentTaskStartTime = startTime;
1208
+ runner._currentTaskTimeout = timeout;
1209
+ return new Promise((resolve_, reject_) => {
1210
+ var _timer$unref;
1211
+ const timer = setTimeout(() => {
1212
+ clearTimeout(timer);
1213
+ rejectTimeoutError();
1214
+ }, timeout);
1215
+ // `unref` might not exist in browser
1216
+ (_timer$unref = timer.unref) === null || _timer$unref === void 0 ? void 0 : _timer$unref.call(timer);
1217
+ function rejectTimeoutError() {
1218
+ const error = makeTimeoutError(isHook, timeout, stackTraceError);
1219
+ onTimeout === null || onTimeout === void 0 ? void 0 : onTimeout(args, error);
1220
+ reject_(error);
1221
+ }
1222
+ function resolve(result) {
1223
+ runner._currentTaskStartTime = undefined;
1224
+ runner._currentTaskTimeout = undefined;
1225
+ clearTimeout(timer);
1226
+ // if test/hook took too long in microtask, setTimeout won't be triggered,
1227
+ // but we still need to fail the test, see
1228
+ // https://github.com/vitest-dev/vitest/issues/2920
1229
+ if (now$2() - startTime >= timeout) {
1230
+ rejectTimeoutError();
1231
+ return;
1232
+ }
1233
+ resolve_(result);
1234
+ }
1235
+ function reject(error) {
1236
+ runner._currentTaskStartTime = undefined;
1237
+ runner._currentTaskTimeout = undefined;
1238
+ clearTimeout(timer);
1239
+ reject_(error);
1240
+ }
1241
+ // sync test/hook will be caught by try/catch
1242
+ try {
1243
+ const result = fn(...args);
1244
+ // the result is a thenable, we don't wrap this in Promise.resolve
1245
+ // to avoid creating new promises
1246
+ if (typeof result === "object" && result != null && typeof result.then === "function") {
1247
+ result.then(resolve, reject);
1248
+ } else {
1249
+ resolve(result);
1250
+ }
1251
+ }
1252
+ // user sync test/hook throws an error
1253
+ catch (error) {
1254
+ reject(error);
1255
+ }
1256
+ });
1257
+ });
1258
+ }
1259
+ const abortControllers = new WeakMap();
1260
+ function abortIfTimeout([context], error) {
1261
+ if (context) {
1262
+ abortContextSignal(context, error);
1263
+ }
1264
+ }
1265
+ function abortContextSignal(context, error) {
1266
+ const abortController = abortControllers.get(context);
1267
+ abortController === null || abortController === void 0 ? void 0 : abortController.abort(error);
1268
+ }
1269
+ function createTestContext(test, runner) {
1270
+ var _runner$extendTaskCon;
1271
+ const context = function() {
1272
+ throw new Error("done() callback is deprecated, use promise instead");
1273
+ };
1274
+ let abortController = abortControllers.get(context);
1275
+ if (!abortController) {
1276
+ abortController = new AbortController();
1277
+ abortControllers.set(context, abortController);
1278
+ }
1279
+ context.signal = abortController.signal;
1280
+ context.task = test;
1281
+ context.skip = (condition, note) => {
1282
+ if (condition === false) {
1283
+ // do nothing
1284
+ return undefined;
1285
+ }
1286
+ test.result ?? (test.result = { state: "skip" });
1287
+ test.result.pending = true;
1288
+ throw new PendingError("test is skipped; abort execution", test, typeof condition === "string" ? condition : note);
1289
+ };
1290
+ context.annotate = ((message, type, attachment) => {
1291
+ if (test.result && test.result.state !== "run") {
1292
+ throw new Error(`Cannot annotate tests outside of the test run. The test "${test.name}" finished running with the "${test.result.state}" state already.`);
1293
+ }
1294
+ const annotation = {
1295
+ message,
1296
+ type: typeof type === "object" || type === undefined ? "notice" : type
1297
+ };
1298
+ const annotationAttachment = typeof type === "object" ? type : attachment;
1299
+ if (annotationAttachment) {
1300
+ annotation.attachment = annotationAttachment;
1301
+ manageArtifactAttachment(annotation.attachment);
1302
+ }
1303
+ return recordAsyncOperation(test, recordArtifact(test, {
1304
+ type: "internal:annotation",
1305
+ annotation
1306
+ }).then(async ({ annotation }) => {
1307
+ if (!runner.onTestAnnotate) {
1308
+ throw new Error(`Test runner doesn't support test annotations.`);
1309
+ }
1310
+ await finishSendTasksUpdate(runner);
1311
+ const resolvedAnnotation = await runner.onTestAnnotate(test, annotation);
1312
+ test.annotations.push(resolvedAnnotation);
1313
+ return resolvedAnnotation;
1314
+ }));
1315
+ });
1316
+ context.onTestFailed = (handler, timeout) => {
1317
+ test.onFailed || (test.onFailed = []);
1318
+ test.onFailed.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
1319
+ };
1320
+ context.onTestFinished = (handler, timeout) => {
1321
+ test.onFinished || (test.onFinished = []);
1322
+ test.onFinished.push(withTimeout(handler, timeout ?? runner.config.hookTimeout, true, new Error("STACK_TRACE_ERROR"), (_, error) => abortController.abort(error)));
1323
+ };
1324
+ return ((_runner$extendTaskCon = runner.extendTaskContext) === null || _runner$extendTaskCon === void 0 ? void 0 : _runner$extendTaskCon.call(runner, context)) || context;
1325
+ }
1326
+ function makeTimeoutError(isHook, timeout, stackTraceError) {
1327
+ const message = `${isHook ? "Hook" : "Test"} timed out in ${timeout}ms.\nIf this is a long-running ${isHook ? "hook" : "test"}, pass a timeout value as the last argument or configure it globally with "${isHook ? "hookTimeout" : "testTimeout"}".`;
1328
+ const error = new Error(message);
1329
+ if (stackTraceError === null || stackTraceError === void 0 ? void 0 : stackTraceError.stack) {
1330
+ error.stack = stackTraceError.stack.replace(error.message, stackTraceError.message);
1331
+ }
1332
+ return error;
1333
+ }
1334
+ const fileContexts = new WeakMap();
1335
+ function getFileContext(file) {
1336
+ const context = fileContexts.get(file);
1337
+ if (!context) {
1338
+ throw new Error(`Cannot find file context for ${file.name}`);
1339
+ }
1340
+ return context;
1341
+ }
1342
+ function setFileContext(file, context) {
1343
+ fileContexts.set(file, context);
1344
+ }
1345
+
1346
+ async function runSetupFiles(config, files, runner) {
1347
+ if (config.sequence.setupFiles === "parallel") {
1348
+ await Promise.all(files.map(async (fsPath) => {
1349
+ await runner.importFile(fsPath, "setup");
1350
+ }));
1351
+ } else {
1352
+ for (const fsPath of files) {
1353
+ await runner.importFile(fsPath, "setup");
1354
+ }
1355
+ }
1356
+ }
1357
+
1358
+ const now$1 = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
1359
+ async function collectTests(specs, runner) {
1360
+ const files = [];
1361
+ const config = runner.config;
1362
+ const $ = runner.trace;
1363
+ for (const spec of specs) {
1364
+ const filepath = typeof spec === "string" ? spec : spec.filepath;
1365
+ await $("collect_spec", { "code.file.path": filepath }, async () => {
1366
+ var _runner$onCollectStar;
1367
+ const testLocations = typeof spec === "string" ? undefined : spec.testLocations;
1368
+ const file = createFileTask(filepath, config.root, config.name, runner.pool, runner.viteEnvironment);
1369
+ setFileContext(file, Object.create(null));
1370
+ file.shuffle = config.sequence.shuffle;
1371
+ (_runner$onCollectStar = runner.onCollectStart) === null || _runner$onCollectStar === void 0 ? void 0 : _runner$onCollectStar.call(runner, file);
1372
+ clearCollectorContext(file, runner);
1373
+ try {
1374
+ var _runner$getImportDura;
1375
+ const setupFiles = toArray(config.setupFiles);
1376
+ if (setupFiles.length) {
1377
+ const setupStart = now$1();
1378
+ await runSetupFiles(config, setupFiles, runner);
1379
+ const setupEnd = now$1();
1380
+ file.setupDuration = setupEnd - setupStart;
1381
+ } else {
1382
+ file.setupDuration = 0;
1383
+ }
1384
+ const collectStart = now$1();
1385
+ await runner.importFile(filepath, "collect");
1386
+ const durations = (_runner$getImportDura = runner.getImportDurations) === null || _runner$getImportDura === void 0 ? void 0 : _runner$getImportDura.call(runner);
1387
+ if (durations) {
1388
+ file.importDurations = durations;
1389
+ }
1390
+ const defaultTasks = await getDefaultSuite().collect(file);
1391
+ const fileHooks = createSuiteHooks();
1392
+ mergeHooks(fileHooks, getHooks(defaultTasks));
1393
+ for (const c of [...defaultTasks.tasks, ...collectorContext.tasks]) {
1394
+ if (c.type === "test" || c.type === "suite") {
1395
+ file.tasks.push(c);
1396
+ } else if (c.type === "collector") {
1397
+ const suite = await c.collect(file);
1398
+ if (suite.name || suite.tasks.length) {
1399
+ mergeHooks(fileHooks, getHooks(suite));
1400
+ file.tasks.push(suite);
1401
+ }
1402
+ } else {
1403
+ // check that types are exhausted
1404
+ c;
1405
+ }
1406
+ }
1407
+ setHooks(file, fileHooks);
1408
+ file.collectDuration = now$1() - collectStart;
1409
+ } catch (e) {
1410
+ var _runner$getImportDura2;
1411
+ const error = processError(e);
1412
+ file.result = {
1413
+ state: "fail",
1414
+ errors: [error]
1415
+ };
1416
+ const durations = (_runner$getImportDura2 = runner.getImportDurations) === null || _runner$getImportDura2 === void 0 ? void 0 : _runner$getImportDura2.call(runner);
1417
+ if (durations) {
1418
+ file.importDurations = durations;
1419
+ }
1420
+ }
1421
+ calculateSuiteHash(file);
1422
+ const hasOnlyTasks = someTasksAreOnly(file);
1423
+ interpretTaskModes(file, config.testNamePattern, testLocations, hasOnlyTasks, false, config.allowOnly);
1424
+ if (file.mode === "queued") {
1425
+ file.mode = "run";
1426
+ }
1427
+ files.push(file);
1428
+ });
1429
+ }
1430
+ return files;
1431
+ }
1432
+ function mergeHooks(baseHooks, hooks) {
1433
+ for (const _key in hooks) {
1434
+ const key = _key;
1435
+ baseHooks[key].push(...hooks[key]);
1436
+ }
1437
+ return baseHooks;
1438
+ }
1439
+
1440
+ const now = globalThis.performance ? globalThis.performance.now.bind(globalThis.performance) : Date.now;
1441
+ const unixNow = Date.now;
1442
+ const { clearTimeout, setTimeout } = getSafeTimers();
1443
+ function updateSuiteHookState(task, name, state, runner) {
1444
+ if (!task.result) {
1445
+ task.result = { state: "run" };
1446
+ }
1447
+ if (!task.result.hooks) {
1448
+ task.result.hooks = {};
1449
+ }
1450
+ const suiteHooks = task.result.hooks;
1451
+ if (suiteHooks) {
1452
+ suiteHooks[name] = state;
1453
+ let event = state === "run" ? "before-hook-start" : "before-hook-end";
1454
+ if (name === "afterAll" || name === "afterEach") {
1455
+ event = state === "run" ? "after-hook-start" : "after-hook-end";
1456
+ }
1457
+ updateTask(event, task, runner);
1458
+ }
1459
+ }
1460
+ function getSuiteHooks(suite, name, sequence) {
1461
+ const hooks = getHooks(suite)[name];
1462
+ if (sequence === "stack" && (name === "afterAll" || name === "afterEach")) {
1463
+ return hooks.slice().reverse();
1464
+ }
1465
+ return hooks;
1466
+ }
1467
+ async function callTestHooks(runner, test, hooks, sequence) {
1468
+ if (sequence === "stack") {
1469
+ hooks = hooks.slice().reverse();
1470
+ }
1471
+ if (!hooks.length) {
1472
+ return;
1473
+ }
1474
+ const context = test.context;
1475
+ const onTestFailed = test.context.onTestFailed;
1476
+ const onTestFinished = test.context.onTestFinished;
1477
+ context.onTestFailed = () => {
1478
+ throw new Error(`Cannot call "onTestFailed" inside a test hook.`);
1479
+ };
1480
+ context.onTestFinished = () => {
1481
+ throw new Error(`Cannot call "onTestFinished" inside a test hook.`);
1482
+ };
1483
+ if (sequence === "parallel") {
1484
+ try {
1485
+ await Promise.all(hooks.map((fn) => fn(test.context)));
1486
+ } catch (e) {
1487
+ failTask(test.result, e, runner.config.diffOptions);
1488
+ }
1489
+ } else {
1490
+ for (const fn of hooks) {
1491
+ try {
1492
+ await fn(test.context);
1493
+ } catch (e) {
1494
+ failTask(test.result, e, runner.config.diffOptions);
1495
+ }
1496
+ }
1497
+ }
1498
+ context.onTestFailed = onTestFailed;
1499
+ context.onTestFinished = onTestFinished;
1500
+ }
1501
+ async function callSuiteHook(suite, currentTask, name, runner, args) {
1502
+ const sequence = runner.config.sequence.hooks;
1503
+ const callbacks = [];
1504
+ // stop at file level
1505
+ const parentSuite = "filepath" in suite ? null : suite.suite || suite.file;
1506
+ if (name === "beforeEach" && parentSuite) {
1507
+ callbacks.push(...await callSuiteHook(parentSuite, currentTask, name, runner, args));
1508
+ }
1509
+ const hooks = getSuiteHooks(suite, name, sequence);
1510
+ if (hooks.length > 0) {
1511
+ updateSuiteHookState(currentTask, name, "run", runner);
1512
+ }
1513
+ async function runHook(hook) {
1514
+ return getBeforeHookCleanupCallback(hook, await hook(...args), name === "beforeEach" ? args[0] : undefined);
1515
+ }
1516
+ if (sequence === "parallel") {
1517
+ callbacks.push(...await Promise.all(hooks.map((hook) => runHook(hook))));
1518
+ } else {
1519
+ for (const hook of hooks) {
1520
+ callbacks.push(await runHook(hook));
1521
+ }
1522
+ }
1523
+ if (hooks.length > 0) {
1524
+ updateSuiteHookState(currentTask, name, "pass", runner);
1525
+ }
1526
+ if (name === "afterEach" && parentSuite) {
1527
+ callbacks.push(...await callSuiteHook(parentSuite, currentTask, name, runner, args));
1528
+ }
1529
+ return callbacks;
1530
+ }
1531
+ const packs = new Map();
1532
+ const eventsPacks = [];
1533
+ const pendingTasksUpdates = [];
1534
+ function sendTasksUpdate(runner) {
1535
+ if (packs.size) {
1536
+ var _runner$onTaskUpdate;
1537
+ const taskPacks = Array.from(packs).map(([id, task]) => {
1538
+ return [
1539
+ id,
1540
+ task[0],
1541
+ task[1]
1542
+ ];
1543
+ });
1544
+ const p = (_runner$onTaskUpdate = runner.onTaskUpdate) === null || _runner$onTaskUpdate === void 0 ? void 0 : _runner$onTaskUpdate.call(runner, taskPacks, eventsPacks);
1545
+ if (p) {
1546
+ pendingTasksUpdates.push(p);
1547
+ // remove successful promise to not grow array indefnitely,
1548
+ // but keep rejections so finishSendTasksUpdate can handle them
1549
+ p.then(() => pendingTasksUpdates.splice(pendingTasksUpdates.indexOf(p), 1), () => {});
1550
+ }
1551
+ eventsPacks.length = 0;
1552
+ packs.clear();
1553
+ }
1554
+ }
1555
+ async function finishSendTasksUpdate(runner) {
1556
+ sendTasksUpdate(runner);
1557
+ await Promise.all(pendingTasksUpdates);
1558
+ }
1559
+ function throttle(fn, ms) {
1560
+ let last = 0;
1561
+ let pendingCall;
1562
+ return function call(...args) {
1563
+ const now = unixNow();
1564
+ if (now - last > ms) {
1565
+ last = now;
1566
+ clearTimeout(pendingCall);
1567
+ pendingCall = undefined;
1568
+ return fn.apply(this, args);
1569
+ }
1570
+ // Make sure fn is still called even if there are no further calls
1571
+ pendingCall ?? (pendingCall = setTimeout(() => call.bind(this)(...args), ms));
1572
+ };
1573
+ }
1574
+ // throttle based on summary reporter's DURATION_UPDATE_INTERVAL_MS
1575
+ const sendTasksUpdateThrottled = throttle(sendTasksUpdate, 100);
1576
+ function updateTask(event, task, runner) {
1577
+ eventsPacks.push([
1578
+ task.id,
1579
+ event,
1580
+ undefined
1581
+ ]);
1582
+ packs.set(task.id, [task.result, task.meta]);
1583
+ sendTasksUpdateThrottled(runner);
1584
+ }
1585
+ async function callCleanupHooks(runner, cleanups) {
1586
+ const sequence = runner.config.sequence.hooks;
1587
+ if (sequence === "stack") {
1588
+ cleanups = cleanups.slice().reverse();
1589
+ }
1590
+ if (sequence === "parallel") {
1591
+ await Promise.all(cleanups.map(async (fn) => {
1592
+ if (typeof fn !== "function") {
1593
+ return;
1594
+ }
1595
+ await fn();
1596
+ }));
1597
+ } else {
1598
+ for (const fn of cleanups) {
1599
+ if (typeof fn !== "function") {
1600
+ continue;
1601
+ }
1602
+ await fn();
1603
+ }
1604
+ }
1605
+ }
1606
+ async function runTest(test, runner) {
1607
+ var _runner$onBeforeRunTa, _test$result, _runner$onAfterRunTas;
1608
+ await ((_runner$onBeforeRunTa = runner.onBeforeRunTask) === null || _runner$onBeforeRunTa === void 0 ? void 0 : _runner$onBeforeRunTa.call(runner, test));
1609
+ if (test.mode !== "run" && test.mode !== "queued") {
1610
+ updateTask("test-prepare", test, runner);
1611
+ updateTask("test-finished", test, runner);
1612
+ return;
1613
+ }
1614
+ if (((_test$result = test.result) === null || _test$result === void 0 ? void 0 : _test$result.state) === "fail") {
1615
+ // should not be possible to get here, I think this is just copy pasted from suite
1616
+ // TODO: maybe someone fails tests in `beforeAll` hooks?
1617
+ // https://github.com/vitest-dev/vitest/pull/7069
1618
+ updateTask("test-failed-early", test, runner);
1619
+ return;
1620
+ }
1621
+ const start = now();
1622
+ test.result = {
1623
+ state: "run",
1624
+ startTime: unixNow(),
1625
+ retryCount: 0
1626
+ };
1627
+ updateTask("test-prepare", test, runner);
1628
+ const cleanupRunningTest = addRunningTest(test);
1629
+ setCurrentTest(test);
1630
+ const suite = test.suite || test.file;
1631
+ const $ = runner.trace;
1632
+ const repeats = test.repeats ?? 0;
1633
+ for (let repeatCount = 0; repeatCount <= repeats; repeatCount++) {
1634
+ const retry = test.retry ?? 0;
1635
+ for (let retryCount = 0; retryCount <= retry; retryCount++) {
1636
+ var _test$onFinished, _test$onFailed, _runner$onAfterRetryT, _test$result2, _test$result3;
1637
+ let beforeEachCleanups = [];
1638
+ try {
1639
+ var _runner$onBeforeTryTa, _runner$onAfterTryTas;
1640
+ await ((_runner$onBeforeTryTa = runner.onBeforeTryTask) === null || _runner$onBeforeTryTa === void 0 ? void 0 : _runner$onBeforeTryTa.call(runner, test, {
1641
+ retry: retryCount,
1642
+ repeats: repeatCount
1643
+ }));
1644
+ test.result.repeatCount = repeatCount;
1645
+ beforeEachCleanups = await $("test.beforeEach", () => callSuiteHook(suite, test, "beforeEach", runner, [test.context, suite]));
1646
+ if (runner.runTask) {
1647
+ await $("test.callback", () => runner.runTask(test));
1648
+ } else {
1649
+ const fn = getFn(test);
1650
+ if (!fn) {
1651
+ throw new Error("Test function is not found. Did you add it using `setFn`?");
1652
+ }
1653
+ await $("test.callback", () => fn());
1654
+ }
1655
+ await ((_runner$onAfterTryTas = runner.onAfterTryTask) === null || _runner$onAfterTryTas === void 0 ? void 0 : _runner$onAfterTryTas.call(runner, test, {
1656
+ retry: retryCount,
1657
+ repeats: repeatCount
1658
+ }));
1659
+ if (test.result.state !== "fail") {
1660
+ if (!test.repeats) {
1661
+ test.result.state = "pass";
1662
+ } else if (test.repeats && retry === retryCount) {
1663
+ test.result.state = "pass";
1664
+ }
1665
+ }
1666
+ } catch (e) {
1667
+ failTask(test.result, e, runner.config.diffOptions);
1668
+ }
1669
+ try {
1670
+ var _runner$onTaskFinishe;
1671
+ await ((_runner$onTaskFinishe = runner.onTaskFinished) === null || _runner$onTaskFinishe === void 0 ? void 0 : _runner$onTaskFinishe.call(runner, test));
1672
+ } catch (e) {
1673
+ failTask(test.result, e, runner.config.diffOptions);
1674
+ }
1675
+ try {
1676
+ await $("test.afterEach", () => callSuiteHook(suite, test, "afterEach", runner, [test.context, suite]));
1677
+ if (beforeEachCleanups.length) {
1678
+ await $("test.cleanup", () => callCleanupHooks(runner, beforeEachCleanups));
1679
+ }
1680
+ await callFixtureCleanup(test.context);
1681
+ } catch (e) {
1682
+ failTask(test.result, e, runner.config.diffOptions);
1683
+ }
1684
+ if ((_test$onFinished = test.onFinished) === null || _test$onFinished === void 0 ? void 0 : _test$onFinished.length) {
1685
+ await $("test.onFinished", () => callTestHooks(runner, test, test.onFinished, "stack"));
1686
+ }
1687
+ if (test.result.state === "fail" && ((_test$onFailed = test.onFailed) === null || _test$onFailed === void 0 ? void 0 : _test$onFailed.length)) {
1688
+ await $("test.onFailed", () => callTestHooks(runner, test, test.onFailed, runner.config.sequence.hooks));
1689
+ }
1690
+ test.onFailed = undefined;
1691
+ test.onFinished = undefined;
1692
+ await ((_runner$onAfterRetryT = runner.onAfterRetryTask) === null || _runner$onAfterRetryT === void 0 ? void 0 : _runner$onAfterRetryT.call(runner, test, {
1693
+ retry: retryCount,
1694
+ repeats: repeatCount
1695
+ }));
1696
+ // skipped with new PendingError
1697
+ if (((_test$result2 = test.result) === null || _test$result2 === void 0 ? void 0 : _test$result2.pending) || ((_test$result3 = test.result) === null || _test$result3 === void 0 ? void 0 : _test$result3.state) === "skip") {
1698
+ var _test$result4;
1699
+ test.mode = "skip";
1700
+ test.result = {
1701
+ state: "skip",
1702
+ note: (_test$result4 = test.result) === null || _test$result4 === void 0 ? void 0 : _test$result4.note,
1703
+ pending: true,
1704
+ duration: now() - start
1705
+ };
1706
+ updateTask("test-finished", test, runner);
1707
+ setCurrentTest(undefined);
1708
+ cleanupRunningTest();
1709
+ return;
1710
+ }
1711
+ if (test.result.state === "pass") {
1712
+ break;
1713
+ }
1714
+ if (retryCount < retry) {
1715
+ // reset state when retry test
1716
+ test.result.state = "run";
1717
+ test.result.retryCount = (test.result.retryCount ?? 0) + 1;
1718
+ }
1719
+ // update retry info
1720
+ updateTask("test-retried", test, runner);
1721
+ }
1722
+ }
1723
+ // if test is marked to be failed, flip the result
1724
+ if (test.fails) {
1725
+ if (test.result.state === "pass") {
1726
+ const error = processError(new Error("Expect test to fail"));
1727
+ test.result.state = "fail";
1728
+ test.result.errors = [error];
1729
+ } else {
1730
+ test.result.state = "pass";
1731
+ test.result.errors = undefined;
1732
+ }
1733
+ }
1734
+ cleanupRunningTest();
1735
+ setCurrentTest(undefined);
1736
+ test.result.duration = now() - start;
1737
+ await ((_runner$onAfterRunTas = runner.onAfterRunTask) === null || _runner$onAfterRunTas === void 0 ? void 0 : _runner$onAfterRunTas.call(runner, test));
1738
+ updateTask("test-finished", test, runner);
1739
+ }
1740
+ function failTask(result, err, diffOptions) {
1741
+ if (err instanceof PendingError) {
1742
+ result.state = "skip";
1743
+ result.note = err.note;
1744
+ result.pending = true;
1745
+ return;
1746
+ }
1747
+ result.state = "fail";
1748
+ const errors = Array.isArray(err) ? err : [err];
1749
+ for (const e of errors) {
1750
+ const error = processError(e, diffOptions);
1751
+ result.errors ?? (result.errors = []);
1752
+ result.errors.push(error);
1753
+ }
1754
+ }
1755
+ function markTasksAsSkipped(suite, runner) {
1756
+ suite.tasks.forEach((t) => {
1757
+ t.mode = "skip";
1758
+ t.result = {
1759
+ ...t.result,
1760
+ state: "skip"
1761
+ };
1762
+ updateTask("test-finished", t, runner);
1763
+ if (t.type === "suite") {
1764
+ markTasksAsSkipped(t, runner);
1765
+ }
1766
+ });
1767
+ }
1768
+ async function runSuite(suite, runner) {
1769
+ var _runner$onBeforeRunSu, _suite$result;
1770
+ await ((_runner$onBeforeRunSu = runner.onBeforeRunSuite) === null || _runner$onBeforeRunSu === void 0 ? void 0 : _runner$onBeforeRunSu.call(runner, suite));
1771
+ if (((_suite$result = suite.result) === null || _suite$result === void 0 ? void 0 : _suite$result.state) === "fail") {
1772
+ markTasksAsSkipped(suite, runner);
1773
+ // failed during collection
1774
+ updateTask("suite-failed-early", suite, runner);
1775
+ return;
1776
+ }
1777
+ const start = now();
1778
+ const mode = suite.mode;
1779
+ suite.result = {
1780
+ state: mode === "skip" || mode === "todo" ? mode : "run",
1781
+ startTime: unixNow()
1782
+ };
1783
+ const $ = runner.trace;
1784
+ updateTask("suite-prepare", suite, runner);
1785
+ let beforeAllCleanups = [];
1786
+ if (suite.mode === "skip") {
1787
+ suite.result.state = "skip";
1788
+ updateTask("suite-finished", suite, runner);
1789
+ } else if (suite.mode === "todo") {
1790
+ suite.result.state = "todo";
1791
+ updateTask("suite-finished", suite, runner);
1792
+ } else {
1793
+ var _runner$onAfterRunSui;
1794
+ try {
1795
+ try {
1796
+ beforeAllCleanups = await $("suite.beforeAll", () => callSuiteHook(suite, suite, "beforeAll", runner, [suite]));
1797
+ } catch (e) {
1798
+ markTasksAsSkipped(suite, runner);
1799
+ throw e;
1800
+ }
1801
+ if (runner.runSuite) {
1802
+ await runner.runSuite(suite);
1803
+ } else {
1804
+ for (let tasksGroup of partitionSuiteChildren(suite)) {
1805
+ if (tasksGroup[0].concurrent === true) {
1806
+ await Promise.all(tasksGroup.map((c) => runSuiteChild(c, runner)));
1807
+ } else {
1808
+ const { sequence } = runner.config;
1809
+ if (suite.shuffle) {
1810
+ // run describe block independently from tests
1811
+ const suites = tasksGroup.filter((group) => group.type === "suite");
1812
+ const tests = tasksGroup.filter((group) => group.type === "test");
1813
+ const groups = shuffle([suites, tests], sequence.seed);
1814
+ tasksGroup = groups.flatMap((group) => shuffle(group, sequence.seed));
1815
+ }
1816
+ for (const c of tasksGroup) {
1817
+ await runSuiteChild(c, runner);
1818
+ }
1819
+ }
1820
+ }
1821
+ }
1822
+ } catch (e) {
1823
+ failTask(suite.result, e, runner.config.diffOptions);
1824
+ }
1825
+ try {
1826
+ await $("suite.afterAll", () => callSuiteHook(suite, suite, "afterAll", runner, [suite]));
1827
+ if (beforeAllCleanups.length) {
1828
+ await $("suite.cleanup", () => callCleanupHooks(runner, beforeAllCleanups));
1829
+ }
1830
+ if (suite.file === suite) {
1831
+ const context = getFileContext(suite);
1832
+ await callFixtureCleanup(context);
1833
+ }
1834
+ } catch (e) {
1835
+ failTask(suite.result, e, runner.config.diffOptions);
1836
+ }
1837
+ if (suite.mode === "run" || suite.mode === "queued") {
1838
+ if (!runner.config.passWithNoTests && !hasTests(suite)) {
1839
+ var _suite$result$errors;
1840
+ suite.result.state = "fail";
1841
+ if (!((_suite$result$errors = suite.result.errors) === null || _suite$result$errors === void 0 ? void 0 : _suite$result$errors.length)) {
1842
+ const error = processError(new Error(`No test found in suite ${suite.name}`));
1843
+ suite.result.errors = [error];
1844
+ }
1845
+ } else if (hasFailed(suite)) {
1846
+ suite.result.state = "fail";
1847
+ } else {
1848
+ suite.result.state = "pass";
1849
+ }
1850
+ }
1851
+ suite.result.duration = now() - start;
1852
+ await ((_runner$onAfterRunSui = runner.onAfterRunSuite) === null || _runner$onAfterRunSui === void 0 ? void 0 : _runner$onAfterRunSui.call(runner, suite));
1853
+ updateTask("suite-finished", suite, runner);
1854
+ }
1855
+ }
1856
+ let limitMaxConcurrency;
1857
+ async function runSuiteChild(c, runner) {
1858
+ const $ = runner.trace;
1859
+ if (c.type === "test") {
1860
+ return limitMaxConcurrency(() => {
1861
+ var _c$location, _c$location2;
1862
+ return $("run.test", {
1863
+ "vitest.test.id": c.id,
1864
+ "vitest.test.name": c.name,
1865
+ "vitest.test.mode": c.mode,
1866
+ "vitest.test.timeout": c.timeout,
1867
+ "code.file.path": c.file.filepath,
1868
+ "code.line.number": (_c$location = c.location) === null || _c$location === void 0 ? void 0 : _c$location.line,
1869
+ "code.column.number": (_c$location2 = c.location) === null || _c$location2 === void 0 ? void 0 : _c$location2.column
1870
+ }, () => runTest(c, runner));
1871
+ });
1872
+ } else if (c.type === "suite") {
1873
+ var _c$location3, _c$location4;
1874
+ return $("run.suite", {
1875
+ "vitest.suite.id": c.id,
1876
+ "vitest.suite.name": c.name,
1877
+ "vitest.suite.mode": c.mode,
1878
+ "code.file.path": c.file.filepath,
1879
+ "code.line.number": (_c$location3 = c.location) === null || _c$location3 === void 0 ? void 0 : _c$location3.line,
1880
+ "code.column.number": (_c$location4 = c.location) === null || _c$location4 === void 0 ? void 0 : _c$location4.column
1881
+ }, () => runSuite(c, runner));
1882
+ }
1883
+ }
1884
+ async function runFiles(files, runner) {
1885
+ limitMaxConcurrency ?? (limitMaxConcurrency = limitConcurrency(runner.config.maxConcurrency));
1886
+ for (const file of files) {
1887
+ if (!file.tasks.length && !runner.config.passWithNoTests) {
1888
+ var _file$result;
1889
+ if (!((_file$result = file.result) === null || _file$result === void 0 || (_file$result = _file$result.errors) === null || _file$result === void 0 ? void 0 : _file$result.length)) {
1890
+ const error = processError(new Error(`No test suite found in file ${file.filepath}`));
1891
+ file.result = {
1892
+ state: "fail",
1893
+ errors: [error]
1894
+ };
1895
+ }
1896
+ }
1897
+ await runner.trace("run.spec", {
1898
+ "code.file.path": file.filepath,
1899
+ "vitest.suite.tasks.length": file.tasks.length
1900
+ }, () => runSuite(file, runner));
1901
+ }
1902
+ }
1903
+ const workerRunners = new WeakSet();
1904
+ function defaultTrace(_, attributes, cb) {
1905
+ if (typeof attributes === "function") {
1906
+ return attributes();
1907
+ }
1908
+ return cb();
1909
+ }
1910
+ async function startTests(specs, runner) {
1911
+ var _runner$cancel;
1912
+ runner.trace ?? (runner.trace = defaultTrace);
1913
+ const cancel = (_runner$cancel = runner.cancel) === null || _runner$cancel === void 0 ? void 0 : _runner$cancel.bind(runner);
1914
+ // Ideally, we need to have an event listener for this, but only have a runner here.
1915
+ // Adding another onCancel felt wrong (maybe it needs to be refactored)
1916
+ runner.cancel = (reason) => {
1917
+ // We intentionally create only one error since there is only one test run that can be cancelled
1918
+ const error = new TestRunAbortError("The test run was aborted by the user.", reason);
1919
+ getRunningTests().forEach((test) => abortContextSignal(test.context, error));
1920
+ return cancel === null || cancel === void 0 ? void 0 : cancel(reason);
1921
+ };
1922
+ if (!workerRunners.has(runner)) {
1923
+ var _runner$onCleanupWork;
1924
+ (_runner$onCleanupWork = runner.onCleanupWorkerContext) === null || _runner$onCleanupWork === void 0 ? void 0 : _runner$onCleanupWork.call(runner, async () => {
1925
+ var _runner$getWorkerCont;
1926
+ const context = (_runner$getWorkerCont = runner.getWorkerContext) === null || _runner$getWorkerCont === void 0 ? void 0 : _runner$getWorkerCont.call(runner);
1927
+ if (context) {
1928
+ await callFixtureCleanup(context);
1929
+ }
1930
+ });
1931
+ workerRunners.add(runner);
1932
+ }
1933
+ try {
1934
+ var _runner$onBeforeColle, _runner$onCollected, _runner$onBeforeRunFi, _runner$onAfterRunFil;
1935
+ const paths = specs.map((f) => typeof f === "string" ? f : f.filepath);
1936
+ await ((_runner$onBeforeColle = runner.onBeforeCollect) === null || _runner$onBeforeColle === void 0 ? void 0 : _runner$onBeforeColle.call(runner, paths));
1937
+ const files = await collectTests(specs, runner);
1938
+ await ((_runner$onCollected = runner.onCollected) === null || _runner$onCollected === void 0 ? void 0 : _runner$onCollected.call(runner, files));
1939
+ await ((_runner$onBeforeRunFi = runner.onBeforeRunFiles) === null || _runner$onBeforeRunFi === void 0 ? void 0 : _runner$onBeforeRunFi.call(runner, files));
1940
+ await runFiles(files, runner);
1941
+ await ((_runner$onAfterRunFil = runner.onAfterRunFiles) === null || _runner$onAfterRunFil === void 0 ? void 0 : _runner$onAfterRunFil.call(runner, files));
1942
+ await finishSendTasksUpdate(runner);
1943
+ return files;
1944
+ } finally {
1945
+ runner.cancel = cancel;
1946
+ }
1947
+ }
1948
+ async function publicCollect(specs, runner) {
1949
+ var _runner$onBeforeColle2, _runner$onCollected2;
1950
+ runner.trace ?? (runner.trace = defaultTrace);
1951
+ const paths = specs.map((f) => typeof f === "string" ? f : f.filepath);
1952
+ await ((_runner$onBeforeColle2 = runner.onBeforeCollect) === null || _runner$onBeforeColle2 === void 0 ? void 0 : _runner$onBeforeColle2.call(runner, paths));
1953
+ const files = await collectTests(specs, runner);
1954
+ await ((_runner$onCollected2 = runner.onCollected) === null || _runner$onCollected2 === void 0 ? void 0 : _runner$onCollected2.call(runner, files));
1955
+ return files;
1956
+ }
1957
+
1958
+ /**
1959
+ * @experimental
1960
+ * @advanced
1961
+ *
1962
+ * Records a custom test artifact during test execution.
1963
+ *
1964
+ * This function allows you to attach structured data, files, or metadata to a test.
1965
+ *
1966
+ * Vitest automatically injects the source location where the artifact was created and manages any attachments you include.
1967
+ *
1968
+ * @param task - The test task context, typically accessed via `this.task` in custom matchers or `context.task` in tests
1969
+ * @param artifact - The artifact to record. Must extend {@linkcode TestArtifactBase}
1970
+ *
1971
+ * @returns A promise that resolves to the recorded artifact with location injected
1972
+ *
1973
+ * @throws {Error} If called after the test has finished running
1974
+ * @throws {Error} If the test runner doesn't support artifacts
1975
+ *
1976
+ * @example
1977
+ * ```ts
1978
+ * // In a custom assertion
1979
+ * async function toHaveValidSchema(this: MatcherState, actual: unknown) {
1980
+ * const validation = validateSchema(actual)
1981
+ *
1982
+ * await recordArtifact(this.task, {
1983
+ * type: 'my-plugin:schema-validation',
1984
+ * passed: validation.valid,
1985
+ * errors: validation.errors,
1986
+ * })
1987
+ *
1988
+ * return { pass: validation.valid, message: () => '...' }
1989
+ * }
1990
+ * ```
1991
+ */
1992
+ async function recordArtifact(task, artifact) {
1993
+ const runner = getRunner();
1994
+ if (task.result && task.result.state !== "run") {
1995
+ throw new Error(`Cannot record a test artifact outside of the test run. The test "${task.name}" finished running with the "${task.result.state}" state already.`);
1996
+ }
1997
+ const stack = findTestFileStackTrace(task.file.filepath, new Error("STACK_TRACE").stack);
1998
+ if (stack) {
1999
+ artifact.location = {
2000
+ file: stack.file,
2001
+ line: stack.line,
2002
+ column: stack.column
2003
+ };
2004
+ if (artifact.type === "internal:annotation") {
2005
+ artifact.annotation.location = artifact.location;
2006
+ }
2007
+ }
2008
+ if (Array.isArray(artifact.attachments)) {
2009
+ for (const attachment of artifact.attachments) {
2010
+ manageArtifactAttachment(attachment);
2011
+ }
2012
+ }
2013
+ // annotations won't resolve as artifacts for backwards compatibility until next major
2014
+ if (artifact.type === "internal:annotation") {
2015
+ return artifact;
2016
+ }
2017
+ if (!runner.onTestArtifactRecord) {
2018
+ throw new Error(`Test runner doesn't support test artifacts.`);
2019
+ }
2020
+ await finishSendTasksUpdate(runner);
2021
+ const resolvedArtifact = await runner.onTestArtifactRecord(task, artifact);
2022
+ task.artifacts.push(resolvedArtifact);
2023
+ return resolvedArtifact;
2024
+ }
2025
+ const table = [];
2026
+ for (let i = 65; i < 91; i++) {
2027
+ table.push(String.fromCharCode(i));
2028
+ }
2029
+ for (let i = 97; i < 123; i++) {
2030
+ table.push(String.fromCharCode(i));
2031
+ }
2032
+ for (let i = 0; i < 10; i++) {
2033
+ table.push(i.toString(10));
2034
+ }
2035
+ table.push("+", "/");
2036
+ function encodeUint8Array(bytes) {
2037
+ let base64 = "";
2038
+ const len = bytes.byteLength;
2039
+ for (let i = 0; i < len; i += 3) {
2040
+ if (len === i + 1) {
2041
+ const a = (bytes[i] & 252) >> 2;
2042
+ const b = (bytes[i] & 3) << 4;
2043
+ base64 += table[a];
2044
+ base64 += table[b];
2045
+ base64 += "==";
2046
+ } else if (len === i + 2) {
2047
+ const a = (bytes[i] & 252) >> 2;
2048
+ const b = (bytes[i] & 3) << 4 | (bytes[i + 1] & 240) >> 4;
2049
+ const c = (bytes[i + 1] & 15) << 2;
2050
+ base64 += table[a];
2051
+ base64 += table[b];
2052
+ base64 += table[c];
2053
+ base64 += "=";
2054
+ } else {
2055
+ const a = (bytes[i] & 252) >> 2;
2056
+ const b = (bytes[i] & 3) << 4 | (bytes[i + 1] & 240) >> 4;
2057
+ const c = (bytes[i + 1] & 15) << 2 | (bytes[i + 2] & 192) >> 6;
2058
+ const d = bytes[i + 2] & 63;
2059
+ base64 += table[a];
2060
+ base64 += table[b];
2061
+ base64 += table[c];
2062
+ base64 += table[d];
2063
+ }
2064
+ }
2065
+ return base64;
2066
+ }
2067
+ /**
2068
+ * Records an async operation associated with a test task.
2069
+ *
2070
+ * This function tracks promises that should be awaited before a test completes.
2071
+ * The promise is automatically removed from the test's promise list once it settles.
2072
+ */
2073
+ function recordAsyncOperation(test, promise) {
2074
+ // if promise is explicitly awaited, remove it from the list
2075
+ promise = promise.finally(() => {
2076
+ if (!test.promises) {
2077
+ return;
2078
+ }
2079
+ const index = test.promises.indexOf(promise);
2080
+ if (index !== -1) {
2081
+ test.promises.splice(index, 1);
2082
+ }
2083
+ });
2084
+ // record promise
2085
+ if (!test.promises) {
2086
+ test.promises = [];
2087
+ }
2088
+ test.promises.push(promise);
2089
+ return promise;
2090
+ }
2091
+ /**
2092
+ * Validates and prepares a test attachment for serialization.
2093
+ *
2094
+ * This function ensures attachments have either `body` or `path` set (but not both), and converts `Uint8Array` bodies to base64-encoded strings for easier serialization.
2095
+ *
2096
+ * @param attachment - The attachment to validate and prepare
2097
+ *
2098
+ * @throws {TypeError} If neither `body` nor `path` is provided
2099
+ * @throws {TypeError} If both `body` and `path` are provided
2100
+ */
2101
+ function manageArtifactAttachment(attachment) {
2102
+ if (attachment.body == null && !attachment.path) {
2103
+ throw new TypeError(`Test attachment requires "body" or "path" to be set. Both are missing.`);
2104
+ }
2105
+ if (attachment.body && attachment.path) {
2106
+ throw new TypeError(`Test attachment requires only one of "body" or "path" to be set. Both are specified.`);
2107
+ }
2108
+ // convert to a string so it's easier to serialise
2109
+ if (attachment.body instanceof Uint8Array) {
2110
+ attachment.body = encodeUint8Array(attachment.body);
2111
+ }
2112
+ }
2113
+
2114
+ export { afterAll, afterEach, beforeAll, beforeEach, publicCollect as collectTests, createTaskCollector, describe, getCurrentSuite, getCurrentTest, getFn, getHooks, it, onTestFailed, onTestFinished, recordArtifact, setFn, setHooks, startTests, suite, test, updateTask };