vitest 0.0.71 → 0.0.75
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 +10 -276
- package/dist/cli.js +113 -50
- package/dist/entry.js +55 -6
- package/dist/{error-fb6ff2e6.js → error-eb493046.js} +39 -23
- package/dist/index.d.ts +29 -2
- package/dist/worker.js +14 -0
- package/package.json +3 -2
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
|
-
|
|
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
|
|
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-eb493046.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.
|
|
635
|
+
var version = "0.0.75";
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
1679
|
+
this.log(` ${getStateSymbol(task)} ${getFullName(task)}`);
|
|
1645
1680
|
if (task.result.state === "fail")
|
|
1646
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1666
|
-
|
|
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
|
-
|
|
1707
|
+
this.error(c.red(`
|
|
1669
1708
|
- ${getFullName(suite)}`));
|
|
1670
1709
|
await printError((_a = suite.result) == null ? void 0 : _a.error);
|
|
1671
|
-
|
|
1710
|
+
errorDivider();
|
|
1672
1711
|
}
|
|
1673
1712
|
}
|
|
1674
1713
|
if (failedTests.length) {
|
|
1675
|
-
|
|
1676
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1735
|
+
this.log(snapshotOutput.map((t, i) => i === 0 ? `${padTitle("Snapshots")} ${t}` : `${padTitle("")} ${t}`).join("\n"));
|
|
1698
1736
|
if (snapshotOutput.length > 1)
|
|
1699
|
-
|
|
1737
|
+
this.log();
|
|
1700
1738
|
}
|
|
1701
|
-
|
|
1702
|
-
|
|
1739
|
+
this.log(padTitle("Test Files"), getStateString(files));
|
|
1740
|
+
this.log(padTitle("Tests"), getStateString(tests));
|
|
1703
1741
|
if (this.watchFilters)
|
|
1704
|
-
|
|
1742
|
+
this.log(padTitle("Time"), time(threadTime));
|
|
1705
1743
|
else
|
|
1706
|
-
|
|
1707
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
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
|
|
|
@@ -2172,7 +2222,8 @@ function createFakePool(ctx) {
|
|
|
2172
2222
|
}
|
|
2173
2223
|
function createWorkerPool(ctx) {
|
|
2174
2224
|
const options = {
|
|
2175
|
-
filename: workerPath
|
|
2225
|
+
filename: workerPath,
|
|
2226
|
+
useAtomics: false
|
|
2176
2227
|
};
|
|
2177
2228
|
if (ctx.config.maxThreads != null)
|
|
2178
2229
|
options.maxThreads = ctx.config.maxThreads;
|
|
@@ -2203,7 +2254,7 @@ function createChannel(ctx) {
|
|
|
2203
2254
|
const port = channel.port2;
|
|
2204
2255
|
const workerPort = channel.port1;
|
|
2205
2256
|
port.on("message", async ({ id, method, args = [] }) => {
|
|
2206
|
-
var _a, _b, _c, _d;
|
|
2257
|
+
var _a, _b, _c, _d, _e, _f;
|
|
2207
2258
|
async function send(fn) {
|
|
2208
2259
|
try {
|
|
2209
2260
|
port.postMessage({ id, result: await fn() });
|
|
@@ -2224,6 +2275,9 @@ function createChannel(ctx) {
|
|
|
2224
2275
|
ctx.state.updateTasks([args[0]]);
|
|
2225
2276
|
(_d = (_c = ctx.reporter).onTaskUpdate) == null ? void 0 : _d.call(_c, args[0]);
|
|
2226
2277
|
return;
|
|
2278
|
+
case "log":
|
|
2279
|
+
(_f = (_e = ctx.reporter).onUserConsoleLog) == null ? void 0 : _f.call(_e, args[0]);
|
|
2280
|
+
return;
|
|
2227
2281
|
}
|
|
2228
2282
|
console.error("Unhandled message", method, args);
|
|
2229
2283
|
});
|
|
@@ -2255,6 +2309,7 @@ async function startWatcher(ctx, pool) {
|
|
|
2255
2309
|
let timer;
|
|
2256
2310
|
const changedTests = new Set();
|
|
2257
2311
|
const seen = new Set();
|
|
2312
|
+
let isFirstRun = true;
|
|
2258
2313
|
let promise;
|
|
2259
2314
|
server.watcher.on("change", (id) => {
|
|
2260
2315
|
id = slash(id);
|
|
@@ -2286,6 +2341,7 @@ async function startWatcher(ctx, pool) {
|
|
|
2286
2341
|
seen.clear();
|
|
2287
2342
|
return;
|
|
2288
2343
|
}
|
|
2344
|
+
isFirstRun = false;
|
|
2289
2345
|
ctx.state.getFiles().forEach((file) => {
|
|
2290
2346
|
var _a2;
|
|
2291
2347
|
if (((_a2 = file.result) == null ? void 0 : _a2.state) === "fail")
|
|
@@ -2295,7 +2351,9 @@ async function startWatcher(ctx, pool) {
|
|
|
2295
2351
|
const tests = Array.from(changedTests);
|
|
2296
2352
|
changedTests.clear();
|
|
2297
2353
|
seen.clear();
|
|
2298
|
-
promise = start(tests, id, invalidates)
|
|
2354
|
+
promise = start(tests, id, invalidates).then(() => {
|
|
2355
|
+
promise = void 0;
|
|
2356
|
+
});
|
|
2299
2357
|
await promise;
|
|
2300
2358
|
}, WATCHER_DEBOUNCE);
|
|
2301
2359
|
}
|
|
@@ -2306,8 +2364,18 @@ async function startWatcher(ctx, pool) {
|
|
|
2306
2364
|
await ((_b = reporter.onFinished) == null ? void 0 : _b.call(reporter, ctx.state.getFiles(tests)));
|
|
2307
2365
|
await ((_c = reporter.onWatcherStart) == null ? void 0 : _c.call(reporter));
|
|
2308
2366
|
}
|
|
2309
|
-
if (process.stdin.isTTY)
|
|
2310
|
-
|
|
2367
|
+
if (process.stdin.isTTY) {
|
|
2368
|
+
readline.emitKeypressEvents(process.stdin);
|
|
2369
|
+
process.stdin.setRawMode(true);
|
|
2370
|
+
process.stdin.on("keypress", (str) => {
|
|
2371
|
+
if (str === "" || str === "")
|
|
2372
|
+
process.exit();
|
|
2373
|
+
if (promise)
|
|
2374
|
+
return;
|
|
2375
|
+
if (isFirstRun)
|
|
2376
|
+
process.exit();
|
|
2377
|
+
});
|
|
2378
|
+
}
|
|
2311
2379
|
await new Promise(() => {
|
|
2312
2380
|
});
|
|
2313
2381
|
}
|
|
@@ -2328,16 +2396,6 @@ function getAffectedTests(ctx, id, set = new Set(), seen = new Set()) {
|
|
|
2328
2396
|
}
|
|
2329
2397
|
return set;
|
|
2330
2398
|
}
|
|
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
2399
|
|
|
2342
2400
|
async function start(ctx) {
|
|
2343
2401
|
var _a, _b;
|
|
@@ -2414,7 +2472,7 @@ var __spreadValues = (a, b) => {
|
|
|
2414
2472
|
};
|
|
2415
2473
|
var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
|
|
2416
2474
|
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", {
|
|
2475
|
+
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
2476
|
default: "node"
|
|
2419
2477
|
}).help();
|
|
2420
2478
|
cli.command("run [...filters]").action(run);
|
|
@@ -2423,20 +2481,25 @@ cli.command("dev [...filters]").action(dev);
|
|
|
2423
2481
|
cli.command("[...filters]").action(dev);
|
|
2424
2482
|
cli.parse();
|
|
2425
2483
|
async function dev(cliFilters, argv) {
|
|
2426
|
-
argv.watch
|
|
2484
|
+
if (argv.watch == null)
|
|
2485
|
+
argv.watch = !process.env.CI && !process.env.NODE_V8_COVERAGE;
|
|
2427
2486
|
await run(cliFilters, argv);
|
|
2428
2487
|
}
|
|
2429
2488
|
async function run(cliFilters, argv) {
|
|
2430
2489
|
process.env.VITEST = "true";
|
|
2431
|
-
|
|
2432
|
-
|
|
2490
|
+
process.env.NODE_ENV = "test";
|
|
2491
|
+
if (!argv.silent) {
|
|
2492
|
+
console.log(c.magenta(c.bold("\nVitest is in closed beta exclusively for Sponsors")));
|
|
2493
|
+
console.log(c.yellow("Learn more at https://vitest.dev\n"));
|
|
2494
|
+
}
|
|
2433
2495
|
const { config, server } = await initViteServer(__spreadProps(__spreadValues({}, argv), { cliFilters }));
|
|
2434
2496
|
const ctx = process.__vitest__ = {
|
|
2435
2497
|
server,
|
|
2436
2498
|
config,
|
|
2437
2499
|
state: new StateManager(),
|
|
2438
2500
|
snapshot: new SnapshotManager(config),
|
|
2439
|
-
reporter: config.reporter
|
|
2501
|
+
reporter: config.reporter,
|
|
2502
|
+
console: globalThis.console
|
|
2440
2503
|
};
|
|
2441
2504
|
ctx.reporter = ctx.reporter || new DefaultReporter(ctx);
|
|
2442
2505
|
try {
|
package/dist/entry.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { Console } from 'console';
|
|
2
|
+
import { Writable } from 'stream';
|
|
1
3
|
import { importModule } from 'local-pkg';
|
|
2
4
|
import chai, { expect, util } from 'chai';
|
|
3
5
|
import SinonChai from 'sinon-chai';
|
|
@@ -5,7 +7,7 @@ import Subset from 'chai-subset';
|
|
|
5
7
|
import path, { basename } from 'path';
|
|
6
8
|
import { g as getNames, i as interpretOnlyMode, p as partitionSuiteChildren, c as hasTests, h as hasFailed } from './utils-9dcc4050.js';
|
|
7
9
|
import fs from 'fs';
|
|
8
|
-
import { d as c$1,
|
|
10
|
+
import { d as c$1, m as generateDiff } from './error-eb493046.js';
|
|
9
11
|
import { performance } from 'perf_hooks';
|
|
10
12
|
import { j as setHooks, c as createSuiteHooks, h as clearContext, d as defaultSuite, k as context, l as getHooks, m as getFn } from './suite-819c135e.js';
|
|
11
13
|
import { n as nanoid } from './index-6427e0f2.js';
|
|
@@ -2829,7 +2831,7 @@ class SnapshotState {
|
|
|
2829
2831
|
this._uncheckedKeys.delete(key);
|
|
2830
2832
|
const receivedSerialized = addExtraLineBreaks(serialize(received, void 0, this._snapshotFormat));
|
|
2831
2833
|
const expected = isInline ? inlineSnapshot : this._snapshotData[key];
|
|
2832
|
-
const pass = expected === receivedSerialized;
|
|
2834
|
+
const pass = (expected == null ? void 0 : expected.trim()) === (receivedSerialized == null ? void 0 : receivedSerialized.trim());
|
|
2833
2835
|
const hasSnapshot = expected !== void 0;
|
|
2834
2836
|
const snapshotIsPersisted = isInline || fs.existsSync(this._snapshotPath);
|
|
2835
2837
|
if (pass && !isInline) {
|
|
@@ -2907,13 +2909,15 @@ class SnapshotClient {
|
|
|
2907
2909
|
clearTest() {
|
|
2908
2910
|
this.test = void 0;
|
|
2909
2911
|
}
|
|
2910
|
-
assert(received, message) {
|
|
2912
|
+
assert(received, message, inlineSnapshot) {
|
|
2911
2913
|
if (!this.test)
|
|
2912
|
-
throw new Error("Snapshot
|
|
2914
|
+
throw new Error("Snapshot cannot be used outside of test");
|
|
2915
|
+
const testName = getNames(this.test).slice(1).join(" > ");
|
|
2913
2916
|
const { actual, expected, key, pass } = this.snapshotState.match({
|
|
2914
|
-
testName
|
|
2917
|
+
testName,
|
|
2915
2918
|
received,
|
|
2916
|
-
isInline:
|
|
2919
|
+
isInline: !!inlineSnapshot,
|
|
2920
|
+
inlineSnapshot: inlineSnapshot == null ? void 0 : inlineSnapshot.trim()
|
|
2917
2921
|
});
|
|
2918
2922
|
if (!pass) {
|
|
2919
2923
|
expect(actual.trim()).equals(expected ? expected.trim() : "", message || `Snapshot name: \`${key}\``);
|
|
@@ -2968,6 +2972,10 @@ function SnapshotPlugin() {
|
|
|
2968
2972
|
getSnapshotClient().assert(expected, message);
|
|
2969
2973
|
});
|
|
2970
2974
|
}
|
|
2975
|
+
utils.addMethod(chai.Assertion.prototype, "toMatchInlineSnapshot", function(inlineSnapshot, message) {
|
|
2976
|
+
const expected = utils.flag(this, "object");
|
|
2977
|
+
getSnapshotClient().assert(expected, message, inlineSnapshot);
|
|
2978
|
+
});
|
|
2971
2979
|
};
|
|
2972
2980
|
}
|
|
2973
2981
|
|
|
@@ -3473,6 +3481,9 @@ function JestChaiExpect() {
|
|
|
3473
3481
|
def("toHaveLength", function(length) {
|
|
3474
3482
|
return this.have.length(length);
|
|
3475
3483
|
});
|
|
3484
|
+
def("toHaveProperty", function(...args) {
|
|
3485
|
+
return this.have.deep.nested.property(...args);
|
|
3486
|
+
});
|
|
3476
3487
|
def("toBeCloseTo", function(number, numDigits = 2) {
|
|
3477
3488
|
utils.expectTypes(this, ["number"]);
|
|
3478
3489
|
return this.closeTo(number, numDigits);
|
|
@@ -3581,11 +3592,47 @@ async function setupChai() {
|
|
|
3581
3592
|
installed = true;
|
|
3582
3593
|
}
|
|
3583
3594
|
|
|
3595
|
+
let globalSetup = false;
|
|
3584
3596
|
async function setupGlobalEnv(config) {
|
|
3597
|
+
if (globalSetup)
|
|
3598
|
+
return;
|
|
3599
|
+
globalSetup = true;
|
|
3600
|
+
setupConsoleLogSpy();
|
|
3585
3601
|
await setupChai();
|
|
3586
3602
|
if (config.global)
|
|
3587
3603
|
(await import('./global-e40b54d6.js')).registerApiGlobally();
|
|
3588
3604
|
}
|
|
3605
|
+
function setupConsoleLogSpy() {
|
|
3606
|
+
const stdout = new Writable({
|
|
3607
|
+
write(data, encoding, callback) {
|
|
3608
|
+
var _a;
|
|
3609
|
+
send("log", {
|
|
3610
|
+
type: "stdout",
|
|
3611
|
+
content: String(data),
|
|
3612
|
+
taskId: (_a = process.__vitest_worker__.current) == null ? void 0 : _a.id
|
|
3613
|
+
});
|
|
3614
|
+
callback();
|
|
3615
|
+
}
|
|
3616
|
+
});
|
|
3617
|
+
const stderr = new Writable({
|
|
3618
|
+
write(data, encoding, callback) {
|
|
3619
|
+
var _a;
|
|
3620
|
+
send("log", {
|
|
3621
|
+
type: "stderr",
|
|
3622
|
+
content: String(data),
|
|
3623
|
+
taskId: (_a = process.__vitest_worker__.current) == null ? void 0 : _a.id
|
|
3624
|
+
});
|
|
3625
|
+
callback();
|
|
3626
|
+
}
|
|
3627
|
+
});
|
|
3628
|
+
const newConsole = new Console({
|
|
3629
|
+
stdout,
|
|
3630
|
+
stderr,
|
|
3631
|
+
colorMode: true,
|
|
3632
|
+
groupIndentation: 2
|
|
3633
|
+
});
|
|
3634
|
+
globalThis.console = newConsole;
|
|
3635
|
+
}
|
|
3589
3636
|
async function withEnv(name, fn) {
|
|
3590
3637
|
const env = await environments[name].setup(globalThis);
|
|
3591
3638
|
try {
|
|
@@ -3668,6 +3715,7 @@ async function runTest(test) {
|
|
|
3668
3715
|
};
|
|
3669
3716
|
updateTask(test);
|
|
3670
3717
|
getSnapshotClient().setTest(test);
|
|
3718
|
+
process.__vitest_worker__.current = test;
|
|
3671
3719
|
try {
|
|
3672
3720
|
await callHook(test.suite, "beforeEach", [test, test.suite]);
|
|
3673
3721
|
await getFn(test)();
|
|
@@ -3684,6 +3732,7 @@ async function runTest(test) {
|
|
|
3684
3732
|
}
|
|
3685
3733
|
getSnapshotClient().clearTest();
|
|
3686
3734
|
test.result.end = performance.now();
|
|
3735
|
+
process.__vitest_worker__.current = void 0;
|
|
3687
3736
|
updateTask(test);
|
|
3688
3737
|
}
|
|
3689
3738
|
async function runSuite(suite) {
|
|
@@ -1246,15 +1246,15 @@ function cliTruncate(text, columns, options) {
|
|
|
1246
1246
|
|
|
1247
1247
|
const F_RIGHT = "\u2192";
|
|
1248
1248
|
const F_DOWN = "\u2193";
|
|
1249
|
-
const F_UP = "\u2191";
|
|
1250
1249
|
const F_DOWN_RIGHT = "\u21B3";
|
|
1251
1250
|
const F_POINTER = "\u276F";
|
|
1252
1251
|
const F_DOT = "\xB7";
|
|
1253
1252
|
const F_CHECK = "\u221A";
|
|
1254
1253
|
const F_CROSS = "\xD7";
|
|
1254
|
+
const F_LONG_DASH = "\u23AF";
|
|
1255
1255
|
|
|
1256
1256
|
async function printError(error) {
|
|
1257
|
-
const
|
|
1257
|
+
const ctx = process.__vitest__;
|
|
1258
1258
|
let e = error;
|
|
1259
1259
|
if (typeof error === "string") {
|
|
1260
1260
|
e = {
|
|
@@ -1263,17 +1263,18 @@ async function printError(error) {
|
|
|
1263
1263
|
};
|
|
1264
1264
|
}
|
|
1265
1265
|
let codeFramePrinted = false;
|
|
1266
|
-
const
|
|
1267
|
-
const
|
|
1266
|
+
const stackStr = e.stack || e.stackStr || "";
|
|
1267
|
+
const stacks = parseStack(stackStr);
|
|
1268
|
+
const nearest = stacks.find((stack) => !stack.file.includes("vitest/dist") && ctx.server.moduleGraph.getModuleById(stack.file));
|
|
1268
1269
|
if (nearest) {
|
|
1269
|
-
const
|
|
1270
|
-
const transformResult = mod == null ? void 0 : mod.ssrTransformResult;
|
|
1271
|
-
const pos = await getOriginalPos(transformResult == null ? void 0 : transformResult.map, nearest);
|
|
1270
|
+
const pos = await getSourcePos(ctx, nearest);
|
|
1272
1271
|
if (pos && existsSync(nearest.file)) {
|
|
1273
1272
|
const sourceCode = await promises.readFile(nearest.file, "utf-8");
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1273
|
+
printErrorMessage(e);
|
|
1274
|
+
await printStack(ctx, stacks, nearest, (s) => {
|
|
1275
|
+
if (s === nearest)
|
|
1276
|
+
ctx.console.log(c.yellow(generateCodeFrame(sourceCode, 4, pos)));
|
|
1277
|
+
});
|
|
1277
1278
|
codeFramePrinted = true;
|
|
1278
1279
|
}
|
|
1279
1280
|
}
|
|
@@ -1282,18 +1283,31 @@ async function printError(error) {
|
|
|
1282
1283
|
if (e.showDiff)
|
|
1283
1284
|
displayDiff(e.actual, e.expected);
|
|
1284
1285
|
}
|
|
1286
|
+
async function getSourcePos(ctx, nearest) {
|
|
1287
|
+
const mod = ctx.server.moduleGraph.getModuleById(nearest.file);
|
|
1288
|
+
const transformResult = mod == null ? void 0 : mod.ssrTransformResult;
|
|
1289
|
+
const pos = await getOriginalPos(transformResult == null ? void 0 : transformResult.map, nearest);
|
|
1290
|
+
return pos;
|
|
1291
|
+
}
|
|
1285
1292
|
function displayDiff(actual, expected) {
|
|
1286
1293
|
console.error(c.gray(generateDiff(stringify(actual), stringify(expected))));
|
|
1287
1294
|
}
|
|
1288
|
-
function
|
|
1295
|
+
function printErrorMessage(error) {
|
|
1289
1296
|
const errorName = error.name || error.nameStr || "Unknown Error";
|
|
1290
1297
|
console.error(c.red(`${c.bold(errorName)}: ${error.message}`));
|
|
1291
1298
|
}
|
|
1292
|
-
function
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1299
|
+
async function printStack(ctx, stack, highlight, onStack) {
|
|
1300
|
+
if (!stack.length)
|
|
1301
|
+
return;
|
|
1302
|
+
for (const frame of stack) {
|
|
1303
|
+
const pos = await getSourcePos(ctx, frame) || frame;
|
|
1304
|
+
const color = frame === highlight ? c.yellow : c.gray;
|
|
1305
|
+
ctx.console.log(color(` ${c.dim(F_POINTER)} ${[frame.method, c.dim(`${frame.file}:${pos.line}:${pos.column}`)].filter(Boolean).join(" ")}`));
|
|
1306
|
+
onStack == null ? void 0 : onStack(frame);
|
|
1307
|
+
if (frame.file in ctx.state.filesMap)
|
|
1308
|
+
break;
|
|
1309
|
+
}
|
|
1310
|
+
ctx.console.log();
|
|
1297
1311
|
}
|
|
1298
1312
|
function getOriginalPos(map, { line, column }) {
|
|
1299
1313
|
return new Promise((resolve) => {
|
|
@@ -1319,12 +1333,12 @@ function posToNumber(source, pos) {
|
|
|
1319
1333
|
start += lines[i].length + 1;
|
|
1320
1334
|
return start + column;
|
|
1321
1335
|
}
|
|
1322
|
-
function generateCodeFrame(source, start = 0, end, range = 2) {
|
|
1336
|
+
function generateCodeFrame(source, indent = 0, start = 0, end, range = 2) {
|
|
1323
1337
|
start = posToNumber(source, start);
|
|
1324
1338
|
end = end || start;
|
|
1325
1339
|
const lines = source.split(splitRE);
|
|
1326
1340
|
let count = 0;
|
|
1327
|
-
|
|
1341
|
+
let res = [];
|
|
1328
1342
|
function lineNo(no = "") {
|
|
1329
1343
|
return c.gray(`${String(no).padStart(3, " ")}| `);
|
|
1330
1344
|
}
|
|
@@ -1337,15 +1351,15 @@ function generateCodeFrame(source, start = 0, end, range = 2) {
|
|
|
1337
1351
|
const lineLength = lines[j].length;
|
|
1338
1352
|
if (lineLength > 200)
|
|
1339
1353
|
return "";
|
|
1340
|
-
res.push(lineNo(j + 1) + cliTruncate(lines[j], process.stdout.columns - 5));
|
|
1354
|
+
res.push(lineNo(j + 1) + cliTruncate(lines[j], process.stdout.columns - 5 - indent));
|
|
1341
1355
|
if (j === i) {
|
|
1342
1356
|
const pad = start - (count - lineLength);
|
|
1343
1357
|
const length = Math.max(1, end > count ? lineLength - pad : end - start);
|
|
1344
|
-
res.push(lineNo() + " ".repeat(pad) +
|
|
1358
|
+
res.push(lineNo() + " ".repeat(pad) + c.red("^".repeat(length)));
|
|
1345
1359
|
} else if (j > i) {
|
|
1346
1360
|
if (end > count) {
|
|
1347
1361
|
const length = Math.max(1, Math.min(end - count, lineLength));
|
|
1348
|
-
res.push(lineNo() +
|
|
1362
|
+
res.push(lineNo() + c.red("^".repeat(length)));
|
|
1349
1363
|
}
|
|
1350
1364
|
count += lineLength + 1;
|
|
1351
1365
|
}
|
|
@@ -1353,13 +1367,15 @@ function generateCodeFrame(source, start = 0, end, range = 2) {
|
|
|
1353
1367
|
break;
|
|
1354
1368
|
}
|
|
1355
1369
|
}
|
|
1370
|
+
if (indent)
|
|
1371
|
+
res = res.map((line) => " ".repeat(indent) + line);
|
|
1356
1372
|
return res.join("\n");
|
|
1357
1373
|
}
|
|
1358
1374
|
function stringify(obj) {
|
|
1359
1375
|
return String(obj);
|
|
1360
1376
|
}
|
|
1361
1377
|
const stackFnCallRE = /at (.*) \((.+):(\d+):(\d+)\)$/;
|
|
1362
|
-
const stackBarePathRE = /at ()(.+):(\d+):(\d+)$/;
|
|
1378
|
+
const stackBarePathRE = /at ?(.*) (.+):(\d+):(\d+)$/;
|
|
1363
1379
|
function parseStack(stack) {
|
|
1364
1380
|
const lines = stack.split("\n");
|
|
1365
1381
|
const stackFrames = lines.map((raw) => {
|
|
@@ -1412,4 +1428,4 @@ function notBlank(line) {
|
|
|
1412
1428
|
return typeof line !== "undefined" && line !== null;
|
|
1413
1429
|
}
|
|
1414
1430
|
|
|
1415
|
-
export { F_POINTER as F, ansiStyles as a, stripAnsi as b, sliceAnsi as c, c as d, F_DOWN as e,
|
|
1431
|
+
export { F_POINTER as F, ansiStyles as a, stripAnsi as b, sliceAnsi as c, c as d, F_DOWN as e, F_LONG_DASH as f, F_DOWN_RIGHT as g, F_DOT as h, F_CHECK as i, F_CROSS as j, cliTruncate as k, F_RIGHT as l, generateDiff as m, printError as p, stringWidth as s };
|
package/dist/index.d.ts
CHANGED
|
@@ -149,6 +149,12 @@ interface VitestContext {
|
|
|
149
149
|
state: StateManager;
|
|
150
150
|
snapshot: SnapshotManager;
|
|
151
151
|
reporter: Reporter;
|
|
152
|
+
console: Console;
|
|
153
|
+
}
|
|
154
|
+
interface UserConsoleLog {
|
|
155
|
+
content: string;
|
|
156
|
+
type: 'stdout' | 'stderr';
|
|
157
|
+
taskId?: string;
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
declare type RunMode = 'run' | 'skip' | 'only' | 'todo';
|
|
@@ -239,6 +245,7 @@ interface Reporter {
|
|
|
239
245
|
onTaskUpdate?: (pack: TaskResultPack) => Awaitable<void>;
|
|
240
246
|
onWatcherStart?: () => Awaitable<void>;
|
|
241
247
|
onWatcherRerun?: (files: string[], trigger: string) => Awaitable<void>;
|
|
248
|
+
onUserConsoleLog?: (log: UserConsoleLog) => Awaitable<void>;
|
|
242
249
|
}
|
|
243
250
|
|
|
244
251
|
declare type SnapshotData = Record<string, string>;
|
|
@@ -372,8 +379,25 @@ interface UserOptions {
|
|
|
372
379
|
*/
|
|
373
380
|
minThreads?: number;
|
|
374
381
|
interpretDefault?: boolean;
|
|
382
|
+
/**
|
|
383
|
+
* Default timeout of a test in milliseconds
|
|
384
|
+
*
|
|
385
|
+
* @default 5000
|
|
386
|
+
*/
|
|
375
387
|
testTimeout?: number;
|
|
388
|
+
/**
|
|
389
|
+
* Default timeout of a hook in milliseconds
|
|
390
|
+
*
|
|
391
|
+
* @default 5000
|
|
392
|
+
*/
|
|
376
393
|
hookTimeout?: number;
|
|
394
|
+
/**
|
|
395
|
+
* Silent mode
|
|
396
|
+
* TODO: implement this
|
|
397
|
+
*
|
|
398
|
+
* @default false
|
|
399
|
+
*/
|
|
400
|
+
silent?: boolean;
|
|
377
401
|
}
|
|
378
402
|
interface CliOptions extends UserOptions {
|
|
379
403
|
/**
|
|
@@ -407,8 +431,8 @@ interface WorkerContext {
|
|
|
407
431
|
invalidates?: string[];
|
|
408
432
|
}
|
|
409
433
|
interface RpcMap {
|
|
410
|
-
workerReady: [[], void];
|
|
411
434
|
fetch: [[id: string], TransformResult | null | undefined];
|
|
435
|
+
log: [[UserConsoleLog], void];
|
|
412
436
|
onCollected: [[files: File[]], void];
|
|
413
437
|
onFinished: [[], void];
|
|
414
438
|
onTaskUpdate: [[pack: TaskResultPack], void];
|
|
@@ -527,6 +551,7 @@ declare global {
|
|
|
527
551
|
config: ResolvedConfig;
|
|
528
552
|
rpc: RpcCall;
|
|
529
553
|
send: RpcSend;
|
|
554
|
+
current?: Test;
|
|
530
555
|
};
|
|
531
556
|
}
|
|
532
557
|
}
|
|
@@ -551,6 +576,7 @@ declare global {
|
|
|
551
576
|
}
|
|
552
577
|
interface Assertion {
|
|
553
578
|
toMatchSnapshot(message?: string): Assertion;
|
|
579
|
+
toMatchInlineSnapshot(snapshot?: string, message?: string): Assertion;
|
|
554
580
|
matchSnapshot(message?: string): Assertion;
|
|
555
581
|
toEqual(expected: any): void;
|
|
556
582
|
toStrictEqual(expected: any): void;
|
|
@@ -572,6 +598,7 @@ declare global {
|
|
|
572
598
|
toBeInstanceOf(c: any): void;
|
|
573
599
|
toBeCalledTimes(n: number): void;
|
|
574
600
|
toHaveLength(l: number): void;
|
|
601
|
+
toHaveProperty(p: string, value?: any): void;
|
|
575
602
|
toBeCloseTo(number: number, numDigits?: number): void;
|
|
576
603
|
toHaveBeenCalledTimes(n: number): void;
|
|
577
604
|
toHaveBeenCalledOnce(): void;
|
|
@@ -599,4 +626,4 @@ declare global {
|
|
|
599
626
|
}
|
|
600
627
|
}
|
|
601
628
|
|
|
602
|
-
export { Arrayable, Awaitable, CliOptions, ComputeMode, Environment, EnvironmentReturn, File, GlobalContext, HookListener, ModuleCache, Nullable, Reporter, ResolvedConfig, RpcCall, RpcMap, RpcPayload, RpcSend, RunMode, SnapshotData, SnapshotMatchOptions, SnapshotResult, SnapshotStateOptions, SnapshotSummary, SnapshotUpdateState, Suite, SuiteCollector, SuiteHooks, Task, TaskBase, TaskResult, TaskResultPack, TaskState, Test, TestCollector, TestFactory, TestFunction, UncheckedSnapshot, UserOptions, VitestContext, WorkerContext, afterAll, afterEach, beforeAll, beforeEach, clearContext, createSuiteHooks, defaultSuite, describe, it, mock, spy, stub, suite, test };
|
|
629
|
+
export { Arrayable, Awaitable, CliOptions, ComputeMode, Environment, EnvironmentReturn, File, GlobalContext, HookListener, ModuleCache, Nullable, Reporter, ResolvedConfig, RpcCall, RpcMap, RpcPayload, RpcSend, RunMode, SnapshotData, SnapshotMatchOptions, SnapshotResult, SnapshotStateOptions, SnapshotSummary, SnapshotUpdateState, Suite, SuiteCollector, SuiteHooks, Task, TaskBase, TaskResult, TaskResultPack, TaskState, Test, TestCollector, TestFactory, TestFunction, UncheckedSnapshot, UserConsoleLog, UserOptions, VitestContext, WorkerContext, afterAll, afterEach, beforeAll, beforeEach, clearContext, createSuiteHooks, defaultSuite, describe, it, mock, spy, stub, suite, test };
|
package/dist/worker.js
CHANGED
|
@@ -53,6 +53,18 @@ async function interpretedImport(path, interpretDefault) {
|
|
|
53
53
|
}
|
|
54
54
|
return mod;
|
|
55
55
|
}
|
|
56
|
+
let SOURCEMAPPING_URL = "sourceMa";
|
|
57
|
+
SOURCEMAPPING_URL += "ppingURL";
|
|
58
|
+
async function withInlineSourcemap(result) {
|
|
59
|
+
const { code, map } = result;
|
|
60
|
+
if (code.includes(`${SOURCEMAPPING_URL}=`))
|
|
61
|
+
return result;
|
|
62
|
+
if (map)
|
|
63
|
+
result.code = `${code}
|
|
64
|
+
|
|
65
|
+
//# ${SOURCEMAPPING_URL}=data:application/json;charset=utf-8;base64,${Buffer.from(JSON.stringify(map), "utf-8").toString("base64")}`;
|
|
66
|
+
return result;
|
|
67
|
+
}
|
|
56
68
|
async function executeInViteNode(options) {
|
|
57
69
|
const { moduleCache, root, files, fetch } = options;
|
|
58
70
|
const externaled = new Set(builtinModules);
|
|
@@ -79,6 +91,8 @@ ${[...callstack, dep].reverse().map((p) => `- ${p}`).join("\n")}`);
|
|
|
79
91
|
const result2 = await fetch(id);
|
|
80
92
|
if (!result2)
|
|
81
93
|
throw new Error(`failed to load ${id}`);
|
|
94
|
+
if (process.env.NODE_V8_COVERAGE)
|
|
95
|
+
withInlineSourcemap(result2);
|
|
82
96
|
const url = pathToFileURL(fsPath).href;
|
|
83
97
|
const exports = {};
|
|
84
98
|
setCache(fsPath, { transformResult: result2, exports });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.75",
|
|
4
4
|
"description": "A blazing fast unit test framework powered by Vite",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vite",
|
|
@@ -51,7 +51,8 @@
|
|
|
51
51
|
"test": "node bin/vitest.mjs -r test/core",
|
|
52
52
|
"test:all": "cross-env CI=true pnpm -r --stream --filter !vitest run test --",
|
|
53
53
|
"test:ci": "cross-env CI=true pnpm -r --stream --filter !vitest --filter !@vitest/test-fails run test --",
|
|
54
|
-
"typecheck": "tsc --noEmit && nr lint"
|
|
54
|
+
"typecheck": "tsc --noEmit && nr lint",
|
|
55
|
+
"ci": "ni && nr typecheck && nr lint && nr build && nr test:all"
|
|
55
56
|
},
|
|
56
57
|
"dependencies": {
|
|
57
58
|
"@types/chai": "^4.3.0",
|