quixote
Version:
CSS unit and integration testing
124 lines (102 loc) • 3.7 kB
JavaScript
// Copyright (c) 2013-2014 Titanium I.T. LLC. All rights reserved. See LICENSE.TXT for details.
;
// Runtime assertions for production code. (Contrast to assert.js, which is for test code.)
var shim = require("./shim.js");
var oop = require("./oop.js");
exports.that = function(variable, message) {
if (message === undefined) message = "Expected condition to be true";
if (variable === false) throw new EnsureException(exports.that, message);
if (variable !== true) throw new EnsureException(exports.that, "Expected condition to be true or false");
};
exports.unreachable = function(message) {
if (!message) message = "Unreachable code executed";
throw new EnsureException(exports.unreachable, message);
};
exports.signature = function(args, signature) {
signature = signature || [];
var expectedArgCount = signature.length;
var actualArgCount = args.length;
if (actualArgCount > expectedArgCount) {
throw new EnsureException(
exports.signature,
"Function called with too many arguments: expected " + expectedArgCount + " but got " + actualArgCount
);
}
var arg, types, name;
for (var i = 0; i < signature.length; i++) {
arg = args[i];
types = signature[i];
name = "Argument #" + (i + 1);
if (!shim.Array.isArray(types)) types = [ types ];
if (!argMatchesAnyPossibleType(arg, types)) {
var message = name + " expected " + explainPossibleTypes(types) + ", but was " + explainArg(arg);
throw new EnsureException(exports.signature, message);
}
}
};
function argMatchesAnyPossibleType(arg, type) {
for (var i = 0; i < type.length; i++) {
if (argMatchesType(arg, type[i])) return true;
}
return false;
function argMatchesType(arg, type) {
switch (getArgType(arg)) {
case "boolean": return type === Boolean;
case "string": return type === String;
case "number": return type === Number;
case "array": return type === Array;
case "function": return type === Function;
case "object": return type === Object || arg instanceof type;
case "undefined": return type === undefined;
case "null": return type === null;
case "NaN": return typeof(type) === "number" && isNaN(type);
default: exports.unreachable();
}
}
}
function explainPossibleTypes(type) {
var joiner = "";
var result = "";
for (var i = 0; i < type.length; i++) {
result += joiner + explainOneType(type[i]);
joiner = (i === type.length - 2) ? ", or " : ", ";
}
return result;
function explainOneType(type) {
switch (type) {
case Boolean: return "boolean";
case String: return "string";
case Number: return "number";
case Array: return "array";
case Function: return "function";
case null: return "null";
case undefined: return "undefined";
default:
if (typeof type === "number" && isNaN(type)) return "NaN";
else {
return oop.className(type) + " instance";
}
}
}
}
function explainArg(arg) {
var type = getArgType(arg);
if (type !== "object") return type;
return oop.instanceName(arg) + " instance";
}
function getArgType(variable) {
var type = typeof variable;
if (variable === null) type = "null";
if (shim.Array.isArray(variable)) type = "array";
if (type === "number" && isNaN(variable)) type = "NaN";
return type;
}
/*****/
var EnsureException = exports.EnsureException = function EnsureException(fnToRemoveFromStackTrace, message) {
if (Error.captureStackTrace) Error.captureStackTrace(this, fnToRemoveFromStackTrace);
else this.stack = (new Error()).stack;
this.message = message;
};
EnsureException.prototype = shim.Object.create(Error.prototype);
EnsureException.prototype.constructor = EnsureException;
EnsureException.prototype.name = "EnsureException";