vitest 0.0.29 → 0.0.33

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,17 +11,21 @@ A blazing fast unit test framework powered by Vite.
11
11
 
12
12
  > Vitest requires Vite v2.7.0 or above
13
13
 
14
+ [**Join the Discord!**](https://discord.com/invite/2zYZNngd7y)
15
+
14
16
  ## Features
15
17
 
16
18
  - [Vite](https://vitejs.dev/)'s config, transformers, resolvers, and plugins.
17
19
  - [Jest Snapshot](https://jestjs.io/docs/snapshot-testing)
18
- - [Chai](https://www.chaijs.com/) for assertions
19
- - [Sinon](https://sinonjs.org/) for mocking
20
- - [JSDOM](https://github.com/jsdom/jsdom) for DOM mocking
20
+ - [Chai](https://www.chaijs.com/) built-in for assertions, with [jest-expect](https://jestjs.io/docs/expect) compatible APIs.
21
+ - [Sinon](https://sinonjs.org/) built-in for mocking
22
+ - [JSDOM](https://github.com/jsdom/jsdom) built-in built-in for DOM mocking
23
+ - Components testing ([Vue example](./test/vue), [React example](./test/react))
21
24
  - Async suite / test, top level await
22
25
  - ESM friendly
23
- - Out-of-box TypeScript support
26
+ - Out-of-box TypeScript / JSX support
24
27
  - Suite and Test filtering (skip, only, todo)
28
+ - [Test coverage](#coverage)
25
29
 
26
30
  ```ts
27
31
  import { it, describe, expect, assert } from 'vitest'
@@ -46,6 +50,12 @@ describe('suite name', () => {
46
50
  $ npx vitest
47
51
  ```
48
52
 
53
+ ## Examples
54
+
55
+ - [Unit Testing](./test/core)
56
+ - [Vue Component Testing](./test/vue)
57
+ - [React Component Testing](./test/react)
58
+
49
59
  ## Configuration
50
60
 
51
61
  `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:
@@ -84,7 +94,7 @@ export default defineConfig({
84
94
 
85
95
  To get TypeScript working with the global APIs, add `vitest/global` to the `types` filed in your `tsconfig.json`
86
96
 
87
- ```json
97
+ ```jsonc
88
98
  // tsconfig.json
89
99
  {
90
100
  "compilerOptions": {
@@ -118,6 +128,31 @@ $ vitest -w
118
128
 
119
129
  Vitest smartly searches the module graph and only rerun the related tests (just like how HMR works in Vite!).
120
130
 
131
+ ## Coverage
132
+
133
+ Vitest works perfectly with [c8](https://github.com/bcoe/c8)
134
+
135
+ ```bash
136
+ $ c8 vitest
137
+ ```
138
+
139
+ ```json
140
+ {
141
+ "scripts": {
142
+ "test": "vitest",
143
+ "coverage": "c8 vitest"
144
+ }
145
+ }
146
+ ```
147
+
148
+ 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`.
149
+
150
+ ```bash
151
+ $ vitest --coverage
152
+ ```
153
+
154
+ For more configuration avaliable, please refer to [c8](https://github.com/bcoe/c8)'s documentation.
155
+
121
156
  ## Filtering
122
157
 
123
158
  ### CLI
@@ -194,19 +229,6 @@ describe('suite', () => {
194
229
  })
195
230
  ```
196
231
 
197
- ## TODO
198
-
199
- - [x] Reporter & Better output
200
- - [x] Task filter
201
- - [x] Mock
202
- - [x] Global Mode & Types
203
- - [ ] Parallel Executing
204
- - [x] CLI Help
205
- - [x] JSDom
206
- - [x] Watch
207
- - [ ] Source Map
208
- - [ ] Coverage
209
-
210
232
  ## Sponsors
211
233
 
212
234
  <p align="center">
package/bin/vitest.mjs CHANGED
@@ -1,4 +1,16 @@
1
1
  #!/usr/bin/env node
2
- 'use strict'
3
2
 
4
- import '../dist/node/cli.js'
3
+ import { fileURLToPath } from 'url'
4
+ import { resolve } from 'path'
5
+
6
+ const argv = process.argv.slice(2)
7
+ const filename = fileURLToPath(import.meta.url)
8
+ const entry = resolve(filename, '../../dist/node/cli.js')
9
+
10
+ if (argv.includes('--coverage')) {
11
+ process.argv.splice(2, 0, process.argv[0], entry)
12
+ await import('c8/bin/c8.js')
13
+ }
14
+ else {
15
+ await import('../dist/node/cli.js')
16
+ }
@@ -24,6 +24,7 @@ export declare class SnapshotManager {
24
24
  setTask(task: Task): void;
25
25
  setContext(context: Context): void;
26
26
  assert(received: unknown, message: string): void;
27
+ clear(): void;
27
28
  saveSnap(): void;
28
- report(): void;
29
+ report(): string[] | undefined;
29
30
  }
@@ -58,6 +58,9 @@ export class SnapshotManager {
58
58
  expect(actual.trim()).equals(expected ? expected.trim() : '', message || `Snapshot name: \`${key}\``);
59
59
  }
60
60
  }
61
+ clear() {
62
+ this.snapshotSummary = makeEmptySnapshotSummary(this.snapshotOptions);
63
+ }
61
64
  saveSnap() {
62
65
  if (!this.testFile || !this.snapshotState)
63
66
  return;
@@ -69,7 +72,6 @@ export class SnapshotManager {
69
72
  report() {
70
73
  const outputs = getSnapshotSummaryOutput(this.rootDir, this.snapshotSummary);
71
74
  if (outputs.length > 1)
72
- // eslint-disable-next-line no-console
73
- console.log(`\n${outputs.join('\n')}`);
75
+ return outputs;
74
76
  }
75
77
  }
package/dist/node/cli.js CHANGED
@@ -24,6 +24,7 @@ sade('vitest [filter]', true)
24
24
  const defaultInline = [
25
25
  'vue',
26
26
  '@vue',
27
+ 'diff',
27
28
  ];
28
29
  const __dirname = dirname(fileURLToPath(import.meta.url));
29
30
  const root = resolve(argv.root || process.cwd());
@@ -5,3 +5,8 @@ const inlineOptions = process.__vite_node__.server.config.test || {};
5
5
  const cliOptions = process.__vitest__.options || {};
6
6
  const options = Object.assign(Object.assign({}, cliOptions), inlineOptions);
7
7
  await run(options);
8
+ const timer = setTimeout(() => {
9
+ // TODO: warn user and maybe error out
10
+ process.exit();
11
+ }, 500);
12
+ timer.unref();
@@ -1,4 +1,4 @@
1
- import { InlineConfig, ViteDevServer } from 'vite';
1
+ import { InlineConfig, ViteDevServer, TransformResult } from 'vite';
2
2
  declare global {
3
3
  namespace NodeJS {
4
4
  interface Process {
@@ -6,6 +6,7 @@ declare global {
6
6
  server: ViteDevServer;
7
7
  watch?: boolean;
8
8
  moduleCache: Map<string, Promise<any>>;
9
+ modulesTransformResult: Map<string, TransformResult>;
9
10
  };
10
11
  }
11
12
  }
package/dist/node/node.js CHANGED
@@ -5,7 +5,8 @@ import vm from 'vm';
5
5
  import { createServer, mergeConfig } from 'vite';
6
6
  import c from 'picocolors';
7
7
  const { red, dim, yellow } = c;
8
- const __pendingModules__ = new Map();
8
+ const moduleCache = new Map();
9
+ const modulesTransformResult = new Map();
9
10
  export async function run(argv) {
10
11
  process.exitCode = 0;
11
12
  const root = argv.root || process.cwd();
@@ -22,7 +23,8 @@ export async function run(argv) {
22
23
  await server.pluginContainer.buildStart({});
23
24
  process.__vite_node__ = {
24
25
  server,
25
- moduleCache: __pendingModules__,
26
+ moduleCache,
27
+ modulesTransformResult,
26
28
  };
27
29
  try {
28
30
  await execute(files, server, argv);
@@ -104,6 +106,7 @@ async function execute(files, server, options) {
104
106
  const result = await transform(server, id);
105
107
  if (!result)
106
108
  throw new Error(`failed to load ${id}`);
109
+ modulesTransformResult.set(id, result);
107
110
  const url = pathToFileURL(fsPath);
108
111
  const exports = {};
109
112
  const context = {
@@ -130,10 +133,10 @@ async function execute(files, server, options) {
130
133
  const fsPath = toFilePath(id, server);
131
134
  if (options.shouldExternalize(fsPath, server))
132
135
  return import(fsPath);
133
- if (__pendingModules__.has(fsPath))
134
- return __pendingModules__.get(fsPath);
135
- __pendingModules__.set(fsPath, directRequest(id, fsPath, callstack));
136
- return await __pendingModules__.get(fsPath);
136
+ if (moduleCache.has(fsPath))
137
+ return moduleCache.get(fsPath);
138
+ moduleCache.set(fsPath, directRequest(id, fsPath, callstack));
139
+ return await moduleCache.get(fsPath);
137
140
  }
138
141
  function exportAll(exports, sourceModule) {
139
142
  // eslint-disable-next-line no-restricted-syntax
@@ -16,7 +16,7 @@ export declare class DefaultReporter implements Reporter {
16
16
  onStart(config: ResolvedConfig): void;
17
17
  onCollected(files: File[]): void;
18
18
  onTaskEnd(task: Task): void;
19
- onFinished(ctx: RunnerContext): Promise<void>;
19
+ onFinished(ctx: RunnerContext, files?: File[]): Promise<void>;
20
20
  onWatcherStart(ctx: RunnerContext): Promise<void>;
21
21
  onWatcherRerun(files: string[], trigger: string): Promise<void>;
22
22
  onSnapshotUpdate(): void;
@@ -3,6 +3,7 @@ import { performance } from 'perf_hooks';
3
3
  import { relative } from 'path';
4
4
  import c from 'picocolors';
5
5
  import Listr from 'listr';
6
+ import { printError } from './error';
6
7
  const CROSS = '✖ ';
7
8
  export class DefaultReporter {
8
9
  constructor() {
@@ -77,11 +78,16 @@ export class DefaultReporter {
77
78
  else
78
79
  (_b = this.taskMap.get(task)) === null || _b === void 0 ? void 0 : _b.resolve();
79
80
  }
80
- async onFinished(ctx) {
81
+ async onFinished(ctx, files = ctx.files) {
82
+ var _a;
81
83
  await this.listrPromise;
82
84
  this.end = performance.now();
83
85
  console.log();
84
- const { tasks, suites, files } = ctx;
86
+ const snapshot = ctx.snapshotManager.report();
87
+ if (snapshot)
88
+ console.log(snapshot.join('\n'));
89
+ const suites = files.flatMap(i => i.suites);
90
+ const tasks = suites.flatMap(i => i.tasks);
85
91
  const failedFiles = files.filter(i => i.error);
86
92
  const failedSuites = suites.filter(i => i.error);
87
93
  const runnable = tasks.filter(i => i.state === 'pass' || i.state === 'fail');
@@ -90,30 +96,30 @@ export class DefaultReporter {
90
96
  const skipped = tasks.filter(i => i.state === 'skip');
91
97
  const todo = tasks.filter(i => i.state === 'todo');
92
98
  if (failedFiles.length) {
93
- console.error(c.bold(`\nFailed to parse ${failedFiles.length} files:`));
94
- failedFiles.forEach((i) => {
95
- console.error(c.red(`\n- ${i.filepath}`));
96
- console.error(i.error || 'Unknown error');
99
+ console.error(c.red(c.bold(`\nFailed to parse ${failedFiles.length} files:`)));
100
+ for (const file of failedFiles)
101
+ console.error(c.red(`- ${file.filepath}`));
102
+ console.log();
103
+ for (const file of failedFiles) {
104
+ await printError(file.error);
97
105
  console.log();
98
- });
106
+ }
99
107
  }
100
108
  if (failedSuites.length) {
101
109
  console.error(c.bold(c.red(`\nFailed to run ${failedSuites.length} suites:`)));
102
- failedSuites.forEach((i) => {
103
- var _a;
104
- console.error(c.red(`\n- ${(_a = i.file) === null || _a === void 0 ? void 0 : _a.filepath} > ${i.name}`));
105
- console.error(i.error || 'Unknown error');
110
+ for (const suite of failedSuites) {
111
+ console.error(c.red(`\n- ${(_a = suite.file) === null || _a === void 0 ? void 0 : _a.filepath} > ${suite.name}`));
112
+ await printError(suite.error);
106
113
  console.log();
107
- });
114
+ }
108
115
  }
109
116
  if (failed.length) {
110
117
  console.error(c.bold(c.red(`\nFailed Tests (${failed.length})`)));
111
- failed.forEach((task) => {
112
- var _a;
113
- console.error(`\n${CROSS + c.inverse(c.red(' FAIL '))} ${[task.suite.name, task.name].filter(Boolean).join(' > ')} ${c.gray(c.dim(`${(_a = task.file) === null || _a === void 0 ? void 0 : _a.filepath}`))}`);
114
- console.error(task.error || 'Unknown error');
118
+ for (const task of failed) {
119
+ console.error(`${c.red(`\n${CROSS + c.inverse(' FAIL ')}`)} ${[task.suite.name, task.name].filter(Boolean).join(' > ')}`);
120
+ await printError(task.error);
115
121
  console.log();
116
- });
122
+ }
117
123
  }
118
124
  console.log(c.bold(c.green(`Passed ${passed.length} / ${runnable.length}`)));
119
125
  if (failed.length)
@@ -0,0 +1,9 @@
1
+ export declare function printError(error: unknown): Promise<void>;
2
+ interface Poisition {
3
+ line: number;
4
+ column: number;
5
+ }
6
+ export declare function posToNumber(source: string, pos: number | Poisition): number;
7
+ export declare function numberToPos(source: string, offset: number | Poisition): Poisition;
8
+ export declare function generateCodeFrame(source: string, start?: number | Poisition, end?: number, range?: number): string;
9
+ export {};
@@ -0,0 +1,182 @@
1
+ /* eslint-disable no-console */
2
+ import { promises as fs, existsSync } from 'fs';
3
+ import c from 'picocolors';
4
+ import * as diff from 'diff';
5
+ import { notNullish } from '@antfu/utils';
6
+ import { SourceMapConsumer } from 'source-map';
7
+ export async function printError(error) {
8
+ if (!(error instanceof Error)) {
9
+ console.error(error);
10
+ return;
11
+ }
12
+ const { modulesTransformResult } = process.__vite_node__;
13
+ const e = error;
14
+ let codeFramePrinted = false;
15
+ const stacks = parseStack(e.stack || '');
16
+ const nearest = stacks.find(stack => modulesTransformResult.has(stack.file));
17
+ if (nearest) {
18
+ const transformResult = modulesTransformResult.get(nearest.file);
19
+ const pos = await getOriginalPos(transformResult === null || transformResult === void 0 ? void 0 : transformResult.map, nearest);
20
+ if (pos && existsSync(nearest.file)) {
21
+ const sourceCode = await fs.readFile(nearest.file, 'utf-8');
22
+ console.error(`${c.red(`${c.bold(e.name)}: ${e.message}`)}`);
23
+ console.log(c.gray(`${nearest.file}:${pos.line}:${pos.column}`));
24
+ console.log(c.yellow(generateCodeFrame(sourceCode, pos)));
25
+ codeFramePrinted = true;
26
+ }
27
+ }
28
+ if (!codeFramePrinted)
29
+ console.error(e);
30
+ if (e.showDiff)
31
+ console.error(c.gray(generateDiff(stringify(e.actual), stringify(e.expected))));
32
+ }
33
+ function getOriginalPos(map, { line, column }) {
34
+ return new Promise((resolve) => {
35
+ if (!map)
36
+ return resolve(null);
37
+ SourceMapConsumer.with(map, null, (consumer) => {
38
+ const pos = consumer.originalPositionFor({ line, column });
39
+ if (pos.line != null && pos.column != null)
40
+ resolve(pos);
41
+ else
42
+ resolve(null);
43
+ });
44
+ });
45
+ }
46
+ const splitRE = /\r?\n/;
47
+ export function posToNumber(source, pos) {
48
+ if (typeof pos === 'number')
49
+ return pos;
50
+ const lines = source.split(splitRE);
51
+ const { line, column } = pos;
52
+ let start = 0;
53
+ for (let i = 0; i < line - 1; i++)
54
+ start += lines[i].length + 1;
55
+ return start + column;
56
+ }
57
+ export function numberToPos(source, offset) {
58
+ if (typeof offset !== 'number')
59
+ return offset;
60
+ if (offset > source.length) {
61
+ throw new Error(`offset is longer than source length! offset ${offset} > length ${source.length}`);
62
+ }
63
+ const lines = source.split(splitRE);
64
+ let counted = 0;
65
+ let line = 0;
66
+ let column = 0;
67
+ for (; line < lines.length; line++) {
68
+ const lineLength = lines[line].length + 1;
69
+ if (counted + lineLength >= offset) {
70
+ column = offset - counted + 1;
71
+ break;
72
+ }
73
+ counted += lineLength;
74
+ }
75
+ return { line: line + 1, column };
76
+ }
77
+ export function generateCodeFrame(source, start = 0, end, range = 2) {
78
+ start = posToNumber(source, start);
79
+ end = end || start;
80
+ const lines = source.split(splitRE);
81
+ let count = 0;
82
+ const res = [];
83
+ for (let i = 0; i < lines.length; i++) {
84
+ count += lines[i].length + 1;
85
+ if (count >= start) {
86
+ for (let j = i - range; j <= i + range || end > count; j++) {
87
+ if (j < 0 || j >= lines.length)
88
+ continue;
89
+ const line = j + 1;
90
+ res.push(`${c.gray(`${line}${' '.repeat(Math.max(3 - String(line).length, 0))}|`)} ${lines[j]}`);
91
+ const lineLength = lines[j].length;
92
+ if (j === i) {
93
+ // push underline
94
+ const pad = start - (count - lineLength) + 1;
95
+ const length = Math.max(1, end > count ? lineLength - pad : end - start);
96
+ res.push(`${c.gray(' |')} ${' '.repeat(pad)}${'^'.repeat(length)}`);
97
+ }
98
+ else if (j > i) {
99
+ if (end > count) {
100
+ const length = Math.max(Math.min(end - count, lineLength), 1);
101
+ res.push(`${c.gray(' |')} ${'^'.repeat(length)}`);
102
+ }
103
+ count += lineLength + 1;
104
+ }
105
+ }
106
+ break;
107
+ }
108
+ }
109
+ return res.join('\n');
110
+ }
111
+ function stringify(obj) {
112
+ // TODO: handle more types
113
+ return String(obj);
114
+ }
115
+ const stackFnCallRE = /at (.*) \((.+):(\d+):(\d+)\)$/;
116
+ const stackBarePathRE = /at ()(.+):(\d+):(\d+)$/;
117
+ function parseStack(stack) {
118
+ const lines = stack.split('\n');
119
+ const stackFrames = lines.map((raw) => {
120
+ const line = raw.trim();
121
+ const match = line.match(stackFnCallRE) || line.match(stackBarePathRE);
122
+ if (!match)
123
+ return null;
124
+ let file = match[2];
125
+ if (file.startsWith('file://'))
126
+ file = file.slice(7);
127
+ return {
128
+ method: match[1],
129
+ file: match[2],
130
+ line: parseInt(match[3]),
131
+ column: parseInt(match[4]),
132
+ };
133
+ });
134
+ return stackFrames.filter(notNullish);
135
+ }
136
+ /**
137
+ * Returns a diff between 2 strings with coloured ANSI output.
138
+ *
139
+ * @description
140
+ * The diff will be either inline or unified dependent on the value
141
+ * of `Base.inlineDiff`.
142
+ *
143
+ * @param {string} actual
144
+ * @param {string} expected
145
+ * @return {string} Diff
146
+ */
147
+ function generateDiff(actual, expected) {
148
+ const diffSize = 2048;
149
+ if (actual.length > diffSize)
150
+ actual = `${actual.substring(0, diffSize)} ... Lines skipped`;
151
+ if (expected.length > diffSize)
152
+ expected = `${expected.substring(0, diffSize)} ... Lines skipped`;
153
+ return unifiedDiff(actual, expected);
154
+ }
155
+ /**
156
+ * Returns unified diff between two strings with coloured ANSI output.
157
+ *
158
+ * @private
159
+ * @param {String} actual
160
+ * @param {String} expected
161
+ * @return {string} The diff.
162
+ */
163
+ function unifiedDiff(actual, expected) {
164
+ const indent = ' ';
165
+ function cleanUp(line) {
166
+ if (line[0] === '+')
167
+ return indent + c.green(`${line[0]} ${line.slice(1)}`);
168
+ if (line[0] === '-')
169
+ return indent + c.red(`${line[0]} ${line.slice(1)}`);
170
+ if (line.match(/@@/))
171
+ return '--';
172
+ if (line.match(/\\ No newline/))
173
+ return null;
174
+ return indent + line;
175
+ }
176
+ const msg = diff.createPatch('string', actual, expected);
177
+ const lines = msg.split('\n').splice(5);
178
+ return (`\n${indent}${c.red('- actual')}\n${indent}${c.green('+ expected')}\n\n${lines.map(cleanUp).filter(notBlank).join('\n')}`);
179
+ }
180
+ function notBlank(line) {
181
+ return typeof line !== 'undefined' && line !== null;
182
+ }
package/dist/run/index.js CHANGED
@@ -163,6 +163,7 @@ export async function run(config) {
163
163
  (await import('../integrations/jsdom')).setupJSDOM(globalThis);
164
164
  await ((_b = reporter.onStart) === null || _b === void 0 ? void 0 : _b.call(reporter, config));
165
165
  const filesMap = await collectFiles(testFilepaths);
166
+ const snapshotManager = getSnapshotManager();
166
167
  const ctx = {
167
168
  filesMap,
168
169
  get files() {
@@ -177,24 +178,25 @@ export async function run(config) {
177
178
  .reduce((tasks, suite) => tasks.concat(suite.tasks), []);
178
179
  },
179
180
  config,
180
- reporter: config.reporter,
181
+ reporter,
182
+ snapshotManager,
181
183
  };
182
184
  await runFiles(filesMap, ctx);
183
- const snapshot = getSnapshotManager();
184
- snapshot === null || snapshot === void 0 ? void 0 : snapshot.saveSnap();
185
- snapshot === null || snapshot === void 0 ? void 0 : snapshot.report();
185
+ snapshotManager.saveSnap();
186
186
  await ((_c = reporter.onFinished) === null || _c === void 0 ? void 0 : _c.call(reporter, ctx));
187
187
  if (config.watch)
188
- startWatcher(ctx);
188
+ await startWatcher(ctx);
189
189
  }
190
190
  export async function startWatcher(ctx) {
191
- var _a, _b;
192
- await ((_b = (_a = ctx.reporter).onWatcherStart) === null || _b === void 0 ? void 0 : _b.call(_a, ctx));
191
+ var _a;
192
+ const { reporter, snapshotManager, filesMap } = ctx;
193
+ await ((_a = reporter.onWatcherStart) === null || _a === void 0 ? void 0 : _a.call(reporter, ctx));
193
194
  let timer;
194
195
  const changedTests = new Set();
195
196
  const seen = new Set();
196
197
  const { server, moduleCache } = process.__vite_node__;
197
198
  server.watcher.on('change', async (id) => {
199
+ id = normalizePath(id);
198
200
  getDependencyTests(id, ctx, changedTests, seen);
199
201
  seen.forEach(i => moduleCache.delete(i));
200
202
  seen.clear();
@@ -202,23 +204,30 @@ export async function startWatcher(ctx) {
202
204
  return;
203
205
  clearTimeout(timer);
204
206
  timer = setTimeout(async () => {
205
- var _a, _b, _c, _d;
207
+ var _a, _b, _c;
206
208
  if (changedTests.size === 0)
207
209
  return;
208
- const snapshot = getSnapshotManager();
210
+ snapshotManager.clear();
209
211
  const paths = Array.from(changedTests);
210
212
  changedTests.clear();
211
- await ((_b = (_a = ctx.reporter).onWatcherRerun) === null || _b === void 0 ? void 0 : _b.call(_a, paths, id, ctx));
213
+ await ((_a = reporter.onWatcherRerun) === null || _a === void 0 ? void 0 : _a.call(reporter, paths, id, ctx));
212
214
  paths.forEach(i => moduleCache.delete(i));
213
- const files = await collectFiles(paths);
214
- Object.assign(ctx.filesMap, files);
215
- await runFiles(files, ctx);
216
- // TODO: clear snapshot state
217
- snapshot === null || snapshot === void 0 ? void 0 : snapshot.saveSnap();
218
- snapshot === null || snapshot === void 0 ? void 0 : snapshot.report();
219
- await ((_d = (_c = ctx.reporter).onWatcherStart) === null || _d === void 0 ? void 0 : _d.call(_c, ctx));
215
+ const newFilesMap = await collectFiles(paths);
216
+ Object.assign(filesMap, newFilesMap);
217
+ await runFiles(newFilesMap, ctx);
218
+ snapshotManager.saveSnap();
219
+ await ((_b = reporter.onFinished) === null || _b === void 0 ? void 0 : _b.call(reporter, ctx, Object.values(newFilesMap)));
220
+ await ((_c = reporter.onWatcherStart) === null || _c === void 0 ? void 0 : _c.call(reporter, ctx));
220
221
  }, 100);
221
222
  });
223
+ // add an empty promise so it never resolves
224
+ await new Promise(() => { });
225
+ }
226
+ function normalizePath(path) {
227
+ const normalized = path.replace(/\\/g, '/');
228
+ if (normalized.startsWith('/'))
229
+ return normalized;
230
+ return `/${normalized}`;
222
231
  }
223
232
  function getDependencyTests(id, ctx, set = new Set(), seen = new Set()) {
224
233
  if (seen.has(id) || set.has(id))
package/dist/types.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { SnapshotManager } from './integrations/chai/snapshot/manager';
1
2
  export declare type Awaitable<T> = Promise<T> | T;
2
3
  export interface UserOptions {
3
4
  /**
@@ -116,6 +117,7 @@ export interface RunnerContext {
116
117
  tasks: Task[];
117
118
  config: ResolvedConfig;
118
119
  reporter: Reporter;
120
+ snapshotManager: SnapshotManager;
119
121
  }
120
122
  export interface GlobalContext {
121
123
  suites: SuiteCollector[];
@@ -124,7 +126,7 @@ export interface GlobalContext {
124
126
  export interface Reporter {
125
127
  onStart?: (config: ResolvedConfig) => Awaitable<void>;
126
128
  onCollected?: (files: File[], ctx: RunnerContext) => Awaitable<void>;
127
- onFinished?: (ctx: RunnerContext) => Awaitable<void>;
129
+ onFinished?: (ctx: RunnerContext, files?: File[]) => Awaitable<void>;
128
130
  onSuiteBegin?: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
129
131
  onSuiteEnd?: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
130
132
  onFileBegin?: (file: File, ctx: RunnerContext) => Awaitable<void>;
@@ -133,5 +135,4 @@ export interface Reporter {
133
135
  onTaskEnd?: (task: Task, ctx: RunnerContext) => Awaitable<void>;
134
136
  onWatcherStart?: (ctx: RunnerContext) => Awaitable<void>;
135
137
  onWatcherRerun?: (files: string[], trigger: string, ctx: RunnerContext) => Awaitable<void>;
136
- onSnapshotUpdate?: () => Awaitable<void>;
137
138
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "vitest",
3
- "version": "0.0.29",
3
+ "version": "0.0.33",
4
4
  "description": "A blazing fast unit test framework powered by Vite",
5
5
  "keywords": [
6
6
  "vite",
@@ -47,14 +47,19 @@
47
47
  "test": "run-s test:*",
48
48
  "test:core": "node bin/vitest.mjs --dev -r test/core",
49
49
  "test:vue": "node bin/vitest.mjs --dev -r test/vue",
50
+ "test:react": "node bin/vitest.mjs --dev -r test/react",
51
+ "coverage": "node bin/vitest.mjs --dev -r test/core --coverage",
50
52
  "watch": "tsc -p src/tsconfig.json --watch"
51
53
  },
52
54
  "dependencies": {
55
+ "@antfu/utils": "^0.3.0",
53
56
  "@jest/test-result": "^27.4.2",
54
57
  "@types/chai": "^4.2.22",
55
58
  "@types/sinon-chai": "^3.2.6",
59
+ "c8": "^7.10.0",
56
60
  "chai": "^4.3.4",
57
61
  "chai-subset": "^1.6.0",
62
+ "diff": "^5.0.0",
58
63
  "fast-glob": "^3.2.7",
59
64
  "find-up": "^6.2.0",
60
65
  "jest-snapshot": "^27.4.2",
@@ -64,28 +69,28 @@
64
69
  "picocolors": "^1.0.0",
65
70
  "sade": "^1.7.4",
66
71
  "sinon": "^12.0.1",
67
- "sinon-chai": "^3.7.0"
72
+ "sinon-chai": "^3.7.0",
73
+ "source-map": "^0.7.3"
68
74
  },
69
75
  "peerDependencies": {
70
- "vite": "^2.7.0"
76
+ "vite": "^2.7.1"
71
77
  },
72
78
  "devDependencies": {
73
- "@antfu/eslint-config": "^0.12.1",
79
+ "@antfu/eslint-config": "^0.12.2",
74
80
  "@antfu/ni": "^0.11.0",
75
81
  "@types/chai-subset": "^1.3.3",
82
+ "@types/diff": "^5.0.1",
76
83
  "@types/jsdom": "^16.2.13",
77
84
  "@types/listr": "^0.14.4",
78
- "@types/node": "^16.11.11",
85
+ "@types/node": "^16.11.12",
79
86
  "@types/sade": "^1.7.3",
80
87
  "@types/sinon": "^10.0.6",
81
- "@vitejs/plugin-vue": "^1.10.1",
82
88
  "bumpp": "^7.1.1",
83
- "eslint": "^8.4.0",
89
+ "eslint": "^8.4.1",
84
90
  "esno": "^0.12.1",
85
91
  "npm-run-all": "^4.1.5",
86
92
  "rimraf": "^3.0.2",
87
93
  "typescript": "^4.5.2",
88
- "vite": "^2.7.0",
89
- "vue": "^3.2.24"
94
+ "vite": "^2.7.1"
90
95
  }
91
96
  }