ruby_css_lint 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (118) hide show
  1. data/.document +5 -0
  2. data/Gemfile +13 -0
  3. data/LICENSE.txt +20 -0
  4. data/README.rdoc +19 -0
  5. data/Rakefile +71 -0
  6. data/VERSION +1 -0
  7. data/csslint/CHANGELOG +286 -0
  8. data/csslint/LICENSE +20 -0
  9. data/csslint/README.md +25 -0
  10. data/csslint/build.xml +242 -0
  11. data/csslint/demos/CSSLintDemo.htm +105 -0
  12. data/csslint/demos/demo.css +43 -0
  13. data/csslint/lib/js.jar +0 -0
  14. data/csslint/lib/jshint.js +3963 -0
  15. data/csslint/lib/parserlib.js +6295 -0
  16. data/csslint/lib/yuitest-rhino-cli.js +3955 -0
  17. data/csslint/lib/yuitest.js +4561 -0
  18. data/csslint/npm/package.json +30 -0
  19. data/csslint/release/csslint-node.js +9125 -0
  20. data/csslint/release/csslint-rhino.js +9390 -0
  21. data/csslint/release/csslint-tests.js +1921 -0
  22. data/csslint/release/csslint-worker.js +9148 -0
  23. data/csslint/release/csslint-wsh.js +9477 -0
  24. data/csslint/release/csslint.js +9127 -0
  25. data/csslint/release/npm/cli.js +307 -0
  26. data/csslint/release/npm/lib/csslint-node.js +9125 -0
  27. data/csslint/release/npm/package.json +30 -0
  28. data/csslint/src/cli/common.js +215 -0
  29. data/csslint/src/cli/node.js +87 -0
  30. data/csslint/src/cli/rhino.js +47 -0
  31. data/csslint/src/cli/wsh.js +134 -0
  32. data/csslint/src/core/CSSLint.js +181 -0
  33. data/csslint/src/core/Reporter.js +161 -0
  34. data/csslint/src/core/Util.js +62 -0
  35. data/csslint/src/formatters/checkstyle-xml.js +109 -0
  36. data/csslint/src/formatters/compact.js +59 -0
  37. data/csslint/src/formatters/csslint-xml.js +68 -0
  38. data/csslint/src/formatters/lint-xml.js +69 -0
  39. data/csslint/src/formatters/text.js +64 -0
  40. data/csslint/src/rules/adjoining-classes.js +45 -0
  41. data/csslint/src/rules/box-model.js +93 -0
  42. data/csslint/src/rules/box-sizing.js +28 -0
  43. data/csslint/src/rules/compatible-vendor-prefixes.js +171 -0
  44. data/csslint/src/rules/display-property-grouping.js +117 -0
  45. data/csslint/src/rules/duplicate-background-images.js +37 -0
  46. data/csslint/src/rules/duplicate-properties.js +46 -0
  47. data/csslint/src/rules/empty-rules.js +34 -0
  48. data/csslint/src/rules/errors.js +23 -0
  49. data/csslint/src/rules/fallback-colors.js +67 -0
  50. data/csslint/src/rules/floats.js +36 -0
  51. data/csslint/src/rules/font-faces.js +30 -0
  52. data/csslint/src/rules/font-sizes.js +35 -0
  53. data/csslint/src/rules/gradients.js +69 -0
  54. data/csslint/src/rules/ids.js +50 -0
  55. data/csslint/src/rules/import.js +23 -0
  56. data/csslint/src/rules/important.js +37 -0
  57. data/csslint/src/rules/known-properties.js +29 -0
  58. data/csslint/src/rules/outline-none.js +73 -0
  59. data/csslint/src/rules/overqualified-elements.js +63 -0
  60. data/csslint/src/rules/qualified-headings.js +38 -0
  61. data/csslint/src/rules/regex-selectors.js +44 -0
  62. data/csslint/src/rules/rules-count.js +28 -0
  63. data/csslint/src/rules/shorthand.js +87 -0
  64. data/csslint/src/rules/star-property-hack.js +27 -0
  65. data/csslint/src/rules/text-indent.js +53 -0
  66. data/csslint/src/rules/underscore-property-hack.js +27 -0
  67. data/csslint/src/rules/unique-headings.js +74 -0
  68. data/csslint/src/rules/universal-selector.js +35 -0
  69. data/csslint/src/rules/unqualified-attributes.js +42 -0
  70. data/csslint/src/rules/vendor-prefix.js +143 -0
  71. data/csslint/src/rules/zero-units.js +34 -0
  72. data/csslint/src/worker/Worker.js +26 -0
  73. data/csslint/tests/all-rules.js +64 -0
  74. data/csslint/tests/core/CSSLint.js +22 -0
  75. data/csslint/tests/core/Reporter.js +36 -0
  76. data/csslint/tests/css/width-100.html +76 -0
  77. data/csslint/tests/formatters/checkstyle-xml.js +44 -0
  78. data/csslint/tests/formatters/compact.js +47 -0
  79. data/csslint/tests/formatters/csslint-xml.js +42 -0
  80. data/csslint/tests/formatters/lint-xml.js +43 -0
  81. data/csslint/tests/formatters/text.js +36 -0
  82. data/csslint/tests/rules/adjoining-classes.js +31 -0
  83. data/csslint/tests/rules/box-model.js +211 -0
  84. data/csslint/tests/rules/box-sizing.js +23 -0
  85. data/csslint/tests/rules/compatible-vendor-prefixes.js +56 -0
  86. data/csslint/tests/rules/display-property-grouping.js +213 -0
  87. data/csslint/tests/rules/duplicate-background-images.js +25 -0
  88. data/csslint/tests/rules/duplicate-properties.js +54 -0
  89. data/csslint/tests/rules/empty-rules.js +18 -0
  90. data/csslint/tests/rules/errors.js +17 -0
  91. data/csslint/tests/rules/fallback-colors.js +162 -0
  92. data/csslint/tests/rules/floats.js +35 -0
  93. data/csslint/tests/rules/font-faces.js +28 -0
  94. data/csslint/tests/rules/font-sizes.js +30 -0
  95. data/csslint/tests/rules/gradients.js +60 -0
  96. data/csslint/tests/rules/ids.js +25 -0
  97. data/csslint/tests/rules/import.js +18 -0
  98. data/csslint/tests/rules/important.js +27 -0
  99. data/csslint/tests/rules/known-properties.js +44 -0
  100. data/csslint/tests/rules/outline-none.js +50 -0
  101. data/csslint/tests/rules/overqualified-elements.js +41 -0
  102. data/csslint/tests/rules/qualified-headings.js +19 -0
  103. data/csslint/tests/rules/regex-selectors.js +52 -0
  104. data/csslint/tests/rules/shorthand.js +36 -0
  105. data/csslint/tests/rules/star-property-hack.js +24 -0
  106. data/csslint/tests/rules/text-indent.js +55 -0
  107. data/csslint/tests/rules/underscore-property-hack.js +24 -0
  108. data/csslint/tests/rules/unique-headings.js +47 -0
  109. data/csslint/tests/rules/universal-selector.js +31 -0
  110. data/csslint/tests/rules/unqualified-attributes.js +37 -0
  111. data/csslint/tests/rules/vendor-prefix.js +76 -0
  112. data/csslint/tests/rules/zero-units.js +44 -0
  113. data/csslint/tests/testrunner.htm +138 -0
  114. data/js.jar +0 -0
  115. data/lib/ruby_css_lint.rb +168 -0
  116. data/test/helper.rb +17 -0
  117. data/test/test_ruby_css_lint.rb +7 -0
  118. metadata +240 -0
@@ -0,0 +1,4561 @@
1
+ /**
2
+ * YUI Test Framework
3
+ * @module yuitest
4
+ */
5
+
6
+ /**
7
+ * The root namespace for YUI Test.
8
+ * @class YUITest
9
+ * @static
10
+ */
11
+
12
+ var YUITest = {
13
+ version: "@VERSION@"
14
+ };
15
+
16
+
17
+ /**
18
+ * Simple custom event implementation.
19
+ * @namespace YUITest
20
+ * @class EventTarget
21
+ * @constructor
22
+ */
23
+ YUITest.EventTarget = function(){
24
+
25
+ /**
26
+ * Event handlers for the various events.
27
+ * @type Object
28
+ * @private
29
+ * @property _handlers
30
+ * @static
31
+ */
32
+ this._handlers = {};
33
+
34
+ };
35
+
36
+ YUITest.EventTarget.prototype = {
37
+
38
+ //restore prototype
39
+ constructor: YUITest.EventTarget,
40
+
41
+ //-------------------------------------------------------------------------
42
+ // Event Handling
43
+ //-------------------------------------------------------------------------
44
+
45
+ /**
46
+ * Adds a listener for a given event type.
47
+ * @param {String} type The type of event to add a listener for.
48
+ * @param {Function} listener The function to call when the event occurs.
49
+ * @return {void}
50
+ * @method attach
51
+ */
52
+ attach: function(type, listener){
53
+ if (typeof this._handlers[type] == "undefined"){
54
+ this._handlers[type] = [];
55
+ }
56
+
57
+ this._handlers[type].push(listener);
58
+ },
59
+
60
+ /**
61
+ * Adds a listener for a given event type.
62
+ * @param {String} type The type of event to add a listener for.
63
+ * @param {Function} listener The function to call when the event occurs.
64
+ * @return {void}
65
+ * @method subscribe
66
+ * @deprecated
67
+ */
68
+ subscribe: function(type, listener){
69
+ this.attach.apply(this, arguments);
70
+ },
71
+
72
+ /**
73
+ * Fires an event based on the passed-in object.
74
+ * @param {Object|String} event An object with at least a 'type' attribute
75
+ * or a string indicating the event name.
76
+ * @return {void}
77
+ * @method fire
78
+ */
79
+ fire: function(event){
80
+ if (typeof event == "string"){
81
+ event = { type: event };
82
+ }
83
+ if (!event.target){
84
+ event.target = this;
85
+ }
86
+
87
+ if (!event.type){
88
+ throw new Error("Event object missing 'type' property.");
89
+ }
90
+
91
+ if (this._handlers[event.type] instanceof Array){
92
+ var handlers = this._handlers[event.type];
93
+ for (var i=0, len=handlers.length; i < len; i++){
94
+ handlers[i].call(this, event);
95
+ }
96
+ }
97
+ },
98
+
99
+ /**
100
+ * Removes a listener for a given event type.
101
+ * @param {String} type The type of event to remove a listener from.
102
+ * @param {Function} listener The function to remove from the event.
103
+ * @return {void}
104
+ * @method detach
105
+ */
106
+ detach: function(type, listener){
107
+ if (this._handlers[type] instanceof Array){
108
+ var handlers = this._handlers[type];
109
+ for (var i=0, len=handlers.length; i < len; i++){
110
+ if (handlers[i] === listener){
111
+ handlers.splice(i, 1);
112
+ break;
113
+ }
114
+ }
115
+ }
116
+ },
117
+
118
+ /**
119
+ * Removes a listener for a given event type.
120
+ * @param {String} type The type of event to remove a listener from.
121
+ * @param {Function} listener The function to remove from the event.
122
+ * @return {void}
123
+ * @method unsubscribe
124
+ * @deprecated
125
+ */
126
+ unsubscribe: function(type, listener){
127
+ this.detach.apply(this, arguments);
128
+ }
129
+
130
+ };
131
+
132
+
133
+ /**
134
+ * Object containing helper methods.
135
+ * @namespace YUITest
136
+ * @class Util
137
+ * @static
138
+ */
139
+ YUITest.Util = {
140
+
141
+ /**
142
+ * Mixes the own properties from the supplier onto the
143
+ * receiver.
144
+ * @param {Object} receiver The object to receive the properties.
145
+ * @param {Object} supplier The object to supply the properties.
146
+ * @return {Object} The receiver that was passed in.
147
+ * @method mix
148
+ * @static
149
+ */
150
+ mix: function(receiver, supplier){
151
+
152
+ for (var prop in supplier){
153
+ if (supplier.hasOwnProperty(prop)){
154
+ receiver[prop] = supplier[prop];
155
+ }
156
+ }
157
+
158
+ return receiver;
159
+ },
160
+
161
+ /**
162
+ * Stub for JSON functionality. When the native JSON utility
163
+ * is available, it will be used. Otherwise, a stub object
164
+ * is created. Developers should override YUITest.Util.JSON
165
+ * when attempting to use it in environments where a native
166
+ * JSON utility is unavailable.
167
+ * @property JSON
168
+ * @type JSON
169
+ * @static
170
+ */
171
+ JSON: typeof JSON != "undefined" ? JSON : {
172
+ stringify: function(){
173
+ //TODO: Should include code to do this?
174
+ throw new Error("No JSON utility specified.");
175
+ }
176
+ }
177
+
178
+ };
179
+
180
+
181
+ /**
182
+ * Error is thrown whenever an assertion fails. It provides methods
183
+ * to more easily get at error information and also provides a base class
184
+ * from which more specific assertion errors can be derived.
185
+ *
186
+ * @param {String} message The message to display when the error occurs.
187
+ * @namespace YUITest
188
+ * @class AssertionError
189
+ * @constructor
190
+ */
191
+ YUITest.AssertionError = function (message){
192
+
193
+ /**
194
+ * Error message. Must be duplicated to ensure browser receives it.
195
+ * @type String
196
+ * @property message
197
+ */
198
+ this.message = message;
199
+
200
+ /**
201
+ * The name of the error that occurred.
202
+ * @type String
203
+ * @property name
204
+ */
205
+ this.name = "Assert Error";
206
+ };
207
+
208
+ YUITest.AssertionError.prototype = {
209
+
210
+ //restore constructor
211
+ constructor: YUITest.AssertionError,
212
+
213
+ /**
214
+ * Returns a fully formatted error for an assertion failure. This should
215
+ * be overridden by all subclasses to provide specific information.
216
+ * @method getMessage
217
+ * @return {String} A string describing the error.
218
+ */
219
+ getMessage : function () {
220
+ return this.message;
221
+ },
222
+
223
+ /**
224
+ * Returns a string representation of the error.
225
+ * @method toString
226
+ * @return {String} A string representation of the error.
227
+ */
228
+ toString : function () {
229
+ return this.name + ": " + this.getMessage();
230
+ }
231
+
232
+ };
233
+
234
+ /**
235
+ * ComparisonFailure is subclass of Error that is thrown whenever
236
+ * a comparison between two values fails. It provides mechanisms to retrieve
237
+ * both the expected and actual value.
238
+ *
239
+ * @param {String} message The message to display when the error occurs.
240
+ * @param {Object} expected The expected value.
241
+ * @param {Object} actual The actual value that caused the assertion to fail.
242
+ * @namespace YUITest
243
+ * @extends AssertionError
244
+ * @class ComparisonFailure
245
+ * @constructor
246
+ */
247
+ YUITest.ComparisonFailure = function (message, expected, actual){
248
+
249
+ //call superclass
250
+ YUITest.AssertionError.call(this, message);
251
+
252
+ /**
253
+ * The expected value.
254
+ * @type Object
255
+ * @property expected
256
+ */
257
+ this.expected = expected;
258
+
259
+ /**
260
+ * The actual value.
261
+ * @type Object
262
+ * @property actual
263
+ */
264
+ this.actual = actual;
265
+
266
+ /**
267
+ * The name of the error that occurred.
268
+ * @type String
269
+ * @property name
270
+ */
271
+ this.name = "ComparisonFailure";
272
+
273
+ };
274
+
275
+ //inherit from YUITest.AssertionError
276
+ YUITest.ComparisonFailure.prototype = new YUITest.AssertionError;
277
+
278
+ //restore constructor
279
+ YUITest.ComparisonFailure.prototype.constructor = YUITest.ComparisonFailure;
280
+
281
+ /**
282
+ * Returns a fully formatted error for an assertion failure. This message
283
+ * provides information about the expected and actual values.
284
+ * @method getMessage
285
+ * @return {String} A string describing the error.
286
+ */
287
+ YUITest.ComparisonFailure.prototype.getMessage = function(){
288
+ return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
289
+ "\nActual: " + this.actual + " (" + (typeof this.actual) + ")";
290
+ };
291
+
292
+ /**
293
+ * ShouldError is subclass of Error that is thrown whenever
294
+ * a test is expected to throw an error but doesn't.
295
+ *
296
+ * @param {String} message The message to display when the error occurs.
297
+ * @namespace YUITest
298
+ * @extends AssertionError
299
+ * @class ShouldError
300
+ * @constructor
301
+ */
302
+ YUITest.ShouldError = function (message){
303
+
304
+ //call superclass
305
+ YUITest.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
306
+
307
+ /**
308
+ * The name of the error that occurred.
309
+ * @type String
310
+ * @property name
311
+ */
312
+ this.name = "ShouldError";
313
+
314
+ };
315
+
316
+ //inherit from YUITest.AssertionError
317
+ YUITest.ShouldError.prototype = new YUITest.AssertionError();
318
+
319
+ //restore constructor
320
+ YUITest.ShouldError.prototype.constructor = YUITest.ShouldError;
321
+
322
+ /**
323
+ * ShouldFail is subclass of AssertionError that is thrown whenever
324
+ * a test was expected to fail but did not.
325
+ *
326
+ * @param {String} message The message to display when the error occurs.
327
+ * @namespace YUITest
328
+ * @extends YUITest.AssertionError
329
+ * @class ShouldFail
330
+ * @constructor
331
+ */
332
+ YUITest.ShouldFail = function (message){
333
+
334
+ //call superclass
335
+ YUITest.AssertionError.call(this, message || "This test should fail but didn't.");
336
+
337
+ /**
338
+ * The name of the error that occurred.
339
+ * @type String
340
+ * @property name
341
+ */
342
+ this.name = "ShouldFail";
343
+
344
+ };
345
+
346
+ //inherit from YUITest.AssertionError
347
+ YUITest.ShouldFail.prototype = new YUITest.AssertionError();
348
+
349
+ //restore constructor
350
+ YUITest.ShouldFail.prototype.constructor = YUITest.ShouldFail;
351
+
352
+ /**
353
+ * UnexpectedError is subclass of AssertionError that is thrown whenever
354
+ * an error occurs within the course of a test and the test was not expected
355
+ * to throw an error.
356
+ *
357
+ * @param {Error} cause The unexpected error that caused this error to be
358
+ * thrown.
359
+ * @namespace YUITest
360
+ * @extends YUITest.AssertionError
361
+ * @class UnexpectedError
362
+ * @constructor
363
+ */
364
+ YUITest.UnexpectedError = function (cause){
365
+
366
+ //call superclass
367
+ YUITest.AssertionError.call(this, "Unexpected error: " + cause.message);
368
+
369
+ /**
370
+ * The unexpected error that occurred.
371
+ * @type Error
372
+ * @property cause
373
+ */
374
+ this.cause = cause;
375
+
376
+ /**
377
+ * The name of the error that occurred.
378
+ * @type String
379
+ * @property name
380
+ */
381
+ this.name = "UnexpectedError";
382
+
383
+ /**
384
+ * Stack information for the error (if provided).
385
+ * @type String
386
+ * @property stack
387
+ */
388
+ this.stack = cause.stack;
389
+
390
+ };
391
+
392
+ //inherit from YUITest.AssertionError
393
+ YUITest.UnexpectedError.prototype = new YUITest.AssertionError();
394
+
395
+ //restore constructor
396
+ YUITest.UnexpectedError.prototype.constructor = YUITest.UnexpectedError;
397
+
398
+ /**
399
+ * UnexpectedValue is subclass of Error that is thrown whenever
400
+ * a value was unexpected in its scope. This typically means that a test
401
+ * was performed to determine that a value was *not* equal to a certain
402
+ * value.
403
+ *
404
+ * @param {String} message The message to display when the error occurs.
405
+ * @param {Object} unexpected The unexpected value.
406
+ * @namespace YUITest
407
+ * @extends AssertionError
408
+ * @class UnexpectedValue
409
+ * @constructor
410
+ */
411
+ YUITest.UnexpectedValue = function (message, unexpected){
412
+
413
+ //call superclass
414
+ YUITest.AssertionError.call(this, message);
415
+
416
+ /**
417
+ * The unexpected value.
418
+ * @type Object
419
+ * @property unexpected
420
+ */
421
+ this.unexpected = unexpected;
422
+
423
+ /**
424
+ * The name of the error that occurred.
425
+ * @type String
426
+ * @property name
427
+ */
428
+ this.name = "UnexpectedValue";
429
+
430
+ };
431
+
432
+ //inherit from YUITest.AssertionError
433
+ YUITest.UnexpectedValue.prototype = new YUITest.AssertionError();
434
+
435
+ //restore constructor
436
+ YUITest.UnexpectedValue.prototype.constructor = YUITest.UnexpectedValue;
437
+
438
+ /**
439
+ * Returns a fully formatted error for an assertion failure. This message
440
+ * provides information about the expected and actual values.
441
+ * @method getMessage
442
+ * @return {String} A string describing the error.
443
+ */
444
+ YUITest.UnexpectedValue.prototype.getMessage = function(){
445
+ return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
446
+ };
447
+
448
+
449
+ /**
450
+ * Represents a stoppage in test execution to wait for an amount of time before
451
+ * continuing.
452
+ * @param {Function} segment A function to run when the wait is over.
453
+ * @param {int} delay The number of milliseconds to wait before running the code.
454
+ * @class Wait
455
+ * @namespace Test
456
+ * @constructor
457
+ *
458
+ */
459
+ YUITest.Wait = function (segment, delay) {
460
+
461
+ /**
462
+ * The segment of code to run when the wait is over.
463
+ * @type Function
464
+ * @property segment
465
+ */
466
+ this.segment = (typeof segment == "function" ? segment : null);
467
+
468
+ /**
469
+ * The delay before running the segment of code.
470
+ * @type int
471
+ * @property delay
472
+ */
473
+ this.delay = (typeof delay == "number" ? delay : 0);
474
+ };
475
+
476
+
477
+ /**
478
+ * The Assert object provides functions to test JavaScript values against
479
+ * known and expected results. Whenever a comparison (assertion) fails,
480
+ * an error is thrown.
481
+ * @namespace YUITest
482
+ * @class Assert
483
+ * @static
484
+ */
485
+ YUITest.Assert = {
486
+
487
+ /**
488
+ * The number of assertions performed.
489
+ * @property _asserts
490
+ * @type int
491
+ * @private
492
+ */
493
+ _asserts: 0,
494
+
495
+ //-------------------------------------------------------------------------
496
+ // Helper Methods
497
+ //-------------------------------------------------------------------------
498
+
499
+ /**
500
+ * Formats a message so that it can contain the original assertion message
501
+ * in addition to the custom message.
502
+ * @param {String} customMessage The message passed in by the developer.
503
+ * @param {String} defaultMessage The message created by the error by default.
504
+ * @return {String} The final error message, containing either or both.
505
+ * @protected
506
+ * @static
507
+ * @method _formatMessage
508
+ */
509
+ _formatMessage : function (customMessage, defaultMessage) {
510
+ if (typeof customMessage == "string" && customMessage.length > 0){
511
+ return customMessage.replace("{message}", defaultMessage);
512
+ } else {
513
+ return defaultMessage;
514
+ }
515
+ },
516
+
517
+ /**
518
+ * Returns the number of assertions that have been performed.
519
+ * @method _getCount
520
+ * @protected
521
+ * @static
522
+ */
523
+ _getCount: function(){
524
+ return this._asserts;
525
+ },
526
+
527
+ /**
528
+ * Increments the number of assertions that have been performed.
529
+ * @method _increment
530
+ * @protected
531
+ * @static
532
+ */
533
+ _increment: function(){
534
+ this._asserts++;
535
+ },
536
+
537
+ /**
538
+ * Resets the number of assertions that have been performed to 0.
539
+ * @method _reset
540
+ * @protected
541
+ * @static
542
+ */
543
+ _reset: function(){
544
+ this._asserts = 0;
545
+ },
546
+
547
+ //-------------------------------------------------------------------------
548
+ // Generic Assertion Methods
549
+ //-------------------------------------------------------------------------
550
+
551
+ /**
552
+ * Forces an assertion error to occur.
553
+ * @param {String} message (Optional) The message to display with the failure.
554
+ * @method fail
555
+ * @static
556
+ */
557
+ fail : function (message) {
558
+ throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Test force-failed."));
559
+ },
560
+
561
+ //-------------------------------------------------------------------------
562
+ // Equality Assertion Methods
563
+ //-------------------------------------------------------------------------
564
+
565
+ /**
566
+ * Asserts that a value is equal to another. This uses the double equals sign
567
+ * so type cohersion may occur.
568
+ * @param {Object} expected The expected value.
569
+ * @param {Object} actual The actual value to test.
570
+ * @param {String} message (Optional) The message to display if the assertion fails.
571
+ * @method areEqual
572
+ * @static
573
+ */
574
+ areEqual : function (expected, actual, message) {
575
+ YUITest.Assert._increment();
576
+ if (expected != actual) {
577
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal."), expected, actual);
578
+ }
579
+ },
580
+
581
+ /**
582
+ * Asserts that a value is not equal to another. This uses the double equals sign
583
+ * so type cohersion may occur.
584
+ * @param {Object} unexpected The unexpected value.
585
+ * @param {Object} actual The actual value to test.
586
+ * @param {String} message (Optional) The message to display if the assertion fails.
587
+ * @method areNotEqual
588
+ * @static
589
+ */
590
+ areNotEqual : function (unexpected, actual,
591
+ message) {
592
+ YUITest.Assert._increment();
593
+ if (unexpected == actual) {
594
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be equal."), unexpected);
595
+ }
596
+ },
597
+
598
+ /**
599
+ * Asserts that a value is not the same as another. This uses the triple equals sign
600
+ * so no type cohersion may occur.
601
+ * @param {Object} unexpected The unexpected value.
602
+ * @param {Object} actual The actual value to test.
603
+ * @param {String} message (Optional) The message to display if the assertion fails.
604
+ * @method areNotSame
605
+ * @static
606
+ */
607
+ areNotSame : function (unexpected, actual, message) {
608
+ YUITest.Assert._increment();
609
+ if (unexpected === actual) {
610
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be the same."), unexpected);
611
+ }
612
+ },
613
+
614
+ /**
615
+ * Asserts that a value is the same as another. This uses the triple equals sign
616
+ * so no type cohersion may occur.
617
+ * @param {Object} expected The expected value.
618
+ * @param {Object} actual The actual value to test.
619
+ * @param {String} message (Optional) The message to display if the assertion fails.
620
+ * @method areSame
621
+ * @static
622
+ */
623
+ areSame : function (expected, actual, message) {
624
+ YUITest.Assert._increment();
625
+ if (expected !== actual) {
626
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be the same."), expected, actual);
627
+ }
628
+ },
629
+
630
+ //-------------------------------------------------------------------------
631
+ // Boolean Assertion Methods
632
+ //-------------------------------------------------------------------------
633
+
634
+ /**
635
+ * Asserts that a value is false. This uses the triple equals sign
636
+ * so no type cohersion may occur.
637
+ * @param {Object} actual The actual value to test.
638
+ * @param {String} message (Optional) The message to display if the assertion fails.
639
+ * @method isFalse
640
+ * @static
641
+ */
642
+ isFalse : function (actual, message) {
643
+ YUITest.Assert._increment();
644
+ if (false !== actual) {
645
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be false."), false, actual);
646
+ }
647
+ },
648
+
649
+ /**
650
+ * Asserts that a value is true. This uses the triple equals sign
651
+ * so no type cohersion may occur.
652
+ * @param {Object} actual The actual value to test.
653
+ * @param {String} message (Optional) The message to display if the assertion fails.
654
+ * @method isTrue
655
+ * @static
656
+ */
657
+ isTrue : function (actual, message) {
658
+ YUITest.Assert._increment();
659
+ if (true !== actual) {
660
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be true."), true, actual);
661
+ }
662
+
663
+ },
664
+
665
+ //-------------------------------------------------------------------------
666
+ // Special Value Assertion Methods
667
+ //-------------------------------------------------------------------------
668
+
669
+ /**
670
+ * Asserts that a value is not a number.
671
+ * @param {Object} actual The value to test.
672
+ * @param {String} message (Optional) The message to display if the assertion fails.
673
+ * @method isNaN
674
+ * @static
675
+ */
676
+ isNaN : function (actual, message){
677
+ YUITest.Assert._increment();
678
+ if (!isNaN(actual)){
679
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
680
+ }
681
+ },
682
+
683
+ /**
684
+ * Asserts that a value is not the special NaN value.
685
+ * @param {Object} actual The value to test.
686
+ * @param {String} message (Optional) The message to display if the assertion fails.
687
+ * @method isNotNaN
688
+ * @static
689
+ */
690
+ isNotNaN : function (actual, message){
691
+ YUITest.Assert._increment();
692
+ if (isNaN(actual)){
693
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be NaN."), NaN);
694
+ }
695
+ },
696
+
697
+ /**
698
+ * Asserts that a value is not null. This uses the triple equals sign
699
+ * so no type cohersion may occur.
700
+ * @param {Object} actual The actual value to test.
701
+ * @param {String} message (Optional) The message to display if the assertion fails.
702
+ * @method isNotNull
703
+ * @static
704
+ */
705
+ isNotNull : function (actual, message) {
706
+ YUITest.Assert._increment();
707
+ if (actual === null) {
708
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be null."), null);
709
+ }
710
+ },
711
+
712
+ /**
713
+ * Asserts that a value is not undefined. This uses the triple equals sign
714
+ * so no type cohersion may occur.
715
+ * @param {Object} actual The actual value to test.
716
+ * @param {String} message (Optional) The message to display if the assertion fails.
717
+ * @method isNotUndefined
718
+ * @static
719
+ */
720
+ isNotUndefined : function (actual, message) {
721
+ YUITest.Assert._increment();
722
+ if (typeof actual == "undefined") {
723
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should not be undefined."), undefined);
724
+ }
725
+ },
726
+
727
+ /**
728
+ * Asserts that a value is null. This uses the triple equals sign
729
+ * so no type cohersion may occur.
730
+ * @param {Object} actual The actual value to test.
731
+ * @param {String} message (Optional) The message to display if the assertion fails.
732
+ * @method isNull
733
+ * @static
734
+ */
735
+ isNull : function (actual, message) {
736
+ YUITest.Assert._increment();
737
+ if (actual !== null) {
738
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be null."), null, actual);
739
+ }
740
+ },
741
+
742
+ /**
743
+ * Asserts that a value is undefined. This uses the triple equals sign
744
+ * so no type cohersion may occur.
745
+ * @param {Object} actual The actual value to test.
746
+ * @param {String} message (Optional) The message to display if the assertion fails.
747
+ * @method isUndefined
748
+ * @static
749
+ */
750
+ isUndefined : function (actual, message) {
751
+ YUITest.Assert._increment();
752
+ if (typeof actual != "undefined") {
753
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
754
+ }
755
+ },
756
+
757
+ //--------------------------------------------------------------------------
758
+ // Instance Assertion Methods
759
+ //--------------------------------------------------------------------------
760
+
761
+ /**
762
+ * Asserts that a value is an array.
763
+ * @param {Object} actual The value to test.
764
+ * @param {String} message (Optional) The message to display if the assertion fails.
765
+ * @method isArray
766
+ * @static
767
+ */
768
+ isArray : function (actual, message) {
769
+ YUITest.Assert._increment();
770
+ var shouldFail = false;
771
+ if (Array.isArray){
772
+ shouldFail = !Array.isArray(actual);
773
+ } else {
774
+ shouldFail = Object.prototype.toString.call(actual) != "[object Array]";
775
+ }
776
+ if (shouldFail){
777
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an array."), actual);
778
+ }
779
+ },
780
+
781
+ /**
782
+ * Asserts that a value is a Boolean.
783
+ * @param {Object} actual The value to test.
784
+ * @param {String} message (Optional) The message to display if the assertion fails.
785
+ * @method isBoolean
786
+ * @static
787
+ */
788
+ isBoolean : function (actual, message) {
789
+ YUITest.Assert._increment();
790
+ if (typeof actual != "boolean"){
791
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a Boolean."), actual);
792
+ }
793
+ },
794
+
795
+ /**
796
+ * Asserts that a value is a function.
797
+ * @param {Object} actual The value to test.
798
+ * @param {String} message (Optional) The message to display if the assertion fails.
799
+ * @method isFunction
800
+ * @static
801
+ */
802
+ isFunction : function (actual, message) {
803
+ YUITest.Assert._increment();
804
+ if (!(actual instanceof Function)){
805
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a function."), actual);
806
+ }
807
+ },
808
+
809
+ /**
810
+ * Asserts that a value is an instance of a particular object. This may return
811
+ * incorrect results when comparing objects from one frame to constructors in
812
+ * another frame. For best results, don't use in a cross-frame manner.
813
+ * @param {Function} expected The function that the object should be an instance of.
814
+ * @param {Object} actual The object to test.
815
+ * @param {String} message (Optional) The message to display if the assertion fails.
816
+ * @method isInstanceOf
817
+ * @static
818
+ */
819
+ isInstanceOf : function (expected, actual, message) {
820
+ YUITest.Assert._increment();
821
+ if (!(actual instanceof expected)){
822
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
823
+ }
824
+ },
825
+
826
+ /**
827
+ * Asserts that a value is a number.
828
+ * @param {Object} actual The value to test.
829
+ * @param {String} message (Optional) The message to display if the assertion fails.
830
+ * @method isNumber
831
+ * @static
832
+ */
833
+ isNumber : function (actual, message) {
834
+ YUITest.Assert._increment();
835
+ if (typeof actual != "number"){
836
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a number."), actual);
837
+ }
838
+ },
839
+
840
+ /**
841
+ * Asserts that a value is an object.
842
+ * @param {Object} actual The value to test.
843
+ * @param {String} message (Optional) The message to display if the assertion fails.
844
+ * @method isObject
845
+ * @static
846
+ */
847
+ isObject : function (actual, message) {
848
+ YUITest.Assert._increment();
849
+ if (!actual || (typeof actual != "object" && typeof actual != "function")){
850
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an object."), actual);
851
+ }
852
+ },
853
+
854
+ /**
855
+ * Asserts that a value is a string.
856
+ * @param {Object} actual The value to test.
857
+ * @param {String} message (Optional) The message to display if the assertion fails.
858
+ * @method isString
859
+ * @static
860
+ */
861
+ isString : function (actual, message) {
862
+ YUITest.Assert._increment();
863
+ if (typeof actual != "string"){
864
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a string."), actual);
865
+ }
866
+ },
867
+
868
+ /**
869
+ * Asserts that a value is of a particular type.
870
+ * @param {String} expectedType The expected type of the variable.
871
+ * @param {Object} actualValue The actual value to test.
872
+ * @param {String} message (Optional) The message to display if the assertion fails.
873
+ * @method isTypeOf
874
+ * @static
875
+ */
876
+ isTypeOf : function (expectedType, actualValue, message){
877
+ YUITest.Assert._increment();
878
+ if (typeof actualValue != expectedType){
879
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expectedType, typeof actualValue);
880
+ }
881
+ },
882
+
883
+ //--------------------------------------------------------------------------
884
+ // Error Detection Methods
885
+ //--------------------------------------------------------------------------
886
+
887
+ /**
888
+ * Asserts that executing a particular method should throw an error of
889
+ * a specific type. This is a replacement for _should.error.
890
+ * @param {String|Function|Object} expectedError If a string, this
891
+ * is the error message that the error must have; if a function, this
892
+ * is the constructor that should have been used to create the thrown
893
+ * error; if an object, this is an instance of a particular error type
894
+ * with a specific error message (both must match).
895
+ * @param {Function} method The method to execute that should throw the error.
896
+ * @param {String} message (Optional) The message to display if the assertion
897
+ * fails.
898
+ * @method throwsError
899
+ * @return {void}
900
+ * @static
901
+ */
902
+ throwsError: function(expectedError, method, message){
903
+ YUITest.Assert._increment();
904
+ var error = false;
905
+
906
+ try {
907
+ method();
908
+ } catch (thrown) {
909
+
910
+ //check to see what type of data we have
911
+ if (typeof expectedError == "string"){
912
+
913
+ //if it's a string, check the error message
914
+ if (thrown.message != expectedError){
915
+ error = true;
916
+ }
917
+ } else if (typeof expectedError == "function"){
918
+
919
+ //if it's a function, see if the error is an instance of it
920
+ if (!(thrown instanceof expectedError)){
921
+ error = true;
922
+ }
923
+
924
+ } else if (typeof expectedError == "object" && expectedError !== null){
925
+
926
+ //if it's an object, check the instance and message
927
+ if (!(thrown instanceof expectedError.constructor) ||
928
+ thrown.message != expectedError.message){
929
+ error = true;
930
+ }
931
+
932
+ } else { //if it gets here, the argument could be wrong
933
+ error = true;
934
+ }
935
+
936
+ if (error){
937
+ throw new YUITest.UnexpectedError(thrown);
938
+ } else {
939
+ return;
940
+ }
941
+ }
942
+
943
+ //if it reaches here, the error wasn't thrown, which is a bad thing
944
+ throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Error should have been thrown."));
945
+ }
946
+
947
+ };
948
+
949
+
950
+ /**
951
+ * The ArrayAssert object provides functions to test JavaScript array objects
952
+ * for a variety of cases.
953
+ * @namespace YUITest
954
+ * @class ArrayAssert
955
+ * @static
956
+ */
957
+
958
+ YUITest.ArrayAssert = {
959
+
960
+ //=========================================================================
961
+ // Private methods
962
+ //=========================================================================
963
+
964
+ /**
965
+ * Simple indexOf() implementation for an array. Defers to native
966
+ * if available.
967
+ * @param {Array} haystack The array to search.
968
+ * @param {Variant} needle The value to locate.
969
+ * @return {int} The index of the needle if found or -1 if not.
970
+ * @method _indexOf
971
+ * @private
972
+ */
973
+ _indexOf: function(haystack, needle){
974
+ if (haystack.indexOf){
975
+ return haystack.indexOf(needle);
976
+ } else {
977
+ for (var i=0; i < haystack.length; i++){
978
+ if (haystack[i] === needle){
979
+ return i;
980
+ }
981
+ }
982
+ return -1;
983
+ }
984
+ },
985
+
986
+ /**
987
+ * Simple some() implementation for an array. Defers to native
988
+ * if available.
989
+ * @param {Array} haystack The array to search.
990
+ * @param {Function} matcher The function to run on each value.
991
+ * @return {Boolean} True if any value, when run through the matcher,
992
+ * returns true.
993
+ * @method _some
994
+ * @private
995
+ */
996
+ _some: function(haystack, matcher){
997
+ if (haystack.some){
998
+ return haystack.some(matcher);
999
+ } else {
1000
+ for (var i=0; i < haystack.length; i++){
1001
+ if (matcher(haystack[i])){
1002
+ return true;
1003
+ }
1004
+ }
1005
+ return false;
1006
+ }
1007
+ },
1008
+
1009
+ /**
1010
+ * Asserts that a value is present in an array. This uses the triple equals
1011
+ * sign so no type cohersion may occur.
1012
+ * @param {Object} needle The value that is expected in the array.
1013
+ * @param {Array} haystack An array of values.
1014
+ * @param {String} message (Optional) The message to display if the assertion fails.
1015
+ * @method contains
1016
+ * @static
1017
+ */
1018
+ contains : function (needle, haystack,
1019
+ message) {
1020
+
1021
+ YUITest.Assert._increment();
1022
+
1023
+ if (this._indexOf(haystack, needle) == -1){
1024
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1025
+ }
1026
+ },
1027
+
1028
+ /**
1029
+ * Asserts that a set of values are present in an array. This uses the triple equals
1030
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
1031
+ * be found.
1032
+ * @param {Object[]} needles An array of values that are expected in the array.
1033
+ * @param {Array} haystack An array of values to check.
1034
+ * @param {String} message (Optional) The message to display if the assertion fails.
1035
+ * @method containsItems
1036
+ * @static
1037
+ */
1038
+ containsItems : function (needles, haystack,
1039
+ message) {
1040
+ YUITest.Assert._increment();
1041
+
1042
+ //begin checking values
1043
+ for (var i=0; i < needles.length; i++){
1044
+ if (this._indexOf(haystack, needles[i]) == -1){
1045
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needles[i] + " (" + (typeof needles[i]) + ") not found in array [" + haystack + "]."));
1046
+ }
1047
+ }
1048
+ },
1049
+
1050
+ /**
1051
+ * Asserts that a value matching some condition is present in an array. This uses
1052
+ * a function to determine a match.
1053
+ * @param {Function} matcher A function that returns true if the items matches or false if not.
1054
+ * @param {Array} haystack An array of values.
1055
+ * @param {String} message (Optional) The message to display if the assertion fails.
1056
+ * @method containsMatch
1057
+ * @static
1058
+ */
1059
+ containsMatch : function (matcher, haystack,
1060
+ message) {
1061
+
1062
+ YUITest.Assert._increment();
1063
+ //check for valid matcher
1064
+ if (typeof matcher != "function"){
1065
+ throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1066
+ }
1067
+
1068
+ if (!this._some(haystack, matcher)){
1069
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1070
+ }
1071
+ },
1072
+
1073
+ /**
1074
+ * Asserts that a value is not present in an array. This uses the triple equals
1075
+ * Asserts that a value is not present in an array. This uses the triple equals
1076
+ * sign so no type cohersion may occur.
1077
+ * @param {Object} needle The value that is expected in the array.
1078
+ * @param {Array} haystack An array of values.
1079
+ * @param {String} message (Optional) The message to display if the assertion fails.
1080
+ * @method doesNotContain
1081
+ * @static
1082
+ */
1083
+ doesNotContain : function (needle, haystack,
1084
+ message) {
1085
+
1086
+ YUITest.Assert._increment();
1087
+
1088
+ if (this._indexOf(haystack, needle) > -1){
1089
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1090
+ }
1091
+ },
1092
+
1093
+ /**
1094
+ * Asserts that a set of values are not present in an array. This uses the triple equals
1095
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
1096
+ * not be found.
1097
+ * @param {Object[]} needles An array of values that are not expected in the array.
1098
+ * @param {Array} haystack An array of values to check.
1099
+ * @param {String} message (Optional) The message to display if the assertion fails.
1100
+ * @method doesNotContainItems
1101
+ * @static
1102
+ */
1103
+ doesNotContainItems : function (needles, haystack,
1104
+ message) {
1105
+
1106
+ YUITest.Assert._increment();
1107
+
1108
+ for (var i=0; i < needles.length; i++){
1109
+ if (this._indexOf(haystack, needles[i]) > -1){
1110
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1111
+ }
1112
+ }
1113
+
1114
+ },
1115
+
1116
+ /**
1117
+ * Asserts that no values matching a condition are present in an array. This uses
1118
+ * a function to determine a match.
1119
+ * @param {Function} matcher A function that returns true if the item matches or false if not.
1120
+ * @param {Array} haystack An array of values.
1121
+ * @param {String} message (Optional) The message to display if the assertion fails.
1122
+ * @method doesNotContainMatch
1123
+ * @static
1124
+ */
1125
+ doesNotContainMatch : function (matcher, haystack,
1126
+ message) {
1127
+
1128
+ YUITest.Assert._increment();
1129
+
1130
+ //check for valid matcher
1131
+ if (typeof matcher != "function"){
1132
+ throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1133
+ }
1134
+
1135
+ if (this._some(haystack, matcher)){
1136
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1137
+ }
1138
+ },
1139
+
1140
+ /**
1141
+ * Asserts that the given value is contained in an array at the specified index.
1142
+ * This uses the triple equals sign so no type cohersion will occur.
1143
+ * @param {Object} needle The value to look for.
1144
+ * @param {Array} haystack The array to search in.
1145
+ * @param {int} index The index at which the value should exist.
1146
+ * @param {String} message (Optional) The message to display if the assertion fails.
1147
+ * @method indexOf
1148
+ * @static
1149
+ */
1150
+ indexOf : function (needle, haystack, index, message) {
1151
+
1152
+ YUITest.Assert._increment();
1153
+
1154
+ //try to find the value in the array
1155
+ for (var i=0; i < haystack.length; i++){
1156
+ if (haystack[i] === needle){
1157
+ if (index != i){
1158
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1159
+ }
1160
+ return;
1161
+ }
1162
+ }
1163
+
1164
+ //if it makes it here, it wasn't found at all
1165
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1166
+ },
1167
+
1168
+ /**
1169
+ * Asserts that the values in an array are equal, and in the same position,
1170
+ * as values in another array. This uses the double equals sign
1171
+ * so type cohersion may occur. Note that the array objects themselves
1172
+ * need not be the same for this test to pass.
1173
+ * @param {Array} expected An array of the expected values.
1174
+ * @param {Array} actual Any array of the actual values.
1175
+ * @param {String} message (Optional) The message to display if the assertion fails.
1176
+ * @method itemsAreEqual
1177
+ * @static
1178
+ */
1179
+ itemsAreEqual : function (expected, actual,
1180
+ message) {
1181
+
1182
+ YUITest.Assert._increment();
1183
+
1184
+ //first check array length
1185
+ if (expected.length != actual.length){
1186
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
1187
+ }
1188
+
1189
+ //begin checking values
1190
+ for (var i=0; i < expected.length; i++){
1191
+ if (expected[i] != actual[i]){
1192
+ throw new YUITest.Assert.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equal."), expected[i], actual[i]);
1193
+ }
1194
+ }
1195
+ },
1196
+
1197
+ /**
1198
+ * Asserts that the values in an array are equivalent, and in the same position,
1199
+ * as values in another array. This uses a function to determine if the values
1200
+ * are equivalent. Note that the array objects themselves
1201
+ * need not be the same for this test to pass.
1202
+ * @param {Array} expected An array of the expected values.
1203
+ * @param {Array} actual Any array of the actual values.
1204
+ * @param {Function} comparator A function that returns true if the values are equivalent
1205
+ * or false if not.
1206
+ * @param {String} message (Optional) The message to display if the assertion fails.
1207
+ * @return {Void}
1208
+ * @method itemsAreEquivalent
1209
+ * @static
1210
+ */
1211
+ itemsAreEquivalent : function (expected, actual,
1212
+ comparator, message) {
1213
+
1214
+ YUITest.Assert._increment();
1215
+
1216
+ //make sure the comparator is valid
1217
+ if (typeof comparator != "function"){
1218
+ throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1219
+ }
1220
+
1221
+ //first check array length
1222
+ if (expected.length != actual.length){
1223
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
1224
+ }
1225
+
1226
+ //begin checking values
1227
+ for (var i=0; i < expected.length; i++){
1228
+ if (!comparator(expected[i], actual[i])){
1229
+ throw new YUITest.Assert.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1230
+ }
1231
+ }
1232
+ },
1233
+
1234
+ /**
1235
+ * Asserts that an array is empty.
1236
+ * @param {Array} actual The array to test.
1237
+ * @param {String} message (Optional) The message to display if the assertion fails.
1238
+ * @method isEmpty
1239
+ * @static
1240
+ */
1241
+ isEmpty : function (actual, message) {
1242
+ YUITest.Assert._increment();
1243
+ if (actual.length > 0){
1244
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should be empty."));
1245
+ }
1246
+ },
1247
+
1248
+ /**
1249
+ * Asserts that an array is not empty.
1250
+ * @param {Array} actual The array to test.
1251
+ * @param {String} message (Optional) The message to display if the assertion fails.
1252
+ * @method isNotEmpty
1253
+ * @static
1254
+ */
1255
+ isNotEmpty : function (actual, message) {
1256
+ YUITest.Assert._increment();
1257
+ if (actual.length === 0){
1258
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should not be empty."));
1259
+ }
1260
+ },
1261
+
1262
+ /**
1263
+ * Asserts that the values in an array are the same, and in the same position,
1264
+ * as values in another array. This uses the triple equals sign
1265
+ * so no type cohersion will occur. Note that the array objects themselves
1266
+ * need not be the same for this test to pass.
1267
+ * @param {Array} expected An array of the expected values.
1268
+ * @param {Array} actual Any array of the actual values.
1269
+ * @param {String} message (Optional) The message to display if the assertion fails.
1270
+ * @method itemsAreSame
1271
+ * @static
1272
+ */
1273
+ itemsAreSame : function (expected, actual,
1274
+ message) {
1275
+
1276
+ YUITest.Assert._increment();
1277
+
1278
+ //first check array length
1279
+ if (expected.length != actual.length){
1280
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
1281
+ }
1282
+
1283
+ //begin checking values
1284
+ for (var i=0; i < expected.length; i++){
1285
+ if (expected[i] !== actual[i]){
1286
+ throw new YUITest.Assert.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not the same."), expected[i], actual[i]);
1287
+ }
1288
+ }
1289
+ },
1290
+
1291
+ /**
1292
+ * Asserts that the given value is contained in an array at the specified index,
1293
+ * starting from the back of the array.
1294
+ * This uses the triple equals sign so no type cohersion will occur.
1295
+ * @param {Object} needle The value to look for.
1296
+ * @param {Array} haystack The array to search in.
1297
+ * @param {int} index The index at which the value should exist.
1298
+ * @param {String} message (Optional) The message to display if the assertion fails.
1299
+ * @method lastIndexOf
1300
+ * @static
1301
+ */
1302
+ lastIndexOf : function (needle, haystack, index, message) {
1303
+
1304
+ //try to find the value in the array
1305
+ for (var i=haystack.length; i >= 0; i--){
1306
+ if (haystack[i] === needle){
1307
+ if (index != i){
1308
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1309
+ }
1310
+ return;
1311
+ }
1312
+ }
1313
+
1314
+ //if it makes it here, it wasn't found at all
1315
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array."));
1316
+ }
1317
+
1318
+ };
1319
+
1320
+
1321
+ /**
1322
+ * The ObjectAssert object provides functions to test JavaScript objects
1323
+ * for a variety of cases.
1324
+ * @namespace YUITest
1325
+ * @class ObjectAssert
1326
+ * @static
1327
+ */
1328
+ YUITest.ObjectAssert = {
1329
+
1330
+ /**
1331
+ * Asserts that an object has all of the same properties
1332
+ * and property values as the other.
1333
+ * @param {Object} expected The object with all expected properties and values.
1334
+ * @param {Object} actual The object to inspect.
1335
+ * @param {String} message (Optional) The message to display if the assertion fails.
1336
+ * @method areEqual
1337
+ * @static
1338
+ * @deprecated
1339
+ */
1340
+ areEqual: function(expected, actual, message) {
1341
+ YUITest.Assert._increment();
1342
+
1343
+ for (var name in expected){
1344
+ if (expected.hasOwnProperty(name)){
1345
+ if (expected[name] != actual[name]){
1346
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal for property " + name), expected[name], actual[name]);
1347
+ }
1348
+ }
1349
+ }
1350
+ },
1351
+
1352
+ /**
1353
+ * Asserts that an object has a property with the given name.
1354
+ * @param {String} propertyName The name of the property to test.
1355
+ * @param {Object} object The object to search.
1356
+ * @param {String} message (Optional) The message to display if the assertion fails.
1357
+ * @method hasKey
1358
+ * @static
1359
+ * @deprecated Use ownsOrInheritsKey() instead
1360
+ */
1361
+ hasKey: function (propertyName, object, message) {
1362
+ YUITest.ObjectAssert.ownsOrInheritsKey(propertyName, object, message);
1363
+ },
1364
+
1365
+ /**
1366
+ * Asserts that an object has all properties of a reference object.
1367
+ * @param {Array} properties An array of property names that should be on the object.
1368
+ * @param {Object} object The object to search.
1369
+ * @param {String} message (Optional) The message to display if the assertion fails.
1370
+ * @method hasKeys
1371
+ * @static
1372
+ * @deprecated Use ownsOrInheritsKeys() instead
1373
+ */
1374
+ hasKeys: function (properties, object, message) {
1375
+ YUITest.ObjectAssert.ownsOrInheritsKeys(properties, objects, message);
1376
+ },
1377
+
1378
+ /**
1379
+ * Asserts that a property with the given name exists on an object's prototype.
1380
+ * @param {String} propertyName The name of the property to test.
1381
+ * @param {Object} object The object to search.
1382
+ * @param {String} message (Optional) The message to display if the assertion fails.
1383
+ * @method inheritsKey
1384
+ * @static
1385
+ */
1386
+ inheritsKey: function (propertyName, object, message) {
1387
+ YUITest.Assert._increment();
1388
+ if (!(propertyName in object && !object.hasOwnProperty(propertyName))){
1389
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1390
+ }
1391
+ },
1392
+
1393
+ /**
1394
+ * Asserts that all properties exist on an object prototype.
1395
+ * @param {Array} properties An array of property names that should be on the object.
1396
+ * @param {Object} object The object to search.
1397
+ * @param {String} message (Optional) The message to display if the assertion fails.
1398
+ * @method inheritsKeys
1399
+ * @static
1400
+ */
1401
+ inheritsKeys: function (properties, object, message) {
1402
+ YUITest.Assert._increment();
1403
+ for (var i=0; i < properties.length; i++){
1404
+ if (!(propertyName in object && !object.hasOwnProperty(properties[i]))){
1405
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
1406
+ }
1407
+ }
1408
+ },
1409
+
1410
+ /**
1411
+ * Asserts that a property with the given name exists on an object instance (not on its prototype).
1412
+ * @param {String} propertyName The name of the property to test.
1413
+ * @param {Object} object The object to search.
1414
+ * @param {String} message (Optional) The message to display if the assertion fails.
1415
+ * @method ownsKey
1416
+ * @static
1417
+ */
1418
+ ownsKey: function (propertyName, object, message) {
1419
+ YUITest.Assert._increment();
1420
+ if (!object.hasOwnProperty(propertyName)){
1421
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1422
+ }
1423
+ },
1424
+
1425
+ /**
1426
+ * Asserts that all properties exist on an object instance (not on its prototype).
1427
+ * @param {Array} properties An array of property names that should be on the object.
1428
+ * @param {Object} object The object to search.
1429
+ * @param {String} message (Optional) The message to display if the assertion fails.
1430
+ * @method ownsKeys
1431
+ * @static
1432
+ */
1433
+ ownsKeys: function (properties, object, message) {
1434
+ YUITest.Assert._increment();
1435
+ for (var i=0; i < properties.length; i++){
1436
+ if (!object.hasOwnProperty(properties[i])){
1437
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
1438
+ }
1439
+ }
1440
+ },
1441
+
1442
+ /**
1443
+ * Asserts that an object owns no properties.
1444
+ * @param {Object} object The object to check.
1445
+ * @param {String} message (Optional) The message to display if the assertion fails.
1446
+ * @method ownsNoKeys
1447
+ * @static
1448
+ */
1449
+ ownsNoKeys : function (object, message) {
1450
+ YUITest.Assert._increment();
1451
+ var count = 0,
1452
+ name;
1453
+ for (name in object){
1454
+ if (object.hasOwnProperty(name)){
1455
+ count++;
1456
+ }
1457
+ }
1458
+
1459
+ if (count !== 0){
1460
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Object owns " + count + " properties but should own none."));
1461
+ }
1462
+
1463
+ },
1464
+
1465
+ /**
1466
+ * Asserts that an object has a property with the given name.
1467
+ * @param {String} propertyName The name of the property to test.
1468
+ * @param {Object} object The object to search.
1469
+ * @param {String} message (Optional) The message to display if the assertion fails.
1470
+ * @method ownsOrInheritsKey
1471
+ * @static
1472
+ */
1473
+ ownsOrInheritsKey: function (propertyName, object, message) {
1474
+ YUITest.Assert._increment();
1475
+ if (!(propertyName in object)){
1476
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
1477
+ }
1478
+ },
1479
+
1480
+ /**
1481
+ * Asserts that an object has all properties of a reference object.
1482
+ * @param {Array} properties An array of property names that should be on the object.
1483
+ * @param {Object} object The object to search.
1484
+ * @param {String} message (Optional) The message to display if the assertion fails.
1485
+ * @method ownsOrInheritsKeys
1486
+ * @static
1487
+ */
1488
+ ownsOrInheritsKeys: function (properties, object, message) {
1489
+ YUITest.Assert._increment();
1490
+ for (var i=0; i < properties.length; i++){
1491
+ if (!(properties[i] in object)){
1492
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object."));
1493
+ }
1494
+ }
1495
+ }
1496
+ };
1497
+
1498
+
1499
+
1500
+ /**
1501
+ * The DateAssert object provides functions to test JavaScript Date objects
1502
+ * for a variety of cases.
1503
+ * @namespace YUITest
1504
+ * @class DateAssert
1505
+ * @static
1506
+ */
1507
+
1508
+ YUITest.DateAssert = {
1509
+
1510
+ /**
1511
+ * Asserts that a date's month, day, and year are equal to another date's.
1512
+ * @param {Date} expected The expected date.
1513
+ * @param {Date} actual The actual date to test.
1514
+ * @param {String} message (Optional) The message to display if the assertion fails.
1515
+ * @method datesAreEqual
1516
+ * @static
1517
+ */
1518
+ datesAreEqual : function (expected, actual, message){
1519
+ YUITest.Assert._increment();
1520
+ if (expected instanceof Date && actual instanceof Date){
1521
+ var msg = "";
1522
+
1523
+ //check years first
1524
+ if (expected.getFullYear() != actual.getFullYear()){
1525
+ msg = "Years should be equal.";
1526
+ }
1527
+
1528
+ //now check months
1529
+ if (expected.getMonth() != actual.getMonth()){
1530
+ msg = "Months should be equal.";
1531
+ }
1532
+
1533
+ //last, check the day of the month
1534
+ if (expected.getDate() != actual.getDate()){
1535
+ msg = "Days of month should be equal.";
1536
+ }
1537
+
1538
+ if (msg.length){
1539
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
1540
+ }
1541
+ } else {
1542
+ throw new TypeError("YUITest.DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
1543
+ }
1544
+ },
1545
+
1546
+ /**
1547
+ * Asserts that a date's hour, minutes, and seconds are equal to another date's.
1548
+ * @param {Date} expected The expected date.
1549
+ * @param {Date} actual The actual date to test.
1550
+ * @param {String} message (Optional) The message to display if the assertion fails.
1551
+ * @method timesAreEqual
1552
+ * @static
1553
+ */
1554
+ timesAreEqual : function (expected, actual, message){
1555
+ YUITest.Assert._increment();
1556
+ if (expected instanceof Date && actual instanceof Date){
1557
+ var msg = "";
1558
+
1559
+ //check hours first
1560
+ if (expected.getHours() != actual.getHours()){
1561
+ msg = "Hours should be equal.";
1562
+ }
1563
+
1564
+ //now check minutes
1565
+ if (expected.getMinutes() != actual.getMinutes()){
1566
+ msg = "Minutes should be equal.";
1567
+ }
1568
+
1569
+ //last, check the seconds
1570
+ if (expected.getSeconds() != actual.getSeconds()){
1571
+ msg = "Seconds should be equal.";
1572
+ }
1573
+
1574
+ if (msg.length){
1575
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
1576
+ }
1577
+ } else {
1578
+ throw new TypeError("YUITest.DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
1579
+ }
1580
+ }
1581
+
1582
+ };
1583
+
1584
+ /**
1585
+ * Creates a new mock object.
1586
+ * @namespace YUITest
1587
+ * @class Mock
1588
+ * @constructor
1589
+ * @param {Object} template (Optional) An object whose methods
1590
+ * should be stubbed out on the mock object.
1591
+ */
1592
+ YUITest.Mock = function(template){
1593
+
1594
+ //use blank object is nothing is passed in
1595
+ template = template || {};
1596
+
1597
+ var mock,
1598
+ name;
1599
+
1600
+ //try to create mock that keeps prototype chain intact
1601
+ //fails in the case of ActiveX objects
1602
+ try {
1603
+ function f(){}
1604
+ f.prototype = template;
1605
+ mock = new f();
1606
+ } catch (ex) {
1607
+ mock = {};
1608
+ }
1609
+
1610
+ //create stubs for all methods
1611
+ for (name in template){
1612
+ if (template.hasOwnProperty(name)){
1613
+ if (typeof template[name] == "function"){
1614
+ mock[name] = function(name){
1615
+ return function(){
1616
+ YUITest.Assert.fail("Method " + name + "() was called but was not expected to be.");
1617
+ };
1618
+ }(name);
1619
+ }
1620
+ }
1621
+ }
1622
+
1623
+ //return it
1624
+ return mock;
1625
+ };
1626
+
1627
+ /**
1628
+ * Assigns an expectation to a mock object. This is used to create
1629
+ * methods and properties on the mock object that are monitored for
1630
+ * calls and changes, respectively.
1631
+ * @param {Object} mock The object to add the expectation to.
1632
+ * @param {Object} expectation An object defining the expectation. For
1633
+ * a method, the keys "method" and "args" are required with
1634
+ * an optional "returns" key available. For properties, the keys
1635
+ * "property" and "value" are required.
1636
+ * @return {void}
1637
+ * @method expect
1638
+ * @static
1639
+ */
1640
+ YUITest.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
1641
+
1642
+ //make sure there's a place to store the expectations
1643
+ if (!mock.__expectations) {
1644
+ mock.__expectations = {};
1645
+ }
1646
+
1647
+ //method expectation
1648
+ if (expectation.method){
1649
+ var name = expectation.method,
1650
+ args = expectation.args || [],
1651
+ result = expectation.returns,
1652
+ callCount = (typeof expectation.callCount == "number") ? expectation.callCount : 1,
1653
+ error = expectation.error,
1654
+ run = expectation.run || function(){},
1655
+ i;
1656
+
1657
+ //save expectations
1658
+ mock.__expectations[name] = expectation;
1659
+ expectation.callCount = callCount;
1660
+ expectation.actualCallCount = 0;
1661
+
1662
+ //process arguments
1663
+ for (i=0; i < args.length; i++){
1664
+ if (!(args[i] instanceof YUITest.Mock.Value)){
1665
+ args[i] = YUITest.Mock.Value(YUITest.Assert.areSame, [args[i]], "Argument " + i + " of " + name + "() is incorrect.");
1666
+ }
1667
+ }
1668
+
1669
+ //if the method is expected to be called
1670
+ if (callCount > 0){
1671
+ mock[name] = function(){
1672
+ try {
1673
+ expectation.actualCallCount++;
1674
+ YUITest.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
1675
+ for (var i=0, len=args.length; i < len; i++){
1676
+ args[i].verify(arguments[i]);
1677
+ }
1678
+
1679
+ run.apply(this, arguments);
1680
+
1681
+ if (error){
1682
+ throw error;
1683
+ }
1684
+ } catch (ex){
1685
+ //route through TestRunner for proper handling
1686
+ YUITest.TestRunner._handleError(ex);
1687
+ }
1688
+
1689
+ return result;
1690
+ };
1691
+ } else {
1692
+
1693
+ //method should fail if called when not expected
1694
+ mock[name] = function(){
1695
+ try {
1696
+ YUITest.Assert.fail("Method " + name + "() should not have been called.");
1697
+ } catch (ex){
1698
+ //route through TestRunner for proper handling
1699
+ YUITest.TestRunner._handleError(ex);
1700
+ }
1701
+ };
1702
+ }
1703
+ } else if (expectation.property){
1704
+ //save expectations
1705
+ mock.__expectations[name] = expectation;
1706
+ }
1707
+ };
1708
+
1709
+ /**
1710
+ * Verifies that all expectations of a mock object have been met and
1711
+ * throws an assertion error if not.
1712
+ * @param {Object} mock The object to verify..
1713
+ * @return {void}
1714
+ * @method verify
1715
+ * @static
1716
+ */
1717
+ YUITest.Mock.verify = function(mock){
1718
+ try {
1719
+
1720
+ for (var name in mock.__expectations){
1721
+ if (mock.__expectations.hasOwnProperty(name)){
1722
+ var expectation = mock.__expectations[name];
1723
+ if (expectation.method) {
1724
+ YUITest.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
1725
+ } else if (expectation.property){
1726
+ YUITest.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value.");
1727
+ }
1728
+ }
1729
+ }
1730
+
1731
+ } catch (ex){
1732
+ //route through TestRunner for proper handling
1733
+ YUITest.TestRunner._handleError(ex);
1734
+ }
1735
+ };
1736
+
1737
+ /**
1738
+ * Creates a new value matcher.
1739
+ * @param {Function} method The function to call on the value.
1740
+ * @param {Array} originalArgs (Optional) Array of arguments to pass to the method.
1741
+ * @param {String} message (Optional) Message to display in case of failure.
1742
+ * @namespace YUITest.Mock
1743
+ * @class Value
1744
+ * @constructor
1745
+ */
1746
+ YUITest.Mock.Value = function(method, originalArgs, message){
1747
+ if (this instanceof YUITest.Mock.Value){
1748
+ this.verify = function(value){
1749
+ var args = [].concat(originalArgs || []);
1750
+ args.push(value);
1751
+ args.push(message);
1752
+ method.apply(null, args);
1753
+ };
1754
+ } else {
1755
+ return new YUITest.Mock.Value(method, originalArgs, message);
1756
+ }
1757
+ };
1758
+
1759
+ /**
1760
+ * Predefined matcher to match any value.
1761
+ * @property Any
1762
+ * @static
1763
+ * @type Function
1764
+ */
1765
+ YUITest.Mock.Value.Any = YUITest.Mock.Value(function(){});
1766
+
1767
+ /**
1768
+ * Predefined matcher to match boolean values.
1769
+ * @property Boolean
1770
+ * @static
1771
+ * @type Function
1772
+ */
1773
+ YUITest.Mock.Value.Boolean = YUITest.Mock.Value(YUITest.Assert.isBoolean);
1774
+
1775
+ /**
1776
+ * Predefined matcher to match number values.
1777
+ * @property Number
1778
+ * @static
1779
+ * @type Function
1780
+ */
1781
+ YUITest.Mock.Value.Number = YUITest.Mock.Value(YUITest.Assert.isNumber);
1782
+
1783
+ /**
1784
+ * Predefined matcher to match string values.
1785
+ * @property String
1786
+ * @static
1787
+ * @type Function
1788
+ */
1789
+ YUITest.Mock.Value.String = YUITest.Mock.Value(YUITest.Assert.isString);
1790
+
1791
+ /**
1792
+ * Predefined matcher to match object values.
1793
+ * @property Object
1794
+ * @static
1795
+ * @type Function
1796
+ */
1797
+ YUITest.Mock.Value.Object = YUITest.Mock.Value(YUITest.Assert.isObject);
1798
+
1799
+ /**
1800
+ * Predefined matcher to match function values.
1801
+ * @property Function
1802
+ * @static
1803
+ * @type Function
1804
+ */
1805
+ YUITest.Mock.Value.Function = YUITest.Mock.Value(YUITest.Assert.isFunction);
1806
+
1807
+ /**
1808
+ * Convenience type for storing and aggregating
1809
+ * test result information.
1810
+ * @private
1811
+ * @namespace YUITest
1812
+ * @class Results
1813
+ * @constructor
1814
+ * @param {String} name The name of the test.
1815
+ */
1816
+ YUITest.Results = function(name){
1817
+
1818
+ /**
1819
+ * Name of the test, test case, or test suite.
1820
+ * @type String
1821
+ * @property name
1822
+ */
1823
+ this.name = name;
1824
+
1825
+ /**
1826
+ * Number of passed tests.
1827
+ * @type int
1828
+ * @property passed
1829
+ */
1830
+ this.passed = 0;
1831
+
1832
+ /**
1833
+ * Number of failed tests.
1834
+ * @type int
1835
+ * @property failed
1836
+ */
1837
+ this.failed = 0;
1838
+
1839
+ /**
1840
+ * Number of errors that occur in non-test methods.
1841
+ * @type int
1842
+ * @property errors
1843
+ */
1844
+ this.errors = 0;
1845
+
1846
+ /**
1847
+ * Number of ignored tests.
1848
+ * @type int
1849
+ * @property ignored
1850
+ */
1851
+ this.ignored = 0;
1852
+
1853
+ /**
1854
+ * Number of total tests.
1855
+ * @type int
1856
+ * @property total
1857
+ */
1858
+ this.total = 0;
1859
+
1860
+ /**
1861
+ * Amount of time (ms) it took to complete testing.
1862
+ * @type int
1863
+ * @property duration
1864
+ */
1865
+ this.duration = 0;
1866
+ };
1867
+
1868
+ /**
1869
+ * Includes results from another results object into this one.
1870
+ * @param {YUITest.Results} result The results object to include.
1871
+ * @method include
1872
+ * @return {void}
1873
+ */
1874
+ YUITest.Results.prototype.include = function(results){
1875
+ this.passed += results.passed;
1876
+ this.failed += results.failed;
1877
+ this.ignored += results.ignored;
1878
+ this.total += results.total;
1879
+ this.errors += results.errors;
1880
+ };
1881
+
1882
+ /**
1883
+ * Test case containing various tests to run.
1884
+ * @param template An object containing any number of test methods, other methods,
1885
+ * an optional name, and anything else the test case needs.
1886
+ * @class TestCase
1887
+ * @namespace YUITest
1888
+ * @constructor
1889
+ */
1890
+ YUITest.TestCase = function (template) {
1891
+
1892
+ /**
1893
+ * Special rules for the test case. Possible subobjects
1894
+ * are fail, for tests that should fail, and error, for
1895
+ * tests that should throw an error.
1896
+ */
1897
+ this._should = {};
1898
+
1899
+ //copy over all properties from the template to this object
1900
+ for (var prop in template) {
1901
+ this[prop] = template[prop];
1902
+ }
1903
+
1904
+ //check for a valid name
1905
+ if (typeof this.name != "string"){
1906
+ this.name = "testCase" + (+new Date());
1907
+ }
1908
+
1909
+ };
1910
+
1911
+ YUITest.TestCase.prototype = {
1912
+
1913
+ //restore constructor
1914
+ constructor: YUITest.TestCase,
1915
+
1916
+ /**
1917
+ * Method to call from an async init method to
1918
+ * restart the test case. When called, returns a function
1919
+ * that should be called when tests are ready to continue.
1920
+ * @method callback
1921
+ * @return {Function} The function to call as a callback.
1922
+ */
1923
+ callback: function(){
1924
+ return YUITest.TestRunner.callback.apply(YUITest.TestRunner,arguments);
1925
+ },
1926
+
1927
+ /**
1928
+ * Resumes a paused test and runs the given function.
1929
+ * @param {Function} segment (Optional) The function to run.
1930
+ * If omitted, the test automatically passes.
1931
+ * @return {Void}
1932
+ * @method resume
1933
+ */
1934
+ resume : function (segment) {
1935
+ YUITest.TestRunner.resume(segment);
1936
+ },
1937
+
1938
+ /**
1939
+ * Causes the test case to wait a specified amount of time and then
1940
+ * continue executing the given code.
1941
+ * @param {Function} segment (Optional) The function to run after the delay.
1942
+ * If omitted, the TestRunner will wait until resume() is called.
1943
+ * @param {int} delay (Optional) The number of milliseconds to wait before running
1944
+ * the function. If omitted, defaults to zero.
1945
+ * @return {Void}
1946
+ * @method wait
1947
+ */
1948
+ wait : function (segment, delay){
1949
+
1950
+ var actualDelay = (typeof segment == "number" ? segment : delay);
1951
+ actualDelay = (typeof actualDelay == "number" ? actualDelay : 10000);
1952
+
1953
+ if (typeof segment == "function"){
1954
+ throw new YUITest.Wait(segment, actualDelay);
1955
+ } else {
1956
+ throw new YUITest.Wait(function(){
1957
+ YUITest.Assert.fail("Timeout: wait() called but resume() never called.");
1958
+ }, actualDelay);
1959
+ }
1960
+ },
1961
+
1962
+ //-------------------------------------------------------------------------
1963
+ // Assertion Methods
1964
+ //-------------------------------------------------------------------------
1965
+
1966
+ /**
1967
+ * Asserts that a given condition is true. If not, then a YUITest.AssertionError object is thrown
1968
+ * and the test fails.
1969
+ * @method assert
1970
+ * @param {Boolean} condition The condition to test.
1971
+ * @param {String} message The message to display if the assertion fails.
1972
+ */
1973
+ assert : function (condition, message){
1974
+ YUITest.Assert._increment();
1975
+ if (!condition){
1976
+ throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Assertion failed."));
1977
+ }
1978
+ },
1979
+
1980
+ /**
1981
+ * Forces an assertion error to occur. Shortcut for YUITest.Assert.fail().
1982
+ * @method fail
1983
+ * @param {String} message (Optional) The message to display with the failure.
1984
+ */
1985
+ fail: function (message) {
1986
+ YUITest.Assert.fail(message);
1987
+ },
1988
+
1989
+ //-------------------------------------------------------------------------
1990
+ // Stub Methods
1991
+ //-------------------------------------------------------------------------
1992
+
1993
+ /**
1994
+ * Function to run once before tests start to run.
1995
+ * This executes before the first call to setUp().
1996
+ */
1997
+ init: function(){
1998
+ //noop
1999
+ },
2000
+
2001
+ /**
2002
+ * Function to run once after tests finish running.
2003
+ * This executes after the last call to tearDown().
2004
+ */
2005
+ destroy: function(){
2006
+ //noop
2007
+ },
2008
+
2009
+ /**
2010
+ * Function to run before each test is executed.
2011
+ * @return {Void}
2012
+ * @method setUp
2013
+ */
2014
+ setUp : function () {
2015
+ //noop
2016
+ },
2017
+
2018
+ /**
2019
+ * Function to run after each test is executed.
2020
+ * @return {Void}
2021
+ * @method tearDown
2022
+ */
2023
+ tearDown: function () {
2024
+ //noop
2025
+ }
2026
+ };
2027
+
2028
+
2029
+
2030
+ /**
2031
+ * A test suite that can contain a collection of TestCase and TestSuite objects.
2032
+ * @param {String||Object} data The name of the test suite or an object containing
2033
+ * a name property as well as setUp and tearDown methods.
2034
+ * @namespace YUITest
2035
+ * @class TestSuite
2036
+ * @constructor
2037
+ */
2038
+ YUITest.TestSuite = function (data) {
2039
+
2040
+ /**
2041
+ * The name of the test suite.
2042
+ * @type String
2043
+ * @property name
2044
+ */
2045
+ this.name = "";
2046
+
2047
+ /**
2048
+ * Array of test suites and test cases.
2049
+ * @type Array
2050
+ * @property items
2051
+ * @private
2052
+ */
2053
+ this.items = [];
2054
+
2055
+ //initialize the properties
2056
+ if (typeof data == "string"){
2057
+ this.name = data;
2058
+ } else if (data instanceof Object){
2059
+ for (var prop in data){
2060
+ if (data.hasOwnProperty(prop)){
2061
+ this[prop] = data[prop];
2062
+ }
2063
+ }
2064
+ }
2065
+
2066
+ //double-check name
2067
+ if (this.name === ""){
2068
+ this.name = "testSuite" + (+new Date());
2069
+ }
2070
+
2071
+ };
2072
+
2073
+ YUITest.TestSuite.prototype = {
2074
+
2075
+ //restore constructor
2076
+ constructor: YUITest.TestSuite,
2077
+
2078
+ /**
2079
+ * Adds a test suite or test case to the test suite.
2080
+ * @param {YUITest.TestSuite||YUITest.TestCase} testObject The test suite or test case to add.
2081
+ * @return {Void}
2082
+ * @method add
2083
+ */
2084
+ add : function (testObject) {
2085
+ if (testObject instanceof YUITest.TestSuite || testObject instanceof YUITest.TestCase) {
2086
+ this.items.push(testObject);
2087
+ }
2088
+ return this;
2089
+ },
2090
+
2091
+ //-------------------------------------------------------------------------
2092
+ // Stub Methods
2093
+ //-------------------------------------------------------------------------
2094
+
2095
+ /**
2096
+ * Function to run before each test is executed.
2097
+ * @return {Void}
2098
+ * @method setUp
2099
+ */
2100
+ setUp : function () {
2101
+ },
2102
+
2103
+ /**
2104
+ * Function to run after each test is executed.
2105
+ * @return {Void}
2106
+ * @method tearDown
2107
+ */
2108
+ tearDown: function () {
2109
+ }
2110
+
2111
+ };
2112
+
2113
+ /**
2114
+ * An object object containing test result formatting methods.
2115
+ * @namespace YUITest
2116
+ * @class TestFormat
2117
+ * @static
2118
+ */
2119
+ YUITest.TestFormat = function(){
2120
+
2121
+ /* (intentionally not documented)
2122
+ * Basic XML escaping method. Replaces quotes, less-than, greater-than,
2123
+ * apostrophe, and ampersand characters with their corresponding entities.
2124
+ * @param {String} text The text to encode.
2125
+ * @return {String} The XML-escaped text.
2126
+ */
2127
+ function xmlEscape(text){
2128
+
2129
+ return text.replace(/[<>"'&]/g, function(value){
2130
+ switch(value){
2131
+ case "<": return "&lt;";
2132
+ case ">": return "&gt;";
2133
+ case "\"": return "&quot;";
2134
+ case "'": return "&apos;";
2135
+ case "&": return "&amp;";
2136
+ }
2137
+ });
2138
+
2139
+ }
2140
+
2141
+
2142
+ return {
2143
+
2144
+ /**
2145
+ * Returns test results formatted as a JSON string. Requires JSON utility.
2146
+ * @param {Object} result The results object created by TestRunner.
2147
+ * @return {String} A JSON-formatted string of results.
2148
+ * @method JSON
2149
+ * @static
2150
+ */
2151
+ JSON: function(results) {
2152
+ return YUITest.Util.JSON.stringify(results);
2153
+ },
2154
+
2155
+ /**
2156
+ * Returns test results formatted as an XML string.
2157
+ * @param {Object} result The results object created by TestRunner.
2158
+ * @return {String} An XML-formatted string of results.
2159
+ * @method XML
2160
+ * @static
2161
+ */
2162
+ XML: function(results) {
2163
+
2164
+ function serializeToXML(results){
2165
+ var xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
2166
+
2167
+ if (typeof(results.duration)=="number"){
2168
+ xml += " duration=\"" + results.duration + "\"";
2169
+ }
2170
+
2171
+ if (results.type == "test"){
2172
+ xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
2173
+ } else {
2174
+ xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
2175
+ for (var prop in results){
2176
+ if (results.hasOwnProperty(prop)){
2177
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2178
+ xml += serializeToXML(results[prop]);
2179
+ }
2180
+ }
2181
+ }
2182
+ }
2183
+
2184
+ xml += "</" + results.type + ">";
2185
+
2186
+ return xml;
2187
+ }
2188
+
2189
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToXML(results);
2190
+
2191
+ },
2192
+
2193
+
2194
+ /**
2195
+ * Returns test results formatted in JUnit XML format.
2196
+ * @param {Object} result The results object created by TestRunner.
2197
+ * @return {String} An XML-formatted string of results.
2198
+ * @method JUnitXML
2199
+ * @static
2200
+ */
2201
+ JUnitXML: function(results) {
2202
+
2203
+ function serializeToJUnitXML(results){
2204
+ var xml = "";
2205
+
2206
+ switch (results.type){
2207
+ //equivalent to testcase in JUnit
2208
+ case "test":
2209
+ if (results.result != "ignore"){
2210
+ xml = "<testcase name=\"" + xmlEscape(results.name) + "\" time=\"" + (results.duration/1000) + "\">";
2211
+ if (results.result == "fail"){
2212
+ xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
2213
+ }
2214
+ xml+= "</testcase>";
2215
+ }
2216
+ break;
2217
+
2218
+ //equivalent to testsuite in JUnit
2219
+ case "testcase":
2220
+
2221
+ xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\" time=\"" + (results.duration/1000) + "\">";
2222
+
2223
+ for (var prop in results){
2224
+ if (results.hasOwnProperty(prop)){
2225
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2226
+ xml += serializeToJUnitXML(results[prop]);
2227
+ }
2228
+ }
2229
+ }
2230
+
2231
+ xml += "</testsuite>";
2232
+ break;
2233
+
2234
+ //no JUnit equivalent, don't output anything
2235
+ case "testsuite":
2236
+ for (var prop in results){
2237
+ if (results.hasOwnProperty(prop)){
2238
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2239
+ xml += serializeToJUnitXML(results[prop]);
2240
+ }
2241
+ }
2242
+ }
2243
+ break;
2244
+
2245
+ //top-level, equivalent to testsuites in JUnit
2246
+ case "report":
2247
+
2248
+ xml = "<testsuites>";
2249
+
2250
+ for (var prop in results){
2251
+ if (results.hasOwnProperty(prop)){
2252
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2253
+ xml += serializeToJUnitXML(results[prop]);
2254
+ }
2255
+ }
2256
+ }
2257
+
2258
+ xml += "</testsuites>";
2259
+
2260
+ //no default
2261
+ }
2262
+
2263
+ return xml;
2264
+
2265
+ }
2266
+
2267
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToJUnitXML(results);
2268
+ },
2269
+
2270
+ /**
2271
+ * Returns test results formatted in TAP format.
2272
+ * For more information, see <a href="http://testanything.org/">Test Anything Protocol</a>.
2273
+ * @param {Object} result The results object created by TestRunner.
2274
+ * @return {String} A TAP-formatted string of results.
2275
+ * @method TAP
2276
+ * @static
2277
+ */
2278
+ TAP: function(results) {
2279
+
2280
+ var currentTestNum = 1;
2281
+
2282
+ function serializeToTAP(results){
2283
+ var text = "";
2284
+
2285
+ switch (results.type){
2286
+
2287
+ case "test":
2288
+ if (results.result != "ignore"){
2289
+
2290
+ text = "ok " + (currentTestNum++) + " - " + results.name;
2291
+
2292
+ if (results.result == "fail"){
2293
+ text = "not " + text + " - " + results.message;
2294
+ }
2295
+
2296
+ text += "\n";
2297
+ } else {
2298
+ text = "#Ignored test " + results.name + "\n";
2299
+ }
2300
+ break;
2301
+
2302
+ case "testcase":
2303
+
2304
+ text = "#Begin testcase " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2305
+
2306
+ for (var prop in results){
2307
+ if (results.hasOwnProperty(prop)){
2308
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2309
+ text += serializeToTAP(results[prop]);
2310
+ }
2311
+ }
2312
+ }
2313
+
2314
+ text += "#End testcase " + results.name + "\n";
2315
+
2316
+
2317
+ break;
2318
+
2319
+ case "testsuite":
2320
+
2321
+ text = "#Begin testsuite " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2322
+
2323
+ for (var prop in results){
2324
+ if (results.hasOwnProperty(prop)){
2325
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2326
+ text += serializeToTAP(results[prop]);
2327
+ }
2328
+ }
2329
+ }
2330
+
2331
+ text += "#End testsuite " + results.name + "\n";
2332
+ break;
2333
+
2334
+ case "report":
2335
+
2336
+ for (var prop in results){
2337
+ if (results.hasOwnProperty(prop)){
2338
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2339
+ text += serializeToTAP(results[prop]);
2340
+ }
2341
+ }
2342
+ }
2343
+
2344
+ //no default
2345
+ }
2346
+
2347
+ return text;
2348
+
2349
+ }
2350
+
2351
+ return "1.." + results.total + "\n" + serializeToTAP(results);
2352
+ }
2353
+
2354
+ };
2355
+ }();
2356
+
2357
+ /**
2358
+ * An object object containing coverage result formatting methods.
2359
+ * @namespace YUITest
2360
+ * @class CoverageFormat
2361
+ * @static
2362
+ */
2363
+ YUITest.CoverageFormat = {
2364
+
2365
+ /**
2366
+ * Returns the coverage report in JSON format. This is the straight
2367
+ * JSON representation of the native coverage report.
2368
+ * @param {Object} coverage The coverage report object.
2369
+ * @return {String} A JSON-formatted string of coverage data.
2370
+ * @method JSON
2371
+ * @namespace YUITest.CoverageFormat
2372
+ */
2373
+ JSON: function(coverage){
2374
+ return YUITest.Util.JSON.stringify(coverage);
2375
+ },
2376
+
2377
+ /**
2378
+ * Returns the coverage report in a JSON format compatible with
2379
+ * Xdebug. See <a href="http://www.xdebug.com/docs/code_coverage">Xdebug Documentation</a>
2380
+ * for more information. Note: function coverage is not available
2381
+ * in this format.
2382
+ * @param {Object} coverage The coverage report object.
2383
+ * @return {String} A JSON-formatted string of coverage data.
2384
+ * @method XdebugJSON
2385
+ * @namespace YUITest.CoverageFormat
2386
+ */
2387
+ XdebugJSON: function(coverage){
2388
+
2389
+ var report = {};
2390
+ for (var prop in coverage){
2391
+ if (coverage.hasOwnProperty(prop)){
2392
+ report[prop] = coverage[prop].lines;
2393
+ }
2394
+ }
2395
+
2396
+ return YUITest.Util.JSON.stringify(coverage);
2397
+ }
2398
+
2399
+ };
2400
+
2401
+
2402
+ /**
2403
+ * An object object containing methods to simulate browser events.
2404
+ * @namespace YUITest
2405
+ * @class Event
2406
+ * @static
2407
+ */
2408
+ YUITest.Event = (function() {
2409
+
2410
+ var
2411
+
2412
+ //mouse events supported
2413
+ mouseEvents = {
2414
+ click: 1,
2415
+ dblclick: 1,
2416
+ mouseover: 1,
2417
+ mouseout: 1,
2418
+ mousedown: 1,
2419
+ mouseup: 1,
2420
+ mousemove: 1
2421
+ },
2422
+
2423
+ //key events supported
2424
+ keyEvents = {
2425
+ keydown: 1,
2426
+ keyup: 1,
2427
+ keypress: 1
2428
+ },
2429
+
2430
+ //HTML events supported
2431
+ uiEvents = {
2432
+ blur: 1,
2433
+ change: 1,
2434
+ focus: 1,
2435
+ resize: 1,
2436
+ scroll: 1,
2437
+ select: 1
2438
+ },
2439
+
2440
+ //events that bubble by default
2441
+ bubbleEvents = {
2442
+ scroll: 1,
2443
+ resize: 1,
2444
+ reset: 1,
2445
+ submit: 1,
2446
+ change: 1,
2447
+ select: 1,
2448
+ error: 1,
2449
+ abort: 1,
2450
+
2451
+ //also mouse events
2452
+ click: 1,
2453
+ dblclick: 1,
2454
+ mouseover: 1,
2455
+ mouseout: 1,
2456
+ mousedown: 1,
2457
+ mouseup: 1,
2458
+ mousemove: 1,
2459
+
2460
+ //and keyboard events
2461
+ keydown: 1,
2462
+ keyup: 1,
2463
+ keypress: 1
2464
+
2465
+ },
2466
+
2467
+ //the object to return
2468
+ object,
2469
+
2470
+ //used for property name iteration
2471
+ prop;
2472
+
2473
+ /*
2474
+ * Note: Intentionally not for YUIDoc generation.
2475
+ * Simulates a key event using the given event information to populate
2476
+ * the generated event object. This method does browser-equalizing
2477
+ * calculations to account for differences in the DOM and IE event models
2478
+ * as well as different browser quirks. Note: keydown causes Safari 2.x to
2479
+ * crash.
2480
+ * @method simulateKeyEvent
2481
+ * @private
2482
+ * @static
2483
+ * @param {HTMLElement} target The target of the given event.
2484
+ * @param {String} type The type of event to fire. This can be any one of
2485
+ * the following: keyup, keydown, and keypress.
2486
+ * @param {Boolean} bubbles (Optional) Indicates if the event can be
2487
+ * bubbled up. DOM Level 3 specifies that all key events bubble by
2488
+ * default. The default is true.
2489
+ * @param {Boolean} cancelable (Optional) Indicates if the event can be
2490
+ * canceled using preventDefault(). DOM Level 3 specifies that all
2491
+ * key events can be cancelled. The default
2492
+ * is true.
2493
+ * @param {Window} view (Optional) The view containing the target. This is
2494
+ * typically the window object. The default is window.
2495
+ * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
2496
+ * is pressed while the event is firing. The default is false.
2497
+ * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
2498
+ * is pressed while the event is firing. The default is false.
2499
+ * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
2500
+ * is pressed while the event is firing. The default is false.
2501
+ * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
2502
+ * is pressed while the event is firing. The default is false.
2503
+ * @param {int} keyCode (Optional) The code for the key that is in use.
2504
+ * The default is 0.
2505
+ * @param {int} charCode (Optional) The Unicode code for the character
2506
+ * associated with the key being used. The default is 0.
2507
+ */
2508
+ function simulateKeyEvent(target /*:HTMLElement*/, type /*:String*/,
2509
+ bubbles /*:Boolean*/, cancelable /*:Boolean*/,
2510
+ view /*:Window*/,
2511
+ ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
2512
+ shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
2513
+ keyCode /*:int*/, charCode /*:int*/) /*:Void*/
2514
+ {
2515
+ //check target
2516
+ if (!target){
2517
+ throw new Error("simulateKeyEvent(): Invalid target.");
2518
+ }
2519
+
2520
+ //check event type
2521
+ if (typeof type == "string"){
2522
+ type = type.toLowerCase();
2523
+ switch(type){
2524
+ case "textevent": //DOM Level 3
2525
+ type = "keypress";
2526
+ break;
2527
+ case "keyup":
2528
+ case "keydown":
2529
+ case "keypress":
2530
+ break;
2531
+ default:
2532
+ throw new Error("simulateKeyEvent(): Event type '" + type + "' not supported.");
2533
+ }
2534
+ } else {
2535
+ throw new Error("simulateKeyEvent(): Event type must be a string.");
2536
+ }
2537
+
2538
+ //setup default values
2539
+ if (typeof bubbles != "boolean"){
2540
+ bubbles = true; //all key events bubble
2541
+ }
2542
+ if (typeof cancelable != "boolean"){
2543
+ cancelable = true; //all key events can be cancelled
2544
+ }
2545
+ if (typeof view != "object" || view == null){
2546
+ view = window; //view is typically window
2547
+ }
2548
+ if (typeof ctrlKey != "boolean"){
2549
+ ctrlKey = false;
2550
+ }
2551
+ if (typeof altKey != "boolean"){
2552
+ altKey = false;
2553
+ }
2554
+ if (typeof shiftKey != "boolean"){
2555
+ shiftKey = false;
2556
+ }
2557
+ if (typeof metaKey != "boolean"){
2558
+ metaKey = false;
2559
+ }
2560
+ if (typeof keyCode != "number"){
2561
+ keyCode = 0;
2562
+ }
2563
+ if (typeof charCode != "number"){
2564
+ charCode = 0;
2565
+ }
2566
+
2567
+ //try to create a mouse event
2568
+ var customEvent = null;
2569
+
2570
+ //check for DOM-compliant browsers first
2571
+ if (typeof document.createEvent == "function"){
2572
+
2573
+ try {
2574
+
2575
+ //try to create key event
2576
+ customEvent = document.createEvent("KeyEvents");
2577
+
2578
+ /*
2579
+ * Interesting problem: Firefox implemented a non-standard
2580
+ * version of initKeyEvent() based on DOM Level 2 specs.
2581
+ * Key event was removed from DOM Level 2 and re-introduced
2582
+ * in DOM Level 3 with a different interface. Firefox is the
2583
+ * only browser with any implementation of Key Events, so for
2584
+ * now, assume it's Firefox if the above line doesn't error.
2585
+ */
2586
+ // @TODO: Decipher between Firefox's implementation and a correct one.
2587
+ customEvent.initKeyEvent(type, bubbles, cancelable, view, ctrlKey,
2588
+ altKey, shiftKey, metaKey, keyCode, charCode);
2589
+
2590
+ } catch (ex){
2591
+
2592
+ /*
2593
+ * If it got here, that means key events aren't officially supported.
2594
+ * Safari/WebKit is a real problem now. WebKit 522 won't let you
2595
+ * set keyCode, charCode, or other properties if you use a
2596
+ * UIEvent, so we first must try to create a generic event. The
2597
+ * fun part is that this will throw an error on Safari 2.x. The
2598
+ * end result is that we need another try...catch statement just to
2599
+ * deal with this mess.
2600
+ */
2601
+ try {
2602
+
2603
+ //try to create generic event - will fail in Safari 2.x
2604
+ customEvent = document.createEvent("Events");
2605
+
2606
+ } catch (uierror /*:Error*/){
2607
+
2608
+ //the above failed, so create a UIEvent for Safari 2.x
2609
+ customEvent = document.createEvent("UIEvents");
2610
+
2611
+ } finally {
2612
+
2613
+ customEvent.initEvent(type, bubbles, cancelable);
2614
+
2615
+ //initialize
2616
+ customEvent.view = view;
2617
+ customEvent.altKey = altKey;
2618
+ customEvent.ctrlKey = ctrlKey;
2619
+ customEvent.shiftKey = shiftKey;
2620
+ customEvent.metaKey = metaKey;
2621
+ customEvent.keyCode = keyCode;
2622
+ customEvent.charCode = charCode;
2623
+
2624
+ }
2625
+
2626
+ }
2627
+
2628
+ //fire the event
2629
+ target.dispatchEvent(customEvent);
2630
+
2631
+ } else if (typeof document.createEventObject != "undefined"){ //IE
2632
+
2633
+ //create an IE event object
2634
+ customEvent = document.createEventObject();
2635
+
2636
+ //assign available properties
2637
+ customEvent.bubbles = bubbles;
2638
+ customEvent.cancelable = cancelable;
2639
+ customEvent.view = view;
2640
+ customEvent.ctrlKey = ctrlKey;
2641
+ customEvent.altKey = altKey;
2642
+ customEvent.shiftKey = shiftKey;
2643
+ customEvent.metaKey = metaKey;
2644
+
2645
+ /*
2646
+ * IE doesn't support charCode explicitly. CharCode should
2647
+ * take precedence over any keyCode value for accurate
2648
+ * representation.
2649
+ */
2650
+ customEvent.keyCode = (charCode > 0) ? charCode : keyCode;
2651
+
2652
+ //fire the event
2653
+ target.fireEvent("on" + type, customEvent);
2654
+
2655
+ } else {
2656
+ throw new Error("simulateKeyEvent(): No event simulation framework present.");
2657
+ }
2658
+ }
2659
+
2660
+ /*
2661
+ * Note: Intentionally not for YUIDoc generation.
2662
+ * Simulates a mouse event using the given event information to populate
2663
+ * the generated event object. This method does browser-equalizing
2664
+ * calculations to account for differences in the DOM and IE event models
2665
+ * as well as different browser quirks.
2666
+ * @method simulateMouseEvent
2667
+ * @private
2668
+ * @static
2669
+ * @param {HTMLElement} target The target of the given event.
2670
+ * @param {String} type The type of event to fire. This can be any one of
2671
+ * the following: click, dblclick, mousedown, mouseup, mouseout,
2672
+ * mouseover, and mousemove.
2673
+ * @param {Boolean} bubbles (Optional) Indicates if the event can be
2674
+ * bubbled up. DOM Level 2 specifies that all mouse events bubble by
2675
+ * default. The default is true.
2676
+ * @param {Boolean} cancelable (Optional) Indicates if the event can be
2677
+ * canceled using preventDefault(). DOM Level 2 specifies that all
2678
+ * mouse events except mousemove can be cancelled. The default
2679
+ * is true for all events except mousemove, for which the default
2680
+ * is false.
2681
+ * @param {Window} view (Optional) The view containing the target. This is
2682
+ * typically the window object. The default is window.
2683
+ * @param {int} detail (Optional) The number of times the mouse button has
2684
+ * been used. The default value is 1.
2685
+ * @param {int} screenX (Optional) The x-coordinate on the screen at which
2686
+ * point the event occured. The default is 0.
2687
+ * @param {int} screenY (Optional) The y-coordinate on the screen at which
2688
+ * point the event occured. The default is 0.
2689
+ * @param {int} clientX (Optional) The x-coordinate on the client at which
2690
+ * point the event occured. The default is 0.
2691
+ * @param {int} clientY (Optional) The y-coordinate on the client at which
2692
+ * point the event occured. The default is 0.
2693
+ * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
2694
+ * is pressed while the event is firing. The default is false.
2695
+ * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
2696
+ * is pressed while the event is firing. The default is false.
2697
+ * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
2698
+ * is pressed while the event is firing. The default is false.
2699
+ * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
2700
+ * is pressed while the event is firing. The default is false.
2701
+ * @param {int} button (Optional) The button being pressed while the event
2702
+ * is executing. The value should be 0 for the primary mouse button
2703
+ * (typically the left button), 1 for the terciary mouse button
2704
+ * (typically the middle button), and 2 for the secondary mouse button
2705
+ * (typically the right button). The default is 0.
2706
+ * @param {HTMLElement} relatedTarget (Optional) For mouseout events,
2707
+ * this is the element that the mouse has moved to. For mouseover
2708
+ * events, this is the element that the mouse has moved from. This
2709
+ * argument is ignored for all other events. The default is null.
2710
+ */
2711
+ function simulateMouseEvent(target /*:HTMLElement*/, type /*:String*/,
2712
+ bubbles /*:Boolean*/, cancelable /*:Boolean*/,
2713
+ view /*:Window*/, detail /*:int*/,
2714
+ screenX /*:int*/, screenY /*:int*/,
2715
+ clientX /*:int*/, clientY /*:int*/,
2716
+ ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
2717
+ shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
2718
+ button /*:int*/, relatedTarget /*:HTMLElement*/) /*:Void*/
2719
+ {
2720
+
2721
+ //check target
2722
+ if (!target){
2723
+ throw new Error("simulateMouseEvent(): Invalid target.");
2724
+ }
2725
+
2726
+ //check event type
2727
+ if (typeof type == "string"){
2728
+ type = type.toLowerCase();
2729
+
2730
+ //make sure it's a supported mouse event
2731
+ if (!mouseEvents[type]){
2732
+ throw new Error("simulateMouseEvent(): Event type '" + type + "' not supported.");
2733
+ }
2734
+ } else {
2735
+ throw new Error("simulateMouseEvent(): Event type must be a string.");
2736
+ }
2737
+
2738
+ //setup default values
2739
+ if (typeof bubbles != "boolean"){
2740
+ bubbles = true; //all mouse events bubble
2741
+ }
2742
+ if (typeof cancelable != "boolean"){
2743
+ cancelable = (type != "mousemove"); //mousemove is the only one that can't be cancelled
2744
+ }
2745
+ if (typeof view != "object" || view != null){
2746
+ view = window; //view is typically window
2747
+ }
2748
+ if (typeof detail != "number"){
2749
+ detail = 1; //number of mouse clicks must be at least one
2750
+ }
2751
+ if (typeof screenX != "number"){
2752
+ screenX = 0;
2753
+ }
2754
+ if (typeof screenY != "number"){
2755
+ screenY = 0;
2756
+ }
2757
+ if (typeof clientX != "number"){
2758
+ clientX = 0;
2759
+ }
2760
+ if (typeof clientY != "number"){
2761
+ clientY = 0;
2762
+ }
2763
+ if (typeof ctrlKey != "boolean"){
2764
+ ctrlKey = false;
2765
+ }
2766
+ if (typeof altKey != "boolean"){
2767
+ altKey = false;
2768
+ }
2769
+ if (typeof shiftKey != "boolean"){
2770
+ shiftKey = false;
2771
+ }
2772
+ if (typeof metaKey != "boolean"){
2773
+ metaKey = false;
2774
+ }
2775
+ if (typeof button != "number"){
2776
+ button = 0;
2777
+ }
2778
+
2779
+ if (!relatedTarget){
2780
+ relatedTarget = null;
2781
+ }
2782
+
2783
+ //try to create a mouse event
2784
+ var customEvent /*:MouseEvent*/ = null;
2785
+
2786
+ //check for DOM-compliant browsers first
2787
+ if (typeof document.createEvent == "function"){
2788
+
2789
+ customEvent = document.createEvent("MouseEvents");
2790
+
2791
+ //Safari 2.x (WebKit 418) still doesn't implement initMouseEvent()
2792
+ if (customEvent.initMouseEvent){
2793
+ customEvent.initMouseEvent(type, bubbles, cancelable, view, detail,
2794
+ screenX, screenY, clientX, clientY,
2795
+ ctrlKey, altKey, shiftKey, metaKey,
2796
+ button, relatedTarget);
2797
+ } else { //Safari
2798
+
2799
+ //the closest thing available in Safari 2.x is UIEvents
2800
+ customEvent = document.createEvent("UIEvents");
2801
+ customEvent.initEvent(type, bubbles, cancelable);
2802
+ customEvent.view = view;
2803
+ customEvent.detail = detail;
2804
+ customEvent.screenX = screenX;
2805
+ customEvent.screenY = screenY;
2806
+ customEvent.clientX = clientX;
2807
+ customEvent.clientY = clientY;
2808
+ customEvent.ctrlKey = ctrlKey;
2809
+ customEvent.altKey = altKey;
2810
+ customEvent.metaKey = metaKey;
2811
+ customEvent.shiftKey = shiftKey;
2812
+ customEvent.button = button;
2813
+ customEvent.relatedTarget = relatedTarget;
2814
+ }
2815
+
2816
+ /*
2817
+ * Check to see if relatedTarget has been assigned. Firefox
2818
+ * versions less than 2.0 don't allow it to be assigned via
2819
+ * initMouseEvent() and the property is readonly after event
2820
+ * creation, so in order to keep YAHOO.util.getRelatedTarget()
2821
+ * working, assign to the IE proprietary toElement property
2822
+ * for mouseout event and fromElement property for mouseover
2823
+ * event.
2824
+ */
2825
+ if (relatedTarget && !customEvent.relatedTarget){
2826
+ if (type == "mouseout"){
2827
+ customEvent.toElement = relatedTarget;
2828
+ } else if (type == "mouseover"){
2829
+ customEvent.fromElement = relatedTarget;
2830
+ }
2831
+ }
2832
+
2833
+ //fire the event
2834
+ target.dispatchEvent(customEvent);
2835
+
2836
+ } else if (typeof document.createEventObject != "undefined"){ //IE
2837
+
2838
+ //create an IE event object
2839
+ customEvent = document.createEventObject();
2840
+
2841
+ //assign available properties
2842
+ customEvent.bubbles = bubbles;
2843
+ customEvent.cancelable = cancelable;
2844
+ customEvent.view = view;
2845
+ customEvent.detail = detail;
2846
+ customEvent.screenX = screenX;
2847
+ customEvent.screenY = screenY;
2848
+ customEvent.clientX = clientX;
2849
+ customEvent.clientY = clientY;
2850
+ customEvent.ctrlKey = ctrlKey;
2851
+ customEvent.altKey = altKey;
2852
+ customEvent.metaKey = metaKey;
2853
+ customEvent.shiftKey = shiftKey;
2854
+
2855
+ //fix button property for IE's wacky implementation
2856
+ switch(button){
2857
+ case 0:
2858
+ customEvent.button = 1;
2859
+ break;
2860
+ case 1:
2861
+ customEvent.button = 4;
2862
+ break;
2863
+ case 2:
2864
+ //leave as is
2865
+ break;
2866
+ default:
2867
+ customEvent.button = 0;
2868
+ }
2869
+
2870
+ /*
2871
+ * Have to use relatedTarget because IE won't allow assignment
2872
+ * to toElement or fromElement on generic events. This keeps
2873
+ * YAHOO.util.customEvent.getRelatedTarget() functional.
2874
+ */
2875
+ customEvent.relatedTarget = relatedTarget;
2876
+
2877
+ //fire the event
2878
+ target.fireEvent("on" + type, customEvent);
2879
+
2880
+ } else {
2881
+ throw new Error("simulateMouseEvent(): No event simulation framework present.");
2882
+ }
2883
+ }
2884
+
2885
+ /*
2886
+ * Note: Intentionally not for YUIDoc generation.
2887
+ * Simulates a UI event using the given event information to populate
2888
+ * the generated event object. This method does browser-equalizing
2889
+ * calculations to account for differences in the DOM and IE event models
2890
+ * as well as different browser quirks.
2891
+ * @method simulateHTMLEvent
2892
+ * @private
2893
+ * @static
2894
+ * @param {HTMLElement} target The target of the given event.
2895
+ * @param {String} type The type of event to fire. This can be any one of
2896
+ * the following: click, dblclick, mousedown, mouseup, mouseout,
2897
+ * mouseover, and mousemove.
2898
+ * @param {Boolean} bubbles (Optional) Indicates if the event can be
2899
+ * bubbled up. DOM Level 2 specifies that all mouse events bubble by
2900
+ * default. The default is true.
2901
+ * @param {Boolean} cancelable (Optional) Indicates if the event can be
2902
+ * canceled using preventDefault(). DOM Level 2 specifies that all
2903
+ * mouse events except mousemove can be cancelled. The default
2904
+ * is true for all events except mousemove, for which the default
2905
+ * is false.
2906
+ * @param {Window} view (Optional) The view containing the target. This is
2907
+ * typically the window object. The default is window.
2908
+ * @param {int} detail (Optional) The number of times the mouse button has
2909
+ * been used. The default value is 1.
2910
+ */
2911
+ function simulateUIEvent(target /*:HTMLElement*/, type /*:String*/,
2912
+ bubbles /*:Boolean*/, cancelable /*:Boolean*/,
2913
+ view /*:Window*/, detail /*:int*/) /*:Void*/
2914
+ {
2915
+
2916
+ //check target
2917
+ if (!target){
2918
+ throw new Error("simulateUIEvent(): Invalid target.");
2919
+ }
2920
+
2921
+ //check event type
2922
+ if (typeof type == "string"){
2923
+ type = type.toLowerCase();
2924
+
2925
+ //make sure it's a supported mouse event
2926
+ if (!uiEvents[type]){
2927
+ throw new Error("simulateUIEvent(): Event type '" + type + "' not supported.");
2928
+ }
2929
+ } else {
2930
+ throw new Error("simulateUIEvent(): Event type must be a string.");
2931
+ }
2932
+
2933
+ //try to create a mouse event
2934
+ var customEvent = null;
2935
+
2936
+
2937
+ //setup default values
2938
+ if (typeof bubbles != "boolean"){
2939
+ bubbles = (type in bubbleEvents); //not all events bubble
2940
+ }
2941
+ if (typeof cancelable != "boolean"){
2942
+ cancelable = (type == "submit"); //submit is the only one that can be cancelled
2943
+ }
2944
+ if (typeof view != "object" || view != null){
2945
+ view = window; //view is typically window
2946
+ }
2947
+ if (typeof detail != "number"){
2948
+ detail = 1; //usually not used but defaulted to this
2949
+ }
2950
+
2951
+ //check for DOM-compliant browsers first
2952
+ if (typeof document.createEvent == "function"){
2953
+
2954
+ //just a generic UI Event object is needed
2955
+ customEvent = document.createEvent("UIEvents");
2956
+ customEvent.initUIEvent(type, bubbles, cancelable, view, detail);
2957
+
2958
+ //fire the event
2959
+ target.dispatchEvent(customEvent);
2960
+
2961
+ } else if (typeof document.createEventObject != "undefined"){ //IE
2962
+
2963
+ //create an IE event object
2964
+ customEvent = document.createEventObject();
2965
+
2966
+ //assign available properties
2967
+ customEvent.bubbles = bubbles;
2968
+ customEvent.cancelable = cancelable;
2969
+ customEvent.view = view;
2970
+ customEvent.detail = detail;
2971
+
2972
+ //fire the event
2973
+ target.fireEvent("on" + type, customEvent);
2974
+
2975
+ } else {
2976
+ throw new Error("simulateUIEvent(): No event simulation framework present.");
2977
+ }
2978
+ }
2979
+
2980
+
2981
+ /**
2982
+ * Allows event simulation for browser-based events.
2983
+ * @namespace YUITest
2984
+ * @class Event
2985
+ * @static
2986
+ */
2987
+ object = {
2988
+
2989
+ /**
2990
+ * Simulates the event with the given name on a target.
2991
+ * @param {HTMLElement} target The DOM element that's the target of the event.
2992
+ * @param {String} type The type of event to simulate (i.e., "click").
2993
+ * @param {Object} options (Optional) Extra options to copy onto the event object.
2994
+ * @return {void}
2995
+ * @method simulate
2996
+ * @static
2997
+ * @deprecated
2998
+ */
2999
+ simulate: function(target, type, options){
3000
+
3001
+ options = options || {};
3002
+
3003
+ if (mouseEvents[type]){
3004
+ simulateMouseEvent(target, type, options.bubbles,
3005
+ options.cancelable, options.view, options.detail, options.screenX,
3006
+ options.screenY, options.clientX, options.clientY, options.ctrlKey,
3007
+ options.altKey, options.shiftKey, options.metaKey, options.button,
3008
+ options.relatedTarget);
3009
+ } else if (keyEvents[type]){
3010
+ simulateKeyEvent(target, type, options.bubbles,
3011
+ options.cancelable, options.view, options.ctrlKey,
3012
+ options.altKey, options.shiftKey, options.metaKey,
3013
+ options.keyCode, options.charCode);
3014
+ } else if (uiEvents[type]){
3015
+ simulateUIEvent(target, type, options.bubbles,
3016
+ options.cancelable, options.view, options.detail);
3017
+ } else {
3018
+ throw new Error("simulate(): Event '" + type + "' can't be simulated.");
3019
+ }
3020
+ }
3021
+ };
3022
+
3023
+ //create the convenience methods for mouse events
3024
+ for (prop in mouseEvents){
3025
+ if (mouseEvents.hasOwnProperty(prop)){
3026
+ object[prop] = (function(type){
3027
+ return function(target, options){
3028
+ options = options || {};
3029
+ simulateMouseEvent(target, type, options.bubbles,
3030
+ options.cancelable, options.view, options.detail, options.screenX,
3031
+ options.screenY, options.clientX, options.clientY, options.ctrlKey,
3032
+ options.altKey, options.shiftKey, options.metaKey, options.button,
3033
+ options.relatedTarget);
3034
+ };
3035
+ })(prop);
3036
+ }
3037
+ }
3038
+
3039
+ //create the convenience methods for key events
3040
+ for (prop in keyEvents){
3041
+ if (keyEvents.hasOwnProperty(prop)){
3042
+ object[prop] = (function(type){
3043
+ return function(target, options){
3044
+ options = options || {};
3045
+ simulateKeyEvent(target, type, options.bubbles,
3046
+ options.cancelable, options.view, options.ctrlKey,
3047
+ options.altKey, options.shiftKey, options.metaKey,
3048
+ options.keyCode, options.charCode);
3049
+ };
3050
+ })(prop);
3051
+ }
3052
+ }
3053
+
3054
+ //create the convenience methods for key events
3055
+ for (prop in uiEvents){
3056
+ if (uiEvents.hasOwnProperty(prop)){
3057
+ object[prop] = (function(type){
3058
+ return function(target, options){
3059
+ options = options || {};
3060
+ simulateUIEvent(target, type, options.bubbles,
3061
+ options.cancelable, options.view, options.detail);
3062
+ };
3063
+ })(prop);
3064
+ }
3065
+ }
3066
+
3067
+ return object;
3068
+
3069
+ })();
3070
+
3071
+
3072
+ /**
3073
+ * An object capable of sending test results to a server.
3074
+ * @param {String} url The URL to submit the results to.
3075
+ * @param {Function} format (Optiona) A function that outputs the results in a specific format.
3076
+ * Default is YUITest.TestFormat.XML.
3077
+ * @constructor
3078
+ * @namespace YUITest
3079
+ * @class Reporter
3080
+ */
3081
+ YUITest.Reporter = function(url, format) {
3082
+
3083
+ /**
3084
+ * The URL to submit the data to.
3085
+ * @type String
3086
+ * @property url
3087
+ */
3088
+ this.url = url;
3089
+
3090
+ /**
3091
+ * The formatting function to call when submitting the data.
3092
+ * @type Function
3093
+ * @property format
3094
+ */
3095
+ this.format = format || YUITest.TestFormat.XML;
3096
+
3097
+ /**
3098
+ * Extra fields to submit with the request.
3099
+ * @type Object
3100
+ * @property _fields
3101
+ * @private
3102
+ */
3103
+ this._fields = new Object();
3104
+
3105
+ /**
3106
+ * The form element used to submit the results.
3107
+ * @type HTMLFormElement
3108
+ * @property _form
3109
+ * @private
3110
+ */
3111
+ this._form = null;
3112
+
3113
+ /**
3114
+ * Iframe used as a target for form submission.
3115
+ * @type HTMLIFrameElement
3116
+ * @property _iframe
3117
+ * @private
3118
+ */
3119
+ this._iframe = null;
3120
+ };
3121
+
3122
+ YUITest.Reporter.prototype = {
3123
+
3124
+ //restore missing constructor
3125
+ constructor: YUITest.Reporter,
3126
+
3127
+ /**
3128
+ * Adds a field to the form that submits the results.
3129
+ * @param {String} name The name of the field.
3130
+ * @param {Variant} value The value of the field.
3131
+ * @return {Void}
3132
+ * @method addField
3133
+ */
3134
+ addField : function (name, value){
3135
+ this._fields[name] = value;
3136
+ },
3137
+
3138
+ /**
3139
+ * Removes all previous defined fields.
3140
+ * @return {Void}
3141
+ * @method addField
3142
+ */
3143
+ clearFields : function(){
3144
+ this._fields = new Object();
3145
+ },
3146
+
3147
+ /**
3148
+ * Cleans up the memory associated with the TestReporter, removing DOM elements
3149
+ * that were created.
3150
+ * @return {Void}
3151
+ * @method destroy
3152
+ */
3153
+ destroy : function() {
3154
+ if (this._form){
3155
+ this._form.parentNode.removeChild(this._form);
3156
+ this._form = null;
3157
+ }
3158
+ if (this._iframe){
3159
+ this._iframe.parentNode.removeChild(this._iframe);
3160
+ this._iframe = null;
3161
+ }
3162
+ this._fields = null;
3163
+ },
3164
+
3165
+ /**
3166
+ * Sends the report to the server.
3167
+ * @param {Object} results The results object created by TestRunner.
3168
+ * @return {Void}
3169
+ * @method report
3170
+ */
3171
+ report : function(results){
3172
+
3173
+ //if the form hasn't been created yet, create it
3174
+ if (!this._form){
3175
+ this._form = document.createElement("form");
3176
+ this._form.method = "post";
3177
+ this._form.style.visibility = "hidden";
3178
+ this._form.style.position = "absolute";
3179
+ this._form.style.top = 0;
3180
+ document.body.appendChild(this._form);
3181
+
3182
+ //IE won't let you assign a name using the DOM, must do it the hacky way
3183
+ try {
3184
+ this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
3185
+ } catch (ex){
3186
+ this._iframe = document.createElement("iframe");
3187
+ this._iframe.name = "yuiTestTarget";
3188
+ }
3189
+
3190
+ this._iframe.src = "javascript:false";
3191
+ this._iframe.style.visibility = "hidden";
3192
+ this._iframe.style.position = "absolute";
3193
+ this._iframe.style.top = 0;
3194
+ document.body.appendChild(this._iframe);
3195
+
3196
+ this._form.target = "yuiTestTarget";
3197
+ }
3198
+
3199
+ //set the form's action
3200
+ this._form.action = this.url;
3201
+
3202
+ //remove any existing fields
3203
+ while(this._form.hasChildNodes()){
3204
+ this._form.removeChild(this._form.lastChild);
3205
+ }
3206
+
3207
+ //create default fields
3208
+ this._fields.results = this.format(results);
3209
+ this._fields.useragent = navigator.userAgent;
3210
+ this._fields.timestamp = (new Date()).toLocaleString();
3211
+
3212
+ //add fields to the form
3213
+ for (var prop in this._fields){
3214
+ var value = this._fields[prop];
3215
+ if (this._fields.hasOwnProperty(prop) && (typeof value != "function")){
3216
+ var input = document.createElement("input");
3217
+ input.type = "hidden";
3218
+ input.name = prop;
3219
+ input.value = value;
3220
+ this._form.appendChild(input);
3221
+ }
3222
+ }
3223
+
3224
+ //remove default fields
3225
+ delete this._fields.results;
3226
+ delete this._fields.useragent;
3227
+ delete this._fields.timestamp;
3228
+
3229
+ if (arguments[1] !== false){
3230
+ this._form.submit();
3231
+ }
3232
+
3233
+ }
3234
+
3235
+ };
3236
+
3237
+
3238
+ /**
3239
+ * Runs pages containing test suite definitions.
3240
+ * @namespace YUITest
3241
+ * @class PageManager
3242
+ * @static
3243
+ */
3244
+ YUITest.PageManager = YUITest.Util.mix(new YUITest.EventTarget(), {
3245
+
3246
+ /**
3247
+ * Constant for the testpagebegin custom event
3248
+ * @property TEST_PAGE_BEGIN_EVENT
3249
+ * @static
3250
+ * @type string
3251
+ * @final
3252
+ */
3253
+ TEST_PAGE_BEGIN_EVENT /*:String*/ : "testpagebegin",
3254
+
3255
+ /**
3256
+ * Constant for the testpagecomplete custom event
3257
+ * @property TEST_PAGE_COMPLETE_EVENT
3258
+ * @static
3259
+ * @type string
3260
+ * @final
3261
+ */
3262
+ TEST_PAGE_COMPLETE_EVENT /*:String*/ : "testpagecomplete",
3263
+
3264
+ /**
3265
+ * Constant for the testmanagerbegin custom event
3266
+ * @property TEST_MANAGER_BEGIN_EVENT
3267
+ * @static
3268
+ * @type string
3269
+ * @final
3270
+ */
3271
+ TEST_MANAGER_BEGIN_EVENT /*:String*/ : "testmanagerbegin",
3272
+
3273
+ /**
3274
+ * Constant for the testmanagercomplete custom event
3275
+ * @property TEST_MANAGER_COMPLETE_EVENT
3276
+ * @static
3277
+ * @type string
3278
+ * @final
3279
+ */
3280
+ TEST_MANAGER_COMPLETE_EVENT /*:String*/ : "testmanagercomplete",
3281
+
3282
+ //-------------------------------------------------------------------------
3283
+ // Private Properties
3284
+ //-------------------------------------------------------------------------
3285
+
3286
+
3287
+ /**
3288
+ * The URL of the page currently being executed.
3289
+ * @type String
3290
+ * @private
3291
+ * @property _curPage
3292
+ * @static
3293
+ */
3294
+ _curPage: null,
3295
+
3296
+ /**
3297
+ * The frame used to load and run tests.
3298
+ * @type Window
3299
+ * @private
3300
+ * @property _frame
3301
+ * @static
3302
+ */
3303
+ _frame: null,
3304
+
3305
+ /**
3306
+ * The timeout ID for the next iteration through the tests.
3307
+ * @type int
3308
+ * @private
3309
+ * @property _timeoutId
3310
+ * @static
3311
+ */
3312
+ _timeoutId: 0,
3313
+
3314
+ /**
3315
+ * Array of pages to load.
3316
+ * @type String[]
3317
+ * @private
3318
+ * @property _pages
3319
+ * @static
3320
+ */
3321
+ _pages: [],
3322
+
3323
+ /**
3324
+ * Aggregated results
3325
+ * @type Object
3326
+ * @private
3327
+ * @property _results
3328
+ * @static
3329
+ */
3330
+ _results: null,
3331
+
3332
+ //-------------------------------------------------------------------------
3333
+ // Private Methods
3334
+ //-------------------------------------------------------------------------
3335
+
3336
+ /**
3337
+ * Handles TestRunner.COMPLETE_EVENT, storing the results and beginning
3338
+ * the loop again.
3339
+ * @param {Object} data Data about the event.
3340
+ * @return {Void}
3341
+ * @method _handleTestRunnerComplete
3342
+ * @private
3343
+ * @static
3344
+ */
3345
+ _handleTestRunnerComplete : function (data /*:Object*/) /*:Void*/ {
3346
+
3347
+ this.fire(this.TEST_PAGE_COMPLETE_EVENT, {
3348
+ page: this._curPage,
3349
+ results: data.results
3350
+ });
3351
+
3352
+ //save results
3353
+ //this._results[this.curPage] = data.results;
3354
+
3355
+ //process 'em
3356
+ this._processResults(this._curPage, data.results);
3357
+
3358
+
3359
+ //if there's more to do, set a timeout to begin again
3360
+ if (this._pages.length){
3361
+ this._timeoutId = setTimeout(function(){
3362
+ YUITest.TestManager._run();
3363
+ }, 1000);
3364
+ } else {
3365
+ this.fire(this.TEST_MANAGER_COMPLETE_EVENT, this._results);
3366
+ }
3367
+ },
3368
+
3369
+ /**
3370
+ * Processes the results of a test page run, outputting log messages
3371
+ * for failed tests.
3372
+ * @return {Void}
3373
+ * @method _processResults
3374
+ * @private
3375
+ * @static
3376
+ */
3377
+ _processResults : function (page, results){
3378
+
3379
+ var r = this._results;
3380
+
3381
+ r.passed += results.passed;
3382
+ r.failed += results.failed;
3383
+ r.ignored += results.ignored;
3384
+ r.total += results.total;
3385
+ r.duration += results.duration;
3386
+
3387
+ if (results.failed){
3388
+ r.failedPages.push(page);
3389
+ } else {
3390
+ r.passedPages.push(page);
3391
+ }
3392
+
3393
+ results.name = page;
3394
+ results.type = "page";
3395
+
3396
+ r[page] = results;
3397
+ },
3398
+
3399
+ /**
3400
+ * Loads the next test page into the iframe.
3401
+ * @return {Void}
3402
+ * @method _run
3403
+ * @static
3404
+ * @private
3405
+ */
3406
+ _run : function () /*:Void*/ {
3407
+
3408
+ //set the current page
3409
+ this._curPage = this._pages.shift();
3410
+
3411
+ this.fire(this.TEST_PAGE_BEGIN_EVENT, this._curPage);
3412
+
3413
+ //load the frame - destroy history in case there are other iframes that
3414
+ //need testing
3415
+ this._frame.location.replace(this._curPage);
3416
+
3417
+ },
3418
+
3419
+ //-------------------------------------------------------------------------
3420
+ // Public Methods
3421
+ //-------------------------------------------------------------------------
3422
+
3423
+ /**
3424
+ * Signals that a test page has been loaded. This should be called from
3425
+ * within the test page itself to notify the TestManager that it is ready.
3426
+ * @return {Void}
3427
+ * @method load
3428
+ * @static
3429
+ */
3430
+ load : function () /*:Void*/ {
3431
+ if (parent.YUITest.PageManager !== this){
3432
+ parent.YUITest.PageManager.load();
3433
+ } else {
3434
+
3435
+ if (this._frame) {
3436
+ //assign event handling
3437
+ var TestRunner = this._frame.YUITest.TestRunner;
3438
+
3439
+ TestRunner.subscribe(TestRunner.COMPLETE_EVENT, this._handleTestRunnerComplete, this, true);
3440
+
3441
+ //run it
3442
+ TestRunner.run();
3443
+ }
3444
+ }
3445
+ },
3446
+
3447
+ /**
3448
+ * Sets the pages to be loaded.
3449
+ * @param {String[]} pages An array of URLs to load.
3450
+ * @return {Void}
3451
+ * @method setPages
3452
+ * @static
3453
+ */
3454
+ setPages : function (pages /*:String[]*/) /*:Void*/ {
3455
+ this._pages = pages;
3456
+ },
3457
+
3458
+ /**
3459
+ * Begins the process of running the tests.
3460
+ * @return {Void}
3461
+ * @method start
3462
+ * @static
3463
+ */
3464
+ start : function () /*:Void*/ {
3465
+
3466
+ if (!this._initialized) {
3467
+
3468
+ /**
3469
+ * Fires when loading a test page
3470
+ * @event testpagebegin
3471
+ * @param curPage {string} the page being loaded
3472
+ * @static
3473
+ */
3474
+
3475
+ /**
3476
+ * Fires when a test page is complete
3477
+ * @event testpagecomplete
3478
+ * @param obj {page: string, results: object} the name of the
3479
+ * page that was loaded, and the test suite results
3480
+ * @static
3481
+ */
3482
+
3483
+ /**
3484
+ * Fires when the test manager starts running all test pages
3485
+ * @event testmanagerbegin
3486
+ * @static
3487
+ */
3488
+
3489
+ /**
3490
+ * Fires when the test manager finishes running all test pages. External
3491
+ * test runners should subscribe to this event in order to get the
3492
+ * aggregated test results.
3493
+ * @event testmanagercomplete
3494
+ * @param obj { pages_passed: int, pages_failed: int, tests_passed: int
3495
+ * tests_failed: int, passed: string[], failed: string[],
3496
+ * page_results: {} }
3497
+ * @static
3498
+ */
3499
+
3500
+ //create iframe if not already available
3501
+ if (!this._frame){
3502
+ var frame /*:HTMLElement*/ = document.createElement("iframe");
3503
+ frame.style.visibility = "hidden";
3504
+ frame.style.position = "absolute";
3505
+ document.body.appendChild(frame);
3506
+ this._frame = frame.contentWindow || frame.contentDocument.parentWindow;
3507
+ }
3508
+
3509
+ this._initialized = true;
3510
+ }
3511
+
3512
+
3513
+ // reset the results cache
3514
+ this._results = {
3515
+
3516
+ passed: 0,
3517
+ failed: 0,
3518
+ ignored: 0,
3519
+ total: 0,
3520
+ type: "report",
3521
+ name: "YUI Test Results",
3522
+ duration: 0,
3523
+ failedPages:[],
3524
+ passedPages:[]
3525
+ /*
3526
+ // number of pages that pass
3527
+ pages_passed: 0,
3528
+ // number of pages that fail
3529
+ pages_failed: 0,
3530
+ // total number of tests passed
3531
+ tests_passed: 0,
3532
+ // total number of tests failed
3533
+ tests_failed: 0,
3534
+ // array of pages that passed
3535
+ passed: [],
3536
+ // array of pages that failed
3537
+ failed: [],
3538
+ // map of full results for each page
3539
+ page_results: {}*/
3540
+ };
3541
+
3542
+ this.fire(this.TEST_MANAGER_BEGIN_EVENT, null);
3543
+ this._run();
3544
+
3545
+ },
3546
+
3547
+ /**
3548
+ * Stops the execution of tests.
3549
+ * @return {Void}
3550
+ * @method stop
3551
+ * @static
3552
+ */
3553
+ stop : function () /*:Void*/ {
3554
+ clearTimeout(this._timeoutId);
3555
+ }
3556
+
3557
+ });
3558
+
3559
+
3560
+ /**
3561
+ * Runs test suites and test cases, providing events to allowing for the
3562
+ * interpretation of test results.
3563
+ * @namespace YUITest
3564
+ * @class TestRunner
3565
+ * @static
3566
+ */
3567
+ YUITest.TestRunner = function(){
3568
+
3569
+ /*(intentionally not documented)
3570
+ * Determines if any of the array of test groups appears
3571
+ * in the given TestRunner filter.
3572
+ * @param {Array} testGroups The array of test groups to
3573
+ * search for.
3574
+ * @param {String} filter The TestRunner groups filter.
3575
+ */
3576
+ function inGroups(testGroups, filter){
3577
+ if (!filter.length){
3578
+ return true;
3579
+ } else {
3580
+ if (testGroups){
3581
+ for (var i=0, len=testGroups.length; i < len; i++){
3582
+ if (filter.indexOf("," + testGroups[i] + ",") > -1){
3583
+ return true;
3584
+ }
3585
+ }
3586
+ }
3587
+ return false;
3588
+ }
3589
+ }
3590
+
3591
+ /**
3592
+ * A node in the test tree structure. May represent a TestSuite, TestCase, or
3593
+ * test function.
3594
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
3595
+ * @class TestNode
3596
+ * @constructor
3597
+ * @private
3598
+ */
3599
+ function TestNode(testObject){
3600
+
3601
+ /**
3602
+ * The TestSuite, TestCase, or test function represented by this node.
3603
+ * @type Variant
3604
+ * @property testObject
3605
+ */
3606
+ this.testObject = testObject;
3607
+
3608
+ /**
3609
+ * Pointer to this node's first child.
3610
+ * @type TestNode
3611
+ * @property firstChild
3612
+ */
3613
+ this.firstChild = null;
3614
+
3615
+ /**
3616
+ * Pointer to this node's last child.
3617
+ * @type TestNode
3618
+ * @property lastChild
3619
+ */
3620
+ this.lastChild = null;
3621
+
3622
+ /**
3623
+ * Pointer to this node's parent.
3624
+ * @type TestNode
3625
+ * @property parent
3626
+ */
3627
+ this.parent = null;
3628
+
3629
+ /**
3630
+ * Pointer to this node's next sibling.
3631
+ * @type TestNode
3632
+ * @property next
3633
+ */
3634
+ this.next = null;
3635
+
3636
+ /**
3637
+ * Test results for this test object.
3638
+ * @type object
3639
+ * @property results
3640
+ */
3641
+ this.results = new YUITest.Results();
3642
+
3643
+ //initialize results
3644
+ if (testObject instanceof YUITest.TestSuite){
3645
+ this.results.type = "testsuite";
3646
+ this.results.name = testObject.name;
3647
+ } else if (testObject instanceof YUITest.TestCase){
3648
+ this.results.type = "testcase";
3649
+ this.results.name = testObject.name;
3650
+ }
3651
+
3652
+ }
3653
+
3654
+ TestNode.prototype = {
3655
+
3656
+ /**
3657
+ * Appends a new test object (TestSuite, TestCase, or test function name) as a child
3658
+ * of this node.
3659
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
3660
+ * @return {Void}
3661
+ */
3662
+ appendChild : function (testObject){
3663
+ var node = new TestNode(testObject);
3664
+ if (this.firstChild === null){
3665
+ this.firstChild = this.lastChild = node;
3666
+ } else {
3667
+ this.lastChild.next = node;
3668
+ this.lastChild = node;
3669
+ }
3670
+ node.parent = this;
3671
+ return node;
3672
+ }
3673
+ };
3674
+
3675
+ /**
3676
+ * Runs test suites and test cases, providing events to allowing for the
3677
+ * interpretation of test results.
3678
+ * @namespace Test
3679
+ * @class Runner
3680
+ * @static
3681
+ */
3682
+ function TestRunner(){
3683
+
3684
+ //inherit from EventTarget
3685
+ YUITest.EventTarget.call(this);
3686
+
3687
+ /**
3688
+ * Suite on which to attach all TestSuites and TestCases to be run.
3689
+ * @type YUITest.TestSuite
3690
+ * @property masterSuite
3691
+ * @static
3692
+ * @private
3693
+ */
3694
+ this.masterSuite = new YUITest.TestSuite("yuitests" + (new Date()).getTime());
3695
+
3696
+ /**
3697
+ * Pointer to the current node in the test tree.
3698
+ * @type TestNode
3699
+ * @private
3700
+ * @property _cur
3701
+ * @static
3702
+ */
3703
+ this._cur = null;
3704
+
3705
+ /**
3706
+ * Pointer to the root node in the test tree.
3707
+ * @type TestNode
3708
+ * @private
3709
+ * @property _root
3710
+ * @static
3711
+ */
3712
+ this._root = null;
3713
+
3714
+ /**
3715
+ * Indicates if the TestRunner will log events or not.
3716
+ * @type Boolean
3717
+ * @property _log
3718
+ * @private
3719
+ * @static
3720
+ */
3721
+ this._log = true;
3722
+
3723
+ /**
3724
+ * Indicates if the TestRunner is waiting as a result of
3725
+ * wait() being called.
3726
+ * @type Boolean
3727
+ * @property _waiting
3728
+ * @private
3729
+ * @static
3730
+ */
3731
+ this._waiting = false;
3732
+
3733
+ /**
3734
+ * Indicates if the TestRunner is currently running tests.
3735
+ * @type Boolean
3736
+ * @private
3737
+ * @property _running
3738
+ * @static
3739
+ */
3740
+ this._running = false;
3741
+
3742
+ /**
3743
+ * Holds copy of the results object generated when all tests are
3744
+ * complete.
3745
+ * @type Object
3746
+ * @private
3747
+ * @property _lastResults
3748
+ * @static
3749
+ */
3750
+ this._lastResults = null;
3751
+
3752
+ /**
3753
+ * Data object that is passed around from method to method.
3754
+ * @type Object
3755
+ * @private
3756
+ * @property _data
3757
+ * @static
3758
+ */
3759
+ this._context = null;
3760
+
3761
+ /**
3762
+ * The list of test groups to run. The list is represented
3763
+ * by a comma delimited string with commas at the start and
3764
+ * end.
3765
+ * @type String
3766
+ * @private
3767
+ * @property _groups
3768
+ * @static
3769
+ */
3770
+ this._groups = "";
3771
+ }
3772
+
3773
+ TestRunner.prototype = YUITest.Util.mix(new YUITest.EventTarget(), {
3774
+
3775
+ //restore prototype
3776
+ constructor: YUITest.TestRunner,
3777
+
3778
+ //-------------------------------------------------------------------------
3779
+ // Constants
3780
+ //-------------------------------------------------------------------------
3781
+
3782
+ /**
3783
+ * Fires when a test case is opened but before the first
3784
+ * test is executed.
3785
+ * @event testcasebegin
3786
+ * @static
3787
+ */
3788
+ TEST_CASE_BEGIN_EVENT : "testcasebegin",
3789
+
3790
+ /**
3791
+ * Fires when all tests in a test case have been executed.
3792
+ * @event testcasecomplete
3793
+ * @static
3794
+ */
3795
+ TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
3796
+
3797
+ /**
3798
+ * Fires when a test suite is opened but before the first
3799
+ * test is executed.
3800
+ * @event testsuitebegin
3801
+ * @static
3802
+ */
3803
+ TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
3804
+
3805
+ /**
3806
+ * Fires when all test cases in a test suite have been
3807
+ * completed.
3808
+ * @event testsuitecomplete
3809
+ * @static
3810
+ */
3811
+ TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
3812
+
3813
+ /**
3814
+ * Fires when a test has passed.
3815
+ * @event pass
3816
+ * @static
3817
+ */
3818
+ TEST_PASS_EVENT : "pass",
3819
+
3820
+ /**
3821
+ * Fires when a test has failed.
3822
+ * @event fail
3823
+ * @static
3824
+ */
3825
+ TEST_FAIL_EVENT : "fail",
3826
+
3827
+ /**
3828
+ * Fires when a non-test method has an error.
3829
+ * @event error
3830
+ * @static
3831
+ */
3832
+ ERROR_EVENT : "error",
3833
+
3834
+ /**
3835
+ * Fires when a test has been ignored.
3836
+ * @event ignore
3837
+ * @static
3838
+ */
3839
+ TEST_IGNORE_EVENT : "ignore",
3840
+
3841
+ /**
3842
+ * Fires when all test suites and test cases have been completed.
3843
+ * @event complete
3844
+ * @static
3845
+ */
3846
+ COMPLETE_EVENT : "complete",
3847
+
3848
+ /**
3849
+ * Fires when the run() method is called.
3850
+ * @event begin
3851
+ * @static
3852
+ */
3853
+ BEGIN_EVENT : "begin",
3854
+
3855
+ //-------------------------------------------------------------------------
3856
+ // Test Tree-Related Methods
3857
+ //-------------------------------------------------------------------------
3858
+
3859
+ /**
3860
+ * Adds a test case to the test tree as a child of the specified node.
3861
+ * @param {TestNode} parentNode The node to add the test case to as a child.
3862
+ * @param {YUITest.TestCase} testCase The test case to add.
3863
+ * @return {Void}
3864
+ * @static
3865
+ * @private
3866
+ * @method _addTestCaseToTestTree
3867
+ */
3868
+ _addTestCaseToTestTree : function (parentNode, testCase){
3869
+
3870
+ //add the test suite
3871
+ var node = parentNode.appendChild(testCase),
3872
+ prop,
3873
+ testName;
3874
+
3875
+ //iterate over the items in the test case
3876
+ for (prop in testCase){
3877
+ if ((prop.indexOf("test") === 0 || prop.indexOf(" ") > -1) && typeof testCase[prop] == "function"){
3878
+ node.appendChild(prop);
3879
+ }
3880
+ }
3881
+
3882
+ },
3883
+
3884
+ /**
3885
+ * Adds a test suite to the test tree as a child of the specified node.
3886
+ * @param {TestNode} parentNode The node to add the test suite to as a child.
3887
+ * @param {YUITest.TestSuite} testSuite The test suite to add.
3888
+ * @return {Void}
3889
+ * @static
3890
+ * @private
3891
+ * @method _addTestSuiteToTestTree
3892
+ */
3893
+ _addTestSuiteToTestTree : function (parentNode, testSuite) {
3894
+
3895
+ //add the test suite
3896
+ var node = parentNode.appendChild(testSuite);
3897
+
3898
+ //iterate over the items in the master suite
3899
+ for (var i=0; i < testSuite.items.length; i++){
3900
+ if (testSuite.items[i] instanceof YUITest.TestSuite) {
3901
+ this._addTestSuiteToTestTree(node, testSuite.items[i]);
3902
+ } else if (testSuite.items[i] instanceof YUITest.TestCase) {
3903
+ this._addTestCaseToTestTree(node, testSuite.items[i]);
3904
+ }
3905
+ }
3906
+ },
3907
+
3908
+ /**
3909
+ * Builds the test tree based on items in the master suite. The tree is a hierarchical
3910
+ * representation of the test suites, test cases, and test functions. The resulting tree
3911
+ * is stored in _root and the pointer _cur is set to the root initially.
3912
+ * @return {Void}
3913
+ * @static
3914
+ * @private
3915
+ * @method _buildTestTree
3916
+ */
3917
+ _buildTestTree : function () {
3918
+
3919
+ this._root = new TestNode(this.masterSuite);
3920
+ //this._cur = this._root;
3921
+
3922
+ //iterate over the items in the master suite
3923
+ for (var i=0; i < this.masterSuite.items.length; i++){
3924
+ if (this.masterSuite.items[i] instanceof YUITest.TestSuite) {
3925
+ this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
3926
+ } else if (this.masterSuite.items[i] instanceof YUITest.TestCase) {
3927
+ this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
3928
+ }
3929
+ }
3930
+
3931
+ },
3932
+
3933
+ //-------------------------------------------------------------------------
3934
+ // Private Methods
3935
+ //-------------------------------------------------------------------------
3936
+
3937
+ /**
3938
+ * Handles the completion of a test object's tests. Tallies test results
3939
+ * from one level up to the next.
3940
+ * @param {TestNode} node The TestNode representing the test object.
3941
+ * @return {Void}
3942
+ * @method _handleTestObjectComplete
3943
+ * @private
3944
+ */
3945
+ _handleTestObjectComplete : function (node) {
3946
+ var parentNode;
3947
+
3948
+ if (typeof node.testObject == "object" && node !== null){
3949
+ parentNode = node.parent;
3950
+
3951
+ if (parentNode){
3952
+ parentNode.results.include(node.results);
3953
+ parentNode.results[node.testObject.name] = node.results;
3954
+ }
3955
+
3956
+ if (node.testObject instanceof YUITest.TestSuite){
3957
+ this._execNonTestMethod(node, "tearDown", false);
3958
+ node.results.duration = (new Date()) - node._start;
3959
+ this.fire({ type: this.TEST_SUITE_COMPLETE_EVENT, testSuite: node.testObject, results: node.results});
3960
+ } else if (node.testObject instanceof YUITest.TestCase){
3961
+ this._execNonTestMethod(node, "destroy", false);
3962
+ node.results.duration = (new Date()) - node._start;
3963
+ this.fire({ type: this.TEST_CASE_COMPLETE_EVENT, testCase: node.testObject, results: node.results});
3964
+ }
3965
+ }
3966
+ },
3967
+
3968
+ //-------------------------------------------------------------------------
3969
+ // Navigation Methods
3970
+ //-------------------------------------------------------------------------
3971
+
3972
+ /**
3973
+ * Retrieves the next node in the test tree.
3974
+ * @return {TestNode} The next node in the test tree or null if the end is reached.
3975
+ * @private
3976
+ * @static
3977
+ * @method _next
3978
+ */
3979
+ _next : function () {
3980
+
3981
+ if (this._cur === null){
3982
+ this._cur = this._root;
3983
+ } else if (this._cur.firstChild) {
3984
+ this._cur = this._cur.firstChild;
3985
+ } else if (this._cur.next) {
3986
+ this._cur = this._cur.next;
3987
+ } else {
3988
+ while (this._cur && !this._cur.next && this._cur !== this._root){
3989
+ this._handleTestObjectComplete(this._cur);
3990
+ this._cur = this._cur.parent;
3991
+ }
3992
+
3993
+ this._handleTestObjectComplete(this._cur);
3994
+
3995
+ if (this._cur == this._root){
3996
+ this._cur.results.type = "report";
3997
+ this._cur.results.timestamp = (new Date()).toLocaleString();
3998
+ this._cur.results.duration = (new Date()) - this._cur._start;
3999
+ this._lastResults = this._cur.results;
4000
+ this._running = false;
4001
+ this.fire({ type: this.COMPLETE_EVENT, results: this._lastResults});
4002
+ this._cur = null;
4003
+ } else {
4004
+ this._cur = this._cur.next;
4005
+ }
4006
+ }
4007
+
4008
+ return this._cur;
4009
+ },
4010
+
4011
+ /**
4012
+ * Executes a non-test method (init, setUp, tearDown, destroy)
4013
+ * and traps an errors. If an error occurs, an error event is
4014
+ * fired.
4015
+ * @param {Object} node The test node in the testing tree.
4016
+ * @param {String} methodName The name of the method to execute.
4017
+ * @param {Boolean} allowAsync Determines if the method can be called asynchronously.
4018
+ * @return {Boolean} True if an async method was called, false if not.
4019
+ * @method _execNonTestMethod
4020
+ * @private
4021
+ */
4022
+ _execNonTestMethod: function(node, methodName, allowAsync){
4023
+ var testObject = node.testObject,
4024
+ event = { type: this.ERROR_EVENT };
4025
+ try {
4026
+ if (allowAsync && testObject["async:" + methodName]){
4027
+ testObject["async:" + methodName](this._context);
4028
+ return true;
4029
+ } else {
4030
+ testObject[methodName](this._context);
4031
+ }
4032
+ } catch (ex){
4033
+ node.results.errors++;
4034
+ event.error = ex;
4035
+ event.methodName = methodName;
4036
+ if (testObject instanceof YUITest.TestCase){
4037
+ event.testCase = testObject;
4038
+ } else {
4039
+ event.testSuite = testSuite;
4040
+ }
4041
+
4042
+ this.fire(event);
4043
+ }
4044
+
4045
+ return false;
4046
+ },
4047
+
4048
+ /**
4049
+ * Runs a test case or test suite, returning the results.
4050
+ * @param {YUITest.TestCase|YUITest.TestSuite} testObject The test case or test suite to run.
4051
+ * @return {Object} Results of the execution with properties passed, failed, and total.
4052
+ * @private
4053
+ * @method _run
4054
+ * @static
4055
+ */
4056
+ _run : function () {
4057
+
4058
+ //flag to indicate if the TestRunner should wait before continuing
4059
+ var shouldWait = false;
4060
+
4061
+ //get the next test node
4062
+ var node = this._next();
4063
+
4064
+ if (node !== null) {
4065
+
4066
+ //set flag to say the testrunner is running
4067
+ this._running = true;
4068
+
4069
+ //eliminate last results
4070
+ this._lastResult = null;
4071
+
4072
+ var testObject = node.testObject;
4073
+
4074
+ //figure out what to do
4075
+ if (typeof testObject == "object" && testObject !== null){
4076
+ if (testObject instanceof YUITest.TestSuite){
4077
+ this.fire({ type: this.TEST_SUITE_BEGIN_EVENT, testSuite: testObject });
4078
+ node._start = new Date();
4079
+ this._execNonTestMethod(node, "setUp" ,false);
4080
+ } else if (testObject instanceof YUITest.TestCase){
4081
+ this.fire({ type: this.TEST_CASE_BEGIN_EVENT, testCase: testObject });
4082
+ node._start = new Date();
4083
+
4084
+ //regular or async init
4085
+ /*try {
4086
+ if (testObject["async:init"]){
4087
+ testObject["async:init"](this._context);
4088
+ return;
4089
+ } else {
4090
+ testObject.init(this._context);
4091
+ }
4092
+ } catch (ex){
4093
+ node.results.errors++;
4094
+ this.fire({ type: this.ERROR_EVENT, error: ex, testCase: testObject, methodName: "init" });
4095
+ }*/
4096
+ if(this._execNonTestMethod(node, "init", true)){
4097
+ return;
4098
+ }
4099
+ }
4100
+
4101
+ //some environments don't support setTimeout
4102
+ if (typeof setTimeout != "undefined"){
4103
+ setTimeout(function(){
4104
+ YUITest.TestRunner._run();
4105
+ }, 0);
4106
+ } else {
4107
+ this._run();
4108
+ }
4109
+ } else {
4110
+ this._runTest(node);
4111
+ }
4112
+
4113
+ }
4114
+ },
4115
+
4116
+ _resumeTest : function (segment) {
4117
+
4118
+ //get relevant information
4119
+ var node = this._cur;
4120
+
4121
+ //we know there's no more waiting now
4122
+ this._waiting = false;
4123
+
4124
+ //if there's no node, it probably means a wait() was called after resume()
4125
+ if (!node){
4126
+ //TODO: Handle in some way?
4127
+ //console.log("wait() called after resume()");
4128
+ //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
4129
+ return;
4130
+ }
4131
+
4132
+ var testName = node.testObject;
4133
+ var testCase = node.parent.testObject;
4134
+
4135
+ //cancel other waits if available
4136
+ if (testCase.__yui_wait){
4137
+ clearTimeout(testCase.__yui_wait);
4138
+ delete testCase.__yui_wait;
4139
+ }
4140
+
4141
+ //get the "should" test cases
4142
+ var shouldFail = testName.indexOf("fail:") === 0 ||
4143
+ (testCase._should.fail || {})[testName];
4144
+ var shouldError = (testCase._should.error || {})[testName];
4145
+
4146
+ //variable to hold whether or not the test failed
4147
+ var failed = false;
4148
+ var error = null;
4149
+
4150
+ //try the test
4151
+ try {
4152
+
4153
+ //run the test
4154
+ segment.call(testCase, this._context);
4155
+
4156
+ //if the test hasn't already failed and doesn't have any asserts...
4157
+ if(YUITest.Assert._getCount() == 0){
4158
+ throw new YUITest.AssertionError("Test has no asserts.");
4159
+ }
4160
+ //if it should fail, and it got here, then it's a fail because it didn't
4161
+ else if (shouldFail){
4162
+ error = new YUITest.ShouldFail();
4163
+ failed = true;
4164
+ } else if (shouldError){
4165
+ error = new YUITest.ShouldError();
4166
+ failed = true;
4167
+ }
4168
+
4169
+ } catch (thrown){
4170
+
4171
+ //cancel any pending waits, the test already failed
4172
+ if (testCase.__yui_wait){
4173
+ clearTimeout(testCase.__yui_wait);
4174
+ delete testCase.__yui_wait;
4175
+ }
4176
+
4177
+ //figure out what type of error it was
4178
+ if (thrown instanceof YUITest.AssertionError) {
4179
+ if (!shouldFail){
4180
+ error = thrown;
4181
+ failed = true;
4182
+ }
4183
+ } else if (thrown instanceof YUITest.Wait){
4184
+
4185
+ if (typeof thrown.segment == "function"){
4186
+ if (typeof thrown.delay == "number"){
4187
+
4188
+ //some environments don't support setTimeout
4189
+ if (typeof setTimeout != "undefined"){
4190
+ testCase.__yui_wait = setTimeout(function(){
4191
+ YUITest.TestRunner._resumeTest(thrown.segment);
4192
+ }, thrown.delay);
4193
+ this._waiting = true;
4194
+ } else {
4195
+ throw new Error("Asynchronous tests not supported in this environment.");
4196
+ }
4197
+ }
4198
+ }
4199
+
4200
+ return;
4201
+
4202
+ } else {
4203
+ //first check to see if it should error
4204
+ if (!shouldError) {
4205
+ error = new YUITest.UnexpectedError(thrown);
4206
+ failed = true;
4207
+ } else {
4208
+ //check to see what type of data we have
4209
+ if (typeof shouldError == "string"){
4210
+
4211
+ //if it's a string, check the error message
4212
+ if (thrown.message != shouldError){
4213
+ error = new YUITest.UnexpectedError(thrown);
4214
+ failed = true;
4215
+ }
4216
+ } else if (typeof shouldError == "function"){
4217
+
4218
+ //if it's a function, see if the error is an instance of it
4219
+ if (!(thrown instanceof shouldError)){
4220
+ error = new YUITest.UnexpectedError(thrown);
4221
+ failed = true;
4222
+ }
4223
+
4224
+ } else if (typeof shouldError == "object" && shouldError !== null){
4225
+
4226
+ //if it's an object, check the instance and message
4227
+ if (!(thrown instanceof shouldError.constructor) ||
4228
+ thrown.message != shouldError.message){
4229
+ error = new YUITest.UnexpectedError(thrown);
4230
+ failed = true;
4231
+ }
4232
+
4233
+ }
4234
+
4235
+ }
4236
+ }
4237
+
4238
+ }
4239
+
4240
+ //fire appropriate event
4241
+ if (failed) {
4242
+ this.fire({ type: this.TEST_FAIL_EVENT, testCase: testCase, testName: testName, error: error });
4243
+ } else {
4244
+ this.fire({ type: this.TEST_PASS_EVENT, testCase: testCase, testName: testName });
4245
+ }
4246
+
4247
+ //run the tear down
4248
+ this._execNonTestMethod(node.parent, "tearDown", false);
4249
+
4250
+ //reset the assert count
4251
+ YUITest.Assert._reset();
4252
+
4253
+ //calculate duration
4254
+ var duration = (new Date()) - node._start;
4255
+
4256
+ //update results
4257
+ node.parent.results[testName] = {
4258
+ result: failed ? "fail" : "pass",
4259
+ message: error ? error.getMessage() : "Test passed",
4260
+ type: "test",
4261
+ name: testName,
4262
+ duration: duration
4263
+ };
4264
+
4265
+ if (failed){
4266
+ node.parent.results.failed++;
4267
+ } else {
4268
+ node.parent.results.passed++;
4269
+ }
4270
+ node.parent.results.total++;
4271
+
4272
+ //set timeout not supported in all environments
4273
+ if (typeof setTimeout != "undefined"){
4274
+ setTimeout(function(){
4275
+ YUITest.TestRunner._run();
4276
+ }, 0);
4277
+ } else {
4278
+ this._run();
4279
+ }
4280
+
4281
+ },
4282
+
4283
+ /**
4284
+ * Handles an error as if it occurred within the currently executing
4285
+ * test. This is for mock methods that may be called asynchronously
4286
+ * and therefore out of the scope of the TestRunner. Previously, this
4287
+ * error would bubble up to the browser. Now, this method is used
4288
+ * to tell TestRunner about the error. This should never be called
4289
+ * by anyplace other than the Mock object.
4290
+ * @param {Error} error The error object.
4291
+ * @return {Void}
4292
+ * @method _handleError
4293
+ * @private
4294
+ * @static
4295
+ */
4296
+ _handleError: function(error){
4297
+
4298
+ if (this._waiting){
4299
+ this._resumeTest(function(){
4300
+ throw error;
4301
+ });
4302
+ } else {
4303
+ throw error;
4304
+ }
4305
+
4306
+ },
4307
+
4308
+ /**
4309
+ * Runs a single test based on the data provided in the node.
4310
+ * @param {TestNode} node The TestNode representing the test to run.
4311
+ * @return {Void}
4312
+ * @static
4313
+ * @private
4314
+ * @name _runTest
4315
+ */
4316
+ _runTest : function (node) {
4317
+
4318
+ //get relevant information
4319
+ var testName = node.testObject,
4320
+ testCase = node.parent.testObject,
4321
+ test = testCase[testName],
4322
+
4323
+ //get the "should" test cases
4324
+ shouldIgnore = testName.indexOf("ignore:") === 0 ||
4325
+ !inGroups(testCase.groups, this._groups) ||
4326
+ (testCase._should.ignore || {})[testName]; //deprecated
4327
+
4328
+ //figure out if the test should be ignored or not
4329
+ if (shouldIgnore){
4330
+
4331
+ //update results
4332
+ node.parent.results[testName] = {
4333
+ result: "ignore",
4334
+ message: "Test ignored",
4335
+ type: "test",
4336
+ name: testName.indexOf("ignore:") === 0 ? testName.substring(7) : testName
4337
+ };
4338
+
4339
+ node.parent.results.ignored++;
4340
+ node.parent.results.total++;
4341
+
4342
+ this.fire({ type: this.TEST_IGNORE_EVENT, testCase: testCase, testName: testName });
4343
+
4344
+ //some environments don't support setTimeout
4345
+ if (typeof setTimeout != "undefined"){
4346
+ setTimeout(function(){
4347
+ YUITest.TestRunner._run();
4348
+ }, 0);
4349
+ } else {
4350
+ this._run();
4351
+ }
4352
+
4353
+ } else {
4354
+
4355
+ //mark the start time
4356
+ node._start = new Date();
4357
+
4358
+ //run the setup
4359
+ this._execNonTestMethod(node.parent, "setUp", false);
4360
+
4361
+ //now call the body of the test
4362
+ this._resumeTest(test);
4363
+ }
4364
+
4365
+ },
4366
+
4367
+ //-------------------------------------------------------------------------
4368
+ // Misc Methods
4369
+ //-------------------------------------------------------------------------
4370
+
4371
+ /**
4372
+ * Retrieves the name of the current result set.
4373
+ * @return {String} The name of the result set.
4374
+ * @method getName
4375
+ */
4376
+ getName: function(){
4377
+ return this.masterSuite.name;
4378
+ },
4379
+
4380
+ /**
4381
+ * The name assigned to the master suite of the TestRunner. This is the name
4382
+ * that is output as the root's name when results are retrieved.
4383
+ * @param {String} name The name of the result set.
4384
+ * @return {Void}
4385
+ * @method setName
4386
+ */
4387
+ setName: function(name){
4388
+ this.masterSuite.name = name;
4389
+ },
4390
+
4391
+ //-------------------------------------------------------------------------
4392
+ // Public Methods
4393
+ //-------------------------------------------------------------------------
4394
+
4395
+ /**
4396
+ * Adds a test suite or test case to the list of test objects to run.
4397
+ * @param testObject Either a TestCase or a TestSuite that should be run.
4398
+ * @return {Void}
4399
+ * @method add
4400
+ * @static
4401
+ */
4402
+ add : function (testObject) {
4403
+ this.masterSuite.add(testObject);
4404
+ return this;
4405
+ },
4406
+
4407
+ /**
4408
+ * Removes all test objects from the runner.
4409
+ * @return {Void}
4410
+ * @method clear
4411
+ * @static
4412
+ */
4413
+ clear : function () {
4414
+ this.masterSuite = new YUITest.TestSuite("yuitests" + (new Date()).getTime());
4415
+ },
4416
+
4417
+ /**
4418
+ * Indicates if the TestRunner is waiting for a test to resume
4419
+ * @return {Boolean} True if the TestRunner is waiting, false if not.
4420
+ * @method isWaiting
4421
+ * @static
4422
+ */
4423
+ isWaiting: function() {
4424
+ return this._waiting;
4425
+ },
4426
+
4427
+ /**
4428
+ * Indicates that the TestRunner is busy running tests and therefore can't
4429
+ * be stopped and results cannot be gathered.
4430
+ * @return {Boolean} True if the TestRunner is running, false if not.
4431
+ * @method isRunning
4432
+ */
4433
+ isRunning: function(){
4434
+ return this._running;
4435
+ },
4436
+
4437
+ /**
4438
+ * Returns the last complete results set from the TestRunner. Null is returned
4439
+ * if the TestRunner is running or no tests have been run.
4440
+ * @param {Function} format (Optional) A test format to return the results in.
4441
+ * @return {Object|String} Either the results object or, if a test format is
4442
+ * passed as the argument, a string representing the results in a specific
4443
+ * format.
4444
+ * @method getResults
4445
+ */
4446
+ getResults: function(format){
4447
+ if (!this._running && this._lastResults){
4448
+ if (typeof format == "function"){
4449
+ return format(this._lastResults);
4450
+ } else {
4451
+ return this._lastResults;
4452
+ }
4453
+ } else {
4454
+ return null;
4455
+ }
4456
+ },
4457
+
4458
+ /**
4459
+ * Returns the coverage report for the files that have been executed.
4460
+ * This returns only coverage information for files that have been
4461
+ * instrumented using YUI Test Coverage and only those that were run
4462
+ * in the same pass.
4463
+ * @param {Function} format (Optional) A coverage format to return results in.
4464
+ * @return {Object|String} Either the coverage object or, if a coverage
4465
+ * format is specified, a string representing the results in that format.
4466
+ * @method getCoverage
4467
+ */
4468
+ getCoverage: function(format){
4469
+ if (!this._running && typeof _yuitest_coverage == "object"){
4470
+ if (typeof format == "function"){
4471
+ return format(_yuitest_coverage);
4472
+ } else {
4473
+ return _yuitest_coverage;
4474
+ }
4475
+ } else {
4476
+ return null;
4477
+ }
4478
+ },
4479
+
4480
+ /**
4481
+ * Used to continue processing when a method marked with
4482
+ * "async:" is executed. This should not be used in test
4483
+ * methods, only in init(). Each argument is a string, and
4484
+ * when the returned function is executed, the arguments
4485
+ * are assigned to the context data object using the string
4486
+ * as the key name (value is the argument itself).
4487
+ * @private
4488
+ * @return {Function} A callback function.
4489
+ */
4490
+ callback: function(){
4491
+ var names = arguments,
4492
+ data = this._context,
4493
+ that = this;
4494
+
4495
+ return function(){
4496
+ for (var i=0; i < arguments.length; i++){
4497
+ data[names[i]] = arguments[i];
4498
+ }
4499
+ that._run();
4500
+ };
4501
+ },
4502
+
4503
+ /**
4504
+ * Resumes the TestRunner after wait() was called.
4505
+ * @param {Function} segment The function to run as the rest
4506
+ * of the haulted test.
4507
+ * @return {Void}
4508
+ * @method resume
4509
+ * @static
4510
+ */
4511
+ resume : function (segment) {
4512
+ if (this._waiting){
4513
+ this._resumeTest(segment || function(){});
4514
+ } else {
4515
+ throw new Error("resume() called without wait().");
4516
+ }
4517
+ },
4518
+
4519
+ /**
4520
+ * Runs the test suite.
4521
+ * @param {Object|Boolean} options (Optional) Options for the runner:
4522
+ * <code>oldMode</code> indicates the TestRunner should work in the YUI <= 2.8 way
4523
+ * of internally managing test suites. <code>groups</code> is an array
4524
+ * of test groups indicating which tests to run.
4525
+ * @return {Void}
4526
+ * @method run
4527
+ * @static
4528
+ */
4529
+ run : function (options) {
4530
+
4531
+ options = options || {};
4532
+
4533
+ //pointer to runner to avoid scope issues
4534
+ var runner = YUITest.TestRunner,
4535
+ oldMode = options.oldMode;
4536
+
4537
+
4538
+ //if there's only one suite on the masterSuite, move it up
4539
+ if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof YUITest.TestSuite){
4540
+ this.masterSuite = this.masterSuite.items[0];
4541
+ }
4542
+
4543
+ //determine if there are any groups to filter on
4544
+ runner._groups = (options.groups instanceof Array) ? "," + options.groups.join(",") + "," : "";
4545
+
4546
+ //initialize the runner
4547
+ runner._buildTestTree();
4548
+ runner._context = {};
4549
+ runner._root._start = new Date();
4550
+
4551
+ //fire the begin event
4552
+ runner.fire(runner.BEGIN_EVENT);
4553
+
4554
+ //begin the testing
4555
+ runner._run();
4556
+ }
4557
+ });
4558
+
4559
+ return new TestRunner();
4560
+
4561
+ }();