0
|
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 } );
|