BX.use('brixy', 'modules/tester/Result.jsxinc');
BX.use('brixy', 'modules/es/types.jsxinc');
BX.use('brixy', 'modules/es/reflection.jsxinc');
/**
* @module 'brixy.tester.Assert'
*/
BX.module.define('brixy.tester.Assert', function() {
var STATUS = BX.module('brixy.tester.Result').STATUS,
Value = BX.module('brixy.tester.Result').Value,
types = BX.module('brixy.es.types'),
reflection = BX.module('brixy.es.reflection');
/**
* Asserts namespace.
* @namespace
* @memberOf module:'brixy.tester.Assert'
*/
var Asserts = {};
/**
* Test rating.
*
* @class
* @alias module:'brixy.tester.Assert'~Rating
* @param {*} actual
* @param {*} expected
* @param {int} depth
* @param {module:'brixy.tester.Result'.STATUS} status
* @param {module:'brixy.tester.Specials'~Specials} specs
*
* @property {module:'brixy.tester.Assert'~Value} actual
* @property {module:'brixy.tester.Assert'~Value} expected
* @property {int} depth
* @property {module:'brixy.tester.Result'.STATUS} status
* @property {string} note
*/
function Rating(actual, expected, depth, status, specs) {
this.actual = createValue(actual, specs);
this.expected = createValue(expected, specs);
this.depth = depth;
if (status === STATUS.OK || status === STATUS.FAILED) {
this.status = status;
this.note = '';
}
else {
this.status = STATUS.SKIPPED;
this.note = status;
}
}
/*
* Create value.
* @property {*} value
* @property {string} caption
* @return {module:'brixy.tester.Result'~Value}
*/
function createValue(val, specs) {
var s,
value,
caption;
if (specs && (s = specs.get(val))) {
value = s.value(val);
caption = s.caption(val) + '';
} else {
value = val;
caption = types.valueString(val);
}
if (caption.length > 70) // trim a long string
caption = caption.substr(0, 70) + '...';
return new Value(value, caption);
}
/**
* Tests if objects are identical.
* @param {*} actual
* @param {*} expected
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.is = function(actual, expected) {
var s = (actual === expected) ? STATUS.OK : STATUS.FAILED;
return new Rating(actual, expected, 1, s);
};
/**
* Tests if objects are not identical.
* @param {*} actual
* @param {*} expected
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.isNot = function(actual, expected) {
var s = (actual !== expected) ? STATUS.OK : STATUS.FAILED;
return new Rating(actual, expected, 1, s);
};
/**
* Tests if value is a member of the object.
* @param {*} actual
* @param {Array|Object} expected
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.isMember = function(actual, expected) {
var s = contains(actual, expected);
return new Rating(actual, expected, 1, s);
};
/**
* Tests if value is not a member of the object.
* @param {*} actual
* @param {Array|Object} expected
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.notMember = function(actual, expected) {
var s = contains(actual, expected);
return new Rating(actual, expected, 1, revertStatus(s));
};
/**
* Tests if objects are equal.
* @param {*} actual
* @param {*} expected
* @param {int} depth
* @param {module:'brixy.tester.Specials'~Specials} [specs] (optional)
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.isLike = function(actual, expected, depth, specs) {
var s = compare(actual, expected, depth, specs);
return new Rating(actual, expected, depth, s, specs);
};
/**
* Tests if objects are not equal.
* @param {*} actual
* @param {*} expected
* @param {int} depth
* @param {module:'brixy.tester.Specials'~Specials} [specs] (optional)
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.notLike = function(actual, expected, depth, specs) {
var s = compare(actual, expected, depth, specs);
return new Rating(actual, expected, depth, revertStatus(s), specs);
};
/**
* Tests if a callback throws an exception. Compares the thrown error only when argument `expected` is given.
* Note: Error messages are localised ("Error: message" vs. "Chyba: message").
* @param {function} actual
* @param {*} [expected] (optional)
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.isThrown = function(actual, expected) {
var s = simulateCall(actual, expected);
return new Rating(actual, expected, 1, s);
};
/**
* Tests if a callback doesn't throw an exception. Compares the thrown error only when argument `expected` is given.
* Note: Error messages are localised ("Error: message" vs. "Chyba: message").
* @param {function} actual
* @param {*} [expected] (optional)
* @return {module:'brixy.tester.Assert'~Rating}
*/
Asserts.notThrown = function(actual, expected) {
var s = simulateCall(actual, expected);
return new Rating(actual, expected, 1, revertStatus(s));
};
/* helpers */
function revertStatus(status) {
switch (status) {
case STATUS.OK: return STATUS.FAILED;
case STATUS.FAILED: return STATUS.OK;
}
return status;
}
function simulateCall(actual, expected) {
var s = STATUS.FAILED;
if (types.className(actual) !== 'Function')
return 'Actual parameter should be a function.';
try {
actual();
}
catch (err) {
if (expected)
s = (expected == err) ? STATUS.OK : STATUS.FAILED;
else
s = STATUS.OK;
}
return s;
}
/*
* Compare two values.
*/
function compare(actual, expected, depth, specs) {
if (actual === expected)
return STATUS.OK;
var a = (specs && specs.getValue(actual)) || actual,
e = (specs && specs.getValue(expected)) || expected,
t = types.baseType(a) + types.baseType(e),
s;
switch (t) {
case 'objectobject':
s = compareObjects(a, e, depth, specs);
break;
case 'arrayarray':
s = compareArrays(a, e, depth, specs);
break;
default:
if (/.*(object|array).*/.test(t)) // array-any OR object-any
s = STATUS.FAILED;
else
s = (a == e) ? STATUS.OK : STATUS.FAILED;
}
return s;
}
/*
* Compare arrays.
* Note: Compares items of the array. If nesting limit occured, it only compares the sum of items.
*/
function compareArrays(a, b, depth, specs) {
var n = a.length,
i = 0,
s;
// check number of items
if (n !== b.length)
return STATUS.FAILED;
// nesting limit
if (depth === 0)
return (n > 0) ? 'Nesting level is too low to compare arrays.' : STATUS.OK;
// check values of items
for ( ; i < n; i++) {
if (i in a !== i in b) // both or none has to contain index
return STATUS.FAILED;
s = compare(a[i], b[i], depth--, specs);
if (s !== STATUS.OK)
return s;
}
return STATUS.OK;
}
/*
* Compare objects.
* Note: Compares properties of the objects (constructors are NOT compared). If nesting limit occured, it only compares the sum of properties.
*/
function compareObjects(a, b, depth, specs) {
var arr,
pr,
s,
i,
n;
n = reflection.getOwnProperties(b).length;
arr = reflection.getOwnProperties(a);
// check number of properties
if (n !== arr.length)
return STATUS.FAILED;
// nesting limit
if (depth === 0)
return (n > 0) ? 'Nesting level is too low to compare objects.' : STATUS.OK;
// check values of properties
for (i = 0; i < n; i++) {
pr = arr[i];
if (!(pr in b))
return STATUS.FAILED;
s = compare(a[pr], b[pr], depth--, specs);
if (s !== STATUS.OK)
return s;
}
return STATUS.OK;
}
/*
* Checks if a value is a member of the container.
*/
function contains(value, container) {
if (value === container)
return STATUS.FAILED;
var t = types.baseType(container),
isOwn = Object.prototype.hasOwnProperty,
i,
n;
if (t === 'array') {
for (i = 0, n = container.length; i < n; i++) {
if (isOwn.call(container, i) && value === container[i])
return STATUS.OK;
}
}
else if (t === 'object') {
for (i in container) {
if (isOwn.call(container, i) && value === container[i])
return STATUS.OK;
}
}
return STATUS.FAILED;
}
return {
Asserts: Asserts,
/**
* Rating class.
* @memberOf module:'brixy.tester.Assert'
* @type {module:'brixy.tester.Assert'~Rating}
*/
Rating: Rating
};
});
►