1 //guarantee in global scope and scope protection
2 (function(/* Array? */scriptArgs) {
4 //here's the definition of doh.runner...which really defines global doh
5 var d = function(doh) {
7 // Utility Functions and Classes
10 if (typeof this["print"] == "undefined" && console) {
11 print = console.log.bind(console);
19 doh._line = "------------------------------------------------------------";
21 doh.debug = function(){
23 // takes any number of arguments and sends them to whatever debugging
24 // or logging facility is available in this environment
26 var a = Array.prototype.slice.call(arguments, 0);
28 doh._print(a.join(" "));
31 doh.error = function(){
33 // logging method to be used to send Error objects, so that
34 // whatever debugging or logging facility you have can decide to treat it
35 // as an Error object and show additional information - such as stack trace
37 // YOUR TEST RUNNER NEEDS TO IMPLEMENT THIS
38 var a = Array.prototype.slice.call(arguments, 0);
40 doh._print(a.join(" "));
43 doh._AssertFailure = function(msg, hint){
44 // idea for this as way of dis-ambiguating error types is from JUM.
45 // The JUM is dead! Long live the JUM!
47 if(!(this instanceof doh._AssertFailure)){
48 return new doh._AssertFailure(msg, hint);
51 msg = (new String(msg||""))+" with hint: \n\t\t"+(new String(hint)+"\n");
53 this.message = new String(msg||"");
56 doh._AssertFailure.prototype = new Error();
57 doh._AssertFailure.prototype.constructor = doh._AssertFailure;
58 doh._AssertFailure.prototype.name = "doh._AssertFailure";
61 // State Keeping and Reporting
67 doh._failureCount = 0;
68 doh._currentGroup = null;
69 doh._currentTest = null;
71 doh._init = function(){
72 this._currentGroup = null;
73 this._currentTest = null;
75 this._failureCount = 0;
76 this.debug(this._testCount, "tests to run in", this._groupCount, "groups");
86 doh.registerTestNs = function(/*String*/ group, /*Object*/ ns){
88 // adds the passed namespace object to the list of objects to be
89 // searched for test groups. Only "public" functions (not prefixed
90 // with "_") will be added as tests to be run. If you'd like to use
91 // fixtures (setUp(), tearDown(), and runTest()), please use
92 // registerTest() or registerTests().
94 if( (x.charAt(0) != "_") &&
95 (typeof ns[x] == "function") ){
96 this.registerTest(group, ns[x]);
101 doh._testRegistered = function(group, fixture){
102 // slot to be filled in
105 doh._groupStarted = function(group){
106 // slot to be filled in
109 doh._groupFinished = function(group, success){
110 // slot to be filled in
113 doh._testStarted = function(group, fixture){
114 // slot to be filled in
117 doh._testFinished = function(group, fixture, success){
118 // slot to be filled in
121 doh.registerGroup = function( /*String*/ group,
122 /*Array||Function||Object*/ tests,
124 /*Function*/ tearDown,
127 // registers an entire group of tests at once and provides a setUp and
128 // tearDown facility for groups. If you call this method with only
129 // setUp and tearDown parameters, they will replace previously
130 // installed setUp or tearDown functions for the group with the new
133 // string name of the group
135 // either a function or an object or an array of functions/objects. If
136 // an object, it must contain at *least* a "runTest" method, and may
137 // also contain "setUp" and "tearDown" methods. These will be invoked
138 // on either side of the "runTest" method (respectively) when the test
139 // is run. If an array, it must contain objects matching the above
140 // description or test functions.
141 // setUp: a function for initializing the test group
142 // tearDown: a function for initializing the test group
143 // type: The type of tests these are, such as a group of performance tests
144 // null/undefied are standard DOH tests, the valye 'perf' enables
145 // registering them as performance tests.
147 this.register(group, tests, type);
150 this._groups[group].setUp = setUp;
153 this._groups[group].tearDown = tearDown;
157 doh._getTestObj = function(group, test, type){
159 if(typeof test == "string"){
160 if(test.substr(0, 4)=="url:"){
161 return this.registerUrl(group, test);
164 name: test.replace("/\s/g", "_") // FIXME: bad escapement
166 tObj.runTest = new Function("t", test);
168 }else if(typeof test == "function"){
169 // if we didn't get a fixture, wrap the function
170 tObj = { "runTest": test };
172 tObj.name = test.name;
175 var fStr = "function ";
176 var ts = tObj.runTest+"";
177 if(0 <= ts.indexOf(fStr)){
178 tObj.name = ts.split(fStr)[1].split("(", 1)[0];
180 // doh.debug(tObj.runTest.toSource());
184 // FIXME: try harder to get the test name here
189 doh.registerTest = function(/*String*/ group,
190 /*Function||Object*/ test,
193 // add the provided test function or fixture object to the specified
196 // string name of the group to add the test to
198 // either a function or an object. If an object, it must contain at
199 // *least* a "runTest" method, and may also contain "setUp" and
200 // "tearDown" methods. These will be invoked on either side of the
201 // "runTest" method (respectively) when the test is run.
203 // An identifier denoting the type of testing that the test performs, such
204 // as a performance test. If null, defaults to regular DOH test.
205 if(!this._groups[group]){
207 this._groups[group] = [];
208 this._groups[group].inFlight = 0;
210 var tObj = this._getTestObj(group, test, type);
211 if(!tObj){ return null; }
212 this._groups[group].push(tObj);
214 this._testRegistered(group, tObj);
218 doh.registerTests = function(/*String*/ group,
222 // registers a group of tests, treating each element of testArr as
223 // though it were being (along with group) passed to the registerTest
224 // method. It also uses the type to decide how the tests should
225 // behave, by defining the type of tests these are, such as performance
227 var register = this.registerTest.bind(this, group);
228 testArr.forEach(function(test) { register(test, type); });
231 // FIXME: remove the doh.add alias SRTL.
232 doh.register = doh.add = function(groupOrNs, testOrNull, type){
234 // "magical" variant of registerTests, registerTest, and
235 // registerTestNs. Will accept the calling arguments of any of these
236 // methods and will correctly guess the right one to register with.
237 if( (arguments.length == 1)&&
238 (typeof groupOrNs == "string") ){
239 if(groupOrNs.substr(0, 4)=="url:"){
240 this.registerUrl(groupOrNs, null, null, type);
242 this.registerTest("ungrouped", groupOrNs, type);
245 if(arguments.length == 1){
246 this.debug("invalid args passed to doh.register():", groupOrNs, ",", testOrNull);
249 if(typeof testOrNull == "string"){
250 if(testOrNull.substr(0, 4)=="url:"){
251 this.registerUrl(testOrNull, null, null, type);
253 this.registerTest(groupOrNs, testOrNull, type);
255 // this.registerTestNs(groupOrNs, testOrNull);
258 if(doh._isArray(testOrNull)){
259 this.registerTests(groupOrNs, testOrNull, type);
262 this.registerTest(groupOrNs, testOrNull, type);
266 // Assertions and In-Test Utilities
269 doh.t = doh.assertTrue = function(/*Object*/ condition, /*String?*/ hint){
271 // is the passed item "truthy"?
272 if(arguments.length < 1){
273 throw new doh._AssertFailure(
274 "assertTrue failed because it was not passed at least 1 argument"
277 if(!eval(condition)){
278 throw new doh._AssertFailure("assertTrue('" + condition + "') failed", hint);
282 doh.f = doh.assertFalse = function(/*Object*/ condition, /*String?*/ hint){
284 // is the passed item "falsey"?
285 if(arguments.length < 1){
286 throw new doh._AssertFailure(
287 "assertFalse failed because it was not passed at least 1 argument"
292 throw new doh._AssertFailure("assertFalse('" + condition + "') failed", hint);
296 doh.e = doh.assertError = function(/*Error object*/expectedError,
298 /*String*/functionName,
302 // Test for a certain error to be thrown by the given function.
304 // t.assertError(dojox.data.QueryReadStore.InvalidAttributeError, store, "getValue", [item, "NOT THERE"]);
305 // t.assertError(dojox.data.QueryReadStore.InvalidItemError, store, "getValue", ["not an item", "NOT THERE"]);
307 scope[functionName].apply(scope, args);
309 if(e instanceof expectedError){
312 throw new doh._AssertFailure(
313 "assertError() failed:\n\texpected error\n\t\t" +
314 expectedError + "\n\tbut got\n\t\t" + e +"\n\n",
319 throw new doh._AssertFailure(
320 "assertError() failed:\n\texpected error\n\t\t" +
321 expectedError+"\n\tbut no error caught\n\n",
327 doh.is = doh.assertEqual = function(/*Object*/ expected, /*Object*/ actual, /*String?*/ hint){
329 // are the passed expected and actual objects/values deeply
332 // Compare undefined always with three equal signs, because undefined==null
333 // is true, but undefined===null is false.
334 if((expected === undefined)&&(actual === undefined)){
337 if(arguments.length < 2){
338 throw doh._AssertFailure(
339 "assertEqual failed because it was not passed 2 arguments");
342 (expected === actual) ||
343 (expected == actual) ||
344 ( typeof expected == "number" &&
345 typeof actual == "number" &&
346 isNaN(expected) && isNaN(actual)
352 (this._isArray(expected) &&
353 this._isArray(actual)
355 this._arrayEq(expected, actual)
360 (typeof expected == "object" && typeof actual == "object") &&
361 this._objPropEq(expected, actual)
365 throw new doh._AssertFailure(
366 "assertEqual() failed:\n\texpected\n\t\t"+expected+
367 "\n\tbut got\n\t\t"+actual+"\n\n",
371 doh.isNot = doh.assertNotEqual = function(/*Object*/ notExpected,
375 // are the passed notexpected and actual objects/values deeply
378 // Compare undefined always with three equal signs, because undefined==null
379 // is true, but undefined===null is false.
381 (notExpected === undefined) &&
382 (actual === undefined)
384 throw new doh._AssertFailure(
385 "assertNotEqual() failed: not expected |"+notExpected+
386 "| but got |"+actual+"|",
390 if(arguments.length < 2){
391 throw doh._AssertFailure(
392 "assertEqual failed because it was not passed 2 arguments"
396 if((notExpected === actual)||(notExpected == actual)){
397 throw new doh._AssertFailure(
398 "assertNotEqual() failed: not expected |"+notExpected+
399 "| but got |"+actual+"|",
403 if( (this._isArray(notExpected) && this._isArray(actual))&&
404 (this._arrayEq(notExpected, actual)) ){
405 throw new doh._AssertFailure(
406 "assertNotEqual() failed: not expected |"+notExpected+
407 "| but got |"+actual+"|",
410 if( ((typeof notExpected == "object")&&((typeof actual == "object"))) ){
413 isequal = this._objPropEq(notExpected, actual);
415 if( !(e instanceof doh._AssertFailure) ){
416 throw e; //other exceptions, just throw it
420 throw new doh._AssertFailure(
421 "assertNotEqual() failed: not expected |"+notExpected+
422 "| but got |"+actual+"|",
429 doh._arrayEq = function(expected, actual){
430 if (expected.length != actual.length) {
434 for(var x=0; x<expected.length; x++){
435 if (!doh.assertEqual(expected[x], actual[x])) { return false; }
440 doh._objPropEq = function(expected, actual){
441 // Degenerate case: if they are both null, then their "properties" are equal.
442 if (expected === null && actual === null) {
446 // If only one is null, they aren't equal.
447 if (expected === null || actual === null) {
451 if (expected instanceof Date) {
452 return actual instanceof Date && expected.getTime() == actual.getTime();
456 // Make sure ALL THE SAME properties are in both objects!
457 for (x in actual) { // Lets check "actual" here, expected is checked below.
458 if (expected[x] === undefined) {
463 for (x in expected) {
464 if (!doh.assertEqual(expected[x], actual[x])) {
472 doh._isArray = function(it){
473 return (it && it instanceof Array || typeof it == "array");
479 doh._setupGroupForRun = function(/*String*/ groupName, /*Integer*/ idx){
480 var tg = this._groups[groupName];
481 this.debug(this._line);
482 this.debug("GROUP", "\""+groupName+"\"", "has", tg.length, "test"+((tg.length > 1) ? "s" : "")+" to run");
485 doh._handleFailure = function(groupName, fixture, e){
486 // this.debug("FAILED test:", fixture.name);
487 // mostly borrowed from JUM
488 this._groups[groupName].failures++;
490 if(e instanceof this._AssertFailure){
491 this._failureCount++;
492 if(e["fileName"]){ out += e.fileName + ':'; }
493 if(e["lineNumber"]){ out += e.lineNumber + ' '; }
494 out += e+": "+e.message;
495 this.debug("\t_AssertFailure:", out);
500 if(fixture.runTest["toSource"]){
501 var ss = fixture.runTest.toSource();
502 this.debug("\tERROR IN:\n\t\t", ss);
504 this.debug("\tERROR IN:\n\t\t", fixture.runTest);
506 if (e.rhinoException) {
507 e.rhinoException.printStackTrace();
508 } else if(e.javaException) {
509 e.javaException.printStackTrace();
518 doh._runFixture = function(groupName, fixture){
519 var tg = this._groups[groupName];
520 this._testStarted(groupName, fixture);
522 // run it, catching exceptions and reporting them
524 doh.debug(fixture.name);
525 // let doh reference "this.group.thinger..." which can be set by
526 // another test or group-level setUp function
528 // only execute the parts of the fixture we've got
530 if(fixture["setUp"]){
533 if(fixture["runTest"]){ // should we error out of a fixture doesn't have a runTest?
534 fixture.startTime = new Date();
535 var ret = fixture.runTest(this);
536 fixture.endTime = new Date();
541 if(fixture["tearDown"]){
542 fixture.tearDown(this);
545 this._handleFailure(groupName, fixture, e);
547 if(!fixture.endTime){
548 fixture.endTime = new Date();
554 doh.runGroup = function(/*String*/ groupName, /*Integer*/ idx){
556 // runs the specified test group
558 var tg = this._groups[groupName];
559 if(tg.skip === true){ return; }
560 if(this._isArray(tg)){
563 if(tg["tearDown"]){ tg.tearDown(this); }
564 doh._groupFinished(groupName, !tg.failures);
573 doh._groupStarted(groupName);
575 this._setupGroupForRun(groupName, idx);
576 if(tg["setUp"]){ tg.setUp(this); }
578 for(var y=(idx||0); y<tg.length; y++){
579 doh._runFixture(groupName, tg[y]);
583 if(tg["tearDown"]){ tg.tearDown(this); }
584 doh._groupFinished(groupName, !tg.failures);
589 doh._onEnd = function(){}
591 doh._report = function(){
593 // a private method to be implemented/replaced by the "locally
594 // appropriate" test runner
596 this.debug(this._line);
597 this.debug("| TEST SUMMARY:");
598 this.debug(this._line);
599 this.debug("\t", this._testCount, "tests in", this._groupCount, "groups");
600 this.debug("\t", this._errorCount, "errors");
601 this.debug("\t", this._failureCount, "failures");
604 doh.run = function(){
606 // begins or resumes the test process.
607 // this.debug("STARTING");
608 var cg = this._currentGroup;
609 var ct = this._currentTest;
615 this._currentGroup = null;
616 this._currentTest = null;
618 for(var x in this._groups){
620 (!found) && (x == cg)
624 this._currentGroup = x;
627 this.runGroup(x, ct);
633 this._currentGroup = null;
634 this._currentTest = null;
639 }; //end of definition of doh/runner, which really defines global doh
641 // this is guaranteed in the global scope, not matter what kind of eval is
642 // thrown at us define global doh
643 if(typeof doh == "undefined") {
646 if(typeof define == "undefined" || define.vendor=="dojotoolkit.org") {
647 // using dojo 1.x loader or no dojo on the page
650 // using an AMD loader
651 doh.runnerFactory = d;
654 }).call(null, typeof arguments=="undefined" ?
655 [] : Array.prototype.slice.call(arguments)