3
Copyright 2011 Yahoo! Inc. All rights reserved.
4
Licensed under the BSD License.
5
http://yuilibrary.com/license/
7
YUI.add('test', function(Y) {
10
* YUI JavaScript Testing Framework
19
* Test case containing various tests to run.
20
* @param template An object containing any number of test methods, other methods,
21
* an optional name, and anything else the test case needs.
26
Y.Test.Case = function (template) {
29
* Special rules for the test case. Possible subobjects
30
* are fail, for tests that should fail, and error, for
31
* tests that should throw an error.
39
//copy over all properties from the template to this object
40
for (var prop in template) {
41
this[prop] = template[prop];
44
//check for a valid name
45
if (!Y.Lang.isString(this.name)){
47
* Name for the test case.
52
this.name = "testCase" + Y.guid();
57
Y.Test.Case.prototype = {
60
* Resumes a paused test and runs the given function.
61
* @param {Function} segment (Optional) The function to run.
62
* If omitted, the test automatically passes.
66
resume : function (segment) {
67
Y.Test.Runner.resume(segment);
71
* Causes the test case to wait a specified amount of time and then
72
* continue executing the given code.
73
* @param {Function} segment (Optional) The function to run after the delay.
74
* If omitted, the TestRunner will wait until resume() is called.
75
* @param {int} delay (Optional) The number of milliseconds to wait before running
76
* the function. If omitted, defaults to zero.
80
wait : function (segment, delay){
82
if (Y.Lang.isFunction(args[0])){
83
throw new Y.Test.Wait(args[0], args[1]);
85
throw new Y.Test.Wait(function(){
86
Y.Assert.fail("Timeout: wait() called but resume() never called.");
87
}, (Y.Lang.isNumber(args[0]) ? args[0] : 10000));
91
//-------------------------------------------------------------------------
93
//-------------------------------------------------------------------------
96
* Function to run before each test is executed.
100
setUp : function () {
104
* Function to run after each test is executed.
108
tearDown: function () {
113
* Represents a stoppage in test execution to wait for an amount of time before
115
* @param {Function} segment A function to run when the wait is over.
116
* @param {int} delay The number of milliseconds to wait before running the code.
122
Y.Test.Wait = function (segment, delay) {
125
* The segment of code to run when the wait is over.
129
this.segment = (Y.Lang.isFunction(segment) ? segment : null);
132
* The delay before running the segment of code.
136
this.delay = (Y.Lang.isNumber(delay) ? delay : 0);
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.
150
Y.Test.Suite = function (data /*:String||Object*/) {
153
* The name of the test suite.
160
* Array of test suites and
167
//initialize the properties
168
if (Y.Lang.isString(data)){
170
} else if (Y.Lang.isObject(data)){
171
Y.mix(this, data, true);
175
if (this.name === ""){
176
this.name = "testSuite" + Y.guid();
181
Y.Test.Suite.prototype = {
184
* Adds a test suite or test case to the test suite.
185
* @param {Test.Suite|Test.Case} testObject The test suite or test case to add.
189
add : function (testObject /*:Y.Test.Suite*/) {
190
if (testObject instanceof Y.Test.Suite || testObject instanceof Y.Test.Case) {
191
this.items.push(testObject);
196
//-------------------------------------------------------------------------
198
//-------------------------------------------------------------------------
201
* Function to run before each test is executed.
205
setUp : function () {
209
* Function to run after each test is executed.
213
tearDown: function () {
219
* Runs test suites and test cases, providing events to allowing for the
220
* interpretation of test results.
225
Y.Test.Runner = (function(){
227
/* (intentionally not documented)
228
* A node in the test tree structure. May represent a TestSuite, TestCase, or
230
* @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
235
function TestNode(testObject){
237
/* (intentionally not documented)
238
* The TestSuite, TestCase, or test function represented by this node.
240
* @property testObject
242
this.testObject = testObject;
244
/* (intentionally not documented)
245
* Pointer to this node's first child.
247
* @property firstChild
249
this.firstChild = null;
251
/* (intentionally not documented)
252
* Pointer to this node's last child.
254
* @property lastChild
256
this.lastChild = null;
258
/* (intentionally not documented)
259
* Pointer to this node's parent.
265
/* (intentionally not documented)
266
* Pointer to this node's next sibling.
272
/* (intentionally not documented)
273
* Test results for this test object.
286
if (testObject instanceof Y.Test.Suite){
287
this.results.type = "testsuite";
288
this.results.name = testObject.name;
289
} else if (testObject instanceof Y.Test.Case){
290
this.results.type = "testcase";
291
this.results.name = testObject.name;
296
TestNode.prototype = {
298
/* (intentionally not documented)
299
* Appends a new test object (TestSuite, TestCase, or test function name) as a child
301
* @param {Variant} testObject A TestSuite, TestCase, or the name of a test function.
304
appendChild : function (testObject){
305
var node = new TestNode(testObject);
306
if (this.firstChild === null){
307
this.firstChild = this.lastChild = node;
309
this.lastChild.next = node;
310
this.lastChild = node;
318
* Runs test suites and test cases, providing events to allowing for the
319
* interpretation of test results.
324
function TestRunner(){
326
//inherit from EventProvider
327
TestRunner.superclass.constructor.apply(this,arguments);
330
* Suite on which to attach all TestSuites and TestCases to be run.
332
* @property masterSuite
336
this.masterSuite /*:Y.Test.Suite*/ = new Y.Test.Suite("yuitests" + (new Date()).getTime());
339
* Pointer to the current node in the test tree.
348
* Pointer to the root node in the test tree.
357
* Indicates if the TestRunner will log events or not.
366
* Indicates if the TestRunner is waiting as a result of
367
* wait() being called.
373
this._waiting = false;
376
* Indicates if the TestRunner is currently running tests.
382
this._running = false;
385
* Holds copy of the results object generated when all tests are
389
* @property _lastResults
392
this._lastResults = null;
396
this.TEST_CASE_BEGIN_EVENT,
397
this.TEST_CASE_COMPLETE_EVENT,
398
this.TEST_SUITE_BEGIN_EVENT,
399
this.TEST_SUITE_COMPLETE_EVENT,
400
this.TEST_PASS_EVENT,
401
this.TEST_FAIL_EVENT,
402
this.TEST_IGNORE_EVENT,
406
for (var i=0; i < events.length; i++){
407
this.on(events[i], this._logEvent, this, true);
412
Y.extend(TestRunner, Y.Event.Target, {
414
//-------------------------------------------------------------------------
416
//-------------------------------------------------------------------------
419
* Fires when a test case is opened but before the first
421
* @event testcasebegin
424
TEST_CASE_BEGIN_EVENT : "testcasebegin",
427
* Fires when all tests in a test case have been executed.
428
* @event testcasecomplete
431
TEST_CASE_COMPLETE_EVENT : "testcasecomplete",
434
* Fires when a test suite is opened but before the first
436
* @event testsuitebegin
439
TEST_SUITE_BEGIN_EVENT : "testsuitebegin",
442
* Fires when all test cases in a test suite have been
444
* @event testsuitecomplete
447
TEST_SUITE_COMPLETE_EVENT : "testsuitecomplete",
450
* Fires when a test has passed.
454
TEST_PASS_EVENT : "pass",
457
* Fires when a test has failed.
461
TEST_FAIL_EVENT : "fail",
464
* Fires when a test has been ignored.
468
TEST_IGNORE_EVENT : "ignore",
471
* Fires when all test suites and test cases have been completed.
475
COMPLETE_EVENT : "complete",
478
* Fires when the run() method is called.
482
BEGIN_EVENT : "begin",
484
//-------------------------------------------------------------------------
485
// Logging-Related Methods
486
//-------------------------------------------------------------------------
490
* Disable logging via Y.log(). Test output will not be visible unless
491
* TestRunner events are subscribed to.
493
* @method disableLogging
496
disableLogging: function(){
501
* Enable logging via Y.log(). Test output is published and can be read via
504
* @method enableLogging
507
enableLogging: function(){
512
* Logs TestRunner events using Y.log().
513
* @param {Object} event The event object for the event.
519
_logEvent: function(event){
523
var messageType = "";
526
case this.BEGIN_EVENT:
527
message = "Testing began at " + (new Date()).toString() + ".";
528
messageType = "info";
531
case this.COMPLETE_EVENT:
532
message = Y.substitute("Testing completed at " +
533
(new Date()).toString() + ".\n" +
534
"Passed:{passed} Failed:{failed} " +
535
"Total:{total} ({ignored} ignored)",
537
messageType = "info";
540
case this.TEST_FAIL_EVENT:
541
message = event.testName + ": failed.\n" + event.error.getMessage();
542
messageType = "fail";
545
case this.TEST_IGNORE_EVENT:
546
message = event.testName + ": ignored.";
547
messageType = "ignore";
550
case this.TEST_PASS_EVENT:
551
message = event.testName + ": passed.";
552
messageType = "pass";
555
case this.TEST_SUITE_BEGIN_EVENT:
556
message = "Test suite \"" + event.testSuite.name + "\" started.";
557
messageType = "info";
560
case this.TEST_SUITE_COMPLETE_EVENT:
561
message = Y.substitute("Test suite \"" +
562
event.testSuite.name + "\" completed" + ".\n" +
563
"Passed:{passed} Failed:{failed} " +
564
"Total:{total} ({ignored} ignored)",
566
messageType = "info";
569
case this.TEST_CASE_BEGIN_EVENT:
570
message = "Test case \"" + event.testCase.name + "\" started.";
571
messageType = "info";
574
case this.TEST_CASE_COMPLETE_EVENT:
575
message = Y.substitute("Test case \"" +
576
event.testCase.name + "\" completed.\n" +
577
"Passed:{passed} Failed:{failed} " +
578
"Total:{total} ({ignored} ignored)",
580
messageType = "info";
583
message = "Unexpected event " + event.type;
587
//only log if required
589
Y.log(message, messageType, "TestRunner");
593
//-------------------------------------------------------------------------
594
// Test Tree-Related Methods
595
//-------------------------------------------------------------------------
598
* Adds a test case to the test tree as a child of the specified node.
599
* @param {TestNode} parentNode The node to add the test case to as a child.
600
* @param {Test.Case} testCase The test case to add.
604
* @method _addTestCaseToTestTree
606
_addTestCaseToTestTree : function (parentNode, testCase /*:Y.Test.Case*/){
609
var node = parentNode.appendChild(testCase),
613
//iterate over the items in the test case
614
for (prop in testCase){
615
if ((prop.indexOf("test") === 0 || (prop.toLowerCase().indexOf("should") > -1 && prop.indexOf(" ") > -1 ))&& Y.Lang.isFunction(testCase[prop])){
616
node.appendChild(prop);
623
* Adds a test suite to the test tree as a child of the specified node.
624
* @param {TestNode} parentNode The node to add the test suite to as a child.
625
* @param {Test.Suite} testSuite The test suite to add.
629
* @method _addTestSuiteToTestTree
631
_addTestSuiteToTestTree : function (parentNode, testSuite /*:Y.Test.Suite*/) {
634
var node = parentNode.appendChild(testSuite);
636
//iterate over the items in the master suite
637
for (var i=0; i < testSuite.items.length; i++){
638
if (testSuite.items[i] instanceof Y.Test.Suite) {
639
this._addTestSuiteToTestTree(node, testSuite.items[i]);
640
} else if (testSuite.items[i] instanceof Y.Test.Case) {
641
this._addTestCaseToTestTree(node, testSuite.items[i]);
647
* Builds the test tree based on items in the master suite. The tree is a hierarchical
648
* representation of the test suites, test cases, and test functions. The resulting tree
649
* is stored in _root and the pointer _cur is set to the root initially.
653
* @method _buildTestTree
655
_buildTestTree : function () {
657
this._root = new TestNode(this.masterSuite);
658
//this._cur = this._root;
660
//iterate over the items in the master suite
661
for (var i=0; i < this.masterSuite.items.length; i++){
662
if (this.masterSuite.items[i] instanceof Y.Test.Suite) {
663
this._addTestSuiteToTestTree(this._root, this.masterSuite.items[i]);
664
} else if (this.masterSuite.items[i] instanceof Y.Test.Case) {
665
this._addTestCaseToTestTree(this._root, this.masterSuite.items[i]);
671
//-------------------------------------------------------------------------
673
//-------------------------------------------------------------------------
676
* Handles the completion of a test object's tests. Tallies test results
677
* from one level up to the next.
678
* @param {TestNode} node The TestNode representing the test object.
680
* @method _handleTestObjectComplete
683
_handleTestObjectComplete : function (node) {
684
if (Y.Lang.isObject(node.testObject)){
687
node.parent.results.passed += node.results.passed;
688
node.parent.results.failed += node.results.failed;
689
node.parent.results.total += node.results.total;
690
node.parent.results.ignored += node.results.ignored;
691
//node.parent.results.duration += node.results.duration;
692
node.parent.results[node.testObject.name] = node.results;
695
if (node.testObject instanceof Y.Test.Suite){
696
node.testObject.tearDown();
697
node.results.duration = (new Date()) - node._start;
698
this.fire(this.TEST_SUITE_COMPLETE_EVENT, { testSuite: node.testObject, results: node.results});
699
} else if (node.testObject instanceof Y.Test.Case){
700
node.results.duration = (new Date()) - node._start;
701
this.fire(this.TEST_CASE_COMPLETE_EVENT, { testCase: node.testObject, results: node.results});
706
//-------------------------------------------------------------------------
707
// Navigation Methods
708
//-------------------------------------------------------------------------
711
* Retrieves the next node in the test tree.
712
* @return {TestNode} The next node in the test tree or null if the end is reached.
717
_next : function () {
719
if (this._cur === null){
720
this._cur = this._root;
721
} else if (this._cur.firstChild) {
722
this._cur = this._cur.firstChild;
723
} else if (this._cur.next) {
724
this._cur = this._cur.next;
726
while (this._cur && !this._cur.next && this._cur !== this._root){
727
this._handleTestObjectComplete(this._cur);
728
this._cur = this._cur.parent;
731
this._handleTestObjectComplete(this._cur);
733
if (this._cur == this._root){
734
this._cur.results.type = "report";
735
this._cur.results.timestamp = (new Date()).toLocaleString();
736
this._cur.results.duration = (new Date()) - this._cur._start;
737
this._lastResults = this._cur.results;
738
this._running = false;
739
this.fire(this.COMPLETE_EVENT, { results: this._lastResults});
742
this._cur = this._cur.next;
750
* Runs a test case or test suite, returning the results.
751
* @param {Test.Case|Test.Suite} testObject The test case or test suite to run.
752
* @return {Object} Results of the execution with properties passed, failed, and total.
759
//flag to indicate if the TestRunner should wait before continuing
760
var shouldWait = false;
762
//get the next test node
763
var node = this._next();
767
//set flag to say the testrunner is running
768
this._running = true;
770
//eliminate last results
771
this._lastResult = null;
773
var testObject = node.testObject;
775
//figure out what to do
776
if (Y.Lang.isObject(testObject)){
777
if (testObject instanceof Y.Test.Suite){
778
this.fire(this.TEST_SUITE_BEGIN_EVENT, { testSuite: testObject });
779
node._start = new Date();
781
} else if (testObject instanceof Y.Test.Case){
782
this.fire(this.TEST_CASE_BEGIN_EVENT, { testCase: testObject });
783
node._start = new Date();
786
//some environments don't support setTimeout
787
if (typeof setTimeout != "undefined"){
788
setTimeout(function(){
789
Y.Test.Runner._run();
801
_resumeTest : function (segment) {
803
//get relevant information
804
var node = this._cur;
806
//we know there's no more waiting now
807
this._waiting = false;
809
//if there's no node, it probably means a wait() was called after resume()
811
//TODO: Handle in some way?
812
//console.log("wait() called after resume()");
813
//this.fire("error", { testCase: "(unknown)", test: "(unknown)", error: new Error("wait() called after resume()")} );
817
var testName = node.testObject;
818
var testCase /*:Y.Test.Case*/ = node.parent.testObject;
820
//cancel other waits if available
821
if (testCase.__yui_wait){
822
clearTimeout(testCase.__yui_wait);
823
delete testCase.__yui_wait;
826
//get the "should" test cases
827
var shouldFail = (testCase._should.fail || {})[testName];
828
var shouldError = (testCase._should.error || {})[testName];
830
//variable to hold whether or not the test failed
838
segment.apply(testCase);
840
//if it should fail, and it got here, then it's a fail because it didn't
842
error = new Y.Assert.ShouldFail();
844
} else if (shouldError){
845
error = new Y.Assert.ShouldError();
851
//cancel any pending waits, the test already failed
852
if (testCase.__yui_wait){
853
clearTimeout(testCase.__yui_wait);
854
delete testCase.__yui_wait;
857
//figure out what type of error it was
858
if (thrown instanceof Y.Assert.Error) {
863
} else if (thrown instanceof Y.Test.Wait){
865
if (Y.Lang.isFunction(thrown.segment)){
866
if (Y.Lang.isNumber(thrown.delay)){
868
//some environments don't support setTimeout
869
if (typeof setTimeout != "undefined"){
870
testCase.__yui_wait = setTimeout(function(){
871
Y.Test.Runner._resumeTest(thrown.segment);
873
this._waiting = true;
875
throw new Error("Asynchronous tests not supported in this environment.");
883
//first check to see if it should error
885
error = new Y.Assert.UnexpectedError(thrown);
888
//check to see what type of data we have
889
if (Y.Lang.isString(shouldError)){
891
//if it's a string, check the error message
892
if (thrown.message != shouldError){
893
error = new Y.Assert.UnexpectedError(thrown);
896
} else if (Y.Lang.isFunction(shouldError)){
898
//if it's a function, see if the error is an instance of it
899
if (!(thrown instanceof shouldError)){
900
error = new Y.Assert.UnexpectedError(thrown);
904
} else if (Y.Lang.isObject(shouldError)){
906
//if it's an object, check the instance and message
907
if (!(thrown instanceof shouldError.constructor) ||
908
thrown.message != shouldError.message){
909
error = new Y.Assert.UnexpectedError(thrown);
920
//fire appropriate event
922
this.fire(this.TEST_FAIL_EVENT, { testCase: testCase, testName: testName, error: error });
924
this.fire(this.TEST_PASS_EVENT, { testCase: testCase, testName: testName });
931
var duration = (new Date()) - node._start;
934
node.parent.results[testName] = {
935
result: failed ? "fail" : "pass",
936
message: error ? error.getMessage() : "Test passed",
943
node.parent.results.failed++;
945
node.parent.results.passed++;
947
node.parent.results.total++;
949
//set timeout not supported in all environments
950
if (typeof setTimeout != "undefined"){
951
setTimeout(function(){
952
Y.Test.Runner._run();
961
* Handles an error as if it occurred within the currently executing
962
* test. This is for mock methods that may be called asynchronously
963
* and therefore out of the scope of the TestRunner. Previously, this
964
* error would bubble up to the browser. Now, this method is used
965
* to tell TestRunner about the error. This should never be called
966
* by anyplace other than the Mock object.
967
* @param {Error} error The error object.
969
* @method _handleError
973
_handleError: function(error){
976
this._resumeTest(function(){
986
* Runs a single test based on the data provided in the node.
987
* @param {TestNode} node The TestNode representing the test to run.
993
_runTest : function (node) {
995
//get relevant information
996
var testName = node.testObject;
997
var testCase /*:Y.Test.Case*/ = node.parent.testObject;
998
var test = testCase[testName];
1000
//get the "should" test cases
1001
var shouldIgnore = (testCase._should.ignore || {})[testName];
1003
//figure out if the test should be ignored or not
1007
node.parent.results[testName] = {
1009
message: "Test ignored",
1014
node.parent.results.ignored++;
1015
node.parent.results.total++;
1017
this.fire(this.TEST_IGNORE_EVENT, { testCase: testCase, testName: testName });
1019
//some environments don't support setTimeout
1020
if (typeof setTimeout != "undefined"){
1021
setTimeout(function(){
1022
Y.Test.Runner._run();
1030
//mark the start time
1031
node._start = new Date();
1036
//now call the body of the test
1037
this._resumeTest(test);
1042
//-------------------------------------------------------------------------
1044
//-------------------------------------------------------------------------
1047
* Retrieves the name of the current result set.
1048
* @return {String} The name of the result set.
1051
getName: function(){
1052
return this.masterSuite.name;
1056
* The name assigned to the master suite of the TestRunner. This is the name
1057
* that is output as the root's name when results are retrieved.
1058
* @param {String} name The name of the result set.
1062
setName: function(name){
1063
this.masterSuite.name = name;
1066
//-------------------------------------------------------------------------
1067
// Protected Methods
1068
//-------------------------------------------------------------------------
1071
* Fires events for the TestRunner. This overrides the default fire()
1072
* method from EventProvider to add the type property to the data that is
1073
* passed through on each event call.
1074
* @param {String} type The type of event to fire.
1075
* @param {Object} data (Optional) Data for the event.
1080
fire : function (type, data) {
1083
TestRunner.superclass.fire.call(this, type, data);
1086
//-------------------------------------------------------------------------
1088
//-------------------------------------------------------------------------
1091
* Adds a test suite or test case to the list of test objects to run.
1092
* @param testObject Either a TestCase or a TestSuite that should be run.
1097
add : function (testObject) {
1098
this.masterSuite.add(testObject);
1103
* Removes all test objects from the runner.
1108
clear : function () {
1109
this.masterSuite = new Y.Test.Suite("yuitests" + (new Date()).getTime());
1113
* Indicates if the TestRunner is waiting for a test to resume
1114
* @return {Boolean} True if the TestRunner is waiting, false if not.
1118
isWaiting: function() {
1119
return this._waiting;
1123
* Indicates that the TestRunner is busy running tests and therefore can't
1124
* be stopped and results cannot be gathered.
1125
* @return {Boolean} True if the TestRunner is running, false if not.
1128
isRunning: function(){
1129
return this._running;
1133
* Returns the last complete results set from the TestRunner. Null is returned
1134
* if the TestRunner is running or no tests have been run.
1135
* @param {Function} format (Optional) A test format to return the results in.
1136
* @return {Object|String} Either the results object or, if a test format is
1137
* passed as the argument, a string representing the results in a specific
1139
* @method getResults
1141
getResults: function(format){
1142
if (!this._running && this._lastResults){
1143
if (Y.Lang.isFunction(format)){
1144
return format(this._lastResults);
1146
return this._lastResults;
1154
* Returns the coverage report for the files that have been executed.
1155
* This returns only coverage information for files that have been
1156
* instrumented using YUI Test Coverage and only those that were run
1158
* @param {Function} format (Optional) A coverage format to return results in.
1159
* @return {Object|String} Either the coverage object or, if a coverage
1160
* format is specified, a string representing the results in that format.
1161
* @method getCoverage
1163
getCoverage: function(format){
1164
if (!this._running && typeof _yuitest_coverage == "object"){
1165
if (Y.Lang.isFunction(format)){
1166
return format(_yuitest_coverage);
1168
return _yuitest_coverage;
1176
* Resumes the TestRunner after wait() was called.
1177
* @param {Function} segment The function to run as the rest
1178
* of the haulted test.
1183
resume : function (segment) {
1184
if (Y.Test.Runner._waiting){
1185
this._resumeTest(segment || function(){});
1187
throw new Error("resume() called without wait().");
1192
* Runs the test suite.
1193
* @param {Boolean} oldMode (Optional) Specifies that the <= 2.8 way of
1194
* internally managing test suites should be used.
1199
run : function (oldMode) {
1201
//pointer to runner to avoid scope issues
1202
var runner = Y.Test.Runner;
1204
//if there's only one suite on the masterSuite, move it up
1205
if (!oldMode && this.masterSuite.items.length == 1 && this.masterSuite.items[0] instanceof Y.Test.Suite){
1206
this.masterSuite = this.masterSuite.items[0];
1209
//build the test tree
1210
runner._buildTestTree();
1212
//set when the test started
1213
runner._root._start = new Date();
1215
//fire the begin event
1216
runner.fire(runner.BEGIN_EVENT);
1223
return new TestRunner();
1231
* The Assert object provides functions to test JavaScript values against
1232
* known and expected results. Whenever a comparison (assertion) fails,
1233
* an error is thrown.
1241
* The number of assertions performed.
1242
* @property _asserts
1248
//-------------------------------------------------------------------------
1250
//-------------------------------------------------------------------------
1253
* Formats a message so that it can contain the original assertion message
1254
* in addition to the custom message.
1255
* @param {String} customMessage The message passed in by the developer.
1256
* @param {String} defaultMessage The message created by the error by default.
1257
* @return {String} The final error message, containing either or both.
1260
* @method _formatMessage
1262
_formatMessage : function (customMessage, defaultMessage) {
1263
var message = customMessage;
1264
if (Y.Lang.isString(customMessage) && customMessage.length > 0){
1265
return Y.Lang.substitute(customMessage, { message: defaultMessage });
1267
return defaultMessage;
1272
* Returns the number of assertions that have been performed.
1277
_getCount: function(){
1278
return this._asserts;
1282
* Increments the number of assertions that have been performed.
1283
* @method _increment
1287
_increment: function(){
1292
* Resets the number of assertions that have been performed to 0.
1301
//-------------------------------------------------------------------------
1302
// Generic Assertion Methods
1303
//-------------------------------------------------------------------------
1306
* Forces an assertion error to occur.
1307
* @param {String} message (Optional) The message to display with the failure.
1311
fail : function (message) {
1312
throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Test force-failed."));
1315
//-------------------------------------------------------------------------
1316
// Equality Assertion Methods
1317
//-------------------------------------------------------------------------
1320
* Asserts that a value is equal to another. This uses the double equals sign
1321
* so type cohersion may occur.
1322
* @param {Object} expected The expected value.
1323
* @param {Object} actual The actual value to test.
1324
* @param {String} message (Optional) The message to display if the assertion fails.
1328
areEqual : function (expected, actual, message) {
1329
Y.Assert._increment();
1330
if (expected != actual) {
1331
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be equal."), expected, actual);
1336
* Asserts that a value is not equal to another. This uses the double equals sign
1337
* so type cohersion may occur.
1338
* @param {Object} unexpected The unexpected value.
1339
* @param {Object} actual The actual value to test.
1340
* @param {String} message (Optional) The message to display if the assertion fails.
1341
* @method areNotEqual
1344
areNotEqual : function (unexpected, actual,
1346
Y.Assert._increment();
1347
if (unexpected == actual) {
1348
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be equal."), unexpected);
1353
* Asserts that a value is not the same as another. This uses the triple equals sign
1354
* so no type cohersion may occur.
1355
* @param {Object} unexpected The unexpected value.
1356
* @param {Object} actual The actual value to test.
1357
* @param {String} message (Optional) The message to display if the assertion fails.
1358
* @method areNotSame
1361
areNotSame : function (unexpected, actual, message) {
1362
Y.Assert._increment();
1363
if (unexpected === actual) {
1364
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be the same."), unexpected);
1369
* Asserts that a value is the same as another. This uses the triple equals sign
1370
* so no type cohersion may occur.
1371
* @param {Object} expected The expected value.
1372
* @param {Object} actual The actual value to test.
1373
* @param {String} message (Optional) The message to display if the assertion fails.
1377
areSame : function (expected, actual, message) {
1378
Y.Assert._increment();
1379
if (expected !== actual) {
1380
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be the same."), expected, actual);
1384
//-------------------------------------------------------------------------
1385
// Boolean Assertion Methods
1386
//-------------------------------------------------------------------------
1389
* Asserts that a value is false. This uses the triple equals sign
1390
* so no type cohersion may occur.
1391
* @param {Object} actual The actual value to test.
1392
* @param {String} message (Optional) The message to display if the assertion fails.
1396
isFalse : function (actual, message) {
1397
Y.Assert._increment();
1398
if (false !== actual) {
1399
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be false."), false, actual);
1404
* Asserts that a value is true. This uses the triple equals sign
1405
* so no type cohersion may occur.
1406
* @param {Object} actual The actual value to test.
1407
* @param {String} message (Optional) The message to display if the assertion fails.
1411
isTrue : function (actual, message) {
1412
Y.Assert._increment();
1413
if (true !== actual) {
1414
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be true."), true, actual);
1419
//-------------------------------------------------------------------------
1420
// Special Value Assertion Methods
1421
//-------------------------------------------------------------------------
1424
* Asserts that a value is not a number.
1425
* @param {Object} actual The value to test.
1426
* @param {String} message (Optional) The message to display if the assertion fails.
1430
isNaN : function (actual, message){
1431
Y.Assert._increment();
1432
if (!isNaN(actual)){
1433
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be NaN."), NaN, actual);
1438
* Asserts that a value is not the special NaN value.
1439
* @param {Object} actual The value to test.
1440
* @param {String} message (Optional) The message to display if the assertion fails.
1444
isNotNaN : function (actual, message){
1445
Y.Assert._increment();
1447
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be NaN."), NaN);
1452
* Asserts that a value is not null. This uses the triple equals sign
1453
* so no type cohersion may occur.
1454
* @param {Object} actual The actual value to test.
1455
* @param {String} message (Optional) The message to display if the assertion fails.
1459
isNotNull : function (actual, message) {
1460
Y.Assert._increment();
1461
if (Y.Lang.isNull(actual)) {
1462
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Values should not be null."), null);
1467
* Asserts that a value is not undefined. This uses the triple equals sign
1468
* so no type cohersion may occur.
1469
* @param {Object} actual The actual value to test.
1470
* @param {String} message (Optional) The message to display if the assertion fails.
1471
* @method isNotUndefined
1474
isNotUndefined : function (actual, message) {
1475
Y.Assert._increment();
1476
if (Y.Lang.isUndefined(actual)) {
1477
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should not be undefined."), undefined);
1482
* Asserts that a value is null. This uses the triple equals sign
1483
* so no type cohersion may occur.
1484
* @param {Object} actual The actual value to test.
1485
* @param {String} message (Optional) The message to display if the assertion fails.
1489
isNull : function (actual, message) {
1490
Y.Assert._increment();
1491
if (!Y.Lang.isNull(actual)) {
1492
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be null."), null, actual);
1497
* Asserts that a value is undefined. This uses the triple equals sign
1498
* so no type cohersion may occur.
1499
* @param {Object} actual The actual value to test.
1500
* @param {String} message (Optional) The message to display if the assertion fails.
1501
* @method isUndefined
1504
isUndefined : function (actual, message) {
1505
Y.Assert._increment();
1506
if (!Y.Lang.isUndefined(actual)) {
1507
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be undefined."), undefined, actual);
1511
//--------------------------------------------------------------------------
1512
// Instance Assertion Methods
1513
//--------------------------------------------------------------------------
1516
* Asserts that a value is an array.
1517
* @param {Object} actual The value to test.
1518
* @param {String} message (Optional) The message to display if the assertion fails.
1522
isArray : function (actual, message) {
1523
Y.Assert._increment();
1524
if (!Y.Lang.isArray(actual)){
1525
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an array."), actual);
1530
* Asserts that a value is a Boolean.
1531
* @param {Object} actual The value to test.
1532
* @param {String} message (Optional) The message to display if the assertion fails.
1536
isBoolean : function (actual, message) {
1537
Y.Assert._increment();
1538
if (!Y.Lang.isBoolean(actual)){
1539
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a Boolean."), actual);
1544
* Asserts that a value is a function.
1545
* @param {Object} actual The value to test.
1546
* @param {String} message (Optional) The message to display if the assertion fails.
1547
* @method isFunction
1550
isFunction : function (actual, message) {
1551
Y.Assert._increment();
1552
if (!Y.Lang.isFunction(actual)){
1553
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a function."), actual);
1558
* Asserts that a value is an instance of a particular object. This may return
1559
* incorrect results when comparing objects from one frame to constructors in
1560
* another frame. For best results, don't use in a cross-frame manner.
1561
* @param {Function} expected The function that the object should be an instance of.
1562
* @param {Object} actual The object to test.
1563
* @param {String} message (Optional) The message to display if the assertion fails.
1564
* @method isInstanceOf
1567
isInstanceOf : function (expected, actual, message) {
1568
Y.Assert._increment();
1569
if (!(actual instanceof expected)){
1570
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value isn't an instance of expected type."), expected, actual);
1575
* Asserts that a value is a number.
1576
* @param {Object} actual The value to test.
1577
* @param {String} message (Optional) The message to display if the assertion fails.
1581
isNumber : function (actual, message) {
1582
Y.Assert._increment();
1583
if (!Y.Lang.isNumber(actual)){
1584
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a number."), actual);
1589
* Asserts that a value is an object.
1590
* @param {Object} actual The value to test.
1591
* @param {String} message (Optional) The message to display if the assertion fails.
1595
isObject : function (actual, message) {
1596
Y.Assert._increment();
1597
if (!Y.Lang.isObject(actual)){
1598
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be an object."), actual);
1603
* Asserts that a value is a string.
1604
* @param {Object} actual The value to test.
1605
* @param {String} message (Optional) The message to display if the assertion fails.
1609
isString : function (actual, message) {
1610
Y.Assert._increment();
1611
if (!Y.Lang.isString(actual)){
1612
throw new Y.Assert.UnexpectedValue(Y.Assert._formatMessage(message, "Value should be a string."), actual);
1617
* Asserts that a value is of a particular type.
1618
* @param {String} expectedType The expected type of the variable.
1619
* @param {Object} actualValue The actual value to test.
1620
* @param {String} message (Optional) The message to display if the assertion fails.
1624
isTypeOf : function (expectedType, actualValue, message){
1625
Y.Assert._increment();
1626
if (typeof actualValue != expectedType){
1627
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Value should be of type " + expectedType + "."), expected, typeof actualValue);
1633
* Asserts that a given condition is true. If not, then a Y.Assert.Error object is thrown
1634
* and the test fails.
1636
* @param {Boolean} condition The condition to test.
1637
* @param {String} message The message to display if the assertion fails.
1641
Y.assert = function(condition, message){
1642
Y.Assert._increment();
1644
throw new Y.Assert.Error(Y.Assert._formatMessage(message, "Assertion failed."));
1649
* Forces an assertion error to occur. Shortcut for Y.Assert.fail().
1651
* @param {String} message (Optional) The message to display with the failure.
1655
Y.fail = Y.Assert.fail;
1657
//-----------------------------------------------------------------------------
1659
//-----------------------------------------------------------------------------
1662
* Error is thrown whenever an assertion fails. It provides methods
1663
* to more easily get at error information and also provides a base class
1664
* from which more specific assertion errors can be derived.
1666
* @param {String} message The message to display when the error occurs.
1667
* @class Assert.Error
1670
Y.Assert.Error = function (message){
1673
arguments.callee.superclass.constructor.call(this, message);
1676
* Error message. Must be duplicated to ensure browser receives it.
1680
this.message = message;
1683
* The name of the error that occurred.
1687
this.name = "Assert Error";
1691
Y.extend(Y.Assert.Error, Error, {
1694
* Returns a fully formatted error for an assertion failure. This should
1695
* be overridden by all subclasses to provide specific information.
1696
* @method getMessage
1697
* @return {String} A string describing the error.
1699
getMessage : function () {
1700
return this.message;
1704
* Returns a string representation of the error.
1706
* @return {String} A string representation of the error.
1708
toString : function () {
1709
return this.name + ": " + this.getMessage();
1713
* Returns a primitive value version of the error. Same as toString().
1715
* @return {String} A primitive value version of the error.
1717
valueOf : function () {
1718
return this.toString();
1724
* ComparisonFailure is subclass of Error that is thrown whenever
1725
* a comparison between two values fails. It provides mechanisms to retrieve
1726
* both the expected and actual value.
1728
* @param {String} message The message to display when the error occurs.
1729
* @param {Object} expected The expected value.
1730
* @param {Object} actual The actual value that caused the assertion to fail.
1731
* @extends Assert.Error
1732
* @class Assert.ComparisonFailure
1735
Y.Assert.ComparisonFailure = function (message, expected, actual){
1738
arguments.callee.superclass.constructor.call(this, message);
1741
* The expected value.
1743
* @property expected
1745
this.expected = expected;
1752
this.actual = actual;
1755
* The name of the error that occurred.
1759
this.name = "ComparisonFailure";
1764
Y.extend(Y.Assert.ComparisonFailure, Y.Assert.Error, {
1767
* Returns a fully formatted error for an assertion failure. This message
1768
* provides information about the expected and actual values.
1770
* @return {String} A string describing the error.
1772
getMessage : function () {
1773
return this.message + "\nExpected: " + this.expected + " (" + (typeof this.expected) + ")" +
1774
"\nActual: " + this.actual + " (" + (typeof this.actual) + ")";
1780
* UnexpectedValue is subclass of Error that is thrown whenever
1781
* a value was unexpected in its scope. This typically means that a test
1782
* was performed to determine that a value was *not* equal to a certain
1785
* @param {String} message The message to display when the error occurs.
1786
* @param {Object} unexpected The unexpected value.
1787
* @extends Assert.Error
1788
* @class Assert.UnexpectedValue
1791
Y.Assert.UnexpectedValue = function (message, unexpected){
1794
arguments.callee.superclass.constructor.call(this, message);
1797
* The unexpected value.
1799
* @property unexpected
1801
this.unexpected = unexpected;
1804
* The name of the error that occurred.
1808
this.name = "UnexpectedValue";
1813
Y.extend(Y.Assert.UnexpectedValue, Y.Assert.Error, {
1816
* Returns a fully formatted error for an assertion failure. The message
1817
* contains information about the unexpected value that was encountered.
1818
* @method getMessage
1819
* @return {String} A string describing the error.
1821
getMessage : function () {
1822
return this.message + "\nUnexpected: " + this.unexpected + " (" + (typeof this.unexpected) + ") ";
1828
* ShouldFail is subclass of Error that is thrown whenever
1829
* a test was expected to fail but did not.
1831
* @param {String} message The message to display when the error occurs.
1832
* @extends Assert.Error
1833
* @class Assert.ShouldFail
1836
Y.Assert.ShouldFail = function (message){
1839
arguments.callee.superclass.constructor.call(this, message || "This test should fail but didn't.");
1842
* The name of the error that occurred.
1846
this.name = "ShouldFail";
1851
Y.extend(Y.Assert.ShouldFail, Y.Assert.Error);
1854
* ShouldError is subclass of Error that is thrown whenever
1855
* a test is expected to throw an error but doesn't.
1857
* @param {String} message The message to display when the error occurs.
1858
* @extends Assert.Error
1859
* @class Assert.ShouldError
1862
Y.Assert.ShouldError = function (message){
1865
arguments.callee.superclass.constructor.call(this, message || "This test should have thrown an error but didn't.");
1868
* The name of the error that occurred.
1872
this.name = "ShouldError";
1877
Y.extend(Y.Assert.ShouldError, Y.Assert.Error);
1880
* UnexpectedError is subclass of Error that is thrown whenever
1881
* an error occurs within the course of a test and the test was not expected
1882
* to throw an error.
1884
* @param {Error} cause The unexpected error that caused this error to be
1886
* @extends Assert.Error
1887
* @class Assert.UnexpectedError
1890
Y.Assert.UnexpectedError = function (cause){
1893
arguments.callee.superclass.constructor.call(this, "Unexpected error: " + cause.message);
1896
* The unexpected error that occurred.
1903
* The name of the error that occurred.
1907
this.name = "UnexpectedError";
1910
* Stack information for the error (if provided).
1914
this.stack = cause.stack;
1919
Y.extend(Y.Assert.UnexpectedError, Y.Assert.Error);
1926
* The ArrayAssert object provides functions to test JavaScript array objects
1927
* for a variety of cases.
1929
* @class ArrayAssert
1936
* Asserts that a value is present in an array. This uses the triple equals
1937
* sign so no type cohersion may occur.
1938
* @param {Object} needle The value that is expected in the array.
1939
* @param {Array} haystack An array of values.
1940
* @param {String} message (Optional) The message to display if the assertion fails.
1944
contains : function (needle, haystack,
1947
Y.Assert._increment();
1949
if (Y.Array.indexOf(haystack, needle) == -1){
1950
Y.Assert.fail(Y.Assert._formatMessage(message, "Value " + needle + " (" + (typeof needle) + ") not found in array [" + haystack + "]."));
1955
* Asserts that a set of values are present in an array. This uses the triple equals
1956
* sign so no type cohersion may occur. For this assertion to pass, all values must
1958
* @param {Object[]} needles An array of values that are expected in the array.
1959
* @param {Array} haystack An array of values to check.
1960
* @param {String} message (Optional) The message to display if the assertion fails.
1961
* @method containsItems
1964
containsItems : function (needles, haystack,
1966
Y.Assert._increment();
1968
//begin checking values
1969
for (var i=0; i < needles.length; i++){
1970
if (Y.Array.indexOf(haystack, needles[i]) == -1){
1971
Y.Assert.fail(Y.Assert._formatMessage(message, "Value " + needles[i] + " (" + (typeof needles[i]) + ") not found in array [" + haystack + "]."));
1977
* Asserts that a value matching some condition is present in an array. This uses
1978
* a function to determine a match.
1979
* @param {Function} matcher A function that returns true if the items matches or false if not.
1980
* @param {Array} haystack An array of values.
1981
* @param {String} message (Optional) The message to display if the assertion fails.
1982
* @method containsMatch
1985
containsMatch : function (matcher, haystack,
1988
Y.Assert._increment();
1989
//check for valid matcher
1990
if (typeof matcher != "function"){
1991
throw new TypeError("ArrayAssert.containsMatch(): First argument must be a function.");
1994
if (!Y.Array.some(haystack, matcher)){
1995
Y.Assert.fail(Y.Assert._formatMessage(message, "No match found in array [" + haystack + "]."));
2000
* Asserts that a value is not present in an array. This uses the triple equals
2001
* Asserts that a value is not present in an array. This uses the triple equals
2002
* sign so no type cohersion may occur.
2003
* @param {Object} needle The value that is expected in the array.
2004
* @param {Array} haystack An array of values.
2005
* @param {String} message (Optional) The message to display if the assertion fails.
2006
* @method doesNotContain
2009
doesNotContain : function (needle, haystack,
2012
Y.Assert._increment();
2014
if (Y.Array.indexOf(haystack, needle) > -1){
2015
Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
2020
* Asserts that a set of values are not present in an array. This uses the triple equals
2021
* sign so no type cohersion may occur. For this assertion to pass, all values must
2023
* @param {Object[]} needles An array of values that are not expected in the array.
2024
* @param {Array} haystack An array of values to check.
2025
* @param {String} message (Optional) The message to display if the assertion fails.
2026
* @method doesNotContainItems
2029
doesNotContainItems : function (needles, haystack,
2032
Y.Assert._increment();
2034
for (var i=0; i < needles.length; i++){
2035
if (Y.Array.indexOf(haystack, needles[i]) > -1){
2036
Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
2043
* Asserts that no values matching a condition are present in an array. This uses
2044
* a function to determine a match.
2045
* @param {Function} matcher A function that returns true if the items matches or false if not.
2046
* @param {Array} haystack An array of values.
2047
* @param {String} message (Optional) The message to display if the assertion fails.
2048
* @method doesNotContainMatch
2051
doesNotContainMatch : function (matcher, haystack,
2054
Y.Assert._increment();
2056
//check for valid matcher
2057
if (typeof matcher != "function"){
2058
throw new TypeError("ArrayAssert.doesNotContainMatch(): First argument must be a function.");
2061
if (Y.Array.some(haystack, matcher)){
2062
Y.Assert.fail(Y.Assert._formatMessage(message, "Value found in array [" + haystack + "]."));
2067
* Asserts that the given value is contained in an array at the specified index.
2068
* This uses the triple equals sign so no type cohersion will occur.
2069
* @param {Object} needle The value to look for.
2070
* @param {Array} haystack The array to search in.
2071
* @param {int} index The index at which the value should exist.
2072
* @param {String} message (Optional) The message to display if the assertion fails.
2076
indexOf : function (needle, haystack, index, message) {
2078
Y.Assert._increment();
2080
//try to find the value in the array
2081
for (var i=0; i < haystack.length; i++){
2082
if (haystack[i] === needle){
2084
Y.Assert.fail(Y.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
2090
//if it makes it here, it wasn't found at all
2091
Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array [" + haystack + "]."));
2095
* Asserts that the values in an array are equal, and in the same position,
2096
* as values in another array. This uses the double equals sign
2097
* so type cohersion may occur. Note that the array objects themselves
2098
* need not be the same for this test to pass.
2099
* @param {Array} expected An array of the expected values.
2100
* @param {Array} actual Any array of the actual values.
2101
* @param {String} message (Optional) The message to display if the assertion fails.
2102
* @method itemsAreEqual
2105
itemsAreEqual : function (expected, actual,
2108
Y.Assert._increment();
2110
//first check array length
2111
if (expected.length != actual.length){
2112
Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
2115
//begin checking values
2116
for (var i=0; i < expected.length; i++){
2117
if (expected[i] != actual[i]){
2118
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not equal."), expected[i], actual[i]);
2124
* Asserts that the values in an array are equivalent, and in the same position,
2125
* as values in another array. This uses a function to determine if the values
2126
* are equivalent. Note that the array objects themselves
2127
* need not be the same for this test to pass.
2128
* @param {Array} expected An array of the expected values.
2129
* @param {Array} actual Any array of the actual values.
2130
* @param {Function} comparator A function that returns true if the values are equivalent
2132
* @param {String} message (Optional) The message to display if the assertion fails.
2134
* @method itemsAreEquivalent
2137
itemsAreEquivalent : function (expected, actual,
2138
comparator, message) {
2140
Y.Assert._increment();
2142
//make sure the comparator is valid
2143
if (typeof comparator != "function"){
2144
throw new TypeError("ArrayAssert.itemsAreEquivalent(): Third argument must be a function.");
2147
//first check array length
2148
if (expected.length != actual.length){
2149
Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
2152
//begin checking values
2153
for (var i=0; i < expected.length; i++){
2154
if (!comparator(expected[i], actual[i])){
2155
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not equivalent."), expected[i], actual[i]);
2161
* Asserts that an array is empty.
2162
* @param {Array} actual The array to test.
2163
* @param {String} message (Optional) The message to display if the assertion fails.
2167
isEmpty : function (actual, message) {
2168
Y.Assert._increment();
2169
if (actual.length > 0){
2170
Y.Assert.fail(Y.Assert._formatMessage(message, "Array should be empty."));
2175
* Asserts that an array is not empty.
2176
* @param {Array} actual The array to test.
2177
* @param {String} message (Optional) The message to display if the assertion fails.
2178
* @method isNotEmpty
2181
isNotEmpty : function (actual, message) {
2182
Y.Assert._increment();
2183
if (actual.length === 0){
2184
Y.Assert.fail(Y.Assert._formatMessage(message, "Array should not be empty."));
2189
* Asserts that the values in an array are the same, and in the same position,
2190
* as values in another array. This uses the triple equals sign
2191
* so no type cohersion will occur. Note that the array objects themselves
2192
* need not be the same for this test to pass.
2193
* @param {Array} expected An array of the expected values.
2194
* @param {Array} actual Any array of the actual values.
2195
* @param {String} message (Optional) The message to display if the assertion fails.
2196
* @method itemsAreSame
2199
itemsAreSame : function (expected, actual,
2202
Y.Assert._increment();
2204
//first check array length
2205
if (expected.length != actual.length){
2206
Y.Assert.fail(Y.Assert._formatMessage(message, "Array should have a length of " + expected.length + " but has a length of " + actual.length));
2209
//begin checking values
2210
for (var i=0; i < expected.length; i++){
2211
if (expected[i] !== actual[i]){
2212
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values in position " + i + " are not the same."), expected[i], actual[i]);
2218
* Asserts that the given value is contained in an array at the specified index,
2219
* starting from the back of the array.
2220
* This uses the triple equals sign so no type cohersion will occur.
2221
* @param {Object} needle The value to look for.
2222
* @param {Array} haystack The array to search in.
2223
* @param {int} index The index at which the value should exist.
2224
* @param {String} message (Optional) The message to display if the assertion fails.
2225
* @method lastIndexOf
2228
lastIndexOf : function (needle, haystack, index, message) {
2230
//try to find the value in the array
2231
for (var i=haystack.length; i >= 0; i--){
2232
if (haystack[i] === needle){
2234
Y.Assert.fail(Y.Assert._formatMessage(message, "Value exists at index " + i + " but should be at index " + index + "."));
2240
//if it makes it here, it wasn't found at all
2241
Y.Assert.fail(Y.Assert._formatMessage(message, "Value doesn't exist in array."));
2250
* The ObjectAssert object provides functions to test JavaScript objects
2251
* for a variety of cases.
2253
* @class ObjectAssert
2258
areEqual: function(expected, actual, message) {
2259
Y.Assert._increment();
2260
Y.Object.each(expected, function(value, name){
2261
if (expected[name] != actual[name]){
2262
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, "Values should be equal for property " + name), expected[name], actual[name]);
2268
* Asserts that an object has a property with the given name. The property may exist either
2269
* on the object instance or in its prototype chain. The same as testing
2270
* "property" in object.
2271
* @param {String} propertyName The name of the property to test.
2272
* @param {Object} object The object to search.
2273
* @param {String} message (Optional) The message to display if the assertion fails.
2277
hasKey: function (propertyName, object, message) {
2278
Y.Assert._increment();
2279
if (!(propertyName in object)){
2280
Y.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object."));
2285
* Asserts that an object has all properties of a reference object. The properties may exist either
2286
* on the object instance or in its prototype chain. The same as testing
2287
* "property" in object.
2288
* @param {Array} properties An array of property names that should be on the object.
2289
* @param {Object} object The object to search.
2290
* @param {String} message (Optional) The message to display if the assertion fails.
2294
hasKeys: function (properties, object, message) {
2295
Y.Assert._increment();
2296
for (var i=0; i < properties.length; i++){
2297
if (!(properties[i] in object)){
2298
Y.fail(Y.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object."));
2304
* Asserts that a property with the given name exists on an object instance (not on its prototype).
2305
* @param {String} propertyName The name of the property to test.
2306
* @param {Object} object The object to search.
2307
* @param {String} message (Optional) The message to display if the assertion fails.
2311
ownsKey: function (propertyName, object, message) {
2312
Y.Assert._increment();
2313
if (!object.hasOwnProperty(propertyName)){
2314
Y.fail(Y.Assert._formatMessage(message, "Property '" + propertyName + "' not found on object instance."));
2319
* Asserts that all properties exist on an object instance (not on its prototype).
2320
* @param {Array} properties An array of property names that should be on the object.
2321
* @param {Object} object The object to search.
2322
* @param {String} message (Optional) The message to display if the assertion fails.
2326
ownsKeys: function (properties, object, message) {
2327
Y.Assert._increment();
2328
for (var i=0; i < properties.length; i++){
2329
if (!object.hasOwnProperty(properties[i])){
2330
Y.fail(Y.Assert._formatMessage(message, "Property '" + properties[i] + "' not found on object instance."));
2336
* Asserts that an object owns no properties.
2337
* @param {Object} object The object to check.
2338
* @param {String} message (Optional) The message to display if the assertion fails.
2339
* @method ownsNoKeys
2342
ownsNoKeys : function (object, message) {
2343
Y.Assert._increment();
2345
var keys = Y.Object.keys(object);
2347
if (keys.length > 0){
2348
Y.fail(Y.Assert._formatMessage(message, "Object owns " + keys.length + " properties but should own none."));
2358
* The DateAssert object provides functions to test JavaScript Date objects
2359
* for a variety of cases.
2368
* Asserts that a date's month, day, and year are equal to another date's.
2369
* @param {Date} expected The expected date.
2370
* @param {Date} actual The actual date to test.
2371
* @param {String} message (Optional) The message to display if the assertion fails.
2372
* @method datesAreEqual
2375
datesAreEqual : function (expected, actual, message){
2376
Y.Assert._increment();
2377
if (expected instanceof Date && actual instanceof Date){
2381
if (expected.getFullYear() != actual.getFullYear()){
2382
msg = "Years should be equal.";
2386
if (expected.getMonth() != actual.getMonth()){
2387
msg = "Months should be equal.";
2390
//last, check the day of the month
2391
if (expected.getDate() != actual.getDate()){
2392
msg = "Days of month should be equal.";
2396
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, msg), expected, actual);
2399
throw new TypeError("Y.Assert.datesAreEqual(): Expected and actual values must be Date objects.");
2404
* Asserts that a date's hour, minutes, and seconds are equal to another date's.
2405
* @param {Date} expected The expected date.
2406
* @param {Date} actual The actual date to test.
2407
* @param {String} message (Optional) The message to display if the assertion fails.
2408
* @method timesAreEqual
2411
timesAreEqual : function (expected, actual, message){
2412
Y.Assert._increment();
2413
if (expected instanceof Date && actual instanceof Date){
2417
if (expected.getHours() != actual.getHours()){
2418
msg = "Hours should be equal.";
2422
if (expected.getMinutes() != actual.getMinutes()){
2423
msg = "Minutes should be equal.";
2426
//last, check the seconds
2427
if (expected.getSeconds() != actual.getSeconds()){
2428
msg = "Seconds should be equal.";
2432
throw new Y.Assert.ComparisonFailure(Y.Assert._formatMessage(message, msg), expected, actual);
2435
throw new TypeError("DateY.AsserttimesAreEqual(): Expected and actual values must be Date objects.");
2441
Y.namespace("Test.Format");
2443
/* (intentionally not documented)
2444
* Basic XML escaping method. Replaces quotes, less-than, greater-than,
2445
* apostrophe, and ampersand characters with their corresponding entities.
2446
* @param {String} text The text to encode.
2447
* @return {String} The XML-escaped text.
2449
function xmlEscape(text){
2451
return text.replace(/[<>"'&]/g, function(value){
2453
case "<": return "<";
2454
case ">": return ">";
2455
case "\"": return """;
2456
case "'": return "'";
2457
case "&": return "&";
2464
* Contains specific formatting options for test result information.
2471
* Returns test results formatted as a JSON string. Requires JSON utility.
2472
* @param {Object} result The results object created by TestRunner.
2473
* @return {String} A JSON-formatted string of results.
2477
Y.Test.Format.JSON = function(results) {
2478
return Y.JSON.stringify(results);
2482
* Returns test results formatted as an XML string.
2483
* @param {Object} result The results object created by TestRunner.
2484
* @return {String} An XML-formatted string of results.
2488
Y.Test.Format.XML = function(results) {
2490
function serializeToXML(results){
2492
xml = "<" + results.type + " name=\"" + xmlEscape(results.name) + "\"";
2494
if (l.isNumber(results.duration)){
2495
xml += " duration=\"" + results.duration + "\"";
2498
if (results.type == "test"){
2499
xml += " result=\"" + results.result + "\" message=\"" + xmlEscape(results.message) + "\">";
2501
xml += " passed=\"" + results.passed + "\" failed=\"" + results.failed + "\" ignored=\"" + results.ignored + "\" total=\"" + results.total + "\">";
2502
Y.Object.each(results, function(value){
2503
if (l.isObject(value) && !l.isArray(value)){
2504
xml += serializeToXML(value);
2509
xml += "</" + results.type + ">";
2514
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToXML(results);
2520
* Returns test results formatted in JUnit XML format.
2521
* @param {Object} result The results object created by TestRunner.
2522
* @return {String} An XML-formatted string of results.
2526
Y.Test.Format.JUnitXML = function(results) {
2528
function serializeToJUnitXML(results){
2532
switch (results.type){
2533
//equivalent to testcase in JUnit
2535
if (results.result != "ignore"){
2536
xml = "<testcase name=\"" + xmlEscape(results.name) + "\" time=\"" + (results.duration/1000) + "\">";
2537
if (results.result == "fail"){
2538
xml += "<failure message=\"" + xmlEscape(results.message) + "\"><![CDATA[" + results.message + "]]></failure>";
2540
xml+= "</testcase>";
2544
//equivalent to testsuite in JUnit
2547
xml = "<testsuite name=\"" + xmlEscape(results.name) + "\" tests=\"" + results.total + "\" failures=\"" + results.failed + "\" time=\"" + (results.duration/1000) + "\">";
2549
Y.Object.each(results, function(value){
2550
if (l.isObject(value) && !l.isArray(value)){
2551
xml += serializeToJUnitXML(value);
2555
xml += "</testsuite>";
2558
//no JUnit equivalent, don't output anything
2560
Y.Object.each(results, function(value){
2561
if (l.isObject(value) && !l.isArray(value)){
2562
xml += serializeToJUnitXML(value);
2567
//top-level, equivalent to testsuites in JUnit
2570
xml = "<testsuites>";
2572
Y.Object.each(results, function(value){
2573
if (l.isObject(value) && !l.isArray(value)){
2574
xml += serializeToJUnitXML(value);
2578
xml += "</testsuites>";
2587
return "<?xml version=\"1.0\" encoding=\"UTF-8\"?>" + serializeToJUnitXML(results);
2591
* Returns test results formatted in TAP format.
2592
* For more information, see <a href="http://testanything.org/">Test Anything Protocol</a>.
2593
* @param {Object} result The results object created by TestRunner.
2594
* @return {String} A TAP-formatted string of results.
2598
Y.Test.Format.TAP = function(results) {
2600
var currentTestNum = 1;
2602
function serializeToTAP(results){
2606
switch (results.type){
2609
if (results.result != "ignore"){
2611
text = "ok " + (currentTestNum++) + " - " + results.name;
2613
if (results.result == "fail"){
2614
text = "not " + text + " - " + results.message;
2619
text = "#Ignored test " + results.name + "\n";
2625
text = "#Begin testcase " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2627
Y.Object.each(results, function(value){
2628
if (l.isObject(value) && !l.isArray(value)){
2629
text += serializeToTAP(value);
2633
text += "#End testcase " + results.name + "\n";
2640
text = "#Begin testsuite " + results.name + "(" + results.failed + " failed of " + results.total + ")\n";
2642
Y.Object.each(results, function(value){
2643
if (l.isObject(value) && !l.isArray(value)){
2644
text += serializeToTAP(value);
2648
text += "#End testsuite " + results.name + "\n";
2653
Y.Object.each(results, function(value){
2654
if (l.isObject(value) && !l.isArray(value)){
2655
text += serializeToTAP(value);
2666
return "1.." + results.total + "\n" + serializeToTAP(results);
2673
Y.namespace("Coverage.Format");
2676
* Contains specific formatting options for coverage information.
2677
* @class Coverage.Format
2682
* Returns the coverage report in JSON format. This is the straight
2683
* JSON representation of the native coverage report.
2684
* @param {Object} coverage The coverage report object.
2685
* @return {String} A JSON-formatted string of coverage data.
2689
Y.Coverage.Format.JSON = function(coverage){
2690
return Y.JSON.stringify(coverage);
2694
* Returns the coverage report in a JSON format compatible with
2695
* Xdebug. See <a href="http://www.xdebug.com/docs/code_coverage">Xdebug Documentation</a>
2696
* for more information. Note: function coverage is not available
2698
* @param {Object} coverage The coverage report object.
2699
* @return {String} A JSON-formatted string of coverage data.
2700
* @method XdebugJSON
2703
Y.Coverage.Format.XdebugJSON = function(coverage){
2705
Y.Object.each(coverage, function(value, name){
2706
report[name] = coverage[name].lines;
2708
return Y.JSON.stringify(report);
2714
Y.namespace("Test");
2717
* An object capable of sending test results to a server.
2718
* @param {String} url The URL to submit the results to.
2719
* @param {Function} format (Optiona) A function that outputs the results in a specific format.
2720
* Default is Y.Test.Format.XML.
2725
Y.Test.Reporter = function(url, format) {
2728
* The URL to submit the data to.
2735
* The formatting function to call when submitting the data.
2739
this.format = format || Y.Test.Format.XML;
2742
* Extra fields to submit with the request.
2747
this._fields = new Object();
2750
* The form element used to submit the results.
2751
* @type HTMLFormElement
2758
* Iframe used as a target for form submission.
2759
* @type HTMLIFrameElement
2763
this._iframe = null;
2766
Y.Test.Reporter.prototype = {
2768
//restore missing constructor
2769
constructor: Y.Test.Reporter,
2772
* Adds a field to the form that submits the results.
2773
* @param {String} name The name of the field.
2774
* @param {Variant} value The value of the field.
2778
addField : function (name, value){
2779
this._fields[name] = value;
2783
* Removes all previous defined fields.
2787
clearFields : function(){
2788
this._fields = new Object();
2792
* Cleans up the memory associated with the TestReporter, removing DOM elements
2793
* that were created.
2797
destroy : function() {
2799
this._form.parentNode.removeChild(this._form);
2803
this._iframe.parentNode.removeChild(this._iframe);
2804
this._iframe = null;
2806
this._fields = null;
2810
* Sends the report to the server.
2811
* @param {Object} results The results object created by TestRunner.
2815
report : function(results){
2817
//if the form hasn't been created yet, create it
2819
this._form = document.createElement("form");
2820
this._form.method = "post";
2821
this._form.style.visibility = "hidden";
2822
this._form.style.position = "absolute";
2823
this._form.style.top = 0;
2824
document.body.appendChild(this._form);
2826
// IE won't let you assign a name using the DOM, must do it the hacky way
2827
var iframeContainer = document.createElement("div");
2828
iframeContainer.innerHTML = "<iframe name=\"yuiTestTarget\"></iframe>";
2829
this._iframe = iframeContainer.firstChild;
2831
this._iframe.src = "javascript:false";
2832
this._iframe.style.visibility = "hidden";
2833
this._iframe.style.position = "absolute";
2834
this._iframe.style.top = 0;
2835
document.body.appendChild(this._iframe);
2837
this._form.target = "yuiTestTarget";
2840
//set the form's action
2841
this._form.action = this.url;
2843
//remove any existing fields
2844
while(this._form.hasChildNodes()){
2845
this._form.removeChild(this._form.lastChild);
2848
//create default fields
2849
this._fields.results = this.format(results);
2850
this._fields.useragent = navigator.userAgent;
2851
this._fields.timestamp = (new Date()).toLocaleString();
2853
//add fields to the form
2854
Y.Object.each(this._fields, function(value, prop){
2855
if (typeof value != "function"){
2856
var input = document.createElement("input");
2857
input.type = "hidden";
2859
input.value = value;
2860
this._form.appendChild(input);
2864
//remove default fields
2865
delete this._fields.results;
2866
delete this._fields.useragent;
2867
delete this._fields.timestamp;
2869
if (arguments[1] !== false){
2870
this._form.submit();
2881
* Creates a new mock object.
2884
* @param {Object} template (Optional) An object whose methods
2885
* should be stubbed out on the mock object. This object
2886
* is used as the prototype of the mock object so instanceof
2889
Y.Mock = function(template){
2891
//use blank object is nothing is passed in
2892
template = template || {};
2896
//try to create mock that keeps prototype chain intact
2898
mock = Y.Object(template);
2901
Y.log("Couldn't create mock with prototype.", "warn", "Mock");
2904
//create new versions of the methods so that they don't actually do anything
2905
Y.Object.each(template, function(name){
2906
if (Y.Lang.isFunction(template[name])){
2907
mock[name] = function(){
2908
Y.Assert.fail("Method " + name + "() was called but was not expected to be.");
2918
* Assigns an expectation to a mock object. This is used to create
2919
* methods and properties on the mock object that are monitored for
2920
* calls and changes, respectively.
2921
* @param {Object} mock The object to add the expectation to.
2922
* @param {Object} expectation An object defining the expectation. For
2923
* a method, the keys "method" and "args" are required with
2924
* an optional "returns" key available. For properties, the keys
2925
* "property" and "value" are required.
2930
Y.Mock.expect = function(mock /*:Object*/, expectation /*:Object*/){
2932
//make sure there's a place to store the expectations
2933
if (!mock.__expectations) {
2934
mock.__expectations = {};
2937
//method expectation
2938
if (expectation.method){
2939
var name = expectation.method,
2940
args = expectation.args || expectation.arguments || [],
2941
result = expectation.returns,
2942
callCount = Y.Lang.isNumber(expectation.callCount) ? expectation.callCount : 1,
2943
error = expectation.error,
2944
run = expectation.run || function(){};
2947
mock.__expectations[name] = expectation;
2948
expectation.callCount = callCount;
2949
expectation.actualCallCount = 0;
2952
Y.Array.each(args, function(arg, i, array){
2953
if (!(array[i] instanceof Y.Mock.Value)){
2954
array[i] = Y.Mock.Value(Y.Assert.areSame, [arg], "Argument " + i + " of " + name + "() is incorrect.");
2958
//if the method is expected to be called
2960
mock[name] = function(){
2962
expectation.actualCallCount++;
2963
Y.Assert.areEqual(args.length, arguments.length, "Method " + name + "() passed incorrect number of arguments.");
2964
for (var i=0, len=args.length; i < len; i++){
2966
args[i].verify(arguments[i]);
2968
// Y.Assert.fail("Argument " + i + " (" + arguments[i] + ") was not expected to be used.");
2973
run.apply(this, arguments);
2979
//route through TestRunner for proper handling
2980
Y.Test.Runner._handleError(ex);
2987
//method should fail if called when not expected
2988
mock[name] = function(){
2990
Y.Assert.fail("Method " + name + "() should not have been called.");
2992
//route through TestRunner for proper handling
2993
Y.Test.Runner._handleError(ex);
2997
} else if (expectation.property){
2999
mock.__expectations[name] = expectation;
3004
* Verifies that all expectations of a mock object have been met and
3005
* throws an assertion error if not.
3006
* @param {Object} mock The object to verify..
3011
Y.Mock.verify = function(mock /*:Object*/){
3013
Y.Object.each(mock.__expectations, function(expectation){
3014
if (expectation.method) {
3015
Y.Assert.areEqual(expectation.callCount, expectation.actualCallCount, "Method " + expectation.method + "() wasn't called the expected number of times.");
3016
} else if (expectation.property){
3017
Y.Assert.areEqual(expectation.value, mock[expectation.property], "Property " + expectation.property + " wasn't set to the correct value.");
3021
//route through TestRunner for proper handling
3022
Y.Test.Runner._handleError(ex);
3027
* Defines a custom mock validator for a particular argument.
3029
* @param {Function} method The method to run on the argument. This should
3030
* throw an assertion error if the value is invalid.
3031
* @param {Array} originalArgs The first few arguments to pass in
3032
* to the method. The value to test and failure message are
3033
* always the last two arguments passed into method.
3034
* @param {String} message The message to display if validation fails. If
3035
* not specified, the default assertion error message is displayed.
3037
* @constructor Value
3040
Y.Mock.Value = function(method, originalArgs, message){
3041
if (Y.instanceOf(this, Y.Mock.Value)){
3042
this.verify = function(value){
3043
var args = [].concat(originalArgs || []);
3046
method.apply(null, args);
3049
return new Y.Mock.Value(method, originalArgs, message);
3054
* Mock argument validator that accepts any value as valid.
3059
Y.Mock.Value.Any = Y.Mock.Value(function(){});
3062
* Mock argument validator that accepts only Boolean values as valid.
3067
Y.Mock.Value.Boolean = Y.Mock.Value(Y.Assert.isBoolean);
3070
* Mock argument validator that accepts only numeric values as valid.
3075
Y.Mock.Value.Number = Y.Mock.Value(Y.Assert.isNumber);
3078
* Mock argument validator that accepts only String values as valid.
3083
Y.Mock.Value.String = Y.Mock.Value(Y.Assert.isString);
3086
* Mock argument validator that accepts only non-null objects values as valid.
3091
Y.Mock.Value.Object = Y.Mock.Value(Y.Assert.isObject);
3094
* Mock argument validator that accepts onlyfunctions as valid.
3095
* @property Function
3099
Y.Mock.Value.Function = Y.Mock.Value(Y.Assert.isFunction);
3100
/*Stub for future compatibility*/
3101
if (typeof YUITest == "undefined" || !YUITest) {
3103
TestRunner: Y.Test.Runner,
3104
ResultsFormat: Y.Test.Format,
3105
CoverageFormat: Y.Coverage.Format
3110
}, '3.4.1' ,{requires:['event-simulate','event-custom','substitute','json-stringify']});