unexpected
Advanced tools
Comparing version 0.0.3 to 0.0.4
@@ -56,38 +56,10 @@ // Copyright (c) 2013 Sune Simonsen <sune@we-knowhow.dk> | ||
function AssertionRule(pattern, handler) { | ||
var regExpString = pattern | ||
.replace(/\(/g, '(:?') | ||
.replace(/\[([^\]]+)\] /g, function (match, flag) { | ||
return '(?:(' + flag + ') )?'; | ||
}); | ||
this.regexp = new RegExp('^' + regExpString + '$'); | ||
this.handler = handler; | ||
} | ||
AssertionRule.prototype.test = function (text) { | ||
return this.regexp.test(text); | ||
}; | ||
AssertionRule.prototype.getFlags = function (text) { | ||
var match = this.regexp.exec(text); | ||
var flags = {}; | ||
for (var i = 1; i < match.length; i += 1) { | ||
var flag = match[i]; | ||
if (flag !== undefined) { | ||
var assertions = {}; | ||
function expect(subject, testDescriptionString) { | ||
var assertionRule = assertions[testDescriptionString]; | ||
if (assertionRule) { | ||
var flags = reduce(assertionRule.flags, function (flags, flag) { | ||
flags[flag] = true; | ||
} | ||
} | ||
return flags; | ||
}; | ||
var assertions = []; | ||
function expect(subject, testDescriptionString) { | ||
var matching = filter(assertions, function (assertionRule) { | ||
return assertionRule.test(testDescriptionString); | ||
}); | ||
if (matching.length > 0) { | ||
var assertionRule = matching[0]; | ||
var flags = assertionRule.getFlags(testDescriptionString); | ||
return flags; | ||
}, {}); | ||
var args = Array.prototype.slice.call(arguments, 2); | ||
@@ -98,6 +70,79 @@ var assertion = new Assertion(subject, testDescriptionString, flags, args); | ||
} else { | ||
throw new Error('Unknown assertion "' + testDescriptionString + '"'); | ||
var similarAssertions = expect.findAssertionSimilarTo(testDescriptionString); | ||
var message = | ||
'Unknown assertion "' + testDescriptionString + '", ' + | ||
'did you mean: "' + similarAssertions[0] + '"'; | ||
throw new Error(message); | ||
} | ||
} | ||
expect.internal = {}; | ||
// TODO refactor this method into the assertion rule | ||
expect.internal.expandPattern = (function () { | ||
function isFlag(token) { | ||
return token.slice(0, 1) === '[' && token.slice(-1) === ']'; | ||
} | ||
function isAlternation(token) { | ||
return token.slice(0, 1) === '(' && token.slice(-1) === ')'; | ||
} | ||
function removeEmptyStrings(texts) { | ||
return filter(texts, function (text) { | ||
return text !== ''; | ||
}); | ||
} | ||
function createPermutations(tokens, index) { | ||
if (index === tokens.length) { | ||
return [{ text: '', flags: []}]; | ||
} | ||
var token = tokens[index]; | ||
var tail = createPermutations(tokens, index + 1); | ||
if (isFlag(token)) { | ||
var flag = token.slice(1, -1); | ||
return map(tail, function (pattern) { | ||
return { | ||
text: flag + ' ' + pattern.text, | ||
flags: [flag].concat(pattern.flags) | ||
}; | ||
}).concat(tail); | ||
} else if (isAlternation(token)) { | ||
var alternations = token.split(/\(|\)|\|/); | ||
alternations = removeEmptyStrings(alternations); | ||
return reduce(alternations, function (result, alternation) { | ||
return result.concat(map(tail, function (pattern) { | ||
return { | ||
text: alternation + pattern.text, | ||
flags: pattern.flags | ||
}; | ||
})); | ||
}, []); | ||
} else { | ||
return map(tail, function (pattern) { | ||
return { | ||
text: token + pattern.text, | ||
flags: pattern.flags | ||
}; | ||
}); | ||
} | ||
} | ||
return function (pattern) { | ||
var result = []; | ||
pattern = pattern.replace(/(\[[^\]]+\]) ?/g, '$1'); | ||
var splitRegex = /\[[^\]]+\]|\([^\)]+\)/g; | ||
var tokens = []; | ||
var m; | ||
var lastIndex = 0; | ||
while ((m = splitRegex.exec(pattern))) { | ||
tokens.push(pattern.slice(lastIndex, m.index)); | ||
tokens.push(pattern.slice(m.index, splitRegex.lastIndex)); | ||
lastIndex = splitRegex.lastIndex; | ||
} | ||
tokens.push(pattern.slice(lastIndex)); | ||
tokens = removeEmptyStrings(tokens); | ||
return createPermutations(tokens, 0); | ||
}; | ||
}()); | ||
expect.format = function (message, args) { | ||
@@ -121,6 +166,70 @@ args = map(args, function (arg) { | ||
expect.addAssertion = function (pattern, handler) { | ||
assertions.push(new AssertionRule(pattern, handler)); | ||
expect.addAssertion = function () { | ||
var patterns = Array.prototype.slice.call(arguments, 0, -1); | ||
var handler = Array.prototype.slice.call(arguments, -1)[0]; | ||
forEach(patterns, function (pattern) { | ||
forEach(expect.internal.expandPattern(pattern), function (expandedPattern) { | ||
assertions[expandedPattern.text] = { | ||
handler: handler, | ||
flags: expandedPattern.flags | ||
}; | ||
}); | ||
}); | ||
}; | ||
/** | ||
* Levenshtein distance algorithm from wikipedia | ||
* http://en.wikibooks.org/wiki/Algorithm_Implementation/Strings/Levenshtein_distance#JavaScript | ||
*/ | ||
function levenshteinDistance(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++){ | ||
matrix[i] = [i]; | ||
} | ||
// increment each column in the first row | ||
var j; | ||
for(j = 0; j <= a.length; j++){ | ||
matrix[0][j] = j; | ||
} | ||
// Fill in the rest of the matrix | ||
for(i = 1; i <= b.length; i++){ | ||
for(j = 1; j <= a.length; j++){ | ||
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]; | ||
} | ||
expect.findAssertionSimilarTo = function (text) { | ||
var editDistrances = []; | ||
forEach(getKeys(assertions), function (assertion) { | ||
var distance = levenshteinDistance(text, assertion); | ||
editDistrances.push({ | ||
assertion: assertion, | ||
distance: distance | ||
}); | ||
}); | ||
editDistrances.sort(function (x, y) { | ||
return x.distance - y.distance; | ||
}); | ||
return map(editDistrances.slice(0, 5), function (editDistrance) { | ||
return editDistrance.assertion; | ||
}); | ||
}; | ||
expect.addAssertion('to [not] be', function (value) { | ||
@@ -159,10 +268,6 @@ this.assert(this.obj === value); | ||
expect.addAssertion('to [not] have [own] property', function (key, value) { | ||
if (!this.obj) { | ||
this.throwStandardError(); | ||
} | ||
if (this.flags.own) { | ||
this.assert(hasOwnProperty(this.obj, key)); | ||
this.assert(this.obj && hasOwnProperty(this.obj, key)); | ||
} else { | ||
this.assert(this.obj[key] !== undefined); | ||
this.assert(this.obj && this.obj[key] !== undefined); | ||
} | ||
@@ -172,3 +277,3 @@ | ||
this.flags.not = false; | ||
this.assert(this.obj[key] === value); | ||
this.assert(this.obj && this.obj[key] === value); | ||
} | ||
@@ -179,3 +284,4 @@ }); | ||
if (!this.obj || typeof this.obj.length !== 'number') { | ||
this.throwStandardError(); | ||
throw new Error("Assertion '" + this.testDescription + | ||
"' only supports array like objects"); | ||
} | ||
@@ -217,6 +323,18 @@ this.assert(length === this.obj.length); | ||
expect.addAssertion('to [not] contain', function (arg) { | ||
if ('string' === typeof this.obj) { | ||
this.assert(this.obj.indexOf(arg) !== -1); | ||
var args = Array.prototype.slice.call(arguments); | ||
var that = this; | ||
if ('string' === typeof that.obj) { | ||
forEach(args, function (arg) { | ||
that.assert(that.obj.indexOf(arg) !== -1); | ||
}); | ||
} else if (isArray(that.obj)) { | ||
forEach(args, function (arg) { | ||
that.assert(that.obj && indexOf(that.obj, arg) !== -1); | ||
}); | ||
} else if (that.obj === null) { | ||
that.assert(!that.flags.not); | ||
} else { | ||
this.assert(this.obj && indexOf(this.obj, arg) !== -1); | ||
throw new Error("Assertion '" + this.testDescription + | ||
"' only supports strings and arrays"); | ||
} | ||
@@ -233,15 +351,15 @@ }); | ||
expect.addAssertion('(<|to be (<|less than|below))', function (value) { | ||
expect.addAssertion('<', 'to be (<|less than|below)', function (value) { | ||
this.assert(this.obj < value); | ||
}); | ||
expect.addAssertion('(<=|to be (<=|less than or equals to))', function (value) { | ||
expect.addAssertion('<=', 'to be (<=|less than or equals to)', function (value) { | ||
this.assert(this.obj <= value); | ||
}); | ||
expect.addAssertion('(>|to be (>|greater than|above))', function (value) { | ||
expect.addAssertion('>', 'to be (>|greater than|above)', function (value) { | ||
this.assert(this.obj > value); | ||
}); | ||
expect.addAssertion('(>=|to be (>=|greater than or equals to))', function (value) { | ||
expect.addAssertion('>=', 'to be (>=|greater than or equals to)', function (value) { | ||
this.assert(this.obj >= value); | ||
@@ -308,3 +426,3 @@ }); | ||
///////////////////////// Helper functions /////////////////////////////// | ||
function bind(fn, scope) { | ||
@@ -566,3 +684,3 @@ return function () { | ||
function isArray (ar) { | ||
return Object.prototype.toString.call(ar) == '[object Array]'; | ||
return Object.prototype.toString.call(ar) === '[object Array]'; | ||
} | ||
@@ -613,2 +731,12 @@ | ||
function forEach(arr, callback, that) { | ||
if (Array.prototype.forEach) { | ||
return Array.prototype.forEach.call(arr, callback, that); | ||
} | ||
for (var i= 0, n = arr.length; i<n; i++) | ||
if (i in arr) | ||
callback.call(that, arr[i], i, arr); | ||
} | ||
function map(arr, mapper, that) { | ||
@@ -671,7 +799,7 @@ if (Array.prototype.map) { | ||
if (arguments.length >= 2) { | ||
rv = arguments[1]; | ||
rv = arguments[2]; | ||
} else { | ||
do { | ||
if (i in this) { | ||
rv = this[i++]; | ||
if (i in arr) { | ||
rv = arr[i++]; | ||
break; | ||
@@ -687,4 +815,4 @@ } | ||
for (; i < len; i++) { | ||
if (i in this) | ||
rv = fun.call(null, rv, this[i], i, this); | ||
if (i in arr) | ||
rv = fun.call(null, rv, arr[i], i, this); | ||
} | ||
@@ -1095,3 +1223,3 @@ | ||
////////////////////////////////////////////////////////////////////////// | ||
// Support three module loading scenarios | ||
@@ -1098,0 +1226,0 @@ if (typeof require === 'function' && typeof exports === 'object' && typeof module === 'object') { |
{ | ||
"name": "unexpected", | ||
"version": "0.0.3", | ||
"author": "Sune Sloth Simonsen <sune@we-knowhow.dk>", | ||
"keywords": [ "assertion", "test", "assert" ], | ||
"license": "MIT", | ||
"bugs": "https://github.com/sunesimonsen/unexpected/issues", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/sunesimonsen/unexpected" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha" | ||
}, | ||
"main": "./lib/unexpected.js", | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"mocha": "~1.9.0", | ||
"mocha-slow-reporter": "*", | ||
"serve": "*" | ||
} | ||
"name": "unexpected", | ||
"version": "0.0.4", | ||
"author": "Sune Sloth Simonsen <sune@we-knowhow.dk>", | ||
"keywords": [ | ||
"assertion", | ||
"test", | ||
"assert" | ||
], | ||
"license": "MIT", | ||
"bugs": "https://github.com/sunesimonsen/unexpected/issues", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/sunesimonsen/unexpected" | ||
}, | ||
"scripts": { | ||
"test": "./node_modules/.bin/mocha" | ||
}, | ||
"main": "./lib/unexpected.js", | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"mocha": "~1.9.0", | ||
"mocha-slow-reporter": "*", | ||
"serve": "*" | ||
} | ||
} |
@@ -22,2 +22,3 @@ # Unexpected | ||
- Fast | ||
- Compatible with all test frameworks. | ||
@@ -24,0 +25,0 @@ - Node.JS ready (`require('unexpected')`). |
@@ -53,2 +53,6 @@ /*global describe, it, expect*/ | ||
}, 'to throw exception', "expected 'foo' to be 'bar'"); | ||
expect(function () { | ||
expect(true, 'to not be', true); | ||
}, 'to throw exception', "expected true to not be true"); | ||
}); | ||
@@ -64,2 +68,3 @@ }); | ||
expect(null, 'to not be an', 'object'); | ||
expect(true, 'to be a', 'boolean'); | ||
}); | ||
@@ -71,2 +76,6 @@ | ||
}, 'to throw exception', /expected 5 to be a \[Function(: Array)?\]/); | ||
expect(function () { | ||
expect([], 'to not be an', 'array'); | ||
}, 'to throw exception', "expected [] to not be an 'array'"); | ||
}); | ||
@@ -79,2 +88,3 @@ }); | ||
expect(1, 'to equal', '1'); | ||
expect(1, 'to equal', 1); | ||
expect(null, 'to not equal', '1'); | ||
@@ -85,2 +95,9 @@ var now = new Date(); | ||
expect({ now: now }, 'to equal', { now: now }); | ||
expect(null, 'to equal', null); | ||
expect(null, 'to not equal', undefined); | ||
expect(undefined, 'to equal', undefined); | ||
expect(true, 'to equal', true); | ||
expect(false, 'to equal', false); | ||
expect({ a: { b: 'c' } }, 'to equal', { a: { b: 'c' } }); | ||
expect({ a: { b: 'c' } }, 'to not equal', { a: { b: 'd' } }); | ||
}); | ||
@@ -90,2 +107,6 @@ | ||
expect(function () { | ||
expect({ a: { b: 'c'} }, 'to equal', { a: { b: 'd'} }); | ||
}, 'to throw exception', "expected { a: { b: 'c' } } to equal { a: { b: 'd' } }"); | ||
expect(function () { | ||
expect({ a: 'b' }, 'to not equal', { a: 'b' }); | ||
@@ -155,6 +176,7 @@ }, 'to throw exception', "expected { a: 'b' } to not equal { a: 'b' }"); | ||
expect([1, 2], 'to contain', 1); | ||
expect([1, 2], 'to contain', 2, 1); | ||
expect('hello world', 'to contain', 'world'); | ||
expect(null, 'to not contain', 'world'); | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -164,2 +186,14 @@ expect(function () { | ||
}, 'to throw exception', "expected 'hello world' to contain 'foo'"); | ||
expect(function () { | ||
expect('hello world', 'to contain', 'hello', 'foo'); | ||
}, 'to throw exception', "expected 'hello world' to contain 'hello', 'foo'"); | ||
expect(function () { | ||
expect([1, 2], 'to contain', 2, 3); | ||
}, 'to throw exception', "expected [ 1, 2 ] to contain 2, 3"); | ||
expect(function () { | ||
expect(1, 'to contain', 1); | ||
}, 'to throw exception', "Assertion 'to contain' only supports strings and arrays"); | ||
}); | ||
@@ -173,2 +207,3 @@ }); | ||
expect([1,2,3], 'to not have length', 4); | ||
expect({ length: 4 }, 'to have length', 4); | ||
}); | ||
@@ -178,8 +213,12 @@ | ||
expect(function () { | ||
expect([1, 2], 'to have length', 3); | ||
}, 'to throw exception', "expected [ 1, 2 ] to have length 3"); | ||
expect(function () { | ||
expect(null, 'to have length', 4); | ||
}, 'to throw exception', "expected null to have length 4"); | ||
}, 'to throw exception', "Assertion 'to have length' only supports array like objects"); | ||
expect(function () { | ||
expect({ length: 'foo' }, 'to have length', 4); | ||
}, 'to throw exception', "expected { length: 'foo' } to have length 4"); | ||
}, 'to throw exception', "Assertion 'to have length' only supports array like objects"); | ||
}); | ||
@@ -195,6 +234,12 @@ }); | ||
expect({a: 'b'}, 'to have property', 'toString'); | ||
expect({a: 'b'}, 'to not have property', 'b'); | ||
expect({a: 'b'}, 'to have own property', 'a'); | ||
expect(create({a: 'b'}), 'to not have own property', 'a'); | ||
expect(1, 'to not have property', 'a'); | ||
expect(null, 'to not have property', 'a'); | ||
expect(undefined, 'to not have property', 'a'); | ||
expect(true, 'to not have property', 'a'); | ||
expect(false, 'to not have property', 'a'); | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -208,2 +253,16 @@ expect(function () { | ||
}, 'to throw exception', "expected null to have property 'b'"); | ||
expect(function () { | ||
expect({a: 'b'}, 'to have property', 'a', 'c'); | ||
}, 'to throw exception', "expected { a: 'b' } to have property 'a', 'c'"); | ||
expect(function () { | ||
// property expectations on value expects the property to be present | ||
expect(null, 'to not have property', 'a', 'b'); | ||
}, 'to throw exception', "expected null to not have property 'a', 'b'"); | ||
expect(function () { | ||
// property expectations on value expects the property to be present | ||
expect(null, 'to not have own property', 'a', 'b'); | ||
}, 'to throw exception', "expected null to not have own property 'a', 'b'"); | ||
}); | ||
@@ -221,3 +280,3 @@ }); | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -236,3 +295,2 @@ expect(function () { | ||
it('asserts the presence of a key', function () { | ||
expect(null, 'to not have key', 'a'); | ||
expect({ a: 'b' }, 'to have key', 'a'); | ||
@@ -244,8 +302,32 @@ expect({ a: 'b' }, 'to not have key', 'b'); | ||
expect({ a: 'b', c: 'd', e: 'f' }, 'to not only have keys', ['a', 'c']); | ||
expect(null, 'to not have key', 'a'); | ||
expect(undefined, 'to not have key', 'a'); | ||
expect(true, 'to not have key', 'a'); | ||
expect(false, 'to not have key', 'a'); | ||
}); | ||
it('throws when the assertion fails', function () { | ||
expect(function () { | ||
expect({ a: 'b', b: 'c' }, 'to have key', 'e'); | ||
}, 'to throw exception', "expected { a: 'b', b: 'c' } to have key 'e'"); | ||
expect(function () { | ||
expect({ a: 'b', b: 'c' }, 'to only have key', 'b'); | ||
}, 'to throw exception', "expected { a: 'b', b: 'c' } to only have key 'b'"); | ||
expect(function () { | ||
expect({ a: 'b', b: 'c' }, 'to not have key', 'b'); | ||
}, 'to throw exception', "expected { a: 'b', b: 'c' } to not have key 'b'"); | ||
expect(function () { | ||
expect({ a: 'b', c: 'd' }, 'to not only have keys', ['a', 'c']); | ||
}, 'to throw exception', "expected { a: 'b', c: 'd' } to not only have keys [ 'a', 'c' ]"); | ||
expect(function () { | ||
expect({ a: 'b', c: 'd' }, 'to only have keys', 'a', 'd'); | ||
}, 'to throw exception', "expected { a: 'b', c: 'd' } to only have keys 'a', 'd'"); | ||
expect(function () { | ||
expect({ a: 'b', c: 'd' }, 'to not only have keys', 'a', 'c'); | ||
}, 'to throw exception', "expected { a: 'b', c: 'd' } to not only have keys 'a', 'c'"); | ||
}); | ||
@@ -262,3 +344,3 @@ }); | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -281,3 +363,3 @@ expect(function () { | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -296,3 +378,3 @@ expect(function () { | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -312,3 +394,3 @@ expect(function () { | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -327,3 +409,3 @@ expect(function () { | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -340,3 +422,3 @@ expect(function () { | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -353,3 +435,3 @@ expect(function () { | ||
}); | ||
it('throws when the assertion fails', function () { | ||
@@ -388,6 +470,44 @@ expect(function () { | ||
expect(1, "foo bar", 2); | ||
}, 'to throw exception', 'Unknown assertion "foo bar"'); | ||
}, 'to throw exception', 'Unknown assertion "foo bar", did you mean: "to be"'); | ||
}); | ||
}); | ||
function sortBy(arr, property) { | ||
arr.sort(function (x, y) { | ||
var xp = x[property]; | ||
var yp = y[property]; | ||
if (xp > yp) { return 1; } | ||
if (xp < yp) { return -1; } | ||
return 0; | ||
}); | ||
} | ||
describe('internal', function () { | ||
describe('expandPattern', function () { | ||
it('expands patterns containing multiple flags', function () { | ||
var expanded = expect.internal.expandPattern('foo [not] [only] bar'); | ||
sortBy(expanded, 'text'); | ||
expect(expanded, 'to equal', [ | ||
{ text: 'foo bar', flags: []}, | ||
{ text: 'foo not bar', flags: ['not']}, | ||
{ text: 'foo not only bar', flags: ['not', 'only']}, | ||
{ text: 'foo only bar', flags: ['only']} | ||
]); | ||
expect(expanded.length, 'to be', 4); | ||
}); | ||
it('expands patterns alternations', function () { | ||
var expanded = expect.internal.expandPattern('foo (bar|bar baz) (qux|quux)'); | ||
sortBy(expanded, 'text'); | ||
expect(expanded, 'to equal', [ | ||
{ text: 'foo bar baz quux', flags: []}, | ||
{ text: 'foo bar baz qux', flags: []}, | ||
{ text: 'foo bar quux', flags: []}, | ||
{ text: 'foo bar qux', flags: []} | ||
]); | ||
expect(expanded.length, 'to be', 4); | ||
}); | ||
}); | ||
}); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
186573
6015
326
0