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