teaspoon 0.7.9 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (163) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +382 -260
  3. data/app/assets/javascripts/teaspoon-angular.js +108 -26241
  4. data/app/assets/javascripts/teaspoon-jasmine.js +103 -2642
  5. data/app/assets/javascripts/teaspoon-mocha.js +109 -5416
  6. data/app/assets/javascripts/teaspoon-qunit.js +107 -2255
  7. data/app/assets/javascripts/teaspoon-teaspoon.js +0 -1
  8. data/app/assets/javascripts/teaspoon/angular.coffee +3 -1
  9. data/app/assets/javascripts/teaspoon/base/hook.coffee +21 -0
  10. data/app/assets/javascripts/teaspoon/base/reporters/html.coffee +26 -14
  11. data/app/assets/javascripts/teaspoon/base/reporters/html/progress_view.coffee +1 -1
  12. data/app/assets/javascripts/teaspoon/base/reporters/html/template.coffee +3 -3
  13. data/app/assets/javascripts/teaspoon/base/teaspoon.coffee +10 -1
  14. data/app/assets/javascripts/teaspoon/jasmine.coffee +3 -1
  15. data/app/assets/javascripts/teaspoon/mocha.coffee +3 -1
  16. data/app/assets/javascripts/teaspoon/mocha/reporters/html.coffee +1 -1
  17. data/app/assets/javascripts/teaspoon/qunit.coffee +3 -1
  18. data/app/assets/javascripts/teaspoon/qunit/reporters/html.coffee +1 -1
  19. data/app/assets/javascripts/teaspoon/teaspoon.coffee +0 -1
  20. data/app/assets/stylesheets/teaspoon.css +12 -8
  21. data/app/controllers/teaspoon/suite_controller.rb +32 -0
  22. data/app/views/teaspoon/suite/_body.html.erb +0 -0
  23. data/app/views/teaspoon/suite/_boot.html.erb +4 -0
  24. data/app/views/teaspoon/suite/_boot_require_js.html.erb +19 -0
  25. data/app/views/teaspoon/{spec/suites.html.erb → suite/index.html.erb} +6 -7
  26. data/app/views/teaspoon/suite/show.html.erb +19 -0
  27. data/bin/teaspoon +1 -1
  28. data/config/routes.rb +14 -4
  29. data/lib/generators/teaspoon/install/POST_INSTALL +2 -2
  30. data/lib/generators/teaspoon/install/install_generator.rb +22 -11
  31. data/lib/generators/teaspoon/install/templates/_body.html.erb +0 -0
  32. data/lib/generators/teaspoon/install/templates/_boot.html.erb +4 -0
  33. data/lib/generators/teaspoon/install/templates/jasmine/env.rb +11 -0
  34. data/lib/generators/teaspoon/install/templates/jasmine/env_comments.rb +182 -0
  35. data/lib/generators/teaspoon/install/templates/jasmine/spec_helper.coffee +8 -6
  36. data/lib/generators/teaspoon/install/templates/jasmine/spec_helper.js +8 -7
  37. data/lib/generators/teaspoon/install/templates/mocha/env.rb +11 -0
  38. data/lib/generators/teaspoon/install/templates/mocha/env_comments.rb +182 -0
  39. data/lib/generators/teaspoon/install/templates/mocha/spec_helper.coffee +13 -13
  40. data/lib/generators/teaspoon/install/templates/mocha/spec_helper.js +13 -13
  41. data/lib/generators/teaspoon/install/templates/qunit/env.rb +11 -0
  42. data/lib/generators/teaspoon/install/templates/qunit/env_comments.rb +182 -0
  43. data/lib/generators/teaspoon/install/templates/qunit/test_helper.coffee +6 -5
  44. data/lib/generators/teaspoon/install/templates/qunit/test_helper.js +6 -5
  45. data/lib/tasks/teaspoon.rake +9 -2
  46. data/lib/teaspoon.rb +4 -6
  47. data/lib/teaspoon/command_line.rb +116 -134
  48. data/lib/teaspoon/configuration.rb +144 -66
  49. data/lib/teaspoon/console.rb +70 -37
  50. data/lib/teaspoon/coverage.rb +42 -15
  51. data/lib/teaspoon/deprecated.rb +65 -0
  52. data/lib/teaspoon/drivers/base.rb +10 -0
  53. data/lib/teaspoon/drivers/phantomjs/runner.js +9 -11
  54. data/lib/teaspoon/drivers/phantomjs_driver.rb +21 -21
  55. data/lib/teaspoon/drivers/selenium_driver.rb +32 -13
  56. data/lib/teaspoon/engine.rb +32 -12
  57. data/lib/teaspoon/environment.rb +16 -12
  58. data/lib/teaspoon/exceptions.rb +41 -5
  59. data/lib/teaspoon/exporter.rb +52 -0
  60. data/lib/teaspoon/formatters/base.rb +171 -0
  61. data/lib/teaspoon/formatters/clean_formatter.rb +2 -4
  62. data/lib/teaspoon/formatters/documentation_formatter.rb +60 -0
  63. data/lib/teaspoon/formatters/dot_formatter.rb +12 -90
  64. data/lib/teaspoon/formatters/json_formatter.rb +36 -0
  65. data/lib/teaspoon/formatters/junit_formatter.rb +51 -32
  66. data/lib/teaspoon/formatters/modules/report_module.rb +76 -0
  67. data/lib/teaspoon/formatters/pride_formatter.rb +23 -27
  68. data/lib/teaspoon/formatters/snowday_formatter.rb +7 -11
  69. data/lib/teaspoon/formatters/swayze_or_oprah_formatter.rb +88 -64
  70. data/lib/teaspoon/formatters/tap_formatter.rb +18 -27
  71. data/lib/teaspoon/formatters/tap_y_formatter.rb +35 -45
  72. data/lib/teaspoon/formatters/teamcity_formatter.rb +69 -31
  73. data/lib/teaspoon/instrumentation.rb +33 -33
  74. data/lib/teaspoon/result.rb +2 -1
  75. data/lib/teaspoon/runner.rb +40 -28
  76. data/lib/teaspoon/server.rb +23 -25
  77. data/lib/teaspoon/suite.rb +52 -72
  78. data/lib/teaspoon/utility.rb +3 -14
  79. data/lib/teaspoon/version.rb +1 -1
  80. data/spec/dummy/app/assets/javascripts/integration/integration_spec.coffee +3 -0
  81. data/spec/dummy/app/assets/javascripts/integration/spec_helper.coffee +2 -0
  82. data/spec/dummy/config/application.rb +3 -0
  83. data/spec/features/console_reporter_spec.rb +48 -18
  84. data/spec/features/hooks_spec.rb +23 -41
  85. data/spec/features/html_reporter_spec.rb +38 -21
  86. data/spec/features/install_generator_spec.rb +34 -20
  87. data/spec/features/instrumentation_spec.rb +3 -2
  88. data/spec/fixtures/coverage.json +243 -0
  89. data/spec/javascripts/fixtures/_body.html.erb +1 -0
  90. data/spec/javascripts/jasmine_helper.coffee +1 -1
  91. data/spec/javascripts/teaspoon/base/fixture_spec.coffee +4 -4
  92. data/spec/javascripts/teaspoon/base/reporters/html_spec.coffee +9 -10
  93. data/spec/javascripts/teaspoon/mocha/reporters/html_mspec.coffee +0 -6
  94. data/spec/javascripts/teaspoon/phantomjs/runner_spec.coffee +5 -6
  95. data/spec/javascripts/turbolinks_helper.coffee +1 -1
  96. data/spec/spec_helper.rb +3 -4
  97. data/spec/teaspoon/command_line_spec.rb +139 -23
  98. data/spec/teaspoon/configuration_spec.rb +164 -46
  99. data/spec/teaspoon/console_spec.rb +142 -47
  100. data/spec/teaspoon/coverage_spec.rb +98 -28
  101. data/spec/teaspoon/drivers/base_spec.rb +5 -0
  102. data/spec/teaspoon/drivers/phantomjs_driver_spec.rb +32 -14
  103. data/spec/teaspoon/drivers/selenium_driver_spec.rb +32 -24
  104. data/spec/teaspoon/engine_spec.rb +8 -5
  105. data/spec/teaspoon/environment_spec.rb +56 -33
  106. data/spec/teaspoon/exceptions_spec.rb +57 -0
  107. data/spec/teaspoon/exporter_spec.rb +96 -0
  108. data/spec/teaspoon/formatters/base_spec.rb +259 -0
  109. data/spec/teaspoon/formatters/clean_formatter_spec.rb +37 -0
  110. data/spec/teaspoon/formatters/documentation_formatter_spec.rb +127 -0
  111. data/spec/teaspoon/formatters/dot_formatter_spec.rb +52 -56
  112. data/spec/teaspoon/formatters/json_formatter_spec.rb +77 -0
  113. data/spec/teaspoon/formatters/junit_formatter_spec.rb +72 -35
  114. data/spec/teaspoon/formatters/pride_formatter_spec.rb +37 -0
  115. data/spec/teaspoon/formatters/snowday_formatter_spec.rb +35 -0
  116. data/spec/teaspoon/formatters/tap_formatter_spec.rb +29 -81
  117. data/spec/teaspoon/formatters/tap_y_formatter_spec.rb +31 -141
  118. data/spec/teaspoon/formatters/teamcity_formatter_spec.rb +99 -42
  119. data/spec/teaspoon/instrumentation_spec.rb +44 -44
  120. data/spec/teaspoon/result_spec.rb +37 -0
  121. data/spec/teaspoon/runner_spec.rb +70 -59
  122. data/spec/teaspoon/server_spec.rb +34 -52
  123. data/spec/teaspoon/suite_spec.rb +42 -188
  124. data/spec/teaspoon_env.rb +39 -28
  125. data/vendor/assets/javascripts/{angular-scenario-1.0.5.js → angular/1.0.5.js} +0 -0
  126. data/vendor/assets/javascripts/{angular-scenario-1.0.5.MIT-LICENSE → angular/MIT-LICENSE} +0 -0
  127. data/vendor/assets/javascripts/{jasmine-1.3.1.js → jasmine/1.3.1.js} +0 -0
  128. data/vendor/assets/javascripts/jasmine/2.0.0.js +2412 -0
  129. data/vendor/assets/javascripts/{jasmine-1.3.1.MIT.LICENSE → jasmine/MIT.LICENSE} +0 -0
  130. data/vendor/assets/javascripts/{mocha-1.10.0.js → mocha/1.10.0.js} +1 -0
  131. data/vendor/assets/javascripts/mocha/1.17.1.js +5813 -0
  132. data/vendor/assets/javascripts/{mocha-1.10.1.MIT.LICENSE → mocha/MIT.LICENSE} +0 -0
  133. data/vendor/assets/javascripts/{qunit-1.12.0.js → qunit/1.12.0.js} +1 -1
  134. data/vendor/assets/javascripts/qunit/1.14.0.js +2288 -0
  135. data/vendor/assets/javascripts/{qunit-1.12.0.MIT.LICENSE → qunit/MIT.LICENSE} +0 -0
  136. data/vendor/assets/javascripts/support/chai.js +827 -385
  137. data/vendor/assets/javascripts/support/jasmine-jquery-1.7.0.js +720 -0
  138. data/vendor/assets/javascripts/support/jasmine-jquery-2.0.0.js +812 -0
  139. data/vendor/assets/javascripts/support/sinon-chai.js +17 -0
  140. data/vendor/assets/javascripts/support/sinon.js +1138 -643
  141. metadata +57 -36
  142. data/app/controllers/teaspoon/spec_controller.rb +0 -38
  143. data/app/helpers/teaspoon/spec_helper.rb +0 -36
  144. data/app/views/teaspoon/spec/_require_js.html.erb +0 -21
  145. data/app/views/teaspoon/spec/_standard.html.erb +0 -4
  146. data/app/views/teaspoon/spec/runner.html.erb +0 -19
  147. data/lib/generators/teaspoon/install/templates/env.rb +0 -38
  148. data/lib/generators/teaspoon/install/templates/jasmine/initializer.rb +0 -64
  149. data/lib/generators/teaspoon/install/templates/mocha/initializer.rb +0 -64
  150. data/lib/generators/teaspoon/install/templates/qunit/initializer.rb +0 -64
  151. data/lib/teaspoon/check_coverage.rb +0 -33
  152. data/lib/teaspoon/drivers/base_driver.rb +0 -10
  153. data/lib/teaspoon/exception_handling.rb +0 -18
  154. data/lib/teaspoon/formatters/base_formatter.rb +0 -63
  155. data/spec/dummy/config/initializers/teaspoon.rb +0 -41
  156. data/spec/teaspoon/check_coverage_spec.rb +0 -50
  157. data/spec/teaspoon/formatters/base_formatter_spec.rb +0 -45
  158. data/vendor/assets/javascripts/support/chai.MIT.LICENSE +0 -22
  159. data/vendor/assets/javascripts/support/expect.MIT.LICENSE +0 -22
  160. data/vendor/assets/javascripts/support/jasmine-jquery.MIT.LICENSE +0 -20
  161. data/vendor/assets/javascripts/support/jasmine-jquery.js +0 -659
  162. data/vendor/assets/javascripts/support/sinon-chai.MIT-ISH.LICENSE +0 -13
  163. data/vendor/assets/javascripts/support/sinon.BSD.LICENSE +0 -27
@@ -1,2217 +1,7 @@
1
- /**
2
- * QUnit v1.12.0 - A JavaScript Unit Testing Framework
3
- *
4
- * http://qunitjs.com
5
- *
6
- * Copyright 2013 jQuery Foundation and other contributors
7
- * Released under the MIT license.
8
- * https://jquery.org/license/
9
- */
10
-
11
-
12
- (function( window ) {
13
-
14
- var QUnit,
15
- assert,
16
- config,
17
- onErrorFnPrev,
18
- testId = 0,
19
- fileName = (sourceFromStacktrace( 0 ) || "" ).replace(/(:\d+)+\)?/, "").replace(/.+\//, ""),
20
- toString = Object.prototype.toString,
21
- hasOwn = Object.prototype.hasOwnProperty,
22
- // Keep a local reference to Date (GH-283)
23
- Date = window.Date,
24
- setTimeout = window.setTimeout,
25
- defined = {
26
- setTimeout: typeof window.setTimeout !== "undefined",
27
- sessionStorage: (function() {
28
- var x = "qunit-test-string";
29
- try {
30
- sessionStorage.setItem( x, x );
31
- sessionStorage.removeItem( x );
32
- return true;
33
- } catch( e ) {
34
- return false;
35
- }
36
- }())
37
- },
38
- /**
39
- * Provides a normalized error string, correcting an issue
40
- * with IE 7 (and prior) where Error.prototype.toString is
41
- * not properly implemented
42
- *
43
- * Based on http://es5.github.com/#x15.11.4.4
44
- *
45
- * @param {String|Error} error
46
- * @return {String} error message
47
- */
48
- errorString = function( error ) {
49
- var name, message,
50
- errorString = error.toString();
51
- if ( errorString.substring( 0, 7 ) === "[object" ) {
52
- name = error.name ? error.name.toString() : "Error";
53
- message = error.message ? error.message.toString() : "";
54
- if ( name && message ) {
55
- return name + ": " + message;
56
- } else if ( name ) {
57
- return name;
58
- } else if ( message ) {
59
- return message;
60
- } else {
61
- return "Error";
62
- }
63
- } else {
64
- return errorString;
65
- }
66
- },
67
- /**
68
- * Makes a clone of an object using only Array or Object as base,
69
- * and copies over the own enumerable properties.
70
- *
71
- * @param {Object} obj
72
- * @return {Object} New object with only the own properties (recursively).
73
- */
74
- objectValues = function( obj ) {
75
- // Grunt 0.3.x uses an older version of jshint that still has jshint/jshint#392.
76
- /*jshint newcap: false */
77
- var key, val,
78
- vals = QUnit.is( "array", obj ) ? [] : {};
79
- for ( key in obj ) {
80
- if ( hasOwn.call( obj, key ) ) {
81
- val = obj[key];
82
- vals[key] = val === Object(val) ? objectValues(val) : val;
83
- }
84
- }
85
- return vals;
86
- };
87
-
88
- function Test( settings ) {
89
- extend( this, settings );
90
- this.assertions = [];
91
- this.testNumber = ++Test.count;
92
- }
93
-
94
- Test.count = 0;
95
-
96
- Test.prototype = {
97
- init: function() {
98
- var a, b, li,
99
- tests = id( "qunit-tests" );
100
-
101
- if ( tests ) {
102
- b = document.createElement( "strong" );
103
- b.innerHTML = this.nameHtml;
104
-
105
- // `a` initialized at top of scope
106
- a = document.createElement( "a" );
107
- a.innerHTML = "Rerun";
108
- a.href = QUnit.url({ testNumber: this.testNumber });
109
-
110
- li = document.createElement( "li" );
111
- li.appendChild( b );
112
- li.appendChild( a );
113
- li.className = "running";
114
- li.id = this.id = "qunit-test-output" + testId++;
115
-
116
- tests.appendChild( li );
117
- }
118
- },
119
- setup: function() {
120
- if (
121
- // Emit moduleStart when we're switching from one module to another
122
- this.module !== config.previousModule ||
123
- // They could be equal (both undefined) but if the previousModule property doesn't
124
- // yet exist it means this is the first test in a suite that isn't wrapped in a
125
- // module, in which case we'll just emit a moduleStart event for 'undefined'.
126
- // Without this, reporters can get testStart before moduleStart which is a problem.
127
- !hasOwn.call( config, "previousModule" )
128
- ) {
129
- if ( hasOwn.call( config, "previousModule" ) ) {
130
- runLoggingCallbacks( "moduleDone", QUnit, {
131
- name: config.previousModule,
132
- failed: config.moduleStats.bad,
133
- passed: config.moduleStats.all - config.moduleStats.bad,
134
- total: config.moduleStats.all
135
- });
136
- }
137
- config.previousModule = this.module;
138
- config.moduleStats = { all: 0, bad: 0 };
139
- runLoggingCallbacks( "moduleStart", QUnit, {
140
- name: this.module
141
- });
142
- }
143
-
144
- config.current = this;
145
-
146
- this.testEnvironment = extend({
147
- setup: function() {},
148
- teardown: function() {}
149
- }, this.moduleTestEnvironment );
150
-
151
- this.started = +new Date();
152
- runLoggingCallbacks( "testStart", QUnit, {
153
- name: this.testName,
154
- module: this.module
155
- });
156
-
157
- /*jshint camelcase:false */
158
-
159
-
160
- /**
161
- * Expose the current test environment.
162
- *
163
- * @deprecated since 1.12.0: Use QUnit.config.current.testEnvironment instead.
164
- */
165
- QUnit.current_testEnvironment = this.testEnvironment;
166
-
167
- /*jshint camelcase:true */
168
-
169
- if ( !config.pollution ) {
170
- saveGlobal();
171
- }
172
- if ( config.notrycatch ) {
173
- this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
174
- return;
175
- }
176
- try {
177
- this.testEnvironment.setup.call( this.testEnvironment, QUnit.assert );
178
- } catch( e ) {
179
- QUnit.pushFailure( "Setup failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
180
- }
181
- },
182
- run: function() {
183
- config.current = this;
184
-
185
- var running = id( "qunit-testresult" );
186
-
187
- if ( running ) {
188
- running.innerHTML = "Running: <br/>" + this.nameHtml;
189
- }
190
-
191
- if ( this.async ) {
192
- QUnit.stop();
193
- }
194
-
195
- this.callbackStarted = +new Date();
196
-
197
- if ( config.notrycatch ) {
198
- this.callback.call( this.testEnvironment, QUnit.assert );
199
- this.callbackRuntime = +new Date() - this.callbackStarted;
200
- return;
201
- }
202
-
203
- try {
204
- this.callback.call( this.testEnvironment, QUnit.assert );
205
- this.callbackRuntime = +new Date() - this.callbackStarted;
206
- } catch( e ) {
207
- this.callbackRuntime = +new Date() - this.callbackStarted;
208
-
209
- QUnit.pushFailure( "Died on test #" + (this.assertions.length + 1) + " " + this.stack + ": " + ( e.message || e ), extractStacktrace( e, 0 ) );
210
- // else next test will carry the responsibility
211
- saveGlobal();
212
-
213
- // Restart the tests if they're blocking
214
- if ( config.blocking ) {
215
- QUnit.start();
216
- }
217
- }
218
- },
219
- teardown: function() {
220
- config.current = this;
221
- if ( config.notrycatch ) {
222
- if ( typeof this.callbackRuntime === "undefined" ) {
223
- this.callbackRuntime = +new Date() - this.callbackStarted;
224
- }
225
- this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
226
- return;
227
- } else {
228
- try {
229
- this.testEnvironment.teardown.call( this.testEnvironment, QUnit.assert );
230
- } catch( e ) {
231
- QUnit.pushFailure( "Teardown failed on " + this.testName + ": " + ( e.message || e ), extractStacktrace( e, 1 ) );
232
- }
233
- }
234
- checkPollution();
235
- },
236
- finish: function() {
237
- config.current = this;
238
- if ( config.requireExpects && this.expected === null ) {
239
- QUnit.pushFailure( "Expected number of assertions to be defined, but expect() was not called.", this.stack );
240
- } else if ( this.expected !== null && this.expected !== this.assertions.length ) {
241
- QUnit.pushFailure( "Expected " + this.expected + " assertions, but " + this.assertions.length + " were run", this.stack );
242
- } else if ( this.expected === null && !this.assertions.length ) {
243
- QUnit.pushFailure( "Expected at least one assertion, but none were run - call expect(0) to accept zero assertions.", this.stack );
244
- }
245
-
246
- var i, assertion, a, b, time, li, ol,
247
- test = this,
248
- good = 0,
249
- bad = 0,
250
- tests = id( "qunit-tests" );
251
-
252
- this.runtime = +new Date() - this.started;
253
- config.stats.all += this.assertions.length;
254
- config.moduleStats.all += this.assertions.length;
255
-
256
- if ( tests ) {
257
- ol = document.createElement( "ol" );
258
- ol.className = "qunit-assert-list";
259
-
260
- for ( i = 0; i < this.assertions.length; i++ ) {
261
- assertion = this.assertions[i];
262
-
263
- li = document.createElement( "li" );
264
- li.className = assertion.result ? "pass" : "fail";
265
- li.innerHTML = assertion.message || ( assertion.result ? "okay" : "failed" );
266
- ol.appendChild( li );
267
-
268
- if ( assertion.result ) {
269
- good++;
270
- } else {
271
- bad++;
272
- config.stats.bad++;
273
- config.moduleStats.bad++;
274
- }
275
- }
276
-
277
- // store result when possible
278
- if ( QUnit.config.reorder && defined.sessionStorage ) {
279
- if ( bad ) {
280
- sessionStorage.setItem( "qunit-test-" + this.module + "-" + this.testName, bad );
281
- } else {
282
- sessionStorage.removeItem( "qunit-test-" + this.module + "-" + this.testName );
283
- }
284
- }
285
-
286
- if ( bad === 0 ) {
287
- addClass( ol, "qunit-collapsed" );
288
- }
289
-
290
- // `b` initialized at top of scope
291
- b = document.createElement( "strong" );
292
- b.innerHTML = this.nameHtml + " <b class='counts'>(<b class='failed'>" + bad + "</b>, <b class='passed'>" + good + "</b>, " + this.assertions.length + ")</b>";
293
-
294
- addEvent(b, "click", function() {
295
- var next = b.parentNode.lastChild,
296
- collapsed = hasClass( next, "qunit-collapsed" );
297
- ( collapsed ? removeClass : addClass )( next, "qunit-collapsed" );
298
- });
299
-
300
- addEvent(b, "dblclick", function( e ) {
301
- var target = e && e.target ? e.target : window.event.srcElement;
302
- if ( target.nodeName.toLowerCase() === "span" || target.nodeName.toLowerCase() === "b" ) {
303
- target = target.parentNode;
304
- }
305
- if ( window.location && target.nodeName.toLowerCase() === "strong" ) {
306
- window.location = QUnit.url({ testNumber: test.testNumber });
307
- }
308
- });
309
-
310
- // `time` initialized at top of scope
311
- time = document.createElement( "span" );
312
- time.className = "runtime";
313
- time.innerHTML = this.runtime + " ms";
314
-
315
- // `li` initialized at top of scope
316
- li = id( this.id );
317
- li.className = bad ? "fail" : "pass";
318
- li.removeChild( li.firstChild );
319
- a = li.firstChild;
320
- li.appendChild( b );
321
- li.appendChild( a );
322
- li.appendChild( time );
323
- li.appendChild( ol );
324
-
325
- } else {
326
- for ( i = 0; i < this.assertions.length; i++ ) {
327
- if ( !this.assertions[i].result ) {
328
- bad++;
329
- config.stats.bad++;
330
- config.moduleStats.bad++;
331
- }
332
- }
333
- }
334
-
335
- runLoggingCallbacks( "testDone", QUnit, {
336
- name: this.testName,
337
- module: this.module,
338
- failed: bad,
339
- passed: this.assertions.length - bad,
340
- total: this.assertions.length,
341
- duration: this.runtime
342
- });
343
-
344
- QUnit.reset();
345
-
346
- config.current = undefined;
347
- },
348
-
349
- queue: function() {
350
- var bad,
351
- test = this;
352
-
353
- synchronize(function() {
354
- test.init();
355
- });
356
- function run() {
357
- // each of these can by async
358
- synchronize(function() {
359
- test.setup();
360
- });
361
- synchronize(function() {
362
- test.run();
363
- });
364
- synchronize(function() {
365
- test.teardown();
366
- });
367
- synchronize(function() {
368
- test.finish();
369
- });
370
- }
371
-
372
- // `bad` initialized at top of scope
373
- // defer when previous test run passed, if storage is available
374
- bad = QUnit.config.reorder && defined.sessionStorage &&
375
- +sessionStorage.getItem( "qunit-test-" + this.module + "-" + this.testName );
376
-
377
- if ( bad ) {
378
- run();
379
- } else {
380
- synchronize( run, true );
381
- }
382
- }
383
- };
384
-
385
- // Root QUnit object.
386
- // `QUnit` initialized at top of scope
387
- QUnit = {
388
-
389
- // call on start of module test to prepend name to all tests
390
- module: function( name, testEnvironment ) {
391
- config.currentModule = name;
392
- config.currentModuleTestEnvironment = testEnvironment;
393
- config.modules[name] = true;
394
- },
395
-
396
- asyncTest: function( testName, expected, callback ) {
397
- if ( arguments.length === 2 ) {
398
- callback = expected;
399
- expected = null;
400
- }
401
-
402
- QUnit.test( testName, expected, callback, true );
403
- },
404
-
405
- test: function( testName, expected, callback, async ) {
406
- var test,
407
- nameHtml = "<span class='test-name'>" + escapeText( testName ) + "</span>";
408
-
409
- if ( arguments.length === 2 ) {
410
- callback = expected;
411
- expected = null;
412
- }
413
-
414
- if ( config.currentModule ) {
415
- nameHtml = "<span class='module-name'>" + escapeText( config.currentModule ) + "</span>: " + nameHtml;
416
- }
417
-
418
- test = new Test({
419
- nameHtml: nameHtml,
420
- testName: testName,
421
- expected: expected,
422
- async: async,
423
- callback: callback,
424
- module: config.currentModule,
425
- moduleTestEnvironment: config.currentModuleTestEnvironment,
426
- stack: sourceFromStacktrace( 2 )
427
- });
428
-
429
- if ( !validTest( test ) ) {
430
- return;
431
- }
432
-
433
- test.queue();
434
- },
435
-
436
- // Specify the number of expected assertions to guarantee that failed test (no assertions are run at all) don't slip through.
437
- expect: function( asserts ) {
438
- if (arguments.length === 1) {
439
- config.current.expected = asserts;
440
- } else {
441
- return config.current.expected;
442
- }
443
- },
444
-
445
- start: function( count ) {
446
- // QUnit hasn't been initialized yet.
447
- // Note: RequireJS (et al) may delay onLoad
448
- if ( config.semaphore === undefined ) {
449
- QUnit.begin(function() {
450
- // This is triggered at the top of QUnit.load, push start() to the event loop, to allow QUnit.load to finish first
451
- setTimeout(function() {
452
- QUnit.start( count );
453
- });
454
- });
455
- return;
456
- }
457
-
458
- config.semaphore -= count || 1;
459
- // don't start until equal number of stop-calls
460
- if ( config.semaphore > 0 ) {
461
- return;
462
- }
463
- // ignore if start is called more often then stop
464
- if ( config.semaphore < 0 ) {
465
- config.semaphore = 0;
466
- QUnit.pushFailure( "Called start() while already started (QUnit.config.semaphore was 0 already)", null, sourceFromStacktrace(2) );
467
- return;
468
- }
469
- // A slight delay, to avoid any current callbacks
470
- if ( defined.setTimeout ) {
471
- setTimeout(function() {
472
- if ( config.semaphore > 0 ) {
473
- return;
474
- }
475
- if ( config.timeout ) {
476
- clearTimeout( config.timeout );
477
- }
478
-
479
- config.blocking = false;
480
- process( true );
481
- }, 13);
482
- } else {
483
- config.blocking = false;
484
- process( true );
485
- }
486
- },
487
-
488
- stop: function( count ) {
489
- config.semaphore += count || 1;
490
- config.blocking = true;
491
-
492
- if ( config.testTimeout && defined.setTimeout ) {
493
- clearTimeout( config.timeout );
494
- config.timeout = setTimeout(function() {
495
- QUnit.ok( false, "Test timed out" );
496
- config.semaphore = 1;
497
- QUnit.start();
498
- }, config.testTimeout );
499
- }
500
- }
501
- };
502
-
503
- // `assert` initialized at top of scope
504
- // Assert helpers
505
- // All of these must either call QUnit.push() or manually do:
506
- // - runLoggingCallbacks( "log", .. );
507
- // - config.current.assertions.push({ .. });
508
- // We attach it to the QUnit object *after* we expose the public API,
509
- // otherwise `assert` will become a global variable in browsers (#341).
510
- assert = {
511
- /**
512
- * Asserts rough true-ish result.
513
- * @name ok
514
- * @function
515
- * @example ok( "asdfasdf".length > 5, "There must be at least 5 chars" );
516
- */
517
- ok: function( result, msg ) {
518
- if ( !config.current ) {
519
- throw new Error( "ok() assertion outside test context, was " + sourceFromStacktrace(2) );
520
- }
521
- result = !!result;
522
- msg = msg || (result ? "okay" : "failed" );
523
-
524
- var source,
525
- details = {
526
- module: config.current.module,
527
- name: config.current.testName,
528
- result: result,
529
- message: msg
530
- };
531
-
532
- msg = "<span class='test-message'>" + escapeText( msg ) + "</span>";
533
-
534
- if ( !result ) {
535
- source = sourceFromStacktrace( 2 );
536
- if ( source ) {
537
- details.source = source;
538
- msg += "<table><tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr></table>";
539
- }
540
- }
541
- runLoggingCallbacks( "log", QUnit, details );
542
- config.current.assertions.push({
543
- result: result,
544
- message: msg
545
- });
546
- },
547
-
548
- /**
549
- * Assert that the first two arguments are equal, with an optional message.
550
- * Prints out both actual and expected values.
551
- * @name equal
552
- * @function
553
- * @example equal( format( "Received {0} bytes.", 2), "Received 2 bytes.", "format() replaces {0} with next argument" );
554
- */
555
- equal: function( actual, expected, message ) {
556
- /*jshint eqeqeq:false */
557
- QUnit.push( expected == actual, actual, expected, message );
558
- },
559
-
560
- /**
561
- * @name notEqual
562
- * @function
563
- */
564
- notEqual: function( actual, expected, message ) {
565
- /*jshint eqeqeq:false */
566
- QUnit.push( expected != actual, actual, expected, message );
567
- },
568
-
569
- /**
570
- * @name propEqual
571
- * @function
572
- */
573
- propEqual: function( actual, expected, message ) {
574
- actual = objectValues(actual);
575
- expected = objectValues(expected);
576
- QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
577
- },
578
-
579
- /**
580
- * @name notPropEqual
581
- * @function
582
- */
583
- notPropEqual: function( actual, expected, message ) {
584
- actual = objectValues(actual);
585
- expected = objectValues(expected);
586
- QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
587
- },
588
-
589
- /**
590
- * @name deepEqual
591
- * @function
592
- */
593
- deepEqual: function( actual, expected, message ) {
594
- QUnit.push( QUnit.equiv(actual, expected), actual, expected, message );
595
- },
596
-
597
- /**
598
- * @name notDeepEqual
599
- * @function
600
- */
601
- notDeepEqual: function( actual, expected, message ) {
602
- QUnit.push( !QUnit.equiv(actual, expected), actual, expected, message );
603
- },
604
-
605
- /**
606
- * @name strictEqual
607
- * @function
608
- */
609
- strictEqual: function( actual, expected, message ) {
610
- QUnit.push( expected === actual, actual, expected, message );
611
- },
612
-
613
- /**
614
- * @name notStrictEqual
615
- * @function
616
- */
617
- notStrictEqual: function( actual, expected, message ) {
618
- QUnit.push( expected !== actual, actual, expected, message );
619
- },
620
-
621
- "throws": function( block, expected, message ) {
622
- var actual,
623
- expectedOutput = expected,
624
- ok = false;
625
-
626
- // 'expected' is optional
627
- if ( typeof expected === "string" ) {
628
- message = expected;
629
- expected = null;
630
- }
631
-
632
- config.current.ignoreGlobalErrors = true;
633
- try {
634
- block.call( config.current.testEnvironment );
635
- } catch (e) {
636
- actual = e;
637
- }
638
- config.current.ignoreGlobalErrors = false;
639
-
640
- if ( actual ) {
641
- // we don't want to validate thrown error
642
- if ( !expected ) {
643
- ok = true;
644
- expectedOutput = null;
645
- // expected is a regexp
646
- } else if ( QUnit.objectType( expected ) === "regexp" ) {
647
- ok = expected.test( errorString( actual ) );
648
- // expected is a constructor
649
- } else if ( actual instanceof expected ) {
650
- ok = true;
651
- // expected is a validation function which returns true is validation passed
652
- } else if ( expected.call( {}, actual ) === true ) {
653
- expectedOutput = null;
654
- ok = true;
655
- }
656
-
657
- QUnit.push( ok, actual, expectedOutput, message );
658
- } else {
659
- QUnit.pushFailure( message, null, "No exception was thrown." );
660
- }
661
- }
662
- };
663
-
664
- /**
665
- * @deprecated since 1.8.0
666
- * Kept assertion helpers in root for backwards compatibility.
667
- */
668
- extend( QUnit, assert );
669
-
670
- /**
671
- * @deprecated since 1.9.0
672
- * Kept root "raises()" for backwards compatibility.
673
- * (Note that we don't introduce assert.raises).
674
- */
675
- QUnit.raises = assert[ "throws" ];
676
-
677
- /**
678
- * @deprecated since 1.0.0, replaced with error pushes since 1.3.0
679
- * Kept to avoid TypeErrors for undefined methods.
680
- */
681
- QUnit.equals = function() {
682
- QUnit.push( false, false, false, "QUnit.equals has been deprecated since 2009 (e88049a0), use QUnit.equal instead" );
683
- };
684
- QUnit.same = function() {
685
- QUnit.push( false, false, false, "QUnit.same has been deprecated since 2009 (e88049a0), use QUnit.deepEqual instead" );
686
- };
687
-
688
- // We want access to the constructor's prototype
689
- (function() {
690
- function F() {}
691
- F.prototype = QUnit;
692
- QUnit = new F();
693
- // Make F QUnit's constructor so that we can add to the prototype later
694
- QUnit.constructor = F;
695
- }());
696
-
697
- /**
698
- * Config object: Maintain internal state
699
- * Later exposed as QUnit.config
700
- * `config` initialized at top of scope
701
- */
702
- config = {
703
- // The queue of tests to run
704
- queue: [],
705
-
706
- // block until document ready
707
- blocking: true,
708
-
709
- // when enabled, show only failing tests
710
- // gets persisted through sessionStorage and can be changed in UI via checkbox
711
- hidepassed: false,
712
-
713
- // by default, run previously failed tests first
714
- // very useful in combination with "Hide passed tests" checked
715
- reorder: true,
716
-
717
- // by default, modify document.title when suite is done
718
- altertitle: true,
719
-
720
- // when enabled, all tests must call expect()
721
- requireExpects: false,
722
-
723
- // add checkboxes that are persisted in the query-string
724
- // when enabled, the id is set to `true` as a `QUnit.config` property
725
- urlConfig: [
726
- {
727
- id: "noglobals",
728
- label: "Check for Globals",
729
- tooltip: "Enabling this will test if any test introduces new properties on the `window` object. Stored as query-strings."
730
- },
731
- {
732
- id: "notrycatch",
733
- label: "No try-catch",
734
- tooltip: "Enabling this will run tests outside of a try-catch block. Makes debugging exceptions in IE reasonable. Stored as query-strings."
735
- }
736
- ],
737
-
738
- // Set of all modules.
739
- modules: {},
740
-
741
- // logging callback queues
742
- begin: [],
743
- done: [],
744
- log: [],
745
- testStart: [],
746
- testDone: [],
747
- moduleStart: [],
748
- moduleDone: []
749
- };
750
-
751
- // Export global variables, unless an 'exports' object exists,
752
- // in that case we assume we're in CommonJS (dealt with on the bottom of the script)
753
- if ( typeof exports === "undefined" ) {
754
- extend( window, QUnit.constructor.prototype );
755
-
756
- // Expose QUnit object
757
- window.QUnit = QUnit;
758
- }
759
-
760
- // Initialize more QUnit.config and QUnit.urlParams
761
- (function() {
762
- var i,
763
- location = window.location || { search: "", protocol: "file:" },
764
- params = location.search.slice( 1 ).split( "&" ),
765
- length = params.length,
766
- urlParams = {},
767
- current;
768
-
769
- if ( params[ 0 ] ) {
770
- for ( i = 0; i < length; i++ ) {
771
- current = params[ i ].split( "=" );
772
- current[ 0 ] = decodeURIComponent( current[ 0 ] );
773
- // allow just a key to turn on a flag, e.g., test.html?noglobals
774
- current[ 1 ] = current[ 1 ] ? decodeURIComponent( current[ 1 ] ) : true;
775
- urlParams[ current[ 0 ] ] = current[ 1 ];
776
- }
777
- }
778
-
779
- QUnit.urlParams = urlParams;
780
-
781
- // String search anywhere in moduleName+testName
782
- config.filter = urlParams.filter;
783
-
784
- // Exact match of the module name
785
- config.module = urlParams.module;
786
-
787
- config.testNumber = parseInt( urlParams.testNumber, 10 ) || null;
788
-
789
- // Figure out if we're running the tests from a server or not
790
- QUnit.isLocal = location.protocol === "file:";
791
- }());
792
-
793
- // Extend QUnit object,
794
- // these after set here because they should not be exposed as global functions
795
- extend( QUnit, {
796
- assert: assert,
797
-
798
- config: config,
799
-
800
- // Initialize the configuration options
801
- init: function() {
802
- extend( config, {
803
- stats: { all: 0, bad: 0 },
804
- moduleStats: { all: 0, bad: 0 },
805
- started: +new Date(),
806
- updateRate: 1000,
807
- blocking: false,
808
- autostart: true,
809
- autorun: false,
810
- filter: "",
811
- queue: [],
812
- semaphore: 1
813
- });
814
-
815
- var tests, banner, result,
816
- qunit = id( "qunit" );
817
-
818
- if ( qunit ) {
819
- qunit.innerHTML =
820
- "<h1 id='qunit-header'>" + escapeText( document.title ) + "</h1>" +
821
- "<h2 id='qunit-banner'></h2>" +
822
- "<div id='qunit-testrunner-toolbar'></div>" +
823
- "<h2 id='qunit-userAgent'></h2>" +
824
- "<ol id='qunit-tests'></ol>";
825
- }
826
-
827
- tests = id( "qunit-tests" );
828
- banner = id( "qunit-banner" );
829
- result = id( "qunit-testresult" );
830
-
831
- if ( tests ) {
832
- tests.innerHTML = "";
833
- }
834
-
835
- if ( banner ) {
836
- banner.className = "";
837
- }
838
-
839
- if ( result ) {
840
- result.parentNode.removeChild( result );
841
- }
842
-
843
- if ( tests ) {
844
- result = document.createElement( "p" );
845
- result.id = "qunit-testresult";
846
- result.className = "result";
847
- tests.parentNode.insertBefore( result, tests );
848
- result.innerHTML = "Running...<br/>&nbsp;";
849
- }
850
- },
851
-
852
- // Resets the test setup. Useful for tests that modify the DOM.
853
- /*
854
- DEPRECATED: Use multiple tests instead of resetting inside a test.
855
- Use testStart or testDone for custom cleanup.
856
- This method will throw an error in 2.0, and will be removed in 2.1
857
- */
858
- reset: function() {
859
- var fixture = id( "qunit-fixture" );
860
- if ( fixture ) {
861
- fixture.innerHTML = config.fixture;
862
- }
863
- },
864
-
865
- // Trigger an event on an element.
866
- // @example triggerEvent( document.body, "click" );
867
- triggerEvent: function( elem, type, event ) {
868
- if ( document.createEvent ) {
869
- event = document.createEvent( "MouseEvents" );
870
- event.initMouseEvent(type, true, true, elem.ownerDocument.defaultView,
871
- 0, 0, 0, 0, 0, false, false, false, false, 0, null);
872
-
873
- elem.dispatchEvent( event );
874
- } else if ( elem.fireEvent ) {
875
- elem.fireEvent( "on" + type );
876
- }
877
- },
878
-
879
- // Safe object type checking
880
- is: function( type, obj ) {
881
- return QUnit.objectType( obj ) === type;
882
- },
883
-
884
- objectType: function( obj ) {
885
- if ( typeof obj === "undefined" ) {
886
- return "undefined";
887
- // consider: typeof null === object
888
- }
889
- if ( obj === null ) {
890
- return "null";
891
- }
892
-
893
- var match = toString.call( obj ).match(/^\[object\s(.*)\]$/),
894
- type = match && match[1] || "";
895
-
896
- switch ( type ) {
897
- case "Number":
898
- if ( isNaN(obj) ) {
899
- return "nan";
900
- }
901
- return "number";
902
- case "String":
903
- case "Boolean":
904
- case "Array":
905
- case "Date":
906
- case "RegExp":
907
- case "Function":
908
- return type.toLowerCase();
909
- }
910
- if ( typeof obj === "object" ) {
911
- return "object";
912
- }
913
- return undefined;
914
- },
915
-
916
- push: function( result, actual, expected, message ) {
917
- if ( !config.current ) {
918
- throw new Error( "assertion outside test context, was " + sourceFromStacktrace() );
919
- }
920
-
921
- var output, source,
922
- details = {
923
- module: config.current.module,
924
- name: config.current.testName,
925
- result: result,
926
- message: message,
927
- actual: actual,
928
- expected: expected
929
- };
930
-
931
- message = escapeText( message ) || ( result ? "okay" : "failed" );
932
- message = "<span class='test-message'>" + message + "</span>";
933
- output = message;
934
-
935
- if ( !result ) {
936
- expected = escapeText( QUnit.jsDump.parse(expected) );
937
- actual = escapeText( QUnit.jsDump.parse(actual) );
938
- output += "<table><tr class='test-expected'><th>Expected: </th><td><pre>" + expected + "</pre></td></tr>";
939
-
940
- if ( actual !== expected ) {
941
- output += "<tr class='test-actual'><th>Result: </th><td><pre>" + actual + "</pre></td></tr>";
942
- output += "<tr class='test-diff'><th>Diff: </th><td><pre>" + QUnit.diff( expected, actual ) + "</pre></td></tr>";
943
- }
944
-
945
- source = sourceFromStacktrace();
946
-
947
- if ( source ) {
948
- details.source = source;
949
- output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
950
- }
951
-
952
- output += "</table>";
953
- }
954
-
955
- runLoggingCallbacks( "log", QUnit, details );
956
-
957
- config.current.assertions.push({
958
- result: !!result,
959
- message: output
960
- });
961
- },
962
-
963
- pushFailure: function( message, source, actual ) {
964
- if ( !config.current ) {
965
- throw new Error( "pushFailure() assertion outside test context, was " + sourceFromStacktrace(2) );
966
- }
967
-
968
- var output,
969
- details = {
970
- module: config.current.module,
971
- name: config.current.testName,
972
- result: false,
973
- message: message
974
- };
975
-
976
- message = escapeText( message ) || "error";
977
- message = "<span class='test-message'>" + message + "</span>";
978
- output = message;
979
-
980
- output += "<table>";
981
-
982
- if ( actual ) {
983
- output += "<tr class='test-actual'><th>Result: </th><td><pre>" + escapeText( actual ) + "</pre></td></tr>";
984
- }
985
-
986
- if ( source ) {
987
- details.source = source;
988
- output += "<tr class='test-source'><th>Source: </th><td><pre>" + escapeText( source ) + "</pre></td></tr>";
989
- }
990
-
991
- output += "</table>";
992
-
993
- runLoggingCallbacks( "log", QUnit, details );
994
-
995
- config.current.assertions.push({
996
- result: false,
997
- message: output
998
- });
999
- },
1000
-
1001
- url: function( params ) {
1002
- params = extend( extend( {}, QUnit.urlParams ), params );
1003
- var key,
1004
- querystring = "?";
1005
-
1006
- for ( key in params ) {
1007
- if ( hasOwn.call( params, key ) ) {
1008
- querystring += encodeURIComponent( key ) + "=" +
1009
- encodeURIComponent( params[ key ] ) + "&";
1010
- }
1011
- }
1012
- return window.location.protocol + "//" + window.location.host +
1013
- window.location.pathname + querystring.slice( 0, -1 );
1014
- },
1015
-
1016
- extend: extend,
1017
- id: id,
1018
- addEvent: addEvent,
1019
- addClass: addClass,
1020
- hasClass: hasClass,
1021
- removeClass: removeClass
1022
- // load, equiv, jsDump, diff: Attached later
1023
- });
1024
-
1025
- /**
1026
- * @deprecated: Created for backwards compatibility with test runner that set the hook function
1027
- * into QUnit.{hook}, instead of invoking it and passing the hook function.
1028
- * QUnit.constructor is set to the empty F() above so that we can add to it's prototype here.
1029
- * Doing this allows us to tell if the following methods have been overwritten on the actual
1030
- * QUnit object.
1031
- */
1032
- extend( QUnit.constructor.prototype, {
1033
-
1034
- // Logging callbacks; all receive a single argument with the listed properties
1035
- // run test/logs.html for any related changes
1036
- begin: registerLoggingCallback( "begin" ),
1037
-
1038
- // done: { failed, passed, total, runtime }
1039
- done: registerLoggingCallback( "done" ),
1040
-
1041
- // log: { result, actual, expected, message }
1042
- log: registerLoggingCallback( "log" ),
1043
-
1044
- // testStart: { name }
1045
- testStart: registerLoggingCallback( "testStart" ),
1046
-
1047
- // testDone: { name, failed, passed, total, duration }
1048
- testDone: registerLoggingCallback( "testDone" ),
1049
-
1050
- // moduleStart: { name }
1051
- moduleStart: registerLoggingCallback( "moduleStart" ),
1052
-
1053
- // moduleDone: { name, failed, passed, total }
1054
- moduleDone: registerLoggingCallback( "moduleDone" )
1055
- });
1056
-
1057
- if ( typeof document === "undefined" || document.readyState === "complete" ) {
1058
- config.autorun = true;
1059
- }
1060
-
1061
- QUnit.load = function() {
1062
- runLoggingCallbacks( "begin", QUnit, {} );
1063
-
1064
- // Initialize the config, saving the execution queue
1065
- var banner, filter, i, label, len, main, ol, toolbar, userAgent, val,
1066
- urlConfigCheckboxesContainer, urlConfigCheckboxes, moduleFilter,
1067
- numModules = 0,
1068
- moduleNames = [],
1069
- moduleFilterHtml = "",
1070
- urlConfigHtml = "",
1071
- oldconfig = extend( {}, config );
1072
-
1073
- QUnit.init();
1074
- extend(config, oldconfig);
1075
-
1076
- config.blocking = false;
1077
-
1078
- len = config.urlConfig.length;
1079
-
1080
- for ( i = 0; i < len; i++ ) {
1081
- val = config.urlConfig[i];
1082
- if ( typeof val === "string" ) {
1083
- val = {
1084
- id: val,
1085
- label: val,
1086
- tooltip: "[no tooltip available]"
1087
- };
1088
- }
1089
- config[ val.id ] = QUnit.urlParams[ val.id ];
1090
- urlConfigHtml += "<input id='qunit-urlconfig-" + escapeText( val.id ) +
1091
- "' name='" + escapeText( val.id ) +
1092
- "' type='checkbox'" + ( config[ val.id ] ? " checked='checked'" : "" ) +
1093
- " title='" + escapeText( val.tooltip ) +
1094
- "'><label for='qunit-urlconfig-" + escapeText( val.id ) +
1095
- "' title='" + escapeText( val.tooltip ) + "'>" + val.label + "</label>";
1096
- }
1097
- for ( i in config.modules ) {
1098
- if ( config.modules.hasOwnProperty( i ) ) {
1099
- moduleNames.push(i);
1100
- }
1101
- }
1102
- numModules = moduleNames.length;
1103
- moduleNames.sort( function( a, b ) {
1104
- return a.localeCompare( b );
1105
- });
1106
- moduleFilterHtml += "<label for='qunit-modulefilter'>Module: </label><select id='qunit-modulefilter' name='modulefilter'><option value='' " +
1107
- ( config.module === undefined ? "selected='selected'" : "" ) +
1108
- ">< All Modules ></option>";
1109
-
1110
-
1111
- for ( i = 0; i < numModules; i++) {
1112
- moduleFilterHtml += "<option value='" + escapeText( encodeURIComponent(moduleNames[i]) ) + "' " +
1113
- ( config.module === moduleNames[i] ? "selected='selected'" : "" ) +
1114
- ">" + escapeText(moduleNames[i]) + "</option>";
1115
- }
1116
- moduleFilterHtml += "</select>";
1117
-
1118
- // `userAgent` initialized at top of scope
1119
- userAgent = id( "qunit-userAgent" );
1120
- if ( userAgent ) {
1121
- userAgent.innerHTML = navigator.userAgent;
1122
- }
1123
-
1124
- // `banner` initialized at top of scope
1125
- banner = id( "qunit-header" );
1126
- if ( banner ) {
1127
- banner.innerHTML = "<a href='" + QUnit.url({ filter: undefined, module: undefined, testNumber: undefined }) + "'>" + banner.innerHTML + "</a> ";
1128
- }
1129
-
1130
- // `toolbar` initialized at top of scope
1131
- toolbar = id( "qunit-testrunner-toolbar" );
1132
- if ( toolbar ) {
1133
- // `filter` initialized at top of scope
1134
- filter = document.createElement( "input" );
1135
- filter.type = "checkbox";
1136
- filter.id = "qunit-filter-pass";
1137
-
1138
- addEvent( filter, "click", function() {
1139
- var tmp,
1140
- ol = document.getElementById( "qunit-tests" );
1141
-
1142
- if ( filter.checked ) {
1143
- ol.className = ol.className + " hidepass";
1144
- } else {
1145
- tmp = " " + ol.className.replace( /[\n\t\r]/g, " " ) + " ";
1146
- ol.className = tmp.replace( / hidepass /, " " );
1147
- }
1148
- if ( defined.sessionStorage ) {
1149
- if (filter.checked) {
1150
- sessionStorage.setItem( "qunit-filter-passed-tests", "true" );
1151
- } else {
1152
- sessionStorage.removeItem( "qunit-filter-passed-tests" );
1153
- }
1154
- }
1155
- });
1156
-
1157
- if ( config.hidepassed || defined.sessionStorage && sessionStorage.getItem( "qunit-filter-passed-tests" ) ) {
1158
- filter.checked = true;
1159
- // `ol` initialized at top of scope
1160
- ol = document.getElementById( "qunit-tests" );
1161
- ol.className = ol.className + " hidepass";
1162
- }
1163
- toolbar.appendChild( filter );
1164
-
1165
- // `label` initialized at top of scope
1166
- label = document.createElement( "label" );
1167
- label.setAttribute( "for", "qunit-filter-pass" );
1168
- label.setAttribute( "title", "Only show tests and assertions that fail. Stored in sessionStorage." );
1169
- label.innerHTML = "Hide passed tests";
1170
- toolbar.appendChild( label );
1171
-
1172
- urlConfigCheckboxesContainer = document.createElement("span");
1173
- urlConfigCheckboxesContainer.innerHTML = urlConfigHtml;
1174
- urlConfigCheckboxes = urlConfigCheckboxesContainer.getElementsByTagName("input");
1175
- // For oldIE support:
1176
- // * Add handlers to the individual elements instead of the container
1177
- // * Use "click" instead of "change"
1178
- // * Fallback from event.target to event.srcElement
1179
- addEvents( urlConfigCheckboxes, "click", function( event ) {
1180
- var params = {},
1181
- target = event.target || event.srcElement;
1182
- params[ target.name ] = target.checked ? true : undefined;
1183
- window.location = QUnit.url( params );
1184
- });
1185
- toolbar.appendChild( urlConfigCheckboxesContainer );
1186
-
1187
- if (numModules > 1) {
1188
- moduleFilter = document.createElement( "span" );
1189
- moduleFilter.setAttribute( "id", "qunit-modulefilter-container" );
1190
- moduleFilter.innerHTML = moduleFilterHtml;
1191
- addEvent( moduleFilter.lastChild, "change", function() {
1192
- var selectBox = moduleFilter.getElementsByTagName("select")[0],
1193
- selectedModule = decodeURIComponent(selectBox.options[selectBox.selectedIndex].value);
1194
-
1195
- window.location = QUnit.url({
1196
- module: ( selectedModule === "" ) ? undefined : selectedModule,
1197
- // Remove any existing filters
1198
- filter: undefined,
1199
- testNumber: undefined
1200
- });
1201
- });
1202
- toolbar.appendChild(moduleFilter);
1203
- }
1204
- }
1205
-
1206
- // `main` initialized at top of scope
1207
- main = id( "qunit-fixture" );
1208
- if ( main ) {
1209
- config.fixture = main.innerHTML;
1210
- }
1211
-
1212
- if ( config.autostart ) {
1213
- QUnit.start();
1214
- }
1215
- };
1216
-
1217
- addEvent( window, "load", QUnit.load );
1218
-
1219
- // `onErrorFnPrev` initialized at top of scope
1220
- // Preserve other handlers
1221
- onErrorFnPrev = window.onerror;
1222
-
1223
- // Cover uncaught exceptions
1224
- // Returning true will suppress the default browser handler,
1225
- // returning false will let it run.
1226
- window.onerror = function ( error, filePath, linerNr ) {
1227
- var ret = false;
1228
- if ( onErrorFnPrev ) {
1229
- ret = onErrorFnPrev( error, filePath, linerNr );
1230
- }
1231
-
1232
- // Treat return value as window.onerror itself does,
1233
- // Only do our handling if not suppressed.
1234
- if ( ret !== true ) {
1235
- if ( QUnit.config.current ) {
1236
- if ( QUnit.config.current.ignoreGlobalErrors ) {
1237
- return true;
1238
- }
1239
- QUnit.pushFailure( error, filePath + ":" + linerNr );
1240
- } else {
1241
- QUnit.test( "global failure", extend( function() {
1242
- QUnit.pushFailure( error, filePath + ":" + linerNr );
1243
- }, { validTest: validTest } ) );
1244
- }
1245
- return false;
1246
- }
1247
-
1248
- return ret;
1249
- };
1250
-
1251
- function done() {
1252
- config.autorun = true;
1253
-
1254
- // Log the last module results
1255
- if ( config.currentModule ) {
1256
- runLoggingCallbacks( "moduleDone", QUnit, {
1257
- name: config.currentModule,
1258
- failed: config.moduleStats.bad,
1259
- passed: config.moduleStats.all - config.moduleStats.bad,
1260
- total: config.moduleStats.all
1261
- });
1262
- }
1263
- delete config.previousModule;
1264
-
1265
- var i, key,
1266
- banner = id( "qunit-banner" ),
1267
- tests = id( "qunit-tests" ),
1268
- runtime = +new Date() - config.started,
1269
- passed = config.stats.all - config.stats.bad,
1270
- html = [
1271
- "Tests completed in ",
1272
- runtime,
1273
- " milliseconds.<br/>",
1274
- "<span class='passed'>",
1275
- passed,
1276
- "</span> assertions of <span class='total'>",
1277
- config.stats.all,
1278
- "</span> passed, <span class='failed'>",
1279
- config.stats.bad,
1280
- "</span> failed."
1281
- ].join( "" );
1282
-
1283
- if ( banner ) {
1284
- banner.className = ( config.stats.bad ? "qunit-fail" : "qunit-pass" );
1285
- }
1286
-
1287
- if ( tests ) {
1288
- id( "qunit-testresult" ).innerHTML = html;
1289
- }
1290
-
1291
- if ( config.altertitle && typeof document !== "undefined" && document.title ) {
1292
- // show ✖ for good, ✔ for bad suite result in title
1293
- // use escape sequences in case file gets loaded with non-utf-8-charset
1294
- document.title = [
1295
- ( config.stats.bad ? "\u2716" : "\u2714" ),
1296
- document.title.replace( /^[\u2714\u2716] /i, "" )
1297
- ].join( " " );
1298
- }
1299
-
1300
- // clear own sessionStorage items if all tests passed
1301
- if ( config.reorder && defined.sessionStorage && config.stats.bad === 0 ) {
1302
- // `key` & `i` initialized at top of scope
1303
- for ( i = 0; i < sessionStorage.length; i++ ) {
1304
- key = sessionStorage.key( i++ );
1305
- if ( key.indexOf( "qunit-test-" ) === 0 ) {
1306
- sessionStorage.removeItem( key );
1307
- }
1308
- }
1309
- }
1310
-
1311
- // scroll back to top to show results
1312
- if ( window.scrollTo ) {
1313
- window.scrollTo(0, 0);
1314
- }
1315
-
1316
- runLoggingCallbacks( "done", QUnit, {
1317
- failed: config.stats.bad,
1318
- passed: passed,
1319
- total: config.stats.all,
1320
- runtime: runtime
1321
- });
1322
- }
1323
-
1324
- /** @return Boolean: true if this test should be ran */
1325
- function validTest( test ) {
1326
- var include,
1327
- filter = config.filter && config.filter.toLowerCase(),
1328
- module = config.module && config.module.toLowerCase(),
1329
- fullName = (test.module + ": " + test.testName).toLowerCase();
1330
-
1331
- // Internally-generated tests are always valid
1332
- if ( test.callback && test.callback.validTest === validTest ) {
1333
- delete test.callback.validTest;
1334
- return true;
1335
- }
1336
-
1337
- if ( config.testNumber ) {
1338
- return test.testNumber === config.testNumber;
1339
- }
1340
-
1341
- if ( module && ( !test.module || test.module.toLowerCase() !== module ) ) {
1342
- return false;
1343
- }
1344
-
1345
- if ( !filter ) {
1346
- return true;
1347
- }
1348
-
1349
- include = filter.charAt( 0 ) !== "!";
1350
- if ( !include ) {
1351
- filter = filter.slice( 1 );
1352
- }
1353
-
1354
- // If the filter matches, we need to honour include
1355
- if ( fullName.indexOf( filter ) !== -1 ) {
1356
- return include;
1357
- }
1358
-
1359
- // Otherwise, do the opposite
1360
- return !include;
1361
- }
1362
-
1363
- // so far supports only Firefox, Chrome and Opera (buggy), Safari (for real exceptions)
1364
- // Later Safari and IE10 are supposed to support error.stack as well
1365
- // See also https://developer.mozilla.org/en/JavaScript/Reference/Global_Objects/Error/Stack
1366
- function extractStacktrace( e, offset ) {
1367
- offset = offset === undefined ? 3 : offset;
1368
-
1369
- var stack, include, i;
1370
-
1371
- if ( e.stacktrace ) {
1372
- // Opera
1373
- return e.stacktrace.split( "\n" )[ offset + 3 ];
1374
- } else if ( e.stack ) {
1375
- // Firefox, Chrome
1376
- stack = e.stack.split( "\n" );
1377
- if (/^error$/i.test( stack[0] ) ) {
1378
- stack.shift();
1379
- }
1380
- if ( fileName ) {
1381
- include = [];
1382
- for ( i = offset; i < stack.length; i++ ) {
1383
- if ( stack[ i ].indexOf( fileName ) !== -1 ) {
1384
- break;
1385
- }
1386
- include.push( stack[ i ] );
1387
- }
1388
- if ( include.length ) {
1389
- return include.join( "\n" );
1390
- }
1391
- }
1392
- return stack[ offset ];
1393
- } else if ( e.sourceURL ) {
1394
- // Safari, PhantomJS
1395
- // hopefully one day Safari provides actual stacktraces
1396
- // exclude useless self-reference for generated Error objects
1397
- if ( /qunit.js$/.test( e.sourceURL ) ) {
1398
- return;
1399
- }
1400
- // for actual exceptions, this is useful
1401
- return e.sourceURL + ":" + e.line;
1402
- }
1403
- }
1404
- function sourceFromStacktrace( offset ) {
1405
- try {
1406
- throw new Error();
1407
- } catch ( e ) {
1408
- return extractStacktrace( e, offset );
1409
- }
1410
- }
1411
-
1412
- /**
1413
- * Escape text for attribute or text content.
1414
- */
1415
- function escapeText( s ) {
1416
- if ( !s ) {
1417
- return "";
1418
- }
1419
- s = s + "";
1420
- // Both single quotes and double quotes (for attributes)
1421
- return s.replace( /['"<>&]/g, function( s ) {
1422
- switch( s ) {
1423
- case "'":
1424
- return "&#039;";
1425
- case "\"":
1426
- return "&quot;";
1427
- case "<":
1428
- return "&lt;";
1429
- case ">":
1430
- return "&gt;";
1431
- case "&":
1432
- return "&amp;";
1433
- }
1434
- });
1435
- }
1436
-
1437
- function synchronize( callback, last ) {
1438
- config.queue.push( callback );
1439
-
1440
- if ( config.autorun && !config.blocking ) {
1441
- process( last );
1442
- }
1443
- }
1444
-
1445
- function process( last ) {
1446
- function next() {
1447
- process( last );
1448
- }
1449
- var start = new Date().getTime();
1450
- config.depth = config.depth ? config.depth + 1 : 1;
1451
-
1452
- while ( config.queue.length && !config.blocking ) {
1453
- if ( !defined.setTimeout || config.updateRate <= 0 || ( ( new Date().getTime() - start ) < config.updateRate ) ) {
1454
- config.queue.shift()();
1455
- } else {
1456
- setTimeout( next, 13 );
1457
- break;
1458
- }
1459
- }
1460
- config.depth--;
1461
- if ( last && !config.blocking && !config.queue.length && config.depth === 0 ) {
1462
- done();
1463
- }
1464
- }
1465
-
1466
- function saveGlobal() {
1467
- config.pollution = [];
1468
-
1469
- if ( config.noglobals ) {
1470
- for ( var key in window ) {
1471
- if ( hasOwn.call( window, key ) ) {
1472
- // in Opera sometimes DOM element ids show up here, ignore them
1473
- if ( /^qunit-test-output/.test( key ) ) {
1474
- continue;
1475
- }
1476
- config.pollution.push( key );
1477
- }
1478
- }
1479
- }
1480
- }
1481
-
1482
- function checkPollution() {
1483
- var newGlobals,
1484
- deletedGlobals,
1485
- old = config.pollution;
1486
-
1487
- saveGlobal();
1488
-
1489
- newGlobals = diff( config.pollution, old );
1490
- if ( newGlobals.length > 0 ) {
1491
- QUnit.pushFailure( "Introduced global variable(s): " + newGlobals.join(", ") );
1492
- }
1493
-
1494
- deletedGlobals = diff( old, config.pollution );
1495
- if ( deletedGlobals.length > 0 ) {
1496
- QUnit.pushFailure( "Deleted global variable(s): " + deletedGlobals.join(", ") );
1497
- }
1498
- }
1499
-
1500
- // returns a new Array with the elements that are in a but not in b
1501
- function diff( a, b ) {
1502
- var i, j,
1503
- result = a.slice();
1504
-
1505
- for ( i = 0; i < result.length; i++ ) {
1506
- for ( j = 0; j < b.length; j++ ) {
1507
- if ( result[i] === b[j] ) {
1508
- result.splice( i, 1 );
1509
- i--;
1510
- break;
1511
- }
1512
- }
1513
- }
1514
- return result;
1515
- }
1516
-
1517
- function extend( a, b ) {
1518
- for ( var prop in b ) {
1519
- if ( hasOwn.call( b, prop ) ) {
1520
- // Avoid "Member not found" error in IE8 caused by messing with window.constructor
1521
- if ( !( prop === "constructor" && a === window ) ) {
1522
- if ( b[ prop ] === undefined ) {
1523
- delete a[ prop ];
1524
- } else {
1525
- a[ prop ] = b[ prop ];
1526
- }
1527
- }
1528
- }
1529
- }
1530
-
1531
- return a;
1532
- }
1533
-
1534
- /**
1535
- * @param {HTMLElement} elem
1536
- * @param {string} type
1537
- * @param {Function} fn
1538
- */
1539
- function addEvent( elem, type, fn ) {
1540
- // Standards-based browsers
1541
- if ( elem.addEventListener ) {
1542
- elem.addEventListener( type, fn, false );
1543
- // IE
1544
- } else {
1545
- elem.attachEvent( "on" + type, fn );
1546
- }
1547
- }
1548
-
1549
- /**
1550
- * @param {Array|NodeList} elems
1551
- * @param {string} type
1552
- * @param {Function} fn
1553
- */
1554
- function addEvents( elems, type, fn ) {
1555
- var i = elems.length;
1556
- while ( i-- ) {
1557
- addEvent( elems[i], type, fn );
1558
- }
1559
- }
1560
-
1561
- function hasClass( elem, name ) {
1562
- return (" " + elem.className + " ").indexOf(" " + name + " ") > -1;
1563
- }
1564
-
1565
- function addClass( elem, name ) {
1566
- if ( !hasClass( elem, name ) ) {
1567
- elem.className += (elem.className ? " " : "") + name;
1568
- }
1569
- }
1570
-
1571
- function removeClass( elem, name ) {
1572
- var set = " " + elem.className + " ";
1573
- // Class name may appear multiple times
1574
- while ( set.indexOf(" " + name + " ") > -1 ) {
1575
- set = set.replace(" " + name + " " , " ");
1576
- }
1577
- // If possible, trim it for prettiness, but not necessarily
1578
- elem.className = typeof set.trim === "function" ? set.trim() : set.replace(/^\s+|\s+$/g, "");
1579
- }
1580
-
1581
- function id( name ) {
1582
- return !!( typeof document !== "undefined" && document && document.getElementById ) &&
1583
- document.getElementById( name );
1584
- }
1585
-
1586
- function registerLoggingCallback( key ) {
1587
- return function( callback ) {
1588
- config[key].push( callback );
1589
- };
1590
- }
1591
-
1592
- // Supports deprecated method of completely overwriting logging callbacks
1593
- function runLoggingCallbacks( key, scope, args ) {
1594
- var i, callbacks;
1595
- if ( QUnit.hasOwnProperty( key ) ) {
1596
- QUnit[ key ].call(scope, args );
1597
- } else {
1598
- callbacks = config[ key ];
1599
- for ( i = 0; i < callbacks.length; i++ ) {
1600
- callbacks[ i ].call( scope, args );
1601
- }
1602
- }
1603
- }
1604
-
1605
- // Test for equality any JavaScript type.
1606
- // Author: Philippe Rathé <prathe@gmail.com>
1607
- QUnit.equiv = (function() {
1608
-
1609
- // Call the o related callback with the given arguments.
1610
- function bindCallbacks( o, callbacks, args ) {
1611
- var prop = QUnit.objectType( o );
1612
- if ( prop ) {
1613
- if ( QUnit.objectType( callbacks[ prop ] ) === "function" ) {
1614
- return callbacks[ prop ].apply( callbacks, args );
1615
- } else {
1616
- return callbacks[ prop ]; // or undefined
1617
- }
1618
- }
1619
- }
1620
-
1621
- // the real equiv function
1622
- var innerEquiv,
1623
- // stack to decide between skip/abort functions
1624
- callers = [],
1625
- // stack to avoiding loops from circular referencing
1626
- parents = [],
1627
- parentsB = [],
1628
-
1629
- getProto = Object.getPrototypeOf || function ( obj ) {
1630
- /*jshint camelcase:false */
1631
- return obj.__proto__;
1632
- },
1633
- callbacks = (function () {
1634
-
1635
- // for string, boolean, number and null
1636
- function useStrictEquality( b, a ) {
1637
- /*jshint eqeqeq:false */
1638
- if ( b instanceof a.constructor || a instanceof b.constructor ) {
1639
- // to catch short annotation VS 'new' annotation of a
1640
- // declaration
1641
- // e.g. var i = 1;
1642
- // var j = new Number(1);
1643
- return a == b;
1644
- } else {
1645
- return a === b;
1646
- }
1647
- }
1648
-
1649
- return {
1650
- "string": useStrictEquality,
1651
- "boolean": useStrictEquality,
1652
- "number": useStrictEquality,
1653
- "null": useStrictEquality,
1654
- "undefined": useStrictEquality,
1655
-
1656
- "nan": function( b ) {
1657
- return isNaN( b );
1658
- },
1659
-
1660
- "date": function( b, a ) {
1661
- return QUnit.objectType( b ) === "date" && a.valueOf() === b.valueOf();
1662
- },
1663
-
1664
- "regexp": function( b, a ) {
1665
- return QUnit.objectType( b ) === "regexp" &&
1666
- // the regex itself
1667
- a.source === b.source &&
1668
- // and its modifiers
1669
- a.global === b.global &&
1670
- // (gmi) ...
1671
- a.ignoreCase === b.ignoreCase &&
1672
- a.multiline === b.multiline &&
1673
- a.sticky === b.sticky;
1674
- },
1675
-
1676
- // - skip when the property is a method of an instance (OOP)
1677
- // - abort otherwise,
1678
- // initial === would have catch identical references anyway
1679
- "function": function() {
1680
- var caller = callers[callers.length - 1];
1681
- return caller !== Object && typeof caller !== "undefined";
1682
- },
1683
-
1684
- "array": function( b, a ) {
1685
- var i, j, len, loop, aCircular, bCircular;
1686
-
1687
- // b could be an object literal here
1688
- if ( QUnit.objectType( b ) !== "array" ) {
1689
- return false;
1690
- }
1691
-
1692
- len = a.length;
1693
- if ( len !== b.length ) {
1694
- // safe and faster
1695
- return false;
1696
- }
1697
-
1698
- // track reference to avoid circular references
1699
- parents.push( a );
1700
- parentsB.push( b );
1701
- for ( i = 0; i < len; i++ ) {
1702
- loop = false;
1703
- for ( j = 0; j < parents.length; j++ ) {
1704
- aCircular = parents[j] === a[i];
1705
- bCircular = parentsB[j] === b[i];
1706
- if ( aCircular || bCircular ) {
1707
- if ( a[i] === b[i] || aCircular && bCircular ) {
1708
- loop = true;
1709
- } else {
1710
- parents.pop();
1711
- parentsB.pop();
1712
- return false;
1713
- }
1714
- }
1715
- }
1716
- if ( !loop && !innerEquiv(a[i], b[i]) ) {
1717
- parents.pop();
1718
- parentsB.pop();
1719
- return false;
1720
- }
1721
- }
1722
- parents.pop();
1723
- parentsB.pop();
1724
- return true;
1725
- },
1726
-
1727
- "object": function( b, a ) {
1728
- /*jshint forin:false */
1729
- var i, j, loop, aCircular, bCircular,
1730
- // Default to true
1731
- eq = true,
1732
- aProperties = [],
1733
- bProperties = [];
1734
-
1735
- // comparing constructors is more strict than using
1736
- // instanceof
1737
- if ( a.constructor !== b.constructor ) {
1738
- // Allow objects with no prototype to be equivalent to
1739
- // objects with Object as their constructor.
1740
- if ( !(( getProto(a) === null && getProto(b) === Object.prototype ) ||
1741
- ( getProto(b) === null && getProto(a) === Object.prototype ) ) ) {
1742
- return false;
1743
- }
1744
- }
1745
-
1746
- // stack constructor before traversing properties
1747
- callers.push( a.constructor );
1748
-
1749
- // track reference to avoid circular references
1750
- parents.push( a );
1751
- parentsB.push( b );
1752
-
1753
- // be strict: don't ensure hasOwnProperty and go deep
1754
- for ( i in a ) {
1755
- loop = false;
1756
- for ( j = 0; j < parents.length; j++ ) {
1757
- aCircular = parents[j] === a[i];
1758
- bCircular = parentsB[j] === b[i];
1759
- if ( aCircular || bCircular ) {
1760
- if ( a[i] === b[i] || aCircular && bCircular ) {
1761
- loop = true;
1762
- } else {
1763
- eq = false;
1764
- break;
1765
- }
1766
- }
1767
- }
1768
- aProperties.push(i);
1769
- if ( !loop && !innerEquiv(a[i], b[i]) ) {
1770
- eq = false;
1771
- break;
1772
- }
1773
- }
1774
-
1775
- parents.pop();
1776
- parentsB.pop();
1777
- callers.pop(); // unstack, we are done
1778
-
1779
- for ( i in b ) {
1780
- bProperties.push( i ); // collect b's properties
1781
- }
1782
-
1783
- // Ensures identical properties name
1784
- return eq && innerEquiv( aProperties.sort(), bProperties.sort() );
1785
- }
1786
- };
1787
- }());
1788
-
1789
- innerEquiv = function() { // can take multiple arguments
1790
- var args = [].slice.apply( arguments );
1791
- if ( args.length < 2 ) {
1792
- return true; // end transition
1793
- }
1794
-
1795
- return (function( a, b ) {
1796
- if ( a === b ) {
1797
- return true; // catch the most you can
1798
- } else if ( a === null || b === null || typeof a === "undefined" ||
1799
- typeof b === "undefined" ||
1800
- QUnit.objectType(a) !== QUnit.objectType(b) ) {
1801
- return false; // don't lose time with error prone cases
1802
- } else {
1803
- return bindCallbacks(a, callbacks, [ b, a ]);
1804
- }
1805
-
1806
- // apply transition with (1..n) arguments
1807
- }( args[0], args[1] ) && innerEquiv.apply( this, args.splice(1, args.length - 1 )) );
1808
- };
1809
-
1810
- return innerEquiv;
1811
- }());
1812
-
1813
- /**
1814
- * jsDump Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com |
1815
- * http://flesler.blogspot.com Licensed under BSD
1816
- * (http://www.opensource.org/licenses/bsd-license.php) Date: 5/15/2008
1817
- *
1818
- * @projectDescription Advanced and extensible data dumping for Javascript.
1819
- * @version 1.0.0
1820
- * @author Ariel Flesler
1821
- * @link {http://flesler.blogspot.com/2008/05/jsdump-pretty-dump-of-any-javascript.html}
1822
- */
1823
- QUnit.jsDump = (function() {
1824
- function quote( str ) {
1825
- return "\"" + str.toString().replace( /"/g, "\\\"" ) + "\"";
1826
- }
1827
- function literal( o ) {
1828
- return o + "";
1829
- }
1830
- function join( pre, arr, post ) {
1831
- var s = jsDump.separator(),
1832
- base = jsDump.indent(),
1833
- inner = jsDump.indent(1);
1834
- if ( arr.join ) {
1835
- arr = arr.join( "," + s + inner );
1836
- }
1837
- if ( !arr ) {
1838
- return pre + post;
1839
- }
1840
- return [ pre, inner + arr, base + post ].join(s);
1841
- }
1842
- function array( arr, stack ) {
1843
- var i = arr.length, ret = new Array(i);
1844
- this.up();
1845
- while ( i-- ) {
1846
- ret[i] = this.parse( arr[i] , undefined , stack);
1847
- }
1848
- this.down();
1849
- return join( "[", ret, "]" );
1850
- }
1851
-
1852
- var reName = /^function (\w+)/,
1853
- jsDump = {
1854
- // type is used mostly internally, you can fix a (custom)type in advance
1855
- parse: function( obj, type, stack ) {
1856
- stack = stack || [ ];
1857
- var inStack, res,
1858
- parser = this.parsers[ type || this.typeOf(obj) ];
1859
-
1860
- type = typeof parser;
1861
- inStack = inArray( obj, stack );
1862
-
1863
- if ( inStack !== -1 ) {
1864
- return "recursion(" + (inStack - stack.length) + ")";
1865
- }
1866
- if ( type === "function" ) {
1867
- stack.push( obj );
1868
- res = parser.call( this, obj, stack );
1869
- stack.pop();
1870
- return res;
1871
- }
1872
- return ( type === "string" ) ? parser : this.parsers.error;
1873
- },
1874
- typeOf: function( obj ) {
1875
- var type;
1876
- if ( obj === null ) {
1877
- type = "null";
1878
- } else if ( typeof obj === "undefined" ) {
1879
- type = "undefined";
1880
- } else if ( QUnit.is( "regexp", obj) ) {
1881
- type = "regexp";
1882
- } else if ( QUnit.is( "date", obj) ) {
1883
- type = "date";
1884
- } else if ( QUnit.is( "function", obj) ) {
1885
- type = "function";
1886
- } else if ( typeof obj.setInterval !== undefined && typeof obj.document !== "undefined" && typeof obj.nodeType === "undefined" ) {
1887
- type = "window";
1888
- } else if ( obj.nodeType === 9 ) {
1889
- type = "document";
1890
- } else if ( obj.nodeType ) {
1891
- type = "node";
1892
- } else if (
1893
- // native arrays
1894
- toString.call( obj ) === "[object Array]" ||
1895
- // NodeList objects
1896
- ( typeof obj.length === "number" && typeof obj.item !== "undefined" && ( obj.length ? obj.item(0) === obj[0] : ( obj.item( 0 ) === null && typeof obj[0] === "undefined" ) ) )
1897
- ) {
1898
- type = "array";
1899
- } else if ( obj.constructor === Error.prototype.constructor ) {
1900
- type = "error";
1901
- } else {
1902
- type = typeof obj;
1903
- }
1904
- return type;
1905
- },
1906
- separator: function() {
1907
- return this.multiline ? this.HTML ? "<br />" : "\n" : this.HTML ? "&nbsp;" : " ";
1908
- },
1909
- // extra can be a number, shortcut for increasing-calling-decreasing
1910
- indent: function( extra ) {
1911
- if ( !this.multiline ) {
1912
- return "";
1913
- }
1914
- var chr = this.indentChar;
1915
- if ( this.HTML ) {
1916
- chr = chr.replace( /\t/g, " " ).replace( / /g, "&nbsp;" );
1917
- }
1918
- return new Array( this.depth + ( extra || 0 ) ).join(chr);
1919
- },
1920
- up: function( a ) {
1921
- this.depth += a || 1;
1922
- },
1923
- down: function( a ) {
1924
- this.depth -= a || 1;
1925
- },
1926
- setParser: function( name, parser ) {
1927
- this.parsers[name] = parser;
1928
- },
1929
- // The next 3 are exposed so you can use them
1930
- quote: quote,
1931
- literal: literal,
1932
- join: join,
1933
- //
1934
- depth: 1,
1935
- // This is the list of parsers, to modify them, use jsDump.setParser
1936
- parsers: {
1937
- window: "[Window]",
1938
- document: "[Document]",
1939
- error: function(error) {
1940
- return "Error(\"" + error.message + "\")";
1941
- },
1942
- unknown: "[Unknown]",
1943
- "null": "null",
1944
- "undefined": "undefined",
1945
- "function": function( fn ) {
1946
- var ret = "function",
1947
- // functions never have name in IE
1948
- name = "name" in fn ? fn.name : (reName.exec(fn) || [])[1];
1949
-
1950
- if ( name ) {
1951
- ret += " " + name;
1952
- }
1953
- ret += "( ";
1954
-
1955
- ret = [ ret, QUnit.jsDump.parse( fn, "functionArgs" ), "){" ].join( "" );
1956
- return join( ret, QUnit.jsDump.parse(fn,"functionCode" ), "}" );
1957
- },
1958
- array: array,
1959
- nodelist: array,
1960
- "arguments": array,
1961
- object: function( map, stack ) {
1962
- /*jshint forin:false */
1963
- var ret = [ ], keys, key, val, i;
1964
- QUnit.jsDump.up();
1965
- keys = [];
1966
- for ( key in map ) {
1967
- keys.push( key );
1968
- }
1969
- keys.sort();
1970
- for ( i = 0; i < keys.length; i++ ) {
1971
- key = keys[ i ];
1972
- val = map[ key ];
1973
- ret.push( QUnit.jsDump.parse( key, "key" ) + ": " + QUnit.jsDump.parse( val, undefined, stack ) );
1974
- }
1975
- QUnit.jsDump.down();
1976
- return join( "{", ret, "}" );
1977
- },
1978
- node: function( node ) {
1979
- var len, i, val,
1980
- open = QUnit.jsDump.HTML ? "&lt;" : "<",
1981
- close = QUnit.jsDump.HTML ? "&gt;" : ">",
1982
- tag = node.nodeName.toLowerCase(),
1983
- ret = open + tag,
1984
- attrs = node.attributes;
1985
-
1986
- if ( attrs ) {
1987
- for ( i = 0, len = attrs.length; i < len; i++ ) {
1988
- val = attrs[i].nodeValue;
1989
- // IE6 includes all attributes in .attributes, even ones not explicitly set.
1990
- // Those have values like undefined, null, 0, false, "" or "inherit".
1991
- if ( val && val !== "inherit" ) {
1992
- ret += " " + attrs[i].nodeName + "=" + QUnit.jsDump.parse( val, "attribute" );
1993
- }
1994
- }
1995
- }
1996
- ret += close;
1997
-
1998
- // Show content of TextNode or CDATASection
1999
- if ( node.nodeType === 3 || node.nodeType === 4 ) {
2000
- ret += node.nodeValue;
2001
- }
2002
-
2003
- return ret + open + "/" + tag + close;
2004
- },
2005
- // function calls it internally, it's the arguments part of the function
2006
- functionArgs: function( fn ) {
2007
- var args,
2008
- l = fn.length;
2009
-
2010
- if ( !l ) {
2011
- return "";
2012
- }
2013
-
2014
- args = new Array(l);
2015
- while ( l-- ) {
2016
- // 97 is 'a'
2017
- args[l] = String.fromCharCode(97+l);
2018
- }
2019
- return " " + args.join( ", " ) + " ";
2020
- },
2021
- // object calls it internally, the key part of an item in a map
2022
- key: quote,
2023
- // function calls it internally, it's the content of the function
2024
- functionCode: "[code]",
2025
- // node calls it internally, it's an html attribute value
2026
- attribute: quote,
2027
- string: quote,
2028
- date: quote,
2029
- regexp: literal,
2030
- number: literal,
2031
- "boolean": literal
2032
- },
2033
- // if true, entities are escaped ( <, >, \t, space and \n )
2034
- HTML: false,
2035
- // indentation unit
2036
- indentChar: " ",
2037
- // if true, items in a collection, are separated by a \n, else just a space.
2038
- multiline: true
2039
- };
2040
-
2041
- return jsDump;
2042
- }());
2043
-
2044
- // from jquery.js
2045
- function inArray( elem, array ) {
2046
- if ( array.indexOf ) {
2047
- return array.indexOf( elem );
2048
- }
2049
-
2050
- for ( var i = 0, length = array.length; i < length; i++ ) {
2051
- if ( array[ i ] === elem ) {
2052
- return i;
2053
- }
2054
- }
2055
-
2056
- return -1;
2057
- }
2058
-
2059
- /*
2060
- * Javascript Diff Algorithm
2061
- * By John Resig (http://ejohn.org/)
2062
- * Modified by Chu Alan "sprite"
2063
- *
2064
- * Released under the MIT license.
2065
- *
2066
- * More Info:
2067
- * http://ejohn.org/projects/javascript-diff-algorithm/
2068
- *
2069
- * Usage: QUnit.diff(expected, actual)
2070
- *
2071
- * QUnit.diff( "the quick brown fox jumped over", "the quick fox jumps over" ) == "the quick <del>brown </del> fox <del>jumped </del><ins>jumps </ins> over"
2072
- */
2073
- QUnit.diff = (function() {
2074
- /*jshint eqeqeq:false, eqnull:true */
2075
- function diff( o, n ) {
2076
- var i,
2077
- ns = {},
2078
- os = {};
2079
-
2080
- for ( i = 0; i < n.length; i++ ) {
2081
- if ( !hasOwn.call( ns, n[i] ) ) {
2082
- ns[ n[i] ] = {
2083
- rows: [],
2084
- o: null
2085
- };
2086
- }
2087
- ns[ n[i] ].rows.push( i );
2088
- }
2089
-
2090
- for ( i = 0; i < o.length; i++ ) {
2091
- if ( !hasOwn.call( os, o[i] ) ) {
2092
- os[ o[i] ] = {
2093
- rows: [],
2094
- n: null
2095
- };
2096
- }
2097
- os[ o[i] ].rows.push( i );
2098
- }
2099
-
2100
- for ( i in ns ) {
2101
- if ( hasOwn.call( ns, i ) ) {
2102
- if ( ns[i].rows.length === 1 && hasOwn.call( os, i ) && os[i].rows.length === 1 ) {
2103
- n[ ns[i].rows[0] ] = {
2104
- text: n[ ns[i].rows[0] ],
2105
- row: os[i].rows[0]
2106
- };
2107
- o[ os[i].rows[0] ] = {
2108
- text: o[ os[i].rows[0] ],
2109
- row: ns[i].rows[0]
2110
- };
2111
- }
2112
- }
2113
- }
2114
-
2115
- for ( i = 0; i < n.length - 1; i++ ) {
2116
- if ( n[i].text != null && n[ i + 1 ].text == null && n[i].row + 1 < o.length && o[ n[i].row + 1 ].text == null &&
2117
- n[ i + 1 ] == o[ n[i].row + 1 ] ) {
2118
-
2119
- n[ i + 1 ] = {
2120
- text: n[ i + 1 ],
2121
- row: n[i].row + 1
2122
- };
2123
- o[ n[i].row + 1 ] = {
2124
- text: o[ n[i].row + 1 ],
2125
- row: i + 1
2126
- };
2127
- }
2128
- }
2129
-
2130
- for ( i = n.length - 1; i > 0; i-- ) {
2131
- if ( n[i].text != null && n[ i - 1 ].text == null && n[i].row > 0 && o[ n[i].row - 1 ].text == null &&
2132
- n[ i - 1 ] == o[ n[i].row - 1 ]) {
2133
-
2134
- n[ i - 1 ] = {
2135
- text: n[ i - 1 ],
2136
- row: n[i].row - 1
2137
- };
2138
- o[ n[i].row - 1 ] = {
2139
- text: o[ n[i].row - 1 ],
2140
- row: i - 1
2141
- };
2142
- }
2143
- }
2144
-
2145
- return {
2146
- o: o,
2147
- n: n
2148
- };
2149
- }
2150
-
2151
- return function( o, n ) {
2152
- o = o.replace( /\s+$/, "" );
2153
- n = n.replace( /\s+$/, "" );
2154
-
2155
- var i, pre,
2156
- str = "",
2157
- out = diff( o === "" ? [] : o.split(/\s+/), n === "" ? [] : n.split(/\s+/) ),
2158
- oSpace = o.match(/\s+/g),
2159
- nSpace = n.match(/\s+/g);
2160
-
2161
- if ( oSpace == null ) {
2162
- oSpace = [ " " ];
2163
- }
2164
- else {
2165
- oSpace.push( " " );
2166
- }
2167
-
2168
- if ( nSpace == null ) {
2169
- nSpace = [ " " ];
2170
- }
2171
- else {
2172
- nSpace.push( " " );
2173
- }
2174
-
2175
- if ( out.n.length === 0 ) {
2176
- for ( i = 0; i < out.o.length; i++ ) {
2177
- str += "<del>" + out.o[i] + oSpace[i] + "</del>";
2178
- }
2179
- }
2180
- else {
2181
- if ( out.n[0].text == null ) {
2182
- for ( n = 0; n < out.o.length && out.o[n].text == null; n++ ) {
2183
- str += "<del>" + out.o[n] + oSpace[n] + "</del>";
2184
- }
2185
- }
2186
-
2187
- for ( i = 0; i < out.n.length; i++ ) {
2188
- if (out.n[i].text == null) {
2189
- str += "<ins>" + out.n[i] + nSpace[i] + "</ins>";
2190
- }
2191
- else {
2192
- // `pre` initialized at top of scope
2193
- pre = "";
2194
-
2195
- for ( n = out.n[i].row + 1; n < out.o.length && out.o[n].text == null; n++ ) {
2196
- pre += "<del>" + out.o[n] + oSpace[n] + "</del>";
2197
- }
2198
- str += " " + out.n[i].text + nSpace[i] + pre;
2199
- }
2200
- }
2201
- }
2202
-
2203
- return str;
2204
- };
2205
- }());
2206
-
2207
- // for CommonJS environments, export everything
2208
- if ( typeof exports !== "undefined" ) {
2209
- extend( exports, QUnit.constructor.prototype );
2210
- }
2211
-
2212
- // get at whatever the global object is, like window in browsers
2213
- }( (function() {return this;}.call()) ));
2214
1
  (function() {
2
+ var __hasProp = {}.hasOwnProperty,
3
+ __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
4
+
2215
5
  this.Teaspoon = (function() {
2216
6
  function Teaspoon() {}
2217
7
 
@@ -2219,7 +9,7 @@ if ( typeof exports !== "undefined" ) {
2219
9
 
2220
10
  Teaspoon.slow = 75;
2221
11
 
2222
- Teaspoon.root = null;
12
+ Teaspoon.root = window.location.pathname.replace(/\/+(index\.html)?$/, "").replace(/\/[^\/]*$/, "");
2223
13
 
2224
14
  Teaspoon.started = false;
2225
15
 
@@ -2308,6 +98,18 @@ if ( typeof exports !== "undefined" ) {
2308
98
 
2309
99
  })();
2310
100
 
101
+ Teaspoon.Error = (function(_super) {
102
+ __extends(Error, _super);
103
+
104
+ function Error(message) {
105
+ this.name = "TeaspoonError";
106
+ this.message = message || "";
107
+ }
108
+
109
+ return Error;
110
+
111
+ })(Error);
112
+
2311
113
  }).call(this);
2312
114
  (function() {
2313
115
  Teaspoon.Runner = (function() {
@@ -2325,7 +127,6 @@ if ( typeof exports !== "undefined" ) {
2325
127
 
2326
128
  Runner.prototype.getParams = function() {
2327
129
  var name, param, params, value, _i, _len, _ref, _ref1;
2328
-
2329
130
  params = {};
2330
131
  _ref = Teaspoon.location.search.substring(1).split("&");
2331
132
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
@@ -2372,7 +173,6 @@ if ( typeof exports !== "undefined" ) {
2372
173
 
2373
174
  fixture.preload = function() {
2374
175
  var url, urls, _i, _len, _results;
2375
-
2376
176
  urls = 1 <= arguments.length ? __slice.call(arguments, 0) : [];
2377
177
  _results = [];
2378
178
  for (_i = 0, _len = urls.length; _i < _len; _i++) {
@@ -2384,7 +184,6 @@ if ( typeof exports !== "undefined" ) {
2384
184
 
2385
185
  fixture.load = function() {
2386
186
  var append, index, url, urls, _i, _j, _len, _results;
2387
-
2388
187
  urls = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), append = arguments[_i++];
2389
188
  if (append == null) {
2390
189
  append = false;
@@ -2403,7 +202,6 @@ if ( typeof exports !== "undefined" ) {
2403
202
 
2404
203
  fixture.set = function() {
2405
204
  var append, html, htmls, index, _i, _j, _len, _results;
2406
-
2407
205
  htmls = 2 <= arguments.length ? __slice.call(arguments, 0, _i = arguments.length - 1) : (_i = 0, []), append = arguments[_i++];
2408
206
  if (append == null) {
2409
207
  append = false;
@@ -2436,7 +234,6 @@ if ( typeof exports !== "undefined" ) {
2436
234
 
2437
235
  load = function(url, append, preload) {
2438
236
  var cached, value;
2439
-
2440
237
  if (preload == null) {
2441
238
  preload = false;
2442
239
  }
@@ -2498,7 +295,6 @@ if ( typeof exports !== "undefined" ) {
2498
295
 
2499
296
  create = function() {
2500
297
  var _ref;
2501
-
2502
298
  Teaspoon.fixture.el = document.createElement("div");
2503
299
  if (typeof window.$ === 'function') {
2504
300
  Teaspoon.fixture.$el = $(Teaspoon.fixture.el);
@@ -2509,7 +305,6 @@ if ( typeof exports !== "undefined" ) {
2509
305
 
2510
306
  cleanup = function() {
2511
307
  var _base, _ref, _ref1;
2512
-
2513
308
  (_base = Teaspoon.fixture).el || (_base.el = document.getElementById("teaspoon-fixtures"));
2514
309
  if ((_ref = Teaspoon.fixture.el) != null) {
2515
310
  if ((_ref1 = _ref.parentNode) != null) {
@@ -2521,7 +316,6 @@ if ( typeof exports !== "undefined" ) {
2521
316
 
2522
317
  xhrRequest = function(url, callback) {
2523
318
  var e;
2524
-
2525
319
  if (window.XMLHttpRequest) {
2526
320
  xhr = new XMLHttpRequest();
2527
321
  } else if (window.ActiveXObject) {
@@ -2548,6 +342,47 @@ if ( typeof exports !== "undefined" ) {
2548
342
 
2549
343
  }).call(this);
2550
344
 
345
+ }).call(this);
346
+ (function() {
347
+ Teaspoon.hook = function(name, options) {
348
+ var xhr, xhrRequest;
349
+ if (options == null) {
350
+ options = {};
351
+ }
352
+ xhr = null;
353
+ xhrRequest = function(url, options, callback) {
354
+ var e;
355
+ if (window.XMLHttpRequest) {
356
+ xhr = new XMLHttpRequest();
357
+ } else if (window.ActiveXObject) {
358
+ try {
359
+ xhr = new ActiveXObject("Msxml2.XMLHTTP");
360
+ } catch (_error) {
361
+ e = _error;
362
+ try {
363
+ xhr = new ActiveXObject("Microsoft.XMLHTTP");
364
+ } catch (_error) {
365
+ e = _error;
366
+ }
367
+ }
368
+ }
369
+ if (!xhr) {
370
+ throw "Unable to make Ajax Request";
371
+ }
372
+ xhr.onreadystatechange = callback;
373
+ xhr.open(options['method'] || "GET", "" + Teaspoon.root + "/" + url, false);
374
+ return xhr.send(options['payload']);
375
+ };
376
+ return xhrRequest("" + Teaspoon.suites.active + "/" + name, options, function() {
377
+ if (xhr.readyState !== 4) {
378
+ return;
379
+ }
380
+ if (xhr.status !== 200) {
381
+ throw "Unable to call hook \"" + url + "\".";
382
+ }
383
+ });
384
+ };
385
+
2551
386
  }).call(this);
2552
387
  (function() {
2553
388
  Teaspoon.Reporters.BaseView = (function() {
@@ -2570,7 +405,6 @@ if ( typeof exports !== "undefined" ) {
2570
405
 
2571
406
  BaseView.prototype.createEl = function(type, className) {
2572
407
  var el;
2573
-
2574
408
  if (className == null) {
2575
409
  className = "";
2576
410
  }
@@ -2581,21 +415,18 @@ if ( typeof exports !== "undefined" ) {
2581
415
 
2582
416
  BaseView.prototype.findEl = function(id) {
2583
417
  var _base;
2584
-
2585
418
  this.elements || (this.elements = {});
2586
419
  return (_base = this.elements)[id] || (_base[id] = document.getElementById("teaspoon-" + id));
2587
420
  };
2588
421
 
2589
422
  BaseView.prototype.setText = function(id, value) {
2590
423
  var el;
2591
-
2592
424
  el = this.findEl(id);
2593
425
  return el.innerHTML = value;
2594
426
  };
2595
427
 
2596
428
  BaseView.prototype.setHtml = function(id, value, add) {
2597
429
  var el;
2598
-
2599
430
  if (add == null) {
2600
431
  add = false;
2601
432
  }
@@ -2609,14 +440,12 @@ if ( typeof exports !== "undefined" ) {
2609
440
 
2610
441
  BaseView.prototype.setClass = function(id, value) {
2611
442
  var el;
2612
-
2613
443
  el = this.findEl(id);
2614
444
  return el.className = value;
2615
445
  };
2616
446
 
2617
447
  BaseView.prototype.htmlSafe = function(str) {
2618
448
  var el;
2619
-
2620
449
  el = document.createElement("div");
2621
450
  el.appendChild(document.createTextNode(str));
2622
451
  return el.innerHTML;
@@ -2636,6 +465,7 @@ if ( typeof exports !== "undefined" ) {
2636
465
  __extends(HTML, _super);
2637
466
 
2638
467
  function HTML() {
468
+ this.changeSuite = __bind(this.changeSuite, this);
2639
469
  this.toggleConfig = __bind(this.toggleConfig, this);
2640
470
  this.reportRunnerResults = __bind(this.reportRunnerResults, this);
2641
471
  this.start = new Teaspoon.Date().getTime();
@@ -2671,7 +501,6 @@ if ( typeof exports !== "undefined" ) {
2671
501
  if ((_ref = this.findEl("suite-select")) != null) {
2672
502
  _ref.onchange = this.changeSuite;
2673
503
  }
2674
- this.findEl("root-link").href = Teaspoon.root;
2675
504
  this.el = this.findEl("report-all");
2676
505
  this.showConfiguration();
2677
506
  this.buildProgress();
@@ -2682,20 +511,26 @@ if ( typeof exports !== "undefined" ) {
2682
511
  var el;
2683
512
  el = this.createEl("div");
2684
513
  el.id = "teaspoon-interface";
2685
- el.innerHTML = Teaspoon.Reporters.HTML.template;
514
+ el.innerHTML = Teaspoon.Reporters.HTML.template();
2686
515
  return document.body.appendChild(el);
2687
516
  };
2688
517
 
2689
518
  HTML.prototype.buildSuiteSelect = function() {
2690
- var options, suite, _i, _len, _ref;
519
+ var filename, options, path, selected, suite, _i, _len, _ref;
2691
520
  if (Teaspoon.suites.all.length === 1) {
2692
521
  return "";
2693
522
  }
523
+ filename = "";
524
+ if (/index\.html$/.test(window.location.pathname)) {
525
+ filename = "/index.html";
526
+ }
2694
527
  options = [];
2695
528
  _ref = Teaspoon.suites.all;
2696
529
  for (_i = 0, _len = _ref.length; _i < _len; _i++) {
2697
530
  suite = _ref[_i];
2698
- options.push("<option" + (Teaspoon.suites.active === suite ? " selected='selected'" : "") + " value=\"" + suite + "\">" + suite + "</option>");
531
+ path = [Teaspoon.root, suite].join("/");
532
+ selected = Teaspoon.suites.active === suite ? " selected" : "";
533
+ options.push("<option" + selected + " value=\"" + path + filename + "\">" + suite + "</option>");
2699
534
  }
2700
535
  return "<select id=\"teaspoon-suite-select\">" + (options.join("")) + "</select>";
2701
536
  };
@@ -2806,19 +641,17 @@ if ( typeof exports !== "undefined" ) {
2806
641
  };
2807
642
 
2808
643
  HTML.prototype.setFilters = function() {
2809
- var link;
2810
- link = [Teaspoon.root, Teaspoon.suites.active].join('/');
2811
644
  if (Teaspoon.params["file"]) {
2812
- this.filters.push("by file: " + Teaspoon.params["file"] + " <a href='" + link + "'>remove</a>");
645
+ this.filters.push("by file: " + Teaspoon.params["file"]);
2813
646
  }
2814
647
  if (Teaspoon.params["grep"]) {
2815
- return this.filters.push("by match: " + Teaspoon.params["grep"] + " <a href='" + link + "'>remove</a>");
648
+ return this.filters.push("by match: " + Teaspoon.params["grep"]);
2816
649
  }
2817
650
  };
2818
651
 
2819
652
  HTML.prototype.readConfig = function() {
2820
653
  var config;
2821
- if (config = this.cookie("teaspoon")) {
654
+ if (config = this.store("teaspoon")) {
2822
655
  return this.config = config;
2823
656
  }
2824
657
  };
@@ -2831,16 +664,23 @@ if ( typeof exports !== "undefined" ) {
2831
664
  }
2832
665
  name = button.getAttribute("id").replace(/^teaspoon-/, "");
2833
666
  this.config[name] = !this.config[name];
2834
- this.cookie("teaspoon", this.config);
2835
- return this.refresh();
667
+ this.store("teaspoon", this.config);
668
+ return Teaspoon.reload();
2836
669
  };
2837
670
 
2838
- HTML.prototype.changeSuite = function() {
2839
- return window.location.href = [Teaspoon.root, this.options[this.options.selectedIndex].value].join('/');
671
+ HTML.prototype.changeSuite = function(e) {
672
+ var options;
673
+ options = e.target.options;
674
+ return window.location.href = options[options.selectedIndex].value;
2840
675
  };
2841
676
 
2842
- HTML.prototype.refresh = function() {
2843
- return window.location.href = window.location.href;
677
+ HTML.prototype.store = function(name, value) {
678
+ var _ref;
679
+ if (((_ref = window.localStorage) != null ? _ref.setItem : void 0) != null) {
680
+ return this.localstore(name, value);
681
+ } else {
682
+ return this.cookie(name, value);
683
+ }
2844
684
  };
2845
685
 
2846
686
  HTML.prototype.cookie = function(name, value) {
@@ -2859,6 +699,17 @@ if ( typeof exports !== "undefined" ) {
2859
699
  }
2860
700
  };
2861
701
 
702
+ HTML.prototype.localstore = function(name, value) {
703
+ if (value == null) {
704
+ value = void 0;
705
+ }
706
+ if (value === void 0) {
707
+ return JSON.parse(unescape(localStorage.getItem(name)));
708
+ } else {
709
+ return localStorage.setItem(name, escape(JSON.stringify(value)));
710
+ }
711
+ };
712
+
2862
713
  return HTML;
2863
714
 
2864
715
  })(Teaspoon.Reporters.BaseView);
@@ -2892,7 +743,7 @@ if ( typeof exports !== "undefined" ) {
2892
743
  };
2893
744
 
2894
745
  ProgressView.prototype.build = function() {
2895
- return this.el = this.createEl("div", "teaspoon-indicator modeset-logo");
746
+ return this.el = this.createEl("div", "teaspoon-indicator teaspoon-logo");
2896
747
  };
2897
748
 
2898
749
  ProgressView.prototype.update = function() {};
@@ -2916,7 +767,6 @@ if ( typeof exports !== "undefined" ) {
2916
767
 
2917
768
  SimpleProgressView.prototype.update = function(total, run) {
2918
769
  var percent;
2919
-
2920
770
  percent = total ? Math.ceil((run * 100) / total) : 0;
2921
771
  return this.setHtml("progress-percent", "" + percent + "%");
2922
772
  };
@@ -2942,7 +792,6 @@ if ( typeof exports !== "undefined" ) {
2942
792
 
2943
793
  RadialProgressView.prototype.appendTo = function() {
2944
794
  var canvas, e;
2945
-
2946
795
  RadialProgressView.__super__.appendTo.apply(this, arguments);
2947
796
  this.size = 80;
2948
797
  try {
@@ -2958,7 +807,6 @@ if ( typeof exports !== "undefined" ) {
2958
807
 
2959
808
  RadialProgressView.prototype.update = function(total, run) {
2960
809
  var half, percent;
2961
-
2962
810
  percent = total ? Math.ceil((run * 100) / total) : 0;
2963
811
  this.setHtml("progress-percent", "" + percent + "%");
2964
812
  if (!this.ctx) {
@@ -3150,7 +998,9 @@ if ( typeof exports !== "undefined" ) {
3150
998
 
3151
999
  }).call(this);
3152
1000
  (function() {
3153
- Teaspoon.Reporters.HTML.template = "<div class=\"teaspoon-clearfix\">\n <div id=\"teaspoon-title\">\n <h1><a href=\"\" id=\"teaspoon-root-link\">Teaspoon</a></h1>\n <ul>\n <li>version: <b id=\"teaspoon-version\"></b></li>\n <li id=\"teaspoon-env-info\"></li>\n </ul>\n </div>\n <div id=\"teaspoon-progress\"></div>\n <ul id=\"teaspoon-stats\">\n <li>passes: <b id=\"teaspoon-stats-passes\">0</b></li>\n <li>failures: <b id=\"teaspoon-stats-failures\">0</b></li>\n <li>skipped: <b id=\"teaspoon-stats-skipped\">0</b></li>\n <li>duration: <b id=\"teaspoon-stats-duration\">&infin;</b></li>\n </ul>\n</div>\n\n<div id=\"teaspoon-controls\" class=\"teaspoon-clearfix\">\n <div id=\"teaspoon-toggles\">\n <button id=\"teaspoon-use-catch\" title=\"Toggle using try/catch wrappers when possible\">Try/Catch</button>\n <button id=\"teaspoon-build-full-report\" title=\"Toggle building the full report\">Full Report</button>\n <button id=\"teaspoon-display-progress\" title=\"Toggle displaying progress as tests run\">Progress</button>\n </div>\n <div id=\"teaspoon-suites\"></div>\n</div>\n\n<hr/>\n\n<div id=\"teaspoon-filter\">\n <h1>Filtering</h1>\n <ul id=\"teaspoon-filter-list\"></ul>\n</div>\n\n<div id=\"teaspoon-report\">\n <ol id=\"teaspoon-report-failures\"></ol>\n <ol id=\"teaspoon-report-all\"></ol>\n</div>";
1001
+ Teaspoon.Reporters.HTML.template = function() {
1002
+ return "<div class=\"teaspoon-clearfix\">\n <div id=\"teaspoon-title\">\n <h1><a href=\"" + Teaspoon.root + "\" id=\"teaspoon-root-link\">Teaspoon</a></h1>\n <ul>\n <li>version: <b id=\"teaspoon-version\"></b></li>\n <li id=\"teaspoon-env-info\"></li>\n </ul>\n </div>\n <div id=\"teaspoon-progress\"></div>\n <ul id=\"teaspoon-stats\">\n <li>passes: <b id=\"teaspoon-stats-passes\">0</b></li>\n <li>failures: <b id=\"teaspoon-stats-failures\">0</b></li>\n <li>skipped: <b id=\"teaspoon-stats-skipped\">0</b></li>\n <li>duration: <b id=\"teaspoon-stats-duration\">&infin;</b></li>\n </ul>\n</div>\n\n<div id=\"teaspoon-controls\" class=\"teaspoon-clearfix\">\n <div id=\"teaspoon-toggles\">\n <button id=\"teaspoon-use-catch\" title=\"Toggle using try/catch wrappers when possible\">Try/Catch</button>\n <button id=\"teaspoon-build-full-report\" title=\"Toggle building the full report\">Full Report</button>\n <button id=\"teaspoon-display-progress\" title=\"Toggle displaying progress as tests run\">Progress</button>\n </div>\n <div id=\"teaspoon-suites\"></div>\n</div>\n\n<hr/>\n\n<div id=\"teaspoon-filter\">\n <h1>Applied Filters [<a href=\"" + window.location.pathname + "\" id=\"teaspoon-filter-clear\">remove</a>]</h1>\n <ul id=\"teaspoon-filter-list\"></ul>\n</div>\n\n<div id=\"teaspoon-report\">\n <ol id=\"teaspoon-report-failures\"></ol>\n <ol id=\"teaspoon-report-all\"></ol>\n</div>";
1003
+ };
3154
1004
 
3155
1005
  }).call(this);
3156
1006
  (function() {
@@ -3158,7 +1008,8 @@ if ( typeof exports !== "undefined" ) {
3158
1008
 
3159
1009
  Teaspoon.Reporters.Console = (function() {
3160
1010
  function Console() {
3161
- this.reportRunnerResults = __bind(this.reportRunnerResults, this); this.start = new Teaspoon.Date();
1011
+ this.reportRunnerResults = __bind(this.reportRunnerResults, this);
1012
+ this.start = new Teaspoon.Date();
3162
1013
  this.suites = {};
3163
1014
  }
3164
1015
 
@@ -3172,7 +1023,6 @@ if ( typeof exports !== "undefined" ) {
3172
1023
 
3173
1024
  Console.prototype.reportSuites = function() {
3174
1025
  var index, suite, _i, _len, _ref, _results;
3175
-
3176
1026
  _ref = this.spec.getParents();
3177
1027
  _results = [];
3178
1028
  for (index = _i = 0, _len = _ref.length; _i < _len; index = ++_i) {
@@ -3192,7 +1042,6 @@ if ( typeof exports !== "undefined" ) {
3192
1042
 
3193
1043
  Console.prototype.reportSpecResults = function(spec) {
3194
1044
  var result;
3195
-
3196
1045
  this.spec = new Teaspoon.Spec(spec);
3197
1046
  result = this.spec.result();
3198
1047
  if (result.skipped) {
@@ -3217,7 +1066,6 @@ if ( typeof exports !== "undefined" ) {
3217
1066
 
3218
1067
  Console.prototype.trackPending = function() {
3219
1068
  var result;
3220
-
3221
1069
  result = this.spec.result();
3222
1070
  return this.log({
3223
1071
  type: "spec",
@@ -3230,7 +1078,6 @@ if ( typeof exports !== "undefined" ) {
3230
1078
 
3231
1079
  Console.prototype.trackFailure = function() {
3232
1080
  var error, result, _i, _len, _ref, _results;
3233
-
3234
1081
  result = this.spec.result();
3235
1082
  _ref = this.spec.errors();
3236
1083
  _results = [];
@@ -3281,7 +1128,8 @@ if ( typeof exports !== "undefined" ) {
3281
1128
  __extends(Console, _super);
3282
1129
 
3283
1130
  function Console(env) {
3284
- this.reportSpecResults = __bind(this.reportSpecResults, this); Console.__super__.constructor.apply(this, arguments);
1131
+ this.reportSpecResults = __bind(this.reportSpecResults, this);
1132
+ Console.__super__.constructor.apply(this, arguments);
3285
1133
  env.log(this.reportSpecResults);
3286
1134
  env.testDone(this.reportSpecResults);
3287
1135
  env.done(this.reportRunnerResults);
@@ -3359,7 +1207,7 @@ if ( typeof exports !== "undefined" ) {
3359
1207
  };
3360
1208
 
3361
1209
  HTML.prototype.envInfo = function() {
3362
- return "qunit 1.11.0";
1210
+ return "qunit " + (_qunit_version || "[unknown version]");
3363
1211
  };
3364
1212
 
3365
1213
  return HTML;
@@ -3450,6 +1298,10 @@ if ( typeof exports !== "undefined" ) {
3450
1298
  __hasProp = {}.hasOwnProperty,
3451
1299
  __extends = function(child, parent) { for (var key in parent) { if (__hasProp.call(parent, key)) child[key] = parent[key]; } function ctor() { this.constructor = child; } ctor.prototype = parent.prototype; child.prototype = new ctor(); child.__super__ = parent.prototype; return child; };
3452
1300
 
1301
+ if (typeof QUnit === "undefined" || QUnit === null) {
1302
+ throw new Teaspoon.Error('QUnit not found -- use `suite.use_framework :qunit` and adjust or remove the `suite.javascripts` directive.');
1303
+ }
1304
+
3453
1305
  Teaspoon.Runner = (function(_super) {
3454
1306
  __extends(Runner, _super);
3455
1307