UNPKG

fancy-test

Version:

extendable utilities for testing

140 lines (139 loc) 5.02 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const _ = require("lodash"); const context = { test: it, plugins: {}, chain: [], }; function assignWithProps(target, ...sources) { sources.forEach(source => { if (!source) return; const descriptors = Object.keys(source).reduce((descriptors, key) => { descriptors[key] = Object.getOwnPropertyDescriptor(source, key); return descriptors; }, {}); // by default, Object.assign copies enumerable Symbols too Object.getOwnPropertySymbols(source).forEach(sym => { const descriptor = Object.getOwnPropertyDescriptor(source, sym); if (descriptor.enumerable) { descriptors[sym] = descriptor; } }); Object.defineProperties(target, descriptors); }); return target; } const base = (context) => { const end = (arg1, cb) => { const originalContext = context; if (_.isFunction(arg1)) { cb = arg1; arg1 = undefined; } if (!arg1) arg1 = context.expectation || 'test'; async function run(done) { context = assignWithProps({}, originalContext); if (context.retries) this.retries(context.retries); if (cb) { context.chain = [...context.chain, { run: async (input) => { await cb.call(this, input, done); }, }]; } for (let i = 0; i < context.chain.length; i++) { const handleError = async (err) => { context.error = err; i++; const handler = context.chain[i]; if (!handler || !handler.catch) return false; try { await handler.catch(context); delete context.error; return true; } catch (error) { return handleError(error); } }; const next = context.chain[i]; try { // eslint-disable-next-line no-await-in-loop if (next.run) await next.run(context); } catch (error) { // eslint-disable-next-line no-await-in-loop if (!await handleError(error)) break; } } for (const p of context.chain.reverse()) { // eslint-disable-next-line no-await-in-loop if (p.finally) await p.finally(context); } if (context.error) throw context.error; } return context.test(arg1, (cb && cb.length === 2) ? function (done) { if (context.timeout) this.timeout(context.timeout); run.call(this, done).catch(done); } : function () { if (context.timeout) this.timeout(context.timeout); return run.call(this); }); }; return Object.assign(Object.assign({}, Object.entries(context.plugins) .reduce((plugins, [k, v]) => { plugins[k] = (...args) => { const plugin = v(...args); // clone context first const c = Object.assign({}, context); if (plugin.init) plugin.init(c); return base(Object.assign(Object.assign({}, c), { chain: [...c.chain, plugin] })); }; return plugins; }, {})), { register(k, v) { return base(Object.assign(Object.assign({}, context), { plugins: Object.assign(Object.assign({}, context.plugins), { [k]: v }) })); }, do(cb) { return base(Object.assign(Object.assign({}, context), { chain: [...context.chain, { run: (input) => cb(input) }] })); }, finally(cb) { return base(Object.assign(Object.assign({}, context), { chain: [...context.chain, { finally: (input) => cb(input) }] })); }, add(key, v) { return base(Object.assign(Object.assign({}, context), { chain: [...context.chain, { run: async (ctx) => { // eslint-disable-next-line require-atomic-updates ctx[key] = await (_.isFunction(v) ? v(ctx) : v); }, }] })); }, end, it: end }); }; exports.default = base(context) .register('skip', () => ({ init: ctx => { ctx.test = it.skip; }, })) .register('only', () => ({ init: ctx => { ctx.test = it.only; }, })) .register('retries', (count) => ({ init: ctx => { ctx.retries = count; }, }));