unexpected
Advanced tools
Comparing version 5.0.0-beta2 to 5.0.0-beta20
@@ -14,20 +14,16 @@ # Development | ||
* *src/unexpected-license.js*:<br> | ||
The license file | ||
* *src/unexpected-namespace.js*:<br> | ||
The internal namespace definition | ||
* *src/unexpected-es4-compatible.js*:<br> | ||
ES5 functions shimmed to work in a ES4 compatible environment | ||
* *src/unexpected-utils.js*:<br> | ||
* *lib/index.js*:<br> | ||
The entry point for the library. | ||
* *lib/Unexpected.js*:<br> | ||
The core of the library. | ||
* *lib/Assertion.js*:<br> | ||
The class that assertions are instantiated from. | ||
* *lib/utils.js*:<br> | ||
Utility functions | ||
* *src/unexpected-equal.js*:<br> | ||
Deep-equality | ||
* *src/unexpected-inspect.js*:<br> | ||
Object inspect | ||
* *src/unexpected-core.js*:<br> | ||
The core part of Unexpected | ||
* *src/unexpected-assertions.js*:<br> | ||
The core assertions provided by Unexpected | ||
* *src/unexpected-module.js*:<br> | ||
The module definitions | ||
* *lib/styles.js*:<br> | ||
MagicPen styles for controling the output. | ||
* *lib/types.js*:<br> | ||
Type definitions for every type Unexpected understands. | ||
* *lib/assertions.js*:<br> | ||
All assertions that is included in the core library. | ||
@@ -38,3 +34,3 @@ ## Running tests in the console | ||
make test | ||
make test-production | ||
make test-phantomjs | ||
``` | ||
@@ -41,0 +37,0 @@ |
function Assertion(expect, subject, testDescription, flags, alternations, args) { | ||
this.expect = expect; | ||
this.obj = subject; // deprecated | ||
this.equal = expect.equal.bind(expect); // deprecated | ||
this.eql = this.equal; // deprecated | ||
this.inspect = expect.inspect.bind(expect); // deprecated | ||
this.subject = subject; | ||
@@ -27,5 +23,12 @@ this.testDescription = testDescription; | ||
if (0 < index) { | ||
argsOutput.text(', '); | ||
if (index !== this.assertionIndex && index - 1 !== this.assertionIndex) { | ||
argsOutput.text(','); | ||
} | ||
argsOutput.text(' '); | ||
} | ||
argsOutput.append(expect.inspect(arg)); | ||
if (index === this.assertionIndex) { | ||
argsOutput.text(arg); | ||
} else { | ||
argsOutput.append(expect.inspect(arg)); | ||
} | ||
}, this); | ||
@@ -68,2 +71,7 @@ } | ||
Assertion.prototype.shift = function (expect, subject, assertionIndex) { | ||
this.assertionIndex = assertionIndex; | ||
expect.apply(expect, [subject].concat(this.args.slice(assertionIndex))); | ||
}; | ||
Assertion.prototype.throwStandardError = function () { | ||
@@ -76,10 +84,2 @@ var err = new Error(); | ||
Assertion.prototype.assert = function (condition) { | ||
var not = !!this.flags.not; | ||
condition = !!condition; | ||
if (condition === not) { | ||
this.throwStandardError(); | ||
} | ||
}; | ||
module.exports = Assertion; |
@@ -8,3 +8,7 @@ var utils = require('./utils'); | ||
expect.addAssertion('[not] to be (ok|truthy)', function (expect, subject) { | ||
this.assert(subject); | ||
var not = !!this.flags.not; | ||
var condition = !!subject; | ||
if (condition === not) { | ||
expect.fail(); | ||
} | ||
}); | ||
@@ -66,8 +70,5 @@ | ||
if ('string' === typeof type) { | ||
// typeof with support for 'array' | ||
expect('array' === type ? isArray(subject) : | ||
'object' === type ? 'object' === typeof subject && null !== subject : | ||
/^reg(?:exp?|ular expression)$/.test(type) ? isRegExp(subject) : | ||
type === typeof subject, | ||
'[not] to be true'); | ||
var subjectType = expect.findTypeOf(subject); | ||
type = /^reg(?:exp?|ular expression)$/.test(type) ? 'regexp' : type; | ||
expect(subjectType.is(type), '[not] to be true'); | ||
} else { | ||
@@ -85,3 +86,3 @@ expect(subject instanceof type, '[not] to be true'); | ||
expect.addAssertion(['string', 'array', 'object'], 'to be (the empty|an empty|a non-empty) (string|array|object)', function (expect, subject) { | ||
expect.addAssertion(['string', 'arrayLike'], 'to be (the empty|an empty|a non-empty) (string|array)', function (expect, subject) { | ||
expect(subject, 'to be a', this.alternations[1]); | ||
@@ -92,23 +93,26 @@ expect(subject, this.alternations[0] === 'a non-empty' ? 'not to be empty' : 'to be empty'); | ||
expect.addAssertion('[not] to match', function (expect, subject, regexp) { | ||
subject = String(subject); | ||
try { | ||
expect(String(subject).match(regexp), '[not] to be truthy'); | ||
} catch (e) { | ||
if (e._isUnexpected && this.flags.not) { | ||
e.createDiff = function () { | ||
var output = expect.output.clone(); | ||
var lastIndex = 0; | ||
function flushUntilIndex(i) { | ||
if (i > lastIndex) { | ||
output.text(subject.substring(lastIndex, i)); | ||
lastIndex = i; | ||
if (e._isUnexpected) { | ||
e.label = 'should match'; | ||
if (this.flags.not) { | ||
e.createDiff = 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.diffRemovedHighlight($0); | ||
}); | ||
flushUntilIndex(subject.length); | ||
return {diff: output}; | ||
}; | ||
subject.replace(new RegExp(regexp.source, 'g'), function ($0, index) { | ||
flushUntilIndex(index); | ||
lastIndex += $0.length; | ||
output.diffRemovedHighlight($0); | ||
}); | ||
flushUntilIndex(subject.length); | ||
return {diff: output}; | ||
}; | ||
} | ||
} | ||
@@ -155,3 +159,3 @@ expect.fail(e); | ||
if (e._isUnexpected) { | ||
e.createDiff = function () { | ||
e.createDiff = function (output, diff) { | ||
var expected = extend({}, properties); | ||
@@ -167,3 +171,3 @@ var actual = {}; | ||
} | ||
return expect.diff(actual, expected); | ||
return diff(actual, expected); | ||
}; | ||
@@ -198,3 +202,3 @@ } | ||
expect.addAssertion(['string', 'object'], 'to be non-empty', function (expect, subject) { | ||
expect.addAssertion(['string', 'arrayLike'], 'to be non-empty', function (expect, subject) { | ||
expect(subject, 'not to be empty'); | ||
@@ -219,52 +223,50 @@ }); | ||
expect.addAssertion(['string', 'array'], '[not] to contain', function (expect, subject) { | ||
expect.addAssertion('string', '[not] to contain', function (expect, subject) { | ||
var args = Array.prototype.slice.call(arguments, 2); | ||
try { | ||
args.forEach(function (arg) { | ||
expect(subject.indexOf(arg) !== -1, '[not] to be truthy'); | ||
}); | ||
} catch (e) { | ||
if (e._isUnexpected && this.flags.not) { | ||
e.createDiff = function (output) { | ||
var lastIndex = 0; | ||
function flushUntilIndex(i) { | ||
if (i > lastIndex) { | ||
output.text(subject.substring(lastIndex, i)); | ||
lastIndex = i; | ||
} | ||
} | ||
subject.replace(new RegExp(args.map(function (arg) { | ||
return utils.escapeRegExpMetaChars(String(arg)); | ||
}).join('|'), 'g'), function ($0, index) { | ||
flushUntilIndex(index); | ||
lastIndex += $0.length; | ||
output.diffRemovedHighlight($0); | ||
}); | ||
flushUntilIndex(subject.length); | ||
return {diff: output}; | ||
}; | ||
} | ||
expect.fail(e); | ||
} | ||
}); | ||
if ('string' === typeof subject) { | ||
try { | ||
args.forEach(function (arg) { | ||
expect(subject.indexOf(arg) !== -1, '[not] to be truthy'); | ||
}); | ||
} catch (e) { | ||
if (e._isUnexpected && this.flags.not) { | ||
e.createDiff = function () { | ||
var output = expect.output.clone(); | ||
var lastIndex = 0; | ||
function flushUntilIndex(i) { | ||
if (i > lastIndex) { | ||
output.text(subject.substring(lastIndex, i)); | ||
lastIndex = i; | ||
} | ||
} | ||
subject.replace(new RegExp(args.map(function (arg) { | ||
return utils.escapeRegExpMetaChars(String(arg)); | ||
}).join('|'), 'g'), function ($0, index) { | ||
flushUntilIndex(index); | ||
lastIndex += $0.length; | ||
output.diffRemovedHighlight($0); | ||
expect.addAssertion('arrayLike', '[not] to contain', function (expect, subject) { | ||
var args = Array.prototype.slice.call(arguments, 2); | ||
try { | ||
args.forEach(function (arg) { | ||
expect(subject && Array.prototype.some.call(subject, function (item) { return expect.equal(item, arg); }), '[not] to be truthy'); | ||
}); | ||
} catch (e) { | ||
if (e._isUnexpected && this.flags.not) { | ||
e.createDiff = function (output, diff, inspect, equal) { | ||
return diff(subject, Array.prototype.filter.call(subject, function (item) { | ||
return !args.some(function (arg) { | ||
return equal(item, arg); | ||
}); | ||
flushUntilIndex(subject.length); | ||
return {diff: output}; | ||
}; | ||
} | ||
expect.fail(e); | ||
})); | ||
}; | ||
} | ||
} else { | ||
// array | ||
try { | ||
args.forEach(function (arg) { | ||
expect(subject && subject.some(function (item) { return expect.equal(item, arg); }), '[not] to be truthy'); | ||
}); | ||
} catch (e) { | ||
if (e._isUnexpected && this.flags.not) { | ||
e.createDiff = function () { | ||
return expect.diff(subject, subject.filter(function (item) { | ||
return !args.some(function (arg) { | ||
return expect.equal(item, arg); | ||
}); | ||
})); | ||
}; | ||
} | ||
expect.fail(e); | ||
} | ||
expect.fail(e); | ||
} | ||
@@ -314,6 +316,9 @@ }); | ||
} catch (e) { | ||
if (!this.flags.not && e._isUnexpected) { | ||
e.createDiff = function () { | ||
return expect.diff(subject, value); | ||
}; | ||
if (e._isUnexpected) { | ||
e.label = 'should equal'; | ||
if (!this.flags.not) { | ||
e.createDiff = function (output, diff) { | ||
return diff(subject, value); | ||
}; | ||
} | ||
} | ||
@@ -367,2 +372,6 @@ expect.fail(e); | ||
expect.addAssertion('function', 'to have arity', function (expect, subject, value) { | ||
expect(subject.length, 'to equal', value); | ||
}); | ||
expect.addAssertion('object', 'to be (a|an) [non-empty] (map|hash|object) whose values satisfy', function (expect, subject) { | ||
@@ -376,3 +385,3 @@ var extraArgs = Array.prototype.slice.call(arguments, 2); | ||
if (this.flags['non-empty']) { | ||
expect(subject, 'to be non-empty'); | ||
expect(subject, 'not to equal', {}); | ||
} | ||
@@ -382,3 +391,3 @@ this.errorMode = 'bubble'; | ||
var errors = {}; | ||
Object.keys(subject).forEach(function (key, index) { | ||
expect.findTypeOf(subject).getKeys(subject).forEach(function (key, index) { | ||
try { | ||
@@ -393,3 +402,3 @@ if (typeof extraArgs[0] === 'function') { | ||
} | ||
}); | ||
}, this); | ||
@@ -427,3 +436,3 @@ var errorKeys = Object.keys(errors); | ||
expect.addAssertion('array', 'to be (a|an) [non-empty] array whose items satisfy', function (expect, subject) { // ... | ||
expect.addAssertion('arrayLike', 'to be (a|an) [non-empty] array whose items satisfy', function (expect, subject) { // ... | ||
var extraArgs = Array.prototype.slice.call(arguments, 2); | ||
@@ -434,3 +443,2 @@ if (extraArgs.length === 0) { | ||
this.errorMode = 'nested'; | ||
expect(subject, 'to be an array'); | ||
if (this.flags['non-empty']) { | ||
@@ -443,3 +451,3 @@ expect(subject, 'to be non-empty'); | ||
expect.addAssertion('array', 'to be (a|an) [non-empty] array of (strings|numbers|booleans|arrays|objects|functions|regexps|regexes|regular expressions)', function (expect, subject) { | ||
expect.addAssertion('arrayLike', 'to be (a|an) [non-empty] array of (strings|numbers|booleans|arrays|objects|functions|regexps|regexes|regular expressions)', function (expect, subject) { | ||
if (this.flags['non-empty']) { | ||
@@ -460,3 +468,3 @@ expect(subject, 'to be non-empty'); | ||
if (this.flags['non-empty']) { | ||
expect(subject, 'to be non-empty'); | ||
expect(subject, 'not to equal', {}); | ||
} | ||
@@ -527,2 +535,21 @@ this.errorMode = 'bubble'; | ||
expect.addAssertion('Error', 'to [exhaustively] satisfy [assertion]', function (expect, subject, value) { | ||
var valueType = expect.findTypeOf(value); | ||
if (valueType.is('Error')) { | ||
expect(subject, 'to equal', value); | ||
} else if (valueType.is('object')) { | ||
var keys = valueType.getKeys(value); | ||
keys.forEach(function (key) { | ||
expect(subject[key], 'to [exhaustively] satisfy', value[key]); | ||
}); | ||
if (this.flags.exhaustively) { | ||
expect(utils.getKeysOfDefinedProperties(subject), 'to have length', keys.filter(function (key) { | ||
return typeof value[key] !== 'undefined'; | ||
}).length); | ||
} | ||
} else { | ||
expect(subject.message, 'to [exhaustively] satisfy', value); | ||
} | ||
}); | ||
expect.addAssertion('[not] to [exhaustively] satisfy [assertion]', function (expect, subject, value) { | ||
@@ -542,4 +569,17 @@ if (this.flags.not) { | ||
expect.apply(expect, Array.prototype.slice.call(arguments, 1)); | ||
} else if (value && value._expectIt) { | ||
try { | ||
value(subject); | ||
} catch (e) { | ||
if (e._isUnexpected) { | ||
e.createDiff = function (output, diff, inspect, equal) { | ||
return { | ||
diff: output.append(e.output), | ||
inline: false | ||
}; | ||
}; | ||
} | ||
expect.fail(e); | ||
} | ||
} else if (typeof value === 'function') { | ||
// FIXME: If expect.fn, it should be possible to produce a better error message | ||
value(subject); | ||
@@ -550,9 +590,98 @@ } else if (isRegExp(value)) { | ||
var type = expect.findTypeOf(subject, value); | ||
if (type.name === 'object' || type.name === 'array') { | ||
expect(subject, 'to be an object'); | ||
Object.keys(value).forEach(function (key) { | ||
expect(subject[key], 'to [exhaustively] satisfy', value[key]); | ||
}); | ||
if (this.flags.exhaustively) { | ||
expect(subject, 'to only have keys', Object.keys(value)); | ||
if (type.is('arrayLike') || type.is('object')) { | ||
try { | ||
expect(subject, 'to be an object'); | ||
var keys = type.getKeys(value); | ||
keys.forEach(function (key) { | ||
expect(subject[key], 'to [exhaustively] satisfy', value[key]); | ||
}); | ||
if (this.flags.exhaustively) { | ||
expect(subject, 'to only have keys', keys); | ||
} | ||
} catch (e) { | ||
if (e._isUnexpected) { | ||
var flags = this.flags; | ||
e.createDiff = function (output, diff, inspect, equal) { | ||
var result = { | ||
diff: output, | ||
inline: true | ||
}; | ||
var keyIndex = {}; | ||
Object.keys(subject).concat(Object.keys(value)).forEach(function (key) { | ||
if (!(key in result)) { | ||
keyIndex[key] = key; | ||
} | ||
}); | ||
var keys = Object.keys(keyIndex); | ||
output.text('{').nl().indentLines(); | ||
keys.forEach(function (key, index) { | ||
output.i().block(function () { | ||
var valueOutput; | ||
var annotation = output.clone(); | ||
var conflicting; | ||
try { | ||
expect(subject[key], 'to [exhaustively] satisfy', value[key]); | ||
} catch (e) { | ||
if (!e._isUnexpected) { | ||
expect.fail(e); | ||
} | ||
conflicting = e; | ||
} | ||
var isInlineDiff = false; | ||
if (conflicting) { | ||
if (!(key in value)) { | ||
if (flags.exhaustively) { | ||
annotation.error('should be removed'); | ||
} | ||
} else { | ||
var keyDiff = conflicting.createDiff && conflicting.createDiff(output.clone(), diff, inspect, equal); | ||
if (value[key] && value[key]._expectIt) { | ||
annotation.append(keyDiff.diff); | ||
} else if (!keyDiff || (keyDiff && !keyDiff.inline)) { | ||
annotation.error(conflicting.label || 'should satisfy').sp() | ||
.block(inspect(value[key])); | ||
if (keyDiff) { | ||
annotation.nl().append(keyDiff.diff); | ||
} | ||
} else { | ||
isInlineDiff = true; | ||
valueOutput = keyDiff.diff; | ||
} | ||
} | ||
} | ||
var last = index === keys.length - 1; | ||
if (!valueOutput) { | ||
valueOutput = inspect(subject[key], conflicting ? Infinity : 1); | ||
} | ||
if (/^[a-z\$\_][a-z0-9\$\_]*$/i.test(key)) { | ||
this.key(key); | ||
} else { | ||
this.append(inspect(key)); | ||
} | ||
this.text(':').sp(); | ||
valueOutput.text(last ? '' : ','); | ||
if (isInlineDiff) { | ||
this.append(valueOutput); | ||
} else { | ||
this.block(valueOutput); | ||
} | ||
if (!annotation.isEmpty()) { | ||
this.sp().annotationBlock(annotation); | ||
} | ||
}).nl(); | ||
}); | ||
output.outdentLines().text('}'); | ||
return result; | ||
}; | ||
} | ||
expect.fail(e); | ||
} | ||
@@ -564,2 +693,46 @@ } else { | ||
}); | ||
function wrapDiffWithTypePrefixAndSuffix(e, type) { | ||
if (e._isUnexpected) { | ||
var createDiff = e.createDiff; | ||
e.createDiff = function (output) { // ... | ||
type.prefix.call(e, output); | ||
var result = createDiff.apply(this, arguments); | ||
type.suffix.call(e, output); | ||
return result; | ||
}; | ||
} | ||
return e; | ||
} | ||
expect.addAssertion('wrapperObject', 'to [exhaustively] satisfy', function (expect, subject, value) { | ||
var valueType = expect.findTypeOf(value); | ||
if (valueType.is('wrapperObject')) { | ||
var type = expect.findTypeOf(subject, value); | ||
expect(type.is('wrapperObject'), 'to be true'); | ||
try { | ||
expect(type.unwrap(subject), 'to [exhaustively] satisfy', type.unwrap(value)); | ||
} catch (e) { | ||
expect.fail(wrapDiffWithTypePrefixAndSuffix(e, type)); | ||
} | ||
} else { | ||
var subjectType = expect.findTypeOf(subject); | ||
expect(subjectType.is('wrapperObject'), 'to be true'); | ||
try { | ||
expect(subjectType.unwrap(subject), 'to [exhaustively] satisfy', value); | ||
} catch (e) { | ||
e.label = 'should satisfy'; | ||
expect.fail(wrapDiffWithTypePrefixAndSuffix(e, subjectType)); | ||
} | ||
} | ||
}); | ||
expect.addAssertion('function', 'when called with', function (expect, subject, args) { // ... | ||
this.shift(expect, subject.apply(subject, args), 1); | ||
}); | ||
expect.addAssertion('array', 'when passed as parameters to', function (expect, subject, fn) { // ... | ||
this.shift(expect, fn.apply(fn, subject), 1); | ||
}); | ||
}; |
var Unexpected = require('./Unexpected'); | ||
module.exports = Unexpected.create() | ||
.installPlugin(require('./colors')) | ||
.installPlugin(require('./styles')) | ||
.installPlugin(require('./types')) | ||
.installPlugin(require('./assertions')); |
259
lib/types.js
@@ -6,5 +6,48 @@ var utils = require('./utils'); | ||
var arrayDiff = require('arraydiff'); | ||
var leven = require('leven'); | ||
module.exports = function (expect) { | ||
expect.addType({ | ||
name: 'wrapperObject', | ||
identify: false, | ||
equal: function (a, b, equal) { | ||
return a === b || equal(this.unwrap(a), this.unwrap(b)); | ||
}, | ||
inspect: function (value, depth, output, inspect) { | ||
output.append(this.prefix(output.clone())); | ||
output.append(inspect(this.unwrap(value))); | ||
return output.append(this.suffix(output.clone())); | ||
}, | ||
diff: function (actual, expected, output, diff, inspect) { | ||
actual = this.unwrap(actual); | ||
expected = this.unwrap(expected); | ||
var comparison = diff(actual, expected); | ||
var prefixOutput = this.prefix(output.clone()); | ||
var suffixOutput = this.suffix(output.clone()); | ||
if (comparison && comparison.inline) { | ||
return { | ||
inline: true, | ||
diff: output.append(prefixOutput).append(comparison.diff).append(suffixOutput) | ||
}; | ||
} else { | ||
return { | ||
inline: true, | ||
diff: output.append(prefixOutput).nl() | ||
.indentLines() | ||
.i().block(function () { | ||
this.append(inspect(actual)).sp().annotationBlock(function () { | ||
this.error('should be ').block(inspect(expected)); | ||
if (comparison) { | ||
this.nl().append(comparison.diff); | ||
} | ||
}); | ||
}).nl() | ||
.outdentLines() | ||
.append(suffixOutput) | ||
}; | ||
} | ||
} | ||
}); | ||
expect.addType({ | ||
name: 'object', | ||
@@ -14,2 +57,3 @@ identify: function (obj) { | ||
}, | ||
getKeys: Object.keys, | ||
equal: function (a, b, equal) { | ||
@@ -72,3 +116,9 @@ if (a === b) { | ||
if (hasGetter || !hasSetter) { | ||
propertyOutput.sp().append(inspect(obj[key])); | ||
var value = obj[key]; | ||
var inspectedValue = inspect(value); | ||
if (value && value._expectIt) { | ||
propertyOutput.sp().block(inspectedValue); | ||
} else { | ||
propertyOutput.sp().append(inspectedValue); | ||
} | ||
} | ||
@@ -161,3 +211,3 @@ | ||
if (!keyDiff || (keyDiff && !keyDiff.inline)) { | ||
annotation.error('should be: ') | ||
annotation.error('should be ') | ||
.block(inspect(expected[key])); | ||
@@ -192,3 +242,5 @@ | ||
} | ||
this.block(annotation.prependLinesWith('error', ' // ')); | ||
if (!annotation.isEmpty()) { | ||
this.sp().annotationBlock(annotation); | ||
} | ||
}).nl(); | ||
@@ -212,3 +264,3 @@ }); | ||
if (typeA === 'string') { | ||
return utils.levenshteinDistance(a, b) < a.length / 2; | ||
return leven(a, b) < a.length / 2; | ||
} | ||
@@ -237,24 +289,38 @@ | ||
expect.addType({ | ||
name: 'array', | ||
name: 'arrayLike', | ||
base: 'object', | ||
identify: function (arr) { | ||
return utils.isArray(arr) || utils.isArguments(arr); | ||
identify: function (obj) { | ||
return obj && typeof obj === 'object' && typeof obj.length === 'number'; | ||
}, | ||
getKeys: function (obj) { | ||
var keys = new Array(obj.length); | ||
for (var i = 0 ; i < obj.length ; i += 1) { | ||
keys[i] = i; | ||
} | ||
return keys; | ||
}, | ||
equal: function (a, b, equal) { | ||
return a === b || (a.length === b.length && a.every(function (v, index) { | ||
return a === b || (a.constructor === b.constructor && a.length === b.length && Array.prototype.every.call(a, function (v, index) { | ||
return equal(v, b[index]); | ||
})); | ||
}, | ||
prefix: function (output) { | ||
return output.text('['); | ||
}, | ||
suffix: function (output) { | ||
return output.text(']'); | ||
}, | ||
inspect: function (arr, depth, output, inspect) { | ||
var prefixOutput = this.prefix(output.clone(), arr); | ||
var suffixOutput = this.suffix(output.clone(), arr); | ||
if (arr.length === 0) { | ||
return output.text('[]'); | ||
return output.append(prefixOutput).append(suffixOutput); | ||
} | ||
if (depth === 1) { | ||
return output.text('[...]'); | ||
return output.append(prefixOutput).text('...').append(suffixOutput); | ||
} | ||
if (utils.isArguments(arr)) { | ||
if (!Array.isArray(arr)) { | ||
arr = Array.prototype.slice.call(arr); | ||
@@ -282,3 +348,3 @@ } | ||
if (multipleLines) { | ||
output.text('[').nl().indentLines(); | ||
output.append(prefixOutput).nl().indentLines(); | ||
@@ -289,5 +355,5 @@ inspectedItems.forEach(function (inspectedItem, index) { | ||
output.outdentLines().text(']'); | ||
output.outdentLines().append(suffixOutput); | ||
} else { | ||
output.text('[ '); | ||
output.append(prefixOutput).sp(); | ||
inspectedItems.forEach(function (inspectedItem, index) { | ||
@@ -300,3 +366,3 @@ output.append(inspectedItem); | ||
}); | ||
output.text(' ]'); | ||
output.sp().append(suffixOutput); | ||
} | ||
@@ -311,7 +377,10 @@ return output; | ||
if (utils.isArguments(actual)) { | ||
if (actual.constructor !== expected.constructor) { | ||
return this.baseType.diff(actual, expected, output, diff, inspect, equal); | ||
} | ||
if (!Array.isArray(actual)) { | ||
actual = Array.prototype.slice.call(actual); | ||
} | ||
if (utils.isArguments(expected)) { | ||
if (!Array.isArray(expected)) { | ||
expected = Array.prototype.slice.call(expected); | ||
@@ -355,8 +424,12 @@ } | ||
removedItems = 0; | ||
actual.forEach(function (_, index) { | ||
removedItems += removesByIndex[index] || 0; | ||
removeTable[index] = removedItems; | ||
}); | ||
function updateRemoveTable() { | ||
removedItems = 0; | ||
actual.forEach(function (_, index) { | ||
removedItems += removesByIndex[index] || 0; | ||
removeTable[index] = removedItems; | ||
}); | ||
} | ||
updateRemoveTable(); | ||
var moves = itemsDiff.filter(function (diffItem) { | ||
@@ -379,10 +452,5 @@ return diffItem.type === 'move'; | ||
removesByIndex[diffItem.from] = movedItems; | ||
updateRemoveTable(); | ||
}); | ||
removedItems = 0; | ||
actual.forEach(function (_, index) { | ||
removedItems += removesByIndex[index] || 0; | ||
removeTable[index] = removedItems; | ||
}); | ||
var inserts = itemsDiff.filter(function (diffItem) { | ||
@@ -411,5 +479,4 @@ return diffItem.type === 'insert'; | ||
}); | ||
output.append(this.prefix(output.clone())).nl().indentLines(); | ||
output.text('[').nl().indentLines(); | ||
mutatedArray.forEach(function (diffItem, index) { | ||
@@ -421,6 +488,7 @@ output.i().block(function () { | ||
if (type === 'insert') { | ||
this.error('missing: ').block(inspect(diffItem.value)) | ||
.prependLinesWith('error', '// '); | ||
this.annotationBlock(function () { | ||
this.error('missing ').block(inspect(diffItem.value)); | ||
}); | ||
} else if (type === 'remove') { | ||
this.block(inspect(diffItem.value).text(last ? '' : ',').error(' // should be removed')); | ||
this.block(inspect(diffItem.value).text(last ? ' ' : ', ').error('// should be removed')); | ||
} else if (equal(diffItem.value, diffItem.expected)) { | ||
@@ -433,11 +501,9 @@ this.block(inspect(diffItem.value).text(last ? '' : ',')); | ||
} else if (valueDiff) { | ||
this.block(inspect(diffItem.value).text(last ? '' : ',')).block(function () { | ||
this.error('should be: ').append(inspect(diffItem.expected)).nl() | ||
.append(valueDiff.diff) | ||
.prependLinesWith('error', ' // '); | ||
this.block(inspect(diffItem.value).text(last ? ' ' : ', ')).annotationBlock(function () { | ||
this.error('should be ').block(inspect(diffItem.expected)).nl() | ||
.append(valueDiff.diff); | ||
}); | ||
} else { | ||
this.block(inspect(diffItem.value).text(last ? '' : ',')).block(function () { | ||
this.error('should be: ').append(inspect(diffItem.expected)) | ||
.prependLinesWith('error', ' // '); | ||
this.block(inspect(diffItem.value).text(last ? ' ' : ', ')).annotationBlock(function () { | ||
this.error('should be ').block(inspect(diffItem.expected)); | ||
}); | ||
@@ -449,3 +515,3 @@ } | ||
output.outdentLines().text(']'); | ||
output.outdentLines().append(this.suffix(output.clone())); | ||
@@ -457,2 +523,24 @@ return result; | ||
expect.addType({ | ||
name: 'array', | ||
base: 'arrayLike', | ||
identify: function (arr) { | ||
return utils.isArray(arr); | ||
} | ||
}); | ||
expect.addType({ | ||
name: 'arguments', | ||
base: 'arrayLike', | ||
prefix: function (output) { | ||
return output.text('arguments(', 'cyan'); | ||
}, | ||
suffix: function (output) { | ||
return output.text(')', 'cyan'); | ||
}, | ||
identify: function (obj) { | ||
return Object.prototype.toString.call(obj) === '[object Arguments]'; | ||
} | ||
}); | ||
expect.addType({ | ||
base: 'object', | ||
@@ -504,9 +592,12 @@ name: 'Error', | ||
}, | ||
inspect: function (f, depth, output) { | ||
inspect: function (f, depth, output, inspect) { | ||
var source = f.toString(); | ||
var name; | ||
var args; | ||
var body; | ||
var matchSource = source.match(/^function (\w*)?\s*\(([^\)]*)\)\s*\{([\s\S]*?( *)?)\}$/); | ||
if (matchSource) { | ||
var name = matchSource[1]; | ||
var args = matchSource[2]; | ||
var body = matchSource[3]; | ||
name = matchSource[1]; | ||
args = matchSource[2]; | ||
body = matchSource[3]; | ||
var bodyIndent = matchSource[4] || ''; | ||
@@ -524,9 +615,8 @@ if (!/\\\n/.test(body)) { | ||
} | ||
output.code('function ' + name + '(' + args + ') {' + body + '}', 'javascript'); | ||
} else { | ||
if (f.name) { | ||
output.sp().code(f.name, 'javascript'); | ||
} | ||
output.text('(...) {...}'); | ||
name = f.name || ''; | ||
args = ' /*...*/ '; | ||
body = ' /*...*/ '; | ||
} | ||
output.code('function ' + name + '(' + args + ') {' + body + '}', 'javascript'); | ||
return output; | ||
@@ -544,7 +634,15 @@ } | ||
output.text('expect.it('); | ||
var orBranch = false; | ||
f._expectations.forEach(function (expectation, index) { | ||
if (0 < index) { | ||
output.text(').and('); | ||
if (expectation === f._OR) { | ||
orBranch = true; | ||
return; | ||
} | ||
if (orBranch) { | ||
output.text(')\n .or('); | ||
} else if (0 < index) { | ||
output.text(')\n .and('); | ||
} | ||
var args = Array.prototype.slice.call(expectation); | ||
@@ -557,2 +655,3 @@ args.forEach(function (arg, i) { | ||
}); | ||
orBranch = false; | ||
}); | ||
@@ -593,8 +692,6 @@ | ||
name: 'binaryArray', | ||
base: 'array', | ||
base: 'arrayLike', | ||
digitWidth: 2, | ||
hexDumpWidth: 16, | ||
identify: function () { | ||
return false; | ||
}, | ||
identify: false, | ||
equal: function (a, b) { | ||
@@ -629,3 +726,3 @@ if (a === b) { | ||
hexChars += leftPad(octet.toString(16).toUpperCase(), this.digitWidth, '0') + ' '; | ||
asciiChars += String.fromCharCode(octet); | ||
asciiChars += String.fromCharCode(octet).replace(/\n/g, '␊').replace(/\r/g, '␍'); | ||
} else if (this.digitWidth === 2) { | ||
@@ -659,38 +756,12 @@ hexChars += ' '; | ||
}, | ||
diffLimit: 512, | ||
diff: function (actual, expected, output, diff, inspect) { | ||
var result = { | ||
diff: utils.diffStrings(this.hexDump(actual), this.hexDump(expected), output, {type: 'Chars', markUpSpecialCharacters: false}) | ||
}; | ||
for (var i = 0 ; i < result.diff.output.length ; i += 1) { | ||
var isInAsciiChars = false; | ||
for (var j = 0 ; j < result.diff.output[i].length ; j += 1) { | ||
var obj = result.diff.output[i][j]; | ||
var replacement = ''; | ||
for (var k = 0 ; k < obj.args[0].length ; k += 1) { | ||
var ch = obj.args[0].charAt(k); | ||
if (ch === '│') { | ||
isInAsciiChars = true; | ||
} else if (isInAsciiChars) { | ||
if (/[\x00-\x1f\x7f-\xff]/.test(ch)) { | ||
ch = '.'; | ||
} | ||
} else if (ch === ' ' && /\bbg/.test(obj.args[1])) { | ||
var leftover = obj.args[0].substr(k + 1); | ||
if (replacement) { | ||
obj.args[0] = replacement; | ||
j += 1; | ||
} | ||
replacement = ''; | ||
result.diff.output[i].splice(j, 0, {style: 'text', args: [' ', 'white']}); | ||
k = 0; | ||
ch = ' '; | ||
if (leftover.length > 0) { | ||
result.diff.output[i].splice(j + 1, 0, {style: 'text', args: [leftover, obj.args[1]]}); | ||
} | ||
obj = result.diff.output[i][j]; | ||
} | ||
replacement += ch; | ||
} | ||
obj.args[0] = replacement; | ||
} | ||
var result = {diff: output}; | ||
if (Math.max(actual.length, expected.length) > this.diffLimit) { | ||
result.diff.comment('Diff suppressed due to size > ' + this.diffLimit); | ||
} else { | ||
result.diff = utils.diffStrings(this.hexDump(actual), this.hexDump(expected), output, {type: 'Chars', markUpSpecialCharacters: false}) | ||
.replaceText(/[\x00-\x1f\x7f-\xff␊␍]/g, '.').replaceText(/[│ ]/g, function (styles, content) { | ||
this.text(content); | ||
}); | ||
} | ||
@@ -697,0 +768,0 @@ return result; |
@@ -7,3 +7,3 @@ /*global window*/ | ||
var extend = utils.extend; | ||
var levenshteinDistance = utils.levenshteinDistance; | ||
var leven = require('leven'); | ||
var cloneError = utils.cloneError; | ||
@@ -24,2 +24,17 @@ | ||
return null; | ||
}, | ||
is: function (typeOrTypeName) { | ||
var typeName; | ||
if (typeof typeOrTypeName === 'string') { | ||
typeName = typeOrTypeName; | ||
} else { | ||
typeName = typeOrTypeName.name; | ||
} | ||
if (this.name === typeName) { | ||
return true; | ||
} else if (this.baseType) { | ||
return this.baseType.is(typeName); | ||
} else { | ||
return false; | ||
} | ||
} | ||
@@ -37,27 +52,102 @@ }; | ||
function createExpectIt(expect, expectations) { | ||
function expectIt(subject) { | ||
var evaluation = expectations.map(function (expectation) { | ||
var OR = {}; | ||
function getOrGroups(expectations) { | ||
var orGroups = [[]]; | ||
expectations.forEach(function (expectation) { | ||
if (expectation === OR) { | ||
orGroups.push([]); | ||
} else { | ||
orGroups[orGroups.length - 1].push(expectation); | ||
} | ||
}); | ||
return orGroups; | ||
} | ||
function evaluateGroup(expect, subject, orGroup) { | ||
var failedGroup = false; | ||
return orGroup.map(function (expectation) { | ||
var args = Array.prototype.slice.call(expectation); | ||
args.unshift(subject); | ||
var evaluation = { expectation: args }; | ||
if (!failedGroup) { | ||
evaluation.evaluated = true; | ||
try { | ||
var args = Array.prototype.slice.call(expectation); | ||
args.unshift(subject); | ||
expect.apply(expect, args); | ||
return null; | ||
} catch (e) { | ||
return e; | ||
if (!e._isUnexpected) { | ||
throw e; | ||
} | ||
failedGroup = true; | ||
evaluation.failure = e; | ||
} | ||
}).filter(function (e) { | ||
return e; | ||
} | ||
return evaluation; | ||
}); | ||
} | ||
function writeGroupEvaluationsToOutput(expect, output, groupEvaluations) { | ||
var hasOrClauses = groupEvaluations.length > 1; | ||
var hasAndClauses = groupEvaluations.some(function (groupEvaluation) { | ||
return groupEvaluation.length > 1; | ||
}); | ||
groupEvaluations.forEach(function (groupEvaluation, i) { | ||
if (i > 0) { | ||
if (hasAndClauses) { | ||
output.nl(); | ||
} else { | ||
output.sp(); | ||
} | ||
output.comment('or').nl(); | ||
} | ||
groupEvaluation.forEach(function (evaluation, j) { | ||
if (j > 0) { | ||
output.comment(' and').nl(); | ||
} | ||
if (evaluation.failure) { | ||
if (hasAndClauses || hasOrClauses) { | ||
output.error('⨯ '); | ||
} | ||
output.append(evaluation.failure.output); | ||
} else { | ||
var style = evaluation.evaluated ? 'success' : 'text'; | ||
var expectation = evaluation.expectation; | ||
if (evaluation.evaluated) { | ||
output.success('✓ '); | ||
} else { | ||
output.sp(2); | ||
} | ||
output[style]('expected '); | ||
output.text(expect.inspect(expectation[0])).sp(); | ||
output[style](expectation[1]); | ||
expectation.slice(2).forEach(function (v) { | ||
output.sp().append(expect.inspect(v)); | ||
}); | ||
} | ||
}); | ||
}); | ||
} | ||
if (evaluation.length > 0) { | ||
expect.fail(function (output) { | ||
evaluation.forEach(function (failedExpectation, index) { | ||
if (index > 0) { | ||
output.text(' and').nl(); | ||
} | ||
function createExpectIt(expect, expectations) { | ||
var orGroups = getOrGroups(expectations); | ||
output.append(failedExpectation.output); | ||
}); | ||
function expectIt(subject) { | ||
var groupEvaluations = orGroups.map(function (orGroup) { | ||
return evaluateGroup(expect, subject, orGroup); | ||
}); | ||
var failed = groupEvaluations.every(function (groupEvaluation) { | ||
return groupEvaluation.some(function (evaluation) { | ||
return evaluation.failure; | ||
}); | ||
}); | ||
if (failed) { | ||
expect.fail(function (output) { | ||
writeGroupEvaluationsToOutput(expect, output, groupEvaluations); | ||
}); | ||
} | ||
@@ -67,2 +157,3 @@ } | ||
expectIt._expectations = expectations; | ||
expectIt._OR = OR; | ||
expectIt.and = function () { | ||
@@ -73,2 +164,7 @@ var copiedExpectations = expectations.slice(); | ||
}; | ||
expectIt.or = function () { | ||
var copiedExpectations = expectations.slice(); | ||
copiedExpectations.push(OR, arguments); | ||
return createExpectIt(expect, copiedExpectations); | ||
}; | ||
return expectIt; | ||
@@ -266,3 +362,10 @@ } | ||
}; | ||
this.types.unshift(extendedType); | ||
if (extendedType.identify === false) { | ||
extendedType.identify = function () { | ||
return false; | ||
}; | ||
this.types.push(extendedType); | ||
} else { | ||
this.types.unshift(extendedType); | ||
} | ||
@@ -288,3 +391,13 @@ return this.expect; | ||
function handleNestedExpects(e, assertion) { | ||
function buildDiff(expect, err) { | ||
return err.createDiff && err.createDiff(expect.output.clone(), function (actual, expected) { | ||
return expect.diff(actual, expected); | ||
}, function (v, depth) { | ||
return expect.inspect(v, depth || Infinity); | ||
}, function (actual, expected) { | ||
return expect.equal(actual, expected); | ||
}); | ||
} | ||
function handleNestedExpects(expect, e, assertion) { | ||
switch (assertion.errorMode) { | ||
@@ -301,2 +414,13 @@ case 'nested': | ||
return errorWithMessage(e, e.output); | ||
case 'diff': | ||
return errorWithMessage(e, e.output.clone().append(function (output) { | ||
var comparison = buildDiff(expect, e); | ||
delete e.createDiff; | ||
if (comparison && comparison.diff) { | ||
output.append(comparison.diff); | ||
} else { | ||
output.append(e.output); | ||
} | ||
})); | ||
default: | ||
@@ -336,7 +460,5 @@ throw new Error("Unknown error mode: '" + assertion.errorMode + "'"); | ||
if (err.createDiff) { | ||
var comparison = err.createDiff(); | ||
if (comparison) { | ||
message.nl(2).blue('Diff:').nl(2).append(comparison.diff); | ||
} | ||
var comparison = buildDiff(this.expect, err); | ||
if (comparison) { | ||
message.nl(2).append(comparison.diff); | ||
} | ||
@@ -382,3 +504,3 @@ | ||
if (nestingLevel === 0) { | ||
var wrappedError = handleNestedExpects(e, assertion); | ||
var wrappedError = handleNestedExpects(wrappedExpect, e, assertion); | ||
that.setErrorMessage(wrappedError); | ||
@@ -418,2 +540,3 @@ throw wrappedError; | ||
wrappedExpect.format = this.format; | ||
wrappedExpect.it = this.it.bind(this); | ||
@@ -463,3 +586,3 @@ var args = Array.prototype.slice.call(arguments, 2); | ||
assertion: assertion, | ||
score: typeMatchBonus - levenshteinDistance(testDescriptionString, assertion) | ||
score: typeMatchBonus - leven(testDescriptionString, assertion) | ||
}); | ||
@@ -466,0 +589,0 @@ }); |
@@ -81,6 +81,2 @@ var stringDiff = require('diff'); | ||
isArguments: function (object) { | ||
return Object.prototype.toString.call(object) === '[object Arguments]'; | ||
}, | ||
getKeysOfDefinedProperties: function (object) { | ||
@@ -98,40 +94,2 @@ var keys = Object.keys(object).filter(function (key) { | ||
/** | ||
* Levenshtein distance algorithm from wikipedia | ||
* http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript | ||
*/ | ||
levenshteinDistance: function (a, b) { | ||
if (a.length === 0) return b.length; | ||
if (b.length === 0) return a.length; | ||
var matrix = []; | ||
// increment along the first column of each row | ||
var i; | ||
for (i = 0; i <= b.length; i += 1) { | ||
matrix[i] = [i]; | ||
} | ||
// increment each column in the first row | ||
var j; | ||
for (j = 0; j <= a.length; j += 1) { | ||
matrix[0][j] = j; | ||
} | ||
// Fill in the rest of the matrix | ||
for (i = 1; i <= b.length; i += 1) { | ||
for (j = 1; j <= a.length; j += 1) { | ||
if (b.charAt(i - 1) === a.charAt(j - 1)) { | ||
matrix[i][j] = matrix[i - 1][j - 1]; | ||
} else { | ||
matrix[i][j] = Math.min(matrix[i - 1][j - 1] + 1, // substitution | ||
Math.min(matrix[i][j - 1] + 1, // insertion | ||
matrix[i - 1][j] + 1)); // deletion | ||
} | ||
} | ||
} | ||
return matrix[b.length][a.length]; | ||
}, | ||
truncateStack: function (err, fn) { | ||
@@ -215,6 +173,6 @@ if (Error.captureStackTrace) { | ||
}); | ||
return output; | ||
} else { | ||
output.write(baseStyle, text); | ||
} | ||
return output; | ||
} | ||
@@ -221,0 +179,0 @@ |
{ | ||
"name": "unexpected", | ||
"version": "5.0.0-beta2", | ||
"version": "5.0.0-beta20", | ||
"author": "Sune Sloth Simonsen <sune@we-knowhow.dk>", | ||
@@ -23,7 +23,8 @@ "keywords": [ | ||
"arraydiff": "0.1.1", | ||
"diff": "=1.0.8", | ||
"magicpen": "=2.1.0" | ||
"diff": "1.1.0", | ||
"leven": "1.0.0", | ||
"magicpen": "3.0.0" | ||
}, | ||
"devDependencies": { | ||
"browserify": "=5.9.1", | ||
"browserify": "5.9.1", | ||
"bundle-collapser": "^1.1.0", | ||
@@ -34,8 +35,8 @@ "coveralls": "^2.11.1", | ||
"jshint": "2.5.6", | ||
"mocha": "=1.21.4", | ||
"mocha-phantomjs": "=3.5.0", | ||
"mocha": "2.0.1", | ||
"mocha-phantomjs": "3.5.1", | ||
"mocha-slow-reporter": "*", | ||
"phantomjs": "=1.9.7-15", | ||
"phantomjs": "1.9.7-15", | ||
"serve": "*" | ||
} | ||
} |
110
README.md
# Unexpected | ||
Minimalistic BDD assertion toolkit inspired by | ||
Extensible BDD assertion toolkit inspired by | ||
[expect.js](https://github.com/LearnBoost/expect.js) | ||
@@ -19,3 +19,3 @@ | ||
[Run the test in the browser](http://sunesimonsen.github.io/unexpected/test/tests.production.html) | ||
[Run the test in the browser](http://sunesimonsen.github.io/unexpected/test/tests.html) | ||
@@ -176,7 +176,3 @@ ## Features | ||
expect(NaN, 'to be NaN'); | ||
expect({}, 'to be NaN'); | ||
expect(2, 'not to be NaN'); | ||
expect(null, 'not to be NaN'); | ||
expect(undefined, 'to be NaN'); | ||
expect("String", 'to be NaN'); | ||
``` | ||
@@ -210,3 +206,2 @@ | ||
expect('hello world', 'to contain', 'world'); | ||
expect(null, 'not to contain', 'world'); | ||
``` | ||
@@ -222,3 +217,3 @@ | ||
**empty**: asserts that an array is empty or not | ||
**empty**: asserts that an array or array-like object (identified by the presence of a `length` property) is empty | ||
@@ -228,6 +223,5 @@ ```js | ||
expect('', 'to be empty'); | ||
expect({}, 'to be empty'); | ||
expect({ length: 0, duck: 'typing' }, 'to be empty'); | ||
expect({ my: 'object' }, 'not to be empty'); | ||
expect([1,2,3], 'not to be empty'); | ||
expect({ length: 0, duck: 'typing' }, 'to be empty'); | ||
``` | ||
@@ -252,3 +246,3 @@ | ||
expect({ a: 'a', b: { c: 'c' }, d: 'd' }, 'to have own properties', ['a', 'b']); | ||
expect({ a: 'a', b: { c: 'c' }, d: 'd' }, 'to not have properties', ['k', 'l']); | ||
expect({ a: 'a', b: { c: 'c' }, d: 'd' }, 'not to have properties', ['k', 'l']); | ||
expect({ a: 'a', b: { c: 'c' }, d: 'd' }, 'to have properties', { | ||
@@ -267,3 +261,2 @@ a: 'a', | ||
```js | ||
expect(null, 'not to have key', 'a'); | ||
expect({ a: 'b' }, 'to have key', 'a'); | ||
@@ -290,2 +283,8 @@ expect({ a: 'b' }, 'not to have key', 'b'); | ||
**arity**: asserts that the `Function` takes the given number of arguments | ||
```js | ||
expect(Math.max, 'to have arity', 2); | ||
``` | ||
**finite/infinite**: asserts a finite or infinite number | ||
@@ -297,3 +296,2 @@ | ||
expect(Infinity, 'to be infinite'); | ||
expect(false, 'not to be infinite'); | ||
``` | ||
@@ -365,3 +363,3 @@ | ||
**array whose items satify**: will run an assertion function for each items in an array | ||
**array whose items satisfy**: will run an assertion function for each items in an array | ||
@@ -383,3 +381,3 @@ ```js | ||
Using this assertion result in very detailed error reporting show in the below example: | ||
Using this assertion result in very detailed error reporting as shown in the below example: | ||
@@ -399,12 +397,44 @@ ```js | ||
failed expectation in [ [ 0, 1, 2 ], [ 4, '5', 6 ], [ 7, 8, '9' ] ]: | ||
1: failed expectation in [ 4, '5', 6 ]: | ||
1: expected '5' to be a 'number' | ||
2: failed expectation in [ 7, 8, '9' ]: | ||
2: expected '9' to be a 'number' | ||
1: failed expectation in [ 4, '5', 6 ]: | ||
1: expected '5' to be a number | ||
2: failed expectation in [ 7, 8, '9' ]: | ||
2: expected '9' to be a number | ||
``` | ||
**map whose keys satify**: will run an assertion function for each key in a map | ||
**satisfy**: match against a spec | ||
All properties and nested objects mentioned in the right-hand side object are | ||
required to be present. Primitive values are compared with `to equal` semantics: | ||
```js | ||
expect({ hey: { there: true } }, 'to satisfy', { hey: {} }); | ||
``` | ||
To disallow additional properties in the subject, use `to exhaustively satisfy`: | ||
```js | ||
expect({ hey: { there: true } }, 'to exhaustively satisfy', { hey: { there: true } }); | ||
``` | ||
Regular expressions and functions in the right-hand side object will be run | ||
against the corresponding values in the subject: | ||
```js | ||
expect({ bar: 'quux', baz: true }, 'to satisfy', { bar: /QU*X/i }); | ||
``` | ||
Can be combined with `expect.it` to create complex specifications that delegate to | ||
existing assertions: | ||
```js | ||
expect({foo: 123, baz: 'bogus'}, 'to satisfy', { | ||
foo: expect.it('to be a number').and('to be greater than', 10), | ||
baz: expect.it('not to match', /^boh/) | ||
}); | ||
``` | ||
**map whose keys satisfy**: will run an assertion function for each key in a map | ||
```js | ||
expect({ foo: 0, bar: 1, baz: 2, qux: 3 }, | ||
@@ -420,3 +450,3 @@ 'to be a map whose keys satisfy', function (key) { | ||
Using this assertion result in very detailed error reporting show in the below example: | ||
Using this assertion result in very detailed error reporting as shown in the below example: | ||
@@ -434,6 +464,6 @@ ```js | ||
failed expectation on keys foo, bar, baz, qux, quux: | ||
quux: expected 'quux' to have length 3 | ||
quux: expected 'quux' to have length 3 | ||
``` | ||
**map whose values satify**: will run an assertion function for each value in a map | ||
**map whose values satisfy**: will run an assertion function for each value in a map | ||
@@ -451,3 +481,3 @@ ```js | ||
Using this assertion result in very detailed error reporting show in the below example: | ||
Using this assertion result in very detailed error reporting as shown in the below example: | ||
@@ -467,9 +497,11 @@ ```js | ||
failed expectation in | ||
{ foo: [ 0, 1, 2 ], | ||
{ | ||
foo: [ 0, 1, 2 ], | ||
bar: [ 4, '5', 6 ], | ||
baz: [ 7, 8, '9' ] }: | ||
bar: failed expectation in [ 4, '5', 6 ]: | ||
1: expected '5' to be a 'number' | ||
baz: failed expectation in [ 7, 8, '9' ]: | ||
2: expected '9' to be a 'number' | ||
baz: [ 7, 8, '9' ] | ||
}: | ||
bar: failed expectation in [ 4, '5', 6 ]: | ||
1: expected '5' to be a number | ||
baz: failed expectation in [ 7, 8, '9' ]: | ||
2: expected '9' to be a number | ||
``` | ||
@@ -586,3 +618,3 @@ | ||
expected [ 4, 3, 1, 2 ] to be sorted | ||
expected [ 4, 3, 1, 2 ] to equal [ 1, 2, 3, 4 ] | ||
expected [ 4, 3, 1, 2 ] to equal [ 1, 2, 3, 4 ] | ||
``` | ||
@@ -593,3 +625,3 @@ | ||
[unexpected-assertions.js](https://github.com/sunesimonsen/unexpected/blob/master/lib/unexpected-assertions.js) | ||
[lib/assertions.js](https://github.com/sunesimonsen/unexpected/blob/master/lib/assertions.js) | ||
@@ -631,9 +663,11 @@ ### expect.installPlugin(plugin) | ||
```js | ||
describe('test suite', function () { | ||
it('should expose a function', function () { | ||
expect(add, 'to be a', 'function'); | ||
}); | ||
describe('math.js', function () { | ||
describe('add', function () { | ||
it('is a function', function () { | ||
expect(add, 'to be a', 'function'); | ||
}); | ||
it('should do math', function () { | ||
expect(add(1, 3), 'to be', 4); | ||
it('does addition on numbers', function () { | ||
expect(add(1, 3), 'to be', 4); | ||
}); | ||
}); | ||
@@ -640,0 +674,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Uses eval
Supply chain riskPackage uses dynamic code execution (e.g., eval()), which is a dangerous practice. This can prevent the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 2 instances in 1 package
673
0
289652
4
18
5644
1
+ Addedleven@1.0.0
+ Addedansi-styles@1.1.0(transitive)
+ Addedcolor-diff@0.1.7(transitive)
+ Addeddiff@1.1.0(transitive)
+ Addedleven@1.0.0(transitive)
+ Addedmagicpen@3.0.0(transitive)
+ Addedsupports-color@1.3.1(transitive)
Updateddiff@1.1.0
Updatedmagicpen@3.0.0