unit_test.js 8.7 KB

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