UNPKG

11.4 kBJavaScriptView Raw
1var zora = (function () {
2'use strict';
3
4const stringify = JSON.stringify;
5const printTestHeader = test => console.log(`# ${test.description} - ${test.executionTime}ms`);
6const printTestCase = (assertion, id) => {
7 const pass = assertion.pass;
8 const status = pass === true ? 'ok' : 'not ok';
9 console.log(`${status} ${id} ${assertion.message}`);
10 if (pass !== true) {
11 console.log(` ---
12 operator: ${assertion.operator}
13 expected: ${stringify(assertion.expected)}
14 actual: ${stringify(assertion.actual)}
15 at: ${(assertion.at || '')}
16 ...
17`);
18 }
19};
20const printSummary = ({count, pass, skipped, fail}) => {
21 //Some parsers seem to fail to detect end of stream if we use a single console.log call with a template string...
22 console.log(`1..${count}`);
23 console.log(`# tests ${count} (${skipped} skipped)`);
24 console.log(`# pass ${pass}`);
25 if (fail > 0) {
26 console.log(`# fail ${fail}`);
27 } else {
28 console.log('# ok');
29 }
30};
31
32var tap = ({displaySkipped = false} = {}) => function * () {
33 let pass = 0;
34 let fail = 0;
35 let id = 0;
36 let skipped = 0;
37 console.log('TAP version 13');
38 try {
39 /* eslint-disable no-constant-condition */
40 while (true) {
41 const test = yield;
42
43 if (test.items.length === 0) {
44 skipped++;
45 }
46
47 if (test.items.length > 0 || displaySkipped === true) {
48 printTestHeader(test);
49 }
50
51 for (const assertion of test.items) {
52 id++;
53 if (assertion.pass === true) {
54 pass++;
55 } else {
56 fail++;
57 }
58 printTestCase(assertion, id);
59 }
60 }
61 /* eslint-enable no-constant-condition */
62 } catch (err) {
63 console.log('Bail out! unhandled exception');
64 throw err;
65 } finally {
66 printSummary({count: id, pass, skipped, fail});
67 }
68};
69
70function createCommonjsModule(fn, module) {
71 return module = { exports: {} }, fn(module, module.exports), module.exports;
72}
73
74var keys = createCommonjsModule(function (module, exports) {
75exports = module.exports = typeof Object.keys === 'function'
76 ? Object.keys : shim;
77
78exports.shim = shim;
79function shim (obj) {
80 var keys = [];
81 for (var key in obj) keys.push(key);
82 return keys;
83}
84});
85
86var keys_1 = keys.shim;
87
88var is_arguments = createCommonjsModule(function (module, exports) {
89var supportsArgumentsClass = (function(){
90 return Object.prototype.toString.call(arguments)
91})() == '[object Arguments]';
92
93exports = module.exports = supportsArgumentsClass ? supported : unsupported;
94
95exports.supported = supported;
96function supported(object) {
97 return Object.prototype.toString.call(object) == '[object Arguments]';
98}
99
100exports.unsupported = unsupported;
101function unsupported(object){
102 return object &&
103 typeof object == 'object' &&
104 typeof object.length == 'number' &&
105 Object.prototype.hasOwnProperty.call(object, 'callee') &&
106 !Object.prototype.propertyIsEnumerable.call(object, 'callee') ||
107 false;
108}
109});
110
111var is_arguments_1 = is_arguments.supported;
112var is_arguments_2 = is_arguments.unsupported;
113
114var deepEqual_1 = createCommonjsModule(function (module) {
115var pSlice = Array.prototype.slice;
116
117
118
119var deepEqual = module.exports = function (actual, expected, opts) {
120 if (!opts) opts = {};
121 // 7.1. All identical values are equivalent, as determined by ===.
122 if (actual === expected) {
123 return true;
124
125 } else if (actual instanceof Date && expected instanceof Date) {
126 return actual.getTime() === expected.getTime();
127
128 // 7.3. Other pairs that do not both pass typeof value == 'object',
129 // equivalence is determined by ==.
130 } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') {
131 return opts.strict ? actual === expected : actual == expected;
132
133 // 7.4. For all other Object pairs, including Array objects, equivalence is
134 // determined by having the same number of owned properties (as verified
135 // with Object.prototype.hasOwnProperty.call), the same set of keys
136 // (although not necessarily the same order), equivalent values for every
137 // corresponding key, and an identical 'prototype' property. Note: this
138 // accounts for both named and indexed properties on Arrays.
139 } else {
140 return objEquiv(actual, expected, opts);
141 }
142};
143
144function isUndefinedOrNull(value) {
145 return value === null || value === undefined;
146}
147
148function isBuffer (x) {
149 if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;
150 if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {
151 return false;
152 }
153 if (x.length > 0 && typeof x[0] !== 'number') return false;
154 return true;
155}
156
157function objEquiv(a, b, opts) {
158 var i, key;
159 if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
160 return false;
161 // an identical 'prototype' property.
162 if (a.prototype !== b.prototype) return false;
163 //~~~I've managed to break Object.keys through screwy arguments passing.
164 // Converting to array solves the problem.
165 if (is_arguments(a)) {
166 if (!is_arguments(b)) {
167 return false;
168 }
169 a = pSlice.call(a);
170 b = pSlice.call(b);
171 return deepEqual(a, b, opts);
172 }
173 if (isBuffer(a)) {
174 if (!isBuffer(b)) {
175 return false;
176 }
177 if (a.length !== b.length) return false;
178 for (i = 0; i < a.length; i++) {
179 if (a[i] !== b[i]) return false;
180 }
181 return true;
182 }
183 try {
184 var ka = keys(a),
185 kb = keys(b);
186 } catch (e) {//happens when one is a string literal and the other isn't
187 return false;
188 }
189 // having the same number of owned properties (keys incorporates
190 // hasOwnProperty)
191 if (ka.length != kb.length)
192 return false;
193 //the same set of keys (although not necessarily the same order),
194 ka.sort();
195 kb.sort();
196 //~~~cheap key test
197 for (i = ka.length - 1; i >= 0; i--) {
198 if (ka[i] != kb[i])
199 return false;
200 }
201 //equivalent values for every corresponding key, and
202 //~~~possibly expensive deep test
203 for (i = ka.length - 1; i >= 0; i--) {
204 key = ka[i];
205 if (!deepEqual(a[key], b[key], opts)) return false;
206 }
207 return typeof a === typeof b;
208}
209});
210
211const getAssertionLocation = () => {
212 const err = new Error();
213 const stack = (err.stack || '').split('\n');
214 return (stack[3] || '').trim().replace(/^at/i, '');
215};
216const assertMethodHook = fn => function (...args) {
217 const assertResult = fn(...args);
218
219 if (assertResult.pass === false) {
220 assertResult.at = getAssertionLocation();
221 }
222
223 this.collect(assertResult);
224 return assertResult;
225};
226
227const Assertion = {
228 ok: assertMethodHook((val, message = 'should be truthy') => ({
229 pass: Boolean(val),
230 actual: val,
231 expected: true,
232 message,
233 operator: 'ok'
234 })),
235 deepEqual: assertMethodHook((actual, expected, message = 'should be equivalent') => ({
236 pass: deepEqual_1(actual, expected),
237 actual,
238 expected,
239 message,
240 operator: 'deepEqual'
241 })),
242 equal: assertMethodHook((actual, expected, message = 'should be equal') => ({
243 pass: actual === expected,
244 actual,
245 expected,
246 message,
247 operator: 'equal'
248 })),
249 notOk: assertMethodHook((val, message = 'should not be truthy') => ({
250 pass: !val,
251 expected: false,
252 actual: val,
253 message,
254 operator: 'notOk'
255 })),
256 notDeepEqual: assertMethodHook((actual, expected, message = 'should not be equivalent') => ({
257 pass: !deepEqual_1(actual, expected),
258 actual,
259 expected,
260 message,
261 operator: 'notDeepEqual'
262 })),
263 notEqual: assertMethodHook((actual, expected, message = 'should not be equal') => ({
264 pass: actual !== expected,
265 actual,
266 expected,
267 message,
268 operator: 'notEqual'
269 })),
270 throws: assertMethodHook((func, expected, message) => {
271 let caught;
272 let pass;
273 let actual;
274 if (typeof expected === 'string') {
275 [expected, message] = [message, expected];
276 }
277 try {
278 func();
279 } catch (err) {
280 caught = {error: err};
281 }
282 pass = caught !== undefined;
283 actual = caught && caught.error;
284 if (expected instanceof RegExp) {
285 pass = expected.test(actual) || expected.test(actual && actual.message);
286 expected = String(expected);
287 } else if (typeof expected === 'function' && caught) {
288 pass = actual instanceof expected;
289 actual = actual.constructor;
290 }
291 return {
292 pass,
293 expected,
294 actual,
295 operator: 'throws',
296 message: message || 'should throw'
297 };
298 }),
299 doesNotThrow: assertMethodHook((func, expected, message) => {
300 let caught;
301 if (typeof expected === 'string') {
302 [expected, message] = [message, expected];
303 }
304 try {
305 func();
306 } catch (err) {
307 caught = {error: err};
308 }
309 return {
310 pass: caught === undefined,
311 expected: 'no thrown error',
312 actual: caught && caught.error,
313 operator: 'doesNotThrow',
314 message: message || 'should not throw'
315 };
316 }),
317 fail: assertMethodHook((message = 'fail called') => ({
318 pass: false,
319 actual: 'fail called',
320 expected: 'fail not called',
321 message,
322 operator: 'fail'
323 }))
324};
325
326var assert = collect => Object.create(Assertion, {collect: {value: collect}});
327
328const noop = () => {};
329
330const skip = description => test('SKIPPED - ' + description, noop);
331
332const Test = {
333 async run() {
334 const collect = assertion => this.items.push(assertion);
335 const start = Date.now();
336 await Promise.resolve(this.spec(assert(collect)));
337 const executionTime = Date.now() - start;
338 return Object.assign(this, {
339 executionTime
340 });
341 },
342 skip() {
343 return skip(this.description);
344 }
345};
346
347function test(description, spec, {only = false} = {}) {
348 return Object.create(Test, {
349 items: {value: []},
350 only: {value: only},
351 spec: {value: spec},
352 description: {value: description}
353 });
354}
355
356// Force to resolve on next tick so consumer can do something with previous iteration result
357const onNextTick = val => new Promise(resolve => setTimeout(() => resolve(val), 0));
358
359const PlanProto = {
360 [Symbol.iterator]() {
361 return this.items[Symbol.iterator]();
362 },
363 test(description, spec, opts) {
364 if (!spec && description.test) {
365 // If it is a plan
366 this.items.push(...description);
367 } else {
368 this.items.push(test(description, spec, opts));
369 }
370 return this;
371 },
372 only(description, spec) {
373 return this.test(description, spec, {only: true});
374 },
375 skip(description, spec) {
376 if (!spec && description.test) {
377 // If it is a plan we skip the whole plan
378 for (const t of description) {
379 this.items.push(t.skip());
380 }
381 } else {
382 this.items.push(skip(description));
383 }
384 return this;
385 }
386};
387
388const runnify = fn => async function (sink = tap()) {
389 const sinkIterator = typeof sink[Symbol.iterator] === 'function' ?
390 sink[Symbol.iterator]() :
391 sink(); // Backward compatibility
392 sinkIterator.next();
393 try {
394 const hasOnly = this.items.some(t => t.only);
395 const tests = hasOnly ? this.items.map(t => t.only ? t : t.skip()) : this.items;
396 await fn(tests, sinkIterator);
397 } catch (err) {
398 sinkIterator.throw(err);
399 } finally {
400 sinkIterator.return();
401 }
402};
403
404function factory({sequence = false} = {sequence: false}) {
405 /* eslint-disable no-await-in-loop */
406 const exec = sequence === true ? async (tests, sinkIterator) => {
407 for (const t of tests) {
408 const result = await onNextTick(t.run());
409 sinkIterator.next(result);
410 }
411 } : async (tests, sinkIterator) => {
412 const runningTests = tests.map(t => t.run());
413 for (const r of runningTests) {
414 const executedTest = await onNextTick(r);
415 sinkIterator.next(executedTest);
416 }
417 };
418 /* eslint-enable no-await-in-loop */
419
420 return Object.assign(Object.create(PlanProto, {
421 items: {value: []}, length: {
422 get() {
423 return this.items.length;
424 }
425 }
426 }), {
427 run: runnify(exec)
428 });
429}
430
431return factory;
432
433}());
434//# sourceMappingURL=zora.js.map