UNPKG

@qooxdoo/framework

Version:

The JS Framework for Coders

944 lines (829 loc) 27.7 kB
/* ************************************************************************ qooxdoo - the new era of web development http://qooxdoo.org Copyright: 2007-2008 1&1 Internet AG, Germany, http://www.1und1.de License: MIT: https://opensource.org/licenses/MIT See the LICENSE file in the project's top-level directory for details. Authors: * Fabian Jakobs (fjakobs) ************************************************************************ */ /** * A collection of assertions. * * These methods can be used to assert incoming parameters, return values, ... * If an assertion fails an {@link AssertionError} is thrown. * * Assertions are used in unit tests as well. * * @require(qx.lang.Type) * @ignore(qx.Class.*) */ qx.Bootstrap.define("qx.core.Assert", { statics : { __logError : true, /** * Assert that the condition evaluates to <code>true</code>. An * {@link AssertionError} is thrown if otherwise. * * @param comment {String} Message to be shown if the assertion fails. This * message is provided by the user. * @param msgvarargs {var} any number of parts of a message to show if assertion * triggers. Each will be converted to a string and all * parts will be concatenated. E. g. instead of * "Got invalid value " + this.__toString(val) + "!!!!!" * use * "Got invalid value ", val, "!!!!!" * (much better performance) * */ __fail : function(comment, msgvarargs) { // Build up message from message varargs. It's not really important // how long this takes as it is done only when assertion is triggered var msg = ""; for (var i=1, l=arguments.length; i<l; i++) { msg = msg + this.__toString(arguments[i] === undefined ? "'undefined'" : arguments[i]); } var fullComment = ""; if (msg) { fullComment = comment + ": " + msg; } else { fullComment = comment; } var errorMsg = "Assertion error! " + fullComment; if (qx.Class && qx.Class.isDefined("qx.core.AssertionError")) { var err = new qx.core.AssertionError(comment, msg); if (this.__logError) { qx.Bootstrap.error(errorMsg + "\n Stack trace: \n" + err.getStackTrace()); } throw err; } else { if (this.__logError) { qx.Bootstrap.error(errorMsg); } throw new Error(errorMsg); } }, /** * Convert an unknown value to a string to display in error messages * * @param value {var} any value * @return {String} a string representation of the value */ __toString : function(value) { var stringValue; if (value === null) { stringValue = "null"; } else if (qx.lang.Type.isArray(value) && value.length > 10) { stringValue = "Array[" + value.length + "]"; } else if ((value instanceof Object) && (value.toString == null)) { stringValue = qx.lang.Json.stringify(value, null, 2); } else { try { stringValue = value.toString(); } catch(e) { stringValue = ""; } } return stringValue; }, /** * Assert that the condition evaluates to <code>true</code>. * * @param condition {var} Condition to check for. Must evaluate to * <code>true</code>. * @param msg {String?} Message to be shown if the assertion fails. */ assert : function(condition, msg) { condition == true || this.__fail(msg || "", "Called assert with 'false'"); }, /** * Raise an {@link AssertionError}. * * @param msg {String} Message to be shown if the assertion fails. * @param compact {Boolean?false} Show less verbose message. Default: false. */ fail : function(msg, compact) { var msgvarargs = compact ? "" : "Called fail()."; this.__fail(msg || "", msgvarargs); }, /** * Assert that the value is <code>true</code> (Identity check). * * @param value {Boolean} Condition to check for. Must be identical to * <code>true</code>. * @param msg {String?} Message to be shown if the assertion fails. */ assertTrue : function(value, msg) { (value === true) || this.__fail(msg || "", "Called assertTrue with '", value, "'"); }, /** * Assert that the value is <code>false</code> (Identity check). * * @param value {Boolean} Condition to check for. Must be identical to * <code>false</code>. * @param msg {String?} Message to be shown if the assertion fails. */ assertFalse : function(value, msg) { (value === false) || this.__fail(msg || "", "Called assertFalse with '", value, "'"); }, /** * Assert that both values are equal. (Uses the equality operator * <code>==</code>.) * * @param expected {var} Reference value * @param found {var} found value * @param msg {String?} Message to be shown if the assertion fails. */ assertEquals : function(expected, found, msg) { expected == found || this.__fail( msg || "", "Expected '", expected, "' but found '", found, "'!" ); }, /** * Assert that both values are not equal. (Uses the not equality operator * <code>!=</code>.) * * @param expected {var} Reference value * @param found {var} found value * @param msg {String?} Message to be shown if the assertion fails. */ assertNotEquals : function(expected, found, msg) { expected != found || this.__fail( msg || "", "Expected '",expected, "' to be not equal with '", found, "'!" ); }, /** * Assert that both float values are equal. This might be needed because * of the natural floating point inaccuracy of computers. * * @param expected {Float} Reference value * @param found {Float} Found value * @param msg {String?} Message to be shown if the assertion fails. */ assertEqualsFloat : function(expected, found, msg) { this.assertNumber(expected); this.assertNumber(found); qx.lang.Number.equals(expected, found) || this.__fail(msg || "", "Expected '", expected, "' to be equal with '", found, "'!" ); }, /** * Assert that both float values are not equal. This might be needed * because of the natural floating point inaccuracy of computers. * * @param expected {Float} Reference value * @param found {Float} Found value * @param msg {String?} Message to be shown if the assertion fails. */ assertNotEqualsFloat : function(expected, found, msg) { this.assertNumber(expected); this.assertNumber(found); !qx.lang.Number.equals(expected, found) || this.__fail(msg || "", "Expected '", expected, "' to be not equal with '", found, "'!" ); }, /** * Assert that both values are identical. (Uses the identity operator * <code>===</code>.) * * @param expected {var} Reference value * @param found {var} found value * @param msg {String?} Message to be shown if the assertion fails. */ assertIdentical : function(expected, found, msg) { expected === found || this.__fail( msg || "", "Expected '", expected, "' (identical) but found '", found, "'!" ); }, /** * Assert that both values are not identical. (Uses the not identity operator * <code>!==</code>.) * * @param expected {var} Reference value * @param found {var} found value * @param msg {String?} Message to be shown if the assertion fails. */ assertNotIdentical : function(expected, found, msg) { expected !== found || this.__fail( msg || "", "Expected '", expected, "' to be not identical with '", found, "'!" ); }, /** * Assert that the value is not <code>undefined</code>. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertNotUndefined : function(value, msg) { value !== undefined || this.__fail( msg || "", "Expected value not to be undefined but found undefined!" ); }, /** * Assert that the value is <code>undefined</code>. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertUndefined : function(value, msg) { value === undefined || this.__fail( msg || "", "Expected value to be undefined but found ", value, "!" ); }, /** * Assert that the value is not <code>null</code>. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertNotNull : function(value, msg) { value !== null || this.__fail( msg || "", "Expected value not to be null but found null!" ); }, /** * Assert that the value is <code>null</code>. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertNull : function(value, msg) { value === null || this.__fail( msg || "", "Expected value to be null but found ", value, "!" ); }, /** * Assert that the first two arguments are equal, when serialized into * JSON. * * @param expected {var} The the expected value * @param found {var} The found value * @param msg {String?} Message to be shown if the assertion fails. */ assertJsonEquals : function(expected, found, msg) { this.assertEquals( qx.lang.Json.stringify(expected), qx.lang.Json.stringify(found), msg ); }, /** * Assert that the given string matches the regular expression * * @param str {String} String, which should match the regular expression * @param re {String|RegExp} Regular expression to match * @param msg {String?} Message to be shown if the assertion fails. */ assertMatch : function(str, re, msg) { this.assertString(str); this.assert( qx.lang.Type.isRegExp(re) || qx.lang.Type.isString(re), "The parameter 're' must be a string or a regular expression." ); str.search(re) >= 0 || this.__fail( msg || "", "The String '", str, "' does not match the regular expression '", re.toString(), "'!" ); }, /** * Assert that the number of arguments is within the given range * * @param args {arguments} The <code>arguments<code> variable of a function * @param minCount {Integer} Minimal number of arguments * @param maxCount {Integer} Maximum number of arguments * @param msg {String?} Message to be shown if the assertion fails. */ assertArgumentsCount : function(args, minCount, maxCount, msg) { var argCount = args.length; (argCount >= minCount && argCount <= maxCount) || this.__fail( msg || "", "Wrong number of arguments given. Expected '", minCount, "' to '", maxCount, "' arguments but found '", argCount, "' arguments." ); }, /** * Assert that an event is fired. * * @param obj {Object} The object on which the event should be fired. * @param event {String} The event which should be fired. * @param invokeFunc {Function} The function which will be invoked and which * fires the event. * @param listenerFunc {Function?null} The function which will be invoked in the * listener. The function receives one parameter which is the event. * @param msg {String?""} Message to be shows if the assertion fails. */ assertEventFired : function(obj, event, invokeFunc, listenerFunc, msg) { var called = false; var listener = function(e) { if (listenerFunc) { listenerFunc.call(obj, e); } called = true; }; var id; try { id = obj.addListener(event, listener, obj); invokeFunc.call(obj); } catch (ex) { throw ex; } finally { try { obj.removeListenerById(id); } catch (ex) { /* ignore */ } } called === true || this.__fail(msg || "", "Event (", event, ") not fired."); }, /** * Assert that an event is not fired. * * @param obj {Object} The object on which the event should be fired. * @param event {String} The event which should be fired. * @param invokeFunc {Function} The function which will be invoked and which * should not fire the event. * @param msg {String?} Message to be shows if the assertion fails. */ assertEventNotFired : function(obj, event, invokeFunc, msg) { var called = false; var listener = function(e) { called = true; }; var id = obj.addListener(event, listener, obj); invokeFunc.call(); called === false || this.__fail(msg || "", "Event (", event, ") was fired."); obj.removeListenerById(id); }, /** * Asserts that the callback raises a matching exception. * * @param callback {Function} function to check * @param exception {Error?Error} Expected constructor of the exception. * The assertion fails if the raised exception is not an instance of the * parameter. * @param re {String|RegExp} The assertion fails if the error message does * not match this parameter * @param msg {String?} Message to be shown if the assertion fails. */ assertException : function(callback, exception, re, msg) { var exception = exception || Error; var error; try { this.__logError = false; callback(); } catch(ex) { error = ex; } finally { this.__logError = true; } if (error == null) { this.__fail(msg || "", "The function did not raise an exception!"); } error instanceof exception || this.__fail(msg || "", "The raised exception does not have the expected type! ", exception , " != ", error); if (re) { this.assertMatch(error.toString(), re, msg); } }, /** * Assert that the value is an item in the given array. * * @param value {var} Value to check * @param array {Array} List of valid values * @param msg {String?} Message to be shown if the assertion fails. */ assertInArray : function(value, array, msg) { array.indexOf(value) !== -1 || this.__fail( msg || "", "The value '", value, "' must have any of the values defined in the array '", array, "'" ); }, /** * Assert that the value is NOT an item in the given array * * @param value {var} Value to check * @param array {Array} List of values * @param msg {String?} Message to be shown if the assertion fails */ assertNotInArray : function(value, array, msg) { array.indexOf(value) === -1 || this.__fail( msg || "", qx.lang.String.format( "The value '%1' must not have any of the values defined in the array '%2'", [value, array] ) ); }, /** * Assert that both array have identical array items. * * @param expected {Array} The expected array * @param found {Array} The found array * @param msg {String?} Message to be shown if the assertion fails. */ assertArrayEquals : function(expected, found, msg) { this.assertArray(expected, msg); this.assertArray(found, msg); msg = msg || "Expected [" + expected.join(", ") + "], but found [" + found.join(", ") + "]"; if (expected.length !== found.length) { this.fail(msg, true); } for (var i=0; i<expected.length; i++) { if (expected[i] !== found[i]) { this.fail(msg, true); } } }, /** * Assert that the value is a key in the given map. * * @param value {var} Value to check * @param map {Map} Map, where the keys represent the valid values * @param msg {String?} Message to be shown if the assertion fails. */ assertKeyInMap : function(value, map, msg) { map[value] !== undefined || this.__fail( msg || "", "The value '", value, "' must must be a key of the map '", map, "'" ); }, /** * Assert that the value is a function. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertFunction : function(value, msg) { qx.lang.Type.isFunction(value) || this.__fail( msg || "", "Expected value to be typeof function but found ", value, "!" ); }, /** * Assert that the value is a function or an async function. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertFunctionOrAsyncFunction : function(value, msg) { qx.lang.Type.isFunctionOrAsyncFunction(value) || this.__fail( msg || "", "Expected value to be typeof function or typeof async function but found ", value, "!" ); }, /** * Assert that the value is a string. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertString : function(value, msg) { qx.lang.Type.isString(value) || this.__fail( msg || "", "Expected value to be a string but found ", value, "!" ); }, /** * Assert that the value is a boolean. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertBoolean : function(value, msg) { qx.lang.Type.isBoolean(value) || this.__fail( msg || "", "Expected value to be a boolean but found ", value, "!" ); }, /** * Assert that the value is a number. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertNumber : function(value, msg) { (qx.lang.Type.isNumber(value) && isFinite(value)) || this.__fail( msg || "", "Expected value to be a number but found ", value, "!" ); }, /** * Assert that the value is a number >= 0. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertPositiveNumber : function(value, msg) { (qx.lang.Type.isNumber(value) && isFinite(value) && value >= 0) || this.__fail( msg || "", "Expected value to be a number >= 0 but found ", value, "!" ); }, /** * Assert that the value is an integer. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertInteger : function(value, msg) { (qx.lang.Type.isNumber(value) && isFinite(value) && value % 1 === 0) || this.__fail( msg || "", "Expected value to be an integer but found ", value, "!" ); }, /** * Assert that the value is an integer >= 0. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertPositiveInteger : function(value, msg) { var condition = ( qx.lang.Type.isNumber(value) && isFinite(value) && value % 1 === 0 && value >= 0 ); condition || this.__fail( msg || "", "Expected value to be an integer >= 0 but found ", value, "!" ); }, /** * Assert that the value is inside the given range. * * @param value {var} Value to check * @param min {Number} lower bound * @param max {Number} upper bound * @param msg {String?} Message to be shown if the assertion fails. */ assertInRange : function(value, min, max, msg) { (value >= min && value <= max) || this.__fail( msg || "", qx.lang.String.format("Expected value '%1' to be in the range '%2'..'%3'!", [value, min, max]) ); }, /** * Assert that the value is an object. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertObject : function(value, msg) { var condition = value !== null && (qx.lang.Type.isObject(value) || typeof value === "object"); condition || this.__fail( msg || "", "Expected value to be typeof object but found ", (value), "!" ); }, /** * Assert that the value is an array. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertArray : function(value, msg) { qx.lang.Type.isArray(value) || this.__fail( msg || "", "Expected value to be an array but found ", value, "!" ); }, /** * Assert that the value is a map either created using <code>new Object</code> * or by using the object literal notation <code>{ ... }</code>. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertMap : function(value, msg) { qx.lang.Type.isObject(value) || this.__fail( msg || "", "Expected value to be a map but found ", value, "!" ); }, /** * Assert that the value is a regular expression. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertRegExp : function(value, msg) { qx.lang.Type.isRegExp(value) || this.__fail( msg || "", "Expected value to be a regular expression but found ", value, "!" ); }, /** * Assert that the value has the given type using the <code>typeof</code> * operator. Because the type is not always what it is supposed to be it is * better to use more explicit checks like {@link #assertString} or * {@link #assertArray}. * * @param value {var} Value to check * @param type {String} expected type of the value * @param msg {String?} Message to be shown if the assertion fails. */ assertType : function(value, type, msg) { this.assertString(type, "Invalid argument 'type'"); typeof(value) === type || this.__fail( msg || "", "Expected value to be typeof '", type, "' but found ", value, "!" ); }, /** * Assert that the value is an instance of the given class. * * @param value {var} Value to check * @param clazz {Class} The value must be an instance of this class * @param msg {String?} Message to be shown if the assertion fails. */ assertInstance : function(value, clazz, msg) { var className = clazz.classname || clazz + ""; value instanceof clazz || this.__fail( msg || "", "Expected value to be instanceof '", className, "' but found ", value, "!" ); }, /** * Assert that the value implements the given interface. * * @param value {var} Value to check * @param iface {Class} The value must implement this interface * @param msg {String?} Message to be shown if the assertion fails. */ assertInterface : function(value, iface, msg) { qx.Class && qx.Class.implementsInterface(value, iface) || this.__fail( msg || "", "Expected object '", value, "' to implement the interface '", iface, "'!" ); }, /** * Assert that the value represents the given CSS color value. This method * parses the color strings and compares the RGB values. It is able to * parse values supported by {@link qx.util.ColorUtil#stringToRgb}. * * @param expected {String} The expected color * @param value {String} The value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertCssColor : function(expected, value, msg) { var ColorUtil = qx.Class ? qx.Class.getByName("qx.util.ColorUtil") : null; if (!ColorUtil) { throw new Error("qx.util.ColorUtil not available! Your code must have a dependency on 'qx.util.ColorUtil'"); } var expectedRgb = ColorUtil.stringToRgb(expected); try { var valueRgb = ColorUtil.stringToRgb(value); } catch (ex) { this.__fail( msg || "", "Expected value to be the CSS color '", expected, "' (rgb(", expectedRgb.join(","), ")), but found value '", value, "', which cannot be converted to a CSS color!" ); } var condition = expectedRgb[0] == valueRgb[0] && expectedRgb[1] == valueRgb[1] && expectedRgb[2] == valueRgb[2]; condition || this.__fail( msg || "", "Expected value to be the CSS color '", expectedRgb, "' (rgb(", expectedRgb.join(","), ")), but found value '", value, "' (rgb(", valueRgb.join(","), "))!" ); }, /** * Assert that the value is a DOM element. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertElement : function(value, msg) { // see qx.dom.Node.isElement !!(value && value.nodeType === 1) || this.__fail( msg || "", "Expected value to be a DOM element but found '", value, "'!" ); }, /** * Assert that the value is an instance of {@link qx.core.Object}. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertQxObject : function(value, msg) { this.__isQxInstance(value, "qx.core.Object") || this.__fail( msg || "", "Expected value to be a qooxdoo object but found ", value, "!" ); }, /** * Assert that the value is an instance of {@link qx.ui.core.Widget}. * * @param value {var} Value to check * @param msg {String?} Message to be shown if the assertion fails. */ assertQxWidget : function(value, msg) { this.__isQxInstance(value, "qx.ui.core.Widget") || this.__fail( msg || "", "Expected value to be a qooxdoo widget but found ", value, "!" ); }, /** * Internal helper for checking the instance of a qooxdoo object using the * classname. * * @param object {var} The object to check. * @param classname {String} The classname of the class as string. * @return {Boolean} <code>true</code> if the object is an instance of the * class */ __isQxInstance : function(object, classname) { if (!object) { return false; } var clazz = object.constructor; while(clazz) { if (clazz.classname === classname) { return true; } clazz = clazz.superclass; } return false; } } });