/* * File: unit_test.js * Version: 0.0.1 * CVS: $Id$ * Description: Unit test framework * Author: Allan Jardine (www.sprymedia.co.uk) * Created: Sun Mar 8 22:02:49 GMT 2009 * Modified: $Date$ by $Author$ * Language: Javascript * License: GPL v2 or BSD 3 point style * Project: DataTables * Contact: allan.jardine@sprymedia.co.uk * * Copyright 2009 Allan Jardine, all rights reserved. * * Description: * This is a javascript library suitable for use as a unit testing framework. Employing a queuing * mechanisim to take account of async events in javascript, this library will communicates with * a controller frame (to report individual test status). * */ var oTest = { /* Block further tests from occuring - might be end of tests or due to async wait */ bBlock: false, /* Number of times to try retesting for a blocking test */ iReTestLimit: 20, /* Amount of time to wait between trying for an async test */ iReTestDelay: 150, /* End tests - external control */ bEnd: false, /* Internal variables */ _aoQueue: [], _iReTest: 0, _bFinished: false, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Recommened public functions */ /* * Function: fnTest * Purpose: Add a test to the queue * Returns: - * Inputs: string:sMessage - name of the test * function:fnTest - function which will be evaludated to get the test result */ "fnTest": function ( sMessage, fnSetup, fnTest ) { this._aoQueue.push( { "sMessage": sMessage, "fnSetup": fnSetup, "fnTest": fnTest, "bPoll": false } ); this._fnNext(); }, /* * Function: fnWaitTest * Purpose: Add a test to the queue which has a re-test cycle * Returns: - * Inputs: string:sMessage - name of the test * function:fnTest - function which will be evaludated to get the test result */ "fnWaitTest": function ( sMessage, fnSetup, fnTest ) { this._aoQueue.push( { "sMessage": sMessage, "fnSetup": fnSetup, "fnTest": fnTest, "bPoll": true } ); this._fnNext(); }, /* * Function: fnStart * Purpose: Indicate that this is a new unit and what it is testing (message to end user) * Returns: - * Inputs: string:sMessage - message to give to the user about this unit */ "fnStart": function ( sMessage ) { window.parent.controller.fnStartMessage( sMessage ); }, /* * Function: fnComplete * Purpose: Tell the controller that we are all done here * Returns: - * Inputs: - */ "fnComplete": function () { this._bFinished = true; this._fnNext(); }, /* * Function: fnCookieDestroy * Purpose: Destroy a cookie of a given name * Returns: - * Inputs: - */ "fnCookieDestroy": function ( oTable ) { var sName = oTable.fnSettings().sCookiePrefix+oTable.fnSettings().sInstance; var aParts = window.location.pathname.split('/'); var sNameFile = sName + '_' + aParts.pop().replace(/[\/:]/g,"").toLowerCase(); document.cookie = sNameFile+"=; expires=Thu, 01-Jan-1970 00:00:01 GMT; path="+ aParts.join('/') + "/"; }, /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * Internal functions */ "_fnReTest": function ( oTestInfo ) { var bResult = oTestInfo.fnTest( ); if ( bResult ) { /* Test passed on retry */ this._fnResult( true ); this._fnNext(); } else { if ( this._iReTest < this.iReTestLimit ) { this._iReTest++; setTimeout( function() { oTest._fnReTest( oTestInfo ); }, this.iReTestDelay ); } else { this._fnResult( false ); } } }, "_fnNext": function () { if ( this.bEnd ) { return; } if ( !this.bBlock && this._aoQueue.length > 0 ) { var oNextTest = this._aoQueue.shift(); window.parent.controller.fnTestStart( oNextTest.sMessage ); this.bBlock = true; if ( typeof oNextTest.fnSetup == 'function' ) { oNextTest.fnSetup( ); } var bResult = oNextTest.fnTest( ); //bResult = false; if ( oNextTest.bPoll ) { if ( bResult ) { this._fnResult( true ); this._fnNext(); } else { _iReTest = 0; setTimeout( function() { oTest._fnReTest( oNextTest ); }, this.iReTestDelay ); } } else { this._fnResult( bResult ); this._fnNext(); } } else if ( !this.bBlock && this._aoQueue.length == 0 && this._bFinished ) { window.parent.controller.fnUnitComplete( ); } }, "_fnResult": function ( b ) { window.parent.controller.fnTestResult( b ); this.bBlock = false; if ( !b ) { this.bEnd = true; } } }; var oDispacher = { "click": function ( nNode, oSpecial ) { var evt = this.fnCreateEvent( 'click', nNode, oSpecial ); if ( nNode.dispatchEvent ) nNode.dispatchEvent(evt); else nNode.fireEvent('onclick', evt); }, "change": function ( nNode ) { var evt = this.fnCreateEvent( 'change', nNode ); if ( nNode.dispatchEvent ) nNode.dispatchEvent(evt); else nNode.fireEvent('onchange', evt); }, /* * Function: fnCreateEvent * Purpose: Create an event oject based on the type to trigger an event - x-platform * Returns: event:evt * Inputs: string:sType - type of event * node:nTarget - target node of the event */ fnCreateEvent: function( sType, nTarget, oSpecial ) { var evt = null; var oTargetPos = this._fnGetPos( nTarget ); var sTypeGroup = this._fnEventTypeGroup( sType ); if ( typeof oSpecial == 'undefined' ) { oSpecial = {}; } var ctrlKey = false; var altKey = false; var shiftKey = (typeof oSpecial.shift != 'undefined') ? oSpecial.shift : false; var metaKey = false; var button = false; if ( document.createEvent ) { switch ( sTypeGroup ) { case 'mouse': evt = document.createEvent( "MouseEvents" ); evt.initMouseEvent( sType, true, true, window, 0, oTargetPos[0], oTargetPos[1], oTargetPos[0], oTargetPos[1], ctrlKey, altKey, shiftKey, metaKey, button, null ); break; case 'html': evt = document.createEvent( "HTMLEvents" ); evt.initEvent( sType, true, true ); break; case 'ui': evt = document.createEvent( "UIEvents" ); evt.initUIEvent( sType, true, true, window, 0 ); break; default: break; } } else if ( document.createEventObject ) { switch ( sTypeGroup ) { case 'mouse': evt = document.createEventObject(); evt.screenX = oTargetPos[0]; evt.screenX = oTargetPos[1]; evt.clientX = oTargetPos[0]; evt.clientY = oTargetPos[1]; evt.ctrlKey = ctrlKey; evt.altKey = altKey; evt.shiftKey = shiftKey; evt.metaKey = metaKey; evt.button = button; evt.relatedTarget = null; break; case 'html': /* fall through to basic event object */ case 'ui': evt = document.createEventObject(); break; default: break; } } return evt; }, /* * Function: DesignCore.fnGetPos * Purpose: Get the position of an element on the page * Returns: array[ 0-int:left, 1-int:top ] * Inputs: node:obj - node to analyse */ _fnGetPos: function ( obj ) { var curleft = 0; var curtop = 0; if (obj.offsetParent) { curleft = obj.offsetLeft; curtop = obj.offsetTop; while (obj = obj.offsetParent ) { curleft += obj.offsetLeft; curtop += obj.offsetTop; } } return [curleft,curtop]; }, /* * Function: _fnEventTypeGroup * Purpose: Group the event types as per w3c groupings * Returns: - * Inputs: string:sType */ _fnEventTypeGroup: function ( sType ) { switch ( sType ) { case 'click': case 'dblclick': case 'mousedown': case 'mousemove': case 'mouseout': case 'mouseover': case 'mouseup': return 'mouse'; case 'change': case 'focus': case 'blur': case 'select': case 'submit': return 'html'; case 'keydown': case 'keypress': case 'keyup': case 'load': case 'unload': return 'ui'; default: return 'custom'; } } } var oSession = { nTable: null, fnCache: function () { this.nTable = document.getElementById('demo').cloneNode(true); }, fnRestore: function () { while( $.fn.dataTableSettings.length > 0 ) { try { $.fn.dataTableSettings[0].oInstance.fnDestroy(); } catch (e) { $.fn.dataTableSettings.splice( 0, 1 ); } } //$.fn.dataTableSettings.splice( 0, $.fn.dataTableSettings.length ); var nDemo = document.getElementById('demo'); nDemo.innerHTML = ""; for ( var i=0, iLen=this.nTable.childNodes.length ; i