]> ToastFreeware Gitweb - philipp/winterrodeln/wradmin.git/blob - wradmin/public/yui/yuitest/yuitest-debug.js
Start to use flask instead of pylons.
[philipp/winterrodeln/wradmin.git] / wradmin / public / yui / yuitest / yuitest-debug.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.7.0
6 */
7 YAHOO.namespace("tool");
8
9 //-----------------------------------------------------------------------------
10 // TestCase object
11 //-----------------------------------------------------------------------------
12 (function(){
13     
14     //used for autogenerating test case names
15     var tempId = 0;
16     
17     /**
18      * Test case containing various tests to run.
19      * @param template An object containing any number of test methods, other methods,
20      *                 an optional name, and anything else the test case needs.
21      * @class TestCase
22      * @namespace YAHOO.tool
23      * @constructor
24      */
25     YAHOO.tool.TestCase = function (template /*:Object*/) {
26         
27         /**
28          * Special rules for the test case. Possible subobjects
29          * are fail, for tests that should fail, and error, for
30          * tests that should throw an error.
31          */
32         this._should /*:Object*/ = {};
33         
34         //copy over all properties from the template to this object
35         for (var prop in template) {
36             this[prop] = template[prop];
37         }    
38         
39         //check for a valid name
40         if (!YAHOO.lang.isString(this.name)){
41             /**
42              * Name for the test case.
43              */
44             this.name /*:String*/ = "testCase" + (tempId++);
45         }
46     
47     };
48     
49     
50     YAHOO.tool.TestCase.prototype = {  
51     
52         /**
53          * Resumes a paused test and runs the given function.
54          * @param {Function} segment (Optional) The function to run.
55          *      If omitted, the test automatically passes.
56          * @return {Void}
57          * @method resume
58          */
59         resume : function (segment /*:Function*/) /*:Void*/ {
60             YAHOO.tool.TestRunner.resume(segment);
61         },
62     
63         /**
64          * Causes the test case to wait a specified amount of time and then
65          * continue executing the given code.
66          * @param {Function} segment (Optional) The function to run after the delay.
67          *      If omitted, the TestRunner will wait until resume() is called.
68          * @param {int} delay (Optional) The number of milliseconds to wait before running
69          *      the function. If omitted, defaults to zero.
70          * @return {Void}
71          * @method wait
72          */
73         wait : function (segment /*:Function*/, delay /*:int*/) /*:Void*/{
74             var args = arguments;
75             if (YAHOO.lang.isFunction(args[0])){
76                 throw new YAHOO.tool.TestCase.Wait(args[0], args[1]);
77             } else {
78                 throw new YAHOO.tool.TestCase.Wait(function(){
79                     YAHOO.util.Assert.fail("Timeout: wait() called but resume() never called.");
80                 }, (YAHOO.lang.isNumber(args[0]) ? args[0] : 10000));
81             }            
82         },
83     
84         //-------------------------------------------------------------------------
85         // Stub Methods
86         //-------------------------------------------------------------------------
87     
88         /**
89          * Function to run before each test is executed.
90          * @return {Void}
91          * @method setUp
92          */
93         setUp : function () /*:Void*/ {
94         },
95         
96         /**
97          * Function to run after each test is executed.
98          * @return {Void}
99          * @method tearDown
100          */
101         tearDown: function () /*:Void*/ {    
102         }
103     };
104     
105     /**
106      * Represents a stoppage in test execution to wait for an amount of time before
107      * continuing.
108      * @param {Function} segment A function to run when the wait is over.
109      * @param {int} delay The number of milliseconds to wait before running the code.
110      * @class Wait
111      * @namespace YAHOO.tool.TestCase
112      * @constructor
113      *
114      */
115     YAHOO.tool.TestCase.Wait = function (segment /*:Function*/, delay /*:int*/) {
116         
117         /**
118          * The segment of code to run when the wait is over.
119          * @type Function
120          * @property segment
121          */
122         this.segment /*:Function*/ = (YAHOO.lang.isFunction(segment) ? segment : null);
123     
124         /**
125          * The delay before running the segment of code.
126          * @type int
127          * @property delay
128          */
129         this.delay /*:int*/ = (YAHOO.lang.isNumber(delay) ? delay : 0);
130     
131     };
132
133 })();
134
135 YAHOO.namespace("tool");
136
137
138 //-----------------------------------------------------------------------------
139 // TestSuite object
140 //-----------------------------------------------------------------------------
141
142 /**
143  * A test suite that can contain a collection of TestCase and TestSuite objects.
144  * @param {String||Object} data The name of the test suite or an object containing
145  *      a name property as well as setUp and tearDown methods.
146  * @namespace YAHOO.tool
147  * @class TestSuite
148  * @constructor
149  */
150 YAHOO.tool.TestSuite = function (data /*:String||Object*/) {
151
152     /**
153      * The name of the test suite.
154      * @type String
155      * @property name
156      */
157     this.name /*:String*/ = "";
158
159     /**
160      * Array of test suites and
161      * @private
162      */
163     this.items /*:Array*/ = [];
164
165     //initialize the properties
166     if (YAHOO.lang.isString(data)){
167         this.name = data;
168     } else if (YAHOO.lang.isObject(data)){
169         YAHOO.lang.augmentObject(this, data, true);
170     }
171
172     //double-check name
173     if (this.name === ""){
174         this.name = YAHOO.util.Dom.generateId(null, "testSuite");
175     }
176
177 };
178
179 YAHOO.tool.TestSuite.prototype = {
180     
181     /**
182      * Adds a test suite or test case to the test suite.
183      * @param {YAHOO.tool.TestSuite||YAHOO.tool.TestCase} testObject The test suite or test case to add.
184      * @return {Void}
185      * @method add
186      */
187     add : function (testObject /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
188         if (testObject instanceof YAHOO.tool.TestSuite || testObject instanceof YAHOO.tool.TestCase) {
189             this.items.push(testObject);
190         }
191     },
192     
193     //-------------------------------------------------------------------------
194     // Stub Methods
195     //-------------------------------------------------------------------------
196
197     /**
198      * Function to run before each test is executed.
199      * @return {Void}
200      * @method setUp
201      */
202     setUp : function () /*:Void*/ {
203     },
204     
205     /**
206      * Function to run after each test is executed.
207      * @return {Void}
208      * @method tearDown
209      */
210     tearDown: function () /*:Void*/ {
211     }
212     
213 };
214
215 YAHOO.namespace("tool");
216
217 /**
218  * The YUI test tool
219  * @module yuitest
220  * @namespace YAHOO.tool
221  * @requires yahoo,dom,event,logger
222  */
223
224
225 //-----------------------------------------------------------------------------
226 // TestRunner object
227 //-----------------------------------------------------------------------------
228
229
230 YAHOO.tool.TestRunner = (function(){
231
232     /**
233      * A node in the test tree structure. May represent a TestSuite, TestCase, or
234      * test function.
235      * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
236      * @class TestNode
237      * @constructor
238      * @private
239      */
240     function TestNode(testObject /*:Variant*/){
241     
242         /**
243          * The TestSuite, TestCase, or test function represented by this node.
244          * @type Variant
245          * @property testObject
246          */
247         this.testObject = testObject;
248         
249         /**
250          * Pointer to this node's first child.
251          * @type TestNode
252          * @property firstChild
253          */        
254         this.firstChild /*:TestNode*/ = null;
255         
256         /**
257          * Pointer to this node's last child.
258          * @type TestNode
259          * @property lastChild
260          */        
261         this.lastChild = null;
262         
263         /**
264          * Pointer to this node's parent.
265          * @type TestNode
266          * @property parent
267          */        
268         this.parent = null; 
269    
270         /**
271          * Pointer to this node's next sibling.
272          * @type TestNode
273          * @property next
274          */        
275         this.next = null;
276         
277         /**
278          * Test results for this test object.
279          * @type object
280          * @property results
281          */                
282         this.results /*:Object*/ = {
283             passed : 0,
284             failed : 0,
285             total : 0,
286             ignored : 0
287         };
288         
289         //initialize results
290         if (testObject instanceof YAHOO.tool.TestSuite){
291             this.results.type = "testsuite";
292             this.results.name = testObject.name;
293         } else if (testObject instanceof YAHOO.tool.TestCase){
294             this.results.type = "testcase";
295             this.results.name = testObject.name;
296         }
297        
298     }
299     
300     TestNode.prototype = {
301     
302         /**
303          * Appends a new test object (TestSuite, TestCase, or test function name) as a child
304          * of this node.
305          * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
306          * @return {Void}
307          */
308         appendChild : function (testObject /*:Variant*/) /*:Void*/{
309             var node = new TestNode(testObject);
310             if (this.firstChild === null){
311                 this.firstChild = this.lastChild = node;
312             } else {
313                 this.lastChild.next = node;
314                 this.lastChild = node;
315             }
316             node.parent = this;
317             return node;
318         }       
319     };
320
321     /**
322      * Runs test suites and test cases, providing events to allowing for the
323      * interpretation of test results.
324      * @namespace YAHOO.tool
325      * @class TestRunner
326      * @static
327      */
328     function TestRunner(){
329     
330         //inherit from EventProvider
331         TestRunner.superclass.constructor.apply(this,arguments);
332         
333         /**
334          * Suite on which to attach all TestSuites and TestCases to be run.
335          * @type YAHOO.tool.TestSuite
336          * @property masterSuite
337          * @private
338          * @static
339          */
340         this.masterSuite /*:YAHOO.tool.TestSuite*/ = new YAHOO.tool.TestSuite("YUI Test Results");        
341
342         /**
343          * Pointer to the current node in the test tree.
344          * @type TestNode
345          * @private
346          * @property _cur
347          * @static
348          */
349         this._cur = null;
350         
351         /**
352          * Pointer to the root node in the test tree.
353          * @type TestNode
354          * @private
355          * @property _root
356          * @static
357          */
358         this._root = null;
359         
360         //create events
361         var events /*:Array*/ = [
362             this.TEST_CASE_BEGIN_EVENT,
363             this.TEST_CASE_COMPLETE_EVENT,
364             this.TEST_SUITE_BEGIN_EVENT,
365             this.TEST_SUITE_COMPLETE_EVENT,
366             this.TEST_PASS_EVENT,
367             this.TEST_FAIL_EVENT,
368             this.TEST_IGNORE_EVENT,
369             this.COMPLETE_EVENT,
370             this.BEGIN_EVENT
371         ];
372         for (var i=0; i < events.length; i++){
373             this.createEvent(events[i], { scope: this });
374         }       
375    
376     }
377     
378     YAHOO.lang.extend(TestRunner, YAHOO.util.EventProvider, {
379     
380         //-------------------------------------------------------------------------
381         // Constants
382         //-------------------------------------------------------------------------
383          
384         /**
385          * Fires when a test case is opened but before the first 
386          * test is executed.
387          * @event testcasebegin
388          */         
389         TEST_CASE_BEGIN_EVENT /*:String*/ : "testcasebegin",
390         
391         /**
392          * Fires when all tests in a test case have been executed.
393          * @event testcasecomplete
394          */        
395         TEST_CASE_COMPLETE_EVENT /*:String*/ : "testcasecomplete",
396         
397         /**
398          * Fires when a test suite is opened but before the first 
399          * test is executed.
400          * @event testsuitebegin
401          */        
402         TEST_SUITE_BEGIN_EVENT /*:String*/ : "testsuitebegin",
403         
404         /**
405          * Fires when all test cases in a test suite have been
406          * completed.
407          * @event testsuitecomplete
408          */        
409         TEST_SUITE_COMPLETE_EVENT /*:String*/ : "testsuitecomplete",
410         
411         /**
412          * Fires when a test has passed.
413          * @event pass
414          */        
415         TEST_PASS_EVENT /*:String*/ : "pass",
416         
417         /**
418          * Fires when a test has failed.
419          * @event fail
420          */        
421         TEST_FAIL_EVENT /*:String*/ : "fail",
422         
423         /**
424          * Fires when a test has been ignored.
425          * @event ignore
426          */        
427         TEST_IGNORE_EVENT /*:String*/ : "ignore",
428         
429         /**
430          * Fires when all test suites and test cases have been completed.
431          * @event complete
432          */        
433         COMPLETE_EVENT /*:String*/ : "complete",
434         
435         /**
436          * Fires when the run() method is called.
437          * @event begin
438          */        
439         BEGIN_EVENT /*:String*/ : "begin",    
440         
441         //-------------------------------------------------------------------------
442         // Test Tree-Related Methods
443         //-------------------------------------------------------------------------
444
445         /**
446          * Adds a test case to the test tree as a child of the specified node.
447          * @param {TestNode} parentNode The node to add the test case to as a child.
448          * @param {YAHOO.tool.TestCase} testCase The test case to add.
449          * @return {Void}
450          * @static
451          * @private
452          * @method _addTestCaseToTestTree
453          */
454        _addTestCaseToTestTree : function (parentNode /*:TestNode*/, testCase /*:YAHOO.tool.TestCase*/) /*:Void*/{
455             
456             //add the test suite
457             var node = parentNode.appendChild(testCase);
458             
459             //iterate over the items in the test case
460             for (var prop in testCase){
461                 if (prop.indexOf("test") === 0 && YAHOO.lang.isFunction(testCase[prop])){
462                     node.appendChild(prop);
463                 }
464             }
465          
466         },
467         
468         /**
469          * Adds a test suite to the test tree as a child of the specified node.
470          * @param {TestNode} parentNode The node to add the test suite to as a child.
471          * @param {YAHOO.tool.TestSuite} testSuite The test suite to add.
472          * @return {Void}
473          * @static
474          * @private
475          * @method _addTestSuiteToTestTree
476          */
477         _addTestSuiteToTestTree : function (parentNode /*:TestNode*/, testSuite /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
478             
479             //add the test suite
480             var node = parentNode.appendChild(testSuite);
481             
482             //iterate over the items in the master suite
483             for (var i=0; i < testSuite.items.length; i++){
484                 if (testSuite.items[i] instanceof YAHOO.tool.TestSuite) {
485                     this._addTestSuiteToTestTree(node, testSuite.items[i]);
486                 } else if (testSuite.items[i] instanceof YAHOO.tool.TestCase) {
487                     this._addTestCaseToTestTree(node, testSuite.items[i]);
488                 }                   
489             }            
490         },
491         
492         /**
493          * Builds the test tree based on items in the master suite. The tree is a hierarchical
494          * representation of the test suites, test cases, and test functions. The resulting tree
495          * is stored in _root and the pointer _cur is set to the root initially.
496          * @return {Void}
497          * @static
498          * @private
499          * @method _buildTestTree
500          */
501         _buildTestTree : function () /*:Void*/ {
502         
503             this._root = new TestNode(this.masterSuite);
504             this._cur = this._root;
505             
506             //iterate over the items in the master suite
507             for (var i=0; i < this.masterSuite.items.length; i++){
508                 if (this.masterSuite.items[i] instanceof YAHOO.tool.TestSuite) {
509                     this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
510                 } else if (this.masterSuite.items[i] instanceof YAHOO.tool.TestCase) {
511                     this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
512                 }                   
513             }            
514         
515         }, 
516     
517         //-------------------------------------------------------------------------
518         // Private Methods
519         //-------------------------------------------------------------------------
520         
521         /**
522          * Handles the completion of a test object's tests. Tallies test results 
523          * from one level up to the next.
524          * @param {TestNode} node The TestNode representing the test object.
525          * @return {Void}
526          * @method _handleTestObjectComplete
527          * @private
528          * @static
529          */
530         _handleTestObjectComplete : function (node /*:TestNode*/) /*:Void*/ {
531             if (YAHOO.lang.isObject(node.testObject)){
532                 node.parent.results.passed += node.results.passed;
533                 node.parent.results.failed += node.results.failed;
534                 node.parent.results.total += node.results.total;                
535                 node.parent.results.ignored += node.results.ignored;                
536                 node.parent.results[node.testObject.name] = node.results;
537             
538                 if (node.testObject instanceof YAHOO.tool.TestSuite){
539                     node.testObject.tearDown();
540                     this.fireEvent(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
541                 } else if (node.testObject instanceof YAHOO.tool.TestCase){
542                     this.fireEvent(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
543                 }      
544             } 
545         },                
546         
547         //-------------------------------------------------------------------------
548         // Navigation Methods
549         //-------------------------------------------------------------------------
550         
551         /**
552          * Retrieves the next node in the test tree.
553          * @return {TestNode} The next node in the test tree or null if the end is reached.
554          * @private
555          * @static
556          * @method _next
557          */
558         _next : function () /*:TestNode*/ {
559         
560             if (this._cur.firstChild) {
561                 this._cur = this._cur.firstChild;
562             } else if (this._cur.next) {
563                 this._cur = this._cur.next;            
564             } else {
565                 while (this._cur && !this._cur.next && this._cur !== this._root){
566                     this._handleTestObjectComplete(this._cur);
567                     this._cur = this._cur.parent;
568                 }
569                 
570                 if (this._cur == this._root){
571                     this._cur.results.type = "report";
572                     this._cur.results.timestamp = (new Date()).toLocaleString();
573                     this._cur.results.duration = (new Date()) - this._cur.results.duration;
574                     this.fireEvent(this.COMPLETE_EVENT, { results: this._cur.results});
575                     this._cur = null;
576                 } else {
577                     this._handleTestObjectComplete(this._cur);               
578                     this._cur = this._cur.next;                
579                 }
580             }
581         
582             return this._cur;
583         },
584         
585         /**
586          * Runs a test case or test suite, returning the results.
587          * @param {YAHOO.tool.TestCase|YAHOO.tool.TestSuite} testObject The test case or test suite to run.
588          * @return {Object} Results of the execution with properties passed, failed, and total.
589          * @private
590          * @method _run
591          * @static
592          */
593         _run : function () /*:Void*/ {
594         
595             //flag to indicate if the TestRunner should wait before continuing
596             var shouldWait /*:Boolean*/ = false;
597             
598             //get the next test node
599             var node = this._next();
600             
601             if (node !== null) {
602                 var testObject = node.testObject;
603                 
604                 //figure out what to do
605                 if (YAHOO.lang.isObject(testObject)){
606                     if (testObject instanceof YAHOO.tool.TestSuite){
607                         this.fireEvent(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
608                         testObject.setUp();
609                     } else if (testObject instanceof YAHOO.tool.TestCase){
610                         this.fireEvent(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
611                     }
612                     
613                     //some environments don't support setTimeout
614                     if (typeof setTimeout != "undefined"){                    
615                         setTimeout(function(){
616                             YAHOO.tool.TestRunner._run();
617                         }, 0);
618                     } else {
619                         this._run();
620                     }
621                 } else {
622                     this._runTest(node);
623                 }
624
625             }
626         },
627         
628         _resumeTest : function (segment /*:Function*/) /*:Void*/ {
629         
630             //get relevant information
631             var node /*:TestNode*/ = this._cur;
632             var testName /*:String*/ = node.testObject;
633             var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
634             
635             //cancel other waits if available
636             if (testCase.__yui_wait){
637                 clearTimeout(testCase.__yui_wait);
638                 delete testCase.__yui_wait;
639             }            
640             
641             //get the "should" test cases
642             var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
643             var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
644             
645             //variable to hold whether or not the test failed
646             var failed /*:Boolean*/ = false;
647             var error /*:Error*/ = null;
648                 
649             //try the test
650             try {
651             
652                 //run the test
653                 segment.apply(testCase);
654                 
655                 //if it should fail, and it got here, then it's a fail because it didn't
656                 if (shouldFail){
657                     error = new YAHOO.util.ShouldFail();
658                     failed = true;
659                 } else if (shouldError){
660                     error = new YAHOO.util.ShouldError();
661                     failed = true;
662                 }
663                            
664             } catch (thrown /*:Error*/){
665                 if (thrown instanceof YAHOO.util.AssertionError) {
666                     if (!shouldFail){
667                         error = thrown;
668                         failed = true;
669                     }
670                 } else if (thrown instanceof YAHOO.tool.TestCase.Wait){
671                 
672                     if (YAHOO.lang.isFunction(thrown.segment)){
673                         if (YAHOO.lang.isNumber(thrown.delay)){
674                         
675                             //some environments don't support setTimeout
676                             if (typeof setTimeout != "undefined"){
677                                 testCase.__yui_wait = setTimeout(function(){
678                                     YAHOO.tool.TestRunner._resumeTest(thrown.segment);
679                                 }, thrown.delay);                             
680                             } else {
681                                 throw new Error("Asynchronous tests not supported in this environment.");
682                             }
683                         }
684                     }
685                     
686                     return;
687                 
688                 } else {
689                     //first check to see if it should error
690                     if (!shouldError) {                        
691                         error = new YAHOO.util.UnexpectedError(thrown);
692                         failed = true;
693                     } else {
694                         //check to see what type of data we have
695                         if (YAHOO.lang.isString(shouldError)){
696                             
697                             //if it's a string, check the error message
698                             if (thrown.message != shouldError){
699                                 error = new YAHOO.util.UnexpectedError(thrown);
700                                 failed = true;                                    
701                             }
702                         } else if (YAHOO.lang.isFunction(shouldError)){
703                         
704                             //if it's a function, see if the error is an instance of it
705                             if (!(thrown instanceof shouldError)){
706                                 error = new YAHOO.util.UnexpectedError(thrown);
707                                 failed = true;
708                             }
709                         
710                         } else if (YAHOO.lang.isObject(shouldError)){
711                         
712                             //if it's an object, check the instance and message
713                             if (!(thrown instanceof shouldError.constructor) || 
714                                     thrown.message != shouldError.message){
715                                 error = new YAHOO.util.UnexpectedError(thrown);
716                                 failed = true;                                    
717                             }
718                         
719                         }
720                     
721                     }
722                 }
723                 
724             }
725             
726             //fireEvent appropriate event
727             if (failed) {
728                 this.fireEvent(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
729             } else {
730                 this.fireEvent(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
731             }
732             
733             //run the tear down
734             testCase.tearDown();
735             
736             //update results
737             node.parent.results[testName] = { 
738                 result: failed ? "fail" : "pass",
739                 message: error ? error.getMessage() : "Test passed",
740                 type: "test",
741                 name: testName
742             };
743             
744             if (failed){
745                 node.parent.results.failed++;
746             } else {
747                 node.parent.results.passed++;
748             }
749             node.parent.results.total++;
750
751             //set timeout not supported in all environments
752             if (typeof setTimeout != "undefined"){
753                 setTimeout(function(){
754                     YAHOO.tool.TestRunner._run();
755                 }, 0);
756             } else {
757                 this._run();
758             }
759         
760         },
761                 
762         /**
763          * Runs a single test based on the data provided in the node.
764          * @param {TestNode} node The TestNode representing the test to run.
765          * @return {Void}
766          * @static
767          * @private
768          * @name _runTest
769          */
770         _runTest : function (node /*:TestNode*/) /*:Void*/ {
771         
772             //get relevant information
773             var testName /*:String*/ = node.testObject;
774             var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
775             var test /*:Function*/ = testCase[testName];
776             
777             //get the "should" test cases
778             var shouldIgnore /*:Object*/ = (testCase._should.ignore || {})[testName];
779             
780             //figure out if the test should be ignored or not
781             if (shouldIgnore){
782             
783                 //update results
784                 node.parent.results[testName] = { 
785                     result: "ignore",
786                     message: "Test ignored",
787                     type: "test",
788                     name: testName
789                 };
790                 
791                 node.parent.results.ignored++;
792                 node.parent.results.total++;
793             
794                 this.fireEvent(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
795                 
796                 //some environments don't support setTimeout
797                 if (typeof setTimeout != "undefined"){                    
798                     setTimeout(function(){
799                         YAHOO.tool.TestRunner._run();
800                     }, 0);              
801                 } else {
802                     this._run();
803                 }
804
805             } else {
806             
807                 //run the setup
808                 testCase.setUp();
809                 
810                 //now call the body of the test
811                 this._resumeTest(test);                
812             }
813
814         },        
815         
816         //-------------------------------------------------------------------------
817         // Protected Methods
818         //-------------------------------------------------------------------------   
819     
820         /**
821          * Fires events for the TestRunner. This overrides the default fireEvent()
822          * method from EventProvider to add the type property to the data that is
823          * passed through on each event call.
824          * @param {String} type The type of event to fire.
825          * @param {Object} data (Optional) Data for the event.
826          * @method fireEvent
827          * @static
828          * @protected
829          */
830         fireEvent : function (type /*:String*/, data /*:Object*/) /*:Void*/ {
831             data = data || {};
832             data.type = type;
833             TestRunner.superclass.fireEvent.call(this, type, data);
834         },
835         
836         //-------------------------------------------------------------------------
837         // Public Methods
838         //-------------------------------------------------------------------------   
839     
840         /**
841          * Adds a test suite or test case to the list of test objects to run.
842          * @param testObject Either a TestCase or a TestSuite that should be run.
843          * @return {Void}
844          * @method add
845          * @static
846          */
847         add : function (testObject /*:Object*/) /*:Void*/ {
848             this.masterSuite.add(testObject);
849         },
850         
851         /**
852          * Removes all test objects from the runner.
853          * @return {Void}
854          * @method clear
855          * @static
856          */
857         clear : function () /*:Void*/ {
858             this.masterSuite.items = [];
859         },
860         
861         /**
862          * Resumes the TestRunner after wait() was called.
863          * @param {Function} segment The function to run as the rest
864          *      of the haulted test.
865          * @return {Void}
866          * @method resume
867          * @static
868          */
869         resume : function (segment /*:Function*/) /*:Void*/ {
870             this._resumeTest(segment || function(){});
871         },
872     
873         /**
874          * Runs the test suite.
875          * @return {Void}
876          * @method run
877          * @static
878          */
879         run : function (testObject /*:Object*/) /*:Void*/ {
880             
881             //pointer to runner to avoid scope issues 
882             var runner = YAHOO.tool.TestRunner;
883
884             //build the test tree
885             runner._buildTestTree();
886             
887             //set when the test started
888             runner._root.results.duration = (new Date()).valueOf();
889             
890             //fire the begin event
891             runner.fireEvent(runner.BEGIN_EVENT);
892        
893             //begin the testing
894             runner._run();
895         }    
896     });
897     
898     return new TestRunner();
899     
900 })();
901
902 YAHOO.namespace("util");
903
904 //-----------------------------------------------------------------------------
905 // Assert object
906 //-----------------------------------------------------------------------------
907
908 /**
909  * The Assert object provides functions to test JavaScript values against
910  * known and expected results. Whenever a comparison (assertion) fails,
911  * an error is thrown.
912  *
913  * @namespace YAHOO.util
914  * @class Assert
915  * @static
916  */
917 YAHOO.util.Assert = {
918
919     //-------------------------------------------------------------------------
920     // Helper Methods
921     //-------------------------------------------------------------------------
922     
923     /**
924      * Formats a message so that it can contain the original assertion message
925      * in addition to the custom message.
926      * @param {String} customMessage The message passed in by the developer.
927      * @param {String} defaultMessage The message created by the error by default.
928      * @return {String} The final error message, containing either or both.
929      * @protected
930      * @static
931      * @method _formatMessage
932      */
933     _formatMessage : function (customMessage /*:String*/, defaultMessage /*:String*/) /*:String*/ {
934         var message = customMessage;
935         if (YAHOO.lang.isString(customMessage) && customMessage.length > 0){
936             return YAHOO.lang.substitute(customMessage, { message: defaultMessage });
937         } else {
938             return defaultMessage;
939         }        
940     },
941     
942     //-------------------------------------------------------------------------
943     // Generic Assertion Methods
944     //-------------------------------------------------------------------------
945     
946     /** 
947      * Forces an assertion error to occur.
948      * @param {String} message (Optional) The message to display with the failure.
949      * @method fail
950      * @static
951      */
952     fail : function (message /*:String*/) /*:Void*/ {
953         throw new YAHOO.util.AssertionError(this._formatMessage(message, "Test force-failed."));
954     },       
955     
956     //-------------------------------------------------------------------------
957     // Equality Assertion Methods
958     //-------------------------------------------------------------------------    
959     
960     /**
961      * Asserts that a value is equal to another. This uses the double equals sign
962      * so type cohersion may occur.
963      * @param {Object} expected The expected value.
964      * @param {Object} actual The actual value to test.
965      * @param {String} message (Optional) The message to display if the assertion fails.
966      * @method areEqual
967      * @static
968      */
969     areEqual : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
970         if (expected != actual) {
971             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be equal."), expected, actual);
972         }
973     },
974     
975     /**
976      * Asserts that a value is not equal to another. This uses the double equals sign
977      * so type cohersion may occur.
978      * @param {Object} unexpected The unexpected value.
979      * @param {Object} actual The actual value to test.
980      * @param {String} message (Optional) The message to display if the assertion fails.
981      * @method areNotEqual
982      * @static
983      */
984     areNotEqual : function (unexpected /*:Object*/, actual /*:Object*/, 
985                          message /*:String*/) /*:Void*/ {
986         if (unexpected == actual) {
987             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be equal."), unexpected);
988         }
989     },
990     
991     /**
992      * Asserts that a value is not the same as another. This uses the triple equals sign
993      * so no type cohersion may occur.
994      * @param {Object} unexpected The unexpected value.
995      * @param {Object} actual The actual value to test.
996      * @param {String} message (Optional) The message to display if the assertion fails.
997      * @method areNotSame
998      * @static
999      */
1000     areNotSame : function (unexpected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1001         if (unexpected === actual) {
1002             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be the same."), unexpected);
1003         }
1004     },
1005
1006     /**
1007      * Asserts that a value is the same as another. This uses the triple equals sign
1008      * so no type cohersion may occur.
1009      * @param {Object} expected The expected value.
1010      * @param {Object} actual The actual value to test.
1011      * @param {String} message (Optional) The message to display if the assertion fails.
1012      * @method areSame
1013      * @static
1014      */
1015     areSame : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1016         if (expected !== actual) {
1017             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be the same."), expected, actual);
1018         }
1019     },    
1020     
1021     //-------------------------------------------------------------------------
1022     // Boolean Assertion Methods
1023     //-------------------------------------------------------------------------    
1024     
1025     /**
1026      * Asserts that a value is false. This uses the triple equals sign
1027      * so no type cohersion may occur.
1028      * @param {Object} actual The actual value to test.
1029      * @param {String} message (Optional) The message to display if the assertion fails.
1030      * @method isFalse
1031      * @static
1032      */
1033     isFalse : function (actual /*:Boolean*/, message /*:String*/) {
1034         if (false !== actual) {
1035             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be false."), false, actual);
1036         }
1037     },
1038     
1039     /**
1040      * Asserts that a value is true. This uses the triple equals sign
1041      * so no type cohersion may occur.
1042      * @param {Object} actual The actual value to test.
1043      * @param {String} message (Optional) The message to display if the assertion fails.
1044      * @method isTrue
1045      * @static
1046      */
1047     isTrue : function (actual /*:Boolean*/, message /*:String*/) /*:Void*/ {
1048         if (true !== actual) {
1049             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be true."), true, actual);
1050         }
1051
1052     },
1053     
1054     //-------------------------------------------------------------------------
1055     // Special Value Assertion Methods
1056     //-------------------------------------------------------------------------    
1057     
1058     /**
1059      * Asserts that a value is not a number.
1060      * @param {Object} actual The value to test.
1061      * @param {String} message (Optional) The message to display if the assertion fails.
1062      * @method isNaN
1063      * @static
1064      */
1065     isNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1066         if (!isNaN(actual)){
1067             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be NaN."), NaN, actual);
1068         }    
1069     },
1070     
1071     /**
1072      * Asserts that a value is not the special NaN value.
1073      * @param {Object} actual The value to test.
1074      * @param {String} message (Optional) The message to display if the assertion fails.
1075      * @method isNotNaN
1076      * @static
1077      */
1078     isNotNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1079         if (isNaN(actual)){
1080             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be NaN."), NaN);
1081         }    
1082     },
1083     
1084     /**
1085      * Asserts that a value is not null. This uses the triple equals sign
1086      * so no type cohersion may occur.
1087      * @param {Object} actual The actual value to test.
1088      * @param {String} message (Optional) The message to display if the assertion fails.
1089      * @method isNotNull
1090      * @static
1091      */
1092     isNotNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1093         if (YAHOO.lang.isNull(actual)) {
1094             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be null."), null);
1095         }
1096     },
1097
1098     /**
1099      * Asserts that a value is not undefined. This uses the triple equals sign
1100      * so no type cohersion may occur.
1101      * @param {Object} actual The actual value to test.
1102      * @param {String} message (Optional) The message to display if the assertion fails.
1103      * @method isNotUndefined
1104      * @static
1105      */
1106     isNotUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1107         if (YAHOO.lang.isUndefined(actual)) {
1108             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should not be undefined."), undefined);
1109         }
1110     },
1111
1112     /**
1113      * Asserts that a value is null. This uses the triple equals sign
1114      * so no type cohersion may occur.
1115      * @param {Object} actual The actual value to test.
1116      * @param {String} message (Optional) The message to display if the assertion fails.
1117      * @method isNull
1118      * @static
1119      */
1120     isNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1121         if (!YAHOO.lang.isNull(actual)) {
1122             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be null."), null, actual);
1123         }
1124     },
1125         
1126     /**
1127      * Asserts that a value is undefined. This uses the triple equals sign
1128      * so no type cohersion may occur.
1129      * @param {Object} actual The actual value to test.
1130      * @param {String} message (Optional) The message to display if the assertion fails.
1131      * @method isUndefined
1132      * @static
1133      */
1134     isUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1135         if (!YAHOO.lang.isUndefined(actual)) {
1136             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be undefined."), undefined, actual);
1137         }
1138     },    
1139     
1140     //--------------------------------------------------------------------------
1141     // Instance Assertion Methods
1142     //--------------------------------------------------------------------------    
1143    
1144     /**
1145      * Asserts that a value is an array.
1146      * @param {Object} actual The value to test.
1147      * @param {String} message (Optional) The message to display if the assertion fails.
1148      * @method isArray
1149      * @static
1150      */
1151     isArray : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1152         if (!YAHOO.lang.isArray(actual)){
1153             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an array."), actual);
1154         }    
1155     },
1156    
1157     /**
1158      * Asserts that a value is a Boolean.
1159      * @param {Object} actual The value to test.
1160      * @param {String} message (Optional) The message to display if the assertion fails.
1161      * @method isBoolean
1162      * @static
1163      */
1164     isBoolean : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1165         if (!YAHOO.lang.isBoolean(actual)){
1166             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a Boolean."), actual);
1167         }    
1168     },
1169    
1170     /**
1171      * Asserts that a value is a function.
1172      * @param {Object} actual The value to test.
1173      * @param {String} message (Optional) The message to display if the assertion fails.
1174      * @method isFunction
1175      * @static
1176      */
1177     isFunction : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1178         if (!YAHOO.lang.isFunction(actual)){
1179             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a function."), actual);
1180         }    
1181     },
1182    
1183     /**
1184      * Asserts that a value is an instance of a particular object. This may return
1185      * incorrect results when comparing objects from one frame to constructors in
1186      * another frame. For best results, don't use in a cross-frame manner.
1187      * @param {Function} expected The function that the object should be an instance of.
1188      * @param {Object} actual The object to test.
1189      * @param {String} message (Optional) The message to display if the assertion fails.
1190      * @method isInstanceOf
1191      * @static
1192      */
1193     isInstanceOf : function (expected /*:Function*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1194         if (!(actual instanceof expected)){
1195             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1196         }
1197     },
1198     
1199     /**
1200      * Asserts that a value is a number.
1201      * @param {Object} actual The value to test.
1202      * @param {String} message (Optional) The message to display if the assertion fails.
1203      * @method isNumber
1204      * @static
1205      */
1206     isNumber : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1207         if (!YAHOO.lang.isNumber(actual)){
1208             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a number."), actual);
1209         }    
1210     },    
1211     
1212     /**
1213      * Asserts that a value is an object.
1214      * @param {Object} actual The value to test.
1215      * @param {String} message (Optional) The message to display if the assertion fails.
1216      * @method isObject
1217      * @static
1218      */
1219     isObject : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1220         if (!YAHOO.lang.isObject(actual)){
1221             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an object."), actual);
1222         }
1223     },
1224     
1225     /**
1226      * Asserts that a value is a string.
1227      * @param {Object} actual The value to test.
1228      * @param {String} message (Optional) The message to display if the assertion fails.
1229      * @method isString
1230      * @static
1231      */
1232     isString : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1233         if (!YAHOO.lang.isString(actual)){
1234             throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a string."), actual);
1235         }
1236     },
1237     
1238     /**
1239      * Asserts that a value is of a particular type. 
1240      * @param {String} expectedType The expected type of the variable.
1241      * @param {Object} actualValue The actual value to test.
1242      * @param {String} message (Optional) The message to display if the assertion fails.
1243      * @method isTypeOf
1244      * @static
1245      */
1246     isTypeOf : function (expectedType /*:String*/, actualValue /*:Object*/, message /*:String*/) /*:Void*/{
1247         if (typeof actualValue != expectedType){
1248             throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be of type " + expected + "."), expected, typeof actual);
1249         }
1250     }
1251 };
1252
1253 //-----------------------------------------------------------------------------
1254 // Assertion errors
1255 //-----------------------------------------------------------------------------
1256
1257 /**
1258  * AssertionError is thrown whenever an assertion fails. It provides methods
1259  * to more easily get at error information and also provides a base class
1260  * from which more specific assertion errors can be derived.
1261  *
1262  * @param {String} message The message to display when the error occurs.
1263  * @namespace YAHOO.util
1264  * @class AssertionError
1265  * @extends Error
1266  * @constructor
1267  */ 
1268 YAHOO.util.AssertionError = function (message /*:String*/){
1269
1270     //call superclass
1271     arguments.callee.superclass.constructor.call(this, message);
1272     
1273     /*
1274      * Error message. Must be duplicated to ensure browser receives it.
1275      * @type String
1276      * @property message
1277      */
1278     this.message /*:String*/ = message;
1279     
1280     /**
1281      * The name of the error that occurred.
1282      * @type String
1283      * @property name
1284      */
1285     this.name /*:String*/ = "AssertionError";
1286 };
1287
1288 //inherit methods
1289 YAHOO.lang.extend(YAHOO.util.AssertionError, Error, {
1290
1291     /**
1292      * Returns a fully formatted error for an assertion failure. This should
1293      * be overridden by all subclasses to provide specific information.
1294      * @method getMessage
1295      * @return {String} A string describing the error.
1296      */
1297     getMessage : function () /*:String*/ {
1298         return this.message;
1299     },
1300     
1301     /**
1302      * Returns a string representation of the error.
1303      * @method toString
1304      * @return {String} A string representation of the error.
1305      */
1306     toString : function () /*:String*/ {
1307         return this.name + ": " + this.getMessage();
1308     },
1309     
1310     /**
1311      * Returns a primitive value version of the error. Same as toString().
1312      * @method valueOf
1313      * @return {String} A primitive value version of the error.
1314      */
1315     valueOf : function () /*:String*/ {
1316         return this.toString();
1317     }
1318
1319 });
1320
1321 /**
1322  * ComparisonFailure is subclass of AssertionError that is thrown whenever
1323  * a comparison between two values fails. It provides mechanisms to retrieve
1324  * both the expected and actual value.
1325  *
1326  * @param {String} message The message to display when the error occurs.
1327  * @param {Object} expected The expected value.
1328  * @param {Object} actual The actual value that caused the assertion to fail.
1329  * @namespace YAHOO.util
1330  * @extends YAHOO.util.AssertionError
1331  * @class ComparisonFailure
1332  * @constructor
1333  */ 
1334 YAHOO.util.ComparisonFailure = function (message /*:String*/, expected /*:Object*/, actual /*:Object*/){
1335
1336     //call superclass
1337     arguments.callee.superclass.constructor.call(this, message);
1338     
1339     /**
1340      * The expected value.
1341      * @type Object
1342      * @property expected
1343      */
1344     this.expected /*:Object*/ = expected;
1345     
1346     /**
1347      * The actual value.
1348      * @type Object
1349      * @property actual
1350      */
1351     this.actual /*:Object*/ = actual;
1352     
1353     /**
1354      * The name of the error that occurred.
1355      * @type String
1356      * @property name
1357      */
1358     this.name /*:String*/ = "ComparisonFailure";
1359     
1360 };
1361
1362 //inherit methods
1363 YAHOO.lang.extend(YAHOO.util.ComparisonFailure, YAHOO.util.AssertionError, {
1364
1365     /**
1366      * Returns a fully formatted error for an assertion failure. This message
1367      * provides information about the expected and actual values.
1368      * @method toString
1369      * @return {String} A string describing the error.
1370      */
1371     getMessage : function () /*:String*/ {
1372         return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")"  +
1373             "\nActual:" + this.actual + " (" + (typeof this.actual) + ")";
1374     }
1375
1376 });
1377
1378 /**
1379  * UnexpectedValue is subclass of AssertionError that is thrown whenever
1380  * a value was unexpected in its scope. This typically means that a test
1381  * was performed to determine that a value was *not* equal to a certain
1382  * value.
1383  *
1384  * @param {String} message The message to display when the error occurs.
1385  * @param {Object} unexpected The unexpected value.
1386  * @namespace YAHOO.util
1387  * @extends YAHOO.util.AssertionError
1388  * @class UnexpectedValue
1389  * @constructor
1390  */ 
1391 YAHOO.util.UnexpectedValue = function (message /*:String*/, unexpected /*:Object*/){
1392
1393     //call superclass
1394     arguments.callee.superclass.constructor.call(this, message);
1395     
1396     /**
1397      * The unexpected value.
1398      * @type Object
1399      * @property unexpected
1400      */
1401     this.unexpected /*:Object*/ = unexpected;
1402     
1403     /**
1404      * The name of the error that occurred.
1405      * @type String
1406      * @property name
1407      */
1408     this.name /*:String*/ = "UnexpectedValue";
1409     
1410 };
1411
1412 //inherit methods
1413 YAHOO.lang.extend(YAHOO.util.UnexpectedValue, YAHOO.util.AssertionError, {
1414
1415     /**
1416      * Returns a fully formatted error for an assertion failure. The message
1417      * contains information about the unexpected value that was encountered.
1418      * @method getMessage
1419      * @return {String} A string describing the error.
1420      */
1421     getMessage : function () /*:String*/ {
1422         return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1423     }
1424
1425 });
1426
1427 /**
1428  * ShouldFail is subclass of AssertionError that is thrown whenever
1429  * a test was expected to fail but did not.
1430  *
1431  * @param {String} message The message to display when the error occurs.
1432  * @namespace YAHOO.util
1433  * @extends YAHOO.util.AssertionError
1434  * @class ShouldFail
1435  * @constructor
1436  */  
1437 YAHOO.util.ShouldFail = function (message /*:String*/){
1438
1439     //call superclass
1440     arguments.callee.superclass.constructor.call(this, message || "This test should fail but didn't.");
1441     
1442     /**
1443      * The name of the error that occurred.
1444      * @type String
1445      * @property name
1446      */
1447     this.name /*:String*/ = "ShouldFail";
1448     
1449 };
1450
1451 //inherit methods
1452 YAHOO.lang.extend(YAHOO.util.ShouldFail, YAHOO.util.AssertionError);
1453
1454 /**
1455  * ShouldError is subclass of AssertionError that is thrown whenever
1456  * a test is expected to throw an error but doesn't.
1457  *
1458  * @param {String} message The message to display when the error occurs.
1459  * @namespace YAHOO.util
1460  * @extends YAHOO.util.AssertionError
1461  * @class ShouldError
1462  * @constructor
1463  */  
1464 YAHOO.util.ShouldError = function (message /*:String*/){
1465
1466     //call superclass
1467     arguments.callee.superclass.constructor.call(this, message || "This test should have thrown an error but didn't.");
1468     
1469     /**
1470      * The name of the error that occurred.
1471      * @type String
1472      * @property name
1473      */
1474     this.name /*:String*/ = "ShouldError";
1475     
1476 };
1477
1478 //inherit methods
1479 YAHOO.lang.extend(YAHOO.util.ShouldError, YAHOO.util.AssertionError);
1480
1481 /**
1482  * UnexpectedError is subclass of AssertionError that is thrown whenever
1483  * an error occurs within the course of a test and the test was not expected
1484  * to throw an error.
1485  *
1486  * @param {Error} cause The unexpected error that caused this error to be 
1487  *                      thrown.
1488  * @namespace YAHOO.util
1489  * @extends YAHOO.util.AssertionError
1490  * @class UnexpectedError
1491  * @constructor
1492  */  
1493 YAHOO.util.UnexpectedError = function (cause /*:Object*/){
1494
1495     //call superclass
1496     arguments.callee.superclass.constructor.call(this, "Unexpected error: " + cause.message);
1497     
1498     /**
1499      * The unexpected error that occurred.
1500      * @type Error
1501      * @property cause
1502      */
1503     this.cause /*:Error*/ = cause;
1504     
1505     /**
1506      * The name of the error that occurred.
1507      * @type String
1508      * @property name
1509      */
1510     this.name /*:String*/ = "UnexpectedError";
1511     
1512     /**
1513      * Stack information for the error (if provided).
1514      * @type String
1515      * @property stack
1516      */
1517     this.stack /*:String*/ = cause.stack;
1518     
1519 };
1520
1521 //inherit methods
1522 YAHOO.lang.extend(YAHOO.util.UnexpectedError, YAHOO.util.AssertionError);
1523
1524 //-----------------------------------------------------------------------------
1525 // ArrayAssert object
1526 //-----------------------------------------------------------------------------
1527
1528 /**
1529  * The ArrayAssert object provides functions to test JavaScript array objects
1530  * for a variety of cases.
1531  *
1532  * @namespace YAHOO.util
1533  * @class ArrayAssert
1534  * @static
1535  */
1536  
1537 YAHOO.util.ArrayAssert = {
1538
1539     /**
1540      * Asserts that a value is present in an array. This uses the triple equals 
1541      * sign so no type cohersion may occur.
1542      * @param {Object} needle The value that is expected in the array.
1543      * @param {Array} haystack An array of values.
1544      * @param {String} message (Optional) The message to display if the assertion fails.
1545      * @method contains
1546      * @static
1547      */
1548     contains : function (needle /*:Object*/, haystack /*:Array*/, 
1549                            message /*:String*/) /*:Void*/ {
1550         
1551         var found /*:Boolean*/ = false;
1552         var Assert = YAHOO.util.Assert;
1553         
1554         //begin checking values
1555         for (var i=0; i < haystack.length && !found; i++){
1556             if (haystack[i] === needle) {
1557                 found = true;
1558             }
1559         }
1560         
1561         if (!found){
1562             Assert.fail(Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1563         }
1564     },
1565
1566     /**
1567      * Asserts that a set of values are present in an array. This uses the triple equals 
1568      * sign so no type cohersion may occur. For this assertion to pass, all values must
1569      * be found.
1570      * @param {Object[]} needles An array of values that are expected in the array.
1571      * @param {Array} haystack An array of values to check.
1572      * @param {String} message (Optional) The message to display if the assertion fails.
1573      * @method containsItems
1574      * @static
1575      */
1576     containsItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1577                            message /*:String*/) /*:Void*/ {
1578
1579         //begin checking values
1580         for (var i=0; i < needles.length; i++){
1581             this.contains(needles[i], haystack, message);
1582         }
1583     },
1584
1585     /**
1586      * Asserts that a value matching some condition is present in an array. This uses
1587      * a function to determine a match.
1588      * @param {Function} matcher A function that returns true if the items matches or false if not.
1589      * @param {Array} haystack An array of values.
1590      * @param {String} message (Optional) The message to display if the assertion fails.
1591      * @method containsMatch
1592      * @static
1593      */
1594     containsMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1595                            message /*:String*/) /*:Void*/ {
1596         
1597         //check for valid matcher
1598         if (typeof matcher != "function"){
1599             throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1600         }
1601         
1602         var found /*:Boolean*/ = false;
1603         var Assert = YAHOO.util.Assert;
1604         
1605         //begin checking values
1606         for (var i=0; i < haystack.length && !found; i++){
1607             if (matcher(haystack[i])) {
1608                 found = true;
1609             }
1610         }
1611         
1612         if (!found){
1613             Assert.fail(Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1614         }
1615     },
1616
1617     /**
1618      * Asserts that a value is not present in an array. This uses the triple equals 
1619      * sign so no type cohersion may occur.
1620      * @param {Object} needle The value that is expected in the array.
1621      * @param {Array} haystack An array of values.
1622      * @param {String} message (Optional) The message to display if the assertion fails.
1623      * @method doesNotContain
1624      * @static
1625      */
1626     doesNotContain : function (needle /*:Object*/, haystack /*:Array*/, 
1627                            message /*:String*/) /*:Void*/ {
1628         
1629         var found /*:Boolean*/ = false;
1630         var Assert = YAHOO.util.Assert;
1631         
1632         //begin checking values
1633         for (var i=0; i < haystack.length && !found; i++){
1634             if (haystack[i] === needle) {
1635                 found = true;
1636             }
1637         }
1638         
1639         if (found){
1640             Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1641         }
1642     },
1643
1644     /**
1645      * Asserts that a set of values are not present in an array. This uses the triple equals 
1646      * sign so no type cohersion may occur. For this assertion to pass, all values must
1647      * not be found.
1648      * @param {Object[]} needles An array of values that are not expected in the array.
1649      * @param {Array} haystack An array of values to check.
1650      * @param {String} message (Optional) The message to display if the assertion fails.
1651      * @method doesNotContainItems
1652      * @static
1653      */
1654     doesNotContainItems : function (needles /*:Object[]*/, haystack /*:Array*/, 
1655                            message /*:String*/) /*:Void*/ {
1656
1657         for (var i=0; i < needles.length; i++){
1658             this.doesNotContain(needles[i], haystack, message);
1659         }
1660
1661     },
1662         
1663     /**
1664      * Asserts that no values matching a condition are present in an array. This uses
1665      * a function to determine a match.
1666      * @param {Function} matcher A function that returns true if the items matches or false if not.
1667      * @param {Array} haystack An array of values.
1668      * @param {String} message (Optional) The message to display if the assertion fails.
1669      * @method doesNotContainMatch
1670      * @static
1671      */
1672     doesNotContainMatch : function (matcher /*:Function*/, haystack /*:Array*/, 
1673                            message /*:String*/) /*:Void*/ {
1674         
1675         //check for valid matcher
1676         if (typeof matcher != "function"){
1677             throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1678         }
1679
1680         var found /*:Boolean*/ = false;
1681         var Assert = YAHOO.util.Assert;
1682         
1683         //begin checking values
1684         for (var i=0; i < haystack.length && !found; i++){
1685             if (matcher(haystack[i])) {
1686                 found = true;
1687             }
1688         }
1689         
1690         if (found){
1691             Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1692         }
1693     },
1694         
1695     /**
1696      * Asserts that the given value is contained in an array at the specified index.
1697      * This uses the triple equals sign so no type cohersion will occur.
1698      * @param {Object} needle The value to look for.
1699      * @param {Array} haystack The array to search in.
1700      * @param {int} index The index at which the value should exist.
1701      * @param {String} message (Optional) The message to display if the assertion fails.
1702      * @method indexOf
1703      * @static
1704      */
1705     indexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1706     
1707         //try to find the value in the array
1708         for (var i=0; i < haystack.length; i++){
1709             if (haystack[i] === needle){
1710                 YAHOO.util.Assert.areEqual(index, i, message || "Value exists at index " + i + " but should be at index " + index + ".");
1711                 return;
1712             }
1713         }
1714         
1715         var Assert = YAHOO.util.Assert;
1716         
1717         //if it makes it here, it wasn't found at all
1718         Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1719     },
1720         
1721     /**
1722      * Asserts that the values in an array are equal, and in the same position,
1723      * as values in another array. This uses the double equals sign
1724      * so type cohersion may occur. Note that the array objects themselves
1725      * need not be the same for this test to pass.
1726      * @param {Array} expected An array of the expected values.
1727      * @param {Array} actual Any array of the actual values.
1728      * @param {String} message (Optional) The message to display if the assertion fails.
1729      * @method itemsAreEqual
1730      * @static
1731      */
1732     itemsAreEqual : function (expected /*:Array*/, actual /*:Array*/, 
1733                            message /*:String*/) /*:Void*/ {
1734         
1735         //one may be longer than the other, so get the maximum length
1736         var len /*:int*/ = Math.max(expected.length, actual.length);
1737         var Assert = YAHOO.util.Assert;
1738        
1739         //begin checking values
1740         for (var i=0; i < len; i++){
1741             Assert.areEqual(expected[i], actual[i], 
1742                 Assert._formatMessage(message, "Values in position " + i + " are not equal."));
1743         }
1744     },
1745     
1746     /**
1747      * Asserts that the values in an array are equivalent, and in the same position,
1748      * as values in another array. This uses a function to determine if the values
1749      * are equivalent. Note that the array objects themselves
1750      * need not be the same for this test to pass.
1751      * @param {Array} expected An array of the expected values.
1752      * @param {Array} actual Any array of the actual values.
1753      * @param {Function} comparator A function that returns true if the values are equivalent
1754      *      or false if not.
1755      * @param {String} message (Optional) The message to display if the assertion fails.
1756      * @return {Void}
1757      * @method itemsAreEquivalent
1758      * @static
1759      */
1760     itemsAreEquivalent : function (expected /*:Array*/, actual /*:Array*/, 
1761                            comparator /*:Function*/, message /*:String*/) /*:Void*/ {
1762         
1763         //make sure the comparator is valid
1764         if (typeof comparator != "function"){
1765             throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1766         }
1767         
1768         //one may be longer than the other, so get the maximum length
1769         var len /*:int*/ = Math.max(expected.length, actual.length);
1770         
1771         //begin checking values
1772         for (var i=0; i < len; i++){
1773             if (!comparator(expected[i], actual[i])){
1774                 throw new YAHOO.util.ComparisonFailure(YAHOO.util.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1775             }
1776         }
1777     },
1778     
1779     /**
1780      * Asserts that an array is empty.
1781      * @param {Array} actual The array to test.
1782      * @param {String} message (Optional) The message to display if the assertion fails.
1783      * @method isEmpty
1784      * @static
1785      */
1786     isEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1787         if (actual.length > 0){
1788             var Assert = YAHOO.util.Assert;
1789             Assert.fail(Assert._formatMessage(message, "Array should be empty."));
1790         }
1791     },    
1792     
1793     /**
1794      * Asserts that an array is not empty.
1795      * @param {Array} actual The array to test.
1796      * @param {String} message (Optional) The message to display if the assertion fails.
1797      * @method isNotEmpty
1798      * @static
1799      */
1800     isNotEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {        
1801         if (actual.length === 0){
1802             var Assert = YAHOO.util.Assert;
1803             Assert.fail(Assert._formatMessage(message, "Array should not be empty."));
1804         }
1805     },    
1806     
1807     /**
1808      * Asserts that the values in an array are the same, and in the same position,
1809      * as values in another array. This uses the triple equals sign
1810      * so no type cohersion will occur. Note that the array objects themselves
1811      * need not be the same for this test to pass.
1812      * @param {Array} expected An array of the expected values.
1813      * @param {Array} actual Any array of the actual values.
1814      * @param {String} message (Optional) The message to display if the assertion fails.
1815      * @method itemsAreSame
1816      * @static
1817      */
1818     itemsAreSame : function (expected /*:Array*/, actual /*:Array*/, 
1819                           message /*:String*/) /*:Void*/ {
1820         
1821         //one may be longer than the other, so get the maximum length
1822         var len /*:int*/ = Math.max(expected.length, actual.length);
1823         var Assert = YAHOO.util.Assert;
1824         
1825         //begin checking values
1826         for (var i=0; i < len; i++){
1827             Assert.areSame(expected[i], actual[i], 
1828                 Assert._formatMessage(message, "Values in position " + i + " are not the same."));
1829         }
1830     },
1831     
1832     /**
1833      * Asserts that the given value is contained in an array at the specified index,
1834      * starting from the back of the array.
1835      * This uses the triple equals sign so no type cohersion will occur.
1836      * @param {Object} needle The value to look for.
1837      * @param {Array} haystack The array to search in.
1838      * @param {int} index The index at which the value should exist.
1839      * @param {String} message (Optional) The message to display if the assertion fails.
1840      * @method lastIndexOf
1841      * @static
1842      */
1843     lastIndexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1844     
1845         var Assert = YAHOO.util.Assert;
1846     
1847         //try to find the value in the array
1848         for (var i=haystack.length; i >= 0; i--){
1849             if (haystack[i] === needle){
1850                 Assert.areEqual(index, i, Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1851                 return;
1852             }
1853         }
1854         
1855         //if it makes it here, it wasn't found at all
1856         Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array."));        
1857     }
1858     
1859 };
1860
1861 YAHOO.namespace("util");
1862
1863
1864 //-----------------------------------------------------------------------------
1865 // ObjectAssert object
1866 //-----------------------------------------------------------------------------
1867
1868 /**
1869  * The ObjectAssert object provides functions to test JavaScript objects
1870  * for a variety of cases.
1871  *
1872  * @namespace YAHOO.util
1873  * @class ObjectAssert
1874  * @static
1875  */
1876 YAHOO.util.ObjectAssert = {
1877         
1878     /**
1879      * Asserts that all properties in the object exist in another object.
1880      * @param {Object} expected An object with the expected properties.
1881      * @param {Object} actual An object with the actual properties.
1882      * @param {String} message (Optional) The message to display if the assertion fails.
1883      * @method propertiesAreEqual
1884      * @static
1885      */
1886     propertiesAreEqual : function (expected /*:Object*/, actual /*:Object*/, 
1887                            message /*:String*/) /*:Void*/ {
1888         
1889         var Assert = YAHOO.util.Assert;
1890         
1891         //get all properties in the object
1892         var properties /*:Array*/ = [];        
1893         for (var property in expected){
1894             properties.push(property);
1895         }
1896         
1897         //see if the properties are in the expected object
1898         for (var i=0; i < properties.length; i++){
1899             Assert.isNotUndefined(actual[properties[i]], 
1900                 Assert._formatMessage(message, "Property '" + properties[i] + "' expected."));
1901         }
1902
1903     },
1904     
1905     /**
1906      * Asserts that an object has a property with the given name.
1907      * @param {String} propertyName The name of the property to test.
1908      * @param {Object} object The object to search.
1909      * @param {String} message (Optional) The message to display if the assertion fails.
1910      * @method hasProperty
1911      * @static
1912      */    
1913     hasProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1914         if (!(propertyName in object)){
1915             var Assert = YAHOO.util.Assert;
1916             Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
1917         }    
1918     },
1919     
1920     /**
1921      * Asserts that a property with the given name exists on an object instance (not on its prototype).
1922      * @param {String} propertyName The name of the property to test.
1923      * @param {Object} object The object to search.
1924      * @param {String} message (Optional) The message to display if the assertion fails.
1925      * @method hasProperty
1926      * @static
1927      */    
1928     hasOwnProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1929         if (!YAHOO.lang.hasOwnProperty(object, propertyName)){
1930             var Assert = YAHOO.util.Assert;
1931             Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1932         }     
1933     }
1934 };
1935
1936 //-----------------------------------------------------------------------------
1937 // DateAssert object
1938 //-----------------------------------------------------------------------------
1939
1940 /**
1941  * The DateAssert object provides functions to test JavaScript Date objects
1942  * for a variety of cases.
1943  *
1944  * @namespace YAHOO.util
1945  * @class DateAssert
1946  * @static
1947  */
1948  
1949 YAHOO.util.DateAssert = {
1950
1951     /**
1952      * Asserts that a date's month, day, and year are equal to another date's.
1953      * @param {Date} expected The expected date.
1954      * @param {Date} actual The actual date to test.
1955      * @param {String} message (Optional) The message to display if the assertion fails.
1956      * @method datesAreEqual
1957      * @static
1958      */
1959     datesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1960         if (expected instanceof Date && actual instanceof Date){
1961             var Assert = YAHOO.util.Assert;
1962             Assert.areEqual(expected.getFullYear(), actual.getFullYear(), Assert._formatMessage(message, "Years should be equal."));
1963             Assert.areEqual(expected.getMonth(), actual.getMonth(), Assert._formatMessage(message, "Months should be equal."));
1964             Assert.areEqual(expected.getDate(), actual.getDate(), Assert._formatMessage(message, "Day of month should be equal."));
1965         } else {
1966             throw new TypeError("DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
1967         }
1968     },
1969
1970     /**
1971      * Asserts that a date's hour, minutes, and seconds are equal to another date's.
1972      * @param {Date} expected The expected date.
1973      * @param {Date} actual The actual date to test.
1974      * @param {String} message (Optional) The message to display if the assertion fails.
1975      * @method timesAreEqual
1976      * @static
1977      */
1978     timesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1979         if (expected instanceof Date && actual instanceof Date){
1980             var Assert = YAHOO.util.Assert;
1981             Assert.areEqual(expected.getHours(), actual.getHours(), Assert._formatMessage(message, "Hours should be equal."));
1982             Assert.areEqual(expected.getMinutes(), actual.getMinutes(), Assert._formatMessage(message, "Minutes should be equal."));
1983             Assert.areEqual(expected.getSeconds(), actual.getSeconds(), Assert._formatMessage(message, "Seconds should be equal."));
1984         } else {
1985             throw new TypeError("DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
1986         }
1987     }
1988     
1989 };
1990
1991 YAHOO.namespace("util");
1992
1993 /**
1994  * The UserAction object provides functions that simulate events occurring in
1995  * the browser. Since these are simulated events, they do not behave exactly
1996  * as regular, user-initiated events do, but can be used to test simple
1997  * user interactions safely.
1998  *
1999  * @namespace YAHOO.util
2000  * @class UserAction
2001  * @static
2002  */
2003 YAHOO.util.UserAction = {
2004
2005     //--------------------------------------------------------------------------
2006     // Generic event methods
2007     //--------------------------------------------------------------------------
2008
2009     /**
2010      * Simulates a key event using the given event information to populate
2011      * the generated event object. This method does browser-equalizing
2012      * calculations to account for differences in the DOM and IE event models
2013      * as well as different browser quirks. Note: keydown causes Safari 2.x to
2014      * crash.
2015      * @method simulateKeyEvent
2016      * @private
2017      * @static
2018      * @param {HTMLElement} target The target of the given event.
2019      * @param {String} type The type of event to fire. This can be any one of
2020      *      the following: keyup, keydown, and keypress.
2021      * @param {Boolean} bubbles (Optional) Indicates if the event can be
2022      *      bubbled up. DOM Level 3 specifies that all key events bubble by
2023      *      default. The default is true.
2024      * @param {Boolean} cancelable (Optional) Indicates if the event can be
2025      *      canceled using preventDefault(). DOM Level 3 specifies that all
2026      *      key events can be cancelled. The default 
2027      *      is true.
2028      * @param {Window} view (Optional) The view containing the target. This is
2029      *      typically the window object. The default is window.
2030      * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
2031      *      is pressed while the event is firing. The default is false.
2032      * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
2033      *      is pressed while the event is firing. The default is false.
2034      * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
2035      *      is pressed while the event is firing. The default is false.
2036      * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
2037      *      is pressed while the event is firing. The default is false.
2038      * @param {int} keyCode (Optional) The code for the key that is in use. 
2039      *      The default is 0.
2040      * @param {int} charCode (Optional) The Unicode code for the character
2041      *      associated with the key being used. The default is 0.
2042      */
2043     simulateKeyEvent : function (target /*:HTMLElement*/, type /*:String*/, 
2044                                  bubbles /*:Boolean*/,  cancelable /*:Boolean*/,    
2045                                  view /*:Window*/,
2046                                  ctrlKey /*:Boolean*/,    altKey /*:Boolean*/, 
2047                                  shiftKey /*:Boolean*/,   metaKey /*:Boolean*/, 
2048                                  keyCode /*:int*/,        charCode /*:int*/) /*:Void*/                             
2049     {
2050         //check target
2051         target = YAHOO.util.Dom.get(target);        
2052         if (!target){
2053             throw new Error("simulateKeyEvent(): Invalid target.");
2054         }
2055         
2056         //check event type
2057         if (YAHOO.lang.isString(type)){
2058             type = type.toLowerCase();
2059             switch(type){
2060                 case "keyup":
2061                 case "keydown":
2062                 case "keypress":
2063                     break;
2064                 case "textevent": //DOM Level 3
2065                     type = "keypress";
2066                     break;
2067                     // @TODO was the fallthrough intentional, if so throw error
2068                 default:
2069                     throw new Error("simulateKeyEvent(): Event type '" + type + "' not supported.");
2070             }
2071         } else {
2072             throw new Error("simulateKeyEvent(): Event type must be a string.");
2073         }
2074         
2075         //setup default values
2076         if (!YAHOO.lang.isBoolean(bubbles)){
2077             bubbles = true; //all key events bubble
2078         }
2079         if (!YAHOO.lang.isBoolean(cancelable)){
2080             cancelable = true; //all key events can be cancelled
2081         }
2082         if (!YAHOO.lang.isObject(view)){
2083             view = window; //view is typically window
2084         }
2085         if (!YAHOO.lang.isBoolean(ctrlKey)){
2086             ctrlKey = false;
2087         }
2088         if (!YAHOO.lang.isBoolean(altKey)){
2089             altKey = false;
2090         }
2091         if (!YAHOO.lang.isBoolean(shiftKey)){
2092             shiftKey = false;
2093         }
2094         if (!YAHOO.lang.isBoolean(metaKey)){
2095             metaKey = false;
2096         }
2097         if (!YAHOO.lang.isNumber(keyCode)){
2098             keyCode = 0;
2099         }
2100         if (!YAHOO.lang.isNumber(charCode)){
2101             charCode = 0; 
2102         }
2103
2104         //try to create a mouse event
2105         var customEvent /*:MouseEvent*/ = null;
2106             
2107         //check for DOM-compliant browsers first
2108         if (YAHOO.lang.isFunction(document.createEvent)){
2109         
2110             try {
2111                 
2112                 //try to create key event
2113                 customEvent = document.createEvent("KeyEvents");
2114                 
2115                 /*
2116                  * Interesting problem: Firefox implemented a non-standard
2117                  * version of initKeyEvent() based on DOM Level 2 specs.
2118                  * Key event was removed from DOM Level 2 and re-introduced
2119                  * in DOM Level 3 with a different interface. Firefox is the
2120                  * only browser with any implementation of Key Events, so for
2121                  * now, assume it's Firefox if the above line doesn't error.
2122                  */
2123                 //TODO: Decipher between Firefox's implementation and a correct one.
2124                 customEvent.initKeyEvent(type, bubbles, cancelable, view, ctrlKey,
2125                     altKey, shiftKey, metaKey, keyCode, charCode);       
2126                 
2127             } catch (ex /*:Error*/){
2128
2129                 /*
2130                  * If it got here, that means key events aren't officially supported. 
2131                  * Safari/WebKit is a real problem now. WebKit 522 won't let you
2132                  * set keyCode, charCode, or other properties if you use a
2133                  * UIEvent, so we first must try to create a generic event. The
2134                  * fun part is that this will throw an error on Safari 2.x. The
2135                  * end result is that we need another try...catch statement just to
2136                  * deal with this mess.
2137                  */
2138                 try {
2139
2140                     //try to create generic event - will fail in Safari 2.x
2141                     customEvent = document.createEvent("Events");
2142
2143                 } catch (uierror /*:Error*/){
2144
2145                     //the above failed, so create a UIEvent for Safari 2.x
2146                     customEvent = document.createEvent("UIEvents");
2147
2148                 } finally {
2149
2150                     customEvent.initEvent(type, bubbles, cancelable);
2151     
2152                     //initialize
2153                     customEvent.view = view;
2154                     customEvent.altKey = altKey;
2155                     customEvent.ctrlKey = ctrlKey;
2156                     customEvent.shiftKey = shiftKey;
2157                     customEvent.metaKey = metaKey;
2158                     customEvent.keyCode = keyCode;
2159                     customEvent.charCode = charCode;
2160           
2161                 }          
2162              
2163             }
2164             
2165             //fire the event
2166             target.dispatchEvent(customEvent);
2167
2168         } else if (YAHOO.lang.isObject(document.createEventObject)){ //IE
2169         
2170             //create an IE event object
2171             customEvent = document.createEventObject();
2172             
2173             //assign available properties
2174             customEvent.bubbles = bubbles;
2175             customEvent.cancelable = cancelable;
2176             customEvent.view = view;
2177             customEvent.ctrlKey = ctrlKey;
2178             customEvent.altKey = altKey;
2179             customEvent.shiftKey = shiftKey;
2180             customEvent.metaKey = metaKey;
2181             
2182             /*
2183              * IE doesn't support charCode explicitly. CharCode should
2184              * take precedence over any keyCode value for accurate
2185              * representation.
2186              */
2187             customEvent.keyCode = (charCode > 0) ? charCode : keyCode;
2188             
2189             //fire the event
2190             target.fireEvent("on" + type, customEvent);  
2191                     
2192         } else {
2193             throw new Error("simulateKeyEvent(): No event simulation framework present.");
2194         }
2195     },
2196
2197     /**
2198      * Simulates a mouse event using the given event information to populate
2199      * the generated event object. This method does browser-equalizing
2200      * calculations to account for differences in the DOM and IE event models
2201      * as well as different browser quirks.
2202      * @method simulateMouseEvent
2203      * @private
2204      * @static
2205      * @param {HTMLElement} target The target of the given event.
2206      * @param {String} type The type of event to fire. This can be any one of
2207      *      the following: click, dblclick, mousedown, mouseup, mouseout,
2208      *      mouseover, and mousemove.
2209      * @param {Boolean} bubbles (Optional) Indicates if the event can be
2210      *      bubbled up. DOM Level 2 specifies that all mouse events bubble by
2211      *      default. The default is true.
2212      * @param {Boolean} cancelable (Optional) Indicates if the event can be
2213      *      canceled using preventDefault(). DOM Level 2 specifies that all
2214      *      mouse events except mousemove can be cancelled. The default 
2215      *      is true for all events except mousemove, for which the default 
2216      *      is false.
2217      * @param {Window} view (Optional) The view containing the target. This is
2218      *      typically the window object. The default is window.
2219      * @param {int} detail (Optional) The number of times the mouse button has
2220      *      been used. The default value is 1.
2221      * @param {int} screenX (Optional) The x-coordinate on the screen at which
2222      *      point the event occured. The default is 0.
2223      * @param {int} screenY (Optional) The y-coordinate on the screen at which
2224      *      point the event occured. The default is 0.
2225      * @param {int} clientX (Optional) The x-coordinate on the client at which
2226      *      point the event occured. The default is 0.
2227      * @param {int} clientY (Optional) The y-coordinate on the client at which
2228      *      point the event occured. The default is 0.
2229      * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
2230      *      is pressed while the event is firing. The default is false.
2231      * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
2232      *      is pressed while the event is firing. The default is false.
2233      * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
2234      *      is pressed while the event is firing. The default is false.
2235      * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
2236      *      is pressed while the event is firing. The default is false.
2237      * @param {int} button (Optional) The button being pressed while the event
2238      *      is executing. The value should be 0 for the primary mouse button
2239      *      (typically the left button), 1 for the terciary mouse button
2240      *      (typically the middle button), and 2 for the secondary mouse button
2241      *      (typically the right button). The default is 0.
2242      * @param {HTMLElement} relatedTarget (Optional) For mouseout events,
2243      *      this is the element that the mouse has moved to. For mouseover
2244      *      events, this is the element that the mouse has moved from. This
2245      *      argument is ignored for all other events. The default is null.
2246      */
2247     simulateMouseEvent : function (target /*:HTMLElement*/, type /*:String*/, 
2248                                    bubbles /*:Boolean*/,  cancelable /*:Boolean*/,    
2249                                    view /*:Window*/,        detail /*:int*/, 
2250                                    screenX /*:int*/,        screenY /*:int*/, 
2251                                    clientX /*:int*/,        clientY /*:int*/,       
2252                                    ctrlKey /*:Boolean*/,    altKey /*:Boolean*/, 
2253                                    shiftKey /*:Boolean*/,   metaKey /*:Boolean*/, 
2254                                    button /*:int*/,         relatedTarget /*:HTMLElement*/) /*:Void*/
2255     {
2256         
2257         //check target
2258         target = YAHOO.util.Dom.get(target);        
2259         if (!target){
2260             throw new Error("simulateMouseEvent(): Invalid target.");
2261         }
2262         
2263         //check event type
2264         if (YAHOO.lang.isString(type)){
2265             type = type.toLowerCase();
2266             switch(type){
2267                 case "mouseover":
2268                 case "mouseout":
2269                 case "mousedown":
2270                 case "mouseup":
2271                 case "click":
2272                 case "dblclick":
2273                 case "mousemove":
2274                     break;
2275                 default:
2276                     throw new Error("simulateMouseEvent(): Event type '" + type + "' not supported.");
2277             }
2278         } else {
2279             throw new Error("simulateMouseEvent(): Event type must be a string.");
2280         }
2281         
2282         //setup default values
2283         if (!YAHOO.lang.isBoolean(bubbles)){
2284             bubbles = true; //all mouse events bubble
2285         }
2286         if (!YAHOO.lang.isBoolean(cancelable)){
2287             cancelable = (type != "mousemove"); //mousemove is the only one that can't be cancelled
2288         }
2289         if (!YAHOO.lang.isObject(view)){
2290             view = window; //view is typically window
2291         }
2292         if (!YAHOO.lang.isNumber(detail)){
2293             detail = 1;  //number of mouse clicks must be at least one
2294         }
2295         if (!YAHOO.lang.isNumber(screenX)){
2296             screenX = 0; 
2297         }
2298         if (!YAHOO.lang.isNumber(screenY)){
2299             screenY = 0; 
2300         }
2301         if (!YAHOO.lang.isNumber(clientX)){
2302             clientX = 0; 
2303         }
2304         if (!YAHOO.lang.isNumber(clientY)){
2305             clientY = 0; 
2306         }
2307         if (!YAHOO.lang.isBoolean(ctrlKey)){
2308             ctrlKey = false;
2309         }
2310         if (!YAHOO.lang.isBoolean(altKey)){
2311             altKey = false;
2312         }
2313         if (!YAHOO.lang.isBoolean(shiftKey)){
2314             shiftKey = false;
2315         }
2316         if (!YAHOO.lang.isBoolean(metaKey)){
2317             metaKey = false;
2318         }
2319         if (!YAHOO.lang.isNumber(button)){
2320             button = 0; 
2321         }
2322
2323         //try to create a mouse event
2324         var customEvent /*:MouseEvent*/ = null;
2325             
2326         //check for DOM-compliant browsers first
2327         if (YAHOO.lang.isFunction(document.createEvent)){
2328         
2329             customEvent = document.createEvent("MouseEvents");
2330         
2331             //Safari 2.x (WebKit 418) still doesn't implement initMouseEvent()
2332             if (customEvent.initMouseEvent){
2333                 customEvent.initMouseEvent(type, bubbles, cancelable, view, detail,
2334                                      screenX, screenY, clientX, clientY, 
2335                                      ctrlKey, altKey, shiftKey, metaKey, 
2336                                      button, relatedTarget);
2337             } else { //Safari
2338             
2339                 //the closest thing available in Safari 2.x is UIEvents
2340                 customEvent = document.createEvent("UIEvents");
2341                 customEvent.initEvent(type, bubbles, cancelable);
2342                 customEvent.view = view;
2343                 customEvent.detail = detail;
2344                 customEvent.screenX = screenX;
2345                 customEvent.screenY = screenY;
2346                 customEvent.clientX = clientX;
2347                 customEvent.clientY = clientY;
2348                 customEvent.ctrlKey = ctrlKey;
2349                 customEvent.altKey = altKey;
2350                 customEvent.metaKey = metaKey;
2351                 customEvent.shiftKey = shiftKey;
2352                 customEvent.button = button;
2353                 customEvent.relatedTarget = relatedTarget;
2354             }
2355             
2356             /*
2357              * Check to see if relatedTarget has been assigned. Firefox
2358              * versions less than 2.0 don't allow it to be assigned via
2359              * initMouseEvent() and the property is readonly after event
2360              * creation, so in order to keep YAHOO.util.getRelatedTarget()
2361              * working, assign to the IE proprietary toElement property
2362              * for mouseout event and fromElement property for mouseover
2363              * event.
2364              */
2365             if (relatedTarget && !customEvent.relatedTarget){
2366                 if (type == "mouseout"){
2367                     customEvent.toElement = relatedTarget;
2368                 } else if (type == "mouseover"){
2369                     customEvent.fromElement = relatedTarget;
2370                 }
2371             }
2372             
2373             //fire the event
2374             target.dispatchEvent(customEvent);
2375
2376         } else if (YAHOO.lang.isObject(document.createEventObject)){ //IE
2377         
2378             //create an IE event object
2379             customEvent = document.createEventObject();
2380             
2381             //assign available properties
2382             customEvent.bubbles = bubbles;
2383             customEvent.cancelable = cancelable;
2384             customEvent.view = view;
2385             customEvent.detail = detail;
2386             customEvent.screenX = screenX;
2387             customEvent.screenY = screenY;
2388             customEvent.clientX = clientX;
2389             customEvent.clientY = clientY;
2390             customEvent.ctrlKey = ctrlKey;
2391             customEvent.altKey = altKey;
2392             customEvent.metaKey = metaKey;
2393             customEvent.shiftKey = shiftKey;
2394
2395             //fix button property for IE's wacky implementation
2396             switch(button){
2397                 case 0:
2398                     customEvent.button = 1;
2399                     break;
2400                 case 1:
2401                     customEvent.button = 4;
2402                     break;
2403                 case 2:
2404                     //leave as is
2405                     break;
2406                 default:
2407                     customEvent.button = 0;                    
2408             }    
2409
2410             /*
2411              * Have to use relatedTarget because IE won't allow assignment
2412              * to toElement or fromElement on generic events. This keeps
2413              * YAHOO.util.customEvent.getRelatedTarget() functional.
2414              */
2415             customEvent.relatedTarget = relatedTarget;
2416             
2417             //fire the event
2418             target.fireEvent("on" + type, customEvent);
2419                     
2420         } else {
2421             throw new Error("simulateMouseEvent(): No event simulation framework present.");
2422         }
2423     },
2424    
2425     //--------------------------------------------------------------------------
2426     // Mouse events
2427     //--------------------------------------------------------------------------
2428
2429     /**
2430      * Simulates a mouse event on a particular element.
2431      * @param {HTMLElement} target The element to click on.
2432      * @param {String} type The type of event to fire. This can be any one of
2433      *      the following: click, dblclick, mousedown, mouseup, mouseout,
2434      *      mouseover, and mousemove.
2435      * @param {Object} options Additional event options (use DOM standard names).
2436      * @method mouseEvent
2437      * @static
2438      */
2439     fireMouseEvent : function (target /*:HTMLElement*/, type /*:String*/, 
2440                            options /*:Object*/) /*:Void*/
2441     {
2442         options = options || {};
2443         this.simulateMouseEvent(target, type, options.bubbles,
2444             options.cancelable, options.view, options.detail, options.screenX,        
2445             options.screenY, options.clientX, options.clientY, options.ctrlKey,
2446             options.altKey, options.shiftKey, options.metaKey, options.button,         
2447             options.relatedTarget);        
2448     },
2449
2450     /**
2451      * Simulates a click on a particular element.
2452      * @param {HTMLElement} target The element to click on.
2453      * @param {Object} options Additional event options (use DOM standard names).
2454      * @method click
2455      * @static     
2456      */
2457     click : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2458         this.fireMouseEvent(target, "click", options);
2459     },
2460     
2461     /**
2462      * Simulates a double click on a particular element.
2463      * @param {HTMLElement} target The element to double click on.
2464      * @param {Object} options Additional event options (use DOM standard names).
2465      * @method dblclick
2466      * @static
2467      */
2468     dblclick : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2469         this.fireMouseEvent( target, "dblclick", options);
2470     },
2471     
2472     /**
2473      * Simulates a mousedown on a particular element.
2474      * @param {HTMLElement} target The element to act on.
2475      * @param {Object} options Additional event options (use DOM standard names).
2476      * @method mousedown
2477      * @static
2478      */
2479     mousedown : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2480         this.fireMouseEvent(target, "mousedown", options);
2481     },
2482     
2483     /**
2484      * Simulates a mousemove on a particular element.
2485      * @param {HTMLElement} target The element to act on.
2486      * @param {Object} options Additional event options (use DOM standard names).
2487      * @method mousemove
2488      * @static
2489      */
2490     mousemove : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2491         this.fireMouseEvent(target, "mousemove", options);
2492     },
2493     
2494     /**
2495      * Simulates a mouseout event on a particular element. Use "relatedTarget"
2496      * on the options object to specify where the mouse moved to.
2497      * Quirks: Firefox less than 2.0 doesn't set relatedTarget properly, so
2498      * toElement is assigned in its place. IE doesn't allow toElement to be
2499      * be assigned, so relatedTarget is assigned in its place. Both of these
2500      * concessions allow YAHOO.util.Event.getRelatedTarget() to work correctly
2501      * in both browsers.
2502      * @param {HTMLElement} target The element to act on.
2503      * @param {Object} options Additional event options (use DOM standard names).
2504      * @method mouseout
2505      * @static
2506      */
2507     mouseout : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2508         this.fireMouseEvent(target, "mouseout", options);
2509     },
2510     
2511     /**
2512      * Simulates a mouseover event on a particular element. Use "relatedTarget"
2513      * on the options object to specify where the mouse moved from.
2514      * Quirks: Firefox less than 2.0 doesn't set relatedTarget properly, so
2515      * fromElement is assigned in its place. IE doesn't allow fromElement to be
2516      * be assigned, so relatedTarget is assigned in its place. Both of these
2517      * concessions allow YAHOO.util.Event.getRelatedTarget() to work correctly
2518      * in both browsers.
2519      * @param {HTMLElement} target The element to act on.
2520      * @param {Object} options Additional event options (use DOM standard names).
2521      * @method mouseover
2522      * @static
2523      */
2524     mouseover : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2525         this.fireMouseEvent(target, "mouseover", options);
2526     },
2527     
2528     /**
2529      * Simulates a mouseup on a particular element.
2530      * @param {HTMLElement} target The element to act on.
2531      * @param {Object} options Additional event options (use DOM standard names).
2532      * @method mouseup
2533      * @static
2534      */
2535     mouseup : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2536         this.fireMouseEvent(target, "mouseup", options);
2537     },
2538     
2539     //--------------------------------------------------------------------------
2540     // Key events
2541     //--------------------------------------------------------------------------
2542
2543     /**
2544      * Fires an event that normally would be fired by the keyboard (keyup,
2545      * keydown, keypress). Make sure to specify either keyCode or charCode as
2546      * an option.
2547      * @private
2548      * @param {String} type The type of event ("keyup", "keydown" or "keypress").
2549      * @param {HTMLElement} target The target of the event.
2550      * @param {Object} options Options for the event. Either keyCode or charCode
2551      *                         are required.
2552      * @method fireKeyEvent
2553      * @static
2554      */     
2555     fireKeyEvent : function (type /*:String*/, target /*:HTMLElement*/,
2556                              options /*:Object*/) /*:Void*/ 
2557     {
2558         options = options || {};
2559         this.simulateKeyEvent(target, type, options.bubbles,
2560             options.cancelable, options.view, options.ctrlKey,
2561             options.altKey, options.shiftKey, options.metaKey, 
2562             options.keyCode, options.charCode);    
2563     },
2564     
2565     /**
2566      * Simulates a keydown event on a particular element.
2567      * @param {HTMLElement} target The element to act on.
2568      * @param {Object} options Additional event options (use DOM standard names).
2569      * @method keydown
2570      * @static
2571      */
2572     keydown : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2573         this.fireKeyEvent("keydown", target, options);
2574     },
2575     
2576     /**
2577      * Simulates a keypress on a particular element.
2578      * @param {HTMLElement} target The element to act on.
2579      * @param {Object} options Additional event options (use DOM standard names).
2580      * @method keypress
2581      * @static
2582      */
2583     keypress : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2584         this.fireKeyEvent("keypress", target, options);
2585     },
2586     
2587     /**
2588      * Simulates a keyup event on a particular element.
2589      * @param {HTMLElement} target The element to act on.
2590      * @param {Object} options Additional event options (use DOM standard names).
2591      * @method keyup
2592      * @static
2593      */
2594     keyup : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2595         this.fireKeyEvent("keyup", target, options);
2596     }
2597     
2598
2599 };
2600
2601 YAHOO.namespace("tool");
2602
2603 //-----------------------------------------------------------------------------
2604 // TestManager object
2605 //-----------------------------------------------------------------------------
2606
2607 /**
2608  * Runs pages containing test suite definitions.
2609  * @namespace YAHOO.tool
2610  * @class TestManager
2611  * @static
2612  */
2613 YAHOO.tool.TestManager = {
2614
2615     /**
2616      * Constant for the testpagebegin custom event
2617      * @property TEST_PAGE_BEGIN_EVENT
2618      * @static
2619      * @type string
2620      * @final
2621      */
2622     TEST_PAGE_BEGIN_EVENT /*:String*/ : "testpagebegin",
2623
2624     /**
2625      * Constant for the testpagecomplete custom event
2626      * @property TEST_PAGE_COMPLETE_EVENT
2627      * @static
2628      * @type string
2629      * @final
2630      */
2631     TEST_PAGE_COMPLETE_EVENT /*:String*/ : "testpagecomplete",
2632
2633     /**
2634      * Constant for the testmanagerbegin custom event
2635      * @property TEST_MANAGER_BEGIN_EVENT
2636      * @static
2637      * @type string
2638      * @final
2639      */
2640     TEST_MANAGER_BEGIN_EVENT /*:String*/ : "testmanagerbegin",
2641
2642     /**
2643      * Constant for the testmanagercomplete custom event
2644      * @property TEST_MANAGER_COMPLETE_EVENT
2645      * @static
2646      * @type string
2647      * @final
2648      */
2649     TEST_MANAGER_COMPLETE_EVENT /*:String*/ : "testmanagercomplete",
2650
2651     //-------------------------------------------------------------------------
2652     // Private Properties
2653     //-------------------------------------------------------------------------
2654     
2655     
2656     /**
2657      * The URL of the page currently being executed.
2658      * @type String
2659      * @private
2660      * @property _curPage
2661      * @static
2662      */
2663     _curPage /*:String*/ : null,
2664     
2665     /**
2666      * The frame used to load and run tests.
2667      * @type Window
2668      * @private
2669      * @property _frame
2670      * @static
2671      */
2672     _frame /*:Window*/ : null,
2673     
2674     /**
2675      * The logger used to output results from the various tests.
2676      * @type YAHOO.tool.TestLogger
2677      * @private
2678      * @property _logger
2679      * @static
2680      */
2681     _logger : null,
2682     
2683     /**
2684      * The timeout ID for the next iteration through the tests.
2685      * @type int
2686      * @private
2687      * @property _timeoutId
2688      * @static
2689      */
2690     _timeoutId /*:int*/ : 0,
2691     
2692     /**
2693      * Array of pages to load.
2694      * @type String[]
2695      * @private
2696      * @property _pages
2697      * @static
2698      */
2699     _pages /*:String[]*/ : [],
2700     
2701     /**
2702      * Aggregated results
2703      * @type Object
2704      * @private
2705      * @property _results
2706      * @static
2707      */
2708     _results: null,
2709     
2710     //-------------------------------------------------------------------------
2711     // Private Methods
2712     //-------------------------------------------------------------------------
2713     
2714     /**
2715      * Handles TestRunner.COMPLETE_EVENT, storing the results and beginning
2716      * the loop again.
2717      * @param {Object} data Data about the event.
2718      * @return {Void}
2719      * @private
2720      * @static
2721      */
2722     _handleTestRunnerComplete : function (data /*:Object*/) /*:Void*/ {
2723
2724         this.fireEvent(this.TEST_PAGE_COMPLETE_EVENT, {
2725                 page: this._curPage,
2726                 results: data.results
2727             });
2728     
2729         //save results
2730         //this._results[this.curPage] = data.results;
2731         
2732         //process 'em
2733         this._processResults(this._curPage, data.results);
2734         
2735         this._logger.clearTestRunner();
2736     
2737         //if there's more to do, set a timeout to begin again
2738         if (this._pages.length){
2739             this._timeoutId = setTimeout(function(){
2740                 YAHOO.tool.TestManager._run();
2741             }, 1000);
2742         } else {
2743             this.fireEvent(this.TEST_MANAGER_COMPLETE_EVENT, this._results);
2744         }
2745     },
2746     
2747     /**
2748      * Processes the results of a test page run, outputting log messages
2749      * for failed tests.
2750      * @return {Void}
2751      * @private
2752      * @static
2753      */
2754     _processResults : function (page /*:String*/, results /*:Object*/) /*:Void*/ {
2755
2756         var r = this._results;
2757         
2758         r.passed += results.passed;
2759         r.failed += results.failed;
2760         r.ignored += results.ignored;
2761         r.total += results.total;
2762         r.duration += results.duration;
2763         
2764         if (results.failed){
2765             r.failedPages.push(page);
2766         } else {
2767             r.passedPages.push(page);
2768         }
2769         
2770         results.name = page;
2771         results.type = "page";
2772         
2773         r[page] = results;
2774     },
2775     
2776     /**
2777      * Loads the next test page into the iframe.
2778      * @return {Void}
2779      * @static
2780      * @private
2781      */
2782     _run : function () /*:Void*/ {
2783     
2784         //set the current page
2785         this._curPage = this._pages.shift();
2786
2787         this.fireEvent(this.TEST_PAGE_BEGIN_EVENT, this._curPage);
2788         
2789         //load the frame - destroy history in case there are other iframes that
2790         //need testing
2791         this._frame.location.replace(this._curPage);
2792     
2793     },
2794         
2795     //-------------------------------------------------------------------------
2796     // Public Methods
2797     //-------------------------------------------------------------------------
2798     
2799     /**
2800      * Signals that a test page has been loaded. This should be called from
2801      * within the test page itself to notify the TestManager that it is ready.
2802      * @return {Void}
2803      * @static
2804      */
2805     load : function () /*:Void*/ {
2806         if (parent.YAHOO.tool.TestManager !== this){
2807             parent.YAHOO.tool.TestManager.load();
2808         } else {
2809             
2810             if (this._frame) {
2811                 //assign event handling
2812                 var TestRunner = this._frame.YAHOO.tool.TestRunner;
2813
2814                 this._logger.setTestRunner(TestRunner);
2815                 TestRunner.subscribe(TestRunner.COMPLETE_EVENT, this._handleTestRunnerComplete, this, true);
2816                 
2817                 //run it
2818                 TestRunner.run();
2819             }
2820         }
2821     },
2822     
2823     /**
2824      * Sets the pages to be loaded.
2825      * @param {String[]} pages An array of URLs to load.
2826      * @return {Void}
2827      * @static
2828      */
2829     setPages : function (pages /*:String[]*/) /*:Void*/ {
2830         this._pages = pages;
2831     },
2832     
2833     /**
2834      * Begins the process of running the tests.
2835      * @return {Void}
2836      * @static
2837      */
2838     start : function () /*:Void*/ {
2839
2840         if (!this._initialized) {
2841
2842             /**
2843              * Fires when loading a test page
2844              * @event testpagebegin
2845              * @param curPage {string} the page being loaded
2846              * @static
2847              */
2848             this.createEvent(this.TEST_PAGE_BEGIN_EVENT);
2849
2850             /**
2851              * Fires when a test page is complete
2852              * @event testpagecomplete
2853              * @param obj {page: string, results: object} the name of the
2854              * page that was loaded, and the test suite results
2855              * @static
2856              */
2857             this.createEvent(this.TEST_PAGE_COMPLETE_EVENT);
2858
2859             /**
2860              * Fires when the test manager starts running all test pages
2861              * @event testmanagerbegin
2862              * @static
2863              */
2864             this.createEvent(this.TEST_MANAGER_BEGIN_EVENT);
2865
2866             /**
2867              * Fires when the test manager finishes running all test pages.  External
2868              * test runners should subscribe to this event in order to get the
2869              * aggregated test results.
2870              * @event testmanagercomplete
2871              * @param obj { pages_passed: int, pages_failed: int, tests_passed: int
2872              *              tests_failed: int, passed: string[], failed: string[],
2873              *              page_results: {} }
2874              * @static
2875              */
2876             this.createEvent(this.TEST_MANAGER_COMPLETE_EVENT);
2877
2878             //create iframe if not already available
2879             if (!this._frame){
2880                 var frame /*:HTMLElement*/ = document.createElement("iframe");
2881                 frame.style.visibility = "hidden";
2882                 frame.style.position = "absolute";
2883                 document.body.appendChild(frame);
2884                 this._frame = frame.contentWindow || frame.contentDocument.parentWindow;
2885             }
2886             
2887             //create test logger if not already available
2888             if (!this._logger){
2889                 this._logger = new YAHOO.tool.TestLogger();
2890             }
2891
2892             this._initialized = true;
2893         }
2894
2895
2896         // reset the results cache
2897         this._results = {
2898         
2899             passed: 0,
2900             failed: 0,
2901             ignored: 0,
2902             total: 0,
2903             type: "report",
2904             name: "YUI Test Results",
2905             duration: 0,
2906             failedPages:[],
2907             passedPages:[]
2908             /*
2909             // number of pages that pass
2910             pages_passed: 0,
2911             // number of pages that fail
2912             pages_failed: 0,
2913             // total number of tests passed
2914             tests_passed: 0,
2915             // total number of tests failed
2916             tests_failed: 0,
2917             // array of pages that passed
2918             passed: [],
2919             // array of pages that failed
2920             failed: [],
2921             // map of full results for each page
2922             page_results: {}*/
2923         };
2924
2925         this.fireEvent(this.TEST_MANAGER_BEGIN_EVENT, null);
2926         this._run();
2927     
2928     },
2929
2930     /**
2931      * Stops the execution of tests.
2932      * @return {Void}
2933      * @static
2934      */
2935     stop : function () /*:Void*/ {
2936         clearTimeout(this._timeoutId);
2937     }
2938
2939 };
2940
2941 YAHOO.lang.augmentObject(YAHOO.tool.TestManager, YAHOO.util.EventProvider.prototype);
2942
2943
2944 YAHOO.namespace("tool");
2945
2946 //-----------------------------------------------------------------------------
2947 // TestLogger object
2948 //-----------------------------------------------------------------------------
2949
2950 /**
2951  * Displays test execution progress and results, providing filters based on
2952  * different key events.
2953  * @namespace YAHOO.tool
2954  * @class TestLogger
2955  * @constructor
2956  * @param {HTMLElement} element (Optional) The element to create the logger in.
2957  * @param {Object} config (Optional) Configuration options for the logger.
2958  */
2959 YAHOO.tool.TestLogger = function (element, config) {
2960     YAHOO.tool.TestLogger.superclass.constructor.call(this, element, config);
2961     this.init();
2962 };
2963
2964 YAHOO.lang.extend(YAHOO.tool.TestLogger, YAHOO.widget.LogReader, {
2965
2966     footerEnabled : true,
2967     newestOnTop : false,
2968
2969     /**
2970      * Formats message string to HTML for output to console.
2971      * @private
2972      * @method formatMsg
2973      * @param oLogMsg {Object} Log message object.
2974      * @return {String} HTML-formatted message for output to console.
2975      */
2976     formatMsg : function(message /*:Object*/) {
2977     
2978         var category /*:String*/ = message.category;        
2979         var text /*:String*/ = this.html2Text(message.msg);
2980         
2981         return "<pre><p><span class=\"" + category + "\">" + category.toUpperCase() + "</span> " + text + "</p></pre>";
2982     
2983     },
2984     
2985     //-------------------------------------------------------------------------
2986     // Private Methods
2987     //-------------------------------------------------------------------------
2988     
2989     /*
2990      * Initializes the logger.
2991      * @private
2992      */
2993     init : function () {
2994     
2995         //attach to any available TestRunner
2996         if (YAHOO.tool.TestRunner){
2997             this.setTestRunner(YAHOO.tool.TestRunner);
2998         }
2999         
3000         //hide useless sources
3001         this.hideSource("global");
3002         this.hideSource("LogReader");
3003         
3004         //hide useless message categories
3005         this.hideCategory("warn");
3006         this.hideCategory("window");
3007         this.hideCategory("time");
3008         
3009         //reset the logger
3010         this.clearConsole();
3011     },
3012     
3013     /**
3014      * Clears the reference to the TestRunner from previous operations. This 
3015      * unsubscribes all events and removes the object reference.
3016      * @return {Void}
3017      * @static
3018      */
3019     clearTestRunner : function () /*:Void*/ {
3020         if (this._runner){
3021             this._runner.unsubscribeAll();
3022             this._runner = null;
3023         }
3024     },
3025     
3026     /**
3027      * Sets the source test runner that the logger should monitor.
3028      * @param {YAHOO.tool.TestRunner} testRunner The TestRunner to observe.
3029      * @return {Void}
3030      * @static
3031      */
3032     setTestRunner : function (testRunner /*:YAHOO.tool.TestRunner*/) /*:Void*/ {
3033     
3034         if (this._runner){
3035             this.clearTestRunner();
3036         }
3037         
3038         this._runner = testRunner;
3039         
3040         //setup event _handlers
3041         testRunner.subscribe(testRunner.TEST_PASS_EVENT, this._handleTestRunnerEvent, this, true);
3042         testRunner.subscribe(testRunner.TEST_FAIL_EVENT, this._handleTestRunnerEvent, this, true);
3043         testRunner.subscribe(testRunner.TEST_IGNORE_EVENT, this._handleTestRunnerEvent, this, true);
3044         testRunner.subscribe(testRunner.BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
3045         testRunner.subscribe(testRunner.COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
3046         testRunner.subscribe(testRunner.TEST_SUITE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
3047         testRunner.subscribe(testRunner.TEST_SUITE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
3048         testRunner.subscribe(testRunner.TEST_CASE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
3049         testRunner.subscribe(testRunner.TEST_CASE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);    
3050     },
3051     
3052     //-------------------------------------------------------------------------
3053     // Event Handlers
3054     //-------------------------------------------------------------------------
3055     
3056     /**
3057      * Handles all TestRunner events, outputting appropriate data into the console.
3058      * @param {Object} data The event data object.
3059      * @return {Void}
3060      * @private
3061      */
3062     _handleTestRunnerEvent : function (data /*:Object*/) /*:Void*/ {
3063     
3064         //shortcut variables
3065         var TestRunner /*:Object*/ = YAHOO.tool.TestRunner;
3066     
3067         //data variables
3068         var message /*:String*/ = "";
3069         var messageType /*:String*/ = "";
3070         
3071         switch(data.type){
3072             case TestRunner.BEGIN_EVENT:
3073                 message = "Testing began at " + (new Date()).toString() + ".";
3074                 messageType = "info";
3075                 break;
3076                 
3077             case TestRunner.COMPLETE_EVENT:
3078                 message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" + 
3079                     data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
3080                 messageType = "info";
3081                 break;
3082                 
3083             case TestRunner.TEST_FAIL_EVENT:
3084                 message = data.testName + ": " + data.error.getMessage();
3085                 messageType = "fail";
3086                 break;
3087                 
3088             case TestRunner.TEST_IGNORE_EVENT:
3089                 message = data.testName + ": ignored.";
3090                 messageType = "ignore";
3091                 break;
3092                 
3093             case TestRunner.TEST_PASS_EVENT:
3094                 message = data.testName + ": passed.";
3095                 messageType = "pass";
3096                 break;
3097                 
3098             case TestRunner.TEST_SUITE_BEGIN_EVENT:
3099                 message = "Test suite \"" + data.testSuite.name + "\" started.";
3100                 messageType = "info";
3101                 break;
3102                 
3103             case TestRunner.TEST_SUITE_COMPLETE_EVENT:
3104                 message = "Test suite \"" + data.testSuite.name + "\" completed.\nPassed:" + 
3105                     data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
3106                 messageType = "info";
3107                 break;
3108                 
3109             case TestRunner.TEST_CASE_BEGIN_EVENT:
3110                 message = "Test case \"" + data.testCase.name + "\" started.";
3111                 messageType = "info";
3112                 break;
3113                 
3114             case TestRunner.TEST_CASE_COMPLETE_EVENT:
3115                 message = "Test case \"" + data.testCase.name + "\" completed.\nPassed:" + 
3116                     data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
3117                 messageType = "info";
3118                 break;
3119             default:
3120                 message = "Unexpected event " + data.type;
3121                 message = "info";
3122         }
3123     
3124         YAHOO.log(message, messageType, "TestRunner");    
3125     }
3126     
3127 });
3128
3129 YAHOO.namespace("tool.TestFormat");
3130
3131 /**
3132  * Returns test results formatted as a JSON string. Requires JSON utility.
3133  * @param {Object} result The results object created by TestRunner.
3134  * @return {String} An XML-formatted string of results.
3135  * @namespace YAHOO.tool.TestFormat
3136  * @method JSON
3137  * @static
3138  */
3139 YAHOO.tool.TestFormat.JSON = function(results /*:Object*/) /*:String*/ {
3140     return YAHOO.lang.JSON.stringify(results);
3141 };
3142
3143 /**
3144  * Returns test results formatted as an XML string.
3145  * @param {Object} result The results object created by TestRunner.
3146  * @return {String} An XML-formatted string of results.
3147  * @namespace YAHOO.tool.TestFormat
3148  * @method XML
3149  * @static
3150  */
3151 YAHOO.tool.TestFormat.XML = function(results /*:Object*/) /*:String*/ {
3152
3153     var l = YAHOO.lang;
3154     var xml /*:String*/ = "<" + results.type + " name=\"" + results.name.replace(/"/g, "&quot;").replace(/'/g, "&apos;") + "\"";
3155     
3156     if (l.isNumber(results.duration)){
3157         xml += " duration=\"" + results.duration + "\"";
3158     }
3159     
3160     if (results.type == "test"){
3161         xml += " result=\"" + results.result + "\" message=\"" + results.message + "\">";
3162     } else {
3163         xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
3164         for (var prop in results) {
3165             if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
3166                 xml += arguments.callee(results[prop]);
3167             }
3168         }        
3169     }
3170
3171     xml += "</" + results.type + ">";
3172     
3173     return xml;
3174
3175 };
3176
3177 YAHOO.namespace("tool");
3178
3179 /**
3180  * An object capable of sending test results to a server.
3181  * @param {String} url The URL to submit the results to.
3182  * @param {Function} format (Optiona) A function that outputs the results in a specific format.
3183  *      Default is YAHOO.tool.TestFormat.XML.
3184  * @constructor
3185  * @namespace YAHOO.tool
3186  * @class TestReporter
3187  */
3188 YAHOO.tool.TestReporter = function(url /*:String*/, format /*:Function*/) {
3189
3190     /**
3191      * The URL to submit the data to.
3192      * @type String
3193      * @property url
3194      */
3195     this.url /*:String*/ = url;
3196
3197     /**
3198      * The formatting function to call when submitting the data.
3199      * @type Function
3200      * @property format
3201      */
3202     this.format /*:Function*/ = format || YAHOO.tool.TestFormat.XML;
3203
3204     /**
3205      * Extra fields to submit with the request.
3206      * @type Object
3207      * @property _fields
3208      * @private
3209      */
3210     this._fields /*:Object*/ = new Object();
3211     
3212     /**
3213      * The form element used to submit the results.
3214      * @type HTMLFormElement
3215      * @property _form
3216      * @private
3217      */
3218     this._form /*:HTMLElement*/ = null;
3219
3220     /**
3221      * Iframe used as a target for form submission.
3222      * @type HTMLIFrameElement
3223      * @property _iframe
3224      * @private
3225      */
3226     this._iframe /*:HTMLElement*/ = null;
3227 };
3228
3229 YAHOO.tool.TestReporter.prototype = {
3230
3231     //restore missing constructor
3232     constructor: YAHOO.tool.TestReporter,
3233     
3234     /**
3235      * Convert a date into ISO format.
3236      * From Douglas Crockford's json2.js
3237      * @param {Date} date The date to convert.
3238      * @return {String} An ISO-formatted date string
3239      * @method _convertToISOString
3240      * @private
3241      */    
3242     _convertToISOString: function(date){
3243         function f(n) {
3244             // Format integers to have at least two digits.
3245             return n < 10 ? '0' + n : n;
3246         }
3247
3248         return date.getUTCFullYear()   + '-' +
3249              f(date.getUTCMonth() + 1) + '-' +
3250              f(date.getUTCDate())      + 'T' +
3251              f(date.getUTCHours())     + ':' +
3252              f(date.getUTCMinutes())   + ':' +
3253              f(date.getUTCSeconds())   + 'Z';     
3254     
3255     },
3256
3257     /**
3258      * Adds a field to the form that submits the results.
3259      * @param {String} name The name of the field.
3260      * @param {Variant} value The value of the field.
3261      * @return {Void}
3262      * @method addField
3263      */
3264     addField : function (name /*:String*/, value /*:Variant*/) /*:Void*/{
3265         this._fields[name] = value;    
3266     },
3267     
3268     /**
3269      * Removes all previous defined fields.
3270      * @return {Void}
3271      * @method addField
3272      */
3273     clearFields : function() /*:Void*/{
3274         this._fields = new Object();
3275     },
3276
3277     /**
3278      * Cleans up the memory associated with the TestReporter, removing DOM elements
3279      * that were created.
3280      * @return {Void}
3281      * @method destroy
3282      */
3283     destroy : function() /*:Void*/ {
3284         if (this._form){
3285             this._form.parentNode.removeChild(this._form);
3286             this._form = null;
3287         }        
3288         if (this._iframe){
3289             this._iframe.parentNode.removeChild(this._iframe);
3290             this._iframe = null;
3291         }
3292         this._fields = null;
3293     },
3294
3295     /**
3296      * Sends the report to the server.
3297      * @param {Object} results The results object created by TestRunner.
3298      * @return {Void}
3299      * @method report
3300      */
3301     report : function(results /*:Object*/) /*:Void*/{
3302     
3303         //if the form hasn't been created yet, create it
3304         if (!this._form){
3305             this._form = document.createElement("form");
3306             this._form.method = "post";
3307             this._form.style.visibility = "hidden";
3308             this._form.style.position = "absolute";
3309             this._form.style.top = 0;
3310             document.body.appendChild(this._form);
3311         
3312             //IE won't let you assign a name using the DOM, must do it the hacky way
3313             if (YAHOO.env.ua.ie){
3314                 this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
3315             } else {
3316                 this._iframe = document.createElement("iframe");
3317                 this._iframe.name = "yuiTestTarget";
3318             }
3319
3320             this._iframe.src = "javascript:false";
3321             this._iframe.style.visibility = "hidden";
3322             this._iframe.style.position = "absolute";
3323             this._iframe.style.top = 0;
3324             document.body.appendChild(this._iframe);
3325
3326             this._form.target = "yuiTestTarget";
3327         }
3328
3329         //set the form's action
3330         this._form.action = this.url;
3331     
3332         //remove any existing fields
3333         while(this._form.hasChildNodes()){
3334             this._form.removeChild(this._form.lastChild);
3335         }
3336         
3337         //create default fields
3338         this._fields.results = this.format(results);
3339         this._fields.useragent = navigator.userAgent;
3340         this._fields.timestamp = this._convertToISOString(new Date());
3341
3342         //add fields to the form
3343         for (var prop in this._fields){
3344             if (YAHOO.lang.hasOwnProperty(this._fields, prop) && typeof this._fields[prop] != "function"){
3345                 if (YAHOO.env.ua.ie){
3346                     input = document.createElement("<input name=\"" + prop + "\" >");
3347                 } else {
3348                     input = document.createElement("input");
3349                     input.name = prop;
3350                 }
3351                 input.type = "hidden";
3352                 input.value = this._fields[prop];
3353                 this._form.appendChild(input);
3354             }
3355         }
3356
3357         //remove default fields
3358         delete this._fields.results;
3359         delete this._fields.useragent;
3360         delete this._fields.timestamp;
3361         
3362         if (arguments[1] !== false){
3363             this._form.submit();
3364         }
3365     
3366     }
3367
3368 };
3369
3370 YAHOO.register("yuitest", YAHOO.tool.TestRunner, {version: "2.7.0", build: "1799"});