vitest 0.0.72 → 0.0.76

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.
package/README.gh.md CHANGED
@@ -11,6 +11,14 @@ A blazing fast unit test framework powered by Vite.
11
11
  <p align="center">
12
12
  <a href="https://www.npmjs.com/package/vitest"><img src="https://img.shields.io/npm/v/vitest?color=a1b858&label="></a>
13
13
  <p>
14
+ <h2 align="center">
15
+ <a href="https://preview.vitest.dev">Open the Docs</a>
16
+ </h2>
17
+ <h3 align="center">
18
+ <a href=https://discord.com/invite/2zYZNngd7y"><i>Get involved!</i></a>
19
+ </h3>
20
+ <br>
21
+ <br>
14
22
 
15
23
  > 💖 **This project is currently in closed beta exclusively for Sponsors.**<br>
16
24
  > Become a Sponsor of [@patak-dev](https://github.com/sponsors/patak-dev) or [@antfu](https://github.com/sponsors/antfu) to access the source code and issues tracker.
@@ -19,7 +27,8 @@ A blazing fast unit test framework powered by Vite.
19
27
 
20
28
  > Vitest requires Vite v2.7 and Node v16
21
29
 
22
- [**Join the Discord!**](https://discord.com/invite/2zYZNngd7y)
30
+
31
+ Switch to Vitest by following the [Getting Started Guide](https://preview.vitest.dev/guide) or learn [why we are building a new test runner](https://preview.vitest.dev/guide).
23
32
 
24
33
  ## Features
25
34
 
@@ -74,281 +83,6 @@ $ npx vitest
74
83
  - [unplugin-vue-components](https://github.com/antfu/unplugin-vue-components)
75
84
  - [vitesse-lite](https://github.com/antfu/vitesse-lite)
76
85
 
77
- ## Configuration
78
-
79
- `vitest` will read your root `vite.config.ts` when it present to match with the plugins and setup as your Vite app. If you want to it to have a different configuration for testing, you could either:
80
-
81
- - Create `vitest.config.ts`, which will have the higher priority
82
- - Pass `--config` option to CLI, e.g. `vitest --config ./path/to/vitest.config.ts`
83
- - Use `process.env.VITEST` to conditionally apply different configuration in `vite.config.ts`
84
-
85
- To configure `vitest` itself, add `test` property in your Vite config
86
-
87
- ```ts
88
- // vite.config.ts
89
- import { defineConfig } from 'vite'
90
-
91
- export default defineConfig({
92
- test: {
93
- // ...
94
- }
95
- })
96
- ```
97
-
98
- ## Global APIs
99
-
100
- By default, `vitest` does not provide global APIs for explicitness. If you prefer to use the APIs globally like Jest, you can pass the `--global` option to CLI or add `global: true` in the config.
101
-
102
- ```ts
103
- // vite.config.ts
104
- import { defineConfig } from 'vite'
105
-
106
- export default defineConfig({
107
- test: {
108
- global: true
109
- }
110
- })
111
- ```
112
-
113
- To get TypeScript working with the global APIs, add `vitest/global` to the `types` filed in your `tsconfig.json`
114
-
115
- ```jsonc
116
- // tsconfig.json
117
- {
118
- "compilerOptions": {
119
- "types": [
120
- "vitest/global"
121
- ]
122
- }
123
- }
124
- ```
125
-
126
- If you are already using [`unplugin-auto-import`](https://github.com/antfu/unplugin-vue-components) in your project, you can also use it directly for auto importing those APIs.
127
-
128
- ```ts
129
- // vite.config.ts
130
- import { defineConfig } from 'vite'
131
- import AutoImport from 'unplugin-auto-import/vite'
132
-
133
- export default defineConfig({
134
- plugins: [
135
- AutoImport({
136
- imports: ['vitest'],
137
- dts: true // generate TypeScript declaration
138
- })
139
- ]
140
- })
141
- ```
142
-
143
- ## Browser Mocking
144
-
145
- Vitest supports both [happy-dom](https://github.com/capricorn86/happy-dom) or [jsdom](https://github.com/jsdom/jsdom) for mocking DOM and browser APIs. They don't come with Vitest, you might need to install them:
146
-
147
- ```bash
148
- $ npm i -D happy-dom
149
- # or
150
- $ npm i -D jsdom
151
- ```
152
-
153
- After that, change the `environment` option in your config file:
154
-
155
- ```ts
156
- // vite.config.ts
157
- import { defineConfig } from 'vite'
158
-
159
- export default defineConfig({
160
- test: {
161
- environment: 'happy-dom' // or 'jsdom', 'node'
162
- }
163
- })
164
- ```
165
-
166
- ## Watch Mode
167
-
168
- ```bash
169
- $ vitest -w
170
- ```
171
-
172
- Vitest smartly searches the module graph and only rerun the related tests (just like how HMR works in Vite!).
173
-
174
- ## Coverage
175
-
176
- Vitest works perfectly with [c8](https://github.com/bcoe/c8)
177
-
178
- ```bash
179
- $ npm i -D c8
180
- $ c8 vitest
181
- ```
182
-
183
- ```json
184
- {
185
- "scripts": {
186
- "test": "vitest",
187
- "coverage": "c8 vitest"
188
- }
189
- }
190
- ```
191
-
192
- For convenience, we also provide a shorthand for passing `--coverage` option to CLI, which will wrap the process with `c8` for you. Note when using the shorthand, you will lose the ability to pass additional options to `c8`.
193
-
194
- ```bash
195
- $ vitest --coverage
196
- ```
197
-
198
- For more configuration available, please refer to [c8](https://github.com/bcoe/c8)'s documentation.
199
-
200
- ## Filtering
201
-
202
- ### CLI
203
-
204
- You can use CLI to filter test files my name:
205
-
206
- ```bash
207
- $ vitest basic
208
- ```
209
-
210
- Will only execute test files that contain `basic`, e.g.
211
-
212
- ```
213
- basic.test.ts
214
- basic-foo.test.ts
215
- ```
216
-
217
- ### Specifying a Timeout
218
-
219
- You can optionally pass a timeout in milliseconds as third argument to tests. The default is 5 seconds.
220
-
221
- ```ts
222
- test('name', async() => { ... }, 1000)
223
- ```
224
-
225
- Hooks also can receive a timeout, with the same 5 seconds default.
226
-
227
- ```ts
228
- beforeAll( async() => { ... }, 1000)
229
- ```
230
-
231
- ### Skipping suites and tests
232
-
233
- Use `.skip` to avoid running certain suites or tests
234
-
235
- ```ts
236
- describe.skip('skipped suite', () => {
237
- it('test', () => {
238
- // Suite skipped, no error
239
- assert.equal(Math.sqrt(4), 3)
240
- })
241
- })
242
-
243
- describe('suite', () => {
244
- it.skip('skipped test', () => {
245
- // Test skipped, no error
246
- assert.equal(Math.sqrt(4), 3)
247
- })
248
- })
249
- ```
250
-
251
- ### Selecting suites and tests to run
252
-
253
- Use `.only` to only run certain suites or tests
254
-
255
- ```ts
256
- // Only this suite (and others marked with only) are run
257
- describe.only('suite', () => {
258
- it('test', () => {
259
- assert.equal(Math.sqrt(4), 3)
260
- })
261
- })
262
-
263
- describe('another suite', () => {
264
- it('skipped test', () => {
265
- // Test skipped, as tests are running in Only mode
266
- assert.equal(Math.sqrt(4), 3)
267
- })
268
-
269
- it.only('test', () => {
270
- // Only this test (and others marked with only) are run
271
- assert.equal(Math.sqrt(4), 2)
272
- })
273
- })
274
- ```
275
-
276
- ### Unimplemented suites and tests
277
-
278
- Use `.todo` to stub suites and tests that should be implemented
279
-
280
- ```ts
281
- // An entry will be shown in the report for this suite
282
- describe.todo('unimplemented suite')
283
-
284
- // An entry will be shown in the report for this test
285
- describe('suite', () => {
286
- it.todo('unimplemented test')
287
- })
288
- ```
289
-
290
- ### Running tests concurrently
291
-
292
- Use `.concurrent` in consecutive tests to run them in parallel
293
-
294
- ```ts
295
- // The two tests marked with concurrent will be run in parallel
296
- describe('suite', () => {
297
- it('serial test', () => {
298
- assert.equal(Math.sqrt(4), 3)
299
- })
300
- it.concurrent('concurrent test 1', () => {
301
- assert.equal(Math.sqrt(4), 3)
302
- })
303
- it.concurrent('concurrent test 2', () => {
304
- assert.equal(Math.sqrt(4), 3)
305
- })
306
- })
307
- ```
308
-
309
- If you use `.concurrent` in a suite, every tests in it will be run in parallel
310
- ```ts
311
- // The two tests marked with concurrent will be run in parallel
312
- describe.concurrent('suite', () => {
313
- it('concurrent test 1', () => {
314
- assert.equal(Math.sqrt(4), 3)
315
- })
316
- it('concurrent test 2', () => {
317
- assert.equal(Math.sqrt(4), 3)
318
- })
319
- // No effect, same as not using .concurrent
320
- it.concurrent('concurrent test 3', () => {
321
- assert.equal(Math.sqrt(4), 3)
322
- })
323
- })
324
- ```
325
-
326
- You can also use `.skip`, `.only`, and `.todo` with concurrent suite and tests. All the following combinations are valid:
327
- ```js
328
- describe.concurrent(...)
329
-
330
- describe.skip.concurrent(...)
331
- describe.concurrent.skip(...)
332
-
333
- describe.only.concurrent(...)
334
- describe.concurrent.only(...)
335
-
336
- describe.todo.concurrent(...)
337
- describe.concurrent.todo(...)
338
-
339
- it.concurrent(...)
340
-
341
- it.skip.concurrent(...)
342
- it.concurrent.skip(...)
343
-
344
- it.only.concurrent(...)
345
- it.concurrent.only(...)
346
-
347
- it.todo.concurrent(...)
348
- it.concurrent.todo(...)
349
- ```
350
-
351
-
352
86
  ## Sponsors
353
87
 
354
88
  <p align="center">
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import require$$2, { EventEmitter } from 'events';
2
- import { s as stringWidth, a as ansiStyles, b as stripAnsi, c as sliceAnsi, d as c, F as F_POINTER, e as F_DOWN, f as F_DOWN_RIGHT, g as F_DOT, h as F_CHECK, i as F_CROSS, j as cliTruncate, k as F_RIGHT, p as printError } from './error-fb6ff2e6.js';
2
+ import { s as stringWidth, a as ansiStyles, b as stripAnsi, c as sliceAnsi, d as c, F as F_POINTER, e as F_DOWN, f as F_LONG_DASH, g as F_DOWN_RIGHT, h as F_DOT, i as F_CHECK, j as F_CROSS, k as cliTruncate, l as F_RIGHT, p as printError } from './error-1df12c37.js';
3
3
  import { performance } from 'perf_hooks';
4
4
  import path, { isAbsolute, relative, dirname, basename, resolve } from 'path';
5
5
  import { g as getNames, s as slash, a as getTests, b as getSuites, t as toArray, h as hasFailed } from './utils-9dcc4050.js';
@@ -632,7 +632,7 @@ const cac = (name = "") => new CAC(name);
632
632
 
633
633
  var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
634
634
 
635
- var version = "0.0.72";
635
+ var version = "0.0.76";
636
636
 
637
637
  const ESC = '\u001B[';
638
638
  const OSC = '\u001B]';
@@ -1451,6 +1451,24 @@ const spinnerMap = new WeakMap();
1451
1451
  const outputMap = new WeakMap();
1452
1452
  const pointer = c.yellow(F_POINTER);
1453
1453
  const skipped = c.yellow(F_DOWN);
1454
+ function divider(text, left, right) {
1455
+ let length = process.stdout.columns;
1456
+ if (!length || isNaN(length))
1457
+ length = 10;
1458
+ if (text) {
1459
+ const textLength = stripAnsi(text).length;
1460
+ if (left == null && right != null) {
1461
+ left = length - textLength - right;
1462
+ } else {
1463
+ left = left ?? Math.floor((length - textLength) / 2);
1464
+ right = length - textLength - left;
1465
+ }
1466
+ left = Math.max(0, left);
1467
+ right = Math.max(0, right);
1468
+ return `${F_LONG_DASH.repeat(left)}${text}${F_LONG_DASH.repeat(right)}`;
1469
+ }
1470
+ return F_LONG_DASH.repeat(length);
1471
+ }
1454
1472
  function formatTestPath(root, path) {
1455
1473
  var _a;
1456
1474
  if (isAbsolute(path))
@@ -1573,7 +1591,8 @@ function renderTree(tasks, level = 0) {
1573
1591
  const createRenderer = (_tasks) => {
1574
1592
  let tasks = _tasks;
1575
1593
  let timer;
1576
- const log = createLogUpdate(process.stdout);
1594
+ const stdout = process.stdout;
1595
+ const log = createLogUpdate(stdout);
1577
1596
  function update() {
1578
1597
  log(renderTree(tasks));
1579
1598
  }
@@ -1595,13 +1614,17 @@ const createRenderer = (_tasks) => {
1595
1614
  timer = void 0;
1596
1615
  }
1597
1616
  log.clear();
1598
- console.log(renderTree(tasks));
1617
+ stdout.write(`${renderTree(tasks)}
1618
+ `);
1599
1619
  return this;
1620
+ },
1621
+ clear() {
1622
+ log.clear();
1600
1623
  }
1601
1624
  };
1602
1625
  };
1603
1626
  function getFullName(task) {
1604
- return getNames(task).join(c.gray(" > "));
1627
+ return getNames(task).join(c.dim(" > "));
1605
1628
  }
1606
1629
  const spinnerFrames = process.platform === "win32" ? ["-", "\\", "|", "/"] : ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
1607
1630
  function elegantSpinner() {
@@ -1618,11 +1641,23 @@ class DefaultReporter {
1618
1641
  this.ctx = ctx;
1619
1642
  this.start = 0;
1620
1643
  this.end = 0;
1644
+ this.console = globalThis.console;
1645
+ this.isFirstWatchRun = true;
1621
1646
  const mode = ctx.config.watch ? c.yellow(" DEV ") : c.cyan(" RUN ");
1622
- console.log(`${c.inverse(c.bold(mode))} ${c.gray(this.ctx.config.root)}
1647
+ this.log(`${c.inverse(c.bold(mode))} ${c.gray(this.ctx.config.root)}
1623
1648
  `);
1624
1649
  this.start = performance.now();
1625
1650
  }
1651
+ log(...args) {
1652
+ if (this.ctx.config.silent)
1653
+ return;
1654
+ this.console.log(...args);
1655
+ }
1656
+ error(...args) {
1657
+ if (this.ctx.config.silent)
1658
+ return;
1659
+ this.console.error(...args);
1660
+ }
1626
1661
  relative(path) {
1627
1662
  return relative(this.ctx.config.root, path);
1628
1663
  }
@@ -1641,16 +1676,16 @@ class DefaultReporter {
1641
1676
  return;
1642
1677
  const task = this.ctx.state.idMap[pack[0]];
1643
1678
  if (task.type === "test" && ((_a = task.result) == null ? void 0 : _a.state) && ((_b = task.result) == null ? void 0 : _b.state) !== "run") {
1644
- console.log(` ${getStateSymbol(task)} ${getFullName(task)}`);
1679
+ this.log(` ${getStateSymbol(task)} ${getFullName(task)}`);
1645
1680
  if (task.result.state === "fail")
1646
- console.log(c.red(` ${F_RIGHT} ${(_c = task.result.error) == null ? void 0 : _c.message}`));
1681
+ this.log(c.red(` ${F_RIGHT} ${(_c = task.result.error) == null ? void 0 : _c.message}`));
1647
1682
  }
1648
1683
  }
1649
1684
  async onFinished(files = this.ctx.state.getFiles()) {
1650
1685
  var _a, _b;
1651
1686
  this.end = performance.now();
1652
1687
  await this.stopListRender();
1653
- console.log();
1688
+ this.log();
1654
1689
  const suites = getSuites(files);
1655
1690
  const tests = getTests(files);
1656
1691
  const failedSuites = suites.filter((i) => {
@@ -1661,24 +1696,27 @@ class DefaultReporter {
1661
1696
  var _a2;
1662
1697
  return ((_a2 = i.result) == null ? void 0 : _a2.state) === "fail";
1663
1698
  });
1699
+ const failedTotal = failedSuites.length + failedTests.length;
1700
+ let current = 1;
1701
+ const errorDivider = () => this.error(`${c.red(c.dim(divider(`[${current++}/${failedTotal}]`, void 0, 1)))}
1702
+ `);
1664
1703
  if (failedSuites.length) {
1665
- console.error(c.bold(c.red(`
1666
- Failed to run ${failedSuites.length} suites:`)));
1704
+ this.error(c.red(divider(c.bold(c.inverse(` Failed Suites ${failedSuites.length} `)))));
1705
+ this.error();
1667
1706
  for (const suite of failedSuites) {
1668
- console.error(c.red(`
1707
+ this.error(c.red(`
1669
1708
  - ${getFullName(suite)}`));
1670
1709
  await printError((_a = suite.result) == null ? void 0 : _a.error);
1671
- console.log();
1710
+ errorDivider();
1672
1711
  }
1673
1712
  }
1674
1713
  if (failedTests.length) {
1675
- console.error(c.bold(c.red(`
1676
- Failed Tests (${failedTests.length})`)));
1714
+ this.error(c.red(divider(c.bold(c.inverse(` Failed Tests ${failedTests.length} `)))));
1715
+ this.error();
1677
1716
  for (const test of failedTests) {
1678
- console.error(`${c.red(`
1679
- ${c.inverse(" FAIL ")}`)} ${getFullName(test)}`);
1717
+ this.error(`${c.red(c.bold(c.inverse(" FAIL ")))} ${getFullName(test)}`);
1680
1718
  await printError((_b = test.result) == null ? void 0 : _b.error);
1681
- console.log();
1719
+ errorDivider();
1682
1720
  }
1683
1721
  }
1684
1722
  const executionTime = this.end - this.start;
@@ -1694,17 +1732,17 @@ ${c.inverse(" FAIL ")}`)} ${getFullName(test)}`);
1694
1732
  };
1695
1733
  const snapshotOutput = renderSnapshotSummary(this.ctx.config.root, this.ctx.snapshot.summary);
1696
1734
  if (snapshotOutput.length) {
1697
- console.log(snapshotOutput.map((t, i) => i === 0 ? `${padTitle("Snapshots")} ${t}` : `${padTitle("")} ${t}`).join("\n"));
1735
+ this.log(snapshotOutput.map((t, i) => i === 0 ? `${padTitle("Snapshots")} ${t}` : `${padTitle("")} ${t}`).join("\n"));
1698
1736
  if (snapshotOutput.length > 1)
1699
- console.log();
1737
+ this.log();
1700
1738
  }
1701
- console.log(padTitle("Test Files"), getStateString(files));
1702
- console.log(padTitle("Tests"), getStateString(tests));
1739
+ this.log(padTitle("Test Files"), getStateString(files));
1740
+ this.log(padTitle("Tests"), getStateString(tests));
1703
1741
  if (this.watchFilters)
1704
- console.log(padTitle("Time"), time(threadTime));
1742
+ this.log(padTitle("Time"), time(threadTime));
1705
1743
  else
1706
- console.log(padTitle("Time"), time(executionTime) + c.gray(` (in thread ${time(threadTime)}, ${(executionTime / threadTime * 100).toFixed(2)}%)`));
1707
- console.log();
1744
+ this.log(padTitle("Time"), time(executionTime) + c.gray(` (in thread ${time(threadTime)}, ${(executionTime / threadTime * 100).toFixed(2)}%)`));
1745
+ this.log();
1708
1746
  }
1709
1747
  async onWatcherStart() {
1710
1748
  await this.stopListRender();
@@ -1713,24 +1751,36 @@ ${c.inverse(" FAIL ")}`)} ${getFullName(test)}`);
1713
1751
  return ((_a = i.result) == null ? void 0 : _a.state) === "fail";
1714
1752
  });
1715
1753
  if (failed.length)
1716
- console.log(`
1754
+ this.log(`
1717
1755
  ${c.bold(c.inverse(c.red(" FAIL ")))}${c.red(` ${failed.length} tests failed. Watching for file changes...`)}`);
1718
1756
  else
1719
- console.log(`
1757
+ this.log(`
1720
1758
  ${c.bold(c.inverse(c.green(" PASS ")))}${c.green(" Waiting for file changes...")}`);
1759
+ if (this.isFirstWatchRun) {
1760
+ this.isFirstWatchRun = false;
1761
+ this.log(c.gray("press any key to exit..."));
1762
+ }
1721
1763
  }
1722
1764
  async onWatcherRerun(files, trigger) {
1723
1765
  await this.stopListRender();
1724
1766
  this.watchFilters = files;
1725
- console.clear();
1726
- console.log(c.blue("Re-running tests...") + c.dim(` [ ${this.relative(trigger)} ]
1767
+ this.console.clear();
1768
+ this.log(c.blue("Re-running tests...") + c.dim(` [ ${this.relative(trigger)} ]
1727
1769
  `));
1728
1770
  }
1729
1771
  async stopListRender() {
1730
1772
  var _a;
1731
1773
  (_a = this.renderer) == null ? void 0 : _a.stop();
1732
1774
  this.renderer = void 0;
1733
- await new Promise((resolve) => setTimeout(resolve, 100));
1775
+ await new Promise((resolve) => setTimeout(resolve, 10));
1776
+ }
1777
+ onUserConsoleLog(log) {
1778
+ var _a;
1779
+ (_a = this.renderer) == null ? void 0 : _a.clear();
1780
+ const task = log.taskId ? this.ctx.state.idMap[log.taskId] : void 0;
1781
+ this.log(c.gray(log.type + c.dim(` | ${task ? getFullName(task) : "unknown test"}`)));
1782
+ process[log.type].write(`${log.content}
1783
+ `);
1734
1784
  }
1735
1785
  }
1736
1786
 
@@ -2126,6 +2176,7 @@ async function initViteServer(options = {}) {
2126
2176
  resolved.maxThreads = parseInt(process.env.VITEST_MAX_THREADS);
2127
2177
  if (process.env.VITEST_MIN_THREADS)
2128
2178
  resolved.minThreads = parseInt(process.env.VITEST_MIN_THREADS);
2179
+ resolved.setupFiles = Array.from(resolved.setupFiles || []).map((i) => resolve(root, i));
2129
2180
  return {
2130
2181
  server,
2131
2182
  config: resolved
@@ -2172,7 +2223,8 @@ function createFakePool(ctx) {
2172
2223
  }
2173
2224
  function createWorkerPool(ctx) {
2174
2225
  const options = {
2175
- filename: workerPath
2226
+ filename: workerPath,
2227
+ useAtomics: false
2176
2228
  };
2177
2229
  if (ctx.config.maxThreads != null)
2178
2230
  options.maxThreads = ctx.config.maxThreads;
@@ -2203,7 +2255,7 @@ function createChannel(ctx) {
2203
2255
  const port = channel.port2;
2204
2256
  const workerPort = channel.port1;
2205
2257
  port.on("message", async ({ id, method, args = [] }) => {
2206
- var _a, _b, _c, _d;
2258
+ var _a, _b, _c, _d, _e, _f;
2207
2259
  async function send(fn) {
2208
2260
  try {
2209
2261
  port.postMessage({ id, result: await fn() });
@@ -2224,6 +2276,9 @@ function createChannel(ctx) {
2224
2276
  ctx.state.updateTasks([args[0]]);
2225
2277
  (_d = (_c = ctx.reporter).onTaskUpdate) == null ? void 0 : _d.call(_c, args[0]);
2226
2278
  return;
2279
+ case "log":
2280
+ (_f = (_e = ctx.reporter).onUserConsoleLog) == null ? void 0 : _f.call(_e, args[0]);
2281
+ return;
2227
2282
  }
2228
2283
  console.error("Unhandled message", method, args);
2229
2284
  });
@@ -2255,6 +2310,7 @@ async function startWatcher(ctx, pool) {
2255
2310
  let timer;
2256
2311
  const changedTests = new Set();
2257
2312
  const seen = new Set();
2313
+ let isFirstRun = true;
2258
2314
  let promise;
2259
2315
  server.watcher.on("change", (id) => {
2260
2316
  id = slash(id);
@@ -2286,6 +2342,7 @@ async function startWatcher(ctx, pool) {
2286
2342
  seen.clear();
2287
2343
  return;
2288
2344
  }
2345
+ isFirstRun = false;
2289
2346
  ctx.state.getFiles().forEach((file) => {
2290
2347
  var _a2;
2291
2348
  if (((_a2 = file.result) == null ? void 0 : _a2.state) === "fail")
@@ -2295,7 +2352,9 @@ async function startWatcher(ctx, pool) {
2295
2352
  const tests = Array.from(changedTests);
2296
2353
  changedTests.clear();
2297
2354
  seen.clear();
2298
- promise = start(tests, id, invalidates);
2355
+ promise = start(tests, id, invalidates).then(() => {
2356
+ promise = void 0;
2357
+ });
2299
2358
  await promise;
2300
2359
  }, WATCHER_DEBOUNCE);
2301
2360
  }
@@ -2306,8 +2365,18 @@ async function startWatcher(ctx, pool) {
2306
2365
  await ((_b = reporter.onFinished) == null ? void 0 : _b.call(reporter, ctx.state.getFiles(tests)));
2307
2366
  await ((_c = reporter.onWatcherStart) == null ? void 0 : _c.call(reporter));
2308
2367
  }
2309
- if (process.stdin.isTTY)
2310
- listenToKeybard();
2368
+ if (process.stdin.isTTY) {
2369
+ readline.emitKeypressEvents(process.stdin);
2370
+ process.stdin.setRawMode(true);
2371
+ process.stdin.on("keypress", (str) => {
2372
+ if (str === "" || str === "")
2373
+ process.exit();
2374
+ if (promise)
2375
+ return;
2376
+ if (isFirstRun)
2377
+ process.exit();
2378
+ });
2379
+ }
2311
2380
  await new Promise(() => {
2312
2381
  });
2313
2382
  }
@@ -2328,16 +2397,6 @@ function getAffectedTests(ctx, id, set = new Set(), seen = new Set()) {
2328
2397
  }
2329
2398
  return set;
2330
2399
  }
2331
- function listenToKeybard() {
2332
- readline.emitKeypressEvents(process.stdin);
2333
- process.stdin.setRawMode(true);
2334
- process.stdin.on("keypress", (str, key) => {
2335
- if (str === "" || str === "")
2336
- process.exit();
2337
- if (str === "\r")
2338
- process.exit();
2339
- });
2340
- }
2341
2400
 
2342
2401
  async function start(ctx) {
2343
2402
  var _a, _b;
@@ -2414,7 +2473,7 @@ var __spreadValues = (a, b) => {
2414
2473
  };
2415
2474
  var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
2416
2475
  const cli = cac("vitest");
2417
- cli.version(version).option("-r, --root <path>", "root path").option("-c, --config <path>", "path to config file").option("-u, --update", "update snapshot").option("--global", "inject apis globally").option("--dom", "mock browser api with happy-dom").option("--environment <env>", "runner environment", {
2476
+ cli.version(version).option("-r, --root <path>", "root path").option("-c, --config <path>", "path to config file").option("-u, --update", "update snapshot").option("-w, --watch", "watch mode").option("--threads", "enabled threads", { default: true }).option("--silent", "silent").option("--global", "inject apis globally").option("--dom", "mock browser api with happy-dom").option("--environment <env>", "runner environment", {
2418
2477
  default: "node"
2419
2478
  }).help();
2420
2479
  cli.command("run [...filters]").action(run);
@@ -2423,20 +2482,25 @@ cli.command("dev [...filters]").action(dev);
2423
2482
  cli.command("[...filters]").action(dev);
2424
2483
  cli.parse();
2425
2484
  async function dev(cliFilters, argv) {
2426
- argv.watch = !process.env.CI && !process.env.NODE_V8_COVERAGE;
2485
+ if (argv.watch == null)
2486
+ argv.watch = !process.env.CI && !process.env.NODE_V8_COVERAGE;
2427
2487
  await run(cliFilters, argv);
2428
2488
  }
2429
2489
  async function run(cliFilters, argv) {
2430
2490
  process.env.VITEST = "true";
2431
- console.log(c.magenta(c.bold("\nVitest is in closed beta exclusively for Sponsors")));
2432
- console.log(c.yellow("Learn more at https://vitest.dev\n"));
2491
+ process.env.NODE_ENV = "test";
2492
+ if (!argv.silent) {
2493
+ console.log(c.magenta(c.bold("\nVitest is in closed beta exclusively for Sponsors")));
2494
+ console.log(c.yellow("Learn more at https://vitest.dev\n"));
2495
+ }
2433
2496
  const { config, server } = await initViteServer(__spreadProps(__spreadValues({}, argv), { cliFilters }));
2434
2497
  const ctx = process.__vitest__ = {
2435
2498
  server,
2436
2499
  config,
2437
2500
  state: new StateManager(),
2438
2501
  snapshot: new SnapshotManager(config),
2439
- reporter: config.reporter
2502
+ reporter: config.reporter,
2503
+ console: globalThis.console
2440
2504
  };
2441
2505
  ctx.reporter = ctx.reporter || new DefaultReporter(ctx);
2442
2506
  try {