UNPKG

6.31 kBJavaScriptView Raw
1'use strict';
2
3const Hoek = require('@hapi/hoek');
4
5const Coverage = require('./coverage');
6const Leaks = require('./leaks');
7const Runner = require('./runner');
8const Types = require('./types');
9
10
11const internals = {};
12
13
14exports.report = Runner.report;
15
16exports.execute = Runner.execute;
17
18exports.coverage = Coverage;
19
20exports.leaks = Leaks;
21
22exports.types = Types;
23
24exports.assertions = null; // Set by the -a command line option
25
26
27/*
28 experiment('Utilities', () => {
29
30 experiment('isEven()', () => {
31
32 test('returns true on even values', () => {
33
34 });
35 });
36 });
37*/
38
39
40exports.script = function (options) {
41
42 options = options || {};
43
44 global._labScriptRun = true; // Compared by CLI to detect missing exports.lab
45
46 const script = {
47 _current: {
48 experiments: [],
49 tests: [],
50 options: {},
51 title: 'script'
52 },
53 _titles: [],
54 _path: [],
55 _count: 0,
56 _executed: false,
57 _onlyNodes: [],
58 _cli: options.cli,
59 _setOnly: function (experiment, test, path) {
60
61 this._onlyNodes.push({ experiment, test, path });
62 }
63 };
64
65 script._root = script._current;
66
67 script.experiment = internals.experiment.bind(script);
68 script.experiment.skip = internals.skip(script, 'experiment');
69 script.experiment.only = internals.only(script, 'experiment');
70 script.describe = script.experiment;
71 script.suite = script.experiment;
72
73 script.test = internals.test.bind(script);
74 script.test.skip = internals.skip(script, 'test');
75 script.test.only = internals.only(script, 'test');
76 script.it = script.test;
77
78 script.before = internals.before.bind(script);
79 script.beforeEach = internals.beforeEach.bind(script);
80 script.after = internals.after.bind(script);
81 script.afterEach = internals.afterEach.bind(script);
82
83 if (exports.assertions) {
84 script.expect = exports.assertions.expect;
85 script.fail = exports.assertions.fail;
86 }
87
88 if (options.schedule !== false) { // Defaults to true
89 setImmediate(() => {
90
91 if (!script._executed) {
92 Runner.report(script, options); // Schedule automatic execution when used without the CLI
93 }
94 });
95 }
96
97 return script;
98};
99
100
101internals.experiment = function (title, options, fn) {
102
103 if (typeof options === 'function') {
104 fn = options;
105 options = {};
106 }
107
108 const settings = internals.mergeOptions(this._current.options, options);
109
110 const child = {
111 title,
112 parent: this._current,
113 experiments: [],
114 tests: [],
115 options: settings
116 };
117
118 this._current.experiments.push(child);
119 this._current = child;
120
121 this._titles.push(title);
122 this._path = this._titles.concat(); // Clone
123
124 if (settings.only) {
125 this._setOnly(child, null, this._path);
126 }
127
128 fn.call(null); // eslint-disable-line no-useless-call
129
130 this._titles.pop();
131 this._path = this._titles.concat(); // Clone
132
133 this._current = child.parent;
134};
135
136
137internals.before = function (options, fn) {
138
139 if (typeof options === 'function') {
140 fn = options;
141 options = {};
142 }
143
144 Hoek.assert(fn, `before in "${this._current.title}" requires a function argument`);
145
146 const before = {
147 title: 'Before ' + this._titles.join(' '),
148 fn,
149 options
150 };
151
152 this._current.befores = this._current.befores || [];
153 this._current.befores.push(before);
154};
155
156
157internals.after = function (options, fn) {
158
159 if (typeof options === 'function') {
160 fn = options;
161 options = {};
162 }
163
164 Hoek.assert(fn, `after in "${this._current.title}" requires a function argument`);
165
166 const after = {
167 title: 'After ' + this._titles.join(' '),
168 fn,
169 options
170 };
171
172 this._current.afters = this._current.afters || [];
173 this._current.afters.push(after);
174};
175
176
177internals.beforeEach = function (options, fn) {
178
179 if (typeof options === 'function') {
180 fn = options;
181 options = {};
182 }
183
184 Hoek.assert(fn, `beforeEach in "${this._current.title}" requires a function argument`);
185
186 const beforeEach = {
187 title: 'Before each ' + this._titles.join(' '),
188 fn,
189 options
190 };
191
192 this._current.beforeEaches = this._current.beforeEaches || [];
193 this._current.beforeEaches.push(beforeEach);
194};
195
196
197internals.afterEach = function (options, fn) {
198
199 if (typeof options === 'function') {
200 fn = options;
201 options = {};
202 }
203
204 Hoek.assert(fn, `afterEach in "${this._current.title}" requires a function argument`);
205
206 const afterEach = {
207 title: 'After each ' + this._titles.join(' '),
208 fn,
209 options
210 };
211
212 this._current.afterEaches = this._current.afterEaches || [];
213 this._current.afterEaches.push(afterEach);
214};
215
216
217internals.test = function (title, options, fn) {
218
219 if (typeof options === 'function') {
220 fn = options;
221 options = {};
222 }
223
224 const settings = internals.mergeOptions(this._current.options, options);
225
226 const test = {
227 path: this._path,
228 title: this._titles.concat(title).join(' '),
229 relativeTitle: title,
230 fn,
231 options: settings
232 };
233
234 if (settings.only) {
235 this._setOnly(this._current, test, this._path);
236 }
237
238 this._current.tests.push(test);
239};
240
241
242internals.skip = function (script, type) {
243
244 return function (title, options, fn) {
245
246 if (typeof options === 'function') {
247 fn = options;
248 options = {};
249 }
250
251 script[type](title, Object.assign({ skip: true }, options), fn);
252 };
253};
254
255
256internals.only = function (script, type) {
257
258 return function (title, options, fn) {
259
260 if (typeof options === 'function') {
261 fn = options;
262 options = {};
263 }
264
265 script[type](title, Object.assign({ only: true }, options), fn);
266 };
267};
268
269
270internals.mergeOptions = function (parent, child) {
271
272 const options = Object.assign({}, parent, child);
273 options.only = child && child.only;
274 return options;
275};