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