@japa/runner 1.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/LICENSE.md ADDED
@@ -0,0 +1,9 @@
1
+ # The MIT License
2
+
3
+ Copyright 2022 Harminder Virk, contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the 'Software'), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6
+
7
+ The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8
+
9
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,19 @@
1
+ # @japa/runner
2
+ > An API first tests runner for Node.js
3
+
4
+ [![github-actions-image]][github-actions-url] [![npm-image]][npm-url] [![license-image]][license-url] [![typescript-image]][typescript-url]
5
+
6
+ Japa is an API-first testing framework. It focuses only on testing Node.js (backend) applications, thus resulting in a fast, small, and simple tests runner.
7
+
8
+ [github-actions-image]: https://img.shields.io/github/workflow/status/japa/runner/test?style=for-the-badge "github-actions"
9
+
10
+ [github-actions-url]: https://github.com/japa/runner/actions/workflows/test.yml
11
+
12
+ [npm-image]: https://img.shields.io/npm/v/@japa/runner.svg?style=for-the-badge&logo=npm
13
+ [npm-url]: https://npmjs.org/package/@japa/runner "npm"
14
+
15
+ [license-image]: https://img.shields.io/npm/l/@japa/runner?color=blueviolet&style=for-the-badge
16
+ [license-url]: LICENSE.md "license"
17
+
18
+ [typescript-image]: https://img.shields.io/badge/Typescript-294E80.svg?style=for-the-badge&logo=typescript
19
+ [typescript-url]: "typescript"
@@ -0,0 +1,24 @@
1
+ import { TestExecutor } from '@japa/core';
2
+ import { Test, TestContext, Group } from './src/Core';
3
+ import { ConfigureOptions, Filters } from './src/Contracts';
4
+ export { Test, TestContext, Group } from './src/Core';
5
+ /**
6
+ * Configure the tests runner
7
+ */
8
+ export declare function configure(options: ConfigureOptions): void;
9
+ /**
10
+ * Add a new test
11
+ */
12
+ declare function test(title: string, callback?: TestExecutor<TestContext, undefined>): Test<TestContext, undefined>;
13
+ declare namespace test {
14
+ var group: (title: string, callback: (group: Group<TestContext>) => void) => void;
15
+ }
16
+ export default test;
17
+ /**
18
+ * Run japa tests
19
+ */
20
+ export declare function run(): Promise<void>;
21
+ /**
22
+ * Process CLI arguments into filters
23
+ */
24
+ export declare function processCliArgs(argv: string[]): Filters;
package/build/index.js ADDED
@@ -0,0 +1,319 @@
1
+ "use strict";
2
+ /*
3
+ * @japa/runner
4
+ *
5
+ * (c) Harminder Virk <virk@adonisjs.com>
6
+ *
7
+ * For the full copyright and license information, please view the LICENSE
8
+ * file that was distributed with this source code.
9
+ */
10
+ var __importDefault = (this && this.__importDefault) || function (mod) {
11
+ return (mod && mod.__esModule) ? mod : { "default": mod };
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.processCliArgs = exports.run = exports.configure = exports.Group = exports.TestContext = exports.Test = void 0;
15
+ const getopts_1 = __importDefault(require("getopts"));
16
+ const path_1 = require("path");
17
+ const fast_glob_1 = __importDefault(require("fast-glob"));
18
+ const inclusion_1 = __importDefault(require("inclusion"));
19
+ const hooks_1 = require("@poppinss/hooks");
20
+ const core_1 = require("@japa/core");
21
+ const Core_1 = require("./src/Core");
22
+ var Core_2 = require("./src/Core");
23
+ Object.defineProperty(exports, "Test", { enumerable: true, get: function () { return Core_2.Test; } });
24
+ Object.defineProperty(exports, "TestContext", { enumerable: true, get: function () { return Core_2.TestContext; } });
25
+ Object.defineProperty(exports, "Group", { enumerable: true, get: function () { return Core_2.Group; } });
26
+ /**
27
+ * Filtering layers allowed by the refiner
28
+ */
29
+ const refinerFilteringLayers = ['tests', 'groups', 'tags'];
30
+ /**
31
+ * Reference to the recently imported file. We pass it to the
32
+ * test and the group both
33
+ */
34
+ let recentlyImportedFile;
35
+ /**
36
+ * Function to create the test context for the test
37
+ */
38
+ const getContext = (testInstance) => new Core_1.TestContext(testInstance);
39
+ /**
40
+ * The global reference to the tests emitter
41
+ */
42
+ const emitter = new core_1.Emitter();
43
+ /**
44
+ * The default suite for registering tests
45
+ */
46
+ const suite = new core_1.Suite('default', emitter);
47
+ /**
48
+ * Currently active group
49
+ */
50
+ let activeGroup;
51
+ /**
52
+ * Configuration options
53
+ */
54
+ let runnerOptions;
55
+ /**
56
+ * Ensure the configure method has been called
57
+ */
58
+ function ensureIsConfigured(message) {
59
+ if (!runnerOptions) {
60
+ throw new Error(message);
61
+ }
62
+ }
63
+ /**
64
+ * End tests. We wait for the "beforeExit" event when
65
+ * forceExit is not set to true
66
+ */
67
+ async function endTests(runner) {
68
+ if (runnerOptions.forceExit) {
69
+ await runner.end();
70
+ }
71
+ else {
72
+ return new Promise((resolve) => {
73
+ async function beforeExit() {
74
+ process.removeListener('beforeExit', beforeExit);
75
+ await runner.end();
76
+ resolve();
77
+ }
78
+ process.on('beforeExit', beforeExit);
79
+ });
80
+ }
81
+ }
82
+ /**
83
+ * Process command line args to an array of strings
84
+ */
85
+ function processArg(flag) {
86
+ return (Array.isArray(flag) ? flag : flag.split(',')).map((tag) => tag.trim());
87
+ }
88
+ /**
89
+ * Find if the file path matches the files filter array.
90
+ * The ending of the file is matched
91
+ */
92
+ function isFileAllowed(filePath, filters) {
93
+ return !!filters.find((matcher) => {
94
+ if (filePath.endsWith(matcher)) {
95
+ return true;
96
+ }
97
+ return filePath.replace((0, path_1.extname)(filePath), '').endsWith(matcher);
98
+ });
99
+ }
100
+ /**
101
+ * Configure the tests runner
102
+ */
103
+ function configure(options) {
104
+ const defaultOptions = {
105
+ files: [],
106
+ plugins: [],
107
+ reporters: [],
108
+ timeout: 2000,
109
+ filters: {},
110
+ setup: [],
111
+ teardown: [],
112
+ importer: (filePath) => (0, inclusion_1.default)(filePath),
113
+ refiner: new core_1.Refiner({}),
114
+ forceExit: false,
115
+ };
116
+ runnerOptions = Object.assign(defaultOptions, options);
117
+ }
118
+ exports.configure = configure;
119
+ /**
120
+ * Add a new test
121
+ */
122
+ function test(title, callback) {
123
+ ensureIsConfigured('Cannot add test without configuring the test runner');
124
+ const testInstance = new Core_1.Test(title, getContext, emitter, runnerOptions.refiner);
125
+ /**
126
+ * Set filename
127
+ */
128
+ testInstance.options.meta.fileName = recentlyImportedFile;
129
+ /**
130
+ * Define timeout on the test when exists globally
131
+ */
132
+ if (runnerOptions.timeout !== undefined) {
133
+ testInstance.timeout(runnerOptions.timeout);
134
+ }
135
+ /**
136
+ * Define test executor function
137
+ */
138
+ if (callback) {
139
+ testInstance.run(callback);
140
+ }
141
+ /**
142
+ * Add test to the group or suite
143
+ */
144
+ if (activeGroup) {
145
+ activeGroup.add(testInstance);
146
+ }
147
+ else {
148
+ suite.add(testInstance);
149
+ }
150
+ return testInstance;
151
+ }
152
+ exports.default = test;
153
+ /**
154
+ * Define test group
155
+ */
156
+ test.group = function (title, callback) {
157
+ ensureIsConfigured('Cannot add test group without configuring the test runner');
158
+ /**
159
+ * Disallow nested groups
160
+ */
161
+ if (activeGroup) {
162
+ throw new Error('Cannot create nested test groups');
163
+ }
164
+ activeGroup = new Core_1.Group(title, emitter, runnerOptions.refiner);
165
+ /**
166
+ * Set filename
167
+ */
168
+ activeGroup.options.meta.fileName = recentlyImportedFile;
169
+ callback(activeGroup);
170
+ /**
171
+ * Add group to the default suite
172
+ */
173
+ suite.add(activeGroup);
174
+ activeGroup = undefined;
175
+ };
176
+ /**
177
+ * Run japa tests
178
+ */
179
+ async function run() {
180
+ const runner = new core_1.Runner(emitter);
181
+ runner.add(suite).manageUnHandledExceptions();
182
+ const hooks = new hooks_1.Hooks();
183
+ let setupRunner;
184
+ let teardownRunner;
185
+ try {
186
+ ensureIsConfigured('Cannot run tests without configuring the tests runner');
187
+ /**
188
+ * Step 1: Run all plugins
189
+ *
190
+ * Plugins can also mutate config. So we process the config after
191
+ * running plugins only
192
+ */
193
+ for (let plugin of runnerOptions.plugins) {
194
+ await plugin(runnerOptions, runner, { Test: Core_1.Test, TestContext: Core_1.TestContext, Group: Core_1.Group });
195
+ }
196
+ /**
197
+ * Step 2: Notify runner about reporters
198
+ */
199
+ runnerOptions.reporters.forEach((reporter) => runner.registerReporter(reporter));
200
+ /**
201
+ * Step 3: Run runner hooks.
202
+ *
203
+ * We run hooks before importing test files. It allows hooks
204
+ * to setup the app environment for the test files
205
+ */
206
+ runnerOptions.setup.forEach((hook) => hooks.add('setup', hook));
207
+ runnerOptions.teardown.forEach((hook) => hooks.add('teardown', hook));
208
+ setupRunner = hooks.runner('setup');
209
+ teardownRunner = hooks.runner('teardown');
210
+ /**
211
+ * Step 3.1: Run setup hooks
212
+ */
213
+ await setupRunner.run(runner);
214
+ /**
215
+ * Step 4: Collect all test files
216
+ */
217
+ let files = [];
218
+ if (Array.isArray(runnerOptions.files)) {
219
+ files = await (0, fast_glob_1.default)(runnerOptions.files, { absolute: true, onlyFiles: true });
220
+ }
221
+ else if (typeof runnerOptions.files === 'function') {
222
+ files = await runnerOptions.files();
223
+ }
224
+ /**
225
+ * Step 5: Import files
226
+ */
227
+ for (let file of files) {
228
+ recentlyImportedFile = file;
229
+ if (runnerOptions.filters.files && runnerOptions.filters.files.length) {
230
+ if (isFileAllowed(file, runnerOptions.filters.files)) {
231
+ await runnerOptions.importer(file);
232
+ }
233
+ }
234
+ else {
235
+ await runnerOptions.importer(file);
236
+ }
237
+ }
238
+ /**
239
+ * Step 6: Add filters to the refiner
240
+ */
241
+ Object.keys(runnerOptions.filters).forEach((layer) => {
242
+ if (refinerFilteringLayers.includes(layer)) {
243
+ const values = runnerOptions.filters[layer];
244
+ if (values) {
245
+ runnerOptions.refiner.add(layer, values);
246
+ }
247
+ }
248
+ });
249
+ /**
250
+ * Step 7.1: Start the tests runner
251
+ */
252
+ await runner.start();
253
+ /**
254
+ * Step 7.2: Execute all the tests
255
+ */
256
+ await runner.exec();
257
+ /**
258
+ * Step 7.3: Run teardown hooks
259
+ */
260
+ await teardownRunner.run(runner);
261
+ /**
262
+ * Step 7.4: End or wait for process to exit
263
+ */
264
+ await endTests(runner);
265
+ /**
266
+ * Step 8: Update the process exit code
267
+ */
268
+ const summary = runner.getSummary();
269
+ if (summary.hasError) {
270
+ process.exitCode = 1;
271
+ }
272
+ runnerOptions.forceExit && process.exit();
273
+ await setupRunner.cleanup(runner);
274
+ await teardownRunner.cleanup(runner);
275
+ }
276
+ catch (error) {
277
+ if (setupRunner && setupRunner.isCleanupPending) {
278
+ await setupRunner.cleanup(error, runner);
279
+ }
280
+ if (teardownRunner && teardownRunner.isCleanupPending) {
281
+ await teardownRunner.cleanup(error, runner);
282
+ }
283
+ process.exitCode = 1;
284
+ runnerOptions.forceExit && process.exit();
285
+ }
286
+ }
287
+ exports.run = run;
288
+ /**
289
+ * Process CLI arguments into filters
290
+ */
291
+ function processCliArgs(argv) {
292
+ const parsed = (0, getopts_1.default)(argv, {
293
+ string: ['tests', 'tags', 'groups', 'ignoreTags', 'files'],
294
+ alias: {
295
+ ignoreTags: 'ignore-tags',
296
+ },
297
+ });
298
+ const filters = {};
299
+ if (parsed.tags) {
300
+ filters.tags = processArg(parsed.tags);
301
+ }
302
+ if (parsed.ignoreTags) {
303
+ filters.tags = filters.tags || [];
304
+ processArg(parsed.ignoreTags).forEach((tag) => {
305
+ filters.tags.push(`!${tag}`);
306
+ });
307
+ }
308
+ if (parsed.groups) {
309
+ filters.groups = processArg(parsed.groups);
310
+ }
311
+ if (parsed.tests) {
312
+ filters.tests = processArg(parsed.tests);
313
+ }
314
+ if (parsed.files) {
315
+ filters.files = processArg(parsed.files);
316
+ }
317
+ return filters;
318
+ }
319
+ exports.processCliArgs = processCliArgs;
@@ -0,0 +1,39 @@
1
+ import { Runner, Refiner, FilteringOptions, ReporterContract } from '@japa/core';
2
+ import { Test, Group, TestContext } from '../Core';
3
+ /**
4
+ * The cleanup function for runner hooks
5
+ */
6
+ export declare type RunnerHooksCleanupHandler = (error: null | any, runner: Runner) => Promise<any> | any;
7
+ /**
8
+ * The function that can be registered as a runner hook
9
+ */
10
+ export declare type RunnerHooksHandler = (runner: Runner) => Promise<any> | any | RunnerHooksCleanupHandler | Promise<RunnerHooksCleanupHandler>;
11
+ /**
12
+ * Allowed filters
13
+ */
14
+ export declare type Filters = FilteringOptions & {
15
+ files?: string[];
16
+ };
17
+ /**
18
+ * Shape of the plugin function
19
+ */
20
+ export declare type PluginFn = (config: Required<ConfigureOptions>, runner: Runner, classes: {
21
+ Test: typeof Test;
22
+ TestContext: typeof TestContext;
23
+ Group: typeof Group;
24
+ }) => void | Promise<void>;
25
+ /**
26
+ * Configuration options
27
+ */
28
+ export declare type ConfigureOptions = {
29
+ files: string[] | (() => string[] | Promise<string[]>);
30
+ timeout?: number;
31
+ plugins?: PluginFn[];
32
+ filters?: Filters;
33
+ setup?: RunnerHooksHandler[];
34
+ teardown?: RunnerHooksHandler[];
35
+ reporters?: ReporterContract[];
36
+ importer?: (filePath: string) => void | Promise<void>;
37
+ refiner?: Refiner;
38
+ forceExit?: boolean;
39
+ };
@@ -0,0 +1,10 @@
1
+ "use strict";
2
+ /*
3
+ * @japa/runner
4
+ *
5
+ * (c) Harminder Virk <virk@adonisjs.com>
6
+ *
7
+ * For the full copyright and license information, please view the LICENSE
8
+ * file that was distributed with this source code.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,6 @@
1
+ import { Test, Group, TestContext as BaseTestContext } from '@japa/core';
2
+ export declare class TestContext extends BaseTestContext {
3
+ test: Test<TestContext, any>;
4
+ constructor(test: Test<TestContext, any>);
5
+ }
6
+ export { Group, Test };
@@ -0,0 +1,21 @@
1
+ "use strict";
2
+ /*
3
+ * @japa/runner
4
+ *
5
+ * (c) Harminder Virk <virk@adonisjs.com>
6
+ *
7
+ * For the full copyright and license information, please view the LICENSE
8
+ * file that was distributed with this source code.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.Test = exports.Group = exports.TestContext = void 0;
12
+ const core_1 = require("@japa/core");
13
+ Object.defineProperty(exports, "Test", { enumerable: true, get: function () { return core_1.Test; } });
14
+ Object.defineProperty(exports, "Group", { enumerable: true, get: function () { return core_1.Group; } });
15
+ class TestContext extends core_1.TestContext {
16
+ constructor(test) {
17
+ super();
18
+ this.test = test;
19
+ }
20
+ }
21
+ exports.TestContext = TestContext;
package/package.json ADDED
@@ -0,0 +1,118 @@
1
+ {
2
+ "name": "@japa/runner",
3
+ "version": "1.0.0",
4
+ "description": "Runner for Japa testing framework",
5
+ "main": "build/index.js",
6
+ "files": [
7
+ "build/src",
8
+ "build/index.d.ts",
9
+ "build/index.js"
10
+ ],
11
+ "exports": {
12
+ ".": "./build/index.js"
13
+ },
14
+ "scripts": {
15
+ "mrm": "mrm --preset=@adonisjs/mrm-preset",
16
+ "pretest": "npm run lint",
17
+ "test": "node .bin/test.js",
18
+ "clean": "del build",
19
+ "compile": "npm run lint && npm run clean && tsc",
20
+ "build": "npm run compile",
21
+ "prepublishOnly": "npm run build",
22
+ "lint": "eslint . --ext=.ts",
23
+ "format": "prettier --write .",
24
+ "commit": "git-cz",
25
+ "release": "np --message=\"chore(release): %s\"",
26
+ "version": "npm run build",
27
+ "sync-labels": "github-label-sync --labels ./node_modules/@adonisjs/mrm-preset/gh-labels.json japa/runner"
28
+ },
29
+ "keywords": [
30
+ "japa",
31
+ "tests",
32
+ "test-runner"
33
+ ],
34
+ "author": "virk,japa",
35
+ "license": "MIT",
36
+ "devDependencies": {
37
+ "@adonisjs/mrm-preset": "^5.0.2",
38
+ "@adonisjs/require-ts": "^2.0.9",
39
+ "@types/node": "^17.0.14",
40
+ "commitizen": "^4.2.4",
41
+ "cz-conventional-changelog": "^3.3.0",
42
+ "del-cli": "^4.0.1",
43
+ "eslint": "^8.8.0",
44
+ "eslint-config-prettier": "^8.3.0",
45
+ "eslint-plugin-adonis": "^2.1.0",
46
+ "eslint-plugin-prettier": "^4.0.0",
47
+ "github-label-sync": "^2.0.2",
48
+ "husky": "^7.0.4",
49
+ "japa": "^4.0.0",
50
+ "mrm": "^3.0.10",
51
+ "np": "^7.6.0",
52
+ "prettier": "^2.5.1",
53
+ "typescript": "^4.5.5"
54
+ },
55
+ "mrmConfig": {
56
+ "core": false,
57
+ "license": "MIT",
58
+ "services": [
59
+ "github-actions"
60
+ ],
61
+ "minNodeVersion": "16.13.1",
62
+ "probotApps": [
63
+ "stale",
64
+ "lock"
65
+ ],
66
+ "runGhActionsOnWindows": true
67
+ },
68
+ "eslintConfig": {
69
+ "extends": [
70
+ "plugin:adonis/typescriptPackage",
71
+ "prettier"
72
+ ],
73
+ "plugins": [
74
+ "prettier"
75
+ ],
76
+ "rules": {
77
+ "prettier/prettier": [
78
+ "error",
79
+ {
80
+ "endOfLine": "auto"
81
+ }
82
+ ]
83
+ }
84
+ },
85
+ "eslintIgnore": [
86
+ "build"
87
+ ],
88
+ "prettier": {
89
+ "trailingComma": "es5",
90
+ "semi": false,
91
+ "singleQuote": true,
92
+ "useTabs": false,
93
+ "quoteProps": "consistent",
94
+ "bracketSpacing": true,
95
+ "arrowParens": "always",
96
+ "printWidth": 100
97
+ },
98
+ "config": {
99
+ "commitizen": {
100
+ "path": "cz-conventional-changelog"
101
+ }
102
+ },
103
+ "np": {
104
+ "contents": ".",
105
+ "anyBranch": false
106
+ },
107
+ "dependencies": {
108
+ "@japa/core": "^5.0.17-0",
109
+ "@poppinss/hooks": "^6.0.1-0",
110
+ "fast-glob": "^3.2.11",
111
+ "getopts": "^2.3.0",
112
+ "inclusion": "^1.0.1"
113
+ },
114
+ "publishConfig": {
115
+ "access": "public",
116
+ "tag": "latest"
117
+ }
118
+ }