unexpected
Advanced tools
Comparing version 5.0.0-beta2 to 5.0.0-beta3
@@ -83,3 +83,3 @@ var utils = require('./utils'); | ||
expect.addAssertion(['string', 'array', 'object'], 'to be (the empty|an empty|a non-empty) (string|array|object)', function (expect, subject) { | ||
expect.addAssertion(['string', 'array-like', 'object'], 'to be (the empty|an empty|a non-empty) (string|array|object)', function (expect, subject) { | ||
expect(subject, 'to be a', this.alternations[1]); | ||
@@ -94,4 +94,3 @@ expect(subject, this.alternations[0] === 'a non-empty' ? 'not to be empty' : 'to be empty'); | ||
if (e._isUnexpected && this.flags.not) { | ||
e.createDiff = function () { | ||
var output = expect.output.clone(); | ||
e.createDiff = function (output) { | ||
var lastIndex = 0; | ||
@@ -153,3 +152,3 @@ function flushUntilIndex(i) { | ||
if (e._isUnexpected) { | ||
e.createDiff = function () { | ||
e.createDiff = function (output, diff) { | ||
var expected = extend({}, properties); | ||
@@ -165,3 +164,3 @@ var actual = {}; | ||
} | ||
return expect.diff(actual, expected); | ||
return diff(actual, expected); | ||
}; | ||
@@ -216,52 +215,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('array-like', '[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); | ||
} | ||
@@ -312,4 +309,4 @@ }); | ||
if (!this.flags.not && e._isUnexpected) { | ||
e.createDiff = function () { | ||
return expect.diff(subject, value); | ||
e.createDiff = function (output, diff) { | ||
return diff(subject, value); | ||
}; | ||
@@ -377,3 +374,3 @@ } | ||
var errors = {}; | ||
Object.keys(subject).forEach(function (key, index) { | ||
expect.findTypeOf(subject).getKeys(subject).forEach(function (key, index) { | ||
try { | ||
@@ -421,3 +418,3 @@ if (typeof extraArgs[0] === 'function') { | ||
expect.addAssertion('array', 'to be (a|an) [non-empty] array whose items satisfy', function (expect, subject) { // ... | ||
expect.addAssertion('array-like', 'to be (a|an) [non-empty] array whose items satisfy', function (expect, subject) { // ... | ||
var extraArgs = Array.prototype.slice.call(arguments, 2); | ||
@@ -428,3 +425,2 @@ if (extraArgs.length === 0) { | ||
this.errorMode = 'nested'; | ||
expect(subject, 'to be an array'); | ||
if (this.flags['non-empty']) { | ||
@@ -434,6 +430,6 @@ expect(subject, 'to be non-empty'); | ||
this.errorMode = 'bubble'; | ||
expect.apply(expect, [subject, 'to be a map whose values satisfy'].concat(extraArgs)); | ||
expect.apply(expect, [subject, 'to be an map whose values satisfy'].concat(extraArgs)); | ||
}); | ||
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('array-like', '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']) { | ||
@@ -535,3 +531,3 @@ expect(subject, 'to be non-empty'); | ||
} else if (typeof value === 'function') { | ||
// FIXME: If expect.fn, it should be possible to produce a better error message | ||
// FIXME: If expect.it, it should be possible to produce a better error message | ||
value(subject); | ||
@@ -542,9 +538,90 @@ } 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('array-like') || type.is('object')) { | ||
try { | ||
expect(subject, 'to be an object'); | ||
type.getKeys(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)); | ||
} | ||
} 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) { | ||
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 (!keyDiff || (keyDiff && !keyDiff.inline)) { | ||
annotation.error('should satisfy: ') | ||
.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); | ||
} | ||
this.block(annotation.prependLinesWith('error', ' // ')); | ||
}).nl(); | ||
}); | ||
output.outdentLines().text('}'); | ||
return result; | ||
}; | ||
} | ||
expect.fail(e); | ||
} | ||
@@ -551,0 +628,0 @@ } else { |
@@ -6,5 +6,51 @@ 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.append(inspect(this.unwrap(value))); | ||
return output.append(this.suffix); | ||
}, | ||
diff: function (actual, expected, output, diff, inspect) { | ||
actual = this.unwrap(actual); | ||
expected = this.unwrap(expected); | ||
var comparison = diff(actual, expected); | ||
if (!comparison) { | ||
return null; | ||
} | ||
if (comparison.inline) { | ||
return { | ||
inline: true, | ||
diff: output.append(this.prefix).append(comparison.diff).append(this.suffix) | ||
}; | ||
} else { | ||
return { | ||
inline: true, | ||
diff: output.append(this.prefix).nl() | ||
.indentLines() | ||
.i().block(function () { | ||
this.append(inspect(actual)).block(function () { | ||
this.error('should be: ').block(inspect(expected)).nl() | ||
.append(comparison.diff); | ||
this.prependLinesWith(function () { | ||
this.error(' // '); | ||
}); | ||
}); | ||
}).nl() | ||
.outdentLines() | ||
.append(this.suffix) | ||
}; | ||
} | ||
} | ||
}); | ||
expect.addType({ | ||
name: 'object', | ||
@@ -14,2 +60,3 @@ identify: function (obj) { | ||
}, | ||
getKeys: Object.keys, | ||
equal: function (a, b, equal) { | ||
@@ -209,3 +256,3 @@ if (a === b) { | ||
if (typeA === 'string') { | ||
return utils.levenshteinDistance(a, b) < a.length / 2; | ||
return leven(a, b) < a.length / 2; | ||
} | ||
@@ -234,11 +281,17 @@ | ||
expect.addType({ | ||
name: 'array', | ||
name: 'array-like', | ||
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]); | ||
@@ -256,3 +309,3 @@ })); | ||
if (utils.isArguments(arr)) { | ||
if (!Array.isArray(arr)) { | ||
arr = Array.prototype.slice.call(arr); | ||
@@ -306,7 +359,10 @@ } | ||
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); | ||
@@ -446,2 +502,18 @@ } | ||
expect.addType({ | ||
name: 'array', | ||
base: 'array-like', | ||
identify: function (arr) { | ||
return utils.isArray(arr); | ||
} | ||
}); | ||
expect.addType({ | ||
name: 'arguments', | ||
base: 'array-like', | ||
identify: function (obj) { | ||
return Object.prototype.toString.call(obj) === '[object Arguments]'; | ||
} | ||
}); | ||
expect.addType({ | ||
base: 'object', | ||
@@ -578,8 +650,6 @@ name: 'Error', | ||
name: 'binaryArray', | ||
base: 'array', | ||
base: 'array-like', | ||
digitWidth: 2, | ||
hexDumpWidth: 16, | ||
identify: function () { | ||
return false; | ||
}, | ||
identify: false, | ||
equal: function (a, b) { | ||
@@ -586,0 +656,0 @@ if (a === b) { |
@@ -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; | ||
} | ||
} | ||
@@ -263,3 +278,10 @@ }; | ||
}; | ||
this.types.unshift(extendedType); | ||
if (extendedType.identify === false) { | ||
extendedType.identify = function () { | ||
return false; | ||
}; | ||
this.types.push(extendedType); | ||
} else { | ||
this.types.unshift(extendedType); | ||
} | ||
@@ -332,3 +354,10 @@ return this.expect; | ||
if (err.createDiff) { | ||
var comparison = err.createDiff(); | ||
var that = this; | ||
var comparison = err.createDiff(message.clone(), function (actual, expected) { | ||
return that.diff(actual, expected); | ||
}, function (v, depth) { | ||
return that.inspect(v, depth || Infinity); | ||
}, function (actual, expected) { | ||
return that.equal(actual, expected); | ||
}); | ||
if (comparison) { | ||
@@ -456,3 +485,3 @@ message.nl(2).blue('Diff:').nl(2).append(comparison.diff); | ||
assertion: assertion, | ||
score: typeMatchBonus - levenshteinDistance(testDescriptionString, assertion) | ||
score: typeMatchBonus - leven(testDescriptionString, assertion) | ||
}); | ||
@@ -459,0 +488,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) { | ||
@@ -138,0 +96,0 @@ if (Error.captureStackTrace) { |
{ | ||
"name": "unexpected", | ||
"version": "5.0.0-beta2", | ||
"version": "5.0.0-beta3", | ||
"author": "Sune Sloth Simonsen <sune@we-knowhow.dk>", | ||
@@ -24,2 +24,3 @@ "keywords": [ | ||
"diff": "=1.0.8", | ||
"leven": "1.0.0", | ||
"magicpen": "=2.1.0" | ||
@@ -26,0 +27,0 @@ }, |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
1174413
36
12265
4
+ Addedleven@1.0.0
+ Addedleven@1.0.0(transitive)