unexpected
Version:
Extensible BDD assertion toolkit
1,196 lines (1,064 loc) • 55 kB
JavaScript
/*global setTimeout*/
var utils = require('./utils');
var objectIs = utils.objectIs;
var isRegExp = utils.isRegExp;
var isArray = utils.isArray;
var extend = utils.extend;
module.exports = function (expect) {
expect.addAssertion('<any> [not] to be (ok|truthy)', function (expect, subject) {
var not = !!expect.flags.not;
var condition = !!subject;
if (condition === not) {
expect.fail();
}
});
expect.addAssertion('<any> [not] to be (ok|truthy) <string>', function (expect, subject, message) {
var not = !!expect.flags.not;
var condition = !!subject;
if (condition === not) {
expect.fail({
errorMode: 'nested',
message: message
});
}
});
expect.addAssertion('<any> [not] to be <any>', function (expect, subject, value) {
expect(objectIs(subject, value), '[not] to be truthy');
});
expect.addAssertion('<string> [not] to be <string>', function (expect, subject, value) {
expect(subject, '[not] to equal', value);
});
expect.addAssertion('<boolean> [not] to be true', function (expect, subject) {
expect(subject, '[not] to be', true);
});
expect.addAssertion('<boolean> [not] to be false', function (expect, subject) {
expect(subject, '[not] to be', false);
});
expect.addAssertion('<any> [not] to be falsy', function (expect, subject) {
expect(subject, '[!not] to be truthy');
});
expect.addAssertion('<any> [not] to be null', function (expect, subject) {
expect(subject, '[not] to be', null);
});
expect.addAssertion('<any> [not] to be undefined', function (expect, subject) {
expect(typeof subject === 'undefined', '[not] to be truthy');
});
expect.addAssertion('<any> to be defined', function (expect, subject) {
expect(subject, 'not to be undefined');
});
expect.addAssertion('<number|NaN> [not] to be NaN', function (expect, subject) {
expect(isNaN(subject), '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be close to <number> <number?>', function (expect, subject, value, epsilon) {
expect.errorMode = 'bubble';
if (typeof epsilon !== 'number') {
epsilon = 1e-9;
}
expect.withError(function () {
expect(Math.abs(subject - value), '[not] to be less than or equal to', epsilon);
}, function (e) {
expect.fail(function (output) {
output.error('expected ')
.appendInspected(subject).sp()
.error(expect.testDescription).sp()
.appendInspected(value).sp()
.text('(epsilon: ')
.jsNumber(epsilon.toExponential())
.text(')');
});
});
});
expect.addAssertion('<any> [not] to be (a|an) <type>', function (expect, subject, type) {
expect.argsOutput[0] = function (output) {
output.text(type.name);
};
expect(type.identify(subject), '[not] to be true');
});
expect.addAssertion('<any> [not] to be (a|an) <string>', function (expect, subject, typeName) {
typeName = /^reg(?:exp?|ular expression)$/.test(typeName) ? 'regexp' : typeName;
expect.argsOutput[0] = function (output) {
output.jsString(typeName);
};
expect(expect.subjectType.is(typeName), '[not] to be truthy');
});
expect.addAssertion('<any> [not] to be (a|an) <function>', function (expect, subject, Constructor) {
var className = utils.getFunctionName(Constructor);
if (className) {
expect.argsOutput[0] = function (output) {
output.text(className);
};
}
expect(subject instanceof Constructor, '[not] to be truthy');
});
// Alias for common '[not] to be (a|an)' assertions
expect.addAssertion('<any> [not] to be an (object|array)', function (expect, subject) {
expect(subject, '[not] to be an', expect.alternations[0]);
});
expect.addAssertion('<any> [not] to be a (boolean|number|string|function|regexp|regex|regular expression)', function (expect, subject) {
expect(subject, '[not] to be a', expect.alternations[0]);
});
expect.addAssertion('<string> to be (the empty|an empty|a non-empty) string', function (expect, subject) {
expect(subject, expect.alternations[0] === 'a non-empty' ? 'not to be empty' : 'to be empty');
});
expect.addAssertion('<array-like> to be (the empty|an empty|a non-empty) array', function (expect, subject) {
expect(subject, expect.alternations[0] === 'a non-empty' ? 'not to be empty' : 'to be empty');
});
expect.addAssertion('<string> to match <regexp>', function (expect, subject, regexp) {
return expect.withError(function () {
var captures = subject.match(regexp);
expect(captures, 'to be truthy');
return captures;
}, function (e) {
e.label = 'should match';
expect.fail(e);
});
});
expect.addAssertion('<string> not to match <regexp>', function (expect, subject, regexp) {
return expect.withError(function () {
expect(regexp.test(subject), 'to be false');
}, function (e) {
expect.fail({
label: 'should not match',
diff: function (output) {
var lastIndex = 0;
function flushUntilIndex(i) {
if (i > lastIndex) {
output.text(subject.substring(lastIndex, i));
lastIndex = i;
}
}
subject.replace(new RegExp(regexp.source, 'g'), function ($0, index) {
flushUntilIndex(index);
lastIndex += $0.length;
output.removedHighlight($0);
});
flushUntilIndex(subject.length);
return {diff: output};
}
});
});
});
expect.addAssertion('<object|function> [not] to have own property <string>', function (expect, subject, key) {
expect(subject.hasOwnProperty(key), '[not] to be truthy');
return subject[key];
});
expect.addAssertion('<object|function> [not] to have property <string>', function (expect, subject, key) {
expect(subject[key], '[!not] to be undefined');
return subject[key];
});
expect.addAssertion('<object|function> to have [own] property <string> <any>', function (expect, subject, key, expectedPropertyValue) {
return expect(subject, 'to have [own] property', key).then(function (actualPropertyValue) {
expect.argsOutput = function () {
this.appendInspected(key).sp().error('with a value of').sp().appendInspected(expectedPropertyValue);
};
expect(actualPropertyValue, 'to equal', expectedPropertyValue);
return actualPropertyValue;
});
});
expect.addAssertion('<object|function> [not] to have [own] properties <array>', function (expect, subject, properties) {
properties.forEach(function (property) {
expect(subject, '[not] to have [own] property', property);
});
});
expect.addAssertion('<object|function> to have [own] properties <object>', function (expect, subject, properties) {
expect.withError(function () {
Object.keys(properties).forEach(function (property) {
var value = properties[property];
if (typeof value === 'undefined') {
expect(subject, 'not to have [own] property', property);
} else {
expect(subject, 'to have [own] property', property, value);
}
});
}, function (e) {
expect.fail({
diff: function (output, diff) {
var expected = extend({}, properties);
var actual = {};
var propertyNames = expect.findTypeOf(subject).getKeys(subject);
// Might put duplicates into propertyNames, but that does not matter:
for (var propertyName in subject) {
if (!subject.hasOwnProperty(propertyName)) {
propertyNames.push(propertyName);
}
}
propertyNames.forEach(function (propertyName) {
if ((!expect.flags.own || subject.hasOwnProperty(propertyName)) && !(propertyName in properties)) {
expected[propertyName] = subject[propertyName];
}
if ((!expect.flags.own || subject.hasOwnProperty(propertyName)) && !(propertyName in actual)) {
actual[propertyName] = subject[propertyName];
}
});
var result = diff(actual, expected);
result.diff = utils.wrapConstructorNameAroundOutput(result.diff, subject);
return result;
}
});
});
});
expect.addAssertion('<string|array-like> [not] to have length <number>', function (expect, subject, length) {
if (!expect.flags.not) {
expect.errorMode = 'nested';
}
expect(subject.length, '[not] to be', length);
});
expect.addAssertion('<string|array-like> [not] to be empty', function (expect, subject) {
expect(subject, '[not] to have length', 0);
});
expect.addAssertion('<string|array-like> to be non-empty', function (expect, subject) {
expect(subject, 'not to be empty');
});
expect.addAssertion('<object> to [not] [only] have keys <array>', function (expect, subject, keys) {
keys = isArray(keys) ?
keys :
Array.prototype.slice.call(arguments, 2);
var keysInSubject = {};
var subjectKeys = expect.findTypeOf(subject).getKeys(subject);
subjectKeys.forEach(function (key) {
keysInSubject[key] = true;
});
if (expect.flags.not && keys.length === 0) {
return;
}
var hasKeys = subject && keys.every(function (key) {
return keysInSubject[key];
});
if (expect.flags.only) {
expect(hasKeys, 'to be truthy');
expect(subjectKeys.length === keys.length, '[not] to be truthy');
} else {
expect(hasKeys, '[not] to be truthy');
}
});
expect.addAssertion('<object> not to have keys <array>', function (expect, subject, keys) {
expect(subject, 'to not have keys', keys);
});
expect.addAssertion('<object> not to have key <string>', function (expect, subject, value) {
expect(subject, 'to not have keys', [ value ]);
});
expect.addAssertion('<object> not to have keys <string+>', function (expect, subject, value) {
expect(subject, 'to not have keys', Array.prototype.slice.call(arguments, 2));
});
expect.addAssertion('<object> to [not] [only] have key <string>', function (expect, subject, value) {
expect(subject, 'to [not] [only] have keys', [ value ]);
});
expect.addAssertion('<object> to [not] [only] have keys <string+>', function (expect, subject) {
expect(subject, 'to [not] [only] have keys', Array.prototype.slice.call(arguments, 2));
});
expect.addAssertion('<string> [not] to contain <string+>', function (expect, subject) {
var args = Array.prototype.slice.call(arguments, 2);
args.forEach(function (arg) {
if (arg === '') {
throw new Error("The '" + expect.testDescription + "' assertion does not support the empty string");
}
});
expect.withError(function () {
args.forEach(function (arg) {
expect(subject.indexOf(arg) !== -1, '[not] to be truthy');
});
}, function (e) {
expect.fail({
diff: function (output) {
var lastIndex = 0;
function flushUntilIndex(i) {
if (i > lastIndex) {
output.text(subject.substring(lastIndex, i));
lastIndex = i;
}
}
if (expect.flags.not) {
subject.replace(new RegExp(args.map(function (arg) {
return utils.escapeRegExpMetaChars(arg);
}).join('|'), 'g'), function ($0, index) {
flushUntilIndex(index);
lastIndex += $0.length;
output.removedHighlight($0);
});
flushUntilIndex(subject.length);
} else {
var ranges = [];
args.forEach(function (arg) {
var needle = arg;
var partial = false;
while (needle.length > 1) {
var found = false;
lastIndex = -1;
var index;
do {
index = subject.indexOf(needle, lastIndex + 1);
if (index !== -1) {
found = true;
ranges.push({
startIndex: index,
endIndex: index + needle.length,
partial: partial
});
}
lastIndex = index;
} while (lastIndex !== -1);
if (found) {
break;
}
needle = arg.substr(0, needle.length - 1);
partial = true;
}
});
lastIndex = 0;
ranges.sort(function (a, b) {
return a.startIndex - b.startIndex;
}).forEach(function (range) {
flushUntilIndex(range.startIndex);
var firstUncoveredIndex = Math.max(range.startIndex, lastIndex);
if (range.endIndex > firstUncoveredIndex) {
if (range.partial) {
output.partialMatch(subject.substring(firstUncoveredIndex, range.endIndex));
} else {
output.match(subject.substring(firstUncoveredIndex, range.endIndex));
}
lastIndex = range.endIndex;
}
});
flushUntilIndex(subject.length);
}
return {diff: output};
}
});
});
});
expect.addAssertion('<array-like> [not] to contain <any+>', function (expect, subject) {
var args = Array.prototype.slice.call(arguments, 2);
expect.withError(function () {
args.forEach(function (arg) {
expect(subject && Array.prototype.some.call(subject, function (item) {
return expect.equal(item, arg);
}), '[not] to be truthy');
});
}, function (e) {
expect.fail({
diff: expect.flags.not && function (output, diff, inspect, equal) {
return diff(subject, Array.prototype.filter.call(subject, function (item) {
return !args.some(function (arg) {
return equal(item, arg);
});
}));
}
});
});
});
expect.addAssertion('<string> [not] to begin with <string>', function (expect, subject, value) {
if (value === '') {
throw new Error("The '" + expect.testDescription + "' assertion does not support a prefix of the empty string");
}
expect.withError(function () {
expect(subject.substr(0, value.length), '[not] to equal', value);
}, function (err) {
expect.fail({
diff: function (output) {
if (expect.flags.not) {
output.removedHighlight(value).text(subject.substr(value.length));
} else {
var i = 0;
while (subject[i] === value[i]) {
i += 1;
}
if (i === 0) {
// No common prefix, omit diff
return null;
} else {
output
.partialMatch(subject.substr(0, i))
.text(subject.substr(i));
}
}
return {diff: output};
}
});
});
});
expect.addAssertion('<string> [not] to end with <string>', function (expect, subject, value) {
if (value === '') {
throw new Error("The '" + expect.testDescription + "' assertion does not support a suffix of the empty string");
}
expect.withError(function () {
expect(subject.substr(-value.length), '[not] to equal', value);
}, function (err) {
expect.fail({
diff: function (output) {
if (expect.flags.not) {
output.text(subject.substr(0, subject.length - value.length)).removedHighlight(value);
} else {
var i = 0;
while (subject[subject.length - 1 - i] === value[value.length - 1 - i]) {
i += 1;
}
if (i === 0) {
// No common suffix, omit diff
return null;
}
output
.text(subject.substr(0, subject.length - i))
.partialMatch(subject.substr(subject.length - i, subject.length));
}
return {diff: output};
}
});
});
});
expect.addAssertion('<number> [not] to be finite', function (expect, subject) {
expect(isFinite(subject), '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be infinite', function (expect, subject) {
expect(!isNaN(subject) && !isFinite(subject), '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be within <number> <number>', function (expect, subject, start, finish) {
expect.argsOutput = function (output) {
output.appendInspected(start).text('..').appendInspected(finish);
};
expect(subject >= start && subject <= finish, '[not] to be truthy');
});
expect.addAssertion('<string> [not] to be within <string> <string>', function (expect, subject, start, finish) {
expect.argsOutput = function (output) {
output.appendInspected(start).text('..').appendInspected(finish);
};
expect(subject >= start && subject <= finish, '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be (less than|below) <number>', function (expect, subject, value) {
expect(subject < value, '[not] to be truthy');
});
expect.addAssertion('<string> [not] to be (less than|below) <string>', function (expect, subject, value) {
expect(subject < value, '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be (less than or equal to) <number>', function (expect, subject, value) {
expect(subject <= value, '[not] to be truthy');
});
expect.addAssertion('<string> [not] to be (less than or equal to) <string>', function (expect, subject, value) {
expect(subject <= value, '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be (greater than|above) <number>', function (expect, subject, value) {
expect(subject > value, '[not] to be truthy');
});
expect.addAssertion('<string> [not] to be (greater than|above) <string>', function (expect, subject, value) {
expect(subject > value, '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be greater than or equal to <number>', function (expect, subject, value) {
expect(subject >= value, '[not] to be truthy');
});
expect.addAssertion('<string> [not] to be greater than or equal to <string>', function (expect, subject, value) {
expect(subject >= value, '[not] to be truthy');
});
expect.addAssertion('<number> [not] to be positive', function (expect, subject) {
expect(subject, '[not] to be greater than', 0);
});
expect.addAssertion('<number> [not] to be negative', function (expect, subject) {
expect(subject, '[not] to be less than', 0);
});
expect.addAssertion('<any> to equal <any>', function (expect, subject, value) {
expect.withError(function () {
expect(expect.equal(value, subject), 'to be truthy');
}, function (e) {
expect.fail({
label: 'should equal',
diff: function (output, diff) {
return diff(subject, value);
}
});
});
});
expect.addAssertion('<any> not to equal <any>', function (expect, subject, value) {
expect(expect.equal(value, subject), 'to be falsy');
});
expect.addAssertion('<function> to error', function (expect, subject) {
return expect.promise(function () {
try {
return subject();
} catch (e) {
throw e;
}
}).then(function () {
return expect.promise(function () {
expect.fail(function (output) {
output.text('expected').sp();
output.appendInspect(subject).sp().text('to error');
});
});
}, function (error) {
return error;
});
});
expect.addAssertion('<function> to error [with] <any>', function (expect, subject, arg) {
return expect(subject, 'to error').then(function (error) {
expect.errorMode = 'nested';
if (error.isUnexpected && (typeof arg === 'string' || isRegExp(arg))) {
return expect(error, 'to have text message', arg);
} else {
return expect(error, 'to satisfy', arg);
}
});
});
expect.addAssertion('<function> not to error', function (expect, subject, arg) {
var threw = false;
return expect.promise(function () {
try {
return subject();
} catch (e) {
threw = true;
throw e;
}
}).caught(function (error) {
expect.errorMode = 'nested';
expect.fail(function (output) {
output.error(threw ? 'threw' : 'returned promise rejected with').error(': ')
.appendErrorMessage(error);
});
});
});
expect.addAssertion('<function> not to throw', function (expect, subject) {
var threw = false;
var error;
try {
subject();
} catch (e) {
error = e;
threw = true;
}
if (threw) {
expect.errorMode = 'nested';
expect.fail(function (output) {
output.error('threw: ').appendErrorMessage(error);
});
}
});
expect.addAssertion('<function> to (throw|throw error|throw exception)', function (expect, subject, arg) {
try {
subject();
} catch (e) {
return e;
}
expect.fail();
});
expect.addAssertion('<function> to (throw|throw error|throw exception) <any>', function (expect, subject, arg) {
return expect(subject, 'to throw').then(function (error) {
var isUnexpected = error && error._isUnexpected;
// in the presence of a matcher an error must have been thrown.
expect.errorMode = 'nested';
if (isUnexpected && (typeof arg === 'string' || isRegExp(arg))) {
return expect(error.getErrorMessage('text').toString(), 'to satisfy', arg);
} else {
return expect(error, 'to satisfy', arg);
}
});
});
expect.addAssertion('<function> to have arity <number>', function (expect, subject, value) {
expect(subject.length, 'to equal', value);
});
expect.addAssertion([
'<object> to have values satisfying <any+>',
'<object> to be (a map|a hash|an object) whose values satisfy <any+>'
], function (expect, subject, nextArg) {
expect.errorMode = 'nested';
expect(subject, 'not to equal', {});
expect.errorMode = 'bubble';
var keys = expect.subjectType.getKeys(subject);
var expected = Array.isArray(subject) ? [] : {};
keys.forEach(function (key, index) {
if (typeof nextArg === 'string') {
expected[key] = function (s) {
return expect.shift(s, 0);
};
} else if (typeof nextArg === 'function') {
expected[key] = function (s) {
return nextArg(s, index);
};
} else {
expected[key] = nextArg;
}
});
return expect.withError(function () {
return expect(subject, 'to satisfy', expected);
}, function (err) {
expect.fail({
message: function (output) {
output.append(expect.standardErrorMessage(output.clone(), { compact: true }));
},
diff: function (output) {
var diff = err.getDiff({ output: output });
diff.inline = true;
return diff;
}
});
});
});
expect.addAssertion([
'<array-like> to have items satisfying <any+>',
'<array-like> to be an array whose items satisfy <any+>'
], function (expect, subject) { // ...
var extraArgs = Array.prototype.slice.call(arguments, 2);
expect.errorMode = 'nested';
expect(subject, 'to be non-empty');
expect.errorMode = 'bubble';
return expect.withError(function () {
return expect.apply(expect, [subject, 'to have values satisfying'].concat(extraArgs));
}, function (err) {
expect.fail({
message: function (output) {
output.append(expect.standardErrorMessage(output.clone(), { compact: true }));
},
diff: function (output) {
var diff = err.getDiff({ output: output });
diff.inline = true;
return diff;
}
});
});
});
expect.addAssertion([
'<object> to have keys satisfying <any+>',
'<object> to be (a map|a hash|an object) whose (keys|properties) satisfy <any+>'
], function (expect, subject) {
var extraArgs = Array.prototype.slice.call(arguments, 2);
expect.errorMode = 'nested';
expect(subject, 'to be an object');
expect(subject, 'not to equal', {});
expect.errorMode = 'default';
var keys = expect.subjectType.getKeys(subject);
return expect.apply(expect, [keys, 'to have items satisfying'].concat(extraArgs));
});
expect.addAssertion('<object> to be canonical', function (expect, subject) {
var stack = [];
(function traverse(obj) {
var i;
for (i = 0 ; i < stack.length ; i += 1) {
if (stack[i] === obj) {
return;
}
}
if (obj && typeof obj === 'object') {
var keys = Object.keys(obj);
for (i = 0 ; i < keys.length - 1 ; i += 1) {
expect(keys[i], 'to be less than', keys[i + 1]);
}
stack.push(obj);
keys.forEach(function (key) {
traverse(obj[key]);
});
stack.pop();
}
}(subject));
});
expect.addAssertion('<Error> to have (ansi|html|text|) (message|diff) <any>', function (expect, subject, value) {
expect.errorMode = 'nested';
var format = expect.alternations[0] || 'text';
var useDiff = expect.alternations[1] === 'diff';
if (subject.isUnexpected) {
var subjectPen;
if (useDiff) {
var diffResult = subject.getDiff({ format: format });
if (diffResult && diffResult.diff) {
subjectPen = diffResult.diff;
} else {
expect.fail('The UnexpectedError instance does not have a diff');
}
} else {
subjectPen = subject.getErrorMessage({ format: format });
}
var valueType = expect.argTypes[0];
if (valueType.is('magicpen')) {
expect(subjectPen, 'to equal', value);
} else if (valueType.is('function') && !valueType.is('expect.it')) {
var expectedOutput = expect.createOutput(format);
var returnValue = value.call(expectedOutput, subjectPen.toString());
if (!expectedOutput.isEmpty()) {
// If the function didn't generate any expected output, assume that it ran assertions based on the serialized message
expect(subjectPen, 'to equal', expectedOutput);
}
return returnValue;
} else {
return expect(subjectPen.toString(), 'to satisfy', value);
}
} else {
if (useDiff) {
expect.fail('Cannot get the diff from a non-Unexpected error');
}
if (format !== 'text') {
expect.fail('Cannot get the ' + format + ' representation of non-Unexpected error');
} else {
return expect(subject.message, 'to satisfy', value);
}
}
});
expect.addAssertion('<Error> to [exhaustively] satisfy <Error>', function (expect, subject, value) {
expect(subject.constructor, 'to be', value.constructor);
expect(subject, 'to have properties', expect.argTypes[0].unwrap(value));
});
expect.addAssertion('<Error> to [exhaustively] satisfy <object>', function (expect, subject, value) {
var valueType = expect.argTypes[0];
var subjectKeys = expect.subjectType.getKeys(subject);
var valueKeys = valueType.getKeys(value);
var convertedSubject = {};
subjectKeys.concat(valueKeys).forEach(function (key) {
convertedSubject[key] = subject[key];
});
return expect(convertedSubject, 'to [exhaustively] satisfy', value);
});
expect.addAssertion('<Error> to [exhaustively] satisfy <regexp|string>', function (expect, subject, value) {
return expect(subject.message, 'to [exhaustively] satisfy', value);
});
expect.addAssertion('<Error> to [exhaustively] satisfy <any>', function (expect, subject, value) {
return expect(subject.message, 'to [exhaustively] satisfy', value);
});
expect.addAssertion('<binaryArray> to [exhaustively] satisfy <expect.it>', function (expect, subject, value) {
return expect.withError(function () {
return value(subject);
}, function (e) {
expect.fail({
diff: function (output, diff, inspect, equal) {
return {
diff: output.appendErrorMessage(e),
inline: false
};
}
});
});
});
expect.addAssertion('<any|Error> to [exhaustively] satisfy <function>', function (expect, subject, value) {
return expect.promise(function () {
return value(subject);
});
});
expect.addAssertion('<binaryArray> to [exhaustively] satisfy <binaryArray>', function (expect, subject, value) {
expect(subject, 'to equal', value);
});
if (typeof Buffer !== 'undefined') {
expect.addAssertion('<Buffer> [when] decoded as <string> <assertion?>', function (expect, subject, value) {
return expect.shift(subject.toString(value));
});
}
expect.addAssertion('<any> not to [exhaustively] satisfy [assertion] <any>', function (expect, subject, value) {
return expect.promise(function (resolve, reject) {
return expect.promise(function () {
return expect(subject, 'to [exhaustively] satisfy [assertion]', value);
}).then(function () {
try {
expect.fail();
} catch (e) {
reject(e);
}
}).caught(function (e) {
if (!e || !e._isUnexpected) {
reject(e);
} else {
resolve();
}
});
});
});
expect.addAssertion('<any> to [exhaustively] satisfy assertion <any>', function (expect, subject, value) {
expect.errorMode = 'bubble'; // to satisfy assertion 'to be a number' => to be a number
return expect(subject, 'to [exhaustively] satisfy', value);
});
expect.addAssertion('<any> to [exhaustively] satisfy assertion <assertion>', function (expect, subject) {
expect.errorMode = 'bubble'; // to satisfy assertion 'to be a number' => to be a number
return expect.shift();
});
expect.addAssertion('<any> to [exhaustively] satisfy [assertion] <expect.it>', function (expect, subject, value) {
return expect.withError(function () {
return value(subject);
}, function (e) {
expect.fail({
diff: function (output) {
return {
diff: output.appendErrorMessage(e),
inline: false
};
}
});
});
});
expect.addAssertion('<regexp> to [exhaustively] satisfy <regexp>', function (expect, subject, value) {
expect(subject, 'to equal', value);
});
expect.addAssertion('<string> to [exhaustively] satisfy <regexp>', function (expect, subject, value) {
expect.errorMode = 'bubble';
return expect(subject, 'to match', value);
});
expect.addAssertion('<function> to [exhaustively] satisfy <function>', function (expect, subject, value) {
expect.errorMode = 'bubble';
expect(subject, 'to equal', value);
});
expect.addAssertion('<binaryArray> to [exhaustively] satisfy <binaryArray>', function (expect, subject, value) {
expect.errorMode = 'bubble';
expect(subject, 'to equal', value);
});
expect.addAssertion('<any> to [exhaustively] satisfy <any>', function (expect, subject, value) {
expect.errorMode = 'bubble';
expect(subject, 'to equal', value);
});
expect.addAssertion('<object> to [exhaustively] satisfy <array-like|object>', function (expect, subject, value) {
var valueType = expect.argTypes[0];
var subjectType = expect.subjectType;
var commonType = expect.findCommonType(subject, value);
var bothAreArrayLike = commonType.is('array-like');
var promiseByKey = {};
var keys = valueType.getKeys(value);
keys.forEach(function (key, index) {
promiseByKey[key] = expect.promise(function () {
var valueKeyType = expect.findTypeOf(value[key]);
if (valueKeyType.is('function')) {
return value[key](subject[key]);
} else {
return expect(subject[key], 'to [exhaustively] satisfy', value[key]);
}
});
});
return expect.promise.all([
expect.promise(function () {
if (commonType.is('array-like') || expect.flags.exhaustively) {
expect(subject, 'to only have keys', keys);
}
}),
expect.promise.all(promiseByKey)
]).caught(function () {
return expect.promise.settle(promiseByKey).then(function () {
expect.fail({
diff: function (output, diff, inspect, equal) {
var result = {
diff: output,
inline: true
};
var keyIndex = {};
var subjectIsArrayLike = subjectType.is('array-like');
var subjectKeys = subjectType.getKeys(subject);
subjectKeys.concat(valueType.getKeys(value)).forEach(function (key) {
if (!(key in keyIndex)) {
keyIndex[key] = key;
}
});
var keys = Object.keys(keyIndex);
subjectType.prefix(output, subject);
output.nl().indentLines();
keys.forEach(function (key, index) {
output.i().block(function () {
var valueOutput;
var annotation = output.clone();
var conflicting;
if (promiseByKey[key] && promiseByKey[key].isRejected()) {
conflicting = promiseByKey[key].reason();
}
var missingArrayIndex = subjectType.is('array-like') && !(key in subject);
var arrayItemOutOfRange = bothAreArrayLike && (index >= subject.length || index >= value.length);
var missingKey = false;
var isInlineDiff = true;
output.omitSubject = subject[key];
if (!(key in value)) {
if (commonType.is('array-like') || expect.flags.exhaustively) {
annotation.error('should be removed');
} else {
conflicting = null;
}
} else if (conflicting || arrayItemOutOfRange || missingArrayIndex) {
var keyDiff = conflicting && conflicting.getDiff({ output: output });
isInlineDiff = !keyDiff || keyDiff.inline ;
missingKey = arrayItemOutOfRange || missingArrayIndex;
if (keyDiff && keyDiff.diff && keyDiff.inline) {
valueOutput = keyDiff.diff;
} else if (typeof value[key] === 'function') {
isInlineDiff = false;
annotation.appendErrorMessage(conflicting);
} else if (!keyDiff || (keyDiff && !keyDiff.inline)) {
annotation.error((conflicting && conflicting.getLabel()) || 'should satisfy').sp()
.block(inspect(value[key]));
if (keyDiff) {
annotation.nl().append(keyDiff.diff);
}
} else {
valueOutput = keyDiff.diff;
}
}
if (!valueOutput) {
if ((bothAreArrayLike && key >= subject.length) || missingArrayIndex) {
valueOutput = output.clone();
} else {
valueOutput = inspect(subject[key]);
}
}
if (!subjectIsArrayLike) {
this.key(key).text(':');
}
var omitDelimiter =
missingArrayIndex ||
subjectType.is('array-like') ?
index >= subjectKeys.length - 1 :
index === keys.length - 1;
if (!omitDelimiter) {
valueOutput.amend(subjectType.delimiter(output.clone(), index, keys.length));
}
if (!subjectIsArrayLike) {
if (valueOutput.isBlock() && valueOutput.isMultiline()) {
this.indentLines();
this.nl().i();
} else {
this.sp();
}
}
var annotationOnNextLine = !isInlineDiff &&
output.preferredWidth < this.size().width + valueOutput.size().width + annotation.size().width;
if (!annotation.isEmpty()) {
if (!valueOutput.isEmpty()) {
if (annotationOnNextLine) {
valueOutput.nl();
} else {
valueOutput.sp();
}
}
valueOutput.annotationBlock(function () {
if (missingKey) {
this.error('missing: ').block(annotation);
} else {
this.append(annotation);
}
});
}
if (isInlineDiff) {
this.append(valueOutput);
} else {
this.block(valueOutput);
}
}).nl();
});
output.outdentLines();
subjectType.suffix(output, subject);
return result;
}
});
});
});
});
function wrapDiffWithTypePrefixAndSuffix(e, type, subject) {
var createDiff = e.getDiffMethod();
if (createDiff) {
return function (output) { // ...
type.prefix.call(type, output, subject);
var result = createDiff.apply(this, arguments);
type.suffix.call(type, output, subject);
return result;
};
}
}
expect.addAssertion('<wrapperObject> to [exhaustively] satisfy <wrapperObject>', function (expect, subject, value) {
var type = expect.findCommonType(subject, value);
expect(type.is('wrapperObject'), 'to be truthy');
return expect.withError(function () {
return expect(type.unwrap(subject), 'to [exhaustively] satisfy', type.unwrap(value));
}, function (e) {
expect.fail({
label: e.getLabel(),
diff: wrapDiffWithTypePrefixAndSuffix(e, type, subject)
});
});
});
expect.addAssertion('<wrapperObject> to [exhaustively] satisfy <any>', function (expect, subject, value) {
var subjectType = expect.subjectType;
return expect.withError(function () {
return expect(subjectType.unwrap(subject), 'to [exhaustively] satisfy', value);
}, function (e) {
expect.fail({
label: e.getLabel(),
diff: wrapDiffWithTypePrefixAndSuffix(e, subjectType, subject)
});
});
});
expect.addAssertion('<function> [when] called with <array-like> <assertion?>', function (expect, subject, args) { // ...
expect.errorMode = 'nested';
return expect.shift(subject.apply(subject, args));
});
function instantiate(Constructor, args) {
function ProxyConstructor() {
return Constructor.apply(this, args);
}
ProxyConstructor.prototype = Constructor.prototype;
return new ProxyConstructor();
}
expect.addAssertion([
'<array-like> [when] passed as parameters to [async] <function> <assertion?>',
'<array-like> [when] passed as parameters to [constructor] <function> <assertion?>'
], function (expect, subject, fn) { // ...
expect.errorMode = 'nested';
var args = subject;
if (expect.flags.async) {
return expect.promise(function (run) {
args = [].concat(args);
args.push(run(function (err, result) {
expect(err, 'to be falsy');
return expect.shift(result);
}));
fn.apply(null, args);
});
} else {
return expect.shift(expect.flags.constructor ? instantiate(fn, args) : fn.apply(fn, args));
}
});
expect.addAssertion([
'<any> [when] passed as parameter to [async] <function> <assertion?>',
'<any> [when] passed as parameter to [constructor] <function> <assertion?>'
], function (expect, subject, fn) { // ...
expect.errorMode = 'nested';
var args = [subject];
if (expect.flags.async) {
return expect.promise(function (run) {
args = [].concat(args);
args.push(run(function (err, result) {
expect(err, 'to be falsy');
return expect.shift(result);
}));
fn.apply(null, args);
});
} else {
return expect.shift(expect.flags.constructor ? instantiate(fn, args) : fn.apply(fn, args));
}
});
expect.addAssertion('<Promise> to be rejected', function (expect, subject) {
expect.errorMode = 'nested';
return subject.then(function (obj) {
expect.fail(function (output) {
output.appendInspected(subject).sp().text('unexpectedly fulfilled');
if (typeof obj !== 'undefined') {
output.sp().text('with').sp().appendInspected(obj);
}
});
}, function (err) {
return err;
});
});
expect.addAssertion('<Promise> to be rejected with <any>', function (expect, subject, value) {
expect.errorMode = 'nested';
return expect(subject, 'to be rejected').then(function (err) {
if (err && err._isUnexpected && (typeof value === 'string' || isRegExp(value))) {
return expect(err, 'to have text message', value);
} else {
return expect(err, 'to satisfy', value);
}
});
});
expect.addAssertion('<Promise> to be fulfilled', function (expect, subject) {
expect.errorMode = 'nested';
return subject.then(function (fulfillmentValue) {
return fulfillmentValue;
}, function (err) {
expect.fail(function (output) {
output.appendInspected(subject).sp().text('unexpectedly rejected');
if (typeof err !== 'undefined') {
output.sp().text('with').sp().appendInspected(err);
}
});
});
});
expect.addAssertion('<Promise> to be fulfilled with <any>', function (expect, subject, value) {
expect.errorMode = 'nested';
return expect(subject, 'to be fulfilled').then(function (fulfillmentValue) {
return expect(fulfillmentValue, 'to satisfy', value);
});
});
expect.addAssertion('<Promise> when rejected <assertion>', function (expect, subject, nextAssertion) {
expect.errorMode = 'nested';
return subject.then(function (ful