From: Michał Gołębiowski-Owczarek Date: Thu, 30 Mar 2023 07:56:33 +0000 (+0200) Subject: Build: Fork vendors to remove QUnit deprecated API usage X-Git-Tag: 1.13.3~30 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=546214e86956804a1b02da173a4c6c5ddea11454;p=jquery-ui.git Build: Fork vendors to remove QUnit deprecated API usage Changes: * add `tests/lib/vendor/**/*` to `.eslintignore` * move `qunit-composite` to `tests/lib` so that we can modify it * move `qunit-assert-classes` to `tests/lib` so that we can modify it * move `qunit-assert-close` to `tests/lib` so that we can modify it * replace `assert.push` with `assert.pushResult` * remove usage of `QUnit.extend` Closes gh-2157 --- diff --git a/.eslintignore b/.eslintignore index 8026d53f7..5e992599f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ dist/**/* external/**/* +tests/lib/vendor/**/* ui/vendor/**/* diff --git a/Gruntfile.js b/Gruntfile.js index 0471be52e..38d4fe199 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -272,18 +272,6 @@ grunt.initConfig( { "qunit/qunit.css": "qunit/qunit/qunit.css", "qunit/LICENSE.txt": "qunit/LICENSE.txt", - "qunit-assert-classes/qunit-assert-classes.js": - "qunit-assert-classes/qunit-assert-classes.js", - "qunit-assert-classes/LICENSE.txt": "qunit-assert-classes/LICENSE", - - "qunit-assert-close/qunit-assert-close.js": - "qunit-assert-close/qunit-assert-close.js", - "qunit-assert-close/MIT-LICENSE.txt": "qunit-assert-close/MIT-LICENSE.txt", - - "qunit-composite/qunit-composite.js": "qunit-composite/qunit-composite.js", - "qunit-composite/qunit-composite.css": "qunit-composite/qunit-composite.css", - "qunit-composite/LICENSE.txt": "qunit-composite/LICENSE.txt", - "requirejs/require.js": "requirejs/require.js", "jquery-mousewheel/jquery.mousewheel.js": "jquery-mousewheel/jquery.mousewheel.js", diff --git a/bower.json b/bower.json index d672cfb30..e239f0b17 100644 --- a/bower.json +++ b/bower.json @@ -16,9 +16,6 @@ "jquery-mousewheel": "3.1.12", "jquery-simulate": "1.1.1", "qunit": "2.19.4", - "qunit-assert-classes": "1.0.2", - "qunit-assert-close": "JamesMGreene/qunit-assert-close#v2.1.2", - "qunit-composite": "JamesMGreene/qunit-composite#v2.0.0", "requirejs": "2.1.14", "jquery-1.8.0": "jquery#1.8.0", diff --git a/external/qunit-assert-classes/LICENSE.txt b/external/qunit-assert-classes/LICENSE.txt deleted file mode 100644 index 938db0368..000000000 --- a/external/qunit-assert-classes/LICENSE.txt +++ /dev/null @@ -1,22 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2015 Alexander Schmitz - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. - diff --git a/external/qunit-assert-classes/qunit-assert-classes.js b/external/qunit-assert-classes/qunit-assert-classes.js deleted file mode 100644 index 93edebf9d..000000000 --- a/external/qunit-assert-classes/qunit-assert-classes.js +++ /dev/null @@ -1,169 +0,0 @@ -( function( factory ) { - if ( typeof define === "function" && define.amd ) { - - // AMD. Register as an anonymous module. - define( [ - "qunit" - ], factory ); - } else { - - // Browser globals - factory( QUnit ); - } -}( function( QUnit ) { - - function inArray( haystack, needle ) { - for ( var i = 0; i < haystack.length; i++ ) { - if ( - ( needle instanceof RegExp && needle.test( haystack[ i ] ) )|| - ( typeof needle === "string" && haystack[ i ] === needle ) - ) { - return true; - } - } - return false; - } - - function check( element, search ) { - var i, classAttribute, elementClassArray, - missing = [], - found = []; - - if ( element.jquery && element.length !== 1 ) { - throw new Error( "Class checks can only be performed on a single element on a collection" ); - } - - element = element.jquery ? element[ 0 ] : element; - classAttribute = element.getAttribute( "class" ); - - if ( classAttribute ) { - elementClassArray = splitClasses( classAttribute ); - if ( search instanceof RegExp ) { - if ( inArray( elementClassArray, search ) ) { - found.push( search ); - } else { - missing.push( search ); - } - } else { - for( i = 0; i < search.length; i++ ) { - if ( !inArray( elementClassArray, search[ i ] ) ) { - missing.push( search[ i ] ); - } else { - found.push( search[ i ] ); - } - } - } - } else { - missing = search; - } - - return { - missing: missing, - found: found, - element: element, - classAttribute: classAttribute - }; - } - - function splitClasses( classes ) { - return classes.match( /\S+/g ) || []; - } - - function pluralize( message, classes ) { - return message + ( classes.length > 1 ? "es" : "" ); - } - - QUnit.extend( QUnit.assert, { - hasClasses: function( element, classes, message ) { - var classArray = splitClasses( classes ), - results = check( element, classArray ); - - message = message || pluralize( "Element must have class", classArray ); - - this.push( !results.missing.length, results.found.join( " " ), classes, message ); - }, - lacksClasses: function( element, classes, message ) { - var classArray = splitClasses( classes ), - results = check( element, classArray ); - - message = message || pluralize( "Element must not have class", classArray ); - - this.push( !results.found.length, results.found.join( " " ), classes, message ); - }, - hasClassesStrict: function( element, classes, message ) { - var result, - classArray = splitClasses( classes ), - results = check( element, classArray ); - - message = message || pluralize( "Element must only have class", classArray ); - - result = !results.missing.length && results.element.getAttribute( "class" ) && - splitClasses( results.element.getAttribute( "class" ) ).length === - results.found.length; - - this.push( result, results.found.join( " " ), classes, message ); - }, - hasClassRegex: function( element, regex, message ) { - var results = check( element, regex ); - - message = message || "Element must have class matching " + regex; - - this.push( !!results.found.length, results.found.join( " " ), regex, message ); - }, - lacksClassRegex: function( element, regex, message ) { - var results = check( element, regex ); - - message = message || "Element must not have class matching " + regex; - - this.push( results.missing.length, results.missing.join( " " ), regex, message ); - }, - hasClassStart: function( element, partialClass, message ) { - var results = check( element, new RegExp( "^" + partialClass ) ); - - message = message || "Element must have class starting with " + partialClass; - - this.push( results.found.length, results.found.join( " " ), partialClass, message ); - }, - lacksClassStart: function( element, partialClass, message ) { - var results = check( element, new RegExp( "^" + partialClass ) ); - - message = message || "Element must not have class starting with " + partialClass; - - this.push( results.missing.length, results.missing.join( " " ), partialClass, message ); - }, - hasClassPartial: function( element, partialClass, message ) { - var results = check( element, new RegExp( partialClass ) ); - - message = message || "Element must have class containing '" + partialClass + "'"; - - this.push( results.found.length, results.found.join( " " ), partialClass, message ); - }, - lacksClassPartial: function( element, partialClass, message ) { - var results = check( element, new RegExp( partialClass ) ); - - message = message || "Element must not have class containing '" + partialClass + "'"; - - this.push( results.missing.length, results.missing.join( " " ), partialClass, message ); - }, - lacksAllClasses: function( element, message ) { - element = element.jquery ? element[ 0 ] : element; - - var classAttribute = element.getAttribute( "class" ) || "", - classes = splitClasses( classAttribute ); - - message = message || "Element must not have any classes"; - - this.push( !classes.length, !classes.length, true, message ); - }, - hasSomeClass: function( element, message ) { - element = element.jquery ? element[ 0 ] : element; - - var classAttribute = element.getAttribute( "class" ) || "", - classes = splitClasses( classAttribute ); - - message = message || "Element must have a class"; - - this.push( classes.length, classes.length, true, message ); - } - }); -} ) ); diff --git a/external/qunit-assert-close/MIT-LICENSE.txt b/external/qunit-assert-close/MIT-LICENSE.txt deleted file mode 100644 index aed5dc97e..000000000 --- a/external/qunit-assert-close/MIT-LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -Copyright jQuery Foundation and other contributors -http://jquery.com/ - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/external/qunit-assert-close/qunit-assert-close.js b/external/qunit-assert-close/qunit-assert-close.js deleted file mode 100644 index 2eee713a0..000000000 --- a/external/qunit-assert-close/qunit-assert-close.js +++ /dev/null @@ -1,182 +0,0 @@ -(function(factory) { - - // NOTE: - // All techniques except for the "browser globals" fallback will extend the - // provided QUnit object but return the isolated API methods - - // For AMD: Register as an anonymous AMD module with a named dependency on "qunit". - if (typeof define === "function" && define.amd) { - define(["qunit"], factory); - } - // For Node.js - else if (typeof module !== "undefined" && module && module.exports && typeof require === "function") { - module.exports = factory(require("qunitjs")); - } - // For CommonJS with `exports`, but without `module.exports`, like Rhino - else if (typeof exports !== "undefined" && exports && typeof require === "function") { - var qunit = require("qunitjs"); - qunit.extend(exports, factory(qunit)); - } - // For browser globals - else { - factory(QUnit); - } - -}(function(QUnit) { - - /** - * Find an appropriate `Assert` context to `push` results to. - * @param * context - An unknown context, possibly `Assert`, `Test`, or neither - * @private - */ - function _getPushContext(context) { - var pushContext; - - if (context && typeof context.push === "function") { - // `context` is an `Assert` context - pushContext = context; - } - else if (context && context.assert && typeof context.assert.push === "function") { - // `context` is a `Test` context - pushContext = context.assert; - } - else if ( - QUnit && QUnit.config && QUnit.config.current && QUnit.config.current.assert && - typeof QUnit.config.current.assert.push === "function" - ) { - // `context` is an unknown context but we can find the `Assert` context via QUnit - pushContext = QUnit.config.current.assert; - } - else if (QUnit && typeof QUnit.push === "function") { - pushContext = QUnit.push; - } - else { - throw new Error("Could not find the QUnit `Assert` context to push results"); - } - - return pushContext; - } - - /** - * Checks that the first two arguments are equal, or are numbers close enough to be considered equal - * based on a specified maximum allowable difference. - * - * @example assert.close(3.141, Math.PI, 0.001); - * - * @param Number actual - * @param Number expected - * @param Number maxDifference (the maximum inclusive difference allowed between the actual and expected numbers) - * @param String message (optional) - */ - function close(actual, expected, maxDifference, message) { - var actualDiff = (actual === expected) ? 0 : Math.abs(actual - expected), - result = actualDiff <= maxDifference, - pushContext = _getPushContext(this); - - message = message || (actual + " should be within " + maxDifference + " (inclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff)); - - pushContext.push(result, actual, expected, message); - } - - - /** - * Checks that the first two arguments are equal, or are numbers close enough to be considered equal - * based on a specified maximum allowable difference percentage. - * - * @example assert.close.percent(155, 150, 3.4); // Difference is ~3.33% - * - * @param Number actual - * @param Number expected - * @param Number maxPercentDifference (the maximum inclusive difference percentage allowed between the actual and expected numbers) - * @param String message (optional) - */ - close.percent = function closePercent(actual, expected, maxPercentDifference, message) { - var actualDiff, result, - pushContext = _getPushContext(this); - - if (actual === expected) { - actualDiff = 0; - result = actualDiff <= maxPercentDifference; - } - else if (actual !== 0 && expected !== 0 && expected !== Infinity && expected !== -Infinity) { - actualDiff = Math.abs(100 * (actual - expected) / expected); - result = actualDiff <= maxPercentDifference; - } - else { - // Dividing by zero (0)! Should return `false` unless the max percentage was `Infinity` - actualDiff = Infinity; - result = maxPercentDifference === Infinity; - } - message = message || (actual + " should be within " + maxPercentDifference + "% (inclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff + "%")); - - pushContext.push(result, actual, expected, message); - }; - - - /** - * Checks that the first two arguments are numbers with differences greater than the specified - * minimum difference. - * - * @example assert.notClose(3.1, Math.PI, 0.001); - * - * @param Number actual - * @param Number expected - * @param Number minDifference (the minimum exclusive difference allowed between the actual and expected numbers) - * @param String message (optional) - */ - function notClose(actual, expected, minDifference, message) { - var actualDiff = Math.abs(actual - expected), - result = actualDiff > minDifference, - pushContext = _getPushContext(this); - - message = message || (actual + " should not be within " + minDifference + " (exclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff)); - - pushContext.push(result, actual, expected, message); - } - - - /** - * Checks that the first two arguments are numbers with differences greater than the specified - * minimum difference percentage. - * - * @example assert.notClose.percent(156, 150, 3.5); // Difference is 4.0% - * - * @param Number actual - * @param Number expected - * @param Number minPercentDifference (the minimum exclusive difference percentage allowed between the actual and expected numbers) - * @param String message (optional) - */ - notClose.percent = function notClosePercent(actual, expected, minPercentDifference, message) { - var actualDiff, result, - pushContext = _getPushContext(this); - - if (actual === expected) { - actualDiff = 0; - result = actualDiff > minPercentDifference; - } - else if (actual !== 0 && expected !== 0 && expected !== Infinity && expected !== -Infinity) { - actualDiff = Math.abs(100 * (actual - expected) / expected); - result = actualDiff > minPercentDifference; - } - else { - // Dividing by zero (0)! Should only return `true` if the min percentage was `Infinity` - actualDiff = Infinity; - result = minPercentDifference !== Infinity; - } - message = message || (actual + " should not be within " + minPercentDifference + "% (exclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff + "%")); - - pushContext.push(result, actual, expected, message); - }; - - - var api = { - close: close, - notClose: notClose, - closePercent: close.percent, - notClosePercent: notClose.percent - }; - - QUnit.extend(QUnit.assert, api); - - return api; -})); diff --git a/external/qunit-composite/LICENSE.txt b/external/qunit-composite/LICENSE.txt deleted file mode 100644 index 155d8e869..000000000 --- a/external/qunit-composite/LICENSE.txt +++ /dev/null @@ -1,36 +0,0 @@ -Copyright jQuery Foundation and other contributors, https://jquery.org/ - -This software consists of voluntary contributions made by many -individuals. For exact contribution history, see the revision history -available at https://github.com/JamesMGreene/qunit-composite - -The following license applies to all parts of this software except as -documented below: - -==== - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -==== - -All files located in the node_modules directory are externally -maintained libraries used by this software which have their own -licenses; we recommend you read them, as their terms may differ from the -terms above. diff --git a/external/qunit-composite/qunit-composite.css b/external/qunit-composite/qunit-composite.css deleted file mode 100644 index a5d06ce77..000000000 --- a/external/qunit-composite/qunit-composite.css +++ /dev/null @@ -1,47 +0,0 @@ -.qunit-composite-suite { - position: fixed; - bottom: 0; - left: 0; - - margin: 0; - padding: 0; - border-width: 1px 0 0; - height: 45%; - width: 100%; - - background: #fff; -} - -#qunit-testsuites { - margin: 0; - padding: 0.5em 1.0em; - font-family: "Helvetica Neue Light","HelveticaNeue-Light","Helvetica Neue",Calibri,Helvetica,Arial,sans-serif; - font-size: small; - background-color: #d2e0e6; - border-bottom: 1px solid #fff; -} - -#qunit-testsuites a { - color: #00c; - text-decoration: none; -} - -#qunit-testsuites a:hover { - text-decoration: underline; -} - -#qunit-testsuites > li { - display: inline-block; -} - -#qunit-testsuites > li:first-child::before { - content: "Suites: "; -} - -#qunit-testsuites > li + li::before { - content: "|\a0"; -} - -#qunit-testsuites > li::after { - content: "\a0"; -} diff --git a/external/qunit-composite/qunit-composite.js b/external/qunit-composite/qunit-composite.js deleted file mode 100644 index 8e4fda45a..000000000 --- a/external/qunit-composite/qunit-composite.js +++ /dev/null @@ -1,228 +0,0 @@ -/** - * QUnit Composite - * - * https://github.com/JamesMGreene/qunit-composite - * - * Copyright jQuery Foundation and other contributors - * Released under the MIT license. - * https://jquery.org/license/ - */ -(function( factory ) { - if ( typeof define === "function" && define.amd ) { - define( [ "qunit" ], factory ); - } else { - factory( QUnit ); - } -}(function( QUnit ) { -var iframe, hasBound, resumeTests, suiteAssert, - modules = 1, - executingComposite = false; - -function hasClass( elem, name ) { - return ( " " + elem.className + " " ).indexOf( " " + name + " " ) > -1; -} - -function addClass( elem, name ) { - if ( !hasClass( elem, name ) ) { - elem.className += ( elem.className ? " " : "" ) + name; - } -} - -function addEvent( elem, type, fn ) { - if ( elem.addEventListener ) { - // Standards-based browsers - elem.addEventListener( type, fn, false ); - } else if ( elem.attachEvent ) { - // support: IE <9 - elem.attachEvent( "on" + type, fn ); - } -} - -function runSuite( suite ) { - var path; - - if ( QUnit.is( "object", suite ) ) { - path = suite.path; - suite = suite.name; - } else { - path = suite; - } - - QUnit.test( suite, function( assert ) { - resumeTests = assert.async(); - suiteAssert = assert; - iframe.setAttribute( "src", path ); - // QUnit.start is called from the child iframe's QUnit.done hook. - }); -} - -function initIframe() { - var iframeWin, - body = document.body; - - function onIframeLoad() { - var moduleName, testName, - count = 0; - - if ( !iframe.src ) { - return; - } - - // Deal with QUnit being loaded asynchronously via AMD - if ( !iframeWin.QUnit && iframeWin.define && iframeWin.define.amd ) { - return iframeWin.require( [ "qunit" ], onIframeLoad ); - } - - iframeWin.QUnit.moduleStart(function( data ) { - // Capture module name for messages - moduleName = data.name; - }); - - iframeWin.QUnit.testStart(function( data ) { - // Capture test name for messages - testName = data.name; - }); - iframeWin.QUnit.testDone(function() { - testName = undefined; - }); - - iframeWin.QUnit.log(function( data ) { - if (testName === undefined) { - return; - } - // Pass all test details through to the main page - var message = ( moduleName ? moduleName + ": " : "" ) + testName + ": " + ( data.message || ( data.result ? "okay" : "failed" ) ); - suiteAssert.expect( ++count ); - suiteAssert.push( data.result, data.actual, data.expected, message ); - }); - - // Continue the outer test when the iframe's test is done - iframeWin.QUnit.done(function() { - resumeTests(); - }); - } - - iframe = document.createElement( "iframe" ); - iframe.className = "qunit-composite-suite"; - body.appendChild( iframe ); - - addEvent( iframe, "load", onIframeLoad ); - - iframeWin = iframe.contentWindow; -} - -function appendSuitesToHeader( suites ) { - var i, suitesLen, suite, path, name, suitesEl, testResultEl, - newSuiteListItemEl, newSuiteLinkEl; - - suitesEl = document.getElementById("qunit-testsuites"); - - if (!suitesEl) { - testResultEl = document.getElementById("qunit-testresult"); - - if (!testResultEl) { - // QUnit has not been set up yet. Defer until QUnit is ready. - QUnit.begin(function () { - appendSuitesToHeader(suites); - }); - return; - } - - suitesEl = document.createElement("ul"); - suitesEl.id = "qunit-testsuites"; - testResultEl.parentNode.insertBefore(suitesEl, testResultEl); - } - - for (i = 0, suitesLen = suites.length; i < suitesLen; ++i) { - suite = suites[i]; - newSuiteLinkEl = document.createElement("a"); - newSuiteLinkEl.innerHTML = suite.name || suite; - newSuiteLinkEl.href = suite.path || suite; - - newSuiteListItemEl = document.createElement("li"); - newSuiteListItemEl.appendChild(newSuiteLinkEl); - - suitesEl.appendChild(newSuiteListItemEl); - } -} - -/** - * @param {string} [name] Module name to group these test suites. - * @param {Array} suites List of suites where each suite - * may either be a string (path to the html test page), - * or an object with a path and name property. - */ -QUnit.testSuites = function( name, suites ) { - var i, suitesLen; - - if ( arguments.length === 1 ) { - suites = name; - name = "Composition #" + modules++; - } - suitesLen = suites.length; - - appendSuitesToHeader(suites); - - if ( !hasBound ) { - hasBound = true; - QUnit.begin( initIframe ); - - // TODO: Would be better to use something like QUnit.once( 'moduleDone' ) - // after the last test suite. - QUnit.moduleDone( function () { - executingComposite = false; - } ); - - QUnit.done(function() { - iframe.style.display = "none"; - }); - } - - QUnit.module( name, { - beforeEach: function () { - executingComposite = true; - } - }); - - for ( i = 0; i < suitesLen; i++ ) { - runSuite( suites[ i ] ); - } -}; - -QUnit.testDone(function( data ) { - if ( !executingComposite ) { - return; - } - - var i, len, - testId = data.testId, - current = document.getElementById( "qunit-test-output-" + testId ), - children = current && current.children, - src = iframe.src; - - if (!(current && children)) { - return; - } - - addEvent( current, "dblclick", function( e ) { - var target = e && e.target ? e.target : window.event.srcElement; - if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { - target = target.parentNode; - } - if ( window.location && target.nodeName.toLowerCase() === "strong" ) { - window.location = src; - } - }); - - // Undo QUnit's auto-expansion for bad tests - for ( i = 0, len = children.length; i < len; i++ ) { - if ( children[ i ].nodeName.toLowerCase() === "ol" ) { - addClass( children[ i ], "qunit-collapsed" ); - } - } - - // Update Rerun link to point to the standalone test suite page - current.getElementsByTagName( "a" )[ 0 ].href = src; -}); - -})); diff --git a/tests/lib/bootstrap.js b/tests/lib/bootstrap.js index 277fecca7..a5bb1711b 100644 --- a/tests/lib/bootstrap.js +++ b/tests/lib/bootstrap.js @@ -12,8 +12,8 @@ requirejs.config( { "jquery-simulate": "../../../external/jquery-simulate/jquery.simulate", "lib": "../../lib", "phantom-bridge": "../../../node_modules/grunt-contrib-qunit/phantomjs/bridge", - "qunit-assert-classes": "../../../external/qunit-assert-classes/qunit-assert-classes", - "qunit-assert-close": "../../../external/qunit-assert-close/qunit-assert-close", + "qunit-assert-classes": "../../lib/vendor/qunit-assert-classes/qunit-assert-classes", + "qunit-assert-close": "../../lib/vendor/qunit-assert-close/qunit-assert-close", "qunit": "../../../external/qunit/qunit", "testswarm": "https://swarm.jquery.org/js/inject.js?" + ( new Date() ).getTime(), "ui": "../../../ui" diff --git a/tests/lib/qunit-assert-domequal.js b/tests/lib/qunit-assert-domequal.js index bdad9844c..066eb203b 100644 --- a/tests/lib/qunit-assert-domequal.js +++ b/tests/lib/qunit-assert-domequal.js @@ -15,11 +15,16 @@ var domEqual = QUnit.assert.domEqual = function( selector, modifier, message ) { var assert = this; // Get current state prior to modifier - var expected = extract( selector, message ); + var expected = extract( assert, selector, message ); function done() { - var actual = extract( selector, message ); - assert.push( QUnit.equiv( actual, expected ), actual, expected, message ); + var actual = extract( assert, selector, message ); + assert.pushResult( { + result: QUnit.equiv( actual, expected ), + actual: actual, + expected: expected, + message: message + } ); } // Run modifier (async or sync), then compare state via done() @@ -116,11 +121,15 @@ function jQueryVersionSince( version ) { return compareVersions( $.fn.jquery, version ) >= 0; } -function extract( selector, message ) { +function extract( assert, selector, message ) { var elem = $( selector ); if ( !elem.length ) { - QUnit.push( false, null, null, - "domEqual failed, can't extract " + selector + ", message was: " + message ); + assert.pushResult( { + result: false, + actual: null, + expected: null, + message: "domEqual failed, can't extract " + selector + ", message was: " + message + } ); return; } @@ -190,7 +199,7 @@ function extract( selector, message ) { children = elem.children(); if ( children.length ) { result.children = elem.children().map( function() { - return extract( $( this ) ); + return extract( assert, $( this ) ); } ).get(); } else { result.text = elem.text(); diff --git a/tests/lib/vendor/qunit-assert-classes/LICENSE.txt b/tests/lib/vendor/qunit-assert-classes/LICENSE.txt new file mode 100644 index 000000000..938db0368 --- /dev/null +++ b/tests/lib/vendor/qunit-assert-classes/LICENSE.txt @@ -0,0 +1,22 @@ +The MIT License (MIT) + +Copyright (c) 2015 Alexander Schmitz + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + diff --git a/tests/lib/vendor/qunit-assert-classes/qunit-assert-classes.js b/tests/lib/vendor/qunit-assert-classes/qunit-assert-classes.js new file mode 100644 index 000000000..3461f1d90 --- /dev/null +++ b/tests/lib/vendor/qunit-assert-classes/qunit-assert-classes.js @@ -0,0 +1,245 @@ +// With custom modifications - all are marked with +// a "Custom modification" comment. +( function( factory ) { + if ( typeof define === "function" && define.amd ) { + + // AMD. Register as an anonymous module. + define( [ + "qunit" + ], factory ); + } else { + + // Browser globals + factory( QUnit ); + } +}( function( QUnit ) { + + function inArray( haystack, needle ) { + for ( var i = 0; i < haystack.length; i++ ) { + if ( + ( needle instanceof RegExp && needle.test( haystack[ i ] ) )|| + ( typeof needle === "string" && haystack[ i ] === needle ) + ) { + return true; + } + } + return false; + } + + function check( element, search ) { + var i, classAttribute, elementClassArray, + missing = [], + found = []; + + if ( element.jquery && element.length !== 1 ) { + throw new Error( "Class checks can only be performed on a single element on a collection" ); + } + + element = element.jquery ? element[ 0 ] : element; + classAttribute = element.getAttribute( "class" ); + + if ( classAttribute ) { + elementClassArray = splitClasses( classAttribute ); + if ( search instanceof RegExp ) { + if ( inArray( elementClassArray, search ) ) { + found.push( search ); + } else { + missing.push( search ); + } + } else { + for( i = 0; i < search.length; i++ ) { + if ( !inArray( elementClassArray, search[ i ] ) ) { + missing.push( search[ i ] ); + } else { + found.push( search[ i ] ); + } + } + } + } else { + missing = search; + } + + return { + missing: missing, + found: found, + element: element, + classAttribute: classAttribute + }; + } + + function splitClasses( classes ) { + return classes.match( /\S+/g ) || []; + } + + function pluralize( message, classes ) { + return message + ( classes.length > 1 ? "es" : "" ); + } + + // Custom modification: removing QUnit.extend + var key; + var qunitAssertExtensions = { + hasClasses: function( element, classes, message ) { + var classArray = splitClasses( classes ), + results = check( element, classArray ); + + message = message || pluralize( "Element must have class", classArray ); + + // Custom modification: push -> pushResult + this.pushResult( { + result: !results.missing.length, + actual: results.found.join( " " ), + expected: classes, + message: message + } ); + }, + lacksClasses: function( element, classes, message ) { + var classArray = splitClasses( classes ), + results = check( element, classArray ); + + message = message || pluralize( "Element must not have class", classArray ); + + // Custom modification: push -> pushResult + this.pushResult( { + result: !results.found.length, + actual: results.found.join( " " ), + expected: classes, + message: message + } ); + }, + hasClassesStrict: function( element, classes, message ) { + var result, + classArray = splitClasses( classes ), + results = check( element, classArray ); + + message = message || pluralize( "Element must only have class", classArray ); + + result = !results.missing.length && results.element.getAttribute( "class" ) && + splitClasses( results.element.getAttribute( "class" ) ).length === + results.found.length; + + // Custom modification: push -> pushResult + this.pushResult( { + result: result, + actual: results.found.join( " " ), + expected: classes, + message: message + } ); + }, + hasClassRegex: function( element, regex, message ) { + var results = check( element, regex ); + + message = message || "Element must have class matching " + regex; + + // Custom modification: push -> pushResult + this.pushResult( { + result: !!results.found.length, + actual: results.found.join( " " ), + expected: regex, + message: message + } ); + }, + lacksClassRegex: function( element, regex, message ) { + var results = check( element, regex ); + + message = message || "Element must not have class matching " + regex; + + // Custom modification: push -> pushResult + this.pushResult( { + result: results.missing.length, + actual: results.missing.join( " " ), + expected: regex, + message: message + } ); + }, + hasClassStart: function( element, partialClass, message ) { + var results = check( element, new RegExp( "^" + partialClass ) ); + + message = message || "Element must have class starting with " + partialClass; + + // Custom modification: push -> pushResult + this.pushResult( { + result: results.found.length, + actual: results.found.join( " " ), + expected: partialClass, + message: message + } ); + }, + lacksClassStart: function( element, partialClass, message ) { + var results = check( element, new RegExp( "^" + partialClass ) ); + + message = message || "Element must not have class starting with " + partialClass; + + // Custom modification: push -> pushResult + this.pushResult( { + result: results.missing.length, + actual: results.missing.join( " " ), + expected: partialClass, + message: message + } ); + }, + hasClassPartial: function( element, partialClass, message ) { + var results = check( element, new RegExp( partialClass ) ); + + message = message || "Element must have class containing '" + partialClass + "'"; + + // Custom modification: push -> pushResult + this.pushResult( { + result: results.found.length, + actual: results.found.join( " " ), + expected: partialClass, + message: message + } ); + }, + lacksClassPartial: function( element, partialClass, message ) { + var results = check( element, new RegExp( partialClass ) ); + + message = message || "Element must not have class containing '" + partialClass + "'"; + + // Custom modification: push -> pushResult + this.pushResult( { + result: results.missing.length, + actual: results.missing.join( " " ), + expected: partialClass, + message: message + } ); + }, + lacksAllClasses: function( element, message ) { + element = element.jquery ? element[ 0 ] : element; + + var classAttribute = element.getAttribute( "class" ) || "", + classes = splitClasses( classAttribute ); + + message = message || "Element must not have any classes"; + + // Custom modification: push -> pushResult + this.pushResult( { + result: !classes.length, + actual: !classes.length, + expected: true, + message: message + } ); + }, + hasSomeClass: function( element, message ) { + element = element.jquery ? element[ 0 ] : element; + + var classAttribute = element.getAttribute( "class" ) || "", + classes = splitClasses( classAttribute ); + + message = message || "Element must have a class"; + + // Custom modification: push -> pushResult + this.pushResult( { + result: classes.length, + actual: classes.length, + expected: true, + message: message + } ); + } + }; + + // Custom modification: removing QUnit.extend + for ( key in qunitAssertExtensions ) { + QUnit.assert[ key ] = qunitAssertExtensions[ key ]; + } + +} ) ); diff --git a/tests/lib/vendor/qunit-assert-close/MIT-LICENSE.txt b/tests/lib/vendor/qunit-assert-close/MIT-LICENSE.txt new file mode 100644 index 000000000..aed5dc97e --- /dev/null +++ b/tests/lib/vendor/qunit-assert-close/MIT-LICENSE.txt @@ -0,0 +1,21 @@ +Copyright jQuery Foundation and other contributors +http://jquery.com/ + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tests/lib/vendor/qunit-assert-close/qunit-assert-close.js b/tests/lib/vendor/qunit-assert-close/qunit-assert-close.js new file mode 100644 index 000000000..69d405205 --- /dev/null +++ b/tests/lib/vendor/qunit-assert-close/qunit-assert-close.js @@ -0,0 +1,209 @@ +// With custom modifications - all are marked with +// a "Custom modification" comment. +(function(factory) { + + // NOTE: + // All techniques except for the "browser globals" fallback will extend the + // provided QUnit object but return the isolated API methods + + // For AMD: Register as an anonymous AMD module with a named dependency on "qunit". + if (typeof define === "function" && define.amd) { + define(["qunit"], factory); + } + // For Node.js + else if (typeof module !== "undefined" && module && module.exports && typeof require === "function") { + module.exports = factory(require("qunitjs")); + } + + // Custom modification: remove the non-Node.js CommonJS part due to its + // usage of QUnit.extend. + // + // For browser globals + else { + factory(QUnit); + } + +}(function(QUnit) { + + /** + * Find an appropriate `Assert` context to `push` results to. + * @param * context - An unknown context, possibly `Assert`, `Test`, or neither + * @private + */ + function _getPushContext(context) { + var pushContext; + + if (context && typeof context.push === "function") { + // `context` is an `Assert` context + pushContext = context; + } + else if (context && context.assert && typeof context.assert.push === "function") { + // `context` is a `Test` context + pushContext = context.assert; + } + else if ( + QUnit && QUnit.config && QUnit.config.current && QUnit.config.current.assert && + typeof QUnit.config.current.assert.push === "function" + ) { + // `context` is an unknown context but we can find the `Assert` context via QUnit + pushContext = QUnit.config.current.assert; + } + else if (QUnit && typeof QUnit.push === "function") { + pushContext = QUnit.push; + } + else { + throw new Error("Could not find the QUnit `Assert` context to push results"); + } + + return pushContext; + } + + /** + * Checks that the first two arguments are equal, or are numbers close enough to be considered equal + * based on a specified maximum allowable difference. + * + * @example assert.close(3.141, Math.PI, 0.001); + * + * @param Number actual + * @param Number expected + * @param Number maxDifference (the maximum inclusive difference allowed between the actual and expected numbers) + * @param String message (optional) + */ + function close(actual, expected, maxDifference, message) { + var actualDiff = (actual === expected) ? 0 : Math.abs(actual - expected), + result = actualDiff <= maxDifference, + pushContext = _getPushContext(this); + + message = message || (actual + " should be within " + maxDifference + " (inclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff)); + + // Custom modification: push -> pushResult + pushContext.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + + + /** + * Checks that the first two arguments are equal, or are numbers close enough to be considered equal + * based on a specified maximum allowable difference percentage. + * + * @example assert.close.percent(155, 150, 3.4); // Difference is ~3.33% + * + * @param Number actual + * @param Number expected + * @param Number maxPercentDifference (the maximum inclusive difference percentage allowed between the actual and expected numbers) + * @param String message (optional) + */ + close.percent = function closePercent(actual, expected, maxPercentDifference, message) { + var actualDiff, result, + pushContext = _getPushContext(this); + + if (actual === expected) { + actualDiff = 0; + result = actualDiff <= maxPercentDifference; + } + else if (actual !== 0 && expected !== 0 && expected !== Infinity && expected !== -Infinity) { + actualDiff = Math.abs(100 * (actual - expected) / expected); + result = actualDiff <= maxPercentDifference; + } + else { + // Dividing by zero (0)! Should return `false` unless the max percentage was `Infinity` + actualDiff = Infinity; + result = maxPercentDifference === Infinity; + } + message = message || (actual + " should be within " + maxPercentDifference + "% (inclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff + "%")); + + // Custom modification: push -> pushResult + pushContext.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + }; + + + /** + * Checks that the first two arguments are numbers with differences greater than the specified + * minimum difference. + * + * @example assert.notClose(3.1, Math.PI, 0.001); + * + * @param Number actual + * @param Number expected + * @param Number minDifference (the minimum exclusive difference allowed between the actual and expected numbers) + * @param String message (optional) + */ + function notClose(actual, expected, minDifference, message) { + var actualDiff = Math.abs(actual - expected), + result = actualDiff > minDifference, + pushContext = _getPushContext(this); + + message = message || (actual + " should not be within " + minDifference + " (exclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff)); + + // Custom modification: push -> pushResult + pushContext.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + } + + + /** + * Checks that the first two arguments are numbers with differences greater than the specified + * minimum difference percentage. + * + * @example assert.notClose.percent(156, 150, 3.5); // Difference is 4.0% + * + * @param Number actual + * @param Number expected + * @param Number minPercentDifference (the minimum exclusive difference percentage allowed between the actual and expected numbers) + * @param String message (optional) + */ + notClose.percent = function notClosePercent(actual, expected, minPercentDifference, message) { + var actualDiff, result, + pushContext = _getPushContext(this); + + if (actual === expected) { + actualDiff = 0; + result = actualDiff > minPercentDifference; + } + else if (actual !== 0 && expected !== 0 && expected !== Infinity && expected !== -Infinity) { + actualDiff = Math.abs(100 * (actual - expected) / expected); + result = actualDiff > minPercentDifference; + } + else { + // Dividing by zero (0)! Should only return `true` if the min percentage was `Infinity` + actualDiff = Infinity; + result = minPercentDifference !== Infinity; + } + message = message || (actual + " should not be within " + minPercentDifference + "% (exclusive) of " + expected + (result ? "" : ". Actual: " + actualDiff + "%")); + + // Custom modification: push -> pushResult + pushContext.pushResult({ + result: result, + actual: actual, + expected: expected, + message: message + }); + }; + + var key; + var api = { + close: close, + notClose: notClose, + closePercent: close.percent, + notClosePercent: notClose.percent + }; + + for (key in api) { + QUnit.assert[key] = api[key]; + } + + return api; +})); diff --git a/tests/lib/vendor/qunit-composite/LICENSE.txt b/tests/lib/vendor/qunit-composite/LICENSE.txt new file mode 100644 index 000000000..155d8e869 --- /dev/null +++ b/tests/lib/vendor/qunit-composite/LICENSE.txt @@ -0,0 +1,36 @@ +Copyright jQuery Foundation and other contributors, https://jquery.org/ + +This software consists of voluntary contributions made by many +individuals. For exact contribution history, see the revision history +available at https://github.com/JamesMGreene/qunit-composite + +The following license applies to all parts of this software except as +documented below: + +==== + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +==== + +All files located in the node_modules directory are externally +maintained libraries used by this software which have their own +licenses; we recommend you read them, as their terms may differ from the +terms above. diff --git a/tests/lib/vendor/qunit-composite/qunit-composite.css b/tests/lib/vendor/qunit-composite/qunit-composite.css new file mode 100644 index 000000000..a5d06ce77 --- /dev/null +++ b/tests/lib/vendor/qunit-composite/qunit-composite.css @@ -0,0 +1,47 @@ +.qunit-composite-suite { + position: fixed; + bottom: 0; + left: 0; + + margin: 0; + padding: 0; + border-width: 1px 0 0; + height: 45%; + width: 100%; + + background: #fff; +} + +#qunit-testsuites { + margin: 0; + padding: 0.5em 1.0em; + font-family: "Helvetica Neue Light","HelveticaNeue-Light","Helvetica Neue",Calibri,Helvetica,Arial,sans-serif; + font-size: small; + background-color: #d2e0e6; + border-bottom: 1px solid #fff; +} + +#qunit-testsuites a { + color: #00c; + text-decoration: none; +} + +#qunit-testsuites a:hover { + text-decoration: underline; +} + +#qunit-testsuites > li { + display: inline-block; +} + +#qunit-testsuites > li:first-child::before { + content: "Suites: "; +} + +#qunit-testsuites > li + li::before { + content: "|\a0"; +} + +#qunit-testsuites > li::after { + content: "\a0"; +} diff --git a/tests/lib/vendor/qunit-composite/qunit-composite.js b/tests/lib/vendor/qunit-composite/qunit-composite.js new file mode 100644 index 000000000..042d024b4 --- /dev/null +++ b/tests/lib/vendor/qunit-composite/qunit-composite.js @@ -0,0 +1,236 @@ +/** + * QUnit Composite + * + * With custom modifications - all are marked with + * a "Custom modification" comment. + * + * https://github.com/JamesMGreene/qunit-composite + * + * Copyright jQuery Foundation and other contributors + * Released under the MIT license. + * https://jquery.org/license/ + */ +(function( factory ) { + if ( typeof define === "function" && define.amd ) { + define( [ "qunit" ], factory ); + } else { + factory( QUnit ); + } +}(function( QUnit ) { +var iframe, hasBound, resumeTests, suiteAssert, + modules = 1, + executingComposite = false; + +function hasClass( elem, name ) { + return ( " " + elem.className + " " ).indexOf( " " + name + " " ) > -1; +} + +function addClass( elem, name ) { + if ( !hasClass( elem, name ) ) { + elem.className += ( elem.className ? " " : "" ) + name; + } +} + +function addEvent( elem, type, fn ) { + if ( elem.addEventListener ) { + // Standards-based browsers + elem.addEventListener( type, fn, false ); + } else if ( elem.attachEvent ) { + // support: IE <9 + elem.attachEvent( "on" + type, fn ); + } +} + +function runSuite( suite ) { + var path; + + if ( QUnit.is( "object", suite ) ) { + path = suite.path; + suite = suite.name; + } else { + path = suite; + } + + QUnit.test( suite, function( assert ) { + resumeTests = assert.async(); + suiteAssert = assert; + iframe.setAttribute( "src", path ); + // QUnit.start is called from the child iframe's QUnit.done hook. + }); +} + +function initIframe() { + var iframeWin, + body = document.body; + + function onIframeLoad() { + var moduleName, testName, + count = 0; + + if ( !iframe.src ) { + return; + } + + // Deal with QUnit being loaded asynchronously via AMD + if ( !iframeWin.QUnit && iframeWin.define && iframeWin.define.amd ) { + return iframeWin.require( [ "qunit" ], onIframeLoad ); + } + + iframeWin.QUnit.moduleStart(function( data ) { + // Capture module name for messages + moduleName = data.name; + }); + + iframeWin.QUnit.testStart(function( data ) { + // Capture test name for messages + testName = data.name; + }); + iframeWin.QUnit.testDone(function() { + testName = undefined; + }); + + iframeWin.QUnit.log(function( data ) { + if (testName === undefined) { + return; + } + // Pass all test details through to the main page + var message = ( moduleName ? moduleName + ": " : "" ) + testName + ": " + ( data.message || ( data.result ? "okay" : "failed" ) ); + suiteAssert.expect( ++count ); + suiteAssert.pushResult( { + result: data.result, + actual: data.actual, + expected: data.expected, + message: message + } ); + }); + + // Continue the outer test when the iframe's test is done + iframeWin.QUnit.done(function() { + resumeTests(); + }); + } + + iframe = document.createElement( "iframe" ); + iframe.className = "qunit-composite-suite"; + body.appendChild( iframe ); + + addEvent( iframe, "load", onIframeLoad ); + + iframeWin = iframe.contentWindow; +} + +function appendSuitesToHeader( suites ) { + var i, suitesLen, suite, path, name, suitesEl, testResultEl, + newSuiteListItemEl, newSuiteLinkEl; + + suitesEl = document.getElementById("qunit-testsuites"); + + if (!suitesEl) { + testResultEl = document.getElementById("qunit-testresult"); + + if (!testResultEl) { + // QUnit has not been set up yet. Defer until QUnit is ready. + QUnit.begin(function () { + appendSuitesToHeader(suites); + }); + return; + } + + suitesEl = document.createElement("ul"); + suitesEl.id = "qunit-testsuites"; + testResultEl.parentNode.insertBefore(suitesEl, testResultEl); + } + + for (i = 0, suitesLen = suites.length; i < suitesLen; ++i) { + suite = suites[i]; + newSuiteLinkEl = document.createElement("a"); + newSuiteLinkEl.innerHTML = suite.name || suite; + newSuiteLinkEl.href = suite.path || suite; + + newSuiteListItemEl = document.createElement("li"); + newSuiteListItemEl.appendChild(newSuiteLinkEl); + + suitesEl.appendChild(newSuiteListItemEl); + } +} + +/** + * @param {string} [name] Module name to group these test suites. + * @param {Array} suites List of suites where each suite + * may either be a string (path to the html test page), + * or an object with a path and name property. + */ +QUnit.testSuites = function( name, suites ) { + var i, suitesLen; + + if ( arguments.length === 1 ) { + suites = name; + name = "Composition #" + modules++; + } + suitesLen = suites.length; + + appendSuitesToHeader(suites); + + if ( !hasBound ) { + hasBound = true; + QUnit.begin( initIframe ); + + // TODO: Would be better to use something like QUnit.once( 'moduleDone' ) + // after the last test suite. + QUnit.moduleDone( function () { + executingComposite = false; + } ); + + QUnit.done(function() { + iframe.style.display = "none"; + }); + } + + QUnit.module( name, { + beforeEach: function () { + executingComposite = true; + } + }); + + for ( i = 0; i < suitesLen; i++ ) { + runSuite( suites[ i ] ); + } +}; + +QUnit.testDone(function( data ) { + if ( !executingComposite ) { + return; + } + + var i, len, + testId = data.testId, + current = document.getElementById( "qunit-test-output-" + testId ), + children = current && current.children, + src = iframe.src; + + if (!(current && children)) { + return; + } + + addEvent( current, "dblclick", function( e ) { + var target = e && e.target ? e.target : window.event.srcElement; + if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { + target = target.parentNode; + } + if ( window.location && target.nodeName.toLowerCase() === "strong" ) { + window.location = src; + } + }); + + // Undo QUnit's auto-expansion for bad tests + for ( i = 0, len = children.length; i < len; i++ ) { + if ( children[ i ].nodeName.toLowerCase() === "ol" ) { + addClass( children[ i ], "qunit-collapsed" ); + } + } + + // Update Rerun link to point to the standalone test suite page + current.getElementsByTagName( "a" )[ 0 ].href = src; +}); + +})); diff --git a/tests/unit/accordion/all.html b/tests/unit/accordion/all.html index 3c247cdae..71593dd39 100644 --- a/tests/unit/accordion/all.html +++ b/tests/unit/accordion/all.html @@ -7,9 +7,9 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - +