teaspoon 0.9.1 → 1.0.0

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