vitest 0.0.12 → 0.0.16

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +34 -4
  2. package/bin/vitest.mjs +1 -41
  3. package/dist/cli.js +3 -2
  4. package/dist/constants.d.ts +1 -0
  5. package/dist/constants.js +20 -0
  6. package/dist/{snapshot/utils/types.js → entry.d.ts} +0 -0
  7. package/dist/entry.js +38 -0
  8. package/dist/global.d.ts +1 -0
  9. package/dist/global.js +8 -0
  10. package/dist/index.d.ts +2 -4
  11. package/dist/index.js +2 -4
  12. package/dist/{chai.js → integrations/chai/index.d.ts} +2 -0
  13. package/dist/integrations/chai/index.js +3 -0
  14. package/dist/integrations/chai/jest-expect.d.ts +21 -0
  15. package/dist/integrations/chai/jest-expect.js +39 -0
  16. package/dist/integrations/chai/setup.d.ts +2 -0
  17. package/dist/integrations/chai/setup.js +12 -0
  18. package/dist/{chai.d.ts → integrations/chai/snapshot/index.d.ts} +6 -1
  19. package/dist/{snapshot → integrations/chai/snapshot}/index.js +1 -1
  20. package/dist/{snapshot → integrations/chai/snapshot}/manager.d.ts +0 -0
  21. package/dist/{snapshot → integrations/chai/snapshot}/manager.js +0 -0
  22. package/dist/{snapshot → integrations/chai/snapshot}/utils/jest-config-helper.d.ts +0 -0
  23. package/dist/{snapshot → integrations/chai/snapshot}/utils/jest-config-helper.js +0 -0
  24. package/dist/{snapshot → integrations/chai/snapshot}/utils/jest-reporters-lite.d.ts +0 -0
  25. package/dist/{snapshot → integrations/chai/snapshot}/utils/jest-reporters-lite.js +0 -0
  26. package/dist/{snapshot → integrations/chai/snapshot}/utils/jest-test-result-helper.d.ts +0 -0
  27. package/dist/{snapshot → integrations/chai/snapshot}/utils/jest-test-result-helper.js +1 -0
  28. package/dist/{snapshot → integrations/chai/snapshot}/utils/types.d.ts +0 -0
  29. package/dist/integrations/chai/snapshot/utils/types.js +1 -0
  30. package/dist/integrations/chai/types.d.ts +3 -0
  31. package/dist/integrations/chai/types.js +1 -0
  32. package/dist/integrations/jsdom.d.ts +5 -0
  33. package/dist/integrations/jsdom.js +20 -0
  34. package/dist/integrations/sinon.d.ts +3 -0
  35. package/dist/integrations/sinon.js +3 -0
  36. package/dist/node.d.ts +11 -0
  37. package/dist/node.js +126 -0
  38. package/dist/reporters/default.d.ts +13 -8
  39. package/dist/reporters/default.js +60 -54
  40. package/dist/run.js +17 -17
  41. package/dist/types.d.ts +22 -10
  42. package/global.d.ts +21 -0
  43. package/index.d.ts +1 -0
  44. package/package.json +34 -27
  45. package/dist/defaultIncludes.d.ts +0 -0
  46. package/dist/defaultIncludes.js +0 -1
  47. package/dist/snapshot/index.d.ts +0 -9
  48. package/dist/snapshot/utils/SnapshotResult.d.ts +0 -0
  49. package/dist/snapshot/utils/SnapshotResult.js +0 -1
  50. package/dist/snapshot/utils/tyoes.d.ts +0 -0
  51. package/dist/snapshot/utils/tyoes.js +0 -1
package/README.md CHANGED
@@ -6,10 +6,11 @@ A blazing fast test runner powered by Vite.
6
6
 
7
7
  ## Features
8
8
 
9
- - [Vite](https://vitejs.dev/)'s config, transformers, resolvers, and plugins. Powered by [vite-node](https://github.com/antfu/vite-node)
9
+ - [Vite](https://vitejs.dev/)'s config, transformers, resolvers, and plugins.
10
10
  - [Jest Snapshot](https://jestjs.io/docs/snapshot-testing)
11
11
  - [Chai](https://www.chaijs.com/) for assertions
12
12
  - [Sinon](https://sinonjs.org/) for mocking
13
+ - [JSDOM](https://github.com/jsdom/jsdom) for DOM mocking
13
14
  - Async suite / test, top level await
14
15
  - ESM friendly
15
16
  - Out-of-box TypeScript support
@@ -20,11 +21,12 @@ import { it, describe, expect, assert } from 'vitest'
20
21
 
21
22
  describe('suite name', () => {
22
23
  it('foo', () => {
23
- assert.equal(Math.sqrt(4), 2)
24
+ expect(1 + 1).toEqual(2)
25
+ expect(true).to.be.true
24
26
  })
25
27
 
26
28
  it('bar', () => {
27
- expect(1 + 1).eq(2)
29
+ assert.equal(Math.sqrt(4), 2)
28
30
  })
29
31
 
30
32
  it('snapshot', () => {
@@ -58,6 +60,34 @@ export default defineConfig({
58
60
  })
59
61
  ```
60
62
 
63
+ ## Global APIs
64
+
65
+ 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.
66
+
67
+ ```ts
68
+ // vite.config.ts
69
+ import { defineConfig } from 'vite'
70
+
71
+ export default defineConfig({
72
+ test: {
73
+ global: true
74
+ }
75
+ })
76
+ ```
77
+
78
+ To get TypeScript working with the global APIs, add `vitest/global` to the `types` filed in your `tsconfig.json`
79
+
80
+ ```json
81
+ // tsconfig.json
82
+ {
83
+ "compilerOptions": {
84
+ "types": [
85
+ "vitest/global"
86
+ ]
87
+ }
88
+ }
89
+ ```
90
+
61
91
  ## Filtering
62
92
 
63
93
  ### Skipping suites and tasks
@@ -124,7 +154,7 @@ describe('suite', () => {
124
154
  - [x] Reporter & Better output
125
155
  - [x] Task filter
126
156
  - [x] Mock
127
- - [ ] Global Mode & Types
157
+ - [x] Global Mode & Types
128
158
  - [ ] Parallel Executing
129
159
  - [ ] CLI Help
130
160
  - [ ] JSDom
package/bin/vitest.mjs CHANGED
@@ -1,44 +1,4 @@
1
1
  #!/usr/bin/env node
2
2
  'use strict'
3
3
 
4
- import { fileURLToPath } from 'url'
5
- import { resolve, dirname } from 'path'
6
- import { run } from 'vite-node'
7
- import minimist from 'minimist'
8
- import { findUp } from 'find-up'
9
-
10
- process.env.VITEST = 'true'
11
-
12
- const argv = minimist(process.argv.slice(2), {
13
- alias: {
14
- c: 'config',
15
- },
16
- string: ['root', 'config'],
17
- boolean: ['dev'],
18
- })
19
-
20
- const __dirname = dirname(fileURLToPath(import.meta.url))
21
- const root = resolve(argv.root || process.cwd())
22
-
23
- const configPath = argv.config ? resolve(root, argv.config) : await findUp(['vitest.config.ts', 'vitest.config.js', 'vitest.config.mjs', 'vite.config.ts', 'vite.config.js', 'vite.config.mjs'], { cwd: root })
24
-
25
- await run({
26
- root,
27
- files: [
28
- resolve(__dirname, argv.dev ? '../src/cli.ts' : '../dist/cli.js'),
29
- ],
30
- config: configPath,
31
- defaultConfig: {
32
- optimizeDeps: {
33
- exclude: [
34
- 'vitest',
35
- ],
36
- },
37
- },
38
- shouldExternalize(id) {
39
- if (id.includes('/node_modules/vitest/'))
40
- return false
41
- else
42
- return id.includes('/node_modules/')
43
- },
44
- })
4
+ import '../dist/entry.js'
package/dist/cli.js CHANGED
@@ -6,9 +6,10 @@ const { log } = console;
6
6
  const argv = minimist(process.argv.slice(2), {
7
7
  alias: {
8
8
  u: 'update',
9
+ w: 'watch',
9
10
  },
10
11
  string: ['root', 'config'],
11
- boolean: ['update', 'dev'],
12
+ boolean: ['update', 'dev', 'global', 'watch', 'jsdom'],
12
13
  unknown(name) {
13
14
  if (name[0] === '-') {
14
15
  console.error(c.red(`Unknown argument: ${name}`));
@@ -22,7 +23,7 @@ const argv = minimist(process.argv.slice(2), {
22
23
  const server = (_a = process === null || process === void 0 ? void 0 : process.__vite_node__) === null || _a === void 0 ? void 0 : _a.server;
23
24
  const viteConfig = (server === null || server === void 0 ? void 0 : server.config) || {};
24
25
  const testOptions = viteConfig.test || {};
25
- await run(Object.assign(Object.assign({}, testOptions), { server, updateSnapshot: argv.update, rootDir: argv.root || process.cwd(), nameFilters: argv._ }));
26
+ await run(Object.assign(Object.assign(Object.assign({}, argv), testOptions), { server, updateSnapshot: argv.update, rootDir: argv.root || process.cwd(), nameFilters: argv._ }));
26
27
  function help() {
27
28
  log('Help: finish help');
28
29
  }
@@ -1,2 +1,3 @@
1
1
  export declare const defaultIncludes: string[];
2
2
  export declare const defaultExcludes: string[];
3
+ export declare const globalApis: string[];
package/dist/constants.js CHANGED
@@ -1,2 +1,22 @@
1
1
  export const defaultIncludes = ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'];
2
2
  export const defaultExcludes = ['**/node_modules/**', '**/dist/**'];
3
+ export const globalApis = [
4
+ 'suite',
5
+ 'test',
6
+ 'describe',
7
+ 'it',
8
+ 'expect',
9
+ 'assert',
10
+ 'spy',
11
+ 'mock',
12
+ 'stub',
13
+ 'sinon',
14
+ 'beforeAll',
15
+ 'afterAll',
16
+ 'beforeEach',
17
+ 'afterEach',
18
+ 'beforeFile',
19
+ 'afterFile',
20
+ 'beforeSuite',
21
+ 'afterSuite',
22
+ ];
File without changes
package/dist/entry.js ADDED
@@ -0,0 +1,38 @@
1
+ import { fileURLToPath } from 'url';
2
+ import { resolve, dirname } from 'path';
3
+ import minimist from 'minimist';
4
+ import { findUp } from 'find-up';
5
+ import { run } from './node.js';
6
+ process.env.VITEST = 'true';
7
+ const argv = minimist(process.argv.slice(2), {
8
+ alias: {
9
+ c: 'config',
10
+ },
11
+ string: ['root', 'config'],
12
+ boolean: ['dev'],
13
+ });
14
+ const __dirname = dirname(fileURLToPath(import.meta.url));
15
+ const root = resolve(argv.root || process.cwd());
16
+ const configPath = argv.config
17
+ ? resolve(root, argv.config)
18
+ : await findUp(['vitest.config.ts', 'vitest.config.js', 'vitest.config.mjs', 'vite.config.ts', 'vite.config.js', 'vite.config.mjs'], { cwd: root });
19
+ await run({
20
+ root,
21
+ files: [
22
+ resolve(__dirname, argv.dev ? '../src/cli.ts' : './cli.js'),
23
+ ],
24
+ config: configPath,
25
+ defaultConfig: {
26
+ optimizeDeps: {
27
+ exclude: [
28
+ 'vitest',
29
+ ],
30
+ },
31
+ },
32
+ shouldExternalize(id) {
33
+ if (id.includes('/node_modules/vitest/'))
34
+ return false;
35
+ else
36
+ return id.includes('/node_modules/');
37
+ },
38
+ });
@@ -0,0 +1 @@
1
+ export declare function registerApiGlobally(): void;
package/dist/global.js ADDED
@@ -0,0 +1,8 @@
1
+ import { globalApis } from './constants';
2
+ import * as index from './index';
3
+ export function registerApiGlobally() {
4
+ globalApis.forEach((api) => {
5
+ // @ts-expect-error
6
+ globalThis[api] = index[api];
7
+ });
8
+ }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,6 @@
1
- import sinon from 'sinon';
2
1
  export * from './types';
3
2
  export * from './suite';
4
3
  export * from './config';
5
- export * from './chai';
4
+ export * from './integrations/chai';
5
+ export * from './integrations/sinon';
6
6
  export { beforeAll, afterAll, beforeEach, afterEach, beforeFile, afterFile, beforeSuite, afterSuite } from './hooks';
7
- export { sinon };
8
- export declare const mock: sinon.SinonMockStatic, spy: sinon.SinonSpyStatic, stub: sinon.SinonStubStatic;
package/dist/index.js CHANGED
@@ -1,8 +1,6 @@
1
- import sinon from 'sinon';
2
1
  export * from './types';
3
2
  export * from './suite';
4
3
  export * from './config';
5
- export * from './chai';
4
+ export * from './integrations/chai';
5
+ export * from './integrations/sinon';
6
6
  export { beforeAll, afterAll, beforeEach, afterEach, beforeFile, afterFile, beforeSuite, afterSuite } from './hooks';
7
- export { sinon };
8
- export const { mock, spy, stub } = sinon;
@@ -1 +1,3 @@
1
+ import chai from 'chai';
1
2
  export { assert, should, expect } from 'chai';
3
+ export { chai };
@@ -0,0 +1,3 @@
1
+ import chai from 'chai';
2
+ export { assert, should, expect } from 'chai';
3
+ export { chai };
@@ -0,0 +1,21 @@
1
+ import { ChaiPlugin } from './types';
2
+ export declare function JestChaiExpect(): ChaiPlugin;
3
+ declare global {
4
+ namespace Chai {
5
+ interface Assertion {
6
+ toEqual(expected: any): void;
7
+ toStrictEqual(expected: any): void;
8
+ toBe(expected: any): void;
9
+ toContain(item: any): void;
10
+ toBeTruthy(): void;
11
+ toBeFalsy(): void;
12
+ toBeNaN(): void;
13
+ toBeUndefined(): void;
14
+ toBeNull(): void;
15
+ toBeDefined(): void;
16
+ }
17
+ interface ExpectStatic {
18
+ addSnapshotSerializer: import('pretty-format').Plugin;
19
+ }
20
+ }
21
+ }
@@ -0,0 +1,39 @@
1
+ // Jest Expect Compact
2
+ // TODO: add more https://jestjs.io/docs/expect
3
+ export function JestChaiExpect() {
4
+ return (chai, utils) => {
5
+ const proto = chai.Assertion.prototype;
6
+ utils.addMethod(proto, 'toEqual', function (expected) {
7
+ return this.eql(expected);
8
+ });
9
+ utils.addMethod(proto, 'toStrictEqual', function (expected) {
10
+ return this.equal(expected);
11
+ });
12
+ utils.addMethod(proto, 'toBe', function (expected) {
13
+ return this.be(expected);
14
+ });
15
+ utils.addMethod(proto, 'toContain', function (item) {
16
+ return this.contain(item);
17
+ });
18
+ utils.addMethod(proto, 'toBeTruthy', function () {
19
+ const obj = utils.flag(this, 'object');
20
+ this.assert(Boolean(obj), 'expected #{this} to be truthy', 'expected #{this} to not be truthy', obj);
21
+ });
22
+ utils.addMethod(proto, 'toFalsy', function () {
23
+ const obj = utils.flag(this, 'object');
24
+ this.assert(!obj, 'expected #{this} to be falsy', 'expected #{this} to not be falsy', obj);
25
+ });
26
+ utils.addMethod(proto, 'toBeNaN', function () {
27
+ return this.be.NaN;
28
+ });
29
+ utils.addMethod(proto, 'toBeUndefined', function () {
30
+ return this.be.undefined;
31
+ });
32
+ utils.addMethod(proto, 'toBeNull', function () {
33
+ return this.be.null;
34
+ });
35
+ utils.addMethod(proto, 'toBeDefined', function () {
36
+ return this.not.be.undefined;
37
+ });
38
+ };
39
+ }
@@ -0,0 +1,2 @@
1
+ import { Config } from 'vitest';
2
+ export declare function setupChai(config: Config): Promise<void>;
@@ -0,0 +1,12 @@
1
+ import chai from 'chai';
2
+ import SinonChai from 'sinon-chai';
3
+ import { JestChaiExpect } from './jest-expect';
4
+ import { SnapshotPlugin } from './snapshot';
5
+ export async function setupChai(config) {
6
+ chai.use(SinonChai);
7
+ chai.use(JestChaiExpect());
8
+ chai.use(await SnapshotPlugin({
9
+ rootDir: config.rootDir || process.cwd(),
10
+ update: config.updateSnapshot,
11
+ }));
12
+ }
@@ -1,4 +1,9 @@
1
- export { assert, should, expect } from 'chai';
1
+ import { ChaiPlugin } from '../types';
2
+ export interface SnapshotOptions {
3
+ rootDir: string;
4
+ update?: boolean;
5
+ }
6
+ export declare function SnapshotPlugin(options: SnapshotOptions): Promise<ChaiPlugin>;
2
7
  declare global {
3
8
  namespace Chai {
4
9
  interface Assertion {
@@ -1,5 +1,5 @@
1
1
  import Snap from 'jest-snapshot';
2
- import { afterAll, beforeEach } from '../hooks';
2
+ import { afterAll, beforeEach } from '../../../hooks';
3
3
  import { SnapshotManager } from './manager';
4
4
  const { addSerializer } = Snap;
5
5
  let _manager;
@@ -4,6 +4,7 @@
4
4
  * This source code is licensed under the MIT license found in the
5
5
  * LICENSE file in the root directory of this source tree.
6
6
  */
7
+ // @ts-ignore
7
8
  import Test from '@jest/test-result';
8
9
  const { makeEmptyAggregatedTestResult, } = Test;
9
10
  export const makeEmptySnapshotSummary = (options) => {
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,3 @@
1
+ import { use as chaiUse } from 'chai';
2
+ export declare type FirstFunctionArgument<T> = T extends (arg: infer A) => unknown ? A : never;
3
+ export declare type ChaiPlugin = FirstFunctionArgument<typeof chaiUse>;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,5 @@
1
+ import { JSDOM } from 'jsdom';
2
+ export declare function setupJSDOM(global: any): {
3
+ dom: JSDOM;
4
+ restore(): void;
5
+ };
@@ -0,0 +1,20 @@
1
+ import { JSDOM } from 'jsdom';
2
+ export function setupJSDOM(global) {
3
+ const dom = new JSDOM('<!DOCTYPE html>', {
4
+ pretendToBeVisual: true,
5
+ runScripts: 'dangerously',
6
+ // TODO: options
7
+ url: 'http://localhost:3000',
8
+ });
9
+ const keys = Object.getOwnPropertyNames(dom.window)
10
+ .filter(k => !k.startsWith('_'))
11
+ .filter(k => !(k in global));
12
+ for (const key of keys)
13
+ global[key] = dom.window[key];
14
+ return {
15
+ dom,
16
+ restore() {
17
+ keys.forEach(key => delete global[key]);
18
+ },
19
+ };
20
+ }
@@ -0,0 +1,3 @@
1
+ import sinon from 'sinon';
2
+ export { sinon };
3
+ export declare const mock: sinon.SinonMockStatic, spy: sinon.SinonSpyStatic, stub: sinon.SinonStubStatic;
@@ -0,0 +1,3 @@
1
+ import sinon from 'sinon';
2
+ export { sinon };
3
+ export const { mock, spy, stub } = sinon;
package/dist/node.d.ts ADDED
@@ -0,0 +1,11 @@
1
+ import { InlineConfig } from 'vite';
2
+ export interface ViteNodeOptions {
3
+ silent?: boolean;
4
+ root: string;
5
+ files: string[];
6
+ _?: string[];
7
+ shouldExternalize?: (file: string) => boolean;
8
+ config?: string;
9
+ defaultConfig?: InlineConfig;
10
+ }
11
+ export declare function run(argv: ViteNodeOptions): Promise<void>;
package/dist/node.js ADDED
@@ -0,0 +1,126 @@
1
+ import { builtinModules, createRequire } from 'module';
2
+ import { pathToFileURL } from 'url';
3
+ import { dirname, resolve, relative } from 'path';
4
+ import vm from 'vm';
5
+ import { createServer, mergeConfig } from 'vite';
6
+ import c from 'picocolors';
7
+ const { red, dim, yellow } = c;
8
+ export async function run(argv) {
9
+ process.exitCode = 0;
10
+ const root = argv.root || process.cwd();
11
+ process.chdir(root);
12
+ const files = argv.files || argv._;
13
+ argv.shouldExternalize = argv.shouldExternalize || (id => id.includes('/node_modules/'));
14
+ const server = await createServer(mergeConfig(argv.defaultConfig || {}, {
15
+ logLevel: 'error',
16
+ clearScreen: false,
17
+ configFile: argv.config,
18
+ root,
19
+ resolve: {},
20
+ }));
21
+ await server.pluginContainer.buildStart({});
22
+ // @ts-expect-error
23
+ process.__vite_node__ = {
24
+ server,
25
+ };
26
+ try {
27
+ await execute(files, server, argv);
28
+ }
29
+ catch (e) {
30
+ process.exitCode = 1;
31
+ throw e;
32
+ }
33
+ finally {
34
+ await server.close();
35
+ }
36
+ }
37
+ function normalizeId(id) {
38
+ // Virtual modules start with `\0`
39
+ if (id && id.startsWith('/@id/__x00__'))
40
+ id = `\0${id.slice('/@id/__x00__'.length)}`;
41
+ if (id && id.startsWith('/@id/'))
42
+ id = id.slice('/@id/'.length);
43
+ return id;
44
+ }
45
+ function toFilePath(id, server) {
46
+ let absolute = id.startsWith('/@fs/')
47
+ ? id.slice(4)
48
+ : id.startsWith(dirname(server.config.root))
49
+ ? id
50
+ : slash(resolve(server.config.root, id.slice(1)));
51
+ if (absolute.startsWith('//'))
52
+ absolute = absolute.slice(1);
53
+ if (!absolute.startsWith('/'))
54
+ absolute = `/${absolute}`;
55
+ return absolute;
56
+ }
57
+ async function execute(files, server, options) {
58
+ const __pendingModules__ = new Map();
59
+ const result = [];
60
+ for (const file of files)
61
+ result.push(await cachedRequest(`/@fs/${slash(resolve(file))}`, []));
62
+ return result;
63
+ async function directRequest(rawId, callstack) {
64
+ if (builtinModules.includes(rawId))
65
+ return import(rawId);
66
+ callstack = [...callstack, rawId];
67
+ const request = async (dep) => {
68
+ if (callstack.includes(dep)) {
69
+ throw new Error(`${red('Circular dependency detected')}\nStack:\n${[...callstack, dep].reverse().map((i) => {
70
+ const path = relative(server.config.root, toFilePath(normalizeId(i), server));
71
+ return dim(' -> ') + (i === dep ? yellow(path) : path);
72
+ }).join('\n')}\n`);
73
+ }
74
+ return cachedRequest(dep, callstack);
75
+ };
76
+ const id = normalizeId(rawId);
77
+ const absolute = toFilePath(id, server);
78
+ if (options.shouldExternalize(absolute))
79
+ return import(absolute);
80
+ const result = await server.transformRequest(id, { ssr: true });
81
+ if (!result)
82
+ throw new Error(`failed to load ${id}`);
83
+ const url = pathToFileURL(absolute);
84
+ const exports = {};
85
+ const context = {
86
+ require: createRequire(url),
87
+ __filename: absolute,
88
+ __dirname: dirname(absolute),
89
+ __vite_ssr_import__: request,
90
+ __vite_ssr_dynamic_import__: request,
91
+ __vite_ssr_exports__: exports,
92
+ __vite_ssr_exportAll__: (obj) => exportAll(exports, obj),
93
+ __vite_ssr_import_meta__: { url },
94
+ };
95
+ const fn = vm.runInThisContext(`async (${Object.keys(context).join(',')}) => { ${result.code} }`, {
96
+ filename: absolute,
97
+ lineOffset: 0,
98
+ });
99
+ await fn(...Object.values(context));
100
+ return exports;
101
+ }
102
+ async function cachedRequest(id, callstack) {
103
+ if (__pendingModules__.has(id))
104
+ return __pendingModules__.get(id);
105
+ __pendingModules__.set(id, directRequest(id, callstack));
106
+ return await __pendingModules__.get(id);
107
+ }
108
+ function exportAll(exports, sourceModule) {
109
+ // eslint-disable-next-line no-restricted-syntax
110
+ for (const key in sourceModule) {
111
+ if (key !== 'default') {
112
+ try {
113
+ Object.defineProperty(exports, key, {
114
+ enumerable: true,
115
+ configurable: true,
116
+ get() { return sourceModule[key]; },
117
+ });
118
+ }
119
+ catch (_err) { }
120
+ }
121
+ }
122
+ }
123
+ }
124
+ function slash(path) {
125
+ return path.replace(/\\/g, '/');
126
+ }
@@ -1,19 +1,24 @@
1
- import { File, Reporter, RunnerContext, Suite, Task } from '../types';
1
+ import Listr from 'listr';
2
+ import { Reporter, RunnerContext, Task } from '../types';
3
+ interface TaskPromise {
4
+ promise: Promise<void>;
5
+ resolve: () => void;
6
+ reject: (e: unknown) => void;
7
+ }
2
8
  export declare class DefaultReporter implements Reporter {
3
9
  indent: number;
4
10
  start: number;
5
11
  end: number;
12
+ listr: Listr | null;
13
+ listrPromise: Promise<void> | null;
14
+ taskMap: Map<Task, TaskPromise>;
6
15
  onStart(): void;
7
- onCollected(): void;
8
- onSuiteBegin(suite: Suite): void;
9
- onSuiteEnd(suite: Suite): void;
10
- onFileBegin(file: File): void;
11
- onFileEnd(): void;
12
- onTaskBegin(task: Task): void;
16
+ onCollected(ctx: RunnerContext): void;
13
17
  onTaskEnd(task: Task): void;
14
- onFinished({ files }: RunnerContext): void;
18
+ onFinished({ files }: RunnerContext): Promise<void>;
15
19
  private getIndent;
16
20
  private log;
17
21
  private error;
18
22
  onSnapshotUpdate(): void;
19
23
  }
24
+ export {};
@@ -1,69 +1,75 @@
1
- import { relative } from 'path';
2
1
  import { performance } from 'perf_hooks';
2
+ import { relative } from 'path';
3
3
  import c from 'picocolors';
4
- import ora from 'ora';
5
- const DOT = '· ';
6
- const CHECK = '✔ ';
7
- const CROSS = '⤫ ';
4
+ import Listr from 'listr';
5
+ const CROSS = ' ';
8
6
  export class DefaultReporter {
9
7
  constructor() {
10
8
  this.indent = 0;
11
9
  this.start = 0;
12
10
  this.end = 0;
11
+ this.listr = null;
12
+ this.listrPromise = null;
13
+ this.taskMap = new Map();
13
14
  }
14
15
  onStart() {
15
16
  this.indent = 0;
16
17
  }
17
- onCollected() {
18
+ onCollected(ctx) {
18
19
  this.start = performance.now();
19
- }
20
- onSuiteBegin(suite) {
21
- if (suite.name) {
22
- this.indent += 1;
23
- const name = DOT + suite.name;
24
- if (suite.mode === 'skip')
25
- this.log(c.dim(c.yellow(`${name} (skipped)`)));
26
- else if (suite.mode === 'todo')
27
- this.log(c.dim(`${name} (todo)`));
28
- else
29
- this.log(name);
30
- }
31
- }
32
- onSuiteEnd(suite) {
33
- if (suite.name)
34
- this.indent -= 1;
35
- }
36
- onFileBegin(file) {
37
- this.log(`- ${relative(process.cwd(), file.filepath)} ${c.dim(`(${file.suites.flatMap(i => i.tasks).length} tests)`)}`);
38
- }
39
- onFileEnd() {
40
- this.log();
41
- }
42
- onTaskBegin(task) {
43
- this.indent += 1;
44
- // @ts-expect-error
45
- task.__ora = ora({ text: task.name, prefixText: this.getIndent().slice(1), spinner: 'arc' }).start();
20
+ this.taskMap = new Map();
21
+ const tasks = ctx.files.reduce((acc, file) => acc.concat(file.suites.flatMap(i => i.tasks)), []);
22
+ tasks.forEach((t) => {
23
+ const obj = {};
24
+ obj.promise = new Promise((resolve, reject) => {
25
+ obj.resolve = resolve;
26
+ obj.reject = reject;
27
+ });
28
+ this.taskMap.set(t, obj);
29
+ });
30
+ const createTasksListr = (tasks) => {
31
+ return tasks.map((task) => {
32
+ return {
33
+ title: task.name,
34
+ skip: () => task.mode === 'skip',
35
+ task: async () => {
36
+ var _a;
37
+ return await ((_a = this.taskMap.get(task)) === null || _a === void 0 ? void 0 : _a.promise);
38
+ },
39
+ };
40
+ });
41
+ };
42
+ const listrOptions = {
43
+ exitOnError: false,
44
+ };
45
+ this.listr = new Listr(ctx.files.map((file) => {
46
+ return {
47
+ title: relative(process.cwd(), file.filepath),
48
+ task: () => {
49
+ return new Listr(file.suites.flatMap((suite) => {
50
+ if (!suite.name)
51
+ return createTasksListr(suite.tasks);
52
+ return [{
53
+ title: suite.name,
54
+ skip: () => suite.mode !== 'run',
55
+ task: () => new Listr(createTasksListr(suite.tasks), listrOptions),
56
+ }];
57
+ }), listrOptions);
58
+ },
59
+ };
60
+ }), listrOptions);
61
+ this.listrPromise = this.listr.run().catch(() => { });
46
62
  }
47
63
  onTaskEnd(task) {
48
- var _a;
49
- // @ts-expect-error
50
- (_a = task.__ora) === null || _a === void 0 ? void 0 : _a.stop();
51
- if (task.state === 'pass') {
52
- this.log(`${c.green(CHECK + task.name)}`);
53
- }
54
- else if (task.state === 'skip') {
55
- this.log(c.dim(c.yellow(`${DOT + task.name} (skipped)`)));
56
- }
57
- else if (task.state === 'todo') {
58
- this.log(c.dim(`${DOT + task.name} (todo)`));
59
- }
60
- else {
61
- this.error(`${c.red(`${CROSS}${task.name}`)}`);
62
- process.exitCode = 1;
63
- }
64
- this.indent -= 1;
64
+ var _a, _b;
65
+ if (task.state === 'fail')
66
+ (_a = this.taskMap.get(task)) === null || _a === void 0 ? void 0 : _a.reject(task.error);
67
+ else
68
+ (_b = this.taskMap.get(task)) === null || _b === void 0 ? void 0 : _b.resolve();
65
69
  }
66
- onFinished({ files }) {
70
+ async onFinished({ files }) {
71
+ await this.listrPromise;
72
+ this.log();
67
73
  this.end = performance.now();
68
74
  const failedFiles = files.filter(i => i.error);
69
75
  const tasks = files.reduce((acc, file) => acc.concat(file.suites.flatMap(i => i.tasks)), []);
@@ -85,14 +91,14 @@ export class DefaultReporter {
85
91
  this.error(c.bold(`\nFailed Tests (${failed.length})`));
86
92
  failed.forEach((task) => {
87
93
  var _a;
88
- this.error(`\n${CROSS + c.inverse(c.red(' FAIL '))} ${[task.suite.name, task.name].filter(Boolean).join(' > ')} ${c.gray(`${(_a = task.file) === null || _a === void 0 ? void 0 : _a.filepath}`)}`);
94
+ this.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}`))}`);
89
95
  console.error(task.error || 'Unknown error');
90
96
  this.log();
91
97
  });
92
98
  }
93
- this.log(c.green(`Passed ${passed.length} / ${runable.length}`));
99
+ this.log(c.bold(c.green(`Passed ${passed.length} / ${runable.length}`)));
94
100
  if (failed.length)
95
- this.log(c.red(`Failed ${failed.length} / ${runable.length}`));
101
+ this.log(c.bold(c.red(`Failed ${failed.length} / ${runable.length}`)));
96
102
  if (skipped.length)
97
103
  this.log(c.yellow(`Skipped ${skipped.length}`));
98
104
  if (todo.length)
package/dist/run.js CHANGED
@@ -1,10 +1,8 @@
1
- import chai from 'chai';
2
1
  import fg from 'fast-glob';
3
- import SinonChai from 'sinon-chai';
2
+ import { setupChai } from './integrations/chai/setup';
4
3
  import { clearContext, defaultSuite } from './suite';
5
4
  import { context } from './context';
6
5
  import { afterEachHook, afterFileHook, afterAllHook, afterSuiteHook, beforeEachHook, beforeFileHook, beforeAllHook, beforeSuiteHook } from './hooks';
7
- import { SnapshotPlugin } from './snapshot';
8
6
  import { DefaultReporter } from './reporters/default';
9
7
  import { defaultIncludes, defaultExcludes } from './constants';
10
8
  export async function runTask(task, ctx) {
@@ -74,33 +72,31 @@ function interpretOnlyMode(items) {
74
72
  }
75
73
  }
76
74
  export async function runFile(file, ctx) {
77
- var _a, _b, _c, _d;
75
+ var _a, _b;
78
76
  const { reporter } = ctx;
79
77
  const runableSuites = file.suites.filter(i => i.mode === 'run');
80
78
  if (runableSuites.length === 0)
81
79
  return;
82
80
  await ((_a = reporter.onFileBegin) === null || _a === void 0 ? void 0 : _a.call(reporter, file, ctx));
83
81
  await beforeFileHook.fire(file);
84
- for (const suite of file.suites) {
85
- await ((_b = reporter.onSuiteBegin) === null || _b === void 0 ? void 0 : _b.call(reporter, suite, ctx));
82
+ // TODO: support toggling parallel or serial
83
+ await Promise.all(file.suites.map(async (suite) => {
84
+ var _a, _b;
85
+ await ((_a = reporter.onSuiteBegin) === null || _a === void 0 ? void 0 : _a.call(reporter, suite, ctx));
86
86
  await beforeSuiteHook.fire(suite);
87
- for (const t of suite.tasks)
88
- await runTask(t, ctx);
87
+ await Promise.all(suite.tasks.map(i => runTask(i, ctx)));
88
+ // for (const t of suite.tasks)
89
+ // await runTask(t, ctx)
89
90
  await afterSuiteHook.fire(suite);
90
- await ((_c = reporter.onSuiteEnd) === null || _c === void 0 ? void 0 : _c.call(reporter, suite, ctx));
91
- }
91
+ await ((_b = reporter.onSuiteEnd) === null || _b === void 0 ? void 0 : _b.call(reporter, suite, ctx));
92
+ }));
92
93
  await afterFileHook.fire(file);
93
- await ((_d = reporter.onFileEnd) === null || _d === void 0 ? void 0 : _d.call(reporter, file, ctx));
94
+ await ((_b = reporter.onFileEnd) === null || _b === void 0 ? void 0 : _b.call(reporter, file, ctx));
94
95
  }
95
96
  export async function run(config) {
96
97
  var _a, _b, _c, _d;
97
- const { rootDir = process.cwd() } = config;
98
98
  // setup chai
99
- chai.use(await SnapshotPlugin({
100
- rootDir,
101
- update: config.updateSnapshot,
102
- }));
103
- chai.use(SinonChai);
99
+ await setupChai(config);
104
100
  // collect files
105
101
  let paths = await fg(config.includes || defaultIncludes, {
106
102
  absolute: true,
@@ -116,6 +112,10 @@ export async function run(config) {
116
112
  }
117
113
  const reporter = new DefaultReporter();
118
114
  await ((_b = reporter.onStart) === null || _b === void 0 ? void 0 : _b.call(reporter, config));
115
+ if (config.global)
116
+ (await import('./global')).registerApiGlobally();
117
+ if (config.jsdom)
118
+ (await import('./integrations/jsdom')).setupJSDOM(globalThis);
119
119
  const files = await collectFiles(paths);
120
120
  const ctx = {
121
121
  files,
package/dist/types.d.ts CHANGED
@@ -3,6 +3,18 @@ export declare type Awaitable<T> = Promise<T> | T;
3
3
  export interface UserOptions {
4
4
  includes?: string[];
5
5
  excludes?: string[];
6
+ /**
7
+ * Register apis globally
8
+ *
9
+ * @default false
10
+ */
11
+ global?: boolean;
12
+ /**
13
+ * Use `js-dom` to mock browser APIs
14
+ *
15
+ * @default false
16
+ */
17
+ jsdom?: boolean;
6
18
  }
7
19
  export interface Config extends UserOptions {
8
20
  rootDir?: string;
@@ -59,14 +71,14 @@ export interface GlobalContext {
59
71
  currentSuite: SuiteCollector | null;
60
72
  }
61
73
  export interface Reporter {
62
- onStart: (userOptions: Config) => Awaitable<void>;
63
- onCollected: (ctx: RunnerContext) => Awaitable<void>;
64
- onFinished: (ctx: RunnerContext) => Awaitable<void>;
65
- onSuiteBegin: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
66
- onSuiteEnd: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
67
- onFileBegin: (file: File, ctx: RunnerContext) => Awaitable<void>;
68
- onFileEnd: (file: File, ctx: RunnerContext) => Awaitable<void>;
69
- onTaskBegin: (task: Task, ctx: RunnerContext) => Awaitable<void>;
70
- onTaskEnd: (task: Task, ctx: RunnerContext) => Awaitable<void>;
71
- onSnapshotUpdate: () => Awaitable<void>;
74
+ onStart?: (userOptions: Config) => Awaitable<void>;
75
+ onCollected?: (ctx: RunnerContext) => Awaitable<void>;
76
+ onFinished?: (ctx: RunnerContext) => Awaitable<void>;
77
+ onSuiteBegin?: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
78
+ onSuiteEnd?: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
79
+ onFileBegin?: (file: File, ctx: RunnerContext) => Awaitable<void>;
80
+ onFileEnd?: (file: File, ctx: RunnerContext) => Awaitable<void>;
81
+ onTaskBegin?: (task: Task, ctx: RunnerContext) => Awaitable<void>;
82
+ onTaskEnd?: (task: Task, ctx: RunnerContext) => Awaitable<void>;
83
+ onSnapshotUpdate?: () => Awaitable<void>;
72
84
  }
package/global.d.ts ADDED
@@ -0,0 +1,21 @@
1
+ declare global {
2
+ const suite: typeof import('vitest')['suite']
3
+ const test: typeof import('vitest')['test']
4
+ const describe: typeof import('vitest')['describe']
5
+ const it: typeof import('vitest')['it']
6
+ const expect: typeof import('vitest')['expect']
7
+ const assert: typeof import('vitest')['assert']
8
+ const spy: typeof import('vitest')['spy']
9
+ const mock: typeof import('vitest')['mock']
10
+ const stub: typeof import('vitest')['stub']
11
+ const sinon: typeof import('vitest')['sinon']
12
+ const beforeAll: typeof import('vitest')['beforeAll']
13
+ const afterAll: typeof import('vitest')['afterAll']
14
+ const beforeEach: typeof import('vitest')['beforeEach']
15
+ const afterEach: typeof import('vitest')['afterEach']
16
+ const beforeFile: typeof import('vitest')['beforeFile']
17
+ const afterFile: typeof import('vitest')['afterFile']
18
+ const beforeSuite: typeof import('vitest')['beforeSuite']
19
+ const afterSuite: typeof import('vitest')['afterSuite']
20
+ }
21
+ export {}
package/index.d.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './dist/index'
package/package.json CHANGED
@@ -1,7 +1,6 @@
1
1
  {
2
2
  "name": "vitest",
3
- "version": "0.0.12",
4
- "type": "module",
3
+ "version": "0.0.16",
5
4
  "description": "",
6
5
  "keywords": [],
7
6
  "homepage": "https://github.com/antfu/vitest#readme",
@@ -15,6 +14,7 @@
15
14
  "funding": "https://github.com/sponsors/antfu",
16
15
  "license": "MIT",
17
16
  "author": "Anthony Fu <anthonyfu117@hotmail.com>",
17
+ "type": "module",
18
18
  "exports": {
19
19
  ".": {
20
20
  "import": "./dist/index.js",
@@ -25,24 +25,23 @@
25
25
  "main": "./dist/index.js",
26
26
  "module": "./dist/index.js",
27
27
  "types": "./dist/index.d.ts",
28
- "files": [
29
- "dist",
30
- "bin"
31
- ],
32
28
  "bin": {
33
29
  "vitest": "./bin/vitest.mjs"
34
30
  },
35
- "devDependencies": {
36
- "@antfu/eslint-config": "^0.11.1",
37
- "@antfu/ni": "^0.11.0",
38
- "@types/minimist": "^1.2.2",
39
- "@types/node": "^16.11.11",
40
- "@types/sinon": "^10.0.6",
41
- "bumpp": "^7.1.1",
42
- "eslint": "^8.3.0",
43
- "esno": "^0.12.1",
44
- "typescript": "^4.5.2",
45
- "vite": "^2.6.14"
31
+ "files": [
32
+ "dist",
33
+ "bin",
34
+ "*.d.ts"
35
+ ],
36
+ "scripts": {
37
+ "prepare": "esmo scripts/generate-types.ts",
38
+ "build": "rimraf dist && tsc -p src/tsconfig.json",
39
+ "lint": "eslint \"{src,test}/**/*.ts\"",
40
+ "prepublishOnly": "nr build",
41
+ "release": "bumpp --commit --push --tag && pnpm publish",
42
+ "test": "node bin/vitest.mjs --dev",
43
+ "test:update": "nr test -u",
44
+ "watch": "tsc -p src/tsconfig.json --watch"
46
45
  },
47
46
  "dependencies": {
48
47
  "@jest/test-result": "^27.4.2",
@@ -53,19 +52,27 @@
53
52
  "find-up": "^6.2.0",
54
53
  "jest-snapshot": "^27.4.2",
55
54
  "jest-util": "^27.4.2",
55
+ "jsdom": "^19.0.0",
56
+ "listr": "^0.14.3",
56
57
  "minimist": "^1.2.5",
57
58
  "ora": "^6.0.1",
58
59
  "picocolors": "^1.0.0",
59
60
  "sinon": "^12.0.1",
60
- "sinon-chai": "^3.7.0",
61
- "vite-node": "^0.1.10"
61
+ "sinon-chai": "^3.7.0"
62
62
  },
63
- "scripts": {
64
- "build": "tsc",
65
- "watch": "tsc --watch",
66
- "lint": "eslint \"{src,test}/**/*.ts\"",
67
- "release": "bumpp --commit --push --tag && pnpm publish",
68
- "test": "node bin/vitest.mjs --dev",
69
- "test:update": "nr test -u"
63
+ "devDependencies": {
64
+ "@antfu/eslint-config": "^0.11.1",
65
+ "@antfu/ni": "^0.11.0",
66
+ "@types/jsdom": "^16.2.13",
67
+ "@types/listr": "^0.14.4",
68
+ "@types/minimist": "^1.2.2",
69
+ "@types/node": "^16.11.11",
70
+ "@types/sinon": "^10.0.6",
71
+ "bumpp": "^7.1.1",
72
+ "eslint": "^8.3.0",
73
+ "esno": "^0.12.1",
74
+ "rimraf": "^3.0.2",
75
+ "typescript": "^4.5.2",
76
+ "vite": "^2.6.14"
70
77
  }
71
- }
78
+ }
File without changes
@@ -1 +0,0 @@
1
- "use strict";
@@ -1,9 +0,0 @@
1
- import { use as chaiUse } from 'chai';
2
- declare type FirstFunctionArgument<T> = T extends (arg: infer A) => unknown ? A : never;
3
- declare type ChaiPlugin = FirstFunctionArgument<typeof chaiUse>;
4
- export interface SnapshotOptions {
5
- rootDir: string;
6
- update?: boolean;
7
- }
8
- export declare function SnapshotPlugin(options: SnapshotOptions): Promise<ChaiPlugin>;
9
- export {};
File without changes
@@ -1 +0,0 @@
1
- "use strict";
File without changes
@@ -1 +0,0 @@
1
- "use strict";