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