blob: 1da4812577c08702ee3bfd02092cf612ebcc900c [file] [log] [blame]
genia.likes.science@gmail.comc5f82a22013-05-08 02:44:35 -07001var easyTest = (function(){
2 var _messages;
3 var _start;
4 var MessageType = {
5 Error: 1,
6 Success: 2,
7 Info: 3
8 };
9
10 /**
11 * Logs the message to the body
12 * @param {String} msg The message to displey
13 * @param {MessageType} type The messagetype
14 */
15 function _log(msg, type){
16 var el = _messages.appendChild(document.createElement("div"));
17 el.innerHTML = msg;
18 _messages.scrollTop = _messages.scrollHeight;
19 switch (type) {
20 case MessageType.Error:
21 el.className = "easyTest_error";
22 break;
23 case MessageType.Success:
24 el.className = "easyTest_success";
25 break;
26 default:
27 el.className = "easyTest_msg";
28 break;
29 }
30 }
31
32 var Assert = {
33 // Type checks
34 isTypeOf: function(type, obj){
35 return typeof obj === type;
36 },
37 isInstanceOf: function(type, obj){
38 return obj instanceof type;
39 },
40 isString: function(obj){
41 return this.isTypeOf("string", obj);
42 },
43 isNumber: function(obj){
44 return this.isTypeOf("number", obj);
45 },
46 isObject: function(obj){
47 return this.isTypeOf("object", obj);
48 },
49 isBoolean: function(obj){
50 return this.isTypeOf("boolean", obj);
51 },
52 isFunction: function(obj){
53 return this.isTypeOf("function", obj);
54 },
55 // Equality
56 areEqual: function(a, b){
57 return a == b;
58 },
59 areNotEqual: function(a, b){
60 return a != b;
61 },
62 // Identical
63 areSame: function(a, b){
64 return a === b;
65 },
66 areNotSame: function(a, b){
67 return a !== b;
68 }
69
70 };
71
72 function Test(test, fn){
73 var _scope, _steps = test.steps, _step, _stepIndex = 0;
74 var _timer, _runStep, _startedAt, _stepStartedAt;
75
76 /**
77 * Clean up and tear down the test.<br/>
78 * Calls back to notify that the test is complete
79 */
80 function _endTest(){
81 // Tear down the test
82 if (test.tearDown) {
83 try {
84 test.tearDown.call(_scope);
85 }
86 catch (ex) {
87 _log("Teardown '" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'", MessageType.Error);
88 }
89 }
90 for (var key in _scope) {
91 if (_scope.hasOwnProperty(key)) {
92 delete _scope[key];
93 }
94 }
95
96 // Call back
97 fn();
98 }
99
100 /**
101 * Used to notify the framework of the result of the test
102 * @param {String} name The name of the test
103 * @param {Boolean} result The result of the test
104 * @param {String} reason An optional reason why the test returned the result
105 */
106 function _notifyResult(name, result, reason){
107 var now = new Date().getTime();
108 var testsetTime = now - _start.getTime();
109 var testTime = now - _startedAt.getTime();
110 var stepTime = now - _stepStartedAt.getTime();
111
112 var times = testsetTime + "ms, " + testTime + "ms, " + stepTime + "ms - ";
113 if (result) {
114 _log(times + name + " succeeded! " + (reason || ""), MessageType.Success);
115 }
116 else {
117 _log(times + name + " failed! " + (reason || ""), MessageType.Error);
118 if (test.failedMessage) {
119 _log(test.failedMessage, MessageType.Info);
120 }
121 }
122 // Go to next step
123 if (result) {
124 _stepIndex++;
125 window.setTimeout(function(){
126 _runStep();
127 }, 0);
128 }
129 else {
130 _endTest();
131 }
132 }
133
134
135 /**
136 * Runs through the test step
137 */
138 _runStep = function(){
139 if (_stepIndex < _steps.length) {
140 // We still have steps to run
141 _step = _steps[_stepIndex];
142 _stepStartedAt = new Date();
143 if (_step.timeout) {
144 // This an asynchronous test
145 _timer = window.setTimeout(function(){
146 _notifyResult(_step.name, false, "Failed due to timeout.");
147 }, _step.timeout);
148 try {
149 _step.run.call(_scope);
150 }
151 catch (ex) {
152 //If it fails we cancel the timeout
153 window.clearTimeout(_timer);
154 _notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
155 }
156 }
157 else {
158 // This is a synchronous test
159 try {
160 var result = _step.run.call(_scope);
161 _notifyResult(_step.name, result);
162 }
163 catch (ex) {
164 _notifyResult(_step.name, false, "'" + ex.message + "(" + ex.fileName + ", " + ex.lineNumber + ")" + "'");
165 }
166 }
167 }
168 else {
169 _endTest();
170 }
171 };
172
173 return {
174 /**
175 * Runs the test.
176 * Will first try to execute the setup method before continuing the steps
177 */
178 run: function(){
179 var excuse;
180 if (test.runIf) {
181 excuse = test.runIf();
182 }
183 if (excuse) {
184 _log("Skipping test ' " + test.name + "'. " + excuse);
185 fn();
186 }
187 else {
188 _log("Running test '" + test.name + "'");
189 _scope = {
190 Assert: Assert,
191 log: _log,
192 notifyResult: function(result){
193 window.clearTimeout(_timer);
194 _notifyResult(_step.name, result);
195 }
196 };
197 if (test.setUp) {
198 // Setup the test
199 try {
200 test.setUp.call(_scope);
201 _log("Setup succeeded", MessageType.Success);
202 }
203 catch (ex) {
204 _log("Setup failed", MessageType.Error);
205 }
206 }
207 _startedAt = new Date();
208 _runStep();
209 }
210 }
211 };
212 }
213
214 return {
215 /**
216 * Runs through all the tests
217 * @param {Array} tests The tests to run
218 */
219 test: function(testset){
220 var tests = [], testConfig, i = testset.length, test;
221
222 // Prepare the messaging facilities
223 if (!_messages) {
224 _messages = document.createElement("div");
225 _messages.className = "easyTest_messages";
226 (document.getElementById("messages") || document.body).appendChild(_messages);
227 }
228 else {
229 _messages.innerHTML = "";
230 }
231
232 // Convert the testset
233 while (i--) {
234 testConfig = testset[i];
235 if (!testConfig.steps) {
236 // Convert a single step test to a proper test
237 testConfig = {
238 steps: testConfig
239 };
240 }
241
242 tests.push(new Test(testConfig, function(){
243 // Get the next test to run
244 test = tests.pop();
245 if (test) {
246 // This is needed to avoid a strange bug in Opera,
247 window.setTimeout(function(){
248 test.run();
249 }, 0);
250 }
251 else {
252 // No more tests to run
253 _log("Test run complete", MessageType.Info);
254 }
255 }));
256 }
257 // Start the first test
258 _start = new Date();
259 tests.pop().run();
260 }
261 };
262}());