UNPKG

5.02 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const _ = require("lodash");
4const context = {
5 test: it,
6 plugins: {},
7 chain: [],
8};
9function assignWithProps(target, ...sources) {
10 sources.forEach(source => {
11 if (!source)
12 return;
13 const descriptors = Object.keys(source).reduce((descriptors, key) => {
14 descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
15 return descriptors;
16 }, {});
17 // by default, Object.assign copies enumerable Symbols too
18 Object.getOwnPropertySymbols(source).forEach(sym => {
19 const descriptor = Object.getOwnPropertyDescriptor(source, sym);
20 if (descriptor.enumerable) {
21 descriptors[sym] = descriptor;
22 }
23 });
24 Object.defineProperties(target, descriptors);
25 });
26 return target;
27}
28const base = (context) => {
29 const end = (arg1, cb) => {
30 const originalContext = context;
31 if (_.isFunction(arg1)) {
32 cb = arg1;
33 arg1 = undefined;
34 }
35 if (!arg1)
36 arg1 = context.expectation || 'test';
37 async function run(done) {
38 context = assignWithProps({}, originalContext);
39 if (context.retries)
40 this.retries(context.retries);
41 if (cb) {
42 context.chain = [...context.chain, {
43 run: async (input) => {
44 await cb.call(this, input, done);
45 },
46 }];
47 }
48 for (let i = 0; i < context.chain.length; i++) {
49 const handleError = async (err) => {
50 context.error = err;
51 i++;
52 const handler = context.chain[i];
53 if (!handler || !handler.catch)
54 return false;
55 try {
56 await handler.catch(context);
57 delete context.error;
58 return true;
59 }
60 catch (error) {
61 return handleError(error);
62 }
63 };
64 const next = context.chain[i];
65 try {
66 // eslint-disable-next-line no-await-in-loop
67 if (next.run)
68 await next.run(context);
69 }
70 catch (error) {
71 // eslint-disable-next-line no-await-in-loop
72 if (!await handleError(error))
73 break;
74 }
75 }
76 for (const p of context.chain.reverse()) {
77 // eslint-disable-next-line no-await-in-loop
78 if (p.finally)
79 await p.finally(context);
80 }
81 if (context.error)
82 throw context.error;
83 }
84 return context.test(arg1, (cb && cb.length === 2) ? function (done) {
85 if (context.timeout)
86 this.timeout(context.timeout);
87 run.call(this, done).catch(done);
88 } : function () {
89 if (context.timeout)
90 this.timeout(context.timeout);
91 return run.call(this);
92 });
93 };
94 return Object.assign(Object.assign({}, Object.entries(context.plugins)
95 .reduce((plugins, [k, v]) => {
96 plugins[k] = (...args) => {
97 const plugin = v(...args);
98 // clone context first
99 const c = Object.assign({}, context);
100 if (plugin.init)
101 plugin.init(c);
102 return base(Object.assign(Object.assign({}, c), { chain: [...c.chain, plugin] }));
103 };
104 return plugins;
105 }, {})), { register(k, v) {
106 return base(Object.assign(Object.assign({}, context), { plugins: Object.assign(Object.assign({}, context.plugins), { [k]: v }) }));
107 },
108 do(cb) {
109 return base(Object.assign(Object.assign({}, context), { chain: [...context.chain, { run: (input) => cb(input) }] }));
110 },
111 finally(cb) {
112 return base(Object.assign(Object.assign({}, context), { chain: [...context.chain, { finally: (input) => cb(input) }] }));
113 },
114 add(key, v) {
115 return base(Object.assign(Object.assign({}, context), { chain: [...context.chain, {
116 run: async (ctx) => {
117 // eslint-disable-next-line require-atomic-updates
118 ctx[key] = await (_.isFunction(v) ? v(ctx) : v);
119 },
120 }] }));
121 },
122 end, it: end });
123};
124exports.default = base(context)
125 .register('skip', () => ({
126 init: ctx => {
127 ctx.test = it.skip;
128 },
129}))
130 .register('only', () => ({
131 init: ctx => {
132 ctx.test = it.only;
133 },
134}))
135 .register('retries', (count) => ({
136 init: ctx => {
137 ctx.retries = count;
138 },
139}));