[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/yuilib/3.17.2/test/ -> test.js (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1