2 Copyright (c) 2009, Yahoo! Inc. All rights reserved.
3 Code licensed under the BSD License:
4 http://developer.yahoo.net/yui/license.txt
7 YAHOO.namespace("tool");
9 //-----------------------------------------------------------------------------
11 //-----------------------------------------------------------------------------
14 //used for autogenerating test case names
18 * Test case containing various tests to run.
19 * @param template An object containing any number of test methods, other methods,
20 * an optional name, and anything else the test case needs.
22 * @namespace YAHOO.tool
25 YAHOO.tool.TestCase = function (template /*:Object*/) {
28 * Special rules for the test case. Possible subobjects
29 * are fail, for tests that should fail, and error, for
30 * tests that should throw an error.
32 this._should /*:Object*/ = {};
34 //copy over all properties from the template to this object
35 for (var prop in template) {
36 this[prop] = template[prop];
39 //check for a valid name
40 if (!YAHOO.lang.isString(this.name)){
42 * Name for the test case.
44 this.name /*:String*/ = "testCase" + (tempId++);
50 YAHOO.tool.TestCase.prototype = {
53 * Resumes a paused test and runs the given function.
54 * @param {Function} segment (Optional) The function to run.
55 * If omitted, the test automatically passes.
59 resume : function (segment /*:Function*/) /*:Void*/ {
60 YAHOO.tool.TestRunner.resume(segment);
64 * Causes the test case to wait a specified amount of time and then
65 * continue executing the given code.
66 * @param {Function} segment (Optional) The function to run after the delay.
67 * If omitted, the TestRunner will wait until resume() is called.
68 * @param {int} delay (Optional) The number of milliseconds to wait before running
69 * the function. If omitted, defaults to zero.
73 wait : function (segment /*:Function*/, delay /*:int*/) /*:Void*/{
75 if (YAHOO.lang.isFunction(args[0])){
76 throw new YAHOO.tool.TestCase.Wait(args[0], args[1]);
78 throw new YAHOO.tool.TestCase.Wait(function(){
79 YAHOO.util.Assert.fail("Timeout: wait() called but resume() never called.");
80 }, (YAHOO.lang.isNumber(args[0]) ? args[0] : 10000));
84 //-------------------------------------------------------------------------
86 //-------------------------------------------------------------------------
89 * Function to run before each test is executed.
93 setUp : function () /*:Void*/ {
97 * Function to run after each test is executed.
101 tearDown: function () /*:Void*/ {
106 * Represents a stoppage in test execution to wait for an amount of time before
108 * @param {Function} segment A function to run when the wait is over.
109 * @param {int} delay The number of milliseconds to wait before running the code.
111 * @namespace YAHOO.tool.TestCase
115 YAHOO.tool.TestCase.Wait = function (segment /*:Function*/, delay /*:int*/) {
118 * The segment of code to run when the wait is over.
122 this.segment /*:Function*/ = (YAHOO.lang.isFunction(segment) ? segment : null);
125 * The delay before running the segment of code.
129 this.delay /*:int*/ = (YAHOO.lang.isNumber(delay) ? delay : 0);
134 YAHOO.namespace("tool");
137 //-----------------------------------------------------------------------------
139 //-----------------------------------------------------------------------------
142 * A test suite that can contain a collection of TestCase and TestSuite objects.
143 * @param {String||Object} data The name of the test suite or an object containing
144 * a name property as well as setUp and tearDown methods.
145 * @namespace YAHOO.tool
149 YAHOO.tool.TestSuite = function (data /*:String||Object*/) {
152 * The name of the test suite.
156 this.name /*:String*/ = "";
159 * Array of test suites and
162 this.items /*:Array*/ = [];
164 //initialize the properties
165 if (YAHOO.lang.isString(data)){
167 } else if (YAHOO.lang.isObject(data)){
168 YAHOO.lang.augmentObject(this, data, true);
172 if (this.name === ""){
173 this.name = YAHOO.util.Dom.generateId(null, "testSuite");
178 YAHOO.tool.TestSuite.prototype = {
181 * Adds a test suite or test case to the test suite.
182 * @param {YAHOO.tool.TestSuite||YAHOO.tool.TestCase} testObject The test suite or test case to add.
186 add : function (testObject /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
187 if (testObject instanceof YAHOO.tool.TestSuite || testObject instanceof YAHOO.tool.TestCase) {
188 this.items.push(testObject);
192 //-------------------------------------------------------------------------
194 //-------------------------------------------------------------------------
197 * Function to run before each test is executed.
201 setUp : function () /*:Void*/ {
205 * Function to run after each test is executed.
209 tearDown: function () /*:Void*/ {
213 YAHOO.namespace("tool");
218 * @namespace YAHOO.tool
219 * @requires yahoo,dom,event,logger
220 * @optional event-simulte
224 //-----------------------------------------------------------------------------
226 //-----------------------------------------------------------------------------
229 YAHOO.tool.TestRunner = (function(){
232 * A node in the test tree structure. May represent a TestSuite, TestCase, or
234 * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
239 function TestNode(testObject /*:Variant*/){
242 * The TestSuite, TestCase, or test function represented by this node.
244 * @property testObject
246 this.testObject = testObject;
249 * Pointer to this node's first child.
251 * @property firstChild
253 this.firstChild /*:TestNode*/ = null;
256 * Pointer to this node's last child.
258 * @property lastChild
260 this.lastChild = null;
263 * Pointer to this node's parent.
270 * Pointer to this node's next sibling.
277 * Test results for this test object.
281 this.results /*:Object*/ = {
289 if (testObject instanceof YAHOO.tool.TestSuite){
290 this.results.type = "testsuite";
291 this.results.name = testObject.name;
292 } else if (testObject instanceof YAHOO.tool.TestCase){
293 this.results.type = "testcase";
294 this.results.name = testObject.name;
299 TestNode.prototype = {
302 * Appends a new test object (TestSuite, TestCase, or test function name) as a child
304 * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
307 appendChild : function (testObject /*:Variant*/) /*:Void*/{
308 var node = new TestNode(testObject);
309 if (this.firstChild === null){
310 this.firstChild = this.lastChild = node;
312 this.lastChild.next = node;
313 this.lastChild = node;
321 * Runs test suites and test cases, providing events to allowing for the
322 * interpretation of test results.
323 * @namespace YAHOO.tool
327 function TestRunner(){
329 //inherit from EventProvider
330 TestRunner.superclass.constructor.apply(this,arguments);
333 * Suite on which to attach all TestSuites and TestCases to be run.
334 * @type YAHOO.tool.TestSuite
335 * @property masterSuite
339 this.masterSuite /*:YAHOO.tool.TestSuite*/ = new YAHOO.tool.TestSuite("YUI Test Results");
342 * Pointer to the current node in the test tree.
351 * Pointer to the root node in the test tree.
360 var events /*:Array*/ = [
361 this.TEST_CASE_BEGIN_EVENT,
362 this.TEST_CASE_COMPLETE_EVENT,
363 this.TEST_SUITE_BEGIN_EVENT,
364 this.TEST_SUITE_COMPLETE_EVENT,
365 this.TEST_PASS_EVENT,
366 this.TEST_FAIL_EVENT,
367 this.TEST_IGNORE_EVENT,
371 for (var i=0; i < events.length; i++){
372 this.createEvent(events[i], { scope: this });
377 YAHOO.lang.extend(TestRunner, YAHOO.util.EventProvider, {
379 //-------------------------------------------------------------------------
381 //-------------------------------------------------------------------------
384 * Fires when a test case is opened but before the first
386 * @event testcasebegin
388 TEST_CASE_BEGIN_EVENT /*:String*/ : "testcasebegin",
391 * Fires when all tests in a test case have been executed.
392 * @event testcasecomplete
394 TEST_CASE_COMPLETE_EVENT /*:String*/ : "testcasecomplete",
397 * Fires when a test suite is opened but before the first
399 * @event testsuitebegin
401 TEST_SUITE_BEGIN_EVENT /*:String*/ : "testsuitebegin",
404 * Fires when all test cases in a test suite have been
406 * @event testsuitecomplete
408 TEST_SUITE_COMPLETE_EVENT /*:String*/ : "testsuitecomplete",
411 * Fires when a test has passed.
414 TEST_PASS_EVENT /*:String*/ : "pass",
417 * Fires when a test has failed.
420 TEST_FAIL_EVENT /*:String*/ : "fail",
423 * Fires when a test has been ignored.
426 TEST_IGNORE_EVENT /*:String*/ : "ignore",
429 * Fires when all test suites and test cases have been completed.
432 COMPLETE_EVENT /*:String*/ : "complete",
435 * Fires when the run() method is called.
438 BEGIN_EVENT /*:String*/ : "begin",
440 //-------------------------------------------------------------------------
441 // Test Tree-Related Methods
442 //-------------------------------------------------------------------------
445 * Adds a test case to the test tree as a child of the specified node.
446 * @param {TestNode} parentNode The node to add the test case to as a child.
447 * @param {YAHOO.tool.TestCase} testCase The test case to add.
451 * @method _addTestCaseToTestTree
453 _addTestCaseToTestTree : function (parentNode /*:TestNode*/, testCase /*:YAHOO.tool.TestCase*/) /*:Void*/{
456 var node = parentNode.appendChild(testCase);
458 //iterate over the items in the test case
459 for (var prop in testCase){
460 if (prop.indexOf("test") === 0 && YAHOO.lang.isFunction(testCase[prop])){
461 node.appendChild(prop);
468 * Adds a test suite to the test tree as a child of the specified node.
469 * @param {TestNode} parentNode The node to add the test suite to as a child.
470 * @param {YAHOO.tool.TestSuite} testSuite The test suite to add.
474 * @method _addTestSuiteToTestTree
476 _addTestSuiteToTestTree : function (parentNode /*:TestNode*/, testSuite /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
479 var node = parentNode.appendChild(testSuite);
481 //iterate over the items in the master suite
482 for (var i=0; i < testSuite.items.length; i++){
483 if (testSuite.items[i] instanceof YAHOO.tool.TestSuite) {
484 this._addTestSuiteToTestTree(node, testSuite.items[i]);
485 } else if (testSuite.items[i] instanceof YAHOO.tool.TestCase) {
486 this._addTestCaseToTestTree(node, testSuite.items[i]);
492 * Builds the test tree based on items in the master suite. The tree is a hierarchical
493 * representation of the test suites, test cases, and test functions. The resulting tree
494 * is stored in _root and the pointer _cur is set to the root initially.
498 * @method _buildTestTree
500 _buildTestTree : function () /*:Void*/ {
502 this._root = new TestNode(this.masterSuite);
503 this._cur = this._root;
505 //iterate over the items in the master suite
506 for (var i=0; i < this.masterSuite.items.length; i++){
507 if (this.masterSuite.items[i] instanceof YAHOO.tool.TestSuite) {
508 this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
509 } else if (this.masterSuite.items[i] instanceof YAHOO.tool.TestCase) {
510 this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
516 //-------------------------------------------------------------------------
518 //-------------------------------------------------------------------------
521 * Handles the completion of a test object's tests. Tallies test results
522 * from one level up to the next.
523 * @param {TestNode} node The TestNode representing the test object.
525 * @method _handleTestObjectComplete
529 _handleTestObjectComplete : function (node /*:TestNode*/) /*:Void*/ {
530 if (YAHOO.lang.isObject(node.testObject)){
531 node.parent.results.passed += node.results.passed;
532 node.parent.results.failed += node.results.failed;
533 node.parent.results.total += node.results.total;
534 node.parent.results.ignored += node.results.ignored;
535 node.parent.results[node.testObject.name] = node.results;
537 if (node.testObject instanceof YAHOO.tool.TestSuite){
538 node.testObject.tearDown();
539 this.fireEvent(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
540 } else if (node.testObject instanceof YAHOO.tool.TestCase){
541 this.fireEvent(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
546 //-------------------------------------------------------------------------
547 // Navigation Methods
548 //-------------------------------------------------------------------------
551 * Retrieves the next node in the test tree.
552 * @return {TestNode} The next node in the test tree or null if the end is reached.
557 _next : function () /*:TestNode*/ {
559 if (this._cur.firstChild) {
560 this._cur = this._cur.firstChild;
561 } else if (this._cur.next) {
562 this._cur = this._cur.next;
564 while (this._cur && !this._cur.next && this._cur !== this._root){
565 this._handleTestObjectComplete(this._cur);
566 this._cur = this._cur.parent;
569 if (this._cur == this._root){
570 this._cur.results.type = "report";
571 this._cur.results.timestamp = (new Date()).toLocaleString();
572 this._cur.results.duration = (new Date()) - this._cur.results.duration;
573 this.fireEvent(this.COMPLETE_EVENT, { results: this._cur.results});
576 this._handleTestObjectComplete(this._cur);
577 this._cur = this._cur.next;
585 * Runs a test case or test suite, returning the results.
586 * @param {YAHOO.tool.TestCase|YAHOO.tool.TestSuite} testObject The test case or test suite to run.
587 * @return {Object} Results of the execution with properties passed, failed, and total.
592 _run : function () /*:Void*/ {
594 //flag to indicate if the TestRunner should wait before continuing
595 var shouldWait /*:Boolean*/ = false;
597 //get the next test node
598 var node = this._next();
601 var testObject = node.testObject;
603 //figure out what to do
604 if (YAHOO.lang.isObject(testObject)){
605 if (testObject instanceof YAHOO.tool.TestSuite){
606 this.fireEvent(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
608 } else if (testObject instanceof YAHOO.tool.TestCase){
609 this.fireEvent(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
612 //some environments don't support setTimeout
613 if (typeof setTimeout != "undefined"){
614 setTimeout(function(){
615 YAHOO.tool.TestRunner._run();
627 _resumeTest : function (segment /*:Function*/) /*:Void*/ {
629 //get relevant information
630 var node /*:TestNode*/ = this._cur;
631 var testName /*:String*/ = node.testObject;
632 var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
634 //cancel other waits if available
635 if (testCase.__yui_wait){
636 clearTimeout(testCase.__yui_wait);
637 delete testCase.__yui_wait;
640 //get the "should" test cases
641 var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
642 var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
644 //variable to hold whether or not the test failed
645 var failed /*:Boolean*/ = false;
646 var error /*:Error*/ = null;
652 segment.apply(testCase);
654 //if it should fail, and it got here, then it's a fail because it didn't
656 error = new YAHOO.util.ShouldFail();
658 } else if (shouldError){
659 error = new YAHOO.util.ShouldError();
663 } catch (thrown /*:Error*/){
664 if (thrown instanceof YAHOO.util.AssertionError) {
669 } else if (thrown instanceof YAHOO.tool.TestCase.Wait){
671 if (YAHOO.lang.isFunction(thrown.segment)){
672 if (YAHOO.lang.isNumber(thrown.delay)){
674 //some environments don't support setTimeout
675 if (typeof setTimeout != "undefined"){
676 testCase.__yui_wait = setTimeout(function(){
677 YAHOO.tool.TestRunner._resumeTest(thrown.segment);
680 throw new Error("Asynchronous tests not supported in this environment.");
688 //first check to see if it should error
690 error = new YAHOO.util.UnexpectedError(thrown);
693 //check to see what type of data we have
694 if (YAHOO.lang.isString(shouldError)){
696 //if it's a string, check the error message
697 if (thrown.message != shouldError){
698 error = new YAHOO.util.UnexpectedError(thrown);
701 } else if (YAHOO.lang.isFunction(shouldError)){
703 //if it's a function, see if the error is an instance of it
704 if (!(thrown instanceof shouldError)){
705 error = new YAHOO.util.UnexpectedError(thrown);
709 } else if (YAHOO.lang.isObject(shouldError)){
711 //if it's an object, check the instance and message
712 if (!(thrown instanceof shouldError.constructor) ||
713 thrown.message != shouldError.message){
714 error = new YAHOO.util.UnexpectedError(thrown);
725 //fireEvent appropriate event
727 this.fireEvent(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
729 this.fireEvent(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
736 node.parent.results[testName] = {
737 result: failed ? "fail" : "pass",
738 message: error ? error.getMessage() : "Test passed",
744 node.parent.results.failed++;
746 node.parent.results.passed++;
748 node.parent.results.total++;
750 //set timeout not supported in all environments
751 if (typeof setTimeout != "undefined"){
752 setTimeout(function(){
753 YAHOO.tool.TestRunner._run();
762 * Runs a single test based on the data provided in the node.
763 * @param {TestNode} node The TestNode representing the test to run.
769 _runTest : function (node /*:TestNode*/) /*:Void*/ {
771 //get relevant information
772 var testName /*:String*/ = node.testObject;
773 var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
774 var test /*:Function*/ = testCase[testName];
776 //get the "should" test cases
777 var shouldIgnore /*:Object*/ = (testCase._should.ignore || {})[testName];
779 //figure out if the test should be ignored or not
783 node.parent.results[testName] = {
785 message: "Test ignored",
790 node.parent.results.ignored++;
791 node.parent.results.total++;
793 this.fireEvent(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
795 //some environments don't support setTimeout
796 if (typeof setTimeout != "undefined"){
797 setTimeout(function(){
798 YAHOO.tool.TestRunner._run();
809 //now call the body of the test
810 this._resumeTest(test);
815 //-------------------------------------------------------------------------
817 //-------------------------------------------------------------------------
820 * Fires events for the TestRunner. This overrides the default fireEvent()
821 * method from EventProvider to add the type property to the data that is
822 * passed through on each event call.
823 * @param {String} type The type of event to fire.
824 * @param {Object} data (Optional) Data for the event.
829 fireEvent : function (type /*:String*/, data /*:Object*/) /*:Void*/ {
832 TestRunner.superclass.fireEvent.call(this, type, data);
835 //-------------------------------------------------------------------------
837 //-------------------------------------------------------------------------
840 * Adds a test suite or test case to the list of test objects to run.
841 * @param testObject Either a TestCase or a TestSuite that should be run.
846 add : function (testObject /*:Object*/) /*:Void*/ {
847 this.masterSuite.add(testObject);
851 * Removes all test objects from the runner.
856 clear : function () /*:Void*/ {
857 this.masterSuite.items = [];
861 * Resumes the TestRunner after wait() was called.
862 * @param {Function} segment The function to run as the rest
863 * of the haulted test.
868 resume : function (segment /*:Function*/) /*:Void*/ {
869 this._resumeTest(segment || function(){});
873 * Runs the test suite.
878 run : function (testObject /*:Object*/) /*:Void*/ {
880 //pointer to runner to avoid scope issues
881 var runner = YAHOO.tool.TestRunner;
883 //build the test tree
884 runner._buildTestTree();
886 //set when the test started
887 runner._root.results.duration = (new Date()).getTime();
889 //fire the begin event
890 runner.fireEvent(runner.BEGIN_EVENT);
897 return new TestRunner();
900 YAHOO.namespace("util");
902 //-----------------------------------------------------------------------------
904 //-----------------------------------------------------------------------------
907 * The Assert object provides functions to test JavaScript values against
908 * known and expected results. Whenever a comparison (assertion) fails,
909 * an error is thrown.
911 * @namespace YAHOO.util
915 YAHOO.util.Assert = {
917 //-------------------------------------------------------------------------
919 //-------------------------------------------------------------------------
922 * Formats a message so that it can contain the original assertion message
923 * in addition to the custom message.
924 * @param {String} customMessage The message passed in by the developer.
925 * @param {String} defaultMessage The message created by the error by default.
926 * @return {String} The final error message, containing either or both.
929 * @method _formatMessage
931 _formatMessage : function (customMessage /*:String*/, defaultMessage /*:String*/) /*:String*/ {
932 var message = customMessage;
933 if (YAHOO.lang.isString(customMessage) && customMessage.length > 0){
934 return YAHOO.lang.substitute(customMessage, { message: defaultMessage });
936 return defaultMessage;
940 //-------------------------------------------------------------------------
941 // Generic Assertion Methods
942 //-------------------------------------------------------------------------
945 * Forces an assertion error to occur.
946 * @param {String} message (Optional) The message to display with the failure.
950 fail : function (message /*:String*/) /*:Void*/ {
951 throw new YAHOO.util.AssertionError(this._formatMessage(message, "Test force-failed."));
954 //-------------------------------------------------------------------------
955 // Equality Assertion Methods
956 //-------------------------------------------------------------------------
959 * Asserts that a value is equal to another. This uses the double equals sign
960 * so type cohersion may occur.
961 * @param {Object} expected The expected value.
962 * @param {Object} actual The actual value to test.
963 * @param {String} message (Optional) The message to display if the assertion fails.
967 areEqual : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
968 if (expected != actual) {
969 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be equal."), expected, actual);
974 * Asserts that a value is not equal to another. This uses the double equals sign
975 * so type cohersion may occur.
976 * @param {Object} unexpected The unexpected value.
977 * @param {Object} actual The actual value to test.
978 * @param {String} message (Optional) The message to display if the assertion fails.
979 * @method areNotEqual
982 areNotEqual : function (unexpected /*:Object*/, actual /*:Object*/,
983 message /*:String*/) /*:Void*/ {
984 if (unexpected == actual) {
985 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be equal."), unexpected);
990 * Asserts that a value is not the same as another. This uses the triple equals sign
991 * so no type cohersion may occur.
992 * @param {Object} unexpected The unexpected value.
993 * @param {Object} actual The actual value to test.
994 * @param {String} message (Optional) The message to display if the assertion fails.
998 areNotSame : function (unexpected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
999 if (unexpected === actual) {
1000 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be the same."), unexpected);
1005 * Asserts that a value is the same as another. This uses the triple equals sign
1006 * so no type cohersion may occur.
1007 * @param {Object} expected The expected value.
1008 * @param {Object} actual The actual value to test.
1009 * @param {String} message (Optional) The message to display if the assertion fails.
1013 areSame : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1014 if (expected !== actual) {
1015 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be the same."), expected, actual);
1019 //-------------------------------------------------------------------------
1020 // Boolean Assertion Methods
1021 //-------------------------------------------------------------------------
1024 * Asserts that a value is false. This uses the triple equals sign
1025 * so no type cohersion may occur.
1026 * @param {Object} actual The actual value to test.
1027 * @param {String} message (Optional) The message to display if the assertion fails.
1031 isFalse : function (actual /*:Boolean*/, message /*:String*/) {
1032 if (false !== actual) {
1033 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be false."), false, actual);
1038 * Asserts that a value is true. This uses the triple equals sign
1039 * so no type cohersion may occur.
1040 * @param {Object} actual The actual value to test.
1041 * @param {String} message (Optional) The message to display if the assertion fails.
1045 isTrue : function (actual /*:Boolean*/, message /*:String*/) /*:Void*/ {
1046 if (true !== actual) {
1047 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be true."), true, actual);
1052 //-------------------------------------------------------------------------
1053 // Special Value Assertion Methods
1054 //-------------------------------------------------------------------------
1057 * Asserts that a value is not a number.
1058 * @param {Object} actual The value to test.
1059 * @param {String} message (Optional) The message to display if the assertion fails.
1063 isNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1064 if (!isNaN(actual)){
1065 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be NaN."), NaN, actual);
1070 * Asserts that a value is not the special NaN value.
1071 * @param {Object} actual The value to test.
1072 * @param {String} message (Optional) The message to display if the assertion fails.
1076 isNotNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1078 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be NaN."), NaN);
1083 * Asserts that a value is not null. This uses the triple equals sign
1084 * so no type cohersion may occur.
1085 * @param {Object} actual The actual value to test.
1086 * @param {String} message (Optional) The message to display if the assertion fails.
1090 isNotNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1091 if (YAHOO.lang.isNull(actual)) {
1092 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be null."), null);
1097 * Asserts that a value is not undefined. This uses the triple equals sign
1098 * so no type cohersion may occur.
1099 * @param {Object} actual The actual value to test.
1100 * @param {String} message (Optional) The message to display if the assertion fails.
1101 * @method isNotUndefined
1104 isNotUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1105 if (YAHOO.lang.isUndefined(actual)) {
1106 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should not be undefined."), undefined);
1111 * Asserts that a value is null. This uses the triple equals sign
1112 * so no type cohersion may occur.
1113 * @param {Object} actual The actual value to test.
1114 * @param {String} message (Optional) The message to display if the assertion fails.
1118 isNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1119 if (!YAHOO.lang.isNull(actual)) {
1120 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be null."), null, actual);
1125 * Asserts that a value is undefined. This uses the triple equals sign
1126 * so no type cohersion may occur.
1127 * @param {Object} actual The actual value to test.
1128 * @param {String} message (Optional) The message to display if the assertion fails.
1129 * @method isUndefined
1132 isUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1133 if (!YAHOO.lang.isUndefined(actual)) {
1134 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be undefined."), undefined, actual);
1138 //--------------------------------------------------------------------------
1139 // Instance Assertion Methods
1140 //--------------------------------------------------------------------------
1143 * Asserts that a value is an array.
1144 * @param {Object} actual The value to test.
1145 * @param {String} message (Optional) The message to display if the assertion fails.
1149 isArray : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1150 if (!YAHOO.lang.isArray(actual)){
1151 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an array."), actual);
1156 * Asserts that a value is a Boolean.
1157 * @param {Object} actual The value to test.
1158 * @param {String} message (Optional) The message to display if the assertion fails.
1162 isBoolean : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1163 if (!YAHOO.lang.isBoolean(actual)){
1164 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a Boolean."), actual);
1169 * Asserts that a value is a function.
1170 * @param {Object} actual The value to test.
1171 * @param {String} message (Optional) The message to display if the assertion fails.
1172 * @method isFunction
1175 isFunction : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1176 if (!YAHOO.lang.isFunction(actual)){
1177 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a function."), actual);
1182 * Asserts that a value is an instance of a particular object. This may return
1183 * incorrect results when comparing objects from one frame to constructors in
1184 * another frame. For best results, don't use in a cross-frame manner.
1185 * @param {Function} expected The function that the object should be an instance of.
1186 * @param {Object} actual The object to test.
1187 * @param {String} message (Optional) The message to display if the assertion fails.
1188 * @method isInstanceOf
1191 isInstanceOf : function (expected /*:Function*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1192 if (!(actual instanceof expected)){
1193 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1198 * Asserts that a value is a number.
1199 * @param {Object} actual The value to test.
1200 * @param {String} message (Optional) The message to display if the assertion fails.
1204 isNumber : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1205 if (!YAHOO.lang.isNumber(actual)){
1206 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a number."), actual);
1211 * Asserts that a value is an object.
1212 * @param {Object} actual The value to test.
1213 * @param {String} message (Optional) The message to display if the assertion fails.
1217 isObject : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1218 if (!YAHOO.lang.isObject(actual)){
1219 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an object."), actual);
1224 * Asserts that a value is a string.
1225 * @param {Object} actual The value to test.
1226 * @param {String} message (Optional) The message to display if the assertion fails.
1230 isString : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1231 if (!YAHOO.lang.isString(actual)){
1232 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a string."), actual);
1237 * Asserts that a value is of a particular type.
1238 * @param {String} expectedType The expected type of the variable.
1239 * @param {Object} actualValue The actual value to test.
1240 * @param {String} message (Optional) The message to display if the assertion fails.
1244 isTypeOf : function (expected /*:String*/, actual /*:Object*/, message /*:String*/) /*:Void*/{
1245 if (typeof actual != expected){
1246 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be of type " + expected + "."), expected, typeof actual);
1251 //-----------------------------------------------------------------------------
1253 //-----------------------------------------------------------------------------
1256 * AssertionError is thrown whenever an assertion fails. It provides methods
1257 * to more easily get at error information and also provides a base class
1258 * from which more specific assertion errors can be derived.
1260 * @param {String} message The message to display when the error occurs.
1261 * @namespace YAHOO.util
1262 * @class AssertionError
1266 YAHOO.util.AssertionError = function (message /*:String*/){
1269 //arguments.callee.superclass.constructor.call(this, message);
1272 * Error message. Must be duplicated to ensure browser receives it.
1276 this.message /*:String*/ = message;
1279 * The name of the error that occurred.
1283 this.name /*:String*/ = "AssertionError";
1287 YAHOO.lang.extend(YAHOO.util.AssertionError, Object, {
1290 * Returns a fully formatted error for an assertion failure. This should
1291 * be overridden by all subclasses to provide specific information.
1292 * @method getMessage
1293 * @return {String} A string describing the error.
1295 getMessage : function () /*:String*/ {
1296 return this.message;
1300 * Returns a string representation of the error.
1302 * @return {String} A string representation of the error.
1304 toString : function () /*:String*/ {
1305 return this.name + ": " + this.getMessage();
1311 * ComparisonFailure is subclass of AssertionError that is thrown whenever
1312 * a comparison between two values fails. It provides mechanisms to retrieve
1313 * both the expected and actual value.
1315 * @param {String} message The message to display when the error occurs.
1316 * @param {Object} expected The expected value.
1317 * @param {Object} actual The actual value that caused the assertion to fail.
1318 * @namespace YAHOO.util
1319 * @extends YAHOO.util.AssertionError
1320 * @class ComparisonFailure
1323 YAHOO.util.ComparisonFailure = function (message /*:String*/, expected /*:Object*/, actual /*:Object*/){
1326 YAHOO.util.AssertionError.call(this, message);
1329 * The expected value.
1331 * @property expected
1333 this.expected /*:Object*/ = expected;
1340 this.actual /*:Object*/ = actual;
1343 * The name of the error that occurred.
1347 this.name /*:String*/ = "ComparisonFailure";
1352 YAHOO.lang.extend(YAHOO.util.ComparisonFailure, YAHOO.util.AssertionError, {
1355 * Returns a fully formatted error for an assertion failure. This message
1356 * provides information about the expected and actual values.
1358 * @return {String} A string describing the error.
1360 getMessage : function () /*:String*/ {
1361 return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
1362 "\nActual:" + this.actual + " (" + (typeof this.actual) + ")";
1368 * UnexpectedValue is subclass of AssertionError that is thrown whenever
1369 * a value was unexpected in its scope. This typically means that a test
1370 * was performed to determine that a value was *not* equal to a certain
1373 * @param {String} message The message to display when the error occurs.
1374 * @param {Object} unexpected The unexpected value.
1375 * @namespace YAHOO.util
1376 * @extends YAHOO.util.AssertionError
1377 * @class UnexpectedValue
1380 YAHOO.util.UnexpectedValue = function (message /*:String*/, unexpected /*:Object*/){
1383 YAHOO.util.AssertionError.call(this, message);
1386 * The unexpected value.
1388 * @property unexpected
1390 this.unexpected /*:Object*/ = unexpected;
1393 * The name of the error that occurred.
1397 this.name /*:String*/ = "UnexpectedValue";
1402 YAHOO.lang.extend(YAHOO.util.UnexpectedValue, YAHOO.util.AssertionError, {
1405 * Returns a fully formatted error for an assertion failure. The message
1406 * contains information about the unexpected value that was encountered.
1407 * @method getMessage
1408 * @return {String} A string describing the error.
1410 getMessage : function () /*:String*/ {
1411 return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1417 * ShouldFail is subclass of AssertionError that is thrown whenever
1418 * a test was expected to fail but did not.
1420 * @param {String} message The message to display when the error occurs.
1421 * @namespace YAHOO.util
1422 * @extends YAHOO.util.AssertionError
1426 YAHOO.util.ShouldFail = function (message /*:String*/){
1429 YAHOO.util.AssertionError.call(this, message || "This test should fail but didn't.");
1432 * The name of the error that occurred.
1436 this.name /*:String*/ = "ShouldFail";
1441 YAHOO.lang.extend(YAHOO.util.ShouldFail, YAHOO.util.AssertionError);
1444 * ShouldError is subclass of AssertionError that is thrown whenever
1445 * a test is expected to throw an error but doesn't.
1447 * @param {String} message The message to display when the error occurs.
1448 * @namespace YAHOO.util
1449 * @extends YAHOO.util.AssertionError
1450 * @class ShouldError
1453 YAHOO.util.ShouldError = function (message /*:String*/){
1456 YAHOO.util.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
1459 * The name of the error that occurred.
1463 this.name /*:String*/ = "ShouldError";
1468 YAHOO.lang.extend(YAHOO.util.ShouldError, YAHOO.util.AssertionError);
1471 * UnexpectedError is subclass of AssertionError that is thrown whenever
1472 * an error occurs within the course of a test and the test was not expected
1473 * to throw an error.
1475 * @param {Error} cause The unexpected error that caused this error to be
1477 * @namespace YAHOO.util
1478 * @extends YAHOO.util.AssertionError
1479 * @class UnexpectedError
1482 YAHOO.util.UnexpectedError = function (cause /*:Object*/){
1485 YAHOO.util.AssertionError.call(this, "Unexpected error: " + cause.message);
1488 * The unexpected error that occurred.
1492 this.cause /*:Error*/ = cause;
1495 * The name of the error that occurred.
1499 this.name /*:String*/ = "UnexpectedError";
1502 * Stack information for the error (if provided).
1506 this.stack /*:String*/ = cause.stack;
1511 YAHOO.lang.extend(YAHOO.util.UnexpectedError, YAHOO.util.AssertionError);
1512 //-----------------------------------------------------------------------------
1513 // ArrayAssert object
1514 //-----------------------------------------------------------------------------
1517 * The ArrayAssert object provides functions to test JavaScript array objects
1518 * for a variety of cases.
1520 * @namespace YAHOO.util
1521 * @class ArrayAssert
1525 YAHOO.util.ArrayAssert = {
1528 * Asserts that a value is present in an array. This uses the triple equals
1529 * sign so no type cohersion may occur.
1530 * @param {Object} needle The value that is expected in the array.
1531 * @param {Array} haystack An array of values.
1532 * @param {String} message (Optional) The message to display if the assertion fails.
1536 contains : function (needle /*:Object*/, haystack /*:Array*/,
1537 message /*:String*/) /*:Void*/ {
1539 var found /*:Boolean*/ = false;
1540 var Assert = YAHOO.util.Assert;
1542 //begin checking values
1543 for (var i=0; i < haystack.length && !found; i++){
1544 if (haystack[i] === needle) {
1550 Assert.fail(Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1555 * Asserts that a set of values are present in an array. This uses the triple equals
1556 * sign so no type cohersion may occur. For this assertion to pass, all values must
1558 * @param {Object[]} needles An array of values that are expected in the array.
1559 * @param {Array} haystack An array of values to check.
1560 * @param {String} message (Optional) The message to display if the assertion fails.
1561 * @method containsItems
1564 containsItems : function (needles /*:Object[]*/, haystack /*:Array*/,
1565 message /*:String*/) /*:Void*/ {
1567 //begin checking values
1568 for (var i=0; i < needles.length; i++){
1569 this.contains(needles[i], haystack, message);
1574 * Asserts that a value matching some condition is present in an array. This uses
1575 * a function to determine a match.
1576 * @param {Function} matcher A function that returns true if the items matches or false if not.
1577 * @param {Array} haystack An array of values.
1578 * @param {String} message (Optional) The message to display if the assertion fails.
1579 * @method containsMatch
1582 containsMatch : function (matcher /*:Function*/, haystack /*:Array*/,
1583 message /*:String*/) /*:Void*/ {
1585 //check for valid matcher
1586 if (typeof matcher != "function"){
1587 throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1590 var found /*:Boolean*/ = false;
1591 var Assert = YAHOO.util.Assert;
1593 //begin checking values
1594 for (var i=0; i < haystack.length && !found; i++){
1595 if (matcher(haystack[i])) {
1601 Assert.fail(Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1606 * Asserts that a value is not present in an array. This uses the triple equals
1607 * sign so no type cohersion may occur.
1608 * @param {Object} needle The value that is expected in the array.
1609 * @param {Array} haystack An array of values.
1610 * @param {String} message (Optional) The message to display if the assertion fails.
1611 * @method doesNotContain
1614 doesNotContain : function (needle /*:Object*/, haystack /*:Array*/,
1615 message /*:String*/) /*:Void*/ {
1617 var found /*:Boolean*/ = false;
1618 var Assert = YAHOO.util.Assert;
1620 //begin checking values
1621 for (var i=0; i < haystack.length && !found; i++){
1622 if (haystack[i] === needle) {
1628 Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1633 * Asserts that a set of values are not present in an array. This uses the triple equals
1634 * sign so no type cohersion may occur. For this assertion to pass, all values must
1636 * @param {Object[]} needles An array of values that are not expected in the array.
1637 * @param {Array} haystack An array of values to check.
1638 * @param {String} message (Optional) The message to display if the assertion fails.
1639 * @method doesNotContainItems
1642 doesNotContainItems : function (needles /*:Object[]*/, haystack /*:Array*/,
1643 message /*:String*/) /*:Void*/ {
1645 for (var i=0; i < needles.length; i++){
1646 this.doesNotContain(needles[i], haystack, message);
1652 * Asserts that no values matching a condition are present in an array. This uses
1653 * a function to determine a match.
1654 * @param {Function} matcher A function that returns true if the items matches or false if not.
1655 * @param {Array} haystack An array of values.
1656 * @param {String} message (Optional) The message to display if the assertion fails.
1657 * @method doesNotContainMatch
1660 doesNotContainMatch : function (matcher /*:Function*/, haystack /*:Array*/,
1661 message /*:String*/) /*:Void*/ {
1663 //check for valid matcher
1664 if (typeof matcher != "function"){
1665 throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1668 var found /*:Boolean*/ = false;
1669 var Assert = YAHOO.util.Assert;
1671 //begin checking values
1672 for (var i=0; i < haystack.length && !found; i++){
1673 if (matcher(haystack[i])) {
1679 Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1684 * Asserts that the given value is contained in an array at the specified index.
1685 * This uses the triple equals sign so no type cohersion will occur.
1686 * @param {Object} needle The value to look for.
1687 * @param {Array} haystack The array to search in.
1688 * @param {int} index The index at which the value should exist.
1689 * @param {String} message (Optional) The message to display if the assertion fails.
1693 indexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1695 //try to find the value in the array
1696 for (var i=0; i < haystack.length; i++){
1697 if (haystack[i] === needle){
1698 YAHOO.util.Assert.areEqual(index, i, message || "Value exists at index " + i + " but should be at index " + index + ".");
1703 var Assert = YAHOO.util.Assert;
1705 //if it makes it here, it wasn't found at all
1706 Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1710 * Asserts that the values in an array are equal, and in the same position,
1711 * as values in another array. This uses the double equals sign
1712 * so type cohersion may occur. Note that the array objects themselves
1713 * need not be the same for this test to pass.
1714 * @param {Array} expected An array of the expected values.
1715 * @param {Array} actual Any array of the actual values.
1716 * @param {String} message (Optional) The message to display if the assertion fails.
1717 * @method itemsAreEqual
1720 itemsAreEqual : function (expected /*:Array*/, actual /*:Array*/,
1721 message /*:String*/) /*:Void*/ {
1723 //one may be longer than the other, so get the maximum length
1724 var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1725 var Assert = YAHOO.util.Assert;
1727 //begin checking values
1728 for (var i=0; i < len; i++){
1729 Assert.areEqual(expected[i], actual[i],
1730 Assert._formatMessage(message, "Values in position " + i + " are not equal."));
1735 * Asserts that the values in an array are equivalent, and in the same position,
1736 * as values in another array. This uses a function to determine if the values
1737 * are equivalent. Note that the array objects themselves
1738 * need not be the same for this test to pass.
1739 * @param {Array} expected An array of the expected values.
1740 * @param {Array} actual Any array of the actual values.
1741 * @param {Function} comparator A function that returns true if the values are equivalent
1743 * @param {String} message (Optional) The message to display if the assertion fails.
1745 * @method itemsAreEquivalent
1748 itemsAreEquivalent : function (expected /*:Array*/, actual /*:Array*/,
1749 comparator /*:Function*/, message /*:String*/) /*:Void*/ {
1751 //make sure the comparator is valid
1752 if (typeof comparator != "function"){
1753 throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1756 //one may be longer than the other, so get the maximum length
1757 var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1759 //begin checking values
1760 for (var i=0; i < len; i++){
1761 if (!comparator(expected[i], actual[i])){
1762 throw new YAHOO.util.ComparisonFailure(YAHOO.util.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1768 * Asserts that an array is empty.
1769 * @param {Array} actual The array to test.
1770 * @param {String} message (Optional) The message to display if the assertion fails.
1774 isEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {
1775 if (actual.length > 0){
1776 var Assert = YAHOO.util.Assert;
1777 Assert.fail(Assert._formatMessage(message, "Array should be empty."));
1782 * Asserts that an array is not empty.
1783 * @param {Array} actual The array to test.
1784 * @param {String} message (Optional) The message to display if the assertion fails.
1785 * @method isNotEmpty
1788 isNotEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {
1789 if (actual.length === 0){
1790 var Assert = YAHOO.util.Assert;
1791 Assert.fail(Assert._formatMessage(message, "Array should not be empty."));
1796 * Asserts that the values in an array are the same, and in the same position,
1797 * as values in another array. This uses the triple equals sign
1798 * so no type cohersion will occur. Note that the array objects themselves
1799 * need not be the same for this test to pass.
1800 * @param {Array} expected An array of the expected values.
1801 * @param {Array} actual Any array of the actual values.
1802 * @param {String} message (Optional) The message to display if the assertion fails.
1803 * @method itemsAreSame
1806 itemsAreSame : function (expected /*:Array*/, actual /*:Array*/,
1807 message /*:String*/) /*:Void*/ {
1809 //one may be longer than the other, so get the maximum length
1810 var len /*:int*/ = Math.max(expected.length, actual.length || 0);
1811 var Assert = YAHOO.util.Assert;
1813 //begin checking values
1814 for (var i=0; i < len; i++){
1815 Assert.areSame(expected[i], actual[i],
1816 Assert._formatMessage(message, "Values in position " + i + " are not the same."));
1821 * Asserts that the given value is contained in an array at the specified index,
1822 * starting from the back of the array.
1823 * This uses the triple equals sign so no type cohersion will occur.
1824 * @param {Object} needle The value to look for.
1825 * @param {Array} haystack The array to search in.
1826 * @param {int} index The index at which the value should exist.
1827 * @param {String} message (Optional) The message to display if the assertion fails.
1828 * @method lastIndexOf
1831 lastIndexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1833 var Assert = YAHOO.util.Assert;
1835 //try to find the value in the array
1836 for (var i=haystack.length; i >= 0; i--){
1837 if (haystack[i] === needle){
1838 Assert.areEqual(index, i, Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1843 //if it makes it here, it wasn't found at all
1844 Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array."));
1848 YAHOO.namespace("util");
1851 //-----------------------------------------------------------------------------
1852 // ObjectAssert object
1853 //-----------------------------------------------------------------------------
1856 * The ObjectAssert object provides functions to test JavaScript objects
1857 * for a variety of cases.
1859 * @namespace YAHOO.util
1860 * @class ObjectAssert
1863 YAHOO.util.ObjectAssert = {
1866 * Asserts that all properties in the object exist in another object.
1867 * @param {Object} expected An object with the expected properties.
1868 * @param {Object} actual An object with the actual properties.
1869 * @param {String} message (Optional) The message to display if the assertion fails.
1870 * @method propertiesAreEqual
1873 propertiesAreEqual : function (expected /*:Object*/, actual /*:Object*/,
1874 message /*:String*/) /*:Void*/ {
1876 var Assert = YAHOO.util.Assert;
1878 //get all properties in the object
1879 var properties /*:Array*/ = [];
1880 for (var property in expected){
1881 properties.push(property);
1884 //see if the properties are in the expected object
1885 for (var i=0; i < properties.length; i++){
1886 Assert.isNotUndefined(actual[properties[i]],
1887 Assert._formatMessage(message, "Property '" + properties[i] + "' expected."));
1893 * Asserts that an object has a property with the given name.
1894 * @param {String} propertyName The name of the property to test.
1895 * @param {Object} object The object to search.
1896 * @param {String} message (Optional) The message to display if the assertion fails.
1897 * @method hasProperty
1900 hasProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1901 if (!(propertyName in object)){
1902 var Assert = YAHOO.util.Assert;
1903 Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
1908 * Asserts that a property with the given name exists on an object instance (not on its prototype).
1909 * @param {String} propertyName The name of the property to test.
1910 * @param {Object} object The object to search.
1911 * @param {String} message (Optional) The message to display if the assertion fails.
1912 * @method hasProperty
1915 hasOwnProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1916 if (!YAHOO.lang.hasOwnProperty(object, propertyName)){
1917 var Assert = YAHOO.util.Assert;
1918 Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1922 //-----------------------------------------------------------------------------
1923 // DateAssert object
1924 //-----------------------------------------------------------------------------
1927 * The DateAssert object provides functions to test JavaScript Date objects
1928 * for a variety of cases.
1930 * @namespace YAHOO.util
1935 YAHOO.util.DateAssert = {
1938 * Asserts that a date's month, day, and year are equal to another date's.
1939 * @param {Date} expected The expected date.
1940 * @param {Date} actual The actual date to test.
1941 * @param {String} message (Optional) The message to display if the assertion fails.
1942 * @method datesAreEqual
1945 datesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1946 if (expected instanceof Date && actual instanceof Date){
1947 var Assert = YAHOO.util.Assert;
1948 Assert.areEqual(expected.getFullYear(), actual.getFullYear(), Assert._formatMessage(message, "Years should be equal."));
1949 Assert.areEqual(expected.getMonth(), actual.getMonth(), Assert._formatMessage(message, "Months should be equal."));
1950 Assert.areEqual(expected.getDate(), actual.getDate(), Assert._formatMessage(message, "Day of month should be equal."));
1952 throw new TypeError("DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
1957 * Asserts that a date's hour, minutes, and seconds are equal to another date's.
1958 * @param {Date} expected The expected date.
1959 * @param {Date} actual The actual date to test.
1960 * @param {String} message (Optional) The message to display if the assertion fails.
1961 * @method timesAreEqual
1964 timesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1965 if (expected instanceof Date && actual instanceof Date){
1966 var Assert = YAHOO.util.Assert;
1967 Assert.areEqual(expected.getHours(), actual.getHours(), Assert._formatMessage(message, "Hours should be equal."));
1968 Assert.areEqual(expected.getMinutes(), actual.getMinutes(), Assert._formatMessage(message, "Minutes should be equal."));
1969 Assert.areEqual(expected.getSeconds(), actual.getSeconds(), Assert._formatMessage(message, "Seconds should be equal."));
1971 throw new TypeError("DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
1976 YAHOO.namespace("tool");
1978 //-----------------------------------------------------------------------------
1979 // TestManager object
1980 //-----------------------------------------------------------------------------
1983 * Runs pages containing test suite definitions.
1984 * @namespace YAHOO.tool
1985 * @class TestManager
1988 YAHOO.tool.TestManager = {
1991 * Constant for the testpagebegin custom event
1992 * @property TEST_PAGE_BEGIN_EVENT
1997 TEST_PAGE_BEGIN_EVENT /*:String*/ : "testpagebegin",
2000 * Constant for the testpagecomplete custom event
2001 * @property TEST_PAGE_COMPLETE_EVENT
2006 TEST_PAGE_COMPLETE_EVENT /*:String*/ : "testpagecomplete",
2009 * Constant for the testmanagerbegin custom event
2010 * @property TEST_MANAGER_BEGIN_EVENT
2015 TEST_MANAGER_BEGIN_EVENT /*:String*/ : "testmanagerbegin",
2018 * Constant for the testmanagercomplete custom event
2019 * @property TEST_MANAGER_COMPLETE_EVENT
2024 TEST_MANAGER_COMPLETE_EVENT /*:String*/ : "testmanagercomplete",
2026 //-------------------------------------------------------------------------
2027 // Private Properties
2028 //-------------------------------------------------------------------------
2032 * The URL of the page currently being executed.
2035 * @property _curPage
2038 _curPage /*:String*/ : null,
2041 * The frame used to load and run tests.
2047 _frame /*:Window*/ : null,
2050 * The logger used to output results from the various tests.
2051 * @type YAHOO.tool.TestLogger
2059 * The timeout ID for the next iteration through the tests.
2062 * @property _timeoutId
2065 _timeoutId /*:int*/ : 0,
2068 * Array of pages to load.
2074 _pages /*:String[]*/ : [],
2077 * Aggregated results
2080 * @property _results
2085 //-------------------------------------------------------------------------
2087 //-------------------------------------------------------------------------
2090 * Handles TestRunner.COMPLETE_EVENT, storing the results and beginning
2092 * @param {Object} data Data about the event.
2097 _handleTestRunnerComplete : function (data /*:Object*/) /*:Void*/ {
2099 this.fireEvent(this.TEST_PAGE_COMPLETE_EVENT, {
2100 page: this._curPage,
2101 results: data.results
2105 //this._results[this.curPage] = data.results;
2108 this._processResults(this._curPage, data.results);
2110 this._logger.clearTestRunner();
2112 //if there's more to do, set a timeout to begin again
2113 if (this._pages.length){
2114 this._timeoutId = setTimeout(function(){
2115 YAHOO.tool.TestManager._run();
2118 this.fireEvent(this.TEST_MANAGER_COMPLETE_EVENT, this._results);
2123 * Processes the results of a test page run, outputting log messages
2129 _processResults : function (page /*:String*/, results /*:Object*/) /*:Void*/ {
2131 var r = this._results;
2133 r.passed += results.passed;
2134 r.failed += results.failed;
2135 r.ignored += results.ignored;
2136 r.total += results.total;
2137 r.duration += results.duration;
2139 if (results.failed){
2140 r.failedPages.push(page);
2142 r.passedPages.push(page);
2145 results.name = page;
2146 results.type = "page";
2152 * Loads the next test page into the iframe.
2157 _run : function () /*:Void*/ {
2159 //set the current page
2160 this._curPage = this._pages.shift();
2162 this.fireEvent(this.TEST_PAGE_BEGIN_EVENT, this._curPage);
2164 //load the frame - destroy history in case there are other iframes that
2166 this._frame.location.replace(this._curPage);
2170 //-------------------------------------------------------------------------
2172 //-------------------------------------------------------------------------
2175 * Signals that a test page has been loaded. This should be called from
2176 * within the test page itself to notify the TestManager that it is ready.
2180 load : function () /*:Void*/ {
2181 if (parent.YAHOO.tool.TestManager !== this){
2182 parent.YAHOO.tool.TestManager.load();
2186 //assign event handling
2187 var TestRunner = this._frame.YAHOO.tool.TestRunner;
2189 this._logger.setTestRunner(TestRunner);
2190 TestRunner.subscribe(TestRunner.COMPLETE_EVENT, this._handleTestRunnerComplete, this, true);
2199 * Sets the pages to be loaded.
2200 * @param {String[]} pages An array of URLs to load.
2204 setPages : function (pages /*:String[]*/) /*:Void*/ {
2205 this._pages = pages;
2209 * Begins the process of running the tests.
2213 start : function () /*:Void*/ {
2215 if (!this._initialized) {
2218 * Fires when loading a test page
2219 * @event testpagebegin
2220 * @param curPage {string} the page being loaded
2223 this.createEvent(this.TEST_PAGE_BEGIN_EVENT);
2226 * Fires when a test page is complete
2227 * @event testpagecomplete
2228 * @param obj {page: string, results: object} the name of the
2229 * page that was loaded, and the test suite results
2232 this.createEvent(this.TEST_PAGE_COMPLETE_EVENT);
2235 * Fires when the test manager starts running all test pages
2236 * @event testmanagerbegin
2239 this.createEvent(this.TEST_MANAGER_BEGIN_EVENT);
2242 * Fires when the test manager finishes running all test pages. External
2243 * test runners should subscribe to this event in order to get the
2244 * aggregated test results.
2245 * @event testmanagercomplete
2246 * @param obj { pages_passed: int, pages_failed: int, tests_passed: int
2247 * tests_failed: int, passed: string[], failed: string[],
2248 * page_results: {} }
2251 this.createEvent(this.TEST_MANAGER_COMPLETE_EVENT);
2253 //create iframe if not already available
2255 var frame /*:HTMLElement*/ = document.createElement("iframe");
2256 frame.style.visibility = "hidden";
2257 frame.style.position = "absolute";
2258 document.body.appendChild(frame);
2259 this._frame = frame.contentWindow || frame.contentDocument.parentWindow;
2262 //create test logger if not already available
2264 this._logger = new YAHOO.tool.TestLogger();
2267 this._initialized = true;
2271 // reset the results cache
2279 name: "YUI Test Results",
2284 // number of pages that pass
2286 // number of pages that fail
2288 // total number of tests passed
2290 // total number of tests failed
2292 // array of pages that passed
2294 // array of pages that failed
2296 // map of full results for each page
2300 this.fireEvent(this.TEST_MANAGER_BEGIN_EVENT, null);
2306 * Stops the execution of tests.
2310 stop : function () /*:Void*/ {
2311 clearTimeout(this._timeoutId);
2316 YAHOO.lang.augmentObject(YAHOO.tool.TestManager, YAHOO.util.EventProvider.prototype);
2318 YAHOO.namespace("tool");
2320 //-----------------------------------------------------------------------------
2321 // TestLogger object
2322 //-----------------------------------------------------------------------------
2325 * Displays test execution progress and results, providing filters based on
2326 * different key events.
2327 * @namespace YAHOO.tool
2330 * @param {HTMLElement} element (Optional) The element to create the logger in.
2331 * @param {Object} config (Optional) Configuration options for the logger.
2333 YAHOO.tool.TestLogger = function (element, config) {
2334 YAHOO.tool.TestLogger.superclass.constructor.call(this, element, config);
2338 YAHOO.lang.extend(YAHOO.tool.TestLogger, YAHOO.widget.LogReader, {
2340 footerEnabled : true,
2341 newestOnTop : false,
2344 * Formats message string to HTML for output to console.
2347 * @param oLogMsg {Object} Log message object.
2348 * @return {String} HTML-formatted message for output to console.
2350 formatMsg : function(message /*:Object*/) {
2352 var category /*:String*/ = message.category;
2353 var text /*:String*/ = this.html2Text(message.msg);
2355 return "<pre><p><span class=\"" + category + "\">" + category.toUpperCase() + "</span> " + text + "</p></pre>";
2359 //-------------------------------------------------------------------------
2361 //-------------------------------------------------------------------------
2364 * Initializes the logger.
2367 init : function () {
2369 //attach to any available TestRunner
2370 if (YAHOO.tool.TestRunner){
2371 this.setTestRunner(YAHOO.tool.TestRunner);
2374 //hide useless sources
2375 this.hideSource("global");
2376 this.hideSource("LogReader");
2378 //hide useless message categories
2379 this.hideCategory("warn");
2380 this.hideCategory("window");
2381 this.hideCategory("time");
2384 this.clearConsole();
2388 * Clears the reference to the TestRunner from previous operations. This
2389 * unsubscribes all events and removes the object reference.
2393 clearTestRunner : function () /*:Void*/ {
2395 this._runner.unsubscribeAll();
2396 this._runner = null;
2401 * Sets the source test runner that the logger should monitor.
2402 * @param {YAHOO.tool.TestRunner} testRunner The TestRunner to observe.
2406 setTestRunner : function (testRunner /*:YAHOO.tool.TestRunner*/) /*:Void*/ {
2409 this.clearTestRunner();
2412 this._runner = testRunner;
2414 //setup event _handlers
2415 testRunner.subscribe(testRunner.TEST_PASS_EVENT, this._handleTestRunnerEvent, this, true);
2416 testRunner.subscribe(testRunner.TEST_FAIL_EVENT, this._handleTestRunnerEvent, this, true);
2417 testRunner.subscribe(testRunner.TEST_IGNORE_EVENT, this._handleTestRunnerEvent, this, true);
2418 testRunner.subscribe(testRunner.BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2419 testRunner.subscribe(testRunner.COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2420 testRunner.subscribe(testRunner.TEST_SUITE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2421 testRunner.subscribe(testRunner.TEST_SUITE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2422 testRunner.subscribe(testRunner.TEST_CASE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
2423 testRunner.subscribe(testRunner.TEST_CASE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
2426 //-------------------------------------------------------------------------
2428 //-------------------------------------------------------------------------
2431 * Handles all TestRunner events, outputting appropriate data into the console.
2432 * @param {Object} data The event data object.
2436 _handleTestRunnerEvent : function (data /*:Object*/) /*:Void*/ {
2438 //shortcut variables
2439 var TestRunner /*:Object*/ = YAHOO.tool.TestRunner;
2442 var message /*:String*/ = "";
2443 var messageType /*:String*/ = "";
2446 case TestRunner.BEGIN_EVENT:
2447 message = "Testing began at " + (new Date()).toString() + ".";
2448 messageType = "info";
2451 case TestRunner.COMPLETE_EVENT:
2452 message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" +
2453 data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2454 messageType = "info";
2457 case TestRunner.TEST_FAIL_EVENT:
2458 message = data.testName + ": " + data.error.getMessage();
2459 messageType = "fail";
2462 case TestRunner.TEST_IGNORE_EVENT:
2463 message = data.testName + ": ignored.";
2464 messageType = "ignore";
2467 case TestRunner.TEST_PASS_EVENT:
2468 message = data.testName + ": passed.";
2469 messageType = "pass";
2472 case TestRunner.TEST_SUITE_BEGIN_EVENT:
2473 message = "Test suite \"" + data.testSuite.name + "\" started.";
2474 messageType = "info";
2477 case TestRunner.TEST_SUITE_COMPLETE_EVENT:
2478 message = "Test suite \"" + data.testSuite.name + "\" completed.\nPassed:" +
2479 data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2480 messageType = "info";
2483 case TestRunner.TEST_CASE_BEGIN_EVENT:
2484 message = "Test case \"" + data.testCase.name + "\" started.";
2485 messageType = "info";
2488 case TestRunner.TEST_CASE_COMPLETE_EVENT:
2489 message = "Test case \"" + data.testCase.name + "\" completed.\nPassed:" +
2490 data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
2491 messageType = "info";
2494 message = "Unexpected event " + data.type;
2498 YAHOO.log(message, messageType, "TestRunner");
2502 YAHOO.namespace("tool.TestFormat");
2505 * Returns test results formatted as a JSON string. Requires JSON utility.
2506 * @param {Object} result The results object created by TestRunner.
2507 * @return {String} An XML-formatted string of results.
2508 * @namespace YAHOO.tool.TestFormat
2512 YAHOO.tool.TestFormat.JSON = function(results /*:Object*/) /*:String*/ {
2513 return YAHOO.lang.JSON.stringify(results);
2517 * Returns test results formatted as an XML string.
2518 * @param {Object} result The results object created by TestRunner.
2519 * @return {String} An XML-formatted string of results.
2520 * @namespace YAHOO.tool.TestFormat
2524 YAHOO.tool.TestFormat.XML = function(results /*:Object*/) /*:String*/ {
2527 var xml /*:String*/ = "<" + results.type + " name=\"" + results.name.replace(/"/g, """).replace(/'/g, "'") + "\"";
2529 if (l.isNumber(results.duration)){
2530 xml += " duration=\"" + results.duration + "\"";
2533 if (results.type == "test"){
2534 xml += " result=\"" + results.result + "\" message=\"" + results.message + "\">";
2536 xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
2537 for (var prop in results) {
2538 if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
2539 xml += arguments.callee(results[prop]);
2544 xml += "</" + results.type + ">";
2549 YAHOO.namespace("tool");
2552 * An object capable of sending test results to a server.
2553 * @param {String} url The URL to submit the results to.
2554 * @param {Function} format (Optiona) A function that outputs the results in a specific format.
2555 * Default is YAHOO.tool.TestFormat.XML.
2557 * @namespace YAHOO.tool
2558 * @class TestReporter
2560 YAHOO.tool.TestReporter = function(url /*:String*/, format /*:Function*/) {
2563 * The URL to submit the data to.
2567 this.url /*:String*/ = url;
2570 * The formatting function to call when submitting the data.
2574 this.format /*:Function*/ = format || YAHOO.tool.TestFormat.XML;
2577 * Extra fields to submit with the request.
2582 this._fields /*:Object*/ = new Object();
2585 * The form element used to submit the results.
2586 * @type HTMLFormElement
2590 this._form /*:HTMLElement*/ = null;
2593 * Iframe used as a target for form submission.
2594 * @type HTMLIFrameElement
2598 this._iframe /*:HTMLElement*/ = null;
2601 YAHOO.tool.TestReporter.prototype = {
2603 //restore missing constructor
2604 constructor: YAHOO.tool.TestReporter,
2607 * Convert a date into ISO format.
2608 * From Douglas Crockford's json2.js
2609 * @param {Date} date The date to convert.
2610 * @return {String} An ISO-formatted date string
2611 * @method _convertToISOString
2614 _convertToISOString: function(date){
2616 // Format integers to have at least two digits.
2617 return n < 10 ? '0' + n : n;
2620 return date.getUTCFullYear() + '-' +
2621 f(date.getUTCMonth() + 1) + '-' +
2622 f(date.getUTCDate()) + 'T' +
2623 f(date.getUTCHours()) + ':' +
2624 f(date.getUTCMinutes()) + ':' +
2625 f(date.getUTCSeconds()) + 'Z';
2630 * Adds a field to the form that submits the results.
2631 * @param {String} name The name of the field.
2632 * @param {Variant} value The value of the field.
2636 addField : function (name /*:String*/, value /*:Variant*/) /*:Void*/{
2637 this._fields[name] = value;
2641 * Removes all previous defined fields.
2645 clearFields : function() /*:Void*/{
2646 this._fields = new Object();
2650 * Cleans up the memory associated with the TestReporter, removing DOM elements
2651 * that were created.
2655 destroy : function() /*:Void*/ {
2657 this._form.parentNode.removeChild(this._form);
2661 this._iframe.parentNode.removeChild(this._iframe);
2662 this._iframe = null;
2664 this._fields = null;
2668 * Sends the report to the server.
2669 * @param {Object} results The results object created by TestRunner.
2673 report : function(results /*:Object*/) /*:Void*/{
2675 //if the form hasn't been created yet, create it
2677 this._form = document.createElement("form");
2678 this._form.method = "post";
2679 this._form.style.visibility = "hidden";
2680 this._form.style.position = "absolute";
2681 this._form.style.top = 0;
2682 document.body.appendChild(this._form);
2684 //IE won't let you assign a name using the DOM, must do it the hacky way
2685 if (YAHOO.env.ua.ie){
2686 this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
2688 this._iframe = document.createElement("iframe");
2689 this._iframe.name = "yuiTestTarget";
2692 this._iframe.src = "javascript:false";
2693 this._iframe.style.visibility = "hidden";
2694 this._iframe.style.position = "absolute";
2695 this._iframe.style.top = 0;
2696 document.body.appendChild(this._iframe);
2698 this._form.target = "yuiTestTarget";
2701 //set the form's action
2702 this._form.action = this.url;
2704 //remove any existing fields
2705 while(this._form.hasChildNodes()){
2706 this._form.removeChild(this._form.lastChild);
2709 //create default fields
2710 this._fields.results = this.format(results);
2711 this._fields.useragent = navigator.userAgent;
2712 this._fields.timestamp = this._convertToISOString(new Date());
2714 //add fields to the form
2715 for (var prop in this._fields){
2716 if (YAHOO.lang.hasOwnProperty(this._fields, prop) && typeof this._fields[prop] != "function"){
2717 if (YAHOO.env.ua.ie){
2718 input = document.createElement("<input name=\"" + prop + "\" >");
2720 input = document.createElement("input");
2723 input.type = "hidden";
2724 input.value = this._fields[prop];
2725 this._form.appendChild(input);
2729 //remove default fields
2730 delete this._fields.results;
2731 delete this._fields.useragent;
2732 delete this._fields.timestamp;
2734 if (arguments[1] !== false){
2735 this._form.submit();
2741 YAHOO.register("yuitest", YAHOO.tool.TestRunner, {version: "2.8.0r4", build: "2449"});