src/cm/media/js/lib/yui/yui_3.10.3/build/test/test-debug.js
changeset 525 89ef5ed3c48b
equal deleted inserted replaced
524:322d0feea350 525:89ef5ed3c48b
       
     1 /*
       
     2 YUI 3.10.3 (build 2fb5187)
       
     3 Copyright 2013 Yahoo! Inc. All rights reserved.
       
     4 Licensed under the BSD License.
       
     5 http://yuilibrary.com/license/
       
     6 */
       
     7 
       
     8 YUI.add('test', function (Y, NAME) {
       
     9 
       
    10 
       
    11 
       
    12 /**
       
    13  * YUI Test Framework
       
    14  * @module test
       
    15  * @main test
       
    16  */
       
    17 
       
    18 /*
       
    19  * The root namespace for YUI Test.
       
    20  */
       
    21 
       
    22 //So we only ever have one YUITest object that's shared
       
    23 if (YUI.YUITest) {
       
    24     Y.Test = YUI.YUITest;
       
    25 } else { //Ends after the YUITest definitions
       
    26 
       
    27     //Make this global for back compat
       
    28     YUITest = {
       
    29         version: "3.10.3",
       
    30         guid: function(pre) {
       
    31             return Y.guid(pre);
       
    32         }
       
    33     };
       
    34 
       
    35 Y.namespace('Test');
       
    36 
       
    37 
       
    38 //Using internal YUI methods here
       
    39 YUITest.Object = Y.Object;
       
    40 YUITest.Array = Y.Array;
       
    41 YUITest.Util = {
       
    42     mix: Y.mix,
       
    43     JSON: Y.JSON
       
    44 };
       
    45 
       
    46 /**
       
    47  * Simple custom event implementation.
       
    48  * @namespace Test
       
    49  * @module test
       
    50  * @class EventTarget
       
    51  * @constructor
       
    52  */
       
    53 YUITest.EventTarget = function(){
       
    54 
       
    55     /**
       
    56      * Event handlers for the various events.
       
    57      * @type Object
       
    58      * @private
       
    59      * @property _handlers
       
    60      * @static
       
    61      */
       
    62     this._handlers = {};
       
    63 
       
    64 };
       
    65     
       
    66 YUITest.EventTarget.prototype = {
       
    67 
       
    68     //restore prototype
       
    69     constructor: YUITest.EventTarget,
       
    70             
       
    71     //-------------------------------------------------------------------------
       
    72     // Event Handling
       
    73     //-------------------------------------------------------------------------
       
    74     
       
    75     /**
       
    76      * Adds a listener for a given event type.
       
    77      * @param {String} type The type of event to add a listener for.
       
    78      * @param {Function} listener The function to call when the event occurs.
       
    79      * @return {void}
       
    80      * @method attach
       
    81      */
       
    82     attach: function(type, listener){
       
    83         if (typeof this._handlers[type] == "undefined"){
       
    84             this._handlers[type] = [];
       
    85         }
       
    86 
       
    87         this._handlers[type].push(listener);
       
    88     },
       
    89     
       
    90     /**
       
    91      * Adds a listener for a given event type.
       
    92      * @param {String} type The type of event to add a listener for.
       
    93      * @param {Function} listener The function to call when the event occurs.
       
    94      * @return {void}
       
    95      * @method subscribe
       
    96      * @deprecated
       
    97      */
       
    98     subscribe: function(type, listener){
       
    99         this.attach.apply(this, arguments);
       
   100     },
       
   101     
       
   102     /**
       
   103      * Fires an event based on the passed-in object.
       
   104      * @param {Object|String} event An object with at least a 'type' attribute
       
   105      *      or a string indicating the event name.
       
   106      * @return {void}
       
   107      * @method fire
       
   108      */    
       
   109     fire: function(event){
       
   110         if (typeof event == "string"){
       
   111             event = { type: event };
       
   112         }
       
   113         if (!event.target){
       
   114             event.target = this;
       
   115         }
       
   116         
       
   117         if (!event.type){
       
   118             throw new Error("Event object missing 'type' property.");
       
   119         }
       
   120         
       
   121         if (this._handlers[event.type] instanceof Array){
       
   122             var handlers = this._handlers[event.type];
       
   123             for (var i=0, len=handlers.length; i < len; i++){
       
   124                 handlers[i].call(this, event);
       
   125             }
       
   126         }            
       
   127     },
       
   128 
       
   129     /**
       
   130      * Removes a listener for a given event type.
       
   131      * @param {String} type The type of event to remove a listener from.
       
   132      * @param {Function} listener The function to remove from the event.
       
   133      * @return {void}
       
   134      * @method detach
       
   135      */
       
   136     detach: function(type, listener){
       
   137         if (this._handlers[type] instanceof Array){
       
   138             var handlers = this._handlers[type];
       
   139             for (var i=0, len=handlers.length; i < len; i++){
       
   140                 if (handlers[i] === listener){
       
   141                     handlers.splice(i, 1);
       
   142                     break;
       
   143                 }
       
   144             }
       
   145         }            
       
   146     },
       
   147     
       
   148     /**
       
   149      * Removes a listener for a given event type.
       
   150      * @param {String} type The type of event to remove a listener from.
       
   151      * @param {Function} listener The function to remove from the event.
       
   152      * @return {void}
       
   153      * @method unsubscribe
       
   154      * @deprecated
       
   155      */
       
   156     unsubscribe: function(type, listener){
       
   157         this.detach.apply(this, arguments);          
       
   158     }    
       
   159 
       
   160 };
       
   161 
       
   162     
       
   163 /**
       
   164  * A test suite that can contain a collection of TestCase and TestSuite objects.
       
   165  * @param {String||Object} data The name of the test suite or an object containing
       
   166  *      a name property as well as setUp and tearDown methods.
       
   167  * @namespace Test
       
   168  * @module test
       
   169  * @class TestSuite
       
   170  * @constructor
       
   171  */
       
   172 YUITest.TestSuite = function (data) {
       
   173 
       
   174     /**
       
   175      * The name of the test suite.
       
   176      * @type String
       
   177      * @property name
       
   178      */
       
   179     this.name = "";
       
   180 
       
   181     /**
       
   182      * Array of test suites and test cases.
       
   183      * @type Array
       
   184      * @property items
       
   185      * @private
       
   186      */
       
   187     this.items = [];
       
   188 
       
   189     //initialize the properties
       
   190     if (typeof data == "string"){
       
   191         this.name = data;
       
   192     } else if (data instanceof Object){
       
   193         for (var prop in data){
       
   194             if (data.hasOwnProperty(prop)){
       
   195                 this[prop] = data[prop];
       
   196             }
       
   197         }
       
   198     }
       
   199 
       
   200     //double-check name
       
   201     if (this.name === "" || !this.name) {
       
   202         this.name = YUITest.guid("testSuite_");
       
   203     }
       
   204 
       
   205 };
       
   206     
       
   207 YUITest.TestSuite.prototype = {
       
   208     
       
   209     //restore constructor
       
   210     constructor: YUITest.TestSuite,
       
   211     
       
   212     /**
       
   213      * Adds a test suite or test case to the test suite.
       
   214      * @param {Test.TestSuite||YUITest.TestCase} testObject The test suite or test case to add.
       
   215      * @return {Void}
       
   216      * @method add
       
   217      */
       
   218     add : function (testObject) {
       
   219         if (testObject instanceof YUITest.TestSuite || testObject instanceof YUITest.TestCase) {
       
   220             this.items.push(testObject);
       
   221         }
       
   222         return this;
       
   223     },
       
   224     
       
   225     //-------------------------------------------------------------------------
       
   226     // Stub Methods
       
   227     //-------------------------------------------------------------------------
       
   228 
       
   229     /**
       
   230      * Function to run before each test is executed.
       
   231      * @return {Void}
       
   232      * @method setUp
       
   233      */
       
   234     setUp : function () {
       
   235     },
       
   236     
       
   237     /**
       
   238      * Function to run after each test is executed.
       
   239      * @return {Void}
       
   240      * @method tearDown
       
   241      */
       
   242     tearDown: function () {
       
   243     }
       
   244     
       
   245 };
       
   246 /**
       
   247  * Test case containing various tests to run.
       
   248  * @param template An object containing any number of test methods, other methods,
       
   249  *                 an optional name, and anything else the test case needs.
       
   250  * @module test
       
   251  * @class TestCase
       
   252  * @namespace Test
       
   253  * @constructor
       
   254  */
       
   255 
       
   256 
       
   257 
       
   258 YUITest.TestCase = function (template) {
       
   259     
       
   260     /*
       
   261      * Special rules for the test case. Possible subobjects
       
   262      * are fail, for tests that should fail, and error, for
       
   263      * tests that should throw an error.
       
   264      */
       
   265     this._should = {};
       
   266     
       
   267     //copy over all properties from the template to this object
       
   268     for (var prop in template) {
       
   269         this[prop] = template[prop];
       
   270     }    
       
   271     
       
   272     //check for a valid name
       
   273     if (typeof this.name != "string") {
       
   274         this.name = YUITest.guid("testCase_");
       
   275     }
       
   276 
       
   277 };
       
   278 
       
   279         
       
   280 YUITest.TestCase.prototype = {  
       
   281 
       
   282     //restore constructor
       
   283     constructor: YUITest.TestCase,
       
   284     
       
   285     /**
       
   286      * Method to call from an async init method to
       
   287      * restart the test case. When called, returns a function
       
   288      * that should be called when tests are ready to continue.
       
   289      * @method callback
       
   290      * @return {Function} The function to call as a callback.
       
   291      */
       
   292     callback: function(){
       
   293         return YUITest.TestRunner.callback.apply(YUITest.TestRunner,arguments);
       
   294     },
       
   295 
       
   296     /**
       
   297      * Resumes a paused test and runs the given function.
       
   298      * @param {Function} segment (Optional) The function to run.
       
   299      *      If omitted, the test automatically passes.
       
   300      * @return {Void}
       
   301      * @method resume
       
   302      */
       
   303     resume : function (segment) {
       
   304         YUITest.TestRunner.resume(segment);
       
   305     },
       
   306 
       
   307     /**
       
   308      * Causes the test case to wait a specified amount of time and then
       
   309      * continue executing the given code.
       
   310      * @param {Function} segment (Optional) The function to run after the delay.
       
   311      *      If omitted, the TestRunner will wait until resume() is called.
       
   312      * @param {int} delay (Optional) The number of milliseconds to wait before running
       
   313      *      the function. If omitted, defaults to zero.
       
   314      * @return {Void}
       
   315      * @method wait
       
   316      */
       
   317     wait : function (segment, delay){
       
   318         
       
   319         var actualDelay = (typeof segment == "number" ? segment : delay);
       
   320         actualDelay = (typeof actualDelay == "number" ? actualDelay : 10000);
       
   321     
       
   322 		if (typeof segment == "function"){
       
   323             throw new YUITest.Wait(segment, actualDelay);
       
   324         } else {
       
   325             throw new YUITest.Wait(function(){
       
   326                 YUITest.Assert.fail("Timeout: wait() called but resume() never called.");
       
   327             }, actualDelay);
       
   328         }
       
   329     },
       
   330     
       
   331     //-------------------------------------------------------------------------
       
   332     // Assertion Methods
       
   333     //-------------------------------------------------------------------------
       
   334 
       
   335     /**
       
   336      * Asserts that a given condition is true. If not, then a YUITest.AssertionError object is thrown
       
   337      * and the test fails.
       
   338      * @method assert
       
   339      * @param {Boolean} condition The condition to test.
       
   340      * @param {String} message The message to display if the assertion fails.
       
   341      */
       
   342     assert : function (condition, message){
       
   343         YUITest.Assert._increment();
       
   344         if (!condition){
       
   345             throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Assertion failed."));
       
   346         }    
       
   347     },
       
   348     
       
   349     /**
       
   350      * Forces an assertion error to occur. Shortcut for YUITest.Assert.fail().
       
   351      * @method fail
       
   352      * @param {String} message (Optional) The message to display with the failure.
       
   353      */
       
   354     fail: function (message) {    
       
   355         YUITest.Assert.fail(message);
       
   356     },
       
   357     
       
   358     //-------------------------------------------------------------------------
       
   359     // Stub Methods
       
   360     //-------------------------------------------------------------------------
       
   361 
       
   362     /**
       
   363      * Function to run once before tests start to run.
       
   364      * This executes before the first call to setUp().
       
   365      * @method init
       
   366      */
       
   367     init: function(){
       
   368         //noop
       
   369     },
       
   370     
       
   371     /**
       
   372      * Function to run once after tests finish running.
       
   373      * This executes after the last call to tearDown().
       
   374      * @method destroy
       
   375      */
       
   376     destroy: function(){
       
   377         //noop
       
   378     },
       
   379 
       
   380     /**
       
   381      * Function to run before each test is executed.
       
   382      * @return {Void}
       
   383      * @method setUp
       
   384      */
       
   385     setUp : function () {
       
   386         //noop
       
   387     },
       
   388     
       
   389     /**
       
   390      * Function to run after each test is executed.
       
   391      * @return {Void}
       
   392      * @method tearDown
       
   393      */
       
   394     tearDown: function () {    
       
   395         //noop
       
   396     }
       
   397 };
       
   398 /**
       
   399  * An object object containing test result formatting methods.
       
   400  * @namespace Test
       
   401  * @module test
       
   402  * @class TestFormat
       
   403  * @static
       
   404  */
       
   405 YUITest.TestFormat = function(){
       
   406     
       
   407     /* (intentionally not documented)
       
   408      * Basic XML escaping method. Replaces quotes, less-than, greater-than,
       
   409      * apostrophe, and ampersand characters with their corresponding entities.
       
   410      * @param {String} text The text to encode.
       
   411      * @return {String} The XML-escaped text.
       
   412      */
       
   413     function xmlEscape(text){
       
   414     
       
   415         return text.replace(/[<>"'&]/g, function(value){
       
   416             switch(value){
       
   417                 case "<":   return "&lt;";
       
   418                 case ">":   return "&gt;";
       
   419                 case "\"":  return "&quot;";
       
   420                 case "'":   return "&apos;";
       
   421                 case "&":   return "&amp;";
       
   422             }
       
   423         });
       
   424     
       
   425     }
       
   426         
       
   427         
       
   428     return {
       
   429     
       
   430         /**
       
   431          * Returns test results formatted as a JSON string. Requires JSON utility.
       
   432          * @param {Object} result The results object created by TestRunner.
       
   433          * @return {String} A JSON-formatted string of results.
       
   434          * @method JSON
       
   435          * @static
       
   436          */
       
   437         JSON: function(results) {
       
   438             return YUITest.Util.JSON.stringify(results);
       
   439         },
       
   440         
       
   441         /**
       
   442          * Returns test results formatted as an XML string.
       
   443          * @param {Object} result The results object created by TestRunner.
       
   444          * @return {String} An XML-formatted string of results.
       
   445          * @method XML
       
   446          * @static
       
   447          */
       
   448         XML: function(results) {
       
   449 
       
   450             function serializeToXML(results){
       
   451                 var xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
       
   452                 
       
   453                 if (typeof(results.duration)=="number"){
       
   454                     xml += " duration=\"" + results.duration + "\"";
       
   455                 }
       
   456                 
       
   457                 if (results.type == "test"){
       
   458                     xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
       
   459                 } else {
       
   460                     xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
       
   461                     for (var prop in results){
       
   462                         if (results.hasOwnProperty(prop)){
       
   463                             if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
       
   464                                 xml += serializeToXML(results[prop]);
       
   465                             }
       
   466                         }
       
   467                     }       
       
   468                 }
       
   469 
       
   470                 xml += "</" + results.type + ">";
       
   471                 
       
   472                 return xml;    
       
   473             }
       
   474 
       
   475             return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToXML(results);
       
   476 
       
   477         },
       
   478 
       
   479 
       
   480         /**
       
   481          * Returns test results formatted in JUnit XML format.
       
   482          * @param {Object} result The results object created by TestRunner.
       
   483          * @return {String} An XML-formatted string of results.
       
   484          * @method JUnitXML
       
   485          * @static
       
   486          */
       
   487         JUnitXML: function(results) {
       
   488 
       
   489             function serializeToJUnitXML(results){
       
   490                 var xml = "";
       
   491                     
       
   492                 switch (results.type){
       
   493                     //equivalent to testcase in JUnit
       
   494                     case "test":
       
   495                         if (results.result != "ignore"){
       
   496                             xml = "<testcase name=\"" + xmlEscape(results.name) + "\" time=\"" + (results.duration/1000) + "\">";
       
   497                             if (results.result == "fail"){
       
   498                                 xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
       
   499                             }
       
   500                             xml+= "</testcase>";
       
   501                         }
       
   502                         break;
       
   503                         
       
   504                     //equivalent to testsuite in JUnit
       
   505                     case "testcase":
       
   506                     
       
   507                         xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\" time=\"" + (results.duration/1000) + "\">";
       
   508                         
       
   509                         for (var prop in results){
       
   510                             if (results.hasOwnProperty(prop)){
       
   511                                 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
       
   512                                     xml += serializeToJUnitXML(results[prop]);
       
   513                                 }
       
   514                             }
       
   515                         }            
       
   516                         
       
   517                         xml += "</testsuite>";
       
   518                         break;
       
   519                     
       
   520                     //no JUnit equivalent, don't output anything
       
   521                     case "testsuite":
       
   522                         for (var prop in results){
       
   523                             if (results.hasOwnProperty(prop)){
       
   524                                 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
       
   525                                     xml += serializeToJUnitXML(results[prop]);
       
   526                                 }
       
   527                             }
       
   528                         }                                                     
       
   529                         break;
       
   530                         
       
   531                     //top-level, equivalent to testsuites in JUnit
       
   532                     case "report":
       
   533                     
       
   534                         xml = "<testsuites>";
       
   535                     
       
   536                         for (var prop in results){
       
   537                             if (results.hasOwnProperty(prop)){
       
   538                                 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
       
   539                                     xml += serializeToJUnitXML(results[prop]);
       
   540                                 }
       
   541                             }
       
   542                         }            
       
   543                         
       
   544                         xml += "</testsuites>";            
       
   545                     
       
   546                     //no default
       
   547                 }
       
   548                 
       
   549                 return xml;
       
   550          
       
   551             }
       
   552 
       
   553             return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToJUnitXML(results);
       
   554         },
       
   555     
       
   556         /**
       
   557          * Returns test results formatted in TAP format.
       
   558          * For more information, see <a href="http://testanything.org/">Test Anything Protocol</a>.
       
   559          * @param {Object} result The results object created by TestRunner.
       
   560          * @return {String} A TAP-formatted string of results.
       
   561          * @method TAP
       
   562          * @static
       
   563          */
       
   564         TAP: function(results) {
       
   565         
       
   566             var currentTestNum = 1;
       
   567 
       
   568             function serializeToTAP(results){
       
   569                 var text = "";
       
   570                     
       
   571                 switch (results.type){
       
   572 
       
   573                     case "test":
       
   574                         if (results.result != "ignore"){
       
   575 
       
   576                             text = "ok " + (currentTestNum++) + " - " + results.name;
       
   577                             
       
   578                             if (results.result == "fail"){
       
   579                                 text = "not " + text + " - " + results.message;
       
   580                             }
       
   581                             
       
   582                             text += "\n";
       
   583                         } else {
       
   584                             text = "#Ignored test " + results.name + "\n";
       
   585                         }
       
   586                         break;
       
   587                         
       
   588                     case "testcase":
       
   589                     
       
   590                         text = "#Begin testcase " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
       
   591                                         
       
   592                         for (var prop in results){
       
   593                             if (results.hasOwnProperty(prop)){
       
   594                                 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
       
   595                                     text += serializeToTAP(results[prop]);
       
   596                                 }
       
   597                             }
       
   598                         }            
       
   599                         
       
   600                         text += "#End testcase " + results.name + "\n";
       
   601                         
       
   602                         
       
   603                         break;
       
   604                     
       
   605                     case "testsuite":
       
   606 
       
   607                         text = "#Begin testsuite " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";                
       
   608                     
       
   609                         for (var prop in results){
       
   610                             if (results.hasOwnProperty(prop)){
       
   611                                 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
       
   612                                     text += serializeToTAP(results[prop]);
       
   613                                 }
       
   614                             }
       
   615                         }                                                      
       
   616 
       
   617                         text += "#End testsuite " + results.name + "\n";
       
   618                         break;
       
   619 
       
   620                     case "report":
       
   621                     
       
   622                         for (var prop in results){
       
   623                             if (results.hasOwnProperty(prop)){
       
   624                                 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
       
   625                                     text += serializeToTAP(results[prop]);
       
   626                                 }
       
   627                             }
       
   628                         }              
       
   629                         
       
   630                     //no default
       
   631                 }
       
   632                 
       
   633                 return text;
       
   634          
       
   635             }
       
   636 
       
   637             return "1.." + results.total + "\n" + serializeToTAP(results);
       
   638         }
       
   639     
       
   640     };
       
   641 }();
       
   642     
       
   643     /**
       
   644      * An object capable of sending test results to a server.
       
   645      * @param {String} url The URL to submit the results to.
       
   646      * @param {Function} format (Optiona) A function that outputs the results in a specific format.
       
   647      *      Default is YUITest.TestFormat.XML.
       
   648      * @constructor
       
   649      * @namespace Test
       
   650      * @module test
       
   651  * @class Reporter
       
   652      */
       
   653     YUITest.Reporter = function(url, format) {
       
   654     
       
   655         /**
       
   656          * The URL to submit the data to.
       
   657          * @type String
       
   658          * @property url
       
   659          */
       
   660         this.url = url;
       
   661     
       
   662         /**
       
   663          * The formatting function to call when submitting the data.
       
   664          * @type Function
       
   665          * @property format
       
   666          */
       
   667         this.format = format || YUITest.TestFormat.XML;
       
   668     
       
   669         /**
       
   670          * Extra fields to submit with the request.
       
   671          * @type Object
       
   672          * @property _fields
       
   673          * @private
       
   674          */
       
   675         this._fields = new Object();
       
   676         
       
   677         /**
       
   678          * The form element used to submit the results.
       
   679          * @type HTMLFormElement
       
   680          * @property _form
       
   681          * @private
       
   682          */
       
   683         this._form = null;
       
   684     
       
   685         /**
       
   686          * Iframe used as a target for form submission.
       
   687          * @type HTMLIFrameElement
       
   688          * @property _iframe
       
   689          * @private
       
   690          */
       
   691         this._iframe = null;
       
   692     };
       
   693     
       
   694     YUITest.Reporter.prototype = {
       
   695     
       
   696         //restore missing constructor
       
   697         constructor: YUITest.Reporter,
       
   698     
       
   699         /**
       
   700          * Adds a field to the form that submits the results.
       
   701          * @param {String} name The name of the field.
       
   702          * @param {Variant} value The value of the field.
       
   703          * @return {Void}
       
   704          * @method addField
       
   705          */
       
   706         addField : function (name, value){
       
   707             this._fields[name] = value;    
       
   708         },
       
   709         
       
   710         /**
       
   711          * Removes all previous defined fields.
       
   712          * @return {Void}
       
   713          * @method clearFields
       
   714          */
       
   715         clearFields : function(){
       
   716             this._fields = new Object();
       
   717         },
       
   718     
       
   719         /**
       
   720          * Cleans up the memory associated with the TestReporter, removing DOM elements
       
   721          * that were created.
       
   722          * @return {Void}
       
   723          * @method destroy
       
   724          */
       
   725         destroy : function() {
       
   726             if (this._form){
       
   727                 this._form.parentNode.removeChild(this._form);
       
   728                 this._form = null;
       
   729             }        
       
   730             if (this._iframe){
       
   731                 this._iframe.parentNode.removeChild(this._iframe);
       
   732                 this._iframe = null;
       
   733             }
       
   734             this._fields = null;
       
   735         },
       
   736     
       
   737         /**
       
   738          * Sends the report to the server.
       
   739          * @param {Object} results The results object created by TestRunner.
       
   740          * @return {Void}
       
   741          * @method report
       
   742          */
       
   743         report : function(results){
       
   744         
       
   745             //if the form hasn't been created yet, create it
       
   746             if (!this._form){
       
   747                 this._form = document.createElement("form");
       
   748                 this._form.method = "post";
       
   749                 this._form.style.visibility = "hidden";
       
   750                 this._form.style.position = "absolute";
       
   751                 this._form.style.top = 0;
       
   752                 document.body.appendChild(this._form);
       
   753             
       
   754                 //IE won't let you assign a name using the DOM, must do it the hacky way
       
   755                 try {
       
   756                     this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
       
   757                 } catch (ex){
       
   758                     this._iframe = document.createElement("iframe");
       
   759                     this._iframe.name = "yuiTestTarget";
       
   760                 }
       
   761     
       
   762                 this._iframe.src = "javascript:false";
       
   763                 this._iframe.style.visibility = "hidden";
       
   764                 this._iframe.style.position = "absolute";
       
   765                 this._iframe.style.top = 0;
       
   766                 document.body.appendChild(this._iframe);
       
   767     
       
   768                 this._form.target = "yuiTestTarget";
       
   769             }
       
   770     
       
   771             //set the form's action
       
   772             this._form.action = this.url;
       
   773         
       
   774             //remove any existing fields
       
   775             while(this._form.hasChildNodes()){
       
   776                 this._form.removeChild(this._form.lastChild);
       
   777             }
       
   778             
       
   779             //create default fields
       
   780             this._fields.results = this.format(results);
       
   781             this._fields.useragent = navigator.userAgent;
       
   782             this._fields.timestamp = (new Date()).toLocaleString();
       
   783     
       
   784             //add fields to the form
       
   785             for (var prop in this._fields){
       
   786                 var value = this._fields[prop];
       
   787                 if (this._fields.hasOwnProperty(prop) && (typeof value != "function")){
       
   788                     var input = document.createElement("input");
       
   789                     input.type = "hidden";
       
   790                     input.name = prop;
       
   791                     input.value = value;
       
   792                     this._form.appendChild(input);
       
   793                 }
       
   794             }
       
   795     
       
   796             //remove default fields
       
   797             delete this._fields.results;
       
   798             delete this._fields.useragent;
       
   799             delete this._fields.timestamp;
       
   800             
       
   801             if (arguments[1] !== false){
       
   802                 this._form.submit();
       
   803             }
       
   804         
       
   805         }
       
   806     
       
   807     };
       
   808     
       
   809     /**
       
   810      * Runs test suites and test cases, providing events to allowing for the
       
   811      * interpretation of test results.
       
   812      * @namespace Test
       
   813      * @module test
       
   814  * @class TestRunner
       
   815      * @static
       
   816      */
       
   817     YUITest.TestRunner = function(){
       
   818 
       
   819         /*(intentionally not documented)
       
   820          * Determines if any of the array of test groups appears
       
   821          * in the given TestRunner filter.
       
   822          * @param {Array} testGroups The array of test groups to
       
   823          *      search for.
       
   824          * @param {String} filter The TestRunner groups filter.
       
   825          */
       
   826         function inGroups(testGroups, filter){
       
   827             if (!filter.length){
       
   828                 return true;
       
   829             } else {                
       
   830                 if (testGroups){
       
   831                     for (var i=0, len=testGroups.length; i < len; i++){
       
   832                         if (filter.indexOf("," + testGroups[i] + ",") > -1){
       
   833                             return true;
       
   834                         }
       
   835                     }
       
   836                 }
       
   837                 return false;
       
   838             }
       
   839         }
       
   840     
       
   841         /**
       
   842          * A node in the test tree structure. May represent a TestSuite, TestCase, or
       
   843          * test function.
       
   844          * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
       
   845          * @module test
       
   846  * @class TestNode
       
   847          * @constructor
       
   848          * @private
       
   849          */
       
   850         function TestNode(testObject){
       
   851         
       
   852             /**
       
   853              * The TestSuite, TestCase, or test function represented by this node.
       
   854              * @type Variant
       
   855              * @property testObject
       
   856              */
       
   857             this.testObject = testObject;
       
   858             
       
   859             /**
       
   860              * Pointer to this node's first child.
       
   861              * @type TestNode
       
   862              * @property firstChild
       
   863              */        
       
   864             this.firstChild = null;
       
   865             
       
   866             /**
       
   867              * Pointer to this node's last child.
       
   868              * @type TestNode
       
   869              * @property lastChild
       
   870              */        
       
   871             this.lastChild = null;
       
   872             
       
   873             /**
       
   874              * Pointer to this node's parent.
       
   875              * @type TestNode
       
   876              * @property parent
       
   877              */        
       
   878             this.parent = null; 
       
   879        
       
   880             /**
       
   881              * Pointer to this node's next sibling.
       
   882              * @type TestNode
       
   883              * @property next
       
   884              */        
       
   885             this.next = null;
       
   886             
       
   887             /**
       
   888              * Test results for this test object.
       
   889              * @type object
       
   890              * @property results
       
   891              */                
       
   892             this.results = new YUITest.Results();
       
   893             
       
   894             //initialize results
       
   895             if (testObject instanceof YUITest.TestSuite){
       
   896                 this.results.type = "testsuite";
       
   897                 this.results.name = testObject.name;
       
   898             } else if (testObject instanceof YUITest.TestCase){
       
   899                 this.results.type = "testcase";
       
   900                 this.results.name = testObject.name;
       
   901             }
       
   902            
       
   903         }
       
   904         
       
   905         TestNode.prototype = {
       
   906         
       
   907             /**
       
   908              * Appends a new test object (TestSuite, TestCase, or test function name) as a child
       
   909              * of this node.
       
   910              * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
       
   911              * @return {Void}
       
   912              * @method appendChild
       
   913              */
       
   914             appendChild : function (testObject){
       
   915                 var node = new TestNode(testObject);
       
   916                 if (this.firstChild === null){
       
   917                     this.firstChild = this.lastChild = node;
       
   918                 } else {
       
   919                     this.lastChild.next = node;
       
   920                     this.lastChild = node;
       
   921                 }
       
   922                 node.parent = this;
       
   923                 return node;
       
   924             }       
       
   925         };
       
   926     
       
   927         /**
       
   928          * Runs test suites and test cases, providing events to allowing for the
       
   929          * interpretation of test results.
       
   930          * @namespace Test
       
   931          * @module test
       
   932  * @class Runner
       
   933          * @static
       
   934          */
       
   935         function TestRunner(){
       
   936         
       
   937             //inherit from EventTarget
       
   938             YUITest.EventTarget.call(this);
       
   939             
       
   940             /**
       
   941              * Suite on which to attach all TestSuites and TestCases to be run.
       
   942              * @type YUITest.TestSuite
       
   943              * @property masterSuite
       
   944              * @static
       
   945              * @private
       
   946              */
       
   947             this.masterSuite = new YUITest.TestSuite(YUITest.guid('testSuite_'));
       
   948     
       
   949             /**
       
   950              * Pointer to the current node in the test tree.
       
   951              * @type TestNode
       
   952              * @private
       
   953              * @property _cur
       
   954              * @static
       
   955              */
       
   956             this._cur = null;
       
   957             
       
   958             /**
       
   959              * Pointer to the root node in the test tree.
       
   960              * @type TestNode
       
   961              * @private
       
   962              * @property _root
       
   963              * @static
       
   964              */
       
   965             this._root = null;
       
   966             
       
   967             /**
       
   968              * Indicates if the TestRunner will log events or not.
       
   969              * @type Boolean
       
   970              * @property _log
       
   971              * @private
       
   972              * @static
       
   973              */
       
   974             this._log = true;
       
   975             
       
   976             /**
       
   977              * Indicates if the TestRunner is waiting as a result of
       
   978              * wait() being called.
       
   979              * @type Boolean
       
   980              * @property _waiting
       
   981              * @private
       
   982              * @static
       
   983              */
       
   984             this._waiting = false;
       
   985             
       
   986             /**
       
   987              * Indicates if the TestRunner is currently running tests.
       
   988              * @type Boolean
       
   989              * @private
       
   990              * @property _running
       
   991              * @static
       
   992              */
       
   993             this._running = false;
       
   994             
       
   995             /**
       
   996              * Holds copy of the results object generated when all tests are
       
   997              * complete.
       
   998              * @type Object
       
   999              * @private
       
  1000              * @property _lastResults
       
  1001              * @static
       
  1002              */
       
  1003             this._lastResults = null;       
       
  1004             
       
  1005             /**
       
  1006              * Data object that is passed around from method to method.
       
  1007              * @type Object
       
  1008              * @private
       
  1009              * @property _data
       
  1010              * @static
       
  1011              */
       
  1012             this._context = null;
       
  1013             
       
  1014             /**
       
  1015              * The list of test groups to run. The list is represented
       
  1016              * by a comma delimited string with commas at the start and
       
  1017              * end.
       
  1018              * @type String
       
  1019              * @private
       
  1020              * @property _groups
       
  1021              * @static
       
  1022              */
       
  1023             this._groups = "";
       
  1024 
       
  1025         }
       
  1026         
       
  1027         TestRunner.prototype = YUITest.Util.mix(new YUITest.EventTarget(), {
       
  1028             
       
  1029             /**
       
  1030             * If true, YUITest will not fire an error for tests with no Asserts.
       
  1031             * @property _ignoreEmpty
       
  1032             * @private
       
  1033             * @type Boolean
       
  1034             * @static
       
  1035             */
       
  1036             _ignoreEmpty: false,
       
  1037 
       
  1038             //restore prototype
       
  1039             constructor: YUITest.TestRunner,
       
  1040         
       
  1041             //-------------------------------------------------------------------------
       
  1042             // Constants
       
  1043             //-------------------------------------------------------------------------
       
  1044              
       
  1045             /**
       
  1046              * Fires when a test case is opened but before the first 
       
  1047              * test is executed.
       
  1048              * @event testcasebegin
       
  1049              * @static
       
  1050              */         
       
  1051             TEST_CASE_BEGIN_EVENT : "testcasebegin",
       
  1052             
       
  1053             /**
       
  1054              * Fires when all tests in a test case have been executed.
       
  1055              * @event testcasecomplete
       
  1056              * @static
       
  1057              */        
       
  1058             TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
       
  1059             
       
  1060             /**
       
  1061              * Fires when a test suite is opened but before the first 
       
  1062              * test is executed.
       
  1063              * @event testsuitebegin
       
  1064              * @static
       
  1065              */        
       
  1066             TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
       
  1067             
       
  1068             /**
       
  1069              * Fires when all test cases in a test suite have been
       
  1070              * completed.
       
  1071              * @event testsuitecomplete
       
  1072              * @static
       
  1073              */        
       
  1074             TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
       
  1075             
       
  1076             /**
       
  1077              * Fires when a test has passed.
       
  1078              * @event pass
       
  1079              * @static
       
  1080              */        
       
  1081             TEST_PASS_EVENT : "pass",
       
  1082             
       
  1083             /**
       
  1084              * Fires when a test has failed.
       
  1085              * @event fail
       
  1086              * @static
       
  1087              */        
       
  1088             TEST_FAIL_EVENT : "fail",
       
  1089             
       
  1090             /**
       
  1091              * Fires when a non-test method has an error.
       
  1092              * @event error
       
  1093              * @static
       
  1094              */        
       
  1095             ERROR_EVENT : "error",
       
  1096             
       
  1097             /**
       
  1098              * Fires when a test has been ignored.
       
  1099              * @event ignore
       
  1100              * @static
       
  1101              */        
       
  1102             TEST_IGNORE_EVENT : "ignore",
       
  1103             
       
  1104             /**
       
  1105              * Fires when all test suites and test cases have been completed.
       
  1106              * @event complete
       
  1107              * @static
       
  1108              */        
       
  1109             COMPLETE_EVENT : "complete",
       
  1110             
       
  1111             /**
       
  1112              * Fires when the run() method is called.
       
  1113              * @event begin
       
  1114              * @static
       
  1115              */        
       
  1116             BEGIN_EVENT : "begin",                           
       
  1117 
       
  1118             //-------------------------------------------------------------------------
       
  1119             // Test Tree-Related Methods
       
  1120             //-------------------------------------------------------------------------
       
  1121     
       
  1122             /**
       
  1123              * Adds a test case to the test tree as a child of the specified node.
       
  1124              * @param {TestNode} parentNode The node to add the test case to as a child.
       
  1125              * @param {Test.TestCase} testCase The test case to add.
       
  1126              * @return {Void}
       
  1127              * @static
       
  1128              * @private
       
  1129              * @method _addTestCaseToTestTree
       
  1130              */
       
  1131            _addTestCaseToTestTree : function (parentNode, testCase){
       
  1132                 
       
  1133                 //add the test suite
       
  1134                 var node = parentNode.appendChild(testCase),
       
  1135                     prop,
       
  1136                     testName;
       
  1137                 
       
  1138                 //iterate over the items in the test case
       
  1139                 for (prop in testCase){
       
  1140                     if ((prop.indexOf("test") === 0 || prop.indexOf(" ") > -1) && typeof testCase[prop] == "function"){
       
  1141                         node.appendChild(prop);
       
  1142                     }
       
  1143                 }
       
  1144              
       
  1145             },
       
  1146             
       
  1147             /**
       
  1148              * Adds a test suite to the test tree as a child of the specified node.
       
  1149              * @param {TestNode} parentNode The node to add the test suite to as a child.
       
  1150              * @param {Test.TestSuite} testSuite The test suite to add.
       
  1151              * @return {Void}
       
  1152              * @static
       
  1153              * @private
       
  1154              * @method _addTestSuiteToTestTree
       
  1155              */
       
  1156             _addTestSuiteToTestTree : function (parentNode, testSuite) {
       
  1157                 
       
  1158                 //add the test suite
       
  1159                 var node = parentNode.appendChild(testSuite);
       
  1160                 
       
  1161                 //iterate over the items in the master suite
       
  1162                 for (var i=0; i < testSuite.items.length; i++){
       
  1163                     if (testSuite.items[i] instanceof YUITest.TestSuite) {
       
  1164                         this._addTestSuiteToTestTree(node, testSuite.items[i]);
       
  1165                     } else if (testSuite.items[i] instanceof YUITest.TestCase) {
       
  1166                         this._addTestCaseToTestTree(node, testSuite.items[i]);
       
  1167                     }                   
       
  1168                 }            
       
  1169             },
       
  1170             
       
  1171             /**
       
  1172              * Builds the test tree based on items in the master suite. The tree is a hierarchical
       
  1173              * representation of the test suites, test cases, and test functions. The resulting tree
       
  1174              * is stored in _root and the pointer _cur is set to the root initially.
       
  1175              * @return {Void}
       
  1176              * @static
       
  1177              * @private
       
  1178              * @method _buildTestTree
       
  1179              */
       
  1180             _buildTestTree : function () {
       
  1181             
       
  1182                 this._root = new TestNode(this.masterSuite);
       
  1183                 //this._cur = this._root;
       
  1184                 
       
  1185                 //iterate over the items in the master suite
       
  1186                 for (var i=0; i < this.masterSuite.items.length; i++){
       
  1187                     if (this.masterSuite.items[i] instanceof YUITest.TestSuite) {
       
  1188                         this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
       
  1189                     } else if (this.masterSuite.items[i] instanceof YUITest.TestCase) {
       
  1190                         this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
       
  1191                     }                   
       
  1192                 }            
       
  1193             
       
  1194             }, 
       
  1195         
       
  1196             //-------------------------------------------------------------------------
       
  1197             // Private Methods
       
  1198             //-------------------------------------------------------------------------
       
  1199             
       
  1200             /**
       
  1201              * Handles the completion of a test object's tests. Tallies test results 
       
  1202              * from one level up to the next.
       
  1203              * @param {TestNode} node The TestNode representing the test object.
       
  1204              * @return {Void}
       
  1205              * @method _handleTestObjectComplete
       
  1206              * @private
       
  1207              */
       
  1208             _handleTestObjectComplete : function (node) {
       
  1209                 var parentNode;
       
  1210                 
       
  1211                 if (node && (typeof node.testObject == "object")) {
       
  1212                     parentNode = node.parent;
       
  1213                 
       
  1214                     if (parentNode){
       
  1215                         parentNode.results.include(node.results); 
       
  1216                         parentNode.results[node.testObject.name] = node.results;
       
  1217                     }
       
  1218                 
       
  1219                     if (node.testObject instanceof YUITest.TestSuite){
       
  1220                         this._execNonTestMethod(node, "tearDown", false);
       
  1221                         node.results.duration = (new Date()) - node._start;
       
  1222                         this.fire({ type: this.TEST_SUITE_COMPLETE_EVENT, testSuite: node.testObject, results: node.results});
       
  1223                     } else if (node.testObject instanceof YUITest.TestCase){
       
  1224                         this._execNonTestMethod(node, "destroy", false);
       
  1225                         node.results.duration = (new Date()) - node._start;
       
  1226                         this.fire({ type: this.TEST_CASE_COMPLETE_EVENT, testCase: node.testObject, results: node.results});
       
  1227                     }      
       
  1228                 } 
       
  1229             },                
       
  1230             
       
  1231             //-------------------------------------------------------------------------
       
  1232             // Navigation Methods
       
  1233             //-------------------------------------------------------------------------
       
  1234             
       
  1235             /**
       
  1236              * Retrieves the next node in the test tree.
       
  1237              * @return {TestNode} The next node in the test tree or null if the end is reached.
       
  1238              * @private
       
  1239              * @static
       
  1240              * @method _next
       
  1241              */
       
  1242             _next : function () {
       
  1243             
       
  1244                 if (this._cur === null){
       
  1245                     this._cur = this._root;
       
  1246                 } else if (this._cur.firstChild) {
       
  1247                     this._cur = this._cur.firstChild;
       
  1248                 } else if (this._cur.next) {
       
  1249                     this._cur = this._cur.next;            
       
  1250                 } else {
       
  1251                     while (this._cur && !this._cur.next && this._cur !== this._root){
       
  1252                         this._handleTestObjectComplete(this._cur);
       
  1253                         this._cur = this._cur.parent;
       
  1254                     }
       
  1255                     
       
  1256                     this._handleTestObjectComplete(this._cur);               
       
  1257                         
       
  1258                     if (this._cur == this._root){
       
  1259                         this._cur.results.type = "report";
       
  1260                         this._cur.results.timestamp = (new Date()).toLocaleString();
       
  1261                         this._cur.results.duration = (new Date()) - this._cur._start;   
       
  1262                         this._lastResults = this._cur.results;
       
  1263                         this._running = false;                         
       
  1264                         this.fire({ type: this.COMPLETE_EVENT, results: this._lastResults});
       
  1265                         this._cur = null;
       
  1266                     } else if (this._cur) {
       
  1267                         this._cur = this._cur.next;                
       
  1268                     }
       
  1269                 }
       
  1270             
       
  1271                 return this._cur;
       
  1272             },
       
  1273             
       
  1274             /**
       
  1275              * Executes a non-test method (init, setUp, tearDown, destroy)
       
  1276              * and traps an errors. If an error occurs, an error event is
       
  1277              * fired.
       
  1278              * @param {Object} node The test node in the testing tree.
       
  1279              * @param {String} methodName The name of the method to execute.
       
  1280              * @param {Boolean} allowAsync Determines if the method can be called asynchronously.
       
  1281              * @return {Boolean} True if an async method was called, false if not.
       
  1282              * @method _execNonTestMethod
       
  1283              * @private
       
  1284              */
       
  1285             _execNonTestMethod: function(node, methodName, allowAsync){
       
  1286                 var testObject = node.testObject,
       
  1287                     event = { type: this.ERROR_EVENT };
       
  1288                 try {
       
  1289                     if (allowAsync && testObject["async:" + methodName]){
       
  1290                         testObject["async:" + methodName](this._context);
       
  1291                         return true;
       
  1292                     } else {
       
  1293                         testObject[methodName](this._context);
       
  1294                     }
       
  1295                 } catch (ex){
       
  1296                     node.results.errors++;
       
  1297                     event.error = ex;
       
  1298                     event.methodName = methodName;
       
  1299                     if (testObject instanceof YUITest.TestCase){
       
  1300                         event.testCase = testObject;
       
  1301                     } else {
       
  1302                         event.testSuite = testSuite;
       
  1303                     }
       
  1304                     
       
  1305                     this.fire(event);
       
  1306                 }  
       
  1307 
       
  1308                 return false;
       
  1309             },
       
  1310             
       
  1311             /**
       
  1312              * Runs a test case or test suite, returning the results.
       
  1313              * @param {Test.TestCase|YUITest.TestSuite} testObject The test case or test suite to run.
       
  1314              * @return {Object} Results of the execution with properties passed, failed, and total.
       
  1315              * @private
       
  1316              * @method _run
       
  1317              * @static
       
  1318              */
       
  1319             _run : function () {
       
  1320             
       
  1321                 //flag to indicate if the TestRunner should wait before continuing
       
  1322                 var shouldWait = false;
       
  1323                 
       
  1324                 //get the next test node
       
  1325                 var node = this._next();
       
  1326                 
       
  1327                 if (node !== null) {
       
  1328                 
       
  1329                     //set flag to say the testrunner is running
       
  1330                     this._running = true;
       
  1331                     
       
  1332                     //eliminate last results
       
  1333                     this._lastResult = null;                  
       
  1334                 
       
  1335                     var testObject = node.testObject;
       
  1336                     
       
  1337                     //figure out what to do
       
  1338                     if (typeof testObject == "object" && testObject !== null){
       
  1339                         if (testObject instanceof YUITest.TestSuite){
       
  1340                             this.fire({ type: this.TEST_SUITE_BEGIN_EVENT, testSuite: testObject });
       
  1341                             node._start = new Date();
       
  1342                             this._execNonTestMethod(node, "setUp" ,false);
       
  1343                         } else if (testObject instanceof YUITest.TestCase){
       
  1344                             this.fire({ type: this.TEST_CASE_BEGIN_EVENT, testCase: testObject });
       
  1345                             node._start = new Date();
       
  1346                             
       
  1347                             //regular or async init
       
  1348                             /*try {
       
  1349                                 if (testObject["async:init"]){
       
  1350                                     testObject["async:init"](this._context);
       
  1351                                     return;
       
  1352                                 } else {
       
  1353                                     testObject.init(this._context);
       
  1354                                 }
       
  1355                             } catch (ex){
       
  1356                                 node.results.errors++;
       
  1357                                 this.fire({ type: this.ERROR_EVENT, error: ex, testCase: testObject, methodName: "init" });
       
  1358                             }*/
       
  1359                             if(this._execNonTestMethod(node, "init", true)){
       
  1360                                 return;
       
  1361                             }
       
  1362                         }
       
  1363                         
       
  1364                         //some environments don't support setTimeout
       
  1365                         if (typeof setTimeout != "undefined"){                    
       
  1366                             setTimeout(function(){
       
  1367                                 YUITest.TestRunner._run();
       
  1368                             }, 0);
       
  1369                         } else {
       
  1370                             this._run();
       
  1371                         }
       
  1372                     } else {
       
  1373                         this._runTest(node);
       
  1374                     }
       
  1375     
       
  1376                 }
       
  1377             },
       
  1378             
       
  1379             _resumeTest : function (segment) {
       
  1380             
       
  1381                 //get relevant information
       
  1382                 var node = this._cur;                
       
  1383                 
       
  1384                 //we know there's no more waiting now
       
  1385                 this._waiting = false;
       
  1386                 
       
  1387                 //if there's no node, it probably means a wait() was called after resume()
       
  1388                 if (!node){
       
  1389                     //TODO: Handle in some way?
       
  1390                     //console.log("wait() called after resume()");
       
  1391                     //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
       
  1392                     return;
       
  1393                 }
       
  1394                 
       
  1395                 var testName = node.testObject;
       
  1396                 var testCase = node.parent.testObject;
       
  1397             
       
  1398                 //cancel other waits if available
       
  1399                 if (testCase.__yui_wait){
       
  1400                     clearTimeout(testCase.__yui_wait);
       
  1401                     delete testCase.__yui_wait;
       
  1402                 }
       
  1403 
       
  1404                 //get the "should" test cases
       
  1405                 var shouldFail = testName.indexOf("fail:") === 0 ||
       
  1406                                     (testCase._should.fail || {})[testName];
       
  1407                 var shouldError = (testCase._should.error || {})[testName];
       
  1408                 
       
  1409                 //variable to hold whether or not the test failed
       
  1410                 var failed = false;
       
  1411                 var error = null;
       
  1412                     
       
  1413                 //try the test
       
  1414                 try {
       
  1415                 
       
  1416                     //run the test
       
  1417                     segment.call(testCase, this._context);                    
       
  1418                 
       
  1419                     //if the test hasn't already failed and doesn't have any asserts...
       
  1420                     if(YUITest.Assert._getCount() == 0 && !this._ignoreEmpty){
       
  1421                         throw new YUITest.AssertionError("Test has no asserts.");
       
  1422                     }                                                        
       
  1423                     //if it should fail, and it got here, then it's a fail because it didn't
       
  1424                      else if (shouldFail){
       
  1425                         error = new YUITest.ShouldFail();
       
  1426                         failed = true;
       
  1427                     } else if (shouldError){
       
  1428                         error = new YUITest.ShouldError();
       
  1429                         failed = true;
       
  1430                     }
       
  1431                                
       
  1432                 } catch (thrown){
       
  1433 
       
  1434                     //cancel any pending waits, the test already failed
       
  1435                     if (testCase.__yui_wait){
       
  1436                         clearTimeout(testCase.__yui_wait);
       
  1437                         delete testCase.__yui_wait;
       
  1438                     }                    
       
  1439                 
       
  1440                     //figure out what type of error it was
       
  1441                     if (thrown instanceof YUITest.AssertionError) {
       
  1442                         if (!shouldFail){
       
  1443                             error = thrown;
       
  1444                             failed = true;
       
  1445                         }
       
  1446                     } else if (thrown instanceof YUITest.Wait){
       
  1447                     
       
  1448                         if (typeof thrown.segment == "function"){
       
  1449                             if (typeof thrown.delay == "number"){
       
  1450                             
       
  1451                                 //some environments don't support setTimeout
       
  1452                                 if (typeof setTimeout != "undefined"){
       
  1453                                     testCase.__yui_wait = setTimeout(function(){
       
  1454                                         YUITest.TestRunner._resumeTest(thrown.segment);
       
  1455                                     }, thrown.delay);
       
  1456                                     this._waiting = true;
       
  1457                                 } else {
       
  1458                                     throw new Error("Asynchronous tests not supported in this environment.");
       
  1459                                 }
       
  1460                             }
       
  1461                         }
       
  1462                         
       
  1463                         return;
       
  1464                     
       
  1465                     } else {
       
  1466                         //first check to see if it should error
       
  1467                         if (!shouldError) {                        
       
  1468                             error = new YUITest.UnexpectedError(thrown);
       
  1469                             failed = true;
       
  1470                         } else {
       
  1471                             //check to see what type of data we have
       
  1472                             if (typeof shouldError == "string"){
       
  1473                                 
       
  1474                                 //if it's a string, check the error message
       
  1475                                 if (thrown.message != shouldError){
       
  1476                                     error = new YUITest.UnexpectedError(thrown);
       
  1477                                     failed = true;                                    
       
  1478                                 }
       
  1479                             } else if (typeof shouldError == "function"){
       
  1480                             
       
  1481                                 //if it's a function, see if the error is an instance of it
       
  1482                                 if (!(thrown instanceof shouldError)){
       
  1483                                     error = new YUITest.UnexpectedError(thrown);
       
  1484                                     failed = true;
       
  1485                                 }
       
  1486                             
       
  1487                             } else if (typeof shouldError == "object" && shouldError !== null){
       
  1488                             
       
  1489                                 //if it's an object, check the instance and message
       
  1490                                 if (!(thrown instanceof shouldError.constructor) || 
       
  1491                                         thrown.message != shouldError.message){
       
  1492                                     error = new YUITest.UnexpectedError(thrown);
       
  1493                                     failed = true;                                    
       
  1494                                 }
       
  1495                             
       
  1496                             }
       
  1497                         
       
  1498                         }
       
  1499                     }
       
  1500                     
       
  1501                 }
       
  1502                 
       
  1503                 //fire appropriate event
       
  1504                 if (failed) {
       
  1505                     this.fire({ type: this.TEST_FAIL_EVENT, testCase: testCase, testName: testName, error: error });
       
  1506                 } else {
       
  1507                     this.fire({ type: this.TEST_PASS_EVENT, testCase: testCase, testName: testName });
       
  1508                 }
       
  1509                 
       
  1510                 //run the tear down
       
  1511                 this._execNonTestMethod(node.parent, "tearDown", false);
       
  1512                 
       
  1513                 //reset the assert count
       
  1514                 YUITest.Assert._reset();
       
  1515                 
       
  1516                 //calculate duration
       
  1517                 var duration = (new Date()) - node._start;
       
  1518                 
       
  1519                 //update results
       
  1520                 node.parent.results[testName] = { 
       
  1521                     result: failed ? "fail" : "pass",
       
  1522                     message: error ? error.getMessage() : "Test passed",
       
  1523                     type: "test",
       
  1524                     name: testName,
       
  1525                     duration: duration
       
  1526                 };
       
  1527                 
       
  1528                 if (failed){
       
  1529                     node.parent.results.failed++;
       
  1530                 } else {
       
  1531                     node.parent.results.passed++;
       
  1532                 }
       
  1533                 node.parent.results.total++;
       
  1534     
       
  1535                 //set timeout not supported in all environments
       
  1536                 if (typeof setTimeout != "undefined"){
       
  1537                     setTimeout(function(){
       
  1538                         YUITest.TestRunner._run();
       
  1539                     }, 0);
       
  1540                 } else {
       
  1541                     this._run();
       
  1542                 }
       
  1543             
       
  1544             },
       
  1545             
       
  1546             /**
       
  1547              * Handles an error as if it occurred within the currently executing
       
  1548              * test. This is for mock methods that may be called asynchronously
       
  1549              * and therefore out of the scope of the TestRunner. Previously, this
       
  1550              * error would bubble up to the browser. Now, this method is used
       
  1551              * to tell TestRunner about the error. This should never be called
       
  1552              * by anyplace other than the Mock object.
       
  1553              * @param {Error} error The error object.
       
  1554              * @return {Void}
       
  1555              * @method _handleError
       
  1556              * @private
       
  1557              * @static
       
  1558              */
       
  1559             _handleError: function(error){
       
  1560             
       
  1561                 if (this._waiting){
       
  1562                     this._resumeTest(function(){
       
  1563                         throw error;
       
  1564                     });
       
  1565                 } else {
       
  1566                     throw error;
       
  1567                 }           
       
  1568             
       
  1569             },
       
  1570                     
       
  1571             /**
       
  1572              * Runs a single test based on the data provided in the node.
       
  1573              * @method _runTest
       
  1574              * @param {TestNode} node The TestNode representing the test to run.
       
  1575              * @return {Void}
       
  1576              * @static
       
  1577              * @private
       
  1578              */
       
  1579             _runTest : function (node) {
       
  1580             
       
  1581                 //get relevant information
       
  1582                 var testName = node.testObject,
       
  1583                     testCase = node.parent.testObject,
       
  1584                     test = testCase[testName],
       
  1585                 
       
  1586                     //get the "should" test cases
       
  1587                     shouldIgnore = testName.indexOf("ignore:") === 0 ||
       
  1588                                     !inGroups(testCase.groups, this._groups) ||
       
  1589                                     (testCase._should.ignore || {})[testName];   //deprecated
       
  1590                 
       
  1591                 //figure out if the test should be ignored or not
       
  1592                 if (shouldIgnore){
       
  1593                 
       
  1594                     //update results
       
  1595                     node.parent.results[testName] = { 
       
  1596                         result: "ignore",
       
  1597                         message: "Test ignored",
       
  1598                         type: "test",
       
  1599                         name: testName.indexOf("ignore:") === 0 ? testName.substring(7) : testName
       
  1600                     };
       
  1601                     
       
  1602                     node.parent.results.ignored++;
       
  1603                     node.parent.results.total++;
       
  1604                 
       
  1605                     this.fire({ type: this.TEST_IGNORE_EVENT,  testCase: testCase, testName: testName });
       
  1606                     
       
  1607                     //some environments don't support setTimeout
       
  1608                     if (typeof setTimeout != "undefined"){                    
       
  1609                         setTimeout(function(){
       
  1610                             YUITest.TestRunner._run();
       
  1611                         }, 0);              
       
  1612                     } else {
       
  1613                         this._run();
       
  1614                     }
       
  1615     
       
  1616                 } else {
       
  1617                 
       
  1618                     //mark the start time
       
  1619                     node._start = new Date();
       
  1620                 
       
  1621                     //run the setup
       
  1622                     this._execNonTestMethod(node.parent, "setUp", false);
       
  1623                     
       
  1624                     //now call the body of the test
       
  1625                     this._resumeTest(test);                
       
  1626                 }
       
  1627     
       
  1628             },            
       
  1629 
       
  1630             //-------------------------------------------------------------------------
       
  1631             // Misc Methods
       
  1632             //-------------------------------------------------------------------------   
       
  1633 
       
  1634             /**
       
  1635              * Retrieves the name of the current result set.
       
  1636              * @return {String} The name of the result set.
       
  1637              * @method getName
       
  1638              */
       
  1639             getName: function(){
       
  1640                 return this.masterSuite.name;
       
  1641             },         
       
  1642 
       
  1643             /**
       
  1644              * The name assigned to the master suite of the TestRunner. This is the name
       
  1645              * that is output as the root's name when results are retrieved.
       
  1646              * @param {String} name The name of the result set.
       
  1647              * @return {Void}
       
  1648              * @method setName
       
  1649              */
       
  1650             setName: function(name){
       
  1651                 this.masterSuite.name = name;
       
  1652             },            
       
  1653             
       
  1654             //-------------------------------------------------------------------------
       
  1655             // Public Methods
       
  1656             //-------------------------------------------------------------------------   
       
  1657         
       
  1658             /**
       
  1659              * Adds a test suite or test case to the list of test objects to run.
       
  1660              * @param testObject Either a TestCase or a TestSuite that should be run.
       
  1661              * @return {Void}
       
  1662              * @method add
       
  1663              * @static
       
  1664              */
       
  1665             add : function (testObject) {
       
  1666                 this.masterSuite.add(testObject);
       
  1667                 return this;
       
  1668             },
       
  1669             
       
  1670             /**
       
  1671              * Removes all test objects from the runner.
       
  1672              * @return {Void}
       
  1673              * @method clear
       
  1674              * @static
       
  1675              */
       
  1676             clear : function () {
       
  1677                 this.masterSuite = new YUITest.TestSuite(YUITest.guid('testSuite_'));
       
  1678             },
       
  1679             
       
  1680             /**
       
  1681              * Indicates if the TestRunner is waiting for a test to resume
       
  1682              * @return {Boolean} True if the TestRunner is waiting, false if not.
       
  1683              * @method isWaiting
       
  1684              * @static
       
  1685              */
       
  1686             isWaiting: function() {
       
  1687                 return this._waiting;
       
  1688             },
       
  1689             
       
  1690             /**
       
  1691              * Indicates that the TestRunner is busy running tests and therefore can't
       
  1692              * be stopped and results cannot be gathered.
       
  1693              * @return {Boolean} True if the TestRunner is running, false if not.
       
  1694              * @method isRunning
       
  1695              */
       
  1696             isRunning: function(){
       
  1697                 return this._running;
       
  1698             },
       
  1699             
       
  1700             /**
       
  1701              * Returns the last complete results set from the TestRunner. Null is returned
       
  1702              * if the TestRunner is running or no tests have been run.
       
  1703              * @param {Function} format (Optional) A test format to return the results in.
       
  1704              * @return {Object|String} Either the results object or, if a test format is 
       
  1705              *      passed as the argument, a string representing the results in a specific
       
  1706              *      format.
       
  1707              * @method getResults
       
  1708              */
       
  1709             getResults: function(format){
       
  1710                 if (!this._running && this._lastResults){
       
  1711                     if (typeof format == "function"){
       
  1712                         return format(this._lastResults);                    
       
  1713                     } else {
       
  1714                         return this._lastResults;
       
  1715                     }
       
  1716                 } else {
       
  1717                     return null;
       
  1718                 }
       
  1719             },            
       
  1720             
       
  1721             /**
       
  1722              * Returns the coverage report for the files that have been executed.
       
  1723              * This returns only coverage information for files that have been
       
  1724              * instrumented using YUI Test Coverage and only those that were run
       
  1725              * in the same pass.
       
  1726              * @param {Function} format (Optional) A coverage format to return results in.
       
  1727              * @return {Object|String} Either the coverage object or, if a coverage
       
  1728              *      format is specified, a string representing the results in that format.
       
  1729              * @method getCoverage
       
  1730              */
       
  1731             getCoverage: function(format) {
       
  1732                 var covObject = null;
       
  1733                 if (typeof _yuitest_coverage === "object") {
       
  1734                     covObject = _yuitest_coverage;
       
  1735                 }
       
  1736                 if (typeof __coverage__ === "object") {
       
  1737                     covObject = __coverage__;
       
  1738                 }
       
  1739                 if (!this._running && typeof covObject == "object"){
       
  1740                     if (typeof format == "function") {
       
  1741                         return format(covObject);                    
       
  1742                     } else {
       
  1743                         return covObject;
       
  1744                     }
       
  1745                 } else {
       
  1746                     return null;
       
  1747                 }            
       
  1748             },
       
  1749             
       
  1750             /**
       
  1751              * Used to continue processing when a method marked with
       
  1752              * "async:" is executed. This should not be used in test
       
  1753              * methods, only in init(). Each argument is a string, and
       
  1754              * when the returned function is executed, the arguments
       
  1755              * are assigned to the context data object using the string
       
  1756              * as the key name (value is the argument itself).
       
  1757              * @private
       
  1758              * @return {Function} A callback function.
       
  1759              * @method callback
       
  1760              */
       
  1761             callback: function(){
       
  1762                 var names   = arguments,
       
  1763                     data    = this._context,
       
  1764                     that    = this;
       
  1765                     
       
  1766                 return function(){
       
  1767                     for (var i=0; i < arguments.length; i++){
       
  1768                         data[names[i]] = arguments[i];
       
  1769                     }
       
  1770                     that._run();
       
  1771                 };
       
  1772             },
       
  1773             
       
  1774             /**
       
  1775              * Resumes the TestRunner after wait() was called.
       
  1776              * @param {Function} segment The function to run as the rest
       
  1777              *      of the haulted test.
       
  1778              * @return {Void}
       
  1779              * @method resume
       
  1780              * @static
       
  1781              */
       
  1782             resume : function (segment) {
       
  1783                 if (this._waiting){
       
  1784                     this._resumeTest(segment || function(){});
       
  1785                 } else {
       
  1786                     throw new Error("resume() called without wait().");
       
  1787                 }
       
  1788             },
       
  1789         
       
  1790             /**
       
  1791              * Runs the test suite.
       
  1792              * @param {Object|Boolean} options (Optional) Options for the runner:
       
  1793              *      <code>oldMode</code> indicates the TestRunner should work in the YUI <= 2.8 way
       
  1794              *      of internally managing test suites. <code>groups</code> is an array
       
  1795              *      of test groups indicating which tests to run.
       
  1796              * @return {Void}
       
  1797              * @method run
       
  1798              * @static
       
  1799              */
       
  1800             run : function (options) {
       
  1801 
       
  1802                 options = options || {};
       
  1803                 
       
  1804                 //pointer to runner to avoid scope issues 
       
  1805                 var runner  = YUITest.TestRunner,
       
  1806                     oldMode = options.oldMode;
       
  1807                 
       
  1808                 
       
  1809                 //if there's only one suite on the masterSuite, move it up
       
  1810                 if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof YUITest.TestSuite){
       
  1811                     this.masterSuite = this.masterSuite.items[0];
       
  1812                 }                
       
  1813                 
       
  1814                 //determine if there are any groups to filter on
       
  1815                 runner._groups = (options.groups instanceof Array) ? "," + options.groups.join(",") + "," : "";
       
  1816                 
       
  1817                 //initialize the runner
       
  1818                 runner._buildTestTree();
       
  1819                 runner._context = {};
       
  1820                 runner._root._start = new Date();
       
  1821                 
       
  1822                 //fire the begin event
       
  1823                 runner.fire(runner.BEGIN_EVENT);
       
  1824            
       
  1825                 //begin the testing
       
  1826                 runner._run();
       
  1827             }    
       
  1828         });
       
  1829         
       
  1830         return new TestRunner();
       
  1831         
       
  1832     }();
       
  1833 
       
  1834 /**
       
  1835  * The ArrayAssert object provides functions to test JavaScript array objects
       
  1836  * for a variety of cases.
       
  1837  * @namespace Test
       
  1838  * @module test
       
  1839  * @class ArrayAssert
       
  1840  * @static
       
  1841  */
       
  1842  
       
  1843 YUITest.ArrayAssert = {
       
  1844 
       
  1845     //=========================================================================
       
  1846     // Private methods
       
  1847     //=========================================================================
       
  1848     
       
  1849     /**
       
  1850      * Simple indexOf() implementation for an array. Defers to native
       
  1851      * if available.
       
  1852      * @param {Array} haystack The array to search.
       
  1853      * @param {Variant} needle The value to locate.
       
  1854      * @return {int} The index of the needle if found or -1 if not.
       
  1855      * @method _indexOf
       
  1856      * @private
       
  1857      */
       
  1858     _indexOf: function(haystack, needle){
       
  1859         if (haystack.indexOf){
       
  1860             return haystack.indexOf(needle);
       
  1861         } else {
       
  1862             for (var i=0; i < haystack.length; i++){
       
  1863                 if (haystack[i] === needle){
       
  1864                     return i;
       
  1865                 }
       
  1866             }
       
  1867             return -1;
       
  1868         }
       
  1869     },
       
  1870     
       
  1871     /**
       
  1872      * Simple some() implementation for an array. Defers to native
       
  1873      * if available.
       
  1874      * @param {Array} haystack The array to search.
       
  1875      * @param {Function} matcher The function to run on each value.
       
  1876      * @return {Boolean} True if any value, when run through the matcher,
       
  1877      *      returns true.
       
  1878      * @method _some
       
  1879      * @private
       
  1880      */
       
  1881     _some: function(haystack, matcher){
       
  1882         if (haystack.some){
       
  1883             return haystack.some(matcher);
       
  1884         } else {
       
  1885             for (var i=0; i < haystack.length; i++){
       
  1886                 if (matcher(haystack[i])){
       
  1887                     return true;
       
  1888                 }
       
  1889             }
       
  1890             return false;
       
  1891         }
       
  1892     },    
       
  1893 
       
  1894     /**
       
  1895      * Asserts that a value is present in an array. This uses the triple equals 
       
  1896      * sign so no type coercion may occur.
       
  1897      * @param {Object} needle The value that is expected in the array.
       
  1898      * @param {Array} haystack An array of values.
       
  1899      * @param {String} message (Optional) The message to display if the assertion fails.
       
  1900      * @method contains
       
  1901      * @static
       
  1902      */
       
  1903     contains : function (needle, haystack, 
       
  1904                            message) {
       
  1905         
       
  1906         YUITest.Assert._increment();               
       
  1907 
       
  1908         if (this._indexOf(haystack, needle) == -1){
       
  1909             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
       
  1910         }
       
  1911     },
       
  1912 
       
  1913     /**
       
  1914      * Asserts that a set of values are present in an array. This uses the triple equals 
       
  1915      * sign so no type coercion may occur. For this assertion to pass, all values must
       
  1916      * be found.
       
  1917      * @param {Object[]} needles An array of values that are expected in the array.
       
  1918      * @param {Array} haystack An array of values to check.
       
  1919      * @param {String} message (Optional) The message to display if the assertion fails.
       
  1920      * @method containsItems
       
  1921      * @static
       
  1922      */
       
  1923     containsItems : function (needles, haystack, 
       
  1924                            message) {
       
  1925         YUITest.Assert._increment();               
       
  1926 
       
  1927         //begin checking values
       
  1928         for (var i=0; i < needles.length; i++){
       
  1929             if (this._indexOf(haystack, needles[i]) == -1){
       
  1930                 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needles[i] + " (" + (typeof needles[i]) + ") not found in array [" + haystack + "]."));
       
  1931             }
       
  1932         }
       
  1933     },
       
  1934 
       
  1935     /**
       
  1936      * Asserts that a value matching some condition is present in an array. This uses
       
  1937      * a function to determine a match.
       
  1938      * @param {Function} matcher A function that returns true if the items matches or false if not.
       
  1939      * @param {Array} haystack An array of values.
       
  1940      * @param {String} message (Optional) The message to display if the assertion fails.
       
  1941      * @method containsMatch
       
  1942      * @static
       
  1943      */
       
  1944     containsMatch : function (matcher, haystack, 
       
  1945                            message) {
       
  1946         
       
  1947         YUITest.Assert._increment();               
       
  1948         //check for valid matcher
       
  1949         if (typeof matcher != "function"){
       
  1950             throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
       
  1951         }
       
  1952         
       
  1953         if (!this._some(haystack, matcher)){
       
  1954             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
       
  1955         }
       
  1956     },
       
  1957 
       
  1958     /**
       
  1959      * Asserts that a value is not present in an array. This uses the triple equals 
       
  1960      * Asserts that a value is not present in an array. This uses the triple equals 
       
  1961      * sign so no type coercion may occur.
       
  1962      * @param {Object} needle The value that is expected in the array.
       
  1963      * @param {Array} haystack An array of values.
       
  1964      * @param {String} message (Optional) The message to display if the assertion fails.
       
  1965      * @method doesNotContain
       
  1966      * @static
       
  1967      */
       
  1968     doesNotContain : function (needle, haystack, 
       
  1969                            message) {
       
  1970         
       
  1971         YUITest.Assert._increment();               
       
  1972 
       
  1973         if (this._indexOf(haystack, needle) > -1){
       
  1974             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
       
  1975         }
       
  1976     },
       
  1977 
       
  1978     /**
       
  1979      * Asserts that a set of values are not present in an array. This uses the triple equals 
       
  1980      * sign so no type coercion may occur. For this assertion to pass, all values must
       
  1981      * not be found.
       
  1982      * @param {Object[]} needles An array of values that are not expected in the array.
       
  1983      * @param {Array} haystack An array of values to check.
       
  1984      * @param {String} message (Optional) The message to display if the assertion fails.
       
  1985      * @method doesNotContainItems
       
  1986      * @static
       
  1987      */
       
  1988     doesNotContainItems : function (needles, haystack, 
       
  1989                            message) {
       
  1990 
       
  1991         YUITest.Assert._increment();               
       
  1992 
       
  1993         for (var i=0; i < needles.length; i++){
       
  1994             if (this._indexOf(haystack, needles[i]) > -1){
       
  1995                 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
       
  1996             }
       
  1997         }
       
  1998 
       
  1999     },
       
  2000         
       
  2001     /**
       
  2002      * Asserts that no values matching a condition are present in an array. This uses
       
  2003      * a function to determine a match.
       
  2004      * @param {Function} matcher A function that returns true if the item matches or false if not.
       
  2005      * @param {Array} haystack An array of values.
       
  2006      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2007      * @method doesNotContainMatch
       
  2008      * @static
       
  2009      */
       
  2010     doesNotContainMatch : function (matcher, haystack, 
       
  2011                            message) {
       
  2012         
       
  2013         YUITest.Assert._increment();     
       
  2014       
       
  2015         //check for valid matcher
       
  2016         if (typeof matcher != "function"){
       
  2017             throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
       
  2018         }
       
  2019         
       
  2020         if (this._some(haystack, matcher)){
       
  2021             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
       
  2022         }
       
  2023     },
       
  2024         
       
  2025     /**
       
  2026      * Asserts that the given value is contained in an array at the specified index.
       
  2027      * This uses the triple equals sign so no type coercion will occur.
       
  2028      * @param {Object} needle The value to look for.
       
  2029      * @param {Array} haystack The array to search in.
       
  2030      * @param {int} index The index at which the value should exist.
       
  2031      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2032      * @method indexOf
       
  2033      * @static
       
  2034      */
       
  2035     indexOf : function (needle, haystack, index, message) {
       
  2036     
       
  2037         YUITest.Assert._increment();     
       
  2038 
       
  2039         //try to find the value in the array
       
  2040         for (var i=0; i < haystack.length; i++){
       
  2041             if (haystack[i] === needle){
       
  2042                 if (index != i){
       
  2043                     YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));                    
       
  2044                 }
       
  2045                 return;
       
  2046             }
       
  2047         }
       
  2048         
       
  2049         //if it makes it here, it wasn't found at all
       
  2050         YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
       
  2051     },
       
  2052         
       
  2053     /**
       
  2054      * Asserts that the values in an array are equal, and in the same position,
       
  2055      * as values in another array. This uses the double equals sign
       
  2056      * so type coercion may occur. Note that the array objects themselves
       
  2057      * need not be the same for this test to pass.
       
  2058      * @param {Array} expected An array of the expected values.
       
  2059      * @param {Array} actual Any array of the actual values.
       
  2060      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2061      * @method itemsAreEqual
       
  2062      * @static
       
  2063      */
       
  2064     itemsAreEqual : function (expected, actual, 
       
  2065                            message) {
       
  2066         
       
  2067         YUITest.Assert._increment();     
       
  2068         
       
  2069         //first make sure they're array-like (this can probably be improved)
       
  2070         if (typeof expected != "object" || typeof actual != "object"){
       
  2071             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value should be an array."));
       
  2072         }
       
  2073         
       
  2074         //next check array length
       
  2075         if (expected.length != actual.length){
       
  2076             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length + "."));
       
  2077         }
       
  2078        
       
  2079         //begin checking values
       
  2080         for (var i=0; i < expected.length; i++){
       
  2081             if (expected[i] != actual[i]){
       
  2082                 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equal."), expected[i], actual[i]);
       
  2083             }
       
  2084         }
       
  2085     },
       
  2086     
       
  2087     /**
       
  2088      * Asserts that the values in an array are equivalent, and in the same position,
       
  2089      * as values in another array. This uses a function to determine if the values
       
  2090      * are equivalent. Note that the array objects themselves
       
  2091      * need not be the same for this test to pass.
       
  2092      * @param {Array} expected An array of the expected values.
       
  2093      * @param {Array} actual Any array of the actual values.
       
  2094      * @param {Function} comparator A function that returns true if the values are equivalent
       
  2095      *      or false if not.
       
  2096      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2097      * @return {Void}
       
  2098      * @method itemsAreEquivalent
       
  2099      * @static
       
  2100      */
       
  2101     itemsAreEquivalent : function (expected, actual, 
       
  2102                            comparator, message) {
       
  2103         
       
  2104         YUITest.Assert._increment();     
       
  2105 
       
  2106         //make sure the comparator is valid
       
  2107         if (typeof comparator != "function"){
       
  2108             throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
       
  2109         }
       
  2110         
       
  2111         //first check array length
       
  2112         if (expected.length != actual.length){
       
  2113             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
       
  2114         }
       
  2115         
       
  2116         //begin checking values
       
  2117         for (var i=0; i < expected.length; i++){
       
  2118             if (!comparator(expected[i], actual[i])){
       
  2119                 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
       
  2120             }
       
  2121         }
       
  2122     },
       
  2123     
       
  2124     /**
       
  2125      * Asserts that an array is empty.
       
  2126      * @param {Array} actual The array to test.
       
  2127      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2128      * @method isEmpty
       
  2129      * @static
       
  2130      */
       
  2131     isEmpty : function (actual, message) {        
       
  2132         YUITest.Assert._increment();     
       
  2133         if (actual.length > 0){
       
  2134             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should be empty."));
       
  2135         }
       
  2136     },    
       
  2137     
       
  2138     /**
       
  2139      * Asserts that an array is not empty.
       
  2140      * @param {Array} actual The array to test.
       
  2141      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2142      * @method isNotEmpty
       
  2143      * @static
       
  2144      */
       
  2145     isNotEmpty : function (actual, message) {        
       
  2146         YUITest.Assert._increment();     
       
  2147         if (actual.length === 0){
       
  2148             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should not be empty."));
       
  2149         }
       
  2150     },    
       
  2151     
       
  2152     /**
       
  2153      * Asserts that the values in an array are the same, and in the same position,
       
  2154      * as values in another array. This uses the triple equals sign
       
  2155      * so no type coercion will occur. Note that the array objects themselves
       
  2156      * need not be the same for this test to pass.
       
  2157      * @param {Array} expected An array of the expected values.
       
  2158      * @param {Array} actual Any array of the actual values.
       
  2159      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2160      * @method itemsAreSame
       
  2161      * @static
       
  2162      */
       
  2163     itemsAreSame : function (expected, actual, 
       
  2164                           message) {
       
  2165         
       
  2166         YUITest.Assert._increment();     
       
  2167 
       
  2168         //first check array length
       
  2169         if (expected.length != actual.length){
       
  2170             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
       
  2171         }
       
  2172                     
       
  2173         //begin checking values
       
  2174         for (var i=0; i < expected.length; i++){
       
  2175             if (expected[i] !== actual[i]){
       
  2176                 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not the same."), expected[i], actual[i]);
       
  2177             }
       
  2178         }
       
  2179     },
       
  2180     
       
  2181     /**
       
  2182      * Asserts that the given value is contained in an array at the specified index,
       
  2183      * starting from the back of the array.
       
  2184      * This uses the triple equals sign so no type coercion will occur.
       
  2185      * @param {Object} needle The value to look for.
       
  2186      * @param {Array} haystack The array to search in.
       
  2187      * @param {int} index The index at which the value should exist.
       
  2188      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2189      * @method lastIndexOf
       
  2190      * @static
       
  2191      */
       
  2192     lastIndexOf : function (needle, haystack, index, message) {
       
  2193     
       
  2194         //try to find the value in the array
       
  2195         for (var i=haystack.length; i >= 0; i--){
       
  2196             if (haystack[i] === needle){
       
  2197                 if (index != i){
       
  2198                     YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));                    
       
  2199                 }
       
  2200                 return;
       
  2201             }
       
  2202         }
       
  2203         
       
  2204         //if it makes it here, it wasn't found at all
       
  2205         YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array."));        
       
  2206     }
       
  2207     
       
  2208 };
       
  2209   
       
  2210 /**
       
  2211  * The Assert object provides functions to test JavaScript values against
       
  2212  * known and expected results. Whenever a comparison (assertion) fails,
       
  2213  * an error is thrown.
       
  2214  * @namespace Test
       
  2215  * @module test
       
  2216  * @class Assert
       
  2217  * @static
       
  2218  */
       
  2219 YUITest.Assert = {
       
  2220 
       
  2221     /**
       
  2222      * The number of assertions performed.
       
  2223      * @property _asserts
       
  2224      * @type int
       
  2225      * @private
       
  2226      */
       
  2227     _asserts: 0,
       
  2228 
       
  2229     //-------------------------------------------------------------------------
       
  2230     // Helper Methods
       
  2231     //-------------------------------------------------------------------------
       
  2232     
       
  2233     /**
       
  2234      * Formats a message so that it can contain the original assertion message
       
  2235      * in addition to the custom message.
       
  2236      * @param {String} customMessage The message passed in by the developer.
       
  2237      * @param {String} defaultMessage The message created by the error by default.
       
  2238      * @return {String} The final error message, containing either or both.
       
  2239      * @protected
       
  2240      * @static
       
  2241      * @method _formatMessage
       
  2242      */
       
  2243     _formatMessage : function (customMessage, defaultMessage) {
       
  2244         if (typeof customMessage == "string" && customMessage.length > 0){
       
  2245             return customMessage.replace("{message}", defaultMessage);
       
  2246         } else {
       
  2247             return defaultMessage;
       
  2248         }        
       
  2249     },
       
  2250     
       
  2251     /**
       
  2252      * Returns the number of assertions that have been performed.
       
  2253      * @method _getCount
       
  2254      * @protected
       
  2255      * @static
       
  2256      */
       
  2257     _getCount: function(){
       
  2258         return this._asserts;
       
  2259     },
       
  2260     
       
  2261     /**
       
  2262      * Increments the number of assertions that have been performed.
       
  2263      * @method _increment
       
  2264      * @protected
       
  2265      * @static
       
  2266      */
       
  2267     _increment: function(){
       
  2268         this._asserts++;
       
  2269     },
       
  2270     
       
  2271     /**
       
  2272      * Resets the number of assertions that have been performed to 0.
       
  2273      * @method _reset
       
  2274      * @protected
       
  2275      * @static
       
  2276      */
       
  2277     _reset: function(){
       
  2278         this._asserts = 0;
       
  2279     },
       
  2280     
       
  2281     //-------------------------------------------------------------------------
       
  2282     // Generic Assertion Methods
       
  2283     //-------------------------------------------------------------------------
       
  2284     
       
  2285     /** 
       
  2286      * Forces an assertion error to occur.
       
  2287      * @param {String} message (Optional) The message to display with the failure.
       
  2288      * @method fail
       
  2289      * @static
       
  2290      */
       
  2291     fail : function (message) {
       
  2292         throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Test force-failed."));
       
  2293     },       
       
  2294     
       
  2295     /** 
       
  2296      * A marker that the test should pass.
       
  2297      * @method pass
       
  2298      * @static
       
  2299      */
       
  2300     pass : function (message) {
       
  2301         YUITest.Assert._increment();
       
  2302     },       
       
  2303     
       
  2304     //-------------------------------------------------------------------------
       
  2305     // Equality Assertion Methods
       
  2306     //-------------------------------------------------------------------------    
       
  2307     
       
  2308     /**
       
  2309      * Asserts that a value is equal to another. This uses the double equals sign
       
  2310      * so type coercion may occur.
       
  2311      * @param {Object} expected The expected value.
       
  2312      * @param {Object} actual The actual value to test.
       
  2313      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2314      * @method areEqual
       
  2315      * @static
       
  2316      */
       
  2317     areEqual : function (expected, actual, message) {
       
  2318         YUITest.Assert._increment();
       
  2319         if (expected != actual) {
       
  2320             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal."), expected, actual);
       
  2321         }
       
  2322     },
       
  2323     
       
  2324     /**
       
  2325      * Asserts that a value is not equal to another. This uses the double equals sign
       
  2326      * so type coercion may occur.
       
  2327      * @param {Object} unexpected The unexpected value.
       
  2328      * @param {Object} actual The actual value to test.
       
  2329      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2330      * @method areNotEqual
       
  2331      * @static
       
  2332      */
       
  2333     areNotEqual : function (unexpected, actual, 
       
  2334                          message) {
       
  2335         YUITest.Assert._increment();
       
  2336         if (unexpected == actual) {
       
  2337             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be equal."), unexpected);
       
  2338         }
       
  2339     },
       
  2340     
       
  2341     /**
       
  2342      * Asserts that a value is not the same as another. This uses the triple equals sign
       
  2343      * so no type coercion may occur.
       
  2344      * @param {Object} unexpected The unexpected value.
       
  2345      * @param {Object} actual The actual value to test.
       
  2346      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2347      * @method areNotSame
       
  2348      * @static
       
  2349      */
       
  2350     areNotSame : function (unexpected, actual, message) {
       
  2351         YUITest.Assert._increment();
       
  2352         if (unexpected === actual) {
       
  2353             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be the same."), unexpected);
       
  2354         }
       
  2355     },
       
  2356 
       
  2357     /**
       
  2358      * Asserts that a value is the same as another. This uses the triple equals sign
       
  2359      * so no type coercion may occur.
       
  2360      * @param {Object} expected The expected value.
       
  2361      * @param {Object} actual The actual value to test.
       
  2362      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2363      * @method areSame
       
  2364      * @static
       
  2365      */
       
  2366     areSame : function (expected, actual, message) {
       
  2367         YUITest.Assert._increment();
       
  2368         if (expected !== actual) {
       
  2369             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be the same."), expected, actual);
       
  2370         }
       
  2371     },    
       
  2372     
       
  2373     //-------------------------------------------------------------------------
       
  2374     // Boolean Assertion Methods
       
  2375     //-------------------------------------------------------------------------    
       
  2376     
       
  2377     /**
       
  2378      * Asserts that a value is false. This uses the triple equals sign
       
  2379      * so no type coercion may occur.
       
  2380      * @param {Object} actual The actual value to test.
       
  2381      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2382      * @method isFalse
       
  2383      * @static
       
  2384      */
       
  2385     isFalse : function (actual, message) {
       
  2386         YUITest.Assert._increment();
       
  2387         if (false !== actual) {
       
  2388             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be false."), false, actual);
       
  2389         }
       
  2390     },
       
  2391     
       
  2392     /**
       
  2393      * Asserts that a value is true. This uses the triple equals sign
       
  2394      * so no type coercion may occur.
       
  2395      * @param {Object} actual The actual value to test.
       
  2396      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2397      * @method isTrue
       
  2398      * @static
       
  2399      */
       
  2400     isTrue : function (actual, message) {
       
  2401         YUITest.Assert._increment();
       
  2402         if (true !== actual) {
       
  2403             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be true."), true, actual);
       
  2404         }
       
  2405 
       
  2406     },
       
  2407     
       
  2408     //-------------------------------------------------------------------------
       
  2409     // Special Value Assertion Methods
       
  2410     //-------------------------------------------------------------------------    
       
  2411     
       
  2412     /**
       
  2413      * Asserts that a value is not a number.
       
  2414      * @param {Object} actual The value to test.
       
  2415      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2416      * @method isNaN
       
  2417      * @static
       
  2418      */
       
  2419     isNaN : function (actual, message){
       
  2420         YUITest.Assert._increment();
       
  2421         if (!isNaN(actual)){
       
  2422             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
       
  2423         }    
       
  2424     },
       
  2425     
       
  2426     /**
       
  2427      * Asserts that a value is not the special NaN value.
       
  2428      * @param {Object} actual The value to test.
       
  2429      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2430      * @method isNotNaN
       
  2431      * @static
       
  2432      */
       
  2433     isNotNaN : function (actual, message){
       
  2434         YUITest.Assert._increment();
       
  2435         if (isNaN(actual)){
       
  2436             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be NaN."), NaN);
       
  2437         }    
       
  2438     },
       
  2439     
       
  2440     /**
       
  2441      * Asserts that a value is not null. This uses the triple equals sign
       
  2442      * so no type coercion may occur.
       
  2443      * @param {Object} actual The actual value to test.
       
  2444      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2445      * @method isNotNull
       
  2446      * @static
       
  2447      */
       
  2448     isNotNull : function (actual, message) {
       
  2449         YUITest.Assert._increment();
       
  2450         if (actual === null) {
       
  2451             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be null."), null);
       
  2452         }
       
  2453     },
       
  2454 
       
  2455     /**
       
  2456      * Asserts that a value is not undefined. This uses the triple equals sign
       
  2457      * so no type coercion may occur.
       
  2458      * @param {Object} actual The actual value to test.
       
  2459      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2460      * @method isNotUndefined
       
  2461      * @static
       
  2462      */
       
  2463     isNotUndefined : function (actual, message) {
       
  2464         YUITest.Assert._increment();
       
  2465         if (typeof actual == "undefined") {
       
  2466             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should not be undefined."), undefined);
       
  2467         }
       
  2468     },
       
  2469 
       
  2470     /**
       
  2471      * Asserts that a value is null. This uses the triple equals sign
       
  2472      * so no type coercion may occur.
       
  2473      * @param {Object} actual The actual value to test.
       
  2474      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2475      * @method isNull
       
  2476      * @static
       
  2477      */
       
  2478     isNull : function (actual, message) {
       
  2479         YUITest.Assert._increment();
       
  2480         if (actual !== null) {
       
  2481             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be null."), null, actual);
       
  2482         }
       
  2483     },
       
  2484         
       
  2485     /**
       
  2486      * Asserts that a value is undefined. This uses the triple equals sign
       
  2487      * so no type coercion may occur.
       
  2488      * @param {Object} actual The actual value to test.
       
  2489      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2490      * @method isUndefined
       
  2491      * @static
       
  2492      */
       
  2493     isUndefined : function (actual, message) {
       
  2494         YUITest.Assert._increment();
       
  2495         if (typeof actual != "undefined") {
       
  2496             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
       
  2497         }
       
  2498     },    
       
  2499     
       
  2500     //--------------------------------------------------------------------------
       
  2501     // Instance Assertion Methods
       
  2502     //--------------------------------------------------------------------------    
       
  2503    
       
  2504     /**
       
  2505      * Asserts that a value is an array.
       
  2506      * @param {Object} actual The value to test.
       
  2507      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2508      * @method isArray
       
  2509      * @static
       
  2510      */
       
  2511     isArray : function (actual, message) {
       
  2512         YUITest.Assert._increment();
       
  2513         var shouldFail = false;
       
  2514         if (Array.isArray){
       
  2515             shouldFail = !Array.isArray(actual);
       
  2516         } else {
       
  2517             shouldFail = Object.prototype.toString.call(actual) != "[object Array]";
       
  2518         }
       
  2519         if (shouldFail){
       
  2520             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an array."), actual);
       
  2521         }    
       
  2522     },
       
  2523    
       
  2524     /**
       
  2525      * Asserts that a value is a Boolean.
       
  2526      * @param {Object} actual The value to test.
       
  2527      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2528      * @method isBoolean
       
  2529      * @static
       
  2530      */
       
  2531     isBoolean : function (actual, message) {
       
  2532         YUITest.Assert._increment();
       
  2533         if (typeof actual != "boolean"){
       
  2534             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a Boolean."), actual);
       
  2535         }    
       
  2536     },
       
  2537    
       
  2538     /**
       
  2539      * Asserts that a value is a function.
       
  2540      * @param {Object} actual The value to test.
       
  2541      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2542      * @method isFunction
       
  2543      * @static
       
  2544      */
       
  2545     isFunction : function (actual, message) {
       
  2546         YUITest.Assert._increment();
       
  2547         if (!(actual instanceof Function)){
       
  2548             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a function."), actual);
       
  2549         }    
       
  2550     },
       
  2551    
       
  2552     /**
       
  2553      * Asserts that a value is an instance of a particular object. This may return
       
  2554      * incorrect results when comparing objects from one frame to constructors in
       
  2555      * another frame. For best results, don't use in a cross-frame manner.
       
  2556      * @param {Function} expected The function that the object should be an instance of.
       
  2557      * @param {Object} actual The object to test.
       
  2558      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2559      * @method isInstanceOf
       
  2560      * @static
       
  2561      */
       
  2562     isInstanceOf : function (expected, actual, message) {
       
  2563         YUITest.Assert._increment();
       
  2564         if (!(actual instanceof expected)){
       
  2565             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
       
  2566         }
       
  2567     },
       
  2568     
       
  2569     /**
       
  2570      * Asserts that a value is a number.
       
  2571      * @param {Object} actual The value to test.
       
  2572      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2573      * @method isNumber
       
  2574      * @static
       
  2575      */
       
  2576     isNumber : function (actual, message) {
       
  2577         YUITest.Assert._increment();
       
  2578         if (typeof actual != "number"){
       
  2579             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a number."), actual);
       
  2580         }    
       
  2581     },    
       
  2582     
       
  2583     /**
       
  2584      * Asserts that a value is an object.
       
  2585      * @param {Object} actual The value to test.
       
  2586      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2587      * @method isObject
       
  2588      * @static
       
  2589      */
       
  2590     isObject : function (actual, message) {
       
  2591         YUITest.Assert._increment();
       
  2592         if (!actual || (typeof actual != "object" && typeof actual != "function")){
       
  2593             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an object."), actual);
       
  2594         }
       
  2595     },
       
  2596     
       
  2597     /**
       
  2598      * Asserts that a value is a string.
       
  2599      * @param {Object} actual The value to test.
       
  2600      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2601      * @method isString
       
  2602      * @static
       
  2603      */
       
  2604     isString : function (actual, message) {
       
  2605         YUITest.Assert._increment();
       
  2606         if (typeof actual != "string"){
       
  2607             throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a string."), actual);
       
  2608         }
       
  2609     },
       
  2610     
       
  2611     /**
       
  2612      * Asserts that a value is of a particular type. 
       
  2613      * @param {String} expectedType The expected type of the variable.
       
  2614      * @param {Object} actualValue The actual value to test.
       
  2615      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2616      * @method isTypeOf
       
  2617      * @static
       
  2618      */
       
  2619     isTypeOf : function (expectedType, actualValue, message){
       
  2620         YUITest.Assert._increment();
       
  2621         if (typeof actualValue != expectedType){
       
  2622             throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expectedType, typeof actualValue);
       
  2623         }
       
  2624     },
       
  2625     
       
  2626     //--------------------------------------------------------------------------
       
  2627     // Error Detection Methods
       
  2628     //--------------------------------------------------------------------------    
       
  2629    
       
  2630     /**
       
  2631      * Asserts that executing a particular method should throw an error of
       
  2632      * a specific type. This is a replacement for _should.error.
       
  2633      * @param {String|Function|Object} expectedError If a string, this
       
  2634      *      is the error message that the error must have; if a function, this
       
  2635      *      is the constructor that should have been used to create the thrown
       
  2636      *      error; if an object, this is an instance of a particular error type
       
  2637      *      with a specific error message (both must match).
       
  2638      * @param {Function} method The method to execute that should throw the error.
       
  2639      * @param {String} message (Optional) The message to display if the assertion
       
  2640      *      fails.
       
  2641      * @method throwsError
       
  2642      * @return {void}
       
  2643      * @static
       
  2644      */
       
  2645     throwsError: function(expectedError, method, message){
       
  2646         YUITest.Assert._increment();
       
  2647         var error = false;
       
  2648     
       
  2649         try {
       
  2650             method();        
       
  2651         } catch (thrown) {
       
  2652             
       
  2653             //check to see what type of data we have
       
  2654             if (typeof expectedError == "string"){
       
  2655                 
       
  2656                 //if it's a string, check the error message
       
  2657                 if (thrown.message != expectedError){
       
  2658                     error = true;
       
  2659                 }
       
  2660             } else if (typeof expectedError == "function"){
       
  2661             
       
  2662                 //if it's a function, see if the error is an instance of it
       
  2663                 if (!(thrown instanceof expectedError)){
       
  2664                     error = true;
       
  2665                 }
       
  2666             
       
  2667             } else if (typeof expectedError == "object" && expectedError !== null){
       
  2668             
       
  2669                 //if it's an object, check the instance and message
       
  2670                 if (!(thrown instanceof expectedError.constructor) || 
       
  2671                         thrown.message != expectedError.message){
       
  2672                     error = true;
       
  2673                 }
       
  2674             
       
  2675             } else { //if it gets here, the argument could be wrong
       
  2676                 error = true;
       
  2677             }
       
  2678             
       
  2679             if (error){
       
  2680                 throw new YUITest.UnexpectedError(thrown);                    
       
  2681             } else {
       
  2682                 return;
       
  2683             }
       
  2684         }
       
  2685         
       
  2686         //if it reaches here, the error wasn't thrown, which is a bad thing
       
  2687         throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Error should have been thrown."));
       
  2688     }
       
  2689 
       
  2690 };
       
  2691 /**
       
  2692  * Error is thrown whenever an assertion fails. It provides methods
       
  2693  * to more easily get at error information and also provides a base class
       
  2694  * from which more specific assertion errors can be derived.
       
  2695  *
       
  2696  * @param {String} message The message to display when the error occurs.
       
  2697  * @namespace Test
       
  2698  * @module test
       
  2699  * @class AssertionError
       
  2700  * @constructor
       
  2701  */ 
       
  2702 YUITest.AssertionError = function (message){
       
  2703     
       
  2704     /**
       
  2705      * Error message. Must be duplicated to ensure browser receives it.
       
  2706      * @type String
       
  2707      * @property message
       
  2708      */
       
  2709     this.message = message;
       
  2710     
       
  2711     /**
       
  2712      * The name of the error that occurred.
       
  2713      * @type String
       
  2714      * @property name
       
  2715      */
       
  2716     this.name = "Assert Error";
       
  2717 };
       
  2718 
       
  2719 YUITest.AssertionError.prototype = {
       
  2720 
       
  2721     //restore constructor
       
  2722     constructor: YUITest.AssertionError,
       
  2723 
       
  2724     /**
       
  2725      * Returns a fully formatted error for an assertion failure. This should
       
  2726      * be overridden by all subclasses to provide specific information.
       
  2727      * @method getMessage
       
  2728      * @return {String} A string describing the error.
       
  2729      */
       
  2730     getMessage : function () {
       
  2731         return this.message;
       
  2732     },
       
  2733     
       
  2734     /**
       
  2735      * Returns a string representation of the error.
       
  2736      * @method toString
       
  2737      * @return {String} A string representation of the error.
       
  2738      */
       
  2739     toString : function () {
       
  2740         return this.name + ": " + this.getMessage();
       
  2741     }
       
  2742 
       
  2743 };/**
       
  2744  * ComparisonFailure is subclass of Error that is thrown whenever
       
  2745  * a comparison between two values fails. It provides mechanisms to retrieve
       
  2746  * both the expected and actual value.
       
  2747  *
       
  2748  * @param {String} message The message to display when the error occurs.
       
  2749  * @param {Object} expected The expected value.
       
  2750  * @param {Object} actual The actual value that caused the assertion to fail.
       
  2751  * @namespace Test 
       
  2752  * @extends AssertionError
       
  2753  * @module test
       
  2754  * @class ComparisonFailure
       
  2755  * @constructor
       
  2756  */ 
       
  2757 YUITest.ComparisonFailure = function (message, expected, actual){
       
  2758 
       
  2759     //call superclass
       
  2760     YUITest.AssertionError.call(this, message);
       
  2761     
       
  2762     /**
       
  2763      * The expected value.
       
  2764      * @type Object
       
  2765      * @property expected
       
  2766      */
       
  2767     this.expected = expected;
       
  2768     
       
  2769     /**
       
  2770      * The actual value.
       
  2771      * @type Object
       
  2772      * @property actual
       
  2773      */
       
  2774     this.actual = actual;
       
  2775     
       
  2776     /**
       
  2777      * The name of the error that occurred.
       
  2778      * @type String
       
  2779      * @property name
       
  2780      */
       
  2781     this.name = "ComparisonFailure";
       
  2782     
       
  2783 };
       
  2784 
       
  2785 //inherit from YUITest.AssertionError
       
  2786 YUITest.ComparisonFailure.prototype = new YUITest.AssertionError;
       
  2787 
       
  2788 //restore constructor
       
  2789 YUITest.ComparisonFailure.prototype.constructor = YUITest.ComparisonFailure;
       
  2790 
       
  2791 /**
       
  2792  * Returns a fully formatted error for an assertion failure. This message
       
  2793  * provides information about the expected and actual values.
       
  2794  * @method getMessage
       
  2795  * @return {String} A string describing the error.
       
  2796  */
       
  2797 YUITest.ComparisonFailure.prototype.getMessage = function(){
       
  2798     return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")"  +
       
  2799             "\nActual: " + this.actual + " (" + (typeof this.actual) + ")";
       
  2800 };
       
  2801 /**
       
  2802  * An object object containing coverage result formatting methods.
       
  2803  * @namespace Test
       
  2804  * @module test
       
  2805  * @class CoverageFormat
       
  2806  * @static
       
  2807  */
       
  2808 YUITest.CoverageFormat = {
       
  2809 
       
  2810     /**
       
  2811      * Returns the coverage report in JSON format. This is the straight
       
  2812      * JSON representation of the native coverage report.
       
  2813      * @param {Object} coverage The coverage report object.
       
  2814      * @return {String} A JSON-formatted string of coverage data.
       
  2815      * @method JSON
       
  2816      * @namespace Test.CoverageFormat
       
  2817      */
       
  2818     JSON: function(coverage){
       
  2819         return YUITest.Util.JSON.stringify(coverage);
       
  2820     },
       
  2821     
       
  2822     /**
       
  2823      * Returns the coverage report in a JSON format compatible with
       
  2824      * Xdebug. See <a href="http://www.xdebug.com/docs/code_coverage">Xdebug Documentation</a>
       
  2825      * for more information. Note: function coverage is not available
       
  2826      * in this format.
       
  2827      * @param {Object} coverage The coverage report object.
       
  2828      * @return {String} A JSON-formatted string of coverage data.
       
  2829      * @method XdebugJSON
       
  2830      * @namespace Test.CoverageFormat
       
  2831      */    
       
  2832     XdebugJSON: function(coverage){
       
  2833     
       
  2834         var report = {};
       
  2835         for (var prop in coverage){
       
  2836             if (coverage.hasOwnProperty(prop)){
       
  2837                 report[prop] = coverage[prop].lines;
       
  2838             }
       
  2839         }
       
  2840 
       
  2841         return YUITest.Util.JSON.stringify(coverage);
       
  2842     }
       
  2843 
       
  2844 };
       
  2845 
       
  2846 /**
       
  2847  * The DateAssert object provides functions to test JavaScript Date objects
       
  2848  * for a variety of cases.
       
  2849  * @namespace Test
       
  2850  * @module test
       
  2851  * @class DateAssert
       
  2852  * @static
       
  2853  */
       
  2854  
       
  2855 YUITest.DateAssert = {
       
  2856 
       
  2857     /**
       
  2858      * Asserts that a date's month, day, and year are equal to another date's.
       
  2859      * @param {Date} expected The expected date.
       
  2860      * @param {Date} actual The actual date to test.
       
  2861      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2862      * @method datesAreEqual
       
  2863      * @static
       
  2864      */
       
  2865     datesAreEqual : function (expected, actual, message){
       
  2866         YUITest.Assert._increment();        
       
  2867         if (expected instanceof Date && actual instanceof Date){
       
  2868             var msg = "";
       
  2869             
       
  2870             //check years first
       
  2871             if (expected.getFullYear() != actual.getFullYear()){
       
  2872                 msg = "Years should be equal.";
       
  2873             }
       
  2874             
       
  2875             //now check months
       
  2876             if (expected.getMonth() != actual.getMonth()){
       
  2877                 msg = "Months should be equal.";
       
  2878             }                
       
  2879             
       
  2880             //last, check the day of the month
       
  2881             if (expected.getDate() != actual.getDate()){
       
  2882                 msg = "Days of month should be equal.";
       
  2883             }                
       
  2884             
       
  2885             if (msg.length){
       
  2886                 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
       
  2887             }
       
  2888         } else {
       
  2889             throw new TypeError("YUITest.DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
       
  2890         }
       
  2891     },
       
  2892 
       
  2893     /**
       
  2894      * Asserts that a date's hour, minutes, and seconds are equal to another date's.
       
  2895      * @param {Date} expected The expected date.
       
  2896      * @param {Date} actual The actual date to test.
       
  2897      * @param {String} message (Optional) The message to display if the assertion fails.
       
  2898      * @method timesAreEqual
       
  2899      * @static
       
  2900      */
       
  2901     timesAreEqual : function (expected, actual, message){
       
  2902         YUITest.Assert._increment();
       
  2903         if (expected instanceof Date && actual instanceof Date){
       
  2904             var msg = "";
       
  2905             
       
  2906             //check hours first
       
  2907             if (expected.getHours() != actual.getHours()){
       
  2908                 msg = "Hours should be equal.";
       
  2909             }
       
  2910             
       
  2911             //now check minutes
       
  2912             if (expected.getMinutes() != actual.getMinutes()){
       
  2913                 msg = "Minutes should be equal.";
       
  2914             }                
       
  2915             
       
  2916             //last, check the seconds
       
  2917             if (expected.getSeconds() != actual.getSeconds()){
       
  2918                 msg = "Seconds should be equal.";
       
  2919             }                
       
  2920             
       
  2921             if (msg.length){
       
  2922                 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
       
  2923             }
       
  2924         } else {
       
  2925             throw new TypeError("YUITest.DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
       
  2926         }
       
  2927     }
       
  2928     
       
  2929 };/**
       
  2930  * Creates a new mock object.
       
  2931  * @namespace Test
       
  2932  * @module test
       
  2933  * @class Mock
       
  2934  * @constructor
       
  2935  * @param {Object} template (Optional) An object whose methods
       
  2936  *      should be stubbed out on the mock object.
       
  2937  */
       
  2938 YUITest.Mock = function(template){
       
  2939 
       
  2940     //use blank object is nothing is passed in
       
  2941     template = template || {};
       
  2942     
       
  2943     var mock,
       
  2944         name;
       
  2945     
       
  2946     //try to create mock that keeps prototype chain intact
       
  2947     //fails in the case of ActiveX objects
       
  2948     try {
       
  2949         function f(){}
       
  2950         f.prototype = template;
       
  2951         mock = new f();
       
  2952     } catch (ex) {
       
  2953         mock = {};
       
  2954     }
       
  2955 
       
  2956     //create stubs for all methods
       
  2957     for (name in template){
       
  2958         if (template.hasOwnProperty(name)){
       
  2959             if (typeof template[name] == "function"){
       
  2960                 mock[name] = function(name){
       
  2961                     return function(){
       
  2962                         YUITest.Assert.fail("Method " + name + "() was called but was not expected to be.");
       
  2963                     };
       
  2964                 }(name);
       
  2965             }
       
  2966         }
       
  2967     }
       
  2968 
       
  2969     //return it
       
  2970     return mock;    
       
  2971 };
       
  2972     
       
  2973 /**
       
  2974  * Assigns an expectation to a mock object. This is used to create
       
  2975  * methods and properties on the mock object that are monitored for
       
  2976  * calls and changes, respectively.
       
  2977  * @param {Object} mock The object to add the expectation to.
       
  2978  * @param {Object} expectation An object defining the expectation. For
       
  2979  *      properties, the keys "property" and "value" are required. For a
       
  2980  *      method the "method" key defines the method's name, the optional "args"
       
  2981  *      key provides an array of argument types. The "returns" key provides
       
  2982  *      an optional return value. An optional "run" key provides a function
       
  2983  *      to be used as the method body. The return value of a mocked method is
       
  2984  *      determined first by the "returns" key, then the "run" function's return
       
  2985  *      value. If neither "returns" nor "run" is provided undefined is returned.
       
  2986  *      An optional 'error' key defines an error type to be thrown in all cases.
       
  2987  *      The "callCount" key provides an optional number of times the method is
       
  2988  *      expected to be called (the default is 1).
       
  2989  * @return {void}
       
  2990  * @method expect
       
  2991  * @static
       
  2992  */ 
       
  2993 YUITest.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
       
  2994 
       
  2995     //make sure there's a place to store the expectations
       
  2996     if (!mock.__expectations) {
       
  2997         mock.__expectations = {};
       
  2998     }
       
  2999 
       
  3000     //method expectation
       
  3001     if (expectation.method){
       
  3002         var name = expectation.method,
       
  3003             args = expectation.args || [],
       
  3004             result = expectation.returns,
       
  3005             callCount = (typeof expectation.callCount == "number") ? expectation.callCount : 1,
       
  3006             error = expectation.error,
       
  3007             run = expectation.run || function(){},
       
  3008             runResult,
       
  3009             i;
       
  3010 
       
  3011         //save expectations
       
  3012         mock.__expectations[name] = expectation;
       
  3013         expectation.callCount = callCount;
       
  3014         expectation.actualCallCount = 0;
       
  3015             
       
  3016         //process arguments
       
  3017         for (i=0; i < args.length; i++){
       
  3018              if (!(args[i] instanceof YUITest.Mock.Value)){
       
  3019                 args[i] = YUITest.Mock.Value(YUITest.Assert.areSame, [args[i]], "Argument " + i + " of " + name + "() is incorrect.");
       
  3020             }       
       
  3021         }
       
  3022     
       
  3023         //if the method is expected to be called
       
  3024         if (callCount > 0){
       
  3025             mock[name] = function(){   
       
  3026                 try {
       
  3027                     expectation.actualCallCount++;
       
  3028                     YUITest.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
       
  3029                     for (var i=0, len=args.length; i < len; i++){
       
  3030                         args[i].verify(arguments[i]);
       
  3031                     }                
       
  3032 
       
  3033                     runResult = run.apply(this, arguments);
       
  3034                     
       
  3035                     if (error){
       
  3036                         throw error;
       
  3037                     }
       
  3038                 } catch (ex){
       
  3039                     //route through TestRunner for proper handling
       
  3040                     YUITest.TestRunner._handleError(ex);
       
  3041                 }
       
  3042 
       
  3043                 // Any value provided for 'returns' overrides any value returned
       
  3044                 // by our 'run' function. 
       
  3045                 return expectation.hasOwnProperty('returns') ? result : runResult;
       
  3046             };
       
  3047         } else {
       
  3048         
       
  3049             //method should fail if called when not expected
       
  3050             mock[name] = function(){
       
  3051                 try {
       
  3052                     YUITest.Assert.fail("Method " + name + "() should not have been called.");
       
  3053                 } catch (ex){
       
  3054                     //route through TestRunner for proper handling
       
  3055                     YUITest.TestRunner._handleError(ex);
       
  3056                 }                    
       
  3057             };
       
  3058         }
       
  3059     } else if (expectation.property){
       
  3060         //save expectations
       
  3061         mock.__expectations[expectation.property] = expectation;
       
  3062     }
       
  3063 };
       
  3064 
       
  3065 /**
       
  3066  * Verifies that all expectations of a mock object have been met and
       
  3067  * throws an assertion error if not.
       
  3068  * @param {Object} mock The object to verify..
       
  3069  * @return {void}
       
  3070  * @method verify
       
  3071  * @static
       
  3072  */ 
       
  3073 YUITest.Mock.verify = function(mock){    
       
  3074     try {
       
  3075     
       
  3076         for (var name in mock.__expectations){
       
  3077             if (mock.__expectations.hasOwnProperty(name)){
       
  3078                 var expectation = mock.__expectations[name];
       
  3079                 if (expectation.method) {
       
  3080                     YUITest.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
       
  3081                 } else if (expectation.property){
       
  3082                     YUITest.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value."); 
       
  3083                 }                
       
  3084             }
       
  3085         }
       
  3086 
       
  3087     } catch (ex){
       
  3088         //route through TestRunner for proper handling
       
  3089         YUITest.TestRunner._handleError(ex);
       
  3090     }
       
  3091 };
       
  3092 
       
  3093 /**
       
  3094  * Creates a new value matcher.
       
  3095  * @param {Function} method The function to call on the value.
       
  3096  * @param {Array} originalArgs (Optional) Array of arguments to pass to the method.
       
  3097  * @param {String} message (Optional) Message to display in case of failure.
       
  3098  * @namespace Test.Mock
       
  3099  * @module test
       
  3100  * @class Value
       
  3101  * @constructor
       
  3102  */
       
  3103 YUITest.Mock.Value = function(method, originalArgs, message){
       
  3104     if (this instanceof YUITest.Mock.Value){
       
  3105         this.verify = function(value){
       
  3106             var args = [].concat(originalArgs || []);
       
  3107             args.push(value);
       
  3108             args.push(message);
       
  3109             method.apply(null, args);
       
  3110         };
       
  3111     } else {
       
  3112         return new YUITest.Mock.Value(method, originalArgs, message);
       
  3113     }
       
  3114 };
       
  3115 
       
  3116 /**
       
  3117  * Predefined matcher to match any value.
       
  3118  * @property Any
       
  3119  * @static
       
  3120  * @type Function
       
  3121  */
       
  3122 YUITest.Mock.Value.Any        = YUITest.Mock.Value(function(){});
       
  3123 
       
  3124 /**
       
  3125  * Predefined matcher to match boolean values.
       
  3126  * @property Boolean
       
  3127  * @static
       
  3128  * @type Function
       
  3129  */
       
  3130 YUITest.Mock.Value.Boolean    = YUITest.Mock.Value(YUITest.Assert.isBoolean);
       
  3131 
       
  3132 /**
       
  3133  * Predefined matcher to match number values.
       
  3134  * @property Number
       
  3135  * @static
       
  3136  * @type Function
       
  3137  */
       
  3138 YUITest.Mock.Value.Number     = YUITest.Mock.Value(YUITest.Assert.isNumber);
       
  3139 
       
  3140 /**
       
  3141  * Predefined matcher to match string values.
       
  3142  * @property String
       
  3143  * @static
       
  3144  * @type Function
       
  3145  */
       
  3146 YUITest.Mock.Value.String     = YUITest.Mock.Value(YUITest.Assert.isString);
       
  3147 
       
  3148 /**
       
  3149  * Predefined matcher to match object values.
       
  3150  * @property Object
       
  3151  * @static
       
  3152  * @type Function
       
  3153  */
       
  3154 YUITest.Mock.Value.Object     = YUITest.Mock.Value(YUITest.Assert.isObject);
       
  3155 
       
  3156 /**
       
  3157  * Predefined matcher to match function values.
       
  3158  * @property Function
       
  3159  * @static
       
  3160  * @type Function
       
  3161  */
       
  3162 YUITest.Mock.Value.Function   = YUITest.Mock.Value(YUITest.Assert.isFunction);
       
  3163 
       
  3164 /**
       
  3165  * The ObjectAssert object provides functions to test JavaScript objects
       
  3166  * for a variety of cases.
       
  3167  * @namespace Test
       
  3168  * @module test
       
  3169  * @class ObjectAssert
       
  3170  * @static
       
  3171  */
       
  3172 YUITest.ObjectAssert = {
       
  3173 
       
  3174     /**
       
  3175      * Asserts that an object has all of the same properties
       
  3176      * and property values as the other.
       
  3177      * @param {Object} expected The object with all expected properties and values.
       
  3178      * @param {Object} actual The object to inspect.
       
  3179      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3180      * @method areEqual
       
  3181      * @static
       
  3182      * @deprecated
       
  3183      */
       
  3184     areEqual: function(expected, actual, message) {
       
  3185         YUITest.Assert._increment();         
       
  3186         
       
  3187         var expectedKeys = YUITest.Object.keys(expected),
       
  3188             actualKeys = YUITest.Object.keys(actual);
       
  3189         
       
  3190         //first check keys array length
       
  3191         if (expectedKeys.length != actualKeys.length){
       
  3192             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Object should have " + expectedKeys.length + " keys but has " + actualKeys.length));
       
  3193         }
       
  3194         
       
  3195         //then check values
       
  3196         for (var name in expected){
       
  3197             if (expected.hasOwnProperty(name)){
       
  3198                 if (expected[name] != actual[name]){
       
  3199                     throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal for property " + name), expected[name], actual[name]);
       
  3200                 }            
       
  3201             }
       
  3202         }           
       
  3203     },
       
  3204     
       
  3205     /**
       
  3206      * Asserts that an object has a property with the given name.
       
  3207      * @param {String} propertyName The name of the property to test.
       
  3208      * @param {Object} object The object to search.
       
  3209      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3210      * @method hasKey
       
  3211      * @static
       
  3212      * @deprecated Use ownsOrInheritsKey() instead
       
  3213      */    
       
  3214     hasKey: function (propertyName, object, message) {
       
  3215         YUITest.ObjectAssert.ownsOrInheritsKey(propertyName, object, message);   
       
  3216     },
       
  3217     
       
  3218     /**
       
  3219      * Asserts that an object has all properties of a reference object.
       
  3220      * @param {Array} properties An array of property names that should be on the object.
       
  3221      * @param {Object} object The object to search.
       
  3222      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3223      * @method hasKeys
       
  3224      * @static
       
  3225      * @deprecated Use ownsOrInheritsKeys() instead
       
  3226      */    
       
  3227     hasKeys: function (properties, object, message) {
       
  3228         YUITest.ObjectAssert.ownsOrInheritsKeys(properties, object, message);
       
  3229     },
       
  3230     
       
  3231     /**
       
  3232      * Asserts that a property with the given name exists on an object's prototype.
       
  3233      * @param {String} propertyName The name of the property to test.
       
  3234      * @param {Object} object The object to search.
       
  3235      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3236      * @method inheritsKey
       
  3237      * @static
       
  3238      */    
       
  3239     inheritsKey: function (propertyName, object, message) {
       
  3240         YUITest.Assert._increment();               
       
  3241         if (!(propertyName in object && !object.hasOwnProperty(propertyName))){
       
  3242             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
       
  3243         }     
       
  3244     },
       
  3245     
       
  3246     /**
       
  3247      * Asserts that all properties exist on an object prototype.
       
  3248      * @param {Array} properties An array of property names that should be on the object.
       
  3249      * @param {Object} object The object to search.
       
  3250      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3251      * @method inheritsKeys
       
  3252      * @static
       
  3253      */    
       
  3254     inheritsKeys: function (properties, object, message) {
       
  3255         YUITest.Assert._increment();        
       
  3256         for (var i=0; i < properties.length; i++){
       
  3257             if (!(propertyName in object && !object.hasOwnProperty(properties[i]))){
       
  3258                 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
       
  3259             }      
       
  3260         }
       
  3261     },
       
  3262     
       
  3263     /**
       
  3264      * Asserts that a property with the given name exists on an object instance (not on its prototype).
       
  3265      * @param {String} propertyName The name of the property to test.
       
  3266      * @param {Object} object The object to search.
       
  3267      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3268      * @method ownsKey
       
  3269      * @static
       
  3270      */    
       
  3271     ownsKey: function (propertyName, object, message) {
       
  3272         YUITest.Assert._increment();               
       
  3273         if (!object.hasOwnProperty(propertyName)){
       
  3274             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
       
  3275         }     
       
  3276     },
       
  3277     
       
  3278     /**
       
  3279      * Asserts that all properties exist on an object instance (not on its prototype).
       
  3280      * @param {Array} properties An array of property names that should be on the object.
       
  3281      * @param {Object} object The object to search.
       
  3282      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3283      * @method ownsKeys
       
  3284      * @static
       
  3285      */    
       
  3286     ownsKeys: function (properties, object, message) {
       
  3287         YUITest.Assert._increment();        
       
  3288         for (var i=0; i < properties.length; i++){
       
  3289             if (!object.hasOwnProperty(properties[i])){
       
  3290                 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
       
  3291             }      
       
  3292         }
       
  3293     },
       
  3294     
       
  3295     /**
       
  3296      * Asserts that an object owns no properties.
       
  3297      * @param {Object} object The object to check.
       
  3298      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3299      * @method ownsNoKeys
       
  3300      * @static
       
  3301      */    
       
  3302     ownsNoKeys : function (object, message) {
       
  3303         YUITest.Assert._increment();  
       
  3304         var count = 0,
       
  3305             name;
       
  3306         for (name in object){
       
  3307             if (object.hasOwnProperty(name)){
       
  3308                 count++;
       
  3309             }
       
  3310         }
       
  3311         
       
  3312         if (count !== 0){
       
  3313             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Object owns " + count + " properties but should own none."));        
       
  3314         }
       
  3315 
       
  3316     },
       
  3317 
       
  3318     /**
       
  3319      * Asserts that an object has a property with the given name.
       
  3320      * @param {String} propertyName The name of the property to test.
       
  3321      * @param {Object} object The object to search.
       
  3322      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3323      * @method ownsOrInheritsKey
       
  3324      * @static
       
  3325      */    
       
  3326     ownsOrInheritsKey: function (propertyName, object, message) {
       
  3327         YUITest.Assert._increment();               
       
  3328         if (!(propertyName in object)){
       
  3329             YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
       
  3330         }    
       
  3331     },
       
  3332     
       
  3333     /**
       
  3334      * Asserts that an object has all properties of a reference object.
       
  3335      * @param {Array} properties An array of property names that should be on the object.
       
  3336      * @param {Object} object The object to search.
       
  3337      * @param {String} message (Optional) The message to display if the assertion fails.
       
  3338      * @method ownsOrInheritsKeys
       
  3339      * @static
       
  3340      */    
       
  3341     ownsOrInheritsKeys: function (properties, object, message) {
       
  3342         YUITest.Assert._increment();  
       
  3343         for (var i=0; i < properties.length; i++){
       
  3344             if (!(properties[i] in object)){
       
  3345                 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object."));
       
  3346             }      
       
  3347         }
       
  3348     }    
       
  3349 };
       
  3350 /**
       
  3351  * Convenience type for storing and aggregating
       
  3352  * test result information.
       
  3353  * @private
       
  3354  * @namespace Test
       
  3355  * @module test
       
  3356  * @class Results
       
  3357  * @constructor
       
  3358  * @param {String} name The name of the test.
       
  3359  */
       
  3360 YUITest.Results = function(name){
       
  3361 
       
  3362     /**
       
  3363      * Name of the test, test case, or test suite.
       
  3364      * @type String
       
  3365      * @property name
       
  3366      */
       
  3367     this.name = name;
       
  3368     
       
  3369     /**
       
  3370      * Number of passed tests.
       
  3371      * @type int
       
  3372      * @property passed
       
  3373      */
       
  3374     this.passed = 0;
       
  3375     
       
  3376     /**
       
  3377      * Number of failed tests.
       
  3378      * @type int
       
  3379      * @property failed
       
  3380      */
       
  3381     this.failed = 0;
       
  3382     
       
  3383     /**
       
  3384      * Number of errors that occur in non-test methods.
       
  3385      * @type int
       
  3386      * @property errors
       
  3387      */
       
  3388     this.errors = 0;
       
  3389     
       
  3390     /**
       
  3391      * Number of ignored tests.
       
  3392      * @type int
       
  3393      * @property ignored
       
  3394      */
       
  3395     this.ignored = 0;
       
  3396     
       
  3397     /**
       
  3398      * Number of total tests.
       
  3399      * @type int
       
  3400      * @property total
       
  3401      */
       
  3402     this.total = 0;
       
  3403     
       
  3404     /**
       
  3405      * Amount of time (ms) it took to complete testing.
       
  3406      * @type int
       
  3407      * @property duration
       
  3408      */
       
  3409     this.duration = 0;
       
  3410 };
       
  3411 
       
  3412 /**
       
  3413  * Includes results from another results object into this one.
       
  3414  * @param {Test.Results} result The results object to include.
       
  3415  * @method include
       
  3416  * @return {void}
       
  3417  */
       
  3418 YUITest.Results.prototype.include = function(results){
       
  3419     this.passed += results.passed;
       
  3420     this.failed += results.failed;
       
  3421     this.ignored += results.ignored;
       
  3422     this.total += results.total;
       
  3423     this.errors += results.errors;
       
  3424 };
       
  3425 /**
       
  3426  * ShouldError is subclass of Error that is thrown whenever
       
  3427  * a test is expected to throw an error but doesn't.
       
  3428  *
       
  3429  * @param {String} message The message to display when the error occurs.
       
  3430  * @namespace Test 
       
  3431  * @extends AssertionError
       
  3432  * @module test
       
  3433  * @class ShouldError
       
  3434  * @constructor
       
  3435  */ 
       
  3436 YUITest.ShouldError = function (message){
       
  3437 
       
  3438     //call superclass
       
  3439     YUITest.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
       
  3440     
       
  3441     /**
       
  3442      * The name of the error that occurred.
       
  3443      * @type String
       
  3444      * @property name
       
  3445      */
       
  3446     this.name = "ShouldError";
       
  3447     
       
  3448 };
       
  3449 
       
  3450 //inherit from YUITest.AssertionError
       
  3451 YUITest.ShouldError.prototype = new YUITest.AssertionError();
       
  3452 
       
  3453 //restore constructor
       
  3454 YUITest.ShouldError.prototype.constructor = YUITest.ShouldError;
       
  3455 /**
       
  3456  * ShouldFail is subclass of AssertionError that is thrown whenever
       
  3457  * a test was expected to fail but did not.
       
  3458  *
       
  3459  * @param {String} message The message to display when the error occurs.
       
  3460  * @namespace Test 
       
  3461  * @extends YUITest.AssertionError
       
  3462  * @module test
       
  3463  * @class ShouldFail
       
  3464  * @constructor
       
  3465  */ 
       
  3466 YUITest.ShouldFail = function (message){
       
  3467 
       
  3468     //call superclass
       
  3469     YUITest.AssertionError.call(this, message || "This test should fail but didn't.");
       
  3470     
       
  3471     /**
       
  3472      * The name of the error that occurred.
       
  3473      * @type String
       
  3474      * @property name
       
  3475      */
       
  3476     this.name = "ShouldFail";
       
  3477     
       
  3478 };
       
  3479 
       
  3480 //inherit from YUITest.AssertionError
       
  3481 YUITest.ShouldFail.prototype = new YUITest.AssertionError();
       
  3482 
       
  3483 //restore constructor
       
  3484 YUITest.ShouldFail.prototype.constructor = YUITest.ShouldFail;
       
  3485 /**
       
  3486  * UnexpectedError is subclass of AssertionError that is thrown whenever
       
  3487  * an error occurs within the course of a test and the test was not expected
       
  3488  * to throw an error.
       
  3489  *
       
  3490  * @param {Error} cause The unexpected error that caused this error to be 
       
  3491  *                      thrown.
       
  3492  * @namespace Test 
       
  3493  * @extends YUITest.AssertionError
       
  3494  * @module test
       
  3495  * @class UnexpectedError
       
  3496  * @constructor
       
  3497  */  
       
  3498 YUITest.UnexpectedError = function (cause){
       
  3499 
       
  3500     //call superclass
       
  3501     YUITest.AssertionError.call(this, "Unexpected error: " + cause.message);
       
  3502     
       
  3503     /**
       
  3504      * The unexpected error that occurred.
       
  3505      * @type Error
       
  3506      * @property cause
       
  3507      */
       
  3508     this.cause = cause;
       
  3509     
       
  3510     /**
       
  3511      * The name of the error that occurred.
       
  3512      * @type String
       
  3513      * @property name
       
  3514      */
       
  3515     this.name = "UnexpectedError";
       
  3516     
       
  3517     /**
       
  3518      * Stack information for the error (if provided).
       
  3519      * @type String
       
  3520      * @property stack
       
  3521      */
       
  3522     this.stack = cause.stack;
       
  3523     
       
  3524 };
       
  3525 
       
  3526 //inherit from YUITest.AssertionError
       
  3527 YUITest.UnexpectedError.prototype = new YUITest.AssertionError();
       
  3528 
       
  3529 //restore constructor
       
  3530 YUITest.UnexpectedError.prototype.constructor = YUITest.UnexpectedError;
       
  3531 /**
       
  3532  * UnexpectedValue is subclass of Error that is thrown whenever
       
  3533  * a value was unexpected in its scope. This typically means that a test
       
  3534  * was performed to determine that a value was *not* equal to a certain
       
  3535  * value.
       
  3536  *
       
  3537  * @param {String} message The message to display when the error occurs.
       
  3538  * @param {Object} unexpected The unexpected value.
       
  3539  * @namespace Test 
       
  3540  * @extends AssertionError
       
  3541  * @module test
       
  3542  * @class UnexpectedValue
       
  3543  * @constructor
       
  3544  */ 
       
  3545 YUITest.UnexpectedValue = function (message, unexpected){
       
  3546 
       
  3547     //call superclass
       
  3548     YUITest.AssertionError.call(this, message);
       
  3549     
       
  3550     /**
       
  3551      * The unexpected value.
       
  3552      * @type Object
       
  3553      * @property unexpected
       
  3554      */
       
  3555     this.unexpected = unexpected;
       
  3556     
       
  3557     /**
       
  3558      * The name of the error that occurred.
       
  3559      * @type String
       
  3560      * @property name
       
  3561      */
       
  3562     this.name = "UnexpectedValue";
       
  3563     
       
  3564 };
       
  3565 
       
  3566 //inherit from YUITest.AssertionError
       
  3567 YUITest.UnexpectedValue.prototype = new YUITest.AssertionError();
       
  3568 
       
  3569 //restore constructor
       
  3570 YUITest.UnexpectedValue.prototype.constructor = YUITest.UnexpectedValue;
       
  3571 
       
  3572 /**
       
  3573  * Returns a fully formatted error for an assertion failure. This message
       
  3574  * provides information about the expected and actual values.
       
  3575  * @method getMessage
       
  3576  * @return {String} A string describing the error.
       
  3577  */
       
  3578 YUITest.UnexpectedValue.prototype.getMessage = function(){
       
  3579     return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
       
  3580 };
       
  3581 
       
  3582 /**
       
  3583  * Represents a stoppage in test execution to wait for an amount of time before
       
  3584  * continuing.
       
  3585  * @param {Function} segment A function to run when the wait is over.
       
  3586  * @param {int} delay The number of milliseconds to wait before running the code.
       
  3587  * @module test
       
  3588  * @class Wait
       
  3589  * @namespace Test
       
  3590  * @constructor
       
  3591  *
       
  3592  */
       
  3593 YUITest.Wait = function (segment, delay) {
       
  3594     
       
  3595     /**
       
  3596      * The segment of code to run when the wait is over.
       
  3597      * @type Function
       
  3598      * @property segment
       
  3599      */
       
  3600     this.segment = (typeof segment == "function" ? segment : null);
       
  3601 
       
  3602     /**
       
  3603      * The delay before running the segment of code.
       
  3604      * @type int
       
  3605      * @property delay
       
  3606      */
       
  3607     this.delay = (typeof delay == "number" ? delay : 0);        
       
  3608 };
       
  3609 
       
  3610 
       
  3611 //Setting up our aliases..
       
  3612 Y.Test = YUITest;
       
  3613 Y.Object.each(YUITest, function(item, name) {
       
  3614     var name = name.replace('Test', '');
       
  3615     Y.Test[name] = item;
       
  3616 });
       
  3617 
       
  3618 } //End of else in top wrapper
       
  3619 
       
  3620 Y.Assert = YUITest.Assert;
       
  3621 Y.Assert.Error = Y.Test.AssertionError;
       
  3622 Y.Assert.ComparisonFailure = Y.Test.ComparisonFailure;
       
  3623 Y.Assert.UnexpectedValue = Y.Test.UnexpectedValue;
       
  3624 Y.Mock = Y.Test.Mock;
       
  3625 Y.ObjectAssert = Y.Test.ObjectAssert;
       
  3626 Y.ArrayAssert = Y.Test.ArrayAssert;
       
  3627 Y.DateAssert = Y.Test.DateAssert;
       
  3628 Y.Test.ResultsFormat = Y.Test.TestFormat;
       
  3629 
       
  3630 var itemsAreEqual = Y.Test.ArrayAssert.itemsAreEqual;
       
  3631 
       
  3632 Y.Test.ArrayAssert.itemsAreEqual = function(expected, actual, message) {
       
  3633     return itemsAreEqual.call(this, Y.Array(expected), Y.Array(actual), message);
       
  3634 };
       
  3635 
       
  3636 
       
  3637 /**
       
  3638  * Asserts that a given condition is true. If not, then a Y.Assert.Error object is thrown
       
  3639  * and the test fails.
       
  3640  * @method assert
       
  3641  * @param {Boolean} condition The condition to test.
       
  3642  * @param {String} message The message to display if the assertion fails.
       
  3643  * @for YUI
       
  3644  * @static
       
  3645  */
       
  3646 Y.assert = function(condition, message){
       
  3647     Y.Assert._increment();
       
  3648     if (!condition){
       
  3649         throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Assertion failed."));
       
  3650     }
       
  3651 };
       
  3652 
       
  3653 /**
       
  3654  * Forces an assertion error to occur. Shortcut for Y.Assert.fail().
       
  3655  * @method fail
       
  3656  * @param {String} message (Optional) The message to display with the failure.
       
  3657  * @for YUI
       
  3658  * @static
       
  3659  */
       
  3660 Y.fail = Y.Assert.fail; 
       
  3661 
       
  3662 Y.Test.Runner.once = Y.Test.Runner.subscribe;
       
  3663 
       
  3664 Y.Test.Runner.disableLogging = function() {
       
  3665     Y.Test.Runner._log = false;
       
  3666 };
       
  3667 
       
  3668 Y.Test.Runner.enableLogging = function() {
       
  3669     Y.Test.Runner._log = true;
       
  3670 };
       
  3671 
       
  3672 Y.Test.Runner._ignoreEmpty = true;
       
  3673 Y.Test.Runner._log = true;
       
  3674 
       
  3675 Y.Test.Runner.on = Y.Test.Runner.attach;
       
  3676 
       
  3677 //Only allow one instance of YUITest
       
  3678 if (!YUI.YUITest) {
       
  3679 
       
  3680     if (Y.config.win) {
       
  3681         Y.config.win.YUITest = YUITest;
       
  3682     }
       
  3683 
       
  3684     YUI.YUITest = Y.Test;
       
  3685 
       
  3686     
       
  3687     //Only setup the listeners once.
       
  3688     var logEvent = function(event) {
       
  3689         
       
  3690         //data variables
       
  3691         var message = "";
       
  3692         var messageType = "";
       
  3693         
       
  3694         switch(event.type){
       
  3695             case this.BEGIN_EVENT:
       
  3696                 message = "Testing began at " + (new Date()).toString() + ".";
       
  3697                 messageType = "info";
       
  3698                 break;
       
  3699                 
       
  3700             case this.COMPLETE_EVENT:
       
  3701                 message = Y.Lang.sub("Testing completed at " +
       
  3702                     (new Date()).toString() + ".\n" +
       
  3703                     "Passed:{passed} Failed:{failed} " +
       
  3704                     "Total:{total} ({ignored} ignored)",
       
  3705                     event.results);
       
  3706                 messageType = "info";
       
  3707                 break;
       
  3708                 
       
  3709             case this.TEST_FAIL_EVENT:
       
  3710                 message = event.testName + ": failed.\n" + event.error.getMessage();
       
  3711                 messageType = "fail";
       
  3712                 break;
       
  3713                 
       
  3714             case this.TEST_IGNORE_EVENT:
       
  3715                 message = event.testName + ": ignored.";
       
  3716                 messageType = "ignore";
       
  3717                 break;
       
  3718                 
       
  3719             case this.TEST_PASS_EVENT:
       
  3720                 message = event.testName + ": passed.";
       
  3721                 messageType = "pass";
       
  3722                 break;
       
  3723                 
       
  3724             case this.TEST_SUITE_BEGIN_EVENT:
       
  3725                 message = "Test suite \"" + event.testSuite.name + "\" started.";
       
  3726                 messageType = "info";
       
  3727                 break;
       
  3728                 
       
  3729             case this.TEST_SUITE_COMPLETE_EVENT:
       
  3730                 message = Y.Lang.sub("Test suite \"" +
       
  3731                     event.testSuite.name + "\" completed" + ".\n" +
       
  3732                     "Passed:{passed} Failed:{failed} " +
       
  3733                     "Total:{total} ({ignored} ignored)",
       
  3734                     event.results);
       
  3735                 messageType = "info";
       
  3736                 break;
       
  3737                 
       
  3738             case this.TEST_CASE_BEGIN_EVENT:
       
  3739                 message = "Test case \"" + event.testCase.name + "\" started.";
       
  3740                 messageType = "info";
       
  3741                 break;
       
  3742                 
       
  3743             case this.TEST_CASE_COMPLETE_EVENT:
       
  3744                 message = Y.Lang.sub("Test case \"" +
       
  3745                     event.testCase.name + "\" completed.\n" +
       
  3746                     "Passed:{passed} Failed:{failed} " +
       
  3747                     "Total:{total} ({ignored} ignored)",
       
  3748                     event.results);
       
  3749                 messageType = "info";
       
  3750                 break;
       
  3751             default:
       
  3752                 message = "Unexpected event " + event.type;
       
  3753                 messageType = "info";
       
  3754         }
       
  3755         
       
  3756         if (Y.Test.Runner._log) {
       
  3757             Y.log(message, messageType, "TestRunner");
       
  3758         }
       
  3759     };
       
  3760 
       
  3761     var i, name;
       
  3762 
       
  3763     for (i in Y.Test.Runner) {
       
  3764         name = Y.Test.Runner[i];
       
  3765         if (i.indexOf('_EVENT') > -1) {
       
  3766             Y.Test.Runner.subscribe(name, logEvent);
       
  3767         }
       
  3768     };
       
  3769 
       
  3770 } //End if for YUI.YUITest
       
  3771 
       
  3772 
       
  3773 }, '3.10.3', {"requires": ["event-simulate", "event-custom", "json-stringify"]});