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