]> CyberLeo.Net >> Repos - Github/sugarcrm.git/blob - include/javascript/yui/build/yuitest/yuitest.js
Release 6.2.0beta4
[Github/sugarcrm.git] / include / javascript / yui / build / yuitest / yuitest.js
1 /*
2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
5 version: 2.8.0r4
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-simulte
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         };
287         
288         //initialize results
289         if (testObject instanceof YAHOO.tool.TestSuite){
290             this.results.type = "testsuite";
291             this.results.name = testObject.name;
292         } else if (testObject instanceof YAHOO.tool.TestCase){
293             this.results.type = "testcase";
294             this.results.name = testObject.name;
295         }
296        
297     }
298     
299     TestNode.prototype = {
300     
301         /**
302          * Appends a new test object (TestSuite, TestCase, or test function name) as a child
303          * of this node.
304          * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
305          * @return {Void}
306          */
307         appendChild : function (testObject /*:Variant*/) /*:Void*/{
308             var node = new TestNode(testObject);
309             if (this.firstChild === null){
310                 this.firstChild = this.lastChild = node;
311             } else {
312                 this.lastChild.next = node;
313                 this.lastChild = node;
314             }
315             node.parent = this;
316             return node;
317         }       
318     };
319
320     /**
321      * Runs test suites and test cases, providing events to allowing for the
322      * interpretation of test results.
323      * @namespace YAHOO.tool
324      * @class TestRunner
325      * @static
326      */
327     function TestRunner(){
328     
329         //inherit from EventProvider
330         TestRunner.superclass.constructor.apply(this,arguments);
331         
332         /**
333          * Suite on which to attach all TestSuites and TestCases to be run.
334          * @type YAHOO.tool.TestSuite
335          * @property masterSuite
336          * @private
337          * @static
338          */
339         this.masterSuite /*:YAHOO.tool.TestSuite*/ = new YAHOO.tool.TestSuite("YUI Test Results");        
340
341         /**
342          * Pointer to the current node in the test tree.
343          * @type TestNode
344          * @private
345          * @property _cur
346          * @static
347          */
348         this._cur = null;
349         
350         /**
351          * Pointer to the root node in the test tree.
352          * @type TestNode
353          * @private
354          * @property _root
355          * @static
356          */
357         this._root = null;
358         
359         //create events
360         var events /*:Array*/ = [
361             this.TEST_CASE_BEGIN_EVENT,
362             this.TEST_CASE_COMPLETE_EVENT,
363             this.TEST_SUITE_BEGIN_EVENT,
364             this.TEST_SUITE_COMPLETE_EVENT,
365             this.TEST_PASS_EVENT,
366             this.TEST_FAIL_EVENT,
367             this.TEST_IGNORE_EVENT,
368             this.COMPLETE_EVENT,
369             this.BEGIN_EVENT
370         ];
371         for (var i=0; i < events.length; i++){
372             this.createEvent(events[i], { scope: this });
373         }       
374    
375     }
376     
377     YAHOO.lang.extend(TestRunner, YAHOO.util.EventProvider, {
378     
379         //-------------------------------------------------------------------------
380         // Constants
381         //-------------------------------------------------------------------------
382          
383         /**
384          * Fires when a test case is opened but before the first 
385          * test is executed.
386          * @event testcasebegin
387          */         
388         TEST_CASE_BEGIN_EVENT /*:String*/ : "testcasebegin",
389         
390         /**
391          * Fires when all tests in a test case have been executed.
392          * @event testcasecomplete
393          */        
394         TEST_CASE_COMPLETE_EVENT /*:String*/ : "testcasecomplete",
395         
396         /**
397          * Fires when a test suite is opened but before the first 
398          * test is executed.
399          * @event testsuitebegin
400          */        
401         TEST_SUITE_BEGIN_EVENT /*:String*/ : "testsuitebegin",
402         
403         /**
404          * Fires when all test cases in a test suite have been
405          * completed.
406          * @event testsuitecomplete
407          */        
408         TEST_SUITE_COMPLETE_EVENT /*:String*/ : "testsuitecomplete",
409         
410         /**
411          * Fires when a test has passed.
412          * @event pass
413          */        
414         TEST_PASS_EVENT /*:String*/ : "pass",
415         
416         /**
417          * Fires when a test has failed.
418          * @event fail
419          */        
420         TEST_FAIL_EVENT /*:String*/ : "fail",
421         
422         /**
423          * Fires when a test has been ignored.
424          * @event ignore
425          */        
426         TEST_IGNORE_EVENT /*:String*/ : "ignore",
427         
428         /**
429          * Fires when all test suites and test cases have been completed.
430          * @event complete
431          */        
432         COMPLETE_EVENT /*:String*/ : "complete",
433         
434         /**
435          * Fires when the run() method is called.
436          * @event begin
437          */        
438         BEGIN_EVENT /*:String*/ : "begin",    
439         
440         //-------------------------------------------------------------------------
441         // Test Tree-Related Methods
442         //-------------------------------------------------------------------------
443
444         /**
445          * Adds a test case to the test tree as a child of the specified node.
446          * @param {TestNode} parentNode The node to add the test case to as a child.
447          * @param {YAHOO.tool.TestCase} testCase The test case to add.
448          * @return {Void}
449          * @static
450          * @private
451          * @method _addTestCaseToTestTree
452          */
453        _addTestCaseToTestTree : function (parentNode /*:TestNode*/, testCase /*:YAHOO.tool.TestCase*/) /*:Void*/{
454             
455             //add the test suite
456             var node = parentNode.appendChild(testCase);
457             
458             //iterate over the items in the test case
459             for (var prop in testCase){
460                 if (prop.indexOf("test") === 0 && YAHOO.lang.isFunction(testCase[prop])){
461                     node.appendChild(prop);
462                 }
463             }
464          
465         },
466         
467         /**
468          * Adds a test suite to the test tree as a child of the specified node.
469          * @param {TestNode} parentNode The node to add the test suite to as a child.
470          * @param {YAHOO.tool.TestSuite} testSuite The test suite to add.
471          * @return {Void}
472          * @static
473          * @private
474          * @method _addTestSuiteToTestTree
475          */
476         _addTestSuiteToTestTree : function (parentNode /*:TestNode*/, testSuite /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
477             
478             //add the test suite
479             var node = parentNode.appendChild(testSuite);
480             
481             //iterate over the items in the master suite
482             for (var i=0; i < testSuite.items.length; i++){
483                 if (testSuite.items[i] instanceof YAHOO.tool.TestSuite) {
484                     this._addTestSuiteToTestTree(node, testSuite.items[i]);
485                 } else if (testSuite.items[i] instanceof YAHOO.tool.TestCase) {
486                     this._addTestCaseToTestTree(node, testSuite.items[i]);
487                 }                   
488             }            
489         },
490         
491         /**
492          * Builds the test tree based on items in the master suite. The tree is a hierarchical
493          * representation of the test suites, test cases, and test functions. The resulting tree
494          * is stored in _root and the pointer _cur is set to the root initially.
495          * @return {Void}
496          * @static
497          * @private
498          * @method _buildTestTree
499          */
500         _buildTestTree : function () /*:Void*/ {
501         
502             this._root = new TestNode(this.masterSuite);
503             this._cur = this._root;
504             
505             //iterate over the items in the master suite
506             for (var i=0; i < this.masterSuite.items.length; i++){
507                 if (this.masterSuite.items[i] instanceof YAHOO.tool.TestSuite) {
508                     this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
509                 } else if (this.masterSuite.items[i] instanceof YAHOO.tool.TestCase) {
510                     this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
511                 }                   
512             }            
513         
514         }, 
515     
516         //-------------------------------------------------------------------------
517         // Private Methods
518         //-------------------------------------------------------------------------
519         
520         /**
521          * Handles the completion of a test object's tests. Tallies test results 
522          * from one level up to the next.
523          * @param {TestNode} node The TestNode representing the test object.
524          * @return {Void}
525          * @method _handleTestObjectComplete
526          * @private
527          * @static
528          */
529         _handleTestObjectComplete : function (node /*:TestNode*/) /*:Void*/ {
530             if (YAHOO.lang.isObject(node.testObject)){
531                 node.parent.results.passed += node.results.passed;
532                 node.parent.results.failed += node.results.failed;
533                 node.parent.results.total += node.results.total;                
534                 node.parent.results.ignored += node.results.ignored;                
535                 node.parent.results[node.testObject.name] = node.results;
536             
537                 if (node.testObject instanceof YAHOO.tool.TestSuite){
538                     node.testObject.tearDown();
539                     this.fireEvent(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
540                 } else if (node.testObject instanceof YAHOO.tool.TestCase){
541                     this.fireEvent(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
542                 }      
543             } 
544         },                
545         
546         //-------------------------------------------------------------------------
547         // Navigation Methods
548         //-------------------------------------------------------------------------
549         
550         /**
551          * Retrieves the next node in the test tree.
552          * @return {TestNode} The next node in the test tree or null if the end is reached.
553          * @private
554          * @static
555          * @method _next
556          */
557         _next : function () /*:TestNode*/ {
558         
559             if (this._cur.firstChild) {
560                 this._cur = this._cur.firstChild;
561             } else if (this._cur.next) {
562                 this._cur = this._cur.next;            
563             } else {
564                 while (this._cur && !this._cur.next && this._cur !== this._root){
565                     this._handleTestObjectComplete(this._cur);
566                     this._cur = this._cur.parent;
567                 }
568                 
569                 if (this._cur == this._root){
570                     this._cur.results.type = "report";
571                     this._cur.results.timestamp = (new Date()).toLocaleString();
572                     this._cur.results.duration = (new Date()) - this._cur.results.duration;
573                     this.fireEvent(this.COMPLETE_EVENT, { results: this._cur.results});
574                     this._cur = null;
575                 } else {
576                     this._handleTestObjectComplete(this._cur);               
577                     this._cur = this._cur.next;                
578                 }
579             }
580         
581             return this._cur;
582         },
583         
584         /**
585          * Runs a test case or test suite, returning the results.
586          * @param {YAHOO.tool.TestCase|YAHOO.tool.TestSuite} testObject The test case or test suite to run.
587          * @return {Object} Results of the execution with properties passed, failed, and total.
588          * @private
589          * @method _run
590          * @static
591          */
592         _run : function () /*:Void*/ {
593         
594             //flag to indicate if the TestRunner should wait before continuing
595             var shouldWait /*:Boolean*/ = false;
596             
597             //get the next test node
598             var node = this._next();
599             
600             if (node !== null) {
601                 var testObject = node.testObject;
602                 
603                 //figure out what to do
604                 if (YAHOO.lang.isObject(testObject)){
605                     if (testObject instanceof YAHOO.tool.TestSuite){
606                         this.fireEvent(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
607                         testObject.setUp();
608                     } else if (testObject instanceof YAHOO.tool.TestCase){
609                         this.fireEvent(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
610                     }
611                     
612                     //some environments don't support setTimeout
613                     if (typeof setTimeout != "undefined"){                    
614                         setTimeout(function(){
615                             YAHOO.tool.TestRunner._run();
616                         }, 0);
617                     } else {
618                         this._run();
619                     }
620                 } else {
621                     this._runTest(node);
622                 }
623
624             }
625         },
626         
627         _resumeTest : function (segment /*:Function*/) /*:Void*/ {
628         
629             //get relevant information
630             var node /*:TestNode*/ = this._cur;
631             var testName /*:String*/ = node.testObject;
632             var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
633             
634             //cancel other waits if available
635             if (testCase.__yui_wait){
636                 clearTimeout(testCase.__yui_wait);
637                 delete testCase.__yui_wait;
638             }            
639             
640             //get the "should" test cases
641             var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
642             var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
643             
644             //variable to hold whether or not the test failed
645             var failed /*:Boolean*/ = false;
646             var error /*:Error*/ = null;
647                 
648             //try the test
649             try {
650             
651                 //run the test
652                 segment.apply(testCase);
653                 
654                 //if it should fail, and it got here, then it's a fail because it didn't
655                 if (shouldFail){
656                     error = new YAHOO.util.ShouldFail();
657                     failed = true;
658                 } else if (shouldError){
659                     error = new YAHOO.util.ShouldError();
660                     failed = true;
661                 }
662                            
663             } catch (thrown /*:Error*/){
664                 if (thrown instanceof YAHOO.util.AssertionError) {
665                     if (!shouldFail){
666                         error = thrown;
667                         failed = true;
668                     }
669                 } else if (thrown instanceof YAHOO.tool.TestCase.Wait){
670                 
671                     if (YAHOO.lang.isFunction(thrown.segment)){
672                         if (YAHOO.lang.isNumber(thrown.delay)){
673                         
674                             //some environments don't support setTimeout
675                             if (typeof setTimeout != "undefined"){
676                                 testCase.__yui_wait = setTimeout(function(){
677                                     YAHOO.tool.TestRunner._resumeTest(thrown.segment);
678                                 }, thrown.delay);                             
679                             } else {
680                                 throw new Error("Asynchronous tests not supported in this environment.");
681                             }
682                         }
683                     }
684                     
685                     return;
686                 
687                 } else {
688                     //first check to see if it should error
689                     if (!shouldError) {                        
690                         error = new YAHOO.util.UnexpectedError(thrown);
691                         failed = true;
692                     } else {
693                         //check to see what type of data we have
694                         if (YAHOO.lang.isString(shouldError)){
695                             
696                             //if it's a string, check the error message
697                             if (thrown.message != shouldError){
698                                 error = new YAHOO.util.UnexpectedError(thrown);
699                                 failed = true;                                    
700                             }
701                         } else if (YAHOO.lang.isFunction(shouldError)){
702                         
703                             //if it's a function, see if the error is an instance of it
704                             if (!(thrown instanceof shouldError)){
705                                 error = new YAHOO.util.UnexpectedError(thrown);
706                                 failed = true;
707                             }
708                         
709                         } else if (YAHOO.lang.isObject(shouldError)){
710                         
711                             //if it's an object, check the instance and message
712                             if (!(thrown instanceof shouldError.constructor) || 
713                                     thrown.message != shouldError.message){
714                                 error = new YAHOO.util.UnexpectedError(thrown);
715                                 failed = true;                                    
716                             }
717                         
718                         }
719                     
720                     }
721                 }
722                 
723             }
724             
725             //fireEvent appropriate event
726             if (failed) {
727                 this.fireEvent(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
728             } else {
729                 this.fireEvent(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
730             }
731             
732             //run the tear down
733             testCase.tearDown();
734             
735             //update results
736             node.parent.results[testName] = { 
737                 result: failed ? "fail" : "pass",
738                 message: error ? error.getMessage() : "Test passed",
739                 type: "test",
740                 name: testName
741             };
742             
743             if (failed){
744                 node.parent.results.failed++;
745             } else {
746                 node.parent.results.passed++;
747             }
748             node.parent.results.total++;
749
750             //set timeout not supported in all environments
751             if (typeof setTimeout != "undefined"){
752                 setTimeout(function(){
753                     YAHOO.tool.TestRunner._run();
754                 }, 0);
755             } else {
756                 this._run();
757             }
758         
759         },
760                 
761         /**
762          * Runs a single test based on the data provided in the node.
763          * @param {TestNode} node The TestNode representing the test to run.
764          * @return {Void}
765          * @static
766          * @private
767          * @name _runTest
768          */
769         _runTest : function (node /*:TestNode*/) /*:Void*/ {
770         
771             //get relevant information
772             var testName /*:String*/ = node.testObject;
773             var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
774             var test /*:Function*/ = testCase[testName];
775             
776             //get the "should" test cases
777             var shouldIgnore /*:Object*/ = (testCase._should.ignore || {})[testName];
778             
779             //figure out if the test should be ignored or not
780             if (shouldIgnore){
781             
782                 //update results
783                 node.parent.results[testName] = { 
784                     result: "ignore",
785                     message: "Test ignored",
786                     type: "test",
787                     name: testName
788                 };
789                 
790                 node.parent.results.ignored++;
791                 node.parent.results.total++;
792             
793                 this.fireEvent(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
794                 
795                 //some environments don't support setTimeout
796                 if (typeof setTimeout != "undefined"){                    
797                     setTimeout(function(){
798                         YAHOO.tool.TestRunner._run();
799                     }, 0);              
800                 } else {
801                     this._run();
802                 }
803
804             } else {
805             
806                 //run the setup
807                 testCase.setUp();
808                 
809                 //now call the body of the test
810                 this._resumeTest(test);                
811             }
812
813         },        
814         
815         //-------------------------------------------------------------------------
816         // Protected Methods
817         //-------------------------------------------------------------------------   
818     
819         /**
820          * Fires events for the TestRunner. This overrides the default fireEvent()
821          * method from EventProvider to add the type property to the data that is
822          * passed through on each event call.
823          * @param {String} type The type of event to fire.
824          * @param {Object} data (Optional) Data for the event.
825          * @method fireEvent
826          * @static
827          * @protected
828          */
829         fireEvent : function (type /*:String*/, data /*:Object*/) /*:Void*/ {
830             data = data || {};
831             data.type = type;
832             TestRunner.superclass.fireEvent.call(this, type, data);
833         },
834         
835         //-------------------------------------------------------------------------
836         // Public Methods
837         //-------------------------------------------------------------------------   
838     
839         /**
840          * Adds a test suite or test case to the list of test objects to run.
841          * @param testObject Either a TestCase or a TestSuite that should be run.
842          * @return {Void}
843          * @method add
844          * @static
845          */
846         add : function (testObject /*:Object*/) /*:Void*/ {
847             this.masterSuite.add(testObject);
848         },
849         
850         /**
851          * Removes all test objects from the runner.
852          * @return {Void}
853          * @method clear
854          * @static
855          */
856         clear : function () /*:Void*/ {
857             this.masterSuite.items = [];
858         },
859         
860         /**
861          * Resumes the TestRunner after wait() was called.
862          * @param {Function} segment The function to run as the rest
863          *      of the haulted test.
864          * @return {Void}
865          * @method resume
866          * @static
867          */
868         resume : function (segment /*:Function*/) /*:Void*/ {
869             this._resumeTest(segment || function(){});
870         },
871     
872         /**
873          * Runs the test suite.
874          * @return {Void}
875          * @method run
876          * @static
877          */
878         run : function (testObject /*:Object*/) /*:Void*/ {
879             
880             //pointer to runner to avoid scope issues 
881             var runner = YAHOO.tool.TestRunner;
882
883             //build the test tree
884             runner._buildTestTree();
885             
886             //set when the test started
887             runner._root.results.duration = (new Date()).getTime();
888             
889             //fire the begin event
890             runner.fireEvent(runner.BEGIN_EVENT);
891        
892             //begin the testing
893             runner._run();
894         }    
895     });
896     
897     return new TestRunner();
898     
899 })();
900 YAHOO.namespace("util");
901
902 //-----------------------------------------------------------------------------
903 // Assert object
904 //-----------------------------------------------------------------------------
905
906 /**
907  * The Assert object provides functions to test JavaScript values against
908  * known and expected results. Whenever a comparison (assertion) fails,
909  * an error is thrown.
910  *
911  * @namespace YAHOO.util
912  * @class Assert
913  * @static
914  */
915 YAHOO.util.Assert = {
916
917     //-------------------------------------------------------------------------
918     // Helper Methods
919     //-------------------------------------------------------------------------
920     
921     /**
922      * Formats a message so that it can contain the original assertion message
923      * in addition to the custom message.
924      * @param {String} customMessage The message passed in by the developer.
925      * @param {String} defaultMessage The message created by the error by default.
926      * @return {String} The final error message, containing either or both.
927      * @protected
928      * @static
929      * @method _formatMessage
930      */
931     _formatMessage : function (customMessage /*:String*/, defaultMessage /*:String*/) /*:String*/ {
932         var message = customMessage;
933         if (YAHOO.lang.isString(customMessage) && customMessage.length > 0){
934             return YAHOO.lang.substitute(customMessage, { message: defaultMessage });
935         } else {
936             return defaultMessage;
937         }        
938     },
939     
940     //-------------------------------------------------------------------------
941     // Generic Assertion Methods
942     //-------------------------------------------------------------------------
943     
944     /** 
945      * Forces an assertion error to occur.
946      * @param {String} message (Optional) The message to display with the failure.
947      * @method fail
948      * @static
949      */
950     fail : function (message /*:String*/) /*:Void*/ {
951         throw new YAHOO.util.AssertionError(this._formatMessage(message, "Test force-failed."));
952     },       
953     
954     //-------------------------------------------------------------------------
955     // Equality Assertion Methods
956     //-------------------------------------------------------------------------    
957     
958     /**
959      * Asserts that a value is equal to another. This uses the double equals sign
960      * so type cohersion may occur.
961      * @param {Object} expected The expected value.
962      * @param {Object} actual The actual value to test.
963      * @param {String} message (Optional) The message to display if the assertion fails.
964      * @method areEqual
965      * @static
966      */
967     areEqual : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
968         if (expected != actual) {
969             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be equal."), expected, actual);
970         }
971     },
972     
973     /**
974      * Asserts that a value is not equal to another. This uses the double equals sign
975      * so type cohersion may occur.
976      * @param {Object} unexpected The unexpected value.
977      * @param {Object} actual The actual value to test.
978      * @param {String} message (Optional) The message to display if the assertion fails.
979      * @method areNotEqual
980      * @static
981      */
982     areNotEqual : function (unexpected /*:Object*/, actual /*:Object*/, 
983                          message /*:String*/) /*:Void*/ {
984         if (unexpected == actual) {
985             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be equal."), unexpected);
986         }
987     },
988     
989     /**
990      * Asserts that a value is not the same as another. This uses the triple equals sign
991      * so no type cohersion may occur.
992      * @param {Object} unexpected The unexpected value.
993      * @param {Object} actual The actual value to test.
994      * @param {String} message (Optional) The message to display if the assertion fails.
995      * @method areNotSame
996      * @static
997      */
998     areNotSame : function (unexpected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
999         if (unexpected === actual) {
1000             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be the same."), unexpected);
1001         }
1002     },
1003
1004     /**
1005      * Asserts that a value is the same as another. This uses the triple equals sign
1006      * so no type cohersion may occur.
1007      * @param {Object} expected The expected value.
1008      * @param {Object} actual The actual value to test.
1009      * @param {String} message (Optional) The message to display if the assertion fails.
1010      * @method areSame
1011      * @static
1012      */
1013     areSame : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1014         if (expected !== actual) {
1015             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be the same."), expected, actual);
1016         }
1017     },    
1018     
1019     //-------------------------------------------------------------------------
1020     // Boolean Assertion Methods
1021     //-------------------------------------------------------------------------    
1022     
1023     /**
1024      * Asserts that a value is false. This uses the triple equals sign
1025      * so no type cohersion may occur.
1026      * @param {Object} actual The actual value to test.
1027      * @param {String} message (Optional) The message to display if the assertion fails.
1028      * @method isFalse
1029      * @static
1030      */
1031     isFalse : function (actual /*:Boolean*/, message /*:String*/) {
1032         if (false !== actual) {
1033             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be false."), false, actual);
1034         }
1035     },
1036     
1037     /**
1038      * Asserts that a value is true. This uses the triple equals sign
1039      * so no type cohersion may occur.
1040      * @param {Object} actual The actual value to test.
1041      * @param {String} message (Optional) The message to display if the assertion fails.
1042      * @method isTrue
1043      * @static
1044      */
1045     isTrue : function (actual /*:Boolean*/, message /*:String*/) /*:Void*/ {
1046         if (true !== actual) {
1047             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be true."), true, actual);
1048         }
1049
1050     },
1051     
1052     //-------------------------------------------------------------------------
1053     // Special Value Assertion Methods
1054     //-------------------------------------------------------------------------    
1055     
1056     /**
1057      * Asserts that a value is not a number.
1058      * @param {Object} actual The value to test.
1059      * @param {String} message (Optional) The message to display if the assertion fails.
1060      * @method isNaN
1061      * @static
1062      */
1063     isNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1064         if (!isNaN(actual)){
1065             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be NaN."), NaN, actual);
1066         }    
1067     },
1068     
1069     /**
1070      * Asserts that a value is not the special NaN value.
1071      * @param {Object} actual The value to test.
1072      * @param {String} message (Optional) The message to display if the assertion fails.
1073      * @method isNotNaN
1074      * @static
1075      */
1076     isNotNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1077         if (isNaN(actual)){
1078             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be NaN."), NaN);
1079         }    
1080     },
1081     
1082     /**
1083      * Asserts that a value is not null. This uses the triple equals sign
1084      * so no type cohersion may occur.
1085      * @param {Object} actual The actual value to test.
1086      * @param {String} message (Optional) The message to display if the assertion fails.
1087      * @method isNotNull
1088      * @static
1089      */
1090     isNotNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1091         if (YAHOO.lang.isNull(actual)) {
1092             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be null."), null);
1093         }
1094     },
1095
1096     /**
1097      * Asserts that a value is not undefined. This uses the triple equals sign
1098      * so no type cohersion may occur.
1099      * @param {Object} actual The actual value to test.
1100      * @param {String} message (Optional) The message to display if the assertion fails.
1101      * @method isNotUndefined
1102      * @static
1103      */
1104     isNotUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1105         if (YAHOO.lang.isUndefined(actual)) {
1106             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should not be undefined."), undefined);
1107         }
1108     },
1109
1110     /**
1111      * Asserts that a value is null. This uses the triple equals sign
1112      * so no type cohersion may occur.
1113      * @param {Object} actual The actual value to test.
1114      * @param {String} message (Optional) The message to display if the assertion fails.
1115      * @method isNull
1116      * @static
1117      */
1118     isNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1119         if (!YAHOO.lang.isNull(actual)) {
1120             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be null."), null, actual);
1121         }
1122     },
1123         
1124     /**
1125      * Asserts that a value is undefined. This uses the triple equals sign
1126      * so no type cohersion may occur.
1127      * @param {Object} actual The actual value to test.
1128      * @param {String} message (Optional) The message to display if the assertion fails.
1129      * @method isUndefined
1130      * @static
1131      */
1132     isUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1133         if (!YAHOO.lang.isUndefined(actual)) {
1134             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be undefined."), undefined, actual);
1135         }
1136     },    
1137     
1138     //--------------------------------------------------------------------------
1139     // Instance Assertion Methods
1140     //--------------------------------------------------------------------------    
1141    
1142     /**
1143      * Asserts that a value is an array.
1144      * @param {Object} actual The value to test.
1145      * @param {String} message (Optional) The message to display if the assertion fails.
1146      * @method isArray
1147      * @static
1148      */
1149     isArray : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1150         if (!YAHOO.lang.isArray(actual)){
1151             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an array."), actual);
1152         }    
1153     },
1154    
1155     /**
1156      * Asserts that a value is a Boolean.
1157      * @param {Object} actual The value to test.
1158      * @param {String} message (Optional) The message to display if the assertion fails.
1159      * @method isBoolean
1160      * @static
1161      */
1162     isBoolean : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1163         if (!YAHOO.lang.isBoolean(actual)){
1164             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a Boolean."), actual);
1165         }    
1166     },
1167    
1168     /**
1169      * Asserts that a value is a function.
1170      * @param {Object} actual The value to test.
1171      * @param {String} message (Optional) The message to display if the assertion fails.
1172      * @method isFunction
1173      * @static
1174      */
1175     isFunction : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1176         if (!YAHOO.lang.isFunction(actual)){
1177             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a function."), actual);
1178         }    
1179     },
1180    
1181     /**
1182      * Asserts that a value is an instance of a particular object. This may return
1183      * incorrect results when comparing objects from one frame to constructors in
1184      * another frame. For best results, don't use in a cross-frame manner.
1185      * @param {Function} expected The function that the object should be an instance of.
1186      * @param {Object} actual The object to test.
1187      * @param {String} message (Optional) The message to display if the assertion fails.
1188      * @method isInstanceOf
1189      * @static
1190      */
1191     isInstanceOf : function (expected /*:Function*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1192         if (!(actual instanceof expected)){
1193             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1194         }
1195     },
1196     
1197     /**
1198      * Asserts that a value is a number.
1199      * @param {Object} actual The value to test.
1200      * @param {String} message (Optional) The message to display if the assertion fails.
1201      * @method isNumber
1202      * @static
1203      */
1204     isNumber : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1205         if (!YAHOO.lang.isNumber(actual)){
1206             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a number."), actual);
1207         }    
1208     },    
1209     
1210     /**
1211      * Asserts that a value is an object.
1212      * @param {Object} actual The value to test.
1213      * @param {String} message (Optional) The message to display if the assertion fails.
1214      * @method isObject
1215      * @static
1216      */
1217     isObject : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1218         if (!YAHOO.lang.isObject(actual)){
1219             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an object."), actual);
1220         }
1221     },
1222     
1223     /**
1224      * Asserts that a value is a string.
1225      * @param {Object} actual The value to test.
1226      * @param {String} message (Optional) The message to display if the assertion fails.
1227      * @method isString
1228      * @static
1229      */
1230     isString : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1231         if (!YAHOO.lang.isString(actual)){
1232             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a string."), actual);
1233         }
1234     },
1235     
1236     /**
1237      * Asserts that a value is of a particular type. 
1238      * @param {String} expectedType The expected type of the variable.
1239      * @param {Object} actualValue The actual value to test.
1240      * @param {String} message (Optional) The message to display if the assertion fails.
1241      * @method isTypeOf
1242      * @static
1243      */
1244     isTypeOf : function (expected /*:String*/, actual /*:Object*/, message /*:String*/) /*:Void*/{
1245         if (typeof actual != expected){
1246             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be of type " + expected + "."), expected, typeof actual);
1247         }
1248     }
1249 };
1250
1251 //-----------------------------------------------------------------------------
1252 // Assertion errors
1253 //-----------------------------------------------------------------------------
1254
1255 /**
1256  * AssertionError is thrown whenever an assertion fails. It provides methods
1257  * to more easily get at error information and also provides a base class
1258  * from which more specific assertion errors can be derived.
1259  *
1260  * @param {String} message The message to display when the error occurs.
1261  * @namespace YAHOO.util
1262  * @class AssertionError
1263  * @extends Error
1264  * @constructor
1265  */ 
1266 YAHOO.util.AssertionError = function (message /*:String*/){
1267
1268     //call superclass
1269     //arguments.callee.superclass.constructor.call(this, message);
1270     
1271     /*
1272      * Error message. Must be duplicated to ensure browser receives it.
1273      * @type String
1274      * @property message
1275      */
1276     this.message /*:String*/ = message;
1277     
1278     /**
1279      * The name of the error that occurred.
1280      * @type String
1281      * @property name
1282      */
1283     this.name /*:String*/ = "AssertionError";
1284 };
1285
1286 //inherit methods
1287 YAHOO.lang.extend(YAHOO.util.AssertionError, Object, {
1288
1289     /**
1290      * Returns a fully formatted error for an assertion failure. This should
1291      * be overridden by all subclasses to provide specific information.
1292      * @method getMessage
1293      * @return {String} A string describing the error.
1294      */
1295     getMessage : function () /*:String*/ {
1296         return this.message;
1297     },
1298     
1299     /**
1300      * Returns a string representation of the error.
1301      * @method toString
1302      * @return {String} A string representation of the error.
1303      */
1304     toString : function () /*:String*/ {
1305         return this.name + ": " + this.getMessage();
1306     }
1307     
1308 });
1309
1310 /**
1311  * ComparisonFailure is subclass of AssertionError that is thrown whenever
1312  * a comparison between two values fails. It provides mechanisms to retrieve
1313  * both the expected and actual value.
1314  *
1315  * @param {String} message The message to display when the error occurs.
1316  * @param {Object} expected The expected value.
1317  * @param {Object} actual The actual value that caused the assertion to fail.
1318  * @namespace YAHOO.util
1319  * @extends YAHOO.util.AssertionError
1320  * @class ComparisonFailure
1321  * @constructor
1322  */ 
1323 YAHOO.util.ComparisonFailure = function (message /*:String*/, expected /*:Object*/, actual /*:Object*/){
1324
1325     //call superclass
1326     YAHOO.util.AssertionError.call(this, message);
1327     
1328     /**
1329      * The expected value.
1330      * @type Object
1331      * @property expected
1332      */
1333     this.expected /*:Object*/ = expected;
1334     
1335     /**
1336      * The actual value.
1337      * @type Object
1338      * @property actual
1339      */
1340     this.actual /*:Object*/ = actual;
1341     
1342     /**
1343      * The name of the error that occurred.
1344      * @type String
1345      * @property name
1346      */
1347     this.name /*:String*/ = "ComparisonFailure";
1348     
1349 };
1350
1351 //inherit methods
1352 YAHOO.lang.extend(YAHOO.util.ComparisonFailure, YAHOO.util.AssertionError, {
1353
1354     /**
1355      * Returns a fully formatted error for an assertion failure. This message
1356      * provides information about the expected and actual values.
1357      * @method toString
1358      * @return {String} A string describing the error.
1359      */
1360     getMessage : function () /*:String*/ {
1361         return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")"  +
1362             "\nActual:" + this.actual + " (" + (typeof this.actual) + ")";
1363     }
1364
1365 });
1366
1367 /**
1368  * UnexpectedValue is subclass of AssertionError that is thrown whenever
1369  * a value was unexpected in its scope. This typically means that a test
1370  * was performed to determine that a value was *not* equal to a certain
1371  * value.
1372  *
1373  * @param {String} message The message to display when the error occurs.
1374  * @param {Object} unexpected The unexpected value.
1375  * @namespace YAHOO.util
1376  * @extends YAHOO.util.AssertionError
1377  * @class UnexpectedValue
1378  * @constructor
1379  */ 
1380 YAHOO.util.UnexpectedValue = function (message /*:String*/, unexpected /*:Object*/){
1381
1382     //call superclass
1383     YAHOO.util.AssertionError.call(this, message);
1384     
1385     /**
1386      * The unexpected value.
1387      * @type Object
1388      * @property unexpected
1389      */
1390     this.unexpected /*:Object*/ = unexpected;
1391     
1392     /**
1393      * The name of the error that occurred.
1394      * @type String
1395      * @property name
1396      */
1397     this.name /*:String*/ = "UnexpectedValue";
1398     
1399 };
1400
1401 //inherit methods
1402 YAHOO.lang.extend(YAHOO.util.UnexpectedValue, YAHOO.util.AssertionError, {
1403
1404     /**
1405      * Returns a fully formatted error for an assertion failure. The message
1406      * contains information about the unexpected value that was encountered.
1407      * @method getMessage
1408      * @return {String} A string describing the error.
1409      */
1410     getMessage : function () /*:String*/ {
1411         return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1412     }
1413
1414 });
1415
1416 /**
1417  * ShouldFail is subclass of AssertionError that is thrown whenever
1418  * a test was expected to fail but did not.
1419  *
1420  * @param {String} message The message to display when the error occurs.
1421  * @namespace YAHOO.util
1422  * @extends YAHOO.util.AssertionError
1423  * @class ShouldFail
1424  * @constructor
1425  */  
1426 YAHOO.util.ShouldFail = function (message /*:String*/){
1427
1428     //call superclass
1429     YAHOO.util.AssertionError.call(this, message || "This test should fail but didn't.");
1430     
1431     /**
1432      * The name of the error that occurred.
1433      * @type String
1434      * @property name
1435      */
1436     this.name /*:String*/ = "ShouldFail";
1437     
1438 };
1439
1440 //inherit methods
1441 YAHOO.lang.extend(YAHOO.util.ShouldFail, YAHOO.util.AssertionError);
1442
1443 /**
1444  * ShouldError is subclass of AssertionError that is thrown whenever
1445  * a test is expected to throw an error but doesn't.
1446  *
1447  * @param {String} message The message to display when the error occurs.
1448  * @namespace YAHOO.util
1449  * @extends YAHOO.util.AssertionError
1450  * @class ShouldError
1451  * @constructor
1452  */  
1453 YAHOO.util.ShouldError = function (message /*:String*/){
1454
1455     //call superclass
1456     YAHOO.util.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
1457     
1458     /**
1459      * The name of the error that occurred.
1460      * @type String
1461      * @property name
1462      */
1463     this.name /*:String*/ = "ShouldError";
1464     
1465 };
1466
1467 //inherit methods
1468 YAHOO.lang.extend(YAHOO.util.ShouldError, YAHOO.util.AssertionError);
1469
1470 /**
1471  * UnexpectedError is subclass of AssertionError that is thrown whenever
1472  * an error occurs within the course of a test and the test was not expected
1473  * to throw an error.
1474  *
1475  * @param {Error} cause The unexpected error that caused this error to be 
1476  *                      thrown.
1477  * @namespace YAHOO.util
1478  * @extends YAHOO.util.AssertionError
1479  * @class UnexpectedError
1480  * @constructor
1481  */  
1482 YAHOO.util.UnexpectedError = function (cause /*:Object*/){
1483
1484     //call superclass
1485     YAHOO.util.AssertionError.call(this, "Unexpected error: " + cause.message);
1486     
1487     /**
1488      * The unexpected error that occurred.
1489      * @type Error
1490      * @property cause
1491      */
1492     this.cause /*:Error*/ = cause;
1493     
1494     /**
1495      * The name of the error that occurred.
1496      * @type String
1497      * @property name
1498      */
1499     this.name /*:String*/ = "UnexpectedError";
1500     
1501     /**
1502      * Stack information for the error (if provided).
1503      * @type String
1504      * @property stack
1505      */
1506     this.stack /*:String*/ = cause.stack;
1507     
1508 };
1509
1510 //inherit methods
1511 YAHOO.lang.extend(YAHOO.util.UnexpectedError, YAHOO.util.AssertionError);
1512 //-----------------------------------------------------------------------------
1513 // ArrayAssert object
1514 //-----------------------------------------------------------------------------
1515
1516 /**
1517  * The ArrayAssert object provides functions to test JavaScript array objects
1518  * for a variety of cases.
1519  *
1520  * @namespace YAHOO.util
1521  * @class ArrayAssert
1522  * @static
1523  */
1524  
1525 YAHOO.util.ArrayAssert = {
1526
1527     /**
1528      * Asserts that a value is present in an array. This uses the triple equals 
1529      * sign so no type cohersion may occur.
1530      * @param {Object} needle The value that is expected in the array.
1531      * @param {Array} haystack An array of values.
1532      * @param {String} message (Optional) The message to display if the assertion fails.
1533      * @method contains
1534      * @static
1535      */
1536     contains : function (needle /*:Object*/, haystack /*:Array*/, 
1537                            message /*:String*/) /*:Void*/ {
1538         
1539         var found /*:Boolean*/ = false;
1540         var Assert = YAHOO.util.Assert;
1541         
1542         //begin checking values
1543         for (var i=0; i < haystack.length && !found; i++){
1544             if (haystack[i] === needle) {
1545                 found = true;
1546             }
1547         }
1548         
1549         if (!found){
1550             Assert.fail(Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1551         }
1552     },
1553
1554     /**
1555      * Asserts that a set of values are present in an array. This uses the triple equals 
1556      * sign so no type cohersion may occur. For this assertion to pass, all values must
1557      * be found.
1558      * @param {Object[]} needles An array of values that are expected in the array.
1559      * @param {Array} haystack An array of values to check.
1560      * @param {String} message (Optional) The message to display if the assertion fails.
1561      * @method containsItems
1562      * @static
1563      */
1564     containsItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1565                            message /*:String*/) /*:Void*/ {
1566
1567         //begin checking values
1568         for (var i=0; i < needles.length; i++){
1569             this.contains(needles[i], haystack, message);
1570         }
1571     },
1572
1573     /**
1574      * Asserts that a value matching some condition is present in an array. This uses
1575      * a function to determine a match.
1576      * @param {Function} matcher A function that returns true if the items matches or false if not.
1577      * @param {Array} haystack An array of values.
1578      * @param {String} message (Optional) The message to display if the assertion fails.
1579      * @method containsMatch
1580      * @static
1581      */
1582     containsMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1583                            message /*:String*/) /*:Void*/ {
1584         
1585         //check for valid matcher
1586         if (typeof matcher != "function"){
1587             throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1588         }
1589         
1590         var found /*:Boolean*/ = false;
1591         var Assert = YAHOO.util.Assert;
1592         
1593         //begin checking values
1594         for (var i=0; i < haystack.length && !found; i++){
1595             if (matcher(haystack[i])) {
1596                 found = true;
1597             }
1598         }
1599         
1600         if (!found){
1601             Assert.fail(Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1602         }
1603     },
1604
1605     /**
1606      * Asserts that a value is not present in an array. This uses the triple equals 
1607      * sign so no type cohersion may occur.
1608      * @param {Object} needle The value that is expected in the array.
1609      * @param {Array} haystack An array of values.
1610      * @param {String} message (Optional) The message to display if the assertion fails.
1611      * @method doesNotContain
1612      * @static
1613      */
1614     doesNotContain : function (needle /*:Object*/, haystack /*:Array*/, 
1615                            message /*:String*/) /*:Void*/ {
1616         
1617         var found /*:Boolean*/ = false;
1618         var Assert = YAHOO.util.Assert;
1619         
1620         //begin checking values
1621         for (var i=0; i < haystack.length && !found; i++){
1622             if (haystack[i] === needle) {
1623                 found = true;
1624             }
1625         }
1626         
1627         if (found){
1628             Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1629         }
1630     },
1631
1632     /**
1633      * Asserts that a set of values are not present in an array. This uses the triple equals 
1634      * sign so no type cohersion may occur. For this assertion to pass, all values must
1635      * not be found.
1636      * @param {Object[]} needles An array of values that are not expected in the array.
1637      * @param {Array} haystack An array of values to check.
1638      * @param {String} message (Optional) The message to display if the assertion fails.
1639      * @method doesNotContainItems
1640      * @static
1641      */
1642     doesNotContainItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1643                            message /*:String*/) /*:Void*/ {
1644
1645         for (var i=0; i < needles.length; i++){
1646             this.doesNotContain(needles[i], haystack, message);
1647         }
1648
1649     },
1650         
1651     /**
1652      * Asserts that no values matching a condition are present in an array. This uses
1653      * a function to determine a match.
1654      * @param {Function} matcher A function that returns true if the items matches or false if not.
1655      * @param {Array} haystack An array of values.
1656      * @param {String} message (Optional) The message to display if the assertion fails.
1657      * @method doesNotContainMatch
1658      * @static
1659      */
1660     doesNotContainMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1661                            message /*:String*/) /*:Void*/ {
1662         
1663         //check for valid matcher
1664         if (typeof matcher != "function"){
1665             throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1666         }
1667
1668         var found /*:Boolean*/ = false;
1669         var Assert = YAHOO.util.Assert;
1670         
1671         //begin checking values
1672         for (var i=0; i < haystack.length && !found; i++){
1673             if (matcher(haystack[i])) {
1674                 found = true;
1675             }
1676         }
1677         
1678         if (found){
1679             Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1680         }
1681     },
1682         
1683     /**
1684      * Asserts that the given value is contained in an array at the specified index.
1685      * This uses the triple equals sign so no type cohersion will occur.
1686      * @param {Object} needle The value to look for.
1687      * @param {Array} haystack The array to search in.
1688      * @param {int} index The index at which the value should exist.
1689      * @param {String} message (Optional) The message to display if the assertion fails.
1690      * @method indexOf
1691      * @static
1692      */
1693     indexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1694     
1695         //try to find the value in the array
1696         for (var i=0; i < haystack.length; i++){
1697             if (haystack[i] === needle){
1698                 YAHOO.util.Assert.areEqual(index, i, message || "Value exists at index " + i + " but should be at index " + index + ".");
1699                 return;
1700             }
1701         }
1702         
1703         var Assert = YAHOO.util.Assert;
1704         
1705         //if it makes it here, it wasn't found at all
1706         Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1707     },
1708         
1709     /**
1710      * Asserts that the values in an array are equal, and in the same position,
1711      * as values in another array. This uses the double equals sign
1712      * so type cohersion may occur. Note that the array objects themselves
1713      * need not be the same for this test to pass.
1714      * @param {Array} expected An array of the expected values.
1715      * @param {Array} actual Any array of the actual values.
1716      * @param {String} message (Optional) The message to display if the assertion fails.
1717      * @method itemsAreEqual
1718      * @static
1719      */
1720     itemsAreEqual : function (expected /*:Array*/, actual /*:Array*/, 
1721                            message /*:String*/) /*:Void*/ {
1722         
1723         //one may be longer than the other, so get the maximum length
1724         var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1725         var Assert = YAHOO.util.Assert;
1726        
1727         //begin checking values
1728         for (var i=0; i < len; i++){
1729             Assert.areEqual(expected[i], actual[i], 
1730                 Assert._formatMessage(message, "Values in position " + i + " are not equal."));
1731         }
1732     },
1733     
1734     /**
1735      * Asserts that the values in an array are equivalent, and in the same position,
1736      * as values in another array. This uses a function to determine if the values
1737      * are equivalent. Note that the array objects themselves
1738      * need not be the same for this test to pass.
1739      * @param {Array} expected An array of the expected values.
1740      * @param {Array} actual Any array of the actual values.
1741      * @param {Function} comparator A function that returns true if the values are equivalent
1742      *      or false if not.
1743      * @param {String} message (Optional) The message to display if the assertion fails.
1744      * @return {Void}
1745      * @method itemsAreEquivalent
1746      * @static
1747      */
1748     itemsAreEquivalent : function (expected /*:Array*/, actual /*:Array*/, 
1749                            comparator /*:Function*/, message /*:String*/) /*:Void*/ {
1750         
1751         //make sure the comparator is valid
1752         if (typeof comparator != "function"){
1753             throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1754         }
1755         
1756         //one may be longer than the other, so get the maximum length
1757         var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1758         
1759         //begin checking values
1760         for (var i=0; i < len; i++){
1761             if (!comparator(expected[i], actual[i])){
1762                 throw new YAHOO.util.ComparisonFailure(YAHOO.util.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1763             }
1764         }
1765     },
1766     
1767     /**
1768      * Asserts that an array is empty.
1769      * @param {Array} actual The array to test.
1770      * @param {String} message (Optional) The message to display if the assertion fails.
1771      * @method isEmpty
1772      * @static
1773      */
1774     isEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1775         if (actual.length > 0){
1776             var Assert = YAHOO.util.Assert;
1777             Assert.fail(Assert._formatMessage(message, "Array should be empty."));
1778         }
1779     },    
1780     
1781     /**
1782      * Asserts that an array is not empty.
1783      * @param {Array} actual The array to test.
1784      * @param {String} message (Optional) The message to display if the assertion fails.
1785      * @method isNotEmpty
1786      * @static
1787      */
1788     isNotEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1789         if (actual.length === 0){
1790             var Assert = YAHOO.util.Assert;
1791             Assert.fail(Assert._formatMessage(message, "Array should not be empty."));
1792         }
1793     },    
1794     
1795     /**
1796      * Asserts that the values in an array are the same, and in the same position,
1797      * as values in another array. This uses the triple equals sign
1798      * so no type cohersion will occur. Note that the array objects themselves
1799      * need not be the same for this test to pass.
1800      * @param {Array} expected An array of the expected values.
1801      * @param {Array} actual Any array of the actual values.
1802      * @param {String} message (Optional) The message to display if the assertion fails.
1803      * @method itemsAreSame
1804      * @static
1805      */
1806     itemsAreSame : function (expected /*:Array*/, actual /*:Array*/, 
1807                           message /*:String*/) /*:Void*/ {
1808         
1809         //one may be longer than the other, so get the maximum length
1810         var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1811         var Assert = YAHOO.util.Assert;
1812         
1813         //begin checking values
1814         for (var i=0; i < len; i++){
1815             Assert.areSame(expected[i], actual[i], 
1816                 Assert._formatMessage(message, "Values in position " + i + " are not the same."));
1817         }
1818     },
1819     
1820     /**
1821      * Asserts that the given value is contained in an array at the specified index,
1822      * starting from the back of the array.
1823      * This uses the triple equals sign so no type cohersion will occur.
1824      * @param {Object} needle The value to look for.
1825      * @param {Array} haystack The array to search in.
1826      * @param {int} index The index at which the value should exist.
1827      * @param {String} message (Optional) The message to display if the assertion fails.
1828      * @method lastIndexOf
1829      * @static
1830      */
1831     lastIndexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1832     
1833         var Assert = YAHOO.util.Assert;
1834     
1835         //try to find the value in the array
1836         for (var i=haystack.length; i >= 0; i--){
1837             if (haystack[i] === needle){
1838                 Assert.areEqual(index, i, Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1839                 return;
1840             }
1841         }
1842         
1843         //if it makes it here, it wasn't found at all
1844         Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array."));        
1845     }
1846     
1847 };
1848 YAHOO.namespace("util");
1849
1850
1851 //-----------------------------------------------------------------------------
1852 // ObjectAssert object
1853 //-----------------------------------------------------------------------------
1854
1855 /**
1856  * The ObjectAssert object provides functions to test JavaScript objects
1857  * for a variety of cases.
1858  *
1859  * @namespace YAHOO.util
1860  * @class ObjectAssert
1861  * @static
1862  */
1863 YAHOO.util.ObjectAssert = {
1864         
1865     /**
1866      * Asserts that all properties in the object exist in another object.
1867      * @param {Object} expected An object with the expected properties.
1868      * @param {Object} actual An object with the actual properties.
1869      * @param {String} message (Optional) The message to display if the assertion fails.
1870      * @method propertiesAreEqual
1871      * @static
1872      */
1873     propertiesAreEqual : function (expected /*:Object*/, actual /*:Object*/, 
1874                            message /*:String*/) /*:Void*/ {
1875         
1876         var Assert = YAHOO.util.Assert;
1877         
1878         //get all properties in the object
1879         var properties /*:Array*/ = [];        
1880         for (var property in expected){
1881             properties.push(property);
1882         }
1883         
1884         //see if the properties are in the expected object
1885         for (var i=0; i < properties.length; i++){
1886             Assert.isNotUndefined(actual[properties[i]], 
1887                 Assert._formatMessage(message, "Property '" + properties[i] + "' expected."));
1888         }
1889
1890     },
1891     
1892     /**
1893      * Asserts that an object has a property with the given name.
1894      * @param {String} propertyName The name of the property to test.
1895      * @param {Object} object The object to search.
1896      * @param {String} message (Optional) The message to display if the assertion fails.
1897      * @method hasProperty
1898      * @static
1899      */    
1900     hasProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1901         if (!(propertyName in object)){
1902             var Assert = YAHOO.util.Assert;
1903             Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
1904         }    
1905     },
1906     
1907     /**
1908      * Asserts that a property with the given name exists on an object instance (not on its prototype).
1909      * @param {String} propertyName The name of the property to test.
1910      * @param {Object} object The object to search.
1911      * @param {String} message (Optional) The message to display if the assertion fails.
1912      * @method hasProperty
1913      * @static
1914      */    
1915     hasOwnProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1916         if (!YAHOO.lang.hasOwnProperty(object, propertyName)){
1917             var Assert = YAHOO.util.Assert;
1918             Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1919         }     
1920     }
1921 };
1922 //-----------------------------------------------------------------------------
1923 // DateAssert object
1924 //-----------------------------------------------------------------------------
1925
1926 /**
1927  * The DateAssert object provides functions to test JavaScript Date objects
1928  * for a variety of cases.
1929  *
1930  * @namespace YAHOO.util
1931  * @class DateAssert
1932  * @static
1933  */
1934  
1935 YAHOO.util.DateAssert = {
1936
1937     /**
1938      * Asserts that a date's month, day, and year are equal to another date's.
1939      * @param {Date} expected The expected date.
1940      * @param {Date} actual The actual date to test.
1941      * @param {String} message (Optional) The message to display if the assertion fails.
1942      * @method datesAreEqual
1943      * @static
1944      */
1945     datesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1946         if (expected instanceof Date && actual instanceof Date){
1947             var Assert = YAHOO.util.Assert;
1948             Assert.areEqual(expected.getFullYear(), actual.getFullYear(), Assert._formatMessage(message, "Years should be equal."));
1949             Assert.areEqual(expected.getMonth(), actual.getMonth(), Assert._formatMessage(message, "Months should be equal."));
1950             Assert.areEqual(expected.getDate(), actual.getDate(), Assert._formatMessage(message, "Day of month should be equal."));
1951         } else {
1952             throw new TypeError("DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
1953         }
1954     },
1955
1956     /**
1957      * Asserts that a date's hour, minutes, and seconds are equal to another date's.
1958      * @param {Date} expected The expected date.
1959      * @param {Date} actual The actual date to test.
1960      * @param {String} message (Optional) The message to display if the assertion fails.
1961      * @method timesAreEqual
1962      * @static
1963      */
1964     timesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1965         if (expected instanceof Date && actual instanceof Date){
1966             var Assert = YAHOO.util.Assert;
1967             Assert.areEqual(expected.getHours(), actual.getHours(), Assert._formatMessage(message, "Hours should be equal."));
1968             Assert.areEqual(expected.getMinutes(), actual.getMinutes(), Assert._formatMessage(message, "Minutes should be equal."));
1969             Assert.areEqual(expected.getSeconds(), actual.getSeconds(), Assert._formatMessage(message, "Seconds should be equal."));
1970         } else {
1971             throw new TypeError("DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
1972         }
1973     }
1974     
1975 };
1976 YAHOO.namespace("tool");
1977
1978 //-----------------------------------------------------------------------------
1979 // TestManager object
1980 //-----------------------------------------------------------------------------
1981
1982 /**
1983  * Runs pages containing test suite definitions.
1984  * @namespace YAHOO.tool
1985  * @class TestManager
1986  * @static
1987  */
1988 YAHOO.tool.TestManager = {
1989
1990     /**
1991      * Constant for the testpagebegin custom event
1992      * @property TEST_PAGE_BEGIN_EVENT
1993      * @static
1994      * @type string
1995      * @final
1996      */
1997     TEST_PAGE_BEGIN_EVENT /*:String*/ : "testpagebegin",
1998
1999     /**
2000      * Constant for the testpagecomplete custom event
2001      * @property TEST_PAGE_COMPLETE_EVENT
2002      * @static
2003      * @type string
2004      * @final
2005      */
2006     TEST_PAGE_COMPLETE_EVENT /*:String*/ : "testpagecomplete",
2007
2008     /**
2009      * Constant for the testmanagerbegin custom event
2010      * @property TEST_MANAGER_BEGIN_EVENT
2011      * @static
2012      * @type string
2013      * @final
2014      */
2015     TEST_MANAGER_BEGIN_EVENT /*:String*/ : "testmanagerbegin",
2016
2017     /**
2018      * Constant for the testmanagercomplete custom event
2019      * @property TEST_MANAGER_COMPLETE_EVENT
2020      * @static
2021      * @type string
2022      * @final
2023      */
2024     TEST_MANAGER_COMPLETE_EVENT /*:String*/ : "testmanagercomplete",
2025
2026     //-------------------------------------------------------------------------
2027     // Private Properties
2028     //-------------------------------------------------------------------------
2029     
2030     
2031     /**
2032      * The URL of the page currently being executed.
2033      * @type String
2034      * @private
2035      * @property _curPage
2036      * @static
2037      */
2038     _curPage /*:String*/ : null,
2039     
2040     /**
2041      * The frame used to load and run tests.
2042      * @type Window
2043      * @private
2044      * @property _frame
2045      * @static
2046      */
2047     _frame /*:Window*/ : null,
2048     
2049     /**
2050      * The logger used to output results from the various tests.
2051      * @type YAHOO.tool.TestLogger
2052      * @private
2053      * @property _logger
2054      * @static
2055      */
2056     _logger : null,
2057     
2058     /**
2059      * The timeout ID for the next iteration through the tests.
2060      * @type int
2061      * @private
2062      * @property _timeoutId
2063      * @static
2064      */
2065     _timeoutId /*:int*/ : 0,
2066     
2067     /**
2068      * Array of pages to load.
2069      * @type String[]
2070      * @private
2071      * @property _pages
2072      * @static
2073      */
2074     _pages /*:String[]*/ : [],
2075     
2076     /**
2077      * Aggregated results
2078      * @type Object
2079      * @private
2080      * @property _results
2081      * @static
2082      */
2083     _results: null,
2084     
2085     //-------------------------------------------------------------------------
2086     // Private Methods
2087     //-------------------------------------------------------------------------
2088     
2089     /**
2090      * Handles TestRunner.COMPLETE_EVENT, storing the results and beginning
2091      * the loop again.
2092      * @param {Object} data Data about the event.
2093      * @return {Void}
2094      * @private
2095      * @static
2096      */
2097     _handleTestRunnerComplete : function (data /*:Object*/) /*:Void*/ {
2098
2099         this.fireEvent(this.TEST_PAGE_COMPLETE_EVENT, {
2100                 page: this._curPage,
2101                 results: data.results
2102             });
2103     
2104         //save results
2105         //this._results[this.curPage] = data.results;
2106         
2107         //process 'em
2108         this._processResults(this._curPage, data.results);
2109         
2110         this._logger.clearTestRunner();
2111     
2112         //if there's more to do, set a timeout to begin again
2113         if (this._pages.length){
2114             this._timeoutId = setTimeout(function(){
2115                 YAHOO.tool.TestManager._run();
2116             }, 1000);
2117         } else {
2118             this.fireEvent(this.TEST_MANAGER_COMPLETE_EVENT, this._results);
2119         }
2120     },
2121     
2122     /**
2123      * Processes the results of a test page run, outputting log messages
2124      * for failed tests.
2125      * @return {Void}
2126      * @private
2127      * @static
2128      */
2129     _processResults : function (page /*:String*/, results /*:Object*/) /*:Void*/ {
2130
2131         var r = this._results;
2132         
2133         r.passed += results.passed;
2134         r.failed += results.failed;
2135         r.ignored += results.ignored;
2136         r.total += results.total;
2137         r.duration += results.duration;
2138         
2139         if (results.failed){
2140             r.failedPages.push(page);
2141         } else {
2142             r.passedPages.push(page);
2143         }
2144         
2145         results.name = page;
2146         results.type = "page";
2147         
2148         r[page] = results;
2149     },
2150     
2151     /**
2152      * Loads the next test page into the iframe.
2153      * @return {Void}
2154      * @static
2155      * @private
2156      */
2157     _run : function () /*:Void*/ {
2158     
2159         //set the current page
2160         this._curPage = this._pages.shift();
2161
2162         this.fireEvent(this.TEST_PAGE_BEGIN_EVENT, this._curPage);
2163         
2164         //load the frame - destroy history in case there are other iframes that
2165         //need testing
2166         this._frame.location.replace(this._curPage);
2167     
2168     },
2169         
2170     //-------------------------------------------------------------------------
2171     // Public Methods
2172     //-------------------------------------------------------------------------
2173     
2174     /**
2175      * Signals that a test page has been loaded. This should be called from
2176      * within the test page itself to notify the TestManager that it is ready.
2177      * @return {Void}
2178      * @static
2179      */
2180     load : function () /*:Void*/ {
2181         if (parent.YAHOO.tool.TestManager !== this){
2182             parent.YAHOO.tool.TestManager.load();
2183         } else {
2184             
2185             if (this._frame) {
2186                 //assign event handling
2187                 var TestRunner = this._frame.YAHOO.tool.TestRunner;
2188
2189                 this._logger.setTestRunner(TestRunner);
2190                 TestRunner.subscribe(TestRunner.COMPLETE_EVENT, this._handleTestRunnerComplete, this, true);
2191                 
2192                 //run it
2193                 TestRunner.run();
2194             }
2195         }
2196     },
2197     
2198     /**
2199      * Sets the pages to be loaded.
2200      * @param {String[]} pages An array of URLs to load.
2201      * @return {Void}
2202      * @static
2203      */
2204     setPages : function (pages /*:String[]*/) /*:Void*/ {
2205         this._pages = pages;
2206     },
2207     
2208     /**
2209      * Begins the process of running the tests.
2210      * @return {Void}
2211      * @static
2212      */
2213     start : function () /*:Void*/ {
2214
2215         if (!this._initialized) {
2216
2217             /**
2218              * Fires when loading a test page
2219              * @event testpagebegin
2220              * @param curPage {string} the page being loaded
2221              * @static
2222              */
2223             this.createEvent(this.TEST_PAGE_BEGIN_EVENT);
2224
2225             /**
2226              * Fires when a test page is complete
2227              * @event testpagecomplete
2228              * @param obj {page: string, results: object} the name of the
2229              * page that was loaded, and the test suite results
2230              * @static
2231              */
2232             this.createEvent(this.TEST_PAGE_COMPLETE_EVENT);
2233
2234             /**
2235              * Fires when the test manager starts running all test pages
2236              * @event testmanagerbegin
2237              * @static
2238              */
2239             this.createEvent(this.TEST_MANAGER_BEGIN_EVENT);
2240
2241             /**
2242              * Fires when the test manager finishes running all test pages.  External
2243              * test runners should subscribe to this event in order to get the
2244              * aggregated test results.
2245              * @event testmanagercomplete
2246              * @param obj { pages_passed: int, pages_failed: int, tests_passed: int
2247              *              tests_failed: int, passed: string[], failed: string[],
2248              *              page_results: {} }
2249              * @static
2250              */
2251             this.createEvent(this.TEST_MANAGER_COMPLETE_EVENT);
2252
2253             //create iframe if not already available
2254             if (!this._frame){
2255                 var frame /*:HTMLElement*/ = document.createElement("iframe");
2256                 frame.style.visibility = "hidden";
2257                 frame.style.position = "absolute";
2258                 document.body.appendChild(frame);
2259                 this._frame = frame.contentWindow || frame.contentDocument.parentWindow;
2260             }
2261             
2262             //create test logger if not already available
2263             if (!this._logger){
2264                 this._logger = new YAHOO.tool.TestLogger();
2265             }
2266
2267             this._initialized = true;
2268         }
2269
2270
2271         // reset the results cache
2272         this._results = {
2273         
2274             passed: 0,
2275             failed: 0,
2276             ignored: 0,
2277             total: 0,
2278             type: "report",
2279             name: "YUI Test Results",
2280             duration: 0,
2281             failedPages:[],
2282             passedPages:[]
2283             /*
2284             // number of pages that pass
2285             pages_passed: 0,
2286             // number of pages that fail
2287             pages_failed: 0,
2288             // total number of tests passed
2289             tests_passed: 0,
2290             // total number of tests failed
2291             tests_failed: 0,
2292             // array of pages that passed
2293             passed: [],
2294             // array of pages that failed
2295             failed: [],
2296             // map of full results for each page
2297             page_results: {}*/
2298         };
2299
2300         this.fireEvent(this.TEST_MANAGER_BEGIN_EVENT, null);
2301         this._run();
2302     
2303     },
2304
2305     /**
2306      * Stops the execution of tests.
2307      * @return {Void}
2308      * @static
2309      */
2310     stop : function () /*:Void*/ {
2311         clearTimeout(this._timeoutId);
2312     }
2313
2314 };
2315
2316 YAHOO.lang.augmentObject(YAHOO.tool.TestManager, YAHOO.util.EventProvider.prototype);
2317
2318 YAHOO.namespace("tool");
2319
2320 //-----------------------------------------------------------------------------
2321 // TestLogger object
2322 //-----------------------------------------------------------------------------
2323
2324 /**
2325  * Displays test execution progress and results, providing filters based on
2326  * different key events.
2327  * @namespace YAHOO.tool
2328  * @class TestLogger
2329  * @constructor
2330  * @param {HTMLElement} element (Optional) The element to create the logger in.
2331  * @param {Object} config (Optional) Configuration options for the logger.
2332  */
2333 YAHOO.tool.TestLogger = function (element, config) {
2334     YAHOO.tool.TestLogger.superclass.constructor.call(this, element, config);
2335     this.init();
2336 };
2337
2338 YAHOO.lang.extend(YAHOO.tool.TestLogger, YAHOO.widget.LogReader, {
2339
2340     footerEnabled : true,
2341     newestOnTop : false,
2342
2343     /**
2344      * Formats message string to HTML for output to console.
2345      * @private
2346      * @method formatMsg
2347      * @param oLogMsg {Object} Log message object.
2348      * @return {String} HTML-formatted message for output to console.
2349      */
2350     formatMsg : function(message /*:Object*/) {
2351     
2352         var category /*:String*/ = message.category;        
2353         var text /*:String*/ = this.html2Text(message.msg);
2354         
2355         return "<pre><p><span class=\"" + category + "\">" + category.toUpperCase() + "</span> " + text + "</p></pre>";
2356     
2357     },
2358     
2359     //-------------------------------------------------------------------------
2360     // Private Methods
2361     //-------------------------------------------------------------------------
2362     
2363     /*
2364      * Initializes the logger.
2365      * @private
2366      */
2367     init : function () {
2368     
2369         //attach to any available TestRunner
2370         if (YAHOO.tool.TestRunner){
2371             this.setTestRunner(YAHOO.tool.TestRunner);
2372         }
2373         
2374         //hide useless sources
2375         this.hideSource("global");
2376         this.hideSource("LogReader");
2377         
2378         //hide useless message categories
2379         this.hideCategory("warn");
2380         this.hideCategory("window");
2381         this.hideCategory("time");
2382         
2383         //reset the logger
2384         this.clearConsole();
2385     },
2386     
2387     /**
2388      * Clears the reference to the TestRunner from previous operations. This 
2389      * unsubscribes all events and removes the object reference.
2390      * @return {Void}
2391      * @static
2392      */
2393     clearTestRunner : function () /*:Void*/ {
2394         if (this._runner){
2395             this._runner.unsubscribeAll();
2396             this._runner = null;
2397         }
2398     },
2399     
2400     /**
2401      * Sets the source test runner that the logger should monitor.
2402      * @param {YAHOO.tool.TestRunner} testRunner The TestRunner to observe.
2403      * @return {Void}
2404      * @static
2405      */
2406     setTestRunner : function (testRunner /*:YAHOO.tool.TestRunner*/) /*:Void*/ {
2407     
2408         if (this._runner){
2409             this.clearTestRunner();
2410         }
2411         
2412         this._runner = testRunner;
2413         
2414         //setup event _handlers
2415         testRunner.subscribe(testRunner.TEST_PASS_EVENT, this._handleTestRunnerEvent, this, true);
2416         testRunner.subscribe(testRunner.TEST_FAIL_EVENT, this._handleTestRunnerEvent, this, true);
2417         testRunner.subscribe(testRunner.TEST_IGNORE_EVENT, this._handleTestRunnerEvent, this, true);
2418         testRunner.subscribe(testRunner.BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2419         testRunner.subscribe(testRunner.COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2420         testRunner.subscribe(testRunner.TEST_SUITE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2421         testRunner.subscribe(testRunner.TEST_SUITE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2422         testRunner.subscribe(testRunner.TEST_CASE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2423         testRunner.subscribe(testRunner.TEST_CASE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);    
2424     },
2425     
2426     //-------------------------------------------------------------------------
2427     // Event Handlers
2428     //-------------------------------------------------------------------------
2429     
2430     /**
2431      * Handles all TestRunner events, outputting appropriate data into the console.
2432      * @param {Object} data The event data object.
2433      * @return {Void}
2434      * @private
2435      */
2436     _handleTestRunnerEvent : function (data /*:Object*/) /*:Void*/ {
2437     
2438         //shortcut variables
2439         var TestRunner /*:Object*/ = YAHOO.tool.TestRunner;
2440     
2441         //data variables
2442         var message /*:String*/ = "";
2443         var messageType /*:String*/ = "";
2444         
2445         switch(data.type){
2446             case TestRunner.BEGIN_EVENT:
2447                 message = "Testing began at " + (new Date()).toString() + ".";
2448                 messageType = "info";
2449                 break;
2450                 
2451             case TestRunner.COMPLETE_EVENT:
2452                 message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" + 
2453                     data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2454                 messageType = "info";
2455                 break;
2456                 
2457             case TestRunner.TEST_FAIL_EVENT:
2458                 message = data.testName + ": " + data.error.getMessage();
2459                 messageType = "fail";
2460                 break;
2461                 
2462             case TestRunner.TEST_IGNORE_EVENT:
2463                 message = data.testName + ": ignored.";
2464                 messageType = "ignore";
2465                 break;
2466                 
2467             case TestRunner.TEST_PASS_EVENT:
2468                 message = data.testName + ": passed.";
2469                 messageType = "pass";
2470                 break;
2471                 
2472             case TestRunner.TEST_SUITE_BEGIN_EVENT:
2473                 message = "Test suite \"" + data.testSuite.name + "\" started.";
2474                 messageType = "info";
2475                 break;
2476                 
2477             case TestRunner.TEST_SUITE_COMPLETE_EVENT:
2478                 message = "Test suite \"" + data.testSuite.name + "\" completed.\nPassed:" + 
2479                     data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2480                 messageType = "info";
2481                 break;
2482                 
2483             case TestRunner.TEST_CASE_BEGIN_EVENT:
2484                 message = "Test case \"" + data.testCase.name + "\" started.";
2485                 messageType = "info";
2486                 break;
2487                 
2488             case TestRunner.TEST_CASE_COMPLETE_EVENT:
2489                 message = "Test case \"" + data.testCase.name + "\" completed.\nPassed:" + 
2490                     data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2491                 messageType = "info";
2492                 break;
2493             default:
2494                 message = "Unexpected event " + data.type;
2495                 message = "info";
2496         }
2497     
2498         YAHOO.log(message, messageType, "TestRunner");    
2499     }
2500     
2501 });
2502 YAHOO.namespace("tool.TestFormat");
2503
2504 /**
2505  * Returns test results formatted as a JSON string. Requires JSON utility.
2506  * @param {Object} result The results object created by TestRunner.
2507  * @return {String} An XML-formatted string of results.
2508  * @namespace YAHOO.tool.TestFormat
2509  * @method JSON
2510  * @static
2511  */
2512 YAHOO.tool.TestFormat.JSON = function(results /*:Object*/) /*:String*/ {
2513     return YAHOO.lang.JSON.stringify(results);
2514 };
2515
2516 /**
2517  * Returns test results formatted as an XML string.
2518  * @param {Object} result The results object created by TestRunner.
2519  * @return {String} An XML-formatted string of results.
2520  * @namespace YAHOO.tool.TestFormat
2521  * @method XML
2522  * @static
2523  */
2524 YAHOO.tool.TestFormat.XML = function(results /*:Object*/) /*:String*/ {
2525
2526     var l = YAHOO.lang;
2527     var xml /*:String*/ = "<" + results.type + " name=\"" + results.name.replace(/"/g, "&quot;").replace(/'/g, "&apos;") + "\"";
2528     
2529     if (l.isNumber(results.duration)){
2530         xml += " duration=\"" + results.duration + "\"";
2531     }
2532     
2533     if (results.type == "test"){
2534         xml += " result=\"" + results.result + "\" message=\"" + results.message + "\">";
2535     } else {
2536         xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
2537         for (var prop in results) {
2538             if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2539                 xml += arguments.callee(results[prop]);
2540             }
2541         }        
2542     }
2543
2544     xml += "</" + results.type + ">";
2545     
2546     return xml;
2547
2548 };
2549 YAHOO.namespace("tool");
2550
2551 /**
2552  * An object capable of sending test results to a server.
2553  * @param {String} url The URL to submit the results to.
2554  * @param {Function} format (Optiona) A function that outputs the results in a specific format.
2555  *      Default is YAHOO.tool.TestFormat.XML.
2556  * @constructor
2557  * @namespace YAHOO.tool
2558  * @class TestReporter
2559  */
2560 YAHOO.tool.TestReporter = function(url /*:String*/, format /*:Function*/) {
2561
2562     /**
2563      * The URL to submit the data to.
2564      * @type String
2565      * @property url
2566      */
2567     this.url /*:String*/ = url;
2568
2569     /**
2570      * The formatting function to call when submitting the data.
2571      * @type Function
2572      * @property format
2573      */
2574     this.format /*:Function*/ = format || YAHOO.tool.TestFormat.XML;
2575
2576     /**
2577      * Extra fields to submit with the request.
2578      * @type Object
2579      * @property _fields
2580      * @private
2581      */
2582     this._fields /*:Object*/ = new Object();
2583     
2584     /**
2585      * The form element used to submit the results.
2586      * @type HTMLFormElement
2587      * @property _form
2588      * @private
2589      */
2590     this._form /*:HTMLElement*/ = null;
2591
2592     /**
2593      * Iframe used as a target for form submission.
2594      * @type HTMLIFrameElement
2595      * @property _iframe
2596      * @private
2597      */
2598     this._iframe /*:HTMLElement*/ = null;
2599 };
2600
2601 YAHOO.tool.TestReporter.prototype = {
2602
2603     //restore missing constructor
2604     constructor: YAHOO.tool.TestReporter,
2605     
2606     /**
2607      * Convert a date into ISO format.
2608      * From Douglas Crockford's json2.js
2609      * @param {Date} date The date to convert.
2610      * @return {String} An ISO-formatted date string
2611      * @method _convertToISOString
2612      * @private
2613      */    
2614     _convertToISOString: function(date){
2615         function f(n) {
2616             // Format integers to have at least two digits.
2617             return n < 10 ? '0' + n : n;
2618         }
2619
2620         return date.getUTCFullYear()   + '-' +
2621              f(date.getUTCMonth() + 1) + '-' +
2622              f(date.getUTCDate())      + 'T' +
2623              f(date.getUTCHours())     + ':' +
2624              f(date.getUTCMinutes())   + ':' +
2625              f(date.getUTCSeconds())   + 'Z';     
2626     
2627     },
2628
2629     /**
2630      * Adds a field to the form that submits the results.
2631      * @param {String} name The name of the field.
2632      * @param {Variant} value The value of the field.
2633      * @return {Void}
2634      * @method addField
2635      */
2636     addField : function (name /*:String*/, value /*:Variant*/) /*:Void*/{
2637         this._fields[name] = value;    
2638     },
2639     
2640     /**
2641      * Removes all previous defined fields.
2642      * @return {Void}
2643      * @method addField
2644      */
2645     clearFields : function() /*:Void*/{
2646         this._fields = new Object();
2647     },
2648
2649     /**
2650      * Cleans up the memory associated with the TestReporter, removing DOM elements
2651      * that were created.
2652      * @return {Void}
2653      * @method destroy
2654      */
2655     destroy : function() /*:Void*/ {
2656         if (this._form){
2657             this._form.parentNode.removeChild(this._form);
2658             this._form = null;
2659         }        
2660         if (this._iframe){
2661             this._iframe.parentNode.removeChild(this._iframe);
2662             this._iframe = null;
2663         }
2664         this._fields = null;
2665     },
2666
2667     /**
2668      * Sends the report to the server.
2669      * @param {Object} results The results object created by TestRunner.
2670      * @return {Void}
2671      * @method report
2672      */
2673     report : function(results /*:Object*/) /*:Void*/{
2674     
2675         //if the form hasn't been created yet, create it
2676         if (!this._form){
2677             this._form = document.createElement("form");
2678             this._form.method = "post";
2679             this._form.style.visibility = "hidden";
2680             this._form.style.position = "absolute";
2681             this._form.style.top = 0;
2682             document.body.appendChild(this._form);
2683         
2684             //IE won't let you assign a name using the DOM, must do it the hacky way
2685             if (YAHOO.env.ua.ie){
2686                 this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
2687             } else {
2688                 this._iframe = document.createElement("iframe");
2689                 this._iframe.name = "yuiTestTarget";
2690             }
2691
2692             this._iframe.src = "javascript:false";
2693             this._iframe.style.visibility = "hidden";
2694             this._iframe.style.position = "absolute";
2695             this._iframe.style.top = 0;
2696             document.body.appendChild(this._iframe);
2697
2698             this._form.target = "yuiTestTarget";
2699         }
2700
2701         //set the form's action
2702         this._form.action = this.url;
2703     
2704         //remove any existing fields
2705         while(this._form.hasChildNodes()){
2706             this._form.removeChild(this._form.lastChild);
2707         }
2708         
2709         //create default fields
2710         this._fields.results = this.format(results);
2711         this._fields.useragent = navigator.userAgent;
2712         this._fields.timestamp = this._convertToISOString(new Date());
2713
2714         //add fields to the form
2715         for (var prop in this._fields){
2716             if (YAHOO.lang.hasOwnProperty(this._fields, prop) && typeof this._fields[prop] != "function"){
2717                 if (YAHOO.env.ua.ie){
2718                     input = document.createElement("<input name=\"" + prop + "\" >");
2719                 } else {
2720                     input = document.createElement("input");
2721                     input.name = prop;
2722                 }
2723                 input.type = "hidden";
2724                 input.value = this._fields[prop];
2725                 this._form.appendChild(input);
2726             }
2727         }
2728
2729         //remove default fields
2730         delete this._fields.results;
2731         delete this._fields.useragent;
2732         delete this._fields.timestamp;
2733         
2734         if (arguments[1] !== false){
2735             this._form.submit();
2736         }
2737     
2738     }
2739
2740 };
2741 YAHOO.register("yuitest", YAHOO.tool.TestRunner, {version: "2.8.0r4", build: "2449"});