acuminous/zUnit

View on GitHub
lib/Test.js

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
const Testable = require('./Testable');
const Options = require('./Options');
const Locals = require('./Locals');
const { timeout } = require('./utils');

const defaults = {
  timeout: 5000,
  reason: 'No reason given',
};

class Test extends Testable {
  constructor(name, fn, initial = {}) {
    super(name);
    this._fn = fn;
    this._options = new Options({ defaults, initial: fn ? initial : { reason: 'Pending', ...initial } });
    this._hooks = [];
  }

  get point() {
    return this._point;
  }

  get numberOfTests() {
    return 1;
  }

  get numberOfPasses() {
    return this.passed ? 1 : 0;
  }

  get numberOfFailures() {
    return this.failed ? 1 : 0;
  }

  get numberOfSkipped() {
    return this.skipped ? 1 : 0;
  }

  get pending() {
    return this._fn === undefined;
  }

  hasExclusiveTests() {
    return false;
  }

  _finalise(parent, point = 1, hooks = [], locals = new Locals()) {
    const test = new Test(this.name, this._fn, this._options.initial);
    test._parent = parent;
    test._locals = locals;
    test._point = point;
    test._hooks = hooks.map((hookSet) => hookSet._finalise(test));
    return test;
  }

  async run(reporter, propagatedOptions) {
    reporter.withTest(this);

    const options = this._options.apply(propagatedOptions);

    this._start();

    try {
      if (this._shouldSkip(options)) return this._skip(options.get('reason'));
      await timeout(() => this._runAll(options), options.get('timeout'));
      if (!this.failed && !this.skipped) this._pass();
    } catch (error) {
      this._fail(error);
    } finally {
      this._finish();
    }
  }

  async _runAll(options) {
    try {
      await this._runBefores(options);
      if (this.skipped) return;
      const done = this._makeDone();
      const api = this._getApi();
      const fn = this._fn(api, done.callback);
      await Promise.all([fn, done.promise]);
    } catch (error) {
      this._fail(error);
    } finally {
      this._run = true;
      await this._runAfters(options);
    }
  }

  async _runBefores(options) {
    for (let i = 0; i < this._hooks.length; i++) {
      await this._hooks[i].runBefores(options);
      if (this.skipped) break;
    }
  }

  async _runAfters(options) {
    for (let i = this._hooks.length - 1; i >= 0; i--) {
      await this._hooks[i].runAfters(options);
    }
  }

  _shouldSkip(options) {
    return this.pending || options.get('skip') || this._skipped;
  }

  _shouldRun(force) {
    return force || this.exclusive;
  }

  _shouldForce(force) {
    return force || this.exclusive;
  }

  _decorateApi(api) {
    return { ...api, test: this._getApi() };
  }
}

module.exports = Test;