@japa/runner 3.0.0-8 → 3.0.0
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/build/factories/create_diverse_tests.d.ts +6 -0
- package/build/factories/create_diverse_tests.js +106 -0
- package/build/factories/main.d.ts +5 -42
- package/build/factories/main.js +24 -208
- package/build/factories/runner.d.ts +26 -0
- package/build/factories/runner.js +93 -0
- package/build/index.d.ts +9 -14
- package/build/index.js +202 -237
- package/build/modules/core/main.d.ts +63 -3
- package/build/modules/core/main.js +121 -21
- package/build/modules/core/reporters/base.d.ts +41 -0
- package/build/modules/core/reporters/base.js +183 -0
- package/build/modules/core/types.d.ts +5 -0
- package/build/modules/core/types.js +9 -0
- package/build/src/cli_parser.d.ts +14 -0
- package/build/src/cli_parser.js +75 -0
- package/build/src/config_manager.d.ts +18 -0
- package/build/src/config_manager.js +168 -0
- package/build/src/create_test.d.ts +21 -0
- package/build/src/create_test.js +53 -0
- package/build/src/debug.d.ts +3 -0
- package/build/src/debug.js +10 -0
- package/build/src/exceptions_manager.d.ts +19 -0
- package/build/src/exceptions_manager.js +85 -0
- package/build/src/files_manager.d.ts +18 -0
- package/build/src/files_manager.js +57 -0
- package/build/src/helpers.d.ts +15 -0
- package/build/src/helpers.js +34 -0
- package/build/src/hooks.d.ts +20 -0
- package/build/src/hooks.js +46 -0
- package/build/src/planner.d.ts +25 -0
- package/build/src/planner.js +98 -0
- package/build/src/plugins/retry.d.ts +20 -0
- package/build/src/plugins/retry.js +66 -0
- package/build/src/reporters/dot.d.ts +15 -0
- package/build/src/reporters/dot.js +41 -0
- package/build/src/reporters/main.d.ts +4 -9
- package/build/src/reporters/main.js +37 -11
- package/build/src/reporters/ndjson.d.ts +15 -0
- package/build/src/reporters/ndjson.js +86 -0
- package/build/src/reporters/spec.d.ts +13 -0
- package/build/src/reporters/spec.js +152 -0
- package/build/src/types.d.ts +20 -24
- package/build/src/types.js +9 -14
- package/build/src/validator.d.ts +30 -0
- package/build/src/validator.js +85 -0
- package/package.json +17 -28
- package/build/chunk-7THDHQFT.js +0 -283
- package/build/chunk-HN4AVHWN.js +0 -17
- package/build/chunk-MCOW34SG.js +0 -269
- package/build/chunk-W5IABAQU.js +0 -502
- package/build/main-63126780.d.ts +0 -109
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import { ErrorsPrinter } from '@japa/errors-printer';
|
|
10
|
+
/**
|
|
11
|
+
* Handles uncaught exceptions and prints them to the
|
|
12
|
+
* console
|
|
13
|
+
*/
|
|
14
|
+
export class ExceptionsManager {
|
|
15
|
+
#exceptionsBuffer = [];
|
|
16
|
+
#rejectionsBuffer = [];
|
|
17
|
+
#state = 'watching';
|
|
18
|
+
#errorsPrinter = new ErrorsPrinter({ stackLinesCount: 2, framesMaxLimit: 4 });
|
|
19
|
+
hasErrors = false;
|
|
20
|
+
/**
|
|
21
|
+
* Monitors unhandled exceptions and rejections. The exceptions
|
|
22
|
+
* are stacked in a buffer, so that we do not clutter the
|
|
23
|
+
* tests output and once the tests are over, we will
|
|
24
|
+
* print them to the console.
|
|
25
|
+
*
|
|
26
|
+
* In case the tests are completed, we will print errors as they
|
|
27
|
+
* happen.
|
|
28
|
+
*/
|
|
29
|
+
monitor() {
|
|
30
|
+
process.on('uncaughtException', async (error) => {
|
|
31
|
+
this.hasErrors = true;
|
|
32
|
+
if (this.#state === 'watching') {
|
|
33
|
+
this.#exceptionsBuffer.push(error);
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
this.#errorsPrinter.printSectionBorder('[Unhandled Error]');
|
|
37
|
+
await this.#errorsPrinter.printError(error);
|
|
38
|
+
process.exitCode = 1;
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
process.on('unhandledRejection', async (error) => {
|
|
42
|
+
this.hasErrors = true;
|
|
43
|
+
if (this.#state === 'watching') {
|
|
44
|
+
this.#rejectionsBuffer.push(error);
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
this.#errorsPrinter.printSectionBorder('[Unhandled Rejection]');
|
|
48
|
+
await this.#errorsPrinter.printError(error);
|
|
49
|
+
process.exitCode = 1;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
async flow() {
|
|
54
|
+
if (this.#state === 'flowing') {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
this.#state = 'flowing';
|
|
58
|
+
/**
|
|
59
|
+
* Print exceptions
|
|
60
|
+
*/
|
|
61
|
+
if (this.#exceptionsBuffer.length) {
|
|
62
|
+
let exceptionsCount = this.#exceptionsBuffer.length;
|
|
63
|
+
let exceptionsIndex = this.#exceptionsBuffer.length;
|
|
64
|
+
this.#errorsPrinter.printSectionHeader('Unhandled Errors');
|
|
65
|
+
for (let exception of this.#exceptionsBuffer) {
|
|
66
|
+
await this.#errorsPrinter.printError(exception);
|
|
67
|
+
this.#errorsPrinter.printSectionBorder(`[${++exceptionsIndex}/${exceptionsCount}]`);
|
|
68
|
+
}
|
|
69
|
+
this.#exceptionsBuffer = [];
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Print rejections
|
|
73
|
+
*/
|
|
74
|
+
if (this.#rejectionsBuffer.length) {
|
|
75
|
+
let rejectionsCount = this.#exceptionsBuffer.length;
|
|
76
|
+
let rejectionsIndex = this.#exceptionsBuffer.length;
|
|
77
|
+
this.#errorsPrinter.printSectionBorder('Unhandled Rejections');
|
|
78
|
+
for (let rejection of this.#rejectionsBuffer) {
|
|
79
|
+
await this.#errorsPrinter.printError(rejection);
|
|
80
|
+
this.#errorsPrinter.printSectionBorder(`[${++rejectionsIndex}/${rejectionsCount}]`);
|
|
81
|
+
}
|
|
82
|
+
this.#rejectionsBuffer = [];
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import type { TestFiles } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Files manager exposes the API to collect, filter and import test
|
|
5
|
+
* files based upon the config
|
|
6
|
+
*/
|
|
7
|
+
export declare class FilesManager {
|
|
8
|
+
/**
|
|
9
|
+
* Returns a collection of files from the user defined
|
|
10
|
+
* glob or the implementation function
|
|
11
|
+
*/
|
|
12
|
+
getFiles(cwd: string, files: TestFiles): Promise<URL[]>;
|
|
13
|
+
/**
|
|
14
|
+
* Applies file name filter on a collection of file
|
|
15
|
+
* URLs
|
|
16
|
+
*/
|
|
17
|
+
grep(files: URL[], filters: string[]): URL[];
|
|
18
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import slash from 'slash';
|
|
10
|
+
import fastGlob from 'fast-glob';
|
|
11
|
+
import { pathToFileURL } from 'node:url';
|
|
12
|
+
/**
|
|
13
|
+
* The expression to remove file extension and optionally
|
|
14
|
+
* .spec|.test from the test file name
|
|
15
|
+
*/
|
|
16
|
+
const FILE_SUFFIX_EXPRESSION = /(\.spec|\.test)?\.[js|ts|jsx|tsx|mjs|mts|cjs|cts]+$/;
|
|
17
|
+
/**
|
|
18
|
+
* Files manager exposes the API to collect, filter and import test
|
|
19
|
+
* files based upon the config
|
|
20
|
+
*/
|
|
21
|
+
export class FilesManager {
|
|
22
|
+
/**
|
|
23
|
+
* Returns a collection of files from the user defined
|
|
24
|
+
* glob or the implementation function
|
|
25
|
+
*/
|
|
26
|
+
async getFiles(cwd, files) {
|
|
27
|
+
if (Array.isArray(files) || typeof files === 'string') {
|
|
28
|
+
const testFiles = await fastGlob(files, {
|
|
29
|
+
absolute: true,
|
|
30
|
+
onlyFiles: true,
|
|
31
|
+
cwd: cwd,
|
|
32
|
+
});
|
|
33
|
+
return testFiles.map((file) => pathToFileURL(file));
|
|
34
|
+
}
|
|
35
|
+
return await files();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Applies file name filter on a collection of file
|
|
39
|
+
* URLs
|
|
40
|
+
*/
|
|
41
|
+
grep(files, filters) {
|
|
42
|
+
return files.filter((file) => {
|
|
43
|
+
const filename = slash(file.pathname);
|
|
44
|
+
const filenameWithoutTestSuffix = filename.replace(FILE_SUFFIX_EXPRESSION, '');
|
|
45
|
+
return !!filters.find((filter) => {
|
|
46
|
+
if (filename.endsWith(filter)) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
const filterSegments = filter.split('/').reverse();
|
|
50
|
+
const fileSegments = filenameWithoutTestSuffix.split('/').reverse();
|
|
51
|
+
return filterSegments.every((segment, index) => {
|
|
52
|
+
return fileSegments[index] && (segment === '*' || fileSegments[index].endsWith(segment));
|
|
53
|
+
});
|
|
54
|
+
});
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Colors } from '@poppinss/colors/types';
|
|
2
|
+
export declare const colors: Colors;
|
|
3
|
+
/**
|
|
4
|
+
* A collection of platform specific icons
|
|
5
|
+
*/
|
|
6
|
+
export declare const icons: {
|
|
7
|
+
tick: string;
|
|
8
|
+
cross: string;
|
|
9
|
+
bullet: string;
|
|
10
|
+
nodejs: string;
|
|
11
|
+
pointer: string;
|
|
12
|
+
info: string;
|
|
13
|
+
warning: string;
|
|
14
|
+
squareSmallFilled: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import useColors from '@poppinss/colors';
|
|
10
|
+
export const colors = useColors.ansi();
|
|
11
|
+
/**
|
|
12
|
+
* A collection of platform specific icons
|
|
13
|
+
*/
|
|
14
|
+
export const icons = process.platform === 'win32' && !process.env.WT_SESSION
|
|
15
|
+
? {
|
|
16
|
+
tick: '√',
|
|
17
|
+
cross: '×',
|
|
18
|
+
bullet: '*',
|
|
19
|
+
nodejs: '♦',
|
|
20
|
+
pointer: '>',
|
|
21
|
+
info: 'i',
|
|
22
|
+
warning: '‼',
|
|
23
|
+
squareSmallFilled: '[█]',
|
|
24
|
+
}
|
|
25
|
+
: {
|
|
26
|
+
tick: '✔',
|
|
27
|
+
cross: '✖',
|
|
28
|
+
bullet: '●',
|
|
29
|
+
nodejs: '⬢',
|
|
30
|
+
pointer: '❯',
|
|
31
|
+
info: 'ℹ',
|
|
32
|
+
warning: '⚠',
|
|
33
|
+
squareSmallFilled: '◼',
|
|
34
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Runner } from '../modules/core/main.js';
|
|
2
|
+
import type { NormalizedConfig } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Exposes API for working with global hooks
|
|
5
|
+
*/
|
|
6
|
+
export declare class GlobalHooks {
|
|
7
|
+
#private;
|
|
8
|
+
/**
|
|
9
|
+
* Apply hooks from the config
|
|
10
|
+
*/
|
|
11
|
+
apply(config: NormalizedConfig): void;
|
|
12
|
+
/**
|
|
13
|
+
* Perform setup
|
|
14
|
+
*/
|
|
15
|
+
setup(runner: Runner): Promise<void>;
|
|
16
|
+
/**
|
|
17
|
+
* Perform cleanup
|
|
18
|
+
*/
|
|
19
|
+
teardown(error: Error | null, runner: Runner): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import Hooks from '@poppinss/hooks';
|
|
10
|
+
/**
|
|
11
|
+
* Exposes API for working with global hooks
|
|
12
|
+
*/
|
|
13
|
+
export class GlobalHooks {
|
|
14
|
+
#hooks = new Hooks();
|
|
15
|
+
#setupRunner;
|
|
16
|
+
#teardownRunner;
|
|
17
|
+
/**
|
|
18
|
+
* Apply hooks from the config
|
|
19
|
+
*/
|
|
20
|
+
apply(config) {
|
|
21
|
+
config.setup.forEach((hook) => this.#hooks.add('setup', hook));
|
|
22
|
+
config.teardown.forEach((hook) => this.#hooks.add('teardown', hook));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Perform setup
|
|
26
|
+
*/
|
|
27
|
+
async setup(runner) {
|
|
28
|
+
this.#setupRunner = this.#hooks.runner('setup');
|
|
29
|
+
this.#teardownRunner = this.#hooks.runner('teardown');
|
|
30
|
+
await this.#setupRunner.run(runner);
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Perform cleanup
|
|
34
|
+
*/
|
|
35
|
+
async teardown(error, runner) {
|
|
36
|
+
if (this.#setupRunner) {
|
|
37
|
+
await this.#setupRunner.cleanup(error, runner);
|
|
38
|
+
}
|
|
39
|
+
if (this.#teardownRunner) {
|
|
40
|
+
if (!error) {
|
|
41
|
+
await this.#teardownRunner.run(runner);
|
|
42
|
+
}
|
|
43
|
+
await this.#teardownRunner.cleanup(error, runner);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/// <reference types="node" resolution-mode="require"/>
|
|
2
|
+
import type { NamedReporterContract, NormalizedConfig, TestSuite } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The tests planner is used to plan the tests by doing all
|
|
5
|
+
* the heavy lifting of executing plugins, registering
|
|
6
|
+
* reporters, filtering tests and so on.
|
|
7
|
+
*/
|
|
8
|
+
export declare class Planner {
|
|
9
|
+
#private;
|
|
10
|
+
constructor(config: NormalizedConfig);
|
|
11
|
+
/**
|
|
12
|
+
* Creates a plan for running the tests
|
|
13
|
+
*/
|
|
14
|
+
plan(): Promise<{
|
|
15
|
+
reporters: NamedReporterContract[];
|
|
16
|
+
suites: (TestSuite & {
|
|
17
|
+
filesURLs: URL[];
|
|
18
|
+
})[];
|
|
19
|
+
refinerFilters: {
|
|
20
|
+
layer: "tags" | "tests" | "groups";
|
|
21
|
+
filters: string[];
|
|
22
|
+
}[];
|
|
23
|
+
config: NormalizedConfig;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import validator from './validator.js';
|
|
10
|
+
import { FilesManager } from './files_manager.js';
|
|
11
|
+
/**
|
|
12
|
+
* The tests planner is used to plan the tests by doing all
|
|
13
|
+
* the heavy lifting of executing plugins, registering
|
|
14
|
+
* reporters, filtering tests and so on.
|
|
15
|
+
*/
|
|
16
|
+
export class Planner {
|
|
17
|
+
#config;
|
|
18
|
+
#fileManager = new FilesManager();
|
|
19
|
+
constructor(config) {
|
|
20
|
+
validator.validateActivatedReporters(config);
|
|
21
|
+
validator.validateSuitesFilter(config);
|
|
22
|
+
validator.validateSuitesForUniqueness(config);
|
|
23
|
+
this.#config = config;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Returns a list of reporters based upon the activated
|
|
27
|
+
* reporters list.
|
|
28
|
+
*/
|
|
29
|
+
#getActivatedReporters() {
|
|
30
|
+
return this.#config.reporters.activated.map((activated) => {
|
|
31
|
+
return this.#config.reporters.list.find(({ name }) => activated === name);
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* A generic method to collect files from the user defined
|
|
36
|
+
* files glob and apply the files filter
|
|
37
|
+
*/
|
|
38
|
+
async #collectFiles(files) {
|
|
39
|
+
let filesURLs = await this.#fileManager.getFiles(this.#config.cwd, files);
|
|
40
|
+
if (this.#config.filters.files && this.#config.filters.files.length) {
|
|
41
|
+
filesURLs = this.#fileManager.grep(filesURLs, this.#config.filters.files);
|
|
42
|
+
}
|
|
43
|
+
return filesURLs;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Returns a collection of suites and their associated
|
|
47
|
+
* test files by applying all the filters
|
|
48
|
+
*/
|
|
49
|
+
async #getSuites() {
|
|
50
|
+
let suites = [];
|
|
51
|
+
let suitesFilters = this.#config.filters.suites || [];
|
|
52
|
+
if ('files' in this.#config) {
|
|
53
|
+
suites.push({
|
|
54
|
+
name: 'default',
|
|
55
|
+
files: this.#config.files,
|
|
56
|
+
timeout: this.#config.timeout,
|
|
57
|
+
retries: this.#config.retries,
|
|
58
|
+
filesURLs: await this.#collectFiles(this.#config.files),
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
if ('suites' in this.#config) {
|
|
62
|
+
for (let suite of this.#config.suites) {
|
|
63
|
+
if (!suitesFilters.length || suitesFilters.includes(suite.name)) {
|
|
64
|
+
suites.push({
|
|
65
|
+
...suite,
|
|
66
|
+
filesURLs: await this.#collectFiles(suite.files),
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
return suites;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Returns a list of filters to the passed to the refiner
|
|
75
|
+
*/
|
|
76
|
+
#getRefinerFilters() {
|
|
77
|
+
return Object.keys(this.#config.filters).reduce((result, layer) => {
|
|
78
|
+
if (layer === 'tests' || layer === 'tags' || layer === 'groups') {
|
|
79
|
+
result.push({ layer, filters: this.#config.filters[layer] });
|
|
80
|
+
}
|
|
81
|
+
return result;
|
|
82
|
+
}, []);
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Creates a plan for running the tests
|
|
86
|
+
*/
|
|
87
|
+
async plan() {
|
|
88
|
+
const suites = await this.#getSuites();
|
|
89
|
+
const reporters = this.#getActivatedReporters();
|
|
90
|
+
const refinerFilters = this.#getRefinerFilters();
|
|
91
|
+
return {
|
|
92
|
+
reporters,
|
|
93
|
+
suites,
|
|
94
|
+
refinerFilters,
|
|
95
|
+
config: this.#config,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { PluginFn } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Returns an object with the title of the tests failed during
|
|
4
|
+
* the last run.
|
|
5
|
+
*/
|
|
6
|
+
export declare function getFailedTests(): Promise<{
|
|
7
|
+
tests?: string[];
|
|
8
|
+
}>;
|
|
9
|
+
/**
|
|
10
|
+
* Writes failing tests to the cache directory
|
|
11
|
+
*/
|
|
12
|
+
export declare function cacheFailedTests(tests: string[]): Promise<void>;
|
|
13
|
+
/**
|
|
14
|
+
* Clears the cache dir
|
|
15
|
+
*/
|
|
16
|
+
export declare function clearCache(): Promise<void>;
|
|
17
|
+
/**
|
|
18
|
+
* Exposes the API to run failing tests using the "failed" CLI flag.
|
|
19
|
+
*/
|
|
20
|
+
export declare const retryPlugin: PluginFn;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import { join } from 'node:path';
|
|
10
|
+
import findCacheDirectory from 'find-cache-dir';
|
|
11
|
+
import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
|
|
12
|
+
import { colors } from '../helpers.js';
|
|
13
|
+
/**
|
|
14
|
+
* Paths to the cache directory and the summary file
|
|
15
|
+
*/
|
|
16
|
+
const CACHE_DIR = findCacheDirectory({ name: '@japa/runner' });
|
|
17
|
+
const SUMMARY_FILE = CACHE_DIR ? join(CACHE_DIR, 'summary.json') : undefined;
|
|
18
|
+
/**
|
|
19
|
+
* Returns an object with the title of the tests failed during
|
|
20
|
+
* the last run.
|
|
21
|
+
*/
|
|
22
|
+
export async function getFailedTests() {
|
|
23
|
+
try {
|
|
24
|
+
const summary = await readFile(SUMMARY_FILE, 'utf-8');
|
|
25
|
+
return JSON.parse(summary);
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
if (error.code === 'ENOENT') {
|
|
29
|
+
return {};
|
|
30
|
+
}
|
|
31
|
+
throw new Error('Unable to read failed tests cache file', { cause: error });
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Writes failing tests to the cache directory
|
|
36
|
+
*/
|
|
37
|
+
export async function cacheFailedTests(tests) {
|
|
38
|
+
await mkdir(CACHE_DIR, { recursive: true });
|
|
39
|
+
await writeFile(SUMMARY_FILE, JSON.stringify({ tests: tests }));
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Clears the cache dir
|
|
43
|
+
*/
|
|
44
|
+
export async function clearCache() {
|
|
45
|
+
await unlink(SUMMARY_FILE);
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Exposes the API to run failing tests using the "failed" CLI flag.
|
|
49
|
+
*/
|
|
50
|
+
export const retryPlugin = async function retry({ config, cliArgs }) {
|
|
51
|
+
if (!SUMMARY_FILE) {
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
config.teardown.push(async (runner) => {
|
|
55
|
+
const summary = runner.getSummary();
|
|
56
|
+
await cacheFailedTests(summary.failedTestsTitles);
|
|
57
|
+
});
|
|
58
|
+
if (cliArgs.failed) {
|
|
59
|
+
const { tests } = await getFailedTests();
|
|
60
|
+
if (!tests || !tests.length) {
|
|
61
|
+
console.log(colors.bgYellow().black(' No failing tests found. Running all the tests '));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
config.filters.tests = tests;
|
|
65
|
+
}
|
|
66
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { TestEndNode } from '../../modules/core/types.js';
|
|
2
|
+
import { BaseReporter } from '../../modules/core/reporters/base.js';
|
|
3
|
+
/**
|
|
4
|
+
* Minimal reporter that prints each test as an icon.
|
|
5
|
+
*/
|
|
6
|
+
export declare class DotReporter extends BaseReporter {
|
|
7
|
+
/**
|
|
8
|
+
* When a test ended
|
|
9
|
+
*/
|
|
10
|
+
protected onTestEnd(payload: TestEndNode): void;
|
|
11
|
+
/**
|
|
12
|
+
* When test runner ended
|
|
13
|
+
*/
|
|
14
|
+
protected end(): Promise<void>;
|
|
15
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import { colors, icons } from '../helpers.js';
|
|
10
|
+
import { BaseReporter } from '../../modules/core/reporters/base.js';
|
|
11
|
+
/**
|
|
12
|
+
* Minimal reporter that prints each test as an icon.
|
|
13
|
+
*/
|
|
14
|
+
export class DotReporter extends BaseReporter {
|
|
15
|
+
/**
|
|
16
|
+
* When a test ended
|
|
17
|
+
*/
|
|
18
|
+
onTestEnd(payload) {
|
|
19
|
+
let output = '';
|
|
20
|
+
if (payload.isTodo) {
|
|
21
|
+
output = colors.cyan(icons.info);
|
|
22
|
+
}
|
|
23
|
+
else if (payload.hasError || payload.isFailing) {
|
|
24
|
+
output = payload.hasError ? colors.magenta(icons.squareSmallFilled) : colors.red(icons.cross);
|
|
25
|
+
}
|
|
26
|
+
else if (payload.isSkipped) {
|
|
27
|
+
output = colors.yellow(icons.bullet);
|
|
28
|
+
}
|
|
29
|
+
else {
|
|
30
|
+
output = colors.green(icons.tick);
|
|
31
|
+
}
|
|
32
|
+
process.stdout.write(`${output}`);
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* When test runner ended
|
|
36
|
+
*/
|
|
37
|
+
async end() {
|
|
38
|
+
console.log('');
|
|
39
|
+
await this.printSummary(this.runner.getSummary());
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { NamedReporterContract } from '@japa/core/types';
|
|
3
|
-
import '@japa/core';
|
|
4
|
-
|
|
1
|
+
import type { BaseReporterOptions, NamedReporterContract } from '../types.js';
|
|
5
2
|
/**
|
|
6
3
|
* Create an instance of the spec reporter
|
|
7
4
|
*/
|
|
8
|
-
declare const spec: (options?: BaseReporterOptions) => NamedReporterContract;
|
|
5
|
+
export declare const spec: (options?: BaseReporterOptions) => NamedReporterContract;
|
|
9
6
|
/**
|
|
10
7
|
* Create an instance of the dot reporter
|
|
11
8
|
*/
|
|
12
|
-
declare const dot: (options?: BaseReporterOptions) => NamedReporterContract;
|
|
9
|
+
export declare const dot: (options?: BaseReporterOptions) => NamedReporterContract;
|
|
13
10
|
/**
|
|
14
11
|
* Create an instance of the ndjson reporter
|
|
15
12
|
*/
|
|
16
|
-
declare const ndjson: (options?: BaseReporterOptions) => NamedReporterContract;
|
|
17
|
-
|
|
18
|
-
export { dot, ndjson, spec };
|
|
13
|
+
export declare const ndjson: (options?: BaseReporterOptions) => NamedReporterContract;
|
|
@@ -1,12 +1,38 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
1
|
+
/*
|
|
2
|
+
* @japa/runner
|
|
3
|
+
*
|
|
4
|
+
* (c) Japa
|
|
5
|
+
*
|
|
6
|
+
* For the full copyright and license information, please view the LICENSE
|
|
7
|
+
* file that was distributed with this source code.
|
|
8
|
+
*/
|
|
9
|
+
import { DotReporter } from './dot.js';
|
|
10
|
+
import { SpecReporter } from './spec.js';
|
|
11
|
+
import { NdJSONReporter } from './ndjson.js';
|
|
12
|
+
/**
|
|
13
|
+
* Create an instance of the spec reporter
|
|
14
|
+
*/
|
|
15
|
+
export const spec = (options) => {
|
|
16
|
+
return {
|
|
17
|
+
name: 'spec',
|
|
18
|
+
handler: (...args) => new SpecReporter(options).boot(...args),
|
|
19
|
+
};
|
|
20
|
+
};
|
|
21
|
+
/**
|
|
22
|
+
* Create an instance of the dot reporter
|
|
23
|
+
*/
|
|
24
|
+
export const dot = (options) => {
|
|
25
|
+
return {
|
|
26
|
+
name: 'dot',
|
|
27
|
+
handler: (...args) => new DotReporter(options).boot(...args),
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Create an instance of the ndjson reporter
|
|
32
|
+
*/
|
|
33
|
+
export const ndjson = (options) => {
|
|
34
|
+
return {
|
|
35
|
+
name: 'ndjson',
|
|
36
|
+
handler: (...args) => new NdJSONReporter(options).boot(...args),
|
|
37
|
+
};
|
|
12
38
|
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BaseReporter } from '../../modules/core/main.js';
|
|
2
|
+
import type { TestEndNode, SuiteEndNode, GroupEndNode, SuiteStartNode, GroupStartNode } from '../../modules/core/types.js';
|
|
3
|
+
/**
|
|
4
|
+
* Prints tests progress as JSON. Each event is emitted
|
|
5
|
+
* independently
|
|
6
|
+
*/
|
|
7
|
+
export declare class NdJSONReporter extends BaseReporter {
|
|
8
|
+
#private;
|
|
9
|
+
protected onTestEnd(payload: TestEndNode): void;
|
|
10
|
+
protected onGroupStart(payload: GroupStartNode): void;
|
|
11
|
+
protected onGroupEnd(payload: GroupEndNode): void;
|
|
12
|
+
protected onSuiteStart(payload: SuiteStartNode): void;
|
|
13
|
+
protected onSuiteEnd(payload: SuiteEndNode): void;
|
|
14
|
+
protected end(): Promise<void>;
|
|
15
|
+
}
|