Edit base component:
-Edit base component:
Base components
--
+
- - - - + + + + - +
Add base component
Add base component
Edit builder:
+Builders
+Add builder
+Edit component:
-Edit component:
Components
--
+
- - - - + + + + - +
- - - - + + + + - +
Add component
Add component
Edit page:
-Edit page:
Edit page frame
-Edit page frame
Pages
--
+
Add page
Add page
App preview
-App preview
let replacements = { "componentPreview" : data, "actions" : editor.transformers.simplyPreviewActions.render(editor.pageData.app.actions ?? []), + "shortcuts" : editor.transformers.simplyPreviewShortcuts.render(editor.pageData.app.shortcuts ?? []), "commands" : editor.transformers.simplyPreviewCommands.render(editor.pageData.app.commands ?? []), "dataApi" : editor.transformers.simplyPreviewDataApi.render(editor.pageData.app.dataApi ?? []), "rawApi" : editor.transformers.simplyPreviewRawApi.render(editor.pageData.app.rawApi ?? []), @@ -3955,6 +5425,7 @@App preview
var fullApp = data.pageFrame.fullApp; let replacements = { "actions" : editor.transformers.simplyPreviewActions.render(data.actions ?? []), + "shortcuts" : editor.transformers.simplyPreviewShortcuts.render(data.shortcuts ?? []), "commands" : editor.transformers.simplyPreviewCommands.render(data.commands ?? []), "dataApi" : editor.transformers.simplyPreviewDataApi.render(data.dataApi ?? []), "rawApi" : editor.transformers.simplyPreviewRawApi.render(data.rawApi ?? []), @@ -4009,6 +5480,7 @@App preview
let replacements = { "pagePreview" : data, "actions" : editor.transformers.simplyPreviewActions.render(editor.pageData.app.actions ?? []), + "shortcuts" : editor.transformers.simplyPreviewShortcuts.render(editor.pageData.app.shortcuts ?? []), "commands" : editor.transformers.simplyPreviewCommands.render(editor.pageData.app.commands ?? []), "dataApi" : editor.transformers.simplyPreviewDataApi.render(editor.pageData.app.dataApi ?? []), "rawApi" : editor.transformers.simplyPreviewRawApi.render(editor.pageData.app.rawApi ?? []), @@ -4097,6 +5569,19 @@App preview
return this.originalData; } }, + "simplyPreviewShortcuts" : { + render : function(data) { + this.originalData = data; + methods = []; + data.forEach(function(method) { + methods.push('"' + method.shortcut + '" : ' + method.code); + }); + return methods.join(",\n"); + }, + extract : function(data) { + return this.originalData; + } + }, "simplyPreviewSorters" : { render : function(data) { this.originalData = data; @@ -4140,6 +5625,18 @@App preview
return this.simplyData; } }, + "builderLink" : { + render : function(data) { + this.simplyData = data; + return { + href : "#builders/" + data, + innerHTML: data + }; + }, + extract : function(data) { + return this.simplyData; + } + }, "componentLink" : { render : function(data) { this.simplyData = data; @@ -4228,6 +5725,12 @@App preview
var newname = event.data.arguments[1]; var oldname = event.data.arguments[2]; + if (typeof editor.pageData.app === "undefined") { + editor.pageData.app = {}; + } + if (typeof editor.pageData.app[component] === "undefined") { + editor.pageData.app[component] = []; + } editor.pageData.app[component].forEach(function(appPart) { if (appPart[componentNameField] == oldname) { appPart[componentNameField] = newname; @@ -4241,7 +5744,10 @@App preview
autoRunPreviewTimeout = window.setTimeout(function() { // Update the app data for the preview for (component in editor.pageData.component.parts) { - if (!editor.pageData.app[component]) { + if (typeof editor.pageData.app === "undefined") { + editor.pageData.app = {}; + } + if (typeof editor.pageData.app[component] === "undefined") { editor.pageData.app[component] = []; } var componentNameField = simplyApp.actions.getComponentNameField(component); diff --git a/simplycode/js/qunit/qunit.css b/simplycode/js/qunit/qunit.css new file mode 100644 index 0000000..d061f04 --- /dev/null +++ b/simplycode/js/qunit/qunit.css @@ -0,0 +1,525 @@ +/*! + * QUnit 2.24.1 + * https://qunitjs.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + */ + +/** Font Family and Sizes */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult { + font-family: "Helvetica Neue Light", "HelveticaNeue-Light", "Helvetica Neue", Calibri, Helvetica, Arial, sans-serif; +} + +#qunit-testrunner-toolbar, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-tests li { font-size: small; } +#qunit-tests { font-size: smaller; } + + +/** Resets */ + +#qunit-tests, #qunit-header, #qunit-banner, #qunit-filteredTest, #qunit-userAgent, #qunit-testresult, #qunit-modulefilter { + margin: 0; + padding: 0; +} + +/* Style our buttons in a simple way, uninfluenced by the styles + the tested app might load. Don't affect buttons in #qunit-fixture! + https://github.com/qunitjs/qunit/pull/1395 + https://github.com/qunitjs/qunit/issues/1437 */ +#qunit-testrunner-toolbar button, +#qunit-testresult button { + all: unset; /* best effort, modern browsers only */ + font: inherit; + color: initial; + border: initial; + background-color: buttonface; + padding: 0 4px; +} + + +/** Fixed headers with scrollable tests */ + +@supports (display: flex) or (display: -webkit-box) { + @media (min-height: 500px) { + #qunit { + position: fixed; + left: 0; + right: 0; + top: 0; + bottom: 0; + padding: 8px; + display: -webkit-box; + display: flex; + flex-direction: column; + } + + #qunit-tests { + overflow: scroll; + } + + #qunit-banner { + flex: 5px 0 0; + } + } +} + + +/** Header (excluding toolbar) */ + +#qunit-header { + padding: 0.5em 0 0.5em 1em; + + color: #C2CCD1; + background-color: #0D3349; + + font-size: 1.5em; + line-height: 1em; + font-weight: 400; + + border-radius: 5px 5px 0 0; +} + +#qunit-header a { + text-decoration: none; + color: inherit; +} + +#qunit-header a:hover, +#qunit-header a:focus { + color: #FFF; +} + +#qunit-banner { + height: 5px; +} + +#qunit-filteredTest { + padding: 0.5em 1em 0.5em 1em; + color: #366097; + background-color: #F4FF77; +} + +#qunit-userAgent { + padding: 0.5em 1em 0.5em 1em; + color: #FFF; + background-color: #2B81AF; + text-shadow: rgba(0, 0, 0, 0.5) 2px 2px 1px; +} + + +/** Toolbar */ + +#qunit-testrunner-toolbar { + padding: 0.5em 1em 0.5em 1em; + color: #5E740B; + background-color: #EEE; +} + +#qunit-testrunner-toolbar .clearfix { + height: 0; + clear: both; +} + +#qunit-testrunner-toolbar label { + display: inline-block; +} + +#qunit-testrunner-toolbar input[type=checkbox], +#qunit-testrunner-toolbar input[type=radio] { + margin: 3px; + vertical-align: -2px; +} + +#qunit-testrunner-toolbar input[type=text] { + box-sizing: border-box; + height: 1.6em; +} + +#qunit-testrunner-toolbar button, +#qunit-testresult button { + border-radius: .25em; + border: 1px solid #AAA; + background-color: #F8F8F8; + color: #222; + line-height: 1.6; + cursor: pointer; +} +#qunit-testrunner-toolbar button:hover, +#qunit-testresult button:hover { + border-color: #AAA; + background-color: #FFF; + color: #444; +} +#qunit-testrunner-toolbar button:active, +#qunit-testresult button:active { + border-color: #777; + background-color: #CCC; + color: #000; +} +#qunit-testrunner-toolbar button:focus, +#qunit-testresult button:focus { + border-color: #2F68DA; + /* emulate 2px border without a layout shift */ + box-shadow: inset 0 0 0 1px #2F68DA +} +#qunit-testrunner-toolbar button:disabled, +#qunit-testresult button:disabled { + border-color: #CCC; + background-color: #CCC; + color: #FFF; + cursor: default; +} + +#qunit-toolbar-filters { + float: right; + /* aligning right avoids overflows and inefficient use of space + around the dropdown menu on narrow viewports */ + text-align: right; +} + +.qunit-url-config, +.qunit-filter, +#qunit-modulefilter { + display: inline-block; + line-height: 2.1em; + text-align: left; +} + +.qunit-filter, +#qunit-modulefilter { + position: relative; + margin-left: 1em; +} + +.qunit-url-config label { + margin-right: 0.5em; +} + +#qunit-modulefilter-search { + box-sizing: border-box; + min-width: 400px; + min-width: min(400px, 80vw); +} + +#qunit-modulefilter-search-container { + position: relative; +} +#qunit-modulefilter-search-container:after { + position: absolute; + right: 0.3em; + bottom: 0; + line-height: 100%; + content: "\25bc"; + color: black; +} + +#qunit-modulefilter-dropdown { + /* align with #qunit-modulefilter-search */ + box-sizing: border-box; + min-width: 400px; + min-width: min(400px, 80vw); + max-width: 80vw; + position: absolute; + right: 0; + top: 100%; + margin-top: 2px; + + /* ensure that when on a narrow viewports and having only one result, + that #qunit-modulefilter-actions fall outside the dropdown rectangle. */ + min-height: 3em; + + border: 1px solid #AAA; + border-top-color: transparent; + border-radius: 0 0 .25em .25em; + color: #0D3349; + background-color: #F5F5F5; + z-index: 99; +} + +#qunit-modulefilter-actions { + display: block; + overflow: auto; + /* align with #qunit-modulefilter-dropdown-list */ + font: smaller/1.5em sans-serif; +} +@media (min-width: 350px) { + #qunit-modulefilter-actions { + position: absolute; + right: 0; + } +} + +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > * { + box-sizing: border-box; + max-height: 2.8em; + display: block; + padding: 0.4em; +} + +#qunit-modulefilter-dropdown #qunit-modulefilter-actions > button { + float: right; + margin: 0.25em; +} + +#qunit-modulefilter-dropdown-list { + margin: 0; + padding: 0; + font: smaller/1.5em sans-serif; +} + +#qunit-modulefilter-dropdown-list li { + list-style: none; +} +#qunit-modulefilter-dropdown-list .clickable { + display: block; + padding: 0.25em 0.50em 0.25em 0.15em; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +#qunit-modulefilter-dropdown-list .clickable.checked { + font-weight: bold; + background-color: #E2F0F7; + color: #0D3349; +} +#qunit-modulefilter-dropdown .clickable:hover { + background-color: #FFF; + color: #444; +} + + +/** Tests: Pass/Fail */ + +#qunit-tests { + list-style-position: inside; +} + +#qunit-tests li { + padding: 0.4em 1em 0.4em 1em; + border-bottom: 1px solid #FFF; + list-style-position: inside; +} + +#qunit-tests > li { + display: none; +} + +#qunit-tests li.running, +#qunit-tests li.pass, +#qunit-tests li.fail, +#qunit-tests li.skipped, +#qunit-tests li.aborted { + display: list-item; +} + +#qunit-tests.hidepass { + position: relative; +} + +#qunit-tests.hidepass li.running, +#qunit-tests.hidepass li.pass:not(.todo) { + visibility: hidden; + position: absolute; + width: 0; + height: 0; + padding: 0; + border: 0; + margin: 0; +} + +#qunit-tests li .qunit-test-name { + cursor: pointer; +} + +#qunit-tests li.skipped .qunit-test-name { + cursor: default; +} + +#qunit-tests li a { + padding: 0.5em; + color: inherit; + text-decoration: underline; +} +#qunit-tests li a:hover, +#qunit-tests li a:focus { + color: #0D3349; +} + +#qunit-tests li .runtime { + float: right; + font-size: smaller; +} + +.qunit-assert-list { + margin-top: 0.5em; + padding: 0.5em; + + background-color: #FFF; + + border-radius: 5px; +} + +.qunit-source { + margin: 0.6em 0 0.3em; +} + +.qunit-collapsed { + display: none; +} + +#qunit-tests table { + border-collapse: collapse; + margin-top: 0.2em; +} + +#qunit-tests th { + text-align: right; + vertical-align: top; + padding: 0 0.5em 0 0; +} + +#qunit-tests td { + vertical-align: top; +} + +#qunit-tests pre { + margin: 0; + white-space: pre-wrap; + word-wrap: break-word; +} + +#qunit-tests del { + color: #374E0C; + background-color: #E0F2BE; + text-decoration: none; +} + +#qunit-tests ins { + color: #500; + background-color: #FFCACA; + text-decoration: none; +} + +/*** Test Counts */ + +#qunit-tests b.counts { color: #0D3349; } +#qunit-tests b.passed { color: #5E740B; } +#qunit-tests b.failed { color: #710909; } + +#qunit-tests li li { + padding: 5px; + background-color: #FFF; + border-bottom: none; + list-style-position: inside; +} + +/*** Passing Styles */ + + +#qunit-tests .pass { + color: #2F68DA; + background-color: #E2F0F7; +} + +#qunit-tests .pass .test-name { + color: #366097; +} + +#qunit-tests li li.pass { + color: #3C510C; + background-color: #FFF; + border-left: 10px solid #C6E746; +} + +#qunit-tests .pass .test-actual, +#qunit-tests .pass .test-expected { color: #999; } + +#qunit-banner.qunit-pass { background-color: #C6E746; } + +/*** Failing Styles */ + +#qunit-tests .fail { + color: #000; + background-color: #EE5757; +} + +#qunit-tests li li.fail { + color: #710909; + background-color: #FFF; + border-left: 10px solid #EE5757; + white-space: pre; +} + +#qunit-tests > li:last-child { + border-radius: 0 0 5px 5px; +} + +#qunit-tests .fail .test-actual { color: #EE5757; } +#qunit-tests .fail .test-expected { color: #008000; } + +#qunit-banner.qunit-fail { background-color: #EE5757; } + + +/*** Aborted tests */ +#qunit-tests .aborted { color: #000; background-color: orange; } +/*** Skipped tests */ + +#qunit-tests .skipped { + background-color: #EBECE9; +} + +#qunit-tests .qunit-todo-label, +#qunit-tests .qunit-skipped-label { + background-color: #F4FF77; + display: inline-block; + font-style: normal; + color: #366097; + line-height: 1.8em; + padding: 0 0.5em; + margin: -0.4em 0.4em -0.4em 0; +} + +#qunit-tests .qunit-todo-label { + background-color: #EEE; +} + +/** Result */ + +#qunit-testresult { + color: #366097; + background-color: #E2F0F7; + + border-bottom: 1px solid #FFF; +} +#qunit-testresult a { + color: #2F68DA; +} +#qunit-testresult .clearfix { + height: 0; + clear: both; +} +#qunit-testresult .module-name { + font-weight: 700; +} +#qunit-testresult-display { + padding: 0.5em 1em 0.5em 1em; + width: 85%; + float:left; +} +#qunit-testresult-controls { + padding: 0.5em 1em 0.5em 1em; + width: 10%; + float:left; +} + +/** Fixture */ + +#qunit-fixture { + position: absolute; + top: -10000px; + left: -10000px; + width: 1000px; + height: 1000px; +} diff --git a/simplycode/js/qunit/qunit.js b/simplycode/js/qunit/qunit.js new file mode 100644 index 0000000..460e0e5 --- /dev/null +++ b/simplycode/js/qunit/qunit.js @@ -0,0 +1,7401 @@ +/*! + * QUnit 2.24.1 + * https://qunitjs.com/ + * + * Copyright OpenJS Foundation and other contributors + * Released under the MIT license + * https://jquery.org/license + */ +(function () { + 'use strict'; + + function _arrayLikeToArray(r, a) { + (null == a || a > r.length) && (a = r.length); + for (var e = 0, n = Array(a); e < a; e++) n[e] = r[e]; + return n; + } + function _arrayWithHoles(r) { + if (Array.isArray(r)) return r; + } + function _arrayWithoutHoles(r) { + if (Array.isArray(r)) return _arrayLikeToArray(r); + } + function _classCallCheck(a, n) { + if (!(a instanceof n)) throw new TypeError("Cannot call a class as a function"); + } + function _defineProperties(e, r) { + for (var t = 0; t < r.length; t++) { + var o = r[t]; + o.enumerable = o.enumerable || !1, o.configurable = !0, "value" in o && (o.writable = !0), Object.defineProperty(e, _toPropertyKey(o.key), o); + } + } + function _createClass(e, r, t) { + return r && _defineProperties(e.prototype, r), t && _defineProperties(e, t), Object.defineProperty(e, "prototype", { + writable: !1 + }), e; + } + function _iterableToArray(r) { + if ("undefined" != typeof Symbol && null != r[Symbol.iterator] || null != r["@@iterator"]) return Array.from(r); + } + function _iterableToArrayLimit(r, l) { + var t = null == r ? null : "undefined" != typeof Symbol && r[Symbol.iterator] || r["@@iterator"]; + if (null != t) { + var e, + n, + i, + u, + a = [], + f = !0, + o = !1; + try { + if (i = (t = t.call(r)).next, 0 === l) { + if (Object(t) !== t) return; + f = !1; + } else for (; !(f = (e = i.call(t)).done) && (a.push(e.value), a.length !== l); f = !0); + } catch (r) { + o = !0, n = r; + } finally { + try { + if (!f && null != t.return && (u = t.return(), Object(u) !== u)) return; + } finally { + if (o) throw n; + } + } + return a; + } + } + 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 _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 _slicedToArray(r, e) { + return _arrayWithHoles(r) || _iterableToArrayLimit(r, e) || _unsupportedIterableToArray(r, e) || _nonIterableRest(); + } + function _toConsumableArray(r) { + return _arrayWithoutHoles(r) || _iterableToArray(r) || _unsupportedIterableToArray(r) || _nonIterableSpread(); + } + function _toPrimitive(t, r) { + if ("object" != typeof t || !t) return t; + var e = t[Symbol.toPrimitive]; + if (void 0 !== e) { + var i = e.call(t, r || "default"); + if ("object" != typeof i) return i; + throw new TypeError("@@toPrimitive must return a primitive value."); + } + return ("string" === r ? String : Number)(t); + } + function _toPropertyKey(t) { + var i = _toPrimitive(t, "string"); + return "symbol" == typeof i ? i : i + ""; + } + function _typeof(o) { + "@babel/helpers - typeof"; + + return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (o) { + return typeof o; + } : function (o) { + return o && "function" == typeof Symbol && o.constructor === Symbol && o !== Symbol.prototype ? "symbol" : typeof o; + }, _typeof(o); + } + function _unsupportedIterableToArray(r, a) { + if (r) { + if ("string" == typeof r) return _arrayLikeToArray(r, a); + var t = {}.toString.call(r).slice(8, -1); + return "Object" === t && r.constructor && (t = r.constructor.name), "Map" === t || "Set" === t ? Array.from(r) : "Arguments" === t || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(t) ? _arrayLikeToArray(r, a) : void 0; + } + } + + // 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(); + + // These optional globals are undefined in one or more environments: + // modern browser, old browser, Node.js, SpiderMonkey. + // Calling code must check these for truthy-ness before use. + var console$1 = g.console; + var setTimeout$1 = g.setTimeout; + var clearTimeout = g.clearTimeout; + var process$1 = g.process; + var window$1 = g.window; + 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$1(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; + } + } + function escapeText(str) { + if (!str) { + return ''; + } + + // Both single quotes and double quotes (for attributes) + return ('' + str).replace(/['"<>&]/g, function (s) { + switch (s) { + case "'": + return '''; + case '"': + return '"'; + case '<': + return '<'; + case '>': + return '>'; + case '&': + return '&'; + } + }); + } + + 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é' : '\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); + } + } + return _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'; + } + } + } + }]); + }(); + + 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, scope) { + var modifiers = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; + if (typeof options === 'function') { + scope = 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 scope === 'function') { + moduleStack.push(module); + try { + var cbReturnValue = scope.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, scope) { + var ignored = focused$1 && !isParentModuleInQueue(); + processModule(name, options, scope, { + 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, scope) { + if (focused$1) { + return; + } + processModule(name, options, scope, { + skip: true + }); + }; + module$1.if = function (name, condition, options, scope) { + if (focused$1) { + return; + } + processModule(name, options, scope, { + skip: !condition + }); + }; + module$1.todo = function (name, options, scope) { + if (focused$1) { + return; + } + processModule(name, options, scope, { + todo: true + }); + }; + + // Stacktrace cleaner to focus on the path from error source to test suite. + // + // This should reduce a raw stack trace like this: + // + // > foo.broken()@/example/foo.js + // > Bar@/example/bar.js + // > @/test/bar.test.js + // > @/lib/qunit.js:500:12 + // > @/lib/qunit.js:100:28 + // > @/lib/qunit.js:200:56 + // > setTimeout@ + // > @/dist/vendor.js + // + // and shorten it to show up until the end of the user's bar.test.js code. + // + // > foo.broken()@/example/foo.js + // > Bar@/example/bar.js + // > @/test/bar.test.js + // + // QUnit will obtain one example trace (once per process/pageload suffices), + // strip off any :
" + escapeText(document.title) + '
' + "" + "" + appendFilteredTest() + "" + "", failedTests.length === 1 ? 'Rerun 1 failed test' : 'Rerun ' + failedTests.length + ' failed tests', ''].join(''); + } + QUnit.on('runEnd', function (runEnd) { + var banner = id('qunit-banner'); + var tests = id('qunit-tests'); + var abortButton = id('qunit-abort-tests-button'); + var assertPassed = config.stats.all - config.stats.bad; + 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.
', "", assertPassed, " assertions of ", config.stats.all, " passed, ", config.stats.bad, ' failed.', getRerunFailedHtml(stats.failedTests)].join(''); + var test; + var assertLi; + var assertList; + + // Update remaining tests to aborted + if (abortButton && abortButton.disabled) { + html = 'Tests aborted after ' + runEnd.runtime + ' milliseconds.'; + for (var i = 0; i < tests.children.length; i++) { + test = tests.children[i]; + if (test.className === '' || test.className === 'running') { + test.className = 'aborted'; + assertList = test.getElementsByTagName('ol')[0]; + assertLi = document.createElement('li'); + assertLi.className = 'fail'; + assertLi.innerHTML = 'Test aborted.'; + assertList.appendChild(assertLi); + } + } + } + if (banner && (!abortButton || abortButton.disabled === false)) { + banner.className = runEnd.status === 'failed' ? 'qunit-fail' : 'qunit-pass'; + } + if (abortButton) { + abortButton.parentNode.removeChild(abortButton); + } + if (tests) { + id('qunit-testresult-display').innerHTML = html; + } + if (config.altertitle && document.title) { + // Show ✖ for good, ✔ for bad suite result in title + // use escape sequences in case file gets loaded with non-utf-8 + // charset + document.title = [runEnd.status === 'failed' ? "\u2716" : "\u2714", document.title.replace(/^[\u2714\u2716] /i, '')].join(' '); + } + + // Scroll back to top to show results + if (config.scrolltop && window$1.scrollTo) { + window$1.scrollTo(0, 0); + } + }); + function getNameHtml(name, module) { + var nameHtml = ''; + if (module) { + nameHtml = "" + escapeText(module) + ': '; + } + nameHtml += "" + escapeText(name) + ''; + return nameHtml; + } + function getProgressHtml(stats) { + return [stats.completed, ' / ', stats.defined, ' tests completed.
'].join(''); + } + QUnit.testStart(function (details) { + var running, bad; + appendTest(details.name, details.testId, details.module); + running = id('qunit-testresult-display'); + if (running) { + addClass(running, 'running'); + bad = QUnit.config.reorder && details.previousFailure; + running.innerHTML = [getProgressHtml(stats), bad ? 'Rerunning previously failed test:
' : 'Running: ', getNameHtml(details.name, details.module), getRerunFailedHtml(stats.failedTests)].join(''); + } + }); + function stripHtml(string) { + // Strip tags, html entity and whitespaces + return string.replace(/<\/?[^>]+(>|$)/g, '').replace(/"/g, '').replace(/\s+/g, ''); + } + QUnit.log(function (details) { + var testItem = id('qunit-test-output-' + details.testId); + if (!testItem) { + return; + } + var message = escapeText(details.message) || (details.result ? 'okay' : 'failed'); + message = "'; + message += "@ " + details.runtime + ' ms'; + var expected; + var actual; + var diff; + var showDiff = false; + + // When pushFailure() is called, it is implied that no expected value + // or diff should be shown, because both expected and actual as undefined. + // + // This must check details.expected existence. If it exists as undefined, + // that's a regular assertion for which to render actual/expected and a diff. + var showAnyValues = !details.result && (details.expected !== undefined || details.actual !== undefined); + if (showAnyValues) { + if (details.negative) { + expected = 'NOT ' + QUnit.dump.parse(details.expected); + } else { + expected = QUnit.dump.parse(details.expected); + } + actual = QUnit.dump.parse(details.actual); + message += "
| Expected: | " + escapeText(expected) + ' |
|---|---|
| Result: | " + escapeText(actual) + ' |
| Diff: | " + diff + ' |
| Source: | " + escapeText(details.source) + ' |
| Source: | " + escapeText(details.source) + ' |
|---|
| Source: | " + escapeText(error.stack) + ' |
|---|