[ Index ] |
PHP Cross Reference of Unnamed Project |
[Summary view] [Print] [Text view]
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 "<"; 518 case ">": return ">"; 519 case "\"": return """; 520 case "'": return "'"; 521 case "&": return "&"; 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"]});
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Aug 11 10:00:09 2016 | Cross-referenced by PHPXref 0.7.1 |