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,3955 @@
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
+ /**
183
+ * Object containing object helper methods.
184
+ * @namespace YUITest
185
+ * @class Object
186
+ * @static
187
+ */
188
+ YUITest.Object = {
189
+ /**
190
+ * Property names that IE doesn't enumerate in for..in loops, even when they
191
+ * should be enumerable. When `_hasEnumBug` is `true`, it's necessary to
192
+ * manually enumerate these properties.
193
+ *
194
+ * @property _forceEnum
195
+ * @type String[]
196
+ * @protected
197
+ * @static
198
+ */
199
+ _forceEnum : [
200
+ 'hasOwnProperty',
201
+ 'isPrototypeOf',
202
+ 'propertyIsEnumerable',
203
+ 'toString',
204
+ 'toLocaleString',
205
+ 'valueOf'
206
+ ],
207
+
208
+ /**
209
+ * `true` if this browser has the JScript enumeration bug that prevents
210
+ * enumeration of the properties named in the `_forceEnum` array, `false`
211
+ * otherwise.
212
+ *
213
+ * See:
214
+ * - <https://developer.mozilla.org/en/ECMAScript_DontEnum_attribute#JScript_DontEnum_Bug>
215
+ * - <http://whattheheadsaid.com/2010/10/a-safer-object-keys-compatibility-implementation>
216
+ *
217
+ * @property _hasEnumBug
218
+ * @type {Boolean}
219
+ * @protected
220
+ * @static
221
+ */
222
+ _hasEnumBug : !{valueOf: 0}.propertyIsEnumerable('valueOf'),
223
+
224
+ /**
225
+ * Determines whether or not the provided item is of type object
226
+ * or function. Note that arrays are also objects, so
227
+ * <code>YUITest.Object.isObject([]) === true</code>.
228
+ * @method isObject
229
+ * @static
230
+ * @param o The object to test.
231
+ * @param failfn {boolean} fail if the input is a function.
232
+ * @return {boolean} true if o is an object.
233
+ */
234
+ _isObject : function(o, failfn) {
235
+ var t = typeof o;
236
+ return (o && (t === 'object' || (!failfn && (t === 'function' || typeof t === 'function')))) || false;
237
+ },
238
+
239
+ /**
240
+ * Returns an array containing the object's enumerable keys. Does not include
241
+ * prototype keys or non-enumerable keys.
242
+ *
243
+ * Note that keys are returned in enumeration order (that is, in the same order
244
+ * that they would be enumerated by a `for-in` loop), which may not be the same
245
+ * as the order in which they were defined.
246
+ *
247
+ * This method is an alias for the native ES5 `Object.keys()` method if
248
+ * available.
249
+ *
250
+ * @example
251
+ *
252
+ * Y.Object.keys({a: 'foo', b: 'bar', c: 'baz'});
253
+ * // => ['a', 'b', 'c']
254
+ *
255
+ * @method keys
256
+ * @param {Object} obj An object.
257
+ * @return {String[]} Array of keys.
258
+ * @static
259
+ */
260
+ keys : Object.keys || function (obj) {
261
+ if (!YUITest.Object.isObject(obj)) {
262
+ throw new TypeError('Object.keys called on a non-object');
263
+ }
264
+
265
+ var keys = [],
266
+ i, key, len;
267
+
268
+ for (key in obj) {
269
+ if (owns(obj, key)) {
270
+ keys.push(key);
271
+ }
272
+ }
273
+
274
+ if (YUITest.Object._hasEnumBug) {
275
+ for (i = 0, len = YUITest.Object._forceEnum.length; i < len; ++i) {
276
+ key = YUITest.Object._forceEnum[i];
277
+
278
+ if (owns(obj, key)) {
279
+ keys.push(key);
280
+ }
281
+ }
282
+ }
283
+
284
+ return keys;
285
+ }
286
+ };
287
+
288
+ /**
289
+ * Error is thrown whenever an assertion fails. It provides methods
290
+ * to more easily get at error information and also provides a base class
291
+ * from which more specific assertion errors can be derived.
292
+ *
293
+ * @param {String} message The message to display when the error occurs.
294
+ * @namespace YUITest
295
+ * @class AssertionError
296
+ * @constructor
297
+ */
298
+ YUITest.AssertionError = function (message){
299
+
300
+ /**
301
+ * Error message. Must be duplicated to ensure browser receives it.
302
+ * @type String
303
+ * @property message
304
+ */
305
+ this.message = message;
306
+
307
+ /**
308
+ * The name of the error that occurred.
309
+ * @type String
310
+ * @property name
311
+ */
312
+ this.name = "Assert Error";
313
+ };
314
+
315
+ YUITest.AssertionError.prototype = {
316
+
317
+ //restore constructor
318
+ constructor: YUITest.AssertionError,
319
+
320
+ /**
321
+ * Returns a fully formatted error for an assertion failure. This should
322
+ * be overridden by all subclasses to provide specific information.
323
+ * @method getMessage
324
+ * @return {String} A string describing the error.
325
+ */
326
+ getMessage : function () {
327
+ return this.message;
328
+ },
329
+
330
+ /**
331
+ * Returns a string representation of the error.
332
+ * @method toString
333
+ * @return {String} A string representation of the error.
334
+ */
335
+ toString : function () {
336
+ return this.name + ": " + this.getMessage();
337
+ }
338
+
339
+ };
340
+
341
+ /**
342
+ * ComparisonFailure is subclass of Error that is thrown whenever
343
+ * a comparison between two values fails. It provides mechanisms to retrieve
344
+ * both the expected and actual value.
345
+ *
346
+ * @param {String} message The message to display when the error occurs.
347
+ * @param {Object} expected The expected value.
348
+ * @param {Object} actual The actual value that caused the assertion to fail.
349
+ * @namespace YUITest
350
+ * @extends AssertionError
351
+ * @class ComparisonFailure
352
+ * @constructor
353
+ */
354
+ YUITest.ComparisonFailure = function (message, expected, actual){
355
+
356
+ //call superclass
357
+ YUITest.AssertionError.call(this, message);
358
+
359
+ /**
360
+ * The expected value.
361
+ * @type Object
362
+ * @property expected
363
+ */
364
+ this.expected = expected;
365
+
366
+ /**
367
+ * The actual value.
368
+ * @type Object
369
+ * @property actual
370
+ */
371
+ this.actual = actual;
372
+
373
+ /**
374
+ * The name of the error that occurred.
375
+ * @type String
376
+ * @property name
377
+ */
378
+ this.name = "ComparisonFailure";
379
+
380
+ };
381
+
382
+ //inherit from YUITest.AssertionError
383
+ YUITest.ComparisonFailure.prototype = new YUITest.AssertionError;
384
+
385
+ //restore constructor
386
+ YUITest.ComparisonFailure.prototype.constructor = YUITest.ComparisonFailure;
387
+
388
+ /**
389
+ * Returns a fully formatted error for an assertion failure. This message
390
+ * provides information about the expected and actual values.
391
+ * @method getMessage
392
+ * @return {String} A string describing the error.
393
+ */
394
+ YUITest.ComparisonFailure.prototype.getMessage = function(){
395
+ return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
396
+ "\nActual: " + this.actual + " (" + (typeof this.actual) + ")";
397
+ };
398
+
399
+ /**
400
+ * ShouldError is subclass of Error that is thrown whenever
401
+ * a test is expected to throw an error but doesn't.
402
+ *
403
+ * @param {String} message The message to display when the error occurs.
404
+ * @namespace YUITest
405
+ * @extends AssertionError
406
+ * @class ShouldError
407
+ * @constructor
408
+ */
409
+ YUITest.ShouldError = function (message){
410
+
411
+ //call superclass
412
+ YUITest.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
413
+
414
+ /**
415
+ * The name of the error that occurred.
416
+ * @type String
417
+ * @property name
418
+ */
419
+ this.name = "ShouldError";
420
+
421
+ };
422
+
423
+ //inherit from YUITest.AssertionError
424
+ YUITest.ShouldError.prototype = new YUITest.AssertionError();
425
+
426
+ //restore constructor
427
+ YUITest.ShouldError.prototype.constructor = YUITest.ShouldError;
428
+
429
+ /**
430
+ * ShouldFail is subclass of AssertionError that is thrown whenever
431
+ * a test was expected to fail but did not.
432
+ *
433
+ * @param {String} message The message to display when the error occurs.
434
+ * @namespace YUITest
435
+ * @extends YUITest.AssertionError
436
+ * @class ShouldFail
437
+ * @constructor
438
+ */
439
+ YUITest.ShouldFail = function (message){
440
+
441
+ //call superclass
442
+ YUITest.AssertionError.call(this, message || "This test should fail but didn't.");
443
+
444
+ /**
445
+ * The name of the error that occurred.
446
+ * @type String
447
+ * @property name
448
+ */
449
+ this.name = "ShouldFail";
450
+
451
+ };
452
+
453
+ //inherit from YUITest.AssertionError
454
+ YUITest.ShouldFail.prototype = new YUITest.AssertionError();
455
+
456
+ //restore constructor
457
+ YUITest.ShouldFail.prototype.constructor = YUITest.ShouldFail;
458
+
459
+ /**
460
+ * UnexpectedError is subclass of AssertionError that is thrown whenever
461
+ * an error occurs within the course of a test and the test was not expected
462
+ * to throw an error.
463
+ *
464
+ * @param {Error} cause The unexpected error that caused this error to be
465
+ * thrown.
466
+ * @namespace YUITest
467
+ * @extends YUITest.AssertionError
468
+ * @class UnexpectedError
469
+ * @constructor
470
+ */
471
+ YUITest.UnexpectedError = function (cause){
472
+
473
+ //call superclass
474
+ YUITest.AssertionError.call(this, "Unexpected error: " + cause.message);
475
+
476
+ /**
477
+ * The unexpected error that occurred.
478
+ * @type Error
479
+ * @property cause
480
+ */
481
+ this.cause = cause;
482
+
483
+ /**
484
+ * The name of the error that occurred.
485
+ * @type String
486
+ * @property name
487
+ */
488
+ this.name = "UnexpectedError";
489
+
490
+ /**
491
+ * Stack information for the error (if provided).
492
+ * @type String
493
+ * @property stack
494
+ */
495
+ this.stack = cause.stack;
496
+
497
+ };
498
+
499
+ //inherit from YUITest.AssertionError
500
+ YUITest.UnexpectedError.prototype = new YUITest.AssertionError();
501
+
502
+ //restore constructor
503
+ YUITest.UnexpectedError.prototype.constructor = YUITest.UnexpectedError;
504
+
505
+ /**
506
+ * UnexpectedValue is subclass of Error that is thrown whenever
507
+ * a value was unexpected in its scope. This typically means that a test
508
+ * was performed to determine that a value was *not* equal to a certain
509
+ * value.
510
+ *
511
+ * @param {String} message The message to display when the error occurs.
512
+ * @param {Object} unexpected The unexpected value.
513
+ * @namespace YUITest
514
+ * @extends AssertionError
515
+ * @class UnexpectedValue
516
+ * @constructor
517
+ */
518
+ YUITest.UnexpectedValue = function (message, unexpected){
519
+
520
+ //call superclass
521
+ YUITest.AssertionError.call(this, message);
522
+
523
+ /**
524
+ * The unexpected value.
525
+ * @type Object
526
+ * @property unexpected
527
+ */
528
+ this.unexpected = unexpected;
529
+
530
+ /**
531
+ * The name of the error that occurred.
532
+ * @type String
533
+ * @property name
534
+ */
535
+ this.name = "UnexpectedValue";
536
+
537
+ };
538
+
539
+ //inherit from YUITest.AssertionError
540
+ YUITest.UnexpectedValue.prototype = new YUITest.AssertionError();
541
+
542
+ //restore constructor
543
+ YUITest.UnexpectedValue.prototype.constructor = YUITest.UnexpectedValue;
544
+
545
+ /**
546
+ * Returns a fully formatted error for an assertion failure. This message
547
+ * provides information about the expected and actual values.
548
+ * @method getMessage
549
+ * @return {String} A string describing the error.
550
+ */
551
+ YUITest.UnexpectedValue.prototype.getMessage = function(){
552
+ return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
553
+ };
554
+
555
+
556
+ /**
557
+ * Represents a stoppage in test execution to wait for an amount of time before
558
+ * continuing.
559
+ * @param {Function} segment A function to run when the wait is over.
560
+ * @param {int} delay The number of milliseconds to wait before running the code.
561
+ * @class Wait
562
+ * @namespace Test
563
+ * @constructor
564
+ *
565
+ */
566
+ YUITest.Wait = function (segment, delay) {
567
+
568
+ /**
569
+ * The segment of code to run when the wait is over.
570
+ * @type Function
571
+ * @property segment
572
+ */
573
+ this.segment = (typeof segment == "function" ? segment : null);
574
+
575
+ /**
576
+ * The delay before running the segment of code.
577
+ * @type int
578
+ * @property delay
579
+ */
580
+ this.delay = (typeof delay == "number" ? delay : 0);
581
+ };
582
+
583
+
584
+ /**
585
+ * The Assert object provides functions to test JavaScript values against
586
+ * known and expected results. Whenever a comparison (assertion) fails,
587
+ * an error is thrown.
588
+ * @namespace YUITest
589
+ * @class Assert
590
+ * @static
591
+ */
592
+ YUITest.Assert = {
593
+
594
+ /**
595
+ * The number of assertions performed.
596
+ * @property _asserts
597
+ * @type int
598
+ * @private
599
+ */
600
+ _asserts: 0,
601
+
602
+ //-------------------------------------------------------------------------
603
+ // Helper Methods
604
+ //-------------------------------------------------------------------------
605
+
606
+ /**
607
+ * Formats a message so that it can contain the original assertion message
608
+ * in addition to the custom message.
609
+ * @param {String} customMessage The message passed in by the developer.
610
+ * @param {String} defaultMessage The message created by the error by default.
611
+ * @return {String} The final error message, containing either or both.
612
+ * @protected
613
+ * @static
614
+ * @method _formatMessage
615
+ */
616
+ _formatMessage : function (customMessage, defaultMessage) {
617
+ if (typeof customMessage == "string" && customMessage.length > 0){
618
+ return customMessage.replace("{message}", defaultMessage);
619
+ } else {
620
+ return defaultMessage;
621
+ }
622
+ },
623
+
624
+ /**
625
+ * Returns the number of assertions that have been performed.
626
+ * @method _getCount
627
+ * @protected
628
+ * @static
629
+ */
630
+ _getCount: function(){
631
+ return this._asserts;
632
+ },
633
+
634
+ /**
635
+ * Increments the number of assertions that have been performed.
636
+ * @method _increment
637
+ * @protected
638
+ * @static
639
+ */
640
+ _increment: function(){
641
+ this._asserts++;
642
+ },
643
+
644
+ /**
645
+ * Resets the number of assertions that have been performed to 0.
646
+ * @method _reset
647
+ * @protected
648
+ * @static
649
+ */
650
+ _reset: function(){
651
+ this._asserts = 0;
652
+ },
653
+
654
+ //-------------------------------------------------------------------------
655
+ // Generic Assertion Methods
656
+ //-------------------------------------------------------------------------
657
+
658
+ /**
659
+ * Forces an assertion error to occur.
660
+ * @param {String} message (Optional) The message to display with the failure.
661
+ * @method fail
662
+ * @static
663
+ */
664
+ fail : function (message) {
665
+ throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Test force-failed."));
666
+ },
667
+
668
+ //-------------------------------------------------------------------------
669
+ // Equality Assertion Methods
670
+ //-------------------------------------------------------------------------
671
+
672
+ /**
673
+ * Asserts that a value is equal to another. This uses the double equals sign
674
+ * so type cohersion may occur.
675
+ * @param {Object} expected The expected value.
676
+ * @param {Object} actual The actual value to test.
677
+ * @param {String} message (Optional) The message to display if the assertion fails.
678
+ * @method areEqual
679
+ * @static
680
+ */
681
+ areEqual : function (expected, actual, message) {
682
+ YUITest.Assert._increment();
683
+ if (expected != actual) {
684
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal."), expected, actual);
685
+ }
686
+ },
687
+
688
+ /**
689
+ * Asserts that a value is not equal to another. This uses the double equals sign
690
+ * so type cohersion may occur.
691
+ * @param {Object} unexpected The unexpected value.
692
+ * @param {Object} actual The actual value to test.
693
+ * @param {String} message (Optional) The message to display if the assertion fails.
694
+ * @method areNotEqual
695
+ * @static
696
+ */
697
+ areNotEqual : function (unexpected, actual,
698
+ message) {
699
+ YUITest.Assert._increment();
700
+ if (unexpected == actual) {
701
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be equal."), unexpected);
702
+ }
703
+ },
704
+
705
+ /**
706
+ * Asserts that a value is not the same as another. This uses the triple equals sign
707
+ * so no type cohersion may occur.
708
+ * @param {Object} unexpected The unexpected value.
709
+ * @param {Object} actual The actual value to test.
710
+ * @param {String} message (Optional) The message to display if the assertion fails.
711
+ * @method areNotSame
712
+ * @static
713
+ */
714
+ areNotSame : function (unexpected, actual, message) {
715
+ YUITest.Assert._increment();
716
+ if (unexpected === actual) {
717
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be the same."), unexpected);
718
+ }
719
+ },
720
+
721
+ /**
722
+ * Asserts that a value is the same as another. This uses the triple equals sign
723
+ * so no type cohersion may occur.
724
+ * @param {Object} expected The expected value.
725
+ * @param {Object} actual The actual value to test.
726
+ * @param {String} message (Optional) The message to display if the assertion fails.
727
+ * @method areSame
728
+ * @static
729
+ */
730
+ areSame : function (expected, actual, message) {
731
+ YUITest.Assert._increment();
732
+ if (expected !== actual) {
733
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be the same."), expected, actual);
734
+ }
735
+ },
736
+
737
+ //-------------------------------------------------------------------------
738
+ // Boolean Assertion Methods
739
+ //-------------------------------------------------------------------------
740
+
741
+ /**
742
+ * Asserts that a value is false. This uses the triple equals sign
743
+ * so no type cohersion may occur.
744
+ * @param {Object} actual The actual value to test.
745
+ * @param {String} message (Optional) The message to display if the assertion fails.
746
+ * @method isFalse
747
+ * @static
748
+ */
749
+ isFalse : function (actual, message) {
750
+ YUITest.Assert._increment();
751
+ if (false !== actual) {
752
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be false."), false, actual);
753
+ }
754
+ },
755
+
756
+ /**
757
+ * Asserts that a value is true. This uses the triple equals sign
758
+ * so no type cohersion may occur.
759
+ * @param {Object} actual The actual value to test.
760
+ * @param {String} message (Optional) The message to display if the assertion fails.
761
+ * @method isTrue
762
+ * @static
763
+ */
764
+ isTrue : function (actual, message) {
765
+ YUITest.Assert._increment();
766
+ if (true !== actual) {
767
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be true."), true, actual);
768
+ }
769
+
770
+ },
771
+
772
+ //-------------------------------------------------------------------------
773
+ // Special Value Assertion Methods
774
+ //-------------------------------------------------------------------------
775
+
776
+ /**
777
+ * Asserts that a value is not a number.
778
+ * @param {Object} actual The value to test.
779
+ * @param {String} message (Optional) The message to display if the assertion fails.
780
+ * @method isNaN
781
+ * @static
782
+ */
783
+ isNaN : function (actual, message){
784
+ YUITest.Assert._increment();
785
+ if (!isNaN(actual)){
786
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
787
+ }
788
+ },
789
+
790
+ /**
791
+ * Asserts that a value is not the special NaN value.
792
+ * @param {Object} actual The value to test.
793
+ * @param {String} message (Optional) The message to display if the assertion fails.
794
+ * @method isNotNaN
795
+ * @static
796
+ */
797
+ isNotNaN : function (actual, message){
798
+ YUITest.Assert._increment();
799
+ if (isNaN(actual)){
800
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be NaN."), NaN);
801
+ }
802
+ },
803
+
804
+ /**
805
+ * Asserts that a value is not null. This uses the triple equals sign
806
+ * so no type cohersion may occur.
807
+ * @param {Object} actual The actual value to test.
808
+ * @param {String} message (Optional) The message to display if the assertion fails.
809
+ * @method isNotNull
810
+ * @static
811
+ */
812
+ isNotNull : function (actual, message) {
813
+ YUITest.Assert._increment();
814
+ if (actual === null) {
815
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be null."), null);
816
+ }
817
+ },
818
+
819
+ /**
820
+ * Asserts that a value is not undefined. This uses the triple equals sign
821
+ * so no type cohersion may occur.
822
+ * @param {Object} actual The actual value to test.
823
+ * @param {String} message (Optional) The message to display if the assertion fails.
824
+ * @method isNotUndefined
825
+ * @static
826
+ */
827
+ isNotUndefined : function (actual, message) {
828
+ YUITest.Assert._increment();
829
+ if (typeof actual == "undefined") {
830
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should not be undefined."), undefined);
831
+ }
832
+ },
833
+
834
+ /**
835
+ * Asserts that a value is null. This uses the triple equals sign
836
+ * so no type cohersion may occur.
837
+ * @param {Object} actual The actual value to test.
838
+ * @param {String} message (Optional) The message to display if the assertion fails.
839
+ * @method isNull
840
+ * @static
841
+ */
842
+ isNull : function (actual, message) {
843
+ YUITest.Assert._increment();
844
+ if (actual !== null) {
845
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be null."), null, actual);
846
+ }
847
+ },
848
+
849
+ /**
850
+ * Asserts that a value is undefined. This uses the triple equals sign
851
+ * so no type cohersion may occur.
852
+ * @param {Object} actual The actual value to test.
853
+ * @param {String} message (Optional) The message to display if the assertion fails.
854
+ * @method isUndefined
855
+ * @static
856
+ */
857
+ isUndefined : function (actual, message) {
858
+ YUITest.Assert._increment();
859
+ if (typeof actual != "undefined") {
860
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
861
+ }
862
+ },
863
+
864
+ //--------------------------------------------------------------------------
865
+ // Instance Assertion Methods
866
+ //--------------------------------------------------------------------------
867
+
868
+ /**
869
+ * Asserts that a value is an array.
870
+ * @param {Object} actual The value to test.
871
+ * @param {String} message (Optional) The message to display if the assertion fails.
872
+ * @method isArray
873
+ * @static
874
+ */
875
+ isArray : function (actual, message) {
876
+ YUITest.Assert._increment();
877
+ var shouldFail = false;
878
+ if (Array.isArray){
879
+ shouldFail = !Array.isArray(actual);
880
+ } else {
881
+ shouldFail = Object.prototype.toString.call(actual) != "[object Array]";
882
+ }
883
+ if (shouldFail){
884
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an array."), actual);
885
+ }
886
+ },
887
+
888
+ /**
889
+ * Asserts that a value is a Boolean.
890
+ * @param {Object} actual The value to test.
891
+ * @param {String} message (Optional) The message to display if the assertion fails.
892
+ * @method isBoolean
893
+ * @static
894
+ */
895
+ isBoolean : function (actual, message) {
896
+ YUITest.Assert._increment();
897
+ if (typeof actual != "boolean"){
898
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a Boolean."), actual);
899
+ }
900
+ },
901
+
902
+ /**
903
+ * Asserts that a value is a function.
904
+ * @param {Object} actual The value to test.
905
+ * @param {String} message (Optional) The message to display if the assertion fails.
906
+ * @method isFunction
907
+ * @static
908
+ */
909
+ isFunction : function (actual, message) {
910
+ YUITest.Assert._increment();
911
+ if (!(actual instanceof Function)){
912
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a function."), actual);
913
+ }
914
+ },
915
+
916
+ /**
917
+ * Asserts that a value is an instance of a particular object. This may return
918
+ * incorrect results when comparing objects from one frame to constructors in
919
+ * another frame. For best results, don't use in a cross-frame manner.
920
+ * @param {Function} expected The function that the object should be an instance of.
921
+ * @param {Object} actual The object to test.
922
+ * @param {String} message (Optional) The message to display if the assertion fails.
923
+ * @method isInstanceOf
924
+ * @static
925
+ */
926
+ isInstanceOf : function (expected, actual, message) {
927
+ YUITest.Assert._increment();
928
+ if (!(actual instanceof expected)){
929
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
930
+ }
931
+ },
932
+
933
+ /**
934
+ * Asserts that a value is a number.
935
+ * @param {Object} actual The value to test.
936
+ * @param {String} message (Optional) The message to display if the assertion fails.
937
+ * @method isNumber
938
+ * @static
939
+ */
940
+ isNumber : function (actual, message) {
941
+ YUITest.Assert._increment();
942
+ if (typeof actual != "number"){
943
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a number."), actual);
944
+ }
945
+ },
946
+
947
+ /**
948
+ * Asserts that a value is an object.
949
+ * @param {Object} actual The value to test.
950
+ * @param {String} message (Optional) The message to display if the assertion fails.
951
+ * @method isObject
952
+ * @static
953
+ */
954
+ isObject : function (actual, message) {
955
+ YUITest.Assert._increment();
956
+ if (!actual || (typeof actual != "object" && typeof actual != "function")){
957
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an object."), actual);
958
+ }
959
+ },
960
+
961
+ /**
962
+ * Asserts that a value is a string.
963
+ * @param {Object} actual The value to test.
964
+ * @param {String} message (Optional) The message to display if the assertion fails.
965
+ * @method isString
966
+ * @static
967
+ */
968
+ isString : function (actual, message) {
969
+ YUITest.Assert._increment();
970
+ if (typeof actual != "string"){
971
+ throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a string."), actual);
972
+ }
973
+ },
974
+
975
+ /**
976
+ * Asserts that a value is of a particular type.
977
+ * @param {String} expectedType The expected type of the variable.
978
+ * @param {Object} actualValue The actual value to test.
979
+ * @param {String} message (Optional) The message to display if the assertion fails.
980
+ * @method isTypeOf
981
+ * @static
982
+ */
983
+ isTypeOf : function (expectedType, actualValue, message){
984
+ YUITest.Assert._increment();
985
+ if (typeof actualValue != expectedType){
986
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expectedType, typeof actualValue);
987
+ }
988
+ },
989
+
990
+ //--------------------------------------------------------------------------
991
+ // Error Detection Methods
992
+ //--------------------------------------------------------------------------
993
+
994
+ /**
995
+ * Asserts that executing a particular method should throw an error of
996
+ * a specific type. This is a replacement for _should.error.
997
+ * @param {String|Function|Object} expectedError If a string, this
998
+ * is the error message that the error must have; if a function, this
999
+ * is the constructor that should have been used to create the thrown
1000
+ * error; if an object, this is an instance of a particular error type
1001
+ * with a specific error message (both must match).
1002
+ * @param {Function} method The method to execute that should throw the error.
1003
+ * @param {String} message (Optional) The message to display if the assertion
1004
+ * fails.
1005
+ * @method throwsError
1006
+ * @return {void}
1007
+ * @static
1008
+ */
1009
+ throwsError: function(expectedError, method, message){
1010
+ YUITest.Assert._increment();
1011
+ var error = false;
1012
+
1013
+ try {
1014
+ method();
1015
+ } catch (thrown) {
1016
+
1017
+ //check to see what type of data we have
1018
+ if (typeof expectedError == "string"){
1019
+
1020
+ //if it's a string, check the error message
1021
+ if (thrown.message != expectedError){
1022
+ error = true;
1023
+ }
1024
+ } else if (typeof expectedError == "function"){
1025
+
1026
+ //if it's a function, see if the error is an instance of it
1027
+ if (!(thrown instanceof expectedError)){
1028
+ error = true;
1029
+ }
1030
+
1031
+ } else if (typeof expectedError == "object" && expectedError !== null){
1032
+
1033
+ //if it's an object, check the instance and message
1034
+ if (!(thrown instanceof expectedError.constructor) ||
1035
+ thrown.message != expectedError.message){
1036
+ error = true;
1037
+ }
1038
+
1039
+ } else { //if it gets here, the argument could be wrong
1040
+ error = true;
1041
+ }
1042
+
1043
+ if (error){
1044
+ throw new YUITest.UnexpectedError(thrown);
1045
+ } else {
1046
+ return;
1047
+ }
1048
+ }
1049
+
1050
+ //if it reaches here, the error wasn't thrown, which is a bad thing
1051
+ throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Error should have been thrown."));
1052
+ }
1053
+
1054
+ };
1055
+
1056
+
1057
+ /**
1058
+ * The ArrayAssert object provides functions to test JavaScript array objects
1059
+ * for a variety of cases.
1060
+ * @namespace YUITest
1061
+ * @class ArrayAssert
1062
+ * @static
1063
+ */
1064
+
1065
+ YUITest.ArrayAssert = {
1066
+
1067
+ //=========================================================================
1068
+ // Private methods
1069
+ //=========================================================================
1070
+
1071
+ /**
1072
+ * Simple indexOf() implementation for an array. Defers to native
1073
+ * if available.
1074
+ * @param {Array} haystack The array to search.
1075
+ * @param {Variant} needle The value to locate.
1076
+ * @return {int} The index of the needle if found or -1 if not.
1077
+ * @method _indexOf
1078
+ * @private
1079
+ */
1080
+ _indexOf: function(haystack, needle){
1081
+ if (haystack.indexOf){
1082
+ return haystack.indexOf(needle);
1083
+ } else {
1084
+ for (var i=0; i < haystack.length; i++){
1085
+ if (haystack[i] === needle){
1086
+ return i;
1087
+ }
1088
+ }
1089
+ return -1;
1090
+ }
1091
+ },
1092
+
1093
+ /**
1094
+ * Simple some() implementation for an array. Defers to native
1095
+ * if available.
1096
+ * @param {Array} haystack The array to search.
1097
+ * @param {Function} matcher The function to run on each value.
1098
+ * @return {Boolean} True if any value, when run through the matcher,
1099
+ * returns true.
1100
+ * @method _some
1101
+ * @private
1102
+ */
1103
+ _some: function(haystack, matcher){
1104
+ if (haystack.some){
1105
+ return haystack.some(matcher);
1106
+ } else {
1107
+ for (var i=0; i < haystack.length; i++){
1108
+ if (matcher(haystack[i])){
1109
+ return true;
1110
+ }
1111
+ }
1112
+ return false;
1113
+ }
1114
+ },
1115
+
1116
+ /**
1117
+ * Asserts that a value is present in an array. This uses the triple equals
1118
+ * sign so no type cohersion may occur.
1119
+ * @param {Object} needle The value that is expected in the array.
1120
+ * @param {Array} haystack An array of values.
1121
+ * @param {String} message (Optional) The message to display if the assertion fails.
1122
+ * @method contains
1123
+ * @static
1124
+ */
1125
+ contains : function (needle, haystack,
1126
+ message) {
1127
+
1128
+ YUITest.Assert._increment();
1129
+
1130
+ if (this._indexOf(haystack, needle) == -1){
1131
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1132
+ }
1133
+ },
1134
+
1135
+ /**
1136
+ * Asserts that a set of values are present in an array. This uses the triple equals
1137
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
1138
+ * be found.
1139
+ * @param {Object[]} needles An array of values that are expected in the array.
1140
+ * @param {Array} haystack An array of values to check.
1141
+ * @param {String} message (Optional) The message to display if the assertion fails.
1142
+ * @method containsItems
1143
+ * @static
1144
+ */
1145
+ containsItems : function (needles, haystack,
1146
+ message) {
1147
+ YUITest.Assert._increment();
1148
+
1149
+ //begin checking values
1150
+ for (var i=0; i < needles.length; i++){
1151
+ if (this._indexOf(haystack, needles[i]) == -1){
1152
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needles[i] + " (" + (typeof needles[i]) + ") not found in array [" + haystack + "]."));
1153
+ }
1154
+ }
1155
+ },
1156
+
1157
+ /**
1158
+ * Asserts that a value matching some condition is present in an array. This uses
1159
+ * a function to determine a match.
1160
+ * @param {Function} matcher A function that returns true if the items matches or false if not.
1161
+ * @param {Array} haystack An array of values.
1162
+ * @param {String} message (Optional) The message to display if the assertion fails.
1163
+ * @method containsMatch
1164
+ * @static
1165
+ */
1166
+ containsMatch : function (matcher, haystack,
1167
+ message) {
1168
+
1169
+ YUITest.Assert._increment();
1170
+ //check for valid matcher
1171
+ if (typeof matcher != "function"){
1172
+ throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1173
+ }
1174
+
1175
+ if (!this._some(haystack, matcher)){
1176
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1177
+ }
1178
+ },
1179
+
1180
+ /**
1181
+ * Asserts that a value is not present in an array. This uses the triple equals
1182
+ * Asserts that a value is not present in an array. This uses the triple equals
1183
+ * sign so no type cohersion may occur.
1184
+ * @param {Object} needle The value that is expected in the array.
1185
+ * @param {Array} haystack An array of values.
1186
+ * @param {String} message (Optional) The message to display if the assertion fails.
1187
+ * @method doesNotContain
1188
+ * @static
1189
+ */
1190
+ doesNotContain : function (needle, haystack,
1191
+ message) {
1192
+
1193
+ YUITest.Assert._increment();
1194
+
1195
+ if (this._indexOf(haystack, needle) > -1){
1196
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1197
+ }
1198
+ },
1199
+
1200
+ /**
1201
+ * Asserts that a set of values are not present in an array. This uses the triple equals
1202
+ * sign so no type cohersion may occur. For this assertion to pass, all values must
1203
+ * not be found.
1204
+ * @param {Object[]} needles An array of values that are not expected in the array.
1205
+ * @param {Array} haystack An array of values to check.
1206
+ * @param {String} message (Optional) The message to display if the assertion fails.
1207
+ * @method doesNotContainItems
1208
+ * @static
1209
+ */
1210
+ doesNotContainItems : function (needles, haystack,
1211
+ message) {
1212
+
1213
+ YUITest.Assert._increment();
1214
+
1215
+ for (var i=0; i < needles.length; i++){
1216
+ if (this._indexOf(haystack, needles[i]) > -1){
1217
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1218
+ }
1219
+ }
1220
+
1221
+ },
1222
+
1223
+ /**
1224
+ * Asserts that no values matching a condition are present in an array. This uses
1225
+ * a function to determine a match.
1226
+ * @param {Function} matcher A function that returns true if the item matches or false if not.
1227
+ * @param {Array} haystack An array of values.
1228
+ * @param {String} message (Optional) The message to display if the assertion fails.
1229
+ * @method doesNotContainMatch
1230
+ * @static
1231
+ */
1232
+ doesNotContainMatch : function (matcher, haystack,
1233
+ message) {
1234
+
1235
+ YUITest.Assert._increment();
1236
+
1237
+ //check for valid matcher
1238
+ if (typeof matcher != "function"){
1239
+ throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1240
+ }
1241
+
1242
+ if (this._some(haystack, matcher)){
1243
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1244
+ }
1245
+ },
1246
+
1247
+ /**
1248
+ * Asserts that the given value is contained in an array at the specified index.
1249
+ * This uses the triple equals sign so no type cohersion will occur.
1250
+ * @param {Object} needle The value to look for.
1251
+ * @param {Array} haystack The array to search in.
1252
+ * @param {int} index The index at which the value should exist.
1253
+ * @param {String} message (Optional) The message to display if the assertion fails.
1254
+ * @method indexOf
1255
+ * @static
1256
+ */
1257
+ indexOf : function (needle, haystack, index, message) {
1258
+
1259
+ YUITest.Assert._increment();
1260
+
1261
+ //try to find the value in the array
1262
+ for (var i=0; i < haystack.length; i++){
1263
+ if (haystack[i] === needle){
1264
+ if (index != i){
1265
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1266
+ }
1267
+ return;
1268
+ }
1269
+ }
1270
+
1271
+ //if it makes it here, it wasn't found at all
1272
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1273
+ },
1274
+
1275
+ /**
1276
+ * Asserts that the values in an array are equal, and in the same position,
1277
+ * as values in another array. This uses the double equals sign
1278
+ * so type cohersion may occur. Note that the array objects themselves
1279
+ * need not be the same for this test to pass.
1280
+ * @param {Array} expected An array of the expected values.
1281
+ * @param {Array} actual Any array of the actual values.
1282
+ * @param {String} message (Optional) The message to display if the assertion fails.
1283
+ * @method itemsAreEqual
1284
+ * @static
1285
+ */
1286
+ itemsAreEqual : function (expected, actual,
1287
+ message) {
1288
+
1289
+ YUITest.Assert._increment();
1290
+
1291
+ //first check array length
1292
+ if (expected.length != actual.length){
1293
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
1294
+ }
1295
+
1296
+ //begin checking values
1297
+ for (var i=0; i < expected.length; i++){
1298
+ if (expected[i] != actual[i]){
1299
+ throw new YUITest.Assert.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equal."), expected[i], actual[i]);
1300
+ }
1301
+ }
1302
+ },
1303
+
1304
+ /**
1305
+ * Asserts that the values in an array are equivalent, and in the same position,
1306
+ * as values in another array. This uses a function to determine if the values
1307
+ * are equivalent. Note that the array objects themselves
1308
+ * need not be the same for this test to pass.
1309
+ * @param {Array} expected An array of the expected values.
1310
+ * @param {Array} actual Any array of the actual values.
1311
+ * @param {Function} comparator A function that returns true if the values are equivalent
1312
+ * or false if not.
1313
+ * @param {String} message (Optional) The message to display if the assertion fails.
1314
+ * @return {Void}
1315
+ * @method itemsAreEquivalent
1316
+ * @static
1317
+ */
1318
+ itemsAreEquivalent : function (expected, actual,
1319
+ comparator, message) {
1320
+
1321
+ YUITest.Assert._increment();
1322
+
1323
+ //make sure the comparator is valid
1324
+ if (typeof comparator != "function"){
1325
+ throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1326
+ }
1327
+
1328
+ //first check array length
1329
+ if (expected.length != actual.length){
1330
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
1331
+ }
1332
+
1333
+ //begin checking values
1334
+ for (var i=0; i < expected.length; i++){
1335
+ if (!comparator(expected[i], actual[i])){
1336
+ throw new YUITest.Assert.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1337
+ }
1338
+ }
1339
+ },
1340
+
1341
+ /**
1342
+ * Asserts that an array is empty.
1343
+ * @param {Array} actual The array to test.
1344
+ * @param {String} message (Optional) The message to display if the assertion fails.
1345
+ * @method isEmpty
1346
+ * @static
1347
+ */
1348
+ isEmpty : function (actual, message) {
1349
+ YUITest.Assert._increment();
1350
+ if (actual.length > 0){
1351
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should be empty."));
1352
+ }
1353
+ },
1354
+
1355
+ /**
1356
+ * Asserts that an array is not empty.
1357
+ * @param {Array} actual The array to test.
1358
+ * @param {String} message (Optional) The message to display if the assertion fails.
1359
+ * @method isNotEmpty
1360
+ * @static
1361
+ */
1362
+ isNotEmpty : function (actual, message) {
1363
+ YUITest.Assert._increment();
1364
+ if (actual.length === 0){
1365
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should not be empty."));
1366
+ }
1367
+ },
1368
+
1369
+ /**
1370
+ * Asserts that the values in an array are the same, and in the same position,
1371
+ * as values in another array. This uses the triple equals sign
1372
+ * so no type cohersion will occur. Note that the array objects themselves
1373
+ * need not be the same for this test to pass.
1374
+ * @param {Array} expected An array of the expected values.
1375
+ * @param {Array} actual Any array of the actual values.
1376
+ * @param {String} message (Optional) The message to display if the assertion fails.
1377
+ * @method itemsAreSame
1378
+ * @static
1379
+ */
1380
+ itemsAreSame : function (expected, actual,
1381
+ message) {
1382
+
1383
+ YUITest.Assert._increment();
1384
+
1385
+ //first check array length
1386
+ if (expected.length != actual.length){
1387
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
1388
+ }
1389
+
1390
+ //begin checking values
1391
+ for (var i=0; i < expected.length; i++){
1392
+ if (expected[i] !== actual[i]){
1393
+ throw new YUITest.Assert.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not the same."), expected[i], actual[i]);
1394
+ }
1395
+ }
1396
+ },
1397
+
1398
+ /**
1399
+ * Asserts that the given value is contained in an array at the specified index,
1400
+ * starting from the back of the array.
1401
+ * This uses the triple equals sign so no type cohersion will occur.
1402
+ * @param {Object} needle The value to look for.
1403
+ * @param {Array} haystack The array to search in.
1404
+ * @param {int} index The index at which the value should exist.
1405
+ * @param {String} message (Optional) The message to display if the assertion fails.
1406
+ * @method lastIndexOf
1407
+ * @static
1408
+ */
1409
+ lastIndexOf : function (needle, haystack, index, message) {
1410
+
1411
+ //try to find the value in the array
1412
+ for (var i=haystack.length; i >= 0; i--){
1413
+ if (haystack[i] === needle){
1414
+ if (index != i){
1415
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1416
+ }
1417
+ return;
1418
+ }
1419
+ }
1420
+
1421
+ //if it makes it here, it wasn't found at all
1422
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array."));
1423
+ }
1424
+
1425
+ };
1426
+
1427
+
1428
+ /**
1429
+ * The ObjectAssert object provides functions to test JavaScript objects
1430
+ * for a variety of cases.
1431
+ * @namespace YUITest
1432
+ * @class ObjectAssert
1433
+ * @static
1434
+ */
1435
+ YUITest.ObjectAssert = {
1436
+
1437
+ /**
1438
+ * Asserts that an object has all of the same properties
1439
+ * and property values as the other.
1440
+ * @param {Object} expected The object with all expected properties and values.
1441
+ * @param {Object} actual The object to inspect.
1442
+ * @param {String} message (Optional) The message to display if the assertion fails.
1443
+ * @method areEqual
1444
+ * @static
1445
+ * @deprecated
1446
+ */
1447
+ areEqual: function(expected, actual, message) {
1448
+ YUITest.Assert._increment();
1449
+
1450
+ var expectedKeys = YUITest.Object.keys(expected),
1451
+ actualKeys = YUITest.Object.keys(actual);
1452
+
1453
+ //first check keys array length
1454
+ if (expectedKeys.length != actualKeys.length){
1455
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Object should have " + expectedKeys.length + " keys but has " + actualKeys.length));
1456
+ }
1457
+
1458
+ //then check values
1459
+ for (var name in expected){
1460
+ if (expected.hasOwnProperty(name)){
1461
+ if (expected[name] != actual[name]){
1462
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal for property " + name), expected[name], actual[name]);
1463
+ }
1464
+ }
1465
+ }
1466
+ },
1467
+
1468
+ /**
1469
+ * Asserts that an object has a property with the given name.
1470
+ * @param {String} propertyName The name of the property to test.
1471
+ * @param {Object} object The object to search.
1472
+ * @param {String} message (Optional) The message to display if the assertion fails.
1473
+ * @method hasKey
1474
+ * @static
1475
+ * @deprecated Use ownsOrInheritsKey() instead
1476
+ */
1477
+ hasKey: function (propertyName, object, message) {
1478
+ YUITest.ObjectAssert.ownsOrInheritsKey(propertyName, object, message);
1479
+ },
1480
+
1481
+ /**
1482
+ * Asserts that an object has all properties of a reference object.
1483
+ * @param {Array} properties An array of property names that should be on the object.
1484
+ * @param {Object} object The object to search.
1485
+ * @param {String} message (Optional) The message to display if the assertion fails.
1486
+ * @method hasKeys
1487
+ * @static
1488
+ * @deprecated Use ownsOrInheritsKeys() instead
1489
+ */
1490
+ hasKeys: function (properties, object, message) {
1491
+ YUITest.ObjectAssert.ownsOrInheritsKeys(properties, objects, message);
1492
+ },
1493
+
1494
+ /**
1495
+ * Asserts that a property with the given name exists on an object's prototype.
1496
+ * @param {String} propertyName The name of the property to test.
1497
+ * @param {Object} object The object to search.
1498
+ * @param {String} message (Optional) The message to display if the assertion fails.
1499
+ * @method inheritsKey
1500
+ * @static
1501
+ */
1502
+ inheritsKey: function (propertyName, object, message) {
1503
+ YUITest.Assert._increment();
1504
+ if (!(propertyName in object && !object.hasOwnProperty(propertyName))){
1505
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1506
+ }
1507
+ },
1508
+
1509
+ /**
1510
+ * Asserts that all properties exist on an object prototype.
1511
+ * @param {Array} properties An array of property names that should be on the object.
1512
+ * @param {Object} object The object to search.
1513
+ * @param {String} message (Optional) The message to display if the assertion fails.
1514
+ * @method inheritsKeys
1515
+ * @static
1516
+ */
1517
+ inheritsKeys: function (properties, object, message) {
1518
+ YUITest.Assert._increment();
1519
+ for (var i=0; i < properties.length; i++){
1520
+ if (!(propertyName in object && !object.hasOwnProperty(properties[i]))){
1521
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
1522
+ }
1523
+ }
1524
+ },
1525
+
1526
+ /**
1527
+ * Asserts that a property with the given name exists on an object instance (not on its prototype).
1528
+ * @param {String} propertyName The name of the property to test.
1529
+ * @param {Object} object The object to search.
1530
+ * @param {String} message (Optional) The message to display if the assertion fails.
1531
+ * @method ownsKey
1532
+ * @static
1533
+ */
1534
+ ownsKey: function (propertyName, object, message) {
1535
+ YUITest.Assert._increment();
1536
+ if (!object.hasOwnProperty(propertyName)){
1537
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1538
+ }
1539
+ },
1540
+
1541
+ /**
1542
+ * Asserts that all properties exist on an object instance (not on its prototype).
1543
+ * @param {Array} properties An array of property names that should be on the object.
1544
+ * @param {Object} object The object to search.
1545
+ * @param {String} message (Optional) The message to display if the assertion fails.
1546
+ * @method ownsKeys
1547
+ * @static
1548
+ */
1549
+ ownsKeys: function (properties, object, message) {
1550
+ YUITest.Assert._increment();
1551
+ for (var i=0; i < properties.length; i++){
1552
+ if (!object.hasOwnProperty(properties[i])){
1553
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
1554
+ }
1555
+ }
1556
+ },
1557
+
1558
+ /**
1559
+ * Asserts that an object owns no properties.
1560
+ * @param {Object} object The object to check.
1561
+ * @param {String} message (Optional) The message to display if the assertion fails.
1562
+ * @method ownsNoKeys
1563
+ * @static
1564
+ */
1565
+ ownsNoKeys : function (object, message) {
1566
+ YUITest.Assert._increment();
1567
+ var count = 0,
1568
+ name;
1569
+ for (name in object){
1570
+ if (object.hasOwnProperty(name)){
1571
+ count++;
1572
+ }
1573
+ }
1574
+
1575
+ if (count !== 0){
1576
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Object owns " + count + " properties but should own none."));
1577
+ }
1578
+
1579
+ },
1580
+
1581
+ /**
1582
+ * Asserts that an object has a property with the given name.
1583
+ * @param {String} propertyName The name of the property to test.
1584
+ * @param {Object} object The object to search.
1585
+ * @param {String} message (Optional) The message to display if the assertion fails.
1586
+ * @method ownsOrInheritsKey
1587
+ * @static
1588
+ */
1589
+ ownsOrInheritsKey: function (propertyName, object, message) {
1590
+ YUITest.Assert._increment();
1591
+ if (!(propertyName in object)){
1592
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
1593
+ }
1594
+ },
1595
+
1596
+ /**
1597
+ * Asserts that an object has all properties of a reference object.
1598
+ * @param {Array} properties An array of property names that should be on the object.
1599
+ * @param {Object} object The object to search.
1600
+ * @param {String} message (Optional) The message to display if the assertion fails.
1601
+ * @method ownsOrInheritsKeys
1602
+ * @static
1603
+ */
1604
+ ownsOrInheritsKeys: function (properties, object, message) {
1605
+ YUITest.Assert._increment();
1606
+ for (var i=0; i < properties.length; i++){
1607
+ if (!(properties[i] in object)){
1608
+ YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object."));
1609
+ }
1610
+ }
1611
+ }
1612
+ };
1613
+
1614
+
1615
+
1616
+ /**
1617
+ * The DateAssert object provides functions to test JavaScript Date objects
1618
+ * for a variety of cases.
1619
+ * @namespace YUITest
1620
+ * @class DateAssert
1621
+ * @static
1622
+ */
1623
+
1624
+ YUITest.DateAssert = {
1625
+
1626
+ /**
1627
+ * Asserts that a date's month, day, and year are equal to another date's.
1628
+ * @param {Date} expected The expected date.
1629
+ * @param {Date} actual The actual date to test.
1630
+ * @param {String} message (Optional) The message to display if the assertion fails.
1631
+ * @method datesAreEqual
1632
+ * @static
1633
+ */
1634
+ datesAreEqual : function (expected, actual, message){
1635
+ YUITest.Assert._increment();
1636
+ if (expected instanceof Date && actual instanceof Date){
1637
+ var msg = "";
1638
+
1639
+ //check years first
1640
+ if (expected.getFullYear() != actual.getFullYear()){
1641
+ msg = "Years should be equal.";
1642
+ }
1643
+
1644
+ //now check months
1645
+ if (expected.getMonth() != actual.getMonth()){
1646
+ msg = "Months should be equal.";
1647
+ }
1648
+
1649
+ //last, check the day of the month
1650
+ if (expected.getDate() != actual.getDate()){
1651
+ msg = "Days of month should be equal.";
1652
+ }
1653
+
1654
+ if (msg.length){
1655
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
1656
+ }
1657
+ } else {
1658
+ throw new TypeError("YUITest.DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
1659
+ }
1660
+ },
1661
+
1662
+ /**
1663
+ * Asserts that a date's hour, minutes, and seconds are equal to another date's.
1664
+ * @param {Date} expected The expected date.
1665
+ * @param {Date} actual The actual date to test.
1666
+ * @param {String} message (Optional) The message to display if the assertion fails.
1667
+ * @method timesAreEqual
1668
+ * @static
1669
+ */
1670
+ timesAreEqual : function (expected, actual, message){
1671
+ YUITest.Assert._increment();
1672
+ if (expected instanceof Date && actual instanceof Date){
1673
+ var msg = "";
1674
+
1675
+ //check hours first
1676
+ if (expected.getHours() != actual.getHours()){
1677
+ msg = "Hours should be equal.";
1678
+ }
1679
+
1680
+ //now check minutes
1681
+ if (expected.getMinutes() != actual.getMinutes()){
1682
+ msg = "Minutes should be equal.";
1683
+ }
1684
+
1685
+ //last, check the seconds
1686
+ if (expected.getSeconds() != actual.getSeconds()){
1687
+ msg = "Seconds should be equal.";
1688
+ }
1689
+
1690
+ if (msg.length){
1691
+ throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
1692
+ }
1693
+ } else {
1694
+ throw new TypeError("YUITest.DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
1695
+ }
1696
+ }
1697
+
1698
+ };
1699
+
1700
+ /**
1701
+ * Creates a new mock object.
1702
+ * @namespace YUITest
1703
+ * @class Mock
1704
+ * @constructor
1705
+ * @param {Object} template (Optional) An object whose methods
1706
+ * should be stubbed out on the mock object.
1707
+ */
1708
+ YUITest.Mock = function(template){
1709
+
1710
+ //use blank object is nothing is passed in
1711
+ template = template || {};
1712
+
1713
+ var mock,
1714
+ name;
1715
+
1716
+ //try to create mock that keeps prototype chain intact
1717
+ //fails in the case of ActiveX objects
1718
+ try {
1719
+ function f(){}
1720
+ f.prototype = template;
1721
+ mock = new f();
1722
+ } catch (ex) {
1723
+ mock = {};
1724
+ }
1725
+
1726
+ //create stubs for all methods
1727
+ for (name in template){
1728
+ if (template.hasOwnProperty(name)){
1729
+ if (typeof template[name] == "function"){
1730
+ mock[name] = function(name){
1731
+ return function(){
1732
+ YUITest.Assert.fail("Method " + name + "() was called but was not expected to be.");
1733
+ };
1734
+ }(name);
1735
+ }
1736
+ }
1737
+ }
1738
+
1739
+ //return it
1740
+ return mock;
1741
+ };
1742
+
1743
+ /**
1744
+ * Assigns an expectation to a mock object. This is used to create
1745
+ * methods and properties on the mock object that are monitored for
1746
+ * calls and changes, respectively.
1747
+ * @param {Object} mock The object to add the expectation to.
1748
+ * @param {Object} expectation An object defining the expectation. For
1749
+ * a method, the keys "method" and "args" are required with
1750
+ * an optional "returns" key available. For properties, the keys
1751
+ * "property" and "value" are required.
1752
+ * @return {void}
1753
+ * @method expect
1754
+ * @static
1755
+ */
1756
+ YUITest.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
1757
+
1758
+ //make sure there's a place to store the expectations
1759
+ if (!mock.__expectations) {
1760
+ mock.__expectations = {};
1761
+ }
1762
+
1763
+ //method expectation
1764
+ if (expectation.method){
1765
+ var name = expectation.method,
1766
+ args = expectation.args || [],
1767
+ result = expectation.returns,
1768
+ callCount = (typeof expectation.callCount == "number") ? expectation.callCount : 1,
1769
+ error = expectation.error,
1770
+ run = expectation.run || function(){},
1771
+ i;
1772
+
1773
+ //save expectations
1774
+ mock.__expectations[name] = expectation;
1775
+ expectation.callCount = callCount;
1776
+ expectation.actualCallCount = 0;
1777
+
1778
+ //process arguments
1779
+ for (i=0; i < args.length; i++){
1780
+ if (!(args[i] instanceof YUITest.Mock.Value)){
1781
+ args[i] = YUITest.Mock.Value(YUITest.Assert.areSame, [args[i]], "Argument " + i + " of " + name + "() is incorrect.");
1782
+ }
1783
+ }
1784
+
1785
+ //if the method is expected to be called
1786
+ if (callCount > 0){
1787
+ mock[name] = function(){
1788
+ try {
1789
+ expectation.actualCallCount++;
1790
+ YUITest.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
1791
+ for (var i=0, len=args.length; i < len; i++){
1792
+ args[i].verify(arguments[i]);
1793
+ }
1794
+
1795
+ run.apply(this, arguments);
1796
+
1797
+ if (error){
1798
+ throw error;
1799
+ }
1800
+ } catch (ex){
1801
+ //route through TestRunner for proper handling
1802
+ YUITest.TestRunner._handleError(ex);
1803
+ }
1804
+
1805
+ return result;
1806
+ };
1807
+ } else {
1808
+
1809
+ //method should fail if called when not expected
1810
+ mock[name] = function(){
1811
+ try {
1812
+ YUITest.Assert.fail("Method " + name + "() should not have been called.");
1813
+ } catch (ex){
1814
+ //route through TestRunner for proper handling
1815
+ YUITest.TestRunner._handleError(ex);
1816
+ }
1817
+ };
1818
+ }
1819
+ } else if (expectation.property){
1820
+ //save expectations
1821
+ mock.__expectations[name] = expectation;
1822
+ }
1823
+ };
1824
+
1825
+ /**
1826
+ * Verifies that all expectations of a mock object have been met and
1827
+ * throws an assertion error if not.
1828
+ * @param {Object} mock The object to verify..
1829
+ * @return {void}
1830
+ * @method verify
1831
+ * @static
1832
+ */
1833
+ YUITest.Mock.verify = function(mock){
1834
+ try {
1835
+
1836
+ for (var name in mock.__expectations){
1837
+ if (mock.__expectations.hasOwnProperty(name)){
1838
+ var expectation = mock.__expectations[name];
1839
+ if (expectation.method) {
1840
+ YUITest.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
1841
+ } else if (expectation.property){
1842
+ YUITest.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value.");
1843
+ }
1844
+ }
1845
+ }
1846
+
1847
+ } catch (ex){
1848
+ //route through TestRunner for proper handling
1849
+ YUITest.TestRunner._handleError(ex);
1850
+ }
1851
+ };
1852
+
1853
+ /**
1854
+ * Creates a new value matcher.
1855
+ * @param {Function} method The function to call on the value.
1856
+ * @param {Array} originalArgs (Optional) Array of arguments to pass to the method.
1857
+ * @param {String} message (Optional) Message to display in case of failure.
1858
+ * @namespace YUITest.Mock
1859
+ * @class Value
1860
+ * @constructor
1861
+ */
1862
+ YUITest.Mock.Value = function(method, originalArgs, message){
1863
+ if (this instanceof YUITest.Mock.Value){
1864
+ this.verify = function(value){
1865
+ var args = [].concat(originalArgs || []);
1866
+ args.push(value);
1867
+ args.push(message);
1868
+ method.apply(null, args);
1869
+ };
1870
+ } else {
1871
+ return new YUITest.Mock.Value(method, originalArgs, message);
1872
+ }
1873
+ };
1874
+
1875
+ /**
1876
+ * Predefined matcher to match any value.
1877
+ * @property Any
1878
+ * @static
1879
+ * @type Function
1880
+ */
1881
+ YUITest.Mock.Value.Any = YUITest.Mock.Value(function(){});
1882
+
1883
+ /**
1884
+ * Predefined matcher to match boolean values.
1885
+ * @property Boolean
1886
+ * @static
1887
+ * @type Function
1888
+ */
1889
+ YUITest.Mock.Value.Boolean = YUITest.Mock.Value(YUITest.Assert.isBoolean);
1890
+
1891
+ /**
1892
+ * Predefined matcher to match number values.
1893
+ * @property Number
1894
+ * @static
1895
+ * @type Function
1896
+ */
1897
+ YUITest.Mock.Value.Number = YUITest.Mock.Value(YUITest.Assert.isNumber);
1898
+
1899
+ /**
1900
+ * Predefined matcher to match string values.
1901
+ * @property String
1902
+ * @static
1903
+ * @type Function
1904
+ */
1905
+ YUITest.Mock.Value.String = YUITest.Mock.Value(YUITest.Assert.isString);
1906
+
1907
+ /**
1908
+ * Predefined matcher to match object values.
1909
+ * @property Object
1910
+ * @static
1911
+ * @type Function
1912
+ */
1913
+ YUITest.Mock.Value.Object = YUITest.Mock.Value(YUITest.Assert.isObject);
1914
+
1915
+ /**
1916
+ * Predefined matcher to match function values.
1917
+ * @property Function
1918
+ * @static
1919
+ * @type Function
1920
+ */
1921
+ YUITest.Mock.Value.Function = YUITest.Mock.Value(YUITest.Assert.isFunction);
1922
+
1923
+ /**
1924
+ * Convenience type for storing and aggregating
1925
+ * test result information.
1926
+ * @private
1927
+ * @namespace YUITest
1928
+ * @class Results
1929
+ * @constructor
1930
+ * @param {String} name The name of the test.
1931
+ */
1932
+ YUITest.Results = function(name){
1933
+
1934
+ /**
1935
+ * Name of the test, test case, or test suite.
1936
+ * @type String
1937
+ * @property name
1938
+ */
1939
+ this.name = name;
1940
+
1941
+ /**
1942
+ * Number of passed tests.
1943
+ * @type int
1944
+ * @property passed
1945
+ */
1946
+ this.passed = 0;
1947
+
1948
+ /**
1949
+ * Number of failed tests.
1950
+ * @type int
1951
+ * @property failed
1952
+ */
1953
+ this.failed = 0;
1954
+
1955
+ /**
1956
+ * Number of errors that occur in non-test methods.
1957
+ * @type int
1958
+ * @property errors
1959
+ */
1960
+ this.errors = 0;
1961
+
1962
+ /**
1963
+ * Number of ignored tests.
1964
+ * @type int
1965
+ * @property ignored
1966
+ */
1967
+ this.ignored = 0;
1968
+
1969
+ /**
1970
+ * Number of total tests.
1971
+ * @type int
1972
+ * @property total
1973
+ */
1974
+ this.total = 0;
1975
+
1976
+ /**
1977
+ * Amount of time (ms) it took to complete testing.
1978
+ * @type int
1979
+ * @property duration
1980
+ */
1981
+ this.duration = 0;
1982
+ }
1983
+
1984
+ /**
1985
+ * Includes results from another results object into this one.
1986
+ * @param {YUITest.Results} result The results object to include.
1987
+ * @method include
1988
+ * @return {void}
1989
+ */
1990
+ YUITest.Results.prototype.include = function(results){
1991
+ this.passed += results.passed;
1992
+ this.failed += results.failed;
1993
+ this.ignored += results.ignored;
1994
+ this.total += results.total;
1995
+ this.errors += results.errors;
1996
+ };
1997
+
1998
+ /**
1999
+ * Test case containing various tests to run.
2000
+ * @param template An object containing any number of test methods, other methods,
2001
+ * an optional name, and anything else the test case needs.
2002
+ * @class TestCase
2003
+ * @namespace YUITest
2004
+ * @constructor
2005
+ */
2006
+ YUITest.TestCase = function (template) {
2007
+
2008
+ /**
2009
+ * Special rules for the test case. Possible subobjects
2010
+ * are fail, for tests that should fail, and error, for
2011
+ * tests that should throw an error.
2012
+ */
2013
+ this._should = {};
2014
+
2015
+ //copy over all properties from the template to this object
2016
+ for (var prop in template) {
2017
+ this[prop] = template[prop];
2018
+ }
2019
+
2020
+ //check for a valid name
2021
+ if (typeof this.name != "string"){
2022
+ this.name = "testCase" + (+new Date());
2023
+ }
2024
+
2025
+ };
2026
+
2027
+ YUITest.TestCase.prototype = {
2028
+
2029
+ //restore constructor
2030
+ constructor: YUITest.TestCase,
2031
+
2032
+ /**
2033
+ * Method to call from an async init method to
2034
+ * restart the test case. When called, returns a function
2035
+ * that should be called when tests are ready to continue.
2036
+ * @method callback
2037
+ * @return {Function} The function to call as a callback.
2038
+ */
2039
+ callback: function(){
2040
+ return YUITest.TestRunner.callback.apply(YUITest.TestRunner,arguments);
2041
+ },
2042
+
2043
+ /**
2044
+ * Resumes a paused test and runs the given function.
2045
+ * @param {Function} segment (Optional) The function to run.
2046
+ * If omitted, the test automatically passes.
2047
+ * @return {Void}
2048
+ * @method resume
2049
+ */
2050
+ resume : function (segment) {
2051
+ YUITest.TestRunner.resume(segment);
2052
+ },
2053
+
2054
+ /**
2055
+ * Causes the test case to wait a specified amount of time and then
2056
+ * continue executing the given code.
2057
+ * @param {Function} segment (Optional) The function to run after the delay.
2058
+ * If omitted, the TestRunner will wait until resume() is called.
2059
+ * @param {int} delay (Optional) The number of milliseconds to wait before running
2060
+ * the function. If omitted, defaults to zero.
2061
+ * @return {Void}
2062
+ * @method wait
2063
+ */
2064
+ wait : function (segment, delay){
2065
+
2066
+ var actualDelay = (typeof segment == "number" ? segment : delay);
2067
+ actualDelay = (typeof actualDelay == "number" ? actualDelay : 10000);
2068
+
2069
+ if (typeof segment == "function"){
2070
+ throw new YUITest.Wait(segment, actualDelay);
2071
+ } else {
2072
+ throw new YUITest.Wait(function(){
2073
+ YUITest.Assert.fail("Timeout: wait() called but resume() never called.");
2074
+ }, actualDelay);
2075
+ }
2076
+ },
2077
+
2078
+ //-------------------------------------------------------------------------
2079
+ // Assertion Methods
2080
+ //-------------------------------------------------------------------------
2081
+
2082
+ /**
2083
+ * Asserts that a given condition is true. If not, then a YUITest.AssertionError object is thrown
2084
+ * and the test fails.
2085
+ * @method assert
2086
+ * @param {Boolean} condition The condition to test.
2087
+ * @param {String} message The message to display if the assertion fails.
2088
+ */
2089
+ assert : function (condition, message){
2090
+ YUITest.Assert._increment();
2091
+ if (!condition){
2092
+ throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Assertion failed."));
2093
+ }
2094
+ },
2095
+
2096
+ /**
2097
+ * Forces an assertion error to occur. Shortcut for YUITest.Assert.fail().
2098
+ * @method fail
2099
+ * @param {String} message (Optional) The message to display with the failure.
2100
+ */
2101
+ fail: function (message) {
2102
+ YUITest.Assert.fail(message);
2103
+ },
2104
+
2105
+ //-------------------------------------------------------------------------
2106
+ // Stub Methods
2107
+ //-------------------------------------------------------------------------
2108
+
2109
+ /**
2110
+ * Function to run once before tests start to run.
2111
+ * This executes before the first call to setUp().
2112
+ */
2113
+ init: function(){
2114
+ //noop
2115
+ },
2116
+
2117
+ /**
2118
+ * Function to run once after tests finish running.
2119
+ * This executes after the last call to tearDown().
2120
+ */
2121
+ destroy: function(){
2122
+ //noop
2123
+ },
2124
+
2125
+ /**
2126
+ * Function to run before each test is executed.
2127
+ * @return {Void}
2128
+ * @method setUp
2129
+ */
2130
+ setUp : function () {
2131
+ //noop
2132
+ },
2133
+
2134
+ /**
2135
+ * Function to run after each test is executed.
2136
+ * @return {Void}
2137
+ * @method tearDown
2138
+ */
2139
+ tearDown: function () {
2140
+ //noop
2141
+ }
2142
+ };
2143
+
2144
+
2145
+
2146
+ /**
2147
+ * A test suite that can contain a collection of TestCase and TestSuite objects.
2148
+ * @param {String||Object} data The name of the test suite or an object containing
2149
+ * a name property as well as setUp and tearDown methods.
2150
+ * @namespace YUITest
2151
+ * @class TestSuite
2152
+ * @constructor
2153
+ */
2154
+ YUITest.TestSuite = function (data) {
2155
+
2156
+ /**
2157
+ * The name of the test suite.
2158
+ * @type String
2159
+ * @property name
2160
+ */
2161
+ this.name = "";
2162
+
2163
+ /**
2164
+ * Array of test suites and test cases.
2165
+ * @type Array
2166
+ * @property items
2167
+ * @private
2168
+ */
2169
+ this.items = [];
2170
+
2171
+ //initialize the properties
2172
+ if (typeof data == "string"){
2173
+ this.name = data;
2174
+ } else if (data instanceof Object){
2175
+ for (var prop in data){
2176
+ if (data.hasOwnProperty(prop)){
2177
+ this[prop] = data[prop];
2178
+ }
2179
+ }
2180
+ }
2181
+
2182
+ //double-check name
2183
+ if (this.name === ""){
2184
+ this.name = "testSuite" + (+new Date());
2185
+ }
2186
+
2187
+ };
2188
+
2189
+ YUITest.TestSuite.prototype = {
2190
+
2191
+ //restore constructor
2192
+ constructor: YUITest.TestSuite,
2193
+
2194
+ /**
2195
+ * Adds a test suite or test case to the test suite.
2196
+ * @param {YUITest.TestSuite||YUITest.TestCase} testObject The test suite or test case to add.
2197
+ * @return {Void}
2198
+ * @method add
2199
+ */
2200
+ add : function (testObject) {
2201
+ if (testObject instanceof YUITest.TestSuite || testObject instanceof YUITest.TestCase) {
2202
+ this.items.push(testObject);
2203
+ }
2204
+ return this;
2205
+ },
2206
+
2207
+ //-------------------------------------------------------------------------
2208
+ // Stub Methods
2209
+ //-------------------------------------------------------------------------
2210
+
2211
+ /**
2212
+ * Function to run before each test is executed.
2213
+ * @return {Void}
2214
+ * @method setUp
2215
+ */
2216
+ setUp : function () {
2217
+ },
2218
+
2219
+ /**
2220
+ * Function to run after each test is executed.
2221
+ * @return {Void}
2222
+ * @method tearDown
2223
+ */
2224
+ tearDown: function () {
2225
+ }
2226
+
2227
+ };
2228
+
2229
+ /**
2230
+ * An object object containing test result formatting methods.
2231
+ * @namespace YUITest
2232
+ * @class TestFormat
2233
+ * @static
2234
+ */
2235
+ YUITest.TestFormat = function(){
2236
+
2237
+ /* (intentionally not documented)
2238
+ * Basic XML escaping method. Replaces quotes, less-than, greater-than,
2239
+ * apostrophe, and ampersand characters with their corresponding entities.
2240
+ * @param {String} text The text to encode.
2241
+ * @return {String} The XML-escaped text.
2242
+ */
2243
+ function xmlEscape(text){
2244
+
2245
+ return text.replace(/[<>"'&]/g, function(value){
2246
+ switch(value){
2247
+ case "<": return "&lt;";
2248
+ case ">": return "&gt;";
2249
+ case "\"": return "&quot;";
2250
+ case "'": return "&apos;";
2251
+ case "&": return "&amp;";
2252
+ }
2253
+ });
2254
+
2255
+ }
2256
+
2257
+
2258
+ return {
2259
+
2260
+ /**
2261
+ * Returns test results formatted as a JSON string. Requires JSON utility.
2262
+ * @param {Object} result The results object created by TestRunner.
2263
+ * @return {String} A JSON-formatted string of results.
2264
+ * @method JSON
2265
+ * @static
2266
+ */
2267
+ JSON: function(results) {
2268
+ return YUITest.Util.JSON.stringify(results);
2269
+ },
2270
+
2271
+ /**
2272
+ * Returns test results formatted as an XML string.
2273
+ * @param {Object} result The results object created by TestRunner.
2274
+ * @return {String} An XML-formatted string of results.
2275
+ * @method XML
2276
+ * @static
2277
+ */
2278
+ XML: function(results) {
2279
+
2280
+ function serializeToXML(results){
2281
+ var xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
2282
+
2283
+ if (typeof(results.duration)=="number"){
2284
+ xml += " duration=\"" + results.duration + "\"";
2285
+ }
2286
+
2287
+ if (results.type == "test"){
2288
+ xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
2289
+ } else {
2290
+ xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
2291
+ for (var prop in results){
2292
+ if (results.hasOwnProperty(prop)){
2293
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2294
+ xml += serializeToXML(results[prop]);
2295
+ }
2296
+ }
2297
+ }
2298
+ }
2299
+
2300
+ xml += "</" + results.type + ">";
2301
+
2302
+ return xml;
2303
+ }
2304
+
2305
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToXML(results);
2306
+
2307
+ },
2308
+
2309
+
2310
+ /**
2311
+ * Returns test results formatted in JUnit XML format.
2312
+ * @param {Object} result The results object created by TestRunner.
2313
+ * @return {String} An XML-formatted string of results.
2314
+ * @method JUnitXML
2315
+ * @static
2316
+ */
2317
+ JUnitXML: function(results) {
2318
+
2319
+ function serializeToJUnitXML(results){
2320
+ var xml = "";
2321
+
2322
+ switch (results.type){
2323
+ //equivalent to testcase in JUnit
2324
+ case "test":
2325
+ if (results.result != "ignore"){
2326
+ xml = "<testcase name=\"" + xmlEscape(results.name) + "\" time=\"" + (results.duration/1000) + "\">";
2327
+ if (results.result == "fail"){
2328
+ xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
2329
+ }
2330
+ xml+= "</testcase>";
2331
+ }
2332
+ break;
2333
+
2334
+ //equivalent to testsuite in JUnit
2335
+ case "testcase":
2336
+
2337
+ xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\" time=\"" + (results.duration/1000) + "\">";
2338
+
2339
+ for (var prop in results){
2340
+ if (results.hasOwnProperty(prop)){
2341
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2342
+ xml += serializeToJUnitXML(results[prop]);
2343
+ }
2344
+ }
2345
+ }
2346
+
2347
+ xml += "</testsuite>";
2348
+ break;
2349
+
2350
+ //no JUnit equivalent, don't output anything
2351
+ case "testsuite":
2352
+ for (var prop in results){
2353
+ if (results.hasOwnProperty(prop)){
2354
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2355
+ xml += serializeToJUnitXML(results[prop]);
2356
+ }
2357
+ }
2358
+ }
2359
+ break;
2360
+
2361
+ //top-level, equivalent to testsuites in JUnit
2362
+ case "report":
2363
+
2364
+ xml = "<testsuites>";
2365
+
2366
+ for (var prop in results){
2367
+ if (results.hasOwnProperty(prop)){
2368
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2369
+ xml += serializeToJUnitXML(results[prop]);
2370
+ }
2371
+ }
2372
+ }
2373
+
2374
+ xml += "</testsuites>";
2375
+
2376
+ //no default
2377
+ }
2378
+
2379
+ return xml;
2380
+
2381
+ }
2382
+
2383
+ return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToJUnitXML(results);
2384
+ },
2385
+
2386
+ /**
2387
+ * Returns test results formatted in TAP format.
2388
+ * For more information, see <a href="http://testanything.org/">Test Anything Protocol</a>.
2389
+ * @param {Object} result The results object created by TestRunner.
2390
+ * @return {String} A TAP-formatted string of results.
2391
+ * @method TAP
2392
+ * @static
2393
+ */
2394
+ TAP: function(results) {
2395
+
2396
+ var currentTestNum = 1;
2397
+
2398
+ function serializeToTAP(results){
2399
+ var text = "";
2400
+
2401
+ switch (results.type){
2402
+
2403
+ case "test":
2404
+ if (results.result != "ignore"){
2405
+
2406
+ text = "ok " + (currentTestNum++) + " - " + results.name;
2407
+
2408
+ if (results.result == "fail"){
2409
+ text = "not " + text + " - " + results.message;
2410
+ }
2411
+
2412
+ text += "\n";
2413
+ } else {
2414
+ text = "#Ignored test " + results.name + "\n";
2415
+ }
2416
+ break;
2417
+
2418
+ case "testcase":
2419
+
2420
+ text = "#Begin testcase " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2421
+
2422
+ for (var prop in results){
2423
+ if (results.hasOwnProperty(prop)){
2424
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2425
+ text += serializeToTAP(results[prop]);
2426
+ }
2427
+ }
2428
+ }
2429
+
2430
+ text += "#End testcase " + results.name + "\n";
2431
+
2432
+
2433
+ break;
2434
+
2435
+ case "testsuite":
2436
+
2437
+ text = "#Begin testsuite " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2438
+
2439
+ for (var prop in results){
2440
+ if (results.hasOwnProperty(prop)){
2441
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2442
+ text += serializeToTAP(results[prop]);
2443
+ }
2444
+ }
2445
+ }
2446
+
2447
+ text += "#End testsuite " + results.name + "\n";
2448
+ break;
2449
+
2450
+ case "report":
2451
+
2452
+ for (var prop in results){
2453
+ if (results.hasOwnProperty(prop)){
2454
+ if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
2455
+ text += serializeToTAP(results[prop]);
2456
+ }
2457
+ }
2458
+ }
2459
+
2460
+ //no default
2461
+ }
2462
+
2463
+ return text;
2464
+
2465
+ }
2466
+
2467
+ return "1.." + results.total + "\n" + serializeToTAP(results);
2468
+ }
2469
+
2470
+ };
2471
+ }();
2472
+
2473
+ /**
2474
+ * An object object containing coverage result formatting methods.
2475
+ * @namespace YUITest
2476
+ * @class CoverageFormat
2477
+ * @static
2478
+ */
2479
+ YUITest.CoverageFormat = {
2480
+
2481
+ /**
2482
+ * Returns the coverage report in JSON format. This is the straight
2483
+ * JSON representation of the native coverage report.
2484
+ * @param {Object} coverage The coverage report object.
2485
+ * @return {String} A JSON-formatted string of coverage data.
2486
+ * @method JSON
2487
+ * @namespace YUITest.CoverageFormat
2488
+ */
2489
+ JSON: function(coverage){
2490
+ return YUITest.Util.JSON.stringify(coverage);
2491
+ },
2492
+
2493
+ /**
2494
+ * Returns the coverage report in a JSON format compatible with
2495
+ * Xdebug. See <a href="http://www.xdebug.com/docs/code_coverage">Xdebug Documentation</a>
2496
+ * for more information. Note: function coverage is not available
2497
+ * in this format.
2498
+ * @param {Object} coverage The coverage report object.
2499
+ * @return {String} A JSON-formatted string of coverage data.
2500
+ * @method XdebugJSON
2501
+ * @namespace YUITest.CoverageFormat
2502
+ */
2503
+ XdebugJSON: function(coverage){
2504
+
2505
+ var report = {};
2506
+ for (var prop in coverage){
2507
+ if (coverage.hasOwnProperty(prop)){
2508
+ report[prop] = coverage[prop].lines;
2509
+ }
2510
+ }
2511
+
2512
+ return YUITest.Util.JSON.stringify(coverage);
2513
+ }
2514
+
2515
+ };
2516
+
2517
+
2518
+ /**
2519
+ * Runs test suites and test cases, providing events to allowing for the
2520
+ * interpretation of test results.
2521
+ * @namespace YUITest
2522
+ * @class TestRunner
2523
+ * @static
2524
+ */
2525
+ YUITest.TestRunner = function(){
2526
+
2527
+ /*(intentionally not documented)
2528
+ * Determines if any of the array of test groups appears
2529
+ * in the given TestRunner filter.
2530
+ * @param {Array} testGroups The array of test groups to
2531
+ * search for.
2532
+ * @param {String} filter The TestRunner groups filter.
2533
+ */
2534
+ function inGroups(testGroups, filter){
2535
+ if (!filter.length){
2536
+ return true;
2537
+ } else {
2538
+ if (testGroups){
2539
+ for (var i=0, len=testGroups.length; i < len; i++){
2540
+ if (filter.indexOf("," + testGroups[i] + ",") > -1){
2541
+ return true;
2542
+ }
2543
+ }
2544
+ }
2545
+ return false;
2546
+ }
2547
+ }
2548
+
2549
+ /**
2550
+ * A node in the test tree structure. May represent a TestSuite, TestCase, or
2551
+ * test function.
2552
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
2553
+ * @class TestNode
2554
+ * @constructor
2555
+ * @private
2556
+ */
2557
+ function TestNode(testObject){
2558
+
2559
+ /**
2560
+ * The TestSuite, TestCase, or test function represented by this node.
2561
+ * @type Variant
2562
+ * @property testObject
2563
+ */
2564
+ this.testObject = testObject;
2565
+
2566
+ /**
2567
+ * Pointer to this node's first child.
2568
+ * @type TestNode
2569
+ * @property firstChild
2570
+ */
2571
+ this.firstChild = null;
2572
+
2573
+ /**
2574
+ * Pointer to this node's last child.
2575
+ * @type TestNode
2576
+ * @property lastChild
2577
+ */
2578
+ this.lastChild = null;
2579
+
2580
+ /**
2581
+ * Pointer to this node's parent.
2582
+ * @type TestNode
2583
+ * @property parent
2584
+ */
2585
+ this.parent = null;
2586
+
2587
+ /**
2588
+ * Pointer to this node's next sibling.
2589
+ * @type TestNode
2590
+ * @property next
2591
+ */
2592
+ this.next = null;
2593
+
2594
+ /**
2595
+ * Test results for this test object.
2596
+ * @type object
2597
+ * @property results
2598
+ */
2599
+ this.results = new YUITest.Results();
2600
+
2601
+ //initialize results
2602
+ if (testObject instanceof YUITest.TestSuite){
2603
+ this.results.type = "testsuite";
2604
+ this.results.name = testObject.name;
2605
+ } else if (testObject instanceof YUITest.TestCase){
2606
+ this.results.type = "testcase";
2607
+ this.results.name = testObject.name;
2608
+ }
2609
+
2610
+ }
2611
+
2612
+ TestNode.prototype = {
2613
+
2614
+ /**
2615
+ * Appends a new test object (TestSuite, TestCase, or test function name) as a child
2616
+ * of this node.
2617
+ * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
2618
+ * @return {Void}
2619
+ */
2620
+ appendChild : function (testObject){
2621
+ var node = new TestNode(testObject);
2622
+ if (this.firstChild === null){
2623
+ this.firstChild = this.lastChild = node;
2624
+ } else {
2625
+ this.lastChild.next = node;
2626
+ this.lastChild = node;
2627
+ }
2628
+ node.parent = this;
2629
+ return node;
2630
+ }
2631
+ };
2632
+
2633
+ /**
2634
+ * Runs test suites and test cases, providing events to allowing for the
2635
+ * interpretation of test results.
2636
+ * @namespace Test
2637
+ * @class Runner
2638
+ * @static
2639
+ */
2640
+ function TestRunner(){
2641
+
2642
+ //inherit from EventTarget
2643
+ YUITest.EventTarget.call(this);
2644
+
2645
+ /**
2646
+ * Suite on which to attach all TestSuites and TestCases to be run.
2647
+ * @type YUITest.TestSuite
2648
+ * @property masterSuite
2649
+ * @static
2650
+ * @private
2651
+ */
2652
+ this.masterSuite = new YUITest.TestSuite("yuitests" + (new Date()).getTime());
2653
+
2654
+ /**
2655
+ * Pointer to the current node in the test tree.
2656
+ * @type TestNode
2657
+ * @private
2658
+ * @property _cur
2659
+ * @static
2660
+ */
2661
+ this._cur = null;
2662
+
2663
+ /**
2664
+ * Pointer to the root node in the test tree.
2665
+ * @type TestNode
2666
+ * @private
2667
+ * @property _root
2668
+ * @static
2669
+ */
2670
+ this._root = null;
2671
+
2672
+ /**
2673
+ * Indicates if the TestRunner will log events or not.
2674
+ * @type Boolean
2675
+ * @property _log
2676
+ * @private
2677
+ * @static
2678
+ */
2679
+ this._log = true;
2680
+
2681
+ /**
2682
+ * Indicates if the TestRunner is waiting as a result of
2683
+ * wait() being called.
2684
+ * @type Boolean
2685
+ * @property _waiting
2686
+ * @private
2687
+ * @static
2688
+ */
2689
+ this._waiting = false;
2690
+
2691
+ /**
2692
+ * Indicates if the TestRunner is currently running tests.
2693
+ * @type Boolean
2694
+ * @private
2695
+ * @property _running
2696
+ * @static
2697
+ */
2698
+ this._running = false;
2699
+
2700
+ /**
2701
+ * Holds copy of the results object generated when all tests are
2702
+ * complete.
2703
+ * @type Object
2704
+ * @private
2705
+ * @property _lastResults
2706
+ * @static
2707
+ */
2708
+ this._lastResults = null;
2709
+
2710
+ /**
2711
+ * Data object that is passed around from method to method.
2712
+ * @type Object
2713
+ * @private
2714
+ * @property _data
2715
+ * @static
2716
+ */
2717
+ this._context = null;
2718
+
2719
+ /**
2720
+ * The list of test groups to run. The list is represented
2721
+ * by a comma delimited string with commas at the start and
2722
+ * end.
2723
+ * @type String
2724
+ * @private
2725
+ * @property _groups
2726
+ * @static
2727
+ */
2728
+ this._groups = "";
2729
+ }
2730
+
2731
+ TestRunner.prototype = YUITest.Util.mix(new YUITest.EventTarget(), {
2732
+
2733
+ //restore prototype
2734
+ constructor: YUITest.TestRunner,
2735
+
2736
+ //-------------------------------------------------------------------------
2737
+ // Constants
2738
+ //-------------------------------------------------------------------------
2739
+
2740
+ /**
2741
+ * Fires when a test case is opened but before the first
2742
+ * test is executed.
2743
+ * @event testcasebegin
2744
+ * @static
2745
+ */
2746
+ TEST_CASE_BEGIN_EVENT : "testcasebegin",
2747
+
2748
+ /**
2749
+ * Fires when all tests in a test case have been executed.
2750
+ * @event testcasecomplete
2751
+ * @static
2752
+ */
2753
+ TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
2754
+
2755
+ /**
2756
+ * Fires when a test suite is opened but before the first
2757
+ * test is executed.
2758
+ * @event testsuitebegin
2759
+ * @static
2760
+ */
2761
+ TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
2762
+
2763
+ /**
2764
+ * Fires when all test cases in a test suite have been
2765
+ * completed.
2766
+ * @event testsuitecomplete
2767
+ * @static
2768
+ */
2769
+ TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
2770
+
2771
+ /**
2772
+ * Fires when a test has passed.
2773
+ * @event pass
2774
+ * @static
2775
+ */
2776
+ TEST_PASS_EVENT : "pass",
2777
+
2778
+ /**
2779
+ * Fires when a test has failed.
2780
+ * @event fail
2781
+ * @static
2782
+ */
2783
+ TEST_FAIL_EVENT : "fail",
2784
+
2785
+ /**
2786
+ * Fires when a non-test method has an error.
2787
+ * @event error
2788
+ * @static
2789
+ */
2790
+ ERROR_EVENT : "error",
2791
+
2792
+ /**
2793
+ * Fires when a test has been ignored.
2794
+ * @event ignore
2795
+ * @static
2796
+ */
2797
+ TEST_IGNORE_EVENT : "ignore",
2798
+
2799
+ /**
2800
+ * Fires when all test suites and test cases have been completed.
2801
+ * @event complete
2802
+ * @static
2803
+ */
2804
+ COMPLETE_EVENT : "complete",
2805
+
2806
+ /**
2807
+ * Fires when the run() method is called.
2808
+ * @event begin
2809
+ * @static
2810
+ */
2811
+ BEGIN_EVENT : "begin",
2812
+
2813
+ //-------------------------------------------------------------------------
2814
+ // Test Tree-Related Methods
2815
+ //-------------------------------------------------------------------------
2816
+
2817
+ /**
2818
+ * Adds a test case to the test tree as a child of the specified node.
2819
+ * @param {TestNode} parentNode The node to add the test case to as a child.
2820
+ * @param {YUITest.TestCase} testCase The test case to add.
2821
+ * @return {Void}
2822
+ * @static
2823
+ * @private
2824
+ * @method _addTestCaseToTestTree
2825
+ */
2826
+ _addTestCaseToTestTree : function (parentNode, testCase){
2827
+
2828
+ //add the test suite
2829
+ var node = parentNode.appendChild(testCase),
2830
+ prop,
2831
+ testName;
2832
+
2833
+ //iterate over the items in the test case
2834
+ for (prop in testCase){
2835
+ if ((prop.indexOf("test") === 0 || prop.indexOf(" ") > -1) && typeof testCase[prop] == "function"){
2836
+ node.appendChild(prop);
2837
+ }
2838
+ }
2839
+
2840
+ },
2841
+
2842
+ /**
2843
+ * Adds a test suite to the test tree as a child of the specified node.
2844
+ * @param {TestNode} parentNode The node to add the test suite to as a child.
2845
+ * @param {YUITest.TestSuite} testSuite The test suite to add.
2846
+ * @return {Void}
2847
+ * @static
2848
+ * @private
2849
+ * @method _addTestSuiteToTestTree
2850
+ */
2851
+ _addTestSuiteToTestTree : function (parentNode, testSuite) {
2852
+
2853
+ //add the test suite
2854
+ var node = parentNode.appendChild(testSuite);
2855
+
2856
+ //iterate over the items in the master suite
2857
+ for (var i=0; i < testSuite.items.length; i++){
2858
+ if (testSuite.items[i] instanceof YUITest.TestSuite) {
2859
+ this._addTestSuiteToTestTree(node, testSuite.items[i]);
2860
+ } else if (testSuite.items[i] instanceof YUITest.TestCase) {
2861
+ this._addTestCaseToTestTree(node, testSuite.items[i]);
2862
+ }
2863
+ }
2864
+ },
2865
+
2866
+ /**
2867
+ * Builds the test tree based on items in the master suite. The tree is a hierarchical
2868
+ * representation of the test suites, test cases, and test functions. The resulting tree
2869
+ * is stored in _root and the pointer _cur is set to the root initially.
2870
+ * @return {Void}
2871
+ * @static
2872
+ * @private
2873
+ * @method _buildTestTree
2874
+ */
2875
+ _buildTestTree : function () {
2876
+
2877
+ this._root = new TestNode(this.masterSuite);
2878
+ //this._cur = this._root;
2879
+
2880
+ //iterate over the items in the master suite
2881
+ for (var i=0; i < this.masterSuite.items.length; i++){
2882
+ if (this.masterSuite.items[i] instanceof YUITest.TestSuite) {
2883
+ this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
2884
+ } else if (this.masterSuite.items[i] instanceof YUITest.TestCase) {
2885
+ this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
2886
+ }
2887
+ }
2888
+
2889
+ },
2890
+
2891
+ //-------------------------------------------------------------------------
2892
+ // Private Methods
2893
+ //-------------------------------------------------------------------------
2894
+
2895
+ /**
2896
+ * Handles the completion of a test object's tests. Tallies test results
2897
+ * from one level up to the next.
2898
+ * @param {TestNode} node The TestNode representing the test object.
2899
+ * @return {Void}
2900
+ * @method _handleTestObjectComplete
2901
+ * @private
2902
+ */
2903
+ _handleTestObjectComplete : function (node) {
2904
+ var parentNode;
2905
+
2906
+ if (typeof node.testObject == "object" && node !== null){
2907
+ parentNode = node.parent;
2908
+
2909
+ if (parentNode){
2910
+ parentNode.results.include(node.results);
2911
+ parentNode.results[node.testObject.name] = node.results;
2912
+ }
2913
+
2914
+ if (node.testObject instanceof YUITest.TestSuite){
2915
+ this._execNonTestMethod(node, "tearDown", false);
2916
+ node.results.duration = (new Date()) - node._start;
2917
+ this.fire({ type: this.TEST_SUITE_COMPLETE_EVENT, testSuite: node.testObject, results: node.results});
2918
+ } else if (node.testObject instanceof YUITest.TestCase){
2919
+ this._execNonTestMethod(node, "destroy", false);
2920
+ node.results.duration = (new Date()) - node._start;
2921
+ this.fire({ type: this.TEST_CASE_COMPLETE_EVENT, testCase: node.testObject, results: node.results});
2922
+ }
2923
+ }
2924
+ },
2925
+
2926
+ //-------------------------------------------------------------------------
2927
+ // Navigation Methods
2928
+ //-------------------------------------------------------------------------
2929
+
2930
+ /**
2931
+ * Retrieves the next node in the test tree.
2932
+ * @return {TestNode} The next node in the test tree or null if the end is reached.
2933
+ * @private
2934
+ * @static
2935
+ * @method _next
2936
+ */
2937
+ _next : function () {
2938
+
2939
+ if (this._cur === null){
2940
+ this._cur = this._root;
2941
+ } else if (this._cur.firstChild) {
2942
+ this._cur = this._cur.firstChild;
2943
+ } else if (this._cur.next) {
2944
+ this._cur = this._cur.next;
2945
+ } else {
2946
+ while (this._cur && !this._cur.next && this._cur !== this._root){
2947
+ this._handleTestObjectComplete(this._cur);
2948
+ this._cur = this._cur.parent;
2949
+ }
2950
+
2951
+ this._handleTestObjectComplete(this._cur);
2952
+
2953
+ if (this._cur == this._root){
2954
+ this._cur.results.type = "report";
2955
+ this._cur.results.timestamp = (new Date()).toLocaleString();
2956
+ this._cur.results.duration = (new Date()) - this._cur._start;
2957
+ this._lastResults = this._cur.results;
2958
+ this._running = false;
2959
+ this.fire({ type: this.COMPLETE_EVENT, results: this._lastResults});
2960
+ this._cur = null;
2961
+ } else {
2962
+ this._cur = this._cur.next;
2963
+ }
2964
+ }
2965
+
2966
+ return this._cur;
2967
+ },
2968
+
2969
+ /**
2970
+ * Executes a non-test method (init, setUp, tearDown, destroy)
2971
+ * and traps an errors. If an error occurs, an error event is
2972
+ * fired.
2973
+ * @param {Object} node The test node in the testing tree.
2974
+ * @param {String} methodName The name of the method to execute.
2975
+ * @param {Boolean} allowAsync Determines if the method can be called asynchronously.
2976
+ * @return {Boolean} True if an async method was called, false if not.
2977
+ * @method _execNonTestMethod
2978
+ * @private
2979
+ */
2980
+ _execNonTestMethod: function(node, methodName, allowAsync){
2981
+ var testObject = node.testObject,
2982
+ event = { type: this.ERROR_EVENT };
2983
+ try {
2984
+ if (allowAsync && testObject["async:" + methodName]){
2985
+ testObject["async:" + methodName](this._context);
2986
+ return true;
2987
+ } else {
2988
+ testObject[methodName](this._context);
2989
+ }
2990
+ } catch (ex){
2991
+ node.results.errors++;
2992
+ event.error = ex;
2993
+ event.methodName = methodName;
2994
+ if (testObject instanceof YUITest.TestCase){
2995
+ event.testCase = testObject;
2996
+ } else {
2997
+ event.testSuite = testSuite;
2998
+ }
2999
+
3000
+ this.fire(event);
3001
+ }
3002
+
3003
+ return false;
3004
+ },
3005
+
3006
+ /**
3007
+ * Runs a test case or test suite, returning the results.
3008
+ * @param {YUITest.TestCase|YUITest.TestSuite} testObject The test case or test suite to run.
3009
+ * @return {Object} Results of the execution with properties passed, failed, and total.
3010
+ * @private
3011
+ * @method _run
3012
+ * @static
3013
+ */
3014
+ _run : function () {
3015
+
3016
+ //flag to indicate if the TestRunner should wait before continuing
3017
+ var shouldWait = false;
3018
+
3019
+ //get the next test node
3020
+ var node = this._next();
3021
+
3022
+ if (node !== null) {
3023
+
3024
+ //set flag to say the testrunner is running
3025
+ this._running = true;
3026
+
3027
+ //eliminate last results
3028
+ this._lastResult = null;
3029
+
3030
+ var testObject = node.testObject;
3031
+
3032
+ //figure out what to do
3033
+ if (typeof testObject == "object" && testObject !== null){
3034
+ if (testObject instanceof YUITest.TestSuite){
3035
+ this.fire({ type: this.TEST_SUITE_BEGIN_EVENT, testSuite: testObject });
3036
+ node._start = new Date();
3037
+ this._execNonTestMethod(node, "setUp" ,false);
3038
+ } else if (testObject instanceof YUITest.TestCase){
3039
+ this.fire({ type: this.TEST_CASE_BEGIN_EVENT, testCase: testObject });
3040
+ node._start = new Date();
3041
+
3042
+ //regular or async init
3043
+ /*try {
3044
+ if (testObject["async:init"]){
3045
+ testObject["async:init"](this._context);
3046
+ return;
3047
+ } else {
3048
+ testObject.init(this._context);
3049
+ }
3050
+ } catch (ex){
3051
+ node.results.errors++;
3052
+ this.fire({ type: this.ERROR_EVENT, error: ex, testCase: testObject, methodName: "init" });
3053
+ }*/
3054
+ if(this._execNonTestMethod(node, "init", true)){
3055
+ return;
3056
+ }
3057
+ }
3058
+
3059
+ //some environments don't support setTimeout
3060
+ if (typeof setTimeout != "undefined"){
3061
+ setTimeout(function(){
3062
+ YUITest.TestRunner._run();
3063
+ }, 0);
3064
+ } else {
3065
+ this._run();
3066
+ }
3067
+ } else {
3068
+ this._runTest(node);
3069
+ }
3070
+
3071
+ }
3072
+ },
3073
+
3074
+ _resumeTest : function (segment) {
3075
+
3076
+ //get relevant information
3077
+ var node = this._cur;
3078
+
3079
+ //we know there's no more waiting now
3080
+ this._waiting = false;
3081
+
3082
+ //if there's no node, it probably means a wait() was called after resume()
3083
+ if (!node){
3084
+ //TODO: Handle in some way?
3085
+ //console.log("wait() called after resume()");
3086
+ //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
3087
+ return;
3088
+ }
3089
+
3090
+ var testName = node.testObject;
3091
+ var testCase = node.parent.testObject;
3092
+
3093
+ //cancel other waits if available
3094
+ if (testCase.__yui_wait){
3095
+ clearTimeout(testCase.__yui_wait);
3096
+ delete testCase.__yui_wait;
3097
+ }
3098
+
3099
+ //get the "should" test cases
3100
+ var shouldFail = testName.indexOf("fail:") === 0 ||
3101
+ (testCase._should.fail || {})[testName];
3102
+ var shouldError = (testCase._should.error || {})[testName];
3103
+
3104
+ //variable to hold whether or not the test failed
3105
+ var failed = false;
3106
+ var error = null;
3107
+
3108
+ //try the test
3109
+ try {
3110
+
3111
+ //run the test
3112
+ segment.call(testCase, this._context);
3113
+
3114
+ //if the test hasn't already failed and doesn't have any asserts...
3115
+ if(YUITest.Assert._getCount() == 0){
3116
+ throw new YUITest.AssertionError("Test has no asserts.");
3117
+ }
3118
+ //if it should fail, and it got here, then it's a fail because it didn't
3119
+ else if (shouldFail){
3120
+ error = new YUITest.ShouldFail();
3121
+ failed = true;
3122
+ } else if (shouldError){
3123
+ error = new YUITest.ShouldError();
3124
+ failed = true;
3125
+ }
3126
+
3127
+ } catch (thrown){
3128
+
3129
+ //cancel any pending waits, the test already failed
3130
+ if (testCase.__yui_wait){
3131
+ clearTimeout(testCase.__yui_wait);
3132
+ delete testCase.__yui_wait;
3133
+ }
3134
+
3135
+ //figure out what type of error it was
3136
+ if (thrown instanceof YUITest.AssertionError) {
3137
+ if (!shouldFail){
3138
+ error = thrown;
3139
+ failed = true;
3140
+ }
3141
+ } else if (thrown instanceof YUITest.Wait){
3142
+
3143
+ if (typeof thrown.segment == "function"){
3144
+ if (typeof thrown.delay == "number"){
3145
+
3146
+ //some environments don't support setTimeout
3147
+ if (typeof setTimeout != "undefined"){
3148
+ testCase.__yui_wait = setTimeout(function(){
3149
+ YUITest.TestRunner._resumeTest(thrown.segment);
3150
+ }, thrown.delay);
3151
+ this._waiting = true;
3152
+ } else {
3153
+ throw new Error("Asynchronous tests not supported in this environment.");
3154
+ }
3155
+ }
3156
+ }
3157
+
3158
+ return;
3159
+
3160
+ } else {
3161
+ //first check to see if it should error
3162
+ if (!shouldError) {
3163
+ error = new YUITest.UnexpectedError(thrown);
3164
+ failed = true;
3165
+ } else {
3166
+ //check to see what type of data we have
3167
+ if (typeof shouldError == "string"){
3168
+
3169
+ //if it's a string, check the error message
3170
+ if (thrown.message != shouldError){
3171
+ error = new YUITest.UnexpectedError(thrown);
3172
+ failed = true;
3173
+ }
3174
+ } else if (typeof shouldError == "function"){
3175
+
3176
+ //if it's a function, see if the error is an instance of it
3177
+ if (!(thrown instanceof shouldError)){
3178
+ error = new YUITest.UnexpectedError(thrown);
3179
+ failed = true;
3180
+ }
3181
+
3182
+ } else if (typeof shouldError == "object" && shouldError !== null){
3183
+
3184
+ //if it's an object, check the instance and message
3185
+ if (!(thrown instanceof shouldError.constructor) ||
3186
+ thrown.message != shouldError.message){
3187
+ error = new YUITest.UnexpectedError(thrown);
3188
+ failed = true;
3189
+ }
3190
+
3191
+ }
3192
+
3193
+ }
3194
+ }
3195
+
3196
+ }
3197
+
3198
+ //fire appropriate event
3199
+ if (failed) {
3200
+ this.fire({ type: this.TEST_FAIL_EVENT, testCase: testCase, testName: testName, error: error });
3201
+ } else {
3202
+ this.fire({ type: this.TEST_PASS_EVENT, testCase: testCase, testName: testName });
3203
+ }
3204
+
3205
+ //run the tear down
3206
+ this._execNonTestMethod(node.parent, "tearDown", false);
3207
+
3208
+ //reset the assert count
3209
+ YUITest.Assert._reset();
3210
+
3211
+ //calculate duration
3212
+ var duration = (new Date()) - node._start;
3213
+
3214
+ //update results
3215
+ node.parent.results[testName] = {
3216
+ result: failed ? "fail" : "pass",
3217
+ message: error ? error.getMessage() : "Test passed",
3218
+ type: "test",
3219
+ name: testName,
3220
+ duration: duration
3221
+ };
3222
+
3223
+ if (failed){
3224
+ node.parent.results.failed++;
3225
+ } else {
3226
+ node.parent.results.passed++;
3227
+ }
3228
+ node.parent.results.total++;
3229
+
3230
+ //set timeout not supported in all environments
3231
+ if (typeof setTimeout != "undefined"){
3232
+ setTimeout(function(){
3233
+ YUITest.TestRunner._run();
3234
+ }, 0);
3235
+ } else {
3236
+ this._run();
3237
+ }
3238
+
3239
+ },
3240
+
3241
+ /**
3242
+ * Handles an error as if it occurred within the currently executing
3243
+ * test. This is for mock methods that may be called asynchronously
3244
+ * and therefore out of the scope of the TestRunner. Previously, this
3245
+ * error would bubble up to the browser. Now, this method is used
3246
+ * to tell TestRunner about the error. This should never be called
3247
+ * by anyplace other than the Mock object.
3248
+ * @param {Error} error The error object.
3249
+ * @return {Void}
3250
+ * @method _handleError
3251
+ * @private
3252
+ * @static
3253
+ */
3254
+ _handleError: function(error){
3255
+
3256
+ if (this._waiting){
3257
+ this._resumeTest(function(){
3258
+ throw error;
3259
+ });
3260
+ } else {
3261
+ throw error;
3262
+ }
3263
+
3264
+ },
3265
+
3266
+ /**
3267
+ * Runs a single test based on the data provided in the node.
3268
+ * @param {TestNode} node The TestNode representing the test to run.
3269
+ * @return {Void}
3270
+ * @static
3271
+ * @private
3272
+ * @name _runTest
3273
+ */
3274
+ _runTest : function (node) {
3275
+
3276
+ //get relevant information
3277
+ var testName = node.testObject,
3278
+ testCase = node.parent.testObject,
3279
+ test = testCase[testName],
3280
+
3281
+ //get the "should" test cases
3282
+ shouldIgnore = testName.indexOf("ignore:") === 0 ||
3283
+ !inGroups(testCase.groups, this._groups) ||
3284
+ (testCase._should.ignore || {})[testName]; //deprecated
3285
+
3286
+ //figure out if the test should be ignored or not
3287
+ if (shouldIgnore){
3288
+
3289
+ //update results
3290
+ node.parent.results[testName] = {
3291
+ result: "ignore",
3292
+ message: "Test ignored",
3293
+ type: "test",
3294
+ name: testName.indexOf("ignore:") === 0 ? testName.substring(7) : testName
3295
+ };
3296
+
3297
+ node.parent.results.ignored++;
3298
+ node.parent.results.total++;
3299
+
3300
+ this.fire({ type: this.TEST_IGNORE_EVENT, testCase: testCase, testName: testName });
3301
+
3302
+ //some environments don't support setTimeout
3303
+ if (typeof setTimeout != "undefined"){
3304
+ setTimeout(function(){
3305
+ YUITest.TestRunner._run();
3306
+ }, 0);
3307
+ } else {
3308
+ this._run();
3309
+ }
3310
+
3311
+ } else {
3312
+
3313
+ //mark the start time
3314
+ node._start = new Date();
3315
+
3316
+ //run the setup
3317
+ this._execNonTestMethod(node.parent, "setUp", false);
3318
+
3319
+ //now call the body of the test
3320
+ this._resumeTest(test);
3321
+ }
3322
+
3323
+ },
3324
+
3325
+ //-------------------------------------------------------------------------
3326
+ // Misc Methods
3327
+ //-------------------------------------------------------------------------
3328
+
3329
+ /**
3330
+ * Retrieves the name of the current result set.
3331
+ * @return {String} The name of the result set.
3332
+ * @method getName
3333
+ */
3334
+ getName: function(){
3335
+ return this.masterSuite.name;
3336
+ },
3337
+
3338
+ /**
3339
+ * The name assigned to the master suite of the TestRunner. This is the name
3340
+ * that is output as the root's name when results are retrieved.
3341
+ * @param {String} name The name of the result set.
3342
+ * @return {Void}
3343
+ * @method setName
3344
+ */
3345
+ setName: function(name){
3346
+ this.masterSuite.name = name;
3347
+ },
3348
+
3349
+ //-------------------------------------------------------------------------
3350
+ // Public Methods
3351
+ //-------------------------------------------------------------------------
3352
+
3353
+ /**
3354
+ * Adds a test suite or test case to the list of test objects to run.
3355
+ * @param testObject Either a TestCase or a TestSuite that should be run.
3356
+ * @return {Void}
3357
+ * @method add
3358
+ * @static
3359
+ */
3360
+ add : function (testObject) {
3361
+ this.masterSuite.add(testObject);
3362
+ return this;
3363
+ },
3364
+
3365
+ /**
3366
+ * Removes all test objects from the runner.
3367
+ * @return {Void}
3368
+ * @method clear
3369
+ * @static
3370
+ */
3371
+ clear : function () {
3372
+ this.masterSuite = new YUITest.TestSuite("yuitests" + (new Date()).getTime());
3373
+ },
3374
+
3375
+ /**
3376
+ * Indicates if the TestRunner is waiting for a test to resume
3377
+ * @return {Boolean} True if the TestRunner is waiting, false if not.
3378
+ * @method isWaiting
3379
+ * @static
3380
+ */
3381
+ isWaiting: function() {
3382
+ return this._waiting;
3383
+ },
3384
+
3385
+ /**
3386
+ * Indicates that the TestRunner is busy running tests and therefore can't
3387
+ * be stopped and results cannot be gathered.
3388
+ * @return {Boolean} True if the TestRunner is running, false if not.
3389
+ * @method isRunning
3390
+ */
3391
+ isRunning: function(){
3392
+ return this._running;
3393
+ },
3394
+
3395
+ /**
3396
+ * Returns the last complete results set from the TestRunner. Null is returned
3397
+ * if the TestRunner is running or no tests have been run.
3398
+ * @param {Function} format (Optional) A test format to return the results in.
3399
+ * @return {Object|String} Either the results object or, if a test format is
3400
+ * passed as the argument, a string representing the results in a specific
3401
+ * format.
3402
+ * @method getResults
3403
+ */
3404
+ getResults: function(format){
3405
+ if (!this._running && this._lastResults){
3406
+ if (typeof format == "function"){
3407
+ return format(this._lastResults);
3408
+ } else {
3409
+ return this._lastResults;
3410
+ }
3411
+ } else {
3412
+ return null;
3413
+ }
3414
+ },
3415
+
3416
+ /**
3417
+ * Returns the coverage report for the files that have been executed.
3418
+ * This returns only coverage information for files that have been
3419
+ * instrumented using YUI Test Coverage and only those that were run
3420
+ * in the same pass.
3421
+ * @param {Function} format (Optional) A coverage format to return results in.
3422
+ * @return {Object|String} Either the coverage object or, if a coverage
3423
+ * format is specified, a string representing the results in that format.
3424
+ * @method getCoverage
3425
+ */
3426
+ getCoverage: function(format){
3427
+ if (!this._running && typeof _yuitest_coverage == "object"){
3428
+ if (typeof format == "function"){
3429
+ return format(_yuitest_coverage);
3430
+ } else {
3431
+ return _yuitest_coverage;
3432
+ }
3433
+ } else {
3434
+ return null;
3435
+ }
3436
+ },
3437
+
3438
+ /**
3439
+ * Used to continue processing when a method marked with
3440
+ * "async:" is executed. This should not be used in test
3441
+ * methods, only in init(). Each argument is a string, and
3442
+ * when the returned function is executed, the arguments
3443
+ * are assigned to the context data object using the string
3444
+ * as the key name (value is the argument itself).
3445
+ * @private
3446
+ * @return {Function} A callback function.
3447
+ */
3448
+ callback: function(){
3449
+ var names = arguments,
3450
+ data = this._context,
3451
+ that = this;
3452
+
3453
+ return function(){
3454
+ for (var i=0; i < arguments.length; i++){
3455
+ data[names[i]] = arguments[i];
3456
+ }
3457
+ that._run();
3458
+ };
3459
+ },
3460
+
3461
+ /**
3462
+ * Resumes the TestRunner after wait() was called.
3463
+ * @param {Function} segment The function to run as the rest
3464
+ * of the haulted test.
3465
+ * @return {Void}
3466
+ * @method resume
3467
+ * @static
3468
+ */
3469
+ resume : function (segment) {
3470
+ if (this._waiting){
3471
+ this._resumeTest(segment || function(){});
3472
+ } else {
3473
+ throw new Error("resume() called without wait().");
3474
+ }
3475
+ },
3476
+
3477
+ /**
3478
+ * Runs the test suite.
3479
+ * @param {Object|Boolean} options (Optional) Options for the runner:
3480
+ * <code>oldMode</code> indicates the TestRunner should work in the YUI <= 2.8 way
3481
+ * of internally managing test suites. <code>groups</code> is an array
3482
+ * of test groups indicating which tests to run.
3483
+ * @return {Void}
3484
+ * @method run
3485
+ * @static
3486
+ */
3487
+ run : function (options) {
3488
+
3489
+ options = options || {};
3490
+
3491
+ //pointer to runner to avoid scope issues
3492
+ var runner = YUITest.TestRunner,
3493
+ oldMode = options.oldMode;
3494
+
3495
+
3496
+ //if there's only one suite on the masterSuite, move it up
3497
+ if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof YUITest.TestSuite){
3498
+ this.masterSuite = this.masterSuite.items[0];
3499
+ }
3500
+
3501
+ //determine if there are any groups to filter on
3502
+ runner._groups = (options.groups instanceof Array) ? "," + options.groups.join(",") + "," : "";
3503
+
3504
+ //initialize the runner
3505
+ runner._buildTestTree();
3506
+ runner._context = {};
3507
+ runner._root._start = new Date();
3508
+
3509
+ //fire the begin event
3510
+ runner.fire(runner.BEGIN_EVENT);
3511
+
3512
+ //begin the testing
3513
+ runner._run();
3514
+ }
3515
+ });
3516
+
3517
+ return new TestRunner();
3518
+
3519
+ }();
3520
+
3521
+ /*!
3522
+ * Portions of this code incorporated from CSS Lint:
3523
+ * https://github.com/stubbornella/csslint
3524
+ */
3525
+
3526
+ importPackage(java.io);
3527
+ importPackage(java.lang);
3528
+
3529
+ YUITest.CLI = {
3530
+ args: arguments,
3531
+
3532
+ print: function(message){
3533
+ System.out.print(message);
3534
+ },
3535
+
3536
+ println: print,
3537
+
3538
+ warn: function(message){
3539
+ System.err.print(message);
3540
+ },
3541
+
3542
+ quit: quit,
3543
+
3544
+ isDirectory: function(name){
3545
+ var dir = new File(name);
3546
+ return dir.isDirectory();
3547
+ },
3548
+
3549
+ getFiles: function(dir){
3550
+ var files = [];
3551
+
3552
+ function traverse(dir) {
3553
+ var dirList = dir.listFiles();
3554
+ dirList.forEach(function (file) {
3555
+ if (/\.js$/.test(file)) {
3556
+ files.push(file.toString());
3557
+ } else if (file.isDirectory()) {
3558
+ traverse(file);
3559
+ }
3560
+ });
3561
+ }
3562
+
3563
+ traverse(new File(dir));
3564
+
3565
+ return files;
3566
+ },
3567
+
3568
+ getFullPath: function(filename){
3569
+ return (new File(filename)).getCanonicalPath();
3570
+ },
3571
+
3572
+ readFile: readFile,
3573
+
3574
+ processFiles: function(){
3575
+ var files = this.files,
3576
+ i, len, output;
3577
+
3578
+ if (files.length){
3579
+ for (i=0, len=files.length; i < len; i++){
3580
+
3581
+ if (this.options.verbose){
3582
+ this.warn("[INFO] Loading " + files[i] + "\n");
3583
+ }
3584
+
3585
+ load(files[i]);
3586
+
3587
+ }
3588
+ } else {
3589
+ this.warn("[ERROR] No tests to run, exiting.\n");
3590
+ this.quit(1);
3591
+ }
3592
+ }
3593
+ };
3594
+
3595
+
3596
+
3597
+
3598
+
3599
+ /**
3600
+ * Console output that mimics logger output from YUI Test for YUI 2/3.
3601
+ * @namespace YUITest.Node.CLI
3602
+ * @class Logger
3603
+ * @constructor
3604
+ */
3605
+ YUITest.CLI.Logger = function(){
3606
+
3607
+ var testRunner = YUITest.TestRunner,
3608
+ cli = YUITest.CLI;
3609
+
3610
+ //handles test runner events
3611
+ function handleEvent(event){
3612
+
3613
+ var message = "";
3614
+ switch(event.type){
3615
+ case testRunner.BEGIN_EVENT:
3616
+ message = "Testing began at " + (new Date()).toString() + ".";
3617
+ messageType = "info";
3618
+ break;
3619
+
3620
+ case testRunner.COMPLETE_EVENT:
3621
+ message = "Testing completed at " +
3622
+ (new Date()).toString() + ".\n" +
3623
+ "Passed:" + event.results.passed + " Failed:" + event.results.failed +
3624
+ " Total:" + event.results.total + "(" + event.results.ignored + " ignored)";
3625
+ messageType = "info";
3626
+ break;
3627
+
3628
+ case testRunner.TEST_FAIL_EVENT:
3629
+ message = event.testName + ": failed.\n" + event.error.getMessage();
3630
+ messageType = "fail";
3631
+ break;
3632
+
3633
+ case testRunner.ERROR_EVENT:
3634
+ message = event.methodName + ": error.\n" + event.error.message;
3635
+ messageType = "error";
3636
+ break;
3637
+
3638
+ case testRunner.TEST_IGNORE_EVENT:
3639
+ message = event.testName + ": ignored.";
3640
+ messageType = "ignore";
3641
+ break;
3642
+
3643
+ case testRunner.TEST_PASS_EVENT:
3644
+ message = event.testName + ": passed.";
3645
+ messageType = "pass";
3646
+ break;
3647
+
3648
+ case testRunner.TEST_SUITE_BEGIN_EVENT:
3649
+ message = "Test suite \"" + event.testSuite.name + "\" started.";
3650
+ messageType = "info";
3651
+ break;
3652
+
3653
+ case testRunner.TEST_SUITE_COMPLETE_EVENT:
3654
+ message = "Testing completed at " +
3655
+ (new Date()).toString() + ".\n" +
3656
+ "Passed:" + event.results.passed + " Failed:" + event.results.failed +
3657
+ " Total:" + event.results.total + "(" + event.results.ignored + " ignored)";
3658
+ messageType = "info";
3659
+ break;
3660
+
3661
+ case testRunner.TEST_CASE_BEGIN_EVENT:
3662
+ message = "Test case \"" + event.testCase.name + "\" started.";
3663
+ messageType = "info";
3664
+ break;
3665
+
3666
+ case testRunner.TEST_CASE_COMPLETE_EVENT:
3667
+ message = "Testing completed at " +
3668
+ (new Date()).toString() + ".\n" +
3669
+ "Passed:" + event.results.passed + " Failed:" + event.results.failed +
3670
+ " Total:" + event.results.total + "(" + event.results.ignored + " ignored)";
3671
+ messageType = "info";
3672
+ break;
3673
+ default:
3674
+ message = "Unexpected event " + event.type;
3675
+ messageType = "info";
3676
+ }
3677
+
3678
+ cli.print(message + "\n");
3679
+ }
3680
+
3681
+ testRunner.subscribe(testRunner.BEGIN_EVENT, handleEvent)
3682
+ testRunner.subscribe(testRunner.TEST_FAIL_EVENT, handleEvent);
3683
+ testRunner.subscribe(testRunner.TEST_PASS_EVENT, handleEvent);
3684
+ testRunner.subscribe(testRunner.TEST_IGNORE_EVENT, handleEvent);
3685
+ testRunner.subscribe(testRunner.TEST_CASE_BEGIN_EVENT, handleEvent);
3686
+ testRunner.subscribe(testRunner.TEST_CASE_COMPLETE_EVENT, handleEvent);
3687
+ testRunner.subscribe(testRunner.TEST_SUITE_BEGIN_EVENT, handleEvent);
3688
+ testRunner.subscribe(testRunner.TEST_SUITE_COMPLETE_EVENT, handleEvent);
3689
+ testRunner.subscribe(testRunner.COMPLETE_EVENT, handleEvent);
3690
+
3691
+ };
3692
+
3693
+
3694
+
3695
+ /**
3696
+ * Console output that mimics XUnit output.
3697
+ * @namespace YUITest.CLI
3698
+ * @class XUnit
3699
+ * @constructor
3700
+ */
3701
+ YUITest.CLI.XUnit = function(){
3702
+
3703
+ var testRunner = YUITest.TestRunner,
3704
+ cli = YUITest.CLI,
3705
+ errors = [],
3706
+ failures = [],
3707
+ stack = [];
3708
+
3709
+ function filterStackTrace(stackTrace){
3710
+ if (stackTrace){
3711
+ var lines = stackTrace.split("\n"),
3712
+ result = [],
3713
+ i, len;
3714
+
3715
+ //skip first line, it's the error
3716
+ for (i=1, len=lines.length; i < len; i++){
3717
+ if (lines[i].indexOf("yuitest-node") > -1){
3718
+ break;
3719
+ } else {
3720
+ result.push(lines[i]);
3721
+ }
3722
+ }
3723
+
3724
+ return result.join("\n");
3725
+ } else {
3726
+ return "Unavailable."
3727
+ }
3728
+ }
3729
+
3730
+ //handles test runner events
3731
+ function handleEvent(event){
3732
+
3733
+ var message = "",
3734
+ results = event.results,
3735
+ i, len;
3736
+
3737
+ switch(event.type){
3738
+ case testRunner.BEGIN_EVENT:
3739
+ message = "YUITest for Node.js\n";
3740
+
3741
+ if (testRunner._groups){
3742
+ message += "Filtering on groups '" + testRunner._groups.slice(1,-1) + "'\n";
3743
+ }
3744
+ break;
3745
+
3746
+ case testRunner.COMPLETE_EVENT:
3747
+ message = "\nTotal tests: " + results.total + ", Failures: " +
3748
+ results.failed + ", Skipped: " + results.ignored +
3749
+ ", Time: " + (results.duration/1000) + " seconds\n";
3750
+
3751
+ if (failures.length){
3752
+ message += "\nTests failed:\n";
3753
+
3754
+ for (i=0,len=failures.length; i < len; i++){
3755
+ message += "\n" + (i+1) + ") " + failures[i].name + " : " + failures[i].error.getMessage() + "\n";
3756
+ message += "Stack trace:\n" + filterStackTrace(failures[i].error.stack) + "\n";
3757
+ }
3758
+
3759
+ message += "\n";
3760
+ }
3761
+
3762
+ if (errors.length){
3763
+ message += "\nErrors:\n";
3764
+
3765
+ for (i=0,len=errors.length; i < len; i++){
3766
+ message += "\n" + (i+1) + ") " + errors[i].name + " : " + errors[i].error.message + "\n";
3767
+ message += "Stack trace:\n" + filterStackTrace(errors[i].error.stack) + "\n";
3768
+ }
3769
+
3770
+ message += "\n";
3771
+ }
3772
+
3773
+ message += "\n\n";
3774
+ break;
3775
+
3776
+ case testRunner.TEST_FAIL_EVENT:
3777
+ message = "F";
3778
+ failures.push({
3779
+ name: stack.concat([event.testName]).join(" > "),
3780
+ error: event.error
3781
+ });
3782
+
3783
+ break;
3784
+
3785
+ case testRunner.ERROR_EVENT:
3786
+ errors.push({
3787
+ name: stack.concat([event.methodName]).join(" > "),
3788
+ error: event.error
3789
+ });
3790
+
3791
+ break;
3792
+
3793
+ case testRunner.TEST_IGNORE_EVENT:
3794
+ message = "S";
3795
+ break;
3796
+
3797
+ case testRunner.TEST_PASS_EVENT:
3798
+ message = ".";
3799
+ break;
3800
+
3801
+ case testRunner.TEST_SUITE_BEGIN_EVENT:
3802
+ stack.push(event.testSuite.name);
3803
+ break;
3804
+
3805
+ case testRunner.TEST_CASE_COMPLETE_EVENT:
3806
+ case testRunner.TEST_SUITE_COMPLETE_EVENT:
3807
+ stack.pop();
3808
+ break;
3809
+
3810
+ case testRunner.TEST_CASE_BEGIN_EVENT:
3811
+ stack.push(event.testCase.name);
3812
+ break;
3813
+
3814
+ //no default
3815
+ }
3816
+
3817
+ cli.print(message);
3818
+ }
3819
+
3820
+ testRunner.subscribe(testRunner.BEGIN_EVENT, handleEvent)
3821
+ testRunner.subscribe(testRunner.TEST_FAIL_EVENT, handleEvent);
3822
+ testRunner.subscribe(testRunner.TEST_PASS_EVENT, handleEvent);
3823
+ testRunner.subscribe(testRunner.ERROR_EVENT, handleEvent);
3824
+ testRunner.subscribe(testRunner.TEST_IGNORE_EVENT, handleEvent);
3825
+ testRunner.subscribe(testRunner.TEST_CASE_BEGIN_EVENT, handleEvent);
3826
+ testRunner.subscribe(testRunner.TEST_CASE_COMPLETE_EVENT, handleEvent);
3827
+ testRunner.subscribe(testRunner.TEST_SUITE_BEGIN_EVENT, handleEvent);
3828
+ testRunner.subscribe(testRunner.TEST_SUITE_COMPLETE_EVENT, handleEvent);
3829
+ testRunner.subscribe(testRunner.COMPLETE_EVENT, handleEvent);
3830
+
3831
+ };
3832
+
3833
+
3834
+
3835
+ /**
3836
+ * Console output that uses TestFormat formats.
3837
+ * @namespace YUITest.Node.CLI
3838
+ * @class Format
3839
+ * @constructor
3840
+ */
3841
+ YUITest.CLI.Format = function(format){
3842
+
3843
+ var testRunner = YUITest.TestRunner,
3844
+ cli = YUITest.CLI;
3845
+
3846
+ //handles test runner events
3847
+ function handleEvent(event){
3848
+
3849
+ var results = event.results;
3850
+
3851
+ cli.print(format(results));
3852
+ }
3853
+
3854
+ testRunner.subscribe(testRunner.COMPLETE_EVENT, handleEvent);
3855
+
3856
+ };
3857
+
3858
+ /*
3859
+ * Augments the environment-specific CLI API with common functionality.
3860
+ */
3861
+ YUITest.Util.mix(YUITest.CLI, {
3862
+
3863
+ files: [],
3864
+
3865
+ options: {
3866
+ verbose: false,
3867
+ webcompat: false,
3868
+ help: false,
3869
+ format: "xunit"
3870
+ },
3871
+
3872
+ outputHelp: function(){
3873
+ this.print([
3874
+ "\nUsage: yuitest [options] [file|dir]*",
3875
+ " ",
3876
+ "Global Options",
3877
+ " --groups groupname Run only tests cases that are part of groupname.",
3878
+ " --help Displays this information.",
3879
+ " --format <format> Specifies output format (junitxml, tap, xunit).",
3880
+ " --verbose Display informational messages and warnings.",
3881
+ " --webcompat Load tests designed for use in browsers."
3882
+ ].join("\n") + "\n\n");
3883
+ },
3884
+
3885
+ processArguments: function(){
3886
+
3887
+ var args = this.args,
3888
+ arg = args.shift(),
3889
+ files = [];
3890
+
3891
+ while(arg){
3892
+ if (arg.indexOf("--") == 0){
3893
+ this.options[arg.substring(2)] = true;
3894
+
3895
+ //get the next argument
3896
+ if (arg == "--groups" || arg == "--format"){
3897
+ this.options[arg.substring(2)] = args.shift();
3898
+ }
3899
+ } else {
3900
+
3901
+ //see if it's a directory or a file
3902
+ if (this.isDirectory(arg)){
3903
+ files = files.concat(this.getFiles(arg));
3904
+ } else {
3905
+ files.push(arg);
3906
+ }
3907
+ }
3908
+ arg = args.shift();
3909
+ }
3910
+
3911
+ if (this.options.help || files.length === 0){
3912
+ this.outputHelp();
3913
+ this.quit(0);
3914
+ }
3915
+
3916
+ this.files = files;
3917
+
3918
+ //-----------------------------------------------------------------------------
3919
+ // Determine output format
3920
+ //-----------------------------------------------------------------------------
3921
+
3922
+ switch(this.options.format){
3923
+ case "junitxml":
3924
+ if (this.options.verbose){
3925
+ this.warn("[INFO] Using JUnitXML output format.\n");
3926
+ }
3927
+ YUITest.CLI.Format(YUITest.TestFormat.JUnitXML);
3928
+ break;
3929
+ case "tap":
3930
+ if (this.options.verbose){
3931
+ this.warn("[INFO] Using TAP output format.\n");
3932
+ }
3933
+ YUITest.CLI.Format(YUITest.TestFormat.TAP);
3934
+ break;
3935
+ default:
3936
+ if (this.options.verbose){
3937
+ this.warn("[INFO] Using XUnit output format.\n");
3938
+ }
3939
+ YUITest.CLI.XUnit();
3940
+ }
3941
+ },
3942
+
3943
+ start: function(){
3944
+
3945
+ this.processArguments();
3946
+ this.processFiles();
3947
+
3948
+ YUITest.TestRunner.run({
3949
+ groups: this.options.groups ? this.options.groups.split(",") : null
3950
+ });
3951
+ }
3952
+ });
3953
+
3954
+ YUITest.CLI.start();
3955
+