diff --git a/js/tests/vendor/qunit.css b/js/tests/vendor/qunit.css index 75d8b6279f677548134364f7aa27a3a58817d0ae..7a469353341b4a610fe6ec41ae40b5461854305f 100644 --- a/js/tests/vendor/qunit.css +++ b/js/tests/vendor/qunit.css @@ -1,12 +1,12 @@ /*! - * QUnit 2.3.2 + * QUnit 2.3.3 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * - * Date: 2017-04-18T02:19Z + * Date: 2017-06-02T14:07Z */ /** Font Family and Sizes */ diff --git a/js/tests/vendor/qunit.js b/js/tests/vendor/qunit.js index fa728db5b61e421e05acf4e0e62989fc8ae534e2..3cda99631a9542dd813c6a0a22f61bcfccef4f52 100644 --- a/js/tests/vendor/qunit.js +++ b/js/tests/vendor/qunit.js @@ -1,12 +1,12 @@ /*! - * QUnit 2.3.2 + * QUnit 2.3.3 * https://qunitjs.com/ * * Copyright jQuery Foundation and other contributors * Released under the MIT license * https://jquery.org/license * - * Date: 2017-04-18T02:19Z + * Date: 2017-06-02T14:07Z */ (function (global$1) { 'use strict'; @@ -14,6 +14,7 @@ global$1 = 'default' in global$1 ? global$1['default'] : global$1; var window = global$1.window; + var self$1 = global$1.self; var console = global$1.console; var setTimeout = global$1.setTimeout; var clearTimeout = global$1.clearTimeout; @@ -238,6 +239,27 @@ 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); + } + // Test for equality any JavaScript type. // Authors: Philippe Rathé <prathe@gmail.com>, David Chan <david@troi.org> var equiv = (function () { @@ -608,21 +630,19 @@ // Set of all modules. modules: [], - // Stack of nested modules - moduleStack: [], - // The first unnamed module currentModule: { name: "", tests: [], childModules: [], - testsRun: 0 + testsRun: 0, + unskippedTestsRun: 0 }, callbacks: {}, // The storage module to use for reordering tests - storage: sessionStorage + storage: localSessionStorage }; // take a predefined QUnit.config and extend the defaults @@ -1064,6 +1084,135 @@ return extractStacktrace(error, offset); } + var priorityCount = 0; + var unitSampler = void 0; + + /** + * Advances the ProcessingQueue to the next item if it is ready. + * @param {Boolean} last + */ + function advance() { + var start = now(); + config.depth = (config.depth || 0) + 1; + + while (config.queue.length && !config.blocking) { + var elapsedTime = now() - start; + + if (!defined.setTimeout || config.updateRate <= 0 || elapsedTime < config.updateRate) { + if (priorityCount > 0) { + priorityCount--; + } + + config.queue.shift()(); + } else { + setTimeout(advance, 13); + break; + } + } + + config.depth--; + + if (!config.blocking && !config.queue.length && config.depth === 0) { + done(); + } + } + + function addToQueueImmediate(callback) { + if (objectType(callback) === "array") { + while (callback.length) { + addToQueueImmediate(callback.pop()); + } + + return; + } + + config.queue.unshift(callback); + priorityCount++; + } + + /** + * Adds a function to the ProcessingQueue for execution. + * @param {Function|Array} callback + * @param {Boolean} priority + * @param {String} seed + */ + function addToQueue(callback, prioritize, seed) { + if (prioritize) { + config.queue.splice(priorityCount++, 0, callback); + } else if (seed) { + if (!unitSampler) { + unitSampler = unitSamplerGenerator(seed); + } + + // Insert into a random position after all prioritized items + var index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1)); + config.queue.splice(priorityCount + index, 0, callback); + } else { + config.queue.push(callback); + } + } + + /** + * Creates a seeded "sample" generator which is used for randomizing tests. + */ + function unitSamplerGenerator(seed) { + + // 32-bit xorshift, requires only a nonzero seed + // http://excamera.com/sphinx/article-xorshift.html + var sample = parseInt(generateHash(seed), 16) || -1; + return function () { + sample ^= sample << 13; + sample ^= sample >>> 17; + sample ^= sample << 5; + + // ECMAScript has no unsigned number type + if (sample < 0) { + sample += 0x100000000; + } + + return sample / 0x100000000; + }; + } + + /** + * This function is called when the ProcessingQueue is done processing all + * items. It handles emitting the final run events. + */ + function done() { + var storage = config.storage; + + ProcessingQueue.finished = true; + + var runtime = now() - config.started; + var passed = config.stats.all - config.stats.bad; + + emit("runEnd", globalSuite.end(true)); + runLoggingCallbacks("done", { + passed: passed, + failed: config.stats.bad, + total: config.stats.all, + runtime: runtime + }); + + // Clear own storage items if all tests passed + if (storage && config.stats.bad === 0) { + for (var i = storage.length - 1; i >= 0; i--) { + var key = storage.key(i); + + if (key.indexOf("qunit-test-") === 0) { + storage.removeItem(key); + } + } + } + } + + var ProcessingQueue = { + finished: false, + add: addToQueue, + addImmediate: addToQueueImmediate, + advance: advance + }; + var TestReport = function () { function TestReport(name, suite, options) { classCallCheck(this, TestReport); @@ -1077,6 +1226,8 @@ this.skipped = !!options.skip; this.todo = !!options.todo; + this.valid = options.valid; + this._startTime = 0; this._endTime = 0; @@ -1149,13 +1300,24 @@ value: function getAssertions() { return this.assertions.slice(); } + + // Remove actual and expected values from assertions. This is to prevent + // leaking memory throughout a test suite. + + }, { + key: "slimAssertions", + value: function slimAssertions() { + this.assertions = this.assertions.map(function (assertion) { + delete assertion.actual; + delete assertion.expected; + return assertion; + }); + } }]); return TestReport; }(); - var unitSampler; var focused = false; - var priorityCount = 0; function Test(settings) { var i, l; @@ -1166,14 +1328,14 @@ extend(this, settings); this.assertions = []; this.semaphore = 0; - this.usedAsync = false; this.module = config.currentModule; this.stack = sourceFromStacktrace(3); this.steps = []; this.testReport = new TestReport(settings.testName, this.module.suiteReport, { todo: settings.todo, - skip: settings.skip + skip: settings.skip, + valid: this.valid() }); // Register unique strings @@ -1187,7 +1349,8 @@ this.module.tests.push({ name: this.testName, - testId: this.testId + testId: this.testId, + skip: !!settings.skip }); if (settings.skip) { @@ -1234,12 +1397,6 @@ config.current = this; - if (module.testEnvironment) { - delete module.testEnvironment.before; - delete module.testEnvironment.beforeEach; - delete module.testEnvironment.afterEach; - delete module.testEnvironment.after; - } this.testEnvironment = extend({}, module.testEnvironment); this.started = now(); @@ -1297,14 +1454,14 @@ test = this; return function runHook() { if (hookName === "before") { - if (hookOwner.testsRun !== 0) { + if (hookOwner.unskippedTestsRun !== 0) { return; } test.preserveEnvironment = true; } - if (hookName === "after" && hookOwner.testsRun !== numberOfTests(hookOwner) - 1) { + if (hookName === "after" && hookOwner.unskippedTestsRun !== numberOfUnskippedTests(hookOwner) - 1 && config.queue.length > 2) { return; } @@ -1334,8 +1491,8 @@ if (module.parentModule) { processHooks(test, module.parentModule); } - if (module.testEnvironment && objectType(module.testEnvironment[handler]) === "function") { - hooks.push(test.queueHook(module.testEnvironment[handler], handler, module)); + if (module.hooks && objectType(module.hooks[handler]) === "function") { + hooks.push(test.queueHook(module.hooks[handler], handler, module)); } } @@ -1378,7 +1535,7 @@ } } - notifyTestsRan(module); + notifyTestsRan(module, skipped); // Store result when possible if (storage) { @@ -1389,7 +1546,11 @@ } } + // After emitting the js-reporters event we cleanup the assertion data to + // avoid leaking it. It is not used by the legacy testDone callbacks. emit("testEnd", this.testReport.end(true)); + this.testReport.slimAssertions(); + runLoggingCallbacks("testDone", { name: testName, module: moduleName, @@ -1409,6 +1570,20 @@ }); if (module.testsRun === numberOfTests(module)) { + logSuiteEnd(module); + + // Check if the parent modules, iteratively, are done. If that the case, + // we emit the `suiteEnd` event and trigger `moduleDone` callback. + var parent = module.parentModule; + while (parent && parent.testsRun === numberOfTests(parent)) { + logSuiteEnd(parent); + parent = parent.parentModule; + } + } + + config.current = undefined; + + function logSuiteEnd(module) { emit("suiteEnd", module.suiteReport.end(true)); runLoggingCallbacks("moduleDone", { name: module.name, @@ -1419,8 +1594,6 @@ runtime: now() - module.stats.started }); } - - config.current = undefined; }, preserveTestEnvironment: function preserveTestEnvironment() { @@ -1431,18 +1604,16 @@ }, queue: function queue() { - var priority, - previousFailCount, - test = this; + var test = this; if (!this.valid()) { return; } - function run() { + function runTest() { // Each of these can by async - synchronize([function () { + ProcessingQueue.addImmediate([function () { test.before(); }, test.hooks("before"), function () { test.preserveTestEnvironment(); @@ -1455,17 +1626,26 @@ }]); } - previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName); + var previousFailCount = config.storage && +config.storage.getItem("qunit-test-" + this.module.name + "-" + this.testName); // Prioritize previously failed tests, detected from storage - priority = config.reorder && previousFailCount; + var prioritize = config.reorder && !!previousFailCount; this.previousFailure = !!previousFailCount; - return synchronize(run, priority, config.seed); + ProcessingQueue.add(runTest, prioritize, config.seed); + + // If the queue has already finished, we manually process the new test + if (ProcessingQueue.finished) { + ProcessingQueue.advance(); + } }, + pushResult: function pushResult(resultInfo) { + if (this !== config.current) { + throw new Error("Assertion occured after test had finished."); + } // Destructure of resultInfo = { result, actual, expected, message, negative } var source, @@ -1503,7 +1683,7 @@ throw new Error("pushFailure() assertion outside test context, was " + sourceFromStacktrace(2)); } - this.assert.pushResult({ + this.pushResult({ result: false, message: message || "error", actual: actual || null, @@ -1643,79 +1823,6 @@ return currentTest.pushFailure.apply(currentTest, arguments); } - // Based on Java's String.hashCode, a simple but not - // rigorously collision resistant hashing function - function generateHash(module, testName) { - var hex, - i = 0, - hash = 0, - str = module + "\x1C" + testName, - len = str.length; - - for (; i < len; 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 - hex = (0x100000000 + hash).toString(16); - if (hex.length < 8) { - hex = "0000000" + hex; - } - - return hex.slice(-8); - } - - function synchronize(callback, priority, seed) { - var last = !priority, - index; - - if (objectType(callback) === "array") { - while (callback.length) { - synchronize(callback.shift()); - } - return; - } - - if (priority) { - config.queue.splice(priorityCount++, 0, callback); - } else if (seed) { - if (!unitSampler) { - unitSampler = unitSamplerGenerator(seed); - } - - // Insert into a random position after all priority items - index = Math.floor(unitSampler() * (config.queue.length - priorityCount + 1)); - config.queue.splice(priorityCount + index, 0, callback); - } else { - config.queue.push(callback); - } - - if (internalState.autorun && !config.blocking) { - process(last); - } - } - - function unitSamplerGenerator(seed) { - - // 32-bit xorshift, requires only a nonzero seed - // http://excamera.com/sphinx/article-xorshift.html - var sample = parseInt(generateHash(seed), 16) || -1; - return function () { - sample ^= sample << 13; - sample ^= sample >>> 17; - sample ^= sample << 5; - - // ECMAScript has no unsigned number type - if (sample < 0) { - sample += 0x100000000; - } - - return sample / 0x100000000; - }; - } - function saveGlobal() { config.pollution = []; @@ -1888,24 +1995,40 @@ } } - function numberOfTests(module) { - var count = module.tests.length, - modules = [].concat(toConsumableArray(module.childModules)); + function collectTests(module) { + var tests = [].concat(module.tests); + var modules = [].concat(toConsumableArray(module.childModules)); // Do a breadth-first traversal of the child modules while (modules.length) { var nextModule = modules.shift(); - count += nextModule.tests.length; + tests.push.apply(tests, nextModule.tests); modules.push.apply(modules, toConsumableArray(nextModule.childModules)); } - return count; + return tests; + } + + function numberOfTests(module) { + return collectTests(module).length; + } + + function numberOfUnskippedTests(module) { + return collectTests(module).filter(function (test) { + return !test.skip; + }).length; } - function notifyTestsRan(module) { + function notifyTestsRan(module, skipped) { module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } while (module = module.parentModule) { module.testsRun++; + if (!skipped) { + module.unskippedTestsRun++; + } } } @@ -1978,18 +2101,22 @@ }, { key: "async", value: function async(count) { - var test$$1 = this.test, - popped = false, + var test$$1 = this.test; + + var popped = false, acceptCallCount = count; if (typeof acceptCallCount === "undefined") { acceptCallCount = 1; } - test$$1.usedAsync = true; var resume = internalStop(test$$1); return function done() { + if (config.current !== test$$1) { + throw Error("assert.async callback called after test finished."); + } + if (popped) { test$$1.pushFailure("Too many calls to the `assert.async` callback", sourceFromStacktrace(2)); return; @@ -2027,8 +2154,8 @@ value: function pushResult(resultInfo) { // Destructure of resultInfo = { result, actual, expected, message, negative } - var assert = this, - currentTest = assert instanceof Assert && assert.test || config.current; + 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.* @@ -2039,12 +2166,6 @@ throw new Error("assertion outside test context, in " + sourceFromStacktrace(2)); } - if (currentTest.usedAsync === true && currentTest.semaphore === 0) { - currentTest.pushFailure("Assertion after the final `assert.async` was resolved", sourceFromStacktrace(2)); - - // Allow this assertion to continue running anyway... - } - if (!(assert instanceof Assert)) { assert = currentTest.assert; } @@ -2181,8 +2302,9 @@ key: "throws", value: function throws(block, expected, message) { var actual = void 0, - result = false, - currentTest = this instanceof Assert && this.test || config.current; + result = false; + + var currentTest = this instanceof Assert && this.test || config.current; // 'expected' is optional unless doing string comparison if (objectType(expected) === "string") { @@ -2306,6 +2428,11 @@ }); QUnit.config.autostart = false; } + + // For Web/Service Workers + if (self$1 && self$1.WorkerGlobalScope && self$1 instanceof self$1.WorkerGlobalScope) { + self$1.QUnit = QUnit; + } } var SuiteReport = function () { @@ -2386,8 +2513,11 @@ var counts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : { passed: 0, failed: 0, skipped: 0, todo: 0, total: 0 }; counts = this.tests.reduce(function (counts, test) { - counts[test.getStatus()]++; - counts.total++; + if (test.valid) { + counts[test.getStatus()]++; + counts.total++; + } + return counts; }, counts); @@ -2451,27 +2581,49 @@ // it since each module has a suiteReport associated with it. config.currentModule.suiteReport = globalSuite; + var moduleStack = []; var globalStartCalled = false; var runStarted = false; - var internalState = { - autorun: false - }; - // Figure out if we're running the tests from a server or not QUnit.isLocal = !(defined.document && window.location.protocol !== "file:"); // Expose the current QUnit version - QUnit.version = "2.2.0"; + QUnit.version = "2.3.3"; + + function createModule(name, testEnvironment) { + var parentModule = moduleStack.length ? moduleStack.slice(-1)[0] : null; + var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name; + var parentSuite = parentModule ? parentModule.suiteReport : globalSuite; + + var module = { + name: moduleName, + parentModule: parentModule, + tests: [], + moduleId: generateHash(moduleName), + testsRun: 0, + unskippedTestsRun: 0, + childModules: [], + suiteReport: new SuiteReport(name, parentSuite) + }; + + var env = {}; + if (parentModule) { + parentModule.childModules.push(module); + extend(env, parentModule.testEnvironment); + } + extend(env, testEnvironment); + module.testEnvironment = env; + + config.modules.push(module); + return module; + } extend(QUnit, { on: on, // Call on start of module test to prepend name to all tests module: function module(name, testEnvironment, executeNow) { - var module, moduleFns; - var currentModule = config.currentModule; - if (arguments.length === 2) { if (objectType(testEnvironment) === "function") { executeNow = testEnvironment; @@ -2479,57 +2631,40 @@ } } - module = createModule(); + var module = createModule(name, testEnvironment); + + // Move any hooks to a 'hooks' object + if (module.testEnvironment) { + module.hooks = { + before: module.testEnvironment.before, + beforeEach: module.testEnvironment.beforeEach, + afterEach: module.testEnvironment.afterEach, + after: module.testEnvironment.after + }; + + delete module.testEnvironment.before; + delete module.testEnvironment.beforeEach; + delete module.testEnvironment.afterEach; + delete module.testEnvironment.after; + } - moduleFns = { + var moduleFns = { before: setHook(module, "before"), beforeEach: setHook(module, "beforeEach"), afterEach: setHook(module, "afterEach"), after: setHook(module, "after") }; + var currentModule = config.currentModule; if (objectType(executeNow) === "function") { - config.moduleStack.push(module); - setCurrentModule(module); + moduleStack.push(module); + config.currentModule = module; executeNow.call(module.testEnvironment, moduleFns); - config.moduleStack.pop(); + moduleStack.pop(); module = module.parentModule || currentModule; } - setCurrentModule(module); - - function createModule() { - var parentModule = config.moduleStack.length ? config.moduleStack.slice(-1)[0] : null; - var moduleName = parentModule !== null ? [parentModule.name, name].join(" > ") : name; - var parentSuite = parentModule ? parentModule.suiteReport : globalSuite; - - var module = { - name: moduleName, - parentModule: parentModule, - tests: [], - moduleId: generateHash(moduleName), - testsRun: 0, - childModules: [], - suiteReport: new SuiteReport(name, parentSuite) - }; - - var env = {}; - if (parentModule) { - parentModule.childModules.push(module); - extend(env, parentModule.testEnvironment); - delete env.beforeEach; - delete env.afterEach; - } - extend(env, testEnvironment); - module.testEnvironment = env; - - config.modules.push(module); - return module; - } - - function setCurrentModule(module) { - config.currentModule = module; - } + config.currentModule = module; }, test: test, @@ -2664,73 +2799,16 @@ } config.blocking = false; - process(true); - } - - function process(last) { - function next() { - process(last); - } - var start = now(); - config.depth = (config.depth || 0) + 1; - - while (config.queue.length && !config.blocking) { - if (!defined.setTimeout || config.updateRate <= 0 || now() - start < config.updateRate) { - if (config.current) { - - // Reset async tracking for each phase of the Test lifecycle - config.current.usedAsync = false; - } - config.queue.shift()(); - } else { - setTimeout(next, 13); - break; - } - } - config.depth--; - if (last && !config.blocking && !config.queue.length && config.depth === 0) { - done(); - } - } - - function done() { - var runtime, - passed, - i, - key, - storage = config.storage; - - internalState.autorun = true; - - runtime = now() - config.started; - passed = config.stats.all - config.stats.bad; - - emit("runEnd", globalSuite.end(true)); - runLoggingCallbacks("done", { - failed: config.stats.bad, - passed: passed, - total: config.stats.all, - runtime: runtime - }); - - // Clear own storage items if all tests passed - if (storage && config.stats.bad === 0) { - for (i = storage.length - 1; i >= 0; i--) { - key = storage.key(i); - if (key.indexOf("qunit-test-") === 0) { - storage.removeItem(key); - } - } - } + ProcessingQueue.advance(); } function setHook(module, hookName) { - if (module.testEnvironment === undefined) { - module.testEnvironment = {}; + if (!module.hooks) { + module.hooks = {}; } return function (callback) { - module.testEnvironment[hookName] = callback; + module.hooks[hookName] = callback; }; } @@ -3588,13 +3666,19 @@ message += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText(actual) + "</pre></td></tr>"; - // Don't show diff if actual or expected are booleans - if (!/^(true|false)$/.test(actual) && !/^(true|false)$/.test(expected)) { + if (typeof details.actual === "number" && typeof details.expected === "number") { + if (!isNaN(details.actual) && !isNaN(details.expected)) { + showDiff = true; + diff = details.actual - details.expected; + diff = (diff > 0 ? "+" : "") + diff; + } + } else if (typeof details.actual !== "boolean" && typeof details.expected !== "boolean") { diff = QUnit.diff(expected, actual); + + // don't show diff if there is zero overlap showDiff = stripHtml(diff).length !== stripHtml(expected).length + stripHtml(actual).length; } - // Don't show diff if expected and actual are totally different if (showDiff) { message += "<tr class='test-diff'><th>Diff: </th><td><pre>" + diff + "</pre></td></tr>"; } @@ -3691,6 +3775,7 @@ var todoLabel = document$$1.createElement("em"); todoLabel.className = "qunit-todo-label"; todoLabel.innerHTML = "todo"; + testItem.className += " todo"; testItem.insertBefore(todoLabel, testTitle); }