UNPKG

tape

Version:

tap-producing test harness for node and browsers

231 lines (190 loc) 6.16 kB
'use strict'; var defined = require('defined'); var through = require('@ljharb/through'); var createDefaultStream = require('./lib/default_stream'); var Test = require('./lib/test'); var Results = require('./lib/results'); var canEmitExit = typeof process !== 'undefined' && process && typeof process.on === 'function' && /** @type {{ browser?: boolean }} */ (process).browser !== true; var canExit = typeof process !== 'undefined' && process && typeof process.exit === 'function'; /** * @import Tape, { * createHarness as CreateHarness, * CreateStream, * Harness, * HarnessConfig, * HarnessEventHandler, * TestOptions, * } from '.' */ /** @import { Result } from './lib/results' */ var tape = (function () { var wait = false; /** @type {undefined | Harness} */ var harness; /** @type {(opts?: HarnessConfig) => Harness} */ function getHarness(opts) { // this override is here since tests fail via nyc if createHarness is moved upwards if (!harness) { // eslint-disable-next-line no-use-before-define harness = createExitHarness(opts || {}, wait); } return harness; } /** @type {(this: Harness, ...args: Parameters<Tape>) => ReturnType<Tape>} */ function lazyLoad() { // eslint-disable-next-line no-invalid-this return getHarness().apply(this, arguments); } lazyLoad.wait = function () { wait = true; }; lazyLoad.run = function () { var run = getHarness().run; if (run) { run(); } }; lazyLoad.only = function () { return getHarness().only.apply(this, arguments); }; /** @type {CreateStream} */ lazyLoad.createStream = function (opts) { var options = opts || {}; if (!harness) { var output = through(); getHarness({ stream: output, objectMode: options.objectMode }); return output; } return harness.createStream(options); }; lazyLoad.onFinish = function () { return getHarness().onFinish.apply(this, arguments); }; lazyLoad.onFailure = function () { return getHarness().onFailure.apply(this, arguments); }; lazyLoad.getHarness = getHarness; return lazyLoad; }()); /** @type {CreateHarness} */ function createHarness(conf_) { var results = new Results({ todoIsOK: !!(process.env.TODO_IS_OK === '1') }); if (!conf_ || conf_.autoclose !== false) { results.once('done', function () { results.close(); }); } /** @type {(name: string, conf: TestOptions, cb: Test.TestCase) => Test} */ function test(name, conf, cb) { var t = new Test(name, conf, cb); test._tests.push(t); (function inspectCode(st) { st.on('test', /** @param {Test} st_ */ function sub(st_) { inspectCode(st_); }); st.on('result', /** @param {Result} r */ function (r) { if (!r.todo && !r.ok && typeof r !== 'string') { test._exitCode = 1; } }); }(t)); results.push(t); return t; } test._results = results; /** @type {Test[]} */ test._tests = []; /** @type {CreateStream} */ test.createStream = function (opts) { return results.createStream(opts); }; /** @type {HarnessEventHandler} */ test.onFinish = function (cb) { results.on('done', cb); }; /** @type {HarnessEventHandler} */ test.onFailure = function (cb) { results.on('fail', cb); }; var only = false; /** @returns {Test} */ test.only = function () { if (only) { throw new Error('there can only be one only test'); } if (conf_ && conf_.noOnly) { throw new Error('`only` tests are prohibited'); } only = true; var t = /** @type {Test} */ (test.apply(null, arguments)); results.only(t); return t; }; test._exitCode = 0; test.close = function () { results.close(); }; // @ts-expect-error TODO FIXME: why is `test` not assignable to `Harness`??? return test; } /** @type {(conf: Omit<HarnessConfig, 'autoclose'>, wait?: boolean) => Harness} */ function createExitHarness(config, wait) { var noOnly = config.noOnly; var objectMode = config.objectMode; var cStream = config.stream; var exit = config.exit; var harness = createHarness({ autoclose: !canEmitExit, noOnly: defined(noOnly, defined(process.env.NODE_TAPE_NO_ONLY_TEST, false)) }); var running = false; var ended = false; function run() { if (running) { return; } running = true; var stream = harness.createStream({ objectMode: objectMode }); var es = stream.pipe(/** @type {NodeJS.WritableStream} */ (cStream || createDefaultStream())); if (canEmitExit && es) { // in node v0.4, `es` is `undefined` // eslint-disable-next-line no-unused-vars es.on('error', function (_) { harness._exitCode = 1; }); } stream.on('end', function () { ended = true; }); } if (wait) { harness.run = run; } else { run(); } if (exit === false) { return harness; } if (!canEmitExit || !canExit) { return harness; } process.on('exit', /** @param {number | undefined} code */ function (code) { // let the process exit cleanly. if (typeof code === 'number' && code !== 0) { return; } if (!ended) { var only = harness._results._only; for (var i = 0; i < harness._tests.length; i++) { var t = harness._tests[i]; if (!only || t === only) { t._exit(); } } } harness.close(); process.removeAllListeners('exit'); // necessary for node v0.6 process.exit(code || harness._exitCode); // eslint-disable-line no-process-exit }); return harness; } module.exports = tape; module.exports.createHarness = createHarness; module.exports.Test = Test; module.exports.test = tape; // tap compat module.exports.skip = Test.skip; // @ts-expect-error TODO FIXME: attw errors without this line module.exports.createStream = tape.createStream; // @ts-expect-error TODO FIXME: attw errors without this line module.exports.only = tape.only; // @ts-expect-error TODO FIXME: attw errors without this line module.exports.getHarness = tape.getHarness; // @ts-expect-error TODO FIXME: attw errors without this line module.exports.run = tape.run; // @ts-expect-error TODO FIXME: attw errors without this line module.exports.wait = tape.wait; // @ts-expect-error TODO FIXME: attw errors without this line module.exports.onFinish = tape.onFinish; // @ts-expect-error TODO FIXME: attw errors without this line module.exports.onFailure = tape.onFailure;