|
|
@ -1,82 +1,64 @@ |
|
|
|
'use strict'; |
|
|
|
const Promise = require('bluebird'); |
|
|
|
const isPromise = require('is-promise'); |
|
|
|
const autoBind = require('auto-bind'); |
|
|
|
const AvaError = require('./ava-error'); |
|
|
|
|
|
|
|
class Concurrent { |
|
|
|
constructor(tests, bail) { |
|
|
|
if (!Array.isArray(tests)) { |
|
|
|
throw new TypeError('Expected an array of tests'); |
|
|
|
constructor(runnables, bail) { |
|
|
|
if (!Array.isArray(runnables)) { |
|
|
|
throw new TypeError('Expected an array of runnables'); |
|
|
|
} |
|
|
|
|
|
|
|
this.results = []; |
|
|
|
this.passed = true; |
|
|
|
this.reason = null; |
|
|
|
this.tests = tests; |
|
|
|
this.runnables = runnables; |
|
|
|
this.bail = bail || false; |
|
|
|
|
|
|
|
autoBind(this); |
|
|
|
} |
|
|
|
|
|
|
|
run() { |
|
|
|
let results; |
|
|
|
let allPassed = true; |
|
|
|
|
|
|
|
try { |
|
|
|
results = this.tests.map(this._runTest); |
|
|
|
} catch (err) { |
|
|
|
if (err instanceof AvaError) { |
|
|
|
return this._results(); |
|
|
|
let pending; |
|
|
|
let rejectPending; |
|
|
|
let resolvePending; |
|
|
|
const allPromises = []; |
|
|
|
const handlePromise = promise => { |
|
|
|
if (!pending) { |
|
|
|
pending = new Promise((resolve, reject) => { |
|
|
|
rejectPending = reject; |
|
|
|
resolvePending = resolve; |
|
|
|
}); |
|
|
|
} |
|
|
|
|
|
|
|
throw err; |
|
|
|
} |
|
|
|
|
|
|
|
const isAsync = results.some(isPromise); |
|
|
|
|
|
|
|
if (isAsync) { |
|
|
|
return Promise.all(results) |
|
|
|
.catch(AvaError, () => {}) |
|
|
|
.then(this._results); |
|
|
|
} |
|
|
|
|
|
|
|
return this._results(); |
|
|
|
} |
|
|
|
_runTest(test, index) { |
|
|
|
const result = test.run(); |
|
|
|
allPromises.push(promise.then(result => { |
|
|
|
if (!result.passed) { |
|
|
|
allPassed = false; |
|
|
|
|
|
|
|
if (isPromise(result)) { |
|
|
|
return result.then(result => this._addResult(result, index)); |
|
|
|
} |
|
|
|
if (this.bail) { |
|
|
|
// Stop if the test failed and bail mode is on.
|
|
|
|
resolvePending(); |
|
|
|
} |
|
|
|
} |
|
|
|
}, rejectPending)); |
|
|
|
}; |
|
|
|
|
|
|
|
return this._addResult(result, index); |
|
|
|
} |
|
|
|
_addResult(result, index) { |
|
|
|
// Always save result when not in bail mode or all previous tests pass
|
|
|
|
if ((this.bail && this.passed) || !this.bail) { |
|
|
|
this.results[index] = result; |
|
|
|
} |
|
|
|
for (const runnable of this.runnables) { |
|
|
|
const result = runnable.run(); |
|
|
|
|
|
|
|
if (result.passed === false) { |
|
|
|
this.passed = false; |
|
|
|
if (isPromise(result)) { |
|
|
|
handlePromise(result); |
|
|
|
} else if (!result.passed) { |
|
|
|
if (this.bail) { |
|
|
|
// Stop if the test failed and bail mode is on.
|
|
|
|
return {passed: false}; |
|
|
|
} |
|
|
|
|
|
|
|
// Only set reason once
|
|
|
|
if (!this.reason) { |
|
|
|
this.reason = result.reason; |
|
|
|
allPassed = false; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (this.bail) { |
|
|
|
throw new AvaError('Error in Concurrent while in bail mode'); |
|
|
|
} |
|
|
|
if (pending) { |
|
|
|
Promise.all(allPromises).then(resolvePending); |
|
|
|
return pending.then(() => ({passed: allPassed})); |
|
|
|
} |
|
|
|
|
|
|
|
return result; |
|
|
|
} |
|
|
|
_results() { |
|
|
|
return { |
|
|
|
passed: this.passed, |
|
|
|
reason: this.reason, |
|
|
|
result: this.results |
|
|
|
}; |
|
|
|
return {passed: allPassed}; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|