1 | 'use strict'
|
2 | const t = require('./tap.js')
|
3 | t.jobs = 1
|
4 | const tapStack = [ t ]
|
5 | let level = 0
|
6 | const suiteStack = []
|
7 |
|
8 | const describe = (name, fn, opt) =>
|
9 | new Suite(name, fn, opt)
|
10 |
|
11 | class Suite {
|
12 | constructor (name, fn, opt) {
|
13 | this.parent = suiteStack[ suiteStack.length - 1 ]
|
14 | if (typeof name === 'function')
|
15 | fn = name, name = null
|
16 | if (fn && fn.name && !name)
|
17 | name = fn.name
|
18 | this.options = opt || {}
|
19 | this.options.todo = this.options.todo || !fn
|
20 | this.fn = fn
|
21 | this.name = name
|
22 | this.after = []
|
23 | this.test = null
|
24 |
|
25 | this.run()
|
26 | }
|
27 |
|
28 | run () {
|
29 | const t = tapStack[ tapStack.length - 1 ]
|
30 | t.test(this.name, this.options, tt => {
|
31 | this.test = tt
|
32 | tapStack.push(tt)
|
33 | suiteStack.push(this)
|
34 | const ret = this.fn()
|
35 | this.runAfter()
|
36 | suiteStack.pop()
|
37 | return ret
|
38 | })
|
39 | }
|
40 |
|
41 | runAfter () {
|
42 | this.after.forEach(a =>
|
43 | before(a[0], a[1], a[2]))
|
44 | let t
|
45 | do {
|
46 | t = tapStack.pop()
|
47 | } while (t && t !== this.test)
|
48 | if (this.test && !this.test.results)
|
49 | t.end()
|
50 | }
|
51 | }
|
52 |
|
53 | const before = (name, fn, options) => {
|
54 | if (typeof name === 'function')
|
55 | fn = name, name = null
|
56 | if (fn && fn.name && !name)
|
57 | name = fn.name
|
58 | options = options || {}
|
59 | const todo = !fn
|
60 | options.todo = options.todo || todo
|
61 | options.silent = true
|
62 | const suite = suiteStack[ suiteStack.length - 1 ]
|
63 | if (!suite)
|
64 | throw new Error('cannot call "before" outside of describe()')
|
65 | const t = tapStack[ tapStack.length - 1 ]
|
66 | if (!name)
|
67 | name = ''
|
68 |
|
69 | const done = tt => er => er ? tt.threw(er) : tt.end()
|
70 | t.test(name, options, tt => {
|
71 | const ret = fn.call(suite, done(tt))
|
72 | if (!ret && fn.length === 0)
|
73 | tt.end()
|
74 | else
|
75 | return ret
|
76 | })
|
77 | }
|
78 |
|
79 | const it = (name, fn, options) => {
|
80 | if (typeof name === 'function')
|
81 | fn = name, name = null
|
82 | if (fn && fn.name && !name)
|
83 | name = fn.name
|
84 | options = options || {}
|
85 | const todo = !fn
|
86 | const suite = suiteStack[ suiteStack.length - 1 ]
|
87 | const t = tapStack[ tapStack.length - 1 ]
|
88 | if (!name)
|
89 | name = ''
|
90 |
|
91 | const done = tt => er => er ? tt.threw(er) : tt.end()
|
92 | options.todo = options.todo || todo
|
93 | options.tapMochaTest = true
|
94 | t.test(name, options, tt => {
|
95 | const ret = fn.call(tt, done(tt))
|
96 | if (ret && ret.then)
|
97 | return ret
|
98 | else if (fn.length === 0)
|
99 | tt.end()
|
100 | })
|
101 | }
|
102 |
|
103 | it.skip = (name, fn) => it(name, fn, { skip: true })
|
104 | it.todo = (name, fn) => it(name, fn, { todo: true })
|
105 |
|
106 | function after (name, fn, options) {
|
107 | const suite = suiteStack[ suiteStack.length - 1 ]
|
108 | if (!suite)
|
109 | throw new Error('cannot call "after" outside of describe()')
|
110 | suite.after.push([name, fn, options])
|
111 | }
|
112 |
|
113 | function moment (when, fn) {
|
114 | const t = tapStack[ tapStack.length - 1 ]
|
115 |
|
116 |
|
117 | t[when](function (cb) {
|
118 | if (!this.options.tapMochaTest)
|
119 | return cb()
|
120 | const suite = suiteStack[ suiteStack.length - 1 ]
|
121 | const ret = fn.call(this, cb)
|
122 | if (ret && ret.then)
|
123 | return ret
|
124 | else if (fn.length === 0)
|
125 | return cb()
|
126 | })
|
127 | }
|
128 |
|
129 | const beforeEach = fn =>
|
130 | moment('beforeEach', fn)
|
131 |
|
132 | const afterEach = fn =>
|
133 | moment('afterEach', fn)
|
134 |
|
135 | exports.it = exports.specify = it
|
136 | exports.context = exports.describe = describe
|
137 | exports.before = before
|
138 | exports.after = after
|
139 | exports.beforeEach = beforeEach
|
140 | exports.afterEach = afterEach
|
141 |
|
142 | let saved
|
143 | exports.global = _ => {
|
144 | if (!saved)
|
145 | saved = new Map()
|
146 |
|
147 | Object.keys(exports).filter(g => g !== 'global').forEach(g => {
|
148 | if (!saved.has(g))
|
149 | saved.set(g, global[g])
|
150 | global[g] = exports[g]
|
151 | })
|
152 | }
|
153 |
|
154 | exports.deglobal = _ =>
|
155 | Object.keys(exports).filter(g => g !== 'global').forEach(g => {
|
156 | if (saved && saved.has(g))
|
157 | global[g] = saved.get(g)
|
158 | })
|