vitest 0.0.3 → 0.0.8
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.md +62 -0
- package/bin/vitest.mjs +12 -3
- package/dist/chai.d.ts +12 -0
- package/dist/chai.js +1 -0
- package/dist/cli.js +8 -8
- package/dist/config.d.ts +17 -0
- package/dist/config.js +3 -0
- package/dist/hooks.d.ts +14 -15
- package/dist/hooks.js +4 -4
- package/dist/index.d.ts +3 -12
- package/dist/index.js +3 -1
- package/dist/reporters/default.d.ts +18 -0
- package/dist/reporters/default.js +86 -0
- package/dist/run.d.ts +4 -4
- package/dist/run.js +75 -65
- package/dist/snapshot/index.d.ts +1 -1
- package/dist/snapshot/index.js +7 -9
- package/dist/suite.d.ts +21 -6
- package/dist/suite.js +50 -16
- package/dist/types.d.ts +47 -11
- package/package.json +13 -13
package/README.md
CHANGED
|
@@ -11,6 +11,7 @@ A blazing fast test runner powered by Vite.
|
|
|
11
11
|
- Chai for assertions.
|
|
12
12
|
- Async suite / test.
|
|
13
13
|
- ESM friendly, top level await.
|
|
14
|
+
- Suite and Test filtering (skip, only, todo).
|
|
14
15
|
|
|
15
16
|
```ts
|
|
16
17
|
import { it, describe, expect, assert } from 'vitest'
|
|
@@ -34,6 +35,67 @@ describe('suite name', () => {
|
|
|
34
35
|
$ npx vitest
|
|
35
36
|
```
|
|
36
37
|
|
|
38
|
+
## Filtering
|
|
39
|
+
|
|
40
|
+
### Skipping suites and tasks
|
|
41
|
+
|
|
42
|
+
Use `.skip` to avoid running certain suites or tests
|
|
43
|
+
|
|
44
|
+
```ts
|
|
45
|
+
describe.skip('skipped suite', () => {
|
|
46
|
+
it('task', () => {
|
|
47
|
+
// Suite skipped, no error
|
|
48
|
+
assert.equal(Math.sqrt(4), 3)
|
|
49
|
+
})
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
describe('suite', () => {
|
|
53
|
+
it.skip('skipped task', () => {
|
|
54
|
+
// Task skipped, no error
|
|
55
|
+
assert.equal(Math.sqrt(4), 3)
|
|
56
|
+
})
|
|
57
|
+
})
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Selecting suites and tests to run
|
|
61
|
+
|
|
62
|
+
Use `.only` to only run certain suites or tests
|
|
63
|
+
|
|
64
|
+
```ts
|
|
65
|
+
// Only this suite (and others marked with only) are run
|
|
66
|
+
describe.only('suite', () => {
|
|
67
|
+
it('task', () => {
|
|
68
|
+
assert.equal(Math.sqrt(4), 3)
|
|
69
|
+
})
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
describe('another suite', () => {
|
|
73
|
+
it('skipped task', () => {
|
|
74
|
+
// Task skipped, as tests are running in Only mode
|
|
75
|
+
assert.equal(Math.sqrt(4), 3)
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
it.only('task', () => {
|
|
79
|
+
// Only this task (and others marked with only) are run
|
|
80
|
+
assert.equal(Math.sqrt(4), 2)
|
|
81
|
+
})
|
|
82
|
+
})
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Unimplemented suites and tests
|
|
86
|
+
|
|
87
|
+
Use `.todo` to stub suites and tests that should be implemented
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
// An entry will be shown in the report for this suite
|
|
91
|
+
describe.todo('unimplemented suite')
|
|
92
|
+
|
|
93
|
+
// An entry will be shown in the report for this task
|
|
94
|
+
describe.suite('suite', () => {
|
|
95
|
+
it.todo('unimplemented task')
|
|
96
|
+
})
|
|
97
|
+
```
|
|
98
|
+
|
|
37
99
|
## TODO
|
|
38
100
|
|
|
39
101
|
- [ ] Reporter & Better output
|
package/bin/vitest.mjs
CHANGED
|
@@ -5,18 +5,27 @@ import { fileURLToPath } from 'url'
|
|
|
5
5
|
import { resolve, dirname } from 'path'
|
|
6
6
|
import { run } from 'vite-node'
|
|
7
7
|
import minimist from 'minimist'
|
|
8
|
+
import { findUp } from 'find-up'
|
|
8
9
|
|
|
9
10
|
const argv = minimist(process.argv.slice(2), {
|
|
10
|
-
|
|
11
|
+
alias: {
|
|
12
|
+
c: 'config',
|
|
13
|
+
},
|
|
14
|
+
string: ['root', 'config'],
|
|
15
|
+
boolean: ['dev'],
|
|
11
16
|
})
|
|
12
17
|
|
|
13
18
|
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
19
|
+
const root = resolve(argv.root || process.cwd())
|
|
20
|
+
|
|
21
|
+
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 })
|
|
14
22
|
|
|
15
23
|
await run({
|
|
16
|
-
root
|
|
24
|
+
root,
|
|
17
25
|
files: [
|
|
18
|
-
resolve(__dirname, '../dist/cli.js'),
|
|
26
|
+
resolve(__dirname, argv.dev ? '../src/cli.ts' : '../dist/cli.js'),
|
|
19
27
|
],
|
|
28
|
+
config: configPath,
|
|
20
29
|
defaultConfig: {
|
|
21
30
|
optimizeDeps: {
|
|
22
31
|
exclude: [
|
package/dist/chai.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export { assert, should, expect } from 'chai';
|
|
2
|
+
declare global {
|
|
3
|
+
namespace Chai {
|
|
4
|
+
interface Assertion {
|
|
5
|
+
toMatchSnapshot(message?: string): Assertion;
|
|
6
|
+
matchSnapshot(message?: string): Assertion;
|
|
7
|
+
}
|
|
8
|
+
interface ExpectStatic {
|
|
9
|
+
addSnapshotSerializer: import('pretty-format').Plugin;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
}
|
package/dist/chai.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { assert, should, expect } from 'chai';
|
package/dist/cli.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
|
|
1
|
+
var _a;
|
|
2
2
|
import minimist from 'minimist';
|
|
3
3
|
import c from 'picocolors';
|
|
4
4
|
import { run } from './run';
|
|
@@ -7,8 +7,8 @@ const argv = minimist(process.argv.slice(2), {
|
|
|
7
7
|
alias: {
|
|
8
8
|
u: 'update',
|
|
9
9
|
},
|
|
10
|
-
string: ['root'],
|
|
11
|
-
boolean: ['update'],
|
|
10
|
+
string: ['root', 'config'],
|
|
11
|
+
boolean: ['update', 'dev'],
|
|
12
12
|
unknown(name) {
|
|
13
13
|
if (name[0] === '-') {
|
|
14
14
|
console.error(c.red(`Unknown argument: ${name}`));
|
|
@@ -18,11 +18,11 @@ const argv = minimist(process.argv.slice(2), {
|
|
|
18
18
|
return true;
|
|
19
19
|
},
|
|
20
20
|
});
|
|
21
|
-
//
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
});
|
|
21
|
+
// @ts-expect-error
|
|
22
|
+
const server = (_a = process === null || process === void 0 ? void 0 : process.__vite_node__) === null || _a === void 0 ? void 0 : _a.server;
|
|
23
|
+
const viteConfig = (server === null || server === void 0 ? void 0 : server.config) || {};
|
|
24
|
+
const testOptions = viteConfig.test || {};
|
|
25
|
+
await run(Object.assign(Object.assign({}, testOptions), { updateSnapshot: argv.update, rootDir: argv.root || process.cwd() }));
|
|
26
26
|
function help() {
|
|
27
27
|
log('Help: finish help');
|
|
28
28
|
}
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { UserConfig } from 'vite';
|
|
2
|
+
import { UserOptions } from './types';
|
|
3
|
+
export interface VitestConfig extends UserConfig {
|
|
4
|
+
/**
|
|
5
|
+
* Options for Vitest
|
|
6
|
+
*/
|
|
7
|
+
test?: UserOptions;
|
|
8
|
+
}
|
|
9
|
+
export declare function defineConfig(config: VitestConfig): VitestConfig;
|
|
10
|
+
declare module 'vite' {
|
|
11
|
+
interface UserConfig {
|
|
12
|
+
/**
|
|
13
|
+
* Options for Vitest
|
|
14
|
+
*/
|
|
15
|
+
test?: UserOptions;
|
|
16
|
+
}
|
|
17
|
+
}
|
package/dist/config.js
ADDED
package/dist/hooks.d.ts
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import { Suite, Task } from './types';
|
|
2
|
-
|
|
3
|
-
export declare const beforeHook: {
|
|
1
|
+
import { File, Suite, Task } from './types';
|
|
2
|
+
export declare const beforeAllHook: {
|
|
4
3
|
on(fn: (...args: any[]) => void | Promise<void>): void;
|
|
5
4
|
fire(...args: any[]): Promise<void>;
|
|
6
5
|
clear(): void;
|
|
7
6
|
};
|
|
8
|
-
export declare const
|
|
7
|
+
export declare const afterAllHook: {
|
|
9
8
|
on(fn: (...args: any[]) => void | Promise<void>): void;
|
|
10
9
|
fire(...args: any[]): Promise<void>;
|
|
11
10
|
clear(): void;
|
|
@@ -16,18 +15,18 @@ export declare const beforeEachHook: {
|
|
|
16
15
|
clear(): void;
|
|
17
16
|
};
|
|
18
17
|
export declare const afterEachHook: {
|
|
19
|
-
on(fn: (args_0: Task
|
|
20
|
-
fire(args_0: Task
|
|
18
|
+
on(fn: (args_0: Task) => void | Promise<void>): void;
|
|
19
|
+
fire(args_0: Task): Promise<void>;
|
|
21
20
|
clear(): void;
|
|
22
21
|
};
|
|
23
22
|
export declare const beforeFileHook: {
|
|
24
|
-
on(fn: (args_0:
|
|
25
|
-
fire(args_0:
|
|
23
|
+
on(fn: (args_0: File) => void | Promise<void>): void;
|
|
24
|
+
fire(args_0: File): Promise<void>;
|
|
26
25
|
clear(): void;
|
|
27
26
|
};
|
|
28
27
|
export declare const afterFileHook: {
|
|
29
|
-
on(fn: (args_0:
|
|
30
|
-
fire(args_0:
|
|
28
|
+
on(fn: (args_0: File) => void | Promise<void>): void;
|
|
29
|
+
fire(args_0: File): Promise<void>;
|
|
31
30
|
clear(): void;
|
|
32
31
|
};
|
|
33
32
|
export declare const beforeSuiteHook: {
|
|
@@ -40,11 +39,11 @@ export declare const afterSuiteHook: {
|
|
|
40
39
|
fire(args_0: Suite): Promise<void>;
|
|
41
40
|
clear(): void;
|
|
42
41
|
};
|
|
43
|
-
export declare const
|
|
44
|
-
export declare const
|
|
42
|
+
export declare const beforeAll: (fn: (...args: any[]) => void | Promise<void>) => void;
|
|
43
|
+
export declare const afterAll: (fn: (...args: any[]) => void | Promise<void>) => void;
|
|
45
44
|
export declare const beforeEach: (fn: (args_0: Task) => void | Promise<void>) => void;
|
|
46
|
-
export declare const afterEach: (fn: (args_0: Task
|
|
47
|
-
export declare const beforeFile: (fn: (args_0:
|
|
48
|
-
export declare const afterFile: (fn: (args_0:
|
|
45
|
+
export declare const afterEach: (fn: (args_0: Task) => void | Promise<void>) => void;
|
|
46
|
+
export declare const beforeFile: (fn: (args_0: File) => void | Promise<void>) => void;
|
|
47
|
+
export declare const afterFile: (fn: (args_0: File) => void | Promise<void>) => void;
|
|
49
48
|
export declare const beforeSuite: (fn: (args_0: Suite) => void | Promise<void>) => void;
|
|
50
49
|
export declare const afterSuite: (fn: (args_0: Suite) => void | Promise<void>) => void;
|
package/dist/hooks.js
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { createHook } from './utils/hook';
|
|
2
|
-
export const
|
|
3
|
-
export const
|
|
2
|
+
export const beforeAllHook = createHook();
|
|
3
|
+
export const afterAllHook = createHook();
|
|
4
4
|
export const beforeEachHook = createHook();
|
|
5
5
|
export const afterEachHook = createHook();
|
|
6
6
|
export const beforeFileHook = createHook();
|
|
7
7
|
export const afterFileHook = createHook();
|
|
8
8
|
export const beforeSuiteHook = createHook();
|
|
9
9
|
export const afterSuiteHook = createHook();
|
|
10
|
-
export const
|
|
11
|
-
export const
|
|
10
|
+
export const beforeAll = beforeAllHook.on;
|
|
11
|
+
export const afterAll = afterAllHook.on;
|
|
12
12
|
export const beforeEach = beforeEachHook.on;
|
|
13
13
|
export const afterEach = afterEachHook.on;
|
|
14
14
|
export const beforeFile = beforeFileHook.on;
|
package/dist/index.d.ts
CHANGED
|
@@ -1,14 +1,5 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export * from './suite';
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
interface Assertion {
|
|
7
|
-
toMatchSnapshot(message?: string): Assertion;
|
|
8
|
-
matchSnapshot(message?: string): Assertion;
|
|
9
|
-
}
|
|
10
|
-
interface ExpectStatic {
|
|
11
|
-
addSnapshotSerializer: import('pretty-format').Plugin;
|
|
12
|
-
}
|
|
13
|
-
}
|
|
14
|
-
}
|
|
3
|
+
export * from './config';
|
|
4
|
+
export * from './chai';
|
|
5
|
+
export { beforeAll, afterAll, beforeEach, afterEach, beforeFile, afterFile, beforeSuite, afterSuite } from './hooks';
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
export * from './types';
|
|
2
2
|
export * from './suite';
|
|
3
|
-
export
|
|
3
|
+
export * from './config';
|
|
4
|
+
export * from './chai';
|
|
5
|
+
export { beforeAll, afterAll, beforeEach, afterEach, beforeFile, afterFile, beforeSuite, afterSuite } from './hooks';
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { File, Reporter, RunnerContext, Suite, Task } from '../types';
|
|
2
|
+
export declare class DefaultReporter implements Reporter {
|
|
3
|
+
indent: number;
|
|
4
|
+
start: number;
|
|
5
|
+
end: number;
|
|
6
|
+
onStart(): void;
|
|
7
|
+
onCollected(): void;
|
|
8
|
+
onFinished({ files }: RunnerContext): void;
|
|
9
|
+
onSuiteBegin(suite: Suite): void;
|
|
10
|
+
onSuiteEnd(suite: Suite): void;
|
|
11
|
+
onFileBegin(file: File): void;
|
|
12
|
+
onFileEnd(): void;
|
|
13
|
+
onTaskBegin(): void;
|
|
14
|
+
onTaskEnd(t: Task): void;
|
|
15
|
+
log(msg?: string, indentDelta?: number): void;
|
|
16
|
+
error(msg?: string, indentDelta?: number): void;
|
|
17
|
+
onSnapshotUpdate(): void;
|
|
18
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { relative } from 'path';
|
|
2
|
+
import { performance } from 'perf_hooks';
|
|
3
|
+
import c from 'picocolors';
|
|
4
|
+
const DOT = '· ';
|
|
5
|
+
export class DefaultReporter {
|
|
6
|
+
constructor() {
|
|
7
|
+
this.indent = 0;
|
|
8
|
+
this.start = 0;
|
|
9
|
+
this.end = 0;
|
|
10
|
+
}
|
|
11
|
+
onStart() {
|
|
12
|
+
this.indent = 0;
|
|
13
|
+
}
|
|
14
|
+
onCollected() {
|
|
15
|
+
this.start = performance.now();
|
|
16
|
+
}
|
|
17
|
+
onFinished({ files }) {
|
|
18
|
+
this.end = performance.now();
|
|
19
|
+
const tasks = files.reduce((acc, file) => acc.concat(file.suites.flatMap(i => i.tasks)), []);
|
|
20
|
+
const passed = tasks.filter(i => i.status === 'pass');
|
|
21
|
+
const failed = tasks.filter(i => i.status === 'fail');
|
|
22
|
+
const skipped = tasks.filter(i => i.status === 'skip');
|
|
23
|
+
const todo = tasks.filter(i => i.status === 'todo');
|
|
24
|
+
this.indent = 0;
|
|
25
|
+
this.log(c.green(`Passed ${passed.length} / ${tasks.length}`));
|
|
26
|
+
if (skipped.length)
|
|
27
|
+
this.log(c.yellow(`Skipped ${skipped.length}`));
|
|
28
|
+
if (todo.length)
|
|
29
|
+
this.log(c.dim(`Todo ${todo.length}`));
|
|
30
|
+
if (failed.length)
|
|
31
|
+
this.log(c.red(`Failed ${failed.length} / ${tasks.length}`));
|
|
32
|
+
this.log(`Time ${(this.end - this.start).toFixed(2)}ms`);
|
|
33
|
+
}
|
|
34
|
+
onSuiteBegin(suite) {
|
|
35
|
+
if (suite.name) {
|
|
36
|
+
this.indent += 1;
|
|
37
|
+
const name = DOT + suite.name;
|
|
38
|
+
if (suite.mode === 'skip')
|
|
39
|
+
this.log(c.dim(c.yellow(`${name} (skipped)`)));
|
|
40
|
+
else if (suite.mode === 'todo')
|
|
41
|
+
this.log(c.dim(`${name} (todo)`));
|
|
42
|
+
else
|
|
43
|
+
this.log(name);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
onSuiteEnd(suite) {
|
|
47
|
+
if (suite.name)
|
|
48
|
+
this.indent -= 1;
|
|
49
|
+
}
|
|
50
|
+
onFileBegin(file) {
|
|
51
|
+
this.log(`- ${relative(process.cwd(), file.filepath)} ${c.dim(`(${file.suites.flatMap(i => i.tasks).length} tests)`)}`);
|
|
52
|
+
}
|
|
53
|
+
onFileEnd() {
|
|
54
|
+
this.log();
|
|
55
|
+
}
|
|
56
|
+
onTaskBegin() {
|
|
57
|
+
this.indent += 1;
|
|
58
|
+
}
|
|
59
|
+
onTaskEnd(t) {
|
|
60
|
+
if (t.status === 'pass') {
|
|
61
|
+
this.log(`${c.green(`✔ ${t.name}`)}`);
|
|
62
|
+
}
|
|
63
|
+
else if (t.status === 'skip') {
|
|
64
|
+
this.log(c.dim(c.yellow(`${DOT + t.name} (skipped)`)));
|
|
65
|
+
}
|
|
66
|
+
else if (t.status === 'todo') {
|
|
67
|
+
this.log(c.dim(`${DOT + t.name} (todo)`));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.error(`${c.red(`⤫ ${c.inverse(c.red(' FAIL '))} ${t.name}`)}`);
|
|
71
|
+
this.error(String(t.error), 1);
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
}
|
|
74
|
+
this.indent -= 1;
|
|
75
|
+
}
|
|
76
|
+
log(msg = '', indentDelta = 0) {
|
|
77
|
+
// eslint-disable-next-line no-console
|
|
78
|
+
console.log(`${' '.repeat((this.indent + indentDelta) * 2)}${msg}`);
|
|
79
|
+
}
|
|
80
|
+
error(msg = '', indentDelta = 0) {
|
|
81
|
+
// eslint-disable-next-line no-console
|
|
82
|
+
console.error(c.red(`${' '.repeat((this.indent + indentDelta) * 2)}${msg}`));
|
|
83
|
+
}
|
|
84
|
+
onSnapshotUpdate() {
|
|
85
|
+
}
|
|
86
|
+
}
|
package/dist/run.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { File, Options, Task,
|
|
2
|
-
export declare function
|
|
3
|
-
export declare function
|
|
4
|
-
export declare function runFile(
|
|
1
|
+
import { File, Options, Task, RunnerContext } from './types';
|
|
2
|
+
export declare function runTask(task: Task, ctx: RunnerContext): Promise<void>;
|
|
3
|
+
export declare function collectFiles(files: string[]): Promise<File[]>;
|
|
4
|
+
export declare function runFile(file: File, ctx: RunnerContext): Promise<void>;
|
|
5
5
|
export declare function run(options?: Options): Promise<void>;
|
package/dist/run.js
CHANGED
|
@@ -1,93 +1,103 @@
|
|
|
1
|
-
import { relative } from 'path';
|
|
2
|
-
import c from 'picocolors';
|
|
3
1
|
import chai from 'chai';
|
|
4
2
|
import fg from 'fast-glob';
|
|
5
3
|
import { clearContext, defaultSuite } from './suite';
|
|
6
4
|
import { context } from './context';
|
|
7
|
-
import { afterEachHook, afterFileHook,
|
|
8
|
-
import { SnapshotPlugin } from './snapshot
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
5
|
+
import { afterEachHook, afterFileHook, afterAllHook, afterSuiteHook, beforeEachHook, beforeFileHook, beforeAllHook, beforeSuiteHook } from './hooks';
|
|
6
|
+
import { SnapshotPlugin } from './snapshot';
|
|
7
|
+
import { DefaultReporter } from './reporters/default';
|
|
8
|
+
export async function runTask(task, ctx) {
|
|
9
|
+
var _a, _b;
|
|
10
|
+
const { reporter } = ctx;
|
|
11
|
+
await ((_a = reporter.onTaskBegin) === null || _a === void 0 ? void 0 : _a.call(reporter, task, ctx));
|
|
12
|
+
await beforeEachHook.fire(task);
|
|
13
|
+
if (task.suite.mode === 'skip' || task.mode === 'skip') {
|
|
14
|
+
task.status = 'skip';
|
|
15
|
+
}
|
|
16
|
+
else if (task.suite.mode === 'todo' || task.mode === 'todo') {
|
|
17
|
+
task.status = 'todo';
|
|
18
|
+
}
|
|
19
|
+
else {
|
|
14
20
|
try {
|
|
15
21
|
await task.fn();
|
|
22
|
+
task.status = 'pass';
|
|
16
23
|
}
|
|
17
24
|
catch (e) {
|
|
18
|
-
|
|
25
|
+
task.status = 'fail';
|
|
26
|
+
task.error = e;
|
|
19
27
|
}
|
|
20
|
-
results.push(result);
|
|
21
|
-
await afterEachHook.fire(task, result);
|
|
22
28
|
}
|
|
23
|
-
|
|
29
|
+
await afterEachHook.fire(task);
|
|
30
|
+
await ((_b = reporter.onTaskEnd) === null || _b === void 0 ? void 0 : _b.call(reporter, task, ctx));
|
|
24
31
|
}
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
tasks,
|
|
40
|
-
};
|
|
41
|
-
file.tasks.forEach(([, tasks]) => tasks.forEach(task => task.file = file));
|
|
42
|
-
return file;
|
|
43
|
-
}
|
|
44
|
-
export async function runFile(filepath) {
|
|
45
|
-
await beforeFileHook.fire(filepath);
|
|
46
|
-
const file = await parseFile(filepath);
|
|
47
|
-
for (const [suite, tasks] of file.tasks) {
|
|
48
|
-
let indent = 1;
|
|
49
|
-
if (suite.name) {
|
|
50
|
-
log(' '.repeat(indent * 2) + suite.name);
|
|
51
|
-
indent += 1;
|
|
52
|
-
}
|
|
53
|
-
const result = await runTasks(tasks);
|
|
54
|
-
for (const r of result) {
|
|
55
|
-
if (r.error === undefined) {
|
|
56
|
-
log(`${' '.repeat(indent * 2)}${c.inverse(c.green(' PASS '))} ${c.green(r.task.name)}`);
|
|
57
|
-
}
|
|
58
|
-
else {
|
|
59
|
-
console.error(`${' '.repeat(indent * 2)}${c.inverse(c.red(' FAIL '))} ${c.red(r.task.name)}`);
|
|
60
|
-
console.error(' '.repeat((indent + 2) * 2) + c.red(String(r.error)));
|
|
61
|
-
process.exitCode = 1;
|
|
62
|
-
}
|
|
32
|
+
export async function collectFiles(files) {
|
|
33
|
+
const result = [];
|
|
34
|
+
for (const filepath of files) {
|
|
35
|
+
clearContext();
|
|
36
|
+
await import(filepath);
|
|
37
|
+
const collectors = [defaultSuite, ...context.suites];
|
|
38
|
+
const suites = [];
|
|
39
|
+
const file = {
|
|
40
|
+
filepath,
|
|
41
|
+
suites: [],
|
|
42
|
+
};
|
|
43
|
+
for (const c of collectors) {
|
|
44
|
+
context.currentSuite = c;
|
|
45
|
+
suites.push(await c.collect(file));
|
|
63
46
|
}
|
|
64
|
-
|
|
65
|
-
|
|
47
|
+
file.suites = suites;
|
|
48
|
+
result.push(file);
|
|
49
|
+
}
|
|
50
|
+
return result;
|
|
51
|
+
}
|
|
52
|
+
export async function runFile(file, ctx) {
|
|
53
|
+
var _a, _b, _c, _d;
|
|
54
|
+
const { reporter } = ctx;
|
|
55
|
+
await ((_a = reporter.onFileBegin) === null || _a === void 0 ? void 0 : _a.call(reporter, file, ctx));
|
|
56
|
+
await beforeFileHook.fire(file);
|
|
57
|
+
for (const suite of file.suites) {
|
|
58
|
+
await ((_b = reporter.onSuiteBegin) === null || _b === void 0 ? void 0 : _b.call(reporter, suite, ctx));
|
|
59
|
+
await beforeSuiteHook.fire(suite);
|
|
60
|
+
for (const t of suite.tasks)
|
|
61
|
+
await runTask(t, ctx);
|
|
66
62
|
await afterSuiteHook.fire(suite);
|
|
63
|
+
await ((_c = reporter.onSuiteEnd) === null || _c === void 0 ? void 0 : _c.call(reporter, suite, ctx));
|
|
67
64
|
}
|
|
68
|
-
await afterFileHook.fire(
|
|
65
|
+
await afterFileHook.fire(file);
|
|
66
|
+
await ((_d = reporter.onFileEnd) === null || _d === void 0 ? void 0 : _d.call(reporter, file, ctx));
|
|
69
67
|
}
|
|
70
68
|
export async function run(options = {}) {
|
|
69
|
+
var _a, _b, _c;
|
|
71
70
|
const { rootDir = process.cwd() } = options;
|
|
72
|
-
chai.use(SnapshotPlugin({
|
|
71
|
+
chai.use(await SnapshotPlugin({
|
|
73
72
|
rootDir,
|
|
74
73
|
update: options.updateSnapshot,
|
|
75
74
|
}));
|
|
76
|
-
const
|
|
75
|
+
const paths = await fg(options.includes || ['**/*.{test,spec}.{js,mjs,cjs,ts,mts,cts,jsx,tsx}'], {
|
|
77
76
|
absolute: true,
|
|
78
77
|
cwd: options.rootDir,
|
|
78
|
+
ignore: options.excludes || ['**/node_modules/**', '**/dist/**'],
|
|
79
79
|
});
|
|
80
|
-
if (!
|
|
80
|
+
if (!paths.length) {
|
|
81
81
|
console.error('No test files found');
|
|
82
82
|
process.exitCode = 1;
|
|
83
83
|
return;
|
|
84
84
|
}
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
85
|
+
const reporter = new DefaultReporter();
|
|
86
|
+
await ((_a = reporter.onStart) === null || _a === void 0 ? void 0 : _a.call(reporter, options));
|
|
87
|
+
const files = await collectFiles(paths);
|
|
88
|
+
const ctx = {
|
|
89
|
+
files,
|
|
90
|
+
mode: isOnlyMode(files) ? 'only' : 'all',
|
|
91
|
+
userOptions: options,
|
|
92
|
+
reporter,
|
|
93
|
+
};
|
|
94
|
+
await ((_b = reporter.onCollected) === null || _b === void 0 ? void 0 : _b.call(reporter, ctx));
|
|
95
|
+
await beforeAllHook.fire();
|
|
96
|
+
for (const file of files)
|
|
97
|
+
await runFile(file, ctx);
|
|
98
|
+
await afterAllHook.fire();
|
|
99
|
+
await ((_c = reporter.onFinished) === null || _c === void 0 ? void 0 : _c.call(reporter, ctx));
|
|
100
|
+
}
|
|
101
|
+
function isOnlyMode(files) {
|
|
102
|
+
return !!files.find(file => file.suites.find(suite => suite.mode === 'only' || suite.tasks.find(t => t.mode === 'only')));
|
|
93
103
|
}
|
package/dist/snapshot/index.d.ts
CHANGED
package/dist/snapshot/index.js
CHANGED
|
@@ -1,21 +1,19 @@
|
|
|
1
1
|
import Snap from 'jest-snapshot';
|
|
2
|
-
import {
|
|
2
|
+
import { afterAll, beforeEach } from '../hooks';
|
|
3
3
|
import { SnapshotManager } from './manager';
|
|
4
4
|
const { addSerializer } = Snap;
|
|
5
5
|
let _manager;
|
|
6
|
-
export function SnapshotPlugin(options) {
|
|
6
|
+
export async function SnapshotPlugin(options) {
|
|
7
7
|
const { rootDir } = options;
|
|
8
8
|
_manager = new SnapshotManager({
|
|
9
9
|
rootDir,
|
|
10
10
|
update: options.update,
|
|
11
11
|
});
|
|
12
|
+
_manager.snapshotResolver = await Snap.buildSnapshotResolver({
|
|
13
|
+
transform: [],
|
|
14
|
+
rootDir,
|
|
15
|
+
});
|
|
12
16
|
return function (chai, utils) {
|
|
13
|
-
before(async () => {
|
|
14
|
-
_manager.snapshotResolver = await Snap.buildSnapshotResolver({
|
|
15
|
-
transform: [],
|
|
16
|
-
rootDir,
|
|
17
|
-
});
|
|
18
|
-
});
|
|
19
17
|
beforeEach((task) => {
|
|
20
18
|
var _a;
|
|
21
19
|
_manager.setContext({
|
|
@@ -24,7 +22,7 @@ export function SnapshotPlugin(options) {
|
|
|
24
22
|
fullTitle: [task.suite.name, task.name].filter(Boolean).join(' > '),
|
|
25
23
|
});
|
|
26
24
|
});
|
|
27
|
-
|
|
25
|
+
afterAll(() => {
|
|
28
26
|
_manager.saveSnap();
|
|
29
27
|
_manager.report();
|
|
30
28
|
});
|
package/dist/suite.d.ts
CHANGED
|
@@ -1,7 +1,22 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export declare const defaultSuite:
|
|
3
|
-
export declare const test:
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
import { SuiteCollector, TestFactory, TestFunction } from './types';
|
|
2
|
+
export declare const defaultSuite: SuiteCollector;
|
|
3
|
+
export declare const test: {
|
|
4
|
+
(name: string, fn: TestFunction): void;
|
|
5
|
+
skip(name: string, fn: TestFunction): void;
|
|
6
|
+
only(name: string, fn: TestFunction): void;
|
|
7
|
+
todo(name: string): void;
|
|
8
|
+
};
|
|
9
|
+
export declare function suite(suiteName: string, factory?: TestFactory): SuiteCollector;
|
|
10
|
+
export declare namespace suite {
|
|
11
|
+
var skip: (suiteName: string, factory?: TestFactory | undefined) => SuiteCollector;
|
|
12
|
+
var only: (suiteName: string, factory?: TestFactory | undefined) => SuiteCollector;
|
|
13
|
+
var todo: (suiteName: string) => SuiteCollector;
|
|
14
|
+
}
|
|
6
15
|
export declare const describe: typeof suite;
|
|
7
|
-
export declare const it:
|
|
16
|
+
export declare const it: {
|
|
17
|
+
(name: string, fn: TestFunction): void;
|
|
18
|
+
skip(name: string, fn: TestFunction): void;
|
|
19
|
+
only(name: string, fn: TestFunction): void;
|
|
20
|
+
todo(name: string): void;
|
|
21
|
+
};
|
|
22
|
+
export declare function clearContext(): void;
|
package/dist/suite.js
CHANGED
|
@@ -1,41 +1,75 @@
|
|
|
1
1
|
import { context } from './context';
|
|
2
2
|
export const defaultSuite = suite('');
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
context.suites.length = 0;
|
|
6
|
-
defaultSuite.clear();
|
|
3
|
+
function getCurrentSuite() {
|
|
4
|
+
return context.currentSuite || defaultSuite;
|
|
7
5
|
}
|
|
8
|
-
export
|
|
6
|
+
export const test = (name, fn) => getCurrentSuite().test(name, fn);
|
|
7
|
+
test.skip = (name, fn) => getCurrentSuite().test.skip(name, fn);
|
|
8
|
+
test.only = (name, fn) => getCurrentSuite().test.only(name, fn);
|
|
9
|
+
test.todo = (name) => getCurrentSuite().test.todo(name);
|
|
10
|
+
function createSuiteCollector(mode, suiteName, factory) {
|
|
9
11
|
const queue = [];
|
|
10
12
|
const factoryQueue = [];
|
|
11
|
-
const
|
|
13
|
+
const collector = {
|
|
12
14
|
name: suiteName,
|
|
15
|
+
mode,
|
|
13
16
|
test,
|
|
14
17
|
collect,
|
|
15
18
|
clear,
|
|
16
19
|
};
|
|
17
|
-
function
|
|
18
|
-
|
|
19
|
-
suite,
|
|
20
|
+
function collectTask(name, fn, mode) {
|
|
21
|
+
queue.push({
|
|
20
22
|
name,
|
|
23
|
+
mode,
|
|
24
|
+
suite: {},
|
|
25
|
+
status: 'init',
|
|
21
26
|
fn,
|
|
22
|
-
};
|
|
23
|
-
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
function test(name, fn) {
|
|
30
|
+
collectTask(name, fn, mode);
|
|
24
31
|
}
|
|
32
|
+
test.skip = (name, fn) => collectTask(name, fn, 'skip');
|
|
33
|
+
test.only = (name, fn) => collectTask(name, fn, 'only');
|
|
34
|
+
test.todo = (name) => collectTask(name, () => { }, 'todo');
|
|
25
35
|
function clear() {
|
|
26
36
|
queue.length = 0;
|
|
27
37
|
factoryQueue.length = 0;
|
|
28
38
|
}
|
|
29
|
-
async function collect() {
|
|
39
|
+
async function collect(file) {
|
|
30
40
|
factoryQueue.length = 0;
|
|
31
41
|
if (factory)
|
|
32
42
|
await factory(test);
|
|
33
|
-
|
|
43
|
+
const tasks = [...factoryQueue, ...queue];
|
|
44
|
+
const suite = {
|
|
45
|
+
name: collector.name,
|
|
46
|
+
mode: collector.mode,
|
|
47
|
+
tasks,
|
|
48
|
+
file,
|
|
49
|
+
};
|
|
50
|
+
tasks.forEach((task) => {
|
|
51
|
+
task.suite = suite;
|
|
52
|
+
if (file)
|
|
53
|
+
task.file = file;
|
|
54
|
+
});
|
|
55
|
+
return suite;
|
|
34
56
|
}
|
|
35
|
-
context.currentSuite =
|
|
36
|
-
context.suites.push(
|
|
37
|
-
return
|
|
57
|
+
context.currentSuite = collector;
|
|
58
|
+
context.suites.push(collector);
|
|
59
|
+
return collector;
|
|
60
|
+
}
|
|
61
|
+
export function suite(suiteName, factory) {
|
|
62
|
+
return createSuiteCollector('run', suiteName, factory);
|
|
38
63
|
}
|
|
64
|
+
suite.skip = (suiteName, factory) => createSuiteCollector('skip', suiteName, factory);
|
|
65
|
+
suite.only = (suiteName, factory) => createSuiteCollector('only', suiteName, factory);
|
|
66
|
+
suite.todo = (suiteName) => createSuiteCollector('todo', suiteName);
|
|
39
67
|
// alias
|
|
40
68
|
export const describe = suite;
|
|
41
69
|
export const it = test;
|
|
70
|
+
// utils
|
|
71
|
+
export function clearContext() {
|
|
72
|
+
context.suites.length = 0;
|
|
73
|
+
defaultSuite.clear();
|
|
74
|
+
context.currentSuite = defaultSuite;
|
|
75
|
+
}
|
package/dist/types.d.ts
CHANGED
|
@@ -1,31 +1,67 @@
|
|
|
1
|
-
export
|
|
2
|
-
|
|
1
|
+
export declare type Awaitable<T> = Promise<T> | T;
|
|
2
|
+
export interface UserOptions {
|
|
3
3
|
includes?: string[];
|
|
4
4
|
excludes?: string[];
|
|
5
|
+
}
|
|
6
|
+
export interface Options extends UserOptions {
|
|
7
|
+
rootDir?: string;
|
|
5
8
|
updateSnapshot?: boolean;
|
|
6
9
|
}
|
|
10
|
+
export declare type RunMode = 'run' | 'skip' | 'only' | 'todo';
|
|
11
|
+
export declare type TaskStatus = 'init' | 'pass' | 'fail' | 'skip' | 'todo';
|
|
7
12
|
export interface Task {
|
|
8
13
|
name: string;
|
|
14
|
+
mode: RunMode;
|
|
9
15
|
suite: Suite;
|
|
10
|
-
fn: () =>
|
|
16
|
+
fn: () => Awaitable<void>;
|
|
11
17
|
file?: File;
|
|
12
|
-
|
|
13
|
-
export interface TaskResult {
|
|
14
|
-
task: Task;
|
|
18
|
+
status: TaskStatus;
|
|
15
19
|
error?: unknown;
|
|
16
20
|
}
|
|
21
|
+
export declare type TestFunction = () => Awaitable<void>;
|
|
22
|
+
export interface TestCollector {
|
|
23
|
+
(name: string, fn: TestFunction): void;
|
|
24
|
+
only: (name: string, fn: TestFunction) => void;
|
|
25
|
+
skip: (name: string, fn: TestFunction) => void;
|
|
26
|
+
todo: (name: string) => void;
|
|
27
|
+
}
|
|
17
28
|
export interface Suite {
|
|
18
29
|
name: string;
|
|
19
|
-
|
|
20
|
-
|
|
30
|
+
mode: RunMode;
|
|
31
|
+
tasks: Task[];
|
|
32
|
+
file?: File;
|
|
33
|
+
}
|
|
34
|
+
export interface SuiteCollector {
|
|
35
|
+
name: string;
|
|
36
|
+
mode: RunMode;
|
|
37
|
+
test: TestCollector;
|
|
38
|
+
collect: (file?: File) => Promise<Suite>;
|
|
21
39
|
clear: () => void;
|
|
22
40
|
}
|
|
41
|
+
export declare type TestFactory = (test: (name: string, fn: TestFunction) => void) => Awaitable<void>;
|
|
23
42
|
export interface File {
|
|
24
43
|
filepath: string;
|
|
25
44
|
suites: Suite[];
|
|
26
|
-
|
|
45
|
+
}
|
|
46
|
+
export interface RunnerContext {
|
|
47
|
+
files: File[];
|
|
48
|
+
mode: 'all' | 'only';
|
|
49
|
+
userOptions: Options;
|
|
50
|
+
reporter: Reporter;
|
|
27
51
|
}
|
|
28
52
|
export interface GlobalContext {
|
|
29
|
-
suites:
|
|
30
|
-
currentSuite:
|
|
53
|
+
suites: SuiteCollector[];
|
|
54
|
+
currentSuite: SuiteCollector | null;
|
|
55
|
+
}
|
|
56
|
+
export interface Reporter {
|
|
57
|
+
onStart: (userOptions: Options) => Awaitable<void>;
|
|
58
|
+
onCollected: (ctx: RunnerContext) => Awaitable<void>;
|
|
59
|
+
onFinished: (ctx: RunnerContext) => Awaitable<void>;
|
|
60
|
+
onSuiteBegin: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
|
|
61
|
+
onSuiteEnd: (suite: Suite, ctx: RunnerContext) => Awaitable<void>;
|
|
62
|
+
onFileBegin: (file: File, ctx: RunnerContext) => Awaitable<void>;
|
|
63
|
+
onFileEnd: (file: File, ctx: RunnerContext) => Awaitable<void>;
|
|
64
|
+
onTaskBegin: (task: Task, ctx: RunnerContext) => Awaitable<void>;
|
|
65
|
+
onTaskEnd: (task: Task, ctx: RunnerContext) => Awaitable<void>;
|
|
66
|
+
onSnapshotUpdate: () => Awaitable<void>;
|
|
31
67
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vitest",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.8",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "",
|
|
6
6
|
"keywords": [],
|
|
@@ -32,19 +32,9 @@
|
|
|
32
32
|
"bin": {
|
|
33
33
|
"vitest": "./bin/vitest.mjs"
|
|
34
34
|
},
|
|
35
|
-
"scripts": {
|
|
36
|
-
"build": "tsc",
|
|
37
|
-
"watch": "tsc --watch",
|
|
38
|
-
"lint": "eslint \"{src,test}/**/*.ts\"",
|
|
39
|
-
"prepublishOnly": "nr build",
|
|
40
|
-
"release": "bumpp --commit --push --tag && pnpm publish",
|
|
41
|
-
"test": "npx vite-node src/cli.ts --",
|
|
42
|
-
"test:update": "nr test -u"
|
|
43
|
-
},
|
|
44
35
|
"devDependencies": {
|
|
45
36
|
"@antfu/eslint-config": "^0.11.1",
|
|
46
37
|
"@antfu/ni": "^0.11.0",
|
|
47
|
-
"@types/chai": "^4.2.22",
|
|
48
38
|
"@types/minimist": "^1.2.2",
|
|
49
39
|
"@types/node": "^16.11.11",
|
|
50
40
|
"bumpp": "^7.1.1",
|
|
@@ -55,12 +45,22 @@
|
|
|
55
45
|
},
|
|
56
46
|
"dependencies": {
|
|
57
47
|
"@jest/test-result": "^27.4.2",
|
|
48
|
+
"@types/chai": "^4.2.22",
|
|
58
49
|
"chai": "^4.3.4",
|
|
59
50
|
"fast-glob": "^3.2.7",
|
|
51
|
+
"find-up": "^6.2.0",
|
|
60
52
|
"jest-snapshot": "^27.4.2",
|
|
61
53
|
"jest-util": "^27.4.2",
|
|
62
54
|
"minimist": "^1.2.5",
|
|
63
55
|
"picocolors": "^1.0.0",
|
|
64
|
-
"vite-node": "
|
|
56
|
+
"vite-node": "^0.1.10"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"build": "tsc",
|
|
60
|
+
"watch": "tsc --watch",
|
|
61
|
+
"lint": "eslint \"{src,test}/**/*.ts\"",
|
|
62
|
+
"release": "bumpp --commit --push --tag && pnpm publish",
|
|
63
|
+
"test": "node bin/vitest.mjs --dev",
|
|
64
|
+
"test:update": "nr test -u"
|
|
65
65
|
}
|
|
66
|
-
}
|
|
66
|
+
}
|