@qooxdoo/framework
Version:
The JS Framework for Coders
1,002 lines (925 loc) • 28.6 kB
JavaScript
/* ************************************************************************
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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(object, classname) {
if (!object) {
return false;
}
var clazz = object.constructor;
while (clazz) {
if (clazz.classname === classname) {
return true;
}
clazz = clazz.superclass;
}
return false;
}
}
});