|
| 1 | +/** |
| 2 | + * QUnit Composite |
| 3 | + * |
| 4 | + * https://github.com/JamesMGreene/qunit-composite |
| 5 | + * |
| 6 | + * Copyright jQuery Foundation and other contributors |
| 7 | + * Released under the MIT license. |
| 8 | + * https://jquery.org/license/ |
| 9 | + */ |
| 10 | +(function( factory ) { |
| 11 | + if ( typeof define === "function" && define.amd ) { |
| 12 | + define( [ "qunit" ], factory ); |
| 13 | + } else { |
| 14 | + factory( QUnit ); |
| 15 | + } |
| 16 | +}(function( QUnit ) { |
| 17 | +var iframe, hasBound, |
| 18 | + modules = 1, |
| 19 | + executingComposite = false; |
| 20 | + |
| 21 | +function hasClass( elem, name ) { |
| 22 | + return ( " " + elem.className + " " ).indexOf( " " + name + " " ) > -1; |
| 23 | +} |
| 24 | + |
| 25 | +function addClass( elem, name ) { |
| 26 | + if ( !hasClass( elem, name ) ) { |
| 27 | + elem.className += ( elem.className ? " " : "" ) + name; |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +function addEvent( elem, type, fn ) { |
| 32 | + if ( elem.addEventListener ) { |
| 33 | + // Standards-based browsers |
| 34 | + elem.addEventListener( type, fn, false ); |
| 35 | + } else if ( elem.attachEvent ) { |
| 36 | + // support: IE <9 |
| 37 | + elem.attachEvent( "on" + type, fn ); |
| 38 | + } |
| 39 | +} |
| 40 | + |
| 41 | +function runSuite( suite ) { |
| 42 | + var path; |
| 43 | + |
| 44 | + if ( QUnit.is( "object", suite ) ) { |
| 45 | + path = suite.path; |
| 46 | + suite = suite.name; |
| 47 | + } else { |
| 48 | + path = suite; |
| 49 | + } |
| 50 | + |
| 51 | + QUnit.asyncTest( suite, function() { |
| 52 | + iframe.setAttribute( "src", path ); |
| 53 | + // QUnit.start is called from the child iframe's QUnit.done hook. |
| 54 | + }); |
| 55 | +} |
| 56 | + |
| 57 | +function initIframe() { |
| 58 | + var iframeWin, |
| 59 | + body = document.body; |
| 60 | + |
| 61 | + function onIframeLoad() { |
| 62 | + var moduleName, testName, |
| 63 | + count = 0; |
| 64 | + |
| 65 | + if ( !iframe.src ) { |
| 66 | + return; |
| 67 | + } |
| 68 | + |
| 69 | + // Deal with QUnit being loaded asynchronously via AMD |
| 70 | + if ( !iframeWin.QUnit && iframeWin.define && iframeWin.define.amd ) { |
| 71 | + return iframeWin.require( [ "qunit" ], onIframeLoad ); |
| 72 | + } |
| 73 | + |
| 74 | + iframeWin.QUnit.moduleStart(function( data ) { |
| 75 | + // Capture module name for messages |
| 76 | + moduleName = data.name; |
| 77 | + }); |
| 78 | + |
| 79 | + iframeWin.QUnit.testStart(function( data ) { |
| 80 | + // Capture test name for messages |
| 81 | + testName = data.name; |
| 82 | + }); |
| 83 | + iframeWin.QUnit.testDone(function() { |
| 84 | + testName = undefined; |
| 85 | + }); |
| 86 | + |
| 87 | + iframeWin.QUnit.log(function( data ) { |
| 88 | + if (testName === undefined) { |
| 89 | + return; |
| 90 | + } |
| 91 | + // Pass all test details through to the main page |
| 92 | + var message = ( moduleName ? moduleName + ": " : "" ) + testName + ": " + ( data.message || ( data.result ? "okay" : "failed" ) ); |
| 93 | + expect( ++count ); |
| 94 | + QUnit.push( data.result, data.actual, data.expected, message ); |
| 95 | + }); |
| 96 | + |
| 97 | + // Continue the outer test when the iframe's test is done |
| 98 | + iframeWin.QUnit.done(function() { |
| 99 | + QUnit.start(); |
| 100 | + }); |
| 101 | + } |
| 102 | + |
| 103 | + iframe = document.createElement( "iframe" ); |
| 104 | + iframe.className = "qunit-composite-suite"; |
| 105 | + body.appendChild( iframe ); |
| 106 | + |
| 107 | + addEvent( iframe, "load", onIframeLoad ); |
| 108 | + |
| 109 | + iframeWin = iframe.contentWindow; |
| 110 | +} |
| 111 | + |
| 112 | +function appendSuitesToHeader( suites ) { |
| 113 | + var i, suitesLen, suite, path, name, suitesEl, testResultEl, |
| 114 | + newSuiteListItemEl, newSuiteLinkEl; |
| 115 | + |
| 116 | + suitesEl = document.getElementById("qunit-testsuites"); |
| 117 | + |
| 118 | + if (!suitesEl) { |
| 119 | + testResultEl = document.getElementById("qunit-testresult"); |
| 120 | + |
| 121 | + if (!testResultEl) { |
| 122 | + // QUnit has not been set up yet. Defer until QUnit is ready. |
| 123 | + QUnit.begin(function () { |
| 124 | + appendSuitesToHeader(suites); |
| 125 | + }); |
| 126 | + return; |
| 127 | + } |
| 128 | + |
| 129 | + suitesEl = document.createElement("ul"); |
| 130 | + suitesEl.id = "qunit-testsuites"; |
| 131 | + testResultEl.parentNode.insertBefore(suitesEl, testResultEl); |
| 132 | + } |
| 133 | + |
| 134 | + for (i = 0, suitesLen = suites.length; i < suitesLen; ++i) { |
| 135 | + suite = suites[i]; |
| 136 | + newSuiteLinkEl = document.createElement("a"); |
| 137 | + newSuiteLinkEl.innerHTML = suite.name || suite; |
| 138 | + newSuiteLinkEl.href = suite.path || suite; |
| 139 | + |
| 140 | + newSuiteListItemEl = document.createElement("li"); |
| 141 | + newSuiteListItemEl.appendChild(newSuiteLinkEl); |
| 142 | + |
| 143 | + suitesEl.appendChild(newSuiteListItemEl); |
| 144 | + } |
| 145 | +} |
| 146 | + |
| 147 | +/** |
| 148 | + * @param {string} [name] Module name to group these test suites. |
| 149 | + * @param {Array} suites List of suites where each suite |
| 150 | + * may either be a string (path to the html test page), |
| 151 | + * or an object with a path and name property. |
| 152 | + */ |
| 153 | +QUnit.testSuites = function( name, suites ) { |
| 154 | + var i, suitesLen; |
| 155 | + |
| 156 | + if ( arguments.length === 1 ) { |
| 157 | + suites = name; |
| 158 | + name = "Composition #" + modules++; |
| 159 | + } |
| 160 | + suitesLen = suites.length; |
| 161 | + |
| 162 | + appendSuitesToHeader(suites); |
| 163 | + |
| 164 | + if ( !hasBound ) { |
| 165 | + hasBound = true; |
| 166 | + QUnit.begin( initIframe ); |
| 167 | + |
| 168 | + // TODO: Would be better to use something like QUnit.once( 'moduleDone' ) |
| 169 | + // after the last test suite. |
| 170 | + QUnit.moduleDone( function () { |
| 171 | + executingComposite = false; |
| 172 | + } ); |
| 173 | + |
| 174 | + QUnit.done(function() { |
| 175 | + iframe.style.display = "none"; |
| 176 | + }); |
| 177 | + } |
| 178 | + |
| 179 | + QUnit.module( name, { |
| 180 | + setup: function () { |
| 181 | + executingComposite = true; |
| 182 | + } |
| 183 | + }); |
| 184 | + |
| 185 | + for ( i = 0; i < suitesLen; i++ ) { |
| 186 | + runSuite( suites[ i ] ); |
| 187 | + } |
| 188 | +}; |
| 189 | + |
| 190 | +QUnit.testDone(function( data ) { |
| 191 | + if ( !executingComposite ) { |
| 192 | + return; |
| 193 | + } |
| 194 | + |
| 195 | + var i, len, |
| 196 | + testId = data.testId || QUnit.config.current.testId || data.testNumber || QUnit.config.current.testNumber, |
| 197 | + current = testId ? |
| 198 | + ( |
| 199 | + // QUnit @^1.16.0 |
| 200 | + document.getElementById( "qunit-test-output-" + testId ) || |
| 201 | + // QUnit @1.15.x |
| 202 | + document.getElementById( "qunit-test-output" + testId ) |
| 203 | + ) : |
| 204 | + // QUnit @<1.15.0 |
| 205 | + document.getElementById( QUnit.config.current.id ), |
| 206 | + children = current && current.children, |
| 207 | + src = iframe.src; |
| 208 | + |
| 209 | + if (!(current && children)) { |
| 210 | + return; |
| 211 | + } |
| 212 | + |
| 213 | + addEvent( current, "dblclick", function( e ) { |
| 214 | + var target = e && e.target ? e.target : window.event.srcElement; |
| 215 | + if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) { |
| 216 | + target = target.parentNode; |
| 217 | + } |
| 218 | + if ( window.location && target.nodeName.toLowerCase() === "strong" ) { |
| 219 | + window.location = src; |
| 220 | + } |
| 221 | + }); |
| 222 | + |
| 223 | + // Undo QUnit's auto-expansion for bad tests |
| 224 | + for ( i = 0, len = children.length; i < len; i++ ) { |
| 225 | + if ( children[ i ].nodeName.toLowerCase() === "ol" ) { |
| 226 | + addClass( children[ i ], "qunit-collapsed" ); |
| 227 | + } |
| 228 | + } |
| 229 | + |
| 230 | + // Update Rerun link to point to the standalone test suite page |
| 231 | + current.getElementsByTagName( "a" )[ 0 ].href = src; |
| 232 | +}); |
| 233 | + |
| 234 | +})); |
0 commit comments