UNPKG

3.58 kBJavaScriptView Raw
1process.setMaxListeners(0)
2process.once('beforeExit', next)
3const queue = []
4exports.test = (label, fn) => push({label, fn})
5exports.test.skip = (label, fn, doSkip = true) => push({
6 label,
7 fn: doSkip ? Function.prototype : fn
8})
9exports.test.timeout = (label, fn, ms, doTimeout = true) => push({
10 label,
11 fn (done, timeoutError = null) {
12 try {
13 const error = new Error(`TimeoutError: ${ms}ms exceeded.`)
14 const timeout = setTimeout(doTimeout ? done : Function.prototype, ms, error)
15 fn((err) => {
16 clearTimeout(timeout)
17 if (!timeout._called || !doTimeout) done(err)
18 })
19 if (fn.length === 0) done(null)
20 } catch (err) {
21 done(err)
22 }
23 }
24})
25exports.beforeEach = (before, {assign} = Object) => {
26 queue.map(context => {
27 const fn = context.fn
28 return assign(context, {
29 fn (done) {
30 try {
31 before()
32 fn(done)
33 if (fn.length === 0) done(null)
34 } catch (err) {
35 done(err)
36 }
37 }
38 })
39 })
40}
41exports.afterEach = (after, {assign} = Object) => {
42 queue.map(context => {
43 const fn = context.fn
44 return assign(context, {
45 fn (done) {
46 try {
47 fn((err) => {
48 try {
49 after(() => done(err))
50 if (after.length === 0) done(err)
51 } catch (err) {
52 done(err)
53 }
54 })
55 if (fn.length === 0) {
56 try {
57 after(done)
58 if (after.length === 0) done(null)
59 } catch (err) {
60 done(err)
61 }
62 }
63 } catch (err) {
64 done(err)
65 }
66 }
67 })
68 })
69}
70function push () {
71 queue.push(...arguments)
72}
73function next () {
74 if (queue.length === 0) return
75 const {fn, done} = shift(queue)
76 const handle = trap(done)
77 try {
78 queue.length = 0
79 fn(handle)
80 if (fn.length === 0) handle(null)
81 } catch (err) {
82 handle(err)
83 }
84 function trap (done) {
85 process.once('uncaughtException', done)
86 return (err = null) => {
87 process.removeListener('uncaughtException', done)
88 done(err)
89 }
90 }
91}
92function shift ([context, ...pending]) {
93 const {elapsed} = timerFor(context)
94 return {
95 done (err) {
96 const indent = indentFor(context)
97 if (queue.length > 0) { // context
98 console.log('%s%s', indent, context.label)
99 } else { // test
100 if (err) {
101 process.exitCode = 1
102 console.log('%s\x1b[31m✘\x1b[0m %s (%dms)', indent, context.label, elapsed())
103 if (err.name) {
104 console.log(' %s\x1b[31m%s\x1b[0m', indent, err.name, err.message)
105 console.error(err.stack.toString().split('\n').splice(1).join('\n'))
106 } else {
107 console.log(' %s%s', indent, err)
108 }
109 } else {
110 if (context.fn === Function.prototype) {
111 console.log('%s- %s', indent, context.label)
112 } else {
113 console.log('%s\x1b[32m✔\x1b[0m %s (%dms)', indent, context.label, elapsed())
114 }
115 }
116 }
117 queue.forEach(bindTo(context))
118 push(...pending)
119 next(err)
120 },
121 fn: context.fn
122 }
123}
124function indentFor (context, length = 0) {
125 if (context.parent === undefined) {
126 return Array.from({length}).fill(' ').join('')
127 }
128 return indentFor(context.parent, ++length)
129}
130function bindTo (parent, {assign} = Object) {
131 return (context) => assign(context, {parent})
132}
133function timerFor (context, initial = Date.now()) {
134 return {
135 elapsed () {
136 return Date.now() - initial
137 }
138 }
139}