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);
135 YAHOO.namespace("tool");
138 //-----------------------------------------------------------------------------
140 //-----------------------------------------------------------------------------
143 * A test suite that can contain a collection of TestCase and TestSuite objects.
144 * @param {String||Object} data The name of the test suite or an object containing
145 * a name property as well as setUp and tearDown methods.
146 * @namespace YAHOO.tool
150 YAHOO.tool.TestSuite = function (data /*:String||Object*/) {
153 * The name of the test suite.
157 this.name /*:String*/ = "";
160 * Array of test suites and
163 this.items /*:Array*/ = [];
165 //initialize the properties
166 if (YAHOO.lang.isString(data)){
168 } else if (YAHOO.lang.isObject(data)){
169 YAHOO.lang.augmentObject(this, data, true);
173 if (this.name === ""){
174 this.name = YAHOO.util.Dom.generateId(null, "testSuite");
179 YAHOO.tool.TestSuite.prototype = {
182 * Adds a test suite or test case to the test suite.
183 * @param {YAHOO.tool.TestSuite||YAHOO.tool.TestCase} testObject The test suite or test case to add.
187 add : function (testObject /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
188 if (testObject instanceof YAHOO.tool.TestSuite || testObject instanceof YAHOO.tool.TestCase) {
189 this.items.push(testObject);
193 //-------------------------------------------------------------------------
195 //-------------------------------------------------------------------------
198 * Function to run before each test is executed.
202 setUp : function () /*:Void*/ {
206 * Function to run after each test is executed.
210 tearDown: function () /*:Void*/ {
215 YAHOO.namespace("tool");
220 * @namespace YAHOO.tool
221 * @requires yahoo,dom,event,logger
225 //-----------------------------------------------------------------------------
227 //-----------------------------------------------------------------------------
230 YAHOO.tool.TestRunner = (function(){
233 * A node in the test tree structure. May represent a TestSuite, TestCase, or
235 * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
240 function TestNode(testObject /*:Variant*/){
243 * The TestSuite, TestCase, or test function represented by this node.
245 * @property testObject
247 this.testObject = testObject;
250 * Pointer to this node's first child.
252 * @property firstChild
254 this.firstChild /*:TestNode*/ = null;
257 * Pointer to this node's last child.
259 * @property lastChild
261 this.lastChild = null;
264 * Pointer to this node's parent.
271 * Pointer to this node's next sibling.
278 * Test results for this test object.
282 this.results /*:Object*/ = {
290 if (testObject instanceof YAHOO.tool.TestSuite){
291 this.results.type = "testsuite";
292 this.results.name = testObject.name;
293 } else if (testObject instanceof YAHOO.tool.TestCase){
294 this.results.type = "testcase";
295 this.results.name = testObject.name;
300 TestNode.prototype = {
303 * Appends a new test object (TestSuite, TestCase, or test function name) as a child
305 * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
308 appendChild : function (testObject /*:Variant*/) /*:Void*/{
309 var node = new TestNode(testObject);
310 if (this.firstChild === null){
311 this.firstChild = this.lastChild = node;
313 this.lastChild.next = node;
314 this.lastChild = node;
322 * Runs test suites and test cases, providing events to allowing for the
323 * interpretation of test results.
324 * @namespace YAHOO.tool
328 function TestRunner(){
330 //inherit from EventProvider
331 TestRunner.superclass.constructor.apply(this,arguments);
334 * Suite on which to attach all TestSuites and TestCases to be run.
335 * @type YAHOO.tool.TestSuite
336 * @property masterSuite
340 this.masterSuite /*:YAHOO.tool.TestSuite*/ = new YAHOO.tool.TestSuite("YUI Test Results");
343 * Pointer to the current node in the test tree.
352 * Pointer to the root node in the test tree.
361 var events /*:Array*/ = [
362 this.TEST_CASE_BEGIN_EVENT,
363 this.TEST_CASE_COMPLETE_EVENT,
364 this.TEST_SUITE_BEGIN_EVENT,
365 this.TEST_SUITE_COMPLETE_EVENT,
366 this.TEST_PASS_EVENT,
367 this.TEST_FAIL_EVENT,
368 this.TEST_IGNORE_EVENT,
372 for (var i=0; i < events.length; i++){
373 this.createEvent(events[i], { scope: this });
378 YAHOO.lang.extend(TestRunner, YAHOO.util.EventProvider, {
380 //-------------------------------------------------------------------------
382 //-------------------------------------------------------------------------
385 * Fires when a test case is opened but before the first
387 * @event testcasebegin
389 TEST_CASE_BEGIN_EVENT /*:String*/ : "testcasebegin",
392 * Fires when all tests in a test case have been executed.
393 * @event testcasecomplete
395 TEST_CASE_COMPLETE_EVENT /*:String*/ : "testcasecomplete",
398 * Fires when a test suite is opened but before the first
400 * @event testsuitebegin
402 TEST_SUITE_BEGIN_EVENT /*:String*/ : "testsuitebegin",
405 * Fires when all test cases in a test suite have been
407 * @event testsuitecomplete
409 TEST_SUITE_COMPLETE_EVENT /*:String*/ : "testsuitecomplete",
412 * Fires when a test has passed.
415 TEST_PASS_EVENT /*:String*/ : "pass",
418 * Fires when a test has failed.
421 TEST_FAIL_EVENT /*:String*/ : "fail",
424 * Fires when a test has been ignored.
427 TEST_IGNORE_EVENT /*:String*/ : "ignore",
430 * Fires when all test suites and test cases have been completed.
433 COMPLETE_EVENT /*:String*/ : "complete",
436 * Fires when the run() method is called.
439 BEGIN_EVENT /*:String*/ : "begin",
441 //-------------------------------------------------------------------------
442 // Test Tree-Related Methods
443 //-------------------------------------------------------------------------
446 * Adds a test case to the test tree as a child of the specified node.
447 * @param {TestNode} parentNode The node to add the test case to as a child.
448 * @param {YAHOO.tool.TestCase} testCase The test case to add.
452 * @method _addTestCaseToTestTree
454 _addTestCaseToTestTree : function (parentNode /*:TestNode*/, testCase /*:YAHOO.tool.TestCase*/) /*:Void*/{
457 var node = parentNode.appendChild(testCase);
459 //iterate over the items in the test case
460 for (var prop in testCase){
461 if (prop.indexOf("test") === 0 && YAHOO.lang.isFunction(testCase[prop])){
462 node.appendChild(prop);
469 * Adds a test suite to the test tree as a child of the specified node.
470 * @param {TestNode} parentNode The node to add the test suite to as a child.
471 * @param {YAHOO.tool.TestSuite} testSuite The test suite to add.
475 * @method _addTestSuiteToTestTree
477 _addTestSuiteToTestTree : function (parentNode /*:TestNode*/, testSuite /*:YAHOO.tool.TestSuite*/) /*:Void*/ {
480 var node = parentNode.appendChild(testSuite);
482 //iterate over the items in the master suite
483 for (var i=0; i < testSuite.items.length; i++){
484 if (testSuite.items[i] instanceof YAHOO.tool.TestSuite) {
485 this._addTestSuiteToTestTree(node, testSuite.items[i]);
486 } else if (testSuite.items[i] instanceof YAHOO.tool.TestCase) {
487 this._addTestCaseToTestTree(node, testSuite.items[i]);
493 * Builds the test tree based on items in the master suite. The tree is a hierarchical
494 * representation of the test suites, test cases, and test functions. The resulting tree
495 * is stored in _root and the pointer _cur is set to the root initially.
499 * @method _buildTestTree
501 _buildTestTree : function () /*:Void*/ {
503 this._root = new TestNode(this.masterSuite);
504 this._cur = this._root;
506 //iterate over the items in the master suite
507 for (var i=0; i < this.masterSuite.items.length; i++){
508 if (this.masterSuite.items[i] instanceof YAHOO.tool.TestSuite) {
509 this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
510 } else if (this.masterSuite.items[i] instanceof YAHOO.tool.TestCase) {
511 this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
517 //-------------------------------------------------------------------------
519 //-------------------------------------------------------------------------
522 * Handles the completion of a test object's tests. Tallies test results
523 * from one level up to the next.
524 * @param {TestNode} node The TestNode representing the test object.
526 * @method _handleTestObjectComplete
530 _handleTestObjectComplete : function (node /*:TestNode*/) /*:Void*/ {
531 if (YAHOO.lang.isObject(node.testObject)){
532 node.parent.results.passed += node.results.passed;
533 node.parent.results.failed += node.results.failed;
534 node.parent.results.total += node.results.total;
535 node.parent.results.ignored += node.results.ignored;
536 node.parent.results[node.testObject.name] = node.results;
538 if (node.testObject instanceof YAHOO.tool.TestSuite){
539 node.testObject.tearDown();
540 this.fireEvent(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
541 } else if (node.testObject instanceof YAHOO.tool.TestCase){
542 this.fireEvent(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
547 //-------------------------------------------------------------------------
548 // Navigation Methods
549 //-------------------------------------------------------------------------
552 * Retrieves the next node in the test tree.
553 * @return {TestNode} The next node in the test tree or null if the end is reached.
558 _next : function () /*:TestNode*/ {
560 if (this._cur.firstChild) {
561 this._cur = this._cur.firstChild;
562 } else if (this._cur.next) {
563 this._cur = this._cur.next;
565 while (this._cur && !this._cur.next && this._cur !== this._root){
566 this._handleTestObjectComplete(this._cur);
567 this._cur = this._cur.parent;
570 if (this._cur == this._root){
571 this._cur.results.type = "report";
572 this._cur.results.timestamp = (new Date()).toLocaleString();
573 this._cur.results.duration = (new Date()) - this._cur.results.duration;
574 this.fireEvent(this.COMPLETE_EVENT, { results: this._cur.results});
577 this._handleTestObjectComplete(this._cur);
578 this._cur = this._cur.next;
586 * Runs a test case or test suite, returning the results.
587 * @param {YAHOO.tool.TestCase|YAHOO.tool.TestSuite} testObject The test case or test suite to run.
588 * @return {Object} Results of the execution with properties passed, failed, and total.
593 _run : function () /*:Void*/ {
595 //flag to indicate if the TestRunner should wait before continuing
596 var shouldWait /*:Boolean*/ = false;
598 //get the next test node
599 var node = this._next();
602 var testObject = node.testObject;
604 //figure out what to do
605 if (YAHOO.lang.isObject(testObject)){
606 if (testObject instanceof YAHOO.tool.TestSuite){
607 this.fireEvent(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
609 } else if (testObject instanceof YAHOO.tool.TestCase){
610 this.fireEvent(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
613 //some environments don't support setTimeout
614 if (typeof setTimeout != "undefined"){
615 setTimeout(function(){
616 YAHOO.tool.TestRunner._run();
628 _resumeTest : function (segment /*:Function*/) /*:Void*/ {
630 //get relevant information
631 var node /*:TestNode*/ = this._cur;
632 var testName /*:String*/ = node.testObject;
633 var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
635 //cancel other waits if available
636 if (testCase.__yui_wait){
637 clearTimeout(testCase.__yui_wait);
638 delete testCase.__yui_wait;
641 //get the "should" test cases
642 var shouldFail /*:Object*/ = (testCase._should.fail || {})[testName];
643 var shouldError /*:Object*/ = (testCase._should.error || {})[testName];
645 //variable to hold whether or not the test failed
646 var failed /*:Boolean*/ = false;
647 var error /*:Error*/ = null;
653 segment.apply(testCase);
655 //if it should fail, and it got here, then it's a fail because it didn't
657 error = new YAHOO.util.ShouldFail();
659 } else if (shouldError){
660 error = new YAHOO.util.ShouldError();
664 } catch (thrown /*:Error*/){
665 if (thrown instanceof YAHOO.util.AssertionError) {
670 } else if (thrown instanceof YAHOO.tool.TestCase.Wait){
672 if (YAHOO.lang.isFunction(thrown.segment)){
673 if (YAHOO.lang.isNumber(thrown.delay)){
675 //some environments don't support setTimeout
676 if (typeof setTimeout != "undefined"){
677 testCase.__yui_wait = setTimeout(function(){
678 YAHOO.tool.TestRunner._resumeTest(thrown.segment);
681 throw new Error("Asynchronous tests not supported in this environment.");
689 //first check to see if it should error
691 error = new YAHOO.util.UnexpectedError(thrown);
694 //check to see what type of data we have
695 if (YAHOO.lang.isString(shouldError)){
697 //if it's a string, check the error message
698 if (thrown.message != shouldError){
699 error = new YAHOO.util.UnexpectedError(thrown);
702 } else if (YAHOO.lang.isFunction(shouldError)){
704 //if it's a function, see if the error is an instance of it
705 if (!(thrown instanceof shouldError)){
706 error = new YAHOO.util.UnexpectedError(thrown);
710 } else if (YAHOO.lang.isObject(shouldError)){
712 //if it's an object, check the instance and message
713 if (!(thrown instanceof shouldError.constructor) ||
714 thrown.message != shouldError.message){
715 error = new YAHOO.util.UnexpectedError(thrown);
726 //fireEvent appropriate event
728 this.fireEvent(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
730 this.fireEvent(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
737 node.parent.results[testName] = {
738 result: failed ? "fail" : "pass",
739 message: error ? error.getMessage() : "Test passed",
745 node.parent.results.failed++;
747 node.parent.results.passed++;
749 node.parent.results.total++;
751 //set timeout not supported in all environments
752 if (typeof setTimeout != "undefined"){
753 setTimeout(function(){
754 YAHOO.tool.TestRunner._run();
763 * Runs a single test based on the data provided in the node.
764 * @param {TestNode} node The TestNode representing the test to run.
770 _runTest : function (node /*:TestNode*/) /*:Void*/ {
772 //get relevant information
773 var testName /*:String*/ = node.testObject;
774 var testCase /*:YAHOO.tool.TestCase*/ = node.parent.testObject;
775 var test /*:Function*/ = testCase[testName];
777 //get the "should" test cases
778 var shouldIgnore /*:Object*/ = (testCase._should.ignore || {})[testName];
780 //figure out if the test should be ignored or not
784 node.parent.results[testName] = {
786 message: "Test ignored",
791 node.parent.results.ignored++;
792 node.parent.results.total++;
794 this.fireEvent(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
796 //some environments don't support setTimeout
797 if (typeof setTimeout != "undefined"){
798 setTimeout(function(){
799 YAHOO.tool.TestRunner._run();
810 //now call the body of the test
811 this._resumeTest(test);
816 //-------------------------------------------------------------------------
818 //-------------------------------------------------------------------------
821 * Fires events for the TestRunner. This overrides the default fireEvent()
822 * method from EventProvider to add the type property to the data that is
823 * passed through on each event call.
824 * @param {String} type The type of event to fire.
825 * @param {Object} data (Optional) Data for the event.
830 fireEvent : function (type /*:String*/, data /*:Object*/) /*:Void*/ {
833 TestRunner.superclass.fireEvent.call(this, type, data);
836 //-------------------------------------------------------------------------
838 //-------------------------------------------------------------------------
841 * Adds a test suite or test case to the list of test objects to run.
842 * @param testObject Either a TestCase or a TestSuite that should be run.
847 add : function (testObject /*:Object*/) /*:Void*/ {
848 this.masterSuite.add(testObject);
852 * Removes all test objects from the runner.
857 clear : function () /*:Void*/ {
858 this.masterSuite.items = [];
862 * Resumes the TestRunner after wait() was called.
863 * @param {Function} segment The function to run as the rest
864 * of the haulted test.
869 resume : function (segment /*:Function*/) /*:Void*/ {
870 this._resumeTest(segment || function(){});
874 * Runs the test suite.
879 run : function (testObject /*:Object*/) /*:Void*/ {
881 //pointer to runner to avoid scope issues
882 var runner = YAHOO.tool.TestRunner;
884 //build the test tree
885 runner._buildTestTree();
887 //set when the test started
888 runner._root.results.duration = (new Date()).valueOf();
890 //fire the begin event
891 runner.fireEvent(runner.BEGIN_EVENT);
898 return new TestRunner();
902 YAHOO.namespace("util");
904 //-----------------------------------------------------------------------------
906 //-----------------------------------------------------------------------------
909 * The Assert object provides functions to test JavaScript values against
910 * known and expected results. Whenever a comparison (assertion) fails,
911 * an error is thrown.
913 * @namespace YAHOO.util
917 YAHOO.util.Assert = {
919 //-------------------------------------------------------------------------
921 //-------------------------------------------------------------------------
924 * Formats a message so that it can contain the original assertion message
925 * in addition to the custom message.
926 * @param {String} customMessage The message passed in by the developer.
927 * @param {String} defaultMessage The message created by the error by default.
928 * @return {String} The final error message, containing either or both.
931 * @method _formatMessage
933 _formatMessage : function (customMessage /*:String*/, defaultMessage /*:String*/) /*:String*/ {
934 var message = customMessage;
935 if (YAHOO.lang.isString(customMessage) && customMessage.length > 0){
936 return YAHOO.lang.substitute(customMessage, { message: defaultMessage });
938 return defaultMessage;
942 //-------------------------------------------------------------------------
943 // Generic Assertion Methods
944 //-------------------------------------------------------------------------
947 * Forces an assertion error to occur.
948 * @param {String} message (Optional) The message to display with the failure.
952 fail : function (message /*:String*/) /*:Void*/ {
953 throw new YAHOO.util.AssertionError(this._formatMessage(message, "Test force-failed."));
956 //-------------------------------------------------------------------------
957 // Equality Assertion Methods
958 //-------------------------------------------------------------------------
961 * Asserts that a value is equal to another. This uses the double equals sign
962 * so type cohersion may occur.
963 * @param {Object} expected The expected value.
964 * @param {Object} actual The actual value to test.
965 * @param {String} message (Optional) The message to display if the assertion fails.
969 areEqual : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
970 if (expected != actual) {
971 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be equal."), expected, actual);
976 * Asserts that a value is not equal to another. This uses the double equals sign
977 * so type cohersion may occur.
978 * @param {Object} unexpected The unexpected value.
979 * @param {Object} actual The actual value to test.
980 * @param {String} message (Optional) The message to display if the assertion fails.
981 * @method areNotEqual
984 areNotEqual : function (unexpected /*:Object*/, actual /*:Object*/,
985 message /*:String*/) /*:Void*/ {
986 if (unexpected == actual) {
987 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be equal."), unexpected);
992 * Asserts that a value is not the same as another. This uses the triple equals sign
993 * so no type cohersion may occur.
994 * @param {Object} unexpected The unexpected value.
995 * @param {Object} actual The actual value to test.
996 * @param {String} message (Optional) The message to display if the assertion fails.
1000 areNotSame : function (unexpected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1001 if (unexpected === actual) {
1002 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be the same."), unexpected);
1007 * Asserts that a value is the same as another. This uses the triple equals sign
1008 * so no type cohersion may occur.
1009 * @param {Object} expected The expected value.
1010 * @param {Object} actual The actual value to test.
1011 * @param {String} message (Optional) The message to display if the assertion fails.
1015 areSame : function (expected /*:Object*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1016 if (expected !== actual) {
1017 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Values should be the same."), expected, actual);
1021 //-------------------------------------------------------------------------
1022 // Boolean Assertion Methods
1023 //-------------------------------------------------------------------------
1026 * Asserts that a value is false. This uses the triple equals sign
1027 * so no type cohersion may occur.
1028 * @param {Object} actual The actual value to test.
1029 * @param {String} message (Optional) The message to display if the assertion fails.
1033 isFalse : function (actual /*:Boolean*/, message /*:String*/) {
1034 if (false !== actual) {
1035 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be false."), false, actual);
1040 * Asserts that a value is true. This uses the triple equals sign
1041 * so no type cohersion may occur.
1042 * @param {Object} actual The actual value to test.
1043 * @param {String} message (Optional) The message to display if the assertion fails.
1047 isTrue : function (actual /*:Boolean*/, message /*:String*/) /*:Void*/ {
1048 if (true !== actual) {
1049 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be true."), true, actual);
1054 //-------------------------------------------------------------------------
1055 // Special Value Assertion Methods
1056 //-------------------------------------------------------------------------
1059 * Asserts that a value is not a number.
1060 * @param {Object} actual The value to test.
1061 * @param {String} message (Optional) The message to display if the assertion fails.
1065 isNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1066 if (!isNaN(actual)){
1067 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be NaN."), NaN, actual);
1072 * Asserts that a value is not the special NaN value.
1073 * @param {Object} actual The value to test.
1074 * @param {String} message (Optional) The message to display if the assertion fails.
1078 isNotNaN : function (actual /*:Object*/, message /*:String*/) /*:Void*/{
1080 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be NaN."), NaN);
1085 * Asserts that a value is not null. This uses the triple equals sign
1086 * so no type cohersion may occur.
1087 * @param {Object} actual The actual value to test.
1088 * @param {String} message (Optional) The message to display if the assertion fails.
1092 isNotNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1093 if (YAHOO.lang.isNull(actual)) {
1094 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Values should not be null."), null);
1099 * Asserts that a value is not undefined. This uses the triple equals sign
1100 * so no type cohersion may occur.
1101 * @param {Object} actual The actual value to test.
1102 * @param {String} message (Optional) The message to display if the assertion fails.
1103 * @method isNotUndefined
1106 isNotUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1107 if (YAHOO.lang.isUndefined(actual)) {
1108 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should not be undefined."), undefined);
1113 * Asserts that a value is null. This uses the triple equals sign
1114 * so no type cohersion may occur.
1115 * @param {Object} actual The actual value to test.
1116 * @param {String} message (Optional) The message to display if the assertion fails.
1120 isNull : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1121 if (!YAHOO.lang.isNull(actual)) {
1122 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be null."), null, actual);
1127 * Asserts that a value is undefined. This uses the triple equals sign
1128 * so no type cohersion may occur.
1129 * @param {Object} actual The actual value to test.
1130 * @param {String} message (Optional) The message to display if the assertion fails.
1131 * @method isUndefined
1134 isUndefined : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1135 if (!YAHOO.lang.isUndefined(actual)) {
1136 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be undefined."), undefined, actual);
1140 //--------------------------------------------------------------------------
1141 // Instance Assertion Methods
1142 //--------------------------------------------------------------------------
1145 * Asserts that a value is an array.
1146 * @param {Object} actual The value to test.
1147 * @param {String} message (Optional) The message to display if the assertion fails.
1151 isArray : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1152 if (!YAHOO.lang.isArray(actual)){
1153 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an array."), actual);
1158 * Asserts that a value is a Boolean.
1159 * @param {Object} actual The value to test.
1160 * @param {String} message (Optional) The message to display if the assertion fails.
1164 isBoolean : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1165 if (!YAHOO.lang.isBoolean(actual)){
1166 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a Boolean."), actual);
1171 * Asserts that a value is a function.
1172 * @param {Object} actual The value to test.
1173 * @param {String} message (Optional) The message to display if the assertion fails.
1174 * @method isFunction
1177 isFunction : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1178 if (!YAHOO.lang.isFunction(actual)){
1179 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a function."), actual);
1184 * Asserts that a value is an instance of a particular object. This may return
1185 * incorrect results when comparing objects from one frame to constructors in
1186 * another frame. For best results, don't use in a cross-frame manner.
1187 * @param {Function} expected The function that the object should be an instance of.
1188 * @param {Object} actual The object to test.
1189 * @param {String} message (Optional) The message to display if the assertion fails.
1190 * @method isInstanceOf
1193 isInstanceOf : function (expected /*:Function*/, actual /*:Object*/, message /*:String*/) /*:Void*/ {
1194 if (!(actual instanceof expected)){
1195 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1200 * Asserts that a value is a number.
1201 * @param {Object} actual The value to test.
1202 * @param {String} message (Optional) The message to display if the assertion fails.
1206 isNumber : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1207 if (!YAHOO.lang.isNumber(actual)){
1208 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a number."), actual);
1213 * Asserts that a value is an object.
1214 * @param {Object} actual The value to test.
1215 * @param {String} message (Optional) The message to display if the assertion fails.
1219 isObject : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1220 if (!YAHOO.lang.isObject(actual)){
1221 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be an object."), actual);
1226 * Asserts that a value is a string.
1227 * @param {Object} actual The value to test.
1228 * @param {String} message (Optional) The message to display if the assertion fails.
1232 isString : function (actual /*:Object*/, message /*:String*/) /*:Void*/ {
1233 if (!YAHOO.lang.isString(actual)){
1234 throw new YAHOO.util.UnexpectedValue(this._formatMessage(message, "Value should be a string."), actual);
1239 * Asserts that a value is of a particular type.
1240 * @param {String} expectedType The expected type of the variable.
1241 * @param {Object} actualValue The actual value to test.
1242 * @param {String} message (Optional) The message to display if the assertion fails.
1246 isTypeOf : function (expectedType /*:String*/, actualValue /*:Object*/, message /*:String*/) /*:Void*/{
1247 if (typeof actualValue != expectedType){
1248 throw new YAHOO.util.ComparisonFailure(this._formatMessage(message, "Value should be of type " + expected + "."), expected, typeof actual);
1253 //-----------------------------------------------------------------------------
1255 //-----------------------------------------------------------------------------
1258 * AssertionError is thrown whenever an assertion fails. It provides methods
1259 * to more easily get at error information and also provides a base class
1260 * from which more specific assertion errors can be derived.
1262 * @param {String} message The message to display when the error occurs.
1263 * @namespace YAHOO.util
1264 * @class AssertionError
1268 YAHOO.util.AssertionError = function (message /*:String*/){
1271 arguments.callee.superclass.constructor.call(this, message);
1274 * Error message. Must be duplicated to ensure browser receives it.
1278 this.message /*:String*/ = message;
1281 * The name of the error that occurred.
1285 this.name /*:String*/ = "AssertionError";
1289 YAHOO.lang.extend(YAHOO.util.AssertionError, Error, {
1292 * Returns a fully formatted error for an assertion failure. This should
1293 * be overridden by all subclasses to provide specific information.
1294 * @method getMessage
1295 * @return {String} A string describing the error.
1297 getMessage : function () /*:String*/ {
1298 return this.message;
1302 * Returns a string representation of the error.
1304 * @return {String} A string representation of the error.
1306 toString : function () /*:String*/ {
1307 return this.name + ": " + this.getMessage();
1311 * Returns a primitive value version of the error. Same as toString().
1313 * @return {String} A primitive value version of the error.
1315 valueOf : function () /*:String*/ {
1316 return this.toString();
1322 * ComparisonFailure is subclass of AssertionError that is thrown whenever
1323 * a comparison between two values fails. It provides mechanisms to retrieve
1324 * both the expected and actual value.
1326 * @param {String} message The message to display when the error occurs.
1327 * @param {Object} expected The expected value.
1328 * @param {Object} actual The actual value that caused the assertion to fail.
1329 * @namespace YAHOO.util
1330 * @extends YAHOO.util.AssertionError
1331 * @class ComparisonFailure
1334 YAHOO.util.ComparisonFailure = function (message /*:String*/, expected /*:Object*/, actual /*:Object*/){
1337 arguments.callee.superclass.constructor.call(this, message);
1340 * The expected value.
1342 * @property expected
1344 this.expected /*:Object*/ = expected;
1351 this.actual /*:Object*/ = actual;
1354 * The name of the error that occurred.
1358 this.name /*:String*/ = "ComparisonFailure";
1363 YAHOO.lang.extend(YAHOO.util.ComparisonFailure, YAHOO.util.AssertionError, {
1366 * Returns a fully formatted error for an assertion failure. This message
1367 * provides information about the expected and actual values.
1369 * @return {String} A string describing the error.
1371 getMessage : function () /*:String*/ {
1372 return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
1373 "\nActual:" + this.actual + " (" + (typeof this.actual) + ")";
1379 * UnexpectedValue is subclass of AssertionError that is thrown whenever
1380 * a value was unexpected in its scope. This typically means that a test
1381 * was performed to determine that a value was *not* equal to a certain
1384 * @param {String} message The message to display when the error occurs.
1385 * @param {Object} unexpected The unexpected value.
1386 * @namespace YAHOO.util
1387 * @extends YAHOO.util.AssertionError
1388 * @class UnexpectedValue
1391 YAHOO.util.UnexpectedValue = function (message /*:String*/, unexpected /*:Object*/){
1394 arguments.callee.superclass.constructor.call(this, message);
1397 * The unexpected value.
1399 * @property unexpected
1401 this.unexpected /*:Object*/ = unexpected;
1404 * The name of the error that occurred.
1408 this.name /*:String*/ = "UnexpectedValue";
1413 YAHOO.lang.extend(YAHOO.util.UnexpectedValue, YAHOO.util.AssertionError, {
1416 * Returns a fully formatted error for an assertion failure. The message
1417 * contains information about the unexpected value that was encountered.
1418 * @method getMessage
1419 * @return {String} A string describing the error.
1421 getMessage : function () /*:String*/ {
1422 return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1428 * ShouldFail is subclass of AssertionError that is thrown whenever
1429 * a test was expected to fail but did not.
1431 * @param {String} message The message to display when the error occurs.
1432 * @namespace YAHOO.util
1433 * @extends YAHOO.util.AssertionError
1437 YAHOO.util.ShouldFail = function (message /*:String*/){
1440 arguments.callee.superclass.constructor.call(this, message || "This test should fail but didn't.");
1443 * The name of the error that occurred.
1447 this.name /*:String*/ = "ShouldFail";
1452 YAHOO.lang.extend(YAHOO.util.ShouldFail, YAHOO.util.AssertionError);
1455 * ShouldError is subclass of AssertionError that is thrown whenever
1456 * a test is expected to throw an error but doesn't.
1458 * @param {String} message The message to display when the error occurs.
1459 * @namespace YAHOO.util
1460 * @extends YAHOO.util.AssertionError
1461 * @class ShouldError
1464 YAHOO.util.ShouldError = function (message /*:String*/){
1467 arguments.callee.superclass.constructor.call(this, message || "This test should have thrown an error but didn't.");
1470 * The name of the error that occurred.
1474 this.name /*:String*/ = "ShouldError";
1479 YAHOO.lang.extend(YAHOO.util.ShouldError, YAHOO.util.AssertionError);
1482 * UnexpectedError is subclass of AssertionError that is thrown whenever
1483 * an error occurs within the course of a test and the test was not expected
1484 * to throw an error.
1486 * @param {Error} cause The unexpected error that caused this error to be
1488 * @namespace YAHOO.util
1489 * @extends YAHOO.util.AssertionError
1490 * @class UnexpectedError
1493 YAHOO.util.UnexpectedError = function (cause /*:Object*/){
1496 arguments.callee.superclass.constructor.call(this, "Unexpected error: " + cause.message);
1499 * The unexpected error that occurred.
1503 this.cause /*:Error*/ = cause;
1506 * The name of the error that occurred.
1510 this.name /*:String*/ = "UnexpectedError";
1513 * Stack information for the error (if provided).
1517 this.stack /*:String*/ = cause.stack;
1522 YAHOO.lang.extend(YAHOO.util.UnexpectedError, YAHOO.util.AssertionError);
1524 //-----------------------------------------------------------------------------
1525 // ArrayAssert object
1526 //-----------------------------------------------------------------------------
1529 * The ArrayAssert object provides functions to test JavaScript array objects
1530 * for a variety of cases.
1532 * @namespace YAHOO.util
1533 * @class ArrayAssert
1537 YAHOO.util.ArrayAssert = {
1540 * Asserts that a value is present in an array. This uses the triple equals
1541 * sign so no type cohersion may occur.
1542 * @param {Object} needle The value that is expected in the array.
1543 * @param {Array} haystack An array of values.
1544 * @param {String} message (Optional) The message to display if the assertion fails.
1548 contains : function (needle /*:Object*/, haystack /*:Array*/,
1549 message /*:String*/) /*:Void*/ {
1551 var found /*:Boolean*/ = false;
1552 var Assert = YAHOO.util.Assert;
1554 //begin checking values
1555 for (var i=0; i < haystack.length && !found; i++){
1556 if (haystack[i] === needle) {
1562 Assert.fail(Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1567 * Asserts that a set of values are present in an array. This uses the triple equals
1568 * sign so no type cohersion may occur. For this assertion to pass, all values must
1570 * @param {Object[]} needles An array of values that are expected in the array.
1571 * @param {Array} haystack An array of values to check.
1572 * @param {String} message (Optional) The message to display if the assertion fails.
1573 * @method containsItems
1576 containsItems : function (needles /*:Object[]*/, haystack /*:Array*/,
1577 message /*:String*/) /*:Void*/ {
1579 //begin checking values
1580 for (var i=0; i < needles.length; i++){
1581 this.contains(needles[i], haystack, message);
1586 * Asserts that a value matching some condition is present in an array. This uses
1587 * a function to determine a match.
1588 * @param {Function} matcher A function that returns true if the items matches or false if not.
1589 * @param {Array} haystack An array of values.
1590 * @param {String} message (Optional) The message to display if the assertion fails.
1591 * @method containsMatch
1594 containsMatch : function (matcher /*:Function*/, haystack /*:Array*/,
1595 message /*:String*/) /*:Void*/ {
1597 //check for valid matcher
1598 if (typeof matcher != "function"){
1599 throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1602 var found /*:Boolean*/ = false;
1603 var Assert = YAHOO.util.Assert;
1605 //begin checking values
1606 for (var i=0; i < haystack.length && !found; i++){
1607 if (matcher(haystack[i])) {
1613 Assert.fail(Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1618 * Asserts that a value is not present in an array. This uses the triple equals
1619 * sign so no type cohersion may occur.
1620 * @param {Object} needle The value that is expected in the array.
1621 * @param {Array} haystack An array of values.
1622 * @param {String} message (Optional) The message to display if the assertion fails.
1623 * @method doesNotContain
1626 doesNotContain : function (needle /*:Object*/, haystack /*:Array*/,
1627 message /*:String*/) /*:Void*/ {
1629 var found /*:Boolean*/ = false;
1630 var Assert = YAHOO.util.Assert;
1632 //begin checking values
1633 for (var i=0; i < haystack.length && !found; i++){
1634 if (haystack[i] === needle) {
1640 Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1645 * Asserts that a set of values are not present in an array. This uses the triple equals
1646 * sign so no type cohersion may occur. For this assertion to pass, all values must
1648 * @param {Object[]} needles An array of values that are not expected in the array.
1649 * @param {Array} haystack An array of values to check.
1650 * @param {String} message (Optional) The message to display if the assertion fails.
1651 * @method doesNotContainItems
1654 doesNotContainItems : function (needles /*:Object[]*/, haystack /*:Array*/,
1655 message /*:String*/) /*:Void*/ {
1657 for (var i=0; i < needles.length; i++){
1658 this.doesNotContain(needles[i], haystack, message);
1664 * Asserts that no values matching a condition are present in an array. This uses
1665 * a function to determine a match.
1666 * @param {Function} matcher A function that returns true if the items matches or false if not.
1667 * @param {Array} haystack An array of values.
1668 * @param {String} message (Optional) The message to display if the assertion fails.
1669 * @method doesNotContainMatch
1672 doesNotContainMatch : function (matcher /*:Function*/, haystack /*:Array*/,
1673 message /*:String*/) /*:Void*/ {
1675 //check for valid matcher
1676 if (typeof matcher != "function"){
1677 throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1680 var found /*:Boolean*/ = false;
1681 var Assert = YAHOO.util.Assert;
1683 //begin checking values
1684 for (var i=0; i < haystack.length && !found; i++){
1685 if (matcher(haystack[i])) {
1691 Assert.fail(Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1696 * Asserts that the given value is contained in an array at the specified index.
1697 * This uses the triple equals sign so no type cohersion will occur.
1698 * @param {Object} needle The value to look for.
1699 * @param {Array} haystack The array to search in.
1700 * @param {int} index The index at which the value should exist.
1701 * @param {String} message (Optional) The message to display if the assertion fails.
1705 indexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1707 //try to find the value in the array
1708 for (var i=0; i < haystack.length; i++){
1709 if (haystack[i] === needle){
1710 YAHOO.util.Assert.areEqual(index, i, message || "Value exists at index " + i + " but should be at index " + index + ".");
1715 var Assert = YAHOO.util.Assert;
1717 //if it makes it here, it wasn't found at all
1718 Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
1722 * Asserts that the values in an array are equal, and in the same position,
1723 * as values in another array. This uses the double equals sign
1724 * so type cohersion may occur. Note that the array objects themselves
1725 * need not be the same for this test to pass.
1726 * @param {Array} expected An array of the expected values.
1727 * @param {Array} actual Any array of the actual values.
1728 * @param {String} message (Optional) The message to display if the assertion fails.
1729 * @method itemsAreEqual
1732 itemsAreEqual : function (expected /*:Array*/, actual /*:Array*/,
1733 message /*:String*/) /*:Void*/ {
1735 //one may be longer than the other, so get the maximum length
1736 var len /*:int*/ = Math.max(expected.length, actual.length);
1737 var Assert = YAHOO.util.Assert;
1739 //begin checking values
1740 for (var i=0; i < len; i++){
1741 Assert.areEqual(expected[i], actual[i],
1742 Assert._formatMessage(message, "Values in position " + i + " are not equal."));
1747 * Asserts that the values in an array are equivalent, and in the same position,
1748 * as values in another array. This uses a function to determine if the values
1749 * are equivalent. Note that the array objects themselves
1750 * need not be the same for this test to pass.
1751 * @param {Array} expected An array of the expected values.
1752 * @param {Array} actual Any array of the actual values.
1753 * @param {Function} comparator A function that returns true if the values are equivalent
1755 * @param {String} message (Optional) The message to display if the assertion fails.
1757 * @method itemsAreEquivalent
1760 itemsAreEquivalent : function (expected /*:Array*/, actual /*:Array*/,
1761 comparator /*:Function*/, message /*:String*/) /*:Void*/ {
1763 //make sure the comparator is valid
1764 if (typeof comparator != "function"){
1765 throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
1768 //one may be longer than the other, so get the maximum length
1769 var len /*:int*/ = Math.max(expected.length, actual.length);
1771 //begin checking values
1772 for (var i=0; i < len; i++){
1773 if (!comparator(expected[i], actual[i])){
1774 throw new YAHOO.util.ComparisonFailure(YAHOO.util.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
1780 * Asserts that an array is empty.
1781 * @param {Array} actual The array to test.
1782 * @param {String} message (Optional) The message to display if the assertion fails.
1786 isEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {
1787 if (actual.length > 0){
1788 var Assert = YAHOO.util.Assert;
1789 Assert.fail(Assert._formatMessage(message, "Array should be empty."));
1794 * Asserts that an array is not empty.
1795 * @param {Array} actual The array to test.
1796 * @param {String} message (Optional) The message to display if the assertion fails.
1797 * @method isNotEmpty
1800 isNotEmpty : function (actual /*:Array*/, message /*:String*/) /*:Void*/ {
1801 if (actual.length === 0){
1802 var Assert = YAHOO.util.Assert;
1803 Assert.fail(Assert._formatMessage(message, "Array should not be empty."));
1808 * Asserts that the values in an array are the same, and in the same position,
1809 * as values in another array. This uses the triple equals sign
1810 * so no type cohersion will occur. Note that the array objects themselves
1811 * need not be the same for this test to pass.
1812 * @param {Array} expected An array of the expected values.
1813 * @param {Array} actual Any array of the actual values.
1814 * @param {String} message (Optional) The message to display if the assertion fails.
1815 * @method itemsAreSame
1818 itemsAreSame : function (expected /*:Array*/, actual /*:Array*/,
1819 message /*:String*/) /*:Void*/ {
1821 //one may be longer than the other, so get the maximum length
1822 var len /*:int*/ = Math.max(expected.length, actual.length);
1823 var Assert = YAHOO.util.Assert;
1825 //begin checking values
1826 for (var i=0; i < len; i++){
1827 Assert.areSame(expected[i], actual[i],
1828 Assert._formatMessage(message, "Values in position " + i + " are not the same."));
1833 * Asserts that the given value is contained in an array at the specified index,
1834 * starting from the back of the array.
1835 * This uses the triple equals sign so no type cohersion will occur.
1836 * @param {Object} needle The value to look for.
1837 * @param {Array} haystack The array to search in.
1838 * @param {int} index The index at which the value should exist.
1839 * @param {String} message (Optional) The message to display if the assertion fails.
1840 * @method lastIndexOf
1843 lastIndexOf : function (needle /*:Object*/, haystack /*:Array*/, index /*:int*/, message /*:String*/) /*:Void*/ {
1845 var Assert = YAHOO.util.Assert;
1847 //try to find the value in the array
1848 for (var i=haystack.length; i >= 0; i--){
1849 if (haystack[i] === needle){
1850 Assert.areEqual(index, i, Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
1855 //if it makes it here, it wasn't found at all
1856 Assert.fail(Assert._formatMessage(message, "Value doesn't exist in array."));
1861 YAHOO.namespace("util");
1864 //-----------------------------------------------------------------------------
1865 // ObjectAssert object
1866 //-----------------------------------------------------------------------------
1869 * The ObjectAssert object provides functions to test JavaScript objects
1870 * for a variety of cases.
1872 * @namespace YAHOO.util
1873 * @class ObjectAssert
1876 YAHOO.util.ObjectAssert = {
1879 * Asserts that all properties in the object exist in another object.
1880 * @param {Object} expected An object with the expected properties.
1881 * @param {Object} actual An object with the actual properties.
1882 * @param {String} message (Optional) The message to display if the assertion fails.
1883 * @method propertiesAreEqual
1886 propertiesAreEqual : function (expected /*:Object*/, actual /*:Object*/,
1887 message /*:String*/) /*:Void*/ {
1889 var Assert = YAHOO.util.Assert;
1891 //get all properties in the object
1892 var properties /*:Array*/ = [];
1893 for (var property in expected){
1894 properties.push(property);
1897 //see if the properties are in the expected object
1898 for (var i=0; i < properties.length; i++){
1899 Assert.isNotUndefined(actual[properties[i]],
1900 Assert._formatMessage(message, "Property '" + properties[i] + "' expected."));
1906 * Asserts that an object has a property with the given name.
1907 * @param {String} propertyName The name of the property to test.
1908 * @param {Object} object The object to search.
1909 * @param {String} message (Optional) The message to display if the assertion fails.
1910 * @method hasProperty
1913 hasProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1914 if (!(propertyName in object)){
1915 var Assert = YAHOO.util.Assert;
1916 Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
1921 * Asserts that a property with the given name exists on an object instance (not on its prototype).
1922 * @param {String} propertyName The name of the property to test.
1923 * @param {Object} object The object to search.
1924 * @param {String} message (Optional) The message to display if the assertion fails.
1925 * @method hasProperty
1928 hasOwnProperty : function (propertyName /*:String*/, object /*:Object*/, message /*:String*/) /*:Void*/ {
1929 if (!YAHOO.lang.hasOwnProperty(object, propertyName)){
1930 var Assert = YAHOO.util.Assert;
1931 Assert.fail(Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
1936 //-----------------------------------------------------------------------------
1937 // DateAssert object
1938 //-----------------------------------------------------------------------------
1941 * The DateAssert object provides functions to test JavaScript Date objects
1942 * for a variety of cases.
1944 * @namespace YAHOO.util
1949 YAHOO.util.DateAssert = {
1952 * Asserts that a date's month, day, and year are equal to another date's.
1953 * @param {Date} expected The expected date.
1954 * @param {Date} actual The actual date to test.
1955 * @param {String} message (Optional) The message to display if the assertion fails.
1956 * @method datesAreEqual
1959 datesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1960 if (expected instanceof Date && actual instanceof Date){
1961 var Assert = YAHOO.util.Assert;
1962 Assert.areEqual(expected.getFullYear(), actual.getFullYear(), Assert._formatMessage(message, "Years should be equal."));
1963 Assert.areEqual(expected.getMonth(), actual.getMonth(), Assert._formatMessage(message, "Months should be equal."));
1964 Assert.areEqual(expected.getDate(), actual.getDate(), Assert._formatMessage(message, "Day of month should be equal."));
1966 throw new TypeError("DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
1971 * Asserts that a date's hour, minutes, and seconds are equal to another date's.
1972 * @param {Date} expected The expected date.
1973 * @param {Date} actual The actual date to test.
1974 * @param {String} message (Optional) The message to display if the assertion fails.
1975 * @method timesAreEqual
1978 timesAreEqual : function (expected /*:Date*/, actual /*:Date*/, message /*:String*/){
1979 if (expected instanceof Date && actual instanceof Date){
1980 var Assert = YAHOO.util.Assert;
1981 Assert.areEqual(expected.getHours(), actual.getHours(), Assert._formatMessage(message, "Hours should be equal."));
1982 Assert.areEqual(expected.getMinutes(), actual.getMinutes(), Assert._formatMessage(message, "Minutes should be equal."));
1983 Assert.areEqual(expected.getSeconds(), actual.getSeconds(), Assert._formatMessage(message, "Seconds should be equal."));
1985 throw new TypeError("DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
1991 YAHOO.namespace("util");
1994 * The UserAction object provides functions that simulate events occurring in
1995 * the browser. Since these are simulated events, they do not behave exactly
1996 * as regular, user-initiated events do, but can be used to test simple
1997 * user interactions safely.
1999 * @namespace YAHOO.util
2003 YAHOO.util.UserAction = {
2005 //--------------------------------------------------------------------------
2006 // Generic event methods
2007 //--------------------------------------------------------------------------
2010 * Simulates a key event using the given event information to populate
2011 * the generated event object. This method does browser-equalizing
2012 * calculations to account for differences in the DOM and IE event models
2013 * as well as different browser quirks. Note: keydown causes Safari 2.x to
2015 * @method simulateKeyEvent
2018 * @param {HTMLElement} target The target of the given event.
2019 * @param {String} type The type of event to fire. This can be any one of
2020 * the following: keyup, keydown, and keypress.
2021 * @param {Boolean} bubbles (Optional) Indicates if the event can be
2022 * bubbled up. DOM Level 3 specifies that all key events bubble by
2023 * default. The default is true.
2024 * @param {Boolean} cancelable (Optional) Indicates if the event can be
2025 * canceled using preventDefault(). DOM Level 3 specifies that all
2026 * key events can be cancelled. The default
2028 * @param {Window} view (Optional) The view containing the target. This is
2029 * typically the window object. The default is window.
2030 * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
2031 * is pressed while the event is firing. The default is false.
2032 * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
2033 * is pressed while the event is firing. The default is false.
2034 * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
2035 * is pressed while the event is firing. The default is false.
2036 * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
2037 * is pressed while the event is firing. The default is false.
2038 * @param {int} keyCode (Optional) The code for the key that is in use.
2040 * @param {int} charCode (Optional) The Unicode code for the character
2041 * associated with the key being used. The default is 0.
2043 simulateKeyEvent : function (target /*:HTMLElement*/, type /*:String*/,
2044 bubbles /*:Boolean*/, cancelable /*:Boolean*/,
2046 ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
2047 shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
2048 keyCode /*:int*/, charCode /*:int*/) /*:Void*/
2051 target = YAHOO.util.Dom.get(target);
2053 throw new Error("simulateKeyEvent(): Invalid target.");
2057 if (YAHOO.lang.isString(type)){
2058 type = type.toLowerCase();
2064 case "textevent": //DOM Level 3
2067 // @TODO was the fallthrough intentional, if so throw error
2069 throw new Error("simulateKeyEvent(): Event type '" + type + "' not supported.");
2072 throw new Error("simulateKeyEvent(): Event type must be a string.");
2075 //setup default values
2076 if (!YAHOO.lang.isBoolean(bubbles)){
2077 bubbles = true; //all key events bubble
2079 if (!YAHOO.lang.isBoolean(cancelable)){
2080 cancelable = true; //all key events can be cancelled
2082 if (!YAHOO.lang.isObject(view)){
2083 view = window; //view is typically window
2085 if (!YAHOO.lang.isBoolean(ctrlKey)){
2088 if (!YAHOO.lang.isBoolean(altKey)){
2091 if (!YAHOO.lang.isBoolean(shiftKey)){
2094 if (!YAHOO.lang.isBoolean(metaKey)){
2097 if (!YAHOO.lang.isNumber(keyCode)){
2100 if (!YAHOO.lang.isNumber(charCode)){
2104 //try to create a mouse event
2105 var customEvent /*:MouseEvent*/ = null;
2107 //check for DOM-compliant browsers first
2108 if (YAHOO.lang.isFunction(document.createEvent)){
2112 //try to create key event
2113 customEvent = document.createEvent("KeyEvents");
2116 * Interesting problem: Firefox implemented a non-standard
2117 * version of initKeyEvent() based on DOM Level 2 specs.
2118 * Key event was removed from DOM Level 2 and re-introduced
2119 * in DOM Level 3 with a different interface. Firefox is the
2120 * only browser with any implementation of Key Events, so for
2121 * now, assume it's Firefox if the above line doesn't error.
2123 //TODO: Decipher between Firefox's implementation and a correct one.
2124 customEvent.initKeyEvent(type, bubbles, cancelable, view, ctrlKey,
2125 altKey, shiftKey, metaKey, keyCode, charCode);
2127 } catch (ex /*:Error*/){
2130 * If it got here, that means key events aren't officially supported.
2131 * Safari/WebKit is a real problem now. WebKit 522 won't let you
2132 * set keyCode, charCode, or other properties if you use a
2133 * UIEvent, so we first must try to create a generic event. The
2134 * fun part is that this will throw an error on Safari 2.x. The
2135 * end result is that we need another try...catch statement just to
2136 * deal with this mess.
2140 //try to create generic event - will fail in Safari 2.x
2141 customEvent = document.createEvent("Events");
2143 } catch (uierror /*:Error*/){
2145 //the above failed, so create a UIEvent for Safari 2.x
2146 customEvent = document.createEvent("UIEvents");
2150 customEvent.initEvent(type, bubbles, cancelable);
2153 customEvent.view = view;
2154 customEvent.altKey = altKey;
2155 customEvent.ctrlKey = ctrlKey;
2156 customEvent.shiftKey = shiftKey;
2157 customEvent.metaKey = metaKey;
2158 customEvent.keyCode = keyCode;
2159 customEvent.charCode = charCode;
2166 target.dispatchEvent(customEvent);
2168 } else if (YAHOO.lang.isObject(document.createEventObject)){ //IE
2170 //create an IE event object
2171 customEvent = document.createEventObject();
2173 //assign available properties
2174 customEvent.bubbles = bubbles;
2175 customEvent.cancelable = cancelable;
2176 customEvent.view = view;
2177 customEvent.ctrlKey = ctrlKey;
2178 customEvent.altKey = altKey;
2179 customEvent.shiftKey = shiftKey;
2180 customEvent.metaKey = metaKey;
2183 * IE doesn't support charCode explicitly. CharCode should
2184 * take precedence over any keyCode value for accurate
2187 customEvent.keyCode = (charCode > 0) ? charCode : keyCode;
2190 target.fireEvent("on" + type, customEvent);
2193 throw new Error("simulateKeyEvent(): No event simulation framework present.");
2198 * Simulates a mouse event using the given event information to populate
2199 * the generated event object. This method does browser-equalizing
2200 * calculations to account for differences in the DOM and IE event models
2201 * as well as different browser quirks.
2202 * @method simulateMouseEvent
2205 * @param {HTMLElement} target The target of the given event.
2206 * @param {String} type The type of event to fire. This can be any one of
2207 * the following: click, dblclick, mousedown, mouseup, mouseout,
2208 * mouseover, and mousemove.
2209 * @param {Boolean} bubbles (Optional) Indicates if the event can be
2210 * bubbled up. DOM Level 2 specifies that all mouse events bubble by
2211 * default. The default is true.
2212 * @param {Boolean} cancelable (Optional) Indicates if the event can be
2213 * canceled using preventDefault(). DOM Level 2 specifies that all
2214 * mouse events except mousemove can be cancelled. The default
2215 * is true for all events except mousemove, for which the default
2217 * @param {Window} view (Optional) The view containing the target. This is
2218 * typically the window object. The default is window.
2219 * @param {int} detail (Optional) The number of times the mouse button has
2220 * been used. The default value is 1.
2221 * @param {int} screenX (Optional) The x-coordinate on the screen at which
2222 * point the event occured. The default is 0.
2223 * @param {int} screenY (Optional) The y-coordinate on the screen at which
2224 * point the event occured. The default is 0.
2225 * @param {int} clientX (Optional) The x-coordinate on the client at which
2226 * point the event occured. The default is 0.
2227 * @param {int} clientY (Optional) The y-coordinate on the client at which
2228 * point the event occured. The default is 0.
2229 * @param {Boolean} ctrlKey (Optional) Indicates if one of the CTRL keys
2230 * is pressed while the event is firing. The default is false.
2231 * @param {Boolean} altKey (Optional) Indicates if one of the ALT keys
2232 * is pressed while the event is firing. The default is false.
2233 * @param {Boolean} shiftKey (Optional) Indicates if one of the SHIFT keys
2234 * is pressed while the event is firing. The default is false.
2235 * @param {Boolean} metaKey (Optional) Indicates if one of the META keys
2236 * is pressed while the event is firing. The default is false.
2237 * @param {int} button (Optional) The button being pressed while the event
2238 * is executing. The value should be 0 for the primary mouse button
2239 * (typically the left button), 1 for the terciary mouse button
2240 * (typically the middle button), and 2 for the secondary mouse button
2241 * (typically the right button). The default is 0.
2242 * @param {HTMLElement} relatedTarget (Optional) For mouseout events,
2243 * this is the element that the mouse has moved to. For mouseover
2244 * events, this is the element that the mouse has moved from. This
2245 * argument is ignored for all other events. The default is null.
2247 simulateMouseEvent : function (target /*:HTMLElement*/, type /*:String*/,
2248 bubbles /*:Boolean*/, cancelable /*:Boolean*/,
2249 view /*:Window*/, detail /*:int*/,
2250 screenX /*:int*/, screenY /*:int*/,
2251 clientX /*:int*/, clientY /*:int*/,
2252 ctrlKey /*:Boolean*/, altKey /*:Boolean*/,
2253 shiftKey /*:Boolean*/, metaKey /*:Boolean*/,
2254 button /*:int*/, relatedTarget /*:HTMLElement*/) /*:Void*/
2258 target = YAHOO.util.Dom.get(target);
2260 throw new Error("simulateMouseEvent(): Invalid target.");
2264 if (YAHOO.lang.isString(type)){
2265 type = type.toLowerCase();
2276 throw new Error("simulateMouseEvent(): Event type '" + type + "' not supported.");
2279 throw new Error("simulateMouseEvent(): Event type must be a string.");
2282 //setup default values
2283 if (!YAHOO.lang.isBoolean(bubbles)){
2284 bubbles = true; //all mouse events bubble
2286 if (!YAHOO.lang.isBoolean(cancelable)){
2287 cancelable = (type != "mousemove"); //mousemove is the only one that can't be cancelled
2289 if (!YAHOO.lang.isObject(view)){
2290 view = window; //view is typically window
2292 if (!YAHOO.lang.isNumber(detail)){
2293 detail = 1; //number of mouse clicks must be at least one
2295 if (!YAHOO.lang.isNumber(screenX)){
2298 if (!YAHOO.lang.isNumber(screenY)){
2301 if (!YAHOO.lang.isNumber(clientX)){
2304 if (!YAHOO.lang.isNumber(clientY)){
2307 if (!YAHOO.lang.isBoolean(ctrlKey)){
2310 if (!YAHOO.lang.isBoolean(altKey)){
2313 if (!YAHOO.lang.isBoolean(shiftKey)){
2316 if (!YAHOO.lang.isBoolean(metaKey)){
2319 if (!YAHOO.lang.isNumber(button)){
2323 //try to create a mouse event
2324 var customEvent /*:MouseEvent*/ = null;
2326 //check for DOM-compliant browsers first
2327 if (YAHOO.lang.isFunction(document.createEvent)){
2329 customEvent = document.createEvent("MouseEvents");
2331 //Safari 2.x (WebKit 418) still doesn't implement initMouseEvent()
2332 if (customEvent.initMouseEvent){
2333 customEvent.initMouseEvent(type, bubbles, cancelable, view, detail,
2334 screenX, screenY, clientX, clientY,
2335 ctrlKey, altKey, shiftKey, metaKey,
2336 button, relatedTarget);
2339 //the closest thing available in Safari 2.x is UIEvents
2340 customEvent = document.createEvent("UIEvents");
2341 customEvent.initEvent(type, bubbles, cancelable);
2342 customEvent.view = view;
2343 customEvent.detail = detail;
2344 customEvent.screenX = screenX;
2345 customEvent.screenY = screenY;
2346 customEvent.clientX = clientX;
2347 customEvent.clientY = clientY;
2348 customEvent.ctrlKey = ctrlKey;
2349 customEvent.altKey = altKey;
2350 customEvent.metaKey = metaKey;
2351 customEvent.shiftKey = shiftKey;
2352 customEvent.button = button;
2353 customEvent.relatedTarget = relatedTarget;
2357 * Check to see if relatedTarget has been assigned. Firefox
2358 * versions less than 2.0 don't allow it to be assigned via
2359 * initMouseEvent() and the property is readonly after event
2360 * creation, so in order to keep YAHOO.util.getRelatedTarget()
2361 * working, assign to the IE proprietary toElement property
2362 * for mouseout event and fromElement property for mouseover
2365 if (relatedTarget && !customEvent.relatedTarget){
2366 if (type == "mouseout"){
2367 customEvent.toElement = relatedTarget;
2368 } else if (type == "mouseover"){
2369 customEvent.fromElement = relatedTarget;
2374 target.dispatchEvent(customEvent);
2376 } else if (YAHOO.lang.isObject(document.createEventObject)){ //IE
2378 //create an IE event object
2379 customEvent = document.createEventObject();
2381 //assign available properties
2382 customEvent.bubbles = bubbles;
2383 customEvent.cancelable = cancelable;
2384 customEvent.view = view;
2385 customEvent.detail = detail;
2386 customEvent.screenX = screenX;
2387 customEvent.screenY = screenY;
2388 customEvent.clientX = clientX;
2389 customEvent.clientY = clientY;
2390 customEvent.ctrlKey = ctrlKey;
2391 customEvent.altKey = altKey;
2392 customEvent.metaKey = metaKey;
2393 customEvent.shiftKey = shiftKey;
2395 //fix button property for IE's wacky implementation
2398 customEvent.button = 1;
2401 customEvent.button = 4;
2407 customEvent.button = 0;
2411 * Have to use relatedTarget because IE won't allow assignment
2412 * to toElement or fromElement on generic events. This keeps
2413 * YAHOO.util.customEvent.getRelatedTarget() functional.
2415 customEvent.relatedTarget = relatedTarget;
2418 target.fireEvent("on" + type, customEvent);
2421 throw new Error("simulateMouseEvent(): No event simulation framework present.");
2425 //--------------------------------------------------------------------------
2427 //--------------------------------------------------------------------------
2430 * Simulates a mouse event on a particular element.
2431 * @param {HTMLElement} target The element to click on.
2432 * @param {String} type The type of event to fire. This can be any one of
2433 * the following: click, dblclick, mousedown, mouseup, mouseout,
2434 * mouseover, and mousemove.
2435 * @param {Object} options Additional event options (use DOM standard names).
2436 * @method mouseEvent
2439 fireMouseEvent : function (target /*:HTMLElement*/, type /*:String*/,
2440 options /*:Object*/) /*:Void*/
2442 options = options || {};
2443 this.simulateMouseEvent(target, type, options.bubbles,
2444 options.cancelable, options.view, options.detail, options.screenX,
2445 options.screenY, options.clientX, options.clientY, options.ctrlKey,
2446 options.altKey, options.shiftKey, options.metaKey, options.button,
2447 options.relatedTarget);
2451 * Simulates a click on a particular element.
2452 * @param {HTMLElement} target The element to click on.
2453 * @param {Object} options Additional event options (use DOM standard names).
2457 click : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2458 this.fireMouseEvent(target, "click", options);
2462 * Simulates a double click on a particular element.
2463 * @param {HTMLElement} target The element to double click on.
2464 * @param {Object} options Additional event options (use DOM standard names).
2468 dblclick : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2469 this.fireMouseEvent( target, "dblclick", options);
2473 * Simulates a mousedown on a particular element.
2474 * @param {HTMLElement} target The element to act on.
2475 * @param {Object} options Additional event options (use DOM standard names).
2479 mousedown : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2480 this.fireMouseEvent(target, "mousedown", options);
2484 * Simulates a mousemove on a particular element.
2485 * @param {HTMLElement} target The element to act on.
2486 * @param {Object} options Additional event options (use DOM standard names).
2490 mousemove : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2491 this.fireMouseEvent(target, "mousemove", options);
2495 * Simulates a mouseout event on a particular element. Use "relatedTarget"
2496 * on the options object to specify where the mouse moved to.
2497 * Quirks: Firefox less than 2.0 doesn't set relatedTarget properly, so
2498 * toElement is assigned in its place. IE doesn't allow toElement to be
2499 * be assigned, so relatedTarget is assigned in its place. Both of these
2500 * concessions allow YAHOO.util.Event.getRelatedTarget() to work correctly
2502 * @param {HTMLElement} target The element to act on.
2503 * @param {Object} options Additional event options (use DOM standard names).
2507 mouseout : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2508 this.fireMouseEvent(target, "mouseout", options);
2512 * Simulates a mouseover event on a particular element. Use "relatedTarget"
2513 * on the options object to specify where the mouse moved from.
2514 * Quirks: Firefox less than 2.0 doesn't set relatedTarget properly, so
2515 * fromElement is assigned in its place. IE doesn't allow fromElement to be
2516 * be assigned, so relatedTarget is assigned in its place. Both of these
2517 * concessions allow YAHOO.util.Event.getRelatedTarget() to work correctly
2519 * @param {HTMLElement} target The element to act on.
2520 * @param {Object} options Additional event options (use DOM standard names).
2524 mouseover : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2525 this.fireMouseEvent(target, "mouseover", options);
2529 * Simulates a mouseup on a particular element.
2530 * @param {HTMLElement} target The element to act on.
2531 * @param {Object} options Additional event options (use DOM standard names).
2535 mouseup : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2536 this.fireMouseEvent(target, "mouseup", options);
2539 //--------------------------------------------------------------------------
2541 //--------------------------------------------------------------------------
2544 * Fires an event that normally would be fired by the keyboard (keyup,
2545 * keydown, keypress). Make sure to specify either keyCode or charCode as
2548 * @param {String} type The type of event ("keyup", "keydown" or "keypress").
2549 * @param {HTMLElement} target The target of the event.
2550 * @param {Object} options Options for the event. Either keyCode or charCode
2552 * @method fireKeyEvent
2555 fireKeyEvent : function (type /*:String*/, target /*:HTMLElement*/,
2556 options /*:Object*/) /*:Void*/
2558 options = options || {};
2559 this.simulateKeyEvent(target, type, options.bubbles,
2560 options.cancelable, options.view, options.ctrlKey,
2561 options.altKey, options.shiftKey, options.metaKey,
2562 options.keyCode, options.charCode);
2566 * Simulates a keydown event on a particular element.
2567 * @param {HTMLElement} target The element to act on.
2568 * @param {Object} options Additional event options (use DOM standard names).
2572 keydown : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2573 this.fireKeyEvent("keydown", target, options);
2577 * Simulates a keypress on a particular element.
2578 * @param {HTMLElement} target The element to act on.
2579 * @param {Object} options Additional event options (use DOM standard names).
2583 keypress : function (target /*:HTMLElement*/, options /*:Object*/) /*:Void*/ {
2584 this.fireKeyEvent("keypress", target, options);
2588 * Simulates a keyup event on a particular element.
2589 * @param {HTMLElement} target The element to act on.
2590 * @param {Object} options Additional event options (use DOM standard names).
2594 keyup : function (target /*:HTMLElement*/, options /*Object*/) /*:Void*/ {
2595 this.fireKeyEvent("keyup", target, options);
2601 YAHOO.namespace("tool");
2603 //-----------------------------------------------------------------------------
2604 // TestManager object
2605 //-----------------------------------------------------------------------------
2608 * Runs pages containing test suite definitions.
2609 * @namespace YAHOO.tool
2610 * @class TestManager
2613 YAHOO.tool.TestManager = {
2616 * Constant for the testpagebegin custom event
2617 * @property TEST_PAGE_BEGIN_EVENT
2622 TEST_PAGE_BEGIN_EVENT /*:String*/ : "testpagebegin",
2625 * Constant for the testpagecomplete custom event
2626 * @property TEST_PAGE_COMPLETE_EVENT
2631 TEST_PAGE_COMPLETE_EVENT /*:String*/ : "testpagecomplete",
2634 * Constant for the testmanagerbegin custom event
2635 * @property TEST_MANAGER_BEGIN_EVENT
2640 TEST_MANAGER_BEGIN_EVENT /*:String*/ : "testmanagerbegin",
2643 * Constant for the testmanagercomplete custom event
2644 * @property TEST_MANAGER_COMPLETE_EVENT
2649 TEST_MANAGER_COMPLETE_EVENT /*:String*/ : "testmanagercomplete",
2651 //-------------------------------------------------------------------------
2652 // Private Properties
2653 //-------------------------------------------------------------------------
2657 * The URL of the page currently being executed.
2660 * @property _curPage
2663 _curPage /*:String*/ : null,
2666 * The frame used to load and run tests.
2672 _frame /*:Window*/ : null,
2675 * The logger used to output results from the various tests.
2676 * @type YAHOO.tool.TestLogger
2684 * The timeout ID for the next iteration through the tests.
2687 * @property _timeoutId
2690 _timeoutId /*:int*/ : 0,
2693 * Array of pages to load.
2699 _pages /*:String[]*/ : [],
2702 * Aggregated results
2705 * @property _results
2710 //-------------------------------------------------------------------------
2712 //-------------------------------------------------------------------------
2715 * Handles TestRunner.COMPLETE_EVENT, storing the results and beginning
2717 * @param {Object} data Data about the event.
2722 _handleTestRunnerComplete : function (data /*:Object*/) /*:Void*/ {
2724 this.fireEvent(this.TEST_PAGE_COMPLETE_EVENT, {
2725 page: this._curPage,
2726 results: data.results
2730 //this._results[this.curPage] = data.results;
2733 this._processResults(this._curPage, data.results);
2735 this._logger.clearTestRunner();
2737 //if there's more to do, set a timeout to begin again
2738 if (this._pages.length){
2739 this._timeoutId = setTimeout(function(){
2740 YAHOO.tool.TestManager._run();
2743 this.fireEvent(this.TEST_MANAGER_COMPLETE_EVENT, this._results);
2748 * Processes the results of a test page run, outputting log messages
2754 _processResults : function (page /*:String*/, results /*:Object*/) /*:Void*/ {
2756 var r = this._results;
2758 r.passed += results.passed;
2759 r.failed += results.failed;
2760 r.ignored += results.ignored;
2761 r.total += results.total;
2762 r.duration += results.duration;
2764 if (results.failed){
2765 r.failedPages.push(page);
2767 r.passedPages.push(page);
2770 results.name = page;
2771 results.type = "page";
2777 * Loads the next test page into the iframe.
2782 _run : function () /*:Void*/ {
2784 //set the current page
2785 this._curPage = this._pages.shift();
2787 this.fireEvent(this.TEST_PAGE_BEGIN_EVENT, this._curPage);
2789 //load the frame - destroy history in case there are other iframes that
2791 this._frame.location.replace(this._curPage);
2795 //-------------------------------------------------------------------------
2797 //-------------------------------------------------------------------------
2800 * Signals that a test page has been loaded. This should be called from
2801 * within the test page itself to notify the TestManager that it is ready.
2805 load : function () /*:Void*/ {
2806 if (parent.YAHOO.tool.TestManager !== this){
2807 parent.YAHOO.tool.TestManager.load();
2811 //assign event handling
2812 var TestRunner = this._frame.YAHOO.tool.TestRunner;
2814 this._logger.setTestRunner(TestRunner);
2815 TestRunner.subscribe(TestRunner.COMPLETE_EVENT, this._handleTestRunnerComplete, this, true);
2824 * Sets the pages to be loaded.
2825 * @param {String[]} pages An array of URLs to load.
2829 setPages : function (pages /*:String[]*/) /*:Void*/ {
2830 this._pages = pages;
2834 * Begins the process of running the tests.
2838 start : function () /*:Void*/ {
2840 if (!this._initialized) {
2843 * Fires when loading a test page
2844 * @event testpagebegin
2845 * @param curPage {string} the page being loaded
2848 this.createEvent(this.TEST_PAGE_BEGIN_EVENT);
2851 * Fires when a test page is complete
2852 * @event testpagecomplete
2853 * @param obj {page: string, results: object} the name of the
2854 * page that was loaded, and the test suite results
2857 this.createEvent(this.TEST_PAGE_COMPLETE_EVENT);
2860 * Fires when the test manager starts running all test pages
2861 * @event testmanagerbegin
2864 this.createEvent(this.TEST_MANAGER_BEGIN_EVENT);
2867 * Fires when the test manager finishes running all test pages. External
2868 * test runners should subscribe to this event in order to get the
2869 * aggregated test results.
2870 * @event testmanagercomplete
2871 * @param obj { pages_passed: int, pages_failed: int, tests_passed: int
2872 * tests_failed: int, passed: string[], failed: string[],
2873 * page_results: {} }
2876 this.createEvent(this.TEST_MANAGER_COMPLETE_EVENT);
2878 //create iframe if not already available
2880 var frame /*:HTMLElement*/ = document.createElement("iframe");
2881 frame.style.visibility = "hidden";
2882 frame.style.position = "absolute";
2883 document.body.appendChild(frame);
2884 this._frame = frame.contentWindow || frame.contentDocument.parentWindow;
2887 //create test logger if not already available
2889 this._logger = new YAHOO.tool.TestLogger();
2892 this._initialized = true;
2896 // reset the results cache
2904 name: "YUI Test Results",
2909 // number of pages that pass
2911 // number of pages that fail
2913 // total number of tests passed
2915 // total number of tests failed
2917 // array of pages that passed
2919 // array of pages that failed
2921 // map of full results for each page
2925 this.fireEvent(this.TEST_MANAGER_BEGIN_EVENT, null);
2931 * Stops the execution of tests.
2935 stop : function () /*:Void*/ {
2936 clearTimeout(this._timeoutId);
2941 YAHOO.lang.augmentObject(YAHOO.tool.TestManager, YAHOO.util.EventProvider.prototype);
2944 YAHOO.namespace("tool");
2946 //-----------------------------------------------------------------------------
2947 // TestLogger object
2948 //-----------------------------------------------------------------------------
2951 * Displays test execution progress and results, providing filters based on
2952 * different key events.
2953 * @namespace YAHOO.tool
2956 * @param {HTMLElement} element (Optional) The element to create the logger in.
2957 * @param {Object} config (Optional) Configuration options for the logger.
2959 YAHOO.tool.TestLogger = function (element, config) {
2960 YAHOO.tool.TestLogger.superclass.constructor.call(this, element, config);
2964 YAHOO.lang.extend(YAHOO.tool.TestLogger, YAHOO.widget.LogReader, {
2966 footerEnabled : true,
2967 newestOnTop : false,
2970 * Formats message string to HTML for output to console.
2973 * @param oLogMsg {Object} Log message object.
2974 * @return {String} HTML-formatted message for output to console.
2976 formatMsg : function(message /*:Object*/) {
2978 var category /*:String*/ = message.category;
2979 var text /*:String*/ = this.html2Text(message.msg);
2981 return "<pre><p><span class=\"" + category + "\">" + category.toUpperCase() + "</span> " + text + "</p></pre>";
2985 //-------------------------------------------------------------------------
2987 //-------------------------------------------------------------------------
2990 * Initializes the logger.
2993 init : function () {
2995 //attach to any available TestRunner
2996 if (YAHOO.tool.TestRunner){
2997 this.setTestRunner(YAHOO.tool.TestRunner);
3000 //hide useless sources
3001 this.hideSource("global");
3002 this.hideSource("LogReader");
3004 //hide useless message categories
3005 this.hideCategory("warn");
3006 this.hideCategory("window");
3007 this.hideCategory("time");
3010 this.clearConsole();
3014 * Clears the reference to the TestRunner from previous operations. This
3015 * unsubscribes all events and removes the object reference.
3019 clearTestRunner : function () /*:Void*/ {
3021 this._runner.unsubscribeAll();
3022 this._runner = null;
3027 * Sets the source test runner that the logger should monitor.
3028 * @param {YAHOO.tool.TestRunner} testRunner The TestRunner to observe.
3032 setTestRunner : function (testRunner /*:YAHOO.tool.TestRunner*/) /*:Void*/ {
3035 this.clearTestRunner();
3038 this._runner = testRunner;
3040 //setup event _handlers
3041 testRunner.subscribe(testRunner.TEST_PASS_EVENT, this._handleTestRunnerEvent, this, true);
3042 testRunner.subscribe(testRunner.TEST_FAIL_EVENT, this._handleTestRunnerEvent, this, true);
3043 testRunner.subscribe(testRunner.TEST_IGNORE_EVENT, this._handleTestRunnerEvent, this, true);
3044 testRunner.subscribe(testRunner.BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
3045 testRunner.subscribe(testRunner.COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
3046 testRunner.subscribe(testRunner.TEST_SUITE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
3047 testRunner.subscribe(testRunner.TEST_SUITE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
3048 testRunner.subscribe(testRunner.TEST_CASE_BEGIN_EVENT, this._handleTestRunnerEvent, this, true);
3049 testRunner.subscribe(testRunner.TEST_CASE_COMPLETE_EVENT, this._handleTestRunnerEvent, this, true);
3052 //-------------------------------------------------------------------------
3054 //-------------------------------------------------------------------------
3057 * Handles all TestRunner events, outputting appropriate data into the console.
3058 * @param {Object} data The event data object.
3062 _handleTestRunnerEvent : function (data /*:Object*/) /*:Void*/ {
3064 //shortcut variables
3065 var TestRunner /*:Object*/ = YAHOO.tool.TestRunner;
3068 var message /*:String*/ = "";
3069 var messageType /*:String*/ = "";
3072 case TestRunner.BEGIN_EVENT:
3073 message = "Testing began at " + (new Date()).toString() + ".";
3074 messageType = "info";
3077 case TestRunner.COMPLETE_EVENT:
3078 message = "Testing completed at " + (new Date()).toString() + ".\nPassed:" +
3079 data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
3080 messageType = "info";
3083 case TestRunner.TEST_FAIL_EVENT:
3084 message = data.testName + ": " + data.error.getMessage();
3085 messageType = "fail";
3088 case TestRunner.TEST_IGNORE_EVENT:
3089 message = data.testName + ": ignored.";
3090 messageType = "ignore";
3093 case TestRunner.TEST_PASS_EVENT:
3094 message = data.testName + ": passed.";
3095 messageType = "pass";
3098 case TestRunner.TEST_SUITE_BEGIN_EVENT:
3099 message = "Test suite \"" + data.testSuite.name + "\" started.";
3100 messageType = "info";
3103 case TestRunner.TEST_SUITE_COMPLETE_EVENT:
3104 message = "Test suite \"" + data.testSuite.name + "\" completed.\nPassed:" +
3105 data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
3106 messageType = "info";
3109 case TestRunner.TEST_CASE_BEGIN_EVENT:
3110 message = "Test case \"" + data.testCase.name + "\" started.";
3111 messageType = "info";
3114 case TestRunner.TEST_CASE_COMPLETE_EVENT:
3115 message = "Test case \"" + data.testCase.name + "\" completed.\nPassed:" +
3116 data.results.passed + " Failed:" + data.results.failed + " Total:" + data.results.total;
3117 messageType = "info";
3120 message = "Unexpected event " + data.type;
3124 YAHOO.log(message, messageType, "TestRunner");
3129 YAHOO.namespace("tool.TestFormat");
3132 * Returns test results formatted as a JSON string. Requires JSON utility.
3133 * @param {Object} result The results object created by TestRunner.
3134 * @return {String} An XML-formatted string of results.
3135 * @namespace YAHOO.tool.TestFormat
3139 YAHOO.tool.TestFormat.JSON = function(results /*:Object*/) /*:String*/ {
3140 return YAHOO.lang.JSON.stringify(results);
3144 * Returns test results formatted as an XML string.
3145 * @param {Object} result The results object created by TestRunner.
3146 * @return {String} An XML-formatted string of results.
3147 * @namespace YAHOO.tool.TestFormat
3151 YAHOO.tool.TestFormat.XML = function(results /*:Object*/) /*:String*/ {
3154 var xml /*:String*/ = "<" + results.type + " name=\"" + results.name.replace(/"/g, """).replace(/'/g, "'") + "\"";
3156 if (l.isNumber(results.duration)){
3157 xml += " duration=\"" + results.duration + "\"";
3160 if (results.type == "test"){
3161 xml += " result=\"" + results.result + "\" message=\"" + results.message + "\">";
3163 xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
3164 for (var prop in results) {
3165 if (l.hasOwnProperty(results, prop) && l.isObject(results[prop]) && !l.isArray(results[prop])){
3166 xml += arguments.callee(results[prop]);
3171 xml += "</" + results.type + ">";
3177 YAHOO.namespace("tool");
3180 * An object capable of sending test results to a server.
3181 * @param {String} url The URL to submit the results to.
3182 * @param {Function} format (Optiona) A function that outputs the results in a specific format.
3183 * Default is YAHOO.tool.TestFormat.XML.
3185 * @namespace YAHOO.tool
3186 * @class TestReporter
3188 YAHOO.tool.TestReporter = function(url /*:String*/, format /*:Function*/) {
3191 * The URL to submit the data to.
3195 this.url /*:String*/ = url;
3198 * The formatting function to call when submitting the data.
3202 this.format /*:Function*/ = format || YAHOO.tool.TestFormat.XML;
3205 * Extra fields to submit with the request.
3210 this._fields /*:Object*/ = new Object();
3213 * The form element used to submit the results.
3214 * @type HTMLFormElement
3218 this._form /*:HTMLElement*/ = null;
3221 * Iframe used as a target for form submission.
3222 * @type HTMLIFrameElement
3226 this._iframe /*:HTMLElement*/ = null;
3229 YAHOO.tool.TestReporter.prototype = {
3231 //restore missing constructor
3232 constructor: YAHOO.tool.TestReporter,
3235 * Convert a date into ISO format.
3236 * From Douglas Crockford's json2.js
3237 * @param {Date} date The date to convert.
3238 * @return {String} An ISO-formatted date string
3239 * @method _convertToISOString
3242 _convertToISOString: function(date){
3244 // Format integers to have at least two digits.
3245 return n < 10 ? '0' + n : n;
3248 return date.getUTCFullYear() + '-' +
3249 f(date.getUTCMonth() + 1) + '-' +
3250 f(date.getUTCDate()) + 'T' +
3251 f(date.getUTCHours()) + ':' +
3252 f(date.getUTCMinutes()) + ':' +
3253 f(date.getUTCSeconds()) + 'Z';
3258 * Adds a field to the form that submits the results.
3259 * @param {String} name The name of the field.
3260 * @param {Variant} value The value of the field.
3264 addField : function (name /*:String*/, value /*:Variant*/) /*:Void*/{
3265 this._fields[name] = value;
3269 * Removes all previous defined fields.
3273 clearFields : function() /*:Void*/{
3274 this._fields = new Object();
3278 * Cleans up the memory associated with the TestReporter, removing DOM elements
3279 * that were created.
3283 destroy : function() /*:Void*/ {
3285 this._form.parentNode.removeChild(this._form);
3289 this._iframe.parentNode.removeChild(this._iframe);
3290 this._iframe = null;
3292 this._fields = null;
3296 * Sends the report to the server.
3297 * @param {Object} results The results object created by TestRunner.
3301 report : function(results /*:Object*/) /*:Void*/{
3303 //if the form hasn't been created yet, create it
3305 this._form = document.createElement("form");
3306 this._form.method = "post";
3307 this._form.style.visibility = "hidden";
3308 this._form.style.position = "absolute";
3309 this._form.style.top = 0;
3310 document.body.appendChild(this._form);
3312 //IE won't let you assign a name using the DOM, must do it the hacky way
3313 if (YAHOO.env.ua.ie){
3314 this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
3316 this._iframe = document.createElement("iframe");
3317 this._iframe.name = "yuiTestTarget";
3320 this._iframe.src = "javascript:false";
3321 this._iframe.style.visibility = "hidden";
3322 this._iframe.style.position = "absolute";
3323 this._iframe.style.top = 0;
3324 document.body.appendChild(this._iframe);
3326 this._form.target = "yuiTestTarget";
3329 //set the form's action
3330 this._form.action = this.url;
3332 //remove any existing fields
3333 while(this._form.hasChildNodes()){
3334 this._form.removeChild(this._form.lastChild);
3337 //create default fields
3338 this._fields.results = this.format(results);
3339 this._fields.useragent = navigator.userAgent;
3340 this._fields.timestamp = this._convertToISOString(new Date());
3342 //add fields to the form
3343 for (var prop in this._fields){
3344 if (YAHOO.lang.hasOwnProperty(this._fields, prop) && typeof this._fields[prop] != "function"){
3345 if (YAHOO.env.ua.ie){
3346 input = document.createElement("<input name=\"" + prop + "\" >");
3348 input = document.createElement("input");
3351 input.type = "hidden";
3352 input.value = this._fields[prop];
3353 this._form.appendChild(input);
3357 //remove default fields
3358 delete this._fields.results;
3359 delete this._fields.useragent;
3360 delete this._fields.timestamp;
3362 if (arguments[1] !== false){
3363 this._form.submit();
3370 YAHOO.register("yuitest", YAHOO.tool.TestRunner, {version: "2.7.0", build: "1799"});