UNPKG

14.8 kBJavaScriptView Raw
1var zora = (function () {
2'use strict';
3
4function createCommonjsModule(fn, module) {
5 return module = { exports: {} }, fn(module, module.exports), module.exports;
6}
7
8var keys = createCommonjsModule(function (module, exports) {
9exports = module.exports = typeof Object.keys === 'function'
10 ? Object.keys : shim;
11
12exports.shim = shim;
13function shim (obj) {
14 var keys = [];
15 for (var key in obj) keys.push(key);
16 return keys;
17}
18});
19
20var keys_1 = keys.shim;
21
22var is_arguments = createCommonjsModule(function (module, exports) {
23var supportsArgumentsClass = (function(){
24 return Object.prototype.toString.call(arguments)
25})() == '[object Arguments]';
26
27exports = module.exports = supportsArgumentsClass ? supported : unsupported;
28
29exports.supported = supported;
30function supported(object) {
31 return Object.prototype.toString.call(object) == '[object Arguments]';
32}
33
34exports.unsupported = unsupported;
35function unsupported(object){
36 return object &&
37 typeof object == 'object' &&
38 typeof object.length == 'number' &&
39 Object.prototype.hasOwnProperty.call(object, 'callee') &&
40 !Object.prototype.propertyIsEnumerable.call(object, 'callee') ||
41 false;
42}
43});
44
45var is_arguments_1 = is_arguments.supported;
46var is_arguments_2 = is_arguments.unsupported;
47
48var deepEqual_1 = createCommonjsModule(function (module) {
49var pSlice = Array.prototype.slice;
50
51
52
53var deepEqual = module.exports = function (actual, expected, opts) {
54 if (!opts) opts = {};
55 // 7.1. All identical values are equivalent, as determined by ===.
56 if (actual === expected) {
57 return true;
58
59 } else if (actual instanceof Date && expected instanceof Date) {
60 return actual.getTime() === expected.getTime();
61
62 // 7.3. Other pairs that do not both pass typeof value == 'object',
63 // equivalence is determined by ==.
64 } else if (!actual || !expected || typeof actual != 'object' && typeof expected != 'object') {
65 return opts.strict ? actual === expected : actual == expected;
66
67 // 7.4. For all other Object pairs, including Array objects, equivalence is
68 // determined by having the same number of owned properties (as verified
69 // with Object.prototype.hasOwnProperty.call), the same set of keys
70 // (although not necessarily the same order), equivalent values for every
71 // corresponding key, and an identical 'prototype' property. Note: this
72 // accounts for both named and indexed properties on Arrays.
73 } else {
74 return objEquiv(actual, expected, opts);
75 }
76};
77
78function isUndefinedOrNull(value) {
79 return value === null || value === undefined;
80}
81
82function isBuffer (x) {
83 if (!x || typeof x !== 'object' || typeof x.length !== 'number') return false;
84 if (typeof x.copy !== 'function' || typeof x.slice !== 'function') {
85 return false;
86 }
87 if (x.length > 0 && typeof x[0] !== 'number') return false;
88 return true;
89}
90
91function objEquiv(a, b, opts) {
92 var i, key;
93 if (isUndefinedOrNull(a) || isUndefinedOrNull(b))
94 return false;
95 // an identical 'prototype' property.
96 if (a.prototype !== b.prototype) return false;
97 //~~~I've managed to break Object.keys through screwy arguments passing.
98 // Converting to array solves the problem.
99 if (is_arguments(a)) {
100 if (!is_arguments(b)) {
101 return false;
102 }
103 a = pSlice.call(a);
104 b = pSlice.call(b);
105 return deepEqual(a, b, opts);
106 }
107 if (isBuffer(a)) {
108 if (!isBuffer(b)) {
109 return false;
110 }
111 if (a.length !== b.length) return false;
112 for (i = 0; i < a.length; i++) {
113 if (a[i] !== b[i]) return false;
114 }
115 return true;
116 }
117 try {
118 var ka = keys(a),
119 kb = keys(b);
120 } catch (e) {//happens when one is a string literal and the other isn't
121 return false;
122 }
123 // having the same number of owned properties (keys incorporates
124 // hasOwnProperty)
125 if (ka.length != kb.length)
126 return false;
127 //the same set of keys (although not necessarily the same order),
128 ka.sort();
129 kb.sort();
130 //~~~cheap key test
131 for (i = ka.length - 1; i >= 0; i--) {
132 if (ka[i] != kb[i])
133 return false;
134 }
135 //equivalent values for every corresponding key, and
136 //~~~possibly expensive deep test
137 for (i = ka.length - 1; i >= 0; i--) {
138 key = ka[i];
139 if (!deepEqual(a[key], b[key], opts)) return false;
140 }
141 return typeof a === typeof b;
142}
143});
144
145const getAssertionLocation = () => {
146 const err = new Error();
147 const stack = (err.stack || '').split('\n');
148 return (stack[3] || '').trim().replace(/^at/i, '');
149};
150
151const assertMethodHook = fn => function (...args) {
152 const assertResult = fn(...args);
153
154 if (assertResult.pass === false) {
155 assertResult.at = getAssertionLocation();
156 }
157
158 this.collect(assertResult);
159 return assertResult;
160};
161
162const Assertion = {
163 ok: assertMethodHook((val, description = 'should be truthy') => ({
164 pass: Boolean(val),
165 actual: val,
166 expected: true,
167 description,
168 operator: 'ok'
169 })),
170 deepEqual: assertMethodHook((actual, expected, description = 'should be equivalent') => ({
171 pass: deepEqual_1(actual, expected),
172 actual,
173 expected,
174 description,
175 operator: 'deepEqual'
176 })),
177 equal: assertMethodHook((actual, expected, description = 'should be equal') => ({
178 pass: actual === expected,
179 actual,
180 expected,
181 description,
182 operator: 'equal'
183 })),
184 notOk: assertMethodHook((val, description = 'should not be truthy') => ({
185 pass: !val,
186 expected: false,
187 actual: val,
188 description,
189 operator: 'notOk'
190 })),
191 notDeepEqual: assertMethodHook((actual, expected, description = 'should not be equivalent') => ({
192 pass: !deepEqual_1(actual, expected),
193 actual,
194 expected,
195 description,
196 operator: 'notDeepEqual'
197 })),
198 notEqual: assertMethodHook((actual, expected, description = 'should not be equal') => ({
199 pass: actual !== expected,
200 actual,
201 expected,
202 description,
203 operator: 'notEqual'
204 })),
205 throws: assertMethodHook((func, expected, description) => {
206 let caught;
207 let pass;
208 let actual;
209 if (typeof expected === 'string') {
210 [expected, description] = [description, expected];
211 }
212 try {
213 func();
214 } catch (err) {
215 caught = {error: err};
216 }
217 pass = caught !== undefined;
218 actual = caught && caught.error;
219 if (expected instanceof RegExp) {
220 pass = expected.test(actual) || expected.test(actual && actual.message);
221 expected = String(expected);
222 } else if (typeof expected === 'function' && caught) {
223 pass = actual instanceof expected;
224 actual = actual.constructor;
225 }
226 return {
227 pass,
228 expected,
229 actual,
230 operator: 'throws',
231 description: description || 'should throw'
232 };
233 }),
234 doesNotThrow: assertMethodHook((func, expected, description) => {
235 let caught;
236 if (typeof expected === 'string') {
237 [expected, description] = [description, expected];
238 }
239 try {
240 func();
241 } catch (err) {
242 caught = {error: err};
243 }
244 return {
245 pass: caught === undefined,
246 expected: 'no thrown error',
247 actual: caught && caught.error,
248 operator: 'doesNotThrow',
249 description: description || 'should not throw'
250 };
251 }),
252 fail: assertMethodHook((description = 'fail called') => ({
253 pass: false,
254 actual: 'fail called',
255 expected: 'fail not called',
256 description,
257 operator: 'fail'
258 }))
259};
260
261var assert = (collect, test) => Object.assign(
262 Object.create(Assertion, {collect: {value: collect}}), {
263 async test(description, spec) {
264 // Note: we return the task so the caller can control whether he wants to wait for the sub test to complete or not
265 return test(description, spec).task;
266 }
267 });
268
269const tester = (collect, {offset = 0} = {}) => (description, spec) => {
270 const buffer = [{type: 'title', data: description, offset}];
271 const result = {count: 0, pass: true, description, spec};
272 let done = false;
273
274 const createAssertion = item => {
275 result.pass = result.pass && item.pass;
276 return {type: 'assert', data: item, offset};
277 };
278
279 const collector = item => {
280 result.count++;
281 item.id = result.count;
282 if (item[Symbol.asyncIterator] === undefined) {
283 // Assertion
284 buffer.push(createAssertion(item));
285 } else {
286 // Sub test
287 buffer.push(item);
288 }
289 };
290
291 const handleDelegate = async delegate => {
292 const {value, done} = await delegate.next();
293
294 // Delegate is exhausted: create a summary test point in the stream and throw the delegate
295 if (done === true) {
296 const {executionTime, pass, description} = value;
297 const subTestAssertion = Object.assign(createAssertion({
298 pass,
299 description,
300 id: delegate.id,
301 executionTime
302 }), {type: 'testAssert'});
303 buffer.shift();
304 buffer.unshift(subTestAssertion);
305 return instance.next();
306 }
307 return {value, done};
308 };
309
310 const subTest = tester(collector, {offset: offset + 1});
311
312 const start = Date.now();
313 // Execute the test collecting assertions
314 const assertFn = assert(collector, subTest);
315 const task = new Promise(resolve => resolve(spec(assertFn)))
316 .then(() => {
317 // Always report a plan and summary: the calling test will know how to deal with it
318 result.executionTime = Date.now() - start;
319 buffer.push({type: 'plan', data: {start: 1, end: result.count}, offset});
320 buffer.push({type: 'time', data: result.executionTime, offset});
321 done = true;
322 return result;
323 })
324 .catch(err => {
325 // We report a failing test before bail out ... while unhandled promise rejection is still allowed by nodejs...
326 buffer.push({type: 'assert', data: {pass: false, description}});
327 buffer.push({type: 'comment', data: 'Unhandled exception'});
328 buffer.push({type: 'bailout', data: err, offset});
329 done = true;
330 });
331
332 const instance = {
333 test: subTest,
334 task,
335 [Symbol.asyncIterator]() {
336 return this;
337 },
338 async next() {
339 if (buffer.length === 0) {
340 if (done === true) {
341 return {done: true, value: result};
342 }
343 // Flush
344 await task;
345 return this.next();
346 }
347
348 const next = buffer[0];
349
350 // Delegate if sub test
351 if (next[Symbol.asyncIterator] !== undefined) {
352 return handleDelegate(next);
353 }
354
355 return {value: buffer.shift(), done: false};
356 }
357 };
358
359 // Collection by the calling test
360 collect(instance);
361
362 return instance;
363};
364
365const print = (message, offset = 0) => {
366 console.log(message.padStart(message.length + (offset * 4))); // 4 white space used as indent (see tap-parser)
367};
368
369const toYaml = print => (obj, offset = 0) => {
370 for (const [prop, value] of Object.entries(obj)) {
371 print(`${prop}: ${JSON.stringify(value)}`, offset + 0.5);
372 }
373};
374
375const tap = print => {
376 const yaml = toYaml(print);
377 return {
378 version(version = 13) {
379 print(`TAP version ${version}`);
380 },
381 title(value, offset = 0) {
382 const message = offset > 0 ? `Subtest: ${value}` : value;
383 this.comment(message, offset);
384 },
385 assert(value, offset = 0) {
386 const {pass, description, id, executionTime, expected = '', actual = '', at = '', operator = ''} = value;
387 const label = pass === true ? 'ok' : 'not ok';
388 print(`${label} ${id} - ${description}${executionTime ? ` # time=${executionTime}ms` : ''}`, offset);
389 if (pass === false && value.operator) {
390 print('---', offset + 0.5);
391 yaml({expected, actual, at, operator}, offset);
392 print('...', offset + 0.5);
393 }
394 },
395 plan(value, offset = 0) {
396 print(`1..${value.end}`, offset);
397 },
398 time(value, offset = 0) {
399 this.comment(`time=${value}ms`, offset);
400 },
401 comment(value, offset = 0) {
402 print(`# ${value}`, offset);
403 },
404 bailout(value = 'Unhandled exception') {
405 print(`Bail out! ${value}`);
406 },
407 testAssert(value, offset = 0) {
408 return this.assert(value, offset);
409 }
410 };
411};
412
413var tap$1 = (printFn = print) => {
414 const reporter = tap(printFn);
415 return (toPrint = {}) => {
416 const {data, type, offset = 0} = toPrint;
417 if (typeof reporter[type] === 'function') {
418 reporter[type](data, offset);
419 }
420 // Else ignore (unknown message type)
421 };
422};
423
424// Some combinators for asynchronous iterators: this will be way more easier when
425// Async generator are widely supported
426
427const asyncIterator = behavior => Object.assign({
428 [Symbol.asyncIterator]() {
429 return this;
430 }
431}, behavior);
432
433const filter = predicate => iterator => asyncIterator({
434 async next() {
435 const {done, value} = await iterator.next();
436
437 if (done === true) {
438 return {done};
439 }
440
441 if (!predicate(value)) {
442 return this.next();
443 }
444
445 return {done, value};
446 }
447});
448
449const map = mapFn => iterator => asyncIterator({
450 [Symbol.asyncIterator]() {
451 return this;
452 },
453 async next() {
454 const {done, value} = await iterator.next();
455 if (done === true) {
456 return {done};
457 }
458 return {done, value: mapFn(value)};
459 }
460});
461
462const stream = asyncIterator => Object.assign(asyncIterator, {
463 map(fn) {
464 return stream(map(fn)(asyncIterator));
465 },
466 filter(fn) {
467 return stream(filter(fn)(asyncIterator));
468 }
469});
470
471const combine = (...iterators) => {
472 const [...pending] = iterators;
473 let current = pending.shift();
474
475 return asyncIterator({
476 async next() {
477 if (current === undefined) {
478 return {done: true};
479 }
480
481 const {done, value} = await current.next();
482
483 if (done === true) {
484 current = pending.shift();
485 return this.next();
486 }
487
488 return {done, value};
489 }
490 });
491};
492
493let flatten = true;
494const tests = [];
495const test = tester(t => tests.push(t));
496
497// Provide a root context for BSD style test suite
498const subTest = (test('Root', () => {})).test;
499test.test = (description, spec) => {
500 flatten = false; // Turn reporter into BSD style
501 return subTest(description, spec);
502};
503
504const start = async ({reporter = tap$1()} = {}) => {
505 let count = 0;
506 let failure = 0;
507 reporter({type: 'version', data: 13});
508
509 // Remove the irrelevant root title
510 await tests[0].next();
511
512 let outputStream = stream(combine(...tests));
513 outputStream = flatten ? outputStream
514 .filter(({type}) => type !== 'testAssert')
515 .map(item => Object.assign(item, {offset: 0})) :
516 outputStream;
517
518 const filterOutAtRootLevel = ['plan', 'time'];
519 outputStream = outputStream
520 .filter(item => item.offset > 0 || !filterOutAtRootLevel.includes(item.type))
521 .map(item => {
522 if (item.offset > 0 || (item.type !== 'assert' && item.type !== 'testAssert')) {
523 return item;
524 }
525
526 count++;
527 item.data.id = count;
528 failure += item.data.pass ? 0 : 1;
529 return item;
530 });
531
532 // One day with for await loops ... :) !
533 while (true) {
534 const {done, value} = await outputStream.next();
535
536 if (done === true) {
537 break;
538 }
539
540 reporter(value);
541
542 if (value.type === 'bailout') {
543 throw value.data; // Rethrow but with Nodejs we keep getting the deprecation warning (unhandled promise) and the process exists with 0 exit code...
544 }
545 }
546
547 reporter({type: 'plan', data: {start: 1, end: count}});
548 reporter({type: 'comment', data: failure > 0 ? `failed ${failure} of ${count} tests` : 'ok'});
549};
550
551// Auto bootstrap following async env vs sync env (browser vs node)
552if (typeof window === 'undefined') {
553 setTimeout(start, 0);
554} else {
555 window.addEventListener('load', start);
556}
557
558return test;
559
560}());
561//# sourceMappingURL=zora.js.map