Muthu Kumar
6 years ago
7 changed files with 186 additions and 195 deletions
@ -1,150 +0,0 @@ |
|||
'use strict'; |
|||
|
|||
const Promise = require('bluebird'); |
|||
Promise.object = require('@codefeathers/promise.object'); |
|||
const chalk = require('chalk'); |
|||
|
|||
const logger = require('./logger'); |
|||
const { isPromise, taggedStringify: _ } = require('../util'); |
|||
const constants = require('../util/symbols'); |
|||
|
|||
const snipStack = e => { |
|||
|
|||
if (e.stack) |
|||
e.stack = e.stack |
|||
.split('\n') |
|||
.reduceRight( |
|||
(acc, x) => |
|||
/* eslint-disable-next-line */ |
|||
acc.done |
|||
? acc.cur |
|||
: x.match(/at Object\.test.*\/src\/gunner\.js/) |
|||
? { cur: x, done: true } |
|||
: { cur: [x, acc.cur].join('\n') }, |
|||
{ cur: '' }) |
|||
.cur.trim(); |
|||
|
|||
return e; |
|||
|
|||
}; |
|||
|
|||
const unitReducer = |
|||
(units = [], stateMark) => |
|||
(state = {}) => |
|||
units.reduce( |
|||
(accumulator, unit) => |
|||
accumulator |
|||
.then(thisState => |
|||
Promise.resolve( |
|||
unit.run({ ...state, [stateMark]: thisState }) |
|||
) |
|||
.then(newState => |
|||
[ ...thisState, newState ])), |
|||
Promise.resolve(state[stateMark] || []), |
|||
); |
|||
|
|||
const runTests = (instance, options) => { |
|||
|
|||
const log = logger.create(options); |
|||
|
|||
const beforeAll = () => |
|||
unitReducer( |
|||
[ |
|||
...(instance.__hooks__.before[constants.Start] || []), |
|||
...(instance.__hooks__.after[constants.Start] || []), |
|||
], |
|||
'@start', |
|||
)(); |
|||
|
|||
const beforeEvery = state => |
|||
unitReducer( |
|||
instance.__hooks__.before['*'], |
|||
'@every', |
|||
)(state); |
|||
|
|||
const runner = state => Promise.mapSeries(instance.__tests__, each => { |
|||
|
|||
const beforeThis = |
|||
unitReducer( |
|||
instance.__hooks__.before[each.description], |
|||
'@this' |
|||
); |
|||
|
|||
const afterThis = |
|||
unitReducer( |
|||
instance.__hooks__.after[each.description], |
|||
'@afterThis' |
|||
); |
|||
|
|||
return Promise.object({ ...state, '@every': beforeEvery(state) }) |
|||
.then(state => Promise.object({ ...state, '@this': beforeThis(state) })) |
|||
.then(state => { |
|||
|
|||
const pred = each.test(state); |
|||
|
|||
/* There are 4 different cases at play: |
|||
1. A plain expect() is returned. |
|||
2. An array of [ expect() ] is returned |
|||
3. A plain expect() is wrapped in a promise |
|||
4. An array of [ expect() ] is wrapped in a promise. |
|||
Here we normalise all of them into something we can process */ |
|||
|
|||
if (!isPromise(pred) && !(pred && isPromise(pred[0]))) |
|||
throw new Error(`Malformed test '${each.description}'`); |
|||
const toTest = Array.isArray(pred) |
|||
? Promise.all(pred) |
|||
: pred.then(x => Array.isArray(x) ? Promise.all(x) : x); |
|||
|
|||
return ([ |
|||
state, |
|||
toTest |
|||
.then(() => { |
|||
log( |
|||
`${chalk`{green ✅}`} :: `, |
|||
`${each.description}` |
|||
); |
|||
return { |
|||
description: each.description, |
|||
result: constants.pass |
|||
}; |
|||
}) |
|||
.catch(e => { |
|||
const error = (e && e.stack) ? snipStack(e) : e; |
|||
const trace = (options.trace && error) |
|||
? `\n Traceback:\n ` + _`${error}` |
|||
: ''; |
|||
log( |
|||
`${chalk`{red ❌}`} :: `, |
|||
`${each.description}`, |
|||
`${trace}` |
|||
); |
|||
return { |
|||
description: each.description, |
|||
result: constants.fail, |
|||
error, |
|||
}; |
|||
}), |
|||
]); |
|||
|
|||
}) |
|||
.then(([state, result]) => afterThis(state).then(() => result)); |
|||
|
|||
}); |
|||
|
|||
const afterAll = |
|||
unitReducer( |
|||
[ |
|||
...(instance.__hooks__.before[constants.End] || []), |
|||
...(instance.__hooks__.after[constants.End] || []), |
|||
], |
|||
'@after-all', |
|||
); |
|||
|
|||
return Promise.object({ '@start': beforeAll() }) |
|||
.then(state => Promise.object({ ...state, '@results': runner(state)})) |
|||
.then(state => Promise.object({ ...state, '@end': afterAll(state) })) |
|||
.then(state => state['@results']); |
|||
|
|||
}; |
|||
|
|||
module.exports = runTests; |
@ -0,0 +1,21 @@ |
|||
const snipStack = e => { |
|||
|
|||
if (e.stack) |
|||
e.stack = e.stack |
|||
.split('\n') |
|||
.reduceRight( |
|||
(acc, x) => |
|||
/* eslint-disable-next-line */ |
|||
acc.done |
|||
? acc.cur |
|||
: x.match(/at Object\.test.*\/src\/gunner\.js/) |
|||
? { cur: x, done: true } |
|||
: { cur: [x, acc.cur].join('\n') }, |
|||
{ cur: '' }) |
|||
.cur.trim(); |
|||
|
|||
return e; |
|||
|
|||
}; |
|||
|
|||
module.exports = snipStack; |
@ -0,0 +1,124 @@ |
|||
// Only imported for JSDoc
|
|||
/* eslint-disable-next-line */ |
|||
const Gunner = require('../gunner'); |
|||
Promise.object = require('@codefeathers/promise.object'); |
|||
|
|||
const { last, pipe, pick, assignToObject } = require('../util'); |
|||
|
|||
const buildTestQueue = require('./buildTestQueue'); |
|||
|
|||
const findSkip = (skip, unit) => { |
|||
|
|||
const startFailed = skip.findIndex(x => |
|||
x.type === '@start'); |
|||
const everyFailed = skip.findIndex(x => |
|||
x.type === '@every'); |
|||
const beforeFailed = skip.findIndex(x => |
|||
x.description === unit.description); |
|||
|
|||
return (startFailed !== -1 |
|||
&& 'A start hook failed\n' |
|||
+ skip[startFailed].error) |
|||
|| (everyFailed !== -1 |
|||
&& 'An every hook failed\n' |
|||
+ skip[everyFailed].error) |
|||
|| (beforeFailed !== -1 |
|||
&& 'A before test hook failed\n' |
|||
+ skip[beforeFailed].error); |
|||
|
|||
}; |
|||
|
|||
const reduceQueue = |
|||
queue => queue.reduce( |
|||
(acc, item) => |
|||
Promise.resolve(acc) |
|||
.then(acc => { |
|||
|
|||
return Promise.all([last(acc.results), Promise.object(acc.state)]) |
|||
.then(([, state]) => { |
|||
|
|||
const toSkip = findSkip(acc.skip, item.unit); |
|||
return [toSkip, state]; |
|||
|
|||
}) |
|||
.then(([toSkip, state]) => { |
|||
|
|||
return toSkip |
|||
? { status: 'skip', description: toSkip } |
|||
: item.unit.run(state); |
|||
|
|||
}) |
|||
.then(result => { |
|||
|
|||
const { status } = result; |
|||
|
|||
const identifier = (item.unit.label) |
|||
|| (queue |
|||
.filter(i => i.type === item.type) |
|||
.filter(i => (i.unit.description |
|||
=== item.unit.description)) |
|||
.length); |
|||
|
|||
if (item.type === '@test') { |
|||
|
|||
const resultObject = { |
|||
status, |
|||
description: item.unit.description, |
|||
...((status === 'notOk' || status === 'skip') |
|||
&& {reason : result.error |
|||
|| result.rejection |
|||
|| result.description}) |
|||
}; |
|||
acc.results.push(resultObject); |
|||
|
|||
} else { |
|||
|
|||
const stateAddition = |
|||
/* eslint-disable-next-line */ |
|||
status === 'ok' |
|||
? result.promise ? result.resolve : result.value |
|||
: null; |
|||
|
|||
if (stateAddition) |
|||
assignToObject( |
|||
acc.state, item.type |
|||
)(identifier, stateAddition); |
|||
|
|||
} |
|||
|
|||
if (status === 'notOk') { |
|||
|
|||
acc.skip.push({ |
|||
type: item.type, |
|||
description: item.unit.description, |
|||
error: result.promise |
|||
? result.rejection |
|||
: result.error, |
|||
}); |
|||
|
|||
} |
|||
|
|||
return acc; |
|||
|
|||
}); |
|||
|
|||
}), |
|||
{ results: [], state: {}, skip: [] }, |
|||
); |
|||
|
|||
/** |
|||
* runs the test suite |
|||
* @param {Gunner} instance |
|||
* @param {object} options |
|||
*/ |
|||
const testrunner = (instance) => { |
|||
|
|||
return pipe( |
|||
buildTestQueue, |
|||
reduceQueue, |
|||
pick('results'), |
|||
)(instance); |
|||
|
|||
}; |
|||
|
|||
module.exports = testrunner; |
Loading…
Reference in new issue