UNPKG

3.79 kBJavaScriptView Raw
1'use strict'
2const t = require('./tap.js')
3t.jobs = 1
4const tapStack = [ t ]
5let level = 0
6const suiteStack = []
7
8const describe = (name, fn, opt) =>
9 new Suite(name, fn, opt)
10
11class 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
53const 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
79const 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
103it.skip = (name, fn) => it(name, fn, { skip: true })
104it.todo = (name, fn) => it(name, fn, { todo: true })
105
106function 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
113function moment (when, fn) {
114 const t = tapStack[ tapStack.length - 1 ]
115 // need function because 'this' tells us which tap object
116 // has the tapMochaTest thing in its options object
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
129const beforeEach = fn =>
130 moment('beforeEach', fn)
131
132const afterEach = fn =>
133 moment('afterEach', fn)
134
135exports.it = exports.specify = it
136exports.context = exports.describe = describe
137exports.before = before
138exports.after = after
139exports.beforeEach = beforeEach
140exports.afterEach = afterEach
141
142let saved
143exports.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
154exports.deglobal = _ =>
155 Object.keys(exports).filter(g => g !== 'global').forEach(g => {
156 if (saved && saved.has(g))
157 global[g] = saved.get(g)
158 })