vitest 3.1.0-beta.1 → 3.1.0

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 (88) hide show
  1. package/LICENSE.md +29 -0
  2. package/dist/browser.js +4 -4
  3. package/dist/chunks/base.bV8rwssx.js +41 -0
  4. package/dist/chunks/benchmark.BKUatJGy.js +39 -0
  5. package/dist/chunks/cac.1WcTh-zl.js +1529 -0
  6. package/dist/chunks/{cli-api.BwkkJsRe.js → cli-api.2yb7XCwB.js} +4640 -5072
  7. package/dist/chunks/console.D6t261w0.js +173 -0
  8. package/dist/chunks/constants.BZZyIeIE.js +43 -0
  9. package/dist/chunks/coverage.0iPg4Wrz.js +33 -0
  10. package/dist/chunks/{coverage.gV8doR2Y.js → coverage.SfnlalVs.js} +2424 -2482
  11. package/dist/chunks/creator.CuL7xDWI.js +705 -0
  12. package/dist/chunks/date.CDOsz-HY.js +53 -0
  13. package/dist/chunks/defaults.DmfNPoe5.js +114 -0
  14. package/dist/chunks/{env.D4Lgay0q.js → env.Dq0hM4Xv.js} +1 -1
  15. package/dist/chunks/execute.CwmnH2oH.js +791 -0
  16. package/dist/chunks/git.DXfdBEfR.js +74 -0
  17. package/dist/chunks/{globals.BEpDe-k3.js → globals.DCbUWjip.js} +10 -10
  18. package/dist/chunks/{index.D7Ny8f_s.js → index.BDobFbcz.js} +6 -7
  19. package/dist/chunks/index.CwHmn5H5.js +2422 -0
  20. package/dist/chunks/index.DFXFpH3w.js +607 -0
  21. package/dist/chunks/index.VfYQ6MXY.js +104 -0
  22. package/dist/chunks/inspector.DbDkSkFn.js +54 -0
  23. package/dist/chunks/node.IqGoMrm4.js +15 -0
  24. package/dist/chunks/{reporters.d.r7poTZjA.d.ts → reporters.d.CfRkRKN2.d.ts} +52 -20
  25. package/dist/chunks/rpc.DGgL5dw7.js +92 -0
  26. package/dist/chunks/run-once.I7PpBOk1.js +47 -0
  27. package/dist/chunks/runBaseTests.CqmKSG99.js +134 -0
  28. package/dist/chunks/setup-common.DEGDGBiA.js +88 -0
  29. package/dist/chunks/{typechecker.BlF3eHsb.js → typechecker.CG0zmr19.js} +620 -622
  30. package/dist/chunks/utils.BfxieIyZ.js +66 -0
  31. package/dist/chunks/utils.CtocqOoE.js +72 -0
  32. package/dist/chunks/utils.Lot3J_8U.js +194 -0
  33. package/dist/chunks/{vi.nSCvwQ7l.js → vi.B-PuvDzu.js} +878 -1019
  34. package/dist/chunks/vite.d.4pkSbgmp.d.ts +23 -0
  35. package/dist/chunks/vm.Lp7mPCVW.js +796 -0
  36. package/dist/cli.js +2 -2
  37. package/dist/config.cjs +97 -103
  38. package/dist/config.d.ts +6 -4
  39. package/dist/config.js +6 -6
  40. package/dist/coverage.d.ts +2 -1
  41. package/dist/coverage.js +7 -7
  42. package/dist/environments.js +1 -1
  43. package/dist/execute.d.ts +0 -2
  44. package/dist/execute.js +1 -1
  45. package/dist/index.d.ts +6 -5
  46. package/dist/index.js +6 -6
  47. package/dist/node.d.ts +17 -6
  48. package/dist/node.js +36 -45
  49. package/dist/path.js +1 -4
  50. package/dist/reporters.d.ts +2 -1
  51. package/dist/reporters.js +4 -4
  52. package/dist/runners.js +231 -267
  53. package/dist/snapshot.js +2 -2
  54. package/dist/suite.js +2 -2
  55. package/dist/worker.js +98 -114
  56. package/dist/workers/forks.js +22 -22
  57. package/dist/workers/runVmTests.js +61 -66
  58. package/dist/workers/threads.js +13 -13
  59. package/dist/workers/vmForks.js +24 -24
  60. package/dist/workers/vmThreads.js +15 -15
  61. package/dist/workers.d.ts +2 -1
  62. package/dist/workers.js +10 -10
  63. package/package.json +17 -17
  64. package/dist/chunks/base.DV59CbtV.js +0 -45
  65. package/dist/chunks/benchmark.DL72EVN-.js +0 -40
  66. package/dist/chunks/cac.BjmXy7OV.js +0 -1664
  67. package/dist/chunks/console.CN7AiMGV.js +0 -179
  68. package/dist/chunks/constants.DTYd6dNH.js +0 -46
  69. package/dist/chunks/coverage.A3sS5-Wm.js +0 -40
  70. package/dist/chunks/creator.BsBnpTzI.js +0 -670
  71. package/dist/chunks/date.W2xKR2qe.js +0 -53
  72. package/dist/chunks/defaults.C2Ndd9wx.js +0 -119
  73. package/dist/chunks/execute.eDH0aFFd.js +0 -839
  74. package/dist/chunks/git.B5SDxu-n.js +0 -69
  75. package/dist/chunks/index.DOyx6FYJ.js +0 -2551
  76. package/dist/chunks/index.K90BXFOx.js +0 -658
  77. package/dist/chunks/index.uXkkC4xl.js +0 -111
  78. package/dist/chunks/inspector.DKLceBVD.js +0 -54
  79. package/dist/chunks/node.AKq966Jp.js +0 -15
  80. package/dist/chunks/rpc.TVf73xOu.js +0 -102
  81. package/dist/chunks/run-once.2ogXb3JV.js +0 -28
  82. package/dist/chunks/runBaseTests.BVrL_ow3.js +0 -142
  83. package/dist/chunks/setup-common.CPvtqi8q.js +0 -96
  84. package/dist/chunks/utils.C8RiOc4B.js +0 -77
  85. package/dist/chunks/utils.Cn0zI1t3.js +0 -68
  86. package/dist/chunks/utils.bLM2atbD.js +0 -198
  87. package/dist/chunks/vite.d.Fvq-NZoa.d.ts +0 -11
  88. package/dist/chunks/vm.jEFQDlX_.js +0 -852
@@ -0,0 +1,705 @@
1
+ import { existsSync, writeFileSync, readFileSync } from 'node:fs';
2
+ import { mkdir, writeFile } from 'node:fs/promises';
3
+ import { resolve, dirname, relative } from 'node:path';
4
+ import { detectPackageManager, installPackage } from './index.Bw6JxgX8.js';
5
+ import { p as prompt, f as findUp } from './index.DBIGubLC.js';
6
+ import { x } from 'tinyexec';
7
+ import c from 'tinyrainbow';
8
+ import { c as configFiles } from './constants.BZZyIeIE.js';
9
+ import 'node:process';
10
+ import 'node:url';
11
+ import './_commonjsHelpers.BFTU3MAI.js';
12
+ import 'readline';
13
+ import 'events';
14
+
15
+ const jsxExample = {
16
+ name: "HelloWorld.jsx",
17
+ js: `
18
+ export default function HelloWorld({ name }) {
19
+ return (
20
+ <div>
21
+ <h1>Hello {name}!</h1>
22
+ </div>
23
+ )
24
+ }
25
+ `,
26
+ ts: `
27
+ export default function HelloWorld({ name }: { name: string }) {
28
+ return (
29
+ <div>
30
+ <h1>Hello {name}!</h1>
31
+ </div>
32
+ )
33
+ }
34
+ `,
35
+ test: `
36
+ import { expect, test } from 'vitest'
37
+ import { render } from '@testing-library/jsx'
38
+ import HelloWorld from './HelloWorld.jsx'
39
+
40
+ test('renders name', async () => {
41
+ const { getByText } = render(<HelloWorld name="Vitest" />)
42
+ await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
43
+ })
44
+ `
45
+ };
46
+ const vueExample = {
47
+ name: "HelloWorld.vue",
48
+ js: `
49
+ <script setup>
50
+ defineProps({
51
+ name: String
52
+ })
53
+ </script>
54
+
55
+ <template>
56
+ <div>
57
+ <h1>Hello {{ name }}!</h1>
58
+ </div>
59
+ </template>
60
+ `,
61
+ ts: `
62
+ <script setup lang="ts">
63
+ defineProps<{
64
+ name: string
65
+ }>()
66
+ </script>
67
+
68
+ <template>
69
+ <div>
70
+ <h1>Hello {{ name }}!</h1>
71
+ </div>
72
+ </template>
73
+ `,
74
+ test: `
75
+ import { expect, test } from 'vitest'
76
+ import { render } from 'vitest-browser-vue'
77
+ import HelloWorld from './HelloWorld.vue'
78
+
79
+ test('renders name', async () => {
80
+ const { getByText } = render(HelloWorld, {
81
+ props: { name: 'Vitest' },
82
+ })
83
+ await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
84
+ })
85
+ `
86
+ };
87
+ const svelteExample = {
88
+ name: "HelloWorld.svelte",
89
+ js: `
90
+ <script>
91
+ export let name
92
+ </script>
93
+
94
+ <h1>Hello {name}!</h1>
95
+ `,
96
+ ts: `
97
+ <script lang="ts">
98
+ export let name: string
99
+ </script>
100
+
101
+ <h1>Hello {name}!</h1>
102
+ `,
103
+ test: `
104
+ import { expect, test } from 'vitest'
105
+ import { render } from 'vitest-browser-svelte'
106
+ import HelloWorld from './HelloWorld.svelte'
107
+
108
+ test('renders name', async () => {
109
+ const { getByText } = render(HelloWorld, { name: 'Vitest' })
110
+ await expect.element(getByText('Hello Vitest!')).toBeInTheDocument()
111
+ })
112
+ `
113
+ };
114
+ const markoExample = {
115
+ name: "HelloWorld.marko",
116
+ js: `
117
+ class {
118
+ onCreate() {
119
+ this.state = { name: null }
120
+ }
121
+ }
122
+
123
+ <h1>Hello \${state.name}!</h1>
124
+ `,
125
+ ts: `
126
+ export interface Input {
127
+ name: string
128
+ }
129
+
130
+ <h1>Hello \${input.name}!</h1>
131
+ `,
132
+ test: `
133
+ import { expect, test } from 'vitest'
134
+ import { render } from '@marko/testing-library'
135
+ import HelloWorld from './HelloWorld.svelte'
136
+
137
+ test('renders name', async () => {
138
+ const { getByText } = await render(HelloWorld, { name: 'Vitest' })
139
+ const element = getByText('Hello Vitest!')
140
+ expect(element).toBeInTheDocument()
141
+ })
142
+ `
143
+ };
144
+ const litExample = {
145
+ name: "HelloWorld.js",
146
+ js: `
147
+ import { html, LitElement } from 'lit'
148
+
149
+ export class HelloWorld extends LitElement {
150
+ static properties = {
151
+ name: { type: String },
152
+ }
153
+
154
+ constructor() {
155
+ super()
156
+ this.name = 'World'
157
+ }
158
+
159
+ render() {
160
+ return html\`<h1>Hello \${this.name}!</h1>\`
161
+ }
162
+ }
163
+
164
+ customElements.define('hello-world', HelloWorld)
165
+ `,
166
+ ts: `
167
+ import { html, LitElement } from 'lit'
168
+ import { customElement, property } from 'lit/decorators.js'
169
+
170
+ @customElement('hello-world')
171
+ export class HelloWorld extends LitElement {
172
+ @property({ type: String })
173
+ name = 'World'
174
+
175
+ render() {
176
+ return html\`<h1>Hello \${this.name}!</h1>\`
177
+ }
178
+ }
179
+
180
+ declare global {
181
+ interface HTMLElementTagNameMap {
182
+ 'hello-world': HelloWorld
183
+ }
184
+ }
185
+ `,
186
+ test: `
187
+ import { expect, test } from 'vitest'
188
+ import { render } from 'vitest-browser-lit'
189
+ import { html } from 'lit'
190
+ import './HelloWorld.js'
191
+
192
+ test('renders name', async () => {
193
+ const screen = render(html\`<hello-world name="Vitest"></hello-world>\`)
194
+ const element = screen.getByText('Hello Vitest!')
195
+ await expect.element(element).toBeInTheDocument()
196
+ })
197
+ `
198
+ };
199
+ const vanillaExample = {
200
+ name: "HelloWorld.js",
201
+ js: `
202
+ export default function HelloWorld({ name }) {
203
+ const parent = document.createElement('div')
204
+
205
+ const h1 = document.createElement('h1')
206
+ h1.textContent = 'Hello ' + name + '!'
207
+ parent.appendChild(h1)
208
+
209
+ return parent
210
+ }
211
+ `,
212
+ ts: `
213
+ export default function HelloWorld({ name }: { name: string }): HTMLDivElement {
214
+ const parent = document.createElement('div')
215
+
216
+ const h1 = document.createElement('h1')
217
+ h1.textContent = 'Hello ' + name + '!'
218
+ parent.appendChild(h1)
219
+
220
+ return parent
221
+ }
222
+ `,
223
+ test: `
224
+ import { expect, test } from 'vitest'
225
+ import { getByText } from '@testing-library/dom'
226
+ import HelloWorld from './HelloWorld.js'
227
+
228
+ test('renders name', () => {
229
+ const parent = HelloWorld({ name: 'Vitest' })
230
+ document.body.appendChild(parent)
231
+
232
+ const element = getByText(parent, 'Hello Vitest!')
233
+ expect(element).toBeInTheDocument()
234
+ })
235
+ `
236
+ };
237
+ function getExampleTest(framework) {
238
+ switch (framework) {
239
+ case "solid":
240
+ case "preact": return {
241
+ ...jsxExample,
242
+ test: jsxExample.test.replace("@testing-library/jsx", `@testing-library/${framework}`)
243
+ };
244
+ case "react": return {
245
+ ...jsxExample,
246
+ test: jsxExample.test.replace("@testing-library/jsx", "vitest-browser-react")
247
+ };
248
+ case "vue": return vueExample;
249
+ case "svelte": return svelteExample;
250
+ case "lit": return litExample;
251
+ case "marko": return markoExample;
252
+ default: return vanillaExample;
253
+ }
254
+ }
255
+ async function generateExampleFiles(framework, lang) {
256
+ const example = getExampleTest(framework);
257
+ let fileName = example.name;
258
+ const folder = resolve(process.cwd(), "vitest-example");
259
+ const fileContent = example[lang];
260
+ if (!existsSync(folder)) {
261
+ await mkdir(folder, { recursive: true });
262
+ }
263
+ const isJSX = fileName.endsWith(".jsx");
264
+ if (isJSX && lang === "ts") {
265
+ fileName = fileName.replace(".jsx", ".tsx");
266
+ } else if (fileName.endsWith(".js") && lang === "ts") {
267
+ fileName = fileName.replace(".js", ".ts");
268
+ }
269
+ const filePath = resolve(folder, fileName);
270
+ const testPath = resolve(folder, `HelloWorld.test.${isJSX ? `${lang}x` : lang}`);
271
+ writeFileSync(filePath, fileContent.trimStart(), "utf-8");
272
+ writeFileSync(testPath, example.test.trimStart(), "utf-8");
273
+ return testPath;
274
+ }
275
+
276
+ const log = console.log;
277
+ function getProviderOptions() {
278
+ const providers = {
279
+ playwright: "Playwright relies on Chrome DevTools protocol. Read more: https://playwright.dev",
280
+ webdriverio: "WebdriverIO uses WebDriver protocol. Read more: https://webdriver.io",
281
+ preview: "Preview is useful to quickly run your tests in the browser, but not suitable for CI."
282
+ };
283
+ return Object.entries(providers).map(([provider, description]) => {
284
+ return {
285
+ title: provider,
286
+ description,
287
+ value: provider
288
+ };
289
+ });
290
+ }
291
+ function getBrowserNames(provider) {
292
+ switch (provider) {
293
+ case "webdriverio": return [
294
+ "chrome",
295
+ "firefox",
296
+ "edge",
297
+ "safari"
298
+ ];
299
+ case "playwright": return [
300
+ "chromium",
301
+ "firefox",
302
+ "webkit"
303
+ ];
304
+ case "preview": return [
305
+ "chrome",
306
+ "firefox",
307
+ "safari"
308
+ ];
309
+ }
310
+ }
311
+ function getProviderPackageNames(provider) {
312
+ switch (provider) {
313
+ case "webdriverio": return {
314
+ types: "@vitest/browser/providers/webdriverio",
315
+ pkg: "webdriverio"
316
+ };
317
+ case "playwright": return {
318
+ types: "@vitest/browser/providers/playwright",
319
+ pkg: "playwright"
320
+ };
321
+ case "preview": return {
322
+ types: "@vitest/browser/matchers",
323
+ pkg: null
324
+ };
325
+ }
326
+ throw new Error(`Unsupported provider: ${provider}`);
327
+ }
328
+ function getFramework() {
329
+ return [
330
+ {
331
+ title: "vanilla",
332
+ value: "vanilla",
333
+ description: "No framework, just plain JavaScript or TypeScript."
334
+ },
335
+ {
336
+ title: "vue",
337
+ value: "vue",
338
+ description: "\"The Progressive JavaScript Framework\""
339
+ },
340
+ {
341
+ title: "svelte",
342
+ value: "svelte",
343
+ description: "\"Svelte: cybernetically enhanced web apps\""
344
+ },
345
+ {
346
+ title: "react",
347
+ value: "react",
348
+ description: "\"The library for web and native user interfaces\""
349
+ },
350
+ {
351
+ title: "lit",
352
+ value: "lit",
353
+ description: "\"A simple library for building fast, lightweight web components.\""
354
+ },
355
+ {
356
+ title: "preact",
357
+ value: "preact",
358
+ description: "\"Fast 3kB alternative to React with the same modern API\""
359
+ },
360
+ {
361
+ title: "solid",
362
+ value: "solid",
363
+ description: "\"Simple and performant reactivity for building user interfaces\""
364
+ },
365
+ {
366
+ title: "marko",
367
+ value: "marko",
368
+ description: "\"A declarative, HTML-based language that makes building web apps fun\""
369
+ }
370
+ ];
371
+ }
372
+ function getFrameworkTestPackage(framework) {
373
+ switch (framework) {
374
+ case "vanilla": return null;
375
+ case "vue": return "vitest-browser-vue";
376
+ case "svelte": return "vitest-browser-svelte";
377
+ case "react": return "vitest-browser-react";
378
+ case "lit": return "vitest-browser-lit";
379
+ case "preact": return "@testing-library/preact";
380
+ case "solid": return "@solidjs/testing-library";
381
+ case "marko": return "@marko/testing-library";
382
+ }
383
+ throw new Error(`Unsupported framework: ${framework}`);
384
+ }
385
+ function getFrameworkPluginPackage(framework) {
386
+ switch (framework) {
387
+ case "vue": return "@vitejs/plugin-vue";
388
+ case "svelte": return "@sveltejs/vite-plugin-svelte";
389
+ case "react": return "@vitejs/plugin-react";
390
+ case "preact": return "@preact/preset-vite";
391
+ case "solid": return "vite-plugin-solid";
392
+ case "marko": return "@marko/vite";
393
+ }
394
+ return null;
395
+ }
396
+ async function updateTsConfig(type) {
397
+ if (type == null) {
398
+ return;
399
+ }
400
+ const msg = `Add "${c.bold(type)}" to your tsconfig.json "${c.bold("compilerOptions.types")}" field to have better intellisense support.`;
401
+ log();
402
+ log(c.yellow("◼"), c.yellow(msg));
403
+ }
404
+ function getLanguageOptions() {
405
+ return [{
406
+ title: "TypeScript",
407
+ description: "Use TypeScript.",
408
+ value: "ts"
409
+ }, {
410
+ title: "JavaScript",
411
+ description: "Use plain JavaScript.",
412
+ value: "js"
413
+ }];
414
+ }
415
+ async function installPackages(pkgManager, packages) {
416
+ if (!packages.length) {
417
+ log(c.green("✔"), c.bold("All packages are already installed."));
418
+ return;
419
+ }
420
+ log(c.cyan("◼"), c.bold("Installing packages..."));
421
+ log(c.cyan("◼"), packages.join(", "));
422
+ log();
423
+ await installPackage(packages, {
424
+ dev: true,
425
+ packageManager: pkgManager ?? undefined
426
+ });
427
+ }
428
+ function readPkgJson(path) {
429
+ if (!existsSync(path)) {
430
+ return null;
431
+ }
432
+ const content = readFileSync(path, "utf-8");
433
+ return JSON.parse(content);
434
+ }
435
+ function getPossibleDefaults(dependencies) {
436
+ const provider = getPossibleProvider(dependencies);
437
+ const framework = getPossibleFramework(dependencies);
438
+ return {
439
+ lang: "ts",
440
+ provider,
441
+ framework
442
+ };
443
+ }
444
+ function getPossibleFramework(dependencies) {
445
+ if (dependencies.vue || dependencies["vue-tsc"] || dependencies["@vue/reactivity"]) {
446
+ return "vue";
447
+ }
448
+ if (dependencies.react || dependencies["react-dom"]) {
449
+ return "react";
450
+ }
451
+ if (dependencies.svelte || dependencies["@sveltejs/kit"]) {
452
+ return "svelte";
453
+ }
454
+ if (dependencies.lit || dependencies["lit-html"]) {
455
+ return "lit";
456
+ }
457
+ if (dependencies.preact) {
458
+ return "preact";
459
+ }
460
+ if (dependencies["solid-js"] || dependencies["@solidjs/start"]) {
461
+ return "solid";
462
+ }
463
+ if (dependencies.marko) {
464
+ return "marko";
465
+ }
466
+ return "vanilla";
467
+ }
468
+ function getPossibleProvider(dependencies) {
469
+ if (dependencies.webdriverio || dependencies["@wdio/cli"] || dependencies["@wdio/config"]) {
470
+ return "webdriverio";
471
+ }
472
+ return "playwright";
473
+ }
474
+ function getProviderDocsLink(provider) {
475
+ switch (provider) {
476
+ case "playwright": return "https://vitest.dev/guide/browser/playwright";
477
+ case "webdriverio": return "https://vitest.dev/guide/browser/webdriverio";
478
+ }
479
+ }
480
+ function sort(choices, value) {
481
+ const index = choices.findIndex((i) => i.value === value);
482
+ if (index === -1) {
483
+ return choices;
484
+ }
485
+ const item = choices.splice(index, 1)[0];
486
+ return [item, ...choices];
487
+ }
488
+ function fail() {
489
+ process.exitCode = 1;
490
+ }
491
+ async function generateWorkspaceFile(options) {
492
+ const relativeRoot = relative(dirname(options.configPath), options.rootConfig);
493
+ const workspaceContent = [
494
+ `import { defineWorkspace } from 'vitest/config'`,
495
+ "",
496
+ "export default defineWorkspace([",
497
+ " // If you want to keep running your existing tests in Node.js, uncomment the next line.",
498
+ ` // '${relativeRoot}',`,
499
+ ` {`,
500
+ ` extends: '${relativeRoot}',`,
501
+ ` test: {`,
502
+ ` browser: {`,
503
+ ` enabled: true,`,
504
+ ` provider: '${options.provider}',`,
505
+ options.provider !== "preview" && ` // ${getProviderDocsLink(options.provider)}`,
506
+ ` instances: [`,
507
+ ...options.browsers.map((browser) => ` { browser: '${browser}' },`),
508
+ ` ],`,
509
+ ` },`,
510
+ ` },`,
511
+ ` },`,
512
+ `])`,
513
+ ""
514
+ ].filter((c) => typeof c === "string").join("\n");
515
+ await writeFile(options.configPath, workspaceContent);
516
+ }
517
+ async function generateFrameworkConfigFile(options) {
518
+ const frameworkImport = options.framework === "svelte" ? `import { svelte } from '${options.frameworkPlugin}'` : `import ${options.framework} from '${options.frameworkPlugin}'`;
519
+ const configContent = [
520
+ `import { defineConfig } from 'vitest/config'`,
521
+ options.frameworkPlugin ? frameworkImport : null,
522
+ ``,
523
+ "export default defineConfig({",
524
+ options.frameworkPlugin ? ` plugins: [${options.framework}()],` : null,
525
+ ` test: {`,
526
+ ` browser: {`,
527
+ ` enabled: true,`,
528
+ ` provider: '${options.provider}',`,
529
+ options.provider !== "preview" && ` // ${getProviderDocsLink(options.provider)}`,
530
+ ` instances: [`,
531
+ ...options.browsers.map((browser) => ` { browser: '${browser}' },`),
532
+ ` ],`,
533
+ ` },`,
534
+ ` },`,
535
+ `})`,
536
+ ""
537
+ ].filter((t) => typeof t === "string").join("\n");
538
+ await writeFile(options.configPath, configContent);
539
+ }
540
+ async function updatePkgJsonScripts(pkgJsonPath, vitestScript) {
541
+ if (!existsSync(pkgJsonPath)) {
542
+ const pkg = { scripts: { "test:browser": vitestScript } };
543
+ await writeFile(pkgJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
544
+ } else {
545
+ const pkg = JSON.parse(readFileSync(pkgJsonPath, "utf-8"));
546
+ pkg.scripts = pkg.scripts || {};
547
+ pkg.scripts["test:browser"] = vitestScript;
548
+ await writeFile(pkgJsonPath, `${JSON.stringify(pkg, null, 2)}\n`, "utf-8");
549
+ }
550
+ log(c.green("✔"), "Added \"test:browser\" script to your package.json.");
551
+ }
552
+ function getRunScript(pkgManager) {
553
+ switch (pkgManager) {
554
+ case "yarn@berry":
555
+ case "yarn": return "yarn test:browser";
556
+ case "pnpm@6":
557
+ case "pnpm": return "pnpm test:browser";
558
+ case "bun": return "bun test:browser";
559
+ default: return "npm run test:browser";
560
+ }
561
+ }
562
+ function getPlaywrightRunArgs(pkgManager) {
563
+ switch (pkgManager) {
564
+ case "yarn@berry":
565
+ case "yarn": return ["yarn", "exec"];
566
+ case "pnpm@6":
567
+ case "pnpm": return ["pnpx"];
568
+ case "bun": return ["bunx"];
569
+ default: return ["npx"];
570
+ }
571
+ }
572
+ async function create() {
573
+ log(c.cyan("◼"), "This utility will help you set up a browser testing environment.\n");
574
+ const pkgJsonPath = resolve(process.cwd(), "package.json");
575
+ const pkg = readPkgJson(pkgJsonPath) || {};
576
+ const dependencies = {
577
+ ...pkg.dependencies,
578
+ ...pkg.devDependencies
579
+ };
580
+ const defaults = getPossibleDefaults(dependencies);
581
+ const { lang } = await prompt({
582
+ type: "select",
583
+ name: "lang",
584
+ message: "Choose a language for your tests",
585
+ choices: sort(getLanguageOptions(), defaults?.lang)
586
+ });
587
+ if (!lang) {
588
+ return fail();
589
+ }
590
+ const { provider } = await prompt({
591
+ type: "select",
592
+ name: "provider",
593
+ message: "Choose a browser provider. Vitest will use its API to control the testing environment",
594
+ choices: sort(getProviderOptions(), defaults?.provider)
595
+ });
596
+ if (!provider) {
597
+ return fail();
598
+ }
599
+ const { browsers } = await prompt({
600
+ type: "multiselect",
601
+ name: "browsers",
602
+ message: "Choose a browser",
603
+ choices: getBrowserNames(provider).map((browser) => ({
604
+ title: browser,
605
+ value: browser
606
+ }))
607
+ });
608
+ if (!provider) {
609
+ return fail();
610
+ }
611
+ const { framework } = await prompt({
612
+ type: "select",
613
+ name: "framework",
614
+ message: "Choose your framework",
615
+ choices: sort(getFramework(), defaults?.framework)
616
+ });
617
+ if (!framework) {
618
+ return fail();
619
+ }
620
+ let installPlaywright = false;
621
+ if (provider === "playwright") {
622
+ ({installPlaywright} = await prompt({
623
+ type: "confirm",
624
+ name: "installPlaywright",
625
+ message: `Install Playwright browsers (can be done manually via 'pnpm exec playwright install')?`
626
+ }));
627
+ }
628
+ if (installPlaywright == null) {
629
+ return fail();
630
+ }
631
+ const dependenciesToInstall = ["@vitest/browser"];
632
+ const frameworkPackage = getFrameworkTestPackage(framework);
633
+ if (frameworkPackage) {
634
+ dependenciesToInstall.push(frameworkPackage);
635
+ }
636
+ const providerPkg = getProviderPackageNames(provider);
637
+ if (providerPkg.pkg) {
638
+ dependenciesToInstall.push(providerPkg.pkg);
639
+ }
640
+ const frameworkPlugin = getFrameworkPluginPackage(framework);
641
+ if (frameworkPlugin) {
642
+ dependenciesToInstall.push(frameworkPlugin);
643
+ }
644
+ const pkgManager = await detectPackageManager();
645
+ log();
646
+ await installPackages(pkgManager, dependenciesToInstall.filter((pkg) => !dependencies[pkg]));
647
+ const rootConfig = await findUp(configFiles, { cwd: process.cwd() });
648
+ let scriptCommand = "vitest";
649
+ log();
650
+ if (rootConfig) {
651
+ let browserWorkspaceFile = resolve(dirname(rootConfig), `vitest.workspace.${lang}`);
652
+ if (existsSync(browserWorkspaceFile)) {
653
+ log(c.yellow("⚠"), c.yellow("A workspace file already exists. Creating a new one for the browser tests - you can merge them manually if needed."));
654
+ browserWorkspaceFile = resolve(process.cwd(), `vitest.workspace.browser.${lang}`);
655
+ }
656
+ scriptCommand = `vitest --workspace=${relative(process.cwd(), browserWorkspaceFile)}`;
657
+ await generateWorkspaceFile({
658
+ configPath: browserWorkspaceFile,
659
+ rootConfig,
660
+ provider,
661
+ browsers
662
+ });
663
+ log(c.green("✔"), "Created a workspace file for browser tests:", c.bold(relative(process.cwd(), browserWorkspaceFile)));
664
+ } else {
665
+ const configPath = resolve(process.cwd(), `vitest.config.${lang}`);
666
+ await generateFrameworkConfigFile({
667
+ configPath,
668
+ framework,
669
+ frameworkPlugin,
670
+ provider,
671
+ browsers
672
+ });
673
+ log(c.green("✔"), "Created a config file for browser tests", c.bold(relative(process.cwd(), configPath)));
674
+ }
675
+ log();
676
+ await updatePkgJsonScripts(pkgJsonPath, scriptCommand);
677
+ if (installPlaywright) {
678
+ log();
679
+ const [command, ...args] = getPlaywrightRunArgs(pkgManager);
680
+ const allArgs = [
681
+ ...args,
682
+ "playwright",
683
+ "install",
684
+ "--with-deps"
685
+ ];
686
+ log(c.cyan("◼"), `Installing Playwright dependencies with \`${c.bold(command)} ${c.bold(allArgs.join(" "))}\`...`);
687
+ log();
688
+ await x(command, allArgs, { nodeOptions: { stdio: [
689
+ "pipe",
690
+ "inherit",
691
+ "inherit"
692
+ ] } });
693
+ }
694
+ if (lang === "ts") {
695
+ await updateTsConfig(providerPkg?.types);
696
+ }
697
+ log();
698
+ const exampleTestFile = await generateExampleFiles(framework, lang);
699
+ log(c.green("✔"), "Created example test file in", c.bold(relative(process.cwd(), exampleTestFile)));
700
+ log(c.dim(" You can safely delete this file once you have written your own tests."));
701
+ log();
702
+ log(c.cyan("◼"), "All done! Run your tests with", c.bold(getRunScript(pkgManager)));
703
+ }
704
+
705
+ export { create };