]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/yuitest/yuitest_core.js
Release 6.5.0
[Github/sugarcrm.git] / include / javascript / yui / build / yuitest / yuitest_core.js
1 /*
2 Copyright (c) 2011, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.com/yui/license.html
5 version: 2.9.0
6 */
7 YAHOO.namespace("tool");
8
9 //-----------------------------------------------------------------------------
10 // TestCase object
11 //-----------------------------------------------------------------------------
12 (function(){
13     
14     //used for autogenerating test case names
15     var tempId = 0;
16     
17     /**
18      * Test case containing various tests to run.
19      * @param template An object containing any number of test methods, other methods,
20      *                 an optional name, and anything else the test case needs.
21      * @class TestCase
22      * @namespace YAHOO.tool
23      * @constructor
24      */
25     YAHOO.tool.TestCase = function (template /*:Object*/) {
26         
27         /**
28          * Special rules for the test case. Possible subobjects
29          * are fail, for tests that should fail, and error, for
30          * tests that should throw an error.
31          */
32         this._should /*:Object*/ = {};
33         
34         //copy over all properties from the template to this object
35         for (var prop in template) {
36             this[prop] = template[prop];
37         }    
38         
39         //check for a valid name
40         if (!YAHOO.lang.isString(this.name)){
41             /**
42              * Name for the test case.
43              */
44             this.name /*:String*/ = "testCase" + (tempId++);
45         }
46     
47     };
48     
49     
50     YAHOO.tool.TestCase.prototype = {  
51     
52         /**
53          * Resumes a paused test and runs the given function.
54          * @param {Function} segment (Optional) The function to run.
55          *      If omitted, the test automatically passes.
56          * @return {Void}
57          * @method resume
58          */
59         resume : function (segment /*:Function*/) /*:Void*/ {
60             YAHOO.tool.TestRunner.resume(segment);
61         },
62     
63         /**
64          * Causes the test case to wait a specified amount of time and then
65          * continue executing the given code.
66          * @param {Function} segment (Optional) The function to run after the delay.
67          *      If omitted, the TestRunner will wait until resume() is called.
68          * @param {int} delay (Optional) The number of milliseconds to wait before running
69          *      the function. If omitted, defaults to zero.
70          * @return {Void}
71          * @method wait
72          */
73         wait : function (segment /*:Function*/, delay /*:int*/) /*:Void*/{
74             var args = arguments;
75             if (YAHOO.lang.isFunction(args[0])){
76                 throw new YAHOO.tool.TestCase.Wait(args[0], args[1]);
77             } else {
78                 throw new YAHOO.tool.TestCase.Wait(function(){
79                     YAHOO.util.Assert.fail("Timeout: wait() called but resume() never called.");
80                 }, (YAHOO.lang.isNumber(args[0]) ? args[0] : 10000));
81             }            
82         },
83     
84         //-------------------------------------------------------------------------
85         // Stub Methods
86         //-------------------------------------------------------------------------
87     
88         /**
89          * Function to run before each test is executed.
90          * @return {Void}
91          * @method setUp
92          */
93         setUp : function () /*:Void*/ {
94         },
95         
96         /**
97          * Function to run after each test is executed.
98          * @return {Void}
99          * @method tearDown
100          */
101         tearDown: function () /*:Void*/ {    
102         }
103     };
104     
105     /**
106      * Represents a stoppage in test execution to wait for an amount of time before
107      * continuing.
108      * @param {Function} segment A function to run when the wait is over.
109      * @param {int} delay The number of milliseconds to wait before running the code.
110      * @class Wait
111      * @namespace YAHOO.tool.TestCase
112      * @constructor
113      *
114      */
115     YAHOO.tool.TestCase.Wait = function (segment /*:Function*/, delay /*:int*/) {
116         
117         /**
118          * The segment of code to run when the wait is over.
119          * @type Function
120          * @property segment
121          */
122         this.segment /*:Function*/ = (YAHOO.lang.isFunction(segment) ? segment : null);
123     
124         /**
125          * The delay before running the segment of code.
126          * @type int
127          * @property delay
128          */
129         this.delay /*:int*/ = (YAHOO.lang.isNumber(delay) ? delay : 0);
130     
131     };
132
133 })();
134 YAHOO.namespace("tool");
135
136
137 //-----------------------------------------------------------------------------
138 // TestSuite object
139 //-----------------------------------------------------------------------------
140
141 /**
142  * A test suite that can contain a collection of TestCase and TestSuite objects.
143  * @param {String||Object} data The name of the test suite or an object containing
144  *      a name property as well as setUp and tearDown methods.
145  * @namespace YAHOO.tool
146  * @class TestSuite
147  * @constructor
148  */
149 YAHOO.tool.TestSuite = function (data /*:String||Object*/) {
150
151     /**
152      * The name of the test suite.
153      * @type String
154      * @property name
155      */
156     this.name /*:String*/ = "";
157
158     /**
159      * Array of test suites and
160      * @private
161      */
162     this.items /*:Array*/ = [];
163
164     //initialize the properties
165     if (YAHOO.lang.isString(data)){
166         this.name = data;
167     } else if (YAHOO.lang.isObject(data)){
168         YAHOO.lang.augmentObject(this, data, true);
169     }
170
171     //double-check name
172     if (this.name === ""){
173         this.name = YAHOO.util.Dom.generateId(null, "testSuite");
174     }
175
176 };
177
178 YAHOO.tool.TestSuite.prototype = {
179     
180     /**
181      * Adds a test suite or test case to the test suite.
182      * @param {YAHOO.tool.TestSuite||YAHOO.tool.TestCase} testObject The test suite or test case to add.
183      * @return {Void}
184      * @method add
185      */
186     add : function (testObject /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
187         if (testObject instanceof YAHOO.tool.TestSuite || testObject instanceof YAHOO.tool.TestCase) {
188             this.items.push(testObject);
189         }
190     },
191     
192     //-------------------------------------------------------------------------
193     // Stub Methods
194     //-------------------------------------------------------------------------
195
196     /**
197      * Function to run before each test is executed.
198      * @return {Void}
199      * @method setUp
200      */
201     setUp : function () /*:Void*/ {
202     },
203     
204     /**
205      * Function to run after each test is executed.
206      * @return {Void}
207      * @method tearDown
208      */
209     tearDown: function () /*:Void*/ {
210     }
211     
212 };
213 YAHOO.namespace("tool");
214
215 /**
216  * The YUI test tool
217  * @module yuitest
218  * @namespace YAHOO.tool
219  * @requires yahoo,dom,event,logger
220  * @optional event-simulate
221  */
222
223
224 //-----------------------------------------------------------------------------
225 // TestRunner object
226 //-----------------------------------------------------------------------------
227
228
229 YAHOO.tool.TestRunner = (function(){
230
231     /**
232      * A node in the test tree structure. May represent a TestSuite, TestCase, or
233      * test function.
234      * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
235      * @class TestNode
236      * @constructor
237      * @private
238      */
239     function TestNode(testObject /*:Variant*/){
240     
241         /**
242          * The TestSuite, TestCase, or test function represented by this node.
243          * @type Variant
244          * @property testObject
245          */
246         this.testObject = testObject;
247         
248         /**
249          * Pointer to this node's first child.
250          * @type TestNode
251          * @property firstChild
252          */        
253         this.firstChild /*:TestNode*/ = null;
254         
255         /**
256          * Pointer to this node's last child.
257          * @type TestNode
258          * @property lastChild
259          */        
260         this.lastChild = null;
261         
262         /**
263          * Pointer to this node's parent.
264          * @type TestNode
265          * @property parent
266          */        
267         this.parent = null; 
268    
269         /**
270          * Pointer to this node's next sibling.
271          * @type TestNode
272          * @property next
273          */        
274         this.next = null;
275         
276         /**
277          * Test results for this test object.
278          * @type object
279          * @property results
280          */                
281         this.results /*:Object*/ = {
282             passed : 0,
283             failed : 0,
284             total : 0,
285             ignored : 0,
286             duration: 0
287         };
288         
289         //initialize results
290         if (testObject instanceof YAHOO.tool.TestSuite){
291             this.results.type = "testsuite";
292             this.results.name = testObject.name;
293         } else if (testObject instanceof YAHOO.tool.TestCase){
294             this.results.type = "testcase";
295             this.results.name = testObject.name;
296         }
297        
298     }
299     
300     TestNode.prototype = {
301     
302         /**
303          * Appends a new test object (TestSuite, TestCase, or test function name) as a child
304          * of this node.
305          * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
306          * @return {Void}
307          */
308         appendChild : function (testObject /*:Variant*/) /*:Void*/{
309             var node = new TestNode(testObject);
310             if (this.firstChild === null){
311                 this.firstChild = this.lastChild = node;
312             } else {
313                 this.lastChild.next = node;
314                 this.lastChild = node;
315             }
316             node.parent = this;
317             return node;
318         }       
319     };
320
321     /**
322      * Runs test suites and test cases, providing events to allowing for the
323      * interpretation of test results.
324      * @namespace YAHOO.tool
325      * @class TestRunner
326      * @static
327      */
328     function TestRunner(){
329     
330         //inherit from EventProvider
331         TestRunner.superclass.constructor.apply(this,arguments);
332         
333         /**
334          * Suite on which to attach all TestSuites and TestCases to be run.
335          * @type YAHOO.tool.TestSuite
336          * @property masterSuite
337          * @private
338          * @static
339          */
340         this.masterSuite = new YAHOO.tool.TestSuite("yuitests" + (new Date()).getTime());        
341
342         /**
343          * Pointer to the current node in the test tree.
344          * @type TestNode
345          * @private
346          * @property _cur
347          * @static
348          */
349         this._cur = null;                
350         
351         /**
352          * Pointer to the root node in the test tree.
353          * @type TestNode
354          * @private
355          * @property _root
356          * @static
357          */
358         this._root = null;
359         
360         /**
361          * Indicates if the TestRunner is currently running tests.
362          * @type Boolean
363          * @private
364          * @property _running
365          * @static
366          */
367         this._running = false;
368         
369         /**
370          * Holds copy of the results object generated when all tests are
371          * complete.
372          * @type Object
373          * @private
374          * @property _lastResults
375          * @static
376          */
377         this._lastResults = null;
378         
379         //create events
380         var events /*:Array*/ = [
381             this.TEST_CASE_BEGIN_EVENT,
382             this.TEST_CASE_COMPLETE_EVENT,
383             this.TEST_SUITE_BEGIN_EVENT,
384             this.TEST_SUITE_COMPLETE_EVENT,
385             this.TEST_PASS_EVENT,
386             this.TEST_FAIL_EVENT,
387             this.TEST_IGNORE_EVENT,
388             this.COMPLETE_EVENT,
389             this.BEGIN_EVENT
390         ];
391         for (var i=0; i < events.length; i++){
392             this.createEvent(events[i], { scope: this });
393         }       
394    
395     }
396     
397     YAHOO.lang.extend(TestRunner, YAHOO.util.EventProvider, {
398     
399         //-------------------------------------------------------------------------
400         // Constants
401         //-------------------------------------------------------------------------
402          
403         /**
404          * Fires when a test case is opened but before the first 
405          * test is executed.
406          * @event testcasebegin
407          */         
408         TEST_CASE_BEGIN_EVENT /*:String*/ : "testcasebegin",
409         
410         /**
411          * Fires when all tests in a test case have been executed.
412          * @event testcasecomplete
413          */        
414         TEST_CASE_COMPLETE_EVENT /*:String*/ : "testcasecomplete",
415         
416         /**
417          * Fires when a test suite is opened but before the first 
418          * test is executed.
419          * @event testsuitebegin
420          */        
421         TEST_SUITE_BEGIN_EVENT /*:String*/ : "testsuitebegin",
422         
423         /**
424          * Fires when all test cases in a test suite have been
425          * completed.
426          * @event testsuitecomplete
427          */        
428         TEST_SUITE_COMPLETE_EVENT /*:String*/ : "testsuitecomplete",
429         
430         /**
431          * Fires when a test has passed.
432          * @event pass
433          */        
434         TEST_PASS_EVENT /*:String*/ : "pass",
435         
436         /**
437          * Fires when a test has failed.
438          * @event fail
439          */        
440         TEST_FAIL_EVENT /*:String*/ : "fail",
441         
442         /**
443          * Fires when a test has been ignored.
444          * @event ignore
445          */        
446         TEST_IGNORE_EVENT /*:String*/ : "ignore",
447         
448         /**
449          * Fires when all test suites and test cases have been completed.
450          * @event complete
451          */        
452         COMPLETE_EVENT /*:String*/ : "complete",
453         
454         /**
455          * Fires when the run() method is called.
456          * @event begin
457          */        
458         BEGIN_EVENT /*:String*/ : "begin",                
459         
460         //-------------------------------------------------------------------------
461         // Misc Methods
462         //-------------------------------------------------------------------------   
463
464         /**
465          * Retrieves the name of the current result set.
466          * @return {String} The name of the result set.
467          * @method getName
468          */
469         getName: function(){
470             return this.masterSuite.name;
471         },         
472
473         /**
474          * The name assigned to the master suite of the TestRunner. This is the name
475          * that is output as the root's name when results are retrieved.
476          * @param {String} name The name of the result set.
477          * @return {Void}
478          * @method setName
479          */
480         setName: function(name){
481             this.masterSuite.name = name;
482         },        
483         
484         
485         //-------------------------------------------------------------------------
486         // State-Related Methods
487         //-------------------------------------------------------------------------
488
489         /**
490          * Indicates that the TestRunner is busy running tests and therefore can't
491          * be stopped and results cannot be gathered.
492          * @return {Boolean} True if the TestRunner is running, false if not.
493          * @method isRunning
494          */
495         isRunning: function(){
496             return this._running;
497         },
498         
499         /**
500          * Returns the last complete results set from the TestRunner. Null is returned
501          * if the TestRunner is running or no tests have been run.
502          * @param {Function} format (Optional) A test format to return the results in.
503          * @return {Object|String} Either the results object or, if a test format is 
504          *      passed as the argument, a string representing the results in a specific
505          *      format.
506          * @method getResults
507          */
508         getResults: function(format){
509             if (!this._running && this._lastResults){
510                 if (YAHOO.lang.isFunction(format)){
511                     return format(this._lastResults);                    
512                 } else {
513                     return this._lastResults;
514                 }
515             } else {
516                 return null;
517             }
518         },
519
520         /**
521          * Returns the coverage report for the files that have been executed.
522          * This returns only coverage information for files that have been
523          * instrumented using YUI Test Coverage and only those that were run
524          * in the same pass.
525          * @param {Function} format (Optional) A coverage format to return results in.
526          * @return {Object|String} Either the coverage object or, if a coverage
527          *      format is specified, a string representing the results in that format.
528          * @method getCoverage
529          */
530         getCoverage: function(format){
531             if (!this._running && typeof _yuitest_coverage == "object"){
532                 if (YAHOO.lang.isFunction(format)){
533                     return format(_yuitest_coverage);                    
534                 } else {
535                     return _yuitest_coverage;
536                 }
537             } else {
538                 return null;
539             }            
540         },
541         
542         //-------------------------------------------------------------------------
543         // Misc Methods
544         //-------------------------------------------------------------------------
545
546         /**
547          * Retrieves the name of the current result set.
548          * @return {String} The name of the result set.
549          * @method getName
550          */
551         getName: function(){
552             return this.masterSuite.name;
553         },         
554
555         /**
556          * The name assigned to the master suite of the TestRunner. This is the name
557          * that is output as the root's name when results are retrieved.
558          * @param {String} name The name of the result set.
559          * @return {Void}
560          * @method setName
561          */
562         setName: function(name){
563             this.masterSuite.name = name;
564         },
565
566         //-------------------------------------------------------------------------
567         // Test Tree-Related Methods
568         //-------------------------------------------------------------------------
569
570         /**
571          * Adds a test case to the test tree as a child of the specified node.
572          * @param {TestNode} parentNode The node to add the test case to as a child.
573          * @param {YAHOO.tool.TestCase} testCase The test case to add.
574          * @return {Void}
575          * @static
576          * @private
577          * @method _addTestCaseToTestTree
578          */
579        _addTestCaseToTestTree : function (parentNode /*:TestNode*/, testCase /*:YAHOO.tool.TestCase*/) /*:Void*/{
580             
581             //add the test suite
582             var node = parentNode.appendChild(testCase);
583             
584             //iterate over the items in the test case
585             for (var prop in testCase){
586                 if (prop.indexOf("test") === 0 && YAHOO.lang.isFunction(testCase[prop])){
587                     node.appendChild(prop);
588                 }
589             }
590          
591         },
592         
593         /**
594          * Adds a test suite to the test tree as a child of the specified node.
595          * @param {TestNode} parentNode The node to add the test suite to as a child.
596          * @param {YAHOO.tool.TestSuite} testSuite The test suite to add.
597          * @return {Void}
598          * @static
599          * @private
600          * @method _addTestSuiteToTestTree
601          */
602         _addTestSuiteToTestTree : function (parentNode /*:TestNode*/, testSuite /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
603             
604             //add the test suite
605             var node = parentNode.appendChild(testSuite);
606             
607             //iterate over the items in the master suite
608             for (var i=0; i < testSuite.items.length; i++){
609                 if (testSuite.items[i] instanceof YAHOO.tool.TestSuite) {
610                     this._addTestSuiteToTestTree(node, testSuite.items[i]);
611                 } else if (testSuite.items[i] instanceof YAHOO.tool.TestCase) {
612                     this._addTestCaseToTestTree(node, testSuite.items[i]);
613                 }                   
614             }            
615         },
616         
617         /**
618          * Builds the test tree based on items in the master suite. The tree is a hierarchical
619          * representation of the test suites, test cases, and test functions. The resulting tree
620          * is stored in _root and the pointer _cur is set to the root initially.
621          * @return {Void}
622          * @static
623          * @private
624          * @method _buildTestTree
625          */
626         _buildTestTree : function () /*:Void*/ {
627         
628             this._root = new TestNode(this.masterSuite);
629             //this._cur = this._root;
630             
631             //iterate over the items in the master suite
632             for (var i=0; i < this.masterSuite.items.length; i++){
633                 if (this.masterSuite.items[i] instanceof YAHOO.tool.TestSuite) {
634                     this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
635                 } else if (this.masterSuite.items[i] instanceof YAHOO.tool.TestCase) {
636                     this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
637                 }                   
638             }            
639         
640         }, 
641     
642         //-------------------------------------------------------------------------
643         // Private Methods
644         //-------------------------------------------------------------------------
645         
646         /**
647          * Handles the completion of a test object's tests. Tallies test results 
648          * from one level up to the next.
649          * @param {TestNode} node The TestNode representing the test object.
650          * @return {Void}
651          * @method _handleTestObjectComplete
652          * @private
653          * @static
654          */
655         _handleTestObjectComplete : function (node /*:TestNode*/) /*:Void*/ {
656             if (YAHOO.lang.isObject(node.testObject)){
657                 node.parent.results.passed += node.results.passed;
658                 node.parent.results.failed += node.results.failed;
659                 node.parent.results.total += node.results.total;                
660                 node.parent.results.ignored += node.results.ignored;                
661                 node.parent.results[node.testObject.name] = node.results;
662             
663                 if (node.testObject instanceof YAHOO.tool.TestSuite){
664                     node.testObject.tearDown();
665                     node.results.duration = (new Date()) - node._start;
666                     this.fireEvent(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
667                 } else if (node.testObject instanceof YAHOO.tool.TestCase){
668                     node.results.duration = (new Date()) - node._start;
669                     this.fireEvent(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
670                 }      
671             } 
672         },                
673         
674         //-------------------------------------------------------------------------
675         // Navigation Methods
676         //-------------------------------------------------------------------------
677         
678         /**
679          * Retrieves the next node in the test tree.
680          * @return {TestNode} The next node in the test tree or null if the end is reached.
681          * @private
682          * @static
683          * @method _next
684          */
685         _next : function () /*:TestNode*/ {
686                 
687             if (this._cur === null){
688                 this._cur = this._root;
689             } else if (this._cur.firstChild) {
690                 this._cur = this._cur.firstChild;
691             } else if (this._cur.next) {
692                 this._cur = this._cur.next;            
693             } else {
694                 while (this._cur && !this._cur.next && this._cur !== this._root){
695                     this._handleTestObjectComplete(this._cur);
696                     this._cur = this._cur.parent;
697                 }
698                 
699                 if (this._cur == this._root){
700                     this._cur.results.type = "report";
701                     this._cur.results.timestamp = (new Date()).toLocaleString();
702                     this._cur.results.duration = (new Date()) - this._cur._start;                       
703                     this._lastResults = this._cur.results;
704                     this._running = false;
705                     this.fireEvent(this.COMPLETE_EVENT, { results: this._lastResults});
706                     this._cur = null;
707                 } else {
708                     this._handleTestObjectComplete(this._cur);               
709                     this._cur = this._cur.next;                
710                 }
711             }
712         
713             return this._cur;
714         },
715         
716         /**
717          * Runs a test case or test suite, returning the results.
718          * @param {YAHOO.tool.TestCase|YAHOO.tool.TestSuite} testObject The test case or test suite to run.
719          * @return {Object} Results of the execution with properties passed, failed, and total.
720          * @private
721          * @method _run
722          * @static
723          */
724         _run : function () /*:Void*/ {
725                                 
726             //flag to indicate if the TestRunner should wait before continuing
727             var shouldWait = false;
728             
729             //get the next test node
730             var node = this._next();
731
732             
733             if (node !== null) {
734             
735                 //set flag to say the testrunner is running
736                 this._running = true;
737                 
738                 //eliminate last results
739                 this._lastResult = null;            
740             
741                 var testObject = node.testObject;
742                 
743                 //figure out what to do
744                 if (YAHOO.lang.isObject(testObject)){
745                     if (testObject instanceof YAHOO.tool.TestSuite){
746                         this.fireEvent(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
747                         node._start = new Date();
748                         testObject.setUp();
749                     } else if (testObject instanceof YAHOO.tool.TestCase){
750                         this.fireEvent(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
751                         node._start = new Date();
752                     }
753                     
754                     //some environments don't support setTimeout
755                     if (typeof setTimeout != "undefined"){                    
756                         setTimeout(function(){
757                             YAHOO.tool.TestRunner._run();
758                         }, 0);
759                     } else {
760                         this._run();
761                     }
762                 } else {
763                     this._runTest(node);
764                 }
765
766             }
767         },
768         
769         _resumeTest : function (segment /*:Function*/) /*:Void*/ {
770         
771             //get relevant information
772             var node /*:TestNode*/ = this._cur;
773             var testName /*:String*/ = node.testObject;
774             var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
775             
776             //cancel other waits if available
777             if (testCase.__yui_wait){
778                 clearTimeout(testCase.__yui_wait);
779                 delete testCase.__yui_wait;
780             }            
781             
782             //get the "should" test cases
783             var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
784             var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
785             
786             //variable to hold whether or not the test failed
787             var failed /*:Boolean*/ = false;
788             var error /*:Error*/ = null;
789                 
790             //try the test
791             try {
792             
793                 //run the test
794                 segment.apply(testCase);
795                 
796                 //if it should fail, and it got here, then it's a fail because it didn't
797                 if (shouldFail){
798                     error = new YAHOO.util.ShouldFail();
799                     failed = true;
800                 } else if (shouldError){
801                     error = new YAHOO.util.ShouldError();
802                     failed = true;
803                 }
804                            
805             } catch (thrown /*:Error*/){
806                 if (thrown instanceof YAHOO.util.AssertionError) {
807                     if (!shouldFail){
808                         error = thrown;
809                         failed = true;
810                     }
811                 } else if (thrown instanceof YAHOO.tool.TestCase.Wait){
812                 
813                     if (YAHOO.lang.isFunction(thrown.segment)){
814                         if (YAHOO.lang.isNumber(thrown.delay)){
815                         
816                             //some environments don't support setTimeout
817                             if (typeof setTimeout != "undefined"){
818                                 testCase.__yui_wait = setTimeout(function(){
819                                     YAHOO.tool.TestRunner._resumeTest(thrown.segment);
820                                 }, thrown.delay);                             
821                             } else {
822                                 throw new Error("Asynchronous tests not supported in this environment.");
823                             }
824                         }
825                     }
826                     
827                     return;
828                 
829                 } else {
830                     //first check to see if it should error
831                     if (!shouldError) {                        
832                         error = new YAHOO.util.UnexpectedError(thrown);
833                         failed = true;
834                     } else {
835                         //check to see what type of data we have
836                         if (YAHOO.lang.isString(shouldError)){
837                             
838                             //if it's a string, check the error message
839                             if (thrown.message != shouldError){
840                                 error = new YAHOO.util.UnexpectedError(thrown);
841                                 failed = true;                                    
842                             }
843                         } else if (YAHOO.lang.isFunction(shouldError)){
844                         
845                             //if it's a function, see if the error is an instance of it
846                             if (!(thrown instanceof shouldError)){
847                                 error = new YAHOO.util.UnexpectedError(thrown);
848                                 failed = true;
849                             }
850                         
851                         } else if (YAHOO.lang.isObject(shouldError)){
852                         
853                             //if it's an object, check the instance and message
854                             if (!(thrown instanceof shouldError.constructor) || 
855                                     thrown.message != shouldError.message){
856                                 error = new YAHOO.util.UnexpectedError(thrown);
857                                 failed = true;                                    
858                             }
859                         
860                         }
861                     
862                     }
863                 }
864                 
865             }
866             
867             //fireEvent appropriate event
868             if (failed) {
869                 this.fireEvent(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
870             } else {
871                 this.fireEvent(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
872             }
873             
874             //run the tear down
875             testCase.tearDown();
876         
877             //calculate duration
878             var duration = (new Date()) - node._start;            
879             
880             //update results
881             node.parent.results[testName] = { 
882                 result: failed ? "fail" : "pass",
883                 message: error ? error.getMessage() : "Test passed",
884                 type: "test",
885                 name: testName,
886                 duration: duration
887             };
888             
889             if (failed){
890                 node.parent.results.failed++;
891             } else {
892                 node.parent.results.passed++;
893             }
894             node.parent.results.total++;
895
896             //set timeout not supported in all environments
897             if (typeof setTimeout != "undefined"){
898                 setTimeout(function(){
899                     YAHOO.tool.TestRunner._run();
900                 }, 0);
901             } else {
902                 this._run();
903             }
904         
905         },
906                 
907         /**
908          * Runs a single test based on the data provided in the node.
909          * @param {TestNode} node The TestNode representing the test to run.
910          * @return {Void}
911          * @static
912          * @private
913          * @name _runTest
914          */
915         _runTest : function (node /*:TestNode*/) /*:Void*/ {
916         
917             //get relevant information
918             var testName /*:String*/ = node.testObject;
919             var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
920             var test /*:Function*/ = testCase[testName];
921             
922             //get the "should" test cases
923             var shouldIgnore /*:Object*/ = (testCase._should.ignore || {})[testName];
924             
925             //figure out if the test should be ignored or not
926             if (shouldIgnore){
927             
928                 //update results
929                 node.parent.results[testName] = { 
930                     result: "ignore",
931                     message: "Test ignored",
932                     type: "test",
933                     name: testName
934                 };
935                 
936                 node.parent.results.ignored++;
937                 node.parent.results.total++;
938             
939                 this.fireEvent(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
940                 
941                 //some environments don't support setTimeout
942                 if (typeof setTimeout != "undefined"){                    
943                     setTimeout(function(){
944                         YAHOO.tool.TestRunner._run();
945                     }, 0);              
946                 } else {
947                     this._run();
948                 }
949
950             } else {
951             
952                 //mark the start time
953                 node._start = new Date();
954             
955                 //run the setup
956                 testCase.setUp();
957                 
958                 //now call the body of the test
959                 this._resumeTest(test);                
960             }
961
962         },        
963         
964         //-------------------------------------------------------------------------
965         // Protected Methods
966         //-------------------------------------------------------------------------   
967     
968         /**
969          * Fires events for the TestRunner. This overrides the default fireEvent()
970          * method from EventProvider to add the type property to the data that is
971          * passed through on each event call.
972          * @param {String} type The type of event to fire.
973          * @param {Object} data (Optional) Data for the event.
974          * @method fireEvent
975          * @static
976          * @protected
977          */
978         fireEvent : function (type /*:String*/, data /*:Object*/) /*:Void*/ {
979             data = data || {};
980             data.type = type;
981             TestRunner.superclass.fireEvent.call(this, type, data);
982         },
983
984         //-------------------------------------------------------------------------
985         // Public Methods
986         //-------------------------------------------------------------------------   
987     
988         /**
989          * Adds a test suite or test case to the list of test objects to run.
990          * @param testObject Either a TestCase or a TestSuite that should be run.
991          * @return {Void}
992          * @method add
993          * @static
994          */
995         add : function (testObject /*:Object*/) /*:Void*/ {
996             this.masterSuite.add(testObject);
997         },
998         
999         /**
1000          * Removes all test objects from the runner.
1001          * @return {Void}
1002          * @method clear
1003          * @static
1004          */
1005         clear : function () /*:Void*/ {
1006             this.masterSuite = new YAHOO.tool.TestSuite("yuitests" + (new Date()).getTime());
1007         },
1008         
1009         /**
1010          * Resumes the TestRunner after wait() was called.
1011          * @param {Function} segment The function to run as the rest
1012          *      of the haulted test.
1013          * @return {Void}
1014          * @method resume
1015          * @static
1016          */
1017         resume : function (segment /*:Function*/) /*:Void*/ {
1018             this._resumeTest(segment || function(){});
1019         },
1020     
1021         /**
1022          * Runs the test suite.
1023          * @param {Boolean} oldMode (Optional) Specifies that the <= 2.8 way of
1024          *      internally managing test suites should be used.
1025          * @return {Void}
1026          * @method run
1027          * @static
1028          */
1029         run : function (oldMode) {
1030             
1031             //pointer to runner to avoid scope issues 
1032             var runner = YAHOO.tool.TestRunner;
1033             
1034             //if there's only one suite on the masterSuite, move it up
1035             if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof YAHOO.tool.TestSuite){
1036                 this.masterSuite = this.masterSuite.items[0];
1037             }
1038
1039             //build the test tree
1040             runner._buildTestTree();
1041             
1042             //set when the test started
1043             runner._root._start = new Date();
1044                             
1045             //fire the begin event
1046             runner.fireEvent(runner.BEGIN_EVENT);
1047        
1048             //begin the testing
1049             runner._run();
1050         }    
1051     });
1052     
1053     return new TestRunner();
1054     
1055 })();
1056 YAHOO.namespace("util");
1057
1058 //-----------------------------------------------------------------------------
1059 // Assert object
1060 //-----------------------------------------------------------------------------
1061
1062 /**
1063  * The Assert object provides functions to test JavaScript values against
1064  * known and expected results. Whenever a comparison (assertion) fails,
1065  * an error is thrown.
1066  *
1067  * @namespace YAHOO.util
1068  * @class Assert
1069  * @static
1070  */
1071 YAHOO.util.Assert = {
1072
1073     //-------------------------------------------------------------------------
1074     // Helper Methods
1075     //-------------------------------------------------------------------------
1076     
1077     /**
1078      * Formats a message so that it can contain the original assertion message
1079      * in addition to the custom message.
1080      * @param {String} customMessage The message passed in by the developer.
1081      * @param {String} defaultMessage The message created by the error by default.
1082      * @return {String} The final error message, containing either or both.
1083      * @protected
1084      * @static
1085      * @method _formatMessage
1086      */
1087     _formatMessage : function (customMessage /*:String*/, defaultMessage /*:String*/) /*:String*/ {
1088         var message = customMessage;
1089         if (YAHOO.lang.isString(customMessage) && customMessage.length > 0){
1090             return YAHOO.lang.substitute(customMessage, { message: defaultMessage });
1091         } else {
1092             return defaultMessage;
1093         }        
1094     },
1095     
1096     //-------------------------------------------------------------------------
1097     // Generic Assertion Methods
1098     //-------------------------------------------------------------------------
1099     
1100     /** 
1101      * Forces an assertion error to occur.
1102      * @param {String} message (Optional) The message to display with the failure.
1103      * @method fail
1104      * @static
1105      */
1106     fail : function (message /*:String*/) /*:Void*/ {
1107         throw new YAHOO.util.AssertionError(this._formatMessage(message, "Test force-failed."));
1108     },       
1109     
1110     //-------------------------------------------------------------------------
1111     // Equality Assertion Methods
1112     //-------------------------------------------------------------------------    
1113     
1114     /**
1115      * Asserts that a value is equal to another. This uses the double equals sign
1116      * so type coercion may occur.
1117      * @param {Object} expected The expected value.
1118      * @param {Object} actual The actual value to test.
1119      * @param {String} message (Optional) The message to display if the assertion fails.
1120      * @method areEqual
1121      * @static
1122      */
1123     areEqual : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1124         if (expected != actual) {
1125             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be equal."), expected, actual);
1126         }
1127     },
1128     
1129     /**
1130      * Asserts that a value is not equal to another. This uses the double equals sign
1131      * so type coercion may occur.
1132      * @param {Object} unexpected The unexpected value.
1133      * @param {Object} actual The actual value to test.
1134      * @param {String} message (Optional) The message to display if the assertion fails.
1135      * @method areNotEqual
1136      * @static
1137      */
1138     areNotEqual : function (unexpected /*:Object*/, actual /*:Object*/, 
1139                          message /*:String*/) /*:Void*/ {
1140         if (unexpected == actual) {
1141             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be equal."), unexpected);
1142         }
1143     },
1144     
1145     /**
1146      * Asserts that a value is not the same as another. This uses the triple equals sign
1147      * so no type coercion may occur.
1148      * @param {Object} unexpected The unexpected value.
1149      * @param {Object} actual The actual value to test.
1150      * @param {String} message (Optional) The message to display if the assertion fails.
1151      * @method areNotSame
1152      * @static
1153      */
1154     areNotSame : function (unexpected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1155         if (unexpected === actual) {
1156             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be the same."), unexpected);
1157         }
1158     },
1159
1160     /**
1161      * Asserts that a value is the same as another. This uses the triple equals sign
1162      * so no type coercion may occur.
1163      * @param {Object} expected The expected value.
1164      * @param {Object} actual The actual value to test.
1165      * @param {String} message (Optional) The message to display if the assertion fails.
1166      * @method areSame
1167      * @static
1168      */
1169     areSame : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1170         if (expected !== actual) {
1171             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be the same."), expected, actual);
1172         }
1173     },    
1174     
1175     //-------------------------------------------------------------------------
1176     // Boolean Assertion Methods
1177     //-------------------------------------------------------------------------    
1178     
1179     /**
1180      * Asserts that a value is false. This uses the triple equals sign
1181      * so no type coercion may occur.
1182      * @param {Object} actual The actual value to test.
1183      * @param {String} message (Optional) The message to display if the assertion fails.
1184      * @method isFalse
1185      * @static
1186      */
1187     isFalse : function (actual /*:Boolean*/, message /*:String*/) {
1188         if (false !== actual) {
1189             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be false."), false, actual);
1190         }
1191     },
1192     
1193     /**
1194      * Asserts that a value is true. This uses the triple equals sign
1195      * so no type coercion may occur.
1196      * @param {Object} actual The actual value to test.
1197      * @param {String} message (Optional) The message to display if the assertion fails.
1198      * @method isTrue
1199      * @static
1200      */
1201     isTrue : function (actual /*:Boolean*/, message /*:String*/) /*:Void*/ {
1202         if (true !== actual) {
1203             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be true."), true, actual);
1204         }
1205
1206     },
1207     
1208     //-------------------------------------------------------------------------
1209     // Special Value Assertion Methods
1210     //-------------------------------------------------------------------------    
1211     
1212     /**
1213      * Asserts that a value is not a number.
1214      * @param {Object} actual The value to test.
1215      * @param {String} message (Optional) The message to display if the assertion fails.
1216      * @method isNaN
1217      * @static
1218      */
1219     isNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1220         if (!isNaN(actual)){
1221             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be NaN."), NaN, actual);
1222         }    
1223     },
1224     
1225     /**
1226      * Asserts that a value is not the special NaN value.
1227      * @param {Object} actual The value to test.
1228      * @param {String} message (Optional) The message to display if the assertion fails.
1229      * @method isNotNaN
1230      * @static
1231      */
1232     isNotNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1233         if (isNaN(actual)){
1234             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be NaN."), NaN);
1235         }    
1236     },
1237     
1238     /**
1239      * Asserts that a value is not null. This uses the triple equals sign
1240      * so no type coercion may occur.
1241      * @param {Object} actual The actual value to test.
1242      * @param {String} message (Optional) The message to display if the assertion fails.
1243      * @method isNotNull
1244      * @static
1245      */
1246     isNotNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1247         if (YAHOO.lang.isNull(actual)) {
1248             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be null."), null);
1249         }
1250     },
1251
1252     /**
1253      * Asserts that a value is not undefined. This uses the triple equals sign
1254      * so no type coercion may occur.
1255      * @param {Object} actual The actual value to test.
1256      * @param {String} message (Optional) The message to display if the assertion fails.
1257      * @method isNotUndefined
1258      * @static
1259      */
1260     isNotUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1261         if (YAHOO.lang.isUndefined(actual)) {
1262             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should not be undefined."), undefined);
1263         }
1264     },
1265
1266     /**
1267      * Asserts that a value is null. This uses the triple equals sign
1268      * so no type coercion may occur.
1269      * @param {Object} actual The actual value to test.
1270      * @param {String} message (Optional) The message to display if the assertion fails.
1271      * @method isNull
1272      * @static
1273      */
1274     isNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1275         if (!YAHOO.lang.isNull(actual)) {
1276             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be null."), null, actual);
1277         }
1278     },
1279         
1280     /**
1281      * Asserts that a value is undefined. This uses the triple equals sign
1282      * so no type coercion may occur.
1283      * @param {Object} actual The actual value to test.
1284      * @param {String} message (Optional) The message to display if the assertion fails.
1285      * @method isUndefined
1286      * @static
1287      */
1288     isUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1289         if (!YAHOO.lang.isUndefined(actual)) {
1290             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be undefined."), undefined, actual);
1291         }
1292     },    
1293     
1294     //--------------------------------------------------------------------------
1295     // Instance Assertion Methods
1296     //--------------------------------------------------------------------------    
1297    
1298     /**
1299      * Asserts that a value is an array.
1300      * @param {Object} actual The value to test.
1301      * @param {String} message (Optional) The message to display if the assertion fails.
1302      * @method isArray
1303      * @static
1304      */
1305     isArray : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1306         if (!YAHOO.lang.isArray(actual)){
1307             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an array."), actual);
1308         }    
1309     },
1310    
1311     /**
1312      * Asserts that a value is a Boolean.
1313      * @param {Object} actual The value to test.
1314      * @param {String} message (Optional) The message to display if the assertion fails.
1315      * @method isBoolean
1316      * @static
1317      */
1318     isBoolean : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1319         if (!YAHOO.lang.isBoolean(actual)){
1320             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a Boolean."), actual);
1321         }    
1322     },
1323    
1324     /**
1325      * Asserts that a value is a function.
1326      * @param {Object} actual The value to test.
1327      * @param {String} message (Optional) The message to display if the assertion fails.
1328      * @method isFunction
1329      * @static
1330      */
1331     isFunction : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1332         if (!YAHOO.lang.isFunction(actual)){
1333             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a function."), actual);
1334         }    
1335     },
1336    
1337     /**
1338      * Asserts that a value is an instance of a particular object. This may return
1339      * incorrect results when comparing objects from one frame to constructors in
1340      * another frame. For best results, don't use in a cross-frame manner.
1341      * @param {Function} expected The function that the object should be an instance of.
1342      * @param {Object} actual The object to test.
1343      * @param {String} message (Optional) The message to display if the assertion fails.
1344      * @method isInstanceOf
1345      * @static
1346      */
1347     isInstanceOf : function (expected /*:Function*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1348         if (!(actual instanceof expected)){
1349             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1350         }
1351     },
1352     
1353     /**
1354      * Asserts that a value is a number.
1355      * @param {Object} actual The value to test.
1356      * @param {String} message (Optional) The message to display if the assertion fails.
1357      * @method isNumber
1358      * @static
1359      */
1360     isNumber : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1361         if (!YAHOO.lang.isNumber(actual)){
1362             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a number."), actual);
1363         }    
1364     },    
1365     
1366     /**
1367      * Asserts that a value is an object.
1368      * @param {Object} actual The value to test.
1369      * @param {String} message (Optional) The message to display if the assertion fails.
1370      * @method isObject
1371      * @static
1372      */
1373     isObject : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1374         if (!YAHOO.lang.isObject(actual)){
1375             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an object."), actual);
1376         }
1377     },
1378     
1379     /**
1380      * Asserts that a value is a string.
1381      * @param {Object} actual The value to test.
1382      * @param {String} message (Optional) The message to display if the assertion fails.
1383      * @method isString
1384      * @static
1385      */
1386     isString : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1387         if (!YAHOO.lang.isString(actual)){
1388             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a string."), actual);
1389         }
1390     },
1391     
1392     /**
1393      * Asserts that a value is of a particular type. 
1394      * @param {String} expectedType The expected type of the variable.
1395      * @param {Object} actualValue The actual value to test.
1396      * @param {String} message (Optional) The message to display if the assertion fails.
1397      * @method isTypeOf
1398      * @static
1399      */
1400     isTypeOf : function (expected /*:String*/, actual /*:Object*/, message /*:String*/) /*:Void*/{
1401         if (typeof actual != expected){
1402             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be of type " + expected + "."), expected, typeof actual);
1403         }
1404     }
1405 };
1406
1407 //-----------------------------------------------------------------------------
1408 // Assertion errors
1409 //-----------------------------------------------------------------------------
1410
1411 /**
1412  * AssertionError is thrown whenever an assertion fails. It provides methods
1413  * to more easily get at error information and also provides a base class
1414  * from which more specific assertion errors can be derived.
1415  *
1416  * @param {String} message The message to display when the error occurs.
1417  * @namespace YAHOO.util
1418  * @class AssertionError
1419  * @extends Error
1420  * @constructor
1421  */ 
1422 YAHOO.util.AssertionError = function (message /*:String*/){
1423
1424     //call superclass
1425     //arguments.callee.superclass.constructor.call(this, message);
1426     
1427     /*
1428      * Error message. Must be duplicated to ensure browser receives it.
1429      * @type String
1430      * @property message
1431      */
1432     this.message /*:String*/ = message;
1433     
1434     /**
1435      * The name of the error that occurred.
1436      * @type String
1437      * @property name
1438      */
1439     this.name /*:String*/ = "AssertionError";
1440 };
1441
1442 //inherit methods
1443 YAHOO.lang.extend(YAHOO.util.AssertionError, Object, {
1444
1445     /**
1446      * Returns a fully formatted error for an assertion failure. This should
1447      * be overridden by all subclasses to provide specific information.
1448      * @method getMessage
1449      * @return {String} A string describing the error.
1450      */
1451     getMessage : function () /*:String*/ {
1452         return this.message;
1453     },
1454     
1455     /**
1456      * Returns a string representation of the error.
1457      * @method toString
1458      * @return {String} A string representation of the error.
1459      */
1460     toString : function () /*:String*/ {
1461         return this.name + ": " + this.getMessage();
1462     }
1463     
1464 });
1465
1466 /**
1467  * ComparisonFailure is subclass of AssertionError that is thrown whenever
1468  * a comparison between two values fails. It provides mechanisms to retrieve
1469  * both the expected and actual value.
1470  *
1471  * @param {String} message The message to display when the error occurs.
1472  * @param {Object} expected The expected value.
1473  * @param {Object} actual The actual value that caused the assertion to fail.
1474  * @namespace YAHOO.util
1475  * @extends YAHOO.util.AssertionError
1476  * @class ComparisonFailure
1477  * @constructor
1478  */ 
1479 YAHOO.util.ComparisonFailure = function (message /*:String*/, expected /*:Object*/, actual /*:Object*/){
1480
1481     //call superclass
1482     YAHOO.util.AssertionError.call(this, message);
1483     
1484     /**
1485      * The expected value.
1486      * @type Object
1487      * @property expected
1488      */
1489     this.expected /*:Object*/ = expected;
1490     
1491     /**
1492      * The actual value.
1493      * @type Object
1494      * @property actual
1495      */
1496     this.actual /*:Object*/ = actual;
1497     
1498     /**
1499      * The name of the error that occurred.
1500      * @type String
1501      * @property name
1502      */
1503     this.name /*:String*/ = "ComparisonFailure";
1504     
1505 };
1506
1507 //inherit methods
1508 YAHOO.lang.extend(YAHOO.util.ComparisonFailure, YAHOO.util.AssertionError, {
1509
1510     /**
1511      * Returns a fully formatted error for an assertion failure. This message
1512      * provides information about the expected and actual values.
1513      * @method toString
1514      * @return {String} A string describing the error.
1515      */
1516     getMessage : function () /*:String*/ {
1517         return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")"  +
1518             "\nActual:" + this.actual + " (" + (typeof this.actual) + ")";
1519     }
1520
1521 });
1522
1523 /**
1524  * UnexpectedValue is subclass of AssertionError that is thrown whenever
1525  * a value was unexpected in its scope. This typically means that a test
1526  * was performed to determine that a value was *not* equal to a certain
1527  * value.
1528  *
1529  * @param {String} message The message to display when the error occurs.
1530  * @param {Object} unexpected The unexpected value.
1531  * @namespace YAHOO.util
1532  * @extends YAHOO.util.AssertionError
1533  * @class UnexpectedValue
1534  * @constructor
1535  */ 
1536 YAHOO.util.UnexpectedValue = function (message /*:String*/, unexpected /*:Object*/){
1537
1538     //call superclass
1539     YAHOO.util.AssertionError.call(this, message);
1540     
1541     /**
1542      * The unexpected value.
1543      * @type Object
1544      * @property unexpected
1545      */
1546     this.unexpected /*:Object*/ = unexpected;
1547     
1548     /**
1549      * The name of the error that occurred.
1550      * @type String
1551      * @property name
1552      */
1553     this.name /*:String*/ = "UnexpectedValue";
1554     
1555 };
1556
1557 //inherit methods
1558 YAHOO.lang.extend(YAHOO.util.UnexpectedValue, YAHOO.util.AssertionError, {
1559
1560     /**
1561      * Returns a fully formatted error for an assertion failure. The message
1562      * contains information about the unexpected value that was encountered.
1563      * @method getMessage
1564      * @return {String} A string describing the error.
1565      */
1566     getMessage : function () /*:String*/ {
1567         return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1568     }
1569
1570 });
1571
1572 /**
1573  * ShouldFail is subclass of AssertionError that is thrown whenever
1574  * a test was expected to fail but did not.
1575  *
1576  * @param {String} message The message to display when the error occurs.
1577  * @namespace YAHOO.util
1578  * @extends YAHOO.util.AssertionError
1579  * @class ShouldFail
1580  * @constructor
1581  */  
1582 YAHOO.util.ShouldFail = function (message /*:String*/){
1583
1584     //call superclass
1585     YAHOO.util.AssertionError.call(this, message || "This test should fail but didn't.");
1586     
1587     /**
1588      * The name of the error that occurred.
1589      * @type String
1590      * @property name
1591      */
1592     this.name /*:String*/ = "ShouldFail";
1593     
1594 };
1595
1596 //inherit methods
1597 YAHOO.lang.extend(YAHOO.util.ShouldFail, YAHOO.util.AssertionError);
1598
1599 /**
1600  * ShouldError is subclass of AssertionError that is thrown whenever
1601  * a test is expected to throw an error but doesn't.
1602  *
1603  * @param {String} message The message to display when the error occurs.
1604  * @namespace YAHOO.util
1605  * @extends YAHOO.util.AssertionError
1606  * @class ShouldError
1607  * @constructor
1608  */  
1609 YAHOO.util.ShouldError = function (message /*:String*/){
1610
1611     //call superclass
1612     YAHOO.util.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
1613     
1614     /**
1615      * The name of the error that occurred.
1616      * @type String
1617      * @property name
1618      */
1619     this.name /*:String*/ = "ShouldError";
1620     
1621 };
1622
1623 //inherit methods
1624 YAHOO.lang.extend(YAHOO.util.ShouldError, YAHOO.util.AssertionError);
1625
1626 /**
1627  * UnexpectedError is subclass of AssertionError that is thrown whenever
1628  * an error occurs within the course of a test and the test was not expected
1629  * to throw an error.
1630  *
1631  * @param {Error} cause The unexpected error that caused this error to be 
1632  *                      thrown.
1633  * @namespace YAHOO.util
1634  * @extends YAHOO.util.AssertionError
1635  * @class UnexpectedError
1636  * @constructor
1637  */  
1638 YAHOO.util.UnexpectedError = function (cause /*:Object*/){
1639
1640     //call superclass
1641     YAHOO.util.AssertionError.call(this, "Unexpected error: " + cause.message);
1642     
1643     /**
1644      * The unexpected error that occurred.
1645      * @type Error
1646      * @property cause
1647      */
1648     this.cause /*:Error*/ = cause;
1649     
1650     /**
1651      * The name of the error that occurred.
1652      * @type String
1653      * @property name
1654      */
1655     this.name /*:String*/ = "UnexpectedError";
1656     
1657     /**
1658      * Stack information for the error (if provided).
1659      * @type String
1660      * @property stack
1661      */
1662     this.stack /*:String*/ = cause.stack;
1663     
1664 };
1665
1666 //inherit methods
1667 YAHOO.lang.extend(YAHOO.util.UnexpectedError, YAHOO.util.AssertionError);
1668 //-----------------------------------------------------------------------------
1669 // ArrayAssert object
1670 //-----------------------------------------------------------------------------
1671
1672 /**
1673  * The ArrayAssert object provides functions to test JavaScript array objects
1674  * for a variety of cases.
1675  *
1676  * @namespace YAHOO.util
1677  * @class ArrayAssert
1678  * @static
1679  */
1680  
1681 YAHOO.util.ArrayAssert = {
1682
1683     /**
1684      * Asserts that a value is present in an array. This uses the triple equals 
1685      * sign so no type coercion may occur.
1686      * @param {Object} needle The value that is expected in the array.
1687      * @param {Array} haystack An array of values.
1688      * @param {String} message (Optional) The message to display if the assertion fails.
1689      * @method contains
1690      * @static
1691      */
1692     contains : function (needle /*:Object*/, haystack /*:Array*/, 
1693                            message /*:String*/) /*:Void*/ {
1694         
1695         var found /*:Boolean*/ = false;
1696         var Assert = YAHOO.util.Assert;
1697         
1698         //begin checking values
1699         for (var i=0; i < haystack.length && !found; i++){
1700             if (haystack[i] === needle) {
1701                 found = true;
1702             }
1703         }
1704         
1705         if (!found){
1706             Assert.fail(Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1707         }
1708     },
1709
1710     /**
1711      * Asserts that a set of values are present in an array. This uses the triple equals 
1712      * sign so no type coercion may occur. For this assertion to pass, all values must
1713      * be found.
1714      * @param {Object[]} needles An array of values that are expected in the array.
1715      * @param {Array} haystack An array of values to check.
1716      * @param {String} message (Optional) The message to display if the assertion fails.
1717      * @method containsItems
1718      * @static
1719      */
1720     containsItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1721                            message /*:String*/) /*:Void*/ {
1722
1723         //begin checking values
1724         for (var i=0; i < needles.length; i++){
1725             this.contains(needles[i], haystack, message);
1726         }
1727     },
1728
1729     /**
1730      * Asserts that a value matching some condition is present in an array. This uses
1731      * a function to determine a match.
1732      * @param {Function} matcher A function that returns true if the items matches or false if not.
1733      * @param {Array} haystack An array of values.
1734      * @param {String} message (Optional) The message to display if the assertion fails.
1735      * @method containsMatch
1736      * @static
1737      */
1738     containsMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1739                            message /*:String*/) /*:Void*/ {
1740         
1741         //check for valid matcher
1742         if (typeof matcher != "function"){
1743             throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1744         }
1745         
1746         var found /*:Boolean*/ = false;
1747         var Assert = YAHOO.util.Assert;
1748         
1749         //begin checking values
1750         for (var i=0; i < haystack.length && !found; i++){
1751             if (matcher(haystack[i])) {
1752                 found = true;
1753             }
1754         }
1755         
1756         if (!found){
1757             Assert.fail(Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1758         }
1759     },
1760
1761     /**
1762      * Asserts that a value is not present in an array. This uses the triple equals 
1763      * sign so no type coercion may occur.
1764      * @param {Object} needle The value that is expected in the array.
1765      * @param {Array} haystack An array of values.
1766      * @param {String} message (Optional) The message to display if the assertion fails.
1767      * @method doesNotContain
1768      * @static
1769      */
1770     doesNotContain : function (needle /*:Object*/, haystack /*:Array*/, 
1771                            message /*:String*/) /*:Void*/ {
1772         
1773         var found /*:Boolean*/ = false;
1774         var Assert = YAHOO.util.Assert;
1775         
1776         //begin checking values
1777         for (var i=0; i < haystack.length && !found; i++){
1778             if (haystack[i] === needle) {
1779                 found = true;
1780             }
1781         }
1782         
1783         if (found){
1784             Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1785         }
1786     },
1787
1788     /**
1789      * Asserts that a set of values are not present in an array. This uses the triple equals 
1790      * sign so no type coercion may occur. For this assertion to pass, all values must
1791      * not be found.
1792      * @param {Object[]} needles An array of values that are not expected in the array.
1793      * @param {Array} haystack An array of values to check.
1794      * @param {String} message (Optional) The message to display if the assertion fails.
1795      * @method doesNotContainItems
1796      * @static
1797      */
1798     doesNotContainItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1799                            message /*:String*/) /*:Void*/ {
1800
1801         for (var i=0; i < needles.length; i++){
1802             this.doesNotContain(needles[i], haystack, message);
1803         }
1804
1805     },
1806         
1807     /**
1808      * Asserts that no values matching a condition are present in an array. This uses
1809      * a function to determine a match.
1810      * @param {Function} matcher A function that returns true if the items matches or false if not.
1811      * @param {Array} haystack An array of values.
1812      * @param {String} message (Optional) The message to display if the assertion fails.
1813      * @method doesNotContainMatch
1814      * @static
1815      */
1816     doesNotContainMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1817                            message /*:String*/) /*:Void*/ {
1818         
1819         //check for valid matcher
1820         if (typeof matcher != "function"){
1821             throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1822         }
1823
1824         var found /*:Boolean*/ = false;
1825         var Assert = YAHOO.util.Assert;
1826         
1827         //begin checking values
1828         for (var i=0; i < haystack.length && !found; i++){
1829             if (matcher(haystack[i])) {
1830                 found = true;
1831             }
1832         }
1833         
1834         if (found){
1835             Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1836         }
1837     },
1838         
1839     /**
1840      * Asserts that the given value is contained in an array at the specified index.
1841      * This uses the triple equals sign so no type coercion will occur.
1842      * @param {Object} needle The value to look for.
1843      * @param {Array} haystack The array to search in.
1844      * @param {int} index The index at which the value should exist.
1845      * @param {String} message (Optional) The message to display if the assertion fails.
1846      * @method indexOf
1847      * @static
1848      */
1849     indexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1850     
1851         //try to find the value in the array
1852         for (var i=0; i < haystack.length; i++){
1853             if (haystack[i] === needle){
1854                 YAHOO.util.Assert.areEqual(index, i, message || "Value exists at index " + i + " but should be at index " + index + ".");
1855                 return;
1856             }
1857         }
1858         
1859         var Assert = YAHOO.util.Assert;
1860         
1861         //if it makes it here, it wasn't found at all
1862         Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1863     },
1864         
1865     /**
1866      * Asserts that the values in an array are equal, and in the same position,
1867      * as values in another array. This uses the double equals sign
1868      * so type coercion may occur. Note that the array objects themselves
1869      * need not be the same for this test to pass.
1870      * @param {Array} expected An array of the expected values.
1871      * @param {Array} actual Any array of the actual values.
1872      * @param {String} message (Optional) The message to display if the assertion fails.
1873      * @method itemsAreEqual
1874      * @static
1875      */
1876     itemsAreEqual : function (expected /*:Array*/, actual /*:Array*/, 
1877                            message /*:String*/) /*:Void*/ {
1878         
1879         //one may be longer than the other, so get the maximum length
1880         var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1881         var Assert = YAHOO.util.Assert;
1882        
1883         //begin checking values
1884         for (var i=0; i < len; i++){
1885             Assert.areEqual(expected[i], actual[i], 
1886                 Assert._formatMessage(message, "Values in position " + i + " are not equal."));
1887         }
1888     },
1889     
1890     /**
1891      * Asserts that the values in an array are equivalent, and in the same position,
1892      * as values in another array. This uses a function to determine if the values
1893      * are equivalent. Note that the array objects themselves
1894      * need not be the same for this test to pass.
1895      * @param {Array} expected An array of the expected values.
1896      * @param {Array} actual Any array of the actual values.
1897      * @param {Function} comparator A function that returns true if the values are equivalent
1898      *      or false if not.
1899      * @param {String} message (Optional) The message to display if the assertion fails.
1900      * @return {Void}
1901      * @method itemsAreEquivalent
1902      * @static
1903      */
1904     itemsAreEquivalent : function (expected /*:Array*/, actual /*:Array*/, 
1905                            comparator /*:Function*/, message /*:String*/) /*:Void*/ {
1906         
1907         //make sure the comparator is valid
1908         if (typeof comparator != "function"){
1909             throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1910         }
1911         
1912         //one may be longer than the other, so get the maximum length
1913         var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1914         
1915         //begin checking values
1916         for (var i=0; i < len; i++){
1917             if (!comparator(expected[i], actual[i])){
1918                 throw new YAHOO.util.ComparisonFailure(YAHOO.util.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1919             }
1920         }
1921     },
1922     
1923     /**
1924      * Asserts that an array is empty.
1925      * @param {Array} actual The array to test.
1926      * @param {String} message (Optional) The message to display if the assertion fails.
1927      * @method isEmpty
1928      * @static
1929      */
1930     isEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1931         if (actual.length > 0){
1932             var Assert = YAHOO.util.Assert;
1933             Assert.fail(Assert._formatMessage(message, "Array should be empty."));
1934         }
1935     },    
1936     
1937     /**
1938      * Asserts that an array is not empty.
1939      * @param {Array} actual The array to test.
1940      * @param {String} message (Optional) The message to display if the assertion fails.
1941      * @method isNotEmpty
1942      * @static
1943      */
1944     isNotEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1945         if (actual.length === 0){
1946             var Assert = YAHOO.util.Assert;
1947             Assert.fail(Assert._formatMessage(message, "Array should not be empty."));
1948         }
1949     },    
1950     
1951     /**
1952      * Asserts that the values in an array are the same, and in the same position,
1953      * as values in another array. This uses the triple equals sign
1954      * so no type coercion will occur. Note that the array objects themselves
1955      * need not be the same for this test to pass.
1956      * @param {Array} expected An array of the expected values.
1957      * @param {Array} actual Any array of the actual values.
1958      * @param {String} message (Optional) The message to display if the assertion fails.
1959      * @method itemsAreSame
1960      * @static
1961      */
1962     itemsAreSame : function (expected /*:Array*/, actual /*:Array*/, 
1963                           message /*:String*/) /*:Void*/ {
1964         
1965         //one may be longer than the other, so get the maximum length
1966         var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1967         var Assert = YAHOO.util.Assert;
1968         
1969         //begin checking values
1970         for (var i=0; i < len; i++){
1971             Assert.areSame(expected[i], actual[i], 
1972                 Assert._formatMessage(message, "Values in position " + i + " are not the same."));
1973         }
1974     },
1975     
1976     /**
1977      * Asserts that the given value is contained in an array at the specified index,
1978      * starting from the back of the array.
1979      * This uses the triple equals sign so no type coercion will occur.
1980      * @param {Object} needle The value to look for.
1981      * @param {Array} haystack The array to search in.
1982      * @param {int} index The index at which the value should exist.
1983      * @param {String} message (Optional) The message to display if the assertion fails.
1984      * @method lastIndexOf
1985      * @static
1986      */
1987     lastIndexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1988     
1989         var Assert = YAHOO.util.Assert;
1990     
1991         //try to find the value in the array
1992         for (var i=haystack.length; i >= 0; i--){
1993             if (haystack[i] === needle){
1994                 Assert.areEqual(index, i, Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1995                 return;
1996             }
1997         }
1998         
1999         //if it makes it here, it wasn't found at all
2000         Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array."));        
2001     }
2002     
2003 };
2004 YAHOO.namespace("util");
2005
2006
2007 //-----------------------------------------------------------------------------
2008 // ObjectAssert object
2009 //-----------------------------------------------------------------------------
2010
2011 /**
2012  * The ObjectAssert object provides functions to test JavaScript objects
2013  * for a variety of cases.
2014  *
2015  * @namespace YAHOO.util
2016  * @class ObjectAssert
2017  * @static
2018  */
2019 YAHOO.util.ObjectAssert = {
2020         
2021     /**
2022      * Asserts that all properties in the object exist in another object.
2023      * @param {Object} expected An object with the expected properties.
2024      * @param {Object} actual An object with the actual properties.
2025      * @param {String} message (Optional) The message to display if the assertion fails.
2026      * @method propertiesAreEqual
2027      * @static
2028      */
2029     propertiesAreEqual : function (expected /*:Object*/, actual /*:Object*/, 
2030                            message /*:String*/) /*:Void*/ {
2031         
2032         var Assert = YAHOO.util.Assert;
2033         
2034         //get all properties in the object
2035         var properties /*:Array*/ = [];        
2036         for (var property in expected){
2037             properties.push(property);
2038         }
2039         
2040         //see if the properties are in the expected object
2041         for (var i=0; i < properties.length; i++){
2042             Assert.isNotUndefined(actual[properties[i]], 
2043                 Assert._formatMessage(message, "Property '" + properties[i] + "' expected."));
2044         }
2045
2046     },
2047     
2048     /**
2049      * Asserts that an object has a property with the given name.
2050      * @param {String} propertyName The name of the property to test.
2051      * @param {Object} object The object to search.
2052      * @param {String} message (Optional) The message to display if the assertion fails.
2053      * @method hasProperty
2054      * @static
2055      */    
2056     hasProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
2057         if (!(propertyName in object)){
2058             var Assert = YAHOO.util.Assert;
2059             Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
2060         }    
2061     },
2062     
2063     /**
2064      * Asserts that a property with the given name exists on an object instance (not on its prototype).
2065      * @param {String} propertyName The name of the property to test.
2066      * @param {Object} object The object to search.
2067      * @param {String} message (Optional) The message to display if the assertion fails.
2068      * @method hasProperty
2069      * @static
2070      */    
2071     hasOwnProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
2072         if (!YAHOO.lang.hasOwnProperty(object, propertyName)){
2073             var Assert = YAHOO.util.Assert;
2074             Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
2075         }     
2076     }
2077 };
2078 //-----------------------------------------------------------------------------
2079 // DateAssert object
2080 //-----------------------------------------------------------------------------
2081
2082 /**
2083  * The DateAssert object provides functions to test JavaScript Date objects
2084  * for a variety of cases.
2085  *
2086  * @namespace YAHOO.util
2087  * @class DateAssert
2088  * @static
2089  */
2090  
2091 YAHOO.util.DateAssert = {
2092
2093     /**
2094      * Asserts that a date's month, day, and year are equal to another date's.
2095      * @param {Date} expected The expected date.
2096      * @param {Date} actual The actual date to test.
2097      * @param {String} message (Optional) The message to display if the assertion fails.
2098      * @method datesAreEqual
2099      * @static
2100      */
2101     datesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
2102         if (expected instanceof Date && actual instanceof Date){
2103             var Assert = YAHOO.util.Assert;
2104             Assert.areEqual(expected.getFullYear(), actual.getFullYear(), Assert._formatMessage(message, "Years should be equal."));
2105             Assert.areEqual(expected.getMonth(), actual.getMonth(), Assert._formatMessage(message, "Months should be equal."));
2106             Assert.areEqual(expected.getDate(), actual.getDate(), Assert._formatMessage(message, "Day of month should be equal."));
2107         } else {
2108             throw new TypeError("DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
2109         }
2110     },
2111
2112     /**
2113      * Asserts that a date's hour, minutes, and seconds are equal to another date's.
2114      * @param {Date} expected The expected date.
2115      * @param {Date} actual The actual date to test.
2116      * @param {String} message (Optional) The message to display if the assertion fails.
2117      * @method timesAreEqual
2118      * @static
2119      */
2120     timesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
2121         if (expected instanceof Date && actual instanceof Date){
2122             var Assert = YAHOO.util.Assert;
2123             Assert.areEqual(expected.getHours(), actual.getHours(), Assert._formatMessage(message, "Hours should be equal."));
2124             Assert.areEqual(expected.getMinutes(), actual.getMinutes(), Assert._formatMessage(message, "Minutes should be equal."));
2125             Assert.areEqual(expected.getSeconds(), actual.getSeconds(), Assert._formatMessage(message, "Seconds should be equal."));
2126         } else {
2127             throw new TypeError("DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
2128         }
2129     }
2130     
2131 };
2132 YAHOO.register("yuitest_core", YAHOO.tool.TestRunner, {version: "2.9.0", build: "2800"});