Comparing version 3.0.1 to 3.1.0
"use strict"; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,12 +14,13 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.reporters = void 0; | ||
const list_1 = __importDefault(require("./src/Reporter/list")); | ||
var Test_1 = require("./src/Test"); | ||
exports.Test = Test_1.Test; | ||
Object.defineProperty(exports, "Test", { enumerable: true, get: function () { return Test_1.Test; } }); | ||
var Group_1 = require("./src/Group"); | ||
exports.Group = Group_1.Group; | ||
Object.defineProperty(exports, "Group", { enumerable: true, get: function () { return Group_1.Group; } }); | ||
var Runner_1 = require("./src/Runner"); | ||
exports.Runner = Runner_1.Runner; | ||
Object.defineProperty(exports, "Runner", { enumerable: true, get: function () { return Runner_1.Runner; } }); | ||
var Assert_1 = require("./src/Assert"); | ||
exports.Assert = Assert_1.Assert; | ||
Object.defineProperty(exports, "Assert", { enumerable: true, get: function () { return Assert_1.Assert; } }); | ||
const reporters = { listReporter: list_1.default }; | ||
exports.reporters = reporters; |
@@ -0,2 +1,5 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
import { test } from './src/SlimRunner'; | ||
export = test; |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const SlimRunner_1 = require("./src/SlimRunner"); | ||
@@ -3,0 +14,0 @@ const nextTick = typeof (setImmediate) !== 'undefined' ? setImmediate : process.nextTick; |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Assert = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const chai_1 = require("chai"); | ||
const Exceptions_1 = require("../Exceptions"); | ||
/** | ||
* The assert interface to run assertions using chaijs. | ||
* This class is a thin wrapper over [chaijs#assert](http://www.chaijs.com/api/assert/) allowing | ||
* assertion planning and all existing methods from chaijs are supported. | ||
* | ||
* The API doesn't cover all the methods. However, all methods from the chaiJs assert syntax are | ||
* supported. | ||
*/ | ||
class Assert { | ||
@@ -14,2 +34,3 @@ constructor() { | ||
} | ||
/* istanbul ignore else */ | ||
if (typeof (chai_1.assert[name]) !== 'undefined') { | ||
@@ -22,8 +43,18 @@ target._counts++; | ||
} | ||
/** | ||
* Use chai plugins | ||
*/ | ||
static use(fn) { | ||
return chai_1.use(fn); | ||
} | ||
/** | ||
* Plan for assertions | ||
*/ | ||
plan(count) { | ||
this._plannedCounts = count; | ||
} | ||
/** | ||
* Evaluate whether assertions count matches the | ||
* planned counts or not. | ||
*/ | ||
evaluate() { | ||
@@ -30,0 +61,0 @@ if (this._plannedCounts && this._plannedCounts !== this._counts) { |
@@ -0,2 +1,10 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
import { IResolver, ICallback } from '../Contracts'; | ||
/** | ||
* Calls a function and tracks it's completion in multiple ways. If original function | ||
* relies on `done`, then it will wait for `done` to be called. Also if timeout is | ||
* defined, the function will fail if doesn't return before the timeout. | ||
*/ | ||
export declare function Callable<T extends any[]>(resolveFn: IResolver<T>, callback: ICallback<T>, timeout: number): Promise<void>; |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,8 +9,29 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Callable = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const Exceptions_1 = require("../Exceptions"); | ||
const debug_1 = __importDefault(require("debug")); | ||
/** | ||
* @ignore | ||
*/ | ||
const debug = debug_1.default('japa'); | ||
/** | ||
* Executes the function as promise | ||
* @ignore | ||
*/ | ||
async function asPromise(fn, args) { | ||
return fn(...args); | ||
} | ||
/** | ||
* Calls a function and tracks it's completion in multiple ways. If original function | ||
* relies on `done`, then it will wait for `done` to be called. Also if timeout is | ||
* defined, the function will fail if doesn't return before the timeout. | ||
*/ | ||
function Callable(resolveFn, callback, timeout) { | ||
@@ -19,6 +43,18 @@ return new Promise((resolve, reject) => { | ||
}); | ||
/** | ||
* Finding if we need to wait for done to be called | ||
*/ | ||
const needsDone = args.length === callback.length; | ||
debug('needsDone %s', needsDone); | ||
/** | ||
* Is callable completed? | ||
*/ | ||
let completed = false; | ||
/** | ||
* Timer to timeout the test, if timeout is defined | ||
*/ | ||
let timer = null; | ||
/** | ||
* Clears the timer if it exists | ||
*/ | ||
function clearTimer() { | ||
@@ -30,2 +66,5 @@ if (timer) { | ||
} | ||
/** | ||
* Finish the callable | ||
*/ | ||
function finish(error) { | ||
@@ -50,2 +89,6 @@ if (typeof (postCallable) === 'function' && !error) { | ||
} | ||
/** | ||
* Done is passed to fn to mark them as completed. When `done` | ||
* is used, we need wait for it to be called or timeout. | ||
*/ | ||
function done(error) { | ||
@@ -61,2 +104,5 @@ if (completed) { | ||
} | ||
/** | ||
* Only set the timer, when timeout is over 0 | ||
*/ | ||
if (timeout > 0) { | ||
@@ -66,2 +112,3 @@ debug('creating timer'); | ||
debug('timeout'); | ||
/* istanbul ignore else */ | ||
if (!completed) { | ||
@@ -68,0 +115,0 @@ finish(new Exceptions_1.TimeoutException(`Test timeout after ${timeout} milliseconds`)); |
@@ -0,1 +1,4 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
export declare enum IEvents { | ||
@@ -17,2 +20,3 @@ TESTSTARTED = "test:started", | ||
regression: boolean; | ||
only?: boolean; | ||
skip?: boolean; | ||
@@ -77,3 +81,10 @@ skipInCI?: boolean; | ||
}; | ||
/** | ||
* Test callback function. Here `T` is the arguments resolved by | ||
* @IResolver | ||
*/ | ||
export declare type ICallback<T extends any[]> = (...args: T) => Promise<void> | void; | ||
/** | ||
* The resolver to return arguments for the tests and hooks | ||
*/ | ||
export declare type IResolver<T> = (done: Function, postRun?: Function) => T; |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.ITestStatus = exports.IGroupStatus = exports.IEvents = void 0; | ||
var IEvents; | ||
@@ -4,0 +8,0 @@ (function (IEvents) { |
@@ -0,4 +1,10 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
/// <reference types="node" /> | ||
import Emitter from 'events'; | ||
declare const emitter: Emitter; | ||
/** | ||
* @hidden | ||
*/ | ||
import { EventEmitter } from 'events'; | ||
declare const emitter: EventEmitter; | ||
export { emitter }; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
/** | ||
* @module Core | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const events_1 = __importDefault(require("events")); | ||
const emitter = new events_1.default(); | ||
exports.emitter = void 0; | ||
/** | ||
* @hidden | ||
*/ | ||
const events_1 = require("events"); | ||
const emitter = new events_1.EventEmitter(); | ||
exports.emitter = emitter; |
@@ -0,6 +1,18 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
/** | ||
* Raised when test or hook times out | ||
*/ | ||
export declare class TimeoutException extends Error { | ||
} | ||
/** | ||
* Raised when regression test passes instead of failing | ||
*/ | ||
export declare class RegressionException extends Error { | ||
} | ||
/** | ||
* Raised when assertion planning fails | ||
*/ | ||
export declare class InvalidAssertionsCount extends Error { | ||
} |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.InvalidAssertionsCount = exports.RegressionException = exports.TimeoutException = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
/** | ||
* Raised when test or hook times out | ||
*/ | ||
class TimeoutException extends Error { | ||
} | ||
exports.TimeoutException = TimeoutException; | ||
/** | ||
* Raised when regression test passes instead of failing | ||
*/ | ||
class RegressionException extends Error { | ||
} | ||
exports.RegressionException = RegressionException; | ||
/** | ||
* Raised when assertion planning fails | ||
*/ | ||
class InvalidAssertionsCount extends Error { | ||
} | ||
exports.InvalidAssertionsCount = InvalidAssertionsCount; |
@@ -0,5 +1,13 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
import { Test } from '../Test'; | ||
import { ICallback, IResolver, IGroupReport, ITestOptions, IOptions } from '../Contracts'; | ||
/** | ||
* Group holds `n` number of tests to be executed. Groups also allows | ||
* defining hooks to be called before and after each test and the | ||
* group itself. | ||
*/ | ||
export declare class Group<T extends any[], H extends any[]> { | ||
private _title; | ||
title: string; | ||
private _resolveTestFn; | ||
@@ -9,20 +17,93 @@ private _resolveHookFn; | ||
private _hooks; | ||
/** | ||
* Timeout defined on group will be applied to | ||
* all the tests by default. | ||
*/ | ||
private _timeout; | ||
/** | ||
* The test error (if any) | ||
*/ | ||
private _error; | ||
/** | ||
* Has test been executed | ||
*/ | ||
private _completed; | ||
/** | ||
* An array of tests related to the group. They are mutated by the | ||
* run method to filter and keep only the one's that matches | ||
* the grep filter. | ||
*/ | ||
private _tests; | ||
/** | ||
* Storing whether the group has any failing tests or | ||
* not. | ||
*/ | ||
private _hasFailingTests; | ||
constructor(_title: string, _resolveTestFn: IResolver<T>, _resolveHookFn: IResolver<H>, _options: IOptions); | ||
readonly hasErrors: boolean; | ||
/** | ||
* Is there a cherry picked test using the `only` property | ||
* or not? | ||
*/ | ||
private _hasCherryPickedTest; | ||
constructor(title: string, _resolveTestFn: IResolver<T>, _resolveHookFn: IResolver<H>, _options: IOptions); | ||
/** | ||
* Returns a boolean telling if group or any of the tests inside | ||
* the group has errors. | ||
*/ | ||
get hasErrors(): boolean; | ||
/** | ||
* Filter tests if grep value is defined | ||
*/ | ||
private _filterTests; | ||
/** | ||
* Run a hook and if it raises error, then we will | ||
* set the completed flag to true, along with the | ||
* error. | ||
*/ | ||
private _runHook; | ||
/** | ||
* Runs a single test along side with it's hooks. | ||
*/ | ||
private _runTest; | ||
/** | ||
* Runs all the tests one by one and also executes | ||
* the beforeEach and afterEach hooks | ||
*/ | ||
private _runTests; | ||
/** | ||
* Returns the JSON report for the group. The output of this | ||
* method is emitted as an event. | ||
*/ | ||
toJSON(): IGroupReport; | ||
/** | ||
* Define timeout for all the tests inside the group. Still | ||
* each test can override it's own timeout. | ||
*/ | ||
timeout(duration: number): this; | ||
/** | ||
* Create a new test as part of this group. | ||
*/ | ||
test(title: string, callback: ICallback<T>, testOptions?: Partial<ITestOptions>): Test<T>; | ||
/** | ||
* Add before hook to be executed before the group starts | ||
* executing tests. | ||
*/ | ||
before(cb: ICallback<H>): this; | ||
/** | ||
* Add after hook to be executed after the group has executed | ||
* all the tests. | ||
*/ | ||
after(cb: ICallback<H>): this; | ||
/** | ||
* Add before each hook to be execute before each test | ||
*/ | ||
beforeEach(cb: ICallback<H>): this; | ||
/** | ||
* Add after each hook to be execute before each test | ||
*/ | ||
afterEach(cb: ICallback<H>): this; | ||
/** | ||
* Run the group with it's hooks and all tests. Shouldn't be called | ||
* by the end user and Japa itself will call this method | ||
*/ | ||
run(): Promise<void>; | ||
} |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
/** | ||
* @module Core | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const ow_1 = __importDefault(require("ow")); | ||
exports.Group = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const Hook_1 = require("../Hook"); | ||
@@ -11,5 +19,10 @@ const Test_1 = require("../Test"); | ||
const Contracts_1 = require("../Contracts"); | ||
/** | ||
* Group holds `n` number of tests to be executed. Groups also allows | ||
* defining hooks to be called before and after each test and the | ||
* group itself. | ||
*/ | ||
class Group { | ||
constructor(_title, _resolveTestFn, _resolveHookFn, _options) { | ||
this._title = _title; | ||
constructor(title, _resolveTestFn, _resolveHookFn, _options) { | ||
this.title = title; | ||
this._resolveTestFn = _resolveTestFn; | ||
@@ -24,10 +37,49 @@ this._resolveHookFn = _resolveHookFn; | ||
}; | ||
/** | ||
* The test error (if any) | ||
*/ | ||
this._error = null; | ||
/** | ||
* Has test been executed | ||
*/ | ||
this._completed = false; | ||
/** | ||
* An array of tests related to the group. They are mutated by the | ||
* run method to filter and keep only the one's that matches | ||
* the grep filter. | ||
*/ | ||
this._tests = []; | ||
/** | ||
* Storing whether the group has any failing tests or | ||
* not. | ||
*/ | ||
this._hasFailingTests = false; | ||
/** | ||
* Is there a cherry picked test using the `only` property | ||
* or not? | ||
*/ | ||
this._hasCherryPickedTest = false; | ||
} | ||
/** | ||
* Returns a boolean telling if group or any of the tests inside | ||
* the group has errors. | ||
*/ | ||
get hasErrors() { | ||
return this._hasFailingTests || !!this._error; | ||
} | ||
/** | ||
* Filter tests if grep value is defined | ||
*/ | ||
_filterTests() { | ||
if (!this._options.grep) { | ||
return; | ||
} | ||
const filteredTests = this._tests.filter((test) => this._options.grep.test(test.title)); | ||
this._tests = filteredTests; | ||
} | ||
/** | ||
* Run a hook and if it raises error, then we will | ||
* set the completed flag to true, along with the | ||
* error. | ||
*/ | ||
async _runHook(fn) { | ||
@@ -42,3 +94,9 @@ try { | ||
} | ||
/** | ||
* Runs a single test along side with it's hooks. | ||
*/ | ||
async _runTest(test) { | ||
/** | ||
* Run beforeEach hooks | ||
*/ | ||
for (let hook of this._hooks.beforeEach) { | ||
@@ -50,6 +108,16 @@ if (this._completed) { | ||
} | ||
/** | ||
* Return early if completed is set to true (happens when any hook throws error) | ||
*/ | ||
if (this._completed) { | ||
return; | ||
} | ||
/** | ||
* Otherwise run the test | ||
*/ | ||
await test.run(); | ||
/** | ||
* Setting flag to true when any one test has failed. This helps | ||
* in telling runner to exit process with the correct status. | ||
*/ | ||
const testFailed = test.toJSON().status === Contracts_1.ITestStatus.FAILED; | ||
@@ -59,2 +127,6 @@ if (!this._hasFailingTests && testFailed) { | ||
} | ||
/** | ||
* Mark group as completed when bail is set to true and | ||
* test has failed | ||
*/ | ||
if (this._options.bail && testFailed) { | ||
@@ -64,2 +136,5 @@ this._completed = true; | ||
} | ||
/** | ||
* Run all after each hooks | ||
*/ | ||
for (let hook of this._hooks.afterEach) { | ||
@@ -72,3 +147,13 @@ if (this._completed) { | ||
} | ||
/** | ||
* Runs all the tests one by one and also executes | ||
* the beforeEach and afterEach hooks | ||
*/ | ||
async _runTests() { | ||
/** | ||
* Run all the tests in sequence. If any hook beforeEach or afterEach | ||
* hook fails, it will set `complete = true` and then we break out | ||
* of the loop, since if hooks are failing, then there is no | ||
* point is running tests. | ||
*/ | ||
for (let test of this._tests) { | ||
@@ -78,7 +163,9 @@ if (this._completed) { | ||
} | ||
if (!this._options.grep || this._options.grep.test(test.toJSON().title)) { | ||
await this._runTest(test); | ||
} | ||
await this._runTest(test); | ||
} | ||
} | ||
/** | ||
* Returns the JSON report for the group. The output of this | ||
* method is emitted as an event. | ||
*/ | ||
toJSON() { | ||
@@ -93,3 +180,3 @@ let status = Contracts_1.IGroupStatus.PENDING; | ||
return { | ||
title: this._title, | ||
title: this.title, | ||
status: status, | ||
@@ -99,4 +186,10 @@ error: this._error, | ||
} | ||
/** | ||
* Define timeout for all the tests inside the group. Still | ||
* each test can override it's own timeout. | ||
*/ | ||
timeout(duration) { | ||
ow_1.default(duration, 'duration', ow_1.default.number.integer); | ||
if (typeof (duration) !== 'number') { | ||
throw new Error('"group.timeout" expects a valid integer'); | ||
} | ||
if (this._tests.length) { | ||
@@ -108,4 +201,9 @@ throw new Error('group.timeout must be called before defining the tests'); | ||
} | ||
/** | ||
* Create a new test as part of this group. | ||
*/ | ||
test(title, callback, testOptions) { | ||
ow_1.default(title, 'title', ow_1.default.string.nonEmpty); | ||
if (!title.trim()) { | ||
throw new Error('test title cannot be empty'); | ||
} | ||
testOptions = Object.assign({ | ||
@@ -117,29 +215,83 @@ regression: false, | ||
}, testOptions); | ||
/** | ||
* Using group timeout as a priority over runner timeout | ||
*/ | ||
testOptions.timeout = this._timeout !== undefined ? this._timeout : this._options.timeout; | ||
const test = new Test_1.Test(title, this._resolveTestFn, callback, testOptions); | ||
/** | ||
* Do not track test when a test has been cherry picked earlier | ||
*/ | ||
if (this._hasCherryPickedTest) { | ||
return test; | ||
} | ||
/** | ||
* Remove all existing tests, when a test has a `.only` property | ||
* set to true. | ||
*/ | ||
if (testOptions.only === true) { | ||
this._hasCherryPickedTest = true; | ||
this._tests = []; | ||
} | ||
this._tests.push(test); | ||
return test; | ||
} | ||
/** | ||
* Add before hook to be executed before the group starts | ||
* executing tests. | ||
*/ | ||
before(cb) { | ||
ow_1.default(cb, 'cb', ow_1.default.function); | ||
if (typeof (cb) !== 'function') { | ||
throw new Error('"group.before" expects callback to be a valid function'); | ||
} | ||
this._hooks.before.push(new Hook_1.Hook(this._resolveHookFn, cb, 'before')); | ||
return this; | ||
} | ||
/** | ||
* Add after hook to be executed after the group has executed | ||
* all the tests. | ||
*/ | ||
after(cb) { | ||
ow_1.default(cb, 'cb', ow_1.default.function); | ||
if (typeof (cb) !== 'function') { | ||
throw new Error('"group.after" expects callback to be a valid function'); | ||
} | ||
this._hooks.after.push(new Hook_1.Hook(this._resolveHookFn, cb, 'after')); | ||
return this; | ||
} | ||
/** | ||
* Add before each hook to be execute before each test | ||
*/ | ||
beforeEach(cb) { | ||
ow_1.default(cb, 'cb', ow_1.default.function); | ||
if (typeof (cb) !== 'function') { | ||
throw new Error('"group.beforeEach" expects callback to be a valid function'); | ||
} | ||
this._hooks.beforeEach.push(new Hook_1.Hook(this._resolveHookFn, cb, 'beforeEach')); | ||
return this; | ||
} | ||
/** | ||
* Add after each hook to be execute before each test | ||
*/ | ||
afterEach(cb) { | ||
ow_1.default(cb, 'cb', ow_1.default.function); | ||
if (typeof (cb) !== 'function') { | ||
throw new Error('"group.afterEach" expects callback to be a valid function'); | ||
} | ||
this._hooks.afterEach.push(new Hook_1.Hook(this._resolveHookFn, cb, 'afterEach')); | ||
return this; | ||
} | ||
/** | ||
* Run the group with it's hooks and all tests. Shouldn't be called | ||
* by the end user and Japa itself will call this method | ||
*/ | ||
async run() { | ||
this._filterTests(); | ||
/** | ||
* Return early when no tests are defined | ||
*/ | ||
if (!this._tests.length) { | ||
this._completed = true; | ||
return; | ||
} | ||
Emitter_1.emitter.emit(Contracts_1.IEvents.GROUPSTARTED, this.toJSON()); | ||
/** | ||
* Run all before hooks for the group | ||
*/ | ||
for (let hook of this._hooks.before) { | ||
@@ -151,5 +303,12 @@ if (this._completed) { | ||
} | ||
/** | ||
* Run the tests, if complete flag is not set to true. It is | ||
* set to true, when any before hooks fail | ||
*/ | ||
if (!this._completed) { | ||
await this._runTests(); | ||
} | ||
/** | ||
* Run all after hooks | ||
*/ | ||
for (let hook of this._hooks.after) { | ||
@@ -156,0 +315,0 @@ if (this._completed) { |
@@ -0,2 +1,8 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
import { IResolver, ICallback } from '../Contracts'; | ||
/** | ||
* Hook class is used for running the group hooks. | ||
*/ | ||
export declare class Hook<T extends any[]> { | ||
@@ -7,3 +13,7 @@ private _resolveFn; | ||
constructor(_resolveFn: IResolver<T>, _fn: ICallback<T>, _hookLifecycle: string); | ||
/** | ||
* Run the hook. The hooks will raise errors and the parent test or | ||
* group should emit required events for them. | ||
*/ | ||
run(): Promise<void>; | ||
} |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Hook = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const Callable_1 = require("../Callable"); | ||
/** | ||
* Hook class is used for running the group hooks. | ||
*/ | ||
class Hook { | ||
@@ -10,2 +25,6 @@ constructor(_resolveFn, _fn, _hookLifecycle) { | ||
} | ||
/** | ||
* Run the hook. The hooks will raise errors and the parent test or | ||
* group should emit required events for them. | ||
*/ | ||
async run() { | ||
@@ -12,0 +31,0 @@ try { |
@@ -0,15 +1,60 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
/** | ||
* List reporter to show the tests progress on stdout in | ||
* a list format | ||
*/ | ||
declare class ListReporter { | ||
private _store; | ||
constructor(emitter: any); | ||
private readonly _indent; | ||
private get _indent(); | ||
/** | ||
* When test runner has started. We just need to initiate | ||
* the store on this event | ||
*/ | ||
private _onStart; | ||
/** | ||
* Everytime a new group starts | ||
*/ | ||
private _onGroupStart; | ||
/** | ||
* Everytime a group has completed running all tests | ||
*/ | ||
private _onGroupEnd; | ||
/** | ||
* Print count for a label | ||
*/ | ||
private _printCount; | ||
/** | ||
* Prints the error for the test. If error is an assertion error, then | ||
* there is no need to print the stack. | ||
*/ | ||
private _printTestError; | ||
/** | ||
* Everytime tests ends | ||
*/ | ||
private _onTestEnd; | ||
/** | ||
* Returns a boolean if the error stack fine part of the | ||
* japa core | ||
*/ | ||
private _isNativeStackLine; | ||
/** | ||
* Returns a boolean telling if error stack is part | ||
* of japa core by finding the sorroundings. | ||
*/ | ||
private _isNativeSorroundedLine; | ||
/** | ||
* Returns the title for the failing test | ||
*/ | ||
private _getFailingTitle; | ||
/** | ||
* Returns the error stack by filtering the japa core | ||
* lines from it. | ||
*/ | ||
private _getStack; | ||
/** | ||
* When test runner stops | ||
*/ | ||
private _onEnd; | ||
@@ -16,0 +61,0 @@ } |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,8 +9,20 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const ms_1 = __importDefault(require("ms")); | ||
const chalk_1 = __importDefault(require("chalk")); | ||
const ms_1 = __importDefault(require("ms")); | ||
const right_pad_1 = __importDefault(require("right-pad")); | ||
const variable_diff_1 = __importDefault(require("variable-diff")); | ||
const right_pad_1 = __importDefault(require("right-pad")); | ||
const utils_1 = require("../utils"); | ||
const Contracts_1 = require("../Contracts"); | ||
/** | ||
* Icons to be used for different test and group | ||
* statuses | ||
*/ | ||
const icons = { | ||
@@ -20,2 +35,6 @@ passed: chalk_1.default.green('✓'), | ||
}; | ||
/** | ||
* Colors to be used for different test and group | ||
* statuses | ||
*/ | ||
const colors = { | ||
@@ -28,2 +47,6 @@ passed: 'grey', | ||
}; | ||
/** | ||
* List reporter to show the tests progress on stdout in | ||
* a list format | ||
*/ | ||
class ListReporter { | ||
@@ -41,2 +64,6 @@ constructor(emitter) { | ||
} | ||
/** | ||
* When test runner has started. We just need to initiate | ||
* the store on this event | ||
*/ | ||
_onStart() { | ||
@@ -46,4 +73,10 @@ console.log(''); | ||
} | ||
/** | ||
* Everytime a new group starts | ||
*/ | ||
_onGroupStart(group) { | ||
this._store.recordGroup(group); | ||
/** | ||
* Log group title when it's not root | ||
*/ | ||
if (group.title !== 'root') { | ||
@@ -53,5 +86,11 @@ console.log(`\n${group.title}`); | ||
} | ||
/** | ||
* Everytime a group has completed running all tests | ||
*/ | ||
_onGroupEnd(group) { | ||
this._store.endGroup(group); | ||
} | ||
/** | ||
* Print count for a label | ||
*/ | ||
_printCount(label, count) { | ||
@@ -62,2 +101,6 @@ if (count) { | ||
} | ||
/** | ||
* Prints the error for the test. If error is an assertion error, then | ||
* there is no need to print the stack. | ||
*/ | ||
_printTestError(error) { | ||
@@ -78,2 +121,5 @@ if (utils_1.isCoreException(error)) { | ||
} | ||
/** | ||
* Everytime tests ends | ||
*/ | ||
_onTestEnd(test) { | ||
@@ -89,11 +135,26 @@ this._store.recordTest(test); | ||
} | ||
/** | ||
* Returns a boolean if the error stack fine part of the | ||
* japa core | ||
*/ | ||
_isNativeStackLine(line) { | ||
return ['Callable'].some((keyword) => line.includes(keyword)); | ||
} | ||
/** | ||
* Returns a boolean telling if error stack is part | ||
* of japa core by finding the sorroundings. | ||
*/ | ||
_isNativeSorroundedLine(line) { | ||
return ['Generator.next', 'new Promise'].some((keyword) => line.includes(keyword)); | ||
} | ||
/** | ||
* Returns the title for the failing test | ||
*/ | ||
_getFailingTitle(title) { | ||
return chalk_1.default.red(`${icons.failed} ${title}`); | ||
} | ||
/** | ||
* Returns the error stack by filtering the japa core | ||
* lines from it. | ||
*/ | ||
_getStack(errorStack) { | ||
@@ -118,5 +179,11 @@ let prevIsNative = false; | ||
} | ||
/** | ||
* When test runner stops | ||
*/ | ||
_onEnd() { | ||
this._store.close(); | ||
const report = this._store.getReport(); | ||
/** | ||
* Show zero executed tests when no tests were ran | ||
*/ | ||
if (report.total === 0 && report.groups.length === 0) { | ||
@@ -148,10 +215,10 @@ console.log(chalk_1.default.bgMagenta.white(' ZERO TESTS EXECUTED ')); | ||
if (failedHooks.length) { | ||
const { error, title } = failedHooks[0]; | ||
console.log(`${chalk_1.default.red(` (${title})`)} ${this._getStack(error.stack)}`); | ||
const failedHook = failedHooks[0]; | ||
console.log(`${chalk_1.default.red(` (${failedHook.title})`)} ${this._getStack(failedHook.error.stack)}`); | ||
} | ||
if (failedTests.length) { | ||
failedTests.forEach(({ title, error }) => { | ||
failedTests.forEach((test) => { | ||
console.log(''); | ||
console.log(` ${this._getFailingTitle(title)}`); | ||
this._printTestError(error); | ||
console.log(` ${this._getFailingTitle(test.title)}`); | ||
this._printTestError(test.error); | ||
}); | ||
@@ -158,0 +225,0 @@ } |
@@ -0,1 +1,4 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
/// <reference types="node" /> | ||
@@ -5,2 +8,6 @@ import { IOptions } from '../Contracts'; | ||
import { EventEmitter } from 'events'; | ||
/** | ||
* Runner class is used for defining global properties | ||
* and run all the tests inside the group. | ||
*/ | ||
export declare class Runner<T extends any[], H extends any[]> { | ||
@@ -11,5 +18,19 @@ private _groups; | ||
constructor(_groups: Group<T, H>[], _options: IOptions); | ||
readonly hasErrors: boolean; | ||
/** | ||
* Returns a boolean telling if any of the groups or it's tests | ||
* has errors. | ||
*/ | ||
get hasErrors(): boolean; | ||
/** | ||
* Define a set of test groups to use | ||
*/ | ||
useGroups(groups: Group<T, H>[]): this; | ||
/** | ||
* Define custom reporter | ||
*/ | ||
reporter(fn: (emitter: EventEmitter) => void): this; | ||
/** | ||
* Run all the tests inside the registered groups | ||
*/ | ||
run(): Promise<void>; | ||
} |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
/** | ||
* @module Core | ||
*/ | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Runner = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const Contracts_1 = require("../Contracts"); | ||
const Emitter_1 = require("../Emitter"); | ||
const ow_1 = __importDefault(require("ow")); | ||
/** | ||
* Runner class is used for defining global properties | ||
* and run all the tests inside the group. | ||
*/ | ||
class Runner { | ||
@@ -14,10 +26,29 @@ constructor(_groups, _options) { | ||
} | ||
/** | ||
* Returns a boolean telling if any of the groups or it's tests | ||
* has errors. | ||
*/ | ||
get hasErrors() { | ||
return !!this._groups.find((group) => group.hasErrors); | ||
} | ||
/** | ||
* Define a set of test groups to use | ||
*/ | ||
useGroups(groups) { | ||
this._groups = groups; | ||
return this; | ||
} | ||
/** | ||
* Define custom reporter | ||
*/ | ||
reporter(fn) { | ||
ow_1.default(fn, 'callback', ow_1.default.function); | ||
if (typeof (fn) !== 'function') { | ||
throw new Error('"runner.reporter" expects callback to be a valid function'); | ||
} | ||
this._reporterFn = fn; | ||
return this; | ||
} | ||
/** | ||
* Run all the tests inside the registered groups | ||
*/ | ||
async run() { | ||
@@ -27,6 +58,18 @@ if (typeof (this._reporterFn) !== 'function') { | ||
} | ||
/** | ||
* Give emitter instance to the reporter | ||
*/ | ||
this._reporterFn(Emitter_1.emitter, this._options); | ||
/** | ||
* Emit the started event | ||
*/ | ||
Emitter_1.emitter.emit(Contracts_1.IEvents.STARTED); | ||
/** | ||
* Run all the tests | ||
*/ | ||
for (let group of this._groups) { | ||
await group.run(); | ||
/** | ||
* Break when bail is true and group has errors | ||
*/ | ||
if (this._options.bail && group.hasErrors) { | ||
@@ -36,2 +79,5 @@ break; | ||
} | ||
/** | ||
* Emit completed event | ||
*/ | ||
Emitter_1.emitter.emit(Contracts_1.IEvents.COMPLETED); | ||
@@ -38,0 +84,0 @@ } |
@@ -0,18 +1,71 @@ | ||
/** | ||
* @module SlimRunner | ||
*/ | ||
import { Test } from '../Test'; | ||
import { Group } from '../Group'; | ||
import { Test } from '../Test'; | ||
import { Assert } from '../Assert'; | ||
import { ICallback, IConfigureOptions } from '../Contracts'; | ||
/** | ||
* The type for the arguments to be passed to a | ||
* test | ||
*/ | ||
declare type testArgs = [Assert, Function]; | ||
/** | ||
* The type for the arguments to be passed to a | ||
* hook | ||
*/ | ||
declare type hookArgs = [Function]; | ||
/** | ||
* Group instance exposed by slim runner | ||
*/ | ||
declare type runnerGroup = Pick<Group<testArgs, hookArgs>, Exclude<keyof Group<testArgs, hookArgs>, 'run' | 'toJSON' | 'test'>>; | ||
export declare function test(title: string, callback: ICallback<testArgs>): Pick<Test<[Assert, Function]>, "timeout" | "retry">; | ||
/** | ||
* Create a new test | ||
*/ | ||
export declare function test(title: string, callback: ICallback<testArgs>): Pick<Test<testArgs>, "title" | "timeout" | "retry">; | ||
/** | ||
* Run all the tests using the runner | ||
*/ | ||
export declare function run(exitProcess?: boolean): Promise<void>; | ||
export declare namespace test { | ||
/** | ||
* Create a new test to group all test together | ||
*/ | ||
function group(title: string, callback: (group: runnerGroup) => void): void; | ||
function skip(title: string, callback: ICallback<testArgs>): Pick<Test<[Assert, Function]>, "timeout" | "retry">; | ||
function skipInCI(title: string, callback: ICallback<testArgs>): Pick<Test<[Assert, Function]>, "timeout" | "retry">; | ||
function runInCI(title: string, callback: ICallback<testArgs>): Pick<Test<[Assert, Function]>, "timeout" | "retry">; | ||
function failing(title: string, callback: ICallback<testArgs>): Pick<Test<[Assert, Function]>, "timeout" | "retry">; | ||
/** | ||
* Only run the specified test | ||
*/ | ||
function only(title: string, callback: ICallback<testArgs>): Pick<Test<testArgs>, "title" | "timeout" | "retry">; | ||
/** | ||
* Create a test, and mark it as skipped. Skipped functions are | ||
* never executed. However, their hooks are executed | ||
*/ | ||
function skip(title: string, callback: ICallback<testArgs>): Pick<Test<testArgs>, "title" | "timeout" | "retry">; | ||
/** | ||
* Create a test, and mark it as skipped only when running in CI. Skipped | ||
* functions are never executed. However, their hooks are executed. | ||
*/ | ||
function skipInCI(title: string, callback: ICallback<testArgs>): Pick<Test<testArgs>, "title" | "timeout" | "retry">; | ||
/** | ||
* Create a test and run it only in the CI. | ||
*/ | ||
function runInCI(title: string, callback: ICallback<testArgs>): Pick<Test<testArgs>, "title" | "timeout" | "retry">; | ||
/** | ||
* Create regression test | ||
*/ | ||
function failing(title: string, callback: ICallback<testArgs>): Pick<Test<testArgs>, "title" | "timeout" | "retry">; | ||
/** | ||
* Configure test runner | ||
*/ | ||
function configure(options: Partial<IConfigureOptions>): void; | ||
/** | ||
* Nested only | ||
*/ | ||
namespace failing { | ||
/** | ||
* Only run the specified test | ||
*/ | ||
function only(title: string, callback: ICallback<testArgs>): Pick<Test<testArgs>, "title" | "timeout" | "retry">; | ||
} | ||
} | ||
export {}; |
"use strict"; | ||
/** | ||
* @module SlimRunner | ||
*/ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,11 +9,23 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const ow_1 = __importDefault(require("ow")); | ||
exports.run = exports.test = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const chalk_1 = __importDefault(require("chalk")); | ||
const Group_1 = require("../Group"); | ||
const Loader_1 = require("./Loader"); | ||
const Runner_1 = require("../Runner"); | ||
const Group_1 = require("../Group"); | ||
const Assert_1 = require("../Assert"); | ||
const Emitter_1 = require("../Emitter"); | ||
const list_1 = __importDefault(require("../Reporter/list")); | ||
const Loader_1 = require("./Loader"); | ||
const Emitter_1 = require("../Emitter"); | ||
const loader = new Loader_1.Loader(); | ||
/** | ||
* Returns arguments to be passed to the callback | ||
* of a test | ||
*/ | ||
function testArgsFn(done, postRun) { | ||
@@ -22,7 +37,26 @@ postRun(function postRunFn(assert) { | ||
} | ||
/** | ||
* Returns arguments to be passed to the callback of | ||
* a hook | ||
*/ | ||
function hookArgsFn(done) { | ||
return [done]; | ||
} | ||
/** | ||
* Store of groups | ||
*/ | ||
let groups = []; | ||
/** | ||
* The active group, in which all tests must be scoped | ||
*/ | ||
let activeGroup = null; | ||
/** | ||
* A flag to track, if `test.only` is used to cherry pick a | ||
* single test. All other tests are ignored from here | ||
* on. | ||
*/ | ||
let cherryPickedTest = false; | ||
/** | ||
* Options for the test runner | ||
*/ | ||
let runnerOptions = { | ||
@@ -32,5 +66,16 @@ bail: false, | ||
}; | ||
/** | ||
* Custom reporter function | ||
*/ | ||
let reporterFn = list_1.default; | ||
/** | ||
* Reference to runner hooks, to be defined inside configure | ||
* method | ||
*/ | ||
let beforeHooks = []; | ||
let afterHooks = []; | ||
/** | ||
* Adds the test to the active group. If there isn't any active | ||
* group, it will be created. | ||
*/ | ||
function addTest(title, callback, options) { | ||
@@ -43,2 +88,5 @@ if (!activeGroup) { | ||
} | ||
/** | ||
* Create a new test | ||
*/ | ||
function test(title, callback) { | ||
@@ -48,5 +96,12 @@ return addTest(title, callback); | ||
exports.test = test; | ||
/** | ||
* Run all the tests using the runner | ||
*/ | ||
async function run(exitProcess = true) { | ||
const runner = new Runner_1.Runner(groups, runnerOptions); | ||
const runner = new Runner_1.Runner([], runnerOptions); | ||
runner.reporter(reporterFn); | ||
/** | ||
* Execute before hooks before loading any files | ||
* from the disk | ||
*/ | ||
for (let hook of beforeHooks) { | ||
@@ -60,6 +115,18 @@ await hook(runner, Emitter_1.emitter); | ||
} | ||
loaderFiles.forEach((file) => require(file)); | ||
/** | ||
* Load all files from the loader | ||
*/ | ||
loaderFiles.forEach((file) => { | ||
/** | ||
* Do not require more files, when cherry picking | ||
* tests | ||
*/ | ||
if (cherryPickedTest) { | ||
return; | ||
} | ||
require(file); | ||
}); | ||
let hardException = null; | ||
try { | ||
await runner.run(); | ||
await runner.useGroups(groups).run(); | ||
} | ||
@@ -69,2 +136,5 @@ catch (error) { | ||
} | ||
/** | ||
* Executing after hooks before cleanup | ||
*/ | ||
for (let hook of afterHooks) { | ||
@@ -80,10 +150,54 @@ await hook(runner, Emitter_1.emitter); | ||
exports.run = run; | ||
// eslint-disable-next-line no-redeclare | ||
(function (test) { | ||
/** | ||
* Create a new test to group all test together | ||
*/ | ||
function group(title, callback) { | ||
/** | ||
* Do not add new groups when already cherry picked a test | ||
*/ | ||
if (cherryPickedTest) { | ||
return; | ||
} | ||
activeGroup = new Group_1.Group(title, testArgsFn, hookArgsFn, runnerOptions); | ||
/** | ||
* Track the group | ||
*/ | ||
groups.push(activeGroup); | ||
/** | ||
* Pass instance of the group to the callback. This enables defining lifecycle | ||
* hooks | ||
*/ | ||
callback(activeGroup); | ||
/** | ||
* Reset group after callback has been executed | ||
*/ | ||
activeGroup = null; | ||
} | ||
test.group = group; | ||
/** | ||
* Only run the specified test | ||
*/ | ||
function only(title, callback) { | ||
const testInstance = addTest(title, callback, { only: true }); | ||
/** | ||
* Empty out existing groups | ||
*/ | ||
groups = []; | ||
/** | ||
* Push the current active group | ||
*/ | ||
groups.push(activeGroup); | ||
/** | ||
* Turn on the flag | ||
*/ | ||
cherryPickedTest = true; | ||
return testInstance; | ||
} | ||
test.only = only; | ||
/** | ||
* Create a test, and mark it as skipped. Skipped functions are | ||
* never executed. However, their hooks are executed | ||
*/ | ||
function skip(title, callback) { | ||
@@ -93,2 +207,6 @@ return addTest(title, callback, { skip: true }); | ||
test.skip = skip; | ||
/** | ||
* Create a test, and mark it as skipped only when running in CI. Skipped | ||
* functions are never executed. However, their hooks are executed. | ||
*/ | ||
function skipInCI(title, callback) { | ||
@@ -98,2 +216,5 @@ return addTest(title, callback, { skipInCI: true }); | ||
test.skipInCI = skipInCI; | ||
/** | ||
* Create a test and run it only in the CI. | ||
*/ | ||
function runInCI(title, callback) { | ||
@@ -103,2 +224,5 @@ return addTest(title, callback, { runInCI: true }); | ||
test.runInCI = runInCI; | ||
/** | ||
* Create regression test | ||
*/ | ||
function failing(title, callback) { | ||
@@ -108,29 +232,77 @@ return addTest(title, callback, { regression: true }); | ||
test.failing = failing; | ||
/** | ||
* Configure test runner | ||
*/ | ||
function configure(options) { | ||
/** | ||
* Reset runner options before every configure call | ||
*/ | ||
runnerOptions = { | ||
bail: false, | ||
timeout: 2000, | ||
}; | ||
if (groups.length) { | ||
throw new Error('test.configure must be called before creating any tests'); | ||
} | ||
/** | ||
* Hold repoter fn to be passed to the runner | ||
*/ | ||
if (options.reporterFn) { | ||
reporterFn = options.reporterFn; | ||
} | ||
/** | ||
* Use bail option if defined by the end user | ||
*/ | ||
if (options.bail !== undefined) { | ||
runnerOptions.bail = options.bail; | ||
} | ||
/** | ||
* Use timeout if defined by the end user | ||
*/ | ||
if (typeof (options.timeout) === 'number') { | ||
runnerOptions.timeout = options.timeout; | ||
} | ||
/** | ||
* Use files glob if defined | ||
*/ | ||
if (options.files !== undefined) { | ||
loader.files(options.files); | ||
} | ||
/** | ||
* Use files filter if defined as function | ||
*/ | ||
if (typeof (options.filter) === 'function') { | ||
loader.filter(options.filter); | ||
} | ||
/** | ||
* Set after hooks | ||
*/ | ||
if (options.before) { | ||
ow_1.default(options.before, 'configure.before', ow_1.default.array); | ||
if (!Array.isArray(options.before)) { | ||
throw new Error('"configure.before" expects an array of functions'); | ||
} | ||
options.before.forEach((fn, index) => { | ||
if (typeof (fn) !== 'function') { | ||
throw new Error(`invalid value for "configure.before" at ${index} index`); | ||
} | ||
}); | ||
beforeHooks = options.before; | ||
} | ||
/** | ||
* Set before hooks | ||
*/ | ||
if (options.after) { | ||
ow_1.default(options.after, 'configure.after', ow_1.default.array); | ||
if (!Array.isArray(options.after)) { | ||
throw new Error('"configure.after" expects an array of functions'); | ||
} | ||
options.after.forEach((fn, index) => { | ||
if (typeof (fn) !== 'function') { | ||
throw new Error(`invalid value for "configure.after" at ${index} index`); | ||
} | ||
}); | ||
afterHooks = options.after; | ||
} | ||
/** | ||
* If grep is defined, then normalize it to regex | ||
*/ | ||
if (options.grep) { | ||
@@ -141,2 +313,16 @@ runnerOptions.grep = options.grep instanceof RegExp ? options.grep : new RegExp(options.grep); | ||
test.configure = configure; | ||
/** | ||
* Nested only | ||
*/ | ||
(function (failing) { | ||
/** | ||
* Only run the specified test | ||
*/ | ||
// eslint-disable-next-line no-shadow | ||
function only(title, callback) { | ||
runnerOptions.grep = new RegExp(title); | ||
return addTest(title, callback, { regression: true }); | ||
} | ||
failing.only = only; | ||
})(failing = test.failing || (test.failing = {})); | ||
})(test = exports.test || (exports.test = {})); |
@@ -0,7 +1,25 @@ | ||
/** | ||
* @module SlimRunner | ||
*/ | ||
/** | ||
* Loads files using the glob patterns. | ||
* | ||
* @class Loader | ||
*/ | ||
export declare class Loader { | ||
private _glob; | ||
private _filterFn; | ||
/** | ||
* Define the glob for the files | ||
*/ | ||
files(glob: string[] | string): void; | ||
/** | ||
* Define a custom filter function to filter files | ||
*/ | ||
filter(cb: (file: string) => void): void; | ||
/** | ||
* Returns an array of sorted files based on the glob | ||
* pattern. | ||
*/ | ||
loadFiles(): Promise<string[]>; | ||
} |
"use strict"; | ||
/** | ||
* @module SlimRunner | ||
*/ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.Loader = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
/** | ||
* Loads files using the glob patterns. | ||
* | ||
* @class Loader | ||
*/ | ||
class Loader { | ||
/** | ||
* Define the glob for the files | ||
*/ | ||
files(glob) { | ||
this._glob = glob; | ||
} | ||
/** | ||
* Define a custom filter function to filter files | ||
*/ | ||
filter(cb) { | ||
this._filterFn = cb; | ||
} | ||
/** | ||
* Returns an array of sorted files based on the glob | ||
* pattern. | ||
*/ | ||
async loadFiles() { | ||
@@ -26,2 +65,5 @@ if (!this._glob) { | ||
}); | ||
/** | ||
* If filterFn is defined, then filter the files | ||
*/ | ||
if (typeof (this._filterFn) === 'function') { | ||
@@ -28,0 +70,0 @@ filesPaths = filesPaths.filter((file) => this._filterFn(file)); |
@@ -0,22 +1,100 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
import { ITestOptions, ITestReport, ICallback, IResolver } from '../Contracts'; | ||
/** | ||
* Test class is used for running and defining a test. It supports following | ||
* top level config properties. | ||
* | ||
* - skip : Skip the test | ||
* - skipInCI : Skip the test on CI | ||
* - runInCI : Run only in CI | ||
*/ | ||
export declare class Test<T extends any[]> { | ||
private _title; | ||
title: string; | ||
private _resolveFn; | ||
private _callback; | ||
/** | ||
* When the callback for the function is not defined, then we mark | ||
* the test as todo | ||
*/ | ||
private _todo; | ||
/** | ||
* Mark failed tests as passed | ||
*/ | ||
private _regression; | ||
/** | ||
* Regression message is set when the passes, but it was meant | ||
* to fail | ||
*/ | ||
private _regressionMessage; | ||
/** | ||
* The test timeout. It can be overridden at multiple levels | ||
*/ | ||
private _timeout; | ||
/** | ||
* How many times, we should retry the function before marking | ||
* it as failed | ||
*/ | ||
private _retries; | ||
/** | ||
* The time spent to run the test. This includes the hooks | ||
* time. | ||
*/ | ||
private _duration; | ||
/** | ||
* The test error (if any) | ||
*/ | ||
private _error; | ||
/** | ||
* Whether or not to skip the test | ||
*/ | ||
private _skip; | ||
/** | ||
* Has test been executed | ||
*/ | ||
private _completed; | ||
constructor(_title: string, _resolveFn: IResolver<T>, _callback: ICallback<T> | undefined, options: ITestOptions); | ||
private readonly _isHardException; | ||
constructor(title: string, _resolveFn: IResolver<T>, _callback: ICallback<T> | undefined, options: ITestOptions); | ||
/** | ||
* Returns a boolean, telling if exception is hard. Hard exceptions | ||
* fails the regression tests too | ||
*/ | ||
private get _isHardException(); | ||
/** | ||
* Runs test for given number retries | ||
*/ | ||
private _runTest; | ||
/** | ||
* The JSON representation of the test. This is emitted | ||
* as an event to show test state. | ||
*/ | ||
toJSON(): ITestReport; | ||
/** | ||
* Retry a test for the given number of counts, before marking | ||
* it as failed. | ||
*/ | ||
retry(counts: number): this; | ||
/** | ||
* Set explicit timeout for the given test. | ||
*/ | ||
timeout(duration: number): this; | ||
/** | ||
* Runs the test. If retries are defined, then the test will be retried for the | ||
* given number of times before marked as failed. When retrying hooks are not | ||
* executed again. | ||
* | ||
* ```js | ||
* // stack | ||
* [before hook 1, before hook 2] | ||
* [test] (2 retries) | ||
* [after hook 1] | ||
* | ||
* + before hook 1 | ||
* + before hook 2 | ||
* test (original attempt = failed) | ||
* test (1st attempt = passed) | ||
* + after hook 1 | ||
* ``` | ||
*/ | ||
run(): Promise<void>; | ||
} |
"use strict"; | ||
/** | ||
* @module Core | ||
*/ | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
@@ -6,20 +9,54 @@ return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
const ow_1 = __importDefault(require("ow")); | ||
exports.Test = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const is_ci_1 = __importDefault(require("is-ci")); | ||
const retry_1 = __importDefault(require("retry")); | ||
const time_span_1 = __importDefault(require("time-span")); | ||
const retry_1 = __importDefault(require("retry")); | ||
const is_ci_1 = __importDefault(require("is-ci")); | ||
const Emitter_1 = require("../Emitter"); | ||
const Callable_1 = require("../Callable"); | ||
const Emitter_1 = require("../Emitter"); | ||
const utils_1 = require("../utils"); | ||
const Exceptions_1 = require("../Exceptions"); | ||
const utils_1 = require("../utils"); | ||
const Contracts_1 = require("../Contracts"); | ||
/** | ||
* Test class is used for running and defining a test. It supports following | ||
* top level config properties. | ||
* | ||
* - skip : Skip the test | ||
* - skipInCI : Skip the test on CI | ||
* - runInCI : Run only in CI | ||
*/ | ||
class Test { | ||
constructor(_title, _resolveFn, _callback, options) { | ||
this._title = _title; | ||
constructor(title, _resolveFn, _callback, options) { | ||
this.title = title; | ||
this._resolveFn = _resolveFn; | ||
this._callback = _callback; | ||
/** | ||
* Regression message is set when the passes, but it was meant | ||
* to fail | ||
*/ | ||
this._regressionMessage = ''; | ||
/** | ||
* How many times, we should retry the function before marking | ||
* it as failed | ||
*/ | ||
this._retries = 0; | ||
/** | ||
* The time spent to run the test. This includes the hooks | ||
* time. | ||
*/ | ||
this._duration = 0; | ||
/** | ||
* The test error (if any) | ||
*/ | ||
this._error = null; | ||
/** | ||
* Has test been executed | ||
*/ | ||
this._completed = false; | ||
@@ -39,5 +76,12 @@ this._todo = typeof (this._callback) !== 'function'; | ||
} | ||
/** | ||
* Returns a boolean, telling if exception is hard. Hard exceptions | ||
* fails the regression tests too | ||
*/ | ||
get _isHardException() { | ||
return utils_1.isCoreException(this._error); | ||
} | ||
/** | ||
* Runs test for given number retries | ||
*/ | ||
_runTest() { | ||
@@ -58,2 +102,6 @@ return new Promise((resolve, reject) => { | ||
} | ||
/** | ||
* The JSON representation of the test. This is emitted | ||
* as an event to show test state. | ||
*/ | ||
toJSON() { | ||
@@ -74,3 +122,3 @@ let status = Contracts_1.ITestStatus.PENDING; | ||
return { | ||
title: this._title, | ||
title: this.title, | ||
status: status, | ||
@@ -83,18 +131,54 @@ regression: this._regression, | ||
} | ||
/** | ||
* Retry a test for the given number of counts, before marking | ||
* it as failed. | ||
*/ | ||
retry(counts) { | ||
ow_1.default(counts, 'counts', ow_1.default.number.integer); | ||
if (typeof (counts) !== 'number') { | ||
throw new Error('"test.retry" expects a number value'); | ||
} | ||
this._retries = counts; | ||
return this; | ||
} | ||
/** | ||
* Set explicit timeout for the given test. | ||
*/ | ||
timeout(duration) { | ||
ow_1.default(duration, 'duration', ow_1.default.number.integer); | ||
if (typeof (duration) !== 'number') { | ||
throw new Error('"test.timeout" expects a number value'); | ||
} | ||
this._timeout = duration; | ||
return this; | ||
} | ||
/** | ||
* Runs the test. If retries are defined, then the test will be retried for the | ||
* given number of times before marked as failed. When retrying hooks are not | ||
* executed again. | ||
* | ||
* ```js | ||
* // stack | ||
* [before hook 1, before hook 2] | ||
* [test] (2 retries) | ||
* [after hook 1] | ||
* | ||
* + before hook 1 | ||
* + before hook 2 | ||
* test (original attempt = failed) | ||
* test (1st attempt = passed) | ||
* + after hook 1 | ||
* ``` | ||
*/ | ||
async run() { | ||
Emitter_1.emitter.emit(Contracts_1.IEvents.TESTSTARTED, this.toJSON()); | ||
const start = time_span_1.default(); | ||
/* istanbul ignore else */ | ||
if (!this._todo && !this._skip) { | ||
/** | ||
* Run the actual test | ||
*/ | ||
try { | ||
await this._runTest(); | ||
/** | ||
* Mark test as failed, when is regression but passed | ||
*/ | ||
if (this._regression) { | ||
@@ -101,0 +185,0 @@ throw new Exceptions_1.RegressionException('Expected regression test to fail'); |
@@ -0,6 +1,16 @@ | ||
/** | ||
* @module Core | ||
*/ | ||
import { IReport, IGroupReport, ITestReport } from '../Contracts'; | ||
/** | ||
* Tests store class records the tests being executed and | ||
* returns a report to be used by the reporters. | ||
*/ | ||
export declare class TestsStore { | ||
private _store; | ||
private _processStart; | ||
readonly activeGroup: { | ||
/** | ||
* Returns the currently active group | ||
*/ | ||
get activeGroup(): { | ||
title: string; | ||
@@ -16,9 +26,31 @@ failedTests: { | ||
}; | ||
/** | ||
* Record the group | ||
*/ | ||
recordGroup(group: IGroupReport): void; | ||
/** | ||
* End the group and record failed hooks | ||
*/ | ||
endGroup(group: IGroupReport): void; | ||
/** | ||
* Record the test | ||
*/ | ||
recordTest(test: ITestReport): void; | ||
/** | ||
* Open store | ||
*/ | ||
open(): void; | ||
/** | ||
* Close store | ||
*/ | ||
close(): void; | ||
/** | ||
* Return store report | ||
*/ | ||
getReport(): IReport; | ||
} | ||
/** | ||
* Returns a boolean telling if exception is part of core exceptions or | ||
* not. | ||
*/ | ||
export declare function isCoreException(error: any): boolean; |
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
/** | ||
* @module Core | ||
*/ | ||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } }); | ||
}) : (function(o, m, k, k2) { | ||
if (k2 === undefined) k2 = k; | ||
o[k2] = m[k]; | ||
})); | ||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { | ||
Object.defineProperty(o, "default", { enumerable: true, value: v }); | ||
}) : function(o, v) { | ||
o["default"] = v; | ||
}); | ||
var __importStar = (this && this.__importStar) || function (mod) { | ||
if (mod && mod.__esModule) return mod; | ||
var result = {}; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k]; | ||
result["default"] = mod; | ||
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); | ||
__setModuleDefault(result, mod); | ||
return result; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.isCoreException = exports.TestsStore = void 0; | ||
/* | ||
* japa | ||
* | ||
* (c) Harminder Virk <virk@adonisjs.com> | ||
* | ||
* For the full copyright and license information, please view the LICENSE | ||
* file that was distributed with this source code. | ||
*/ | ||
const time_span_1 = __importDefault(require("time-span")); | ||
const Contracts_1 = require("../Contracts"); | ||
const exceptions = __importStar(require("../Exceptions")); | ||
/** | ||
* Tests store class records the tests being executed and | ||
* returns a report to be used by the reporters. | ||
*/ | ||
class TestsStore { | ||
@@ -29,5 +57,11 @@ constructor() { | ||
} | ||
/** | ||
* Returns the currently active group | ||
*/ | ||
get activeGroup() { | ||
return this._store.groups[this._store.groups.length - 1]; | ||
} | ||
/** | ||
* Record the group | ||
*/ | ||
recordGroup(group) { | ||
@@ -37,2 +71,5 @@ this.open(); | ||
} | ||
/** | ||
* End the group and record failed hooks | ||
*/ | ||
endGroup(group) { | ||
@@ -44,8 +81,21 @@ if (group.status === Contracts_1.IGroupStatus.FAILED && this.activeGroup.title === group.title) { | ||
} | ||
/** | ||
* Record the test | ||
*/ | ||
recordTest(test) { | ||
this.open(); | ||
/** | ||
* Store reference to test and the group when test has been | ||
* failed | ||
*/ | ||
if (test.status === Contracts_1.ITestStatus.FAILED) { | ||
this.activeGroup.failedTests.push({ title: test.title, error: test.error }); | ||
} | ||
/** | ||
* Increment the total counter | ||
*/ | ||
this._store.total++; | ||
/** | ||
* Increment individual test statuses counters | ||
*/ | ||
switch (test.status) { | ||
@@ -65,2 +115,5 @@ case Contracts_1.ITestStatus.FAILED: | ||
} | ||
/** | ||
* Increment the regression counter | ||
*/ | ||
if (test.regression) { | ||
@@ -70,2 +123,5 @@ this._store.regressionCount++; | ||
} | ||
/** | ||
* Open store | ||
*/ | ||
open() { | ||
@@ -76,2 +132,5 @@ if (!this._processStart) { | ||
} | ||
/** | ||
* Close store | ||
*/ | ||
close() { | ||
@@ -82,2 +141,5 @@ if (!this._store.duration) { | ||
} | ||
/** | ||
* Return store report | ||
*/ | ||
getReport() { | ||
@@ -89,2 +151,6 @@ this.close(); | ||
exports.TestsStore = TestsStore; | ||
/** | ||
* Returns a boolean telling if exception is part of core exceptions or | ||
* not. | ||
*/ | ||
function isCoreException(error) { | ||
@@ -91,0 +157,0 @@ return !!Object.keys(exceptions).find((e) => error instanceof exceptions[e]); |
# The MIT License | ||
Copyright 2018 Harminder Virk, contributors | ||
Copyright 2020 Harminder virk, contributors | ||
@@ -5,0 +5,0 @@ 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: |
{ | ||
"name": "japa", | ||
"version": "3.0.1", | ||
"version": "3.1.0", | ||
"description": "Lean test runner for Node.js", | ||
@@ -22,5 +22,6 @@ "main": "build/index.js", | ||
"build": "npm run compile && typedoc && node bin/postBuild.js", | ||
"lint": "tslint --project tsconfig.json", | ||
"lint": "eslint . --ext=.ts", | ||
"release": "np", | ||
"version": "npm run build" | ||
"version": "npm run build", | ||
"prepublishOnly": "npm run build" | ||
}, | ||
@@ -39,32 +40,31 @@ "repository": { | ||
"chai": "^4.2.0", | ||
"chalk": "^2.4.2", | ||
"chalk": "^4.0.0", | ||
"debug": "^4.1.1", | ||
"fast-glob": "^3.0.4", | ||
"fast-glob": "^3.2.2", | ||
"is-ci": "^2.0.0", | ||
"ms": "^2.1.2", | ||
"ow": "^0.13.2", | ||
"retry": "^0.12.0", | ||
"right-pad": "^1.0.1", | ||
"time-span": "^3.1.0", | ||
"variable-diff": "^1.1.0" | ||
"time-span": "^4.0.0", | ||
"variable-diff": "^2.0.1" | ||
}, | ||
"devDependencies": { | ||
"@adonisjs/mrm-preset": "^2.1.0", | ||
"@types/chai": "^4.2.1", | ||
"@types/mocha": "^5.2.7", | ||
"@types/node": "^12.7.4", | ||
"commitizen": "^4.0.3", | ||
"cz-conventional-changelog": "^3.0.2", | ||
"del-cli": "^2.0.0", | ||
"@adonisjs/mrm-preset": "^2.3.0", | ||
"@types/chai": "^4.2.11", | ||
"@types/mocha": "^7.0.2", | ||
"@types/node": "^14.0.6", | ||
"commitizen": "^4.1.2", | ||
"cz-conventional-changelog": "^3.2.0", | ||
"del-cli": "^3.0.1", | ||
"doctoc": "^1.4.0", | ||
"mocha": "^6.2.0", | ||
"mrm": "^1.2.2", | ||
"np": "^5.0.3", | ||
"ts-node": "^8.3.0", | ||
"tslint": "^5.19.0", | ||
"tslint-eslint-rules": "^5.4.0", | ||
"typedoc": "^0.15.0", | ||
"typedoc-plugin-external-module-name": "^2.1.0", | ||
"typescript": "^3.6.2", | ||
"yorkie": "^2.0.0" | ||
"eslint": "^7.1.0", | ||
"eslint-plugin-adonis": "^1.0.10", | ||
"husky": "^4.2.5", | ||
"mocha": "^7.2.0", | ||
"mrm": "^2.3.0", | ||
"np": "^6.2.3", | ||
"ts-node": "^8.10.2", | ||
"typedoc": "^0.17.7", | ||
"typedoc-plugin-external-module-name": "^3.1.0", | ||
"typescript": "^3.9.3" | ||
}, | ||
@@ -78,4 +78,3 @@ "config": { | ||
"exclude": [ | ||
"test", | ||
"src/Reporter" | ||
"test" | ||
], | ||
@@ -94,9 +93,12 @@ "extension": [ | ||
], | ||
"gitHooks": { | ||
"pre-commit": "doctoc README.md --title='## Table of contents' && git add README.md" | ||
}, | ||
"np": { | ||
"contents": ".", | ||
"anyBranch": false | ||
}, | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "doctoc README.md --title='## Table of contents' && git add README.md", | ||
"commit-msg": "node ./node_modules/@adonisjs/mrm-preset/validateCommit/conventional/validate.js" | ||
} | ||
} | ||
} |
@@ -30,2 +30,3 @@ ![](http://res.cloudinary.com/adonisjs/image/upload/v1484834197/monk_di16hz.png) | ||
- [Skipping/Running tests in CI](#skippingrunning-tests-in-ci) | ||
- [Run selected tests](#run-selected-tests) | ||
- [Retry flaky tests](#retry-flaky-tests) | ||
@@ -301,2 +302,16 @@ - [Regression tests](#regression-tests) | ||
### Run selected tests | ||
Just like skipping tests, you can also run a specific test using `test.only` method. | ||
- If multiple tests uses `test.only`, then only the last one will be entertained. | ||
```js | ||
test.only('all other tests will be skipped, except me', () => { | ||
}) | ||
``` | ||
<br> | ||
--- | ||
### Retry flaky tests | ||
@@ -303,0 +318,0 @@ |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
140299
10
3609
563
+ Addedansi-styles@4.3.0(transitive)
+ Addedchalk@4.1.2(transitive)
+ Addedcolor-convert@2.0.1(transitive)
+ Addedcolor-name@1.1.4(transitive)
+ Addedconvert-hrtime@3.0.0(transitive)
+ Addedhas-flag@4.0.0(transitive)
+ Addedsupports-color@7.2.0(transitive)
+ Addedtime-span@4.0.0(transitive)
+ Addedvariable-diff@2.0.2(transitive)
- Removedow@^0.13.2
- Removedansi-regex@2.1.1(transitive)
- Removedansi-styles@2.2.13.2.1(transitive)
- Removedchalk@1.1.32.4.2(transitive)
- Removedcolor-convert@1.9.3(transitive)
- Removedcolor-name@1.1.3(transitive)
- Removedconvert-hrtime@2.0.0(transitive)
- Removedescape-string-regexp@1.0.5(transitive)
- Removedhas-ansi@2.0.0(transitive)
- Removedhas-flag@3.0.0(transitive)
- Removedobject-assign@4.1.1(transitive)
- Removedow@0.13.2(transitive)
- Removedstrip-ansi@3.0.1(transitive)
- Removedsupports-color@2.0.05.5.0(transitive)
- Removedtime-span@3.1.0(transitive)
- Removedtype-fest@0.5.2(transitive)
- Removedvariable-diff@1.1.0(transitive)
Updatedchalk@^4.0.0
Updatedfast-glob@^3.2.2
Updatedtime-span@^4.0.0
Updatedvariable-diff@^2.0.1