1 |
/* |
2 |
* File: unit_test.js |
3 |
* Version: 0.0.1 |
4 |
* CVS: $Id$ |
5 |
* Description: Unit test framework |
6 |
* Author: Allan Jardine (www.sprymedia.co.uk) |
7 |
* Created: Sun Mar 8 22:02:49 GMT 2009 |
8 |
* Modified: $Date$ by $Author$ |
9 |
* Language: Javascript |
10 |
* License: GPL v2 or BSD 3 point style |
11 |
* Project: DataTables |
12 |
* Contact: allan.jardine@sprymedia.co.uk |
13 |
* |
14 |
* Copyright 2009 Allan Jardine, all rights reserved. |
15 |
* |
16 |
* Description: |
17 |
* This is a javascript library suitable for use as a unit testing framework. Employing a queuing |
18 |
* mechanisim to take account of async events in javascript, this library will communicates with |
19 |
* a controller frame (to report individual test status). |
20 |
* |
21 |
*/ |
22 |
|
23 |
|
24 |
var oTest = { |
25 |
/* Block further tests from occuring - might be end of tests or due to async wait */ |
26 |
bBlock: false, |
27 |
|
28 |
/* Number of times to try retesting for a blocking test */ |
29 |
iReTestLimit: 20, |
30 |
|
31 |
/* Amount of time to wait between trying for an async test */ |
32 |
iReTestDelay: 150, |
33 |
|
34 |
/* End tests - external control */ |
35 |
bEnd: false, |
36 |
|
37 |
/* Internal variables */ |
38 |
_aoQueue: [], |
39 |
_iReTest: 0, |
40 |
_bFinished: false, |
41 |
|
42 |
|
43 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
44 |
* Recommened public functions |
45 |
*/ |
46 |
|
47 |
/* |
48 |
* Function: fnTest |
49 |
* Purpose: Add a test to the queue |
50 |
* Returns: - |
51 |
* Inputs: string:sMessage - name of the test |
52 |
* function:fnTest - function which will be evaludated to get the test result |
53 |
*/ |
54 |
"fnTest": function ( sMessage, fnSetup, fnTest ) |
55 |
{ |
56 |
this._aoQueue.push( { |
57 |
"sMessage": sMessage, |
58 |
"fnSetup": fnSetup, |
59 |
"fnTest": fnTest, |
60 |
"bPoll": false |
61 |
} ); |
62 |
this._fnNext(); |
63 |
}, |
64 |
|
65 |
/* |
66 |
* Function: fnWaitTest |
67 |
* Purpose: Add a test to the queue which has a re-test cycle |
68 |
* Returns: - |
69 |
* Inputs: string:sMessage - name of the test |
70 |
* function:fnTest - function which will be evaludated to get the test result |
71 |
*/ |
72 |
"fnWaitTest": function ( sMessage, fnSetup, fnTest ) |
73 |
{ |
74 |
this._aoQueue.push( { |
75 |
"sMessage": sMessage, |
76 |
"fnSetup": fnSetup, |
77 |
"fnTest": fnTest, |
78 |
"bPoll": true |
79 |
} ); |
80 |
this._fnNext(); |
81 |
}, |
82 |
|
83 |
/* |
84 |
* Function: fnStart |
85 |
* Purpose: Indicate that this is a new unit and what it is testing (message to end user) |
86 |
* Returns: - |
87 |
* Inputs: string:sMessage - message to give to the user about this unit |
88 |
*/ |
89 |
"fnStart": function ( sMessage ) |
90 |
{ |
91 |
window.parent.controller.fnStartMessage( sMessage ); |
92 |
}, |
93 |
|
94 |
/* |
95 |
* Function: fnComplete |
96 |
* Purpose: Tell the controller that we are all done here |
97 |
* Returns: - |
98 |
* Inputs: - |
99 |
*/ |
100 |
"fnComplete": function () |
101 |
{ |
102 |
this._bFinished = true; |
103 |
this._fnNext(); |
104 |
}, |
105 |
|
106 |
/* |
107 |
* Function: fnCookieDestroy |
108 |
* Purpose: Destroy a cookie of a given name |
109 |
* Returns: - |
110 |
* Inputs: - |
111 |
*/ |
112 |
"fnCookieDestroy": function ( oTable ) |
113 |
{ |
114 |
var sName = oTable.fnSettings().sCookiePrefix+oTable.fnSettings().sInstance; |
115 |
var aParts = window.location.pathname.split('/'); |
116 |
var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase(); |
117 |
document.cookie = sNameFile+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+ |
118 |
aParts.join('/') + "/"; |
119 |
}, |
120 |
|
121 |
|
122 |
|
123 |
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * |
124 |
* Internal functions |
125 |
*/ |
126 |
|
127 |
|
128 |
"_fnReTest": function ( oTestInfo ) |
129 |
{ |
130 |
var bResult = oTestInfo.fnTest( ); |
131 |
if ( bResult ) |
132 |
{ |
133 |
/* Test passed on retry */ |
134 |
this._fnResult( true ); |
135 |
this._fnNext(); |
136 |
} |
137 |
else |
138 |
{ |
139 |
if ( this._iReTest < this.iReTestLimit ) |
140 |
{ |
141 |
this._iReTest++; |
142 |
setTimeout( function() { |
143 |
oTest._fnReTest( oTestInfo ); |
144 |
}, this.iReTestDelay ); |
145 |
} |
146 |
else |
147 |
{ |
148 |
this._fnResult( false ); |
149 |
} |
150 |
} |
151 |
}, |
152 |
|
153 |
"_fnNext": function () |
154 |
{ |
155 |
if ( this.bEnd ) |
156 |
{ |
157 |
return; |
158 |
} |
159 |
|
160 |
if ( !this.bBlock && this._aoQueue.length > 0 ) |
161 |
{ |
162 |
var oNextTest = this._aoQueue.shift(); |
163 |
window.parent.controller.fnTestStart( oNextTest.sMessage ); |
164 |
this.bBlock = true; |
165 |
|
166 |
if ( typeof oNextTest.fnSetup == 'function' ) |
167 |
{ |
168 |
oNextTest.fnSetup( ); |
169 |
} |
170 |
var bResult = oNextTest.fnTest( ); |
171 |
//bResult = false; |
172 |
|
173 |
if ( oNextTest.bPoll ) |
174 |
{ |
175 |
if ( bResult ) |
176 |
{ |
177 |
this._fnResult( true ); |
178 |
this._fnNext(); |
179 |
} |
180 |
else |
181 |
{ |
182 |
_iReTest = 0; |
183 |
setTimeout( function() { |
184 |
oTest._fnReTest( oNextTest ); |
185 |
}, this.iReTestDelay ); |
186 |
} |
187 |
} |
188 |
else |
189 |
{ |
190 |
this._fnResult( bResult ); |
191 |
this._fnNext(); |
192 |
} |
193 |
} |
194 |
else if ( !this.bBlock && this._aoQueue.length == 0 && this._bFinished ) |
195 |
{ |
196 |
window.parent.controller.fnUnitComplete( ); |
197 |
} |
198 |
}, |
199 |
|
200 |
"_fnResult": function ( b ) |
201 |
{ |
202 |
window.parent.controller.fnTestResult( b ); |
203 |
this.bBlock = false; |
204 |
if ( !b ) |
205 |
{ |
206 |
this.bEnd = true; |
207 |
} |
208 |
} |
209 |
}; |
210 |
|
211 |
|
212 |
var oDispacher = { |
213 |
"click": function ( nNode, oSpecial ) |
214 |
{ |
215 |
var evt = this.fnCreateEvent( 'click', nNode, oSpecial ); |
216 |
if ( nNode.dispatchEvent ) |
217 |
nNode.dispatchEvent(evt); |
218 |
else |
219 |
nNode.fireEvent('onclick', evt); |
220 |
}, |
221 |
|
222 |
"change": function ( nNode ) |
223 |
{ |
224 |
var evt = this.fnCreateEvent( 'change', nNode ); |
225 |
if ( nNode.dispatchEvent ) |
226 |
nNode.dispatchEvent(evt); |
227 |
else |
228 |
nNode.fireEvent('onchange', evt); |
229 |
}, |
230 |
|
231 |
|
232 |
/* |
233 |
* Function: fnCreateEvent |
234 |
* Purpose: Create an event oject based on the type to trigger an event - x-platform |
235 |
* Returns: event:evt |
236 |
* Inputs: string:sType - type of event |
237 |
* node:nTarget - target node of the event |
238 |
*/ |
239 |
fnCreateEvent: function( sType, nTarget, oSpecial ) |
240 |
{ |
241 |
var evt = null; |
242 |
var oTargetPos = this._fnGetPos( nTarget ); |
243 |
var sTypeGroup = this._fnEventTypeGroup( sType ); |
244 |
if ( typeof oSpecial == 'undefined' ) |
245 |
{ |
246 |
oSpecial = {}; |
247 |
} |
248 |
|
249 |
var ctrlKey = false; |
250 |
var altKey = false; |
251 |
var shiftKey = (typeof oSpecial.shift != 'undefined') ? oSpecial.shift : false; |
252 |
var metaKey = false; |
253 |
var button = false; |
254 |
|
255 |
if ( document.createEvent ) |
256 |
{ |
257 |
switch ( sTypeGroup ) |
258 |
{ |
259 |
case 'mouse': |
260 |
evt = document.createEvent( "MouseEvents" ); |
261 |
evt.initMouseEvent( sType, true, true, window, 0, oTargetPos[0], oTargetPos[1], |
262 |
oTargetPos[0], oTargetPos[1], ctrlKey, altKey, shiftKey, |
263 |
metaKey, button, null ); |
264 |
break; |
265 |
|
266 |
case 'html': |
267 |
evt = document.createEvent( "HTMLEvents" ); |
268 |
evt.initEvent( sType, true, true ); |
269 |
break; |
270 |
|
271 |
case 'ui': |
272 |
evt = document.createEvent( "UIEvents" ); |
273 |
evt.initUIEvent( sType, true, true, window, 0 ); |
274 |
break; |
275 |
|
276 |
default: |
277 |
break; |
278 |
} |
279 |
} |
280 |
else if ( document.createEventObject ) |
281 |
{ |
282 |
switch ( sTypeGroup ) |
283 |
{ |
284 |
case 'mouse': |
285 |
evt = document.createEventObject(); |
286 |
evt.screenX = oTargetPos[0]; |
287 |
evt.screenX = oTargetPos[1]; |
288 |
evt.clientX = oTargetPos[0]; |
289 |
evt.clientY = oTargetPos[1]; |
290 |
evt.ctrlKey = ctrlKey; |
291 |
evt.altKey = altKey; |
292 |
evt.shiftKey = shiftKey; |
293 |
evt.metaKey = metaKey; |
294 |
evt.button = button; |
295 |
evt.relatedTarget = null; |
296 |
break; |
297 |
|
298 |
case 'html': |
299 |
/* fall through to basic event object */ |
300 |
|
301 |
case 'ui': |
302 |
evt = document.createEventObject(); |
303 |
break; |
304 |
|
305 |
default: |
306 |
break; |
307 |
} |
308 |
} |
309 |
|
310 |
return evt; |
311 |
}, |
312 |
|
313 |
/* |
314 |
* Function: DesignCore.fnGetPos |
315 |
* Purpose: Get the position of an element on the page |
316 |
* Returns: array[ 0-int:left, 1-int:top ] |
317 |
* Inputs: node:obj - node to analyse |
318 |
*/ |
319 |
_fnGetPos: function ( obj ) |
320 |
{ |
321 |
var curleft = 0; |
322 |
var curtop = 0; |
323 |
|
324 |
if (obj.offsetParent) |
325 |
{ |
326 |
curleft = obj.offsetLeft; |
327 |
curtop = obj.offsetTop; |
328 |
while (obj = obj.offsetParent ) |
329 |
{ |
330 |
curleft += obj.offsetLeft; |
331 |
curtop += obj.offsetTop; |
332 |
} |
333 |
} |
334 |
return [curleft,curtop]; |
335 |
}, |
336 |
|
337 |
|
338 |
/* |
339 |
* Function: _fnEventTypeGroup |
340 |
* Purpose: Group the event types as per w3c groupings |
341 |
* Returns: - |
342 |
* Inputs: string:sType |
343 |
*/ |
344 |
_fnEventTypeGroup: function ( sType ) |
345 |
{ |
346 |
switch ( sType ) |
347 |
{ |
348 |
case 'click': |
349 |
case 'dblclick': |
350 |
case 'mousedown': |
351 |
case 'mousemove': |
352 |
case 'mouseout': |
353 |
case 'mouseover': |
354 |
case 'mouseup': |
355 |
return 'mouse'; |
356 |
|
357 |
case 'change': |
358 |
case 'focus': |
359 |
case 'blur': |
360 |
case 'select': |
361 |
case 'submit': |
362 |
return 'html'; |
363 |
|
364 |
case 'keydown': |
365 |
case 'keypress': |
366 |
case 'keyup': |
367 |
case 'load': |
368 |
case 'unload': |
369 |
return 'ui'; |
370 |
|
371 |
default: |
372 |
return 'custom'; |
373 |
} |
374 |
} |
375 |
} |
376 |
|
377 |
|
378 |
var oSession = { |
379 |
nTable: null, |
380 |
|
381 |
fnCache: function () |
382 |
{ |
383 |
this.nTable = document.getElementById('demo').cloneNode(true); |
384 |
}, |
385 |
|
386 |
fnRestore: function () |
387 |
{ |
388 |
while( $.fn.dataTableSettings.length > 0 ) |
389 |
{ |
390 |
try { |
391 |
$.fn.dataTableSettings[0].oInstance.fnDestroy(); |
392 |
} catch (e) { |
393 |
$.fn.dataTableSettings.splice( 0, 1 ); |
394 |
} |
395 |
} |
396 |
//$.fn.dataTableSettings.splice( 0, $.fn.dataTableSettings.length ); |
397 |
var nDemo = document.getElementById('demo'); |
398 |
nDemo.innerHTML = ""; |
399 |
for ( var i=0, iLen=this.nTable.childNodes.length ; i<iLen ; i++ ) |
400 |
{ |
401 |
nDemo.appendChild( this.nTable.childNodes[0] ); |
402 |
} |
403 |
this.fnCache(); |
404 |
} |
405 |
} |
406 |
|
407 |
$(document).ready( function () { |
408 |
oSession.fnCache(); |
409 |
} ); |