@japa/runner 2.0.8 → 2.1.1

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 CHANGED
@@ -7,6 +7,11 @@ Japa is an API first testing framework. It focuses only on testing Node.js (back
7
7
 
8
8
  #### 💁 Please visit https://japa.dev for documentation
9
9
 
10
+ <br />
11
+ <hr>
12
+
13
+ ![](https://cdn.jsdelivr.net/gh/thetutlage/static/sponsorkit/sponsors.png)
14
+
10
15
  [github-actions-image]: https://img.shields.io/github/workflow/status/japa/runner/test?style=for-the-badge "github-actions"
11
16
 
12
17
  [github-actions-url]: https://github.com/japa/runner/actions/workflows/test.yml
package/build/index.d.ts CHANGED
@@ -1,22 +1,11 @@
1
- import { TestExecutor, ReporterContract } from '@japa/core';
2
1
  import { Test, TestContext, Group, Suite, Runner } from './src/Core';
2
+ import { TestExecutor, ReporterContract } from '@japa/core';
3
3
  import { Config, PluginFn, RunnerHooksHandler, RunnerHooksCleanupHandler } from './src/Contracts';
4
4
  export { Test, Config, Suite, Runner, Group, PluginFn, TestContext, ReporterContract, RunnerHooksHandler, RunnerHooksCleanupHandler, };
5
5
  /**
6
6
  * Configure the tests runner
7
7
  */
8
8
  export declare function configure(options: Config): void;
9
- /**
10
- * Add a new test
11
- */
12
- export declare function test(title: string, callback?: TestExecutor<TestContext, undefined>): Test<undefined>;
13
- export declare namespace test {
14
- var group: (title: string, callback: (group: Group) => void) => void;
15
- }
16
- /**
17
- * Run japa tests
18
- */
19
- export declare function run(): Promise<void>;
20
9
  /**
21
10
  * Process CLI arguments into configuration options. The following
22
11
  * command line arguments are processed.
@@ -28,5 +17,17 @@ export declare function run(): Promise<void>;
28
17
  * * --files=Specify files to match and run
29
18
  * * --force-exit=Enable/disable force exit
30
19
  * * --timeout=Define timeout for all the tests
20
+ * * -h, --help=Show help
31
21
  */
32
22
  export declare function processCliArgs(argv: string[]): Partial<Config>;
23
+ /**
24
+ * Run japa tests
25
+ */
26
+ export declare function run(): Promise<void>;
27
+ /**
28
+ * Add a new test
29
+ */
30
+ export declare function test(title: string, callback?: TestExecutor<TestContext, undefined>): Test<undefined>;
31
+ export declare namespace test {
32
+ var group: (title: string, callback: (group: Group) => void) => void;
33
+ }
package/build/index.js CHANGED
@@ -11,21 +11,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
11
11
  return (mod && mod.__esModule) ? mod : { "default": mod };
12
12
  };
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.processCliArgs = exports.run = exports.test = exports.configure = exports.TestContext = exports.Group = exports.Runner = exports.Suite = exports.Test = void 0;
14
+ exports.test = exports.run = exports.processCliArgs = exports.configure = exports.TestContext = exports.Group = exports.Runner = exports.Suite = exports.Test = void 0;
15
15
  const getopts_1 = __importDefault(require("getopts"));
16
16
  const path_1 = require("path");
17
17
  const fast_glob_1 = __importDefault(require("fast-glob"));
18
18
  const inclusion_1 = __importDefault(require("inclusion"));
19
19
  const url_1 = require("url");
20
20
  const hooks_1 = require("@poppinss/hooks");
21
+ const cliui_1 = require("@poppinss/cliui");
21
22
  const errors_printer_1 = require("@japa/errors-printer");
22
- const core_1 = require("@japa/core");
23
23
  const Core_1 = require("./src/Core");
24
24
  Object.defineProperty(exports, "Test", { enumerable: true, get: function () { return Core_1.Test; } });
25
25
  Object.defineProperty(exports, "TestContext", { enumerable: true, get: function () { return Core_1.TestContext; } });
26
26
  Object.defineProperty(exports, "Group", { enumerable: true, get: function () { return Core_1.Group; } });
27
27
  Object.defineProperty(exports, "Suite", { enumerable: true, get: function () { return Core_1.Suite; } });
28
28
  Object.defineProperty(exports, "Runner", { enumerable: true, get: function () { return Core_1.Runner; } });
29
+ const core_1 = require("@japa/core");
29
30
  /**
30
31
  * Filtering layers allowed by the refiner
31
32
  */
@@ -51,7 +52,7 @@ const emitter = new core_1.Emitter();
51
52
  /**
52
53
  * Active suite for tests
53
54
  */
54
- let activeSuite = new Core_1.Suite('default', emitter);
55
+ let activeSuite;
55
56
  /**
56
57
  * Currently active group
57
58
  */
@@ -85,25 +86,6 @@ function validateSuitesFilter() {
85
86
  throw new Error(`Unrecognized suite "${invalidSuites[0]}". Make sure to define it in the config first`);
86
87
  }
87
88
  }
88
- /**
89
- * End tests. We wait for the "beforeExit" event when
90
- * forceExit is not set to true
91
- */
92
- async function endTests(runner) {
93
- if (runnerOptions.forceExit) {
94
- await runner.end();
95
- }
96
- else {
97
- return new Promise((resolve) => {
98
- async function beforeExit() {
99
- process.removeListener('beforeExit', beforeExit);
100
- await runner.end();
101
- resolve();
102
- }
103
- process.on('beforeExit', beforeExit);
104
- });
105
- }
106
- }
107
89
  /**
108
90
  * Process command line argument into a string value
109
91
  */
@@ -118,7 +100,10 @@ function processAsString(argv, flagName, onMatch) {
118
100
  * The ending of the file is matched
119
101
  */
120
102
  function isFileAllowed(filePath, filters) {
121
- return !!filters.find((matcher) => {
103
+ if (!filters.files || !filters.files.length) {
104
+ return true;
105
+ }
106
+ return !!filters.files.find((matcher) => {
122
107
  if (filePath.endsWith(matcher)) {
123
108
  return true;
124
109
  }
@@ -127,13 +112,84 @@ function isFileAllowed(filePath, filters) {
127
112
  }
128
113
  /**
129
114
  * Returns "true" when no filters are applied or the name is part
130
- * of the applied filter
115
+ * of the applied filter.
131
116
  */
132
- function isSuiteAllowed(name, filters) {
133
- if (!filters || !filters.length) {
117
+ function isSuiteAllowed(suite, filters) {
118
+ if (!filters.suites || !filters.suites.length) {
134
119
  return true;
135
120
  }
136
- return filters.includes(name);
121
+ return filters.suites.includes(suite.name);
122
+ }
123
+ /**
124
+ * Collect files using the files collector function or by processing
125
+ * the glob pattern.
126
+ *
127
+ * The return value is further filtered against the `--files` filter.
128
+ */
129
+ async function collectFiles(files) {
130
+ if (Array.isArray(files) || typeof files === 'string') {
131
+ const collectedFiles = await (0, fast_glob_1.default)(files, {
132
+ absolute: true,
133
+ onlyFiles: true,
134
+ cwd: runnerOptions.cwd,
135
+ });
136
+ return collectedFiles.filter((file) => isFileAllowed(file, runnerOptions.filters));
137
+ }
138
+ else if (typeof files === 'function') {
139
+ const collectedFiles = await files();
140
+ return collectedFiles.filter((file) => isFileAllowed(file, runnerOptions.filters));
141
+ }
142
+ throw new Error('Invalid value for "files" property. Expected a string, array or a function');
143
+ }
144
+ /**
145
+ * Import test files using the configured importer.
146
+ */
147
+ async function importFiles(files) {
148
+ for (let file of files) {
149
+ recentlyImportedFile = file;
150
+ await runnerOptions.importer(file);
151
+ }
152
+ }
153
+ /**
154
+ * End tests. We wait for the "beforeExit" event when
155
+ * forceExit is not set to true
156
+ */
157
+ async function endTests(runner) {
158
+ if (runnerOptions.forceExit) {
159
+ await runner.end();
160
+ }
161
+ else {
162
+ return new Promise((resolve) => {
163
+ async function beforeExit() {
164
+ process.removeListener('beforeExit', beforeExit);
165
+ await runner.end();
166
+ resolve();
167
+ }
168
+ process.on('beforeExit', beforeExit);
169
+ });
170
+ }
171
+ }
172
+ /**
173
+ * Show help output in stdout.
174
+ */
175
+ function showHelp() {
176
+ const green = cliui_1.logger.colors.green.bind(cliui_1.logger.colors);
177
+ const grey = cliui_1.logger.colors.grey.bind(cliui_1.logger.colors);
178
+ console.log(`@japa/runner v2.1.1
179
+
180
+ Options:
181
+ ${green('--tests')} ${grey('Specify test titles')}
182
+ ${green('--tags')} ${grey('Specify test tags')}
183
+ ${green('--groups')} ${grey('Specify group titles')}
184
+ ${green('--ignore-tags')} ${grey('Specify negated tags')}
185
+ ${green('--files')} ${grey('Specify files to match and run')}
186
+ ${green('--force-exit')} ${grey('Enable/disable force exit')}
187
+ ${green('--timeout')} ${grey('Define timeout for all the tests')}
188
+ ${green('-h, --help')} ${grey('Display this message')}
189
+
190
+ Examples:
191
+ ${grey('$ node bin/test.js --tags="@github"')}
192
+ ${grey('$ node bin/test.js --files="example.spec.js" --force-exit')}`);
137
193
  }
138
194
  /**
139
195
  * Configure the tests runner
@@ -158,92 +214,70 @@ function configure(options) {
158
214
  }
159
215
  exports.configure = configure;
160
216
  /**
161
- * Add a new test
217
+ * Process CLI arguments into configuration options. The following
218
+ * command line arguments are processed.
219
+ *
220
+ * * --tests=Specify test titles
221
+ * * --tags=Specify test tags
222
+ * * --groups=Specify group titles
223
+ * * --ignore-tags=Specify negated tags
224
+ * * --files=Specify files to match and run
225
+ * * --force-exit=Enable/disable force exit
226
+ * * --timeout=Define timeout for all the tests
227
+ * * -h, --help=Show help
162
228
  */
163
- function test(title, callback) {
164
- ensureIsConfigured('Cannot add test without configuring the test runner');
165
- const testInstance = new Core_1.Test(title, getContext, emitter, runnerOptions.refiner);
166
- /**
167
- * Set filename
168
- */
169
- testInstance.options.meta.fileName = recentlyImportedFile;
170
- /**
171
- * Define timeout on the test when exists globally
172
- */
173
- if (globalTimeout !== undefined) {
174
- testInstance.timeout(globalTimeout);
175
- }
229
+ function processCliArgs(argv) {
230
+ const parsed = (0, getopts_1.default)(argv, {
231
+ string: ['tests', 'tags', 'groups', 'ignoreTags', 'files', 'timeout'],
232
+ boolean: ['forceExit', 'help'],
233
+ alias: {
234
+ ignoreTags: 'ignore-tags',
235
+ forceExit: 'force-exit',
236
+ help: 'h',
237
+ },
238
+ });
239
+ const config = {
240
+ filters: {},
241
+ };
242
+ processAsString(parsed, 'tags', (tags) => (config.filters.tags = tags));
243
+ processAsString(parsed, 'ignoreTags', (tags) => {
244
+ config.filters.tags = config.filters.tags || [];
245
+ tags.forEach((tag) => config.filters.tags.push(`!${tag}`));
246
+ });
247
+ processAsString(parsed, 'groups', (groups) => (config.filters.groups = groups));
248
+ processAsString(parsed, 'tests', (tests) => (config.filters.tests = tests));
249
+ processAsString(parsed, 'files', (files) => (config.filters.files = files));
176
250
  /**
177
- * Define test executor function
251
+ * Show help
178
252
  */
179
- if (callback) {
180
- testInstance.run(callback);
253
+ if (parsed.help) {
254
+ showHelp();
255
+ process.exit(0);
181
256
  }
182
257
  /**
183
- * Add test to the group or suite
258
+ * Get suites
184
259
  */
185
- if (activeGroup) {
186
- activeGroup.add(testInstance);
187
- }
188
- else {
189
- activeSuite.add(testInstance);
260
+ if (parsed._.length) {
261
+ processAsString({ suites: parsed._ }, 'suites', (suites) => (config.filters.suites = suites));
190
262
  }
191
- return testInstance;
192
- }
193
- exports.test = test;
194
- /**
195
- * Define test group
196
- */
197
- test.group = function (title, callback) {
198
- ensureIsConfigured('Cannot add test group without configuring the test runner');
199
263
  /**
200
- * Disallow nested groups
264
+ * Get timeout
201
265
  */
202
- if (activeGroup) {
203
- throw new Error('Cannot create nested test groups');
266
+ if (parsed.timeout) {
267
+ const value = Number(parsed.timeout);
268
+ if (!isNaN(value)) {
269
+ config.timeout = value;
270
+ }
204
271
  }
205
- activeGroup = new Core_1.Group(title, emitter, runnerOptions.refiner);
206
272
  /**
207
- * Set filename
208
- */
209
- activeGroup.options.meta.fileName = recentlyImportedFile;
210
- /**
211
- * Add group to the default suite
273
+ * Get forceExit
212
274
  */
213
- activeSuite.add(activeGroup);
214
- callback(activeGroup);
215
- activeGroup = undefined;
216
- };
217
- /**
218
- * Collect files using the files collector function or by processing
219
- * the glob pattern
220
- */
221
- async function collectFiles(files) {
222
- if (Array.isArray(files) || typeof files === 'string') {
223
- return await (0, fast_glob_1.default)(files, { absolute: true, onlyFiles: true, cwd: runnerOptions.cwd });
224
- }
225
- else if (typeof files === 'function') {
226
- return await files();
227
- }
228
- throw new Error('Invalid value for "files" property. Expected a string, array or a function');
229
- }
230
- /**
231
- * Import test files using the configured importer. Also
232
- * filter files using the file filter. (if mentioned).
233
- */
234
- async function importFiles(files) {
235
- for (let file of files) {
236
- recentlyImportedFile = file;
237
- if (runnerOptions.filters.files && runnerOptions.filters.files.length) {
238
- if (isFileAllowed(file, runnerOptions.filters.files)) {
239
- await runnerOptions.importer(file);
240
- }
241
- }
242
- else {
243
- await runnerOptions.importer(file);
244
- }
275
+ if (parsed.forceExit) {
276
+ config.forceExit = true;
245
277
  }
278
+ return config;
246
279
  }
280
+ exports.processCliArgs = processCliArgs;
247
281
  /**
248
282
  * Run japa tests
249
283
  */
@@ -290,10 +324,19 @@ async function run() {
290
324
  * as part of the default suite
291
325
  */
292
326
  if ('files' in runnerOptions && runnerOptions.files.length) {
327
+ /**
328
+ * Create a default suite for files with no suite
329
+ */
293
330
  globalTimeout = runnerOptions.timeout;
294
331
  const files = await collectFiles(runnerOptions.files);
295
- runner.add(activeSuite);
296
- await importFiles(files);
332
+ /**
333
+ * Create and register suite when files are collected.
334
+ */
335
+ if (files.length) {
336
+ activeSuite = new Core_1.Suite('default', emitter, runnerOptions.refiner);
337
+ runner.add(activeSuite);
338
+ await importFiles(files);
339
+ }
297
340
  }
298
341
  /**
299
342
  * Step 5: Entertain suites property and import test files
@@ -301,20 +344,26 @@ async function run() {
301
344
  */
302
345
  if ('suites' in runnerOptions) {
303
346
  for (let suite of runnerOptions.suites) {
304
- if (isSuiteAllowed(suite.name, runnerOptions.filters.suites)) {
347
+ if (isSuiteAllowed(suite, runnerOptions.filters)) {
305
348
  if (suite.timeout !== undefined) {
306
349
  globalTimeout = suite.timeout;
307
350
  }
308
351
  else {
309
352
  globalTimeout = runnerOptions.timeout;
310
353
  }
311
- activeSuite = new Core_1.Suite(suite.name, emitter);
312
- if (typeof suite.configure === 'function') {
313
- suite.configure(activeSuite);
314
- }
315
354
  const files = await collectFiles(suite.files);
316
- runner.add(activeSuite);
317
- await importFiles(files);
355
+ /**
356
+ * Only register the suite and import files when the suite
357
+ * files glob + filter has returned one or more files.
358
+ */
359
+ if (files.length) {
360
+ activeSuite = new Core_1.Suite(suite.name, emitter, runnerOptions.refiner);
361
+ if (typeof suite.configure === 'function') {
362
+ suite.configure(activeSuite);
363
+ }
364
+ runner.add(activeSuite);
365
+ await importFiles(files);
366
+ }
318
367
  }
319
368
  }
320
369
  }
@@ -371,58 +420,59 @@ async function run() {
371
420
  }
372
421
  exports.run = run;
373
422
  /**
374
- * Process CLI arguments into configuration options. The following
375
- * command line arguments are processed.
376
- *
377
- * * --tests=Specify test titles
378
- * * --tags=Specify test tags
379
- * * --groups=Specify group titles
380
- * * --ignore-tags=Specify negated tags
381
- * * --files=Specify files to match and run
382
- * * --force-exit=Enable/disable force exit
383
- * * --timeout=Define timeout for all the tests
423
+ * Add a new test
384
424
  */
385
- function processCliArgs(argv) {
386
- const parsed = (0, getopts_1.default)(argv, {
387
- string: ['tests', 'tags', 'groups', 'ignoreTags', 'files', 'timeout'],
388
- boolean: ['forceExit'],
389
- alias: {
390
- ignoreTags: 'ignore-tags',
391
- forceExit: 'force-exit',
392
- },
393
- });
394
- const config = {
395
- filters: {},
396
- };
397
- processAsString(parsed, 'tags', (tags) => (config.filters.tags = tags));
398
- processAsString(parsed, 'ignoreTags', (tags) => {
399
- config.filters.tags = config.filters.tags || [];
400
- tags.forEach((tag) => config.filters.tags.push(`!${tag}`));
401
- });
402
- processAsString(parsed, 'groups', (groups) => (config.filters.groups = groups));
403
- processAsString(parsed, 'tests', (tests) => (config.filters.tests = tests));
404
- processAsString(parsed, 'files', (files) => (config.filters.files = files));
425
+ function test(title, callback) {
426
+ ensureIsConfigured('Cannot add test without configuring the test runner');
427
+ const testInstance = new Core_1.Test(title, getContext, emitter, runnerOptions.refiner, activeGroup);
405
428
  /**
406
- * Get suites
429
+ * Set filename
407
430
  */
408
- if (parsed._.length) {
409
- processAsString({ suites: parsed._ }, 'suites', (suites) => (config.filters.suites = suites));
431
+ testInstance.options.meta.fileName = recentlyImportedFile;
432
+ /**
433
+ * Define timeout on the test when exists globally
434
+ */
435
+ if (globalTimeout !== undefined) {
436
+ testInstance.timeout(globalTimeout);
410
437
  }
411
438
  /**
412
- * Get timeout
439
+ * Define test executor function
413
440
  */
414
- if (parsed.timeout) {
415
- const value = Number(parsed.timeout);
416
- if (!isNaN(value)) {
417
- config.timeout = value;
418
- }
441
+ if (callback) {
442
+ testInstance.run(callback);
419
443
  }
420
444
  /**
421
- * Get forceExit
445
+ * Add test to the group or suite
422
446
  */
423
- if (parsed.forceExit) {
424
- config.forceExit = true;
447
+ if (activeGroup) {
448
+ activeGroup.add(testInstance);
425
449
  }
426
- return config;
450
+ else {
451
+ activeSuite.add(testInstance);
452
+ }
453
+ return testInstance;
427
454
  }
428
- exports.processCliArgs = processCliArgs;
455
+ exports.test = test;
456
+ /**
457
+ * Define test group
458
+ */
459
+ test.group = function (title, callback) {
460
+ ensureIsConfigured('Cannot add test group without configuring the test runner');
461
+ /**
462
+ * Disallow nested groups
463
+ */
464
+ if (activeGroup) {
465
+ throw new Error('Cannot create nested test groups');
466
+ }
467
+ activeGroup = new Core_1.Group(title, emitter, runnerOptions.refiner);
468
+ /**
469
+ * Set filename
470
+ */
471
+ activeGroup.options.meta.fileName = recentlyImportedFile;
472
+ /**
473
+ * Add group to the default suite
474
+ */
475
+ activeSuite.add(activeGroup);
476
+ callback(activeGroup);
477
+ activeGroup = undefined;
478
+ };
@@ -44,16 +44,24 @@ export declare type BaseConfig = {
44
44
  refiner?: Refiner;
45
45
  forceExit?: boolean;
46
46
  } & Partial<RunnerHooks>;
47
+ /**
48
+ * Type for the "config.files" property
49
+ */
50
+ export declare type ConfigFiles = string[] | (() => string[] | Promise<string[]>);
51
+ /**
52
+ * Type for the "config.suite" property
53
+ */
54
+ export declare type ConfigSuite = {
55
+ name: string;
56
+ files: string | string[] | (() => string[] | Promise<string[]>);
57
+ configure?: (suite: Suite) => void;
58
+ timeout?: number;
59
+ };
47
60
  /**
48
61
  * Configuration options
49
62
  */
50
63
  export declare type Config = BaseConfig & ({
51
- files: string[] | (() => string[] | Promise<string[]>);
64
+ files: ConfigFiles;
52
65
  } | {
53
- suites: {
54
- name: string;
55
- files: string | string[] | (() => string[] | Promise<string[]>);
56
- configure?: (suite: Suite) => void;
57
- timeout?: number;
58
- }[];
66
+ suites: ConfigSuite[];
59
67
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@japa/runner",
3
- "version": "2.0.8",
3
+ "version": "2.1.1",
4
4
  "description": "Runner for Japa testing framework",
5
5
  "main": "build/index.js",
6
6
  "files": [
@@ -15,7 +15,7 @@
15
15
  "mrm": "mrm --preset=@adonisjs/mrm-preset",
16
16
  "pretest": "npm run lint",
17
17
  "test": "node .bin/test.js",
18
- "clean": "del build",
18
+ "clean": "del-cli build",
19
19
  "compile": "npm run lint && npm run clean && tsc",
20
20
  "build": "npm run compile",
21
21
  "prepublishOnly": "npm run build",
@@ -35,22 +35,22 @@
35
35
  "license": "MIT",
36
36
  "devDependencies": {
37
37
  "@adonisjs/mrm-preset": "^5.0.3",
38
- "@adonisjs/require-ts": "^2.0.11",
39
- "@types/node": "^17.0.34",
40
- "commitizen": "^4.2.4",
38
+ "@adonisjs/require-ts": "^2.0.12",
39
+ "@types/node": "^18.7.14",
40
+ "commitizen": "^4.2.5",
41
41
  "cz-conventional-changelog": "^3.3.0",
42
- "del-cli": "^4.0.1",
43
- "eslint": "^8.15.0",
42
+ "del-cli": "^5.0.0",
43
+ "eslint": "^8.23.0",
44
44
  "eslint-config-prettier": "^8.5.0",
45
45
  "eslint-plugin-adonis": "^2.1.0",
46
- "eslint-plugin-prettier": "^4.0.0",
46
+ "eslint-plugin-prettier": "^4.2.1",
47
47
  "github-label-sync": "^2.2.0",
48
48
  "husky": "^8.0.1",
49
49
  "japa": "^4.0.0",
50
- "mrm": "^4.0.0",
51
- "np": "^7.6.1",
52
- "prettier": "^2.6.2",
53
- "typescript": "^4.6.4"
50
+ "mrm": "^4.1.0",
51
+ "np": "^7.6.2",
52
+ "prettier": "^2.7.1",
53
+ "typescript": "^4.8.2"
54
54
  },
55
55
  "mrmConfig": {
56
56
  "core": false,
@@ -105,8 +105,9 @@
105
105
  "anyBranch": false
106
106
  },
107
107
  "dependencies": {
108
- "@japa/core": "^6.0.5",
109
- "@japa/errors-printer": "^1.3.8",
108
+ "@japa/core": "^7.0.0",
109
+ "@japa/errors-printer": "^1.3.10",
110
+ "@poppinss/cliui": "^3.0.2",
110
111
  "@poppinss/hooks": "^6.0.2-0",
111
112
  "fast-glob": "^3.2.11",
112
113
  "getopts": "^2.3.0",