LXR Roots - seamonkey Cross Reference: seamonkey
seamonkey/ dom/ tests/ mochitest/ ajax/ lib/ SimpleTest.js
CVS Log
CVS Blame
CVS Graph
Raw output
changes to
this file in
the last:
day
week
month
  1 /**
  2  * SimpleTest, a partial Test.Simple/Test.More API compatible test library.
  3  *
  4  * Why?
  5  *
  6  * Test.Simple doesn't work on IE < 6.
  7  * TODO:
  8  *  * Support the Test.Simple API used by MochiKit, to be able to test MochiKit 
  9  * itself against IE 5.5
 10  *
 11 **/
 12 
 13 if (typeof(SimpleTest) == "undefined") {
 14     var SimpleTest = {};
 15 }
 16 
 17 var parentRunner = null;
 18 if (typeof(parent) != "undefined" && parent.TestRunner) {
 19     parentRunner = parent.TestRunner;
 20 } else if (parent && parent.wrappedJSObject &&
 21            parent.wrappedJSObject.TestRunner) {
 22     parentRunner = parent.wrappedJSObject.TestRunner;
 23 }
 24 
 25 // Check to see if the TestRunner is present and has logging
 26 if (parentRunner) {
 27     SimpleTest._logEnabled = parentRunner.logEnabled;
 28 }
 29 
 30 SimpleTest._tests = [];
 31 SimpleTest._stopOnLoad = true;
 32 
 33 /**
 34  * Something like assert.
 35 **/
 36 SimpleTest.ok = function (condition, name, diag) {
 37     var test = {'result': !!condition, 'name': name, 'diag': diag || ""};
 38     if (SimpleTest._logEnabled)
 39         SimpleTest._logResult(test, "PASS", "FAIL");
 40     SimpleTest._tests.push(test);
 41 };
 42 
 43 /**
 44  * Roughly equivalent to ok(a==b, name)
 45 **/
 46 SimpleTest.is = function (a, b, name) {
 47     var repr = MochiKit.Base.repr;
 48     SimpleTest.ok(a == b, name, "got " + repr(a) + ", expected " + repr(b));
 49 };
 50 
 51 SimpleTest.isnot = function (a, b, name) {
 52     var repr = MochiKit.Base.repr;
 53     SimpleTest.ok(a != b, name, "Didn't expect " + repr(a) + ", but got it.");
 54 };
 55 
 56 //  --------------- Test.Builder/Test.More todo() -----------------
 57 
 58 SimpleTest.todo = function(condition, name, diag) {
 59   var test = {'result': !!condition, 'name': name, 'diag': diag || "", todo: true};
 60   if (SimpleTest._logEnabled)
 61       SimpleTest._logResult(test, "TODO WORKED?", "TODO");
 62   SimpleTest._tests.push(test);
 63 }
 64 
 65 SimpleTest._logResult = function(test, passString, failString) {
 66   var msg = test.result ? passString : failString;
 67   msg += " | " + test.name;
 68   if (test.result) {
 69       if (test.todo)
 70           parentRunner.logger.error(msg)
 71       else
 72           parentRunner.logger.log(msg);
 73   } else {
 74       msg += " | " + test.diag;
 75       if (test.todo)
 76           parentRunner.logger.log(msg)
 77       else
 78           parentRunner.logger.error(msg);
 79   }
 80 }
 81 
 82 
 83 /**
 84  * Makes a test report, returns it as a DIV element.
 85 **/
 86 SimpleTest.report = function () {
 87     var DIV = MochiKit.DOM.DIV;
 88     var passed = 0;
 89     var failed = 0;
 90     var todo = 0;
 91     var results = MochiKit.Base.map(
 92         function (test) {
 93             var cls, msg;
 94             if (test.todo && !test.result) {
 95                 todo++;
 96                 cls = "test_todo"
 97                 msg = "todo - " + test.name;   
 98             } else if (test.result &&!test.todo) {
 99                 passed++;
100                 cls = "test_ok";
101                 msg = "ok - " + test.name;
102             } else {
103                 failed++;
104                 cls = "test_not_ok";
105                 msg = "not ok - " + test.name + " " + test.diag;
106             }
107             return DIV({"class": cls}, msg);
108         },
109         SimpleTest._tests
110     );
111     var summary_class = ((failed == 0) ? 'all_pass' : 'some_fail');
112     return DIV({'class': 'tests_report'},
113         DIV({'class': 'tests_summary ' + summary_class},
114             DIV({'class': 'tests_passed'}, "Passed: " + passed),
115             DIV({'class': 'tests_failed'}, "Failed: " + failed),
116             DIV({'class': 'tests_todo'}, "Todo: " + todo)),
117         results
118     );
119 };
120 
121 /**
122  * Toggle element visibility
123 **/
124 SimpleTest.toggle = function(el) {
125     if (MochiKit.Style.computedStyle(el, 'display') == 'block') {
126         el.style.display = 'none';
127     } else {
128         el.style.display = 'block';
129     }
130 };
131 
132 /**
133  * Toggle visibility for divs with a specific class.
134 **/
135 SimpleTest.toggleByClass = function (cls) {
136     var elems = getElementsByTagAndClassName('div', cls);
137     MochiKit.Base.map(SimpleTest.toggle, elems);
138 };
139 
140 /**
141  * Shows the report in the browser
142 **/
143 
144 SimpleTest.showReport = function() {
145     var togglePassed = A({'href': '#'}, "Toggle passed tests");
146     var toggleFailed = A({'href': '#'}, "Toggle failed tests");
147     togglePassed.onclick = partial(SimpleTest.toggleByClass, 'test_ok');
148     toggleFailed.onclick = partial(SimpleTest.toggleByClass, 'test_not_ok');
149     var body = document.getElementsByTagName("body")[0];
150     var firstChild = body.childNodes[0];
151     var addNode;
152     if (firstChild) {
153         addNode = function (el) {
154             body.insertBefore(el, firstChild);
155         };
156     } else {
157         addNode = function (el) {
158             body.appendChild(el)
159         };
160     }
161     addNode(togglePassed);
162     addNode(SPAN(null, " "));
163     addNode(toggleFailed);
164     addNode(SimpleTest.report());
165 };
166 
167 /**
168  * Tells SimpleTest to don't finish the test when the document is loaded,
169  * useful for asynchronous tests.
170  *
171  * When SimpleTest.waitForExplicitFinish is called,
172  * explicit SimpleTest.finish() is required.
173 **/
174 SimpleTest.waitForExplicitFinish = function () {
175     SimpleTest._stopOnLoad = false;
176 };
177 
178 /**
179  * Talks to the TestRunner if being ran on a iframe and the parent has a 
180  * TestRunner object.
181 **/
182 SimpleTest.talkToRunner = function () {
183     if (parentRunner) {
184         parentRunner.testFinished(document);
185     }
186 };
187 
188 /**
189  * Finishes the tests. This is automatically called, except when 
190  * SimpleTest.waitForExplicitFinish() has been invoked.
191 **/
192 SimpleTest.finish = function () {
193     SimpleTest.showReport();
194     SimpleTest.talkToRunner();
195 };
196 
197 
198 addLoadEvent(function() {
199     if (SimpleTest._stopOnLoad) {
200         SimpleTest.finish();
201     }
202 });
203 
204 //  --------------- Test.Builder/Test.More isDeeply() -----------------
205 
206 
207 SimpleTest.DNE = {dne: 'Does not exist'};
208 SimpleTest.LF = "\r\n";
209 SimpleTest._isRef = function (object) {
210     var type = typeof(object);
211     return type == 'object' || type == 'function';
212 };
213 
214 
215 SimpleTest._deepCheck = function (e1, e2, stack, seen) {
216     var ok = false;
217     // Either they're both references or both not.
218     var sameRef = !(!SimpleTest._isRef(e1) ^ !SimpleTest._isRef(e2));
219     if (e1 == null && e2 == null) {
220         ok = true;
221     } else if (e1 != null ^ e2 != null) {
222         ok = false;
223     } else if (e1 == SimpleTest.DNE ^ e2 == SimpleTest.DNE) {
224         ok = false;
225     } else if (sameRef && e1 == e2) {
226         // Handles primitives and any variables that reference the same
227         // object, including functions.
228         ok = true;
229     } else if (SimpleTest.isa(e1, 'Array') && SimpleTest.isa(e2, 'Array')) {
230         ok = SimpleTest._eqArray(e1, e2, stack, seen);
231     } else if (typeof e1 == "object" && typeof e2 == "object") {
232         ok = SimpleTest._eqAssoc(e1, e2, stack, seen);
233     } else {
234         // If we get here, they're not the same (function references must
235         // always simply rererence the same function).
236         stack.push({ vals: [e1, e2] });
237         ok = false;
238     }
239     return ok;
240 };
241 
242 SimpleTest._eqArray = function (a1, a2, stack, seen) {
243     // Return if they're the same object.
244     if (a1 == a2) return true;
245 
246     // JavaScript objects have no unique identifiers, so we have to store
247     // references to them all in an array, and then compare the references
248     // directly. It's slow, but probably won't be much of an issue in
249     // practice. Start by making a local copy of the array to as to avoid
250     // confusing a reference seen more than once (such as [a, a]) for a
251     // circular reference.
252     for (var j = 0; j < seen.length; j++) {
253         if (seen[j][0] == a1) {
254             return seen[j][1] == a2;
255         }
256     }
257 
258     // If we get here, we haven't seen a1 before, so store it with reference
259     // to a2.
260     seen.push([ a1, a2 ]);
261 
262     var ok = true;
263     // Only examines enumerable attributes. Only works for numeric arrays!
264     // Associative arrays return 0. So call _eqAssoc() for them, instead.
265     var max = a1.length > a2.length ? a1.length : a2.length;
266     if (max == 0) return SimpleTest._eqAssoc(a1, a2, stack, seen);
267     for (var i = 0; i < max; i++) {
268         var e1 = i > a1.length - 1 ? SimpleTest.DNE : a1[i];
269         var e2 = i > a2.length - 1 ? SimpleTest.DNE : a2[i];
270         stack.push({ type: 'Array', idx: i, vals: [e1, e2] });
271         if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) {
272             stack.pop();
273         } else {
274             break;
275         }
276     }
277     return ok;
278 };
279 
280 SimpleTest._eqAssoc = function (o1, o2, stack, seen) {
281     // Return if they're the same object.
282     if (o1 == o2) return true;
283 
284     // JavaScript objects have no unique identifiers, so we have to store
285     // references to them all in an array, and then compare the references
286     // directly. It's slow, but probably won't be much of an issue in
287     // practice. Start by making a local copy of the array to as to avoid
288     // confusing a reference seen more than once (such as [a, a]) for a
289     // circular reference.
290     seen = seen.slice(0);
291     for (var j = 0; j < seen.length; j++) {
292         if (seen[j][0] == o1) {
293             return seen[j][1] == o2;
294         }
295     }
296 
297     // If we get here, we haven't seen o1 before, so store it with reference
298     // to o2.
299     seen.push([ o1, o2 ]);
300 
301     // They should be of the same class.
302 
303     var ok = true;
304     // Only examines enumerable attributes.
305     var o1Size = 0; for (var i in o1) o1Size++;
306     var o2Size = 0; for (var i in o2) o2Size++;
307     var bigger = o1Size > o2Size ? o1 : o2;
308     for (var i in bigger) {
309         var e1 = o1[i] == undefined ? SimpleTest.DNE : o1[i];
310         var e2 = o2[i] == undefined ? SimpleTest.DNE : o2[i];
311         stack.push({ type: 'Object', idx: i, vals: [e1, e2] });
312         if (ok = SimpleTest._deepCheck(e1, e2, stack, seen)) {
313             stack.pop();
314         } else {
315             break;
316         }
317     }
318     return ok;
319 };
320 
321 SimpleTest._formatStack = function (stack) {
322     var variable = '$Foo';
323     for (var i = 0; i < stack.length; i++) {
324         var entry = stack[i];
325         var type = entry['type'];
326         var idx = entry['idx'];
327         if (idx != null) {
328             if (/^\d+$/.test(idx)) {
329                 // Numeric array index.
330                 variable += '[' + idx + ']';
331             } else {
332                 // Associative array index.
333                 idx = idx.replace("'", "\\'");
334                 variable += "['" + idx + "']";
335             }
336         }
337     }
338 
339     var vals = stack[stack.length-1]['vals'].slice(0, 2);
340     var vars = [
341         variable.replace('$Foo',     'got'),
342         variable.replace('$Foo',     'expected')
343     ];
344 
345     var out = "Structures begin differing at:" + SimpleTest.LF;
346     for (var i = 0; i < vals.length; i++) {
347         var val = vals[i];
348         if (val == null) {
349             val = 'undefined';
350         } else {
351              val == SimpleTest.DNE ? "Does not exist" : "'" + val + "'";
352         }
353     }
354 
355     out += vars[0] + ' = ' + vals[0] + SimpleTest.LF;
356     out += vars[1] + ' = ' + vals[1] + SimpleTest.LF;
357     
358     return '    ' + out;
359 };
360 
361 
362 SimpleTest.isDeeply = function (it, as, name) {
363     var ok;
364     // ^ is the XOR operator.
365     if (SimpleTest._isRef(it) ^ SimpleTest._isRef(as)) {
366         // One's a reference, one isn't.
367         ok = false;
368     } else if (!SimpleTest._isRef(it) && !SimpleTest._isRef(as)) {
369         // Neither is an object.
370         ok = SimpleTest.is(it, as, name);
371     } else {
372         // We have two objects. Do a deep comparison.
373         var stack = [], seen = [];
374         if ( SimpleTest._deepCheck(it, as, stack, seen)) {
375             ok = SimpleTest.ok(true, name);
376         } else {
377             ok = SimpleTest.ok(false, name, SimpleTest._formatStack(stack));
378         }
379     }
380     return ok;
381 };
382 
383 SimpleTest.typeOf = function (object) {
384     var c = Object.prototype.toString.apply(object);
385     var name = c.substring(8, c.length - 1);
386     if (name != 'Object') return name;
387     // It may be a non-core class. Try to extract the class name from
388     // the constructor function. This may not work in all implementations.
389     if (/function ([^(\s]+)/.test(Function.toString.call(object.constructor))) {
390         return RegExp.$1;
391     }
392     // No idea. :-(
393     return name;
394 };
395 
396 SimpleTest.isa = function (object, clas) {
397     return SimpleTest.typeOf(object) == clas;
398 };
399 
400 // Global symbols:
401 var ok = SimpleTest.ok;
402 var is = SimpleTest.is;
403 var isnot = SimpleTest.isnot;
404 var todo = SimpleTest.todo;
405 var isDeeply = SimpleTest.isDeeply;
406 var oldOnError = window.onerror;
407 window.onerror = function (ev) {
408     is(0, 1, "Error thrown during test: " + ev);
409     if (oldOnError) {
410         try {
411           oldOnError(ev);
412         } catch (e) {
413         }
414     }
415     if (SimpleTest._stopOnLoad == false) {
416       // Need to finish() manually here
417       SimpleTest.finish();
418     }
419 }

This page was automatically generated by LXR.