Diffstat (limited to 'frontend/gamma/tests/SimpleTest/SimpleTest.js') (more/less context) (ignore whitespace changes)
-rw-r--r-- | frontend/gamma/tests/SimpleTest/SimpleTest.js | 424 |
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 | |||
13 | if (typeof(SimpleTest) == "undefined") { | ||
14 | var SimpleTest = {}; | ||
15 | } | ||
16 | |||
17 | // Check to see if the TestRunner is present and has logging | ||
18 | if (typeof(parent) != "undefined" && parent.TestRunner) { | ||
19 | SimpleTest._logEnabled = parent.TestRunner.logEnabled; | ||
20 | } | ||
21 | |||
22 | SimpleTest._tests = []; | ||
23 | SimpleTest._stopOnLoad = true; | ||
24 | SimpleTest._scopeCopy = {}; | ||
25 | |||
26 | /** | ||
27 | * Saves a copy of the specified scope variables. | ||
28 | */ | ||
29 | SimpleTest.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 | */ | ||
40 | SimpleTest.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 | */ | ||
65 | SimpleTest.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 | */ | ||
83 | SimpleTest.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 | */ | ||
91 | SimpleTest.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 | **/ | ||
105 | SimpleTest.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 | **/ | ||
140 | SimpleTest.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 | **/ | ||
151 | SimpleTest.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 | |||
161 | SimpleTest.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 | **/ | ||
193 | SimpleTest.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 | **/ | ||
201 | SimpleTest.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 | **/ | ||
211 | SimpleTest.finish = function () { | ||
212 | SimpleTest.showReport(); | ||
213 | SimpleTest.talkToRunner(); | ||
214 | }; | ||
215 | |||
216 | |||
217 | MochiKit.DOM.addLoadEvent(function() { | ||
218 | if (SimpleTest._stopOnLoad) { | ||
219 | SimpleTest.finish(); | ||
220 | } | ||
221 | }); | ||
222 | |||
223 | // --------------- Test.Builder/Test.More isDeeply() ----------------- | ||
224 | |||
225 | |||
226 | SimpleTest.DNE = {dne: 'Does not exist'}; | ||
227 | SimpleTest.LF = "\r\n"; | ||
228 | SimpleTest._isRef = function (object) { | ||
229 | var type = typeof(object); | ||
230 | return type == 'object' || type == 'function'; | ||
231 | }; | ||
232 | |||
233 | |||
234 | SimpleTest._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 | |||
261 | SimpleTest._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 | |||
299 | SimpleTest._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 | |||
340 | SimpleTest._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 | |||
381 | SimpleTest.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 | |||
402 | SimpleTest.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 | |||
415 | SimpleTest.isa = function (object, clas) { | ||
416 | return SimpleTest.typeOf(object) == clas; | ||
417 | }; | ||
418 | |||
419 | |||
420 | |||
421 | // Global symbols: | ||
422 | var ok = SimpleTest.ok; | ||
423 | var is = SimpleTest.is; | ||
424 | var isDeeply = SimpleTest.isDeeply; | ||