Added visual pass/fail indicator (JUnit/Eclipse style)
[jquery.git] / build / test / data / testrunner.js
1 var asyncTimeout = 2 // seconds for async timeout
2
3 var fixture;
4 var Test;
5 var stats = {
6         all: 0,
7         bad: 0
8 };
9 var queue = [];
10 var blocking = false;
11 var timeout;
12
13 function synchronize(callback) {
14         queue[queue.length] = callback;
15         if(!blocking) {
16                 process();
17         }
18 }
19
20 function process() {
21         while(queue.length && !blocking) {
22                 var call = queue[0];
23                 queue = queue.slice(1);
24                 call();
25         }
26 }
27
28 function stop() {
29         blocking = true;
30         timeout = setTimeout(start, asyncTimeout * 1000);
31 }
32 function start() {
33         if(timeout)
34                 clearTimeout(timeout);
35         blocking = false;
36         process();
37 }
38
39 function runTest(tests) {
40         var startTime = new Date();
41         fixture = document.getElementById('main').innerHTML;
42         tests();
43         synchronize(function() {
44                 var runTime = new Date() - startTime;
45                 var result = document.createElement("div");
46                 result.innerHTML = ['<p class="result">Tests completed in ',
47                         runTime, ' milliseconds.<br/>',
48                         stats.bad, ' tests of ', stats.all, ' failed.</p>'].join('');
49                 document.getElementsByTagName("body")[0].appendChild(result);
50                 $("<div id='banner'>").addClass(stats.bad ? "fail" : "pass").insertAfter("h1");
51         });
52 }
53
54 function test(name, callback) {
55         synchronize(function() {
56                 Test = [];
57                 try {
58                         callback();
59                 } catch(e) {
60                         if( typeof console != "undefined" && console.error && console.warn ) {
61                                 console.error("Test " + name + " died, exception and test follows");
62                                 console.error(e);
63                                 console.warn(callback.toString());
64                         }
65                         Test.push( [ false, "Died on test #" + (Test.length+1) + ": " + e ] );
66                 }
67         });
68         synchronize(function() {
69                 reset();
70                 
71                 var good = 0, bad = 0;
72                 var ol = document.createElement("ol");
73                 ol.style.display = "none";
74                 var li = "", state = "pass";
75                 for ( var i = 0; i < Test.length; i++ ) {
76                         var li = document.createElement("li");
77                         li.className = Test[i][0] ? "pass" : "fail";
78                         li.innerHTML = Test[i][1];
79                         ol.appendChild( li );
80                         
81                         stats.all++;
82                         if ( !Test[i][0] ) {
83                                 state = "fail";
84                                 bad++;
85                                 stats.bad++;
86                         } else good++;
87                 }
88         
89                 var li = document.createElement("li");
90                 li.className = state;
91         
92                 var b = document.createElement("b");
93                 b.innerHTML = name + " <b style='color:black;'>(<b class='fail'>" + bad + "</b>, <b class='pass'>" + good + "</b>, " + Test.length + ")</b>";
94                 b.onclick = function(){
95                         var n = this.nextSibling;
96                         if ( jQuery.css( n, "display" ) == "none" )
97                                 n.style.display = "block";
98                         else
99                                 n.style.display = "none";
100                 };
101                 li.appendChild( b );
102                 li.appendChild( ol );
103         
104                 document.getElementById("tests").appendChild( li );             
105         });
106 }
107
108 /**
109  * Resets the test setup. Useful for tests that modify the DOM.
110  */
111 function reset() {
112         document.getElementById('main').innerHTML = fixture;
113 }
114
115 /**
116  * Asserts true.
117  * @example ok( $("a").size() > 5, "There must be at least 5 anchors" );
118  */
119 function ok(a, msg) {
120         Test.push( [ !!a, msg ] );
121 }
122
123 /**
124  * Asserts that two arrays are the same
125  */
126 function isSet(a, b, msg) {
127         var ret = true;
128         if ( a && b && a.length == b.length ) {
129                 for ( var i in a )
130                         if ( a[i] != b[i] )
131                                 ret = false;
132         } else
133                 ret = false;
134         if ( !ret )
135                 Test.push( [ ret, msg + " expected: " + b + " result: " + a ] );
136         else 
137                 Test.push( [ ret, msg ] );
138 }
139
140 /**
141  * Returns an array of elements with the given IDs, eg.
142  * @example q("main", "foo", "bar")
143  * @result [<div id="main">, <span id="foo">, <input id="bar">]
144  */
145 function q() {
146         var r = [];
147         for ( var i = 0; i < arguments.length; i++ )
148                 r.push( document.getElementById( arguments[i] ) );
149         return r;
150 }
151
152 /**
153  * Asserts that a select matches the given IDs
154  * @example t("Check for something", "//[a]", ["foo", "baar"]);
155  * @result returns true if "//[a]" return two elements with the IDs 'foo' and 'baar'
156  */
157 function t(a,b,c) {
158         var f = jQuery.find(b);
159         var s = "";
160         for ( var i = 0; i < f.length; i++ )
161                 s += (s && ",") + '"' + f[i].id + '"';
162         isSet(f, q.apply(q,c), a + " (" + b + ")");
163 }