japa
Version:
Lean test runner for Node.js
196 lines (195 loc) • 6.13 kB
JavaScript
"use strict";
/**
* @module Core
*/
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
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 Emitter_1 = require("../Emitter");
const Callable_1 = require("../Callable");
const utils_1 = require("../utils");
const Exceptions_1 = require("../Exceptions");
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;
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;
this._todo = typeof (this._callback) !== 'function';
this._timeout = options.timeout;
this._regression = options.regression;
if (options.skip) {
this._skip = true;
}
else if (options.skipInCI && is_ci_1.default) {
this._skip = true;
}
else if (options.runInCI && !is_ci_1.default) {
this._skip = true;
}
}
/**
* Returns a boolean, telling if exception is hard. Hard exceptions
* fails the regression tests too
*/
get _isHardException() {
return (0, utils_1.isCoreException)(this._error);
}
/**
* Runs test for given number retries
*/
_runTest() {
return new Promise((resolve, reject) => {
const op = retry_1.default.operation({ retries: this._retries, factor: 1 });
op.attempt(async () => {
(0, Callable_1.Callable)(this._resolveFn, this._callback, this._timeout)
.then(resolve)
.catch((error) => {
if (op.retry(error)) {
return;
}
reject(op.mainError());
});
});
});
}
/**
* The JSON representation of the test. This is emitted
* as an event to show test state.
*/
toJSON() {
let status = Contracts_1.ITestStatus.PENDING;
if (this._todo) {
status = Contracts_1.ITestStatus.TODO;
}
else if (this._skip) {
status = Contracts_1.ITestStatus.SKIPPED;
}
else if (this._completed && this._error) {
status = (this._regression && !this._isHardException) ? Contracts_1.ITestStatus.PASSED : Contracts_1.ITestStatus.FAILED;
}
else if (this._completed && !this._error) {
status = Contracts_1.ITestStatus.PASSED;
}
return {
title: this.title,
status: status,
regression: this._regression,
regressionMessage: this._regressionMessage,
duration: this._duration,
error: this._error,
};
}
/**
* Retry a test for the given number of counts, before marking
* it as failed.
*/
retry(counts) {
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) {
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 = (0, 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) {
throw new Exceptions_1.RegressionException('Expected regression test to fail');
}
}
catch (error) {
this._error = error;
if (!this._isHardException && this._regression) {
this._regressionMessage = error.message;
}
}
}
this._duration = start.rounded();
this._completed = true;
Emitter_1.emitter.emit(Contracts_1.IEvents.TESTCOMPLETED, this.toJSON());
}
}
exports.Test = Test;