UNPKG

undo3d-run-test

Version:

A collection of utilities for testing Undo3D, and apps built with Undo3D

144 lines (107 loc) 4.91 kB
//// LevelRunner (re-exported by all.mjs) //// //// The only public class in the ‘runner’ component. An instance of LevelRunner //// is the top level of a test, and contains several test suites. import Hub from './hub.mjs' import Level from './level.mjs' import LevelSuite from './level-suite.mjs' //// CLASS export default class LevelRunner extends Level { constructor (options={}) { //// A LevelRunner always has index `1`. if (null != options.index) throw Error('Don’t try passing in index!') const optionsCopy = Object.assign({ index:1 }, options) //// Create the event hub, if not in `options.hub`. optionsCopy.hub = options.hub || new Hub() //// Let `Level` validate, normalise and record `options`. super(optionsCopy) //// An array of LevelSuites. this._testSuites = [] } //// PUBLIC PROPERTIES //// Xx. get testSuites () { return this._testSuites } //// Together, `first` and `last` make the TAP ‘plan’. get tapPlan () { const max = this._testSuites.length + 1 // `+ 1` because the runner is index 1 return `${Math.min(max,this.last,this.first)}..${Math.min(max,this.last)}` } //// The test results, as a TAP string (read-only). @TODO proper TAP string get tap () { //// Best practice to start a TAP with this line. let result = [ 'TAP version 13' ] //// A skipped runner does not show its suites. if (this.skip) { result = result.concat([ `${this.tapPlan} # Skipped: ${this.skip}` , this.tapTestLine ]) //// Xx. } else { result = result.concat([ this.tapPlan // eg '1..300' , this.tapTestLine // eg 'not ok 1 - All the main unit tests' ]) this.testSuites.map( suite => result = result.concat(suite.tap) ) } return result.join('\n') } //// PUBLIC METHODS //// Create a new test suite, based on `options`. add (options) { if (null != options.hub) throw Error('Don’t try passing in hub here!') if (null != options.index) throw Error('Don’t try passing in index!') const hub = this._hub const index = this.testSuites.length + 2 // `+ 2` because the runner is index 1 @TODO nah - runner should not have an index const optionsCopy = Object.assign({ hub, index }, options) if (this.first > index || this.last < index) // override existing options.skip optionsCopy.skip = `Suite ${index} is outside ${this.tapPlan}` const testSuite = new LevelSuite(optionsCopy) this.testSuites.push(testSuite) return testSuite } //// Add an event listener. The behavior of this method depends on the `hub` //// option passed to the constructor. If no `hub` option was passed, see //// runner/hub.mjs for details. on (...args) { return this._hub.on.apply(this._hub, args) } //// Run all test suites. async run () { const { hub, testSuites, before, after, beforeEach, afterEach } = this //// Potentially don’t run the runner. if (this.skip) return //@TODO fire an event? //// Update the suite status and fire a 'start' event. this._status = 'running' hub.fire('runner-start runner-update') //// Update the runner’s status every time a suite’s status updates. hub.on('suite-update', (eventName, suiteIndex) => { const testSuite = testSuites[ suiteIndex - 2 ] const oldStatus = this._status if ( /^not ok$|^failing$/.test(testSuite.status) ) this._status = 'failing' if (oldStatus !== this._status) hub.fire('runner-update') }) //// Create an array of `parcels` (empty objects), and run `before()`. const parcels = [...Array(testSuites.length+2)].map( ()=>new Object() ) parcels[0] = parcels[1] = null // parcels at index 0 and 1 are not used before(parcels) //// Run suites in parallel. @TODO guard against too many threads? async function runTestSuites () { const promises = testSuites.map( testSuite => { const parcel = parcels[testSuite.index] // each suite gets its own parcel beforeEach(parcel) const promise = testSuite.run(parcel) afterEach(parcel) return promise }) await Promise.all(promises) } await runTestSuites.call(this) //// Run `after()`. after(parcels) //// Update the status and fire an 'end' event. this._status = 'failing' === this._status ? 'not ok' : 'ok' hub.fire('runner-end runner-update') ////@TODO return something } }