vexflow
Version:
A JavaScript library for rendering music notation and guitar tablature.
1,486 lines (1,429 loc) • 262 kB
JavaScript
/*!
* QUnit 2.20.0
* https://qunitjs.com/
*
* Copyright OpenJS Foundation and other contributors
* Released under the MIT license
* https://jquery.org/license
*/
(function () {
'use strict';
function _typeof(obj) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
}, _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, descriptor.key, descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", {
writable: false
});
return Constructor;
}
function _slicedToArray(arr, i) {
return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest();
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _arrayWithHoles(arr) {
if (Array.isArray(arr)) return arr;
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _iterableToArrayLimit(arr, i) {
var _i = arr == null ? null : typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"];
if (_i == null) return;
var _arr = [];
var _n = true;
var _d = false;
var _s, _e;
try {
for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) {
_arr.push(_s.value);
if (i && _arr.length === i) break;
}
} catch (err) {
_d = true;
_e = err;
} finally {
try {
if (!_n && _i["return"] != null) _i["return"]();
} finally {
if (_d) throw _e;
}
}
return _arr;
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _nonIterableRest() {
throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _createForOfIteratorHelper(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (!it) {
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
var F = function () {};
return {
s: F,
n: function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
},
e: function (e) {
throw e;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var normalCompletion = true,
didErr = false,
err;
return {
s: function () {
it = it.call(o);
},
n: function () {
var step = it.next();
normalCompletion = step.done;
return step;
},
e: function (e) {
didErr = true;
err = e;
},
f: function () {
try {
if (!normalCompletion && it.return != null) it.return();
} finally {
if (didErr) throw err;
}
}
};
}
// We don't use global-this-polyfill [1], because it modifies
// the globals scope by default. QUnit must not affect the host context
// as developers may test their project may be such a polyfill, and/or
// they may intentionally test their project with and without certain
// polyfills and we must not affect that. It also uses an obscure
// mechanism that seems to sometimes causes a runtime error in older
// browsers (specifically Safari and IE versions that support
// Object.defineProperty but then report _T_ as undefined).
// [1] https://github.com/ungap/global-this/blob/v0.4.4/esm/index.js
//
// Another way is `Function('return this')()`, but doing so relies
// on eval which will cause a CSP error on some servers.
//
// Instead, simply check the four options that already exist
// in all supported environments.
function getGlobalThis() {
if (typeof globalThis !== 'undefined') {
// For SpiderMonkey, modern browsers, and recent Node.js
// eslint-disable-next-line no-undef
return globalThis;
}
if (typeof self !== 'undefined') {
// For web workers
// eslint-disable-next-line no-undef
return self;
}
if (typeof window$1 !== 'undefined') {
// For document context in browsers
return window$1;
}
if (typeof global !== 'undefined') {
// For Node.js
// eslint-disable-next-line no-undef
return global;
}
throw new Error('Unable to locate global object');
}
// This avoids a simple `export const` assignment as that would lead Rollup
// to change getGlobalThis and use the same (generated) variable name there.
var g = getGlobalThis();
var window$1 = g.window;
var console$1 = g.console;
var setTimeout$1 = g.setTimeout;
var clearTimeout = g.clearTimeout;
var document = window$1 && window$1.document;
var navigator = window$1 && window$1.navigator;
var localSessionStorage = function () {
var x = 'qunit-test-string';
try {
g.sessionStorage.setItem(x, x);
g.sessionStorage.removeItem(x);
return g.sessionStorage;
} catch (e) {
return undefined;
}
}();
// Basic fallback for ES6 Map
// Support: IE 9-10, Safari 7, PhantomJS; Map is undefined
// Support: iOS 8; `new Map(iterable)` is not supported
//
// Fallback for ES7 Map#keys
// Support: IE 11; Map#keys is undefined
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) {
var _this = this;
var store = Object.create(null);
var hasOwn = Object.prototype.hasOwnProperty;
this.has = function (strKey) {
return hasOwn.call(store, strKey);
};
this.get = function (strKey) {
return store[strKey];
};
this.set = function (strKey, val) {
if (!hasOwn.call(store, strKey)) {
this.size++;
}
store[strKey] = val;
return this;
};
this.delete = function (strKey) {
if (hasOwn.call(store, strKey)) {
delete store[strKey];
this.size--;
}
};
this.forEach = function (callback) {
for (var strKey in store) {
callback(store[strKey], strKey);
}
};
this.keys = function () {
return Object.keys(store);
};
this.clear = function () {
store = Object.create(null);
this.size = 0;
};
this.size = 0;
if (input) {
input.forEach(function (val, strKey) {
_this.set(strKey, val);
});
}
};
// Basic fallback for ES6 Set
// Support: IE 11, `new Set(iterable)` parameter not yet implemented
// Test for Set#values() which came after Set(iterable).
var StringSet = typeof g.Set === 'function' && typeof g.Set.prototype.values === 'function' ? g.Set : function (input) {
var set = Object.create(null);
if (Array.isArray(input)) {
input.forEach(function (item) {
set[item] = true;
});
}
return {
add: function add(value) {
set[value] = true;
},
has: function has(value) {
return value in set;
},
get size() {
return Object.keys(set).length;
}
};
};
var toString = Object.prototype.toString;
var hasOwn$1 = Object.prototype.hasOwnProperty;
var performance = {
// eslint-disable-next-line compat/compat -- Checked
now: window$1 && window$1.performance && window$1.performance.now ? window$1.performance.now.bind(window$1.performance) : Date.now
};
// Returns a new Array with the elements that are in a but not in b
function diff(a, b) {
return a.filter(function (a) {
return b.indexOf(a) === -1;
});
}
/**
* Determines whether an element exists in a given array or not.
*
* @method inArray
* @param {any} elem
* @param {Array} array
* @return {boolean}
*/
var inArray = Array.prototype.includes ? function (elem, array) {
return array.includes(elem);
} : function (elem, array) {
return array.indexOf(elem) !== -1;
};
/**
* Recursively clone an object into a plain array or object, taking only the
* own enumerable properties.
*
* @param {any} obj
* @param {bool} [allowArray=true]
* @return {Object|Array}
*/
function objectValues(obj) {
var allowArray = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
var vals = allowArray && is('array', obj) ? [] : {};
for (var key in obj) {
if (hasOwn$1.call(obj, key)) {
var val = obj[key];
vals[key] = val === Object(val) ? objectValues(val, allowArray) : val;
}
}
return vals;
}
/**
* Recursively clone an object into a plain object, taking only the
* subset of own enumerable properties that exist a given model.
*
* @param {any} obj
* @param {any} model
* @return {Object}
*/
function objectValuesSubset(obj, model) {
// Return primitive values unchanged to avoid false positives or confusing
// results from assert.propContains().
// E.g. an actual null or false wrongly equaling an empty object,
// or an actual string being reported as object not matching a partial object.
if (obj !== Object(obj)) {
return obj;
}
// Unlike objectValues(), subset arrays to a plain objects as well.
// This enables subsetting [20, 30] with {1: 30}.
var subset = {};
for (var key in model) {
if (hasOwn$1.call(model, key) && hasOwn$1.call(obj, key)) {
subset[key] = objectValuesSubset(obj[key], model[key]);
}
}
return subset;
}
function extend(a, b, undefOnly) {
for (var prop in b) {
if (hasOwn$1.call(b, prop)) {
if (b[prop] === undefined) {
delete a[prop];
} else if (!(undefOnly && typeof a[prop] !== 'undefined')) {
a[prop] = b[prop];
}
}
}
return a;
}
function objectType(obj) {
if (typeof obj === 'undefined') {
return 'undefined';
}
// Consider: typeof null === object
if (obj === null) {
return 'null';
}
var match = toString.call(obj).match(/^\[object\s(.*)\]$/);
var type = match && match[1];
switch (type) {
case 'Number':
if (isNaN(obj)) {
return 'nan';
}
return 'number';
case 'String':
case 'Boolean':
case 'Array':
case 'Set':
case 'Map':
case 'Date':
case 'RegExp':
case 'Function':
case 'Symbol':
return type.toLowerCase();
default:
return _typeof(obj);
}
}
// Safe object type checking
function is(type, obj) {
return objectType(obj) === type;
}
// Based on Java's String.hashCode, a simple but not
// rigorously collision resistant hashing function
function generateHash(module, testName) {
var str = module + '\x1C' + testName;
var hash = 0;
for (var i = 0; i < str.length; i++) {
hash = (hash << 5) - hash + str.charCodeAt(i);
hash |= 0;
}
// Convert the possibly negative integer hash code into an 8 character hex string, which isn't
// strictly necessary but increases user understanding that the id is a SHA-like hash
var hex = (0x100000000 + hash).toString(16);
if (hex.length < 8) {
hex = '0000000' + hex;
}
return hex.slice(-8);
}
/**
* Converts an error into a simple string for comparisons.
*
* @param {Error|any} error
* @return {string}
*/
function errorString(error) {
// Use String() instead of toString() to handle non-object values like undefined or null.
var resultErrorString = String(error);
// If the error wasn't a subclass of Error but something like
// an object literal with name and message properties...
if (resultErrorString.slice(0, 7) === '[object') {
// Based on https://es5.github.io/#x15.11.4.4
return (error.name || 'Error') + (error.message ? ": ".concat(error.message) : '');
} else {
return resultErrorString;
}
}
var BOXABLE_TYPES = new StringSet(['boolean', 'number', 'string']);
// Memory for previously seen containers (object, array, map, set).
// Used for recursion detection, and to avoid repeated comparison.
//
// Elements are { a: val, b: val }.
var memory = [];
function useStrictEquality(a, b) {
return a === b;
}
function useObjectValueEquality(a, b) {
return a === b || a.valueOf() === b.valueOf();
}
function compareConstructors(a, b) {
// Comparing constructors is more strict than using `instanceof`
return getConstructor(a) === getConstructor(b);
}
function getConstructor(obj) {
var proto = Object.getPrototypeOf(obj);
// If the obj prototype descends from a null constructor, treat it
// as a null prototype.
// Ref https://github.com/qunitjs/qunit/issues/851
//
// Allow objects with no prototype, from Object.create(null), to be equivalent to
// plain objects that have Object as their constructor.
return !proto || proto.constructor === null ? Object : obj.constructor;
}
function getRegExpFlags(regexp) {
return 'flags' in regexp ? regexp.flags : regexp.toString().match(/[gimuy]*$/)[0];
}
// Specialised comparisons after entryTypeCallbacks.object, based on `objectType()`
var objTypeCallbacks = {
undefined: useStrictEquality,
null: useStrictEquality,
// Handle boxed boolean
boolean: useObjectValueEquality,
number: function number(a, b) {
// Handle NaN and boxed number
return a === b || a.valueOf() === b.valueOf() || isNaN(a.valueOf()) && isNaN(b.valueOf());
},
// Handle boxed string
string: useObjectValueEquality,
symbol: useStrictEquality,
date: useObjectValueEquality,
nan: function nan() {
return true;
},
regexp: function regexp(a, b) {
return a.source === b.source &&
// Include flags in the comparison
getRegExpFlags(a) === getRegExpFlags(b);
},
// identical reference only
function: useStrictEquality,
array: function array(a, b) {
if (a.length !== b.length) {
// Safe and faster
return false;
}
for (var i = 0; i < a.length; i++) {
if (!typeEquiv(a[i], b[i])) {
return false;
}
}
return true;
},
// Define sets a and b to be equivalent if for each element aVal in a, there
// is some element bVal in b such that aVal and bVal are equivalent. Element
// repetitions are not counted, so these are equivalent:
// a = new Set( [ X={}, Y=[], Y ] );
// b = new Set( [ Y, X, X ] );
set: function set(a, b) {
if (a.size !== b.size) {
// This optimization has certain quirks because of the lack of
// repetition counting. For instance, adding the same
// (reference-identical) element to two equivalent sets can
// make them non-equivalent.
return false;
}
var outerEq = true;
a.forEach(function (aVal) {
// Short-circuit if the result is already known. (Using for...of
// with a break clause would be cleaner here, but it would cause
// a syntax error on older JavaScript implementations even if
// Set is unused)
if (!outerEq) {
return;
}
var innerEq = false;
b.forEach(function (bVal) {
// Likewise, short-circuit if the result is already known
if (innerEq) {
return;
}
// Swap out the global memory, as nested typeEquiv() would clobber it
var originalMemory = memory;
memory = [];
if (typeEquiv(bVal, aVal)) {
innerEq = true;
}
// Restore
memory = originalMemory;
});
if (!innerEq) {
outerEq = false;
}
});
return outerEq;
},
// Define maps a and b to be equivalent if for each key-value pair (aKey, aVal)
// in a, there is some key-value pair (bKey, bVal) in b such that
// [ aKey, aVal ] and [ bKey, bVal ] are equivalent. Key repetitions are not
// counted, so these are equivalent:
// a = new Map( [ [ {}, 1 ], [ {}, 1 ], [ [], 1 ] ] );
// b = new Map( [ [ {}, 1 ], [ [], 1 ], [ [], 1 ] ] );
map: function map(a, b) {
if (a.size !== b.size) {
// This optimization has certain quirks because of the lack of
// repetition counting. For instance, adding the same
// (reference-identical) key-value pair to two equivalent maps
// can make them non-equivalent.
return false;
}
var outerEq = true;
a.forEach(function (aVal, aKey) {
// Short-circuit if the result is already known. (Using for...of
// with a break clause would be cleaner here, but it would cause
// a syntax error on older JavaScript implementations even if
// Map is unused)
if (!outerEq) {
return;
}
var innerEq = false;
b.forEach(function (bVal, bKey) {
// Likewise, short-circuit if the result is already known
if (innerEq) {
return;
}
// Swap out the global memory, as nested typeEquiv() would clobber it
var originalMemory = memory;
memory = [];
if (objTypeCallbacks.array([bVal, bKey], [aVal, aKey])) {
innerEq = true;
}
// Restore
memory = originalMemory;
});
if (!innerEq) {
outerEq = false;
}
});
return outerEq;
}
};
// Entry points from typeEquiv, based on `typeof`
var entryTypeCallbacks = {
undefined: useStrictEquality,
null: useStrictEquality,
boolean: useStrictEquality,
number: function number(a, b) {
// Handle NaN
return a === b || isNaN(a) && isNaN(b);
},
string: useStrictEquality,
symbol: useStrictEquality,
function: useStrictEquality,
object: function object(a, b) {
// Handle memory (skip recursion)
if (memory.some(function (pair) {
return pair.a === a && pair.b === b;
})) {
return true;
}
memory.push({
a: a,
b: b
});
var aObjType = objectType(a);
var bObjType = objectType(b);
if (aObjType !== 'object' || bObjType !== 'object') {
// Handle literal `null`
// Handle: Array, Map/Set, Date, Regxp/Function, boxed primitives
return aObjType === bObjType && objTypeCallbacks[aObjType](a, b);
}
// NOTE: Literal null must not make it here as it would throw
if (compareConstructors(a, b) === false) {
return false;
}
var aProperties = [];
var bProperties = [];
// Be strict and go deep, no filtering with hasOwnProperty.
for (var i in a) {
// Collect a's properties
aProperties.push(i);
// Skip OOP methods that look the same
if (a.constructor !== Object && typeof a.constructor !== 'undefined' && typeof a[i] === 'function' && typeof b[i] === 'function' && a[i].toString() === b[i].toString()) {
continue;
}
if (!typeEquiv(a[i], b[i])) {
return false;
}
}
for (var _i in b) {
// Collect b's properties
bProperties.push(_i);
}
return objTypeCallbacks.array(aProperties.sort(), bProperties.sort());
}
};
function typeEquiv(a, b) {
// Optimization: Only perform type-specific comparison when pairs are not strictly equal.
if (a === b) {
return true;
}
var aType = _typeof(a);
var bType = _typeof(b);
if (aType !== bType) {
// Support comparing primitive to boxed primitives
// Try again after possibly unwrapping one
return (aType === 'object' && BOXABLE_TYPES.has(objectType(a)) ? a.valueOf() : a) === (bType === 'object' && BOXABLE_TYPES.has(objectType(b)) ? b.valueOf() : b);
}
return entryTypeCallbacks[aType](a, b);
}
function innerEquiv(a, b) {
var res = typeEquiv(a, b);
// Release any retained objects and reset recursion detection for next call
memory = [];
return res;
}
/**
* Test any two types of JavaScript values for equality.
*
* @author Philippe Rathé <prathe@gmail.com>
* @author David Chan <david@troi.org>
*/
function equiv(a, b) {
if (arguments.length === 2) {
return a === b || innerEquiv(a, b);
}
// Given 0 or 1 arguments, just return true (nothing to compare).
// Given (A,B,C,D) compare C,D then B,C then A,B.
var i = arguments.length - 1;
while (i > 0) {
if (!innerEquiv(arguments[i - 1], arguments[i])) {
return false;
}
i--;
}
return true;
}
/**
* Config object: Maintain internal state
* Later exposed as QUnit.config
* `config` initialized at top of scope
*/
var config = {
// HTML Reporter: Modify document.title when suite is done
altertitle: true,
// HTML Reporter: collapse every test except the first failing test
// If false, all failing tests will be expanded
collapse: true,
// whether or not to fail when there are zero tests
// defaults to `true`
failOnZeroTests: true,
// Select by pattern or case-insensitive substring match against "moduleName: testName"
filter: undefined,
// Depth up-to which object will be dumped
maxDepth: 5,
// Select case-insensitive match of the module name
module: undefined,
// HTML Reporter: Select module/test by array of internal IDs
moduleId: undefined,
// By default, run previously failed tests first
// very useful in combination with "Hide passed tests" checked
reorder: true,
// When enabled, all tests must call expect()
requireExpects: false,
// By default, scroll to top of the page when suite is done
scrolltop: true,
// The storage module to use for reordering tests
storage: localSessionStorage,
testId: undefined,
// HTML Reporter: List of URL parameters that are given visual controls
urlConfig: [],
// Internal: The first unnamed module
//
// By being defined as the intial value for currentModule, it is the
// receptacle and implied parent for any global tests. It is as if we
// called `QUnit.module( "" );` before any other tests were defined.
//
// If we reach begin() and no tests were put in it, we dequeue it as if it
// never existed, and in that case never expose it to the events and
// callbacks API.
//
// When global tests are defined, then this unnamed module will execute
// as any other module, including moduleStart/moduleDone events etc.
//
// Since this module isn't explicitly created by the user, they have no
// access to add hooks for it. The hooks object is defined to comply
// with internal expectations of test.js, but they will be empty.
// To apply hooks, place tests explicitly in a QUnit.module(), and use
// its hooks accordingly.
//
// For global hooks that apply to all tests and all modules, use QUnit.hooks.
//
// NOTE: This is *not* a "global module". It is not an ancestor of all modules
// and tests. It is merely the parent for any tests defined globally,
// before the first QUnit.module(). As such, the events for this unnamed
// module will fire as normal, right after its last test, and *not* at
// the end of the test run.
//
// NOTE: This also should probably also not become a global module, unless
// we keep it out of the public API. For example, it would likely not
// improve the user interface and plugin behaviour if all modules became
// wrapped between the start and end events of this module, and thus
// needlessly add indentation, indirection, or other visible noise.
// Unit tests for the callbacks API would detect that as a regression.
currentModule: {
name: '',
tests: [],
childModules: [],
testsRun: 0,
testsIgnored: 0,
hooks: {
before: [],
beforeEach: [],
afterEach: [],
after: []
}
},
// Internal: Exposed to make resets easier
// Ref https://github.com/qunitjs/qunit/pull/1598
globalHooks: {},
// Internal state
blocking: true,
callbacks: {},
modules: [],
queue: [],
stats: {
all: 0,
bad: 0,
testCount: 0
}
};
// Apply a predefined QUnit.config object
//
// Ignore QUnit.config if it is a QUnit distribution instead of preconfig.
// That means QUnit was loaded twice! (Use the same approach as export.js)
var preConfig = g && g.QUnit && !g.QUnit.version && g.QUnit.config;
if (preConfig) {
extend(config, preConfig);
}
// Push a loose unnamed module to the modules collection
config.modules.push(config.currentModule);
var dump = (function () {
function quote(str) {
return '"' + str.toString().replace(/\\/g, '\\\\').replace(/"/g, '\\"') + '"';
}
function literal(o) {
return o + '';
}
function join(pre, arr, post) {
var s = dump.separator();
var inner = dump.indent(1);
if (arr.join) {
arr = arr.join(',' + s + inner);
}
if (!arr) {
return pre + post;
}
var base = dump.indent();
return [pre, inner + arr, base + post].join(s);
}
function array(arr, stack) {
if (dump.maxDepth && dump.depth > dump.maxDepth) {
return '[object Array]';
}
this.up();
var i = arr.length;
var ret = new Array(i);
while (i--) {
ret[i] = this.parse(arr[i], undefined, stack);
}
this.down();
return join('[', ret, ']');
}
function isArray(obj) {
return (
// Native Arrays
toString.call(obj) === '[object Array]' ||
// NodeList objects
typeof obj.length === 'number' && obj.item !== undefined && (obj.length ? obj.item(0) === obj[0] : obj.item(0) === null && obj[0] === undefined)
);
}
var reName = /^function (\w+)/;
var dump = {
// The objType is used mostly internally, you can fix a (custom) type in advance
parse: function parse(obj, objType, stack) {
stack = stack || [];
var objIndex = stack.indexOf(obj);
if (objIndex !== -1) {
return "recursion(".concat(objIndex - stack.length, ")");
}
objType = objType || this.typeOf(obj);
var parser = this.parsers[objType];
var parserType = _typeof(parser);
if (parserType === 'function') {
stack.push(obj);
var res = parser.call(this, obj, stack);
stack.pop();
return res;
}
if (parserType === 'string') {
return parser;
}
return '[ERROR: Missing QUnit.dump formatter for type ' + objType + ']';
},
typeOf: function typeOf(obj) {
var type;
if (obj === null) {
type = 'null';
} else if (typeof obj === 'undefined') {
type = 'undefined';
} else if (is('regexp', obj)) {
type = 'regexp';
} else if (is('date', obj)) {
type = 'date';
} else if (is('function', obj)) {
type = 'function';
} else if (obj.setInterval !== undefined && obj.document !== undefined && obj.nodeType === undefined) {
type = 'window';
} else if (obj.nodeType === 9) {
type = 'document';
} else if (obj.nodeType) {
type = 'node';
} else if (isArray(obj)) {
type = 'array';
} else if (obj.constructor === Error.prototype.constructor) {
type = 'error';
} else {
type = _typeof(obj);
}
return type;
},
separator: function separator() {
if (this.multiline) {
return this.HTML ? '<br />' : '\n';
} else {
return this.HTML ? ' ' : ' ';
}
},
// Extra can be a number, shortcut for increasing-calling-decreasing
indent: function indent(extra) {
if (!this.multiline) {
return '';
}
var chr = this.indentChar;
if (this.HTML) {
chr = chr.replace(/\t/g, ' ').replace(/ /g, ' ');
}
return new Array(this.depth + (extra || 0)).join(chr);
},
up: function up(a) {
this.depth += a || 1;
},
down: function down(a) {
this.depth -= a || 1;
},
setParser: function setParser(name, parser) {
this.parsers[name] = parser;
},
// The next 3 are exposed so you can use them
quote: quote,
literal: literal,
join: join,
depth: 1,
maxDepth: config.maxDepth,
// This is the list of parsers, to modify them, use dump.setParser
parsers: {
window: '[Window]',
document: '[Document]',
error: function error(_error) {
return 'Error("' + _error.message + '")';
},
// This has been unused since QUnit 1.0.0.
// @todo Deprecate and remove.
unknown: '[Unknown]',
null: 'null',
undefined: 'undefined',
function: function _function(fn) {
var ret = 'function';
// Functions never have name in IE
var name = 'name' in fn ? fn.name : (reName.exec(fn) || [])[1];
if (name) {
ret += ' ' + name;
}
ret += '(';
ret = [ret, dump.parse(fn, 'functionArgs'), '){'].join('');
return join(ret, dump.parse(fn, 'functionCode'), '}');
},
array: array,
nodelist: array,
arguments: array,
object: function object(map, stack) {
var ret = [];
if (dump.maxDepth && dump.depth > dump.maxDepth) {
return '[object Object]';
}
dump.up();
var keys = [];
for (var key in map) {
keys.push(key);
}
// Some properties are not always enumerable on Error objects.
var nonEnumerableProperties = ['message', 'name'];
for (var i in nonEnumerableProperties) {
var _key = nonEnumerableProperties[i];
if (_key in map && !inArray(_key, keys)) {
keys.push(_key);
}
}
keys.sort();
for (var _i = 0; _i < keys.length; _i++) {
var _key2 = keys[_i];
var val = map[_key2];
ret.push(dump.parse(_key2, 'key') + ': ' + dump.parse(val, undefined, stack));
}
dump.down();
return join('{', ret, '}');
},
node: function node(_node) {
var open = dump.HTML ? '<' : '<';
var close = dump.HTML ? '>' : '>';
var tag = _node.nodeName.toLowerCase();
var ret = open + tag;
var attrs = _node.attributes;
if (attrs) {
for (var i = 0; i < attrs.length; i++) {
var val = attrs[i].nodeValue;
// IE6 includes all attributes in .attributes, even ones not explicitly
// set. Those have values like undefined, null, 0, false, "" or
// "inherit".
if (val && val !== 'inherit') {
ret += ' ' + attrs[i].nodeName + '=' + dump.parse(val, 'attribute');
}
}
}
ret += close;
// Show content of TextNode or CDATASection
if (_node.nodeType === 3 || _node.nodeType === 4) {
ret += _node.nodeValue;
}
return ret + open + '/' + tag + close;
},
// Function calls it internally, it's the arguments part of the function
functionArgs: function functionArgs(fn) {
var l = fn.length;
if (!l) {
return '';
}
var args = new Array(l);
while (l--) {
// 97 is 'a'
args[l] = String.fromCharCode(97 + l);
}
return ' ' + args.join(', ') + ' ';
},
// Object calls it internally, the key part of an item in a map
key: quote,
// Function calls it internally, it's the content of the function
functionCode: '[code]',
// Node calls it internally, it's a html attribute value
attribute: quote,
string: quote,
date: quote,
regexp: literal,
number: literal,
boolean: literal,
symbol: function symbol(sym) {
return sym.toString();
}
},
// If true, entities are escaped ( <, >, \t, space and \n )
HTML: false,
// Indentation unit
indentChar: ' ',
// If true, items in a collection, are separated by a \n, else just a space.
multiline: true
};
return dump;
})();
// Support: IE 9
// Detect if the console object exists and no-op otherwise.
// This allows support for IE 9, which doesn't have a console
// object if the developer tools are not open.
// Support: IE 9
// Function#bind is supported, but no console.log.bind().
// Support: SpiderMonkey (mozjs 68+)
// The console object has a log method, but no warn method.
var Logger = {
warn: console$1 ? Function.prototype.bind.call(console$1.warn || console$1.log, console$1) : function () {}
};
var SuiteReport = /*#__PURE__*/function () {
function SuiteReport(name, parentSuite) {
_classCallCheck(this, SuiteReport);
this.name = name;
this.fullName = parentSuite ? parentSuite.fullName.concat(name) : [];
// When an "error" event is emitted from onUncaughtException(), the
// "runEnd" event should report the status as failed. The "runEnd" event data
// is tracked through this property (via the "runSuite" instance).
this.globalFailureCount = 0;
this.tests = [];
this.childSuites = [];
if (parentSuite) {
parentSuite.pushChildSuite(this);
}
}
_createClass(SuiteReport, [{
key: "start",
value: function start(recordTime) {
if (recordTime) {
this._startTime = performance.now();
}
return {
name: this.name,
fullName: this.fullName.slice(),
tests: this.tests.map(function (test) {
return test.start();
}),
childSuites: this.childSuites.map(function (suite) {
return suite.start();
}),
testCounts: {
total: this.getTestCounts().total
}
};
}
}, {
key: "end",
value: function end(recordTime) {
if (recordTime) {
this._endTime = performance.now();
}
return {
name: this.name,
fullName: this.fullName.slice(),
tests: this.tests.map(function (test) {
return test.end();
}),
childSuites: this.childSuites.map(function (suite) {
return suite.end();
}),
testCounts: this.getTestCounts(),
runtime: this.getRuntime(),
status: this.getStatus()
};
}
}, {
key: "pushChildSuite",
value: function pushChildSuite(suite) {
this.childSuites.push(suite);
}
}, {
key: "pushTest",
value: function pushTest(test) {
this.tests.push(test);
}
}, {
key: "getRuntime",
value: function getRuntime() {
return Math.round(this._endTime - this._startTime);
}
}, {
key: "getTestCounts",
value: function getTestCounts() {
var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
passed: 0,
failed: 0,
skipped: 0,
todo: 0,
total: 0
};
counts.failed += this.globalFailureCount;
counts.total += this.globalFailureCount;
counts = this.tests.reduce(function (counts, test) {
if (test.valid) {
counts[test.getStatus()]++;
counts.total++;
}
return counts;
}, counts);
return this.childSuites.reduce(function (counts, suite) {
return suite.getTestCounts(counts);
}, counts);
}
}, {
key: "getStatus",
value: function getStatus() {
var _this$getTestCounts = this.getTestCounts(),
total = _this$getTestCounts.total,
failed = _this$getTestCounts.failed,
skipped = _this$getTestCounts.skipped,
todo = _this$getTestCounts.todo;
if (failed) {
return 'failed';
} else {
if (skipped === total) {
return 'skipped';
} else if (todo === total) {
return 'todo';
} else {
return 'passed';
}
}
}
}]);
return SuiteReport;
}();
var moduleStack = [];
var runSuite = new SuiteReport();
function isParentModuleInQueue() {
var modulesInQueue = config.modules.filter(function (module) {
return !module.ignored;
}).map(function (module) {
return module.moduleId;
});
return moduleStack.some(function (module) {
return modulesInQueue.includes(module.moduleId);
});
}
function createModule(name, testEnvironment, modifiers) {
var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null;
var moduleName = parentModule !== null ? [parentModule.name, name].join(' > ') : name;
var parentSuite = parentModule ? parentModule.suiteReport : runSuite;
var skip = parentModule !== null && parentModule.skip || modifiers.skip;
var todo = parentModule !== null && parentModule.todo || modifiers.todo;
var env = {};
if (parentModule) {
extend(env, parentModule.testEnvironment);
}
extend(env, testEnvironment);
var module = {
name: moduleName,
parentModule: parentModule,
hooks: {
before: [],
beforeEach: [],
afterEach: [],
after: []
},
testEnvironment: env,
tests: [],
moduleId: generateHash(moduleName),
testsRun: 0,
testsIgnored: 0,
childModules: [],
suiteReport: new SuiteReport(name, parentSuite),
// Initialised by test.js when the module start executing,
// i.e. before the first test in this module (or a child).
stats: null,
// Pass along `skip` and `todo` properties from parent module, in case
// there is one, to childs. And use own otherwise.
// This property will be used to mark own tests and tests of child suites
// as either `skipped` or `todo`.
skip: skip,
todo: skip ? false : todo,
ignored: modifiers.ignored || false
};
if (parentModule) {
parentModule.childModules.push(module);
}
config.modules.push(module);
return module;
}
function setHookFromEnvironment(hooks, environment, name) {
var potentialHook = environment[name];
if (typeof potentialHook === 'function') {
hooks[name].push(potentialHook);
}
delete environment[name];
}
function makeSetHook(module, hookName) {
return function setHook(callback) {
if (config.currentModule !== module) {
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.');
}
module.hooks[hookName].push(callback);
};
}
function processModule(name, options, executeNow) {
var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {};
if (typeof options === 'function') {
executeNow = options;
options = undefined;
}
var module = createModule(name, options, modifiers);
// Transfer any initial hooks from the options object to the 'hooks' object
var testEnvironment = module.testEnvironment;
var hooks = module.hooks;
setHookFromEnvironment(hooks, testEnvironment, 'before');
setHookFromEnvironment(hooks, testEnvironment, 'beforeEach');
setHookFromEnvironment(hooks, testEnvironment, 'afterEach');
setHookFromEnvironment(hooks, testEnvironment, 'after');
var moduleFns = {
before: makeSetHook(module, 'before'),
beforeEach: makeSetHook(module, 'beforeEach'),
afterEach: makeSetHook(module, 'afterEach'),
after: makeSetHook(module, 'after')
};
var prevModule = config.currentModule;
config.currentModule = module;
if (typeof executeNow === 'function') {
moduleStack.push(module);
try {
var cbReturnValue = executeNow.call(module.testEnvironment, moduleFns);
if (cbReturnValue && typeof cbReturnValue.then === 'function') {
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.');
}
} finally {
// If the module closure threw an uncaught error during the load phase,
// we let this bubble up to global error handlers. But, not until after
// we teardown internal state to ensure correct module nesting.
// Ref https://github.com/qunitjs/qunit/issues/1478.
moduleStack.pop();
config.currentModule = module.parentModule || prevModule;
}
}
}
var focused$1 = false; // indicates that the "only" filter was used
function module$1(name, options, executeNow) {
var ignored = focused$1 && !isParentModuleInQueue();
processModule(name, options, executeNow, {
ignored: ignored
});
}
module$1.only = function () {
if (!focused$1) {
// Upon the first module.only() call,
// delete any and all previously registered modules and tests.
config.modules.length = 0;
config.queue.length = 0;
// Ignore any tests declared after this block within the same
// module parent. https://github.com/qunitjs/qunit/issues/1645
config.currentModule.ignored = true;
}
focused$1 = true;
processModule.apply(void 0, arguments);
};
module$1.skip = function (name, options, executeNow) {
if (focused$1) {
return;
}
processModule(name, options, executeNow, {
skip: true
});
};
module$1.todo = function (name, options, executeNow) {
if (focused$1) {
return;
}
processModule(name, options, executeNow, {
todo: true
});
};
// Doesn't support IE9, it will return undefined on these browsers
// See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
var fileName = (sourceFromStacktrace(0) || '').replace(/(:\d+)+\)?/, '')
// Remove anything prior to the last slash (Unix/Windows)
// from the last frame
.replace(/.+[/\\]/, '');
function extractStacktrace(e, offset) {
offset = offset === undefined ? 4 : offset;
if (e && e.stack) {
var stack = e.stack.split('\n');
if (/^error$/i.test(stack[0])) {
stack.shift();
}
if (fileName) {
var include = [];
for (var i = offset; i < stack.length; i++) {
if (stack[i].indexOf(fileName) !== -1) {
break;
}
include.push(stack[i]);
}
if (include.length) {
return include.join('\n');
}
}
return stack[offset];
}
}
function sourceFromStacktrace(offset) {
var error = new Error();
// Support: Safari <=7 only, IE <=10 - 11 only
// Not all browsers generate the `stack` property for `new Error()`, see also #636
if (!error.stack) {
try {
throw error;
} catch (err) {
error = err;
}
}
return extractStacktrace(error, offset);
}
var Assert = /*#__PURE__*/function () {
function Assert(testContext) {
_classCallCheck(this, Assert);
this.test = testContext;
}
_createClass(Assert, [{
key: "timeout",
value: function timeout(duration) {
if (typeof duration !== 'number') {
throw new Error('You must pass a number as the duration to assert.timeout');
}
this.test.timeout = duration;
// If a timeout has been set, clear it and reset with the new duration
if (config.timeout) {
clearTimeout(config.timeout);
config.timeout = null;
if (config.timeoutHandler && this.test.timeout > 0) {
this.test.internalResetTimeout(this.test.timeout);
}
}
}
// Documents a "step", which is a string value, in a test as a passing assertion
}, {
key: "step",
value: function step(message) {
var assertionMessage = message;
var result = !!message;
this.test.steps.push(message);
if (typeof message === 'undefined' || message === '') {
assertionMessage = 'You must provide a message to assert.step';
} else if (typeof message !== 'string') {
assertionMessage = 'You must provide a string value to assert.step';
result = false;
}
this.pushResult({
result: result,
message: assertionMessage
});
}
// Verifies the steps in a test match a given array of string values
}, {
key: "verifySteps",
value: function verifySteps(steps, message) {
// Since the steps array is just string values, we can clone with slice
var actualStepsClone = this.test.steps.slice();
this.deepEqual(actualStepsClone, steps, message);
this.test.steps.length = 0;
}
}, {
key: "expect",
value: function expect(asserts) {
if (arguments.length === 1) {
this.test.expected = asserts;
} else {
return this.test.expected;
}
}
// Create a new async pause and return a new function that can release the pause.
}, {
key: "async",
value: function async(count) {
if (count === undefined) {
count = 1;
} else if (typeof count !== 'number') {
throw new TypeError('async takes number as an input');
}
var requiredCalls = count;
return this.test.internalStop(requiredCalls);
}
// Exports test.push() to the user API
// Alias of pushResult.
}, {
key: "push",
value: function push(result, actual, expected, message, negative) {
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).');
var currentAssert = this instanceof Assert ? this : config.current.assert;
return currentAssert.pushResult({
result: result,
actual: actual,
expected: expected,
message: message,
negative: negative
});
}
}, {
key: "pushResult",
value: function pushResult(resultInfo) {
// Destructure of resultInfo = { result, actual, expected, message, negative }
var assert = this;
var currentTest = assert instanceof Assert && assert.test || config.current;
// Backwards compatibility fix.
// Allows the direct use of global exported assertions and QUnit.assert.*
// Although, it's use is not recommended as it can leak assertions
// to other tests from async tests, because we only get a reference to the current test,
// not exactly the test where assertion were intended to be called.
if (!currentTest) {
throw new Error('assertion outside test context, in ' + sourceFromStacktrace(2));
}
if (!(asse