const metaltest = (title) => { const suite = [] const only = [] const skipped = [] const before = [] const after = [] const end = [] let success = 0 let fail = 0 let skip = 0 const testSuccess = [] const testFail = [] const stats = (type, test, error) => { switch (type) { case 'success': success++ testSuccess.push(test) break case 'fail': fail++ testFail.push(Object.assign({}, test, { error })) break } return { title, success, fail, skip, total: success + fail, testSuccess, testFail } } const runner = (name, fn) => { suite.push({ name, fn }) } runner.only = (name, fn) => { only.push({ name, fn }) } runner.skip = (name, fn) => { skipped.push({ name, fn }) } runner.before = (fn) => { before.push(fn) } runner.after = (fn) => { after.push(fn) } runner.end = (name, fn) => { end.push({ name, fn }) } const notify = async (reporters, event, ...args) => { for (const reporter of reporters) { if (!reporter[event]) continue await reporter[event](...args) } } runner.run = async (...reporters) => { await notify(reporters, 'start', title) const tests = only.length ? only : suite if (only.length) { for (const test of suite) await notify(reporters, 'skip', test) } for (const test of skipped) await notify(reporters, 'skip', test) skip = only.length ? suite.length : skip skip += skipped.length for (const test of tests) { try { for (const fn of before) await fn() await test.fn() stats('success', test) notify(reporters, 'success', test) } catch (error) { stats('fail', test, error) await notify(reporters, 'fail', test, error) } } for (const test of end) { try { await test.fn(stats()) stats('success', test) await notify(reporters, 'success', test) } catch (error) { stats('fail', test, error) await notify(reporters, 'fail', test, error) } } for (const fn of after) await fn() const r = stats() await notify(reporters, 'end', r) return r } return runner } export { metaltest }