unexpected
Advanced tools
Comparing version 1.0.1 to 1.0.2
@@ -56,22 +56,58 @@ // Copyright (c) 2013 Sune Simonsen <sune@we-knowhow.dk> | ||
var assertions = {}; | ||
function truncateStack(err, fn) { | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(err, fn); | ||
} else if ('stack' in err) { | ||
// Excludes IE<10, and fn cannot be anonymous for this backup plan to work: | ||
var stackEntries = err.stack.split(/\r\n?|\n\r?/), | ||
needle = 'at ' + fn.name + ' '; | ||
for (var i = 0 ; i < stackEntries.length ; i += 1) { | ||
if (stackEntries[i].indexOf(needle) !== -1) { | ||
stackEntries.splice(1, i); | ||
err.stack = stackEntries.join("\n"); | ||
} | ||
} | ||
} | ||
function Unexpected(assertions) { | ||
this.assertions = assertions || {}; | ||
} | ||
function expect(subject, testDescriptionString) { | ||
var assertionRule = assertions[testDescriptionString]; | ||
Unexpected.prototype.format = function (message, args) { | ||
args = map(args, function (arg) { | ||
return inspect(arg); | ||
}); | ||
message = message.replace(/\{(\d+)\}/g, function (match, n) { | ||
return args[n] || match; | ||
}); | ||
return message; | ||
}; | ||
Unexpected.prototype.fail = function (message) { | ||
message = message || "explicit failure"; | ||
var args = Array.prototype.slice.call(arguments, 1); | ||
throw new Error(this.format(message, args)); | ||
}; | ||
Unexpected.prototype.findAssertionSimilarTo = function (text) { | ||
var editDistrances = []; | ||
forEach(getKeys(this.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; | ||
}); | ||
}; | ||
Unexpected.prototype.addAssertion = function () { | ||
var assertions = this.assertions; | ||
var patterns = Array.prototype.slice.call(arguments, 0, -1); | ||
var handler = Array.prototype.slice.call(arguments, -1)[0]; | ||
forEach(patterns, function (pattern) { | ||
ensureValidPattern(pattern); | ||
forEach(expandPattern(pattern), function (expandedPattern) { | ||
assertions[expandedPattern.text] = { | ||
handler: handler, | ||
flags: expandedPattern.flags | ||
}; | ||
}); | ||
}); | ||
return this; // for chaining | ||
}; | ||
Unexpected.prototype.expect = function (subject, testDescriptionString) { | ||
var assertionRule = this.assertions[testDescriptionString]; | ||
if (assertionRule) { | ||
@@ -88,7 +124,7 @@ var flags = reduce(assertionRule.flags, function (flags, flag) { | ||
} catch (e) { | ||
truncateStack(e, expect); | ||
truncateStack(e, this.expect); | ||
throw e; | ||
} | ||
} else { | ||
var similarAssertions = expect.findAssertionSimilarTo(testDescriptionString); | ||
var similarAssertions = this.findAssertionSimilarTo(testDescriptionString); | ||
var message = | ||
@@ -98,11 +134,49 @@ 'Unknown assertion "' + testDescriptionString + '", ' + | ||
var err = new Error(message); | ||
truncateStack(err, expect); | ||
truncateStack(err, this.expect); | ||
throw err; | ||
} | ||
}; | ||
Unexpected.prototype.inspect = inspect; | ||
Unexpected.prototype.eql = eql; | ||
function makeExpectFunction(unexpected) { | ||
var expect = bind(unexpected.expect, unexpected); | ||
unexpected.expect = expect; | ||
expect.fail = bind(unexpected.fail, unexpected); | ||
expect.addAssertion = bind(unexpected.addAssertion, unexpected); | ||
expect.clone = bind(unexpected.clone, unexpected); | ||
expect.assertions = unexpected.assertions; | ||
return expect; | ||
} | ||
expect.internal = {}; | ||
Unexpected.prototype.clone = function () { | ||
var unexpected = new Unexpected(objectCreate(this.assertions)); | ||
return makeExpectFunction(unexpected); | ||
}; | ||
// TODO refactor this method into the assertion rule | ||
expect.internal.expandPattern = (function () { | ||
Unexpected.create = function () { | ||
var unexpected = new Unexpected(); | ||
return makeExpectFunction(unexpected); | ||
}; | ||
var expect = Unexpected.create(); | ||
function truncateStack(err, fn) { | ||
if (Error.captureStackTrace) { | ||
Error.captureStackTrace(err, fn); | ||
} else if ('stack' in err) { | ||
// Excludes IE<10, and fn cannot be anonymous for this backup plan to work: | ||
var stackEntries = err.stack.split(/\r\n?|\n\r?/), | ||
needle = 'at ' + fn.name + ' '; | ||
for (var i = 0 ; i < stackEntries.length ; i += 1) { | ||
if (stackEntries[i].indexOf(needle) !== -1) { | ||
stackEntries.splice(1, i); | ||
err.stack = stackEntries.join("\n"); | ||
} | ||
} | ||
} | ||
} | ||
var expandPattern = (function () { | ||
function isFlag(token) { | ||
@@ -172,2 +246,6 @@ return token.slice(0, 1) === '[' && token.slice(-1) === ']'; | ||
permutation.text = trim(permutation.text); | ||
if (permutation.text === '') { | ||
// This can only happen if the pattern only contains flags | ||
throw new Error("Assertion patterns must not only contain flags"); | ||
} | ||
}); | ||
@@ -178,33 +256,64 @@ return permutations; | ||
expect.format = function (message, args) { | ||
args = map(args, function (arg) { | ||
return inspect(arg); | ||
}); | ||
message = message.replace(/\{(\d+)\}/g, function (match, n) { | ||
return args[n] || match; | ||
}); | ||
return message; | ||
}; | ||
expect.fail = function (message) { | ||
message = message || "explicit failure"; | ||
var args = Array.prototype.slice.call(arguments, 1); | ||
throw new Error(expect.format(message, args)); | ||
}; | ||
function ensureValidUseOfParenthesisOrBrackets(pattern) { | ||
var counts = { | ||
'[': 0, | ||
']': 0, | ||
'(': 0, | ||
')': 0 | ||
}; | ||
for (var i = 0; i < pattern.length; i += 1) { | ||
var c = pattern[i]; | ||
if (c in counts) { | ||
counts[c] += 1; | ||
} | ||
if (c === ']' && counts['['] >= counts[']']) { | ||
if (counts['['] === counts[']'] + 1) { | ||
throw new Error("Assertion patterns must not contain flags with brackets: '" + pattern + "'"); | ||
} | ||
expect.assertions = assertions; | ||
if (counts['('] !== counts[')']) { | ||
throw new Error("Assertion patterns must not contain flags with parenthesis: '" + pattern + "'"); | ||
} | ||
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 | ||
}; | ||
}); | ||
}); | ||
}; | ||
if (pattern[i-1] === '[') { | ||
throw new Error("Assertion patterns must not contain empty flags: '" + pattern + "'"); | ||
} | ||
} else if (c === ')' && counts['('] >= counts[')']) { | ||
if (counts['('] === counts[')'] + 1) { | ||
throw new Error("Assertion patterns must not contain alternations with parenthesis: '" + pattern + "'"); | ||
} | ||
if (counts['['] !== counts[']']) { | ||
throw new Error("Assertion patterns must not contain alternations with brackets: '" + pattern + "'"); | ||
} | ||
} | ||
if ((c === ')' || c === '|') && counts['('] >= counts[')']) { | ||
if (pattern[i-1] === '(' || pattern[i-1] === '|') { | ||
throw new Error("Assertion patterns must not contain empty alternations: '" + pattern + "'"); | ||
} | ||
} | ||
} | ||
if (counts['['] !== counts[']']) { | ||
throw new Error("Assertion patterns must not contain unbalanced brackets: '" + pattern + "'"); | ||
} | ||
if (counts['('] !== counts[')']) { | ||
throw new Error("Assertion patterns must not contain unbalanced parenthesis: '" + pattern + "'"); | ||
} | ||
} | ||
function ensureValidPattern(pattern) { | ||
if (typeof pattern !== 'string' || pattern === '') { | ||
throw new Error("Assertion patterns must be a non empty string"); | ||
} | ||
if (pattern.match(/^\s|\s$/)) { | ||
throw new Error("Assertion patterns can't start or end with whitespace"); | ||
} | ||
ensureValidUseOfParenthesisOrBrackets(pattern); | ||
} | ||
/** | ||
@@ -248,18 +357,2 @@ * Levenshtein distance algorithm from wikipedia | ||
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; | ||
}); | ||
}; | ||
@@ -430,3 +523,3 @@ expect.addAssertion('[not] to be', function (value) { | ||
try { | ||
this.assert(expect.eql(value, this.obj)); | ||
this.assert(this.eql(value, this.obj)); | ||
} catch (e) { | ||
@@ -441,3 +534,3 @@ if (!this.flags.not) { | ||
expect.addAssertion('[not] to throw', '[not] to throw (error|exception)', function (fn) { | ||
expect.addAssertion('[not] to (throw|throw error|throw exception)', function (fn) { | ||
if (typeof this.obj !== 'function') { | ||
@@ -483,5 +576,3 @@ throw new Error("Assertion '" + this.testDescription + | ||
Assertion.prototype.inspect = inspect; | ||
expect.inspect = inspect; | ||
Assertion.prototype.eql = eql; | ||
expect.eql = eql; | ||
@@ -847,2 +938,8 @@ ///////////////////////// Helper functions /////////////////////////////// | ||
function objectCreate(parent) { | ||
function F() {} | ||
F.prototype = parent; | ||
return new F(); | ||
} | ||
function reduce(arr, fun) { | ||
@@ -889,2 +986,15 @@ if (Array.prototype.reduce) { | ||
function extend(target) { | ||
var extensions = Array.prototype.slice.call(arguments, 1); | ||
return reduce(extensions, function (result, extension) { | ||
for(var prop in extension) { | ||
if(hasOwnProperty(extension, prop)) { | ||
target[prop] = extension[prop]; | ||
} | ||
} | ||
return target; | ||
}, target); | ||
} | ||
/** | ||
@@ -957,3 +1067,3 @@ * Asserts deep equality | ||
b = pSlice.call(b); | ||
return expect.eql(a, b); | ||
return eql(a, b); | ||
} | ||
@@ -960,0 +1070,0 @@ var ka, kb, key, i; |
{ | ||
"name": "unexpected", | ||
"version": "1.0.1", | ||
"version": "1.0.2", | ||
"author": "Sune Sloth Simonsen <sune@we-knowhow.dk>", | ||
@@ -5,0 +5,0 @@ "keywords": [ |
/*global describe, it, expect*/ | ||
// use this instead of Object.create in order to make the tests run in | ||
// es5 compatible browsers | ||
// browsers that are not es5 compatible. | ||
function create(o) { | ||
@@ -577,30 +577,166 @@ function F() {} | ||
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'); | ||
describe('addAssertion', function () { | ||
it('is chainable', function () { | ||
expect.addAssertion('foo', function () {}) | ||
.addAssertion('bar', function () {}); | ||
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); | ||
expect(expect.assertions, 'to have keys', | ||
'foo', | ||
'bar'); | ||
}); | ||
describe('pattern', function () { | ||
it("must be a string", function () { | ||
expect(function () { | ||
expect.addAssertion(null, function () {}); | ||
}, 'to throw', "Assertion patterns must be a non empty string"); | ||
}); | ||
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); | ||
it("must be a non empty", function () { | ||
expect(function () { | ||
expect.addAssertion('', function () {}); | ||
}, 'to throw', "Assertion patterns must be a non empty string"); | ||
}); | ||
it("can't start or end with whitespace", function () { | ||
expect(function () { | ||
expect.addAssertion(' ', function () {}); | ||
}, 'to throw', "Assertion patterns can't start or end with whitespace"); | ||
}); | ||
it("can't start with whitespace", function () { | ||
expect(function () { | ||
expect.addAssertion(' foo', function () {}); | ||
}, 'to throw', "Assertion patterns can't start or end with whitespace"); | ||
}); | ||
it("can't end with whitespace", function () { | ||
expect(function () { | ||
expect.addAssertion('foo ', function () {}); | ||
}, 'to throw', "Assertion patterns can't start or end with whitespace"); | ||
}); | ||
it("must not contain unbalanced brackets", function () { | ||
expect(function () { | ||
expect.addAssertion('foo [', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain unbalanced brackets: 'foo ['"); | ||
expect(function () { | ||
expect.addAssertion('foo ]', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain unbalanced brackets: 'foo ]'"); | ||
}); | ||
it("must not contain unbalanced parenthesis", function () { | ||
expect(function () { | ||
expect.addAssertion('foo (', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain unbalanced parenthesis: 'foo ('"); | ||
expect(function () { | ||
expect.addAssertion('foo )', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain unbalanced parenthesis: 'foo )'"); | ||
}); | ||
it("must not only contain flags", function () { | ||
expect(function () { | ||
expect.addAssertion('[foo] [bar]', function () {}); | ||
}, 'to throw', "Assertion patterns must not only contain flags"); | ||
}); | ||
describe('flags', function () { | ||
it("must not be empty", function () { | ||
expect(function () { | ||
expect.addAssertion('foo []', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain empty flags: 'foo []'"); | ||
}); | ||
it("must not contain brackets", function () { | ||
expect(function () { | ||
expect.addAssertion('foo [[bar]', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain flags with brackets: 'foo [[bar]'"); | ||
expect(function () { | ||
expect.addAssertion('foo [[bar]]', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain flags with brackets: 'foo [[bar]]'"); | ||
}); | ||
it("must not contain parenthesis", function () { | ||
expect(function () { | ||
expect.addAssertion('foo [(bar]', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain flags with parenthesis: 'foo [(bar]'"); | ||
expect(function () { | ||
expect.addAssertion('foo [bar)]', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain flags with parenthesis: 'foo [bar)]'"); | ||
}); | ||
}); | ||
describe('alternations', function () { | ||
it("must not be empty", function () { | ||
expect(function () { | ||
expect.addAssertion('foo ()', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain empty alternations: 'foo ()'"); | ||
expect(function () { | ||
expect.addAssertion('foo (bar|)', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain empty alternations: 'foo (bar|)'"); | ||
expect(function () { | ||
expect.addAssertion('foo (||)', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain empty alternations: 'foo (||)'"); | ||
expect(function () { | ||
expect.addAssertion('foo (|bar|)', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain empty alternations: 'foo (|bar|)'"); | ||
}); | ||
it("must not contain brackets", function () { | ||
expect(function () { | ||
expect.addAssertion('foo ([bar)', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain alternations with brackets: 'foo ([bar)'"); | ||
expect(function () { | ||
expect.addAssertion('foo (bar])', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain alternations with brackets: 'foo (bar])'"); | ||
}); | ||
it("must not contain parenthesis", function () { | ||
expect(function () { | ||
expect.addAssertion('foo ((bar)', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain alternations with parenthesis: 'foo ((bar)'"); | ||
expect(function () { | ||
expect.addAssertion('foo ((bar))', function () {}); | ||
}, 'to throw', "Assertion patterns must not contain alternations with parenthesis: 'foo ((bar))'"); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('clone', function () { | ||
var clonedExpect; | ||
beforeEach(function () { | ||
clonedExpect = expect.clone(); | ||
clonedExpect.addAssertion('[not] to be answer to the Ultimate Question of Life, the Universe, and Everything', function () { | ||
this.assert(this.obj === 42); | ||
}); | ||
}); | ||
it('changes to the clone does not affect the orignal instance', function () { | ||
expect(expect.assertions, 'not to have keys', | ||
'to be answer to the Ultimate Question of Life, the Universe, and Everything', | ||
'not to be answer to the Ultimate Question of Life, the Universe, and Everything'); | ||
}); | ||
it('assertions can be added to the clone', function () { | ||
expect(clonedExpect.assertions, 'to have keys', | ||
'to be answer to the Ultimate Question of Life, the Universe, and Everything', | ||
'not to be answer to the Ultimate Question of Life, the Universe, and Everything'); | ||
clonedExpect(42, 'to be answer to the Ultimate Question of Life, the Universe, and Everything'); | ||
clonedExpect(41, 'not to be answer to the Ultimate Question of Life, the Universe, and Everything'); | ||
expect(function () { | ||
clonedExpect(41, 'to be answer to the Ultimate Question of Life, the Universe, and Everything'); | ||
}, 'to throw'); | ||
}); | ||
}); | ||
}); |
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
205687
6387