5 * Copyright jQuery Foundation and other contributors
6 * Released under the MIT license
7 * https://jquery.org/license
9 * Date: 2019-01-07T16:37Z
11 (function (global
$1) {
14 global
$1 = global
$1 && global
$1.hasOwnProperty('default') ? global
$1['default'] : global
$1;
16 var window
$1 = global
$1.window
;
17 var self
$1 = global
$1.self
;
18 var console
= global
$1.console
;
19 var setTimeout
$1 = global
$1.setTimeout
;
20 var clearTimeout
= global
$1.clearTimeout
;
22 var document
$1 = window
$1 && window
$1.document
;
23 var navigator
= window
$1 && window
$1.navigator
;
25 var localSessionStorage = function () {
26 var x
= "qunit-test-string";
28 global
$1.sessionStorage
.setItem(x
, x
);
29 global
$1.sessionStorage
.removeItem(x
);
30 return global
$1.sessionStorage
;
37 * Returns a function that proxies to the given method name on the globals
38 * console object. The proxy will also detect if the console doesn't exist and
39 * will appropriately no-op. This allows support for IE9, which doesn't have a
40 * console if the developer tools are not open.
42 function consoleProxy(method
) {
45 console
[method
].apply(console
, arguments
);
51 warn
: consoleProxy("warn")
54 var _typeof
= typeof Symbol
=== "function" && typeof Symbol
.iterator
=== "symbol" ? function (obj
) {
57 return obj
&& typeof Symbol
=== "function" && obj
.constructor === Symbol
&& obj
!== Symbol
.prototype ? "symbol" : typeof obj
;
70 var classCallCheck = function (instance
, Constructor
) {
71 if (!(instance
instanceof Constructor
)) {
72 throw new TypeError("Cannot call a class as a function");
76 var createClass = function () {
77 function defineProperties(target
, props
) {
78 for (var i
= 0; i
< props
.length
; i
++) {
79 var descriptor
= props
[i
];
80 descriptor
.enumerable
= descriptor
.enumerable
|| false;
81 descriptor
.configurable
= true;
82 if ("value" in descriptor
) descriptor
.writable
= true;
83 Object
.defineProperty(target
, descriptor
.key
, descriptor
);
87 return function (Constructor
, protoProps
, staticProps
) {
88 if (protoProps
) defineProperties(Constructor
.prototype, protoProps
);
89 if (staticProps
) defineProperties(Constructor
, staticProps
);
134 var toConsumableArray = function (arr
) {
135 if (Array
.isArray(arr
)) {
136 for (var i
= 0, arr2
= Array(arr
.length
); i
< arr
.length
; i
++) arr2
[i
] = arr
[i
];
140 return Array
.from(arr
);
144 var toString
= Object
.prototype.toString
;
145 var hasOwn
= Object
.prototype.hasOwnProperty
;
146 var now
= Date
.now
|| function () {
147 return new Date().getTime();
150 var hasPerformanceApi
= detectPerformanceApi();
151 var performance
= hasPerformanceApi
? window
$1.performance
: undefined;
152 var performanceNow
= hasPerformanceApi
? performance
.now
.bind(performance
) : now
;
154 function detectPerformanceApi() {
155 return window
$1 && typeof window
$1.performance
!== "undefined" && typeof window
$1.performance
.mark
=== "function" && typeof window
$1.performance
.measure
=== "function";
158 function measure(comment
, startMark
, endMark
) {
160 // `performance.measure` may fail if the mark could not be found.
161 // reasons a specific mark could not be found include: outside code invoking `performance.clearMarks()`
163 performance
.measure(comment
, startMark
, endMark
);
165 Logger
.warn("performance.measure could not be executed because of ", ex
.message
);
170 document
: window
$1 && window
$1.document
!== undefined,
171 setTimeout
: setTimeout
$1 !== undefined
174 // Returns a new Array with the elements that are in a but not in b
175 function diff(a
, b
) {
180 for (i
= 0; i
< result
.length
; i
++) {
181 for (j
= 0; j
< b
.length
; j
++) {
182 if (result
[i
] === b
[j
]) {
193 * Determines whether an element exists in a given array or not.
197 * @param {Array} array
200 function inArray(elem
, array
) {
201 return array
.indexOf(elem
) !== -1;
205 * Makes a clone of an object using only Array or Object as base,
206 * and copies over the own enumerable properties.
208 * @param {Object} obj
209 * @return {Object} New object with only the own properties (recursively).
211 function objectValues(obj
) {
214 vals
= is("array", obj
) ? [] : {};
216 if (hasOwn
.call(obj
, key
)) {
218 vals
[key
] = val
=== Object(val
) ? objectValues(val
) : val
;
224 function extend(a
, b
, undefOnly
) {
225 for (var prop
in b
) {
226 if (hasOwn
.call(b
, prop
)) {
227 if (b
[prop
] === undefined) {
229 } else if (!(undefOnly
&& typeof a
[prop
] !== "undefined")) {
238 function objectType(obj
) {
239 if (typeof obj
=== "undefined") {
243 // Consider: typeof null === object
248 var match
= toString
.call(obj
).match(/^\[object\s(.*)\]$/),
249 type
= match
&& match
[1];
266 return type
.toLowerCase();
268 return typeof obj
=== "undefined" ? "undefined" : _typeof(obj
);
272 // Safe object type checking
273 function is(type
, obj
) {
274 return objectType(obj
) === type
;
277 // Based on Java's String.hashCode, a simple but not
278 // rigorously collision resistant hashing function
279 function generateHash(module
, testName
) {
280 var str
= module
+ "\x1C" + testName
;
283 for (var i
= 0; i
< str
.length
; i
++) {
284 hash
= (hash
<< 5) - hash
+ str
.charCodeAt(i
);
288 // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
289 // strictly necessary but increases user understanding that the id is a SHA-like hash
290 var hex
= (0x100000000 + hash
).toString(16);
291 if (hex
.length
< 8) {
292 hex
= "0000000" + hex
;
295 return hex
.slice(-8);
298 // Test for equality any JavaScript type.
299 // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org>
300 var equiv
= (function () {
302 // Value pairs queued for comparison. Used for breadth-first processing order, recursion
303 // detection and avoiding repeated comparison (see below for details).
304 // Elements are { a: val, b: val }.
307 var getProto
= Object
.getPrototypeOf
|| function (obj
) {
308 return obj
.__proto__
;
311 function useStrictEquality(a
, b
) {
313 // This only gets called if a and b are not strict equal, and is used to compare on
314 // the primitive values inside object wrappers. For example:
316 // `var j = new Number(1);`
317 // Neither a nor b can be null, as a !== b and they have the same type.
318 if ((typeof a
=== "undefined" ? "undefined" : _typeof(a
)) === "object") {
321 if ((typeof b
=== "undefined" ? "undefined" : _typeof(b
)) === "object") {
328 function compareConstructors(a
, b
) {
329 var protoA
= getProto(a
);
330 var protoB
= getProto(b
);
332 // Comparing constructors is more strict than using `instanceof`
333 if (a
.constructor === b
.constructor) {
338 // If the obj prototype descends from a null constructor, treat it
339 // as a null prototype.
340 if (protoA
&& protoA
.constructor === null) {
343 if (protoB
&& protoB
.constructor === null) {
347 // Allow objects with no prototype to be equivalent to
348 // objects with Object as their constructor.
349 if (protoA
=== null && protoB
=== Object
.prototype || protoB
=== null && protoA
=== Object
.prototype) {
356 function getRegExpFlags(regexp
) {
357 return "flags" in regexp
? regexp
.flags
: regexp
.toString().match(/[gimuy]*$/)[0];
360 function isContainer(val
) {
361 return ["object", "array", "map", "set"].indexOf(objectType(val
)) !== -1;
364 function breadthFirstCompareChild(a
, b
) {
366 // If a is a container not reference-equal to b, postpone the comparison to the
367 // end of the pairs queue -- unless (a, b) has been seen before, in which case skip
372 if (!isContainer(a
)) {
373 return typeEquiv(a
, b
);
375 if (pairs
.every(function (pair
) {
376 return pair
.a
!== a
|| pair
.b
!== b
;
379 // Not yet started comparing this pair
380 pairs
.push({ a
: a
, b
: b
});
386 "string": useStrictEquality
,
387 "boolean": useStrictEquality
,
388 "number": useStrictEquality
,
389 "null": useStrictEquality
,
390 "undefined": useStrictEquality
,
391 "symbol": useStrictEquality
,
392 "date": useStrictEquality
,
394 "nan": function nan() {
398 "regexp": function regexp(a
, b
) {
399 return a
.source
=== b
.source
&&
401 // Include flags in the comparison
402 getRegExpFlags(a
) === getRegExpFlags(b
);
405 // abort (identical references / instance methods were skipped earlier)
406 "function": function _function() {
410 "array": function array(a
, b
) {
414 if (len
!== b
.length
) {
420 for (i
= 0; i
< len
; i
++) {
422 // Compare non-containers; queue non-reference-equal containers
423 if (!breadthFirstCompareChild(a
[i
], b
[i
])) {
430 // Define sets a and b to be equivalent if for each element aVal in a, there
431 // is some element bVal in b such that aVal and bVal are equivalent. Element
432 // repetitions are not counted, so these are equivalent:
433 // a = new Set( [ {}, [], [] ] );
434 // b = new Set( [ {}, {}, [] ] );
435 "set": function set$$1(a
, b
) {
439 if (a
.size
!== b
.size
) {
441 // This optimization has certain quirks because of the lack of
442 // repetition counting. For instance, adding the same
443 // (reference-identical) element to two equivalent sets can
444 // make them non-equivalent.
448 a
.forEach(function (aVal
) {
450 // Short-circuit if the result is already known. (Using for...of
451 // with a break clause would be cleaner here, but it would cause
452 // a syntax error on older Javascript implementations even if
460 b
.forEach(function (bVal
) {
463 // Likewise, short-circuit if the result is already known
468 // Swap out the global pairs list, as the nested call to
469 // innerEquiv will clobber its contents
471 if (innerEquiv(bVal
, aVal
)) {
475 // Replace the global pairs list
487 // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
488 // in a, there is some key-value pair (bKey, bVal) in b such that
489 // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
490 // counted, so these are equivalent:
491 // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
492 // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
493 "map": function map(a
, b
) {
497 if (a
.size
!== b
.size
) {
499 // This optimization has certain quirks because of the lack of
500 // repetition counting. For instance, adding the same
501 // (reference-identical) key-value pair to two equivalent maps
502 // can make them non-equivalent.
506 a
.forEach(function (aVal
, aKey
) {
508 // Short-circuit if the result is already known. (Using for...of
509 // with a break clause would be cleaner here, but it would cause
510 // a syntax error on older Javascript implementations even if
518 b
.forEach(function (bVal
, bKey
) {
521 // Likewise, short-circuit if the result is already known
526 // Swap out the global pairs list, as the nested call to
527 // innerEquiv will clobber its contents
529 if (innerEquiv([bVal
, bKey
], [aVal
, aKey
])) {
533 // Replace the global pairs list
545 "object": function object(a
, b
) {
550 if (compareConstructors(a
, b
) === false) {
554 // Be strict: don't ensure hasOwnProperty and go deep
557 // Collect a's properties
560 // Skip OOP methods that look the same
561 if (a
.constructor !== Object
&& typeof a
.constructor !== "undefined" && typeof a
[i
] === "function" && typeof b
[i
] === "function" && a
[i
].toString() === b
[i
].toString()) {
565 // Compare non-containers; queue non-reference-equal containers
566 if (!breadthFirstCompareChild(a
[i
], b
[i
])) {
573 // Collect b's properties
577 // Ensures identical properties name
578 return typeEquiv(aProperties
.sort(), bProperties
.sort());
582 function typeEquiv(a
, b
) {
583 var type
= objectType(a
);
585 // Callbacks for containers will append to the pairs queue to achieve breadth-first
586 // search order. The pairs queue is also used to avoid reprocessing any pair of
587 // containers that are reference-equal to a previously visited pair (a special case
588 // this being recursion detection).
590 // Because of this approach, once typeEquiv returns a false value, it should not be
591 // called again without clearing the pair queue else it may wrongly report a visited
592 // pair as being equivalent.
593 return objectType(b
) === type
&& callbacks
[type
](a
, b
);
596 function innerEquiv(a
, b
) {
599 // We're done when there's nothing more to compare
600 if (arguments
.length
< 2) {
604 // Clear the global pair queue and add the top-level values being compared
605 pairs
= [{ a
: a
, b
: b
}];
607 for (i
= 0; i
< pairs
.length
; i
++) {
610 // Perform type-specific comparison on any pairs that are not strictly
611 // equal. For container types, that comparison will postpone comparison
612 // of any sub-container pair to the end of the pair queue. This gives
613 // breadth-first search order. It also avoids the reprocessing of
614 // reference-equal siblings, cousins etc, which can have a significant speed
615 // impact when comparing a container of small objects each of which has a
616 // reference to the same (singleton) large object.
617 if (pair
.a
!== pair
.b
&& !typeEquiv(pair
.a
, pair
.b
)) {
622 // ...across all consecutive argument pairs
623 return arguments
.length
=== 2 || innerEquiv
.apply(this, [].slice
.call(arguments
, 1));
627 var result
= innerEquiv
.apply(undefined, arguments
);
629 // Release any retained objects
636 * Config object: Maintain internal state
637 * Later exposed as QUnit.config
638 * `config` initialized at top of scope
642 // The queue of tests to run
645 // Block until document ready
648 // By default, run previously failed tests first
649 // very useful in combination with "Hide passed tests" checked
652 // By default, modify document.title when suite is done
655 // HTML Reporter: collapse every test except the first failing test
656 // If false, all failing tests will be expanded
659 // By default, scroll to top of the page when suite is done
662 // Depth up-to which object will be dumped
665 // When enabled, all tests must call expect()
666 requireExpects
: false,
668 // Placeholder for user-configurable form-exposed URL parameters
671 // Set of all modules.
674 // The first unnamed module
680 unskippedTestsRun
: 0,
691 // The storage module to use for reordering tests
692 storage
: localSessionStorage
695 // take a predefined QUnit.config and extend the defaults
696 var globalConfig
= window
$1 && window
$1.QUnit
&& window
$1.QUnit
.config
;
698 // only extend the global config if there is no QUnit overload
699 if (window
$1 && window
$1.QUnit
&& !window
$1.QUnit
.version
) {
700 extend(config
, globalConfig
);
703 // Push a loose unnamed module to the modules collection
704 config
.modules
.push(config
.currentModule
);
706 // Based on jsDump by Ariel Flesler
707 // http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html
708 var dump
= (function () {
709 function quote(str
) {
710 return "\"" + str
.toString().replace(/\\/g, "\\\\").replace(/"/g, "\\\"") + "\"";
712 function literal(o) {
715 function join(pre, arr, post) {
716 var s = dump.separator(),
717 base = dump.indent(),
718 inner = dump.indent(1);
720 arr = arr.join("," + s + inner);
725 return [pre, inner + arr, base + post].join(s);
727 function array(arr, stack) {
731 if (dump.maxDepth && dump.depth > dump.maxDepth) {
732 return "[object Array
]";
737 ret[i] = this.parse(arr[i], undefined, stack);
740 return join("[", ret, "]");
743 function isArray(obj) {
747 toString.call(obj) === "[object Array
]" ||
750 typeof obj.length === "number
" && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
754 var reName = /^function (\w+)/,
757 // The objType is used mostly internally, you can fix a (custom) type in advance
758 parse: function parse(obj, objType, stack) {
763 objIndex = stack.indexOf(obj);
765 if (objIndex !== -1) {
766 return "recursion(" + (objIndex - stack.length) + ")";
769 objType = objType || this.typeOf(obj);
770 parser = this.parsers[objType];
771 parserType = typeof parser === "undefined" ? "undefined" : _typeof(parser);
773 if (parserType === "function") {
775 res = parser.call(this, obj, stack);
779 return parserType === "string
" ? parser : this.parsers.error;
781 typeOf: function typeOf(obj) {
786 } else if (typeof obj === "undefined") {
788 } else if (is("regexp
", obj)) {
790 } else if (is("date
", obj)) {
792 } else if (is("function", obj)) {
794 } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
796 } else if (obj.nodeType === 9) {
798 } else if (obj.nodeType) {
800 } else if (isArray(obj)) {
802 } else if (obj.constructor === Error.prototype.constructor) {
805 type = typeof obj === "undefined" ? "undefined" : _typeof(obj);
810 separator: function separator() {
811 if (this.multiline) {
812 return this.HTML ? "<br
/>" : "\n";
814 return this.HTML ? " " : " ";
818 // Extra can be a number, shortcut for increasing-calling-decreasing
819 indent: function indent(extra) {
820 if (!this.multiline) {
823 var chr = this.indentChar;
825 chr = chr.replace(/\t/g, " ").replace(/ /g, " ");
827 return new Array(this.depth + (extra || 0)).join(chr);
830 this.depth += a || 1;
832 down: function down(a) {
833 this.depth -= a || 1;
835 setParser: function setParser(name, parser) {
836 this.parsers[name] = parser;
839 // The next 3 are exposed so you can use them
844 maxDepth: config.maxDepth,
846 // This is the list of parsers, to modify them, use dump.setParser
849 document: "[Document
]",
850 error: function error(_error) {
851 return "Error(\"" + _error.message + "\")";
853 unknown: "[Unknown
]",
855 "undefined": "undefined",
856 "function": function _function(fn) {
857 var ret = "function",
860 // Functions never have name in IE
861 name = "name
" in fn ? fn.name : (reName.exec(fn) || [])[1];
868 ret = [ret, dump.parse(fn, "functionArgs
"), "){"].join("");
869 return join(ret, dump.parse(fn, "functionCode
"), "}");
874 object: function object(map, stack) {
879 nonEnumerableProperties,
882 if (dump.maxDepth && dump.depth > dump.maxDepth) {
883 return "[object Object
]";
892 // Some properties are not always enumerable on Error objects.
893 nonEnumerableProperties = ["message
", "name
"];
894 for (i in nonEnumerableProperties) {
895 key = nonEnumerableProperties[i];
896 if (key in map && !inArray(key, keys)) {
901 for (i = 0; i < keys.length; i++) {
904 ret.push(dump.parse(key, "key
") + ": " + dump.parse(val, undefined, stack));
907 return join("{", ret, "}");
909 node: function node(_node) {
913 open = dump.HTML ? "<
;" : "<",
914 close = dump.HTML ? ">
;" : ">",
915 tag = _node.nodeName.toLowerCase(),
917 attrs = _node.attributes;
920 for (i = 0, len = attrs.length; i < len; i++) {
921 val = attrs[i].nodeValue;
923 // IE6 includes all attributes in .attributes, even ones not explicitly
924 // set. Those have values like undefined, null, 0, false, "" or
926 if (val && val !== "inherit
") {
927 ret += " " + attrs[i].nodeName + "=" + dump.parse(val, "attribute
");
933 // Show content of TextNode or CDATASection
934 if (_node.nodeType === 3 || _node.nodeType === 4) {
935 ret += _node.nodeValue;
938 return ret + open + "/" + tag + close;
941 // Function calls it internally, it's the arguments part of the function
942 functionArgs: function functionArgs(fn) {
954 args[l] = String.fromCharCode(97 + l);
956 return " " + args.join(", ") + " ";
959 // Object calls it internally, the key part of an item in a map
962 // Function calls it internally, it's the content of the function
963 functionCode: "[code
]",
965 // Node calls it internally, it's a html attribute value
972 symbol: function symbol(sym) {
973 return sym.toString();
977 // If true, entities are escaped ( <, >, \t, space and \n )
983 // If true, items in a collection, are separated by a \n, else just a space.
990 var SuiteReport = function () {
991 function SuiteReport(name, parentSuite) {
992 classCallCheck(this, SuiteReport);
995 this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
998 this.childSuites = [];
1001 parentSuite.pushChildSuite(this);
1005 createClass(SuiteReport, [{
1007 value: function start(recordTime) {
1009 this._startTime = performanceNow();
1012 var suiteLevel = this.fullName.length;
1013 performance.mark("qunit_suite_
" + suiteLevel + "_start
");
1019 fullName: this.fullName.slice(),
1020 tests: this.tests.map(function (test) {
1021 return test.start();
1023 childSuites: this.childSuites.map(function (suite) {
1024 return suite.start();
1027 total: this.getTestCounts().total
1033 value: function end(recordTime) {
1035 this._endTime = performanceNow();
1038 var suiteLevel = this.fullName.length;
1039 performance.mark("qunit_suite_
" + suiteLevel + "_end
");
1041 var suiteName = this.fullName.join(" – ");
1043 measure(suiteLevel === 0 ? "QUnit Test Run
" : "QUnit Test Suite
: " + suiteName, "qunit_suite_
" + suiteLevel + "_start
", "qunit_suite_
" + suiteLevel + "_end
");
1049 fullName: this.fullName.slice(),
1050 tests: this.tests.map(function (test) {
1053 childSuites: this.childSuites.map(function (suite) {
1056 testCounts: this.getTestCounts(),
1057 runtime: this.getRuntime(),
1058 status: this.getStatus()
1062 key: "pushChildSuite
",
1063 value: function pushChildSuite(suite) {
1064 this.childSuites.push(suite);
1068 value: function pushTest(test) {
1069 this.tests.push(test);
1073 value: function getRuntime() {
1074 return this._endTime - this._startTime;
1077 key: "getTestCounts
",
1078 value: function getTestCounts() {
1079 var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 };
1081 counts = this.tests.reduce(function (counts, test) {
1083 counts[test.getStatus()]++;
1090 return this.childSuites.reduce(function (counts, suite) {
1091 return suite.getTestCounts(counts);
1096 value: function getStatus() {
1097 var _getTestCounts = this.getTestCounts(),
1098 total = _getTestCounts.total,
1099 failed = _getTestCounts.failed,
1100 skipped = _getTestCounts.skipped,
1101 todo = _getTestCounts.todo;
1106 if (skipped === total) {
1108 } else if (todo === total) {
1119 var focused = false;
1121 var moduleStack = [];
1123 function createModule(name, testEnvironment, modifiers) {
1124 var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
1125 var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name;
1126 var parentSuite = parentModule ? parentModule.suiteReport : globalSuite;
1128 var skip = parentModule !== null && parentModule.skip || modifiers.skip;
1129 var todo = parentModule !== null && parentModule.todo || modifiers.todo;
1133 parentModule: parentModule,
1135 moduleId: generateHash(moduleName),
1137 unskippedTestsRun: 0,
1139 suiteReport: new SuiteReport(name, parentSuite),
1141 // Pass along `skip` and `todo` properties from parent module, in case
1142 // there is one, to childs. And use own otherwise.
1143 // This property will be used to mark own tests and tests of child suites
1144 // as either `skipped` or `todo`.
1146 todo: skip ? false : todo
1151 parentModule.childModules.push(module);
1152 extend(env, parentModule.testEnvironment);
1154 extend(env, testEnvironment);
1155 module.testEnvironment = env;
1157 config.modules.push(module);
1161 function processModule(name, options, executeNow) {
1162 var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1164 if (objectType(options) === "function") {
1165 executeNow = options;
1166 options = undefined;
1169 var module = createModule(name, options, modifiers);
1171 // Move any hooks to a 'hooks' object
1172 var testEnvironment = module.testEnvironment;
1173 var hooks = module.hooks = {};
1175 setHookFromEnvironment(hooks, testEnvironment, "before
");
1176 setHookFromEnvironment(hooks, testEnvironment, "beforeEach
");
1177 setHookFromEnvironment(hooks, testEnvironment, "afterEach
");
1178 setHookFromEnvironment(hooks, testEnvironment, "after
");
1181 before: setHookFunction(module, "before
"),
1182 beforeEach: setHookFunction(module, "beforeEach
"),
1183 afterEach: setHookFunction(module, "afterEach
"),
1184 after: setHookFunction(module, "after
")
1187 var currentModule = config.currentModule;
1188 if (objectType(executeNow) === "function") {
1189 moduleStack.push(module);
1190 config.currentModule = module;
1191 executeNow.call(module.testEnvironment, moduleFns);
1193 module = module.parentModule || currentModule;
1196 config.currentModule = module;
1198 function setHookFromEnvironment(hooks, environment, name) {
1199 var potentialHook = environment[name];
1200 hooks[name] = typeof potentialHook === "function" ? [potentialHook] : [];
1201 delete environment[name];
1204 function setHookFunction(module, hookName) {
1205 return function setHook(callback) {
1206 module.hooks[hookName].push(callback);
1211 function module$1(name, options, executeNow) {
1216 processModule(name, options, executeNow);
1219 module$1.only = function () {
1224 config.modules.length = 0;
1225 config.queue.length = 0;
1227 module$1.apply(undefined, arguments);
1232 module$1.skip = function (name, options, executeNow) {
1237 processModule(name, options, executeNow, { skip: true });
1240 module$1.todo = function (name, options, executeNow) {
1245 processModule(name, options, executeNow, { todo: true });
1248 var LISTENERS = Object.create(null);
1249 var SUPPORTED_EVENTS = ["runStart
", "suiteStart
", "testStart
", "assertion
", "testEnd
", "suiteEnd
", "runEnd
"];
1252 * Emits an event with the specified data to all currently registered listeners.
1253 * Callbacks will fire in the order in which they are registered (FIFO). This
1254 * function is not exposed publicly; it is used by QUnit internals to emit
1259 * @param {String} eventName
1260 * @param {Object} data
1263 function emit(eventName, data) {
1264 if (objectType(eventName) !== "string
") {
1265 throw new TypeError("eventName must be a string when emitting an event
");
1268 // Clone the callbacks in case one of them registers a new callback
1269 var originalCallbacks = LISTENERS[eventName];
1270 var callbacks = originalCallbacks ? [].concat(toConsumableArray(originalCallbacks)) : [];
1272 for (var i = 0; i < callbacks.length; i++) {
1278 * Registers a callback as a listener to the specified event.
1282 * @param {String} eventName
1283 * @param {Function} callback
1286 function on(eventName, callback) {
1287 if (objectType(eventName) !== "string
") {
1288 throw new TypeError("eventName must be a string when registering a listener
");
1289 } else if (!inArray(eventName, SUPPORTED_EVENTS)) {
1290 var events = SUPPORTED_EVENTS.join(", ");
1291 throw new Error("\"" + eventName + "\" is not a valid event
; must be one
of: " + events + ".");
1292 } else if (objectType(callback) !== "function") {
1293 throw new TypeError("callback must be a
function when registering a listener
");
1296 if (!LISTENERS[eventName]) {
1297 LISTENERS[eventName] = [];
1300 // Don't register the same callback more than once
1301 if (!inArray(callback, LISTENERS[eventName])) {
1302 LISTENERS[eventName].push(callback);
1306 function objectOrFunction(x) {
1307 var type = typeof x === 'undefined' ? 'undefined' : _typeof(x);
1308 return x !== null && (type === 'object' || type === 'function');
1311 function isFunction(x) {
1312 return typeof x === 'function';
1317 var _isArray = void 0;
1318 if (Array.isArray) {
1319 _isArray = Array.isArray;
1321 _isArray = function _isArray(x) {
1322 return Object.prototype.toString.call(x) === '[object Array]';
1326 var isArray = _isArray;
1329 var vertxNext = void 0;
1330 var customSchedulerFn = void 0;
1332 var asap = function asap(callback, arg) {
1333 queue[len] = callback;
1334 queue[len + 1] = arg;
1337 // If len is 2, that means that we need to schedule an async flush.
1338 // If additional callbacks are queued before the queue is flushed, they
1339 // will be processed by this flush that we are scheduling.
1340 if (customSchedulerFn) {
1341 customSchedulerFn(flush);
1348 function setScheduler(scheduleFn) {
1349 customSchedulerFn = scheduleFn;
1352 function setAsap(asapFn) {
1356 var browserWindow = typeof window !== 'undefined' ? window : undefined;
1357 var browserGlobal = browserWindow || {};
1358 var BrowserMutationObserver = browserGlobal.MutationObserver || browserGlobal.WebKitMutationObserver;
1359 var isNode = typeof self === 'undefined' && typeof process !== 'undefined' && {}.toString.call(process) === '[object process]';
1361 // test for web worker but not in IE10
1362 var isWorker = typeof Uint8ClampedArray !== 'undefined' && typeof importScripts !== 'undefined' && typeof MessageChannel !== 'undefined';
1365 function useNextTick() {
1366 // node version 0.10.x displays a deprecation warning when nextTick is used recursively
1367 // see https://github.com/cujojs/when/issues/410 for details
1368 return function () {
1369 return process.nextTick(flush);
1374 function useVertxTimer() {
1375 if (typeof vertxNext !== 'undefined') {
1376 return function () {
1381 return useSetTimeout();
1384 function useMutationObserver() {
1386 var observer = new BrowserMutationObserver(flush);
1387 var node = document.createTextNode('');
1388 observer.observe(node, { characterData: true });
1390 return function () {
1391 node.data = iterations = ++iterations % 2;
1396 function useMessageChannel() {
1397 var channel = new MessageChannel();
1398 channel.port1.onmessage = flush;
1399 return function () {
1400 return channel.port2.postMessage(0);
1404 function useSetTimeout() {
1405 // Store setTimeout reference so es6-promise will be unaffected by
1406 // other code modifying setTimeout (like sinon.useFakeTimers())
1407 var globalSetTimeout = setTimeout;
1408 return function () {
1409 return globalSetTimeout(flush, 1);
1413 var queue = new Array(1000);
1415 for (var i = 0; i < len; i += 2) {
1416 var callback = queue[i];
1417 var arg = queue[i + 1];
1421 queue[i] = undefined;
1422 queue[i + 1] = undefined;
1428 function attemptVertx() {
1430 var vertx = Function('return this')().require('vertx');
1431 vertxNext = vertx.runOnLoop || vertx.runOnContext;
1432 return useVertxTimer();
1434 return useSetTimeout();
1438 var scheduleFlush = void 0;
1439 // Decide what async method to use to triggering processing of queued callbacks:
1441 scheduleFlush = useNextTick();
1442 } else if (BrowserMutationObserver) {
1443 scheduleFlush = useMutationObserver();
1444 } else if (isWorker) {
1445 scheduleFlush = useMessageChannel();
1446 } else if (browserWindow === undefined && typeof require === 'function') {
1447 scheduleFlush = attemptVertx();
1449 scheduleFlush = useSetTimeout();
1452 function then(onFulfillment, onRejection) {
1455 var child = new this.constructor(noop);
1457 if (child[PROMISE_ID] === undefined) {
1461 var _state = parent._state;
1465 var callback = arguments[_state - 1];
1467 return invokeCallback(_state, child, callback, parent._result);
1470 subscribe(parent, child, onFulfillment, onRejection);
1477 `Promise.resolve` returns a promise that will become resolved with the
1478 passed `value`. It is shorthand for the following:
1481 let promise = new Promise(function(resolve, reject){
1485 promise.then(function(value){
1490 Instead of writing the above, your code now simply becomes the following:
1493 let promise = Promise.resolve(1);
1495 promise.then(function(value){
1502 @param {Any} value value that the returned promise will be resolved with
1504 @return {Promise} a promise that will become fulfilled with the given
1507 function resolve$1(object) {
1508 /*jshint validthis:true */
1509 var Constructor = this;
1511 if (object && (typeof object === 'undefined' ? 'undefined' : _typeof(object)) === 'object' && object.constructor === Constructor) {
1515 var promise = new Constructor(noop);
1516 resolve(promise, object);
1520 var PROMISE_ID = Math.random().toString(36).substring(2);
1524 var PENDING = void 0;
1528 var TRY_CATCH_ERROR = { error: null };
1530 function selfFulfillment() {
1531 return new TypeError("You cannot resolve a promise
with itself
");
1534 function cannotReturnOwn() {
1535 return new TypeError('A promises callback cannot return that same promise.');
1538 function getThen(promise) {
1540 return promise.then;
1542 TRY_CATCH_ERROR.error = error;
1543 return TRY_CATCH_ERROR;
1547 function tryThen(then$$1, value, fulfillmentHandler, rejectionHandler) {
1549 then$$1.call(value, fulfillmentHandler, rejectionHandler);
1555 function handleForeignThenable(promise, thenable, then$$1) {
1556 asap(function (promise) {
1558 var error = tryThen(then$$1, thenable, function (value) {
1563 if (thenable !== value) {
1564 resolve(promise, value);
1566 fulfill(promise, value);
1568 }, function (reason) {
1574 reject(promise, reason);
1575 }, 'Settle: ' + (promise._label || ' unknown promise'));
1577 if (!sealed && error) {
1579 reject(promise, error);
1584 function handleOwnThenable(promise, thenable) {
1585 if (thenable._state === FULFILLED) {
1586 fulfill(promise, thenable._result);
1587 } else if (thenable._state === REJECTED) {
1588 reject(promise, thenable._result);
1590 subscribe(thenable, undefined, function (value) {
1591 return resolve(promise, value);
1592 }, function (reason) {
1593 return reject(promise, reason);
1598 function handleMaybeThenable(promise, maybeThenable, then$$1) {
1599 if (maybeThenable.constructor === promise.constructor && then$$1 === then && maybeThenable.constructor.resolve === resolve$1) {
1600 handleOwnThenable(promise, maybeThenable);
1602 if (then$$1 === TRY_CATCH_ERROR) {
1603 reject(promise, TRY_CATCH_ERROR.error);
1604 TRY_CATCH_ERROR.error = null;
1605 } else if (then$$1 === undefined) {
1606 fulfill(promise, maybeThenable);
1607 } else if (isFunction(then$$1)) {
1608 handleForeignThenable(promise, maybeThenable, then$$1);
1610 fulfill(promise, maybeThenable);
1615 function resolve(promise, value) {
1616 if (promise === value) {
1617 reject(promise, selfFulfillment());
1618 } else if (objectOrFunction(value)) {
1619 handleMaybeThenable(promise, value, getThen(value));
1621 fulfill(promise, value);
1625 function publishRejection(promise) {
1626 if (promise._onerror) {
1627 promise._onerror(promise._result);
1633 function fulfill(promise, value) {
1634 if (promise._state !== PENDING) {
1638 promise._result = value;
1639 promise._state = FULFILLED;
1641 if (promise._subscribers.length !== 0) {
1642 asap(publish, promise);
1646 function reject(promise, reason) {
1647 if (promise._state !== PENDING) {
1650 promise._state = REJECTED;
1651 promise._result = reason;
1653 asap(publishRejection, promise);
1656 function subscribe(parent, child, onFulfillment, onRejection) {
1657 var _subscribers = parent._subscribers;
1658 var length = _subscribers.length;
1661 parent._onerror = null;
1663 _subscribers[length] = child;
1664 _subscribers[length + FULFILLED] = onFulfillment;
1665 _subscribers[length + REJECTED] = onRejection;
1667 if (length === 0 && parent._state) {
1668 asap(publish, parent);
1672 function publish(promise) {
1673 var subscribers = promise._subscribers;
1674 var settled = promise._state;
1676 if (subscribers.length === 0) {
1682 detail = promise._result;
1684 for (var i = 0; i < subscribers.length; i += 3) {
1685 child = subscribers[i];
1686 callback = subscribers[i + settled];
1689 invokeCallback(settled, child, callback, detail);
1695 promise._subscribers.length = 0;
1698 function tryCatch(callback, detail) {
1700 return callback(detail);
1702 TRY_CATCH_ERROR.error = e;
1703 return TRY_CATCH_ERROR;
1707 function invokeCallback(settled, promise, callback, detail) {
1708 var hasCallback = isFunction(callback),
1715 value = tryCatch(callback, detail);
1717 if (value === TRY_CATCH_ERROR) {
1719 error = value.error;
1725 if (promise === value) {
1726 reject(promise, cannotReturnOwn());
1734 if (promise._state !== PENDING) {
1736 } else if (hasCallback && succeeded) {
1737 resolve(promise, value);
1738 } else if (failed) {
1739 reject(promise, error);
1740 } else if (settled === FULFILLED) {
1741 fulfill(promise, value);
1742 } else if (settled === REJECTED) {
1743 reject(promise, value);
1747 function initializePromise(promise, resolver) {
1749 resolver(function resolvePromise(value) {
1750 resolve(promise, value);
1751 }, function rejectPromise(reason) {
1752 reject(promise, reason);
1764 function makePromise(promise) {
1765 promise[PROMISE_ID] = id++;
1766 promise._state = undefined;
1767 promise._result = undefined;
1768 promise._subscribers = [];
1771 function validationError() {
1772 return new Error('Array Methods must be provided an Array');
1775 var Enumerator = function () {
1776 function Enumerator(Constructor, input) {
1777 classCallCheck(this, Enumerator);
1779 this._instanceConstructor = Constructor;
1780 this.promise = new Constructor(noop);
1782 if (!this.promise[PROMISE_ID]) {
1783 makePromise(this.promise);
1786 if (isArray(input)) {
1787 this.length = input.length;
1788 this._remaining = input.length;
1790 this._result = new Array(this.length);
1792 if (this.length === 0) {
1793 fulfill(this.promise, this._result);
1795 this.length = this.length || 0;
1796 this._enumerate(input);
1797 if (this._remaining === 0) {
1798 fulfill(this.promise, this._result);
1802 reject(this.promise, validationError());
1806 createClass(Enumerator, [{
1808 value: function _enumerate(input) {
1809 for (var i = 0; this._state === PENDING && i < input.length; i++) {
1810 this._eachEntry(input[i], i);
1815 value: function _eachEntry(entry, i) {
1816 var c = this._instanceConstructor;
1817 var resolve$$1 = c.resolve;
1820 if (resolve$$1 === resolve$1) {
1821 var _then = getThen(entry);
1823 if (_then === then && entry._state !== PENDING) {
1824 this._settledAt(entry._state, i, entry._result);
1825 } else if (typeof _then !== 'function') {
1827 this._result[i] = entry;
1828 } else if (c === Promise$2) {
1829 var promise = new c(noop);
1830 handleMaybeThenable(promise, entry, _then);
1831 this._willSettleAt(promise, i);
1833 this._willSettleAt(new c(function (resolve$$1) {
1834 return resolve$$1(entry);
1838 this._willSettleAt(resolve$$1(entry), i);
1843 value: function _settledAt(state, i, value) {
1844 var promise = this.promise;
1847 if (promise._state === PENDING) {
1850 if (state === REJECTED) {
1851 reject(promise, value);
1853 this._result[i] = value;
1857 if (this._remaining === 0) {
1858 fulfill(promise, this._result);
1862 key: '_willSettleAt',
1863 value: function _willSettleAt(promise, i) {
1864 var enumerator = this;
1866 subscribe(promise, undefined, function (value) {
1867 return enumerator._settledAt(FULFILLED, i, value);
1868 }, function (reason) {
1869 return enumerator._settledAt(REJECTED, i, reason);
1877 `Promise.all` accepts an array of promises, and returns a new promise which
1878 is fulfilled with an array of fulfillment values for the passed promises, or
1879 rejected with the reason of the first passed promise to be rejected. It casts all
1880 elements of the passed iterable to promises as it runs this algorithm.
1885 let promise1 = resolve(1);
1886 let promise2 = resolve(2);
1887 let promise3 = resolve(3);
1888 let promises = [ promise1, promise2, promise3 ];
1890 Promise.all(promises).then(function(array){
1891 // The array here would be [ 1, 2, 3 ];
1895 If any of the `promises` given to `all` are rejected, the first promise
1896 that is rejected will be given as an argument to the returned promises's
1897 rejection handler. For example:
1902 let promise1 = resolve(1);
1903 let promise2 = reject(new Error("2"));
1904 let promise3 = reject(new Error("3"));
1905 let promises = [ promise1, promise2, promise3 ];
1907 Promise.all(promises).then(function(array){
1908 // Code here never runs because there are rejected promises!
1909 }, function(error) {
1910 // error.message === "2"
1916 @param {Array} entries array of promises
1917 @param {String} label optional string for labeling the promise.
1919 @return {Promise} promise that is fulfilled when all `promises` have been
1920 fulfilled, or rejected if any of them become rejected.
1923 function all(entries) {
1924 return new Enumerator(this, entries).promise;
1928 `Promise.race` returns a new promise which is settled in the same way as the
1929 first passed promise to settle.
1934 let promise1 = new Promise(function(resolve, reject){
1935 setTimeout(function(){
1936 resolve('promise 1');
1940 let promise2 = new Promise(function(resolve, reject){
1941 setTimeout(function(){
1942 resolve('promise 2');
1946 Promise.race([promise1, promise2]).then(function(result){
1947 // result === 'promise 2' because it was resolved before promise1
1952 `Promise.race` is deterministic in that only the state of the first
1953 settled promise matters. For example, even if other promises given to the
1954 `promises` array argument are resolved, but the first settled promise has
1955 become rejected before the other promises became fulfilled, the returned
1956 promise will become rejected:
1959 let promise1 = new Promise(function(resolve, reject){
1960 setTimeout(function(){
1961 resolve('promise 1');
1965 let promise2 = new Promise(function(resolve, reject){
1966 setTimeout(function(){
1967 reject(new Error('promise 2'));
1971 Promise.race([promise1, promise2]).then(function(result){
1972 // Code here never runs
1973 }, function(reason){
1974 // reason.message === 'promise 2' because promise 2 became rejected before
1975 // promise 1 became fulfilled
1979 An example real-world use case is implementing timeouts:
1982 Promise.race([ajax('foo.json'), timeout(5000)])
1987 @param {Array} promises array of promises to observe
1989 @return {Promise} a promise which settles in the same way as the first passed
1992 function race(entries) {
1993 /*jshint validthis:true */
1994 var Constructor = this;
1996 if (!isArray(entries)) {
1997 return new Constructor(function (_, reject) {
1998 return reject(new TypeError('You must pass an array to race.'));
2001 return new Constructor(function (resolve, reject) {
2002 var length = entries.length;
2003 for (var i = 0; i < length; i++) {
2004 Constructor.resolve(entries[i]).then(resolve, reject);
2011 `Promise.reject` returns a promise rejected with the passed `reason`.
2012 It is shorthand for the following:
2015 let promise = new Promise(function(resolve, reject){
2016 reject(new Error('WHOOPS'));
2019 promise.then(function(value){
2020 // Code here doesn't run because the promise is rejected!
2021 }, function(reason){
2022 // reason.message === 'WHOOPS'
2026 Instead of writing the above, your code now simply becomes the following:
2029 let promise = Promise.reject(new Error('WHOOPS'));
2031 promise.then(function(value){
2032 // Code here doesn't run because the promise is rejected!
2033 }, function(reason){
2034 // reason.message === 'WHOOPS'
2040 @param {Any} reason value that the returned promise will be rejected with.
2042 @return {Promise} a promise rejected with the given `reason`.
2044 function reject$1(reason) {
2045 /*jshint validthis:true */
2046 var Constructor = this;
2047 var promise = new Constructor(noop);
2048 reject(promise, reason);
2052 function needsResolver() {
2053 throw new TypeError('You must pass a resolver function as the first argument to the promise constructor');
2056 function needsNew() {
2057 throw new TypeError("Failed to construct
'Promise': Please
use the
'new' operator
, this object
constructor cannot be called as a
function.");
2061 Promise objects represent the eventual result of an asynchronous operation. The
2062 primary way of interacting with a promise is through its `then` method, which
2063 registers callbacks to receive either a promise's eventual value or the reason
2064 why the promise cannot be fulfilled.
2069 - `promise` is an object or function with a `then` method whose behavior conforms to this specification.
2070 - `thenable` is an object or function that defines a `then` method.
2071 - `value` is any legal JavaScript value (including undefined, a thenable, or a promise).
2072 - `exception` is a value that is thrown using the throw statement.
2073 - `reason` is a value that indicates why a promise was rejected.
2074 - `settled` the final resting state of a promise, fulfilled or rejected.
2076 A promise can be in one of three states: pending, fulfilled, or rejected.
2078 Promises that are fulfilled have a fulfillment value and are in the fulfilled
2079 state. Promises that are rejected have a rejection reason and are in the
2080 rejected state. A fulfillment value is never a thenable.
2082 Promises can also be said to *resolve* a value. If this value is also a
2083 promise, then the original promise's settled state will match the value's
2084 settled state. So a promise that *resolves* a promise that rejects will
2085 itself reject, and a promise that *resolves* a promise that fulfills will
2093 let promise = new Promise(function(resolve, reject) {
2101 promise.then(function(value) {
2103 }, function(reason) {
2111 Promises shine when abstracting away asynchronous interactions such as
2115 function getJSON(url) {
2116 return new Promise(function(resolve, reject){
2117 let xhr = new XMLHttpRequest();
2119 xhr.open('GET', url);
2120 xhr.onreadystatechange = handler;
2121 xhr.responseType = 'json';
2122 xhr.setRequestHeader('Accept', 'application/json');
2125 function handler() {
2126 if (this.readyState === this.DONE) {
2127 if (this.status === 200) {
2128 resolve(this.response);
2130 reject(new Error('getJSON: `' + url + '` failed with status: [' + this.status + ']'));
2137 getJSON('/posts.json').then(function(json) {
2139 }, function(reason) {
2144 Unlike callbacks, promises are great composable primitives.
2149 getJSON('/comments')
2150 ]).then(function(values){
2151 values[0] // => postsJSON
2152 values[1] // => commentsJSON
2159 @param {Function} resolver
2164 var Promise$2 = function () {
2165 function Promise(resolver) {
2166 classCallCheck(this, Promise);
2168 this[PROMISE_ID] = nextId();
2169 this._result = this._state = undefined;
2170 this._subscribers = [];
2172 if (noop !== resolver) {
2173 typeof resolver !== 'function' && needsResolver();
2174 this instanceof Promise ? initializePromise(this, resolver) : needsNew();
2179 The primary way of interacting with a promise is through its `then` method,
2180 which registers callbacks to receive either a promise's eventual value or the
2181 reason why the promise cannot be fulfilled.
2183 findUser().then(function(user){
2184 // user is available
2185 }, function(reason){
2186 // user is unavailable, and you are given the reason why
2191 The return value of `then` is itself a promise. This second, 'downstream'
2192 promise is resolved with the return value of the first promise's fulfillment
2193 or rejection handler, or rejected if the handler throws an exception.
2195 findUser().then(function (user) {
2197 }, function (reason) {
2198 return 'default name';
2199 }).then(function (userName) {
2200 // If `findUser` fulfilled, `userName` will be the user's name, otherwise it
2201 // will be `'default name'`
2203 findUser().then(function (user) {
2204 throw new Error('Found user, but still unhappy');
2205 }, function (reason) {
2206 throw new Error('`findUser` rejected and we're unhappy');
2207 }).then(function (value) {
2209 }, function (reason) {
2210 // if `findUser` fulfilled, `reason` will be 'Found user, but still unhappy'.
2211 // If `findUser` rejected, `reason` will be '`findUser` rejected and we're unhappy'.
2214 If the downstream promise does not specify a rejection handler, rejection reasons will be propagated further downstream.
2216 findUser().then(function (user) {
2217 throw new PedagogicalException('Upstream error');
2218 }).then(function (value) {
2220 }).then(function (value) {
2222 }, function (reason) {
2223 // The `PedgagocialException` is propagated all the way down to here
2228 Sometimes the value you want to propagate to a downstream promise can only be
2229 retrieved asynchronously. This can be achieved by returning a promise in the
2230 fulfillment or rejection handler. The downstream promise will then be pending
2231 until the returned promise is settled. This is called *assimilation*.
2233 findUser().then(function (user) {
2234 return findCommentsByAuthor(user);
2235 }).then(function (comments) {
2236 // The user's comments are now available
2239 If the assimliated promise rejects, then the downstream promise will also reject.
2241 findUser().then(function (user) {
2242 return findCommentsByAuthor(user);
2243 }).then(function (comments) {
2244 // If `findCommentsByAuthor` fulfills, we'll have the value here
2245 }, function (reason) {
2246 // If `findCommentsByAuthor` rejects, we'll have the reason here
2255 result = findResult();
2263 findResult(function(result, err){
2273 findResult().then(function(result){
2275 }, function(reason){
2285 author = findAuthor();
2286 books = findBooksByAuthor(author);
2294 function foundBooks(books) {
2296 function failure(reason) {
2298 findAuthor(function(author, err){
2304 findBoooksByAuthor(author, function(books, err) {
2325 then(findBooksByAuthor).
2326 then(function(books){
2328 }).catch(function(reason){
2329 // something went wrong
2333 @param {Function} onFulfilled
2334 @param {Function} onRejected
2340 `catch` is simply sugar for `then(undefined, onRejection)` which makes it the same
2341 as the catch block of a try/catch statement.
2343 function findAuthor(){
2344 throw new Error('couldn't find that author');
2350 // something went wrong
2352 // async with promises
2353 findAuthor().catch(function(reason){
2354 // something went wrong
2358 @param {Function} onRejection
2364 createClass(Promise, [{
2366 value: function _catch(onRejection) {
2367 return this.then(null, onRejection);
2371 `finally` will be invoked regardless of the promise's fate just as native
2372 try/catch/finally behaves
2374 Synchronous example:
2378 if (Math.random() > 0.5) {
2381 return new Author();
2385 return findAuthor(); // succeed or fail
2387 return findOtherAuther();
2390 // doesn't affect the return value
2394 Asynchronous example:
2397 findAuthor().catch(function(reason){
2398 return findOtherAuther();
2399 }).finally(function(){
2400 // author was either found, or not
2405 @param {Function} callback
2411 value: function _finally(callback) {
2413 var constructor = promise.constructor;
2415 if (isFunction(callback)) {
2416 return promise.then(function (value) {
2417 return constructor.resolve(callback()).then(function () {
2420 }, function (reason) {
2421 return constructor.resolve(callback()).then(function () {
2427 return promise.then(callback, callback);
2433 Promise$2.prototype.then = then;
2434 Promise$2.all = all;
2435 Promise$2.race = race;
2436 Promise$2.resolve = resolve$1;
2437 Promise$2.reject = reject$1;
2438 Promise$2._setScheduler = setScheduler;
2439 Promise$2._setAsap = setAsap;
2440 Promise$2._asap = asap;
2443 function polyfill() {
2446 if (typeof global !== 'undefined') {
2448 } else if (typeof self !== 'undefined') {
2452 local = Function('return this')();
2454 throw new Error('polyfill failed because global object is unavailable in this environment');
2458 var P = local.Promise;
2461 var promiseToString = null;
2463 promiseToString = Object.prototype.toString.call(P.resolve());
2468 if (promiseToString === '[object Promise]' && !P.cast) {
2473 local.Promise = Promise$2;
2477 Promise$2.polyfill = polyfill;
2478 Promise$2.Promise = Promise$2;
2480 var Promise$1 = typeof Promise !== "undefined" ? Promise : Promise$2;
2482 // Register logging callbacks
2483 function registerLoggingCallbacks(obj) {
2487 callbackNames = ["begin
", "done
", "log
", "testStart
", "testDone
", "moduleStart
", "moduleDone
"];
2489 function registerLoggingCallback(key) {
2490 var loggingCallback = function loggingCallback(callback) {
2491 if (objectType(callback) !== "function") {
2492 throw new Error("QUnit logging methods require a callback
function as their first parameters
.");
2495 config.callbacks[key].push(callback);
2498 return loggingCallback;
2501 for (i = 0, l = callbackNames.length; i < l; i++) {
2502 key = callbackNames[i];
2504 // Initialize key collection of logging callback
2505 if (objectType(config.callbacks[key]) === "undefined") {
2506 config.callbacks[key] = [];
2509 obj[key] = registerLoggingCallback(key);
2513 function runLoggingCallbacks(key, args) {
2514 var callbacks = config.callbacks[key];
2516 // Handling 'log' callbacks separately. Unlike the other callbacks,
2517 // the log callback is not controlled by the processing queue,
2518 // but rather used by asserts. Hence to promisfy the 'log' callback
2519 // would mean promisfying each step of a test
2520 if (key === "log
") {
2521 callbacks.map(function (callback) {
2522 return callback(args);
2527 // ensure that each callback is executed serially
2528 return callbacks.reduce(function (promiseChain, callback) {
2529 return promiseChain.then(function () {
2530 return Promise$1.resolve(callback(args));
2532 }, Promise$1.resolve([]));
2535 // Doesn't support IE9, it will return undefined on these browsers
2536 // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
2537 var fileName = (sourceFromStacktrace(0) || "").replace(/(:\d+)+\)?/, "").replace(/.+\//, "");
2539 function extractStacktrace(e, offset) {
2540 offset = offset === undefined ? 4 : offset;
2542 var stack, include, i;
2545 stack = e.stack.split("\n");
2546 if (/^error$/i.test(stack[0])) {
2551 for (i = offset; i < stack.length; i++) {
2552 if (stack[i].indexOf(fileName) !== -1) {
2555 include.push(stack[i]);
2557 if (include.length) {
2558 return include.join("\n");
2561 return stack[offset];
2565 function sourceFromStacktrace(offset) {
2566 var error = new Error();
2568 // Support: Safari <=7 only, IE <=10 - 11 only
2569 // Not all browsers generate the `stack` property for `new Error()`, see also #636
2578 return extractStacktrace(error, offset);
2581 var priorityCount = 0;
2582 var unitSampler = void 0;
2584 // This is a queue of functions that are tasks within a single test.
2585 // After tests are dequeued from config.queue they are expanded into
2586 // a set of tasks in this queue.
2590 * Advances the taskQueue to the next task. If the taskQueue is empty,
2591 * process the testQueue
2593 function advance() {
2596 if (!taskQueue.length && !config.blocking && !config.current) {
2602 * Advances the taskQueue with an increased depth
2604 function advanceTaskQueue() {
2606 config.depth = (config.depth || 0) + 1;
2608 processTaskQueue(start);
2614 * Process the first task on the taskQueue as a promise.
2615 * Each task is a function returned by https://github.com/qunitjs/qunit/blob/master/src/test.js#L381
2617 function processTaskQueue(start) {
2618 if (taskQueue.length && !config.blocking) {
2619 var elapsedTime = now() - start;
2621 if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) {
2622 var task = taskQueue.shift();
2623 Promise$1.resolve(task()).then(function () {
2624 if (!taskQueue.length) {
2627 processTaskQueue(start);
2631 setTimeout$1(advance);
2637 * Advance the testQueue to the next test to process. Call done() if testQueue completes.
2639 function advanceTestQueue() {
2640 if (!config.blocking && !config.queue.length && config.depth === 0) {
2645 var testTasks = config.queue.shift();
2646 addToTaskQueue(testTasks());
2648 if (priorityCount > 0) {
2656 * Enqueue the tasks for a test into the task queue.
2657 * @param {Array} tasksArray
2659 function addToTaskQueue(tasksArray) {
2660 taskQueue.push.apply(taskQueue, toConsumableArray(tasksArray));
2664 * Return the number of tasks remaining in the task queue to be processed.
2667 function taskQueueLength() {
2668 return taskQueue.length;
2672 * Adds a test to the TestQueue for execution.
2673 * @param {Function} testTasksFunc
2674 * @param {Boolean} prioritize
2675 * @param {String} seed
2677 function addToTestQueue(testTasksFunc, prioritize, seed) {
2679 config.queue.splice(priorityCount++, 0, testTasksFunc);
2682 unitSampler = unitSamplerGenerator(seed);
2685 // Insert into a random position after all prioritized items
2686 var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
2687 config.queue.splice(priorityCount + index, 0, testTasksFunc);
2689 config.queue.push(testTasksFunc);
2694 * Creates a seeded "sample
" generator which is used for randomizing tests.
2696 function unitSamplerGenerator(seed) {
2698 // 32-bit xorshift, requires only a nonzero seed
2699 // http://excamera.com/sphinx/article-xorshift.html
2700 var sample = parseInt(generateHash(seed), 16) || -1;
2701 return function () {
2702 sample ^= sample << 13;
2703 sample ^= sample >>> 17;
2704 sample ^= sample << 5;
2706 // ECMAScript has no unsigned number type
2708 sample += 0x100000000;
2711 return sample / 0x100000000;
2716 * This function is called when the ProcessingQueue is done processing all
2717 * items. It handles emitting the final run events.
2720 var storage = config.storage;
2722 ProcessingQueue.finished = true;
2724 var runtime = now() - config.started;
2725 var passed = config.stats.all - config.stats.bad;
2727 if (config.stats.all === 0) {
2729 if (config.filter && config.filter.length) {
2730 throw new Error("No tests matched the filter
\"" + config.filter + "\".");
2733 if (config.module && config.module.length) {
2734 throw new Error("No tests matched the module
\"" + config.module + "\".");
2737 if (config.moduleId && config.moduleId.length) {
2738 throw new Error("No tests matched the moduleId
\"" + config.moduleId + "\".");
2741 if (config.testId && config.testId.length) {
2742 throw new Error("No tests matched the testId
\"" + config.testId + "\".");
2745 throw new Error("No tests were run
.");
2748 emit("runEnd
", globalSuite.end(true));
2749 runLoggingCallbacks("done
", {
2751 failed: config.stats.bad,
2752 total: config.stats.all,
2754 }).then(function () {
2756 // Clear own storage items if all tests passed
2757 if (storage && config.stats.bad === 0) {
2758 for (var i = storage.length - 1; i >= 0; i--) {
2759 var key = storage.key(i);
2761 if (key.indexOf("qunit
-test
-") === 0) {
2762 storage.removeItem(key);
2769 var ProcessingQueue = {
2771 add: addToTestQueue,
2773 taskCount: taskQueueLength
2776 var TestReport = function () {
2777 function TestReport(name, suite, options) {
2778 classCallCheck(this, TestReport);
2781 this.suiteName = suite.name;
2782 this.fullName = suite.fullName.concat(name);
2784 this.assertions = [];
2786 this.skipped = !!options.skip;
2787 this.todo = !!options.todo;
2789 this.valid = options.valid;
2791 this._startTime = 0;
2794 suite.pushTest(this);
2797 createClass(TestReport, [{
2799 value: function start(recordTime) {
2801 this._startTime = performanceNow();
2803 performance.mark("qunit_test_start
");
2809 suiteName: this.suiteName,
2810 fullName: this.fullName.slice()
2815 value: function end(recordTime) {
2817 this._endTime = performanceNow();
2819 performance.mark("qunit_test_end
");
2821 var testName = this.fullName.join(" – ");
2823 measure("QUnit Test
: " + testName, "qunit_test_start
", "qunit_test_end
");
2827 return extend(this.start(), {
2828 runtime: this.getRuntime(),
2829 status: this.getStatus(),
2830 errors: this.getFailedAssertions(),
2831 assertions: this.getAssertions()
2835 key: "pushAssertion
",
2836 value: function pushAssertion(assertion) {
2837 this.assertions.push(assertion);
2841 value: function getRuntime() {
2842 return this._endTime - this._startTime;
2846 value: function getStatus() {
2851 var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
2855 } else if (this.todo) {
2862 key: "getFailedAssertions
",
2863 value: function getFailedAssertions() {
2864 return this.assertions.filter(function (assertion) {
2865 return !assertion.passed;
2869 key: "getAssertions
",
2870 value: function getAssertions() {
2871 return this.assertions.slice();
2874 // Remove actual and expected values from assertions. This is to prevent
2875 // leaking memory throughout a test suite.
2878 key: "slimAssertions
",
2879 value: function slimAssertions() {
2880 this.assertions = this.assertions.map(function (assertion) {
2881 delete assertion.actual;
2882 delete assertion.expected;
2890 var focused$1 = false;
2892 function Test(settings) {
2897 this.expected = null;
2898 this.assertions = [];
2900 this.module = config.currentModule;
2901 this.stack = sourceFromStacktrace(3);
2903 this.timeout = undefined;
2905 // If a module is skipped, all its tests and the tests of the child suites
2906 // should be treated as skipped even if they are defined as `only` or `todo`.
2907 // As for `todo` module, all its tests will be treated as `todo` except for
2908 // tests defined as `skip` which will be left intact.
2910 // So, if a test is defined as `todo` and is inside a skipped module, we should
2911 // then treat that test as if was defined as `skip`.
2912 if (this.module.skip) {
2913 settings.skip = true;
2914 settings.todo = false;
2916 // Skipped tests should be left intact
2917 } else if (this.module.todo && !settings.skip) {
2918 settings.todo = true;
2921 extend(this, settings);
2923 this.testReport = new TestReport(settings.testName, this.module.suiteReport, {
2924 todo: settings.todo,
2925 skip: settings.skip,
2929 // Register unique strings
2930 for (i = 0, l = this.module.tests; i < l.length; i++) {
2931 if (this.module.tests[i].name === this.testName) {
2932 this.testName += " ";
2936 this.testId = generateHash(this.module.name, this.testName);
2938 this.module.tests.push({
2939 name: this.testName,
2940 testId: this.testId,
2941 skip: !!settings.skip
2944 if (settings.skip) {
2946 // Skipped tests will fully ignore any sent callback
2947 this.callback = function () {};
2951 if (typeof this.callback !== "function") {
2952 var method = this.todo ? "todo
" : "test
";
2954 // eslint-disable-next-line max-len
2955 throw new TypeError("You must provide a
function as a test callback to QUnit
." + method + "(\"" + settings.testName + "\")");
2958 this.assert = new Assert(this);
2964 function getNotStartedModules(startModule) {
2965 var module = startModule,
2968 while (module && module.testsRun === 0) {
2969 modules.push(module);
2970 module = module.parentModule;
2973 // The above push modules from the child to the parent
2974 // return a reversed order with the top being the top most parent module
2975 return modules.reverse();
2979 before: function before() {
2982 var module = this.module,
2983 notStartedModules = getNotStartedModules(module);
2985 // ensure the callbacks are executed serially for each module
2986 var callbackPromises = notStartedModules.reduce(function (promiseChain, startModule) {
2987 return promiseChain.then(function () {
2988 startModule.stats = { all: 0, bad: 0, started: now() };
2989 emit("suiteStart
", startModule.suiteReport.start(true));
2990 return runLoggingCallbacks("moduleStart
", {
2991 name: startModule.name,
2992 tests: startModule.tests
2995 }, Promise$1.resolve([]));
2997 return callbackPromises.then(function () {
2998 config.current = _this;
3000 _this.testEnvironment = extend({}, module.testEnvironment);
3002 _this.started = now();
3003 emit("testStart
", _this.testReport.start(true));
3004 return runLoggingCallbacks("testStart
", {
3005 name: _this.testName,
3006 module: module.name,
3007 testId: _this.testId,
3008 previousFailure: _this.previousFailure
3009 }).then(function () {
3010 if (!config.pollution) {
3017 run: function run() {
3020 config.current = this;
3022 this.callbackStarted = now();
3024 if (config.notrycatch) {
3032 this.pushFailure("Died on test
#" + (this.assertions.length + 1) + " " + this.stack + ": " + (e.message || e), extractStacktrace(e, 0));
3034 // Else next test will carry the responsibility
3037 // Restart the tests if they're blocking
3038 if (config.blocking) {
3039 internalRecover(this);
3043 function runTest(test) {
3044 promise = test.callback.call(test.testEnvironment, test.assert);
3045 test.resolvePromise(promise);
3047 // If the test has a "lock
" on it, but the timeout is 0, then we push a
3048 // failure as the test should be synchronous.
3049 if (test.timeout === 0 && test.semaphore !== 0) {
3050 pushFailure("Test did not finish synchronously even though assert
.timeout( 0 ) was used
.", sourceFromStacktrace(2));
3055 after: function after() {
3059 queueHook: function queueHook(hook, hookName, hookOwner) {
3062 var callHook = function callHook() {
3063 var promise = hook.call(_this2.testEnvironment, _this2.assert);
3064 _this2.resolvePromise(promise, hookName);
3067 var runHook = function runHook() {
3068 if (hookName === "before
") {
3069 if (hookOwner.unskippedTestsRun !== 0) {
3073 _this2.preserveEnvironment = true;
3076 // The 'after' hook should only execute when there are not tests left and
3077 // when the 'after' and 'finish' tasks are the only tasks left to process
3078 if (hookName === "after
" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && (config.queue.length > 0 || ProcessingQueue.taskCount() > 2)) {
3082 config.current = _this2;
3083 if (config.notrycatch) {
3090 _this2.pushFailure(hookName + " failed on
" + _this2.testName + ": " + (error.message || error), extractStacktrace(error, 0));
3098 // Currently only used for module level hooks, can be used to add global level ones
3099 hooks: function hooks(handler) {
3102 function processHooks(test, module) {
3103 if (module.parentModule) {
3104 processHooks(test, module.parentModule);
3107 if (module.hooks[handler].length) {
3108 for (var i = 0; i < module.hooks[handler].length; i++) {
3109 hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
3114 // Hooks are ignored on skipped tests
3116 processHooks(this, this.module);
3123 finish: function finish() {
3124 config.current = this;
3126 // Release the test callback to ensure that anything referenced has been
3127 // released to be garbage collected.
3128 this.callback = undefined;
3130 if (this.steps.length) {
3131 var stepsList = this.steps.join(", ");
3132 this.pushFailure("Expected assert
.verifySteps() to be called before end
of test
" + ("after using assert
.step(). Unverified steps
: " + stepsList), this.stack);
3135 if (config.requireExpects && this.expected === null) {
3136 this.pushFailure("Expected number
of assertions to be defined
, but
expect() was
" + "not called
.", this.stack);
3137 } else if (this.expected !== null && this.expected !== this.assertions.length) {
3138 this.pushFailure("Expected
" + this.expected + " assertions
, but
" + this.assertions.length + " were run
", this.stack);
3139 } else if (this.expected === null && !this.assertions.length) {
3140 this.pushFailure("Expected at least one assertion
, but none were run
- call
" + "expect(0) to accept zero assertions
.", this.stack);
3144 module = this.module,
3145 moduleName = module.name,
3146 testName = this.testName,
3147 skipped = !!this.skip,
3150 storage = config.storage;
3152 this.runtime = now() - this.started;
3154 config.stats.all += this.assertions.length;
3155 module.stats.all += this.assertions.length;
3157 for (i = 0; i < this.assertions.length; i++) {
3158 if (!this.assertions[i].result) {
3165 notifyTestsRan(module, skipped);
3167 // Store result when possible
3170 storage.setItem("qunit
-test
-" + moduleName + "-" + testName, bad);
3172 storage.removeItem("qunit
-test
-" + moduleName + "-" + testName);
3176 // After emitting the js-reporters event we cleanup the assertion data to
3177 // avoid leaking it. It is not used by the legacy testDone callbacks.
3178 emit("testEnd
", this.testReport.end(true));
3179 this.testReport.slimAssertions();
3181 return runLoggingCallbacks("testDone
", {
3187 passed: this.assertions.length - bad,
3188 total: this.assertions.length,
3189 runtime: skipped ? 0 : this.runtime,
3191 // HTML Reporter use
3192 assertions: this.assertions,
3193 testId: this.testId,
3197 }).then(function () {
3198 if (module.testsRun === numberOfTests(module)) {
3199 var completedModules = [module];
3201 // Check if the parent modules, iteratively, are done. If that the case,
3202 // we emit the `suiteEnd` event and trigger `moduleDone` callback.
3203 var parent = module.parentModule;
3204 while (parent && parent.testsRun === numberOfTests(parent)) {
3205 completedModules.push(parent);
3206 parent = parent.parentModule;
3209 return completedModules.reduce(function (promiseChain, completedModule) {
3210 return promiseChain.then(function () {
3211 return logSuiteEnd(completedModule);
3213 }, Promise$1.resolve([]));
3215 }).then(function () {
3216 config.current = undefined;
3219 function logSuiteEnd(module) {
3221 // Reset `module.hooks` to ensure that anything referenced in these hooks
3222 // has been released to be garbage collected.
3225 emit("suiteEnd
", module.suiteReport.end(true));
3226 return runLoggingCallbacks("moduleDone
", {
3228 tests: module.tests,
3229 failed: module.stats.bad,
3230 passed: module.stats.all - module.stats.bad,
3231 total: module.stats.all,
3232 runtime: now() - module.stats.started
3237 preserveTestEnvironment: function preserveTestEnvironment() {
3238 if (this.preserveEnvironment) {
3239 this.module.testEnvironment = this.testEnvironment;
3240 this.testEnvironment = extend({}, this.module.testEnvironment);
3244 queue: function queue() {
3247 if (!this.valid()) {
3251 function runTest() {
3252 return [function () {
3253 return test.before();
3254 }].concat(toConsumableArray(test.hooks("before
")), [function () {
3255 test.preserveTestEnvironment();
3256 }], toConsumableArray(test.hooks("beforeEach
")), [function () {
3258 }], toConsumableArray(test.hooks("afterEach
").reverse()), toConsumableArray(test.hooks("after
").reverse()), [function () {
3261 return test.finish();
3265 var previousFailCount = config.storage && +config.storage.getItem("qunit
-test
-" + this.module.name + "-" + this.testName);
3267 // Prioritize previously failed tests, detected from storage
3268 var prioritize = config.reorder && !!previousFailCount;
3270 this.previousFailure = !!previousFailCount;
3272 ProcessingQueue.add(runTest, prioritize, config.seed);
3274 // If the queue has already finished, we manually process the new test
3275 if (ProcessingQueue.finished) {
3276 ProcessingQueue.advance();
3281 pushResult: function pushResult(resultInfo) {
3282 if (this !== config.current) {
3283 throw new Error("Assertion occurred after test had finished
.");
3286 // Destructure of resultInfo = { result, actual, expected, message, negative }
3289 module: this.module.name,
3290 name: this.testName,
3291 result: resultInfo.result,
3292 message: resultInfo.message,
3293 actual: resultInfo.actual,
3294 testId: this.testId,
3295 negative: resultInfo.negative || false,
3296 runtime: now() - this.started,
3300 if (hasOwn.call(resultInfo, "expected
")) {
3301 details.expected = resultInfo.expected;
3304 if (!resultInfo.result) {
3305 source = resultInfo.source || sourceFromStacktrace();
3308 details.source = source;
3312 this.logAssertion(details);
3314 this.assertions.push({
3315 result: !!resultInfo.result,
3316 message: resultInfo.message
3320 pushFailure: function pushFailure(message, source, actual) {
3321 if (!(this instanceof Test)) {
3322 throw new Error("pushFailure() assertion outside test context
, was
" + sourceFromStacktrace(2));
3327 message: message || "error
",
3328 actual: actual || null,
3334 * Log assertion details using both the old QUnit.log interface and
3335 * QUnit.on( "assertion
" ) interface.
3339 logAssertion: function logAssertion(details) {
3340 runLoggingCallbacks("log
", details);
3343 passed: details.result,
3344 actual: details.actual,
3345 expected: details.expected,
3346 message: details.message,
3347 stack: details.source,
3350 this.testReport.pushAssertion(assertion);
3351 emit("assertion
", assertion);
3355 resolvePromise: function resolvePromise(promise, phase) {
3360 if (promise != null) {
3361 then = promise.then;
3362 if (objectType(then) === "function") {
3363 resume = internalStop(test);
3364 if (config.notrycatch) {
3365 then.call(promise, function () {
3369 then.call(promise, function () {
3371 }, function (error) {
3372 message = "Promise rejected
" + (!phase ? "during
" : phase.replace(/Each$/, "")) + " \"" + test.testName + "\": " + (error && error.message || error);
3373 test.pushFailure(message, extractStacktrace(error, 0));
3375 // Else next test will carry the responsibility
3379 internalRecover(test);
3386 valid: function valid() {
3387 var filter = config.filter,
3388 regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter),
3389 module = config.module && config.module.toLowerCase(),
3390 fullName = this.module.name + ": " + this.testName;
3392 function moduleChainNameMatch(testModule) {
3393 var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
3394 if (testModuleName === module) {
3396 } else if (testModule.parentModule) {
3397 return moduleChainNameMatch(testModule.parentModule);
3403 function moduleChainIdMatch(testModule) {
3404 return inArray(testModule.moduleId, config.moduleId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule);
3407 // Internally-generated tests are always valid
3408 if (this.callback && this.callback.validTest) {
3412 if (config.moduleId && config.moduleId.length > 0 && !moduleChainIdMatch(this.module)) {
3417 if (config.testId && config.testId.length > 0 && !inArray(this.testId, config.testId)) {
3422 if (module && !moduleChainNameMatch(this.module)) {
3430 return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
3433 regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
3434 var regex = new RegExp(pattern, flags);
3435 var match = regex.test(fullName);
3437 return match !== exclude;
3440 stringFilter: function stringFilter(filter, fullName) {
3441 filter = filter.toLowerCase();
3442 fullName = fullName.toLowerCase();
3444 var include = filter.charAt(0) !== "!";
3446 filter = filter.slice(1);
3449 // If the filter matches, we need to honour include
3450 if (fullName.indexOf(filter) !== -1) {
3454 // Otherwise, do the opposite
3459 function pushFailure() {
3460 if (!config.current) {
3461 throw new Error("pushFailure() assertion outside test context
, in " + sourceFromStacktrace(2));
3464 // Gets current test obj
3465 var currentTest = config.current;
3467 return currentTest.pushFailure.apply(currentTest, arguments);
3470 function saveGlobal() {
3471 config.pollution = [];
3473 if (config.noglobals) {
3474 for (var key in global$1) {
3475 if (hasOwn.call(global$1, key)) {
3477 // In Opera sometimes DOM element ids show up here, ignore them
3478 if (/^qunit-test-output/.test(key)) {
3481 config.pollution.push(key);
3487 function checkPollution() {
3490 old = config.pollution;
3494 newGlobals = diff(config.pollution, old);
3495 if (newGlobals.length > 0) {
3496 pushFailure("Introduced global
variable(s
): " + newGlobals.join(", "));
3499 deletedGlobals = diff(old, config.pollution);
3500 if (deletedGlobals.length > 0) {
3501 pushFailure("Deleted global
variable(s
): " + deletedGlobals.join(", "));
3505 // Will be exposed as QUnit.test
3506 function test(testName, callback) {
3511 var newTest = new Test({
3519 function todo(testName, callback) {
3524 var newTest = new Test({
3533 // Will be exposed as QUnit.skip
3534 function skip(testName) {
3539 var test = new Test({
3547 // Will be exposed as QUnit.only
3548 function only(testName, callback) {
3553 config.queue.length = 0;
3556 var newTest = new Test({
3564 // Put a hold on processing and return a function that will release it.
3565 function internalStop(test) {
3566 test.semaphore += 1;
3567 config.blocking = true;
3569 // Set a recovery timeout, if so configured.
3570 if (defined.setTimeout) {
3571 var timeoutDuration = void 0;
3573 if (typeof test.timeout === "number
") {
3574 timeoutDuration = test.timeout;
3575 } else if (typeof config.testTimeout === "number
") {
3576 timeoutDuration = config.testTimeout;
3579 if (typeof timeoutDuration === "number
" && timeoutDuration > 0) {
3580 clearTimeout(config.timeout);
3581 config.timeout = setTimeout$1(function () {
3582 pushFailure("Test took longer than
" + timeoutDuration + "ms
; test timed out
.", sourceFromStacktrace(2));
3583 internalRecover(test);
3584 }, timeoutDuration);
3588 var released = false;
3589 return function resume() {
3595 test.semaphore -= 1;
3596 internalStart(test);
3600 // Forcefully release all processing holds.
3601 function internalRecover(test) {
3603 internalStart(test);
3606 // Release a processing hold, scheduling a resumption attempt if no holds remain.
3607 function internalStart(test) {
3609 // If semaphore is non-numeric, throw error
3610 if (isNaN(test.semaphore)) {
3613 pushFailure("Invalid value on test
.semaphore
", sourceFromStacktrace(2));
3617 // Don't start until equal number of stop-calls
3618 if (test.semaphore > 0) {
3622 // Throw an Error if start is called more often than stop
3623 if (test.semaphore < 0) {
3626 pushFailure("Tried to restart test
while already
started (test
's semaphore was 0 already)", sourceFromStacktrace(2));
3630 // Add a slight delay to allow more assertions etc.
3631 if (defined.setTimeout) {
3632 if (config.timeout) {
3633 clearTimeout(config.timeout);
3635 config.timeout = setTimeout$1(function () {
3636 if (test.semaphore > 0) {
3640 if (config.timeout) {
3641 clearTimeout(config.timeout);
3651 function collectTests(module) {
3652 var tests = [].concat(module.tests);
3653 var modules = [].concat(toConsumableArray(module.childModules));
3655 // Do a breadth-first traversal of the child modules
3656 while (modules.length) {
3657 var nextModule = modules.shift();
3658 tests.push.apply(tests, nextModule.tests);
3659 modules.push.apply(modules, toConsumableArray(nextModule.childModules));
3665 function numberOfTests(module) {
3666 return collectTests(module).length;
3669 function numberOfUnskippedTests(module) {
3670 return collectTests(module).filter(function (test) {
3675 function notifyTestsRan(module, skipped) {
3678 module.unskippedTestsRun++;
3680 while (module = module.parentModule) {
3683 module.unskippedTestsRun++;
3688 var Assert = function () {
3689 function Assert(testContext) {
3690 classCallCheck(this, Assert);
3692 this.test = testContext;
3697 createClass(Assert, [{
3699 value: function timeout(duration) {
3700 if (typeof duration !== "number") {
3701 throw new Error("You must pass a number as the duration to assert.timeout");
3704 this.test.timeout = duration;
3707 // Documents a "step", which is a string value, in a test as a passing assertion
3711 value: function step(message) {
3712 var assertionMessage = message;
3713 var result = !!message;
3715 this.test.steps.push(message);
3717 if (objectType(message) === "undefined" || message === "") {
3718 assertionMessage = "You must provide a message to assert.step";
3719 } else if (objectType(message) !== "string") {
3720 assertionMessage = "You must provide a string value to assert.step";
3726 message: assertionMessage
3730 // Verifies the steps in a test match a given array of string values
3734 value: function verifySteps(steps, message) {
3736 // Since the steps array is just string values, we can clone with slice
3737 var actualStepsClone = this.test.steps.slice();
3738 this.deepEqual(actualStepsClone, steps, message);
3739 this.test.steps.length = 0;
3742 // Specify the number of expected assertions to guarantee that failed test
3743 // (no assertions are run at all) don't slip through
.
3747 value
: function expect(asserts
) {
3748 if (arguments
.length
=== 1) {
3749 this.test
.expected
= asserts
;
3751 return this.test
.expected
;
3755 // Put a hold on processing and return a function that will release it a maximum of once.
3759 value
: function async(count
) {
3760 var test
$$1 = this.test
;
3763 acceptCallCount
= count
;
3765 if (typeof acceptCallCount
=== "undefined") {
3766 acceptCallCount
= 1;
3769 var resume
= internalStop(test
$$1);
3771 return function done() {
3772 if (config
.current
!== test
$$1) {
3773 throw Error("assert.async callback called after test finished.");
3777 test
$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2));
3781 acceptCallCount
-= 1;
3782 if (acceptCallCount
> 0) {
3791 // Exports test.push() to the user API
3792 // Alias of pushResult.
3796 value
: function push(result
, actual
, expected
, message
, negative
) {
3797 Logger
.warn("assert.push is deprecated and will be removed in QUnit 3.0." + " Please use assert.pushResult instead (https://api.qunitjs.com/assert/pushResult).");
3799 var currentAssert
= this instanceof Assert
? this : config
.current
.assert
;
3800 return currentAssert
.pushResult({
3810 value
: function pushResult(resultInfo
) {
3812 // Destructure of resultInfo = { result, actual, expected, message, negative }
3814 var currentTest
= assert
instanceof Assert
&& assert
.test
|| config
.current
;
3816 // Backwards compatibility fix.
3817 // Allows the direct use of global exported assertions and QUnit.assert.*
3818 // Although, it's use is not recommended as it can leak assertions
3819 // to other tests from async tests, because we only get a reference to the current test,
3820 // not exactly the test where assertion were intended to be called.
3822 throw new Error("assertion outside test context, in " + sourceFromStacktrace(2));
3825 if (!(assert
instanceof Assert
)) {
3826 assert
= currentTest
.assert
;
3829 return assert
.test
.pushResult(resultInfo
);
3833 value
: function ok(result
, message
) {
3835 message
= result
? "okay" : "failed, expected argument to be truthy, was: " + dump
.parse(result
);
3847 value
: function notOk(result
, message
) {
3849 message
= !result
? "okay" : "failed, expected argument to be falsy, was: " + dump
.parse(result
);
3861 value
: function equal(actual
, expected
, message
) {
3863 // eslint-disable-next-line eqeqeq
3864 var result
= expected
== actual
;
3875 value
: function notEqual(actual
, expected
, message
) {
3877 // eslint-disable-next-line eqeqeq
3878 var result
= expected
!= actual
;
3890 value
: function propEqual(actual
, expected
, message
) {
3891 actual
= objectValues(actual
);
3892 expected
= objectValues(expected
);
3895 result
: equiv(actual
, expected
),
3902 key
: "notPropEqual",
3903 value
: function notPropEqual(actual
, expected
, message
) {
3904 actual
= objectValues(actual
);
3905 expected
= objectValues(expected
);
3908 result
: !equiv(actual
, expected
),
3917 value
: function deepEqual(actual
, expected
, message
) {
3919 result
: equiv(actual
, expected
),
3926 key
: "notDeepEqual",
3927 value
: function notDeepEqual(actual
, expected
, message
) {
3929 result
: !equiv(actual
, expected
),
3938 value
: function strictEqual(actual
, expected
, message
) {
3940 result
: expected
=== actual
,
3947 key
: "notStrictEqual",
3948 value
: function notStrictEqual(actual
, expected
, message
) {
3950 result
: expected
!== actual
,
3959 value
: function throws(block
, expected
, message
) {
3960 var actual
= void 0,
3963 var currentTest
= this instanceof Assert
&& this.test
|| config
.current
;
3965 // 'expected' is optional unless doing string comparison
3966 if (objectType(expected
) === "string") {
3967 if (message
== null) {
3971 throw new Error("throws/raises does not accept a string value for the expected argument.\n" + "Use a non-string object value (e.g. regExp) instead if it's necessary.");
3975 currentTest
.ignoreGlobalErrors
= true;
3977 block
.call(currentTest
.testEnvironment
);
3981 currentTest
.ignoreGlobalErrors
= false;
3984 var expectedType
= objectType(expected
);
3986 // We don't want to validate thrown error
3990 // Expected is a regexp
3991 } else if (expectedType
=== "regexp") {
3992 result
= expected
.test(errorString(actual
));
3994 // Log the string form of the regexp
3995 expected
= String(expected
);
3997 // Expected is a constructor, maybe an Error constructor
3998 } else if (expectedType
=== "function" && actual
instanceof expected
) {
4001 // Expected is an Error object
4002 } else if (expectedType
=== "object") {
4003 result
= actual
instanceof expected
.constructor && actual
.name
=== expected
.name
&& actual
.message
=== expected
.message
;
4005 // Log the string form of the Error object
4006 expected
= errorString(expected
);
4008 // Expected is a validation function which returns true if validation passed
4009 } else if (expectedType
=== "function" && expected
.call({}, actual
) === true) {
4015 currentTest
.assert
.pushResult({
4018 // undefined if it didn't throw
4019 actual
: actual
&& errorString(actual
),
4026 value
: function rejects(promise
, expected
, message
) {
4029 var currentTest
= this instanceof Assert
&& this.test
|| config
.current
;
4031 // 'expected' is optional unless doing string comparison
4032 if (objectType(expected
) === "string") {
4033 if (message
=== undefined) {
4035 expected
= undefined;
4037 message
= "assert.rejects does not accept a string value for the expected " + "argument.\nUse a non-string object value (e.g. validator function) instead " + "if necessary.";
4039 currentTest
.assert
.pushResult({
4048 var then
= promise
&& promise
.then
;
4049 if (objectType(then
) !== "function") {
4050 var _message
= "The value provided to `assert.rejects` in " + "\"" + currentTest
.testName
+ "\" was not a promise.";
4052 currentTest
.assert
.pushResult({
4061 var done
= this.async();
4063 return then
.call(promise
, function handleFulfillment() {
4064 var message
= "The promise returned by the `assert.rejects` callback in " + "\"" + currentTest
.testName
+ "\" did not reject.";
4066 currentTest
.assert
.pushResult({
4073 }, function handleRejection(actual
) {
4074 var expectedType
= objectType(expected
);
4076 // We don't want to validate
4077 if (expected
=== undefined) {
4080 // Expected is a regexp
4081 } else if (expectedType
=== "regexp") {
4082 result
= expected
.test(errorString(actual
));
4084 // Log the string form of the regexp
4085 expected
= String(expected
);
4087 // Expected is a constructor, maybe an Error constructor
4088 } else if (expectedType
=== "function" && actual
instanceof expected
) {
4091 // Expected is an Error object
4092 } else if (expectedType
=== "object") {
4093 result
= actual
instanceof expected
.constructor && actual
.name
=== expected
.name
&& actual
.message
=== expected
.message
;
4095 // Log the string form of the Error object
4096 expected
= errorString(expected
);
4098 // Expected is a validation function which returns true if validation passed
4100 if (expectedType
=== "function") {
4101 result
= expected
.call({}, actual
) === true;
4104 // Expected is some other invalid type
4107 message
= "invalid expected value provided to `assert.rejects` " + "callback in \"" + currentTest
.testName
+ "\": " + expectedType
+ ".";
4111 currentTest
.assert
.pushResult({
4114 // leave rejection value of undefined as-is
4115 actual
: actual
&& errorString(actual
),
4127 // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
4128 // Known to us are: Closure Compiler, Narwhal
4129 // eslint-disable-next-line dot-notation
4132 Assert
.prototype.raises
= Assert
.prototype["throws"];
4135 * Converts an error into a simple string for comparisons.
4137 * @param {Error|Object} error
4140 function errorString(error
) {
4141 var resultErrorString
= error
.toString();
4143 // If the error wasn't a subclass of Error but something like
4144 // an object literal with name and message properties...
4145 if (resultErrorString
.substring(0, 7) === "[object") {
4146 var name
= error
.name
? error
.name
.toString() : "Error";
4147 var message
= error
.message
? error
.message
.toString() : "";
4149 if (name
&& message
) {
4150 return name
+ ": " + message
;
4153 } else if (message
) {
4159 return resultErrorString
;
4163 /* global module, exports, define */
4164 function exportQUnit(QUnit
) {
4166 if (defined
.document
) {
4168 // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
4169 if (window
$1.QUnit
&& window
$1.QUnit
.version
) {
4170 throw new Error("QUnit has already been defined.");
4173 window
$1.QUnit
= QUnit
;
4177 if (typeof module
!== "undefined" && module
&& module
.exports
) {
4178 module
.exports
= QUnit
;
4180 // For consistency with CommonJS environments' exports
4181 module
.exports
.QUnit
= QUnit
;
4184 // For CommonJS with exports, but without module.exports, like Rhino
4185 if (typeof exports
!== "undefined" && exports
) {
4186 exports
.QUnit
= QUnit
;
4189 if (typeof define
=== "function" && define
.amd
) {
4190 define(function () {
4193 QUnit
.config
.autostart
= false;
4196 // For Web/Service Workers
4197 if (self
$1 && self
$1.WorkerGlobalScope
&& self
$1 instanceof self
$1.WorkerGlobalScope
) {
4198 self
$1.QUnit
= QUnit
;
4202 // Handle an unhandled exception. By convention, returns true if further
4203 // error handling should be suppressed and false otherwise.
4204 // In this case, we will only suppress further error handling if the
4205 // "ignoreGlobalErrors" configuration option is enabled.
4206 function onError(error
) {
4207 for (var _len
= arguments
.length
, args
= Array(_len
> 1 ? _len
- 1 : 0), _key
= 1; _key
< _len
; _key
++) {
4208 args
[_key
- 1] = arguments
[_key
];
4211 if (config
.current
) {
4212 if (config
.current
.ignoreGlobalErrors
) {
4215 pushFailure
.apply(undefined, [error
.message
, error
.stacktrace
|| error
.fileName
+ ":" + error
.lineNumber
].concat(args
));
4217 test("global failure", extend(function () {
4218 pushFailure
.apply(undefined, [error
.message
, error
.stacktrace
|| error
.fileName
+ ":" + error
.lineNumber
].concat(args
));
4219 }, { validTest
: true }));
4225 // Handle an unhandled rejection
4226 function onUnhandledRejection(reason
) {
4229 message
: reason
.message
|| "error",
4231 source
: reason
.stack
|| sourceFromStacktrace(3)
4234 var currentTest
= config
.current
;
4236 currentTest
.assert
.pushResult(resultInfo
);
4238 test("global failure", extend(function (assert
) {
4239 assert
.pushResult(resultInfo
);
4240 }, { validTest
: true }));
4245 var globalSuite
= new SuiteReport();
4247 // The initial "currentModule" represents the global (or top-level) module that
4248 // is not explicitly defined by the user, therefore we add the "globalSuite" to
4249 // it since each module has a suiteReport associated with it.
4250 config
.currentModule
.suiteReport
= globalSuite
;
4252 var globalStartCalled
= false;
4253 var runStarted
= false;
4255 // Figure out if we're running the tests from a server or not
4256 QUnit
.isLocal
= !(defined
.document
&& window
$1.location
.protocol
!== "file:");
4258 // Expose the current QUnit version
4259 QUnit
.version
= "2.9.1";
4274 start
: function start(count
) {
4275 var globalStartAlreadyCalled
= globalStartCalled
;
4277 if (!config
.current
) {
4278 globalStartCalled
= true;
4281 throw new Error("Called start() while test already started running");
4282 } else if (globalStartAlreadyCalled
|| count
> 1) {
4283 throw new Error("Called start() outside of a test context too many times");
4284 } else if (config
.autostart
) {
4285 throw new Error("Called start() outside of a test context when " + "QUnit.config.autostart was true");
4286 } else if (!config
.pageLoaded
) {
4288 // The page isn't completely loaded yet, so we set autostart and then
4289 // load if we're in Node or wait for the browser's load event.
4290 config
.autostart
= true;
4292 // Starts from Node even if .load was not previously called. We still return
4293 // early otherwise we'll wind up "beginning" twice.
4294 if (!defined
.document
) {
4301 throw new Error("QUnit.start cannot be called inside a test context.");
4311 objectType
: objectType
,
4315 load
: function load() {
4316 config
.pageLoaded
= true;
4318 // Initialize the configuration options
4320 stats
: { all
: 0, bad
: 0 },
4328 config
.blocking
= false;
4330 if (config
.autostart
) {
4336 stack
: function stack(offset
) {
4337 offset
= (offset
|| 0) + 2;
4338 return sourceFromStacktrace(offset
);
4343 onUnhandledRejection
: onUnhandledRejection
4346 QUnit
.pushFailure
= pushFailure
;
4347 QUnit
.assert
= Assert
.prototype;
4348 QUnit
.equiv
= equiv
;
4351 registerLoggingCallbacks(QUnit
);
4353 function scheduleBegin() {
4357 // Add a slight delay to allow definition of more modules and tests.
4358 if (defined
.setTimeout
) {
4359 setTimeout
$1(function () {
4367 function unblockAndAdvanceQueue() {
4368 config
.blocking
= false;
4369 ProcessingQueue
.advance();
4377 // If the test run hasn't officially begun yet
4378 if (!config
.started
) {
4380 // Record the time of the test run's beginning
4381 config
.started
= now();
4383 // Delete the loose unnamed module if unused.
4384 if (config
.modules
[0].name
=== "" && config
.modules
[0].tests
.length
=== 0) {
4385 config
.modules
.shift();
4388 // Avoid unnecessary information by not logging modules' test environments
4389 for (i
= 0, l
= config
.modules
.length
; i
< l
; i
++) {
4391 name
: config
.modules
[i
].name
,
4392 tests
: config
.modules
[i
].tests
4396 // The test run is officially beginning now
4397 emit("runStart", globalSuite
.start(true));
4398 runLoggingCallbacks("begin", {
4399 totalTests
: Test
.count
,
4401 }).then(unblockAndAdvanceQueue
);
4403 unblockAndAdvanceQueue();
4411 if (typeof window
$1 === "undefined" || typeof document
$1 === "undefined") {
4415 var config
= QUnit
.config
,
4416 hasOwn
= Object
.prototype.hasOwnProperty
;
4418 // Stores fixture HTML for resetting later
4419 function storeFixture() {
4421 // Avoid overwriting user-defined values
4422 if (hasOwn
.call(config
, "fixture")) {
4426 var fixture
= document
$1.getElementById("qunit-fixture");
4428 config
.fixture
= fixture
.cloneNode(true);
4432 QUnit
.begin(storeFixture
);
4434 // Resets the fixture DOM element if available.
4435 function resetFixture() {
4436 if (config
.fixture
== null) {
4440 var fixture
= document
$1.getElementById("qunit-fixture");
4441 var resetFixtureType
= _typeof(config
.fixture
);
4442 if (resetFixtureType
=== "string") {
4444 // support user defined values for `config.fixture`
4445 var newFixture
= document
$1.createElement("div");
4446 newFixture
.setAttribute("id", "qunit-fixture");
4447 newFixture
.innerHTML
= config
.fixture
;
4448 fixture
.parentNode
.replaceChild(newFixture
, fixture
);
4450 var clonedFixture
= config
.fixture
.cloneNode(true);
4451 fixture
.parentNode
.replaceChild(clonedFixture
, fixture
);
4455 QUnit
.testStart(resetFixture
);
4460 // Only interact with URLs via window.location
4461 var location
= typeof window
$1 !== "undefined" && window
$1.location
;
4466 var urlParams
= getUrlParams();
4468 QUnit
.urlParams
= urlParams
;
4470 // Match module/test by inclusion in an array
4471 QUnit
.config
.moduleId
= [].concat(urlParams
.moduleId
|| []);
4472 QUnit
.config
.testId
= [].concat(urlParams
.testId
|| []);
4474 // Exact case-insensitive match of the module name
4475 QUnit
.config
.module
= urlParams
.module
;
4477 // Regular expression or case-insenstive substring match against "moduleName: testName"
4478 QUnit
.config
.filter
= urlParams
.filter
;
4480 // Test order randomization
4481 if (urlParams
.seed
=== true) {
4483 // Generate a random seed if the option is specified without a value
4484 QUnit
.config
.seed
= Math
.random().toString(36).slice(2);
4485 } else if (urlParams
.seed
) {
4486 QUnit
.config
.seed
= urlParams
.seed
;
4489 // Add URL-parameter-mapped config values with UI form rendering data
4490 QUnit
.config
.urlConfig
.push({
4492 label
: "Hide passed tests",
4493 tooltip
: "Only show tests and assertions that fail. Stored as query-strings."
4496 label
: "Check for Globals",
4497 tooltip
: "Enabling this will test if any test introduces new properties on the " + "global object (`window` in Browsers). Stored as query-strings."
4500 label
: "No try-catch",
4501 tooltip
: "Enabling this will run tests outside of a try-catch block. Makes debugging " + "exceptions in IE reasonable. Stored as query-strings."
4504 QUnit
.begin(function () {
4507 urlConfig
= QUnit
.config
.urlConfig
;
4509 for (i
= 0; i
< urlConfig
.length
; i
++) {
4511 // Options can be either strings or objects with nonempty "id" properties
4512 option
= QUnit
.config
.urlConfig
[i
];
4513 if (typeof option
!== "string") {
4517 if (QUnit
.config
[option
] === undefined) {
4518 QUnit
.config
[option
] = urlParams
[option
];
4523 function getUrlParams() {
4524 var i
, param
, name
, value
;
4525 var urlParams
= Object
.create(null);
4526 var params
= location
.search
.slice(1).split("&");
4527 var length
= params
.length
;
4529 for (i
= 0; i
< length
; i
++) {
4531 param
= params
[i
].split("=");
4532 name
= decodeQueryParam(param
[0]);
4534 // Allow just a key to turn on a flag, e.g., test.html?noglobals
4535 value
= param
.length
=== 1 || decodeQueryParam(param
.slice(1).join("="));
4536 if (name
in urlParams
) {
4537 urlParams
[name
] = [].concat(urlParams
[name
], value
);
4539 urlParams
[name
] = value
;
4547 function decodeQueryParam(param
) {
4548 return decodeURIComponent(param
.replace(/\+/g, "%20"));
4559 // Escape text for attribute or text content.
4560 function escapeText(s
) {
4566 // Both single quotes and double quotes (for attributes)
4567 return s
.replace(/['"<>&]/g, function (s
) {
4585 // Don't load the HTML Reporter on non-browser environments
4586 if (typeof window
$1 === "undefined" || !window
$1.document
) {
4590 var config
= QUnit
.config
,
4592 document
= window
$1.document
,
4593 collapseNext
= false,
4594 hasOwn
= Object
.prototype.hasOwnProperty
,
4595 unfilteredUrl
= setUrl({ filter
: undefined, module
: undefined,
4596 moduleId
: undefined, testId
: undefined }),
4599 function addEvent(elem
, type
, fn
) {
4600 elem
.addEventListener(type
, fn
, false);
4603 function removeEvent(elem
, type
, fn
) {
4604 elem
.removeEventListener(type
, fn
, false);
4607 function addEvents(elems
, type
, fn
) {
4608 var i
= elems
.length
;
4610 addEvent(elems
[i
], type
, fn
);
4614 function hasClass(elem
, name
) {
4615 return (" " + elem
.className
+ " ").indexOf(" " + name
+ " ") >= 0;
4618 function addClass(elem
, name
) {
4619 if (!hasClass(elem
, name
)) {
4620 elem
.className
+= (elem
.className
? " " : "") + name
;
4624 function toggleClass(elem
, name
, force
) {
4625 if (force
|| typeof force
=== "undefined" && !hasClass(elem
, name
)) {
4626 addClass(elem
, name
);
4628 removeClass(elem
, name
);
4632 function removeClass(elem
, name
) {
4633 var set = " " + elem
.className
+ " ";
4635 // Class name may appear multiple times
4636 while (set.indexOf(" " + name
+ " ") >= 0) {
4637 set = set.replace(" " + name
+ " ", " ");
4640 // Trim for prettiness
4641 elem
.className
= typeof set.trim
=== "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
4645 return document
.getElementById
&& document
.getElementById(name
);
4648 function abortTests() {
4649 var abortButton
= id("qunit-abort-tests-button");
4651 abortButton
.disabled
= true;
4652 abortButton
.innerHTML
= "Aborting...";
4654 QUnit
.config
.queue
.length
= 0;
4658 function interceptNavigation(ev
) {
4661 if (ev
&& ev
.preventDefault
) {
4662 ev
.preventDefault();
4668 function getUrlConfigHtml() {
4675 urlConfig
= config
.urlConfig
,
4678 for (i
= 0; i
< urlConfig
.length
; i
++) {
4680 // Options can be either strings or objects with nonempty "id" properties
4681 val
= config
.urlConfig
[i
];
4682 if (typeof val
=== "string") {
4689 escaped
= escapeText(val
.id
);
4690 escapedTooltip
= escapeText(val
.tooltip
);
4692 if (!val
.value
|| typeof val
.value
=== "string") {
4693 urlConfigHtml
+= "<label for='qunit-urlconfig-" + escaped
+ "' title='" + escapedTooltip
+ "'><input id='qunit-urlconfig-" + escaped
+ "' name='" + escaped
+ "' type='checkbox'" + (val
.value
? " value='" + escapeText(val
.value
) + "'" : "") + (config
[val
.id
] ? " checked='checked'" : "") + " title='" + escapedTooltip
+ "' />" + escapeText(val
.label
) + "</label>";
4695 urlConfigHtml
+= "<label for='qunit-urlconfig-" + escaped
+ "' title='" + escapedTooltip
+ "'>" + val
.label
+ ": </label><select id='qunit-urlconfig-" + escaped
+ "' name='" + escaped
+ "' title='" + escapedTooltip
+ "'><option></option>";
4697 if (QUnit
.is("array", val
.value
)) {
4698 for (j
= 0; j
< val
.value
.length
; j
++) {
4699 escaped
= escapeText(val
.value
[j
]);
4700 urlConfigHtml
+= "<option value='" + escaped
+ "'" + (config
[val
.id
] === val
.value
[j
] ? (selection
= true) && " selected='selected'" : "") + ">" + escaped
+ "</option>";
4703 for (j
in val
.value
) {
4704 if (hasOwn
.call(val
.value
, j
)) {
4705 urlConfigHtml
+= "<option value='" + escapeText(j
) + "'" + (config
[val
.id
] === j
? (selection
= true) && " selected='selected'" : "") + ">" + escapeText(val
.value
[j
]) + "</option>";
4709 if (config
[val
.id
] && !selection
) {
4710 escaped
= escapeText(config
[val
.id
]);
4711 urlConfigHtml
+= "<option value='" + escaped
+ "' selected='selected' disabled='disabled'>" + escaped
+ "</option>";
4713 urlConfigHtml
+= "</select>";
4717 return urlConfigHtml
;
4720 // Handle "click" events on toolbar checkboxes and "change" for select menus.
4721 // Updates the URL with the new state of `config.urlConfig` values.
4722 function toolbarChanged() {
4729 // Detect if field is a select menu or a checkbox
4730 if ("selectedIndex" in field
) {
4731 value
= field
.options
[field
.selectedIndex
].value
|| undefined;
4733 value
= field
.checked
? field
.defaultValue
|| true : undefined;
4736 params
[field
.name
] = value
;
4737 updatedUrl
= setUrl(params
);
4739 // Check if we can apply the change without a page refresh
4740 if ("hidepassed" === field
.name
&& "replaceState" in window
$1.history
) {
4741 QUnit
.urlParams
[field
.name
] = value
;
4742 config
[field
.name
] = value
|| false;
4743 tests
= id("qunit-tests");
4745 var length
= tests
.children
.length
;
4746 var children
= tests
.children
;
4748 if (field
.checked
) {
4749 for (var i
= 0; i
< length
; i
++) {
4750 var test
= children
[i
];
4752 if (test
&& test
.className
.indexOf("pass") > -1) {
4753 hiddenTests
.push(test
);
4757 var _iteratorNormalCompletion
= true;
4758 var _didIteratorError
= false;
4759 var _iteratorError
= undefined;
4762 for (var _iterator
= hiddenTests
[Symbol
.iterator
](), _step
; !(_iteratorNormalCompletion
= (_step
= _iterator
.next()).done
); _iteratorNormalCompletion
= true) {
4763 var hiddenTest
= _step
.value
;
4765 tests
.removeChild(hiddenTest
);
4768 _didIteratorError
= true;
4769 _iteratorError
= err
;
4772 if (!_iteratorNormalCompletion
&& _iterator
.return) {
4776 if (_didIteratorError
) {
4777 throw _iteratorError
;
4782 while ((test
= hiddenTests
.pop()) != null) {
4783 tests
.appendChild(test
);
4787 window
$1.history
.replaceState(null, "", updatedUrl
);
4789 window
$1.location
= updatedUrl
;
4793 function setUrl(params
) {
4798 location
= window
$1.location
;
4800 params
= QUnit
.extend(QUnit
.extend({}, QUnit
.urlParams
), params
);
4802 for (key
in params
) {
4804 // Skip inherited or undefined properties
4805 if (hasOwn
.call(params
, key
) && params
[key
] !== undefined) {
4807 // Output a parameter for each value of this key
4808 // (but usually just one)
4809 arrValue
= [].concat(params
[key
]);
4810 for (i
= 0; i
< arrValue
.length
; i
++) {
4811 querystring
+= encodeURIComponent(key
);
4812 if (arrValue
[i
] !== true) {
4813 querystring
+= "=" + encodeURIComponent(arrValue
[i
]);
4819 return location
.protocol
+ "//" + location
.host
+ location
.pathname
+ querystring
.slice(0, -1);
4822 function applyUrlParams() {
4824 selectedModules
= [],
4825 modulesList
= id("qunit-modulefilter-dropdown-list").getElementsByTagName("input"),
4826 filter
= id("qunit-filter-input").value
;
4828 for (i
= 0; i
< modulesList
.length
; i
++) {
4829 if (modulesList
[i
].checked
) {
4830 selectedModules
.push(modulesList
[i
].value
);
4834 window
$1.location
= setUrl({
4835 filter
: filter
=== "" ? undefined : filter
,
4836 moduleId
: selectedModules
.length
=== 0 ? undefined : selectedModules
,
4838 // Remove module and testId filter
4844 function toolbarUrlConfigContainer() {
4845 var urlConfigContainer
= document
.createElement("span");
4847 urlConfigContainer
.innerHTML
= getUrlConfigHtml();
4848 addClass(urlConfigContainer
, "qunit-url-config");
4850 addEvents(urlConfigContainer
.getElementsByTagName("input"), "change", toolbarChanged
);
4851 addEvents(urlConfigContainer
.getElementsByTagName("select"), "change", toolbarChanged
);
4853 return urlConfigContainer
;
4856 function abortTestsButton() {
4857 var button
= document
.createElement("button");
4858 button
.id
= "qunit-abort-tests-button";
4859 button
.innerHTML
= "Abort";
4860 addEvent(button
, "click", abortTests
);
4864 function toolbarLooseFilter() {
4865 var filter
= document
.createElement("form"),
4866 label
= document
.createElement("label"),
4867 input
= document
.createElement("input"),
4868 button
= document
.createElement("button");
4870 addClass(filter
, "qunit-filter");
4872 label
.innerHTML
= "Filter: ";
4874 input
.type
= "text";
4875 input
.value
= config
.filter
|| "";
4876 input
.name
= "filter";
4877 input
.id
= "qunit-filter-input";
4879 button
.innerHTML
= "Go";
4881 label
.appendChild(input
);
4883 filter
.appendChild(label
);
4884 filter
.appendChild(document
.createTextNode(" "));
4885 filter
.appendChild(button
);
4886 addEvent(filter
, "submit", interceptNavigation
);
4891 function moduleListHtml() {
4896 for (i
= 0; i
< config
.modules
.length
; i
++) {
4897 if (config
.modules
[i
].name
!== "") {
4898 checked
= config
.moduleId
.indexOf(config
.modules
[i
].moduleId
) > -1;
4899 html
+= "<li><label class='clickable" + (checked
? " checked" : "") + "'><input type='checkbox' " + "value='" + config
.modules
[i
].moduleId
+ "'" + (checked
? " checked='checked'" : "") + " />" + escapeText(config
.modules
[i
].name
) + "</label></li>";
4906 function toolbarModuleFilter() {
4910 moduleFilter
= document
.createElement("form"),
4911 label
= document
.createElement("label"),
4912 moduleSearch
= document
.createElement("input"),
4913 dropDown
= document
.createElement("div"),
4914 actions
= document
.createElement("span"),
4915 dropDownList
= document
.createElement("ul"),
4918 moduleSearch
.id
= "qunit-modulefilter-search";
4919 moduleSearch
.autocomplete
= "off";
4920 addEvent(moduleSearch
, "input", searchInput
);
4921 addEvent(moduleSearch
, "input", searchFocus
);
4922 addEvent(moduleSearch
, "focus", searchFocus
);
4923 addEvent(moduleSearch
, "click", searchFocus
);
4925 label
.id
= "qunit-modulefilter-search-container";
4926 label
.innerHTML
= "Module: ";
4927 label
.appendChild(moduleSearch
);
4929 actions
.id
= "qunit-modulefilter-actions";
4930 actions
.innerHTML
= "<button style='display:none'>Apply</button>" + "<button type='reset' style='display:none'>Reset</button>" + "<label class='clickable" + (config
.moduleId
.length
? "" : " checked") + "'><input type='checkbox'" + (config
.moduleId
.length
? "" : " checked='checked'") + " />All modules</label>";
4931 allCheckbox
= actions
.lastChild
.firstChild
;
4932 commit
= actions
.firstChild
;
4933 reset
= commit
.nextSibling
;
4934 addEvent(commit
, "click", applyUrlParams
);
4936 dropDownList
.id
= "qunit-modulefilter-dropdown-list";
4937 dropDownList
.innerHTML
= moduleListHtml();
4939 dropDown
.id
= "qunit-modulefilter-dropdown";
4940 dropDown
.style
.display
= "none";
4941 dropDown
.appendChild(actions
);
4942 dropDown
.appendChild(dropDownList
);
4943 addEvent(dropDown
, "change", selectionChange
);
4946 moduleFilter
.id
= "qunit-modulefilter";
4947 moduleFilter
.appendChild(label
);
4948 moduleFilter
.appendChild(dropDown
);
4949 addEvent(moduleFilter
, "submit", interceptNavigation
);
4950 addEvent(moduleFilter
, "reset", function () {
4952 // Let the reset happen, then update styles
4953 window
$1.setTimeout(selectionChange
);
4956 // Enables show/hide for the dropdown
4957 function searchFocus() {
4958 if (dropDown
.style
.display
!== "none") {
4962 dropDown
.style
.display
= "block";
4963 addEvent(document
, "click", hideHandler
);
4964 addEvent(document
, "keydown", hideHandler
);
4966 // Hide on Escape keydown or outside-container click
4967 function hideHandler(e
) {
4968 var inContainer
= moduleFilter
.contains(e
.target
);
4970 if (e
.keyCode
=== 27 || !inContainer
) {
4971 if (e
.keyCode
=== 27 && inContainer
) {
4972 moduleSearch
.focus();
4974 dropDown
.style
.display
= "none";
4975 removeEvent(document
, "click", hideHandler
);
4976 removeEvent(document
, "keydown", hideHandler
);
4977 moduleSearch
.value
= "";
4983 // Processes module search box input
4984 function searchInput() {
4987 searchText
= moduleSearch
.value
.toLowerCase(),
4988 listItems
= dropDownList
.children
;
4990 for (i
= 0; i
< listItems
.length
; i
++) {
4991 item
= listItems
[i
];
4992 if (!searchText
|| item
.textContent
.toLowerCase().indexOf(searchText
) > -1) {
4993 item
.style
.display
= "";
4995 item
.style
.display
= "none";
5000 // Processes selection changes
5001 function selectionChange(evt
) {
5004 checkbox
= evt
&& evt
.target
|| allCheckbox
,
5005 modulesList
= dropDownList
.getElementsByTagName("input"),
5008 toggleClass(checkbox
.parentNode
, "checked", checkbox
.checked
);
5011 if (checkbox
.checked
&& checkbox
!== allCheckbox
) {
5012 allCheckbox
.checked
= false;
5013 removeClass(allCheckbox
.parentNode
, "checked");
5015 for (i
= 0; i
< modulesList
.length
; i
++) {
5016 item
= modulesList
[i
];
5018 toggleClass(item
.parentNode
, "checked", item
.checked
);
5019 } else if (checkbox
=== allCheckbox
&& checkbox
.checked
) {
5020 item
.checked
= false;
5021 removeClass(item
.parentNode
, "checked");
5023 dirty
= dirty
|| item
.checked
!== item
.defaultChecked
;
5025 selectedNames
.push(item
.parentNode
.textContent
);
5029 commit
.style
.display
= reset
.style
.display
= dirty
? "" : "none";
5030 moduleSearch
.placeholder
= selectedNames
.join(", ") || allCheckbox
.parentNode
.textContent
;
5031 moduleSearch
.title
= "Type to filter list. Current selection:\n" + (selectedNames
.join("\n") || allCheckbox
.parentNode
.textContent
);
5034 return moduleFilter
;
5037 function appendToolbar() {
5038 var toolbar
= id("qunit-testrunner-toolbar");
5041 toolbar
.appendChild(toolbarUrlConfigContainer());
5042 toolbar
.appendChild(toolbarModuleFilter());
5043 toolbar
.appendChild(toolbarLooseFilter());
5044 toolbar
.appendChild(document
.createElement("div")).className
= "clearfix";
5048 function appendHeader() {
5049 var header
= id("qunit-header");
5052 header
.innerHTML
= "<a href='" + escapeText(unfilteredUrl
) + "'>" + header
.innerHTML
+ "</a> ";
5056 function appendBanner() {
5057 var banner
= id("qunit-banner");
5060 banner
.className
= "";
5064 function appendTestResults() {
5065 var tests
= id("qunit-tests"),
5066 result
= id("qunit-testresult"),
5070 result
.parentNode
.removeChild(result
);
5074 tests
.innerHTML
= "";
5075 result
= document
.createElement("p");
5076 result
.id
= "qunit-testresult";
5077 result
.className
= "result";
5078 tests
.parentNode
.insertBefore(result
, tests
);
5079 result
.innerHTML
= "<div id=\"qunit-testresult-display\">Running...<br /> </div>" + "<div id=\"qunit-testresult-controls\"></div>" + "<div class=\"clearfix\"></div>";
5080 controls
= id("qunit-testresult-controls");
5084 controls
.appendChild(abortTestsButton());
5088 function appendFilteredTest() {
5089 var testId
= QUnit
.config
.testId
;
5090 if (!testId
|| testId
.length
<= 0) {
5093 return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId
.join(", ")) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl
) + "'>Run all tests</a></div>";
5096 function appendUserAgent() {
5097 var userAgent
= id("qunit-userAgent");
5100 userAgent
.innerHTML
= "";
5101 userAgent
.appendChild(document
.createTextNode("QUnit " + QUnit
.version
+ "; " + navigator
.userAgent
));
5105 function appendInterface() {
5106 var qunit
= id("qunit");
5109 qunit
.innerHTML
= "<h1 id='qunit-header'>" + escapeText(document
.title
) + "</h1>" + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
5114 appendTestResults();
5119 function appendTest(name
, testId
, moduleName
) {
5124 tests
= id("qunit-tests");
5130 title
= document
.createElement("strong");
5131 title
.innerHTML
= getNameHtml(name
, moduleName
);
5133 rerunTrigger
= document
.createElement("a");
5134 rerunTrigger
.innerHTML
= "Rerun";
5135 rerunTrigger
.href
= setUrl({ testId
: testId
});
5137 testBlock
= document
.createElement("li");
5138 testBlock
.appendChild(title
);
5139 testBlock
.appendChild(rerunTrigger
);
5140 testBlock
.id
= "qunit-test-output-" + testId
;
5142 assertList
= document
.createElement("ol");
5143 assertList
.className
= "qunit-assert-list";
5145 testBlock
.appendChild(assertList
);
5147 tests
.appendChild(testBlock
);
5150 // HTML Reporter initialization and load
5151 QUnit
.begin(function (details
) {
5154 // Sort modules by name for the picker
5155 for (i
= 0; i
< details
.modules
.length
; i
++) {
5156 moduleObj
= details
.modules
[i
];
5157 if (moduleObj
.name
) {
5158 modulesList
.push(moduleObj
.name
);
5161 modulesList
.sort(function (a
, b
) {
5162 return a
.localeCompare(b
);
5165 // Initialize QUnit elements
5169 QUnit
.done(function (details
) {
5170 var banner
= id("qunit-banner"),
5171 tests
= id("qunit-tests"),
5172 abortButton
= id("qunit-abort-tests-button"),
5173 totalTests
= stats
.passedTests
+ stats
.skippedTests
+ stats
.todoTests
+ stats
.failedTests
,
5174 html
= [totalTests
, " tests completed in ", details
.runtime
, " milliseconds, with ", stats
.failedTests
, " failed, ", stats
.skippedTests
, " skipped, and ", stats
.todoTests
, " todo.<br />", "<span class='passed'>", details
.passed
, "</span> assertions of <span class='total'>", details
.total
, "</span> passed, <span class='failed'>", details
.failed
, "</span> failed."].join(""),
5179 // Update remaing tests to aborted
5180 if (abortButton
&& abortButton
.disabled
) {
5181 html
= "Tests aborted after " + details
.runtime
+ " milliseconds.";
5183 for (var i
= 0; i
< tests
.children
.length
; i
++) {
5184 test
= tests
.children
[i
];
5185 if (test
.className
=== "" || test
.className
=== "running") {
5186 test
.className
= "aborted";
5187 assertList
= test
.getElementsByTagName("ol")[0];
5188 assertLi
= document
.createElement("li");
5189 assertLi
.className
= "fail";
5190 assertLi
.innerHTML
= "Test aborted.";
5191 assertList
.appendChild(assertLi
);
5196 if (banner
&& (!abortButton
|| abortButton
.disabled
=== false)) {
5197 banner
.className
= stats
.failedTests
? "qunit-fail" : "qunit-pass";
5201 abortButton
.parentNode
.removeChild(abortButton
);
5205 id("qunit-testresult-display").innerHTML
= html
;
5208 if (config
.altertitle
&& document
.title
) {
5210 // Show ✖ for good, ✔ for bad suite result in title
5211 // use escape sequences in case file gets loaded with non-utf-8
5213 document
.title
= [stats
.failedTests
? "\u2716" : "\u2714", document
.title
.replace(/^[\u2714\u2716] /i, "")].join(" ");
5216 // Scroll back to top to show results
5217 if (config
.scrolltop
&& window
$1.scrollTo
) {
5218 window
$1.scrollTo(0, 0);
5222 function getNameHtml(name
, module
) {
5226 nameHtml
= "<span class='module-name'>" + escapeText(module
) + "</span>: ";
5229 nameHtml
+= "<span class='test-name'>" + escapeText(name
) + "</span>";
5234 QUnit
.testStart(function (details
) {
5237 appendTest(details
.name
, details
.testId
, details
.module
);
5239 running
= id("qunit-testresult-display");
5242 addClass(running
, "running");
5244 bad
= QUnit
.config
.reorder
&& details
.previousFailure
;
5246 running
.innerHTML
= [bad
? "Rerunning previously failed test: <br />" : "Running: <br />", getNameHtml(details
.name
, details
.module
)].join("");
5250 function stripHtml(string
) {
5252 // Strip tags, html entity and whitespaces
5253 return string
.replace(/<\/?[^>]+(>|$)/g, "").replace(/"/g, "").replace(/\s+/g, "");
5256 QUnit
.log(function (details
) {
5264 testItem
= id("qunit-test-output-" + details
.testId
);
5270 message
= escapeText(details
.message
) || (details
.result
? "okay" : "failed");
5271 message
= "<span class='test-message'>" + message
+ "</span>";
5272 message
+= "<span class='runtime'>@ " + details
.runtime
+ " ms</span>";
5274 // The pushFailure doesn't provide details.expected
5275 // when it calls, it's implicit to also not show expected and diff stuff
5276 // Also, we need to check details.expected existence, as it can exist and be undefined
5277 if (!details
.result
&& hasOwn
.call(details
, "expected")) {
5278 if (details
.negative
) {
5279 expected
= "NOT " + QUnit
.dump
.parse(details
.expected
);
5281 expected
= QUnit
.dump
.parse(details
.expected
);
5284 actual
= QUnit
.dump
.parse(details
.actual
);
5285 message
+= "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected
) + "</pre></td></tr>";
5287 if (actual
!== expected
) {
5289 message
+= "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual
) + "</pre></td></tr>";
5291 if (typeof details
.actual
=== "number" && typeof details
.expected
=== "number") {
5292 if (!isNaN(details
.actual
) && !isNaN(details
.expected
)) {
5294 diff
= details
.actual
- details
.expected
;
5295 diff
= (diff
> 0 ? "+" : "") + diff
;
5297 } else if (typeof details
.actual
!== "boolean" && typeof details
.expected
!== "boolean") {
5298 diff
= QUnit
.diff(expected
, actual
);
5300 // don't show diff if there is zero overlap
5301 showDiff
= stripHtml(diff
).length
!== stripHtml(expected
).length
+ stripHtml(actual
).length
;
5305 message
+= "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff
+ "</pre></td></tr>";
5307 } else if (expected
.indexOf("[object Array]") !== -1 || expected
.indexOf("[object Object]") !== -1) {
5308 message
+= "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the depth of object is more than current max depth (" + QUnit
.config
.maxDepth
+ ").<p>Hint: Use <code>QUnit.dump.maxDepth</code> to " + " run with a higher max depth or <a href='" + escapeText(setUrl({ maxDepth
: -1 })) + "'>" + "Rerun</a> without max depth.</p></td></tr>";
5310 message
+= "<tr class='test-message'><th>Message: </th><td>" + "Diff suppressed as the expected and actual results have an equivalent" + " serialization</td></tr>";
5313 if (details
.source
) {
5314 message
+= "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details
.source
) + "</pre></td></tr>";
5317 message
+= "</table>";
5319 // This occurs when pushFailure is set and we have an extracted stack trace
5320 } else if (!details
.result
&& details
.source
) {
5321 message
+= "<table>" + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details
.source
) + "</pre></td></tr>" + "</table>";
5324 assertList
= testItem
.getElementsByTagName("ol")[0];
5326 assertLi
= document
.createElement("li");
5327 assertLi
.className
= details
.result
? "pass" : "fail";
5328 assertLi
.innerHTML
= message
;
5329 assertList
.appendChild(assertLi
);
5332 QUnit
.testDone(function (details
) {
5343 tests
= id("qunit-tests");
5349 testItem
= id("qunit-test-output-" + details
.testId
);
5351 removeClass(testItem
, "running");
5353 if (details
.failed
> 0) {
5355 } else if (details
.todo
) {
5358 status
= details
.skipped
? "skipped" : "passed";
5361 assertList
= testItem
.getElementsByTagName("ol")[0];
5363 good
= details
.passed
;
5364 bad
= details
.failed
;
5366 // This test passed if it has no unexpected failed assertions
5367 var testPassed
= details
.failed
> 0 ? details
.todo
: !details
.todo
;
5371 // Collapse the passing tests
5372 addClass(assertList
, "qunit-collapsed");
5373 } else if (config
.collapse
) {
5374 if (!collapseNext
) {
5376 // Skip collapsing the first failing test
5377 collapseNext
= true;
5380 // Collapse remaining tests
5381 addClass(assertList
, "qunit-collapsed");
5385 // The testItem.firstChild is the test name
5386 testTitle
= testItem
.firstChild
;
5388 testCounts
= bad
? "<b class='failed'>" + bad
+ "</b>, " + "<b class='passed'>" + good
+ "</b>, " : "";
5390 testTitle
.innerHTML
+= " <b class='counts'>(" + testCounts
+ details
.assertions
.length
+ ")</b>";
5392 if (details
.skipped
) {
5393 stats
.skippedTests
++;
5395 testItem
.className
= "skipped";
5396 skipped
= document
.createElement("em");
5397 skipped
.className
= "qunit-skipped-label";
5398 skipped
.innerHTML
= "skipped";
5399 testItem
.insertBefore(skipped
, testTitle
);
5401 addEvent(testTitle
, "click", function () {
5402 toggleClass(assertList
, "qunit-collapsed");
5405 testItem
.className
= testPassed
? "pass" : "fail";
5408 var todoLabel
= document
.createElement("em");
5409 todoLabel
.className
= "qunit-todo-label";
5410 todoLabel
.innerHTML
= "todo";
5411 testItem
.className
+= " todo";
5412 testItem
.insertBefore(todoLabel
, testTitle
);
5415 time
= document
.createElement("span");
5416 time
.className
= "runtime";
5417 time
.innerHTML
= details
.runtime
+ " ms";
5418 testItem
.insertBefore(time
, assertList
);
5421 stats
.failedTests
++;
5422 } else if (details
.todo
) {
5425 stats
.passedTests
++;
5429 // Show the source of the test when showing assertions
5430 if (details
.source
) {
5431 sourceName
= document
.createElement("p");
5432 sourceName
.innerHTML
= "<strong>Source: </strong>" + escapeText(details
.source
);
5433 addClass(sourceName
, "qunit-source");
5435 addClass(sourceName
, "qunit-collapsed");
5437 addEvent(testTitle
, "click", function () {
5438 toggleClass(sourceName
, "qunit-collapsed");
5440 testItem
.appendChild(sourceName
);
5443 if (config
.hidepassed
&& status
=== "passed") {
5445 // use removeChild instead of remove because of support
5446 hiddenTests
.push(testItem
);
5448 tests
.removeChild(testItem
);
5452 // Avoid readyState issue with phantomjs
5454 var notPhantom = function (p
) {
5455 return !(p
&& p
.version
&& p
.version
.major
> 0);
5456 }(window
$1.phantom
);
5458 if (notPhantom
&& document
.readyState
=== "complete") {
5461 addEvent(window
$1, "load", QUnit
.load
);
5464 // Wrap window.onerror. We will call the original window.onerror to see if
5465 // the existing handler fully handles the error; if not, we will call the
5466 // QUnit.onError function.
5467 var originalWindowOnError
= window
$1.onerror
;
5469 // Cover uncaught exceptions
5470 // Returning true will suppress the default browser handler,
5471 // returning false will let it run.
5472 window
$1.onerror = function (message
, fileName
, lineNumber
, columnNumber
, errorObj
) {
5474 if (originalWindowOnError
) {
5475 for (var _len
= arguments
.length
, args
= Array(_len
> 5 ? _len
- 5 : 0), _key
= 5; _key
< _len
; _key
++) {
5476 args
[_key
- 5] = arguments
[_key
];
5479 ret
= originalWindowOnError
.call
.apply(originalWindowOnError
, [this, message
, fileName
, lineNumber
, columnNumber
, errorObj
].concat(args
));
5482 // Treat return value as window.onerror itself does,
5483 // Only do our handling if not suppressed.
5488 lineNumber
: lineNumber
5492 // https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror,
5493 // most modern browsers support an errorObj argument; use that to
5494 // get a full stack trace if it's available.
5495 if (errorObj
&& errorObj
.stack
) {
5496 error
.stacktrace
= extractStacktrace(errorObj
, 0);
5499 ret
= QUnit
.onError(error
);
5505 // Listen for unhandled rejections, and call QUnit.onUnhandledRejection
5506 window
$1.addEventListener("unhandledrejection", function (event
) {
5507 QUnit
.onUnhandledRejection(event
.reason
);
5512 * This file is a modified version of google-diff-match-patch's JavaScript implementation
5513 * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
5514 * modifications are licensed as more fully set forth in LICENSE.txt.
5516 * The original source of google-diff-match-patch is attributable and licensed as follows:
5518 * Copyright 2006 Google Inc.
5519 * https://code.google.com/p/google-diff-match-patch/
5521 * Licensed under the Apache License, Version 2.0 (the "License");
5522 * you may not use this file except in compliance with the License.
5523 * You may obtain a copy of the License at
5525 * https://www.apache.org/licenses/LICENSE-2.0
5527 * Unless required by applicable law or agreed to in writing, software
5528 * distributed under the License is distributed on an "AS IS" BASIS,
5529 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
5530 * See the License for the specific language governing permissions and
5531 * limitations under the License.
5534 * https://code.google.com/p/google-diff-match-patch/
5536 * Usage: QUnit.diff(expected, actual)
5539 QUnit
.diff = function () {
5540 function DiffMatchPatch() {}
5545 * The data structure representing a diff is an array of tuples:
5546 * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
5547 * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
5549 var DIFF_DELETE
= -1,
5554 * Find the differences between two texts. Simplifies the problem by stripping
5555 * any common prefix or suffix off the texts before diffing.
5556 * @param {string} text1 Old string to be diffed.
5557 * @param {string} text2 New string to be diffed.
5558 * @param {boolean=} optChecklines Optional speedup flag. If present and false,
5559 * then don't run a line-level diff first to identify the changed areas.
5560 * Defaults to true, which does a faster, slightly less optimal diff.
5561 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
5563 DiffMatchPatch
.prototype.DiffMain = function (text1
, text2
, optChecklines
) {
5564 var deadline
, checklines
, commonlength
, commonprefix
, commonsuffix
, diffs
;
5566 // The diff must be complete in up to 1 second.
5567 deadline
= new Date().getTime() + 1000;
5569 // Check for null inputs.
5570 if (text1
=== null || text2
=== null) {
5571 throw new Error("Null input. (DiffMain)");
5574 // Check for equality (speedup).
5575 if (text1
=== text2
) {
5577 return [[DIFF_EQUAL
, text1
]];
5582 if (typeof optChecklines
=== "undefined") {
5583 optChecklines
= true;
5586 checklines
= optChecklines
;
5588 // Trim off common prefix (speedup).
5589 commonlength
= this.diffCommonPrefix(text1
, text2
);
5590 commonprefix
= text1
.substring(0, commonlength
);
5591 text1
= text1
.substring(commonlength
);
5592 text2
= text2
.substring(commonlength
);
5594 // Trim off common suffix (speedup).
5595 commonlength
= this.diffCommonSuffix(text1
, text2
);
5596 commonsuffix
= text1
.substring(text1
.length
- commonlength
);
5597 text1
= text1
.substring(0, text1
.length
- commonlength
);
5598 text2
= text2
.substring(0, text2
.length
- commonlength
);
5600 // Compute the diff on the middle block.
5601 diffs
= this.diffCompute(text1
, text2
, checklines
, deadline
);
5603 // Restore the prefix and suffix.
5605 diffs
.unshift([DIFF_EQUAL
, commonprefix
]);
5608 diffs
.push([DIFF_EQUAL
, commonsuffix
]);
5610 this.diffCleanupMerge(diffs
);
5615 * Reduce the number of edits by eliminating operationally trivial equalities.
5616 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
5618 DiffMatchPatch
.prototype.diffCleanupEfficiency = function (diffs
) {
5619 var changes
, equalities
, equalitiesLength
, lastequality
, pointer
, preIns
, preDel
, postIns
, postDel
;
5621 equalities
= []; // Stack of indices where equalities are found.
5622 equalitiesLength
= 0; // Keeping our own length var is faster in JS.
5623 /** @type {?string} */
5624 lastequality
= null;
5626 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
5627 pointer
= 0; // Index of current position.
5629 // Is there an insertion operation before the last equality.
5632 // Is there a deletion operation before the last equality.
5635 // Is there an insertion operation after the last equality.
5638 // Is there a deletion operation after the last equality.
5640 while (pointer
< diffs
.length
) {
5643 if (diffs
[pointer
][0] === DIFF_EQUAL
) {
5644 if (diffs
[pointer
][1].length
< 4 && (postIns
|| postDel
)) {
5647 equalities
[equalitiesLength
++] = pointer
;
5650 lastequality
= diffs
[pointer
][1];
5653 // Not a candidate, and can never become one.
5654 equalitiesLength
= 0;
5655 lastequality
= null;
5657 postIns
= postDel
= false;
5659 // An insertion or deletion.
5662 if (diffs
[pointer
][0] === DIFF_DELETE
) {
5669 * Five types to be split:
5670 * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
5671 * <ins>A</ins>X<ins>C</ins><del>D</del>
5672 * <ins>A</ins><del>B</del>X<ins>C</ins>
5673 * <ins>A</del>X<ins>C</ins><del>D</del>
5674 * <ins>A</ins><del>B</del>X<del>C</del>
5676 if (lastequality
&& (preIns
&& preDel
&& postIns
&& postDel
|| lastequality
.length
< 2 && preIns
+ preDel
+ postIns
+ postDel
=== 3)) {
5678 // Duplicate record.
5679 diffs
.splice(equalities
[equalitiesLength
- 1], 0, [DIFF_DELETE
, lastequality
]);
5681 // Change second copy to insert.
5682 diffs
[equalities
[equalitiesLength
- 1] + 1][0] = DIFF_INSERT
;
5683 equalitiesLength
--; // Throw away the equality we just deleted;
5684 lastequality
= null;
5685 if (preIns
&& preDel
) {
5687 // No changes made which could affect previous entry, keep going.
5688 postIns
= postDel
= true;
5689 equalitiesLength
= 0;
5691 equalitiesLength
--; // Throw away the previous equality.
5692 pointer
= equalitiesLength
> 0 ? equalities
[equalitiesLength
- 1] : -1;
5693 postIns
= postDel
= false;
5702 this.diffCleanupMerge(diffs
);
5707 * Convert a diff array into a pretty HTML report.
5708 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
5709 * @param {integer} string to be beautified.
5710 * @return {string} HTML representation.
5712 DiffMatchPatch
.prototype.diffPrettyHtml = function (diffs
) {
5717 for (x
= 0; x
< diffs
.length
; x
++) {
5718 op
= diffs
[x
][0]; // Operation (insert, delete, equal)
5719 data
= diffs
[x
][1]; // Text of change.
5722 html
[x
] = "<ins>" + escapeText(data
) + "</ins>";
5725 html
[x
] = "<del>" + escapeText(data
) + "</del>";
5728 html
[x
] = "<span>" + escapeText(data
) + "</span>";
5732 return html
.join("");
5736 * Determine the common prefix of two strings.
5737 * @param {string} text1 First string.
5738 * @param {string} text2 Second string.
5739 * @return {number} The number of characters common to the start of each
5742 DiffMatchPatch
.prototype.diffCommonPrefix = function (text1
, text2
) {
5743 var pointermid
, pointermax
, pointermin
, pointerstart
;
5745 // Quick check for common null cases.
5746 if (!text1
|| !text2
|| text1
.charAt(0) !== text2
.charAt(0)) {
5751 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
5753 pointermax
= Math
.min(text1
.length
, text2
.length
);
5754 pointermid
= pointermax
;
5756 while (pointermin
< pointermid
) {
5757 if (text1
.substring(pointerstart
, pointermid
) === text2
.substring(pointerstart
, pointermid
)) {
5758 pointermin
= pointermid
;
5759 pointerstart
= pointermin
;
5761 pointermax
= pointermid
;
5763 pointermid
= Math
.floor((pointermax
- pointermin
) / 2 + pointermin
);
5769 * Determine the common suffix of two strings.
5770 * @param {string} text1 First string.
5771 * @param {string} text2 Second string.
5772 * @return {number} The number of characters common to the end of each string.
5774 DiffMatchPatch
.prototype.diffCommonSuffix = function (text1
, text2
) {
5775 var pointermid
, pointermax
, pointermin
, pointerend
;
5777 // Quick check for common null cases.
5778 if (!text1
|| !text2
|| text1
.charAt(text1
.length
- 1) !== text2
.charAt(text2
.length
- 1)) {
5783 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
5785 pointermax
= Math
.min(text1
.length
, text2
.length
);
5786 pointermid
= pointermax
;
5788 while (pointermin
< pointermid
) {
5789 if (text1
.substring(text1
.length
- pointermid
, text1
.length
- pointerend
) === text2
.substring(text2
.length
- pointermid
, text2
.length
- pointerend
)) {
5790 pointermin
= pointermid
;
5791 pointerend
= pointermin
;
5793 pointermax
= pointermid
;
5795 pointermid
= Math
.floor((pointermax
- pointermin
) / 2 + pointermin
);
5801 * Find the differences between two texts. Assumes that the texts do not
5802 * have any common prefix or suffix.
5803 * @param {string} text1 Old string to be diffed.
5804 * @param {string} text2 New string to be diffed.
5805 * @param {boolean} checklines Speedup flag. If false, then don't run a
5806 * line-level diff first to identify the changed areas.
5807 * If true, then run a faster, slightly less optimal diff.
5808 * @param {number} deadline Time when the diff should be complete by.
5809 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
5812 DiffMatchPatch
.prototype.diffCompute = function (text1
, text2
, checklines
, deadline
) {
5813 var diffs
, longtext
, shorttext
, i
, hm
, text1A
, text2A
, text1B
, text2B
, midCommon
, diffsA
, diffsB
;
5817 // Just add some text (speedup).
5818 return [[DIFF_INSERT
, text2
]];
5823 // Just delete some text (speedup).
5824 return [[DIFF_DELETE
, text1
]];
5827 longtext
= text1
.length
> text2
.length
? text1
: text2
;
5828 shorttext
= text1
.length
> text2
.length
? text2
: text1
;
5829 i
= longtext
.indexOf(shorttext
);
5832 // Shorter text is inside the longer text (speedup).
5833 diffs
= [[DIFF_INSERT
, longtext
.substring(0, i
)], [DIFF_EQUAL
, shorttext
], [DIFF_INSERT
, longtext
.substring(i
+ shorttext
.length
)]];
5835 // Swap insertions for deletions if diff is reversed.
5836 if (text1
.length
> text2
.length
) {
5837 diffs
[0][0] = diffs
[2][0] = DIFF_DELETE
;
5842 if (shorttext
.length
=== 1) {
5844 // Single character string.
5845 // After the previous speedup, the character can't be an equality.
5846 return [[DIFF_DELETE
, text1
], [DIFF_INSERT
, text2
]];
5849 // Check to see if the problem can be split in two.
5850 hm
= this.diffHalfMatch(text1
, text2
);
5853 // A half-match was found, sort out the return data.
5860 // Send both pairs off for separate processing.
5861 diffsA
= this.DiffMain(text1A
, text2A
, checklines
, deadline
);
5862 diffsB
= this.DiffMain(text1B
, text2B
, checklines
, deadline
);
5864 // Merge the results.
5865 return diffsA
.concat([[DIFF_EQUAL
, midCommon
]], diffsB
);
5868 if (checklines
&& text1
.length
> 100 && text2
.length
> 100) {
5869 return this.diffLineMode(text1
, text2
, deadline
);
5872 return this.diffBisect(text1
, text2
, deadline
);
5876 * Do the two texts share a substring which is at least half the length of the
5878 * This speedup can produce non-minimal diffs.
5879 * @param {string} text1 First string.
5880 * @param {string} text2 Second string.
5881 * @return {Array.<string>} Five element Array, containing the prefix of
5882 * text1, the suffix of text1, the prefix of text2, the suffix of
5883 * text2 and the common middle. Or null if there was no match.
5886 DiffMatchPatch
.prototype.diffHalfMatch = function (text1
, text2
) {
5887 var longtext
, shorttext
, dmp
, text1A
, text2B
, text2A
, text1B
, midCommon
, hm1
, hm2
, hm
;
5889 longtext
= text1
.length
> text2
.length
? text1
: text2
;
5890 shorttext
= text1
.length
> text2
.length
? text2
: text1
;
5891 if (longtext
.length
< 4 || shorttext
.length
* 2 < longtext
.length
) {
5892 return null; // Pointless.
5894 dmp
= this; // 'this' becomes 'window' in a closure.
5897 * Does a substring of shorttext exist within longtext such that the substring
5898 * is at least half the length of longtext?
5899 * Closure, but does not reference any external variables.
5900 * @param {string} longtext Longer string.
5901 * @param {string} shorttext Shorter string.
5902 * @param {number} i Start index of quarter length substring within longtext.
5903 * @return {Array.<string>} Five element Array, containing the prefix of
5904 * longtext, the suffix of longtext, the prefix of shorttext, the suffix
5905 * of shorttext and the common middle. Or null if there was no match.
5908 function diffHalfMatchI(longtext
, shorttext
, i
) {
5909 var seed
, j
, bestCommon
, prefixLength
, suffixLength
, bestLongtextA
, bestLongtextB
, bestShorttextA
, bestShorttextB
;
5911 // Start with a 1/4 length substring at position i as a seed.
5912 seed
= longtext
.substring(i
, i
+ Math
.floor(longtext
.length
/ 4));
5915 while ((j
= shorttext
.indexOf(seed
, j
+ 1)) !== -1) {
5916 prefixLength
= dmp
.diffCommonPrefix(longtext
.substring(i
), shorttext
.substring(j
));
5917 suffixLength
= dmp
.diffCommonSuffix(longtext
.substring(0, i
), shorttext
.substring(0, j
));
5918 if (bestCommon
.length
< suffixLength
+ prefixLength
) {
5919 bestCommon
= shorttext
.substring(j
- suffixLength
, j
) + shorttext
.substring(j
, j
+ prefixLength
);
5920 bestLongtextA
= longtext
.substring(0, i
- suffixLength
);
5921 bestLongtextB
= longtext
.substring(i
+ prefixLength
);
5922 bestShorttextA
= shorttext
.substring(0, j
- suffixLength
);
5923 bestShorttextB
= shorttext
.substring(j
+ prefixLength
);
5926 if (bestCommon
.length
* 2 >= longtext
.length
) {
5927 return [bestLongtextA
, bestLongtextB
, bestShorttextA
, bestShorttextB
, bestCommon
];
5933 // First check if the second quarter is the seed for a half-match.
5934 hm1
= diffHalfMatchI(longtext
, shorttext
, Math
.ceil(longtext
.length
/ 4));
5936 // Check again based on the third quarter.
5937 hm2
= diffHalfMatchI(longtext
, shorttext
, Math
.ceil(longtext
.length
/ 2));
5946 // Both matched. Select the longest.
5947 hm
= hm1
[4].length
> hm2
[4].length
? hm1
: hm2
;
5950 // A half-match was found, sort out the return data.
5951 if (text1
.length
> text2
.length
) {
5963 return [text1A
, text1B
, text2A
, text2B
, midCommon
];
5967 * Do a quick line-level diff on both strings, then rediff the parts for
5969 * This speedup can produce non-minimal diffs.
5970 * @param {string} text1 Old string to be diffed.
5971 * @param {string} text2 New string to be diffed.
5972 * @param {number} deadline Time when the diff should be complete by.
5973 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
5976 DiffMatchPatch
.prototype.diffLineMode = function (text1
, text2
, deadline
) {
5977 var a
, diffs
, linearray
, pointer
, countInsert
, countDelete
, textInsert
, textDelete
, j
;
5979 // Scan the text on a line-by-line basis first.
5980 a
= this.diffLinesToChars(text1
, text2
);
5983 linearray
= a
.lineArray
;
5985 diffs
= this.DiffMain(text1
, text2
, false, deadline
);
5987 // Convert the diff back to original text.
5988 this.diffCharsToLines(diffs
, linearray
);
5990 // Eliminate freak matches (e.g. blank lines)
5991 this.diffCleanupSemantic(diffs
);
5993 // Rediff any replacement blocks, this time character-by-character.
5994 // Add a dummy entry at the end.
5995 diffs
.push([DIFF_EQUAL
, ""]);
6001 while (pointer
< diffs
.length
) {
6002 switch (diffs
[pointer
][0]) {
6005 textInsert
+= diffs
[pointer
][1];
6009 textDelete
+= diffs
[pointer
][1];
6013 // Upon reaching an equality, check for prior redundancies.
6014 if (countDelete
>= 1 && countInsert
>= 1) {
6016 // Delete the offending records and add the merged ones.
6017 diffs
.splice(pointer
- countDelete
- countInsert
, countDelete
+ countInsert
);
6018 pointer
= pointer
- countDelete
- countInsert
;
6019 a
= this.DiffMain(textDelete
, textInsert
, false, deadline
);
6020 for (j
= a
.length
- 1; j
>= 0; j
--) {
6021 diffs
.splice(pointer
, 0, a
[j
]);
6023 pointer
= pointer
+ a
.length
;
6033 diffs
.pop(); // Remove the dummy entry at the end.
6039 * Find the 'middle snake' of a diff, split the problem in two
6040 * and return the recursively constructed diff.
6041 * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
6042 * @param {string} text1 Old string to be diffed.
6043 * @param {string} text2 New string to be diffed.
6044 * @param {number} deadline Time at which to bail if not yet complete.
6045 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6048 DiffMatchPatch
.prototype.diffBisect = function (text1
, text2
, deadline
) {
6049 var text1Length
, text2Length
, maxD
, vOffset
, vLength
, v1
, v2
, x
, delta
, front
, k1start
, k1end
, k2start
, k2end
, k2Offset
, k1Offset
, x1
, x2
, y1
, y2
, d
, k1
, k2
;
6051 // Cache the text lengths to prevent multiple calls.
6052 text1Length
= text1
.length
;
6053 text2Length
= text2
.length
;
6054 maxD
= Math
.ceil((text1Length
+ text2Length
) / 2);
6057 v1
= new Array(vLength
);
6058 v2
= new Array(vLength
);
6060 // Setting all elements to -1 is faster in Chrome & Firefox than mixing
6061 // integers and undefined.
6062 for (x
= 0; x
< vLength
; x
++) {
6066 v1
[vOffset
+ 1] = 0;
6067 v2
[vOffset
+ 1] = 0;
6068 delta
= text1Length
- text2Length
;
6070 // If the total number of characters is odd, then the front path will collide
6071 // with the reverse path.
6072 front
= delta
% 2 !== 0;
6074 // Offsets for start and end of k loop.
6075 // Prevents mapping of space beyond the grid.
6080 for (d
= 0; d
< maxD
; d
++) {
6082 // Bail out if deadline is reached.
6083 if (new Date().getTime() > deadline
) {
6087 // Walk the front path one step.
6088 for (k1
= -d
+ k1start
; k1
<= d
- k1end
; k1
+= 2) {
6089 k1Offset
= vOffset
+ k1
;
6090 if (k1
=== -d
|| k1
!== d
&& v1
[k1Offset
- 1] < v1
[k1Offset
+ 1]) {
6091 x1
= v1
[k1Offset
+ 1];
6093 x1
= v1
[k1Offset
- 1] + 1;
6096 while (x1
< text1Length
&& y1
< text2Length
&& text1
.charAt(x1
) === text2
.charAt(y1
)) {
6101 if (x1
> text1Length
) {
6103 // Ran off the right of the graph.
6105 } else if (y1
> text2Length
) {
6107 // Ran off the bottom of the graph.
6110 k2Offset
= vOffset
+ delta
- k1
;
6111 if (k2Offset
>= 0 && k2Offset
< vLength
&& v2
[k2Offset
] !== -1) {
6113 // Mirror x2 onto top-left coordinate system.
6114 x2
= text1Length
- v2
[k2Offset
];
6117 // Overlap detected.
6118 return this.diffBisectSplit(text1
, text2
, x1
, y1
, deadline
);
6124 // Walk the reverse path one step.
6125 for (k2
= -d
+ k2start
; k2
<= d
- k2end
; k2
+= 2) {
6126 k2Offset
= vOffset
+ k2
;
6127 if (k2
=== -d
|| k2
!== d
&& v2
[k2Offset
- 1] < v2
[k2Offset
+ 1]) {
6128 x2
= v2
[k2Offset
+ 1];
6130 x2
= v2
[k2Offset
- 1] + 1;
6133 while (x2
< text1Length
&& y2
< text2Length
&& text1
.charAt(text1Length
- x2
- 1) === text2
.charAt(text2Length
- y2
- 1)) {
6138 if (x2
> text1Length
) {
6140 // Ran off the left of the graph.
6142 } else if (y2
> text2Length
) {
6144 // Ran off the top of the graph.
6146 } else if (!front
) {
6147 k1Offset
= vOffset
+ delta
- k2
;
6148 if (k1Offset
>= 0 && k1Offset
< vLength
&& v1
[k1Offset
] !== -1) {
6150 y1
= vOffset
+ x1
- k1Offset
;
6152 // Mirror x2 onto top-left coordinate system.
6153 x2
= text1Length
- x2
;
6156 // Overlap detected.
6157 return this.diffBisectSplit(text1
, text2
, x1
, y1
, deadline
);
6164 // Diff took too long and hit the deadline or
6165 // number of diffs equals number of characters, no commonality at all.
6166 return [[DIFF_DELETE
, text1
], [DIFF_INSERT
, text2
]];
6170 * Given the location of the 'middle snake', split the diff in two parts
6172 * @param {string} text1 Old string to be diffed.
6173 * @param {string} text2 New string to be diffed.
6174 * @param {number} x Index of split point in text1.
6175 * @param {number} y Index of split point in text2.
6176 * @param {number} deadline Time at which to bail if not yet complete.
6177 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6180 DiffMatchPatch
.prototype.diffBisectSplit = function (text1
, text2
, x
, y
, deadline
) {
6181 var text1a
, text1b
, text2a
, text2b
, diffs
, diffsb
;
6182 text1a
= text1
.substring(0, x
);
6183 text2a
= text2
.substring(0, y
);
6184 text1b
= text1
.substring(x
);
6185 text2b
= text2
.substring(y
);
6187 // Compute both diffs serially.
6188 diffs
= this.DiffMain(text1a
, text2a
, false, deadline
);
6189 diffsb
= this.DiffMain(text1b
, text2b
, false, deadline
);
6191 return diffs
.concat(diffsb
);
6195 * Reduce the number of edits by eliminating semantically trivial equalities.
6196 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6198 DiffMatchPatch
.prototype.diffCleanupSemantic = function (diffs
) {
6199 var changes
, equalities
, equalitiesLength
, lastequality
, pointer
, lengthInsertions2
, lengthDeletions2
, lengthInsertions1
, lengthDeletions1
, deletion
, insertion
, overlapLength1
, overlapLength2
;
6201 equalities
= []; // Stack of indices where equalities are found.
6202 equalitiesLength
= 0; // Keeping our own length var is faster in JS.
6203 /** @type {?string} */
6204 lastequality
= null;
6206 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
6207 pointer
= 0; // Index of current position.
6209 // Number of characters that changed prior to the equality.
6210 lengthInsertions1
= 0;
6211 lengthDeletions1
= 0;
6213 // Number of characters that changed after the equality.
6214 lengthInsertions2
= 0;
6215 lengthDeletions2
= 0;
6216 while (pointer
< diffs
.length
) {
6217 if (diffs
[pointer
][0] === DIFF_EQUAL
) {
6219 equalities
[equalitiesLength
++] = pointer
;
6220 lengthInsertions1
= lengthInsertions2
;
6221 lengthDeletions1
= lengthDeletions2
;
6222 lengthInsertions2
= 0;
6223 lengthDeletions2
= 0;
6224 lastequality
= diffs
[pointer
][1];
6226 // An insertion or deletion.
6227 if (diffs
[pointer
][0] === DIFF_INSERT
) {
6228 lengthInsertions2
+= diffs
[pointer
][1].length
;
6230 lengthDeletions2
+= diffs
[pointer
][1].length
;
6233 // Eliminate an equality that is smaller or equal to the edits on both
6235 if (lastequality
&& lastequality
.length
<= Math
.max(lengthInsertions1
, lengthDeletions1
) && lastequality
.length
<= Math
.max(lengthInsertions2
, lengthDeletions2
)) {
6237 // Duplicate record.
6238 diffs
.splice(equalities
[equalitiesLength
- 1], 0, [DIFF_DELETE
, lastequality
]);
6240 // Change second copy to insert.
6241 diffs
[equalities
[equalitiesLength
- 1] + 1][0] = DIFF_INSERT
;
6243 // Throw away the equality we just deleted.
6246 // Throw away the previous equality (it needs to be reevaluated).
6248 pointer
= equalitiesLength
> 0 ? equalities
[equalitiesLength
- 1] : -1;
6250 // Reset the counters.
6251 lengthInsertions1
= 0;
6252 lengthDeletions1
= 0;
6253 lengthInsertions2
= 0;
6254 lengthDeletions2
= 0;
6255 lastequality
= null;
6262 // Normalize the diff.
6264 this.diffCleanupMerge(diffs
);
6267 // Find any overlaps between deletions and insertions.
6268 // e.g: <del>abcxxx</del><ins>xxxdef</ins>
6269 // -> <del>abc</del>xxx<ins>def</ins>
6270 // e.g: <del>xxxabc</del><ins>defxxx</ins>
6271 // -> <ins>def</ins>xxx<del>abc</del>
6272 // Only extract an overlap if it is as big as the edit ahead or behind it.
6274 while (pointer
< diffs
.length
) {
6275 if (diffs
[pointer
- 1][0] === DIFF_DELETE
&& diffs
[pointer
][0] === DIFF_INSERT
) {
6276 deletion
= diffs
[pointer
- 1][1];
6277 insertion
= diffs
[pointer
][1];
6278 overlapLength1
= this.diffCommonOverlap(deletion
, insertion
);
6279 overlapLength2
= this.diffCommonOverlap(insertion
, deletion
);
6280 if (overlapLength1
>= overlapLength2
) {
6281 if (overlapLength1
>= deletion
.length
/ 2 || overlapLength1
>= insertion
.length
/ 2) {
6283 // Overlap found. Insert an equality and trim the surrounding edits.
6284 diffs
.splice(pointer
, 0, [DIFF_EQUAL
, insertion
.substring(0, overlapLength1
)]);
6285 diffs
[pointer
- 1][1] = deletion
.substring(0, deletion
.length
- overlapLength1
);
6286 diffs
[pointer
+ 1][1] = insertion
.substring(overlapLength1
);
6290 if (overlapLength2
>= deletion
.length
/ 2 || overlapLength2
>= insertion
.length
/ 2) {
6292 // Reverse overlap found.
6293 // Insert an equality and swap and trim the surrounding edits.
6294 diffs
.splice(pointer
, 0, [DIFF_EQUAL
, deletion
.substring(0, overlapLength2
)]);
6296 diffs
[pointer
- 1][0] = DIFF_INSERT
;
6297 diffs
[pointer
- 1][1] = insertion
.substring(0, insertion
.length
- overlapLength2
);
6298 diffs
[pointer
+ 1][0] = DIFF_DELETE
;
6299 diffs
[pointer
+ 1][1] = deletion
.substring(overlapLength2
);
6310 * Determine if the suffix of one string is the prefix of another.
6311 * @param {string} text1 First string.
6312 * @param {string} text2 Second string.
6313 * @return {number} The number of characters common to the end of the first
6314 * string and the start of the second string.
6317 DiffMatchPatch
.prototype.diffCommonOverlap = function (text1
, text2
) {
6318 var text1Length
, text2Length
, textLength
, best
, length
, pattern
, found
;
6320 // Cache the text lengths to prevent multiple calls.
6321 text1Length
= text1
.length
;
6322 text2Length
= text2
.length
;
6324 // Eliminate the null case.
6325 if (text1Length
=== 0 || text2Length
=== 0) {
6329 // Truncate the longer string.
6330 if (text1Length
> text2Length
) {
6331 text1
= text1
.substring(text1Length
- text2Length
);
6332 } else if (text1Length
< text2Length
) {
6333 text2
= text2
.substring(0, text1Length
);
6335 textLength
= Math
.min(text1Length
, text2Length
);
6337 // Quick check for the worst case.
6338 if (text1
=== text2
) {
6342 // Start by looking for a single character match
6343 // and increase length until no match is found.
6344 // Performance analysis: https://neil.fraser.name/news/2010/11/04/
6348 pattern
= text1
.substring(textLength
- length
);
6349 found
= text2
.indexOf(pattern
);
6354 if (found
=== 0 || text1
.substring(textLength
- length
) === text2
.substring(0, length
)) {
6362 * Split two texts into an array of strings. Reduce the texts to a string of
6363 * hashes where each Unicode character represents one line.
6364 * @param {string} text1 First string.
6365 * @param {string} text2 Second string.
6366 * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
6367 * An object containing the encoded text1, the encoded text2 and
6368 * the array of unique strings.
6369 * The zeroth element of the array of unique strings is intentionally blank.
6372 DiffMatchPatch
.prototype.diffLinesToChars = function (text1
, text2
) {
6373 var lineArray
, lineHash
, chars1
, chars2
;
6374 lineArray
= []; // E.g. lineArray[4] === 'Hello\n'
6375 lineHash
= {}; // E.g. lineHash['Hello\n'] === 4
6377 // '\x00' is a valid character, but various debuggers don't like it.
6378 // So we'll insert a junk entry to avoid generating a null character.
6382 * Split a text into an array of strings. Reduce the texts to a string of
6383 * hashes where each Unicode character represents one line.
6384 * Modifies linearray and linehash through being a closure.
6385 * @param {string} text String to encode.
6386 * @return {string} Encoded string.
6389 function diffLinesToCharsMunge(text
) {
6390 var chars
, lineStart
, lineEnd
, lineArrayLength
, line
;
6393 // Walk the text, pulling out a substring for each line.
6394 // text.split('\n') would would temporarily double our memory footprint.
6395 // Modifying text would create many large strings to garbage collect.
6399 // Keeping our own length variable is faster than looking it up.
6400 lineArrayLength
= lineArray
.length
;
6401 while (lineEnd
< text
.length
- 1) {
6402 lineEnd
= text
.indexOf("\n", lineStart
);
6403 if (lineEnd
=== -1) {
6404 lineEnd
= text
.length
- 1;
6406 line
= text
.substring(lineStart
, lineEnd
+ 1);
6407 lineStart
= lineEnd
+ 1;
6409 var lineHashExists
= lineHash
.hasOwnProperty
? lineHash
.hasOwnProperty(line
) : lineHash
[line
] !== undefined;
6411 if (lineHashExists
) {
6412 chars
+= String
.fromCharCode(lineHash
[line
]);
6414 chars
+= String
.fromCharCode(lineArrayLength
);
6415 lineHash
[line
] = lineArrayLength
;
6416 lineArray
[lineArrayLength
++] = line
;
6422 chars1
= diffLinesToCharsMunge(text1
);
6423 chars2
= diffLinesToCharsMunge(text2
);
6427 lineArray
: lineArray
6432 * Rehydrate the text in a diff from a string of line hashes to real lines of
6434 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6435 * @param {!Array.<string>} lineArray Array of unique strings.
6438 DiffMatchPatch
.prototype.diffCharsToLines = function (diffs
, lineArray
) {
6439 var x
, chars
, text
, y
;
6440 for (x
= 0; x
< diffs
.length
; x
++) {
6441 chars
= diffs
[x
][1];
6443 for (y
= 0; y
< chars
.length
; y
++) {
6444 text
[y
] = lineArray
[chars
.charCodeAt(y
)];
6446 diffs
[x
][1] = text
.join("");
6451 * Reorder and merge like edit sections. Merge equalities.
6452 * Any edit section can move as long as it doesn't cross an equality.
6453 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6455 DiffMatchPatch
.prototype.diffCleanupMerge = function (diffs
) {
6456 var pointer
, countDelete
, countInsert
, textInsert
, textDelete
, commonlength
, changes
, diffPointer
, position
;
6457 diffs
.push([DIFF_EQUAL
, ""]); // Add a dummy entry at the end.
6464 while (pointer
< diffs
.length
) {
6465 switch (diffs
[pointer
][0]) {
6468 textInsert
+= diffs
[pointer
][1];
6473 textDelete
+= diffs
[pointer
][1];
6478 // Upon reaching an equality, check for prior redundancies.
6479 if (countDelete
+ countInsert
> 1) {
6480 if (countDelete
!== 0 && countInsert
!== 0) {
6482 // Factor out any common prefixes.
6483 commonlength
= this.diffCommonPrefix(textInsert
, textDelete
);
6484 if (commonlength
!== 0) {
6485 if (pointer
- countDelete
- countInsert
> 0 && diffs
[pointer
- countDelete
- countInsert
- 1][0] === DIFF_EQUAL
) {
6486 diffs
[pointer
- countDelete
- countInsert
- 1][1] += textInsert
.substring(0, commonlength
);
6488 diffs
.splice(0, 0, [DIFF_EQUAL
, textInsert
.substring(0, commonlength
)]);
6491 textInsert
= textInsert
.substring(commonlength
);
6492 textDelete
= textDelete
.substring(commonlength
);
6495 // Factor out any common suffixies.
6496 commonlength
= this.diffCommonSuffix(textInsert
, textDelete
);
6497 if (commonlength
!== 0) {
6498 diffs
[pointer
][1] = textInsert
.substring(textInsert
.length
- commonlength
) + diffs
[pointer
][1];
6499 textInsert
= textInsert
.substring(0, textInsert
.length
- commonlength
);
6500 textDelete
= textDelete
.substring(0, textDelete
.length
- commonlength
);
6504 // Delete the offending records and add the merged ones.
6505 if (countDelete
=== 0) {
6506 diffs
.splice(pointer
- countInsert
, countDelete
+ countInsert
, [DIFF_INSERT
, textInsert
]);
6507 } else if (countInsert
=== 0) {
6508 diffs
.splice(pointer
- countDelete
, countDelete
+ countInsert
, [DIFF_DELETE
, textDelete
]);
6510 diffs
.splice(pointer
- countDelete
- countInsert
, countDelete
+ countInsert
, [DIFF_DELETE
, textDelete
], [DIFF_INSERT
, textInsert
]);
6512 pointer
= pointer
- countDelete
- countInsert
+ (countDelete
? 1 : 0) + (countInsert
? 1 : 0) + 1;
6513 } else if (pointer
!== 0 && diffs
[pointer
- 1][0] === DIFF_EQUAL
) {
6515 // Merge this equality with the previous one.
6516 diffs
[pointer
- 1][1] += diffs
[pointer
][1];
6517 diffs
.splice(pointer
, 1);
6528 if (diffs
[diffs
.length
- 1][1] === "") {
6529 diffs
.pop(); // Remove the dummy entry at the end.
6532 // Second pass: look for single edits surrounded on both sides by equalities
6533 // which can be shifted sideways to eliminate an equality.
6534 // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
6538 // Intentionally ignore the first and last element (don't need checking).
6539 while (pointer
< diffs
.length
- 1) {
6540 if (diffs
[pointer
- 1][0] === DIFF_EQUAL
&& diffs
[pointer
+ 1][0] === DIFF_EQUAL
) {
6542 diffPointer
= diffs
[pointer
][1];
6543 position
= diffPointer
.substring(diffPointer
.length
- diffs
[pointer
- 1][1].length
);
6545 // This is a single edit surrounded by equalities.
6546 if (position
=== diffs
[pointer
- 1][1]) {
6548 // Shift the edit over the previous equality.
6549 diffs
[pointer
][1] = diffs
[pointer
- 1][1] + diffs
[pointer
][1].substring(0, diffs
[pointer
][1].length
- diffs
[pointer
- 1][1].length
);
6550 diffs
[pointer
+ 1][1] = diffs
[pointer
- 1][1] + diffs
[pointer
+ 1][1];
6551 diffs
.splice(pointer
- 1, 1);
6553 } else if (diffPointer
.substring(0, diffs
[pointer
+ 1][1].length
) === diffs
[pointer
+ 1][1]) {
6555 // Shift the edit over the next equality.
6556 diffs
[pointer
- 1][1] += diffs
[pointer
+ 1][1];
6557 diffs
[pointer
][1] = diffs
[pointer
][1].substring(diffs
[pointer
+ 1][1].length
) + diffs
[pointer
+ 1][1];
6558 diffs
.splice(pointer
+ 1, 1);
6565 // If shifts were made, the diff needs reordering and another shift sweep.
6567 this.diffCleanupMerge(diffs
);
6571 return function (o
, n
) {
6572 var diff
, output
, text
;
6573 diff
= new DiffMatchPatch();
6574 output
= diff
.DiffMain(o
, n
);
6575 diff
.diffCleanupEfficiency(output
);
6576 text
= diff
.diffPrettyHtml(output
);
6582 }((function() { return this; }())));