ruby_css_lint 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
+