Skip to content
This repository
Browse code

upgrade qunit

  • Loading branch information...
commit 8610bad890fd82273edee75a2e6dcab655c92319 1 parent 59f75e5
John Bender johnbender authored

Showing 35 changed files with 1,531 additions and 1,147 deletions. Show diff stats Hide diff stats

  1. +28 23 external/qunit.css
  2. +1,148 769 external/qunit.js
  3. +2 2 tests/jquery.testHelper.js
  4. +4 4 tests/unit/button-markup/buttonMarkup_core.js
  5. +9 9 tests/unit/button/button_core.js
  6. +2 2 tests/unit/checkboxradio/checkboxradio_core.js
  7. +1 1  tests/unit/collapsible/collapsible_core.js
  8. +2 2 tests/unit/controlgroup/controlgroup_core.js
  9. +31 31 tests/unit/core/core.js
  10. +2 2 tests/unit/core/core_scroll.js
  11. +3 3 tests/unit/degrade-inputs/degradeInputs.js
  12. +15 15 tests/unit/event/event_core.js
  13. +1 1  tests/unit/field-contain/fieldContain_events.js
  14. +4 4 tests/unit/fixed-toolbar/fixedToolbar.js
  15. +9 9 tests/unit/init/init_core.js
  16. +30 30 tests/unit/listview/listview_core.js
  17. +1 1  tests/unit/listview/listview_nested.js
  18. +11 11 tests/unit/loader/loader_core.js
  19. +4 4 tests/unit/media/media_core.js
  20. +2 2 tests/unit/navbar/navbar_core.js
  21. +49 49 tests/unit/navigation/navigation_core.js
  22. +111 111 tests/unit/navigation/navigation_helpers.js
  23. +1 1  tests/unit/navigation/navigation_paths.js
  24. +1 1  tests/unit/navigation/navigation_transitions.js
  25. +2 2 tests/unit/page-sections/page_core.js
  26. +7 7 tests/unit/page/page_core.js
  27. +1 1  tests/unit/popup/popup_core.js
  28. +7 7 tests/unit/select/select_cached.js
  29. +14 14 tests/unit/select/select_core.js
  30. +3 3 tests/unit/select/select_events.js
  31. +3 3 tests/unit/select/select_native.js
  32. +7 7 tests/unit/slider/slider_core.js
  33. +11 11 tests/unit/slider/slider_events.js
  34. +2 2 tests/unit/support/support_core.js
  35. +3 3 tests/unit/widget/widget_core.js
51 external/qunit.css
... ... @@ -1,9 +1,9 @@
1 1 /**
2   - * QUnit - A JavaScript Unit Testing Framework
  2 + * QUnit v1.9.0 - A JavaScript Unit Testing Framework
3 3 *
4 4 * http://docs.jquery.com/QUnit
5 5 *
6   - * Copyright (c) 2011 John Resig, Jörn Zaefferer
  6 + * Copyright (c) 2012 John Resig, Jörn Zaefferer
7 7 * Dual licensed under the MIT (MIT-LICENSE.txt)
8 8 * or GPL (GPL-LICENSE.txt) licenses.
9 9 */
@@ -38,10 +38,10 @@
38 38 line-height: 1em;
39 39 font-weight: normal;
40 40
41   - border-radius: 15px 15px 0 0;
42   - -moz-border-radius: 15px 15px 0 0;
43   - -webkit-border-top-right-radius: 15px;
44   - -webkit-border-top-left-radius: 15px;
  41 + border-radius: 5px 5px 0 0;
  42 + -moz-border-radius: 5px 5px 0 0;
  43 + -webkit-border-top-right-radius: 5px;
  44 + -webkit-border-top-left-radius: 5px;
45 45 }
46 46
47 47 #qunit-header a {
@@ -54,6 +54,11 @@
54 54 color: #fff;
55 55 }
56 56
  57 +#qunit-testrunner-toolbar label {
  58 + display: inline-block;
  59 + padding: 0 .5em 0 .1em;
  60 +}
  61 +
57 62 #qunit-banner {
58 63 height: 5px;
59 64 }
@@ -108,13 +113,9 @@
108 113
109 114 background-color: #fff;
110 115
111   - border-radius: 15px;
112   - -moz-border-radius: 15px;
113   - -webkit-border-radius: 15px;
114   -
115   - box-shadow: inset 0px 2px 13px #999;
116   - -moz-box-shadow: inset 0px 2px 13px #999;
117   - -webkit-box-shadow: inset 0px 2px 13px #999;
  116 + border-radius: 5px;
  117 + -moz-border-radius: 5px;
  118 + -webkit-border-radius: 5px;
118 119 }
119 120
120 121 #qunit-tests table {
@@ -157,8 +158,7 @@
157 158 #qunit-tests b.failed { color: #710909; }
158 159
159 160 #qunit-tests li li {
160   - margin: 0.5em;
161   - padding: 0.4em 0.5em 0.4em 0.5em;
  161 + padding: 5px;
162 162 background-color: #fff;
163 163 border-bottom: none;
164 164 list-style-position: inside;
@@ -167,9 +167,9 @@
167 167 /*** Passing Styles */
168 168
169 169 #qunit-tests li li.pass {
170   - color: #5E740B;
  170 + color: #3c510c;
171 171 background-color: #fff;
172   - border-left: 26px solid #C6E746;
  172 + border-left: 10px solid #C6E746;
173 173 }
174 174
175 175 #qunit-tests .pass { color: #528CE0; background-color: #D2E0E6; }
@@ -185,15 +185,15 @@
185 185 #qunit-tests li li.fail {
186 186 color: #710909;
187 187 background-color: #fff;
188   - border-left: 26px solid #EE5757;
  188 + border-left: 10px solid #EE5757;
189 189 white-space: pre;
190 190 }
191 191
192 192 #qunit-tests > li:last-child {
193   - border-radius: 0 0 15px 15px;
194   - -moz-border-radius: 0 0 15px 15px;
195   - -webkit-border-bottom-right-radius: 15px;
196   - -webkit-border-bottom-left-radius: 15px;
  193 + border-radius: 0 0 5px 5px;
  194 + -moz-border-radius: 0 0 5px 5px;
  195 + -webkit-border-bottom-right-radius: 5px;
  196 + -webkit-border-bottom-left-radius: 5px;
197 197 }
198 198
199 199 #qunit-tests .fail { color: #000000; background-color: #EE5757; }
@@ -216,6 +216,9 @@
216 216
217 217 border-bottom: 1px solid white;
218 218 }
  219 +#qunit-testresult .module-name {
  220 + font-weight: bold;
  221 +}
219 222
220 223 /** Fixture */
221 224
@@ -223,9 +226,11 @@
223 226 position: absolute;
224 227 top: -10000px;
225 228 left: -10000px;
  229 + width: 1000px;
  230 + height: 1000px;
226 231 }
227 232
228 233 [data-nstest-role='page'], [data-nstest-role='dialog'] {
229 234 position: absolute !important;
230 235 top: -10000px !important;
231   -}
  236 +}
1,917 external/qunit.js
... ... @@ -1,77 +1,96 @@
1 1 /**
2   - * QUnit - A JavaScript Unit Testing Framework
  2 + * QUnit v1.9.0 - A JavaScript Unit Testing Framework
3 3 *
4 4 * http://docs.jquery.com/QUnit
5 5 *
6   - * Copyright (c) 2011 John Resig, Jörn Zaefferer
  6 + * Copyright (c) 2012 John Resig, Jörn Zaefferer
7 7 * Dual licensed under the MIT (MIT-LICENSE.txt)
8 8 * or GPL (GPL-LICENSE.txt) licenses.
9 9 */
10 10
11   -(function(window) {
  11 +(function( window ) {
12 12
13   -var defined = {
  13 +var QUnit,
  14 + config,
  15 + onErrorFnPrev,
  16 + testId = 0,
  17 + fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
  18 + toString = Object.prototype.toString,
  19 + hasOwn = Object.prototype.hasOwnProperty,
  20 + defined = {
14 21 setTimeout: typeof window.setTimeout !== "undefined",
15 22 sessionStorage: (function() {
  23 + var x = "qunit-test-string";
16 24 try {
17   - return !!sessionStorage.getItem;
18   - } catch(e) {
  25 + sessionStorage.setItem( x, x );
  26 + sessionStorage.removeItem( x );
  27 + return true;
  28 + } catch( e ) {
19 29 return false;
20 30 }
21   - })()
  31 + }())
22 32 };
23 33
24   -var testId = 0;
25   -
26   -var Test = function(name, testName, expected, testEnvironmentArg, async, callback) {
27   - this.name = name;
28   - this.testName = testName;
29   - this.expected = expected;
30   - this.testEnvironmentArg = testEnvironmentArg;
31   - this.async = async;
32   - this.callback = callback;
  34 +function Test( settings ) {
  35 + extend( this, settings );
33 36 this.assertions = [];
34   -};
  37 + this.testNumber = ++Test.count;
  38 +}
  39 +
  40 +Test.count = 0;
  41 +
35 42 Test.prototype = {
36 43 init: function() {
37   - var tests = id("qunit-tests");
38   - if (tests) {
39   - var b = document.createElement("strong");
40   - b.innerHTML = "Running " + this.name;
41   - var li = document.createElement("li");
42   - li.appendChild( b );
43   - li.className = "running";
44   - li.id = this.id = "test-output" + testId++;
  44 + var a, b, li,
  45 + tests = id( "qunit-tests" );
  46 +
  47 + if ( tests ) {
  48 + b = document.createElement( "strong" );
  49 + b.innerHTML = this.name;
  50 +
  51 + // `a` initialized at top of scope
  52 + a = document.createElement( "a" );
  53 + a.innerHTML = "Rerun";
  54 + a.href = QUnit.url({ testNumber: this.testNumber });
  55 +
  56 + li = document.createElement( "li" );
  57 + li.appendChild( b );
  58 + li.appendChild( a );
  59 + li.className = "running";
  60 + li.id = this.id = "qunit-test-output" + testId++;
  61 +
45 62 tests.appendChild( li );
46 63 }
47 64 },
48 65 setup: function() {
49   - if (this.module != config.previousModule) {
  66 + if ( this.module !== config.previousModule ) {
50 67 if ( config.previousModule ) {
51   - runLoggingCallbacks('moduleDone', QUnit, {
  68 + runLoggingCallbacks( "moduleDone", QUnit, {
52 69 name: config.previousModule,
53 70 failed: config.moduleStats.bad,
54 71 passed: config.moduleStats.all - config.moduleStats.bad,
55 72 total: config.moduleStats.all
56   - } );
  73 + });
57 74 }
58 75 config.previousModule = this.module;
59 76 config.moduleStats = { all: 0, bad: 0 };
60   - runLoggingCallbacks( 'moduleStart', QUnit, {
  77 + runLoggingCallbacks( "moduleStart", QUnit, {
  78 + name: this.module
  79 + });
  80 + } else if ( config.autorun ) {
  81 + runLoggingCallbacks( "moduleStart", QUnit, {
61 82 name: this.module
62   - } );
  83 + });
63 84 }
64 85
65 86 config.current = this;
  87 +
66 88 this.testEnvironment = extend({
67 89 setup: function() {},
68 90 teardown: function() {}
69   - }, this.moduleTestEnvironment);
70   - if (this.testEnvironmentArg) {
71   - extend(this.testEnvironment, this.testEnvironmentArg);
72   - }
  91 + }, this.moduleTestEnvironment );
73 92
74   - runLoggingCallbacks( 'testStart', QUnit, {
  93 + runLoggingCallbacks( "testStart", QUnit, {
75 94 name: this.testName,
76 95 module: this.module
77 96 });
@@ -80,67 +99,92 @@ Test.prototype = {
80 99 // TODO why??
81 100 QUnit.current_testEnvironment = this.testEnvironment;
82 101
  102 + if ( !config.pollution ) {
  103 + saveGlobal();
  104 + }
  105 + if ( config.notrycatch ) {
  106 + this.testEnvironment.setup.call( this.testEnvironment );
  107 + return;
  108 + }
83 109 try {
84   - if ( !config.pollution ) {
85   - saveGlobal();
86   - }
87   -
88   - this.testEnvironment.setup.call(this.testEnvironment);
89   - } catch(e) {
90   - QUnit.ok( false, "Setup failed on " + this.testName + ": " + e.message );
  110 + this.testEnvironment.setup.call( this.testEnvironment );
  111 + } catch( e ) {
  112 + QUnit.pushFailure( "Setup failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
91 113 }
92 114 },
93 115 run: function() {
  116 + config.current = this;
  117 +
  118 + var running = id( "qunit-testresult" );
  119 +
  120 + if ( running ) {
  121 + running.innerHTML = "Running: <br/>" + this.name;
  122 + }
  123 +
94 124 if ( this.async ) {
95 125 QUnit.stop();
96 126 }
97 127
98 128 if ( config.notrycatch ) {
99   - this.callback.call(this.testEnvironment);
  129 + this.callback.call( this.testEnvironment, QUnit.assert );
100 130 return;
101 131 }
  132 +
102 133 try {
103   - this.callback.call(this.testEnvironment);
104   - } catch(e) {
105   - fail("Test " + this.testName + " died, exception and test follows", e, this.callback);
106   - QUnit.ok( false, "Died on test #" + (this.assertions.length + 1) + ": " + e.message + " - " + QUnit.jsDump.parse(e) );
  134 + this.callback.call( this.testEnvironment, QUnit.assert );
  135 + } catch( e ) {
  136 + QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + e.message, extractStacktrace( e, 0 ) );
107 137 // else next test will carry the responsibility
108 138 saveGlobal();
109 139
110 140 // Restart the tests if they're blocking
111 141 if ( config.blocking ) {
112   - start();
  142 + QUnit.start();
113 143 }
114 144 }
115 145 },
116 146 teardown: function() {
117   - try {
118   - this.testEnvironment.teardown.call(this.testEnvironment);
119   - checkPollution();
120   - } catch(e) {
121   - QUnit.ok( false, "Teardown failed on " + this.testName + ": " + e.message );
  147 + config.current = this;
  148 + if ( config.notrycatch ) {
  149 + this.testEnvironment.teardown.call( this.testEnvironment );
  150 + return;
  151 + } else {
  152 + try {
  153 + this.testEnvironment.teardown.call( this.testEnvironment );
  154 + } catch( e ) {
  155 + QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + e.message, extractStacktrace( e, 1 ) );
  156 + }
122 157 }
  158 + checkPollution();
123 159 },
124 160 finish: function() {
125   - if ( this.expected && this.expected != this.assertions.length ) {
126   - QUnit.ok( false, "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run" );
  161 + config.current = this;
  162 + if ( config.requireExpects && this.expected == null ) {
  163 + QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
  164 + } else if ( this.expected != null && this.expected != this.assertions.length ) {
  165 + QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
  166 + } else if ( this.expected == null && !this.assertions.length ) {
  167 + QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
127 168 }
128 169
129   - var good = 0, bad = 0,
130   - tests = id("qunit-tests");
  170 + var assertion, a, b, i, li, ol,
  171 + test = this,
  172 + good = 0,
  173 + bad = 0,
  174 + tests = id( "qunit-tests" );
131 175
132 176 config.stats.all += this.assertions.length;
133 177 config.moduleStats.all += this.assertions.length;
134 178
135 179 if ( tests ) {
136   - var ol = document.createElement("ol");
  180 + ol = document.createElement( "ol" );
137 181
138   - for ( var i = 0; i < this.assertions.length; i++ ) {
139   - var assertion = this.assertions[i];
  182 + for ( i = 0; i < this.assertions.length; i++ ) {
  183 + assertion = this.assertions[i];
140 184
141   - var li = document.createElement("li");
  185 + li = document.createElement( "li" );
142 186 li.className = assertion.result ? "pass" : "fail";
143   - li.innerHTML = assertion.message || (assertion.result ? "okay" : "failed");
  187 + li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
144 188 ol.appendChild( li );
145 189
146 190 if ( assertion.result ) {
@@ -154,49 +198,48 @@ Test.prototype = {
154 198
155 199 // store result when possible
156 200 if ( QUnit.config.reorder && defined.sessionStorage ) {
157   - if (bad) {
158   - sessionStorage.setItem("qunit-" + this.module + "-" + this.testName, bad);
  201 + if ( bad ) {
  202 + sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
159 203 } else {
160   - sessionStorage.removeItem("qunit-" + this.module + "-" + this.testName);
  204 + sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
161 205 }
162 206 }
163 207
164   - if (bad == 0) {
  208 + if ( bad === 0 ) {
165 209 ol.style.display = "none";
166 210 }
167 211
168   - var b = document.createElement("strong");
  212 + // `b` initialized at top of scope
  213 + b = document.createElement( "strong" );
169 214 b.innerHTML = this.name + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
170 215
171   - var a = document.createElement("a");
172   - a.innerHTML = "Rerun";
173   - a.href = QUnit.url({ filter: getText([b]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
174   -
175 216 addEvent(b, "click", function() {
176 217 var next = b.nextSibling.nextSibling,
177 218 display = next.style.display;
178 219 next.style.display = display === "none" ? "block" : "none";
179 220 });
180 221
181   - addEvent(b, "dblclick", function(e) {
  222 + addEvent(b, "dblclick", function( e ) {
182 223 var target = e && e.target ? e.target : window.event.srcElement;
183 224 if ( target.nodeName.toLowerCase() == "span" || target.nodeName.toLowerCase() == "b" ) {
184 225 target = target.parentNode;
185 226 }
186 227 if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
187   - window.location = QUnit.url({ filter: getText([target]).replace(/\([^)]+\)$/, "").replace(/(^\s*|\s*$)/g, "") });
  228 + window.location = QUnit.url({ testNumber: test.testNumber });
188 229 }
189 230 });
190 231
191   - var li = id(this.id);
  232 + // `li` initialized at top of scope
  233 + li = id( this.id );
192 234 li.className = bad ? "fail" : "pass";
193 235 li.removeChild( li.firstChild );
  236 + a = li.firstChild;
194 237 li.appendChild( b );
195   - li.appendChild( a );
  238 + li.appendChild ( a );
196 239 li.appendChild( ol );
197 240
198 241 } else {
199   - for ( var i = 0; i < this.assertions.length; i++ ) {
  242 + for ( i = 0; i < this.assertions.length; i++ ) {
200 243 if ( !this.assertions[i].result ) {
201 244 bad++;
202 245 config.stats.bad++;
@@ -205,23 +248,23 @@ Test.prototype = {
205 248 }
206 249 }
207 250
208   - try {
209   - QUnit.reset();
210   - } catch(e) {
211   - fail("reset() failed, following Test " + this.testName + ", exception and reset fn follows", e, QUnit.reset);
212   - }
213   -
214   - runLoggingCallbacks( 'testDone', QUnit, {
  251 + runLoggingCallbacks( "testDone", QUnit, {
215 252 name: this.testName,
216 253 module: this.module,
217 254 failed: bad,
218 255 passed: this.assertions.length - bad,
219 256 total: this.assertions.length
220   - } );
  257 + });
  258 +
  259 + QUnit.reset();
  260 +
  261 + config.current = undefined;
221 262 },
222 263
223 264 queue: function() {
224   - var test = this;
  265 + var bad,
  266 + test = this;
  267 +
225 268 synchronize(function() {
226 269 test.init();
227 270 });
@@ -240,214 +283,288 @@ Test.prototype = {
240 283 test.finish();
241 284 });
242 285 }
  286 +
  287 + // `bad` initialized at top of scope
243 288 // defer when previous test run passed, if storage is available
244   - var bad = QUnit.config.reorder && defined.sessionStorage && +sessionStorage.getItem("qunit-" + this.module + "-" + this.testName);
245   - if (bad) {
  289 + bad = QUnit.config.reorder && defined.sessionStorage &&
  290 + +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
  291 +
  292 + if ( bad ) {
246 293 run();
247 294 } else {
248   - synchronize(run);
249   - };
  295 + synchronize( run, true );
  296 + }
250 297 }
251   -
252 298 };
253 299
254   -var QUnit = {
  300 +// Root QUnit object.
  301 +// `QUnit` initialized at top of scope
  302 +QUnit = {
255 303
256 304 // call on start of module test to prepend name to all tests
257   - module: function(name, testEnvironment) {
  305 + module: function( name, testEnvironment ) {
258 306 config.currentModule = name;
259 307 config.currentModuleTestEnviroment = testEnvironment;
260 308 },
261 309
262   - asyncTest: function(testName, expected, callback) {
  310 + asyncTest: function( testName, expected, callback ) {
263 311 if ( arguments.length === 2 ) {
264 312 callback = expected;
265   - expected = 0;
  313 + expected = null;
266 314 }
267 315
268   - QUnit.test(testName, expected, callback, true);
  316 + QUnit.test( testName, expected, callback, true );
269 317 },
270 318
271   - test: function(testName, expected, callback, async) {
272   - var name = '<span class="test-name">' + testName + '</span>', testEnvironmentArg;
  319 + test: function( testName, expected, callback, async ) {
  320 + var test,
  321 + name = "<span class='test-name'>" + escapeInnerText( testName ) + "</span>";
273 322
274 323 if ( arguments.length === 2 ) {
275 324 callback = expected;
276 325 expected = null;
277 326 }
278   - // is 2nd argument a testEnvironment?
279   - if ( expected && typeof expected === 'object') {
280   - testEnvironmentArg = expected;
281   - expected = null;
282   - }
283 327
284 328 if ( config.currentModule ) {
285   - name = '<span class="module-name">' + config.currentModule + "</span>: " + name;
  329 + name = "<span class='module-name'>" + config.currentModule + "</span>: " + name;
286 330 }
287 331
288   - if ( !validTest(config.currentModule + ": " + testName) ) {
  332 + test = new Test({
  333 + name: name,
  334 + testName: testName,
  335 + expected: expected,
  336 + async: async,
  337 + callback: callback,
  338 + module: config.currentModule,
  339 + moduleTestEnvironment: config.currentModuleTestEnviroment,
  340 + stack: sourceFromStacktrace( 2 )
  341 + });
  342 +
  343 + if ( !validTest( test ) ) {
289 344 return;
290 345 }
291 346
292   - var test = new Test(name, testName, expected, testEnvironmentArg, async, callback);
293   - test.module = config.currentModule;
294   - test.moduleTestEnvironment = config.currentModuleTestEnviroment;
295 347 test.queue();
296 348 },
297 349
298   - /**
299   - * Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
300   - */
301   - expect: function(asserts) {
  350 + // Specify the number of expected assertions to gurantee that failed test (no assertions are run at all) don't slip through.
  351 + expect: function( asserts ) {
302 352 config.current.expected = asserts;
303 353 },
304 354
  355 + start: function( count ) {
  356 + config.semaphore -= count || 1;
  357 + // don't start until equal number of stop-calls
  358 + if ( config.semaphore > 0 ) {
  359 + return;
  360 + }
  361 + // ignore if start is called more often then stop
  362 + if ( config.semaphore < 0 ) {
  363 + config.semaphore = 0;
  364 + }
  365 + // A slight delay, to avoid any current callbacks
  366 + if ( defined.setTimeout ) {
  367 + window.setTimeout(function() {
  368 + if ( config.semaphore > 0 ) {
  369 + return;
  370 + }
  371 + if ( config.timeout ) {
  372 + clearTimeout( config.timeout );
  373 + }
  374 +
  375 + config.blocking = false;
  376 + process( true );
  377 + }, 13);
  378 + } else {
  379 + config.blocking = false;
  380 + process( true );
  381 + }
  382 + },
  383 +
  384 + stop: function( count ) {
  385 + config.semaphore += count || 1;
  386 + config.blocking = true;
  387 +
  388 + if ( config.testTimeout && defined.setTimeout ) {
  389 + clearTimeout( config.timeout );
  390 + config.timeout = window.setTimeout(function() {
  391 + QUnit.ok( false, "Test timed out" );
  392 + config.semaphore = 1;
  393 + QUnit.start();
  394 + }, config.testTimeout );
  395 + }
  396 + }
  397 +};
  398 +
  399 +// Asssert helpers
  400 +// All of these must call either QUnit.push() or manually do:
  401 +// - runLoggingCallbacks( "log", .. );
  402 +// - config.current.assertions.push({ .. });
  403 +QUnit.assert = {
305 404 /**
306   - * Asserts true.
  405 + * Asserts rough true-ish result.
  406 + * @name ok
  407 + * @function
307 408 * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
308 409 */
309   - ok: function(a, msg) {
310   - a = !!a;
311   - var details = {
312   - result: a,
313   - message: msg
314   - };
315   - msg = escapeInnerText(msg);
316   - runLoggingCallbacks( 'log', QUnit, details );
  410 + ok: function( result, msg ) {
  411 + if ( !config.current ) {
  412 + throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
  413 + }
  414 + result = !!result;
  415 +
  416 + var source,
  417 + details = {
  418 + result: result,
  419 + message: msg
  420 + };
  421 +
  422 + msg = escapeInnerText( msg || (result ? "okay" : "failed" ) );
  423 + msg = "<span class='test-message'>" + msg + "</span>";
  424 +
  425 + if ( !result ) {
  426 + source = sourceFromStacktrace( 2 );
  427 + if ( source ) {
  428 + details.source = source;
  429 + msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr></table>";
  430 + }
  431 + }
  432 + runLoggingCallbacks( "log", QUnit, details );
317 433 config.current.assertions.push({
318   - result: a,
  434 + result: result,
319 435 message: msg
320 436 });
321 437 },
322 438
323 439 /**
324   - * Checks that the first two arguments are equal, with an optional message.
  440 + * Assert that the first two arguments are equal, with an optional message.
325 441 * Prints out both actual and expected values.
326   - *
327   - * Prefered to ok( actual == expected, message )
328   - *
329   - * @example equal( format("Received {0} bytes.", 2), "Received 2 bytes." );
330   - *
331   - * @param Object actual
332   - * @param Object expected
333   - * @param String message (optional)
  442 + * @name equal
  443 + * @function
  444 + * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
334 445 */
335   - equal: function(actual, expected, message) {
336   - QUnit.push(expected == actual, actual, expected, message);
  446 + equal: function( actual, expected, message ) {
  447 + QUnit.push( expected == actual, actual, expected, message );
337 448 },
338 449
339   - notEqual: function(actual, expected, message) {
340   - QUnit.push(expected != actual, actual, expected, message);
  450 + /**
  451 + * @name notEqual
  452 + * @function
  453 + */
  454 + notEqual: function( actual, expected, message ) {
  455 + QUnit.push( expected != actual, actual, expected, message );
341 456 },
342 457
343   - deepEqual: function(actual, expected, message) {
344   - QUnit.push(QUnit.equiv(actual, expected), actual, expected, message);
  458 + /**
  459 + * @name deepEqual
  460 + * @function
  461 + */
  462 + deepEqual: function( actual, expected, message ) {
  463 + QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
345 464 },
346 465
347   - notDeepEqual: function(actual, expected, message) {
348   - QUnit.push(!QUnit.equiv(actual, expected), actual, expected, message);
  466 + /**
  467 + * @name notDeepEqual
  468 + * @function
  469 + */
  470 + notDeepEqual: function( actual, expected, message ) {
  471 + QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
349 472 },
350 473
351   - strictEqual: function(actual, expected, message) {
352   - QUnit.push(expected === actual, actual, expected, message);
  474 + /**
  475 + * @name strictEqual
  476 + * @function
  477 + */
  478 + strictEqual: function( actual, expected, message ) {
  479 + QUnit.push( expected === actual, actual, expected, message );
353 480 },
354 481
355   - notStrictEqual: function(actual, expected, message) {
356   - QUnit.push(expected !== actual, actual, expected, message);
  482 + /**
  483 + * @name notStrictEqual
  484 + * @function
  485 + */
  486 + notStrictEqual: function( actual, expected, message ) {
  487 + QUnit.push( expected !== actual, actual, expected, message );
357 488 },
358 489
359   - raises: function(block, expected, message) {
360   - var actual, ok = false;
  490 + throws: function( block, expected, message ) {
  491 + var actual,
  492 + ok = false;
361 493
362   - if (typeof expected === 'string') {
  494 + // 'expected' is optional
  495 + if ( typeof expected === "string" ) {
363 496 message = expected;
364 497 expected = null;
365 498 }
366 499
  500 + config.current.ignoreGlobalErrors = true;
367 501 try {
368   - block();
  502 + block.call( config.current.testEnvironment );
369 503 } catch (e) {
370 504 actual = e;
371 505 }
  506 + config.current.ignoreGlobalErrors = false;
372 507
373   - if (actual) {
  508 + if ( actual ) {
374 509 // we don't want to validate thrown error
375   - if (!expected) {
  510 + if ( !expected ) {
376 511 ok = true;
377 512 // expected is a regexp
378   - } else if (QUnit.objectType(expected) === "regexp") {
379   - ok = expected.test(actual);
  513 + } else if ( QUnit.objectType( expected ) === "regexp" ) {
  514 + ok = expected.test( actual );
380 515 // expected is a constructor
381   - } else if (actual instanceof expected) {
  516 + } else if ( actual instanceof expected ) {
382 517 ok = true;
383 518 // expected is a validation function which returns true is validation passed
384   - } else if (expected.call({}, actual) === true) {
  519 + } else if ( expected.call( {}, actual ) === true ) {
385 520 ok = true;
386 521 }
387   - }
388 522
389   - QUnit.ok(ok, message);
390   - },
391   -
392   - start: function(count) {
393   - config.semaphore -= count || 1;
394   - if (config.semaphore > 0) {
395   - // don't start until equal number of stop-calls
396   - return;
397   - }
398   - if (config.semaphore < 0) {
399   - // ignore if start is called more often then stop
400   - config.semaphore = 0;
401   - }
402   - // A slight delay, to avoid any current callbacks
403   - if ( defined.setTimeout ) {
404   - window.setTimeout(function() {
405   - if (config.semaphore > 0) {
406   - return;
407   - }
408   - if ( config.timeout ) {
409   - clearTimeout(config.timeout);
410   - }
411   -
412   - config.blocking = false;
413   - process();
414   - }, 13);
  523 + QUnit.push( ok, actual, null, message );
415 524 } else {
416   - config.blocking = false;
417   - process();
  525 + QUnit.pushFailure( message, null, 'No exception was thrown.' );
418 526 }
419   - },
  527 + }
  528 +};
420 529
421   - stop: function(count) {
422   - config.semaphore += count || 1;
423   - config.blocking = true;
  530 +/**
  531 + * @deprecate since 1.8.0
  532 + * Kept assertion helpers in root for backwards compatibility
  533 + */
  534 +extend( QUnit, QUnit.assert );
424 535
425   - if ( config.testTimeout && defined.setTimeout ) {
426   - clearTimeout(config.timeout);
427   - config.timeout = window.setTimeout(function() {
428   - QUnit.ok( false, "Test timed out" );
429   - config.semaphore = 1;
430   - QUnit.start();
431   - }, config.testTimeout);
432   - }
433   - }
  536 +/**
  537 + * @deprecated since 1.9.0
  538 + * Kept global "raises()" for backwards compatibility
  539 + */
  540 +QUnit.raises = QUnit.assert.throws;
  541 +
  542 +/**
  543 + * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
  544 + * Kept to avoid TypeErrors for undefined methods.
  545 + */
  546 +QUnit.equals = function() {
  547 + QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
  548 +};
  549 +QUnit.same = function() {
  550 + QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
434 551 };
435 552
436   -//We want access to the constructor's prototype
  553 +// We want access to the constructor's prototype
437 554 (function() {
438   - function F(){};
  555 + function F() {}
439 556 F.prototype = QUnit;
440 557 QUnit = new F();
441   - //Make F QUnit's constructor so that we can add to the prototype later
  558 + // Make F QUnit's constructor so that we can add to the prototype later
442 559 QUnit.constructor = F;
443   -})();
  560 +}());
444 561
445   -// Backwards compatibility, deprecated
446   -QUnit.equals = QUnit.equal;
447   -QUnit.same = QUnit.deepEqual;
448   -
449   -// Maintain internal state
450   -var config = {
  562 +/**
  563 + * Config object: Maintain internal state
  564 + * Later exposed as QUnit.config
  565 + * `config` initialized at top of scope
  566 + */
  567 +config = {
451 568 // The queue of tests to run
452 569 queue: [],
453 570
@@ -465,9 +582,25 @@ var config = {
465 582 // by default, modify document.title when suite is done
466 583 altertitle: true,
467 584
468   - urlConfig: ['noglobals', 'notrycatch'],
  585 + // when enabled, all tests must call expect()
  586 + requireExpects: false,
  587 +
  588 + // add checkboxes that are persisted in the query-string
  589 + // when enabled, the id is set to `true` as a `QUnit.config` property
  590 + urlConfig: [
  591 + {
  592 + id: "noglobals",
  593 + label: "Check for Globals",
  594 + tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
  595 + },
  596 + {
  597 + id: "notrycatch",
  598 + label: "No try-catch",
  599 + tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
  600 + }
  601 + ],
469 602
470   - //logging callback queues
  603 + // logging callback queues
471 604 begin: [],
472 605 done: [],
473 606 log: [],
@@ -477,16 +610,17 @@ var config = {
477 610 moduleDone: []
478 611 };
479 612
480   -// Load paramaters
  613 +// Initialize more QUnit.config and QUnit.urlParams
481 614 (function() {
482   - var location = window.location || { search: "", protocol: "file:" },
  615 + var i,
  616 + location = window.location || { search: "", protocol: "file:" },
483 617 params = location.search.slice( 1 ).split( "&" ),
484 618 length = params.length,
485 619 urlParams = {},
486 620 current;
487 621
488 622 if ( params[ 0 ] ) {
489   - for ( var i = 0; i < length; i++ ) {
  623 + for ( i = 0; i < length; i++ ) {
490 624 current = params[ i ].split( "=" );
491 625 current[ 0 ] = decodeURIComponent( current[ 0 ] );
492 626 // allow just a key to turn on a flag, e.g., test.html?noglobals
@@ -496,32 +630,39 @@ var config = {
496 630 }
497 631
498 632 QUnit.urlParams = urlParams;
  633 +
  634 + // String search anywhere in moduleName+testName
499 635 config.filter = urlParams.filter;
500 636
  637 + // Exact match of the module name
  638 + config.module = urlParams.module;
  639 +
  640 + config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
  641 +
501 642 // Figure out if we're running the tests from a server or not
502   - QUnit.isLocal = !!(location.protocol === 'file:');
503   -})();
  643 + QUnit.isLocal = location.protocol === "file:";
  644 +}());
504 645
505   -// Expose the API as global variables, unless an 'exports'
506   -// object exists, in that case we assume we're in CommonJS
507   -if ( typeof exports === "undefined" || typeof require === "undefined" ) {
508   - extend(window, QUnit);
  646 +// Export global variables, unless an 'exports' object exists,
  647 +// in that case we assume we're in CommonJS (dealt with on the bottom of the script)
  648 +if ( typeof exports === "undefined" ) {
  649 + extend( window, QUnit );
  650 +
  651 + // Expose QUnit object
509 652 window.QUnit = QUnit;
510   -} else {
511   - extend(exports, QUnit);
512   - exports.QUnit = QUnit;
513 653 }
514 654
515   -// define these after exposing globals to keep them in these QUnit namespace only
516   -extend(QUnit, {
  655 +// Extend QUnit object,
  656 +// these after set here because they should not be exposed as global functions
  657 +extend( QUnit, {
517 658 config: config,
518 659
519 660 // Initialize the configuration options
520 661 init: function() {
521   - extend(config, {
  662 + extend( config, {
522 663 stats: { all: 0, bad: 0 },
523 664 moduleStats: { all: 0, bad: 0 },
524   - started: +new Date,
  665 + started: +new Date(),
525 666 updateRate: 1000,
526 667 blocking: false,
527 668 autostart: true,
@@ -531,9 +672,21 @@ extend(QUnit, {
531 672 semaphore: 0
532 673 });
533 674
534   - var tests = id( "qunit-tests" ),
535   - banner = id( "qunit-banner" ),
536   - result = id( "qunit-testresult" );
  675 + var tests, banner, result,
  676 + qunit = id( "qunit" );
  677 +
  678 + if ( qunit ) {
  679 + qunit.innerHTML =
  680 + "<h1 id='qunit-header'>" + escapeInnerText( document.title ) + "</h1>" +
  681 + "<h2 id='qunit-banner'></h2>" +
  682 + "<div id='qunit-testrunner-toolbar'></div>" +
  683 + "<h2 id='qunit-userAgent'></h2>" +
  684 + "<ol id='qunit-tests'></ol>";
  685 + }
  686 +
  687 + tests = id( "qunit-tests" );
  688 + banner = id( "qunit-banner" );
  689 + result = id( "qunit-testresult" );
537 690
538 691 if ( tests ) {
539 692 tests.innerHTML = "";
@@ -552,43 +705,36 @@ extend(QUnit, {
552 705 result.id = "qunit-testresult";
553 706 result.className = "result";
554 707 tests.parentNode.insertBefore( result, tests );
555   - result.innerHTML = 'Running...<br/>&nbsp;';
  708 + result.innerHTML = "Running...<br/>&nbsp;";
556 709 }
557 710 },
558 711
559   - /**
560   - * Resets the test setup. Useful for tests that modify the DOM.
561   - *
562   - * If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
563   - */
  712 + // Resets the test setup. Useful for tests that modify the DOM.
  713 + // If jQuery is available, uses jQuery's html(), otherwise just innerHTML.
564 714 reset: function() {
  715 + var fixture;
  716 +
565 717 if ( window.jQuery ) {
566 718 jQuery( "#qunit-fixture" ).html( config.fixture );
567 719 } else {
568   - var main = id( 'qunit-fixture' );
569   - if ( main ) {
570   - main.innerHTML = config.fixture;
  720 + fixture = id( "qunit-fixture" );
  721 + if ( fixture ) {
  722 + fixture.innerHTML = config.fixture;
571 723 }
572 724 }
573 725 },
574 726
575   - /**
576   - * Trigger an event on an element.
577   - *
578   - * @example triggerEvent( document.body, "click" );
579   - *
580   - * @param DOMElement elem
581   - * @param String type
582   - */
  727 + // Trigger an event on an element.
  728 + // @example triggerEvent( document.body, "click" );
583 729 triggerEvent: function( elem, type, event ) {
584 730 if ( document.createEvent ) {
585   - event = document.createEvent("MouseEvents");
  731 + event = document.createEvent( "MouseEvents" );
586 732 event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
587 733 0, 0, 0, 0, 0, false, false, false, false, 0, null);
588   - elem.dispatchEvent( event );
589 734
  735 + elem.dispatchEvent( event );
590 736 } else if ( elem.fireEvent ) {
591   - elem.fireEvent("on"+type);
  737 + elem.fireEvent( "on" + type );
592 738 }
593 739 },
594 740
@@ -598,66 +744,74 @@ extend(QUnit, {
598 744 },
599 745
600 746 objectType: function( obj ) {
601   - if (typeof obj === "undefined") {
  747 + if ( typeof obj === "undefined" ) {
602 748 return "undefined";
603   -
604 749 // consider: typeof null === object
605 750 }
606   - if (obj === null) {
  751 + if ( obj === null ) {
607 752 return "null";
608 753 }
609 754
610   - var type = Object.prototype.toString.call( obj )
611   - .match(/^\[object\s(.*)\]$/)[1] || '';
  755 + var type = toString.call( obj ).match(/^\[object\s(.*)\]$/)[1] || "";
612 756
613   - switch (type) {
614   - case 'Number':
615   - if (isNaN(obj)) {
616   - return "nan";
617   - } else {
618   - return "number";
619   - }
620   - case 'String':
621   - case 'Boolean':
622   - case 'Array':
623   - case 'Date':
624   - case 'RegExp':
625   - case 'Function':
626   - return type.toLowerCase();
  757 + switch ( type ) {
  758 + case "Number":
  759 + if ( isNaN(obj) ) {
  760 + return "nan";
  761 + }
  762 + return "number";
  763 + case "String":
  764 + case "Boolean":
  765 + case "Array":
  766 + case "Date":
  767 + case "RegExp":
  768 + case "Function":
  769 + return type.toLowerCase();
627 770 }
628   - if (typeof obj === "object") {
629   - return "object";
  771 + if ( typeof obj === "object" ) {
  772 + return "object";
630 773 }
631 774 return undefined;
632 775 },
633 776
634   - push: function(result, actual, expected, message) {
635   - var details = {
636   - result: result,
637   - message: message,
638   - actual: actual,
639   - expected: expected
640   - };
  777 + push: function( result, actual, expected, message ) {
  778 + if ( !config.current ) {
  779 + throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
  780 + }
  781 +
  782 + var output, source,
  783 + details = {
  784 + result: result,
  785 + message: message,
  786 + actual: actual,
  787 + expected: expected
  788 + };
  789 +
  790 + message = escapeInnerText( message ) || ( result ? "okay" : "failed" );
  791 + message = "<span class='test-message'>" + message + "</span>";
  792 + output = message;
  793 +
  794 + if ( !result ) {
  795 + expected = escapeInnerText( QUnit.jsDump.parse(expected) );
  796 + actual = escapeInnerText( QUnit.jsDump.parse(actual) );
  797 + output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
  798 +
  799 + if ( actual != expected ) {
  800 + output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
  801 + output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
  802 + }
641 803
642   - message = escapeInnerText(message) || (result ? "okay" : "failed");
643   - message = '<span class="test-message">' + message + "</span>";
644   - expected = escapeInnerText(QUnit.jsDump.parse(expected));
645   - actual = escapeInnerText(QUnit.jsDump.parse(actual));
646   - var output = message + '<table><tr class="test-expected"><th>Expected: </th><td><pre>' + expected + '</pre></td></tr>';
647   - if (actual != expected) {
648   - output += '<tr class="test-actual"><th>Result: </th><td><pre>' + actual + '</pre></td></tr>';
649   - output += '<tr class="test-diff"><th>Diff: </th><td><pre>' + QUnit.diff(expected, actual) +'</pre></td></tr>';
650   - }
651   - if (!result) {
652   - var source = sourceFromStacktrace();
653   - if (source) {
  804 + source = sourceFromStacktrace();
  805 +
  806 + if ( source ) {
654 807 details.source = source;
655   - output += '<tr class="test-source"><th>Source: </th><td><pre>' + escapeInnerText(source) + '</pre></td></tr>';
  808 + output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeInnerText( source ) + "</pre></td></tr>";
656 809 }
  810 +
  811 + output += "</table>";
657 812 }
658   - output += "</table>";
659 813
660   - runLoggingCallbacks( 'log', QUnit, details );
  814 + runLoggingCallbacks( "log", QUnit, details );
661 815
662 816 config.current.assertions.push({
663 817 result: !!result,
@@ -665,11 +819,51 @@ extend(QUnit, {
665 819 });
666 820 },