UNPKG

19.2 kBJavaScriptView Raw
1
2
3!function(exports, _setTimeout, _clearTimeout, _Date, _Error, _Infinity) {
4 var started, testSuite, timerType, inSuite
5 , tests = []
6 , describe = exports.describe = curry(def, 1)
7 , _global = describe.global = exports.window || global
8 , _process = _global.process || /* c8 ignore next */ { exit: This }
9 , _isArray = Array.isArray
10 , _keys = Object.keys
11 , call = def.bind.bind(def.call)
12 , slice = call(tests.slice)
13 , push = call(tests.push)
14 , lineRe = /{(\w+)}/g
15 , totalCases = 0
16 , failedCases = []
17 , totalAsserts = 0
18 , passedAsserts = 0
19 , skipped = 0
20 , runPos = 0
21 , splicePos = 0
22 , assert = describe.assert = {
23 notOk: function(value, message) {
24 return this(!value, message || "!=", value, "falsy")
25 },
26 equal: function(actual, expected, message) {
27 return this(
28 arguments.length > 1 && _deepEqual(actual, expected, []),
29 message || "equal", actual, expected
30 )
31 },
32 notEqual: function(actual, expected, message) {
33 return this(
34 arguments.length > 1 && !_deepEqual(actual, expected, []),
35 message || "notEqual", actual, expected
36 )
37 },
38 skip: This,
39 strictEqual: function(actual, expected, message) {
40 return this(
41 arguments.length > 1 && actual === expected,
42 message || "===", actual, expected
43 )
44 },
45 notStrictEqual: function(actual, expected, message) {
46 return this(
47 arguments.length > 1 && actual !== expected,
48 message || "!==", actual, expected
49 )
50 },
51 own: function(actual, expected, message) {
52 own.lastMsg = ""
53 return this(own(actual, expected), message || own.lastMsg)
54 },
55 notOwn: function(actual, expected, message) {
56 own.lastMsg = ""
57 return this(!own(actual, expected), message || own.lastMsg)
58 },
59 throws: function(fn, message) {
60 var actual = false
61 try {
62 fn()
63 } catch(e) {
64 actual = true
65 }
66 return this(actual, message || "throws", actual, true)
67 },
68 type: function(thing, expected, message) {
69 var actual = type(thing)
70 return this(actual === expected, message || "type", actual, expected)
71 },
72 anyOf: function(a, b, message) {
73 return this(
74 _isArray(b) && b.indexOf(a) > -1,
75 message || "anyOf", a, b
76 )
77 }
78 }
79 , argv = _process.argv && _process.argv.slice(2) || /* c8 ignore next */ []
80 , conf = describe.conf = opts(argv, {
81 // process.platform === 'win32' -> √×.
82 file: (_Error().stack + " /cli/test.js:").match(/\S+?:(?=[:\d)]*$)/m)[0],
83 global: "describe,it,test",
84 head: "",
85 indent: " ",
86 suite: "{indent}{n}", //➜✺✽❖❣❢•※⁕∅
87 ok: "{indent} {green}✔{reset} {i}. {n} [{passed}/{total}]",
88 nok: "{indent} {red}✘{reset} {i}. {n} [{passed}/{total}]",
89 skip: "{indent} {yellow}∅{reset} {i}. {n}",
90 sum: "1..{total}\n#{passGreen} pass {pass}/{total} [{passAsserts}/{totalAsserts}]{timeStr}",
91 failSum: "#{red}{bold} FAIL tests {failNums}",
92 skipSum: "#{yellow}{bold} skip {s}",
93 bold: "\x1b[1m",
94 red: "\x1b[31m",
95 green: "\x1b[32m",
96 yellow: "\x1b[33m",
97 reset: "\x1b[0m",
98 color: (_process.stdout || /* c8 ignore next */ _process).isTTY,
99 cut: 1500,
100 delay: 1,
101 seed: (Math.random() * 1e5)|0,
102 stack: 9,
103 status: 1,
104 time: 1,
105 timeout: 999,
106 total: 0
107 })
108 , toStr = conf.toString
109 , hasOwn = call(conf.hasOwnProperty)
110 /*** mockTime ***/
111 , fakeNow
112 , timers = []
113 , timerId = 0
114 , fakeTimers = {
115 setTimeout: curry(fakeTimeout, false),
116 setInterval: curry(fakeTimeout, true),
117 clearTimeout: fakeClear,
118 clearInterval: fakeClear,
119 setImmediate: fakeNextTick,
120 clearImmediate: fakeClear,
121 Date: fakeDate
122 }
123 function fakeDate(year, month, date, hr, min, sec, ms) {
124 return (
125 arguments.length > 1 ?
126 new _Date(num(year), num(month), num(date, 1), num(hr), num(min), num(sec), num(ms)) :
127 new _Date(num(year, fakeNow))
128 )
129 }
130 fakeDate.now = function() {
131 return fakeNow
132 }
133 fakeDate.parse = _Date.parse
134 fakeDate.UTC = _Date.UTC
135 function fakeHrtime(time) {
136 var diff = _isArray(time) ? fakeNow - (time[0] * 1e3 + time[1] / 1e6) : fakeNow
137 return [Math.floor(diff / 1000), Math.round((diff % 1e3) * 1e3) * 1e3] // [seconds, nanoseconds]
138 }
139 function fakeTimeout(repeat, fn, ms) {
140 if (Date === _Date) {
141 return _setTimeout.apply(this, slice(arguments, 1))
142 }
143 if (!isObj(repeat)) {
144 repeat = {
145 id: ++timerId,
146 repeat: repeat,
147 fn: fn,
148 args: slice(arguments, 3),
149 at: fakeNow + ms,
150 ms: ms
151 }
152 }
153 for (var i = timers.length; i-- && !(timers[i].at <= repeat.at);); // jshint ignore:line
154 timers.splice(i + 1, 0, repeat)
155 return timerType == "number" ? /* c8 ignore next */ repeat.id : {
156 id: repeat.id,
157 unref: This
158 }
159 }
160 function fakeNextTick(fn) {
161 fakeTimeout({
162 id: ++timerId,
163 fn: fn,
164 args: slice(arguments, 1),
165 at: fakeNow - 1
166 })
167 }
168 function fakeClear(id) {
169 if (id) for (var i = timers.length; i--; ) {
170 if (timers[i].id === id || timers[i].id === id.id) {
171 timers.splice(i, 1)
172 break
173 }
174 }
175 }
176 /* mockTime end */
177
178 describe.describe = describe
179 describe.test = curry(def, 2)
180 describe.it = curry(def, 3)
181 describe.should = curry(def, 4)
182 describe.failed = 0
183 describe.output = ""
184
185 describe.diff = diff
186 describe.format = format
187 describe.opts = opts
188 describe.print = print
189 describe.stringify = stringify
190
191 each(conf.global, function(_i, value) {
192 _global[value] = describe[value]
193 })
194
195 function def(t, name, data, fn) {
196 if (t < 1) t = isFn(data) + 1
197 if (!started) {
198 started = new _Date()
199
200 if (!conf.color) {
201 conf.bold = conf.red = conf.green = conf.yellow = conf.reset = ""
202 }
203
204 if (conf.tap) {
205 conf.head = "TAP version 13"
206 conf.suite = "# {n}"
207 conf.ok = conf.skip = "ok {i} - {n} [{passed}/{total}]"
208 conf.nok = "not " + conf.ok
209 conf.indent = ""
210 } else if (conf.brief) {
211 conf.suite = conf.ok = conf.indent = ""
212 conf.skip = "{yellow}skip {i} - {n}"
213 conf.sum = conf.sum.slice(11)
214 }
215
216 if (t !== 1) def(1, "Tests")
217 line("head")
218 timerType = type(_setTimeout(nextCase, conf.delay|0))
219 }
220 if (!isStr(name)) {
221 fn = data
222 data = name
223 name = "Unnamed Test" + (t > 1 ? "Case" : "Suite")
224 }
225 if (!isFn(fn)) {
226 fn = data
227 }
228 var spliceData = [++splicePos, 0, {
229 p: inSuite,
230 indent: inSuite ? inSuite.indent + (t > 1 ? "" : conf.indent) : "",
231 s: t > 1 && !isFn(fn) ? "pending" : data === false ? "by data" : 0,
232 t: t,
233 n: name,
234 f: fn
235 }]
236 if (data !== fn) {
237 each(data, curry(function(item, i, row) {
238 conf.i = i
239 i = spliceData[i - 0 + 2] = Object.create(item)
240 i.f = curry(i.f, i.r = _isArray(row) ? row : isObj(row) ? Object.entries(row) : (row = [row]))
241 i.n = format(i.n, row, conf)
242 if (item.f.length > i.r.length + 2 || i.r.length !== spliceData[2].r.length) throw "Invalid data for: " + i.n
243 }, spliceData[2]))
244 splicePos += data.length - 1
245 }
246 tests.splice.apply(tests, spliceData)
247 return describe
248 }
249
250 function nextCase() {
251 var tick
252 , args = tests[splicePos = runPos++]
253 if (!args) printResult()
254 else if (args.t === 1) nextSuite(args)
255 else {
256 testCase.i = ++totalCases
257 if (args.p && args.p !== testSuite) testSuite = args.p
258 testCase.indent = testSuite.indent
259 testCase.n = (args.t < 3 ? "" : "it " + (args.t < 4 ? "" : "should ")) + args.n
260 testCase.errors = []
261 testCase.total = testCase.passed = 0
262 if (args.s || testSuite.s || argv.length && argv.indexOf("" + totalCases) < 0) {
263 skipped++
264 if (!argv.length) line("skip", testCase)
265 return nextCase()
266 }
267 Object.assign(testCase, assert)
268 testCase.end = end
269 testCase.ok = testCase
270 testCase.plan = function(planned) {
271 testCase.planned = planned
272 if (planned <= testCase.total) end()
273 return testCase
274 }
275 testCase.setTimeout = function(ms) {
276 _clearTimeout(tick)
277 tick = _setTimeout(end, ms, "TIMEOUT: " + ms + "ms")
278 return testCase
279 }
280
281 try {
282 testCase.setTimeout(conf.timeout)
283 args = args.f.call(testCase, testCase, (testCase.mock = args.f.length > 1 && new Mock()))
284 if (args && args.then) args.then(curry(end, null), end)
285 } catch (e) {
286 print("" + e)
287 end(e)
288 }
289 }
290 function testCase(value, message, actual, expected) {
291 testCase.total++
292 if (testCase.ended) {
293 fail("assertion after end")
294 }
295 if (value) {
296 testCase.passed++
297 } else {
298 fail("Assertion:" + testCase.total + ": " + (message ?
299 message + "\nexpected: " + stringify(expected) + "\nactual: " + stringify(actual) :
300 stringify(value) + " is truthy"
301 ))
302 }
303 return testCase.plan(testCase.planned)
304 }
305 function fail(_err) {
306 var row, start, i = 0
307 , err = type(_err) != "error" ? _Error(_err) : _err
308 , stack = err.stack
309 if (stack) {
310 // iotjs returns stack as Array
311 for (stack = _isArray(stack) ? stack : (stack + "").replace(err, "").split("\n"); (row = stack[++i]); ) {
312 if (row.indexOf(conf.file) < 0) {
313 if (!start) start = i
314 }
315 if (i - start >= conf.stack) break
316 }
317 err = [ err ].concat(stack.slice(start, i)).join("\n")
318 }
319
320 if (push(testCase.errors, err) == 1) {
321 push(failedCases, testCase)
322 }
323 if (describe.result) printResult()
324 return testCase
325 }
326 function end(err) {
327 _clearTimeout(tick)
328 if (err) fail(err)
329 if (testCase.ended) return fail("ended multiple times")
330 testCase.ended = _Date.now()
331
332 if (testCase.planned != void 0 && testCase.planned !== testCase.total) {
333 fail("planned " + testCase.planned + " actual " + testCase.total)
334 }
335 if (testCase.mock) {
336 testCase.n += testCase.mock.txt
337 testCase.mock.restore()
338 }
339
340 totalAsserts += testCase.total
341 passedAsserts += testCase.passed
342
343 line(testCase.errors.length ? "nok" : "ok", testCase)
344 if (runPos % 1000) nextCase()
345 else _setTimeout(nextCase, 1)
346 }
347 }
348 function nextSuite(newSuite) {
349 if (!argv.length) line("suite", newSuite)
350 newSuite.p = inSuite
351 inSuite = testSuite = newSuite
352 if (isFn(testSuite.f)) {
353 testSuite.f.call(describe)
354 } else if (isObj(testSuite.f)) {
355 each(testSuite.f, curry(def, 0))
356 }
357 inSuite = newSuite.p
358 nextCase()
359 }
360 function printResult() {
361 testSuite = null
362 conf.total = totalCases
363 var testCase
364 , nums = []
365 , failed = failedCases.length
366 conf.fail = describe.failed += failed
367 conf.pass = totalCases - conf.fail
368 conf.s = skipped
369 conf.passAsserts = passedAsserts
370 conf.totalAsserts = totalAsserts
371 conf.passGreen = conf.fail ? "" : conf.green + conf.bold
372 conf.failRed = conf.fail ? conf.red : ""
373 conf.timeStr = conf.time ? " in " + (_Date.now() - started) + " ms at " + started.toTimeString().slice(0, 8) : ""
374 if (conf.status) _process.exitCode = conf.fail
375 if (failed) {
376 for (; (testCase = failedCases[--failed]); ) {
377 nums[failed] = testCase.i
378 print("---")
379 line("nok", testCase)
380 print(testCase.errors.join("\n"))
381 }
382 conf.failNums = nums.join(", ")
383 print("...")
384 line("failSum", conf)
385 failedCases.length = 0
386 }
387 describe.result = line("sum", conf)
388 if (skipped) {
389 line("skipSum", conf)
390 }
391 if (describe.onend) describe.onend()
392 }
393
394 function This() {
395 return this
396 }
397 function diff(a, b, sep) {
398 sep = sep || ""
399 a = a.split(sep)
400 b = b.split(sep)
401 var del, ins, pre
402 , aLen = a.length
403 , bLen = b.length
404 , aPos = 0
405 , bPos = 0
406 , out = []
407 for (; aPos < aLen || bPos < bLen; aPos++, bPos++) {
408 if (a[aPos] !== b[bPos]) {
409 for (pre = bPos; b[pre] !== a[aPos] && pre < bLen; pre++);
410 pre -= bPos;
411
412 for (del = 0, ins = 0; a[aPos] !== b[bPos] && (aPos < aLen || bPos < bLen); ) {
413 if (aPos < aLen) { del++; aPos++; }
414 if (bPos < bLen && a[aPos] !== b[bPos]) { ins++; bPos++; }
415 }
416
417 if (ins > 0 || del > 0) {
418 if (pre > 0 && pre < ins + del) {
419 aPos -= del
420 bPos -= ins - pre
421 del = 0
422 ins = pre
423 }
424 out.push([aPos - del, del, b.slice(bPos - ins, bPos).join(sep)]);
425 }
426 }
427 }
428 return out
429 }
430 function format(str, map, fallback) {
431 return str.replace(lineRe, function(_, field) {
432 return map[field] != null ? map[field] : fallback[field]
433 })
434 }
435 function line(name, map) {
436 return print(format(conf[name], map, conf))
437 }
438 function opts(argv, defaults) {
439 for (var arg, conf = Object.assign({}, defaults), i = argv.length; i; ) {
440 arg = argv[--i].split(/=|--(no-)?/)
441 if (arg[0] === "") {
442 conf[arg[2]] = arg[4] || !arg[1]
443 argv.splice(i, 1)
444 }
445 }
446 return conf
447 }
448 function print(str) {
449 if (!str) return
450 if (testSuite && testSuite.indent) {
451 str = str.split("\n").join("\n" + testSuite.indent)
452 }
453 describe.output += str + "\n"
454 if (describe.onprint) describe.onprint(str)
455 if (_global.console && console.log) console.log(str + conf.reset)
456 return str
457 }
458
459 // A spy is a wrapper function to verify an invocation
460 // A stub is a spy with replaced behavior
461 function Mock() {
462 this.txt = ""
463 this._r = []
464 }
465 Mock.prototype = describe.mock = {
466 fn: function(origin) {
467 spy.called = 0
468 spy.calls = []
469 spy.errors = 0
470 spy.results = []
471 return spy
472 function spy() {
473 var err, key, result
474 , args = slice(arguments)
475 if (isFn(origin)) {
476 try {
477 result = origin.apply(this, arguments)
478 } catch(e) {
479 spy.errors++
480 err = e
481 }
482 } else if (_isArray(origin)) {
483 result = origin[spy.called % origin.length]
484 } else if (isObj(origin)) {
485 key = JSON.stringify(args).slice(1, -1)
486 result = hasOwn(origin, key) ? origin[key] : origin["*"]
487 } else result = origin
488 spy.called++
489 push(spy.results, result)
490 push(spy.calls, {
491 scope: this,
492 args: args,
493 error: err,
494 result: result
495 })
496 return result
497 }
498 },
499 rand: function(seed_) {
500 var seed = seed_ || conf.seed
501 this.txt += " #seed:" + seed
502 this.swap(Math, "random", xorshift128(seed))
503 },
504 spy: function(obj, name, stub) {
505 this.swap(obj, name, this.fn(stub || obj[name]))
506 },
507 swap: function swap(obj, name, fn) {
508 if (isObj(name)) {
509 each(name, curry(swap, obj, this))
510 return
511 }
512 var existing = obj[name]
513 push(this._r, obj, name, hasOwn(obj, name) && existing)
514 obj[name] = fn
515 if (fn === fn && obj[name] !== fn) throw _Error("Unable to swap " + stringify(name))
516 return existing
517 },
518 restore: function() {
519 for (var arr = this._r, i = arr.length; --i > 0; i -= 2) {
520 if (arr[i]) {
521 arr[i - 2][arr[i - 1]] = arr[i]
522 } else {
523 delete arr[i - 2][arr[i - 1]]
524 }
525 }
526 /*** mockTime ***/
527 this.tick(_Infinity, true)
528 },
529 time: function(newTime, newZone) {
530 var mock = this
531 if (!mock._time) {
532 mock._time = fakeNow = _Date.now()
533 mock.swap(_global, fakeTimers)
534 mock.swap(_process, { nextTick: fakeNextTick, hrtime: fakeHrtime })
535 }
536 if (newTime) {
537 fakeNow = isStr(newTime) ? _Date.parse(newTime) : newTime
538 mock.tick(0)
539 }
540 fakeDate._z = newZone
541 },
542 tick: function(amount, noRepeat) {
543 var t
544 , nextNow = type(amount) === "number" ? fakeNow + amount : timers[0] ? timers[0].at : fakeNow
545
546 for (; (t = timers[0]) && t.at <= nextNow; ) {
547 fakeNow = t.at
548 timers.shift()
549 if (isStr(t.fn)) t.fn = Function(t.fn)
550 if (isFn(t.fn)) t.fn.apply(null, t.args)
551 if (!noRepeat && t.repeat) {
552 t.at += t.ms
553 fakeTimeout(t)
554 }
555 }
556 fakeNow = nextNow
557 /* mockTime end */
558 }
559 }
560
561 function xorshift128(a) {
562 var b = a * 2e3, c = a * 3e4, d = a * 4e5
563 return function() {
564 var t = d ^ (d << 11)
565 d = c; c = b; b = a
566 a ^= t ^ (t >>> 8) ^ (a >>> 19)
567 return (a >>> 0) / 4294967295
568 }
569 }
570
571 function _deepEqual(actual, expected, circ) {
572 if (
573 actual === expected ||
574 // null == undefined
575 expected === null && actual == expected ||
576 // make NaN equal to NaN
577 actual !== actual && expected !== expected
578 ) return true
579
580 var key, aKeys, len
581 , aType = typeof actual
582
583 if (
584 aType !== "object" ||
585 actual == null || // jshint ignore:line
586 aType !== typeof expected ||
587 (aType = type(actual)) != type(expected) ||
588 (actual.constructor && actual.constructor !== expected.constructor) ||
589 (aType == "date" && actual.getTime() !== expected.getTime()) ||
590 (aType == "regexp" && "" + actual !== "" + expected)
591 ) {
592 return false
593 }
594
595 key = circ.indexOf(actual)
596 if (key > -1) return true
597 push(circ, actual)
598
599 if (aType == "array" || aType == "arguments") {
600 len = actual.length
601 if (len !== expected.length) return false
602 for (; len--; ) {
603 if (!_deepEqual(actual[len], expected[len], circ)) return false
604 }
605 } else {
606 aKeys = _keys(actual)
607 len = aKeys.length
608 if (len !== _keys(expected).length) return false
609 for (; len--; ) {
610 key = aKeys[len]
611 if (
612 !hasOwn(expected, key) ||
613 !_deepEqual(actual[key], expected[key], circ)
614 ) return false
615 }
616 }
617 return true
618 }
619
620 function type(obj) {
621 /* jshint -W041 */
622 // Standard clearly states that NaN and Infinity are numbers
623 // but this is not useful for testing.
624 return (
625 obj !== obj ? "nan" :
626 obj === _Infinity || obj === -_Infinity ? "infinity" :
627 obj == null ? "" + obj :
628 toStr.call(obj).slice(8, -1).toLowerCase()
629 )
630 }
631 function num(a, b) {
632 return type(a -= 0) === "number" ? a : b
633 }
634 function isStr(str) {
635 return typeof str === "string"
636 }
637 function isFn(fn) {
638 return typeof fn === "function"
639 }
640 function isObj(obj) {
641 return type(obj) === "object"
642 }
643 function own(a, b) {
644 if (a === b) {
645 own.lastMsg = "Can not be strictEqual"
646 } else if (a) {
647 for (var k in b) if (hasOwn(b, k)) {
648 if (!hasOwn(a, k) || (
649 isObj(b[k]) ? !own(a[k], b[k]) :
650 _isArray(b[k]) ? b[k].some(itemNotOwn, a[k]) :
651 a[k] !== b[k]
652 )) {
653 own.lastMsg = own.lastMsg || k + " " + stringify(a[k]) + " != " + stringify(b[k])
654 return false
655 }
656 }
657 return true
658 }
659 function itemNotOwn(val, idx) {
660 return isObj(val) ? !own(this[idx], val) : this[idx] !== val
661 }
662 }
663 function curry(fn, arg, scope) {
664 return fn.bind.apply(fn, [scope].concat(arg))
665 }
666
667 function each(arr, fn) {
668 if (arr) {
669 if (isStr(arr)) arr = arr.split(",")
670 for (var i in arr) if (hasOwn(arr, i)) fn(i, arr[i])
671 }
672 }
673
674 function stringify(item) {
675 var max = conf.cut > 0 ? conf.cut : _Infinity
676 , str = _stringify(item, max, [])
677 return str.length > max ? str.slice(0, max - 3) + ".." + str.slice(-1) : str
678 }
679
680 function _stringify(item, max, circ) {
681 var i, t, tmp
682 , left = max
683 , str =
684 isStr(item) ? JSON.stringify(item) :
685 isFn(item) ? ("" + item).replace(/^\w+|\s+|{[\s\S]*/g, "") :
686 !item || item === true || typeof item === "number" ? "" + item :
687 (t = type(item)) === "error" || t === "symbol" || t === "regexp" ? item.toString() :
688 item.toJSON ? item.toJSON() :
689 item
690
691 if (!isStr(str)) {
692 if (circ.indexOf(str) > -1) return "Circular"
693 push(circ, str)
694 tmp = []
695 for (i in str) if (hasOwn(str, i)) {
696 i = (t === "object" ? _stringify(i, left) + ":" : "") + _stringify(str[i], left, circ)
697 push(tmp, i)
698 left -= i.length
699 if (left < 0) break
700 }
701 str =
702 t === "array" ? "[" + tmp + "]" :
703 t === "arguments" ? t + "[" + tmp + "]" :
704 (t = item.constructor) === Object ? "{" + tmp + "}" :
705 (t ? t.name || /^\w+\s+([^\s(]+)|/.exec(t)[1] || "<anon>" : "<null>") + "{" + tmp + "}"
706 }
707 return str
708 }
709}(this, setTimeout, clearTimeout, Date, Error, Infinity) // jshint ignore:line
710