3 Copyright 2012 Yahoo! Inc. All rights reserved.
4 Licensed under the BSD License.
5 http://yuilibrary.com/license/
7 YUI.add('test', function(Y) {
15 * The root namespace for YUI Test.
27 //Using internal YUI methods here
28 YUITest.Object = Y.Object;
29 YUITest.Array = Y.Array;
36 * Simple custom event implementation.
41 YUITest.EventTarget = function(){
44 * Event handlers for the various events.
54 YUITest.EventTarget.prototype = {
57 constructor: YUITest.EventTarget,
59 //-------------------------------------------------------------------------
61 //-------------------------------------------------------------------------
64 * Adds a listener for a given event type.
65 * @param {String} type The type of event to add a listener for.
66 * @param {Function} listener The function to call when the event occurs.
70 attach: function(type, listener){
71 if (typeof this._handlers[type] == "undefined"){
72 this._handlers[type] = [];
75 this._handlers[type].push(listener);
79 * Adds a listener for a given event type.
80 * @param {String} type The type of event to add a listener for.
81 * @param {Function} listener The function to call when the event occurs.
86 subscribe: function(type, listener){
87 this.attach.apply(this, arguments);
91 * Fires an event based on the passed-in object.
92 * @param {Object|String} event An object with at least a 'type' attribute
93 * or a string indicating the event name.
97 fire: function(event){
98 if (typeof event == "string"){
99 event = { type: event };
106 throw new Error("Event object missing 'type' property.");
109 if (this._handlers[event.type] instanceof Array){
110 var handlers = this._handlers[event.type];
111 for (var i=0, len=handlers.length; i < len; i++){
112 handlers[i].call(this, event);
118 * Removes a listener for a given event type.
119 * @param {String} type The type of event to remove a listener from.
120 * @param {Function} listener The function to remove from the event.
124 detach: function(type, listener){
125 if (this._handlers[type] instanceof Array){
126 var handlers = this._handlers[type];
127 for (var i=0, len=handlers.length; i < len; i++){
128 if (handlers[i] === listener){
129 handlers.splice(i, 1);
137 * Removes a listener for a given event type.
138 * @param {String} type The type of event to remove a listener from.
139 * @param {Function} listener The function to remove from the event.
141 * @method unsubscribe
144 unsubscribe: function(type, listener){
145 this.detach.apply(this, arguments);
152 * A test suite that can contain a collection of TestCase and TestSuite objects.
153 * @param {String||Object} data The name of the test suite or an object containing
154 * a name property as well as setUp and tearDown methods.
159 YUITest.TestSuite = function (data) {
162 * The name of the test suite.
169 * Array of test suites and test cases.
176 //initialize the properties
177 if (typeof data == "string"){
179 } else if (data instanceof Object){
180 for (var prop in data){
181 if (data.hasOwnProperty(prop)){
182 this[prop] = data[prop];
188 if (this.name === ""){
189 this.name = "testSuite" + (+new Date());
194 YUITest.TestSuite.prototype = {
196 //restore constructor
197 constructor: YUITest.TestSuite,
200 * Adds a test suite or test case to the test suite.
201 * @param {Test.TestSuite||YUITest.TestCase} testObject The test suite or test case to add.
205 add : function (testObject) {
206 if (testObject instanceof YUITest.TestSuite || testObject instanceof YUITest.TestCase) {
207 this.items.push(testObject);
212 //-------------------------------------------------------------------------
214 //-------------------------------------------------------------------------
217 * Function to run before each test is executed.
221 setUp : function () {
225 * Function to run after each test is executed.
229 tearDown: function () {
234 * Test case containing various tests to run.
235 * @param template An object containing any number of test methods, other methods,
236 * an optional name, and anything else the test case needs.
241 YUITest.TestCase = function (template) {
244 * Special rules for the test case. Possible subobjects
245 * are fail, for tests that should fail, and error, for
246 * tests that should throw an error.
250 //copy over all properties from the template to this object
251 for (var prop in template) {
252 this[prop] = template[prop];
255 //check for a valid name
256 if (typeof this.name != "string"){
257 this.name = "testCase" + (+new Date());
262 YUITest.TestCase.prototype = {
264 //restore constructor
265 constructor: YUITest.TestCase,
268 * Method to call from an async init method to
269 * restart the test case. When called, returns a function
270 * that should be called when tests are ready to continue.
272 * @return {Function} The function to call as a callback.
274 callback: function(){
275 return YUITest.TestRunner.callback.apply(YUITest.TestRunner,arguments);
279 * Resumes a paused test and runs the given function.
280 * @param {Function} segment (Optional) The function to run.
281 * If omitted, the test automatically passes.
285 resume : function (segment) {
286 YUITest.TestRunner.resume(segment);
290 * Causes the test case to wait a specified amount of time and then
291 * continue executing the given code.
292 * @param {Function} segment (Optional) The function to run after the delay.
293 * If omitted, the TestRunner will wait until resume() is called.
294 * @param {int} delay (Optional) The number of milliseconds to wait before running
295 * the function. If omitted, defaults to zero.
299 wait : function (segment, delay){
301 var actualDelay = (typeof segment == "number" ? segment : delay);
302 actualDelay = (typeof actualDelay == "number" ? actualDelay : 10000);
304 if (typeof segment == "function"){
305 throw new YUITest.Wait(segment, actualDelay);
307 throw new YUITest.Wait(function(){
308 YUITest.Assert.fail("Timeout: wait() called but resume() never called.");
313 //-------------------------------------------------------------------------
315 //-------------------------------------------------------------------------
318 * Asserts that a given condition is true. If not, then a YUITest.AssertionError object is thrown
319 * and the test fails.
321 * @param {Boolean} condition The condition to test.
322 * @param {String} message The message to display if the assertion fails.
324 assert : function (condition, message){
325 YUITest.Assert._increment();
327 throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Assertion failed."));
332 * Forces an assertion error to occur. Shortcut for YUITest.Assert.fail().
334 * @param {String} message (Optional) The message to display with the failure.
336 fail: function (message) {
337 YUITest.Assert.fail(message);
340 //-------------------------------------------------------------------------
342 //-------------------------------------------------------------------------
345 * Function to run once before tests start to run.
346 * This executes before the first call to setUp().
353 * Function to run once after tests finish running.
354 * This executes after the last call to tearDown().
361 * Function to run before each test is executed.
365 setUp : function () {
370 * Function to run after each test is executed.
374 tearDown: function () {
379 * An object object containing test result formatting methods.
384 YUITest.TestFormat = function(){
386 /* (intentionally not documented)
387 * Basic XML escaping method. Replaces quotes, less-than, greater-than,
388 * apostrophe, and ampersand characters with their corresponding entities.
389 * @param {String} text The text to encode.
390 * @return {String} The XML-escaped text.
392 function xmlEscape(text){
394 return text.replace(/[<>"'&]/g, function(value){
396 case "<": return "<";
397 case ">": return ">";
398 case "\"": return """;
399 case "'": return "'";
400 case "&": return "&";
410 * Returns test results formatted as a JSON string. Requires JSON utility.
411 * @param {Object} result The results object created by TestRunner.
412 * @return {String} A JSON-formatted string of results.
416 JSON: function(results) {
417 return YUITest.Util.JSON.stringify(results);
421 * Returns test results formatted as an XML string.
422 * @param {Object} result The results object created by TestRunner.
423 * @return {String} An XML-formatted string of results.
427 XML: function(results) {
429 function serializeToXML(results){
430 var xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
432 if (typeof(results.duration)=="number"){
433 xml += " duration=\"" + results.duration + "\"";
436 if (results.type == "test"){
437 xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
439 xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
440 for (var prop in results){
441 if (results.hasOwnProperty(prop)){
442 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
443 xml += serializeToXML(results[prop]);
449 xml += "</" + results.type + ">";
454 return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToXML(results);
460 * Returns test results formatted in JUnit XML format.
461 * @param {Object} result The results object created by TestRunner.
462 * @return {String} An XML-formatted string of results.
466 JUnitXML: function(results) {
468 function serializeToJUnitXML(results){
471 switch (results.type){
472 //equivalent to testcase in JUnit
474 if (results.result != "ignore"){
475 xml = "<testcase name=\"" + xmlEscape(results.name) + "\" time=\"" + (results.duration/1000) + "\">";
476 if (results.result == "fail"){
477 xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
483 //equivalent to testsuite in JUnit
486 xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\" time=\"" + (results.duration/1000) + "\">";
488 for (var prop in results){
489 if (results.hasOwnProperty(prop)){
490 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
491 xml += serializeToJUnitXML(results[prop]);
496 xml += "</testsuite>";
499 //no JUnit equivalent, don't output anything
501 for (var prop in results){
502 if (results.hasOwnProperty(prop)){
503 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
504 xml += serializeToJUnitXML(results[prop]);
510 //top-level, equivalent to testsuites in JUnit
513 xml = "<testsuites>";
515 for (var prop in results){
516 if (results.hasOwnProperty(prop)){
517 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
518 xml += serializeToJUnitXML(results[prop]);
523 xml += "</testsuites>";
532 return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToJUnitXML(results);
536 * Returns test results formatted in TAP format.
537 * For more information, see <a href="http://testanything.org/">Test Anything Protocol</a>.
538 * @param {Object} result The results object created by TestRunner.
539 * @return {String} A TAP-formatted string of results.
543 TAP: function(results) {
545 var currentTestNum = 1;
547 function serializeToTAP(results){
550 switch (results.type){
553 if (results.result != "ignore"){
555 text = "ok " + (currentTestNum++) + " - " + results.name;
557 if (results.result == "fail"){
558 text = "not " + text + " - " + results.message;
563 text = "#Ignored test " + results.name + "\n";
569 text = "#Begin testcase " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
571 for (var prop in results){
572 if (results.hasOwnProperty(prop)){
573 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
574 text += serializeToTAP(results[prop]);
579 text += "#End testcase " + results.name + "\n";
586 text = "#Begin testsuite " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
588 for (var prop in results){
589 if (results.hasOwnProperty(prop)){
590 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
591 text += serializeToTAP(results[prop]);
596 text += "#End testsuite " + results.name + "\n";
601 for (var prop in results){
602 if (results.hasOwnProperty(prop)){
603 if (results[prop] && typeof results[prop] == "object" && !(results[prop] instanceof Array)){
604 text += serializeToTAP(results[prop]);
616 return "1.." + results.total + "\n" + serializeToTAP(results);
623 * An object capable of sending test results to a server.
624 * @param {String} url The URL to submit the results to.
625 * @param {Function} format (Optiona) A function that outputs the results in a specific format.
626 * Default is YUITest.TestFormat.XML.
631 YUITest.Reporter = function(url, format) {
634 * The URL to submit the data to.
641 * The formatting function to call when submitting the data.
645 this.format = format || YUITest.TestFormat.XML;
648 * Extra fields to submit with the request.
653 this._fields = new Object();
656 * The form element used to submit the results.
657 * @type HTMLFormElement
664 * Iframe used as a target for form submission.
665 * @type HTMLIFrameElement
672 YUITest.Reporter.prototype = {
674 //restore missing constructor
675 constructor: YUITest.Reporter,
678 * Adds a field to the form that submits the results.
679 * @param {String} name The name of the field.
680 * @param {Variant} value The value of the field.
684 addField : function (name, value){
685 this._fields[name] = value;
689 * Removes all previous defined fields.
693 clearFields : function(){
694 this._fields = new Object();
698 * Cleans up the memory associated with the TestReporter, removing DOM elements
703 destroy : function() {
705 this._form.parentNode.removeChild(this._form);
709 this._iframe.parentNode.removeChild(this._iframe);
716 * Sends the report to the server.
717 * @param {Object} results The results object created by TestRunner.
721 report : function(results){
723 //if the form hasn't been created yet, create it
725 this._form = document.createElement("form");
726 this._form.method = "post";
727 this._form.style.visibility = "hidden";
728 this._form.style.position = "absolute";
729 this._form.style.top = 0;
730 document.body.appendChild(this._form);
732 //IE won't let you assign a name using the DOM, must do it the hacky way
734 this._iframe = document.createElement("<iframe name=\"yuiTestTarget\" />");
736 this._iframe = document.createElement("iframe");
737 this._iframe.name = "yuiTestTarget";
740 this._iframe.src = "javascript:false";
741 this._iframe.style.visibility = "hidden";
742 this._iframe.style.position = "absolute";
743 this._iframe.style.top = 0;
744 document.body.appendChild(this._iframe);
746 this._form.target = "yuiTestTarget";
749 //set the form's action
750 this._form.action = this.url;
752 //remove any existing fields
753 while(this._form.hasChildNodes()){
754 this._form.removeChild(this._form.lastChild);
757 //create default fields
758 this._fields.results = this.format(results);
759 this._fields.useragent = navigator.userAgent;
760 this._fields.timestamp = (new Date()).toLocaleString();
762 //add fields to the form
763 for (var prop in this._fields){
764 var value = this._fields[prop];
765 if (this._fields.hasOwnProperty(prop) && (typeof value != "function")){
766 var input = document.createElement("input");
767 input.type = "hidden";
770 this._form.appendChild(input);
774 //remove default fields
775 delete this._fields.results;
776 delete this._fields.useragent;
777 delete this._fields.timestamp;
779 if (arguments[1] !== false){
788 * Runs test suites and test cases, providing events to allowing for the
789 * interpretation of test results.
794 YUITest.TestRunner = function(){
796 /*(intentionally not documented)
797 * Determines if any of the array of test groups appears
798 * in the given TestRunner filter.
799 * @param {Array} testGroups The array of test groups to
801 * @param {String} filter The TestRunner groups filter.
803 function inGroups(testGroups, filter){
808 for (var i=0, len=testGroups.length; i < len; i++){
809 if (filter.indexOf("," + testGroups[i] + ",") > -1){
819 * A node in the test tree structure. May represent a TestSuite, TestCase, or
821 * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
826 function TestNode(testObject){
829 * The TestSuite, TestCase, or test function represented by this node.
831 * @property testObject
833 this.testObject = testObject;
836 * Pointer to this node's first child.
838 * @property firstChild
840 this.firstChild = null;
843 * Pointer to this node's last child.
845 * @property lastChild
847 this.lastChild = null;
850 * Pointer to this node's parent.
857 * Pointer to this node's next sibling.
864 * Test results for this test object.
868 this.results = new YUITest.Results();
871 if (testObject instanceof YUITest.TestSuite){
872 this.results.type = "testsuite";
873 this.results.name = testObject.name;
874 } else if (testObject instanceof YUITest.TestCase){
875 this.results.type = "testcase";
876 this.results.name = testObject.name;
881 TestNode.prototype = {
884 * Appends a new test object (TestSuite, TestCase, or test function name) as a child
886 * @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
889 appendChild : function (testObject){
890 var node = new TestNode(testObject);
891 if (this.firstChild === null){
892 this.firstChild = this.lastChild = node;
894 this.lastChild.next = node;
895 this.lastChild = node;
903 * Runs test suites and test cases, providing events to allowing for the
904 * interpretation of test results.
909 function TestRunner(){
911 //inherit from EventTarget
912 YUITest.EventTarget.call(this);
915 * Suite on which to attach all TestSuites and TestCases to be run.
916 * @type YUITest.TestSuite
917 * @property masterSuite
921 this.masterSuite = new YUITest.TestSuite("yuitests" + (new Date()).getTime());
924 * Pointer to the current node in the test tree.
933 * Pointer to the root node in the test tree.
942 * Indicates if the TestRunner will log events or not.
951 * Indicates if the TestRunner is waiting as a result of
952 * wait() being called.
958 this._waiting = false;
961 * Indicates if the TestRunner is currently running tests.
967 this._running = false;
970 * Holds copy of the results object generated when all tests are
974 * @property _lastResults
977 this._lastResults = null;
980 * Data object that is passed around from method to method.
986 this._context = null;
989 * The list of test groups to run. The list is represented
990 * by a comma delimited string with commas at the start and
1001 TestRunner.prototype = YUITest.Util.mix(new YUITest.EventTarget(), {
1004 * If true, YUITest will not fire an error for tests with no Asserts.
1005 * @prop _ignoreEmpty
1010 _ignoreEmpty: false,
1013 constructor: YUITest.TestRunner,
1015 //-------------------------------------------------------------------------
1017 //-------------------------------------------------------------------------
1020 * Fires when a test case is opened but before the first
1022 * @event testcasebegin
1025 TEST_CASE_BEGIN_EVENT : "testcasebegin",
1028 * Fires when all tests in a test case have been executed.
1029 * @event testcasecomplete
1032 TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
1035 * Fires when a test suite is opened but before the first
1037 * @event testsuitebegin
1040 TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
1043 * Fires when all test cases in a test suite have been
1045 * @event testsuitecomplete
1048 TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
1051 * Fires when a test has passed.
1055 TEST_PASS_EVENT : "pass",
1058 * Fires when a test has failed.
1062 TEST_FAIL_EVENT : "fail",
1065 * Fires when a non-test method has an error.
1069 ERROR_EVENT : "error",
1072 * Fires when a test has been ignored.
1076 TEST_IGNORE_EVENT : "ignore",
1079 * Fires when all test suites and test cases have been completed.
1083 COMPLETE_EVENT : "complete",
1086 * Fires when the run() method is called.
1090 BEGIN_EVENT : "begin",
1092 //-------------------------------------------------------------------------
1093 // Test Tree-Related Methods
1094 //-------------------------------------------------------------------------
1097 * Adds a test case to the test tree as a child of the specified node.
1098 * @param {TestNode} parentNode The node to add the test case to as a child.
1099 * @param {Test.TestCase} testCase The test case to add.
1103 * @method _addTestCaseToTestTree
1105 _addTestCaseToTestTree : function (parentNode, testCase){
1107 //add the test suite
1108 var node = parentNode.appendChild(testCase),
1112 //iterate over the items in the test case
1113 for (prop in testCase){
1114 if ((prop.indexOf("test") === 0 || prop.indexOf(" ") > -1) && typeof testCase[prop] == "function"){
1115 node.appendChild(prop);
1122 * Adds a test suite to the test tree as a child of the specified node.
1123 * @param {TestNode} parentNode The node to add the test suite to as a child.
1124 * @param {Test.TestSuite} testSuite The test suite to add.
1128 * @method _addTestSuiteToTestTree
1130 _addTestSuiteToTestTree : function (parentNode, testSuite) {
1132 //add the test suite
1133 var node = parentNode.appendChild(testSuite);
1135 //iterate over the items in the master suite
1136 for (var i=0; i < testSuite.items.length; i++){
1137 if (testSuite.items[i] instanceof YUITest.TestSuite) {
1138 this._addTestSuiteToTestTree(node, testSuite.items[i]);
1139 } else if (testSuite.items[i] instanceof YUITest.TestCase) {
1140 this._addTestCaseToTestTree(node, testSuite.items[i]);
1146 * Builds the test tree based on items in the master suite. The tree is a hierarchical
1147 * representation of the test suites, test cases, and test functions. The resulting tree
1148 * is stored in _root and the pointer _cur is set to the root initially.
1152 * @method _buildTestTree
1154 _buildTestTree : function () {
1156 this._root = new TestNode(this.masterSuite);
1157 //this._cur = this._root;
1159 //iterate over the items in the master suite
1160 for (var i=0; i < this.masterSuite.items.length; i++){
1161 if (this.masterSuite.items[i] instanceof YUITest.TestSuite) {
1162 this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
1163 } else if (this.masterSuite.items[i] instanceof YUITest.TestCase) {
1164 this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
1170 //-------------------------------------------------------------------------
1172 //-------------------------------------------------------------------------
1175 * Handles the completion of a test object's tests. Tallies test results
1176 * from one level up to the next.
1177 * @param {TestNode} node The TestNode representing the test object.
1179 * @method _handleTestObjectComplete
1182 _handleTestObjectComplete : function (node) {
1185 if (node && (typeof node.testObject == "object")) {
1186 parentNode = node.parent;
1189 parentNode.results.include(node.results);
1190 parentNode.results[node.testObject.name] = node.results;
1193 if (node.testObject instanceof YUITest.TestSuite){
1194 this._execNonTestMethod(node, "tearDown", false);
1195 node.results.duration = (new Date()) - node._start;
1196 this.fire({ type: this.TEST_SUITE_COMPLETE_EVENT, testSuite: node.testObject, results: node.results});
1197 } else if (node.testObject instanceof YUITest.TestCase){
1198 this._execNonTestMethod(node, "destroy", false);
1199 node.results.duration = (new Date()) - node._start;
1200 this.fire({ type: this.TEST_CASE_COMPLETE_EVENT, testCase: node.testObject, results: node.results});
1205 //-------------------------------------------------------------------------
1206 // Navigation Methods
1207 //-------------------------------------------------------------------------
1210 * Retrieves the next node in the test tree.
1211 * @return {TestNode} The next node in the test tree or null if the end is reached.
1216 _next : function () {
1218 if (this._cur === null){
1219 this._cur = this._root;
1220 } else if (this._cur.firstChild) {
1221 this._cur = this._cur.firstChild;
1222 } else if (this._cur.next) {
1223 this._cur = this._cur.next;
1225 while (this._cur && !this._cur.next && this._cur !== this._root){
1226 this._handleTestObjectComplete(this._cur);
1227 this._cur = this._cur.parent;
1230 this._handleTestObjectComplete(this._cur);
1232 if (this._cur == this._root){
1233 this._cur.results.type = "report";
1234 this._cur.results.timestamp = (new Date()).toLocaleString();
1235 this._cur.results.duration = (new Date()) - this._cur._start;
1236 this._lastResults = this._cur.results;
1237 this._running = false;
1238 this.fire({ type: this.COMPLETE_EVENT, results: this._lastResults});
1240 } else if (this._cur) {
1241 this._cur = this._cur.next;
1249 * Executes a non-test method (init, setUp, tearDown, destroy)
1250 * and traps an errors. If an error occurs, an error event is
1252 * @param {Object} node The test node in the testing tree.
1253 * @param {String} methodName The name of the method to execute.
1254 * @param {Boolean} allowAsync Determines if the method can be called asynchronously.
1255 * @return {Boolean} True if an async method was called, false if not.
1256 * @method _execNonTestMethod
1259 _execNonTestMethod: function(node, methodName, allowAsync){
1260 var testObject = node.testObject,
1261 event = { type: this.ERROR_EVENT };
1263 if (allowAsync && testObject["async:" + methodName]){
1264 testObject["async:" + methodName](this._context);
1267 testObject[methodName](this._context);
1270 node.results.errors++;
1272 event.methodName = methodName;
1273 if (testObject instanceof YUITest.TestCase){
1274 event.testCase = testObject;
1276 event.testSuite = testSuite;
1286 * Runs a test case or test suite, returning the results.
1287 * @param {Test.TestCase|YUITest.TestSuite} testObject The test case or test suite to run.
1288 * @return {Object} Results of the execution with properties passed, failed, and total.
1293 _run : function () {
1295 //flag to indicate if the TestRunner should wait before continuing
1296 var shouldWait = false;
1298 //get the next test node
1299 var node = this._next();
1301 if (node !== null) {
1303 //set flag to say the testrunner is running
1304 this._running = true;
1306 //eliminate last results
1307 this._lastResult = null;
1309 var testObject = node.testObject;
1311 //figure out what to do
1312 if (typeof testObject == "object" && testObject !== null){
1313 if (testObject instanceof YUITest.TestSuite){
1314 this.fire({ type: this.TEST_SUITE_BEGIN_EVENT, testSuite: testObject });
1315 node._start = new Date();
1316 this._execNonTestMethod(node, "setUp" ,false);
1317 } else if (testObject instanceof YUITest.TestCase){
1318 this.fire({ type: this.TEST_CASE_BEGIN_EVENT, testCase: testObject });
1319 node._start = new Date();
1321 //regular or async init
1323 if (testObject["async:init"]){
1324 testObject["async:init"](this._context);
1327 testObject.init(this._context);
1330 node.results.errors++;
1331 this.fire({ type: this.ERROR_EVENT, error: ex, testCase: testObject, methodName: "init" });
1333 if(this._execNonTestMethod(node, "init", true)){
1338 //some environments don't support setTimeout
1339 if (typeof setTimeout != "undefined"){
1340 setTimeout(function(){
1341 YUITest.TestRunner._run();
1347 this._runTest(node);
1353 _resumeTest : function (segment) {
1355 //get relevant information
1356 var node = this._cur;
1358 //we know there's no more waiting now
1359 this._waiting = false;
1361 //if there's no node, it probably means a wait() was called after resume()
1363 //TODO: Handle in some way?
1364 //console.log("wait() called after resume()");
1365 //this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
1369 var testName = node.testObject;
1370 var testCase = node.parent.testObject;
1372 //cancel other waits if available
1373 if (testCase.__yui_wait){
1374 clearTimeout(testCase.__yui_wait);
1375 delete testCase.__yui_wait;
1378 //get the "should" test cases
1379 var shouldFail = testName.indexOf("fail:") === 0 ||
1380 (testCase._should.fail || {})[testName];
1381 var shouldError = (testCase._should.error || {})[testName];
1383 //variable to hold whether or not the test failed
1391 segment.call(testCase, this._context);
1393 //if the test hasn't already failed and doesn't have any asserts...
1394 if(YUITest.Assert._getCount() == 0 && !this._ignoreEmpty){
1395 throw new YUITest.AssertionError("Test has no asserts.");
1397 //if it should fail, and it got here, then it's a fail because it didn't
1398 else if (shouldFail){
1399 error = new YUITest.ShouldFail();
1401 } else if (shouldError){
1402 error = new YUITest.ShouldError();
1408 //cancel any pending waits, the test already failed
1409 if (testCase.__yui_wait){
1410 clearTimeout(testCase.__yui_wait);
1411 delete testCase.__yui_wait;
1414 //figure out what type of error it was
1415 if (thrown instanceof YUITest.AssertionError) {
1420 } else if (thrown instanceof YUITest.Wait){
1422 if (typeof thrown.segment == "function"){
1423 if (typeof thrown.delay == "number"){
1425 //some environments don't support setTimeout
1426 if (typeof setTimeout != "undefined"){
1427 testCase.__yui_wait = setTimeout(function(){
1428 YUITest.TestRunner._resumeTest(thrown.segment);
1430 this._waiting = true;
1432 throw new Error("Asynchronous tests not supported in this environment.");
1440 //first check to see if it should error
1442 error = new YUITest.UnexpectedError(thrown);
1445 //check to see what type of data we have
1446 if (typeof shouldError == "string"){
1448 //if it's a string, check the error message
1449 if (thrown.message != shouldError){
1450 error = new YUITest.UnexpectedError(thrown);
1453 } else if (typeof shouldError == "function"){
1455 //if it's a function, see if the error is an instance of it
1456 if (!(thrown instanceof shouldError)){
1457 error = new YUITest.UnexpectedError(thrown);
1461 } else if (typeof shouldError == "object" && shouldError !== null){
1463 //if it's an object, check the instance and message
1464 if (!(thrown instanceof shouldError.constructor) ||
1465 thrown.message != shouldError.message){
1466 error = new YUITest.UnexpectedError(thrown);
1477 //fire appropriate event
1479 this.fire({ type: this.TEST_FAIL_EVENT, testCase: testCase, testName: testName, error: error });
1481 this.fire({ type: this.TEST_PASS_EVENT, testCase: testCase, testName: testName });
1485 this._execNonTestMethod(node.parent, "tearDown", false);
1487 //reset the assert count
1488 YUITest.Assert._reset();
1490 //calculate duration
1491 var duration = (new Date()) - node._start;
1494 node.parent.results[testName] = {
1495 result: failed ? "fail" : "pass",
1496 message: error ? error.getMessage() : "Test passed",
1503 node.parent.results.failed++;
1505 node.parent.results.passed++;
1507 node.parent.results.total++;
1509 //set timeout not supported in all environments
1510 if (typeof setTimeout != "undefined"){
1511 setTimeout(function(){
1512 YUITest.TestRunner._run();
1521 * Handles an error as if it occurred within the currently executing
1522 * test. This is for mock methods that may be called asynchronously
1523 * and therefore out of the scope of the TestRunner. Previously, this
1524 * error would bubble up to the browser. Now, this method is used
1525 * to tell TestRunner about the error. This should never be called
1526 * by anyplace other than the Mock object.
1527 * @param {Error} error The error object.
1529 * @method _handleError
1533 _handleError: function(error){
1536 this._resumeTest(function(){
1546 * Runs a single test based on the data provided in the node.
1547 * @param {TestNode} node The TestNode representing the test to run.
1553 _runTest : function (node) {
1555 //get relevant information
1556 var testName = node.testObject,
1557 testCase = node.parent.testObject,
1558 test = testCase[testName],
1560 //get the "should" test cases
1561 shouldIgnore = testName.indexOf("ignore:") === 0 ||
1562 !inGroups(testCase.groups, this._groups) ||
1563 (testCase._should.ignore || {})[testName]; //deprecated
1565 //figure out if the test should be ignored or not
1569 node.parent.results[testName] = {
1571 message: "Test ignored",
1573 name: testName.indexOf("ignore:") === 0 ? testName.substring(7) : testName
1576 node.parent.results.ignored++;
1577 node.parent.results.total++;
1579 this.fire({ type: this.TEST_IGNORE_EVENT, testCase: testCase, testName: testName });
1581 //some environments don't support setTimeout
1582 if (typeof setTimeout != "undefined"){
1583 setTimeout(function(){
1584 YUITest.TestRunner._run();
1592 //mark the start time
1593 node._start = new Date();
1596 this._execNonTestMethod(node.parent, "setUp", false);
1598 //now call the body of the test
1599 this._resumeTest(test);
1604 //-------------------------------------------------------------------------
1606 //-------------------------------------------------------------------------
1609 * Retrieves the name of the current result set.
1610 * @return {String} The name of the result set.
1613 getName: function(){
1614 return this.masterSuite.name;
1618 * The name assigned to the master suite of the TestRunner. This is the name
1619 * that is output as the root's name when results are retrieved.
1620 * @param {String} name The name of the result set.
1624 setName: function(name){
1625 this.masterSuite.name = name;
1628 //-------------------------------------------------------------------------
1630 //-------------------------------------------------------------------------
1633 * Adds a test suite or test case to the list of test objects to run.
1634 * @param testObject Either a TestCase or a TestSuite that should be run.
1639 add : function (testObject) {
1640 this.masterSuite.add(testObject);
1645 * Removes all test objects from the runner.
1650 clear : function () {
1651 this.masterSuite = new YUITest.TestSuite("yuitests" + (new Date()).getTime());
1655 * Indicates if the TestRunner is waiting for a test to resume
1656 * @return {Boolean} True if the TestRunner is waiting, false if not.
1660 isWaiting: function() {
1661 return this._waiting;
1665 * Indicates that the TestRunner is busy running tests and therefore can't
1666 * be stopped and results cannot be gathered.
1667 * @return {Boolean} True if the TestRunner is running, false if not.
1670 isRunning: function(){
1671 return this._running;
1675 * Returns the last complete results set from the TestRunner. Null is returned
1676 * if the TestRunner is running or no tests have been run.
1677 * @param {Function} format (Optional) A test format to return the results in.
1678 * @return {Object|String} Either the results object or, if a test format is
1679 * passed as the argument, a string representing the results in a specific
1681 * @method getResults
1683 getResults: function(format){
1684 if (!this._running && this._lastResults){
1685 if (typeof format == "function"){
1686 return format(this._lastResults);
1688 return this._lastResults;
1696 * Returns the coverage report for the files that have been executed.
1697 * This returns only coverage information for files that have been
1698 * instrumented using YUI Test Coverage and only those that were run
1700 * @param {Function} format (Optional) A coverage format to return results in.
1701 * @return {Object|String} Either the coverage object or, if a coverage
1702 * format is specified, a string representing the results in that format.
1703 * @method getCoverage
1705 getCoverage: function(format){
1706 if (!this._running && typeof _yuitest_coverage == "object"){
1707 if (typeof format == "function"){
1708 return format(_yuitest_coverage);
1710 return _yuitest_coverage;
1718 * Used to continue processing when a method marked with
1719 * "async:" is executed. This should not be used in test
1720 * methods, only in init(). Each argument is a string, and
1721 * when the returned function is executed, the arguments
1722 * are assigned to the context data object using the string
1723 * as the key name (value is the argument itself).
1725 * @return {Function} A callback function.
1727 callback: function(){
1728 var names = arguments,
1729 data = this._context,
1733 for (var i=0; i < arguments.length; i++){
1734 data[names[i]] = arguments[i];
1741 * Resumes the TestRunner after wait() was called.
1742 * @param {Function} segment The function to run as the rest
1743 * of the haulted test.
1748 resume : function (segment) {
1750 this._resumeTest(segment || function(){});
1752 throw new Error("resume() called without wait().");
1757 * Runs the test suite.
1758 * @param {Object|Boolean} options (Optional) Options for the runner:
1759 * <code>oldMode</code> indicates the TestRunner should work in the YUI <= 2.8 way
1760 * of internally managing test suites. <code>groups</code> is an array
1761 * of test groups indicating which tests to run.
1766 run : function (options) {
1768 options = options || {};
1770 //pointer to runner to avoid scope issues
1771 var runner = YUITest.TestRunner,
1772 oldMode = options.oldMode;
1775 //if there's only one suite on the masterSuite, move it up
1776 if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof YUITest.TestSuite){
1777 this.masterSuite = this.masterSuite.items[0];
1780 //determine if there are any groups to filter on
1781 runner._groups = (options.groups instanceof Array) ? "," + options.groups.join(",") + "," : "";
1783 //initialize the runner
1784 runner._buildTestTree();
1785 runner._context = {};
1786 runner._root._start = new Date();
1788 //fire the begin event
1789 runner.fire(runner.BEGIN_EVENT);
1796 return new TestRunner();
1801 * The ArrayAssert object provides functions to test JavaScript array objects
1802 * for a variety of cases.
1804 * @class ArrayAssert
1808 YUITest.ArrayAssert = {
1810 //=========================================================================
1812 //=========================================================================
1815 * Simple indexOf() implementation for an array. Defers to native
1817 * @param {Array} haystack The array to search.
1818 * @param {Variant} needle The value to locate.
1819 * @return {int} The index of the needle if found or -1 if not.
1823 _indexOf: function(haystack, needle){
1824 if (haystack.indexOf){
1825 return haystack.indexOf(needle);
1827 for (var i=0; i < haystack.length; i++){
1828 if (haystack[i] === needle){
1837 * Simple some() implementation for an array. Defers to native
1839 * @param {Array} haystack The array to search.
1840 * @param {Function} matcher The function to run on each value.
1841 * @return {Boolean} True if any value, when run through the matcher,
1846 _some: function(haystack, matcher){
1848 return haystack.some(matcher);
1850 for (var i=0; i < haystack.length; i++){
1851 if (matcher(haystack[i])){
1860 * Asserts that a value is present in an array. This uses the triple equals
1861 * sign so no type cohersion may occur.
1862 * @param {Object} needle The value that is expected in the array.
1863 * @param {Array} haystack An array of values.
1864 * @param {String} message (Optional) The message to display if the assertion fails.
1868 contains : function (needle, haystack,
1871 YUITest.Assert._increment();
1873 if (this._indexOf(haystack, needle) == -1){
1874 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1879 * Asserts that a set of values are present in an array. This uses the triple equals
1880 * sign so no type cohersion may occur. For this assertion to pass, all values must
1882 * @param {Object[]} needles An array of values that are expected in the array.
1883 * @param {Array} haystack An array of values to check.
1884 * @param {String} message (Optional) The message to display if the assertion fails.
1885 * @method containsItems
1888 containsItems : function (needles, haystack,
1890 YUITest.Assert._increment();
1892 //begin checking values
1893 for (var i=0; i < needles.length; i++){
1894 if (this._indexOf(haystack, needles[i]) == -1){
1895 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value " + needles[i] + " (" + (typeof needles[i]) + ") not found in array [" + haystack + "]."));
1901 * Asserts that a value matching some condition is present in an array. This uses
1902 * a function to determine a match.
1903 * @param {Function} matcher A function that returns true if the items matches or false if not.
1904 * @param {Array} haystack An array of values.
1905 * @param {String} message (Optional) The message to display if the assertion fails.
1906 * @method containsMatch
1909 containsMatch : function (matcher, haystack,
1912 YUITest.Assert._increment();
1913 //check for valid matcher
1914 if (typeof matcher != "function"){
1915 throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1918 if (!this._some(haystack, matcher)){
1919 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
1924 * Asserts that a value is not present in an array. This uses the triple equals
1925 * Asserts that a value is not present in an array. This uses the triple equals
1926 * sign so no type cohersion may occur.
1927 * @param {Object} needle The value that is expected in the array.
1928 * @param {Array} haystack An array of values.
1929 * @param {String} message (Optional) The message to display if the assertion fails.
1930 * @method doesNotContain
1933 doesNotContain : function (needle, haystack,
1936 YUITest.Assert._increment();
1938 if (this._indexOf(haystack, needle) > -1){
1939 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1944 * Asserts that a set of values are not present in an array. This uses the triple equals
1945 * sign so no type cohersion may occur. For this assertion to pass, all values must
1947 * @param {Object[]} needles An array of values that are not expected in the array.
1948 * @param {Array} haystack An array of values to check.
1949 * @param {String} message (Optional) The message to display if the assertion fails.
1950 * @method doesNotContainItems
1953 doesNotContainItems : function (needles, haystack,
1956 YUITest.Assert._increment();
1958 for (var i=0; i < needles.length; i++){
1959 if (this._indexOf(haystack, needles[i]) > -1){
1960 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1967 * Asserts that no values matching a condition are present in an array. This uses
1968 * a function to determine a match.
1969 * @param {Function} matcher A function that returns true if the item matches or false if not.
1970 * @param {Array} haystack An array of values.
1971 * @param {String} message (Optional) The message to display if the assertion fails.
1972 * @method doesNotContainMatch
1975 doesNotContainMatch : function (matcher, haystack,
1978 YUITest.Assert._increment();
1980 //check for valid matcher
1981 if (typeof matcher != "function"){
1982 throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
1985 if (this._some(haystack, matcher)){
1986 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
1991 * Asserts that the given value is contained in an array at the specified index.
1992 * This uses the triple equals sign so no type cohersion will occur.
1993 * @param {Object} needle The value to look for.
1994 * @param {Array} haystack The array to search in.
1995 * @param {int} index The index at which the value should exist.
1996 * @param {String} message (Optional) The message to display if the assertion fails.
2000 indexOf : function (needle, haystack, index, message) {
2002 YUITest.Assert._increment();
2004 //try to find the value in the array
2005 for (var i=0; i < haystack.length; i++){
2006 if (haystack[i] === needle){
2008 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
2014 //if it makes it here, it wasn't found at all
2015 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
2019 * Asserts that the values in an array are equal, and in the same position,
2020 * as values in another array. This uses the double equals sign
2021 * so type cohersion may occur. Note that the array objects themselves
2022 * need not be the same for this test to pass.
2023 * @param {Array} expected An array of the expected values.
2024 * @param {Array} actual Any array of the actual values.
2025 * @param {String} message (Optional) The message to display if the assertion fails.
2026 * @method itemsAreEqual
2029 itemsAreEqual : function (expected, actual,
2032 YUITest.Assert._increment();
2034 //first make sure they're array-like (this can probably be improved)
2035 if (typeof expected != "object" || typeof actual != "object"){
2036 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value should be an array."));
2039 //next check array length
2040 if (expected.length != actual.length){
2041 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length + "."));
2044 //begin checking values
2045 for (var i=0; i < expected.length; i++){
2046 if (expected[i] != actual[i]){
2047 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equal."), expected[i], actual[i]);
2053 * Asserts that the values in an array are equivalent, and in the same position,
2054 * as values in another array. This uses a function to determine if the values
2055 * are equivalent. Note that the array objects themselves
2056 * need not be the same for this test to pass.
2057 * @param {Array} expected An array of the expected values.
2058 * @param {Array} actual Any array of the actual values.
2059 * @param {Function} comparator A function that returns true if the values are equivalent
2061 * @param {String} message (Optional) The message to display if the assertion fails.
2063 * @method itemsAreEquivalent
2066 itemsAreEquivalent : function (expected, actual,
2067 comparator, message) {
2069 YUITest.Assert._increment();
2071 //make sure the comparator is valid
2072 if (typeof comparator != "function"){
2073 throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
2076 //first check array length
2077 if (expected.length != actual.length){
2078 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
2081 //begin checking values
2082 for (var i=0; i < expected.length; i++){
2083 if (!comparator(expected[i], actual[i])){
2084 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
2090 * Asserts that an array is empty.
2091 * @param {Array} actual The array to test.
2092 * @param {String} message (Optional) The message to display if the assertion fails.
2096 isEmpty : function (actual, message) {
2097 YUITest.Assert._increment();
2098 if (actual.length > 0){
2099 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should be empty."));
2104 * Asserts that an array is not empty.
2105 * @param {Array} actual The array to test.
2106 * @param {String} message (Optional) The message to display if the assertion fails.
2107 * @method isNotEmpty
2110 isNotEmpty : function (actual, message) {
2111 YUITest.Assert._increment();
2112 if (actual.length === 0){
2113 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should not be empty."));
2118 * Asserts that the values in an array are the same, and in the same position,
2119 * as values in another array. This uses the triple equals sign
2120 * so no type cohersion will occur. Note that the array objects themselves
2121 * need not be the same for this test to pass.
2122 * @param {Array} expected An array of the expected values.
2123 * @param {Array} actual Any array of the actual values.
2124 * @param {String} message (Optional) The message to display if the assertion fails.
2125 * @method itemsAreSame
2128 itemsAreSame : function (expected, actual,
2131 YUITest.Assert._increment();
2133 //first check array length
2134 if (expected.length != actual.length){
2135 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
2138 //begin checking values
2139 for (var i=0; i < expected.length; i++){
2140 if (expected[i] !== actual[i]){
2141 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values in position " + i + " are not the same."), expected[i], actual[i]);
2147 * Asserts that the given value is contained in an array at the specified index,
2148 * starting from the back of the array.
2149 * This uses the triple equals sign so no type cohersion will occur.
2150 * @param {Object} needle The value to look for.
2151 * @param {Array} haystack The array to search in.
2152 * @param {int} index The index at which the value should exist.
2153 * @param {String} message (Optional) The message to display if the assertion fails.
2154 * @method lastIndexOf
2157 lastIndexOf : function (needle, haystack, index, message) {
2159 //try to find the value in the array
2160 for (var i=haystack.length; i >= 0; i--){
2161 if (haystack[i] === needle){
2163 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
2169 //if it makes it here, it wasn't found at all
2170 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Value doesn't exist in array."));
2176 * The Assert object provides functions to test JavaScript values against
2177 * known and expected results. Whenever a comparison (assertion) fails,
2178 * an error is thrown.
2186 * The number of assertions performed.
2187 * @property _asserts
2193 //-------------------------------------------------------------------------
2195 //-------------------------------------------------------------------------
2198 * Formats a message so that it can contain the original assertion message
2199 * in addition to the custom message.
2200 * @param {String} customMessage The message passed in by the developer.
2201 * @param {String} defaultMessage The message created by the error by default.
2202 * @return {String} The final error message, containing either or both.
2205 * @method _formatMessage
2207 _formatMessage : function (customMessage, defaultMessage) {
2208 if (typeof customMessage == "string" && customMessage.length > 0){
2209 return customMessage.replace("{message}", defaultMessage);
2211 return defaultMessage;
2216 * Returns the number of assertions that have been performed.
2221 _getCount: function(){
2222 return this._asserts;
2226 * Increments the number of assertions that have been performed.
2227 * @method _increment
2231 _increment: function(){
2236 * Resets the number of assertions that have been performed to 0.
2245 //-------------------------------------------------------------------------
2246 // Generic Assertion Methods
2247 //-------------------------------------------------------------------------
2250 * Forces an assertion error to occur.
2251 * @param {String} message (Optional) The message to display with the failure.
2255 fail : function (message) {
2256 throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Test force-failed."));
2260 * A marker that the test should pass.
2264 pass : function (message) {
2265 YUITest.Assert._increment();
2268 //-------------------------------------------------------------------------
2269 // Equality Assertion Methods
2270 //-------------------------------------------------------------------------
2273 * Asserts that a value is equal to another. This uses the double equals sign
2274 * so type cohersion may occur.
2275 * @param {Object} expected The expected value.
2276 * @param {Object} actual The actual value to test.
2277 * @param {String} message (Optional) The message to display if the assertion fails.
2281 areEqual : function (expected, actual, message) {
2282 YUITest.Assert._increment();
2283 if (expected != actual) {
2284 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal."), expected, actual);
2289 * Asserts that a value is not equal to another. This uses the double equals sign
2290 * so type cohersion may occur.
2291 * @param {Object} unexpected The unexpected value.
2292 * @param {Object} actual The actual value to test.
2293 * @param {String} message (Optional) The message to display if the assertion fails.
2294 * @method areNotEqual
2297 areNotEqual : function (unexpected, actual,
2299 YUITest.Assert._increment();
2300 if (unexpected == actual) {
2301 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be equal."), unexpected);
2306 * Asserts that a value is not the same as another. This uses the triple equals sign
2307 * so no type cohersion may occur.
2308 * @param {Object} unexpected The unexpected value.
2309 * @param {Object} actual The actual value to test.
2310 * @param {String} message (Optional) The message to display if the assertion fails.
2311 * @method areNotSame
2314 areNotSame : function (unexpected, actual, message) {
2315 YUITest.Assert._increment();
2316 if (unexpected === actual) {
2317 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be the same."), unexpected);
2322 * Asserts that a value is the same as another. This uses the triple equals sign
2323 * so no type cohersion may occur.
2324 * @param {Object} expected The expected value.
2325 * @param {Object} actual The actual value to test.
2326 * @param {String} message (Optional) The message to display if the assertion fails.
2330 areSame : function (expected, actual, message) {
2331 YUITest.Assert._increment();
2332 if (expected !== actual) {
2333 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be the same."), expected, actual);
2337 //-------------------------------------------------------------------------
2338 // Boolean Assertion Methods
2339 //-------------------------------------------------------------------------
2342 * Asserts that a value is false. This uses the triple equals sign
2343 * so no type cohersion may occur.
2344 * @param {Object} actual The actual value to test.
2345 * @param {String} message (Optional) The message to display if the assertion fails.
2349 isFalse : function (actual, message) {
2350 YUITest.Assert._increment();
2351 if (false !== actual) {
2352 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be false."), false, actual);
2357 * Asserts that a value is true. This uses the triple equals sign
2358 * so no type cohersion may occur.
2359 * @param {Object} actual The actual value to test.
2360 * @param {String} message (Optional) The message to display if the assertion fails.
2364 isTrue : function (actual, message) {
2365 YUITest.Assert._increment();
2366 if (true !== actual) {
2367 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be true."), true, actual);
2372 //-------------------------------------------------------------------------
2373 // Special Value Assertion Methods
2374 //-------------------------------------------------------------------------
2377 * Asserts that a value is not a number.
2378 * @param {Object} actual The value to test.
2379 * @param {String} message (Optional) The message to display if the assertion fails.
2383 isNaN : function (actual, message){
2384 YUITest.Assert._increment();
2385 if (!isNaN(actual)){
2386 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
2391 * Asserts that a value is not the special NaN value.
2392 * @param {Object} actual The value to test.
2393 * @param {String} message (Optional) The message to display if the assertion fails.
2397 isNotNaN : function (actual, message){
2398 YUITest.Assert._increment();
2400 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be NaN."), NaN);
2405 * Asserts that a value is not null. This uses the triple equals sign
2406 * so no type cohersion may occur.
2407 * @param {Object} actual The actual value to test.
2408 * @param {String} message (Optional) The message to display if the assertion fails.
2412 isNotNull : function (actual, message) {
2413 YUITest.Assert._increment();
2414 if (actual === null) {
2415 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Values should not be null."), null);
2420 * Asserts that a value is not undefined. This uses the triple equals sign
2421 * so no type cohersion may occur.
2422 * @param {Object} actual The actual value to test.
2423 * @param {String} message (Optional) The message to display if the assertion fails.
2424 * @method isNotUndefined
2427 isNotUndefined : function (actual, message) {
2428 YUITest.Assert._increment();
2429 if (typeof actual == "undefined") {
2430 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should not be undefined."), undefined);
2435 * Asserts that a value is null. This uses the triple equals sign
2436 * so no type cohersion may occur.
2437 * @param {Object} actual The actual value to test.
2438 * @param {String} message (Optional) The message to display if the assertion fails.
2442 isNull : function (actual, message) {
2443 YUITest.Assert._increment();
2444 if (actual !== null) {
2445 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be null."), null, actual);
2450 * Asserts that a value is undefined. This uses the triple equals sign
2451 * so no type cohersion may occur.
2452 * @param {Object} actual The actual value to test.
2453 * @param {String} message (Optional) The message to display if the assertion fails.
2454 * @method isUndefined
2457 isUndefined : function (actual, message) {
2458 YUITest.Assert._increment();
2459 if (typeof actual != "undefined") {
2460 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
2464 //--------------------------------------------------------------------------
2465 // Instance Assertion Methods
2466 //--------------------------------------------------------------------------
2469 * Asserts that a value is an array.
2470 * @param {Object} actual The value to test.
2471 * @param {String} message (Optional) The message to display if the assertion fails.
2475 isArray : function (actual, message) {
2476 YUITest.Assert._increment();
2477 var shouldFail = false;
2479 shouldFail = !Array.isArray(actual);
2481 shouldFail = Object.prototype.toString.call(actual) != "[object Array]";
2484 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an array."), actual);
2489 * Asserts that a value is a Boolean.
2490 * @param {Object} actual The value to test.
2491 * @param {String} message (Optional) The message to display if the assertion fails.
2495 isBoolean : function (actual, message) {
2496 YUITest.Assert._increment();
2497 if (typeof actual != "boolean"){
2498 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a Boolean."), actual);
2503 * Asserts that a value is a function.
2504 * @param {Object} actual The value to test.
2505 * @param {String} message (Optional) The message to display if the assertion fails.
2506 * @method isFunction
2509 isFunction : function (actual, message) {
2510 YUITest.Assert._increment();
2511 if (!(actual instanceof Function)){
2512 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a function."), actual);
2517 * Asserts that a value is an instance of a particular object. This may return
2518 * incorrect results when comparing objects from one frame to constructors in
2519 * another frame. For best results, don't use in a cross-frame manner.
2520 * @param {Function} expected The function that the object should be an instance of.
2521 * @param {Object} actual The object to test.
2522 * @param {String} message (Optional) The message to display if the assertion fails.
2523 * @method isInstanceOf
2526 isInstanceOf : function (expected, actual, message) {
2527 YUITest.Assert._increment();
2528 if (!(actual instanceof expected)){
2529 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
2534 * Asserts that a value is a number.
2535 * @param {Object} actual The value to test.
2536 * @param {String} message (Optional) The message to display if the assertion fails.
2540 isNumber : function (actual, message) {
2541 YUITest.Assert._increment();
2542 if (typeof actual != "number"){
2543 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a number."), actual);
2548 * Asserts that a value is an object.
2549 * @param {Object} actual The value to test.
2550 * @param {String} message (Optional) The message to display if the assertion fails.
2554 isObject : function (actual, message) {
2555 YUITest.Assert._increment();
2556 if (!actual || (typeof actual != "object" && typeof actual != "function")){
2557 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be an object."), actual);
2562 * Asserts that a value is a string.
2563 * @param {Object} actual The value to test.
2564 * @param {String} message (Optional) The message to display if the assertion fails.
2568 isString : function (actual, message) {
2569 YUITest.Assert._increment();
2570 if (typeof actual != "string"){
2571 throw new YUITest.UnexpectedValue(YUITest.Assert._formatMessage(message, "Value should be a string."), actual);
2576 * Asserts that a value is of a particular type.
2577 * @param {String} expectedType The expected type of the variable.
2578 * @param {Object} actualValue The actual value to test.
2579 * @param {String} message (Optional) The message to display if the assertion fails.
2583 isTypeOf : function (expectedType, actualValue, message){
2584 YUITest.Assert._increment();
2585 if (typeof actualValue != expectedType){
2586 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expectedType, typeof actualValue);
2590 //--------------------------------------------------------------------------
2591 // Error Detection Methods
2592 //--------------------------------------------------------------------------
2595 * Asserts that executing a particular method should throw an error of
2596 * a specific type. This is a replacement for _should.error.
2597 * @param {String|Function|Object} expectedError If a string, this
2598 * is the error message that the error must have; if a function, this
2599 * is the constructor that should have been used to create the thrown
2600 * error; if an object, this is an instance of a particular error type
2601 * with a specific error message (both must match).
2602 * @param {Function} method The method to execute that should throw the error.
2603 * @param {String} message (Optional) The message to display if the assertion
2605 * @method throwsError
2609 throwsError: function(expectedError, method, message){
2610 YUITest.Assert._increment();
2617 //check to see what type of data we have
2618 if (typeof expectedError == "string"){
2620 //if it's a string, check the error message
2621 if (thrown.message != expectedError){
2624 } else if (typeof expectedError == "function"){
2626 //if it's a function, see if the error is an instance of it
2627 if (!(thrown instanceof expectedError)){
2631 } else if (typeof expectedError == "object" && expectedError !== null){
2633 //if it's an object, check the instance and message
2634 if (!(thrown instanceof expectedError.constructor) ||
2635 thrown.message != expectedError.message){
2639 } else { //if it gets here, the argument could be wrong
2644 throw new YUITest.UnexpectedError(thrown);
2650 //if it reaches here, the error wasn't thrown, which is a bad thing
2651 throw new YUITest.AssertionError(YUITest.Assert._formatMessage(message, "Error should have been thrown."));
2656 * Error is thrown whenever an assertion fails. It provides methods
2657 * to more easily get at error information and also provides a base class
2658 * from which more specific assertion errors can be derived.
2660 * @param {String} message The message to display when the error occurs.
2662 * @class AssertionError
2665 YUITest.AssertionError = function (message){
2668 * Error message. Must be duplicated to ensure browser receives it.
2672 this.message = message;
2675 * The name of the error that occurred.
2679 this.name = "Assert Error";
2682 YUITest.AssertionError.prototype = {
2684 //restore constructor
2685 constructor: YUITest.AssertionError,
2688 * Returns a fully formatted error for an assertion failure. This should
2689 * be overridden by all subclasses to provide specific information.
2690 * @method getMessage
2691 * @return {String} A string describing the error.
2693 getMessage : function () {
2694 return this.message;
2698 * Returns a string representation of the error.
2700 * @return {String} A string representation of the error.
2702 toString : function () {
2703 return this.name + ": " + this.getMessage();
2708 * ComparisonFailure is subclass of Error that is thrown whenever
2709 * a comparison between two values fails. It provides mechanisms to retrieve
2710 * both the expected and actual value.
2712 * @param {String} message The message to display when the error occurs.
2713 * @param {Object} expected The expected value.
2714 * @param {Object} actual The actual value that caused the assertion to fail.
2716 * @extends AssertionError
2717 * @class ComparisonFailure
2720 YUITest.ComparisonFailure = function (message, expected, actual){
2723 YUITest.AssertionError.call(this, message);
2726 * The expected value.
2728 * @property expected
2730 this.expected = expected;
2737 this.actual = actual;
2740 * The name of the error that occurred.
2744 this.name = "ComparisonFailure";
2748 //inherit from YUITest.AssertionError
2749 YUITest.ComparisonFailure.prototype = new YUITest.AssertionError;
2751 //restore constructor
2752 YUITest.ComparisonFailure.prototype.constructor = YUITest.ComparisonFailure;
2755 * Returns a fully formatted error for an assertion failure. This message
2756 * provides information about the expected and actual values.
2757 * @method getMessage
2758 * @return {String} A string describing the error.
2760 YUITest.ComparisonFailure.prototype.getMessage = function(){
2761 return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
2762 "\nActual: " + this.actual + " (" + (typeof this.actual) + ")";
2765 * An object object containing coverage result formatting methods.
2767 * @class CoverageFormat
2770 YUITest.CoverageFormat = {
2773 * Returns the coverage report in JSON format. This is the straight
2774 * JSON representation of the native coverage report.
2775 * @param {Object} coverage The coverage report object.
2776 * @return {String} A JSON-formatted string of coverage data.
2778 * @namespace Test.CoverageFormat
2780 JSON: function(coverage){
2781 return YUITest.Util.JSON.stringify(coverage);
2785 * Returns the coverage report in a JSON format compatible with
2786 * Xdebug. See <a href="http://www.xdebug.com/docs/code_coverage">Xdebug Documentation</a>
2787 * for more information. Note: function coverage is not available
2789 * @param {Object} coverage The coverage report object.
2790 * @return {String} A JSON-formatted string of coverage data.
2791 * @method XdebugJSON
2792 * @namespace Test.CoverageFormat
2794 XdebugJSON: function(coverage){
2797 for (var prop in coverage){
2798 if (coverage.hasOwnProperty(prop)){
2799 report[prop] = coverage[prop].lines;
2803 return YUITest.Util.JSON.stringify(coverage);
2810 * The DateAssert object provides functions to test JavaScript Date objects
2811 * for a variety of cases.
2817 YUITest.DateAssert = {
2820 * Asserts that a date's month, day, and year are equal to another date's.
2821 * @param {Date} expected The expected date.
2822 * @param {Date} actual The actual date to test.
2823 * @param {String} message (Optional) The message to display if the assertion fails.
2824 * @method datesAreEqual
2827 datesAreEqual : function (expected, actual, message){
2828 YUITest.Assert._increment();
2829 if (expected instanceof Date && actual instanceof Date){
2833 if (expected.getFullYear() != actual.getFullYear()){
2834 msg = "Years should be equal.";
2838 if (expected.getMonth() != actual.getMonth()){
2839 msg = "Months should be equal.";
2842 //last, check the day of the month
2843 if (expected.getDate() != actual.getDate()){
2844 msg = "Days of month should be equal.";
2848 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
2851 throw new TypeError("YUITest.DateAssert.datesAreEqual(): Expected and actual values must be Date objects.");
2856 * Asserts that a date's hour, minutes, and seconds are equal to another date's.
2857 * @param {Date} expected The expected date.
2858 * @param {Date} actual The actual date to test.
2859 * @param {String} message (Optional) The message to display if the assertion fails.
2860 * @method timesAreEqual
2863 timesAreEqual : function (expected, actual, message){
2864 YUITest.Assert._increment();
2865 if (expected instanceof Date && actual instanceof Date){
2869 if (expected.getHours() != actual.getHours()){
2870 msg = "Hours should be equal.";
2874 if (expected.getMinutes() != actual.getMinutes()){
2875 msg = "Minutes should be equal.";
2878 //last, check the seconds
2879 if (expected.getSeconds() != actual.getSeconds()){
2880 msg = "Seconds should be equal.";
2884 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, msg), expected, actual);
2887 throw new TypeError("YUITest.DateAssert.timesAreEqual(): Expected and actual values must be Date objects.");
2893 * Creates a new mock object.
2897 * @param {Object} template (Optional) An object whose methods
2898 * should be stubbed out on the mock object.
2900 YUITest.Mock = function(template){
2902 //use blank object is nothing is passed in
2903 template = template || {};
2908 //try to create mock that keeps prototype chain intact
2909 //fails in the case of ActiveX objects
2912 f.prototype = template;
2918 //create stubs for all methods
2919 for (name in template){
2920 if (template.hasOwnProperty(name)){
2921 if (typeof template[name] == "function"){
2922 mock[name] = function(name){
2924 YUITest.Assert.fail("Method " + name + "() was called but was not expected to be.");
2936 * Assigns an expectation to a mock object. This is used to create
2937 * methods and properties on the mock object that are monitored for
2938 * calls and changes, respectively.
2939 * @param {Object} mock The object to add the expectation to.
2940 * @param {Object} expectation An object defining the expectation. For
2941 * a method, the keys "method" and "args" are required with
2942 * an optional "returns" key available. For properties, the keys
2943 * "property" and "value" are required.
2948 YUITest.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
2950 //make sure there's a place to store the expectations
2951 if (!mock.__expectations) {
2952 mock.__expectations = {};
2955 //method expectation
2956 if (expectation.method){
2957 var name = expectation.method,
2958 args = expectation.args || [],
2959 result = expectation.returns,
2960 callCount = (typeof expectation.callCount == "number") ? expectation.callCount : 1,
2961 error = expectation.error,
2962 run = expectation.run || function(){},
2966 mock.__expectations[name] = expectation;
2967 expectation.callCount = callCount;
2968 expectation.actualCallCount = 0;
2971 for (i=0; i < args.length; i++){
2972 if (!(args[i] instanceof YUITest.Mock.Value)){
2973 args[i] = YUITest.Mock.Value(YUITest.Assert.areSame, [args[i]], "Argument " + i + " of " + name + "() is incorrect.");
2977 //if the method is expected to be called
2979 mock[name] = function(){
2981 expectation.actualCallCount++;
2982 YUITest.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
2983 for (var i=0, len=args.length; i < len; i++){
2984 args[i].verify(arguments[i]);
2987 run.apply(this, arguments);
2993 //route through TestRunner for proper handling
2994 YUITest.TestRunner._handleError(ex);
3001 //method should fail if called when not expected
3002 mock[name] = function(){
3004 YUITest.Assert.fail("Method " + name + "() should not have been called.");
3006 //route through TestRunner for proper handling
3007 YUITest.TestRunner._handleError(ex);
3011 } else if (expectation.property){
3013 mock.__expectations[expectation.property] = expectation;
3018 * Verifies that all expectations of a mock object have been met and
3019 * throws an assertion error if not.
3020 * @param {Object} mock The object to verify..
3025 YUITest.Mock.verify = function(mock){
3028 for (var name in mock.__expectations){
3029 if (mock.__expectations.hasOwnProperty(name)){
3030 var expectation = mock.__expectations[name];
3031 if (expectation.method) {
3032 YUITest.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
3033 } else if (expectation.property){
3034 YUITest.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value.");
3040 //route through TestRunner for proper handling
3041 YUITest.TestRunner._handleError(ex);
3046 * Creates a new value matcher.
3047 * @param {Function} method The function to call on the value.
3048 * @param {Array} originalArgs (Optional) Array of arguments to pass to the method.
3049 * @param {String} message (Optional) Message to display in case of failure.
3050 * @namespace Test.Mock
3054 YUITest.Mock.Value = function(method, originalArgs, message){
3055 if (this instanceof YUITest.Mock.Value){
3056 this.verify = function(value){
3057 var args = [].concat(originalArgs || []);
3060 method.apply(null, args);
3063 return new YUITest.Mock.Value(method, originalArgs, message);
3068 * Predefined matcher to match any value.
3073 YUITest.Mock.Value.Any = YUITest.Mock.Value(function(){});
3076 * Predefined matcher to match boolean values.
3081 YUITest.Mock.Value.Boolean = YUITest.Mock.Value(YUITest.Assert.isBoolean);
3084 * Predefined matcher to match number values.
3089 YUITest.Mock.Value.Number = YUITest.Mock.Value(YUITest.Assert.isNumber);
3092 * Predefined matcher to match string values.
3097 YUITest.Mock.Value.String = YUITest.Mock.Value(YUITest.Assert.isString);
3100 * Predefined matcher to match object values.
3105 YUITest.Mock.Value.Object = YUITest.Mock.Value(YUITest.Assert.isObject);
3108 * Predefined matcher to match function values.
3109 * @property Function
3113 YUITest.Mock.Value.Function = YUITest.Mock.Value(YUITest.Assert.isFunction);
3116 * The ObjectAssert object provides functions to test JavaScript objects
3117 * for a variety of cases.
3119 * @class ObjectAssert
3122 YUITest.ObjectAssert = {
3125 * Asserts that an object has all of the same properties
3126 * and property values as the other.
3127 * @param {Object} expected The object with all expected properties and values.
3128 * @param {Object} actual The object to inspect.
3129 * @param {String} message (Optional) The message to display if the assertion fails.
3134 areEqual: function(expected, actual, message) {
3135 YUITest.Assert._increment();
3137 var expectedKeys = YUITest.Object.keys(expected),
3138 actualKeys = YUITest.Object.keys(actual);
3140 //first check keys array length
3141 if (expectedKeys.length != actualKeys.length){
3142 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Object should have " + expectedKeys.length + " keys but has " + actualKeys.length));
3146 for (var name in expected){
3147 if (expected.hasOwnProperty(name)){
3148 if (expected[name] != actual[name]){
3149 throw new YUITest.ComparisonFailure(YUITest.Assert._formatMessage(message, "Values should be equal for property " + name), expected[name], actual[name]);
3156 * Asserts that an object has a property with the given name.
3157 * @param {String} propertyName The name of the property to test.
3158 * @param {Object} object The object to search.
3159 * @param {String} message (Optional) The message to display if the assertion fails.
3162 * @deprecated Use ownsOrInheritsKey() instead
3164 hasKey: function (propertyName, object, message) {
3165 YUITest.ObjectAssert.ownsOrInheritsKey(propertyName, object, message);
3169 * Asserts that an object has all properties of a reference object.
3170 * @param {Array} properties An array of property names that should be on the object.
3171 * @param {Object} object The object to search.
3172 * @param {String} message (Optional) The message to display if the assertion fails.
3175 * @deprecated Use ownsOrInheritsKeys() instead
3177 hasKeys: function (properties, object, message) {
3178 YUITest.ObjectAssert.ownsOrInheritsKeys(properties, object, message);
3182 * Asserts that a property with the given name exists on an object's prototype.
3183 * @param {String} propertyName The name of the property to test.
3184 * @param {Object} object The object to search.
3185 * @param {String} message (Optional) The message to display if the assertion fails.
3186 * @method inheritsKey
3189 inheritsKey: function (propertyName, object, message) {
3190 YUITest.Assert._increment();
3191 if (!(propertyName in object && !object.hasOwnProperty(propertyName))){
3192 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
3197 * Asserts that all properties exist on an object prototype.
3198 * @param {Array} properties An array of property names that should be on the object.
3199 * @param {Object} object The object to search.
3200 * @param {String} message (Optional) The message to display if the assertion fails.
3201 * @method inheritsKeys
3204 inheritsKeys: function (properties, object, message) {
3205 YUITest.Assert._increment();
3206 for (var i=0; i < properties.length; i++){
3207 if (!(propertyName in object && !object.hasOwnProperty(properties[i]))){
3208 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
3214 * Asserts that a property with the given name exists on an object instance (not on its prototype).
3215 * @param {String} propertyName The name of the property to test.
3216 * @param {Object} object The object to search.
3217 * @param {String} message (Optional) The message to display if the assertion fails.
3221 ownsKey: function (propertyName, object, message) {
3222 YUITest.Assert._increment();
3223 if (!object.hasOwnProperty(propertyName)){
3224 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
3229 * Asserts that all properties exist on an object instance (not on its prototype).
3230 * @param {Array} properties An array of property names that should be on the object.
3231 * @param {Object} object The object to search.
3232 * @param {String} message (Optional) The message to display if the assertion fails.
3236 ownsKeys: function (properties, object, message) {
3237 YUITest.Assert._increment();
3238 for (var i=0; i < properties.length; i++){
3239 if (!object.hasOwnProperty(properties[i])){
3240 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
3246 * Asserts that an object owns no properties.
3247 * @param {Object} object The object to check.
3248 * @param {String} message (Optional) The message to display if the assertion fails.
3249 * @method ownsNoKeys
3252 ownsNoKeys : function (object, message) {
3253 YUITest.Assert._increment();
3256 for (name in object){
3257 if (object.hasOwnProperty(name)){
3263 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Object owns " + count + " properties but should own none."));
3269 * Asserts that an object has a property with the given name.
3270 * @param {String} propertyName The name of the property to test.
3271 * @param {Object} object The object to search.
3272 * @param {String} message (Optional) The message to display if the assertion fails.
3273 * @method ownsOrInheritsKey
3276 ownsOrInheritsKey: function (propertyName, object, message) {
3277 YUITest.Assert._increment();
3278 if (!(propertyName in object)){
3279 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
3284 * Asserts that an object has all properties of a reference object.
3285 * @param {Array} properties An array of property names that should be on the object.
3286 * @param {Object} object The object to search.
3287 * @param {String} message (Optional) The message to display if the assertion fails.
3288 * @method ownsOrInheritsKeys
3291 ownsOrInheritsKeys: function (properties, object, message) {
3292 YUITest.Assert._increment();
3293 for (var i=0; i < properties.length; i++){
3294 if (!(properties[i] in object)){
3295 YUITest.Assert.fail(YUITest.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object."));
3301 * Convenience type for storing and aggregating
3302 * test result information.
3307 * @param {String} name The name of the test.
3309 YUITest.Results = function(name){
3312 * Name of the test, test case, or test suite.
3319 * Number of passed tests.
3326 * Number of failed tests.
3333 * Number of errors that occur in non-test methods.
3340 * Number of ignored tests.
3347 * Number of total tests.
3354 * Amount of time (ms) it took to complete testing.
3356 * @property duration
3362 * Includes results from another results object into this one.
3363 * @param {Test.Results} result The results object to include.
3367 YUITest.Results.prototype.include = function(results){
3368 this.passed += results.passed;
3369 this.failed += results.failed;
3370 this.ignored += results.ignored;
3371 this.total += results.total;
3372 this.errors += results.errors;
3375 * ShouldError is subclass of Error that is thrown whenever
3376 * a test is expected to throw an error but doesn't.
3378 * @param {String} message The message to display when the error occurs.
3380 * @extends AssertionError
3381 * @class ShouldError
3384 YUITest.ShouldError = function (message){
3387 YUITest.AssertionError.call(this, message || "This test should have thrown an error but didn't.");
3390 * The name of the error that occurred.
3394 this.name = "ShouldError";
3398 //inherit from YUITest.AssertionError
3399 YUITest.ShouldError.prototype = new YUITest.AssertionError();
3401 //restore constructor
3402 YUITest.ShouldError.prototype.constructor = YUITest.ShouldError;
3404 * ShouldFail is subclass of AssertionError that is thrown whenever
3405 * a test was expected to fail but did not.
3407 * @param {String} message The message to display when the error occurs.
3409 * @extends YUITest.AssertionError
3413 YUITest.ShouldFail = function (message){
3416 YUITest.AssertionError.call(this, message || "This test should fail but didn't.");
3419 * The name of the error that occurred.
3423 this.name = "ShouldFail";
3427 //inherit from YUITest.AssertionError
3428 YUITest.ShouldFail.prototype = new YUITest.AssertionError();
3430 //restore constructor
3431 YUITest.ShouldFail.prototype.constructor = YUITest.ShouldFail;
3433 * UnexpectedError is subclass of AssertionError that is thrown whenever
3434 * an error occurs within the course of a test and the test was not expected
3435 * to throw an error.
3437 * @param {Error} cause The unexpected error that caused this error to be
3440 * @extends YUITest.AssertionError
3441 * @class UnexpectedError
3444 YUITest.UnexpectedError = function (cause){
3447 YUITest.AssertionError.call(this, "Unexpected error: " + cause.message);
3450 * The unexpected error that occurred.
3457 * The name of the error that occurred.
3461 this.name = "UnexpectedError";
3464 * Stack information for the error (if provided).
3468 this.stack = cause.stack;
3472 //inherit from YUITest.AssertionError
3473 YUITest.UnexpectedError.prototype = new YUITest.AssertionError();
3475 //restore constructor
3476 YUITest.UnexpectedError.prototype.constructor = YUITest.UnexpectedError;
3478 * UnexpectedValue is subclass of Error that is thrown whenever
3479 * a value was unexpected in its scope. This typically means that a test
3480 * was performed to determine that a value was *not* equal to a certain
3483 * @param {String} message The message to display when the error occurs.
3484 * @param {Object} unexpected The unexpected value.
3486 * @extends AssertionError
3487 * @class UnexpectedValue
3490 YUITest.UnexpectedValue = function (message, unexpected){
3493 YUITest.AssertionError.call(this, message);
3496 * The unexpected value.
3498 * @property unexpected
3500 this.unexpected = unexpected;
3503 * The name of the error that occurred.
3507 this.name = "UnexpectedValue";
3511 //inherit from YUITest.AssertionError
3512 YUITest.UnexpectedValue.prototype = new YUITest.AssertionError();
3514 //restore constructor
3515 YUITest.UnexpectedValue.prototype.constructor = YUITest.UnexpectedValue;
3518 * Returns a fully formatted error for an assertion failure. This message
3519 * provides information about the expected and actual values.
3520 * @method getMessage
3521 * @return {String} A string describing the error.
3523 YUITest.UnexpectedValue.prototype.getMessage = function(){
3524 return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
3528 * Represents a stoppage in test execution to wait for an amount of time before
3530 * @param {Function} segment A function to run when the wait is over.
3531 * @param {int} delay The number of milliseconds to wait before running the code.
3537 YUITest.Wait = function (segment, delay) {
3540 * The segment of code to run when the wait is over.
3544 this.segment = (typeof segment == "function" ? segment : null);
3547 * The delay before running the segment of code.
3551 this.delay = (typeof delay == "number" ? delay : 0);
3555 //Setting up our aliases..
3557 Y.Object.each(YUITest, function(item, name) {
3558 var name = name.replace('Test', '');
3559 Y.Test[name] = item;
3562 Y.Assert = YUITest.Assert;
3563 Y.Assert.Error = Y.Test.AssertionError;
3564 Y.Assert.ComparisonFailure = Y.Test.ComparisonFailure;
3565 Y.Assert.UnexpectedValue = Y.Test.UnexpectedValue;
3566 Y.Mock = Y.Test.Mock;
3567 Y.ObjectAssert = Y.Test.ObjectAssert;
3568 Y.ArrayAssert = Y.Test.ArrayAssert;
3569 Y.DateAssert = Y.Test.DateAssert;
3570 Y.Test.ResultsFormat = Y.Test.TestFormat;
3573 * Asserts that a given condition is true. If not, then a Y.Assert.Error object is thrown
3574 * and the test fails.
3576 * @param {Boolean} condition The condition to test.
3577 * @param {String} message The message to display if the assertion fails.
3581 Y.assert = function(condition, message){
3582 Y.Assert._increment();
3584 throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Assertion failed."));
3589 * Forces an assertion error to occur. Shortcut for Y.Assert.fail().
3591 * @param {String} message (Optional) The message to display with the failure.
3595 Y.fail = Y.Assert.fail;
3597 var logEvent = function(event) {
3601 var messageType = "";
3604 case this.BEGIN_EVENT:
3605 message = "Testing began at " + (new Date()).toString() + ".";
3606 messageType = "info";
3609 case this.COMPLETE_EVENT:
3610 message = Y.substitute("Testing completed at " +
3611 (new Date()).toString() + ".\n" +
3612 "Passed:{passed} Failed:{failed} " +
3613 "Total:{total} ({ignored} ignored)",
3615 messageType = "info";
3618 case this.TEST_FAIL_EVENT:
3619 message = event.testName + ": failed.\n" + event.error.getMessage();
3620 messageType = "fail";
3623 case this.TEST_IGNORE_EVENT:
3624 message = event.testName + ": ignored.";
3625 messageType = "ignore";
3628 case this.TEST_PASS_EVENT:
3629 message = event.testName + ": passed.";
3630 messageType = "pass";
3633 case this.TEST_SUITE_BEGIN_EVENT:
3634 message = "Test suite \"" + event.testSuite.name + "\" started.";
3635 messageType = "info";
3638 case this.TEST_SUITE_COMPLETE_EVENT:
3639 message = Y.substitute("Test suite \"" +
3640 event.testSuite.name + "\" completed" + ".\n" +
3641 "Passed:{passed} Failed:{failed} " +
3642 "Total:{total} ({ignored} ignored)",
3644 messageType = "info";
3647 case this.TEST_CASE_BEGIN_EVENT:
3648 message = "Test case \"" + event.testCase.name + "\" started.";
3649 messageType = "info";
3652 case this.TEST_CASE_COMPLETE_EVENT:
3653 message = Y.substitute("Test case \"" +
3654 event.testCase.name + "\" completed.\n" +
3655 "Passed:{passed} Failed:{failed} " +
3656 "Total:{total} ({ignored} ignored)",
3658 messageType = "info";
3661 message = "Unexpected event " + event.type;
3665 if (Y.Test.Runner._log) {
3666 Y.log(message, messageType, "TestRunner");
3672 for (i in Y.Test.Runner) {
3673 name = Y.Test.Runner[i];
3674 if (i.indexOf('_EVENT') > -1) {
3675 Y.Test.Runner.subscribe(name, logEvent);
3679 Y.Test.Runner.once = Y.Test.Runner.subscribe;
3681 Y.Test.Runner.disableLogging = function() {
3682 Y.Test.Runner._log = false;
3685 Y.Test.Runner.enableLogging = function() {
3686 Y.Test.Runner._log = true;
3689 Y.Test.Runner._ignoreEmpty = true;
3690 Y.Test.Runner._log = true;
3692 Y.Test.Runner.on = Y.Test.Runner.attach;
3695 Y.config.win.YUITest = YUITest;
3700 }, '3.5.0' ,{requires:['event-simulate','event-custom','substitute','json-stringify']});