Added: incubator/shindig/trunk/features/src/test/javascript/lib/JsUtil.js URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/lib/JsUtil.js?rev=752632&view=auto ============================================================================== --- incubator/shindig/trunk/features/src/test/javascript/lib/JsUtil.js (added) +++ incubator/shindig/trunk/features/src/test/javascript/lib/JsUtil.js Wed Mar 11 21:36:14 2009 @@ -0,0 +1,914 @@ +/* +JsUnit - a JUnit port for JavaScript +Copyright (C) 1999,2000,2001,2002,2003,2006,2007 Joerg Schaible + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +/** + * @file + * Utility classes needed for the JsUnit classes. + * JsUnit need several helper classes to work properly. This file contains + * anything that is not related directly to JsUnit, but may be useful in other + * environments, too. + */ + + +if( !this.Error ) +{ + /** + * Error class according ECMA specification. + * This class is only active, if the ECMA implementation of the current + * engine does not support it. + * @ctor + * Constructor. + * The constructor initializes the \c message member with the argument + * \a msg. + * \attention The ECMA standard does not ensure, that the constructor + * of the internal Error class may be called by derived objects. It will + * normally return a new Error instance if called as function. + * @tparam String msg The error message. + */ + function Error( msg ) + { + if( this instanceof Error ) + { + /** + * The error message. + * @type String + */ + this.message = msg || ""; + return; + } + else + { + return new Error( msg ); + } + } + /** + * String representation of the error. + * @treturn String Returns a \c String containing the Error class name + * and the error message. + * \attention The format of the returned string is not defined by ECMA + * and is up to the vendor only. This implementation follows the behavior + * of Mozilla.org's SpiderMonkey. + */ + function Error_toString() + { + var msg = this.message; + return this.name + ": " + msg; + } + Error.prototype = new Object(); + Error.prototype.toString = Error_toString; + /** + * The name of the Error class as String. + * @type String + */ + Error.prototype.name = "Error"; + /** + * \internal + */ + Error.prototype.testable = true; +} +else +{ + /** + * \internal + */ + Error.prototype.testable = false; +} + + +/** + * JsUnitError class. + * Since ECMA does not define any inheritability of the Error class and the + * class itself is highly vender specific, JsUnit uses its own base class for + * all errors in the framework. + * @ctor + * Constructor. + * The constructor initializes the \c message member with the argument + * \a msg. + * \attention The ECMA standard does not ensure, that the constructor + * of the internal Error class may be called by derived objects. It will + * normally return a new Error instance if called as function. + * @tparam String msg The error message. + * \attention This constructor may <b>not</b> be called as normal function. + */ +function JsUnitError( msg ) +{ + this.message = msg || ""; +} +/** + * String representation of the error. + * The format of the returned string is not defined by ECMA + * and is up to the vendor only. This implementation follows the behavior + * of Mozilla.org's SpiderMonkey. + * @treturn String Returns a \c String containing the Error class name + * and the error message. + */ +function JsUnitError_toString() +{ + var msg = this.message; + return this.name + ": " + msg; +} +JsUnitError.prototype = new Error(); +JsUnitError.prototype.toString = JsUnitError_toString; +/** + * The name of the Error class as String. + * @type String + */ +JsUnitError.prototype.name = "JsUnitError"; + + +/** + * InterfaceDefinitionError class. + * This error class is used for interface definitions. Such definitions are + * simulated using Function::fulfills. The class has no explicit functionality + * despite the separate type + * @see Function::fulfills + * @ctor + * Constructor. + * The constructor initializes the \c message member with the argument + * \a msg. + * @tparam String msg The error message. + **/ +function InterfaceDefinitionError( msg ) +{ + JsUnitError.call( this, msg ); +} +InterfaceDefinitionError.prototype = new JsUnitError(); +/** + * The name of the InterfaceDefinitionError class as String. + * @type String + **/ +InterfaceDefinitionError.prototype.name = "InterfaceDefinitionError"; + + +/** + * FunctionGluingError class. + * This error class is used for gluing member functions to a class. This convenience + * functionality of Function::glue ensures by throwing an instance of this class, that + * only valid functions are injected to the prototype. The class has no explicit + * functionality despite the separate type + * @see Function::glue + * @ctor + * Constructor. + * The constructor initializes the \c message member with the argument + * \a msg. + * @tparam String msg The error message. + **/ +function FunctionGluingError( msg ) +{ + JsUnitError.call( this, msg ); +} +FunctionGluingError.prototype = new JsUnitError(); +/** + * The name of the FunctionGluingError class as String. + * @type String + **/ +FunctionGluingError.prototype.name = "FunctionGluingError"; + + +/** + * \class Function + * Standard ECMA class. + * \docgen function Function() {} + */ +/** + * Ensures that a function fulfills an interface. + * Since with ECMA 262 (3rd edition) interfaces are not supported yet, this + * function will simulate the functionality. The arguments for the functions + * are all classes that the current class will implement. The function checks + * whether the current class fulfills the interface of the given classes or not. + * @exception TypeError If the current object is not a class or the interface + * is not a Function object with a prototype. + * @exception InterfaceDefinitionError If an interface is not fulfilled or the + * interface has invalid members. + */ +function Function_fulfills() +{ + for( var i = 0; i < arguments.length; ++i ) + { + var I = arguments[i]; + if( typeof I != "function" || !I.prototype ) + throw new InterfaceDefinitionError( + I.toString() + " is not an Interface" ); + if( !this.prototype ) + throw new InterfaceDefinitionError( + "Current instance is not a Function definition" ); + for( var f in I.prototype ) + { + if( typeof I.prototype[f] != "function" ) + throw new InterfaceDefinitionError( f.toString() + + " is not a method in Interface " + I.toString()); + if( typeof this.prototype[f] != "function" + && typeof this[f] != "function" ) + { + if( typeof this.prototype[f] == "undefined" + && typeof this[f] == "undefined" ) + throw new InterfaceDefinitionError( + f.toString() + " is not defined" ); + else + throw new InterfaceDefinitionError( + f.toString() + " is not a function" ); + } + } + } +} +/** + * Glue functions to a JavaScript class as member functions. + * The method attaches the functions given as arguments to the prototype of the + * current instance. + * @exception InterfaceDefinitionError If the current instance of a given + * argument is not a Function object with a prototype. + */ +function Function_glue( scope ) +{ + if( !this.prototype ) + throw new FunctionGluingError( + "Current instance is not a Function definition" ); + var r = /function (\w+)[^\{\}]*\)/; + if( !r.exec( this.toString())) + throw new FunctionGluingError( "Cannot glue to anonymous function" ); + var className = new String( RegExp.$1 ); + if( scope === undefined ) + scope = JsUtil.prototype.global; + for( var name in scope ) + { + if( name.indexOf( className + "_" ) == 0 ) + { + var fnName = name.substr( className.length + 1 ); + var fn = scope[name]; + if( typeof( fn ) == "function" ) + { + if( ! /^[a-z_][\w]*$/.test( fnName )) + throw new FunctionGluingError( + "Not a valid method name: " + fnName ); + this.prototype[fnName] = fn; + } + } + } +} +Function.prototype.fulfills = Function_fulfills; +Function.prototype.glue = Function_glue; + + +// MS engine does not implement Array.push and Array.pop until JScript 5.6 +if( !Array.prototype.pop ) +{ + /** + * \class Array + * Standard ECMA class. + * \docgen function Array() {} + */ + /** + * Pops last element from Array. + * The function is an implementation of the Array::pop method described + * in the ECMA standard. It removes the last element of the Array and + * returns it. + * + * The function is active if the ECMA implementation does not implement + * it (like Microsoft JScript engine up to version 5.5). + * @treturn Object Last element or undefined + */ + function Array_pop() + { + var obj; + if( this instanceof Array && this.length > 0 ) + { + var last = parseInt( this.length ) - 1; + obj = this[last]; + this.length = last; + } + return obj; + } + Array.prototype.pop = Array_pop; +} +if( !Array.prototype.push ) +{ + /** + * Pushes elements into Array. + * The function is an implementation of the Array::push method described + * in the ECMA standard. It adds all given parameters at the end of the + * array. + * + * The function is active if the ECMA implementation does not implement + * it (like Microsoft JScript engine up to version 5.5). + * @treturn Object Number of added elements + */ + function Array_push() + { + var i = 0; + if( this instanceof Array ) + { + i = this.length; + + // Preallocation of array + if( arguments.length > 0 ) + this[arguments.length + this.length - 1] = null; + + for( ; i < this.length; ++i ) + this[i] = arguments[i - this.length + arguments.length]; + } + return i; + } + Array.prototype.push = Array_push; +} + + +/** + * \class String + * Standard ECMA class. + * \docgen function String() {} + */ +/** + * Trims characters from string. + * @tparam String chars String with characters to remove. The character may + * also be a regular expression character class like "\\s" (which is the + * default). + * + * The function removes the given characters \a chars from the beginning an + * the end from the current string and returns the result. The function will + * not modify the current string. + * + * The function is written as String enhancement and available as new member + * function of the class String. + * @treturn String String without given characters at start or end. + */ +function String_trim( chars ) +{ + if( !chars ) + chars = "\\s"; + var re = new RegExp( "^[" + chars + "]*(.*?)[" + chars + "]*$" ); + var s = this.replace( re, "$1" ); + return s; +} +String.prototype.trim = String_trim; + + +/** + * Helper class with static flags. + */ +function JsUtil() +{ +} +/** + * Retrieve the caller of a function. + * @tparam Function fn The function to examine. + * @treturn Function The caller as Function or undefined. + **/ +function JsUtil_getCaller( fn ) +{ + switch( typeof( fn )) + { + case "undefined": + return JsUtil_getCaller( JsUtil_getCaller ); + + case "function": + if( fn.caller ) + return fn.caller; + if( fn.arguments && fn.arguments.caller ) + return fn.arguments.caller; + } + return undefined; +} +/** + * Includes a JavaScript file. + * @tparam String fname The file name. + * Loads the content of a JavaScript file into a String that has to be + * evaluated. Works for command line shells WSH, Rhino and SpiderMonkey. + * @note This function is highly quirky. While WSH works as expected, the + * Mozilla shells will evaluate the file immediately and add any symbols to + * the global name space and return just "true". Therefore you have to + * evaluate the returned string for WSH at global level also. Otherwise the + * function is not portable. + * @treturn String The JavaScript code to be evaluated. + */ +function JsUtil_include( fname ) +{ + var ret = "true"; + if( JsUtil.prototype.isMozillaShell || JsUtil.prototype.isKJS ) + { + load( fname ); + } + else if( JsUtil.prototype.isWSH ) + { + var fso = new ActiveXObject( "Scripting.FileSystemObject" ); + var file = fso.OpenTextFile( fname, 1 ); + ret = file.ReadAll(); + file.Close(); + } + return ret; +} +/** + * Returns the SystemWriter. + * Instantiates a SystemWriter depending on the current JavaScript engine. + * Works for command line shells WSH, Rhino and SpiderMonkey. + * @type SystemWriter + */ +function JsUtil_getSystemWriter() +{ + if( !JsUtil.prototype.mWriter ) + JsUtil.prototype.mWriter = new SystemWriter(); + return JsUtil.prototype.mWriter; +} +/** + * Quits the JavaScript engine. + * @tparam Number ret The exit code. + * Stops current JavaScript engine and returns an exit code. Works for + * command line shells WSH, Rhino and SpiderMonkey. + */ +function JsUtil_quit( ret ) +{ + if( JsUtil.prototype.isMozillaShell ) + quit( ret ); + else if( JsUtil.prototype.isKJS ) + exit( ret ); + else if( JsUtil.prototype.isWSH ) + WScript.Quit( ret ); +} +JsUtil.prototype.getCaller = JsUtil_getCaller; +JsUtil.prototype.getSystemWriter = JsUtil_getSystemWriter; +JsUtil.prototype.include = JsUtil_include; +JsUtil.prototype.quit = JsUtil_quit; +/** + * The SystemWriter. + * @type SystemWriter + * @see getSystemWriter + */ +JsUtil.prototype.mWriter = null; +/** + * Flag for a browser. + * @type Boolean + * The member is true, if the script runs within a browser environment. + */ +JsUtil.prototype.isBrowser = this.window != null; +/** + * Flag for Microsoft JScript. + * @type Boolean + * The member is true, if the script runs in the Microsoft JScript engine. + */ +JsUtil.prototype.isJScript = this.ScriptEngine != null; +/** + * Flag for Microsoft Windows Scripting Host. + * @type Boolean + * The member is true, if the script runs in the Microsoft Windows Scripting + * Host. + */ +JsUtil.prototype.isWSH = this.WScript != null; +/** + * Flag for Microsoft IIS. + * @type Boolean + * The member is true, if the script runs in the Microsoft JScript engine. + */ +JsUtil.prototype.isIIS = + JsUtil.prototype.isJScript + && this.Server != null; +/** + * Flag for Netscape Enterprise Server (iPlanet) engine. + * @type Boolean + * The member is true, if the script runs in the iPlanet as SSJS. + */ +JsUtil.prototype.isNSServer = + this.Packages != null + && !this.importPackage + && !JsUtil.prototype.isBrowser; +/** + * Flag for Rhino. + * @type Boolean + * The member is true, if the script runs in an embedded Rhino of Mozilla.org. + */ +JsUtil.prototype.isRhino = + this.java != null + && this.java.lang != null + && this.java.lang.System != null; +/** + * Flag for a Mozilla JavaScript shell. + * @type Boolean + * The member is true, if the script runs in a command line shell of a + * Mozilla.org script engine (either SpiderMonkey or Rhino). + */ +JsUtil.prototype.isMozillaShell = this.quit != null; +/** + * Flag for a KJS shell. + * @type Boolean + * The member is true, if the script runs in a command line shell of a + * KDE's script engine. + */ +JsUtil.prototype.isKJS = this.exit != null; +/** + * Flag for a command line shell. + * @type Boolean + * The member is true, if the script runs in a command line shell. + */ +JsUtil.prototype.isShell = + JsUtil.prototype.isMozillaShell + || JsUtil.prototype.isKJS + || JsUtil.prototype.isWSH; +/** + * Flag for Obtree C4. + * @type Boolean + * The member is true, if the script runs in Obtree C4 of IXOS. + */ +JsUtil.prototype.isObtree = this.WebObject != null; +/** + * Flag for call stack support. + * @type Boolean + * The member is true, if the engine provides call stack info. + */ +JsUtil.prototype.hasCallStackSupport = + JsUtil.prototype.getCaller() !== undefined; +/** + * The global object. + * @type Object + * The member keeps the execution scope of this file, which is normally the + * global object. + */ +JsUtil.prototype.global = this; + + +/** + * CallStack object. + * The object is extremely system dependent, since its functionality is not + * within the range of ECMA 262, 3rd edition. It is supported by JScript + * and SpiderMonkey and was supported in Netscape Enterprise Server 2.x, + * but not in the newer version 4.x. + * @ctor + * Constructor. + * The object collects the current call stack up to the JavaScript engine. + * Most engines will not support call stack information with a recursion. + * Therefore the collection is stopped when the stack has two identical + * functions in direct sequence. + * @tparam Number depth Maximum recorded stack depth (defaults to 10). + **/ +function CallStack( depth ) +{ + /** + * The array with the stack. + * @type Array<String> + */ + this.mStack = null; + if( JsUtil.prototype.hasCallStackSupport ) + this._fill( depth ); +} + +/** + * \internal + */ +function CallStack__fill( depth ) +{ + this.mStack = new Array(); + + // set stack depth to default + if( depth == null ) + depth = 10; + + ++depth; + var fn = JsUtil.prototype.getCaller( CallStack__fill ); + while( fn != null && depth > 0 ) + { + var s = new String( fn ); + --depth; + + // Extract function name and argument list + var r = /function (\w+)([^\{\}]*\))/; + r.exec( s ); + var f = new String( RegExp.$1 ); + var args = new String( RegExp.$2 ); + this.mStack.push(( f + args ).replace( /\s/g, "" )); + + // Retrieve caller function + if( fn == JsUtil.prototype.getCaller( fn )) + { + // Some interpreter's caller use global objects and may start + // an endless recursion. + this.mStack.push( "[JavaScript recursion]" ); + break; + } + else + fn = JsUtil.prototype.getCaller( fn ); + } + + if( fn == null ) + this.mStack.push( "[JavaScript engine]" ); + + // remove direct calling function CallStack or CallStack_fill + this.mStack.shift(); +} +/** + * Fills the object with the current call stack info. + * The function collects the current call stack up to the JavaScript engine. + * Any previous data of the instance is lost. + * Most engines will not support call stack information with a recursion. + * Therefore the collection is stopped when the stack has two identical + * functions in direct sequence. + * @tparam Number depth Maximum recorded stack depth (defaults to 10). + **/ +function CallStack_fill( depth ) +{ + this.mStack = null; + if( JsUtil.prototype.hasCallStackSupport ) + this._fill( depth ); +} +/** + * Retrieve call stack as array. + * The function returns the call stack as Array of Strings. + * @treturn Array<String> The call stack as array of strings. + **/ +function CallStack_getStack() +{ + var a = new Array(); + if( this.mStack != null ) + for( var i = this.mStack.length; i--; ) + a[i] = this.mStack[i]; + return a; +} +/** + * Retrieve call stack as string. + * The function returns the call stack as string. Each stack frame has an + * own line and is prepended with the call stack depth. + * @treturn String The call stack as string. + **/ +function CallStack_toString() +{ + var s = ""; + if( this.mStack != null ) + for( var i = 1; i <= this.mStack.length; ++i ) + { + if( s.length != 0 ) + s += "\n"; + s += i.toString() + ": " + this.mStack[i-1]; + } + return s; +} +CallStack.prototype._fill = CallStack__fill; +CallStack.prototype.fill = CallStack_fill; +CallStack.prototype.getStack = CallStack_getStack; +CallStack.prototype.toString = CallStack_toString; + + +/** + * PrinterWriterError class. + * This error class is used for errors in the PrinterWriter. + * @see PrinterWriter::close + * @ctor + * Constructor. + * The constructor initializes the \c message member with the argument + * \a msg. + * @tparam String msg The error message. + **/ +function PrinterWriterError( msg ) +{ + JsUnitError.call( this, msg ); +} +PrinterWriterError.prototype = new JsUnitError(); +/** + * The name of the PrinterWriterError class as String. + * @type String + **/ +PrinterWriterError.prototype.name = "PrinterWriterError"; + + +/** + * A PrinterWriter is an abstract base class for printing text. + * @note This is a helper construct to support different writers in + * ResultPrinter e.g. depending on the JavaScript engine. + */ +function PrinterWriter() +{ + this.mBuffer = null; + this.mClosed = false; +} +/** + * Closes the writer. + * After closing the steam no further writing is allowed. Multiple calls to + * close should be allowed. + */ +function PrinterWriter_close() +{ + this.flush(); + this.mClosed = true; +} +/** + * Flushes the writer. + * Writes any buffered data to the underlaying output stream system immediately. + * @exception PrinterWriterError If flush was called after closing. + */ +function PrinterWriter_flush() +{ + if( !this.mClosed ) + { + if( this.mBuffer !== null ) + { + this._flush( this.mBuffer + "\n" ); + this.mBuffer = null; + } + } + else + throw new PrinterWriterError( + "'flush' called for closed PrinterWriter." ); +} +/** + * Prints into the writer. + * @tparam Object data The data to print as String. + * @exception PrinterWriterError If print was called after closing. + */ +function PrinterWriter_print( data ) +{ + if( !this.mClosed ) + { + var undef; + if( data === undef || data == null ) + data = ""; + if( this.mBuffer ) + this.mBuffer += data.toString(); + else + this.mBuffer = data.toString(); + } + else + throw new PrinterWriterError( + "'print' called for closed PrinterWriter." ); +} +/** + * Prints a line into the writer. + * @tparam Object data The data to print as String. + * @exception PrinterWriterError If println was called after closing. + */ +function PrinterWriter_println( data ) +{ + this.print( data ); + this.flush(); +} +PrinterWriter.prototype.close = PrinterWriter_close; +PrinterWriter.prototype.flush = PrinterWriter_flush; +PrinterWriter.prototype.print = PrinterWriter_print; +PrinterWriter.prototype.println = PrinterWriter_println; +/** + * \internal + */ +PrinterWriter.prototype._flush = function() {}; + + +/** + * The PrinterWriter of the JavaScript engine. + */ +function SystemWriter() +{ + PrinterWriter.call( this ); +} +/** + * Closes the writer. + * Function just flushes the writer. Closing the system writer is not possible. + */ +function SystemWriter_close() +{ + this.flush(); +} +/** + * \internal + */ +function SystemWriter__flush( str ) +{ + /* self-modifying code ... */ + if( JsUtil.prototype.isMozillaShell ) + this._flush = + function SystemWriter__flush( str ) + { + print( str.substring( 0, str.length - 1 )); + } + else if( JsUtil.prototype.isKJS ) + this._flush = + function SystemWriter__flush( str ) + { + print( str ); + } + else if( JsUtil.prototype.isBrowser ) + this._flush = + function SystemWriter__flush( str ) + { + document.write( str ); + } + else if( JsUtil.prototype.isWSH ) + this._flush = + function SystemWriter__flush( str ) + { + WScript.Echo( str.substring( 0, str.length - 1 )); + } + else if( JsUtil.prototype.isIIS ) + this._flush = + function SystemWriter__flush( str ) + { + Response.write( str ); + } + /* + else if( JsUtil.prototype.isNSServer ) + this._flush = + function SystemWriter__flush( str ) + { + write( str ); + } + */ + else if( JsUtil.prototype.isObtree ) + this._flush = + function SystemWriter__flush( str ) + { + write( str ); + } + else + this._flush = function() {} + + this._flush( str ); +} +SystemWriter.prototype = new PrinterWriter(); +SystemWriter.prototype.close = SystemWriter_close; +SystemWriter.prototype._flush = SystemWriter__flush; + + +/** + * The PrinterWriter into a String. + */ +function StringWriter() +{ + PrinterWriter.call( this ); + this.mString = ""; +} +/** + * Returns the written String. + * The function will close also the stream if it is still open. + * @type String + */ +function StringWriter_get() +{ + if( !this.mClosed ) + this.close(); + return this.mString; +} +/** + * \internal + */ +function StringWriter__flush( str ) +{ + this.mString += str; +} +StringWriter.prototype = new PrinterWriter(); +StringWriter.prototype.get = StringWriter_get; +StringWriter.prototype._flush = StringWriter__flush; + + +/** + * A filter for a PrinterWriter encoding HTML. + * @ctor + * Constructor. + * @tparam PrinterWriter writer The writer to filter. + * The constructor accepts the writer to wrap. + */ +function HTMLWriterFilter( writer ) +{ + PrinterWriter.call( this ); + this.setWriter( writer ); +} +/** + * Returns the wrapped PrinterWriter. + * @type PrinterWriter + */ +function HTMLWriterFilter_getWriter() +{ + return this.mWriter; +} +/** + * Sets the PrinterWriter to wrap. + * @tparam PrinterWriter writer The writer to filter. + * If the argument is omitted a StringWriter is created and wrapped. + */ +function HTMLWriterFilter_setWriter( writer ) +{ + this.mWriter = writer ? writer : new StringWriter(); +} +/** + * \internal + */ +function HTMLWriterFilter__flush( str ) +{ + str = str.toString(); + str = str.replace( /&/g, "&" ); + str = str.replace( /</g, "<" ); + str = str.replace( />/g, ">" ); + str = str.replace( /\'/g, "'" ); + str = str.replace( /\"/g, """ ); + str = str.replace( /\n/g, "<br>" ); + this.mWriter._flush( str ); +} +HTMLWriterFilter.prototype = new PrinterWriter(); +HTMLWriterFilter.prototype.getWriter = HTMLWriterFilter_getWriter; +HTMLWriterFilter.prototype.setWriter = HTMLWriterFilter_setWriter; +HTMLWriterFilter.prototype._flush = HTMLWriterFilter__flush;
Added: incubator/shindig/trunk/features/src/test/javascript/lib/testutils.js URL: http://svn.apache.org/viewvc/incubator/shindig/trunk/features/src/test/javascript/lib/testutils.js?rev=752632&view=auto ============================================================================== --- incubator/shindig/trunk/features/src/test/javascript/lib/testutils.js (added) +++ incubator/shindig/trunk/features/src/test/javascript/lib/testutils.js Wed Mar 11 21:36:14 2009 @@ -0,0 +1,179 @@ +/** + * Verifies members for a single service request function + * @param fn (Function) Service Request function + */ +TestCase.prototype.assertRequestPropertiesForService = function(fn) { + this.assertTrue('Should have produced a result', fn); + this.assertTrue('Should have an execute method', fn.execute); + this.assertTrue('Should have a json method', fn.json); +}; + + +/** + * Verify that arguments sent out of system (to non-proxied xhr) are built properly + * @param argsInCall + * @param expectedJson + */ +TestCase.prototype.assertArgsToMakeNonProxiedRequest = function(argsInCall, expectedJson) { + this.assertTrue('url should be passed to makeNonProxiedRequest', + argsInCall.hasOwnProperty('url')); + this.assertTrue('callback should be passed to makeNonProxiedRequest', + argsInCall.callback); + this.assertTrue('params should be passed to makeNonProxiedRequest', + argsInCall.params); + this.assertEquals('Content type should match', 'application/json', + argsInCall.contentType); + this.assertEquals('Json for batch should match', expectedJson, + gadgets.json.parse(argsInCall.params.POST_DATA)); + +}; + +/** + * Verify that arguments sent out of system (to proxied xhr) are built properly + * @param argsInCall + * @param expectedJson + */ +TestCase.prototype.assertArgsToMakeRequest = function(argsInCall) { + this.assertTrue('url should be passed to makeNonProxiedRequest', + argsInCall.hasOwnProperty('url')); + this.assertTrue('callback should be passed to makeNonProxiedRequest', + argsInCall.callback); + this.assertTrue('options should be passed to makeNonProxiedRequest', + argsInCall.options); + +}; + +/** + * Make a callback that can be asserted that it was called, and that still calls + * a callback. + * @return (Function) A callback function + */ +var makeInspectableCallback = function(realCallback) { + var called = false; + + return { + callback : function(result) { + called = true; + if (realCallback != null) { + if (typeof result !== 'array') { + result = [result]; + } + realCallback.apply(this, result); + } + }, + wasCalled : function() { + return called; + } + }; +}; + + +/** + * An assert equals that compares correctly and prints useful output for object types, like json. + * @param msg + * @param expected + * @param actual + */ +TestCase.prototype.assertEquals = function(msg, expected, actual) { + if (arguments.length == 2) { + actual = expected; + expected = msg; + msg = null; + } + + if (!deepEquals(expected, actual)) { + if (typeof( expected ) == 'string' && typeof( actual ) == 'string') { + throw new ComparisonFailure(msg, expected, actual, new CallStack()); + } else { + this.fail('Expected:<' + getSourceMessage(expected) + + '>\n, but was:<' + getSourceMessage(actual) + '>' + , new CallStack(), msg); + } + } +}; + +/** + * Print an operand to an assert operation. + * @param expected + * @param actual + */ +var getSourceMessage = function(operand) { + if (operand === undefined) { + return "undefined"; + } else if (operand === null) { + return "null"; + } else { + return operand.toSource(); + } +}; + +/** + * Implements deep equality for JSON objects; the two objects + * are equal if they have the same properties with the same values. + * + * @param {Object} a first object + * @param {Object} b second object + * @return {boolean} true if the objects are equal + * @private + */ +function deepEquals(expected, actual) { + // Undefined/null can be treated as equal here, I believe + if (expected == null) { + return actual == null; + } + + // If the types are different, the objects are different + var typeOfExpected = typeof expected; + if (typeOfExpected != typeof actual) { + return false; + } + + // A few types that can handle a straight === check + if ((typeOfExpected == 'string') || (typeOfExpected == 'boolean') || + (typeOfExpected == 'number')) { + return expected === actual; + } + + // If it's an array, use deepEquals on each entry + if (typeOfExpected == 'array') { + if (expected.length != actual.length) { + return false; + } + + for (var i = 0; i < expected.length; i++) { + if (!deepEquals(expected[i], actual[i])) { + return false; + } + } + + return true; + } + + // OK, we figure it's just an object. + + // Make sure everything in a matches the value in b + for (var aKey in expected) { + if (!expected.hasOwnProperty(aKey)) { + continue; + } + + if (!deepEquals(expected[aKey], actual[aKey])) { + return false; + } + } + + // And make sure everything in b is in a + for (var bKey in actual) { + if (!actual.hasOwnProperty(bKey)) { + continue; + } + if (!(bKey in expected)) { + return false; + } + } + + return true; +} +; + +
