5 * Copyright OpenJS Foundation and other contributors
6 * Released under the MIT license
7 * https://jquery.org/license
12 function _typeof(obj) {
13 "@babel/helpers - typeof";
15 return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
18 return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
21 function _classCallCheck(instance, Constructor) {
22 if (!(instance instanceof Constructor)) {
23 throw new TypeError("Cannot call a class as a function");
26 function _defineProperties(target, props) {
27 for (var i = 0; i < props.length; i++) {
28 var descriptor = props[i];
29 descriptor.enumerable = descriptor.enumerable || false;
30 descriptor.configurable = true;
31 if ("value" in descriptor) descriptor.writable = true;
32 Object.defineProperty(target, descriptor.key, descriptor);
35 function _createClass(Constructor, protoProps, staticProps) {
36 if (protoProps) _defineProperties(Constructor.prototype, protoProps);
37 if (staticProps) _defineProperties(Constructor, staticProps);
38 Object.defineProperty(Constructor, "prototype", {
43 function _slicedToArray(arr, i) {
44 return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
46 function _toConsumableArray(arr) {
47 return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
49 function _arrayWithoutHoles(arr) {
50 if (Array.isArray(arr)) return _arrayLikeToArray(arr);
52 function _arrayWithHoles(arr) {
53 if (Array.isArray(arr)) return arr;
55 function _iterableToArray(iter) {
56 if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
58 function _iterableToArrayLimit(arr, i) {
59 var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
60 if (_i == null) return;
66 for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
68 if (i && _arr.length === i) break;
75 if (!_n && _i["return"] != null) _i["return"]();
82 function _unsupportedIterableToArray(o, minLen) {
84 if (typeof o === "string") return _arrayLikeToArray(o, minLen);
85 var n = Object.prototype.toString.call(o).slice(8, -1);
86 if (n === "Object" && o.constructor) n = o.constructor.name;
87 if (n === "Map" || n === "Set") return Array.from(o);
88 if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
90 function _arrayLikeToArray(arr, len) {
91 if (len == null || len > arr.length) len = arr.length;
92 for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
95 function _nonIterableSpread() {
96 throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
98 function _nonIterableRest() {
99 throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
101 function _createForOfIteratorHelper(o, allowArrayLike) {
102 var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
104 if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
107 var F = function () {};
111 if (i >= o.length) return {
125 throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
127 var normalCompletion = true,
135 var step = it.next();
136 normalCompletion = step.done;
145 if (!normalCompletion && it.return != null) it.return();
147 if (didErr) throw err;
153 // We don't use global-this-polyfill [1], because it modifies
154 // the globals scope by default. QUnit must not affect the host context
155 // as developers may test their project may be such a polyfill, and/or
156 // they may intentionally test their project with and without certain
157 // polyfills and we must not affect that. It also uses an obscure
158 // mechanism that seems to sometimes causes a runtime error in older
159 // browsers (specifically Safari and IE versions that support
160 // Object.defineProperty but then report _T_ as undefined).
161 // [1] https://github.com/ungap/global-this/blob/v0.4.4/esm/index.js
163 // Another way is `Function('return this')()`, but doing so relies
164 // on eval which will cause a CSP error on some servers.
166 // Instead, simply check the four options that already exist
167 // in all supported environments.
168 function getGlobalThis() {
169 if (typeof globalThis !== 'undefined') {
170 // For SpiderMonkey, modern browsers, and recent Node.js
171 // eslint-disable-next-line no-undef
174 if (typeof self !== 'undefined') {
176 // eslint-disable-next-line no-undef
179 if (typeof window$1 !== 'undefined') {
180 // For document context in browsers
183 if (typeof global !== 'undefined') {
185 // eslint-disable-next-line no-undef
188 throw new Error('Unable to locate global object');
191 // This avoids a simple `export const` assignment as that would lead Rollup
192 // to change getGlobalThis and use the same (generated) variable name there.
193 var g = getGlobalThis();
194 var window$1 = g.window;
195 var console$1 = g.console;
196 var setTimeout$1 = g.setTimeout;
197 var clearTimeout = g.clearTimeout;
198 var document = window$1 && window$1.document;
199 var navigator = window$1 && window$1.navigator;
200 var localSessionStorage = function () {
201 var x = 'qunit-test-string';
203 g.sessionStorage.setItem(x, x);
204 g.sessionStorage.removeItem(x);
205 return g.sessionStorage;
211 // Basic fallback for ES6 Map
212 // Support: IE 9-10, Safari 7, PhantomJS; Map is undefined
213 // Support: iOS 8; `new Map(iterable)` is not supported
215 // Fallback for ES7 Map#keys
216 // Support: IE 11; Map#keys is undefined
217 var StringMap = typeof g.Map === 'function' && typeof g.Map.prototype.keys === 'function' && typeof g.Symbol === 'function' && _typeof(g.Symbol.iterator) === 'symbol' ? g.Map : function StringMap(input) {
219 var store = Object.create(null);
220 var hasOwn = Object.prototype.hasOwnProperty;
221 this.has = function (strKey) {
222 return hasOwn.call(store, strKey);
224 this.get = function (strKey) {
225 return store[strKey];
227 this.set = function (strKey, val) {
228 if (!hasOwn.call(store, strKey)) {
234 this.delete = function (strKey) {
235 if (hasOwn.call(store, strKey)) {
236 delete store[strKey];
240 this.forEach = function (callback) {
241 for (var strKey in store) {
242 callback(store[strKey], strKey);
245 this.keys = function () {
246 return Object.keys(store);
248 this.clear = function () {
249 store = Object.create(null);
254 input.forEach(function (val, strKey) {
255 _this.set(strKey, val);
260 // Basic fallback for ES6 Set
261 // Support: IE 11, `new Set(iterable)` parameter not yet implemented
262 // Test for Set#values() which came after Set(iterable).
263 var StringSet = typeof g.Set === 'function' && typeof g.Set.prototype.values === 'function' ? g.Set : function (input) {
264 var set = Object.create(null);
265 if (Array.isArray(input)) {
266 input.forEach(function (item) {
271 add: function add(value) {
274 has: function has(value) {
278 return Object.keys(set).length;
283 var toString = Object.prototype.toString;
284 var hasOwn$1 = Object.prototype.hasOwnProperty;
286 // eslint-disable-next-line compat/compat -- Checked
287 now: window$1 && window$1.performance && window$1.performance.now ? window$1.performance.now.bind(window$1.performance) : Date.now
290 // Returns a new Array with the elements that are in a but not in b
291 function diff(a, b) {
292 return a.filter(function (a) {
293 return b.indexOf(a) === -1;
298 * Determines whether an element exists in a given array or not.
302 * @param {Array} array
305 var inArray = Array.prototype.includes ? function (elem, array) {
306 return array.includes(elem);
307 } : function (elem, array) {
308 return array.indexOf(elem) !== -1;
312 * Recursively clone an object into a plain array or object, taking only the
313 * own enumerable properties.
316 * @param {bool} [allowArray=true]
317 * @return {Object|Array}
319 function objectValues(obj) {
320 var allowArray = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
321 var vals = allowArray && is('array', obj) ? [] : {};
322 for (var key in obj) {
323 if (hasOwn$1.call(obj, key)) {
325 vals[key] = val === Object(val) ? objectValues(val, allowArray) : val;
332 * Recursively clone an object into a plain object, taking only the
333 * subset of own enumerable properties that exist a given model.
339 function objectValuesSubset(obj, model) {
340 // Return primitive values unchanged to avoid false positives or confusing
341 // results from assert.propContains().
342 // E.g. an actual null or false wrongly equaling an empty object,
343 // or an actual string being reported as object not matching a partial object.
344 if (obj !== Object(obj)) {
348 // Unlike objectValues(), subset arrays to a plain objects as well.
349 // This enables subsetting [20, 30] with {1: 30}.
351 for (var key in model) {
352 if (hasOwn$1.call(model, key) && hasOwn$1.call(obj, key)) {
353 subset[key] = objectValuesSubset(obj[key], model[key]);
358 function extend(a, b, undefOnly) {
359 for (var prop in b) {
360 if (hasOwn$1.call(b, prop)) {
361 if (b[prop] === undefined) {
363 } else if (!(undefOnly && typeof a[prop] !== 'undefined')) {
370 function objectType(obj) {
371 if (typeof obj === 'undefined') {
375 // Consider: typeof null === object
379 var match = toString.call(obj).match(/^\[object\s(.*)\]$/);
380 var type = match && match[1];
396 return type.toLowerCase();
402 // Safe object type checking
403 function is(type, obj) {
404 return objectType(obj) === type;
407 // Based on Java's String.hashCode, a simple but not
408 // rigorously collision resistant hashing function
409 function generateHash(module, testName) {
410 var str = module + '\x1C' + testName;
412 for (var i = 0; i < str.length; i++) {
413 hash = (hash << 5) - hash + str.charCodeAt(i);
417 // Convert the possibly negative integer hash code into an 8 character hex string, which isn't
418 // strictly necessary but increases user understanding that the id is a SHA-like hash
419 var hex = (0x100000000 + hash).toString(16);
420 if (hex.length < 8) {
421 hex = '0000000' + hex;
423 return hex.slice(-8);
427 * Converts an error into a simple string for comparisons.
429 * @param {Error|any} error
432 function errorString(error) {
433 // Use String() instead of toString() to handle non-object values like undefined or null.
434 var resultErrorString = String(error);
436 // If the error wasn't a subclass of Error but something like
437 // an object literal with name and message properties...
438 if (resultErrorString.slice(0, 7) === '[object') {
439 // Based on https://es5.github.io/#x15.11.4.4
440 return (error.name || 'Error') + (error.message ? ": ".concat(error.message) : '');
442 return resultErrorString;
446 var BOXABLE_TYPES = new StringSet(['boolean', 'number', 'string']);
448 // Memory for previously seen containers (object, array, map, set).
449 // Used for recursion detection, and to avoid repeated comparison.
451 // Elements are { a: val, b: val }.
453 function useStrictEquality(a, b) {
456 function useObjectValueEquality(a, b) {
457 return a === b || a.valueOf() === b.valueOf();
459 function compareConstructors(a, b) {
460 // Comparing constructors is more strict than using `instanceof`
461 return getConstructor(a) === getConstructor(b);
463 function getConstructor(obj) {
464 var proto = Object.getPrototypeOf(obj);
466 // If the obj prototype descends from a null constructor, treat it
467 // as a null prototype.
468 // Ref https://github.com/qunitjs/qunit/issues/851
470 // Allow objects with no prototype, from Object.create(null), to be equivalent to
471 // plain objects that have Object as their constructor.
472 return !proto || proto.constructor === null ? Object : obj.constructor;
474 function getRegExpFlags(regexp) {
475 return 'flags' in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
478 // Specialised comparisons after entryTypeCallbacks.object, based on `objectType()`
479 var objTypeCallbacks = {
480 undefined: useStrictEquality,
481 null: useStrictEquality,
482 // Handle boxed boolean
483 boolean: useObjectValueEquality,
484 number: function number(a, b) {
485 // Handle NaN and boxed number
486 return a === b || a.valueOf() === b.valueOf() || isNaN(a.valueOf()) && isNaN(b.valueOf());
488 // Handle boxed string
489 string: useObjectValueEquality,
490 symbol: useStrictEquality,
491 date: useObjectValueEquality,
492 nan: function nan() {
495 regexp: function regexp(a, b) {
496 return a.source === b.source &&
497 // Include flags in the comparison
498 getRegExpFlags(a) === getRegExpFlags(b);
500 // identical reference only
501 function: useStrictEquality,
502 array: function array(a, b) {
503 if (a.length !== b.length) {
507 for (var i = 0; i < a.length; i++) {
508 if (!typeEquiv(a[i], b[i])) {
514 // Define sets a and b to be equivalent if for each element aVal in a, there
515 // is some element bVal in b such that aVal and bVal are equivalent. Element
516 // repetitions are not counted, so these are equivalent:
517 // a = new Set( [ X={}, Y=[], Y ] );
518 // b = new Set( [ Y, X, X ] );
519 set: function set(a, b) {
520 if (a.size !== b.size) {
521 // This optimization has certain quirks because of the lack of
522 // repetition counting. For instance, adding the same
523 // (reference-identical) element to two equivalent sets can
524 // make them non-equivalent.
528 a.forEach(function (aVal) {
529 // Short-circuit if the result is already known. (Using for...of
530 // with a break clause would be cleaner here, but it would cause
531 // a syntax error on older JavaScript implementations even if
537 b.forEach(function (bVal) {
538 // Likewise, short-circuit if the result is already known
543 // Swap out the global memory, as nested typeEquiv() would clobber it
544 var originalMemory = memory;
546 if (typeEquiv(bVal, aVal)) {
550 memory = originalMemory;
558 // Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
559 // in a, there is some key-value pair (bKey, bVal) in b such that
560 // [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
561 // counted, so these are equivalent:
562 // a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
563 // b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
564 map: function map(a, b) {
565 if (a.size !== b.size) {
566 // This optimization has certain quirks because of the lack of
567 // repetition counting. For instance, adding the same
568 // (reference-identical) key-value pair to two equivalent maps
569 // can make them non-equivalent.
573 a.forEach(function (aVal, aKey) {
574 // Short-circuit if the result is already known. (Using for...of
575 // with a break clause would be cleaner here, but it would cause
576 // a syntax error on older JavaScript implementations even if
582 b.forEach(function (bVal, bKey) {
583 // Likewise, short-circuit if the result is already known
588 // Swap out the global memory, as nested typeEquiv() would clobber it
589 var originalMemory = memory;
591 if (objTypeCallbacks.array([bVal, bKey], [aVal, aKey])) {
595 memory = originalMemory;
605 // Entry points from typeEquiv, based on `typeof`
606 var entryTypeCallbacks = {
607 undefined: useStrictEquality,
608 null: useStrictEquality,
609 boolean: useStrictEquality,
610 number: function number(a, b) {
612 return a === b || isNaN(a) && isNaN(b);
614 string: useStrictEquality,
615 symbol: useStrictEquality,
616 function: useStrictEquality,
617 object: function object(a, b) {
618 // Handle memory (skip recursion)
619 if (memory.some(function (pair) {
620 return pair.a === a && pair.b === b;
628 var aObjType = objectType(a);
629 var bObjType = objectType(b);
630 if (aObjType !== 'object' || bObjType !== 'object') {
631 // Handle literal `null`
632 // Handle: Array, Map/Set, Date, Regxp/Function, boxed primitives
633 return aObjType === bObjType && objTypeCallbacks[aObjType](a, b);
636 // NOTE: Literal null must not make it here as it would throw
637 if (compareConstructors(a, b) === false) {
640 var aProperties = [];
641 var bProperties = [];
643 // Be strict and go deep, no filtering with hasOwnProperty.
645 // Collect a's properties
648 // Skip OOP methods that look the same
649 if (a.constructor !== Object && typeof a.constructor !== 'undefined' && typeof a[i] === 'function' && typeof b[i] === 'function' && a[i].toString() === b[i].toString()) {
652 if (!typeEquiv(a[i], b[i])) {
657 // Collect b's properties
658 bProperties.push(_i);
660 return objTypeCallbacks.array(aProperties.sort(), bProperties.sort());
663 function typeEquiv(a, b) {
664 // Optimization: Only perform type-specific comparison when pairs are not strictly equal.
668 var aType = _typeof(a);
669 var bType = _typeof(b);
670 if (aType !== bType) {
671 // Support comparing primitive to boxed primitives
672 // Try again after possibly unwrapping one
673 return (aType === 'object' && BOXABLE_TYPES.has(objectType(a)) ? a.valueOf() : a) === (bType === 'object' && BOXABLE_TYPES.has(objectType(b)) ? b.valueOf() : b);
675 return entryTypeCallbacks[aType](a, b);
677 function innerEquiv(a, b) {
678 var res = typeEquiv(a, b);
679 // Release any retained objects and reset recursion detection for next call
685 * Test any two types of JavaScript values for equality.
687 * @author Philippe Rathé <prathe@gmail.com>
688 * @author David Chan <david@troi.org>
690 function equiv(a, b) {
691 if (arguments.length === 2) {
692 return a === b || innerEquiv(a, b);
695 // Given 0 or 1 arguments, just return true (nothing to compare).
696 // Given (A,B,C,D) compare C,D then B,C then A,B.
697 var i = arguments.length - 1;
699 if (!innerEquiv(arguments[i - 1], arguments[i])) {
708 * Config object: Maintain internal state
709 * Later exposed as QUnit.config
710 * `config` initialized at top of scope
713 // HTML Reporter: Modify document.title when suite is done
715 // HTML Reporter: collapse every test except the first failing test
716 // If false, all failing tests will be expanded
718 // whether or not to fail when there are zero tests
719 // defaults to `true`
720 failOnZeroTests: true,
721 // Select by pattern or case-insensitive substring match against "moduleName: testName"
723 // Depth up-to which object will be dumped
725 // Select case-insensitive match of the module name
727 // HTML Reporter: Select module/test by array of internal IDs
729 // By default, run previously failed tests first
730 // very useful in combination with "Hide passed tests" checked
732 // When enabled, all tests must call expect()
733 requireExpects: false,
734 // By default, scroll to top of the page when suite is done
736 // The storage module to use for reordering tests
737 storage: localSessionStorage,
739 // HTML Reporter: List of URL parameters that are given visual controls
741 // Internal: The first unnamed module
743 // By being defined as the intial value for currentModule, it is the
744 // receptacle and implied parent for any global tests. It is as if we
745 // called `QUnit.module( "" );` before any other tests were defined.
747 // If we reach begin() and no tests were put in it, we dequeue it as if it
748 // never existed, and in that case never expose it to the events and
751 // When global tests are defined, then this unnamed module will execute
752 // as any other module, including moduleStart/moduleDone events etc.
754 // Since this module isn't explicitly created by the user, they have no
755 // access to add hooks for it. The hooks object is defined to comply
756 // with internal expectations of test.js, but they will be empty.
757 // To apply hooks, place tests explicitly in a QUnit.module(), and use
758 // its hooks accordingly.
760 // For global hooks that apply to all tests and all modules, use QUnit.hooks.
762 // NOTE: This is *not* a "global module". It is not an ancestor of all modules
763 // and tests. It is merely the parent for any tests defined globally,
764 // before the first QUnit.module(). As such, the events for this unnamed
765 // module will fire as normal, right after its last test, and *not* at
766 // the end of the test run.
768 // NOTE: This also should probably also not become a global module, unless
769 // we keep it out of the public API. For example, it would likely not
770 // improve the user interface and plugin behaviour if all modules became
771 // wrapped between the start and end events of this module, and thus
772 // needlessly add indentation, indirection, or other visible noise.
773 // Unit tests for the callbacks API would detect that as a regression.
787 // Internal: Exposed to make resets easier
788 // Ref https://github.com/qunitjs/qunit/pull/1598
802 // Apply a predefined QUnit.config object
804 // Ignore QUnit.config if it is a QUnit distribution instead of preconfig.
805 // That means QUnit was loaded twice! (Use the same approach as export.js)
806 var preConfig = g && g.QUnit && !g.QUnit.version && g.QUnit.config;
808 extend(config, preConfig);
811 // Push a loose unnamed module to the modules collection
812 config.modules.push(config.currentModule);
814 var dump = (function () {
815 function quote(str) {
816 return '"' + str.toString().replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
818 function literal(o) {
821 function join(pre, arr, post) {
822 var s = dump.separator();
823 var inner = dump.indent(1);
825 arr = arr.join(',' + s + inner);
830 var base = dump.indent();
831 return [pre, inner + arr, base + post].join(s);
833 function array(arr, stack) {
834 if (dump.maxDepth && dump.depth > dump.maxDepth) {
835 return '[object Array]';
839 var ret = new Array(i);
841 ret[i] = this.parse(arr[i], undefined, stack);
844 return join('[', ret, ']');
846 function isArray(obj) {
849 toString.call(obj) === '[object Array]' ||
851 typeof obj.length === 'number' && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
854 var reName = /^function (\w+)/;
856 // The objType is used mostly internally, you can fix a (custom) type in advance
857 parse: function parse(obj, objType, stack) {
859 var objIndex = stack.indexOf(obj);
860 if (objIndex !== -1) {
861 return "recursion(".concat(objIndex - stack.length, ")");
863 objType = objType || this.typeOf(obj);
864 var parser = this.parsers[objType];
865 var parserType = _typeof(parser);
866 if (parserType === 'function') {
868 var res = parser.call(this, obj, stack);
872 if (parserType === 'string') {
875 return '[ERROR: Missing QUnit.dump formatter for type ' + objType + ']';
877 typeOf: function typeOf(obj) {
881 } else if (typeof obj === 'undefined') {
883 } else if (is('regexp', obj)) {
885 } else if (is('date', obj)) {
887 } else if (is('function', obj)) {
889 } else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
891 } else if (obj.nodeType === 9) {
893 } else if (obj.nodeType) {
895 } else if (isArray(obj)) {
897 } else if (obj.constructor === Error.prototype.constructor) {
904 separator: function separator() {
905 if (this.multiline) {
906 return this.HTML ? '<br />' : '\n';
908 return this.HTML ? ' ' : ' ';
911 // Extra can be a number, shortcut for increasing-calling-decreasing
912 indent: function indent(extra) {
913 if (!this.multiline) {
916 var chr = this.indentChar;
918 chr = chr.replace(/\t/g, ' ').replace(/ /g, ' ');
920 return new Array(this.depth + (extra || 0)).join(chr);
923 this.depth += a || 1;
925 down: function down(a) {
926 this.depth -= a || 1;
928 setParser: function setParser(name, parser) {
929 this.parsers[name] = parser;
931 // The next 3 are exposed so you can use them
936 maxDepth: config.maxDepth,
937 // This is the list of parsers, to modify them, use dump.setParser
940 document: '[Document]',
941 error: function error(_error) {
942 return 'Error("' + _error.message + '")';
944 // This has been unused since QUnit 1.0.0.
945 // @todo Deprecate and remove.
946 unknown: '[Unknown]',
948 undefined: 'undefined',
949 function: function _function(fn) {
950 var ret = 'function';
952 // Functions never have name in IE
953 var name = 'name' in fn ? fn.name : (reName.exec(fn) || [])[1];
958 ret = [ret, dump.parse(fn, 'functionArgs'), '){'].join('');
959 return join(ret, dump.parse(fn, 'functionCode'), '}');
964 object: function object(map, stack) {
966 if (dump.maxDepth && dump.depth > dump.maxDepth) {
967 return '[object Object]';
971 for (var key in map) {
975 // Some properties are not always enumerable on Error objects.
976 var nonEnumerableProperties = ['message', 'name'];
977 for (var i in nonEnumerableProperties) {
978 var _key = nonEnumerableProperties[i];
979 if (_key in map && !inArray(_key, keys)) {
984 for (var _i = 0; _i < keys.length; _i++) {
985 var _key2 = keys[_i];
986 var val = map[_key2];
987 ret.push(dump.parse(_key2, 'key') + ': ' + dump.parse(val, undefined, stack));
990 return join('{', ret, '}');
992 node: function node(_node) {
993 var open = dump.HTML ? '<' : '<';
994 var close = dump.HTML ? '>' : '>';
995 var tag = _node.nodeName.toLowerCase();
996 var ret = open + tag;
997 var attrs = _node.attributes;
999 for (var i = 0; i < attrs.length; i++) {
1000 var val = attrs[i].nodeValue;
1002 // IE6 includes all attributes in .attributes, even ones not explicitly
1003 // set. Those have values like undefined, null, 0, false, "" or
1005 if (val && val !== 'inherit') {
1006 ret += ' ' + attrs[i].nodeName + '=' + dump.parse(val, 'attribute');
1012 // Show content of TextNode or CDATASection
1013 if (_node.nodeType === 3 || _node.nodeType === 4) {
1014 ret += _node.nodeValue;
1016 return ret + open + '/' + tag + close;
1018 // Function calls it internally, it's the arguments part of the function
1019 functionArgs: function functionArgs(fn) {
1024 var args = new Array(l);
1027 args[l] = String.fromCharCode(97 + l);
1029 return ' ' + args.join(', ') + ' ';
1031 // Object calls it internally, the key part of an item in a map
1033 // Function calls it internally, it's the content of the function
1034 functionCode: '[code]',
1035 // Node calls it internally, it's a html attribute value
1042 symbol: function symbol(sym) {
1043 return sym.toString();
1046 // If true, entities are escaped ( <, >, \t, space and \n )
1050 // If true, items in a collection, are separated by a \n, else just a space.
1057 // Detect if the console object exists and no-op otherwise.
1058 // This allows support for IE 9, which doesn't have a console
1059 // object if the developer tools are not open.
1062 // Function#bind is supported, but no console.log.bind().
1064 // Support: SpiderMonkey (mozjs 68+)
1065 // The console object has a log method, but no warn method.
1068 warn: console$1 ? Function.prototype.bind.call(console$1.warn || console$1.log, console$1) : function () {}
1071 var SuiteReport = /*#__PURE__*/function () {
1072 function SuiteReport(name, parentSuite) {
1073 _classCallCheck(this, SuiteReport);
1075 this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
1077 // When an "error" event is emitted from onUncaughtException(), the
1078 // "runEnd" event should report the status as failed. The "runEnd" event data
1079 // is tracked through this property (via the "runSuite" instance).
1080 this.globalFailureCount = 0;
1082 this.childSuites = [];
1084 parentSuite.pushChildSuite(this);
1087 _createClass(SuiteReport, [{
1089 value: function start(recordTime) {
1091 this._startTime = performance.now();
1095 fullName: this.fullName.slice(),
1096 tests: this.tests.map(function (test) {
1097 return test.start();
1099 childSuites: this.childSuites.map(function (suite) {
1100 return suite.start();
1103 total: this.getTestCounts().total
1109 value: function end(recordTime) {
1111 this._endTime = performance.now();
1115 fullName: this.fullName.slice(),
1116 tests: this.tests.map(function (test) {
1119 childSuites: this.childSuites.map(function (suite) {
1122 testCounts: this.getTestCounts(),
1123 runtime: this.getRuntime(),
1124 status: this.getStatus()
1128 key: "pushChildSuite",
1129 value: function pushChildSuite(suite) {
1130 this.childSuites.push(suite);
1134 value: function pushTest(test) {
1135 this.tests.push(test);
1139 value: function getRuntime() {
1140 return Math.round(this._endTime - this._startTime);
1143 key: "getTestCounts",
1144 value: function getTestCounts() {
1145 var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
1152 counts.failed += this.globalFailureCount;
1153 counts.total += this.globalFailureCount;
1154 counts = this.tests.reduce(function (counts, test) {
1156 counts[test.getStatus()]++;
1161 return this.childSuites.reduce(function (counts, suite) {
1162 return suite.getTestCounts(counts);
1167 value: function getStatus() {
1168 var _this$getTestCounts = this.getTestCounts(),
1169 total = _this$getTestCounts.total,
1170 failed = _this$getTestCounts.failed,
1171 skipped = _this$getTestCounts.skipped,
1172 todo = _this$getTestCounts.todo;
1176 if (skipped === total) {
1178 } else if (todo === total) {
1189 var moduleStack = [];
1190 var runSuite = new SuiteReport();
1191 function isParentModuleInQueue() {
1192 var modulesInQueue = config.modules.filter(function (module) {
1193 return !module.ignored;
1194 }).map(function (module) {
1195 return module.moduleId;
1197 return moduleStack.some(function (module) {
1198 return modulesInQueue.includes(module.moduleId);
1201 function createModule(name, testEnvironment, modifiers) {
1202 var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
1203 var moduleName = parentModule !== null ? [parentModule.name, name].join(' > ') : name;
1204 var parentSuite = parentModule ? parentModule.suiteReport : runSuite;
1205 var skip = parentModule !== null && parentModule.skip || modifiers.skip;
1206 var todo = parentModule !== null && parentModule.todo || modifiers.todo;
1209 extend(env, parentModule.testEnvironment);
1211 extend(env, testEnvironment);
1214 parentModule: parentModule,
1221 testEnvironment: env,
1223 moduleId: generateHash(moduleName),
1227 suiteReport: new SuiteReport(name, parentSuite),
1228 // Initialised by test.js when the module start executing,
1229 // i.e. before the first test in this module (or a child).
1231 // Pass along `skip` and `todo` properties from parent module, in case
1232 // there is one, to childs. And use own otherwise.
1233 // This property will be used to mark own tests and tests of child suites
1234 // as either `skipped` or `todo`.
1236 todo: skip ? false : todo,
1237 ignored: modifiers.ignored || false
1240 parentModule.childModules.push(module);
1242 config.modules.push(module);
1245 function setHookFromEnvironment(hooks, environment, name) {
1246 var potentialHook = environment[name];
1247 if (typeof potentialHook === 'function') {
1248 hooks[name].push(potentialHook);
1250 delete environment[name];
1252 function makeSetHook(module, hookName) {
1253 return function setHook(callback) {
1254 if (config.currentModule !== module) {
1255 Logger.warn('The `' + hookName + '` hook was called inside the wrong module (`' + config.currentModule.name + '`). ' + 'Instead, use hooks provided by the callback to the containing module (`' + module.name + '`). ' + 'This will become an error in QUnit 3.0.');
1257 module.hooks[hookName].push(callback);
1260 function processModule(name, options, executeNow) {
1261 var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
1262 if (typeof options === 'function') {
1263 executeNow = options;
1264 options = undefined;
1266 var module = createModule(name, options, modifiers);
1268 // Transfer any initial hooks from the options object to the 'hooks' object
1269 var testEnvironment = module.testEnvironment;
1270 var hooks = module.hooks;
1271 setHookFromEnvironment(hooks, testEnvironment, 'before');
1272 setHookFromEnvironment(hooks, testEnvironment, 'beforeEach');
1273 setHookFromEnvironment(hooks, testEnvironment, 'afterEach');
1274 setHookFromEnvironment(hooks, testEnvironment, 'after');
1276 before: makeSetHook(module, 'before'),
1277 beforeEach: makeSetHook(module, 'beforeEach'),
1278 afterEach: makeSetHook(module, 'afterEach'),
1279 after: makeSetHook(module, 'after')
1281 var prevModule = config.currentModule;
1282 config.currentModule = module;
1283 if (typeof executeNow === 'function') {
1284 moduleStack.push(module);
1286 var cbReturnValue = executeNow.call(module.testEnvironment, moduleFns);
1287 if (cbReturnValue && typeof cbReturnValue.then === 'function') {
1288 Logger.warn('Returning a promise from a module callback is not supported. ' + 'Instead, use hooks for async behavior. ' + 'This will become an error in QUnit 3.0.');
1291 // If the module closure threw an uncaught error during the load phase,
1292 // we let this bubble up to global error handlers. But, not until after
1293 // we teardown internal state to ensure correct module nesting.
1294 // Ref https://github.com/qunitjs/qunit/issues/1478.
1296 config.currentModule = module.parentModule || prevModule;
1300 var focused$1 = false; // indicates that the "only" filter was used
1302 function module$1(name, options, executeNow) {
1303 var ignored = focused$1 && !isParentModuleInQueue();
1304 processModule(name, options, executeNow, {
1308 module$1.only = function () {
1310 // Upon the first module.only() call,
1311 // delete any and all previously registered modules and tests.
1312 config.modules.length = 0;
1313 config.queue.length = 0;
1315 // Ignore any tests declared after this block within the same
1316 // module parent. https://github.com/qunitjs/qunit/issues/1645
1317 config.currentModule.ignored = true;
1320 processModule.apply(void 0, arguments);
1322 module$1.skip = function (name, options, executeNow) {
1326 processModule(name, options, executeNow, {
1330 module$1.todo = function (name, options, executeNow) {
1334 processModule(name, options, executeNow, {
1339 // Doesn't support IE9, it will return undefined on these browsers
1340 // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
1341 var fileName = (sourceFromStacktrace(0) || '').replace(/(:\d+)+\)?/, '')
1342 // Remove anything prior to the last slash (Unix/Windows)
1343 // from the last frame
1344 .replace(/.+[/\\]/, '');
1345 function extractStacktrace(e, offset) {
1346 offset = offset === undefined ? 4 : offset;
1348 var stack = e.stack.split('\n');
1349 if (/^error$/i.test(stack[0])) {
1354 for (var i = offset; i < stack.length; i++) {
1355 if (stack[i].indexOf(fileName) !== -1) {
1358 include.push(stack[i]);
1360 if (include.length) {
1361 return include.join('\n');
1364 return stack[offset];
1367 function sourceFromStacktrace(offset) {
1368 var error = new Error();
1370 // Support: Safari <=7 only, IE <=10 - 11 only
1371 // Not all browsers generate the `stack` property for `new Error()`, see also #636
1379 return extractStacktrace(error, offset);
1382 var Assert = /*#__PURE__*/function () {
1383 function Assert(testContext) {
1384 _classCallCheck(this, Assert);
1385 this.test = testContext;
1387 _createClass(Assert, [{
1389 value: function timeout(duration) {
1390 if (typeof duration !== 'number') {
1391 throw new Error('You must pass a number as the duration to assert.timeout');
1393 this.test.timeout = duration;
1395 // If a timeout has been set, clear it and reset with the new duration
1396 if (config.timeout) {
1397 clearTimeout(config.timeout);
1398 config.timeout = null;
1399 if (config.timeoutHandler && this.test.timeout > 0) {
1400 this.test.internalResetTimeout(this.test.timeout);
1405 // Documents a "step", which is a string value, in a test as a passing assertion
1408 value: function step(message) {
1409 var assertionMessage = message;
1410 var result = !!message;
1411 this.test.steps.push(message);
1412 if (typeof message === 'undefined' || message === '') {
1413 assertionMessage = 'You must provide a message to assert.step';
1414 } else if (typeof message !== 'string') {
1415 assertionMessage = 'You must provide a string value to assert.step';
1420 message: assertionMessage
1424 // Verifies the steps in a test match a given array of string values
1427 value: function verifySteps(steps, message) {
1428 // Since the steps array is just string values, we can clone with slice
1429 var actualStepsClone = this.test.steps.slice();
1430 this.deepEqual(actualStepsClone, steps, message);
1431 this.test.steps.length = 0;
1435 value: function expect(asserts) {
1436 if (arguments.length === 1) {
1437 this.test.expected = asserts;
1439 return this.test.expected;
1443 // Create a new async pause and return a new function that can release the pause.
1446 value: function async(count) {
1447 if (count === undefined) {
1449 } else if (typeof count !== 'number') {
1450 throw new TypeError('async takes number as an input');
1452 var requiredCalls = count;
1453 return this.test.internalStop(requiredCalls);
1456 // Exports test.push() to the user API
1457 // Alias of pushResult.
1460 value: function push(result, actual, expected, message, negative) {
1461 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).');
1462 var currentAssert = this instanceof Assert ? this : config.current.assert;
1463 return currentAssert.pushResult({
1473 value: function pushResult(resultInfo) {
1474 // Destructure of resultInfo = { result, actual, expected, message, negative }
1476 var currentTest = assert instanceof Assert && assert.test || config.current;
1478 // Backwards compatibility fix.
1479 // Allows the direct use of global exported assertions and QUnit.assert.*
1480 // Although, it's use is not recommended as it can leak assertions
1481 // to other tests from async tests, because we only get a reference to the current test,
1482 // not exactly the test where assertion were intended to be called.
1484 throw new Error('assertion outside test context, in ' + sourceFromStacktrace(2));
1486 if (!(assert instanceof Assert)) {
1487 assert = currentTest.assert;
1489 return assert.test.pushResult(resultInfo);
1493 value: function ok(result, message) {
1495 message = result ? 'okay' : "failed, expected argument to be truthy, was: ".concat(dump.parse(result));
1506 value: function notOk(result, message) {
1508 message = !result ? 'okay' : "failed, expected argument to be falsy, was: ".concat(dump.parse(result));
1519 value: function _true(result, message) {
1521 result: result === true,
1529 value: function _false(result, message) {
1531 result: result === false,
1539 value: function equal(actual, expected, message) {
1541 // eslint-disable-next-line eqeqeq
1542 result: expected == actual,
1550 value: function notEqual(actual, expected, message) {
1552 // eslint-disable-next-line eqeqeq
1553 result: expected != actual,
1562 value: function propEqual(actual, expected, message) {
1563 actual = objectValues(actual);
1564 expected = objectValues(expected);
1566 result: equiv(actual, expected),
1573 key: "notPropEqual",
1574 value: function notPropEqual(actual, expected, message) {
1575 actual = objectValues(actual);
1576 expected = objectValues(expected);
1578 result: !equiv(actual, expected),
1586 key: "propContains",
1587 value: function propContains(actual, expected, message) {
1588 actual = objectValuesSubset(actual, expected);
1590 // The expected parameter is usually a plain object, but clone it for
1591 // consistency with propEqual(), and to make it easy to explain that
1592 // inheritence is not considered (on either side), and to support
1593 // recursively checking subsets of nested objects.
1594 expected = objectValues(expected, false);
1596 result: equiv(actual, expected),
1603 key: "notPropContains",
1604 value: function notPropContains(actual, expected, message) {
1605 actual = objectValuesSubset(actual, expected);
1606 expected = objectValues(expected);
1608 result: !equiv(actual, expected),
1617 value: function deepEqual(actual, expected, message) {
1619 result: equiv(actual, expected),
1626 key: "notDeepEqual",
1627 value: function notDeepEqual(actual, expected, message) {
1629 result: !equiv(actual, expected),
1638 value: function strictEqual(actual, expected, message) {
1640 result: expected === actual,
1647 key: "notStrictEqual",
1648 value: function notStrictEqual(actual, expected, message) {
1650 result: expected !== actual,
1659 value: function throws(block, expected, message) {
1660 var _validateExpectedExce = validateExpectedExceptionArgs(expected, message, 'throws');
1661 var _validateExpectedExce2 = _slicedToArray(_validateExpectedExce, 2);
1662 expected = _validateExpectedExce2[0];
1663 message = _validateExpectedExce2[1];
1664 var currentTest = this instanceof Assert && this.test || config.current;
1665 if (typeof block !== 'function') {
1666 currentTest.assert.pushResult({
1669 message: 'The value provided to `assert.throws` in ' + '"' + currentTest.testName + '" was not a function.'
1675 currentTest.ignoreGlobalErrors = true;
1677 block.call(currentTest.testEnvironment);
1681 currentTest.ignoreGlobalErrors = false;
1683 var _validateException = validateException(actual, expected, message);
1684 var _validateException2 = _slicedToArray(_validateException, 3);
1685 result = _validateException2[0];
1686 expected = _validateException2[1];
1687 message = _validateException2[2];
1689 currentTest.assert.pushResult({
1691 // undefined if it didn't throw
1692 actual: actual && errorString(actual),
1699 value: function rejects(promise, expected, message) {
1700 var _validateExpectedExce3 = validateExpectedExceptionArgs(expected, message, 'rejects');
1701 var _validateExpectedExce4 = _slicedToArray(_validateExpectedExce3, 2);
1702 expected = _validateExpectedExce4[0];
1703 message = _validateExpectedExce4[1];
1704 var currentTest = this instanceof Assert && this.test || config.current;
1705 var then = promise && promise.then;
1706 if (typeof then !== 'function') {
1707 currentTest.assert.pushResult({
1709 message: 'The value provided to `assert.rejects` in ' + '"' + currentTest.testName + '" was not a promise.',
1714 var done = this.async();
1715 return then.call(promise, function handleFulfillment() {
1716 currentTest.assert.pushResult({
1718 message: 'The promise returned by the `assert.rejects` callback in ' + '"' + currentTest.testName + '" did not reject.',
1722 }, function handleRejection(actual) {
1724 var _validateException3 = validateException(actual, expected, message);
1725 var _validateException4 = _slicedToArray(_validateException3, 3);
1726 result = _validateException4[0];
1727 expected = _validateException4[1];
1728 message = _validateException4[2];
1729 currentTest.assert.pushResult({
1731 // leave rejection value of undefined as-is
1732 actual: actual && errorString(actual),
1742 function validateExpectedExceptionArgs(expected, message, assertionMethod) {
1743 var expectedType = objectType(expected);
1745 // 'expected' is optional unless doing string comparison
1746 if (expectedType === 'string') {
1747 if (message === undefined) {
1749 expected = undefined;
1750 return [expected, message];
1752 throw new Error('assert.' + assertionMethod + ' does not accept a string value for the expected argument.\n' + 'Use a non-string object value (e.g. RegExp or validator function) ' + 'instead if necessary.');
1755 var valid = !expected ||
1756 // TODO: be more explicit here
1757 expectedType === 'regexp' || expectedType === 'function' || expectedType === 'object';
1759 throw new Error('Invalid expected value type (' + expectedType + ') ' + 'provided to assert.' + assertionMethod + '.');
1761 return [expected, message];
1763 function validateException(actual, expected, message) {
1765 var expectedType = objectType(expected);
1767 // These branches should be exhaustive, based on validation done in validateExpectedException
1769 // We don't want to validate
1773 // Expected is a regexp
1774 } else if (expectedType === 'regexp') {
1775 result = expected.test(errorString(actual));
1777 // Log the string form of the regexp
1778 expected = String(expected);
1780 // Expected is a constructor, maybe an Error constructor.
1781 // Note the extra check on its prototype - this is an implicit
1782 // requirement of "instanceof", else it will throw a TypeError.
1783 } else if (expectedType === 'function' && expected.prototype !== undefined && actual instanceof expected) {
1786 // Expected is an Error object
1787 } else if (expectedType === 'object') {
1788 result = actual instanceof expected.constructor && actual.name === expected.name && actual.message === expected.message;
1790 // Log the string form of the Error object
1791 expected = errorString(expected);
1793 // Expected is a validation function which returns true if validation passed
1794 } else if (expectedType === 'function') {
1795 // protect against accidental semantics which could hard error in the test
1797 result = expected.call({}, actual) === true;
1800 // assign the "expected" to a nice error string to communicate the local failure to the user
1801 expected = errorString(e);
1804 return [result, expected, message];
1807 // Provide an alternative to assert.throws(), for environments that consider throws a reserved word
1808 // Known to us are: Closure Compiler, Narwhal
1809 // eslint-disable-next-line dot-notation
1810 Assert.prototype.raises = Assert.prototype['throws'];
1812 var LISTENERS = Object.create(null);
1813 var SUPPORTED_EVENTS = ['error', 'runStart', 'suiteStart', 'testStart', 'assertion', 'testEnd', 'suiteEnd', 'runEnd'];
1816 * Emits an event with the specified data to all currently registered listeners.
1817 * Callbacks will fire in the order in which they are registered (FIFO). This
1818 * function is not exposed publicly; it is used by QUnit internals to emit
1823 * @param {string} eventName
1824 * @param {Object} data
1827 function emit(eventName, data) {
1828 if (typeof eventName !== 'string') {
1829 throw new TypeError('eventName must be a string when emitting an event');
1832 // Clone the callbacks in case one of them registers a new callback
1833 var originalCallbacks = LISTENERS[eventName];
1834 var callbacks = originalCallbacks ? _toConsumableArray(originalCallbacks) : [];
1835 for (var i = 0; i < callbacks.length; i++) {
1841 * Registers a callback as a listener to the specified event.
1845 * @param {string} eventName
1846 * @param {Function} callback
1849 function on(eventName, callback) {
1850 if (typeof eventName !== 'string') {
1851 throw new TypeError('eventName must be a string when registering a listener');
1852 } else if (!inArray(eventName, SUPPORTED_EVENTS)) {
1853 var events = SUPPORTED_EVENTS.join(', ');
1854 throw new Error("\"".concat(eventName, "\" is not a valid event; must be one of: ").concat(events, "."));
1855 } else if (typeof callback !== 'function') {
1856 throw new TypeError('callback must be a function when registering a listener');
1858 if (!LISTENERS[eventName]) {
1859 LISTENERS[eventName] = [];
1862 // Don't register the same callback more than once
1863 if (!inArray(callback, LISTENERS[eventName])) {
1864 LISTENERS[eventName].push(callback);
1868 var commonjsGlobal = typeof globalThis !== 'undefined' ? globalThis : typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {};
1870 function commonjsRequire (path) {
1871 throw new Error('Could not dynamically require "' + path + '". Please configure the dynamicRequireTargets or/and ignoreDynamicRequires option of @rollup/plugin-commonjs appropriately for this require call to work.');
1874 var promisePolyfill = {exports: {}};
1878 /** @suppress {undefinedVars} */
1879 var globalNS = function () {
1880 // the only reliable means to get the global object is
1881 // `Function('return this')()`
1882 // However, this causes CSP violations in Chrome apps.
1883 if (typeof globalThis !== 'undefined') {
1886 if (typeof self !== 'undefined') {
1889 if (typeof window !== 'undefined') {
1892 if (typeof commonjsGlobal !== 'undefined') {
1893 return commonjsGlobal;
1895 throw new Error('unable to locate global object');
1898 // Expose the polyfill if Promise is undefined or set to a
1899 // non-function value. The latter can be due to a named HTMLElement
1900 // being exposed by browsers for legacy reasons.
1901 // https://github.com/taylorhakes/promise-polyfill/issues/114
1902 if (typeof globalNS['Promise'] === 'function') {
1903 promisePolyfill.exports = globalNS['Promise'];
1910 function finallyConstructor(callback) {
1911 var constructor = this.constructor;
1912 return this.then(function (value) {
1914 return constructor.resolve(callback()).then(function () {
1917 }, function (reason) {
1919 return constructor.resolve(callback()).then(function () {
1921 return constructor.reject(reason);
1925 function allSettled(arr) {
1927 return new P(function (resolve, reject) {
1928 if (!(arr && typeof arr.length !== 'undefined')) {
1929 return reject(new TypeError(_typeof(arr) + ' ' + arr + ' is not iterable(cannot read property Symbol(Symbol.iterator))'));
1931 var args = Array.prototype.slice.call(arr);
1932 if (args.length === 0) return resolve([]);
1933 var remaining = args.length;
1934 function res(i, val) {
1935 if (val && (_typeof(val) === 'object' || typeof val === 'function')) {
1936 var then = val.then;
1937 if (typeof then === 'function') {
1938 then.call(val, function (val) {
1945 if (--remaining === 0) {
1953 status: 'fulfilled',
1956 if (--remaining === 0) {
1960 for (var i = 0; i < args.length; i++) {
1966 // Store setTimeout reference so promise-polyfill will be unaffected by
1967 // other code modifying setTimeout (like sinon.useFakeTimers())
1968 var setTimeoutFunc = setTimeout;
1969 function isArray(x) {
1970 return Boolean(x && typeof x.length !== 'undefined');
1974 // Polyfill for Function.prototype.bind
1975 function bind(fn, thisArg) {
1976 return function () {
1977 fn.apply(thisArg, arguments);
1983 * @param {Function} fn
1985 function Promise(fn) {
1986 if (!(this instanceof Promise)) throw new TypeError('Promises must be constructed via new');
1987 if (typeof fn !== 'function') throw new TypeError('not a function');
1988 /** @type {!number} */
1990 /** @type {!boolean} */
1991 this._handled = false;
1992 /** @type {Promise|undefined} */
1993 this._value = undefined;
1994 /** @type {!Array<!Function>} */
1995 this._deferreds = [];
1996 doResolve(fn, this);
1998 function handle(self, deferred) {
1999 while (self._state === 3) {
2002 if (self._state === 0) {
2003 self._deferreds.push(deferred);
2006 self._handled = true;
2007 Promise._immediateFn(function () {
2008 var cb = self._state === 1 ? deferred.onFulfilled : deferred.onRejected;
2010 (self._state === 1 ? resolve : reject)(deferred.promise, self._value);
2015 ret = cb(self._value);
2017 reject(deferred.promise, e);
2020 resolve(deferred.promise, ret);
2023 function resolve(self, newValue) {
2025 // Promise Resolution Procedure: https://github.com/promises-aplus/promises-spec#the-promise-resolution-procedure
2026 if (newValue === self) throw new TypeError('A promise cannot be resolved with itself.');
2027 if (newValue && (_typeof(newValue) === 'object' || typeof newValue === 'function')) {
2028 var then = newValue.then;
2029 if (newValue instanceof Promise) {
2031 self._value = newValue;
2034 } else if (typeof then === 'function') {
2035 doResolve(bind(then, newValue), self);
2040 self._value = newValue;
2046 function reject(self, newValue) {
2048 self._value = newValue;
2051 function finale(self) {
2052 if (self._state === 2 && self._deferreds.length === 0) {
2053 Promise._immediateFn(function () {
2054 if (!self._handled) {
2055 Promise._unhandledRejectionFn(self._value);
2059 for (var i = 0, len = self._deferreds.length; i < len; i++) {
2060 handle(self, self._deferreds[i]);
2062 self._deferreds = null;
2068 function Handler(onFulfilled, onRejected, promise) {
2069 this.onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : null;
2070 this.onRejected = typeof onRejected === 'function' ? onRejected : null;
2071 this.promise = promise;
2075 * Take a potentially misbehaving resolver function and make sure
2076 * onFulfilled and onRejected are only called once.
2078 * Makes no guarantees about asynchrony.
2080 function doResolve(fn, self) {
2083 fn(function (value) {
2086 resolve(self, value);
2087 }, function (reason) {
2090 reject(self, reason);
2098 Promise.prototype['catch'] = function (onRejected) {
2099 return this.then(null, onRejected);
2101 Promise.prototype.then = function (onFulfilled, onRejected) {
2103 var prom = new this.constructor(noop);
2104 handle(this, new Handler(onFulfilled, onRejected, prom));
2107 Promise.prototype['finally'] = finallyConstructor;
2108 Promise.all = function (arr) {
2109 return new Promise(function (resolve, reject) {
2110 if (!isArray(arr)) {
2111 return reject(new TypeError('Promise.all accepts an array'));
2113 var args = Array.prototype.slice.call(arr);
2114 if (args.length === 0) return resolve([]);
2115 var remaining = args.length;
2116 function res(i, val) {
2118 if (val && (_typeof(val) === 'object' || typeof val === 'function')) {
2119 var then = val.then;
2120 if (typeof then === 'function') {
2121 then.call(val, function (val) {
2128 if (--remaining === 0) {
2135 for (var i = 0; i < args.length; i++) {
2140 Promise.allSettled = allSettled;
2141 Promise.resolve = function (value) {
2142 if (value && _typeof(value) === 'object' && value.constructor === Promise) {
2145 return new Promise(function (resolve) {
2149 Promise.reject = function (value) {
2150 return new Promise(function (resolve, reject) {
2154 Promise.race = function (arr) {
2155 return new Promise(function (resolve, reject) {
2156 if (!isArray(arr)) {
2157 return reject(new TypeError('Promise.race accepts an array'));
2159 for (var i = 0, len = arr.length; i < len; i++) {
2160 Promise.resolve(arr[i]).then(resolve, reject);
2165 // Use polyfill for setImmediate for performance gains
2166 Promise._immediateFn =
2168 typeof setImmediate === 'function' && function (fn) {
2171 } || function (fn) {
2172 setTimeoutFunc(fn, 0);
2174 Promise._unhandledRejectionFn = function _unhandledRejectionFn(err) {
2175 if (typeof console !== 'undefined' && console) {
2176 console.warn('Possible Unhandled Promise Rejection:', err); // eslint-disable-line no-console
2180 promisePolyfill.exports = Promise;
2182 var _Promise = promisePolyfill.exports;
2184 // Register logging callbacks
2185 function registerLoggingCallbacks(obj) {
2186 var callbackNames = ['begin', 'done', 'log', 'testStart', 'testDone', 'moduleStart', 'moduleDone'];
2187 function registerLoggingCallback(key) {
2188 return function loggingCallback(callback) {
2189 if (typeof callback !== 'function') {
2190 throw new Error('Callback parameter must be a function');
2192 config.callbacks[key].push(callback);
2195 for (var i = 0; i < callbackNames.length; i++) {
2196 var key = callbackNames[i];
2198 // Initialize key collection of logging callback
2199 if (typeof config.callbacks[key] === 'undefined') {
2200 config.callbacks[key] = [];
2202 obj[key] = registerLoggingCallback(key);
2205 function runLoggingCallbacks(key, args) {
2206 var callbacks = config.callbacks[key];
2208 // Handling 'log' callbacks separately. Unlike the other callbacks,
2209 // the log callback is not controlled by the processing queue,
2210 // but rather used by asserts. Hence to promisfy the 'log' callback
2211 // would mean promisfying each step of a test
2212 if (key === 'log') {
2213 callbacks.map(function (callback) {
2214 return callback(args);
2219 // ensure that each callback is executed serially
2220 var promiseChain = _Promise.resolve();
2221 callbacks.forEach(function (callback) {
2222 promiseChain = promiseChain.then(function () {
2223 return _Promise.resolve(callback(args));
2226 return promiseChain;
2229 var priorityCount = 0;
2232 // This is a queue of functions that are tasks within a single test.
2233 // After tests are dequeued from config.queue they are expanded into
2234 // a set of tasks in this queue.
2238 * Advances the taskQueue to the next task. If the taskQueue is empty,
2239 * process the testQueue
2241 function advance() {
2243 if (!taskQueue.length && !config.blocking && !config.current) {
2249 * Advances the taskQueue with an increased depth
2251 function advanceTaskQueue() {
2252 var start = performance.now();
2253 config.depth = (config.depth || 0) + 1;
2254 processTaskQueue(start);
2259 * Process the first task on the taskQueue as a promise.
2260 * Each task is a function added by Test#queue() in /src/test.js
2262 function processTaskQueue(start) {
2263 if (taskQueue.length && !config.blocking) {
2264 var elapsedTime = performance.now() - start;
2266 // The updateRate ensures that a user interface (HTML Reporter) can be updated
2267 // at least once every second. This can also prevent browsers from prompting
2268 // a warning about long running scripts.
2269 if (!setTimeout$1 || config.updateRate <= 0 || elapsedTime < config.updateRate) {
2270 var task = taskQueue.shift();
2271 _Promise.resolve(task()).then(function () {
2272 if (!taskQueue.length) {
2275 processTaskQueue(start);
2279 setTimeout$1(advance);
2285 * Advance the testQueue to the next test to process. Call done() if testQueue completes.
2287 function advanceTestQueue() {
2288 if (!config.blocking && !config.queue.length && config.depth === 0) {
2292 var testTasks = config.queue.shift();
2293 addToTaskQueue(testTasks());
2294 if (priorityCount > 0) {
2301 * Enqueue the tasks for a test into the task queue.
2302 * @param {Array} tasksArray
2304 function addToTaskQueue(tasksArray) {
2305 taskQueue.push.apply(taskQueue, _toConsumableArray(tasksArray));
2309 * Return the number of tasks remaining in the task queue to be processed.
2312 function taskQueueLength() {
2313 return taskQueue.length;
2317 * Adds a test to the TestQueue for execution.
2318 * @param {Function} testTasksFunc
2319 * @param {boolean} prioritize
2320 * @param {string} seed
2322 function addToTestQueue(testTasksFunc, prioritize, seed) {
2324 config.queue.splice(priorityCount++, 0, testTasksFunc);
2327 unitSampler = unitSamplerGenerator(seed);
2330 // Insert into a random position after all prioritized items
2331 var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1));
2332 config.queue.splice(priorityCount + index, 0, testTasksFunc);
2334 config.queue.push(testTasksFunc);
2339 * Creates a seeded "sample" generator which is used for randomizing tests.
2341 function unitSamplerGenerator(seed) {
2342 // 32-bit xorshift, requires only a nonzero seed
2343 // https://excamera.com/sphinx/article-xorshift.html
2344 var sample = parseInt(generateHash(seed), 16) || -1;
2345 return function () {
2346 sample ^= sample << 13;
2347 sample ^= sample >>> 17;
2348 sample ^= sample << 5;
2350 // ECMAScript has no unsigned number type
2352 sample += 0x100000000;
2354 return sample / 0x100000000;
2359 * This function is called when the ProcessingQueue is done processing all
2360 * items. It handles emitting the final run events.
2363 // We have reached the end of the processing queue and are about to emit the
2364 // "runEnd" event after which reporters typically stop listening and exit
2365 // the process. First, check if we need to emit one final test.
2366 if (config.stats.testCount === 0 && config.failOnZeroTests === true) {
2368 if (config.filter && config.filter.length) {
2369 error = new Error("No tests matched the filter \"".concat(config.filter, "\"."));
2370 } else if (config.module && config.module.length) {
2371 error = new Error("No tests matched the module \"".concat(config.module, "\"."));
2372 } else if (config.moduleId && config.moduleId.length) {
2373 error = new Error("No tests matched the moduleId \"".concat(config.moduleId, "\"."));
2374 } else if (config.testId && config.testId.length) {
2375 error = new Error("No tests matched the testId \"".concat(config.testId, "\"."));
2377 error = new Error('No tests were run.');
2379 test('global failure', extend(function (assert) {
2382 message: error.message,
2389 // We do need to call `advance()` in order to resume the processing queue.
2390 // Once this new test is finished processing, we'll reach `done` again, and
2391 // that time the above condition will evaluate to false.
2395 var storage = config.storage;
2396 var runtime = Math.round(performance.now() - config.started);
2397 var passed = config.stats.all - config.stats.bad;
2398 ProcessingQueue.finished = true;
2399 emit('runEnd', runSuite.end(true));
2400 runLoggingCallbacks('done', {
2401 // @deprecated since 2.19.0 Use done() without `details` parameter,
2402 // or use `QUnit.on('runEnd')` instead. Parameter to be replaced in
2403 // QUnit 3.0 with test counts.
2405 failed: config.stats.bad,
2406 total: config.stats.all,
2408 }).then(function () {
2409 // Clear own storage items if all tests passed
2410 if (storage && config.stats.bad === 0) {
2411 for (var i = storage.length - 1; i >= 0; i--) {
2412 var key = storage.key(i);
2413 if (key.indexOf('qunit-test-') === 0) {
2414 storage.removeItem(key);
2420 var ProcessingQueue = {
2422 add: addToTestQueue,
2424 taskCount: taskQueueLength
2427 var TestReport = /*#__PURE__*/function () {
2428 function TestReport(name, suite, options) {
2429 _classCallCheck(this, TestReport);
2431 this.suiteName = suite.name;
2432 this.fullName = suite.fullName.concat(name);
2434 this.assertions = [];
2435 this.skipped = !!options.skip;
2436 this.todo = !!options.todo;
2437 this.valid = options.valid;
2438 this._startTime = 0;
2440 suite.pushTest(this);
2442 _createClass(TestReport, [{
2444 value: function start(recordTime) {
2446 this._startTime = performance.now();
2450 suiteName: this.suiteName,
2451 fullName: this.fullName.slice()
2456 value: function end(recordTime) {
2458 this._endTime = performance.now();
2460 return extend(this.start(), {
2461 runtime: this.getRuntime(),
2462 status: this.getStatus(),
2463 errors: this.getFailedAssertions(),
2464 assertions: this.getAssertions()
2468 key: "pushAssertion",
2469 value: function pushAssertion(assertion) {
2470 this.assertions.push(assertion);
2474 value: function getRuntime() {
2475 return Math.round(this._endTime - this._startTime);
2479 value: function getStatus() {
2483 var testPassed = this.getFailedAssertions().length > 0 ? this.todo : !this.todo;
2486 } else if (this.todo) {
2493 key: "getFailedAssertions",
2494 value: function getFailedAssertions() {
2495 return this.assertions.filter(function (assertion) {
2496 return !assertion.passed;
2500 key: "getAssertions",
2501 value: function getAssertions() {
2502 return this.assertions.slice();
2505 // Remove actual and expected values from assertions. This is to prevent
2506 // leaking memory throughout a test suite.
2508 key: "slimAssertions",
2509 value: function slimAssertions() {
2510 this.assertions = this.assertions.map(function (assertion) {
2511 delete assertion.actual;
2512 delete assertion.expected;
2520 function Test(settings) {
2521 this.expected = null;
2522 this.assertions = [];
2523 this.module = config.currentModule;
2525 this.timeout = undefined;
2526 this.data = undefined;
2527 this.withData = false;
2528 this.pauses = new StringMap();
2529 this.nextPauseId = 1;
2531 // For the most common case, we have:
2537 // This needs is customised by test.each()
2538 this.stackOffset = 3;
2539 extend(this, settings);
2541 // If a module is skipped, all its tests and the tests of the child suites
2542 // should be treated as skipped even if they are defined as `only` or `todo`.
2543 // As for `todo` module, all its tests will be treated as `todo` except for
2544 // tests defined as `skip` which will be left intact.
2546 // So, if a test is defined as `todo` and is inside a skipped module, we should
2547 // then treat that test as if was defined as `skip`.
2548 if (this.module.skip) {
2552 // Skipped tests should be left intact
2553 } else if (this.module.todo && !this.skip) {
2557 // Queuing a late test after the run has ended is not allowed.
2558 // This was once supported for internal use by QUnit.onError().
2559 // Ref https://github.com/qunitjs/qunit/issues/1377
2560 if (ProcessingQueue.finished) {
2561 // Using this for anything other than onError(), such as testing in QUnit.done(),
2562 // is unstable and will likely result in the added tests being ignored by CI.
2563 // (Meaning the CI passes irregardless of the added tests).
2565 // TODO: Make this an error in QUnit 3.0
2566 // throw new Error( "Unexpected test after runEnd" );
2567 Logger.warn('Unexpected test after runEnd. This is unstable and will fail in QUnit 3.0.');
2570 if (!this.skip && typeof this.callback !== 'function') {
2571 var method = this.todo ? 'QUnit.todo' : 'QUnit.test';
2572 throw new TypeError("You must provide a callback to ".concat(method, "(\"").concat(this.testName, "\")"));
2575 // Register unique strings
2576 for (var i = 0, l = this.module.tests; i < l.length; i++) {
2577 if (this.module.tests[i].name === this.testName) {
2578 this.testName += ' ';
2581 this.testId = generateHash(this.module.name, this.testName);
2583 // No validation after this. Beyond this point, failures must be recorded as
2584 // a completed test with errors, instead of early bail out.
2585 // Otherwise, internals may be left in an inconsistent state.
2586 // Ref https://github.com/qunitjs/qunit/issues/1514
2589 this.errorForStack = new Error();
2590 if (this.callback && this.callback.validTest) {
2591 // Omit the test-level trace for the internal "No tests" test failure,
2592 // There is already an assertion-level trace, and that's noisy enough
2594 this.errorForStack.stack = undefined;
2596 this.testReport = new TestReport(this.testName, this.module.suiteReport, {
2601 this.module.tests.push({
2602 name: this.testName,
2603 testId: this.testId,
2607 // Skipped tests will fully ignore any sent callback
2608 this.callback = function () {};
2612 this.assert = new Assert(this);
2616 function getNotStartedModules(startModule) {
2617 var module = startModule;
2619 while (module && module.testsRun === 0) {
2620 modules.push(module);
2621 module = module.parentModule;
2624 // The above push modules from the child to the parent
2625 // return a reversed order with the top being the top most parent module
2626 return modules.reverse();
2629 // Use a getter to avoid computing a stack trace (which can be expensive),
2630 // This is displayed by the HTML Reporter, but most other integrations do
2633 return extractStacktrace(this.errorForStack, this.stackOffset);
2635 before: function before() {
2637 var module = this.module;
2638 var notStartedModules = getNotStartedModules(module);
2640 // ensure the callbacks are executed serially for each module
2641 var moduleStartChain = _Promise.resolve();
2642 notStartedModules.forEach(function (startModule) {
2643 moduleStartChain = moduleStartChain.then(function () {
2644 startModule.stats = {
2647 started: performance.now()
2649 emit('suiteStart', startModule.suiteReport.start(true));
2650 return runLoggingCallbacks('moduleStart', {
2651 name: startModule.name,
2652 tests: startModule.tests
2656 return moduleStartChain.then(function () {
2657 config.current = _this;
2658 _this.testEnvironment = extend({}, module.testEnvironment);
2659 _this.started = performance.now();
2660 emit('testStart', _this.testReport.start(true));
2661 return runLoggingCallbacks('testStart', {
2662 name: _this.testName,
2663 module: module.name,
2664 testId: _this.testId,
2665 previousFailure: _this.previousFailure
2666 }).then(function () {
2667 if (!config.pollution) {
2673 run: function run() {
2674 config.current = this;
2675 if (config.notrycatch) {
2682 this.pushFailure('Died on test #' + (this.assertions.length + 1) + ': ' + (e.message || e) + '\n' + this.stack, extractStacktrace(e, 0));
2684 // Else next test will carry the responsibility
2687 // Restart the tests if they're blocking
2688 if (config.blocking) {
2689 internalRecover(this);
2692 function runTest(test) {
2694 if (test.withData) {
2695 promise = test.callback.call(test.testEnvironment, test.assert, test.data);
2697 promise = test.callback.call(test.testEnvironment, test.assert);
2699 test.resolvePromise(promise);
2701 // If the test has an async "pause" on it, but the timeout is 0, then we push a
2702 // failure as the test should be synchronous.
2703 if (test.timeout === 0 && test.pauses.size > 0) {
2704 pushFailure('Test did not finish synchronously even though assert.timeout( 0 ) was used.', sourceFromStacktrace(2));
2708 after: function after() {
2711 queueGlobalHook: function queueGlobalHook(hook, hookName) {
2713 var runHook = function runHook() {
2714 config.current = _this2;
2716 if (config.notrycatch) {
2717 promise = hook.call(_this2.testEnvironment, _this2.assert);
2720 promise = hook.call(_this2.testEnvironment, _this2.assert);
2722 _this2.pushFailure('Global ' + hookName + ' failed on ' + _this2.testName + ': ' + errorString(error), extractStacktrace(error, 0));
2726 _this2.resolvePromise(promise, hookName);
2730 queueHook: function queueHook(hook, hookName, hookOwner) {
2732 var callHook = function callHook() {
2733 var promise = hook.call(_this3.testEnvironment, _this3.assert);
2734 _this3.resolvePromise(promise, hookName);
2736 var runHook = function runHook() {
2737 if (hookName === 'before') {
2738 if (hookOwner.testsRun !== 0) {
2741 _this3.preserveEnvironment = true;
2744 // The 'after' hook should only execute when there are not tests left and
2745 // when the 'after' and 'finish' tasks are the only tasks left to process
2746 if (hookName === 'after' && !lastTestWithinModuleExecuted(hookOwner) && (config.queue.length > 0 || ProcessingQueue.taskCount() > 2)) {
2749 config.current = _this3;
2750 if (config.notrycatch) {
2755 // This try-block includes the indirect call to resolvePromise, which shouldn't
2756 // have to be inside try-catch. But, since we support any user-provided thenable
2757 // object, the thenable might throw in some unexpected way.
2758 // This subtle behaviour is undocumented. To avoid new failures in minor releases
2759 // we will not change this until QUnit 3.
2760 // TODO: In QUnit 3, reduce this try-block to just hook.call(), matching
2761 // the simplicity of queueGlobalHook.
2764 _this3.pushFailure(hookName + ' failed on ' + _this3.testName + ': ' + (error.message || error), extractStacktrace(error, 0));
2769 // Currently only used for module level hooks, can be used to add global level ones
2770 hooks: function hooks(handler) {
2772 function processGlobalhooks(test) {
2773 if ((handler === 'beforeEach' || handler === 'afterEach') && config.globalHooks[handler]) {
2774 for (var i = 0; i < config.globalHooks[handler].length; i++) {
2775 hooks.push(test.queueGlobalHook(config.globalHooks[handler][i], handler));
2779 function processHooks(test, module) {
2780 if (module.parentModule) {
2781 processHooks(test, module.parentModule);
2783 if (module.hooks[handler].length) {
2784 for (var i = 0; i < module.hooks[handler].length; i++) {
2785 hooks.push(test.queueHook(module.hooks[handler][i], handler, module));
2790 // Hooks are ignored on skipped tests
2792 processGlobalhooks(this);
2793 processHooks(this, this.module);
2797 finish: function finish() {
2798 config.current = this;
2800 // Release the timeout and timeout callback references to be garbage collected.
2801 // https://github.com/qunitjs/qunit/pull/1708
2803 clearTimeout(this.timeout);
2804 config.timeoutHandler = null;
2807 // Release the test callback to ensure that anything referenced has been
2808 // released to be garbage collected.
2809 this.callback = undefined;
2810 if (this.steps.length) {
2811 var stepsList = this.steps.join(', ');
2812 this.pushFailure('Expected assert.verifySteps() to be called before end of test ' + "after using assert.step(). Unverified steps: ".concat(stepsList), this.stack);
2814 if (config.requireExpects && this.expected === null) {
2815 this.pushFailure('Expected number of assertions to be defined, but expect() was ' + 'not called.', this.stack);
2816 } else if (this.expected !== null && this.expected !== this.assertions.length) {
2817 this.pushFailure('Expected ' + this.expected + ' assertions, but ' + this.assertions.length + ' were run', this.stack);
2818 } else if (this.expected === null && !this.assertions.length) {
2819 this.pushFailure('Expected at least one assertion, but none were run - call ' + 'expect(0) to accept zero assertions.', this.stack);
2821 var module = this.module;
2822 var moduleName = module.name;
2823 var testName = this.testName;
2824 var skipped = !!this.skip;
2825 var todo = !!this.todo;
2827 var storage = config.storage;
2828 this.runtime = Math.round(performance.now() - this.started);
2829 config.stats.all += this.assertions.length;
2830 config.stats.testCount += 1;
2831 module.stats.all += this.assertions.length;
2832 for (var i = 0; i < this.assertions.length; i++) {
2833 // A failing assertion will counts toward the HTML Reporter's
2834 // "X assertions, Y failed" line even if it was inside a todo.
2835 // Inverting this would be similarly confusing since all but the last
2836 // passing assertion inside a todo test should be considered as good.
2837 // These stats don't decide the outcome of anything, so counting them
2838 // as failing seems the most intuitive.
2839 if (!this.assertions[i].result) {
2846 incrementTestsIgnored(module);
2848 incrementTestsRun(module);
2851 // Store result when possible.
2852 // Note that this also marks todo tests as bad, thus they get hoisted,
2853 // and always run first on refresh.
2856 storage.setItem('qunit-test-' + moduleName + '-' + testName, bad);
2858 storage.removeItem('qunit-test-' + moduleName + '-' + testName);
2862 // After emitting the js-reporters event we cleanup the assertion data to
2863 // avoid leaking it. It is not used by the legacy testDone callbacks.
2864 emit('testEnd', this.testReport.end(true));
2865 this.testReport.slimAssertions();
2867 return runLoggingCallbacks('testDone', {
2873 passed: this.assertions.length - bad,
2874 total: this.assertions.length,
2875 runtime: skipped ? 0 : this.runtime,
2876 // HTML Reporter use
2877 assertions: this.assertions,
2878 testId: this.testId,
2880 // generating stack trace is expensive, so using a getter will help defer this until we need it
2884 }).then(function () {
2885 if (allTestsExecuted(module)) {
2886 var completedModules = [module];
2888 // Check if the parent modules, iteratively, are done. If that the case,
2889 // we emit the `suiteEnd` event and trigger `moduleDone` callback.
2890 var parent = module.parentModule;
2891 while (parent && allTestsExecuted(parent)) {
2892 completedModules.push(parent);
2893 parent = parent.parentModule;
2895 var moduleDoneChain = _Promise.resolve();
2896 completedModules.forEach(function (completedModule) {
2897 moduleDoneChain = moduleDoneChain.then(function () {
2898 return logSuiteEnd(completedModule);
2901 return moduleDoneChain;
2903 }).then(function () {
2904 config.current = undefined;
2906 function logSuiteEnd(module) {
2907 // Reset `module.hooks` to ensure that anything referenced in these hooks
2908 // has been released to be garbage collected. Descendant modules that were
2909 // entirely skipped, e.g. due to filtering, will never have this method
2910 // called for them, but might have hooks with references pinning data in
2911 // memory (even if the hooks weren't actually executed), so we reset the
2912 // hooks on all descendant modules here as well. This is safe because we
2913 // will never call this as long as any descendant modules still have tests
2914 // to run. This also means that in multi-tiered nesting scenarios we might
2915 // reset the hooks multiple times on some modules, but that's harmless.
2916 var modules = [module];
2917 while (modules.length) {
2918 var nextModule = modules.shift();
2919 nextModule.hooks = {};
2920 modules.push.apply(modules, _toConsumableArray(nextModule.childModules));
2922 emit('suiteEnd', module.suiteReport.end(true));
2923 return runLoggingCallbacks('moduleDone', {
2925 tests: module.tests,
2926 failed: module.stats.bad,
2927 passed: module.stats.all - module.stats.bad,
2928 total: module.stats.all,
2929 runtime: Math.round(performance.now() - module.stats.started)
2933 preserveTestEnvironment: function preserveTestEnvironment() {
2934 if (this.preserveEnvironment) {
2935 this.module.testEnvironment = this.testEnvironment;
2936 this.testEnvironment = extend({}, this.module.testEnvironment);
2939 queue: function queue() {
2941 if (!this.valid()) {
2942 incrementTestsIgnored(this.module);
2945 function runTest() {
2946 return [function () {
2947 return test.before();
2948 }].concat(_toConsumableArray(test.hooks('before')), [function () {
2949 test.preserveTestEnvironment();
2950 }], _toConsumableArray(test.hooks('beforeEach')), [function () {
2952 }], _toConsumableArray(test.hooks('afterEach').reverse()), _toConsumableArray(test.hooks('after').reverse()), [function () {
2955 return test.finish();
2958 var previousFailCount = config.storage && +config.storage.getItem('qunit-test-' + this.module.name + '-' + this.testName);
2960 // Prioritize previously failed tests, detected from storage
2961 var prioritize = config.reorder && !!previousFailCount;
2962 this.previousFailure = !!previousFailCount;
2963 ProcessingQueue.add(runTest, prioritize, config.seed);
2965 pushResult: function pushResult(resultInfo) {
2966 if (this !== config.current) {
2967 var message = resultInfo && resultInfo.message || '';
2968 var testName = this && this.testName || '';
2969 var error = 'Assertion occurred after test finished.\n' + '> Test: ' + testName + '\n' + '> Message: ' + message + '\n';
2970 throw new Error(error);
2973 // Destructure of resultInfo = { result, actual, expected, message, negative }
2975 module: this.module.name,
2976 name: this.testName,
2977 result: resultInfo.result,
2978 message: resultInfo.message,
2979 actual: resultInfo.actual,
2980 testId: this.testId,
2981 negative: resultInfo.negative || false,
2982 runtime: Math.round(performance.now() - this.started),
2985 if (hasOwn$1.call(resultInfo, 'expected')) {
2986 details.expected = resultInfo.expected;
2988 if (!resultInfo.result) {
2989 var source = resultInfo.source || sourceFromStacktrace();
2991 details.source = source;
2994 this.logAssertion(details);
2995 this.assertions.push({
2996 result: !!resultInfo.result,
2997 message: resultInfo.message
3000 pushFailure: function pushFailure(message, source, actual) {
3001 if (!(this instanceof Test)) {
3002 throw new Error('pushFailure() assertion outside test context, was ' + sourceFromStacktrace(2));
3006 message: message || 'error',
3007 actual: actual || null,
3012 * Log assertion details using both the old QUnit.log interface and
3013 * QUnit.on( "assertion" ) interface.
3017 logAssertion: function logAssertion(details) {
3018 runLoggingCallbacks('log', details);
3020 passed: details.result,
3021 actual: details.actual,
3022 expected: details.expected,
3023 message: details.message,
3024 stack: details.source,
3027 this.testReport.pushAssertion(assertion);
3028 emit('assertion', assertion);
3031 * Reset config.timeout with a new timeout duration.
3033 * @param {number} timeoutDuration
3035 internalResetTimeout: function internalResetTimeout(timeoutDuration) {
3036 clearTimeout(config.timeout);
3037 config.timeout = setTimeout$1(config.timeoutHandler(timeoutDuration), timeoutDuration);
3040 * Create a new async pause and return a new function that can release the pause.
3042 * This mechanism is internally used by:
3044 * - explicit async pauses, created by calling `assert.async()`,
3045 * - implicit async pauses, created when `QUnit.test()` or module hook callbacks
3046 * use async-await or otherwise return a Promise.
3050 * - Pause is created by calling internalStop().
3052 * Pause is released normally by invoking release() during the same test.
3054 * The release() callback lets internal processing resume.
3056 * Failure scenarios:
3058 * - The test fails due to an uncaught exception.
3060 * In this case, Test.run() will call internalRecover() which empties the clears all
3061 * async pauses and sets the cancelled flag, which means we silently ignore any
3062 * late calls to the resume() callback, as we will have moved on to a different
3063 * test by then, and we don't want to cause an extra "release during a different test"
3064 * errors that the developer isn't really responsible for. This can happen when a test
3065 * correctly schedules a call to release(), but also causes an uncaught error. The
3066 * uncaught error means we will no longer wait for the release (as it might not arrive).
3068 * - Pause is never released, or called an insufficient number of times.
3070 * Our timeout handler will kill the pause and resume test processing, basically
3071 * like internalRecover(), but for one pause instead of any/all.
3073 * Here, too, any late calls to resume() will be silently ignored to avoid
3074 * extra errors. We tolerate this since the original test will have already been
3075 * marked as failure.
3077 * TODO: QUnit 3 will enable timeouts by default <https://github.com/qunitjs/qunit/issues/1483>,
3078 * but right now a test will hang indefinitely if async pauses are not released,
3079 * unless QUnit.config.testTimeout or assert.timeout() is used.
3081 * - Pause is spontaneously released during a different test,
3082 * or when no test is currently running.
3084 * This is close to impossible because this error only happens if the original test
3085 * succesfully finished first (since other failure scenarios kill pauses and ignore
3086 * late calls). It can happen if a test ended exactly as expected, but has some
3087 * external or shared state continuing to hold a reference to the release callback,
3088 * and either the same test scheduled another call to it in the future, or a later test
3089 * causes it to be called through some shared state.
3091 * - Pause release() is called too often, during the same test.
3093 * This simply throws an error, after which uncaught error handling picks it up
3094 * and processing resumes.
3096 * @param {number} [requiredCalls=1]
3098 internalStop: function internalStop() {
3099 var requiredCalls = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 1;
3100 config.blocking = true;
3102 var pauseId = this.nextPauseId++;
3105 remaining: requiredCalls
3107 test.pauses.set(pauseId, pause);
3108 function release() {
3109 if (pause.cancelled) {
3112 if (config.current === undefined) {
3113 throw new Error('Unexpected release of async pause after tests finished.\n' + "> Test: ".concat(test.testName, " [async #").concat(pauseId, "]"));
3115 if (config.current !== test) {
3116 throw new Error('Unexpected release of async pause during a different test.\n' + "> Test: ".concat(test.testName, " [async #").concat(pauseId, "]"));
3118 if (pause.remaining <= 0) {
3119 throw new Error('Tried to release async pause that was already released.\n' + "> Test: ".concat(test.testName, " [async #").concat(pauseId, "]"));
3122 // The `requiredCalls` parameter exists to support `assert.async(count)`
3124 if (pause.remaining === 0) {
3125 test.pauses.delete(pauseId);
3127 internalStart(test);
3130 // Set a recovery timeout, if so configured.
3132 var timeoutDuration;
3133 if (typeof test.timeout === 'number') {
3134 timeoutDuration = test.timeout;
3135 } else if (typeof config.testTimeout === 'number') {
3136 timeoutDuration = config.testTimeout;
3138 if (typeof timeoutDuration === 'number' && timeoutDuration > 0) {
3139 config.timeoutHandler = function (timeout) {
3140 return function () {
3141 config.timeout = null;
3142 pause.cancelled = true;
3143 test.pauses.delete(pauseId);
3144 test.pushFailure("Test took longer than ".concat(timeout, "ms; test timed out."), sourceFromStacktrace(2));
3145 internalStart(test);
3148 clearTimeout(config.timeout);
3149 config.timeout = setTimeout$1(config.timeoutHandler(timeoutDuration), timeoutDuration);
3154 resolvePromise: function resolvePromise(promise, phase) {
3155 if (promise != null) {
3157 var then = promise.then;
3158 if (typeof then === 'function') {
3159 var resume = _test.internalStop();
3160 var resolve = function resolve() {
3163 if (config.notrycatch) {
3164 then.call(promise, resolve);
3166 var reject = function reject(error) {
3167 var message = 'Promise rejected ' + (!phase ? 'during' : phase.replace(/Each$/, '')) + ' "' + _test.testName + '": ' + (error && error.message || error);
3168 _test.pushFailure(message, extractStacktrace(error, 0));
3170 // Else next test will carry the responsibility
3174 internalRecover(_test);
3176 then.call(promise, resolve, reject);
3181 valid: function valid() {
3182 // Internally-generated tests are always valid
3183 if (this.callback && this.callback.validTest) {
3186 function moduleChainIdMatch(testModule, selectedId) {
3188 // undefined or empty array
3189 !selectedId || !selectedId.length || inArray(testModule.moduleId, selectedId) || testModule.parentModule && moduleChainIdMatch(testModule.parentModule, selectedId)
3192 if (!moduleChainIdMatch(this.module, config.moduleId)) {
3195 if (config.testId && config.testId.length && !inArray(this.testId, config.testId)) {
3198 function moduleChainNameMatch(testModule, selectedModule) {
3199 if (!selectedModule) {
3200 // undefined or empty string
3203 var testModuleName = testModule.name ? testModule.name.toLowerCase() : null;
3204 if (testModuleName === selectedModule) {
3206 } else if (testModule.parentModule) {
3207 return moduleChainNameMatch(testModule.parentModule, selectedModule);
3212 var selectedModule = config.module && config.module.toLowerCase();
3213 if (!moduleChainNameMatch(this.module, selectedModule)) {
3216 var filter = config.filter;
3220 var regexFilter = /^(!?)\/([\w\W]*)\/(i?$)/.exec(filter);
3221 var fullName = this.module.name + ': ' + this.testName;
3222 return regexFilter ? this.regexFilter(!!regexFilter[1], regexFilter[2], regexFilter[3], fullName) : this.stringFilter(filter, fullName);
3224 regexFilter: function regexFilter(exclude, pattern, flags, fullName) {
3225 var regex = new RegExp(pattern, flags);
3226 var match = regex.test(fullName);
3227 return match !== exclude;
3229 stringFilter: function stringFilter(filter, fullName) {
3230 filter = filter.toLowerCase();
3231 fullName = fullName.toLowerCase();
3232 var include = filter.charAt(0) !== '!';
3234 filter = filter.slice(1);
3237 // If the filter matches, we need to honour include
3238 if (fullName.indexOf(filter) !== -1) {
3242 // Otherwise, do the opposite
3246 function pushFailure() {
3247 if (!config.current) {
3248 throw new Error('pushFailure() assertion outside test context, in ' + sourceFromStacktrace(2));
3251 // Gets current test obj
3252 var currentTest = config.current;
3253 return currentTest.pushFailure.apply(currentTest, arguments);
3255 function saveGlobal() {
3256 config.pollution = [];
3257 if (config.noglobals) {
3258 for (var key in g) {
3259 if (hasOwn$1.call(g, key)) {
3260 // In Opera sometimes DOM element ids show up here, ignore them
3261 if (/^qunit-test-output/.test(key)) {
3264 config.pollution.push(key);
3269 function checkPollution() {
3270 var old = config.pollution;
3272 var newGlobals = diff(config.pollution, old);
3273 if (newGlobals.length > 0) {
3274 pushFailure('Introduced global variable(s): ' + newGlobals.join(', '));
3276 var deletedGlobals = diff(old, config.pollution);
3277 if (deletedGlobals.length > 0) {
3278 pushFailure('Deleted global variable(s): ' + deletedGlobals.join(', '));
3281 var focused = false; // indicates that the "only" filter was used
3283 function addTest(settings) {
3284 if (focused || config.currentModule.ignored) {
3287 var newTest = new Test(settings);
3290 function addOnlyTest(settings) {
3291 if (config.currentModule.ignored) {
3295 config.queue.length = 0;
3298 var newTest = new Test(settings);
3302 // Will be exposed as QUnit.test
3303 function test(testName, callback) {
3309 function makeEachTestName(testName, argument) {
3310 return "".concat(testName, " [").concat(argument, "]");
3312 function runEach(data, eachFn) {
3313 if (Array.isArray(data)) {
3314 for (var i = 0; i < data.length; i++) {
3317 } else if (_typeof(data) === 'object' && data !== null) {
3318 for (var key in data) {
3319 eachFn(data[key], key);
3322 throw new Error("test.each() expects an array or object as input, but\nfound ".concat(_typeof(data), " instead."));
3326 todo: function todo(testName, callback) {
3333 skip: function skip(testName) {
3339 only: function only(testName, callback) {
3345 each: function each(testName, dataset, callback) {
3346 runEach(dataset, function (data, testKey) {
3348 testName: makeEachTestName(testName, testKey),
3357 test.todo.each = function (testName, dataset, callback) {
3358 runEach(dataset, function (data, testKey) {
3360 testName: makeEachTestName(testName, testKey),
3369 test.skip.each = function (testName, dataset) {
3370 runEach(dataset, function (_, testKey) {
3372 testName: makeEachTestName(testName, testKey),
3378 test.only.each = function (testName, dataset, callback) {
3379 runEach(dataset, function (data, testKey) {
3381 testName: makeEachTestName(testName, testKey),
3390 // Forcefully release all processing holds.
3391 function internalRecover(test) {
3392 test.pauses.forEach(function (pause) {
3393 pause.cancelled = true;
3395 test.pauses.clear();
3396 internalStart(test);
3399 // Release a processing hold, scheduling a resumption attempt if no holds remain.
3400 function internalStart(test) {
3401 // Ignore if other async pauses still exist.
3402 if (test.pauses.size > 0) {
3406 // Add a slight delay to allow more assertions etc.
3408 clearTimeout(config.timeout);
3409 config.timeout = setTimeout$1(function () {
3410 if (test.pauses.size > 0) {
3413 clearTimeout(config.timeout);
3414 config.timeout = null;
3415 config.blocking = false;
3416 ProcessingQueue.advance();
3419 config.blocking = false;
3420 ProcessingQueue.advance();
3423 function collectTests(module) {
3424 var tests = [].concat(module.tests);
3425 var modules = _toConsumableArray(module.childModules);
3427 // Do a breadth-first traversal of the child modules
3428 while (modules.length) {
3429 var nextModule = modules.shift();
3430 tests.push.apply(tests, nextModule.tests);
3431 modules.push.apply(modules, _toConsumableArray(nextModule.childModules));
3436 // This returns true after all executable and skippable tests
3437 // in a module have been proccessed, and informs 'suiteEnd'
3438 // and moduleDone().
3439 function allTestsExecuted(module) {
3440 return module.testsRun + module.testsIgnored === collectTests(module).length;
3443 // This returns true during the last executable non-skipped test
3444 // within a module, and informs the running of the 'after' hook
3445 // for a given module. This runs only once for a given module,
3446 // but must run during the last non-skipped test. When it runs,
3447 // there may be non-zero skipped tests left.
3448 function lastTestWithinModuleExecuted(module) {
3449 return module.testsRun === collectTests(module).filter(function (test) {
3453 function incrementTestsRun(module) {
3455 while (module = module.parentModule) {
3459 function incrementTestsIgnored(module) {
3460 module.testsIgnored++;
3461 while (module = module.parentModule) {
3462 module.testsIgnored++;
3466 /* global module, exports, define */
3467 function exportQUnit(QUnit) {
3468 var exportedModule = false;
3469 if (window$1 && document) {
3470 // QUnit may be defined when it is preconfigured but then only QUnit and QUnit.config may be defined.
3471 if (window$1.QUnit && window$1.QUnit.version) {
3472 throw new Error('QUnit has already been defined.');
3474 window$1.QUnit = QUnit;
3475 exportedModule = true;
3479 if (typeof module !== 'undefined' && module && module.exports) {
3480 module.exports = QUnit;
3482 // For consistency with CommonJS environments' exports
3483 module.exports.QUnit = QUnit;
3484 exportedModule = true;
3487 // For CommonJS with exports, but without module.exports, like Rhino
3488 if (typeof exports !== 'undefined' && exports) {
3489 exports.QUnit = QUnit;
3490 exportedModule = true;
3494 if (typeof define === 'function' && define.amd) {
3495 define(function () {
3498 QUnit.config.autostart = false;
3499 exportedModule = true;
3502 // For other environments, including Web Workers (globalThis === self),
3503 // SpiderMonkey (mozjs), and other embedded JavaScript engines
3504 if (!exportedModule) {
3509 var ConsoleReporter = /*#__PURE__*/function () {
3510 function ConsoleReporter(runner) {
3511 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3512 _classCallCheck(this, ConsoleReporter);
3513 // Cache references to console methods to ensure we can report failures
3514 // from tests tests that mock the console object itself.
3515 // https://github.com/qunitjs/qunit/issues/1340
3516 // Support IE 9: Function#bind is supported, but no console.log.bind().
3517 this.log = options.log || Function.prototype.bind.call(console$1.log, console$1);
3518 runner.on('error', this.onError.bind(this));
3519 runner.on('runStart', this.onRunStart.bind(this));
3520 runner.on('testStart', this.onTestStart.bind(this));
3521 runner.on('testEnd', this.onTestEnd.bind(this));
3522 runner.on('runEnd', this.onRunEnd.bind(this));
3524 _createClass(ConsoleReporter, [{
3526 value: function onError(error) {
3527 this.log('error', error);
3531 value: function onRunStart(runStart) {
3532 this.log('runStart', runStart);
3536 value: function onTestStart(test) {
3537 this.log('testStart', test);
3541 value: function onTestEnd(test) {
3542 this.log('testEnd', test);
3546 value: function onRunEnd(runEnd) {
3547 this.log('runEnd', runEnd);
3551 value: function init(runner, options) {
3552 return new ConsoleReporter(runner, options);
3555 return ConsoleReporter;
3558 // TODO: Consider using globalThis instead of window, so that the reporter
3559 // works for Node.js as well. As this can add overhead, we should make
3560 // this opt-in before we enable it for CLI.
3562 // QUnit 3 will switch from `window` to `globalThis` and then make it
3563 // no longer an implicit feature of the HTML Reporter, but rather let
3564 // it be opt-in via `QUnit.config.reporters = ['perf']` or something
3566 var nativePerf = window$1 && typeof window$1.performance !== 'undefined' &&
3567 // eslint-disable-next-line compat/compat -- Checked
3568 typeof window$1.performance.mark === 'function' &&
3569 // eslint-disable-next-line compat/compat -- Checked
3570 typeof window$1.performance.measure === 'function' ? window$1.performance : undefined;
3572 measure: nativePerf ? function (comment, startMark, endMark) {
3573 // `performance.measure` may fail if the mark could not be found.
3574 // reasons a specific mark could not be found include: outside code invoking `performance.clearMarks()`
3576 nativePerf.measure(comment, startMark, endMark);
3578 Logger.warn('performance.measure could not be executed because of ', ex.message);
3581 mark: nativePerf ? nativePerf.mark.bind(nativePerf) : function () {}
3583 var PerfReporter = /*#__PURE__*/function () {
3584 function PerfReporter(runner) {
3585 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3586 _classCallCheck(this, PerfReporter);
3587 this.perf = options.perf || perf;
3588 runner.on('runStart', this.onRunStart.bind(this));
3589 runner.on('runEnd', this.onRunEnd.bind(this));
3590 runner.on('suiteStart', this.onSuiteStart.bind(this));
3591 runner.on('suiteEnd', this.onSuiteEnd.bind(this));
3592 runner.on('testStart', this.onTestStart.bind(this));
3593 runner.on('testEnd', this.onTestEnd.bind(this));
3595 _createClass(PerfReporter, [{
3597 value: function onRunStart() {
3598 this.perf.mark('qunit_suite_0_start');
3601 key: "onSuiteStart",
3602 value: function onSuiteStart(suiteStart) {
3603 var suiteLevel = suiteStart.fullName.length;
3604 this.perf.mark("qunit_suite_".concat(suiteLevel, "_start"));
3608 value: function onSuiteEnd(suiteEnd) {
3609 var suiteLevel = suiteEnd.fullName.length;
3610 var suiteName = suiteEnd.fullName.join(' – ');
3611 this.perf.mark("qunit_suite_".concat(suiteLevel, "_end"));
3612 this.perf.measure("QUnit Test Suite: ".concat(suiteName), "qunit_suite_".concat(suiteLevel, "_start"), "qunit_suite_".concat(suiteLevel, "_end"));
3616 value: function onTestStart() {
3617 this.perf.mark('qunit_test_start');
3621 value: function onTestEnd(testEnd) {
3622 this.perf.mark('qunit_test_end');
3623 var testName = testEnd.fullName.join(' – ');
3624 this.perf.measure("QUnit Test: ".concat(testName), 'qunit_test_start', 'qunit_test_end');
3628 value: function onRunEnd() {
3629 this.perf.mark('qunit_suite_0_end');
3630 this.perf.measure('QUnit Test Run', 'qunit_suite_0_start', 'qunit_suite_0_end');
3634 value: function init(runner, options) {
3635 return new PerfReporter(runner, options);
3638 return PerfReporter;
3642 NODE_DISABLE_COLORS,
3646 if (typeof process !== 'undefined') {
3647 var _ref = process.env || {};
3648 FORCE_COLOR = _ref.FORCE_COLOR;
3649 NODE_DISABLE_COLORS = _ref.NODE_DISABLE_COLORS;
3650 NO_COLOR = _ref.NO_COLOR;
3652 isTTY = process.stdout && process.stdout.isTTY;
3655 enabled: !NODE_DISABLE_COLORS && NO_COLOR == null && TERM !== 'dumb' && (FORCE_COLOR != null && FORCE_COLOR !== '0' || isTTY),
3660 italic: init(3, 23),
3661 underline: init(4, 24),
3662 inverse: init(7, 27),
3663 hidden: init(8, 28),
3664 strikethrough: init(9, 29),
3666 black: init(30, 39),
3668 green: init(32, 39),
3669 yellow: init(33, 39),
3671 magenta: init(35, 39),
3673 white: init(37, 39),
3676 // background colors
3677 bgBlack: init(40, 49),
3678 bgRed: init(41, 49),
3679 bgGreen: init(42, 49),
3680 bgYellow: init(43, 49),
3681 bgBlue: init(44, 49),
3682 bgMagenta: init(45, 49),
3683 bgCyan: init(46, 49),
3684 bgWhite: init(47, 49)
3686 function run(arr, str) {
3691 for (; i < arr.length; i++) {
3695 if (!!~str.indexOf(tmp.close)) {
3696 str = str.replace(tmp.rgx, tmp.close + tmp.open);
3699 return beg + str + end;
3701 function chain(has, keys) {
3706 ctx.reset = $.reset.bind(ctx);
3707 ctx.bold = $.bold.bind(ctx);
3708 ctx.dim = $.dim.bind(ctx);
3709 ctx.italic = $.italic.bind(ctx);
3710 ctx.underline = $.underline.bind(ctx);
3711 ctx.inverse = $.inverse.bind(ctx);
3712 ctx.hidden = $.hidden.bind(ctx);
3713 ctx.strikethrough = $.strikethrough.bind(ctx);
3714 ctx.black = $.black.bind(ctx);
3715 ctx.red = $.red.bind(ctx);
3716 ctx.green = $.green.bind(ctx);
3717 ctx.yellow = $.yellow.bind(ctx);
3718 ctx.blue = $.blue.bind(ctx);
3719 ctx.magenta = $.magenta.bind(ctx);
3720 ctx.cyan = $.cyan.bind(ctx);
3721 ctx.white = $.white.bind(ctx);
3722 ctx.gray = $.gray.bind(ctx);
3723 ctx.grey = $.grey.bind(ctx);
3724 ctx.bgBlack = $.bgBlack.bind(ctx);
3725 ctx.bgRed = $.bgRed.bind(ctx);
3726 ctx.bgGreen = $.bgGreen.bind(ctx);
3727 ctx.bgYellow = $.bgYellow.bind(ctx);
3728 ctx.bgBlue = $.bgBlue.bind(ctx);
3729 ctx.bgMagenta = $.bgMagenta.bind(ctx);
3730 ctx.bgCyan = $.bgCyan.bind(ctx);
3731 ctx.bgWhite = $.bgWhite.bind(ctx);
3734 function init(open, close) {
3736 open: "\x1B[".concat(open, "m"),
3737 close: "\x1B[".concat(close, "m"),
3738 rgx: new RegExp("\\x1b\\[".concat(close, "m"), 'g')
3740 return function (txt) {
3741 if (this !== void 0 && this.has !== void 0) {
3742 !!~this.has.indexOf(open) || (this.has.push(open), this.keys.push(blk));
3743 return txt === void 0 ? this : $.enabled ? run(this.keys, txt + '') : txt + '';
3745 return txt === void 0 ? chain([open], [blk]) : $.enabled ? run([blk], txt + '') : txt + '';
3749 var hasOwn = Object.prototype.hasOwnProperty;
3752 * Format a given value into YAML.
3754 * YAML is a superset of JSON that supports all the same data
3755 * types and syntax, and more. As such, it is always possible
3756 * to fallback to JSON.stringfify, but we generally avoid
3757 * that to make output easier to read for humans.
3759 * Supported data types:
3768 * Anything else (including NaN, Infinity, and undefined)
3769 * must be described in strings, for display purposes.
3771 * Note that quotes are optional in YAML strings if the
3772 * strings are "simple", and as such we generally prefer
3773 * that for improved readability. We output strings in
3774 * one of three ways:
3776 * - bare unquoted text, for simple one-line strings.
3777 * - JSON (quoted text), for complex one-line strings.
3778 * - YAML Block, for complex multi-line strings.
3780 * Objects with cyclical references will be stringifed as
3781 * "[Circular]" as they cannot otherwise be represented.
3783 function prettyYamlValue(value) {
3784 var indent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 4;
3785 if (value === undefined) {
3786 // Not supported in JSON/YAML, turn into string
3787 // and let the below output it as bare string.
3788 value = String(value);
3791 // Support IE 9-11: Use isFinite instead of ES6 Number.isFinite
3792 if (typeof value === 'number' && !isFinite(value)) {
3793 // Turn NaN and Infinity into simple strings.
3794 // Paranoia: Don't return directly just in case there's
3795 // a way to add special characters here.
3796 value = String(value);
3798 if (typeof value === 'number') {
3800 return JSON.stringify(value);
3802 if (typeof value === 'string') {
3803 // If any of these match, then we can't output it
3804 // as bare unquoted text, because that would either
3805 // cause data loss or invalid YAML syntax.
3807 // - Quotes, escapes, line breaks, or JSON-like stuff.
3808 var rSpecialJson = /['"\\/[{}\]\r\n]/;
3810 // - Characters that are special at the start of a YAML value
3811 var rSpecialYaml = /[-?:,[\]{}#&*!|=>'"%@`]/;
3813 // - Leading or trailing whitespace.
3814 var rUntrimmed = /(^\s|\s$)/;
3816 // - Ambiguous as YAML number, e.g. '2', '-1.2', '.2', or '2_000'
3817 var rNumerical = /^[\d._-]+$/;
3819 // - Ambiguous as YAML bool.
3820 // Use case-insensitive match, although technically only
3821 // fully-lower, fully-upper, or uppercase-first would be ambiguous.
3822 // e.g. true/True/TRUE, but not tRUe.
3823 var rBool = /^(true|false|y|n|yes|no|on|off)$/i;
3825 // Is this a complex string?
3826 if (value === '' || rSpecialJson.test(value) || rSpecialYaml.test(value[0]) || rUntrimmed.test(value) || rNumerical.test(value) || rBool.test(value)) {
3827 if (!/\n/.test(value)) {
3828 // Complex one-line string, use JSON (quoted string)
3829 return JSON.stringify(value);
3832 // See also <https://yaml-multiline.info/>
3833 // Support IE 9-11: Avoid ES6 String#repeat
3834 var prefix = new Array(indent + 1).join(' ');
3835 var trailingLinebreakMatch = value.match(/\n+$/);
3836 var trailingLinebreaks = trailingLinebreakMatch ? trailingLinebreakMatch[0].length : 0;
3837 if (trailingLinebreaks === 1) {
3838 // Use the most straight-forward "Block" string in YAML
3839 // without any "Chomping" indicators.
3842 // Ignore the last new line, since we'll get that one for free
3843 // with the straight-forward Block syntax.
3844 .replace(/\n$/, '').split('\n').map(function (line) {
3845 return prefix + line;
3847 return '|\n' + lines.join('\n');
3849 // This has either no trailing new lines, or more than 1.
3850 // Use |+ so that YAML parsers will preserve it exactly.
3851 var _lines = value.split('\n').map(function (line) {
3852 return prefix + line;
3854 return '|+\n' + _lines.join('\n');
3857 // Simple string, use bare unquoted text
3862 // Handle null, boolean, array, and object
3863 return JSON.stringify(decycledShallowClone(value), null, 2);
3867 * Creates a shallow clone of an object where cycles have
3868 * been replaced with "[Circular]".
3870 function decycledShallowClone(object) {
3871 var ancestors = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
3872 if (ancestors.indexOf(object) !== -1) {
3873 return '[Circular]';
3875 var type = Object.prototype.toString.call(object).replace(/^\[.+\s(.+?)]$/, '$1').toLowerCase();
3879 ancestors.push(object);
3880 clone = object.map(function (element) {
3881 return decycledShallowClone(element, ancestors);
3886 ancestors.push(object);
3888 Object.keys(object).forEach(function (key) {
3889 clone[key] = decycledShallowClone(object[key], ancestors);
3898 var TapReporter = /*#__PURE__*/function () {
3899 function TapReporter(runner) {
3900 var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
3901 _classCallCheck(this, TapReporter);
3902 // Cache references to console methods to ensure we can report failures
3903 // from tests tests that mock the console object itself.
3904 // https://github.com/qunitjs/qunit/issues/1340
3905 // Support IE 9: Function#bind is supported, but no console.log.bind().
3906 this.log = options.log || Function.prototype.bind.call(console$1.log, console$1);
3909 this.bailed = false;
3910 runner.on('error', this.onError.bind(this));
3911 runner.on('runStart', this.onRunStart.bind(this));
3912 runner.on('testEnd', this.onTestEnd.bind(this));
3913 runner.on('runEnd', this.onRunEnd.bind(this));
3915 _createClass(TapReporter, [{
3917 value: function onRunStart(_runSuite) {
3918 this.log('TAP version 13');
3922 value: function onError(error) {
3928 // Imitate onTestEnd
3929 // Skip this if we're past "runEnd" as it would look odd
3931 this.testCount = this.testCount + 1;
3932 this.log($.red("not ok ".concat(this.testCount, " global failure")));
3933 this.logError(error);
3935 this.log('Bail out! ' + errorString(error).split('\n')[0]);
3937 this.logError(error);
3942 value: function onTestEnd(test) {
3944 this.testCount = this.testCount + 1;
3945 if (test.status === 'passed') {
3946 this.log("ok ".concat(this.testCount, " ").concat(test.fullName.join(' > ')));
3947 } else if (test.status === 'skipped') {
3948 this.log($.yellow("ok ".concat(this.testCount, " # SKIP ").concat(test.fullName.join(' > '))));
3949 } else if (test.status === 'todo') {
3950 this.log($.cyan("not ok ".concat(this.testCount, " # TODO ").concat(test.fullName.join(' > '))));
3951 test.errors.forEach(function (error) {
3952 return _this.logAssertion(error, 'todo');
3955 this.log($.red("not ok ".concat(this.testCount, " ").concat(test.fullName.join(' > '))));
3956 test.errors.forEach(function (error) {
3957 return _this.logAssertion(error);
3963 value: function onRunEnd(runSuite) {
3965 this.log("1..".concat(runSuite.testCounts.total));
3966 this.log("# pass ".concat(runSuite.testCounts.passed));
3967 this.log($.yellow("# skip ".concat(runSuite.testCounts.skipped)));
3968 this.log($.cyan("# todo ".concat(runSuite.testCounts.todo)));
3969 this.log($.red("# fail ".concat(runSuite.testCounts.failed)));
3972 key: "logAssertion",
3973 value: function logAssertion(error, severity) {
3975 out += "\n message: ".concat(prettyYamlValue(error.message || 'failed'));
3976 out += "\n severity: ".concat(prettyYamlValue(severity || 'failed'));
3977 if (hasOwn.call(error, 'actual')) {
3978 out += "\n actual : ".concat(prettyYamlValue(error.actual));
3980 if (hasOwn.call(error, 'expected')) {
3981 out += "\n expected: ".concat(prettyYamlValue(error.expected));
3984 // Since stacks aren't user generated, take a bit of liberty by
3985 // adding a trailing new line to allow a straight-forward YAML Blocks.
3986 out += "\n stack: ".concat(prettyYamlValue(error.stack + '\n'));
3993 value: function logError(error) {
3995 out += "\n message: ".concat(prettyYamlValue(errorString(error)));
3996 out += "\n severity: ".concat(prettyYamlValue('failed'));
3997 if (error && error.stack) {
3998 out += "\n stack: ".concat(prettyYamlValue(error.stack + '\n'));
4005 value: function init(runner, options) {
4006 return new TapReporter(runner, options);
4013 console: ConsoleReporter,
4018 function makeAddGlobalHook(hookName) {
4019 return function addGlobalHook(callback) {
4020 if (!config.globalHooks[hookName]) {
4021 config.globalHooks[hookName] = [];
4023 config.globalHooks[hookName].push(callback);
4027 beforeEach: makeAddGlobalHook('beforeEach'),
4028 afterEach: makeAddGlobalHook('afterEach')
4032 * Handle a global error that should result in a failed test run.
4036 * - If we're strictly inside a test (or one if its module hooks), the exception
4037 * becomes a failed assertion.
4039 * This has the important side-effect that uncaught exceptions (such as
4040 * calling an undefined function) during a "todo" test do NOT result in
4041 * a failed test run.
4043 * - If we're anywhere outside a test (be it in early event callbacks, or
4044 * internally between tests, or somewhere after "runEnd" if the process is
4045 * still alive for some reason), then send an "error" event to the reporters.
4048 * @param {Error|any} error
4050 function onUncaughtException(error) {
4051 if (config.current) {
4052 config.current.assert.pushResult({
4054 message: "global failure: ".concat(errorString(error)),
4055 // We could let callers specify an offset to subtract a number of frames via
4056 // sourceFromStacktrace, in case they are a wrapper further away from the error
4057 // handler, and thus reduce some noise in the stack trace. However, we're not
4058 // doing this right now because it would almost never be used in practice given
4059 // the vast majority of error values will be Error objects, and thus have their
4060 // own stack trace already.
4061 source: error && error.stack || sourceFromStacktrace(2)
4064 // The "error" event was added in QUnit 2.17.
4065 // Increase "bad assertion" stats despite no longer pushing an assertion in this case.
4066 // This ensures "runEnd" and "QUnit.done()" handlers behave as expected, since the "bad"
4067 // count is typically how reporters decide on the boolean outcome of the test run.
4068 runSuite.globalFailureCount++;
4071 emit('error', error);
4076 * Handle a window.onerror error.
4078 * If there is a current test that sets the internal `ignoreGlobalErrors` field
4079 * (such as during `assert.throws()`), then the error is ignored and native
4080 * error reporting is suppressed as well. This is because in browsers, an error
4081 * can sometimes end up in `window.onerror` instead of in the local try/catch.
4082 * This ignoring of errors does not apply to our general onUncaughtException
4083 * method, nor to our `unhandledRejection` handlers, as those are not meant
4084 * to receive an "expected" error during `assert.throws()`.
4086 * @see <https://developer.mozilla.org/en-US/docs/Web/API/GlobalEventHandlers/onerror>
4087 * @deprecated since 2.17.0 Use QUnit.onUncaughtException instead.
4088 * @param {Object} details
4089 * @param {string} details.message
4090 * @param {string} details.fileName
4091 * @param {number} details.lineNumber
4092 * @param {string|undefined} [details.stacktrace]
4093 * @return {bool} True if native error reporting should be suppressed.
4095 function onWindowError(details) {
4096 Logger.warn('QUnit.onError is deprecated and will be removed in QUnit 3.0.' + ' Please use QUnit.onUncaughtException instead.');
4097 if (config.current && config.current.ignoreGlobalErrors) {
4100 var err = new Error(details.message);
4101 err.stack = details.stacktrace || details.fileName + ':' + details.lineNumber;
4102 onUncaughtException(err);
4108 // The "currentModule" object would ideally be defined using the createModule()
4109 // function. Since it isn't, add the missing suiteReport property to it now that
4110 // we have loaded all source code required to do so.
4112 // TODO: Consider defining currentModule in core.js or module.js in its entirely
4113 // rather than partly in config.js and partly here.
4114 config.currentModule.suiteReport = runSuite;
4115 var globalStartCalled = false;
4116 var runStarted = false;
4118 // Figure out if we're running the tests from a server or not
4119 QUnit.isLocal = window$1 && window$1.location && window$1.location.protocol === 'file:';
4121 // Expose the current QUnit version
4122 QUnit.version = '2.20.0';
4127 reporters: reporters,
4130 objectType: objectType,
4132 onError: onWindowError,
4133 onUncaughtException: onUncaughtException,
4134 pushFailure: pushFailure,
4135 assert: Assert.prototype,
4138 // alias other test flavors for easy access
4142 start: function start(count) {
4143 if (config.current) {
4144 throw new Error('QUnit.start cannot be called inside a test context.');
4146 var globalStartAlreadyCalled = globalStartCalled;
4147 globalStartCalled = true;
4149 throw new Error('Called start() while test already started running');
4151 if (globalStartAlreadyCalled || count > 1) {
4152 throw new Error('Called start() outside of a test context too many times');
4154 if (config.autostart) {
4155 throw new Error('Called start() outside of a test context when ' + 'QUnit.config.autostart was true');
4157 if (!config.pageLoaded) {
4158 // The page isn't completely loaded yet, so we set autostart and then
4159 // load if we're in Node or wait for the browser's load event.
4160 config.autostart = true;
4162 // Starts from Node even if .load was not previously called. We still return
4163 // early otherwise we'll wind up "beginning" twice.
4171 onUnhandledRejection: function onUnhandledRejection(reason) {
4172 Logger.warn('QUnit.onUnhandledRejection is deprecated and will be removed in QUnit 3.0.' + ' Please use QUnit.onUncaughtException instead.');
4173 onUncaughtException(reason);
4175 extend: function extend$1() {
4176 Logger.warn('QUnit.extend is deprecated and will be removed in QUnit 3.0.' + ' Please use Object.assign instead.');
4178 // delegate to utility implementation, which does not warn and can be used elsewhere internally
4179 for (var _len = arguments.length, args = new Array(_len), _key = 0; _key < _len; _key++) {
4180 args[_key] = arguments[_key];
4182 return extend.apply(this, args);
4184 load: function load() {
4185 config.pageLoaded = true;
4187 // Initialize the configuration options
4195 config.blocking = false;
4196 if (config.autostart) {
4201 stack: function stack(offset) {
4202 offset = (offset || 0) + 2;
4203 return sourceFromStacktrace(offset);
4206 registerLoggingCallbacks(QUnit);
4207 function scheduleBegin() {
4210 // Add a slight delay to allow definition of more modules and tests.
4212 setTimeout$1(function () {
4219 function unblockAndAdvanceQueue() {
4220 config.blocking = false;
4221 ProcessingQueue.advance();
4224 if (config.started) {
4225 unblockAndAdvanceQueue();
4229 // The test run hasn't officially begun yet
4230 // Record the time of the test run's beginning
4231 config.started = performance.now();
4233 // Delete the loose unnamed module if unused.
4234 if (config.modules[0].name === '' && config.modules[0].tests.length === 0) {
4235 config.modules.shift();
4237 var modulesLog = [];
4238 for (var i = 0; i < config.modules.length; i++) {
4239 // Don't expose the unnamed global test module to plugins.
4240 if (config.modules[i].name !== '') {
4242 name: config.modules[i].name,
4243 moduleId: config.modules[i].moduleId,
4244 // Added in QUnit 1.16.0 for internal use by html-reporter,
4245 // but no longer used since QUnit 2.7.0.
4246 // @deprecated Kept unofficially to be removed in QUnit 3.0.
4247 tests: config.modules[i].tests
4252 // The test run is officially beginning now
4253 emit('runStart', runSuite.start(true));
4254 runLoggingCallbacks('begin', {
4255 totalTests: Test.count,
4257 }).then(unblockAndAdvanceQueue);
4262 if (!window$1 || !document) {
4265 var config = QUnit.config;
4266 var hasOwn = Object.prototype.hasOwnProperty;
4268 // Stores fixture HTML for resetting later
4269 function storeFixture() {
4270 // Avoid overwriting user-defined values
4271 if (hasOwn.call(config, 'fixture')) {
4274 var fixture = document.getElementById('qunit-fixture');
4276 config.fixture = fixture.cloneNode(true);
4279 QUnit.begin(storeFixture);
4281 // Resets the fixture DOM element if available.
4282 function resetFixture() {
4283 if (config.fixture == null) {
4286 var fixture = document.getElementById('qunit-fixture');
4287 var resetFixtureType = _typeof(config.fixture);
4288 if (resetFixtureType === 'string') {
4289 // support user defined values for `config.fixture`
4290 var newFixture = document.createElement('div');
4291 newFixture.setAttribute('id', 'qunit-fixture');
4292 newFixture.innerHTML = config.fixture;
4293 fixture.parentNode.replaceChild(newFixture, fixture);
4295 var clonedFixture = config.fixture.cloneNode(true);
4296 fixture.parentNode.replaceChild(clonedFixture, fixture);
4299 QUnit.testStart(resetFixture);
4303 // Only interact with URLs via window.location
4304 var location = typeof window$1 !== 'undefined' && window$1.location;
4308 var urlParams = getUrlParams();
4309 QUnit.urlParams = urlParams;
4310 QUnit.config.filter = urlParams.filter;
4311 QUnit.config.module = urlParams.module;
4312 QUnit.config.moduleId = [].concat(urlParams.moduleId || []);
4313 QUnit.config.testId = [].concat(urlParams.testId || []);
4315 // Test order randomization
4316 if (urlParams.seed === true) {
4317 // Generate a random seed if the option is specified without a value
4318 QUnit.config.seed = Math.random().toString(36).slice(2);
4319 } else if (urlParams.seed) {
4320 QUnit.config.seed = urlParams.seed;
4323 // Add URL-parameter-mapped config values with UI form rendering data
4324 QUnit.config.urlConfig.push({
4326 label: 'Hide passed tests',
4327 tooltip: 'Only show tests and assertions that fail. Stored as query-strings.'
4330 label: 'Check for Globals',
4331 tooltip: 'Enabling this will test if any test introduces new properties on the ' + 'global object (`window` in Browsers). Stored as query-strings.'
4334 label: 'No try-catch',
4335 tooltip: 'Enabling this will run tests outside of a try-catch block. Makes debugging ' + 'exceptions in IE reasonable. Stored as query-strings.'
4337 QUnit.begin(function () {
4338 var urlConfig = QUnit.config.urlConfig;
4339 for (var i = 0; i < urlConfig.length; i++) {
4340 // Options can be either strings or objects with nonempty "id" properties
4341 var option = QUnit.config.urlConfig[i];
4342 if (typeof option !== 'string') {
4345 if (QUnit.config[option] === undefined) {
4346 QUnit.config[option] = urlParams[option];
4350 function getUrlParams() {
4351 var urlParams = Object.create(null);
4352 var params = location.search.slice(1).split('&');
4353 var length = params.length;
4354 for (var i = 0; i < length; i++) {
4356 var param = params[i].split('=');
4357 var name = decodeQueryParam(param[0]);
4359 // Allow just a key to turn on a flag, e.g., test.html?noglobals
4360 var value = param.length === 1 || decodeQueryParam(param.slice(1).join('='));
4361 if (name in urlParams) {
4362 urlParams[name] = [].concat(urlParams[name], value);
4364 urlParams[name] = value;
4370 function decodeQueryParam(param) {
4371 return decodeURIComponent(param.replace(/\+/g, '%20'));
4375 var fuzzysort$1 = {exports: {}};
4377 (function (module) {
4378 (function (root, UMD) {
4379 if (module.exports) module.exports = UMD();else root.fuzzysort = UMD();
4380 })(commonjsGlobal, function UMD() {
4381 function fuzzysortNew(instanceOptions) {
4383 single: function single(search, target, options) {
4384 if (search == 'farzher') return {
4385 target: "farzher was here (^-^*)/",
4387 indexes: [0, 1, 2, 3, 4, 5, 6]
4389 if (!search) return null;
4390 if (!isObj(search)) search = fuzzysort.getPreparedSearch(search);
4391 if (!target) return null;
4392 if (!isObj(target)) target = fuzzysort.getPrepared(target);
4393 var allowTypo = options && options.allowTypo !== undefined ? options.allowTypo : instanceOptions && instanceOptions.allowTypo !== undefined ? instanceOptions.allowTypo : true;
4394 var algorithm = allowTypo ? fuzzysort.algorithm : fuzzysort.algorithmNoTypo;
4395 return algorithm(search, target, search[0]);
4397 go: function go(search, targets, options) {
4398 if (search == 'farzher') return [{
4399 target: "farzher was here (^-^*)/",
4401 indexes: [0, 1, 2, 3, 4, 5, 6],
4402 obj: targets ? targets[0] : null
4404 if (!search) return noResults;
4405 search = fuzzysort.prepareSearch(search);
4406 var searchLowerCode = search[0];
4407 var threshold = options && options.threshold || instanceOptions && instanceOptions.threshold || -9007199254740991;
4408 var limit = options && options.limit || instanceOptions && instanceOptions.limit || 9007199254740991;
4409 var allowTypo = options && options.allowTypo !== undefined ? options.allowTypo : instanceOptions && instanceOptions.allowTypo !== undefined ? instanceOptions.allowTypo : true;
4410 var algorithm = allowTypo ? fuzzysort.algorithm : fuzzysort.algorithmNoTypo;
4412 var limitedCount = 0;
4413 var targetsLen = targets.length;
4415 // This code is copy/pasted 3 times for performance reasons [options.keys, options.key, no keys]
4418 if (options && options.keys) {
4419 var scoreFn = options.scoreFn || defaultScoreFn;
4420 var keys = options.keys;
4421 var keysLen = keys.length;
4422 for (var i = targetsLen - 1; i >= 0; --i) {
4423 var obj = targets[i];
4424 var objResults = new Array(keysLen);
4425 for (var keyI = keysLen - 1; keyI >= 0; --keyI) {
4426 var key = keys[keyI];
4427 var target = getValue(obj, key);
4429 objResults[keyI] = null;
4432 if (!isObj(target)) target = fuzzysort.getPrepared(target);
4433 objResults[keyI] = algorithm(search, target, searchLowerCode);
4435 objResults.obj = obj; // before scoreFn so scoreFn can use it
4436 var score = scoreFn(objResults);
4437 if (score === null) continue;
4438 if (score < threshold) continue;
4439 objResults.score = score;
4440 if (resultsLen < limit) {
4445 if (score > q.peek().score) q.replaceTop(objResults);
4450 } else if (options && options.key) {
4451 var key = options.key;
4452 for (var i = targetsLen - 1; i >= 0; --i) {
4453 var obj = targets[i];
4454 var target = getValue(obj, key);
4455 if (!target) continue;
4456 if (!isObj(target)) target = fuzzysort.getPrepared(target);
4457 var result = algorithm(search, target, searchLowerCode);
4458 if (result === null) continue;
4459 if (result.score < threshold) continue;
4461 // have to clone result so duplicate targets from different obj can each reference the correct obj
4463 target: result.target,
4464 _targetLowerCodes: null,
4465 _nextBeginningIndexes: null,
4466 score: result.score,
4467 indexes: result.indexes,
4471 if (resultsLen < limit) {
4476 if (result.score > q.peek().score) q.replaceTop(result);
4482 for (var i = targetsLen - 1; i >= 0; --i) {
4483 var target = targets[i];
4484 if (!target) continue;
4485 if (!isObj(target)) target = fuzzysort.getPrepared(target);
4486 var result = algorithm(search, target, searchLowerCode);
4487 if (result === null) continue;
4488 if (result.score < threshold) continue;
4489 if (resultsLen < limit) {
4494 if (result.score > q.peek().score) q.replaceTop(result);
4498 if (resultsLen === 0) return noResults;
4499 var results = new Array(resultsLen);
4500 for (var i = resultsLen - 1; i >= 0; --i) {
4501 results[i] = q.poll();
4503 results.total = resultsLen + limitedCount;
4506 goAsync: function goAsync(search, targets, options) {
4507 var canceled = false;
4508 var p = new Promise(function (resolve, reject) {
4509 if (search == 'farzher') return resolve([{
4510 target: "farzher was here (^-^*)/",
4512 indexes: [0, 1, 2, 3, 4, 5, 6],
4513 obj: targets ? targets[0] : null
4515 if (!search) return resolve(noResults);
4516 search = fuzzysort.prepareSearch(search);
4517 var searchLowerCode = search[0];
4518 var q = fastpriorityqueue();
4519 var iCurrent = targets.length - 1;
4520 var threshold = options && options.threshold || instanceOptions && instanceOptions.threshold || -9007199254740991;
4521 var limit = options && options.limit || instanceOptions && instanceOptions.limit || 9007199254740991;
4522 var allowTypo = options && options.allowTypo !== undefined ? options.allowTypo : instanceOptions && instanceOptions.allowTypo !== undefined ? instanceOptions.allowTypo : true;
4523 var algorithm = allowTypo ? fuzzysort.algorithm : fuzzysort.algorithmNoTypo;
4525 var limitedCount = 0;
4527 if (canceled) return reject('canceled');
4528 var startMs = Date.now();
4530 // This code is copy/pasted 3 times for performance reasons [options.keys, options.key, no keys]
4533 if (options && options.keys) {
4534 var scoreFn = options.scoreFn || defaultScoreFn;
4535 var keys = options.keys;
4536 var keysLen = keys.length;
4537 for (; iCurrent >= 0; --iCurrent) {
4538 if (iCurrent % 1000 /*itemsPerCheck*/ === 0) {
4539 if (Date.now() - startMs >= 10 /*asyncInterval*/) {
4540 isNode ? setImmediate(step) : setTimeout(step);
4544 var obj = targets[iCurrent];
4545 var objResults = new Array(keysLen);
4546 for (var keyI = keysLen - 1; keyI >= 0; --keyI) {
4547 var key = keys[keyI];
4548 var target = getValue(obj, key);
4550 objResults[keyI] = null;
4553 if (!isObj(target)) target = fuzzysort.getPrepared(target);
4554 objResults[keyI] = algorithm(search, target, searchLowerCode);
4556 objResults.obj = obj; // before scoreFn so scoreFn can use it
4557 var score = scoreFn(objResults);
4558 if (score === null) continue;
4559 if (score < threshold) continue;
4560 objResults.score = score;
4561 if (resultsLen < limit) {
4566 if (score > q.peek().score) q.replaceTop(objResults);
4571 } else if (options && options.key) {
4572 var key = options.key;
4573 for (; iCurrent >= 0; --iCurrent) {
4574 if (iCurrent % 1000 /*itemsPerCheck*/ === 0) {
4575 if (Date.now() - startMs >= 10 /*asyncInterval*/) {
4576 isNode ? setImmediate(step) : setTimeout(step);
4580 var obj = targets[iCurrent];
4581 var target = getValue(obj, key);
4582 if (!target) continue;
4583 if (!isObj(target)) target = fuzzysort.getPrepared(target);
4584 var result = algorithm(search, target, searchLowerCode);
4585 if (result === null) continue;
4586 if (result.score < threshold) continue;
4588 // have to clone result so duplicate targets from different obj can each reference the correct obj
4590 target: result.target,
4591 _targetLowerCodes: null,
4592 _nextBeginningIndexes: null,
4593 score: result.score,
4594 indexes: result.indexes,
4598 if (resultsLen < limit) {
4603 if (result.score > q.peek().score) q.replaceTop(result);
4609 for (; iCurrent >= 0; --iCurrent) {
4610 if (iCurrent % 1000 /*itemsPerCheck*/ === 0) {
4611 if (Date.now() - startMs >= 10 /*asyncInterval*/) {
4612 isNode ? setImmediate(step) : setTimeout(step);
4616 var target = targets[iCurrent];
4617 if (!target) continue;
4618 if (!isObj(target)) target = fuzzysort.getPrepared(target);
4619 var result = algorithm(search, target, searchLowerCode);
4620 if (result === null) continue;
4621 if (result.score < threshold) continue;
4622 if (resultsLen < limit) {
4627 if (result.score > q.peek().score) q.replaceTop(result);
4631 if (resultsLen === 0) return resolve(noResults);
4632 var results = new Array(resultsLen);
4633 for (var i = resultsLen - 1; i >= 0; --i) {
4634 results[i] = q.poll();
4636 results.total = resultsLen + limitedCount;
4639 isNode ? setImmediate(step) : step(); //setTimeout here is too slow
4642 p.cancel = function () {
4647 highlight: function highlight(result, hOpen, hClose) {
4648 if (typeof hOpen == 'function') return fuzzysort.highlightCallback(result, hOpen);
4649 if (result === null) return null;
4650 if (hOpen === undefined) hOpen = '<b>';
4651 if (hClose === undefined) hClose = '</b>';
4652 var highlighted = '';
4653 var matchesIndex = 0;
4655 var target = result.target;
4656 var targetLen = target.length;
4657 var matchesBest = result.indexes;
4658 for (var i = 0; i < targetLen; ++i) {
4659 var char = target[i];
4660 if (matchesBest[matchesIndex] === i) {
4664 highlighted += hOpen;
4666 if (matchesIndex === matchesBest.length) {
4667 highlighted += char + hClose + target.substr(i + 1);
4673 highlighted += hClose;
4676 highlighted += char;
4680 highlightCallback: function highlightCallback(result, cb) {
4681 if (result === null) return null;
4682 var target = result.target;
4683 var targetLen = target.length;
4684 var indexes = result.indexes;
4685 var highlighted = '';
4690 for (var i = 0; i < targetLen; ++i) {
4691 var char = target[i];
4692 if (indexes[indexesI] === i) {
4696 result.push(highlighted);
4699 if (indexesI === indexes.length) {
4700 highlighted += char;
4701 result.push(cb(highlighted, matchI++));
4703 result.push(target.substr(i + 1));
4709 result.push(cb(highlighted, matchI++));
4713 highlighted += char;
4717 prepare: function prepare(target) {
4718 if (!target) return {
4720 _targetLowerCodes: [0 /*this 0 doesn't make sense. here because an empty array causes the algorithm to deoptimize and run 50% slower!*/],
4721 _nextBeginningIndexes: null,
4728 _targetLowerCodes: fuzzysort.prepareLowerCodes(target),
4729 _nextBeginningIndexes: null,
4736 prepareSlow: function prepareSlow(target) {
4737 if (!target) return {
4739 _targetLowerCodes: [0 /*this 0 doesn't make sense. here because an empty array causes the algorithm to deoptimize and run 50% slower!*/],
4740 _nextBeginningIndexes: null,
4747 _targetLowerCodes: fuzzysort.prepareLowerCodes(target),
4748 _nextBeginningIndexes: fuzzysort.prepareNextBeginningIndexes(target),
4755 prepareSearch: function prepareSearch(search) {
4756 if (!search) search = '';
4757 return fuzzysort.prepareLowerCodes(search);
4759 // Below this point is only internal code
4760 // Below this point is only internal code
4761 // Below this point is only internal code
4762 // Below this point is only internal code
4764 getPrepared: function getPrepared(target) {
4765 if (target.length > 999) return fuzzysort.prepare(target); // don't cache huge targets
4766 var targetPrepared = preparedCache.get(target);
4767 if (targetPrepared !== undefined) return targetPrepared;
4768 targetPrepared = fuzzysort.prepare(target);
4769 preparedCache.set(target, targetPrepared);
4770 return targetPrepared;
4772 getPreparedSearch: function getPreparedSearch(search) {
4773 if (search.length > 999) return fuzzysort.prepareSearch(search); // don't cache huge searches
4774 var searchPrepared = preparedSearchCache.get(search);
4775 if (searchPrepared !== undefined) return searchPrepared;
4776 searchPrepared = fuzzysort.prepareSearch(search);
4777 preparedSearchCache.set(search, searchPrepared);
4778 return searchPrepared;
4780 algorithm: function algorithm(searchLowerCodes, prepared, searchLowerCode) {
4781 var targetLowerCodes = prepared._targetLowerCodes;
4782 var searchLen = searchLowerCodes.length;
4783 var targetLen = targetLowerCodes.length;
4784 var searchI = 0; // where we at
4785 var targetI = 0; // where you at
4786 var typoSimpleI = 0;
4787 var matchesSimpleLen = 0;
4789 // very basic fuzzy match; to remove non-matching targets ASAP!
4790 // walk through target. find sequential matches.
4791 // if all chars aren't found then exit
4793 var isMatch = searchLowerCode === targetLowerCodes[targetI];
4795 matchesSimple[matchesSimpleLen++] = targetI;
4797 if (searchI === searchLen) break;
4798 searchLowerCode = searchLowerCodes[typoSimpleI === 0 ? searchI : typoSimpleI === searchI ? searchI + 1 : typoSimpleI === searchI - 1 ? searchI - 1 : searchI];
4801 if (targetI >= targetLen) {
4802 // Failed to find searchI
4803 // Check for typo or exit
4804 // we go as far as possible before trying to transpose
4805 // then we transpose backwards until we reach the beginning
4807 if (searchI <= 1) return null; // not allowed to transpose first char
4808 if (typoSimpleI === 0) {
4809 // we haven't tried to transpose yet
4811 var searchLowerCodeNew = searchLowerCodes[searchI];
4812 if (searchLowerCode === searchLowerCodeNew) continue; // doesn't make sense to transpose a repeat char
4813 typoSimpleI = searchI;
4815 if (typoSimpleI === 1) return null; // reached the end of the line for transposing
4817 searchI = typoSimpleI;
4818 searchLowerCode = searchLowerCodes[searchI + 1];
4819 var searchLowerCodeNew = searchLowerCodes[searchI];
4820 if (searchLowerCode === searchLowerCodeNew) continue; // doesn't make sense to transpose a repeat char
4823 matchesSimpleLen = searchI;
4824 targetI = matchesSimple[matchesSimpleLen - 1] + 1;
4830 var typoStrictI = 0;
4831 var successStrict = false;
4832 var matchesStrictLen = 0;
4833 var nextBeginningIndexes = prepared._nextBeginningIndexes;
4834 if (nextBeginningIndexes === null) nextBeginningIndexes = prepared._nextBeginningIndexes = fuzzysort.prepareNextBeginningIndexes(prepared.target);
4835 var firstPossibleI = targetI = matchesSimple[0] === 0 ? 0 : nextBeginningIndexes[matchesSimple[0] - 1];
4837 // Our target string successfully matched all characters in sequence!
4838 // Let's try a more advanced and strict test to improve the score
4839 // only count it as a match if it's consecutive or a beginning character!
4840 if (targetI !== targetLen) for (;;) {
4841 if (targetI >= targetLen) {
4842 // We failed to find a good spot for this search char, go back to the previous search char and force it forward
4844 // We failed to push chars forward for a better match
4845 // transpose, starting from the beginning
4847 if (typoStrictI > searchLen - 2) break;
4848 if (searchLowerCodes[typoStrictI] === searchLowerCodes[typoStrictI + 1]) continue; // doesn't make sense to transpose a repeat char
4849 targetI = firstPossibleI;
4853 var lastMatch = matchesStrict[--matchesStrictLen];
4854 targetI = nextBeginningIndexes[lastMatch];
4856 var isMatch = searchLowerCodes[typoStrictI === 0 ? searchI : typoStrictI === searchI ? searchI + 1 : typoStrictI === searchI - 1 ? searchI - 1 : searchI] === targetLowerCodes[targetI];
4858 matchesStrict[matchesStrictLen++] = targetI;
4860 if (searchI === searchLen) {
4861 successStrict = true;
4866 targetI = nextBeginningIndexes[targetI];
4871 // tally up the score & keep track of matches for highlighting later
4872 if (successStrict) {
4873 var matchesBest = matchesStrict;
4874 var matchesBestLen = matchesStrictLen;
4876 var matchesBest = matchesSimple;
4877 var matchesBestLen = matchesSimpleLen;
4880 var lastTargetI = -1;
4881 for (var i = 0; i < searchLen; ++i) {
4882 var targetI = matchesBest[i];
4883 // score only goes down if they're not consecutive
4884 if (lastTargetI !== targetI - 1) score -= targetI;
4885 lastTargetI = targetI;
4887 if (!successStrict) {
4889 if (typoSimpleI !== 0) score += -20; /*typoPenalty*/
4891 if (typoStrictI !== 0) score += -20; /*typoPenalty*/
4894 score -= targetLen - searchLen;
4895 prepared.score = score;
4896 prepared.indexes = new Array(matchesBestLen);
4897 for (var i = matchesBestLen - 1; i >= 0; --i) {
4898 prepared.indexes[i] = matchesBest[i];
4903 algorithmNoTypo: function algorithmNoTypo(searchLowerCodes, prepared, searchLowerCode) {
4904 var targetLowerCodes = prepared._targetLowerCodes;
4905 var searchLen = searchLowerCodes.length;
4906 var targetLen = targetLowerCodes.length;
4907 var searchI = 0; // where we at
4908 var targetI = 0; // where you at
4909 var matchesSimpleLen = 0;
4911 // very basic fuzzy match; to remove non-matching targets ASAP!
4912 // walk through target. find sequential matches.
4913 // if all chars aren't found then exit
4915 var isMatch = searchLowerCode === targetLowerCodes[targetI];
4917 matchesSimple[matchesSimpleLen++] = targetI;
4919 if (searchI === searchLen) break;
4920 searchLowerCode = searchLowerCodes[searchI];
4923 if (targetI >= targetLen) return null; // Failed to find searchI
4927 var successStrict = false;
4928 var matchesStrictLen = 0;
4929 var nextBeginningIndexes = prepared._nextBeginningIndexes;
4930 if (nextBeginningIndexes === null) nextBeginningIndexes = prepared._nextBeginningIndexes = fuzzysort.prepareNextBeginningIndexes(prepared.target);
4931 targetI = matchesSimple[0] === 0 ? 0 : nextBeginningIndexes[matchesSimple[0] - 1];
4933 // Our target string successfully matched all characters in sequence!
4934 // Let's try a more advanced and strict test to improve the score
4935 // only count it as a match if it's consecutive or a beginning character!
4936 if (targetI !== targetLen) for (;;) {
4937 if (targetI >= targetLen) {
4938 // We failed to find a good spot for this search char, go back to the previous search char and force it forward
4939 if (searchI <= 0) break; // We failed to push chars forward for a better match
4942 var lastMatch = matchesStrict[--matchesStrictLen];
4943 targetI = nextBeginningIndexes[lastMatch];
4945 var isMatch = searchLowerCodes[searchI] === targetLowerCodes[targetI];
4947 matchesStrict[matchesStrictLen++] = targetI;
4949 if (searchI === searchLen) {
4950 successStrict = true;
4955 targetI = nextBeginningIndexes[targetI];
4960 // tally up the score & keep track of matches for highlighting later
4961 if (successStrict) {
4962 var matchesBest = matchesStrict;
4963 var matchesBestLen = matchesStrictLen;
4965 var matchesBest = matchesSimple;
4966 var matchesBestLen = matchesSimpleLen;
4969 var lastTargetI = -1;
4970 for (var i = 0; i < searchLen; ++i) {
4971 var targetI = matchesBest[i];
4972 // score only goes down if they're not consecutive
4973 if (lastTargetI !== targetI - 1) score -= targetI;
4974 lastTargetI = targetI;
4976 if (!successStrict) score *= 1000;
4977 score -= targetLen - searchLen;
4978 prepared.score = score;
4979 prepared.indexes = new Array(matchesBestLen);
4980 for (var i = matchesBestLen - 1; i >= 0; --i) {
4981 prepared.indexes[i] = matchesBest[i];
4986 prepareLowerCodes: function prepareLowerCodes(str) {
4987 var strLen = str.length;
4988 var lowerCodes = []; // new Array(strLen) sparse array is too slow
4989 var lower = str.toLowerCase();
4990 for (var i = 0; i < strLen; ++i) {
4991 lowerCodes[i] = lower.charCodeAt(i);
4995 prepareBeginningIndexes: function prepareBeginningIndexes(target) {
4996 var targetLen = target.length;
4997 var beginningIndexes = [];
4998 var beginningIndexesLen = 0;
4999 var wasUpper = false;
5000 var wasAlphanum = false;
5001 for (var i = 0; i < targetLen; ++i) {
5002 var targetCode = target.charCodeAt(i);
5003 var isUpper = targetCode >= 65 && targetCode <= 90;
5004 var isAlphanum = isUpper || targetCode >= 97 && targetCode <= 122 || targetCode >= 48 && targetCode <= 57;
5005 var isBeginning = isUpper && !wasUpper || !wasAlphanum || !isAlphanum;
5007 wasAlphanum = isAlphanum;
5008 if (isBeginning) beginningIndexes[beginningIndexesLen++] = i;
5010 return beginningIndexes;
5012 prepareNextBeginningIndexes: function prepareNextBeginningIndexes(target) {
5013 var targetLen = target.length;
5014 var beginningIndexes = fuzzysort.prepareBeginningIndexes(target);
5015 var nextBeginningIndexes = []; // new Array(targetLen) sparse array is too slow
5016 var lastIsBeginning = beginningIndexes[0];
5017 var lastIsBeginningI = 0;
5018 for (var i = 0; i < targetLen; ++i) {
5019 if (lastIsBeginning > i) {
5020 nextBeginningIndexes[i] = lastIsBeginning;
5022 lastIsBeginning = beginningIndexes[++lastIsBeginningI];
5023 nextBeginningIndexes[i] = lastIsBeginning === undefined ? targetLen : lastIsBeginning;
5026 return nextBeginningIndexes;
5034 // This stuff is outside fuzzysortNew, because it's shared with instances of fuzzysort.new()
5035 var isNode = typeof commonjsRequire !== 'undefined' && typeof window === 'undefined';
5036 var MyMap = typeof Map === 'function' ? Map : function () {
5037 var s = Object.create(null);
5038 this.get = function (k) {
5041 this.set = function (k, val) {
5045 this.clear = function () {
5046 s = Object.create(null);
5049 var preparedCache = new MyMap();
5050 var preparedSearchCache = new MyMap();
5052 noResults.total = 0;
5053 var matchesSimple = [];
5054 var matchesStrict = [];
5055 function cleanup() {
5056 preparedCache.clear();
5057 preparedSearchCache.clear();
5061 function defaultScoreFn(a) {
5062 var max = -9007199254740991;
5063 for (var i = a.length - 1; i >= 0; --i) {
5065 if (result === null) continue;
5066 var score = result.score;
5067 if (score > max) max = score;
5069 if (max === -9007199254740991) return null;
5073 // prop = 'key' 2.5ms optimized for this case, seems to be about as fast as direct obj[prop]
5074 // prop = 'key1.key2' 10ms
5075 // prop = ['key1', 'key2'] 27ms
5076 function getValue(obj, prop) {
5077 var tmp = obj[prop];
5078 if (tmp !== undefined) return tmp;
5080 if (!Array.isArray(prop)) segs = prop.split('.');
5081 var len = segs.length;
5083 while (obj && ++i < len) {
5089 return _typeof(x) === 'object';
5090 } // faster as a function
5092 // Hacked version of https://github.com/lemire/FastPriorityQueue.js
5093 var fastpriorityqueue = function fastpriorityqueue() {
5098 for (var e = 0, n = r[e], c = 1; c < o;) {
5100 e = c, f < o && r[f].score < r[c].score && (e = f), r[e - 1 >> 1] = r[e], c = 1 + (e << 1);
5102 for (var a = e - 1 >> 1; e > 0 && n.score < r[a].score; a = (e = a) - 1 >> 1) {
5107 return e.add = function (e) {
5110 for (var c = n - 1 >> 1; n > 0 && e.score < r[c].score; c = (n = c) - 1 >> 1) {
5114 }, e.poll = function () {
5117 return r[0] = r[--o], n(), e;
5119 }, e.peek = function (e) {
5120 if (0 !== o) return r[0];
5121 }, e.replaceTop = function (o) {
5125 var q = fastpriorityqueue(); // reuse this, except for async, it needs to make its own
5127 return fuzzysortNew();
5130 // TODO: (performance) wasm version!?
5131 // TODO: (performance) threads?
5132 // TODO: (performance) avoid cache misses
5133 // TODO: (performance) preparedCache is a memory leak
5134 // TODO: (like sublime) backslash === forwardslash
5135 // TODO: (like sublime) spaces: "a b" should do 2 searches 1 for a and 1 for b
5136 // TODO: (scoring) garbage in targets that allows most searches to strict match need a penality
5137 // TODO: (performance) idk if allowTypo is optimized
5139 var fuzzysort = fuzzysort$1.exports;
5147 // Escape text for attribute or text content.
5148 function escapeText(str) {
5153 // Both single quotes and double quotes (for attributes)
5154 return ('' + str).replace(/['"<>&]/g, function (s) {
5170 // Don't load the HTML Reporter on non-browser environments
5171 if (!window$1 || !document) {
5174 QUnit.reporters.perf.init(QUnit);
5175 var config = QUnit.config;
5176 var hiddenTests = [];
5177 var collapseNext = false;
5178 var hasOwn = Object.prototype.hasOwnProperty;
5179 var unfilteredUrl = setUrl({
5182 moduleId: undefined,
5185 var dropdownData = null;
5186 function trim(string) {
5187 if (typeof string.trim === 'function') {
5188 return string.trim();
5190 return string.replace(/^\s+|\s+$/g, '');
5193 function addEvent(elem, type, fn) {
5194 elem.addEventListener(type, fn, false);
5196 function removeEvent(elem, type, fn) {
5197 elem.removeEventListener(type, fn, false);
5199 function addEvents(elems, type, fn) {
5200 var i = elems.length;
5202 addEvent(elems[i], type, fn);
5205 function hasClass(elem, name) {
5206 return (' ' + elem.className + ' ').indexOf(' ' + name + ' ') >= 0;
5208 function addClass(elem, name) {
5209 if (!hasClass(elem, name)) {
5210 elem.className += (elem.className ? ' ' : '') + name;
5213 function toggleClass(elem, name, force) {
5214 if (force || typeof force === 'undefined' && !hasClass(elem, name)) {
5215 addClass(elem, name);
5217 removeClass(elem, name);
5220 function removeClass(elem, name) {
5221 var set = ' ' + elem.className + ' ';
5223 // Class name may appear multiple times
5224 while (set.indexOf(' ' + name + ' ') >= 0) {
5225 set = set.replace(' ' + name + ' ', ' ');
5228 // Trim for prettiness
5229 elem.className = trim(set);
5232 return document.getElementById && document.getElementById(name);
5234 function abortTests() {
5235 var abortButton = id('qunit-abort-tests-button');
5237 abortButton.disabled = true;
5238 abortButton.innerHTML = 'Aborting...';
5240 QUnit.config.queue.length = 0;
5243 function interceptNavigation(ev) {
5244 // Trim potential accidental whitespace so that QUnit doesn't throw an error about no tests matching the filter.
5245 var filterInputElem = id('qunit-filter-input');
5246 filterInputElem.value = trim(filterInputElem.value);
5248 if (ev && ev.preventDefault) {
5249 ev.preventDefault();
5253 function getUrlConfigHtml() {
5254 var selection = false;
5255 var urlConfig = config.urlConfig;
5256 var urlConfigHtml = '';
5257 for (var i = 0; i < urlConfig.length; i++) {
5258 // Options can be either strings or objects with nonempty "id" properties
5259 var val = config.urlConfig[i];
5260 if (typeof val === 'string') {
5266 var escaped = escapeText(val.id);
5267 var escapedTooltip = escapeText(val.tooltip);
5268 if (!val.value || typeof val.value === 'string') {
5269 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>';
5271 urlConfigHtml += "<label for='qunit-urlconfig-" + escaped + "' title='" + escapedTooltip + "'>" + val.label + ": </label><select id='qunit-urlconfig-" + escaped + "' name='" + escaped + "' title='" + escapedTooltip + "'><option></option>";
5272 if (Array.isArray(val.value)) {
5273 for (var j = 0; j < val.value.length; j++) {
5274 escaped = escapeText(val.value[j]);
5275 urlConfigHtml += "<option value='" + escaped + "'" + (config[val.id] === val.value[j] ? (selection = true) && " selected='selected'" : '') + '>' + escaped + '</option>';
5278 for (var _j in val.value) {
5279 if (hasOwn.call(val.value, _j)) {
5280 urlConfigHtml += "<option value='" + escapeText(_j) + "'" + (config[val.id] === _j ? (selection = true) && " selected='selected'" : '') + '>' + escapeText(val.value[_j]) + '</option>';
5284 if (config[val.id] && !selection) {
5285 escaped = escapeText(config[val.id]);
5286 urlConfigHtml += "<option value='" + escaped + "' selected='selected' disabled='disabled'>" + escaped + '</option>';
5288 urlConfigHtml += '</select>';
5291 return urlConfigHtml;
5294 // Handle "click" events on toolbar checkboxes and "change" for select menus.
5295 // Updates the URL with the new state of `config.urlConfig` values.
5296 function toolbarChanged() {
5300 // Detect if field is a select menu or a checkbox
5302 if ('selectedIndex' in field) {
5303 value = field.options[field.selectedIndex].value || undefined;
5305 value = field.checked ? field.defaultValue || true : undefined;
5307 params[field.name] = value;
5308 var updatedUrl = setUrl(params);
5310 // Check if we can apply the change without a page refresh
5311 if (field.name === 'hidepassed' && 'replaceState' in window$1.history) {
5312 QUnit.urlParams[field.name] = value;
5313 config[field.name] = value || false;
5314 var tests = id('qunit-tests');
5316 var length = tests.children.length;
5317 var children = tests.children;
5318 if (field.checked) {
5319 for (var i = 0; i < length; i++) {
5320 var test = children[i];
5321 var className = test ? test.className : '';
5322 var classNameHasPass = className.indexOf('pass') > -1;
5323 var classNameHasSkipped = className.indexOf('skipped') > -1;
5324 if (classNameHasPass || classNameHasSkipped) {
5325 hiddenTests.push(test);
5328 var _iterator = _createForOfIteratorHelper(hiddenTests),
5331 for (_iterator.s(); !(_step = _iterator.n()).done;) {
5332 var hiddenTest = _step.value;
5333 tests.removeChild(hiddenTest);
5342 while ((_test = hiddenTests.pop()) != null) {
5343 tests.appendChild(_test);
5347 window$1.history.replaceState(null, '', updatedUrl);
5349 window$1.location = updatedUrl;
5352 function setUrl(params) {
5353 var querystring = '?';
5354 var location = window$1.location;
5355 params = extend(extend({}, QUnit.urlParams), params);
5356 for (var key in params) {
5357 // Skip inherited or undefined properties
5358 if (hasOwn.call(params, key) && params[key] !== undefined) {
5359 // Output a parameter for each value of this key
5360 // (but usually just one)
5361 var arrValue = [].concat(params[key]);
5362 for (var i = 0; i < arrValue.length; i++) {
5363 querystring += encodeURIComponent(key);
5364 if (arrValue[i] !== true) {
5365 querystring += '=' + encodeURIComponent(arrValue[i]);
5371 return location.protocol + '//' + location.host + location.pathname + querystring.slice(0, -1);
5373 function applyUrlParams() {
5374 var filter = id('qunit-filter-input').value;
5375 window$1.location = setUrl({
5376 filter: filter === '' ? undefined : filter,
5377 moduleId: _toConsumableArray(dropdownData.selectedMap.keys()),
5378 // Remove module and testId filter
5383 function toolbarUrlConfigContainer() {
5384 var urlConfigContainer = document.createElement('span');
5385 urlConfigContainer.innerHTML = getUrlConfigHtml();
5386 addClass(urlConfigContainer, 'qunit-url-config');
5387 addEvents(urlConfigContainer.getElementsByTagName('input'), 'change', toolbarChanged);
5388 addEvents(urlConfigContainer.getElementsByTagName('select'), 'change', toolbarChanged);
5389 return urlConfigContainer;
5391 function abortTestsButton() {
5392 var button = document.createElement('button');
5393 button.id = 'qunit-abort-tests-button';
5394 button.innerHTML = 'Abort';
5395 addEvent(button, 'click', abortTests);
5398 function toolbarLooseFilter() {
5399 var filter = document.createElement('form');
5400 var label = document.createElement('label');
5401 var input = document.createElement('input');
5402 var button = document.createElement('button');
5403 addClass(filter, 'qunit-filter');
5404 label.innerHTML = 'Filter: ';
5405 input.type = 'text';
5406 input.value = config.filter || '';
5407 input.name = 'filter';
5408 input.id = 'qunit-filter-input';
5409 button.innerHTML = 'Go';
5410 label.appendChild(input);
5411 filter.appendChild(label);
5412 filter.appendChild(document.createTextNode(' '));
5413 filter.appendChild(button);
5414 addEvent(filter, 'submit', interceptNavigation);
5417 function createModuleListItem(moduleId, name, checked) {
5418 return '<li><label class="clickable' + (checked ? ' checked' : '') + '"><input type="checkbox" ' + 'value="' + escapeText(moduleId) + '"' + (checked ? ' checked="checked"' : '') + ' />' + escapeText(name) + '</label></li>';
5422 * @param {Array} Results from fuzzysort
5423 * @return {string} HTML
5425 function moduleListHtml(results) {
5428 // Hoist the already selected items, and show them always
5429 // even if not matched by the current search.
5430 dropdownData.selectedMap.forEach(function (name, moduleId) {
5431 html += createModuleListItem(moduleId, name, true);
5433 for (var i = 0; i < results.length; i++) {
5434 var mod = results[i].obj;
5435 if (!dropdownData.selectedMap.has(mod.moduleId)) {
5436 html += createModuleListItem(mod.moduleId, mod.name, false);
5441 function toolbarModuleFilter(beginDetails) {
5442 var initialSelected = null;
5444 options: beginDetails.modules.slice(),
5445 selectedMap: new StringMap(),
5446 isDirty: function isDirty() {
5447 return _toConsumableArray(dropdownData.selectedMap.keys()).sort().join(',') !== _toConsumableArray(initialSelected.keys()).sort().join(',');
5450 if (config.moduleId.length) {
5451 // The module dropdown is seeded with the runtime configuration of the last run.
5453 // We don't reference `config.moduleId` directly after this and keep our own
5455 // 1. This naturally filters out unknown moduleIds.
5456 // 2. Gives us a place to manage and remember unsubmitted checkbox changes.
5457 // 3. Gives us an efficient way to map a selected moduleId to module name
5458 // during rendering.
5459 for (var i = 0; i < beginDetails.modules.length; i++) {
5460 var mod = beginDetails.modules[i];
5461 if (config.moduleId.indexOf(mod.moduleId) !== -1) {
5462 dropdownData.selectedMap.set(mod.moduleId, mod.name);
5466 initialSelected = new StringMap(dropdownData.selectedMap);
5467 var moduleSearch = document.createElement('input');
5468 moduleSearch.id = 'qunit-modulefilter-search';
5469 moduleSearch.autocomplete = 'off';
5470 addEvent(moduleSearch, 'input', searchInput);
5471 addEvent(moduleSearch, 'input', searchFocus);
5472 addEvent(moduleSearch, 'focus', searchFocus);
5473 addEvent(moduleSearch, 'click', searchFocus);
5474 var label = document.createElement('label');
5475 label.htmlFor = 'qunit-modulefilter-search';
5476 label.textContent = 'Module:';
5477 var searchContainer = document.createElement('span');
5478 searchContainer.id = 'qunit-modulefilter-search-container';
5479 searchContainer.appendChild(moduleSearch);
5480 var applyButton = document.createElement('button');
5481 applyButton.textContent = 'Apply';
5482 applyButton.title = 'Re-run the selected test modules';
5483 addEvent(applyButton, 'click', applyUrlParams);
5484 var resetButton = document.createElement('button');
5485 resetButton.textContent = 'Reset';
5486 resetButton.type = 'reset';
5487 resetButton.title = 'Restore the previous module selection';
5488 var clearButton = document.createElement('button');
5489 clearButton.textContent = 'Select none';
5490 clearButton.type = 'button';
5491 clearButton.title = 'Clear the current module selection';
5492 addEvent(clearButton, 'click', function () {
5493 dropdownData.selectedMap.clear();
5497 var actions = document.createElement('span');
5498 actions.id = 'qunit-modulefilter-actions';
5499 actions.appendChild(applyButton);
5500 actions.appendChild(resetButton);
5501 if (initialSelected.size) {
5502 // Only show clear button if functionally different from reset
5503 actions.appendChild(clearButton);
5505 var dropDownList = document.createElement('ul');
5506 dropDownList.id = 'qunit-modulefilter-dropdown-list';
5507 var dropDown = document.createElement('div');
5508 dropDown.id = 'qunit-modulefilter-dropdown';
5509 dropDown.style.display = 'none';
5510 dropDown.appendChild(actions);
5511 dropDown.appendChild(dropDownList);
5512 addEvent(dropDown, 'change', selectionChange);
5513 searchContainer.appendChild(dropDown);
5514 // Set initial moduleSearch.placeholder and clearButton/resetButton.
5516 var moduleFilter = document.createElement('form');
5517 moduleFilter.id = 'qunit-modulefilter';
5518 moduleFilter.appendChild(label);
5519 moduleFilter.appendChild(document.createTextNode(' '));
5520 moduleFilter.appendChild(searchContainer);
5521 addEvent(moduleFilter, 'submit', interceptNavigation);
5522 addEvent(moduleFilter, 'reset', function () {
5523 dropdownData.selectedMap = new StringMap(initialSelected);
5524 // Set moduleSearch.placeholder and reflect non-dirty state
5529 // Enables show/hide for the dropdown
5530 function searchFocus() {
5531 if (dropDown.style.display !== 'none') {
5535 // Optimization: Defer rendering options until focussed.
5536 // https://github.com/qunitjs/qunit/issues/1664
5538 dropDown.style.display = 'block';
5540 // Hide on Escape keydown or on click outside the container
5541 addEvent(document, 'click', hideHandler);
5542 addEvent(document, 'keydown', hideHandler);
5543 function hideHandler(e) {
5544 var inContainer = moduleFilter.contains(e.target);
5545 if (e.keyCode === 27 || !inContainer) {
5546 if (e.keyCode === 27 && inContainer) {
5547 moduleSearch.focus();
5549 dropDown.style.display = 'none';
5550 removeEvent(document, 'click', hideHandler);
5551 removeEvent(document, 'keydown', hideHandler);
5552 moduleSearch.value = '';
5559 * @param {string} searchText
5560 * @return {string} HTML
5562 function filterModules(searchText) {
5564 if (searchText === '') {
5565 // Improve on-boarding experience by having an immediate display of
5566 // module names, indicating how the interface works. This also makes
5567 // for a quicker interaction in the common case of small projects.
5568 // Don't mandate typing just to get the menu.
5569 results = dropdownData.options.slice(0, 20).map(function (obj) {
5570 // Fake empty results. https://github.com/farzher/fuzzysort/issues/41
5576 results = fuzzysort.go(searchText, dropdownData.options, {
5582 return moduleListHtml(results);
5585 // Processes module search box input
5586 var searchInputTimeout;
5587 function searchInput() {
5588 // Use a debounce with a ~0ms timeout. This is effectively instantaneous,
5589 // but is better than undebounced because it avoids an ever-growing
5590 // backlog of unprocessed now-outdated input events if fuzzysearch or
5591 // drodown DOM is slow (e.g. very large test suite).
5592 window$1.clearTimeout(searchInputTimeout);
5593 searchInputTimeout = window$1.setTimeout(function () {
5594 dropDownList.innerHTML = filterModules(moduleSearch.value);
5598 // Processes checkbox change, or a generic render (initial render, or after reset event)
5599 // Avoid any dropdown rendering here as this is used by toolbarModuleFilter()
5600 // during the initial render, which should not delay test execution.
5601 function selectionChange(evt) {
5602 var checkbox = evt && evt.target || null;
5604 // Update internal state
5605 if (checkbox.checked) {
5606 dropdownData.selectedMap.set(checkbox.value, checkbox.parentNode.textContent);
5608 dropdownData.selectedMap.delete(checkbox.value);
5612 toggleClass(checkbox.parentNode, 'checked', checkbox.checked);
5614 var textForm = dropdownData.selectedMap.size ? dropdownData.selectedMap.size + ' ' + (dropdownData.selectedMap.size === 1 ? 'module' : 'modules') : 'All modules';
5615 moduleSearch.placeholder = textForm;
5616 moduleSearch.title = 'Type to search through and reduce the list.';
5617 resetButton.disabled = !dropdownData.isDirty();
5618 clearButton.style.display = dropdownData.selectedMap.size ? '' : 'none';
5620 return moduleFilter;
5622 function appendToolbar(beginDetails) {
5623 var toolbar = id('qunit-testrunner-toolbar');
5625 toolbar.appendChild(toolbarUrlConfigContainer());
5626 var toolbarFilters = document.createElement('span');
5627 toolbarFilters.id = 'qunit-toolbar-filters';
5628 toolbarFilters.appendChild(toolbarLooseFilter());
5629 toolbarFilters.appendChild(toolbarModuleFilter(beginDetails));
5630 var clearfix = document.createElement('div');
5631 clearfix.className = 'clearfix';
5632 toolbar.appendChild(toolbarFilters);
5633 toolbar.appendChild(clearfix);
5636 function appendHeader() {
5637 var header = id('qunit-header');
5639 header.innerHTML = "<a href='" + escapeText(unfilteredUrl) + "'>" + header.innerHTML + '</a> ';
5642 function appendBanner() {
5643 var banner = id('qunit-banner');
5645 banner.className = '';
5648 function appendTestResults() {
5649 var tests = id('qunit-tests');
5650 var result = id('qunit-testresult');
5653 result.parentNode.removeChild(result);
5656 tests.innerHTML = '';
5657 result = document.createElement('p');
5658 result.id = 'qunit-testresult';
5659 result.className = 'result';
5660 tests.parentNode.insertBefore(result, tests);
5661 result.innerHTML = '<div id="qunit-testresult-display">Running...<br /> </div>' + '<div id="qunit-testresult-controls"></div>' + '<div class="clearfix"></div>';
5662 controls = id('qunit-testresult-controls');
5665 controls.appendChild(abortTestsButton());
5668 function appendFilteredTest() {
5669 var testId = QUnit.config.testId;
5670 if (!testId || testId.length <= 0) {
5673 return "<div id='qunit-filteredTest'>Rerunning selected tests: " + escapeText(testId.join(', ')) + " <a id='qunit-clearFilter' href='" + escapeText(unfilteredUrl) + "'>Run all tests</a></div>";
5675 function appendUserAgent() {
5676 var userAgent = id('qunit-userAgent');
5678 userAgent.innerHTML = '';
5679 userAgent.appendChild(document.createTextNode('QUnit ' + QUnit.version + '; ' + navigator.userAgent));
5682 function appendInterface(beginDetails) {
5683 var qunit = id('qunit');
5685 // For compat with QUnit 1.2, and to support fully custom theme HTML,
5686 // we will use any existing elements if no id="qunit" element exists.
5688 // Note that we don't fail or fallback to creating it ourselves,
5689 // because not having id="qunit" (and not having the below elements)
5690 // simply means QUnit acts headless, allowing users to use their own
5691 // reporters, or for a test runner to listen for events directly without
5692 // having the HTML reporter actively render anything.
5694 qunit.setAttribute('role', 'main');
5696 // Since QUnit 1.3, these are created automatically if the page
5697 // contains id="qunit".
5698 qunit.innerHTML = "<h1 id='qunit-header'>" + escapeText(document.title) + '</h1>' + "<h2 id='qunit-banner'></h2>" + "<div id='qunit-testrunner-toolbar' role='navigation'></div>" + appendFilteredTest() + "<h2 id='qunit-userAgent'></h2>" + "<ol id='qunit-tests'></ol>";
5702 appendTestResults();
5704 appendToolbar(beginDetails);
5706 function appendTest(name, testId, moduleName) {
5707 var tests = id('qunit-tests');
5711 var title = document.createElement('strong');
5712 title.innerHTML = getNameHtml(name, moduleName);
5713 var testBlock = document.createElement('li');
5714 testBlock.appendChild(title);
5716 // No ID or rerun link for "global failure" blocks
5717 if (testId !== undefined) {
5718 var rerunTrigger = document.createElement('a');
5719 rerunTrigger.innerHTML = 'Rerun';
5720 rerunTrigger.href = setUrl({
5723 testBlock.id = 'qunit-test-output-' + testId;
5724 testBlock.appendChild(rerunTrigger);
5726 var assertList = document.createElement('ol');
5727 assertList.className = 'qunit-assert-list';
5728 testBlock.appendChild(assertList);
5729 tests.appendChild(testBlock);
5733 // HTML Reporter initialization and load
5734 QUnit.on('runStart', function (runStart) {
5735 stats.defined = runStart.testCounts.total;
5737 QUnit.begin(function (beginDetails) {
5738 // Initialize QUnit elements
5739 // This is done from begin() instead of runStart, because
5740 // urlparams.js uses begin(), which we need to wait for.
5741 // urlparams.js in turn uses begin() to allow plugins to
5742 // add entries to QUnit.config.urlConfig, which may be done
5744 // <https://github.com/qunitjs/qunit/issues/1657>
5745 appendInterface(beginDetails);
5747 function getRerunFailedHtml(failedTests) {
5748 if (failedTests.length === 0) {
5754 return ["<br /><a href='" + escapeText(href) + "'>", failedTests.length === 1 ? 'Rerun 1 failed test' : 'Rerun ' + failedTests.length + ' failed tests', '</a>'].join('');
5756 QUnit.on('runEnd', function (runEnd) {
5757 var banner = id('qunit-banner');
5758 var tests = id('qunit-tests');
5759 var abortButton = id('qunit-abort-tests-button');
5760 var assertPassed = config.stats.all - config.stats.bad;
5761 var html = [runEnd.testCounts.total, ' tests completed in ', runEnd.runtime, ' milliseconds, with ', runEnd.testCounts.failed, ' failed, ', runEnd.testCounts.skipped, ' skipped, and ', runEnd.testCounts.todo, ' todo.<br />', "<span class='passed'>", assertPassed, "</span> assertions of <span class='total'>", config.stats.all, "</span> passed, <span class='failed'>", config.stats.bad, '</span> failed.', getRerunFailedHtml(stats.failedTests)].join('');
5766 // Update remaining tests to aborted
5767 if (abortButton && abortButton.disabled) {
5768 html = 'Tests aborted after ' + runEnd.runtime + ' milliseconds.';
5769 for (var i = 0; i < tests.children.length; i++) {
5770 test = tests.children[i];
5771 if (test.className === '' || test.className === 'running') {
5772 test.className = 'aborted';
5773 assertList = test.getElementsByTagName('ol')[0];
5774 assertLi = document.createElement('li');
5775 assertLi.className = 'fail';
5776 assertLi.innerHTML = 'Test aborted.';
5777 assertList.appendChild(assertLi);
5781 if (banner && (!abortButton || abortButton.disabled === false)) {
5782 banner.className = runEnd.status === 'failed' ? 'qunit-fail' : 'qunit-pass';
5785 abortButton.parentNode.removeChild(abortButton);
5788 id('qunit-testresult-display').innerHTML = html;
5790 if (config.altertitle && document.title) {
5791 // Show ✖ for good, ✔ for bad suite result in title
5792 // use escape sequences in case file gets loaded with non-utf-8
5794 document.title = [runEnd.status === 'failed' ? "\u2716" : "\u2714", document.title.replace(/^[\u2714\u2716] /i, '')].join(' ');
5797 // Scroll back to top to show results
5798 if (config.scrolltop && window$1.scrollTo) {
5799 window$1.scrollTo(0, 0);
5802 function getNameHtml(name, module) {
5805 nameHtml = "<span class='module-name'>" + escapeText(module) + '</span>: ';
5807 nameHtml += "<span class='test-name'>" + escapeText(name) + '</span>';
5810 function getProgressHtml(stats) {
5811 return [stats.completed, ' / ', stats.defined, ' tests completed.<br />'].join('');
5813 QUnit.testStart(function (details) {
5815 appendTest(details.name, details.testId, details.module);
5816 running = id('qunit-testresult-display');
5818 addClass(running, 'running');
5819 bad = QUnit.config.reorder && details.previousFailure;
5820 running.innerHTML = [getProgressHtml(stats), bad ? 'Rerunning previously failed test: <br />' : 'Running: ', getNameHtml(details.name, details.module), getRerunFailedHtml(stats.failedTests)].join('');
5823 function stripHtml(string) {
5824 // Strip tags, html entity and whitespaces
5825 return string.replace(/<\/?[^>]+(>|$)/g, '').replace(/"/g, '').replace(/\s+/g, '');
5827 QUnit.log(function (details) {
5828 var testItem = id('qunit-test-output-' + details.testId);
5832 var message = escapeText(details.message) || (details.result ? 'okay' : 'failed');
5833 message = "<span class='test-message'>" + message + '</span>';
5834 message += "<span class='runtime'>@ " + details.runtime + ' ms</span>';
5838 var showDiff = false;
5840 // The pushFailure doesn't provide details.expected
5841 // when it calls, it's implicit to also not show expected and diff stuff
5842 // Also, we need to check details.expected existence, as it can exist and be undefined
5843 if (!details.result && hasOwn.call(details, 'expected')) {
5844 if (details.negative) {
5845 expected = 'NOT ' + QUnit.dump.parse(details.expected);
5847 expected = QUnit.dump.parse(details.expected);
5849 actual = QUnit.dump.parse(details.actual);
5850 message += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + escapeText(expected) + '</pre></td></tr>';
5851 if (actual !== expected) {
5852 message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + '</pre></td></tr>';
5853 if (typeof details.actual === 'number' && typeof details.expected === 'number') {
5854 if (!isNaN(details.actual) && !isNaN(details.expected)) {
5856 diff = details.actual - details.expected;
5857 diff = (diff > 0 ? '+' : '') + diff;
5859 } else if (typeof details.actual !== 'boolean' && typeof details.expected !== 'boolean') {
5860 diff = QUnit.diff(expected, actual);
5862 // don't show diff if there is zero overlap
5863 showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length;
5866 message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + '</pre></td></tr>';
5868 } else if (expected.indexOf('[object Array]') !== -1 || expected.indexOf('[object Object]') !== -1) {
5869 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({
5871 })) + "'>" + 'Rerun</a> without max depth.</p></td></tr>';
5873 message += "<tr class='test-message'><th>Message: </th><td>" + 'Diff suppressed as the expected and actual results have an equivalent' + ' serialization</td></tr>';
5875 if (details.source) {
5876 message += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + '</pre></td></tr>';
5878 message += '</table>';
5880 // This occurs when pushFailure is set and we have an extracted stack trace
5881 } else if (!details.result && details.source) {
5882 message += '<table>' + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(details.source) + '</pre></td></tr>' + '</table>';
5884 var assertList = testItem.getElementsByTagName('ol')[0];
5885 var assertLi = document.createElement('li');
5886 assertLi.className = details.result ? 'pass' : 'fail';
5887 assertLi.innerHTML = message;
5888 assertList.appendChild(assertLi);
5890 QUnit.testDone(function (details) {
5891 var tests = id('qunit-tests');
5892 var testItem = id('qunit-test-output-' + details.testId);
5893 if (!tests || !testItem) {
5896 removeClass(testItem, 'running');
5898 if (details.failed > 0) {
5900 } else if (details.todo) {
5903 status = details.skipped ? 'skipped' : 'passed';
5905 var assertList = testItem.getElementsByTagName('ol')[0];
5906 var good = details.passed;
5907 var bad = details.failed;
5909 // This test passed if it has no unexpected failed assertions
5910 var testPassed = details.failed > 0 ? details.todo : !details.todo;
5912 // Collapse the passing tests
5913 addClass(assertList, 'qunit-collapsed');
5915 stats.failedTests.push(details.testId);
5916 if (config.collapse) {
5917 if (!collapseNext) {
5918 // Skip collapsing the first failing test
5919 collapseNext = true;
5921 // Collapse remaining tests
5922 addClass(assertList, 'qunit-collapsed');
5927 // The testItem.firstChild is the test name
5928 var testTitle = testItem.firstChild;
5929 var testCounts = bad ? "<b class='failed'>" + bad + '</b>, ' + "<b class='passed'>" + good + '</b>, ' : '';
5930 testTitle.innerHTML += " <b class='counts'>(" + testCounts + details.assertions.length + ')</b>';
5932 if (details.skipped) {
5933 testItem.className = 'skipped';
5934 var skipped = document.createElement('em');
5935 skipped.className = 'qunit-skipped-label';
5936 skipped.innerHTML = 'skipped';
5937 testItem.insertBefore(skipped, testTitle);
5939 addEvent(testTitle, 'click', function () {
5940 toggleClass(assertList, 'qunit-collapsed');
5942 testItem.className = testPassed ? 'pass' : 'fail';
5944 var todoLabel = document.createElement('em');
5945 todoLabel.className = 'qunit-todo-label';
5946 todoLabel.innerHTML = 'todo';
5947 testItem.className += ' todo';
5948 testItem.insertBefore(todoLabel, testTitle);
5950 var time = document.createElement('span');
5951 time.className = 'runtime';
5952 time.innerHTML = details.runtime + ' ms';
5953 testItem.insertBefore(time, assertList);
5956 // Show the source of the test when showing assertions
5957 if (details.source) {
5958 var sourceName = document.createElement('p');
5959 sourceName.innerHTML = '<strong>Source: </strong>' + escapeText(details.source);
5960 addClass(sourceName, 'qunit-source');
5962 addClass(sourceName, 'qunit-collapsed');
5964 addEvent(testTitle, 'click', function () {
5965 toggleClass(sourceName, 'qunit-collapsed');
5967 testItem.appendChild(sourceName);
5969 if (config.hidepassed && (status === 'passed' || details.skipped)) {
5970 // use removeChild instead of remove because of support
5971 hiddenTests.push(testItem);
5972 tests.removeChild(testItem);
5975 QUnit.on('error', function (error) {
5976 var testItem = appendTest('global failure');
5978 // HTML Reporter is probably disabled or not yet initialized.
5982 // Render similar to a failed assertion (see above QUnit.log callback)
5983 var message = escapeText(errorString(error));
5984 message = "<span class='test-message'>" + message + '</span>';
5985 if (error && error.stack) {
5986 message += '<table>' + "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText(error.stack) + '</pre></td></tr>' + '</table>';
5988 var assertList = testItem.getElementsByTagName('ol')[0];
5989 var assertLi = document.createElement('li');
5990 assertLi.className = 'fail';
5991 assertLi.innerHTML = message;
5992 assertList.appendChild(assertLi);
5995 testItem.className = 'fail';
5998 // Avoid readyState issue with phantomjs
6000 var usingPhantom = function (p) {
6001 return p && p.version && p.version.major > 0;
6002 }(window$1.phantom);
6004 console$1.warn('Support for PhantomJS is deprecated and will be removed in QUnit 3.0.');
6006 if (!usingPhantom && document.readyState === 'complete') {
6009 addEvent(window$1, 'load', QUnit.load);
6012 // Wrap window.onerror. We will call the original window.onerror to see if
6013 // the existing handler fully handles the error; if not, we will call the
6014 // QUnit.onError function.
6015 var originalWindowOnError = window$1.onerror;
6017 // Cover uncaught exceptions
6018 // Returning true will suppress the default browser handler,
6019 // returning false will let it run.
6020 window$1.onerror = function (message, fileName, lineNumber, columnNumber, errorObj) {
6022 if (originalWindowOnError) {
6023 for (var _len = arguments.length, args = new Array(_len > 5 ? _len - 5 : 0), _key = 5; _key < _len; _key++) {
6024 args[_key - 5] = arguments[_key];
6026 ret = originalWindowOnError.call.apply(originalWindowOnError, [this, message, fileName, lineNumber, columnNumber, errorObj].concat(args));
6029 // Treat return value as window.onerror itself does,
6030 // Only do our handling if not suppressed.
6032 // If there is a current test that sets the internal `ignoreGlobalErrors` field
6033 // (such as during `assert.throws()`), then the error is ignored and native
6034 // error reporting is suppressed as well. This is because in browsers, an error
6035 // can sometimes end up in `window.onerror` instead of in the local try/catch.
6036 // This ignoring of errors does not apply to our general onUncaughtException
6037 // method, nor to our `unhandledRejection` handlers, as those are not meant
6038 // to receive an "expected" error during `assert.throws()`.
6039 if (config.current && config.current.ignoreGlobalErrors) {
6044 // https://blog.sentry.io/2016/01/04/client-javascript-reporting-window-onerror,
6045 // most modern browsers support an errorObj argument; use that to
6046 // get a full stack trace if it's available.
6047 var error = errorObj || new Error(message);
6048 if (!error.stack && fileName && lineNumber) {
6049 error.stack = "".concat(fileName, ":").concat(lineNumber);
6051 QUnit.onUncaughtException(error);
6055 window$1.addEventListener('unhandledrejection', function (event) {
6056 QUnit.onUncaughtException(event.reason);
6061 * This file is a modified version of google-diff-match-patch's JavaScript implementation
6062 * (https://code.google.com/p/google-diff-match-patch/source/browse/trunk/javascript/diff_match_patch_uncompressed.js),
6063 * modifications are licensed as more fully set forth in LICENSE.txt.
6065 * The original source of google-diff-match-patch is attributable and licensed as follows:
6067 * Copyright 2006 Google Inc.
6068 * https://code.google.com/p/google-diff-match-patch/
6070 * Licensed under the Apache License, Version 2.0 (the "License");
6071 * you may not use this file except in compliance with the License.
6072 * You may obtain a copy of the License at
6074 * https://www.apache.org/licenses/LICENSE-2.0
6076 * Unless required by applicable law or agreed to in writing, software
6077 * distributed under the License is distributed on an "AS IS" BASIS,
6078 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
6079 * See the License for the specific language governing permissions and
6080 * limitations under the License.
6083 * https://code.google.com/p/google-diff-match-patch/
6085 * Usage: QUnit.diff(expected, actual)
6088 QUnit.diff = function () {
6089 function DiffMatchPatch() {}
6094 * The data structure representing a diff is an array of tuples:
6095 * [[DIFF_DELETE, 'Hello'], [DIFF_INSERT, 'Goodbye'], [DIFF_EQUAL, ' world.']]
6096 * which means: delete 'Hello', add 'Goodbye' and keep ' world.'
6098 var DIFF_DELETE = -1;
6099 var DIFF_INSERT = 1;
6101 var hasOwn = Object.prototype.hasOwnProperty;
6104 * Find the differences between two texts. Simplifies the problem by stripping
6105 * any common prefix or suffix off the texts before diffing.
6106 * @param {string} text1 Old string to be diffed.
6107 * @param {string} text2 New string to be diffed.
6108 * @param {boolean=} optChecklines Optional speedup flag. If present and false,
6109 * then don't run a line-level diff first to identify the changed areas.
6110 * Defaults to true, which does a faster, slightly less optimal diff.
6111 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6113 DiffMatchPatch.prototype.DiffMain = function (text1, text2, optChecklines) {
6114 // The diff must be complete in up to 1 second.
6115 var deadline = Date.now() + 1000;
6117 // Check for null inputs.
6118 if (text1 === null || text2 === null) {
6119 throw new Error('Cannot diff null input.');
6122 // Check for equality (speedup).
6123 if (text1 === text2) {
6125 return [[DIFF_EQUAL, text1]];
6129 if (typeof optChecklines === 'undefined') {
6130 optChecklines = true;
6133 // Trim off common prefix (speedup).
6134 var commonlength = this.diffCommonPrefix(text1, text2);
6135 var commonprefix = text1.substring(0, commonlength);
6136 text1 = text1.substring(commonlength);
6137 text2 = text2.substring(commonlength);
6139 // Trim off common suffix (speedup).
6140 commonlength = this.diffCommonSuffix(text1, text2);
6141 var commonsuffix = text1.substring(text1.length - commonlength);
6142 text1 = text1.substring(0, text1.length - commonlength);
6143 text2 = text2.substring(0, text2.length - commonlength);
6145 // Compute the diff on the middle block.
6146 var diffs = this.diffCompute(text1, text2, optChecklines, deadline);
6148 // Restore the prefix and suffix.
6150 diffs.unshift([DIFF_EQUAL, commonprefix]);
6153 diffs.push([DIFF_EQUAL, commonsuffix]);
6155 this.diffCleanupMerge(diffs);
6160 * Reduce the number of edits by eliminating operationally trivial equalities.
6161 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6163 DiffMatchPatch.prototype.diffCleanupEfficiency = function (diffs) {
6164 var changes, equalities, equalitiesLength, lastequality, pointer, preIns, preDel, postIns, postDel;
6166 equalities = []; // Stack of indices where equalities are found.
6167 equalitiesLength = 0; // Keeping our own length var is faster in JS.
6168 /** @type {?string} */
6169 lastequality = null;
6171 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
6172 pointer = 0; // Index of current position.
6174 // Is there an insertion operation before the last equality.
6177 // Is there a deletion operation before the last equality.
6180 // Is there an insertion operation after the last equality.
6183 // Is there a deletion operation after the last equality.
6185 while (pointer < diffs.length) {
6187 if (diffs[pointer][0] === DIFF_EQUAL) {
6188 if (diffs[pointer][1].length < 4 && (postIns || postDel)) {
6190 equalities[equalitiesLength++] = pointer;
6193 lastequality = diffs[pointer][1];
6195 // Not a candidate, and can never become one.
6196 equalitiesLength = 0;
6197 lastequality = null;
6199 postIns = postDel = false;
6201 // An insertion or deletion.
6203 if (diffs[pointer][0] === DIFF_DELETE) {
6210 * Five types to be split:
6211 * <ins>A</ins><del>B</del>XY<ins>C</ins><del>D</del>
6212 * <ins>A</ins>X<ins>C</ins><del>D</del>
6213 * <ins>A</ins><del>B</del>X<ins>C</ins>
6214 * <ins>A</del>X<ins>C</ins><del>D</del>
6215 * <ins>A</ins><del>B</del>X<del>C</del>
6217 if (lastequality && (preIns && preDel && postIns && postDel || lastequality.length < 2 && preIns + preDel + postIns + postDel === 3)) {
6218 // Duplicate record.
6219 diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
6221 // Change second copy to insert.
6222 diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
6223 equalitiesLength--; // Throw away the equality we just deleted;
6224 lastequality = null;
6225 if (preIns && preDel) {
6226 // No changes made which could affect previous entry, keep going.
6227 postIns = postDel = true;
6228 equalitiesLength = 0;
6230 equalitiesLength--; // Throw away the previous equality.
6231 pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
6232 postIns = postDel = false;
6240 this.diffCleanupMerge(diffs);
6245 * Convert a diff array into a pretty HTML report.
6246 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6247 * @param {integer} string to be beautified.
6248 * @return {string} HTML representation.
6250 DiffMatchPatch.prototype.diffPrettyHtml = function (diffs) {
6252 for (var x = 0; x < diffs.length; x++) {
6253 var op = diffs[x][0]; // Operation (insert, delete, equal)
6254 var data = diffs[x][1]; // Text of change.
6257 html[x] = '<ins>' + escapeText(data) + '</ins>';
6260 html[x] = '<del>' + escapeText(data) + '</del>';
6263 html[x] = '<span>' + escapeText(data) + '</span>';
6267 return html.join('');
6271 * Determine the common prefix of two strings.
6272 * @param {string} text1 First string.
6273 * @param {string} text2 Second string.
6274 * @return {number} The number of characters common to the start of each
6277 DiffMatchPatch.prototype.diffCommonPrefix = function (text1, text2) {
6278 var pointermid, pointermax, pointermin, pointerstart;
6280 // Quick check for common null cases.
6281 if (!text1 || !text2 || text1.charAt(0) !== text2.charAt(0)) {
6286 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
6288 pointermax = Math.min(text1.length, text2.length);
6289 pointermid = pointermax;
6291 while (pointermin < pointermid) {
6292 if (text1.substring(pointerstart, pointermid) === text2.substring(pointerstart, pointermid)) {
6293 pointermin = pointermid;
6294 pointerstart = pointermin;
6296 pointermax = pointermid;
6298 pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
6304 * Determine the common suffix of two strings.
6305 * @param {string} text1 First string.
6306 * @param {string} text2 Second string.
6307 * @return {number} The number of characters common to the end of each string.
6309 DiffMatchPatch.prototype.diffCommonSuffix = function (text1, text2) {
6310 var pointermid, pointermax, pointermin, pointerend;
6312 // Quick check for common null cases.
6313 if (!text1 || !text2 || text1.charAt(text1.length - 1) !== text2.charAt(text2.length - 1)) {
6318 // Performance analysis: https://neil.fraser.name/news/2007/10/09/
6320 pointermax = Math.min(text1.length, text2.length);
6321 pointermid = pointermax;
6323 while (pointermin < pointermid) {
6324 if (text1.substring(text1.length - pointermid, text1.length - pointerend) === text2.substring(text2.length - pointermid, text2.length - pointerend)) {
6325 pointermin = pointermid;
6326 pointerend = pointermin;
6328 pointermax = pointermid;
6330 pointermid = Math.floor((pointermax - pointermin) / 2 + pointermin);
6336 * Find the differences between two texts. Assumes that the texts do not
6337 * have any common prefix or suffix.
6338 * @param {string} text1 Old string to be diffed.
6339 * @param {string} text2 New string to be diffed.
6340 * @param {boolean} checklines Speedup flag. If false, then don't run a
6341 * line-level diff first to identify the changed areas.
6342 * If true, then run a faster, slightly less optimal diff.
6343 * @param {number} deadline Time when the diff should be complete by.
6344 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6347 DiffMatchPatch.prototype.diffCompute = function (text1, text2, checklines, deadline) {
6348 var diffs, longtext, shorttext, i, hm, text1A, text2A, text1B, text2B, midCommon, diffsA, diffsB;
6350 // Just add some text (speedup).
6351 return [[DIFF_INSERT, text2]];
6354 // Just delete some text (speedup).
6355 return [[DIFF_DELETE, text1]];
6357 longtext = text1.length > text2.length ? text1 : text2;
6358 shorttext = text1.length > text2.length ? text2 : text1;
6359 i = longtext.indexOf(shorttext);
6361 // Shorter text is inside the longer text (speedup).
6362 diffs = [[DIFF_INSERT, longtext.substring(0, i)], [DIFF_EQUAL, shorttext], [DIFF_INSERT, longtext.substring(i + shorttext.length)]];
6364 // Swap insertions for deletions if diff is reversed.
6365 if (text1.length > text2.length) {
6366 diffs[0][0] = diffs[2][0] = DIFF_DELETE;
6370 if (shorttext.length === 1) {
6371 // Single character string.
6372 // After the previous speedup, the character can't be an equality.
6373 return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
6376 // Check to see if the problem can be split in two.
6377 hm = this.diffHalfMatch(text1, text2);
6379 // A half-match was found, sort out the return data.
6386 // Send both pairs off for separate processing.
6387 diffsA = this.DiffMain(text1A, text2A, checklines, deadline);
6388 diffsB = this.DiffMain(text1B, text2B, checklines, deadline);
6390 // Merge the results.
6391 return diffsA.concat([[DIFF_EQUAL, midCommon]], diffsB);
6393 if (checklines && text1.length > 100 && text2.length > 100) {
6394 return this.diffLineMode(text1, text2, deadline);
6396 return this.diffBisect(text1, text2, deadline);
6400 * Do the two texts share a substring which is at least half the length of the
6402 * This speedup can produce non-minimal diffs.
6403 * @param {string} text1 First string.
6404 * @param {string} text2 Second string.
6405 * @return {Array.<string>} Five element Array, containing the prefix of
6406 * text1, the suffix of text1, the prefix of text2, the suffix of
6407 * text2 and the common middle. Or null if there was no match.
6410 DiffMatchPatch.prototype.diffHalfMatch = function (text1, text2) {
6411 var longtext, shorttext, dmp, text1A, text2B, text2A, text1B, midCommon, hm1, hm2, hm;
6412 longtext = text1.length > text2.length ? text1 : text2;
6413 shorttext = text1.length > text2.length ? text2 : text1;
6414 if (longtext.length < 4 || shorttext.length * 2 < longtext.length) {
6415 return null; // Pointless.
6418 dmp = this; // 'this' becomes 'window' in a closure.
6421 * Does a substring of shorttext exist within longtext such that the substring
6422 * is at least half the length of longtext?
6423 * Closure, but does not reference any external variables.
6424 * @param {string} longtext Longer string.
6425 * @param {string} shorttext Shorter string.
6426 * @param {number} i Start index of quarter length substring within longtext.
6427 * @return {Array.<string>} Five element Array, containing the prefix of
6428 * longtext, the suffix of longtext, the prefix of shorttext, the suffix
6429 * of shorttext and the common middle. Or null if there was no match.
6432 function diffHalfMatchI(longtext, shorttext, i) {
6433 var seed, j, bestCommon, prefixLength, suffixLength, bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB;
6435 // Start with a 1/4 length substring at position i as a seed.
6436 seed = longtext.substring(i, i + Math.floor(longtext.length / 4));
6439 while ((j = shorttext.indexOf(seed, j + 1)) !== -1) {
6440 prefixLength = dmp.diffCommonPrefix(longtext.substring(i), shorttext.substring(j));
6441 suffixLength = dmp.diffCommonSuffix(longtext.substring(0, i), shorttext.substring(0, j));
6442 if (bestCommon.length < suffixLength + prefixLength) {
6443 bestCommon = shorttext.substring(j - suffixLength, j) + shorttext.substring(j, j + prefixLength);
6444 bestLongtextA = longtext.substring(0, i - suffixLength);
6445 bestLongtextB = longtext.substring(i + prefixLength);
6446 bestShorttextA = shorttext.substring(0, j - suffixLength);
6447 bestShorttextB = shorttext.substring(j + prefixLength);
6450 if (bestCommon.length * 2 >= longtext.length) {
6451 return [bestLongtextA, bestLongtextB, bestShorttextA, bestShorttextB, bestCommon];
6457 // First check if the second quarter is the seed for a half-match.
6458 hm1 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 4));
6460 // Check again based on the third quarter.
6461 hm2 = diffHalfMatchI(longtext, shorttext, Math.ceil(longtext.length / 2));
6469 // Both matched. Select the longest.
6470 hm = hm1[4].length > hm2[4].length ? hm1 : hm2;
6473 // A half-match was found, sort out the return data.
6474 if (text1.length > text2.length) {
6486 return [text1A, text1B, text2A, text2B, midCommon];
6490 * Do a quick line-level diff on both strings, then rediff the parts for
6492 * This speedup can produce non-minimal diffs.
6493 * @param {string} text1 Old string to be diffed.
6494 * @param {string} text2 New string to be diffed.
6495 * @param {number} deadline Time when the diff should be complete by.
6496 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6499 DiffMatchPatch.prototype.diffLineMode = function (text1, text2, deadline) {
6500 var a, diffs, linearray, pointer, countInsert, countDelete, textInsert, textDelete, j;
6502 // Scan the text on a line-by-line basis first.
6503 a = this.diffLinesToChars(text1, text2);
6506 linearray = a.lineArray;
6507 diffs = this.DiffMain(text1, text2, false, deadline);
6509 // Convert the diff back to original text.
6510 this.diffCharsToLines(diffs, linearray);
6512 // Eliminate freak matches (e.g. blank lines)
6513 this.diffCleanupSemantic(diffs);
6515 // Rediff any replacement blocks, this time character-by-character.
6516 // Add a dummy entry at the end.
6517 diffs.push([DIFF_EQUAL, '']);
6523 while (pointer < diffs.length) {
6524 switch (diffs[pointer][0]) {
6527 textInsert += diffs[pointer][1];
6531 textDelete += diffs[pointer][1];
6534 // Upon reaching an equality, check for prior redundancies.
6535 if (countDelete >= 1 && countInsert >= 1) {
6536 // Delete the offending records and add the merged ones.
6537 diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert);
6538 pointer = pointer - countDelete - countInsert;
6539 a = this.DiffMain(textDelete, textInsert, false, deadline);
6540 for (j = a.length - 1; j >= 0; j--) {
6541 diffs.splice(pointer, 0, a[j]);
6543 pointer = pointer + a.length;
6553 diffs.pop(); // Remove the dummy entry at the end.
6559 * Find the 'middle snake' of a diff, split the problem in two
6560 * and return the recursively constructed diff.
6561 * See Myers 1986 paper: An O(ND) Difference Algorithm and Its Variations.
6562 * @param {string} text1 Old string to be diffed.
6563 * @param {string} text2 New string to be diffed.
6564 * @param {number} deadline Time at which to bail if not yet complete.
6565 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6568 DiffMatchPatch.prototype.diffBisect = function (text1, text2, deadline) {
6569 var text1Length, text2Length, maxD, vOffset, vLength, v1, v2, x, delta, front, k1start, k1end, k2start, k2end, k2Offset, k1Offset, x1, x2, y1, y2, d, k1, k2;
6571 // Cache the text lengths to prevent multiple calls.
6572 text1Length = text1.length;
6573 text2Length = text2.length;
6574 maxD = Math.ceil((text1Length + text2Length) / 2);
6577 v1 = new Array(vLength);
6578 v2 = new Array(vLength);
6580 // Setting all elements to -1 is faster in Chrome & Firefox than mixing
6581 // integers and undefined.
6582 for (x = 0; x < vLength; x++) {
6586 v1[vOffset + 1] = 0;
6587 v2[vOffset + 1] = 0;
6588 delta = text1Length - text2Length;
6590 // If the total number of characters is odd, then the front path will collide
6591 // with the reverse path.
6592 front = delta % 2 !== 0;
6594 // Offsets for start and end of k loop.
6595 // Prevents mapping of space beyond the grid.
6600 for (d = 0; d < maxD; d++) {
6601 // Bail out if deadline is reached.
6602 if (Date.now() > deadline) {
6606 // Walk the front path one step.
6607 for (k1 = -d + k1start; k1 <= d - k1end; k1 += 2) {
6608 k1Offset = vOffset + k1;
6609 if (k1 === -d || k1 !== d && v1[k1Offset - 1] < v1[k1Offset + 1]) {
6610 x1 = v1[k1Offset + 1];
6612 x1 = v1[k1Offset - 1] + 1;
6615 while (x1 < text1Length && y1 < text2Length && text1.charAt(x1) === text2.charAt(y1)) {
6620 if (x1 > text1Length) {
6621 // Ran off the right of the graph.
6623 } else if (y1 > text2Length) {
6624 // Ran off the bottom of the graph.
6627 k2Offset = vOffset + delta - k1;
6628 if (k2Offset >= 0 && k2Offset < vLength && v2[k2Offset] !== -1) {
6629 // Mirror x2 onto top-left coordinate system.
6630 x2 = text1Length - v2[k2Offset];
6632 // Overlap detected.
6633 return this.diffBisectSplit(text1, text2, x1, y1, deadline);
6639 // Walk the reverse path one step.
6640 for (k2 = -d + k2start; k2 <= d - k2end; k2 += 2) {
6641 k2Offset = vOffset + k2;
6642 if (k2 === -d || k2 !== d && v2[k2Offset - 1] < v2[k2Offset + 1]) {
6643 x2 = v2[k2Offset + 1];
6645 x2 = v2[k2Offset - 1] + 1;
6648 while (x2 < text1Length && y2 < text2Length && text1.charAt(text1Length - x2 - 1) === text2.charAt(text2Length - y2 - 1)) {
6653 if (x2 > text1Length) {
6654 // Ran off the left of the graph.
6656 } else if (y2 > text2Length) {
6657 // Ran off the top of the graph.
6659 } else if (!front) {
6660 k1Offset = vOffset + delta - k2;
6661 if (k1Offset >= 0 && k1Offset < vLength && v1[k1Offset] !== -1) {
6663 y1 = vOffset + x1 - k1Offset;
6665 // Mirror x2 onto top-left coordinate system.
6666 x2 = text1Length - x2;
6668 // Overlap detected.
6669 return this.diffBisectSplit(text1, text2, x1, y1, deadline);
6676 // Diff took too long and hit the deadline or
6677 // number of diffs equals number of characters, no commonality at all.
6678 return [[DIFF_DELETE, text1], [DIFF_INSERT, text2]];
6682 * Given the location of the 'middle snake', split the diff in two parts
6684 * @param {string} text1 Old string to be diffed.
6685 * @param {string} text2 New string to be diffed.
6686 * @param {number} x Index of split point in text1.
6687 * @param {number} y Index of split point in text2.
6688 * @param {number} deadline Time at which to bail if not yet complete.
6689 * @return {!Array.<!DiffMatchPatch.Diff>} Array of diff tuples.
6692 DiffMatchPatch.prototype.diffBisectSplit = function (text1, text2, x, y, deadline) {
6693 var text1a, text1b, text2a, text2b, diffs, diffsb;
6694 text1a = text1.substring(0, x);
6695 text2a = text2.substring(0, y);
6696 text1b = text1.substring(x);
6697 text2b = text2.substring(y);
6699 // Compute both diffs serially.
6700 diffs = this.DiffMain(text1a, text2a, false, deadline);
6701 diffsb = this.DiffMain(text1b, text2b, false, deadline);
6702 return diffs.concat(diffsb);
6706 * Reduce the number of edits by eliminating semantically trivial equalities.
6707 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6709 DiffMatchPatch.prototype.diffCleanupSemantic = function (diffs) {
6710 var changes = false;
6711 var equalities = []; // Stack of indices where equalities are found.
6712 var equalitiesLength = 0; // Keeping our own length var is faster in JS.
6713 /** @type {?string} */
6714 var lastequality = null;
6716 // Always equal to diffs[equalities[equalitiesLength - 1]][1]
6717 var pointer = 0; // Index of current position.
6719 // Number of characters that changed prior to the equality.
6720 var lengthInsertions1 = 0;
6721 var lengthDeletions1 = 0;
6723 // Number of characters that changed after the equality.
6724 var lengthInsertions2 = 0;
6725 var lengthDeletions2 = 0;
6726 while (pointer < diffs.length) {
6727 if (diffs[pointer][0] === DIFF_EQUAL) {
6729 equalities[equalitiesLength++] = pointer;
6730 lengthInsertions1 = lengthInsertions2;
6731 lengthDeletions1 = lengthDeletions2;
6732 lengthInsertions2 = 0;
6733 lengthDeletions2 = 0;
6734 lastequality = diffs[pointer][1];
6736 // An insertion or deletion.
6737 if (diffs[pointer][0] === DIFF_INSERT) {
6738 lengthInsertions2 += diffs[pointer][1].length;
6740 lengthDeletions2 += diffs[pointer][1].length;
6743 // Eliminate an equality that is smaller or equal to the edits on both
6745 if (lastequality && lastequality.length <= Math.max(lengthInsertions1, lengthDeletions1) && lastequality.length <= Math.max(lengthInsertions2, lengthDeletions2)) {
6746 // Duplicate record.
6747 diffs.splice(equalities[equalitiesLength - 1], 0, [DIFF_DELETE, lastequality]);
6749 // Change second copy to insert.
6750 diffs[equalities[equalitiesLength - 1] + 1][0] = DIFF_INSERT;
6752 // Throw away the equality we just deleted.
6755 // Throw away the previous equality (it needs to be reevaluated).
6757 pointer = equalitiesLength > 0 ? equalities[equalitiesLength - 1] : -1;
6759 // Reset the counters.
6760 lengthInsertions1 = 0;
6761 lengthDeletions1 = 0;
6762 lengthInsertions2 = 0;
6763 lengthDeletions2 = 0;
6764 lastequality = null;
6771 // Normalize the diff.
6773 this.diffCleanupMerge(diffs);
6775 var deletion, insertion, overlapLength1, overlapLength2;
6777 // Find any overlaps between deletions and insertions.
6778 // e.g: <del>abcxxx</del><ins>xxxdef</ins>
6779 // -> <del>abc</del>xxx<ins>def</ins>
6780 // e.g: <del>xxxabc</del><ins>defxxx</ins>
6781 // -> <ins>def</ins>xxx<del>abc</del>
6782 // Only extract an overlap if it is as big as the edit ahead or behind it.
6784 while (pointer < diffs.length) {
6785 if (diffs[pointer - 1][0] === DIFF_DELETE && diffs[pointer][0] === DIFF_INSERT) {
6786 deletion = diffs[pointer - 1][1];
6787 insertion = diffs[pointer][1];
6788 overlapLength1 = this.diffCommonOverlap(deletion, insertion);
6789 overlapLength2 = this.diffCommonOverlap(insertion, deletion);
6790 if (overlapLength1 >= overlapLength2) {
6791 if (overlapLength1 >= deletion.length / 2 || overlapLength1 >= insertion.length / 2) {
6792 // Overlap found. Insert an equality and trim the surrounding edits.
6793 diffs.splice(pointer, 0, [DIFF_EQUAL, insertion.substring(0, overlapLength1)]);
6794 diffs[pointer - 1][1] = deletion.substring(0, deletion.length - overlapLength1);
6795 diffs[pointer + 1][1] = insertion.substring(overlapLength1);
6799 if (overlapLength2 >= deletion.length / 2 || overlapLength2 >= insertion.length / 2) {
6800 // Reverse overlap found.
6801 // Insert an equality and swap and trim the surrounding edits.
6802 diffs.splice(pointer, 0, [DIFF_EQUAL, deletion.substring(0, overlapLength2)]);
6803 diffs[pointer - 1][0] = DIFF_INSERT;
6804 diffs[pointer - 1][1] = insertion.substring(0, insertion.length - overlapLength2);
6805 diffs[pointer + 1][0] = DIFF_DELETE;
6806 diffs[pointer + 1][1] = deletion.substring(overlapLength2);
6817 * Determine if the suffix of one string is the prefix of another.
6818 * @param {string} text1 First string.
6819 * @param {string} text2 Second string.
6820 * @return {number} The number of characters common to the end of the first
6821 * string and the start of the second string.
6824 DiffMatchPatch.prototype.diffCommonOverlap = function (text1, text2) {
6825 // Cache the text lengths to prevent multiple calls.
6826 var text1Length = text1.length;
6827 var text2Length = text2.length;
6829 // Eliminate the null case.
6830 if (text1Length === 0 || text2Length === 0) {
6834 // Truncate the longer string.
6835 if (text1Length > text2Length) {
6836 text1 = text1.substring(text1Length - text2Length);
6837 } else if (text1Length < text2Length) {
6838 text2 = text2.substring(0, text1Length);
6840 var textLength = Math.min(text1Length, text2Length);
6842 // Quick check for the worst case.
6843 if (text1 === text2) {
6847 // Start by looking for a single character match
6848 // and increase length until no match is found.
6849 // Performance analysis: https://neil.fraser.name/news/2010/11/04/
6853 var pattern = text1.substring(textLength - length);
6854 var found = text2.indexOf(pattern);
6859 if (found === 0 || text1.substring(textLength - length) === text2.substring(0, length)) {
6867 * Split two texts into an array of strings. Reduce the texts to a string of
6868 * hashes where each Unicode character represents one line.
6869 * @param {string} text1 First string.
6870 * @param {string} text2 Second string.
6871 * @return {{chars1: string, chars2: string, lineArray: !Array.<string>}}
6872 * An object containing the encoded text1, the encoded text2 and
6873 * the array of unique strings.
6874 * The zeroth element of the array of unique strings is intentionally blank.
6877 DiffMatchPatch.prototype.diffLinesToChars = function (text1, text2) {
6878 var lineArray = []; // E.g. lineArray[4] === 'Hello\n'
6879 var lineHash = {}; // E.g. lineHash['Hello\n'] === 4
6881 // '\x00' is a valid character, but various debuggers don't like it.
6882 // So we'll insert a junk entry to avoid generating a null character.
6886 * Split a text into an array of strings. Reduce the texts to a string of
6887 * hashes where each Unicode character represents one line.
6888 * Modifies linearray and linehash through being a closure.
6889 * @param {string} text String to encode.
6890 * @return {string} Encoded string.
6893 function diffLinesToCharsMunge(text) {
6896 // Walk the text, pulling out a substring for each line.
6897 // text.split('\n') would would temporarily double our memory footprint.
6898 // Modifying text would create many large strings to garbage collect.
6902 // Keeping our own length variable is faster than looking it up.
6903 var lineArrayLength = lineArray.length;
6904 while (lineEnd < text.length - 1) {
6905 lineEnd = text.indexOf('\n', lineStart);
6906 if (lineEnd === -1) {
6907 lineEnd = text.length - 1;
6909 var line = text.substring(lineStart, lineEnd + 1);
6910 lineStart = lineEnd + 1;
6911 if (hasOwn.call(lineHash, line)) {
6912 chars += String.fromCharCode(lineHash[line]);
6914 chars += String.fromCharCode(lineArrayLength);
6915 lineHash[line] = lineArrayLength;
6916 lineArray[lineArrayLength++] = line;
6921 var chars1 = diffLinesToCharsMunge(text1);
6922 var chars2 = diffLinesToCharsMunge(text2);
6926 lineArray: lineArray
6931 * Rehydrate the text in a diff from a string of line hashes to real lines of
6933 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6934 * @param {!Array.<string>} lineArray Array of unique strings.
6937 DiffMatchPatch.prototype.diffCharsToLines = function (diffs, lineArray) {
6938 for (var x = 0; x < diffs.length; x++) {
6939 var chars = diffs[x][1];
6941 for (var y = 0; y < chars.length; y++) {
6942 text[y] = lineArray[chars.charCodeAt(y)];
6944 diffs[x][1] = text.join('');
6949 * Reorder and merge like edit sections. Merge equalities.
6950 * Any edit section can move as long as it doesn't cross an equality.
6951 * @param {!Array.<!DiffMatchPatch.Diff>} diffs Array of diff tuples.
6953 DiffMatchPatch.prototype.diffCleanupMerge = function (diffs) {
6954 diffs.push([DIFF_EQUAL, '']); // Add a dummy entry at the end.
6956 var countDelete = 0;
6957 var countInsert = 0;
6958 var textDelete = '';
6959 var textInsert = '';
6960 while (pointer < diffs.length) {
6961 switch (diffs[pointer][0]) {
6964 textInsert += diffs[pointer][1];
6969 textDelete += diffs[pointer][1];
6973 // Upon reaching an equality, check for prior redundancies.
6974 if (countDelete + countInsert > 1) {
6975 if (countDelete !== 0 && countInsert !== 0) {
6976 // Factor out any common prefixes.
6977 var commonlength = this.diffCommonPrefix(textInsert, textDelete);
6978 if (commonlength !== 0) {
6979 if (pointer - countDelete - countInsert > 0 && diffs[pointer - countDelete - countInsert - 1][0] === DIFF_EQUAL) {
6980 diffs[pointer - countDelete - countInsert - 1][1] += textInsert.substring(0, commonlength);
6982 diffs.splice(0, 0, [DIFF_EQUAL, textInsert.substring(0, commonlength)]);
6985 textInsert = textInsert.substring(commonlength);
6986 textDelete = textDelete.substring(commonlength);
6989 // Factor out any common suffixies.
6990 commonlength = this.diffCommonSuffix(textInsert, textDelete);
6991 if (commonlength !== 0) {
6992 diffs[pointer][1] = textInsert.substring(textInsert.length - commonlength) + diffs[pointer][1];
6993 textInsert = textInsert.substring(0, textInsert.length - commonlength);
6994 textDelete = textDelete.substring(0, textDelete.length - commonlength);
6998 // Delete the offending records and add the merged ones.
6999 if (countDelete === 0) {
7000 diffs.splice(pointer - countInsert, countDelete + countInsert, [DIFF_INSERT, textInsert]);
7001 } else if (countInsert === 0) {
7002 diffs.splice(pointer - countDelete, countDelete + countInsert, [DIFF_DELETE, textDelete]);
7004 diffs.splice(pointer - countDelete - countInsert, countDelete + countInsert, [DIFF_DELETE, textDelete], [DIFF_INSERT, textInsert]);
7006 pointer = pointer - countDelete - countInsert + (countDelete ? 1 : 0) + (countInsert ? 1 : 0) + 1;
7007 } else if (pointer !== 0 && diffs[pointer - 1][0] === DIFF_EQUAL) {
7008 // Merge this equality with the previous one.
7009 diffs[pointer - 1][1] += diffs[pointer][1];
7010 diffs.splice(pointer, 1);
7021 if (diffs[diffs.length - 1][1] === '') {
7022 diffs.pop(); // Remove the dummy entry at the end.
7025 // Second pass: look for single edits surrounded on both sides by equalities
7026 // which can be shifted sideways to eliminate an equality.
7027 // e.g: A<ins>BA</ins>C -> <ins>AB</ins>AC
7028 var changes = false;
7031 // Intentionally ignore the first and last element (don't need checking).
7032 while (pointer < diffs.length - 1) {
7033 if (diffs[pointer - 1][0] === DIFF_EQUAL && diffs[pointer + 1][0] === DIFF_EQUAL) {
7034 var diffPointer = diffs[pointer][1];
7035 var position = diffPointer.substring(diffPointer.length - diffs[pointer - 1][1].length);
7037 // This is a single edit surrounded by equalities.
7038 if (position === diffs[pointer - 1][1]) {
7039 // Shift the edit over the previous equality.
7040 diffs[pointer][1] = diffs[pointer - 1][1] + diffs[pointer][1].substring(0, diffs[pointer][1].length - diffs[pointer - 1][1].length);
7041 diffs[pointer + 1][1] = diffs[pointer - 1][1] + diffs[pointer + 1][1];
7042 diffs.splice(pointer - 1, 1);
7044 } else if (diffPointer.substring(0, diffs[pointer + 1][1].length) === diffs[pointer + 1][1]) {
7045 // Shift the edit over the next equality.
7046 diffs[pointer - 1][1] += diffs[pointer + 1][1];
7047 diffs[pointer][1] = diffs[pointer][1].substring(diffs[pointer + 1][1].length) + diffs[pointer + 1][1];
7048 diffs.splice(pointer + 1, 1);
7055 // If shifts were made, the diff needs reordering and another shift sweep.
7057 this.diffCleanupMerge(diffs);
7060 return function (o, n) {
7061 var diff, output, text;
7062 diff = new DiffMatchPatch();
7063 output = diff.DiffMain(o, n);
7064 diff.diffCleanupEfficiency(output);
7065 text = diff.diffPrettyHtml(output);