Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

unexpected

Package Overview
Dependencies
Maintainers
2
Versions
330
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

unexpected - npm Package Compare versions

Comparing version 4.1.6 to 5.0.0-beta1

58

lib/Assertion.js

@@ -1,14 +0,11 @@

var shim = require('./shim');
var bind = shim.bind;
var forEach = shim.forEach;
function Assertion(expect, subject, testDescription, flags, args) {
function Assertion(expect, subject, testDescription, flags, alternations, args) {
this.expect = expect;
this.obj = subject; // deprecated
this.equal = bind(expect.equal, expect); // deprecated
this.equal = expect.equal.bind(expect); // deprecated
this.eql = this.equal; // deprecated
this.inspect = bind(expect.inspect, expect); // deprecated
this.inspect = expect.inspect.bind(expect); // deprecated
this.subject = subject;
this.testDescription = testDescription;
this.flags = flags;
this.alternations = alternations;
this.args = args;

@@ -22,16 +19,47 @@ this.errorMode = 'default';

output.error('expected ')
.append(expect.inspect(this.subject))
.sp().error(this.testDescription);
var preamble = 'expected';
var subjectOutput = expect.inspect(this.subject);
var argsOutput = output.clone();
if (this.args.length > 0) {
output.sp();
forEach(this.args, function (arg, index) {
output.append(expect.inspect(arg));
if (index < this.args.length - 1) {
output.text(', ');
this.args.forEach(function (arg, index) {
if (0 < index) {
argsOutput.text(', ');
}
argsOutput.append(expect.inspect(arg));
}, this);
}
var subjectSize = subjectOutput.size();
var argsSize = argsOutput.size();
var width = preamble.length + subjectSize.width + argsSize.width + this.testDescription.length;
var height = Math.max(subjectSize.height, argsSize.height);
output.error(preamble);
if (subjectSize.height > 1) {
output.nl();
} else {
output.sp();
}
output.append(subjectOutput);
if (subjectSize.height > 1 || (height === 1 && width > 120)) {
output.nl();
} else {
output.sp();
}
output.error(this.testDescription);
if (argsSize.height > 1) {
output.nl();
} else if (argsSize.width > 0) {
output.sp();
}
output.append(argsOutput);
return output;

@@ -38,0 +66,0 @@ };

@@ -1,10 +0,5 @@

var shim = require('./shim');
var forEach = shim.forEach;
var getKeys = shim.getKeys;
var every = shim.every;
var indexOf = shim.indexOf;
var utils = require('./utils');
var isRegExp = utils.isRegExp;
var isArray = utils.isArray;
var extend = utils.extend;

@@ -24,7 +19,7 @@ module.exports = function (expect) {

expect.addAssertion('[not] to be true', function (expect, subject) {
expect.addAssertion('boolean', '[not] to be true', function (expect, subject) {
expect(subject, '[not] to be', true);
});
expect.addAssertion('[not] to be false', function (expect, subject) {
expect.addAssertion('boolean', '[not] to be false', function (expect, subject) {
expect(subject, '[not] to be', false);

@@ -49,7 +44,7 @@ });

expect.addAssertion('[not] to be NaN', function (expect, subject) {
expect.addAssertion('number', '[not] to be NaN', function (expect, subject) {
expect(isNaN(subject), '[not] to be true');
});
expect.addAssertion('[not] to be close to', function (expect, subject, value, epsilon) {
expect.addAssertion('number', '[not] to be close to', function (expect, subject, value, epsilon) {
this.errorMode = 'bubble';

@@ -87,23 +82,38 @@ if (typeof epsilon !== 'number') {

expect.addAssertion('[not] to be (a|an) (boolean|number|string|function|object|array|regexp|regex|regular expression)', function (expect, subject) {
var matches = /(.* be (?:a|an)) ([\w\s]+)/.exec(this.testDescription);
expect(subject, matches[1], matches[2]);
expect(subject, '[not] to be ' + this.alternations[0], this.alternations[1]);
});
forEach(['string', 'array', 'object'], function (type) {
expect.addAssertion('to be (the|an) empty ' + type, function (expect, subject) {
expect(subject, 'to be a', type);
expect(subject, 'to be empty');
});
expect.addAssertion('to be a non-empty ' + type, function (expect, subject) {
expect(subject, 'to be a', type);
expect(subject, 'not to be empty');
});
expect.addAssertion(['string', 'array', 'object'], 'to be (the empty|an empty|a non-empty) (string|array|object)', function (expect, subject) {
expect(subject, 'to be a', this.alternations[1]);
expect(subject, this.alternations[0] === 'a non-empty' ? 'not to be empty' : 'to be empty');
});
expect.addAssertion('[not] to match', function (expect, subject, regexp) {
expect(regexp.exec(subject), '[not] to be truthy');
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;
}
}
subject.replace(new RegExp(regexp.source, 'g'), function ($0, index) {
flushUntilIndex(index);
lastIndex += $0.length;
output.diffRemovedHighlight($0);
});
flushUntilIndex(subject.length);
return {diff: output};
};
}
expect.fail(e);
}
});
expect.addAssertion('[not] to have [own] property', function (expect, subject, key, value) {
expect.addAssertion('object', '[not] to have [own] property', function (expect, subject, key, value) {
if (arguments.length === 4) {

@@ -120,5 +130,5 @@ expect(subject, 'to have [own] property', key);

expect.addAssertion('[not] to have [own] properties', function (expect, subject, properties) {
expect.addAssertion('object', '[not] to have [own] properties', function (expect, subject, properties) {
if (properties && isArray(properties)) {
forEach(properties, function (property) {
properties.forEach(function (property) {
expect(subject, '[not] to have [own] property', property);

@@ -128,4 +138,5 @@ });

// TODO the not flag does not make a lot of sense in this case
if (this.flags.not) {
forEach(getKeys(properties), function (property) {
var flags = this.flags;
if (flags.not) {
Object.keys(properties).forEach(function (property) {
expect(subject, 'not to have [own] property', property);

@@ -135,3 +146,3 @@ });

try {
forEach(getKeys(properties), function (property) {
Object.keys(properties).forEach(function (property) {
var value = properties[property];

@@ -145,14 +156,18 @@ if (typeof value === 'undefined') {

} catch (e) {
e.actual = expect.sanitize(subject);
e.expected = expect.sanitize(properties);
for (var propertyName in subject) {
if ((!this.flags.own || subject.hasOwnProperty(propertyName)) && !(propertyName in properties)) {
e.expected[propertyName] = expect.sanitize(subject[propertyName]);
}
if (!this.flags.own && !(propertyName in e.actual)) {
e.actual[propertyName] = expect.sanitize(subject[propertyName]);
}
if (e._isUnexpected) {
e.createDiff = function () {
var expected = extend({}, properties);
var actual = {};
for (var propertyName in subject) {
if ((!flags.own || subject.hasOwnProperty(propertyName)) && !(propertyName in properties)) {
expected[propertyName] = subject[propertyName];
}
if (!flags.own && !(propertyName in actual)) {
actual[propertyName] = subject[propertyName];
}
}
return expect.diff(actual, expected);
};
}
e.showDiff = true;
throw e;
expect.fail(e);
}

@@ -166,4 +181,4 @@ }

expect.addAssertion('[not] to have length', function (expect, subject, length) {
if (!subject || typeof subject.length !== 'number') {
expect.addAssertion(['string', 'object'], '[not] to have length', function (expect, subject, length) {
if ((!subject && typeof subject !== 'string') || typeof subject.length !== 'number') {
throw new Error("Assertion '" + this.testDescription +

@@ -175,13 +190,8 @@ "' only supports array like objects");

expect.addAssertion('[not] to be empty', function (expect, subject) {
expect.addAssertion(['string', 'object'], '[not] to be empty', function (expect, subject) {
var length;
if (subject && 'number' === typeof subject.length) {
if (isArray(subject) || typeof subject === 'string' || typeof subject.length === 'number') {
length = subject.length;
} else if (isArray(subject) || typeof subject === 'string') {
length = subject.length;
} else if (subject && typeof subject === 'object') {
length = getKeys(subject).length;
} else {
throw new Error("Assertion '" + this.testDescription +
"' only supports strings, arrays and objects");
length = Object.keys(subject).length;
}

@@ -191,7 +201,7 @@ expect(length, '[not] to be', 0);

expect.addAssertion('to be non-empty', function (expect, subject) {
expect.addAssertion(['string', 'object'], 'to be non-empty', function (expect, subject) {
expect(subject, 'not to be empty');
});
expect.addAssertion('to [not] [only] have (key|keys)', '[not] to have (key|keys)', function (expect, subject, keys) {
expect.addAssertion('object', ['to [not] [only] have (key|keys)', '[not] to have (key|keys)'], function (expect, subject, keys) {
keys = isArray(keys) ?

@@ -201,3 +211,3 @@ keys :

var hasKeys = subject && every(keys, function (key) {
var hasKeys = subject && keys.every(function (key) {
return subject.hasOwnProperty(key);

@@ -207,3 +217,3 @@ });

expect(hasKeys, 'to be truthy');
expect(getKeys(subject).length === keys.length, '[not] to be truthy');
expect(Object.keys(subject).length === keys.length, '[not] to be truthy');
} else {

@@ -214,57 +224,89 @@ expect(hasKeys, '[not] to be truthy');

expect.addAssertion('[not] to contain', function (expect, subject, arg) {
expect.addAssertion(['string', 'array'], '[not] to contain', function (expect, subject) {
var args = Array.prototype.slice.call(arguments, 2);
var that = this;
if ('string' === typeof subject) {
forEach(args, function (arg) {
expect(subject.indexOf(arg) !== -1, '[not] to be truthy');
});
} else if (isArray(subject)) {
forEach(args, function (arg) {
expect(subject && indexOf(subject, arg) !== -1, '[not] to be truthy');
});
} else if (subject === null) {
expect(that.flags.not, '[not] to be falsy');
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);
});
flushUntilIndex(subject.length);
return {diff: output};
};
}
expect.fail(e);
}
} else {
throw new Error("Assertion '" + this.testDescription +
"' only supports strings and arrays");
// 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.addAssertion('[not] to be finite', function (expect, subject) {
expect(typeof subject === 'number' && isFinite(subject), '[not] to be truthy');
expect.addAssertion('number', '[not] to be finite', function (expect, subject) {
expect(isFinite(subject), '[not] to be true');
});
expect.addAssertion('[not] to be infinite', function (expect, subject) {
expect(typeof subject === 'number' && !isNaN(subject) && !isFinite(subject), '[not] to be truthy');
expect.addAssertion('number', '[not] to be infinite', function (expect, subject) {
expect(!isNaN(subject) && !isFinite(subject), '[not] to be truthy');
});
expect.addAssertion('[not] to be within', function (expect, subject, start, finish) {
expect.addAssertion(['number', 'string'], '[not] to be within', function (expect, subject, start, finish) {
this.args = [start + '..' + finish];
expect(subject, 'to be a number');
expect(subject >= start && subject <= finish, '[not] to be true');
});
expect.addAssertion('<', '[not] to be (<|less than|below)', function (expect, subject, value) {
expect.addAssertion(['number', 'string'], ['<', '[not] to be (<|less than|below)'], function (expect, subject, value) {
expect(subject < value, '[not] to be true');
});
expect.addAssertion('<=', '[not] to be (<=|less than or equal to)', function (expect, subject, value) {
expect.addAssertion(['number', 'string'], ['<=', '[not] to be (<=|less than or equal to)'], function (expect, subject, value) {
expect(subject <= value, '[not] to be true');
});
expect.addAssertion('>', '[not] to be (>|greater than|above)', function (expect, subject, value) {
expect.addAssertion(['number', 'string'], ['>', '[not] to be (>|greater than|above)'], function (expect, subject, value) {
expect(subject > value, '[not] to be true');
});
expect.addAssertion('>=', '[not] to be (>=|greater than or equal to)', function (expect, subject, value) {
expect.addAssertion(['number', 'string'], ['>=', '[not] to be (>=|greater than or equal to)'], function (expect, subject, value) {
expect(subject >= value, '[not] to be true');
});
expect.addAssertion('[not] to be positive', function (expect, subject) {
expect.addAssertion('number', '[not] to be positive', function (expect, subject) {
expect(subject, '[not] to be >', 0);
});
expect.addAssertion('[not] to be negative', function (expect, subject) {
expect.addAssertion('number', '[not] to be negative', function (expect, subject) {
expect(subject, '[not] to be <', 0);

@@ -277,25 +319,13 @@ });

} catch (e) {
if (!this.flags.not) {
e.expected = expect.sanitize(value);
e.actual = expect.sanitize(subject);
// Explicitly tell mocha to stringify and diff arrays
// and objects, but only when the types are identical
// and non-primitive:
if (e.actual && e.expected &&
typeof e.actual === 'object' &&
typeof e.expected === 'object' &&
isArray(e.actual) === isArray(e.expected)) {
e.showDiff = true;
}
if (!this.flags.not && e._isUnexpected) {
e.createDiff = function () {
return expect.diff(subject, value);
};
}
throw e;
expect.fail(e);
}
});
expect.addAssertion('[not] to (throw|throw error|throw exception)', function (expect, subject, arg) {
expect.addAssertion('function', '[not] to (throw|throw error|throw exception)', function (expect, subject, arg) {
this.errorMode = 'nested';
if (typeof subject !== 'function') {
throw new Error("Assertion '" + this.testDescription +
"' only supports functions");
}

@@ -308,19 +338,22 @@ var thrown = false;

} catch (e) {
var subject;
if (e._isUnexpected) {
subject = e.output.toString();
} else if (typeof e === 'string') {
subject = e;
if ('function' === argType) {
arg(e);
} else {
subject = e.message;
}
var message,
isUnexpected = e && e._isUnexpected;
if (isUnexpected) {
message = e.output.toString();
} else if (e && Object.prototype.toString.call(e) === '[object Error]') {
message = e.message;
} else {
message = String(e);
}
if ('function' === argType) {
arg(e);
} else if ('string' === argType) {
expect(subject, '[not] to equal', arg);
} else if (isRegExp(arg)) {
expect(subject, '[not] to match', arg);
} else if (this.flags.not) {
expect.fail('threw: {0}', e._isUnexpected ? e.output : subject);
if ('string' === argType) {
expect(message, '[not] to equal', arg);
} else if (isRegExp(arg)) {
expect(message, '[not] to match', arg);
} else if (this.flags.not) {
expect.fail('threw: {0}', isUnexpected ? e.output : expect.inspect(e));
}
}

@@ -340,13 +373,6 @@ thrown = true;

expect.addAssertion('to be (a|an) [non-empty] (map|hash|object) whose values satisfy', function (expect, subject, callbackOrString) {
var callback;
if ('function' === typeof callbackOrString) {
callback = callbackOrString;
} else if ('string' === typeof callbackOrString) {
var args = Array.prototype.slice.call(arguments, 2);
callback = function (value) {
expect.apply(expect, [value].concat(args));
};
} else {
throw new Error('Assertion "' + this.testDescription + '" expects a function as argument');
expect.addAssertion('object', 'to be (a|an) [non-empty] (map|hash|object) whose values satisfy', function (expect, subject) {
var extraArgs = Array.prototype.slice.call(arguments, 2);
if (extraArgs.length === 0) {
throw new Error('Assertion "' + this.testDescription + '" expects a third argument');
}

@@ -361,5 +387,9 @@ this.errorMode = 'nested';

var errors = {};
forEach(getKeys(subject), function (key, index) {
Object.keys(subject).forEach(function (key, index) {
try {
callback(subject[key], index);
if (typeof extraArgs[0] === 'function') {
extraArgs[0](subject[key], index);
} else {
expect.apply(expect, [subject[key], 'to satisfy assertion'].concat(extraArgs));
}
} catch (e) {

@@ -370,3 +400,3 @@ errors[key] = e;

var errorKeys = getKeys(errors);
var errorKeys = Object.keys(errors);
if (errorKeys.length > 0) {

@@ -385,3 +415,3 @@ expect.fail(function (output) {

forEach(errorKeys, function (key, index) {
errorKeys.forEach(function (key, index) {
var error = errors[key];

@@ -403,13 +433,6 @@ output.i().text(key).text(': ');

expect.addAssertion('to be (a|an) [non-empty] array whose items satisfy', function (expect, subject, callbackOrString) {
var callback;
if ('function' === typeof callbackOrString) {
callback = callbackOrString;
} else if ('string' === typeof callbackOrString) {
var args = Array.prototype.slice.call(arguments, 2);
callback = function (item) {
expect.apply(expect, [item].concat(args));
};
} else {
throw new Error('Assertion "' + this.testDescription + '" expects a function as argument');
expect.addAssertion('array', 'to be (a|an) [non-empty] array whose items satisfy', function (expect, subject) { // ...
var extraArgs = Array.prototype.slice.call(arguments, 2);
if (extraArgs.length === 0) {
throw new Error('Assertion "' + this.testDescription + '" expects a third argument');
}

@@ -422,28 +445,17 @@ this.errorMode = 'nested';

this.errorMode = 'bubble';
expect(subject, 'to be a map whose values satisfy', callback);
expect.apply(expect, [subject, 'to be a map whose values satisfy'].concat(extraArgs));
});
forEach(['string', 'number', 'boolean', 'array', 'object', 'function', 'regexp', 'regex', 'regular expression'], function (type) {
expect.addAssertion('to be (a|an) [non-empty] array of ' + type + 's', function (expect, subject) {
expect(subject, 'to be an array whose items satisfy', function (item) {
expect(item, 'to be a', type);
});
if (this.flags['non-empty']) {
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) {
if (this.flags['non-empty']) {
expect(subject, 'to be non-empty');
}
var type = this.alternations[1].replace(/e?s$/, '');
expect(subject, 'to be an array whose items satisfy', 'to be a', type);
});
expect.addAssertion('to be (a|an) [non-empty] (map|hash|object) whose keys satisfy', function (expect, subject, callbackOrString) {
var callback;
if ('function' === typeof callbackOrString) {
this.errorMode = 'nested';
callback = callbackOrString;
} else if ('string' === typeof callbackOrString) {
var args = Array.prototype.slice.call(arguments, 2);
callback = function (key) {
expect.apply(expect, [key].concat(args));
};
} else {
throw new Error('Assertion "' + this.testDescription + '" expects a function as argument');
expect.addAssertion('object', 'to be (a|an) [non-empty] (map|hash|object) whose (keys|properties) satisfy', function (expect, subject) {
var extraArgs = Array.prototype.slice.call(arguments, 2);
if (extraArgs.length === 0) {
throw new Error('Assertion "' + this.testDescription + '" expects a third argument');
}

@@ -458,5 +470,9 @@ this.errorMode = 'nested';

var errors = {};
forEach(getKeys(subject), function (key, index) {
Object.keys(subject).forEach(function (key, index) {
try {
callback(key);
if (typeof extraArgs[0] === 'function') {
extraArgs[0](key, subject[key]);
} else {
expect.apply(expect, [key, 'to satisfy assertion'].concat(extraArgs));
}
} catch (e) {

@@ -467,11 +483,11 @@ errors[key] = e;

var errorKeys = getKeys(errors);
var errorKeys = Object.keys(errors);
if (errorKeys.length > 0) {
expect.fail(function (output) {
output.error('failed expectation on keys ')
.text(getKeys(subject).join(', '))
.text(Object.keys(subject).join(', '))
.error(':').nl()
.indentLines();
forEach(errorKeys, function (key, index) {
errorKeys.forEach(function (key, index) {
var error = errors[key];

@@ -493,23 +509,60 @@ output.i().text(key).text(': ');

expect.addAssertion('to be canonical', function (expect, subject, stack) {
stack = stack || [];
expect.addAssertion('object', 'to be canonical', function (expect, subject) {
var stack = [];
var i;
for (i = 0 ; i < stack.length ; i += 1) {
if (stack[i] === subject) {
(function traverse(obj) {
var i;
for (i = 0 ; i < stack.length ; i += 1) {
if (stack[i] === obj) {
return;
}
}
if (obj && typeof obj === 'object') {
var keys = Object.keys(obj);
for (i = 0 ; i < keys.length - 1 ; i += 1) {
expect(keys[i], 'to be less than', keys[i + 1]);
}
stack.push(obj);
keys.forEach(function (key) {
traverse(obj[key]);
});
stack.pop();
}
}(subject));
});
expect.addAssertion('[not] to [exhaustively] satisfy [assertion]', function (expect, subject, value) {
if (this.flags.not) {
try {
expect(subject, 'to [exhaustively] satisfy [assertion]', value);
} catch (e) {
if (!e || !e._isUnexpected) {
throw e;
}
return;
}
}
if (subject && typeof subject === 'object') {
var keys = getKeys(subject);
for (i = 0 ; i < keys.length - 1 ; i += 1) {
expect(keys[i], 'to be less than', keys[i + 1]);
expect.fail();
} else if (this.flags.assertion && typeof value === 'string') {
this.errorMode = 'bubble'; // to satisfy assertion 'to be a number' => to be a number
expect.apply(expect, Array.prototype.slice.call(arguments, 1));
} else if (typeof value === 'function') {
// FIXME: If expect.fn, it should be possible to produce a better error message
value(subject);
} else if (isRegExp(value)) {
expect(subject, 'to match', value);
} else {
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));
}
} else {
expect(subject, 'to equal', value);
}
stack.push(subject);
forEach(keys, function (key) {
expect(subject[key], 'to be canonical', stack);
});
stack.pop(subject);
}
});
};
module.exports = function (expect) {
expect.output.addStyle('error', function (content) {
this.text(content, 'red, bold');
this.text(content, 'red', 'bold');
});
expect.output.addStyle('strings', function (content) {
this.text(content, 'cyan');
this.text(content, '#00A0A0');
});

@@ -11,2 +11,26 @@ expect.output.addStyle('key', function (content) {

});
expect.output.addStyle('comment', function (content) {
this.gray(content);
});
expect.output.addStyle('regexp', function (content) {
this.green(content);
});
expect.output.addStyle('diffAddedLine', function (content) {
this.text(content, 'green');
});
expect.output.addStyle('diffAddedHighlight', function (content) {
this.text(content, 'bgGreen', 'white');
});
expect.output.addStyle('diffAddedSpecialChar', function (content) {
this.text(content, 'bgGreen', 'cyan', 'bold');
});
expect.output.addStyle('diffRemovedLine', function (content) {
this.text(content, 'red');
});
expect.output.addStyle('diffRemovedHighlight', function (content) {
this.text(content, 'bgRed', 'white');
});
expect.output.addStyle('diffRemovedSpecialChar', function (content) {
this.text(content, 'bgRed', 'cyan', 'bold');
});
// Intended to be redefined by a plugin that offers syntax highlighting:

@@ -16,2 +40,3 @@ expect.output.addStyle('code', function (content, language) {

});
};

@@ -1,15 +0,6 @@

/*global Uint8Array, Uint16Array*/
var utils = require('./utils');
var isRegExp = utils.isRegExp;
var leftPad = utils.leftPad;
var shim = require('./shim');
var json = shim.JSON;
var every = shim.every;
var some = shim.some;
var forEach = shim.forEach;
var map = shim.map;
var getKeys = shim.getKeys;
var reduce = shim.reduce;
var extend = utils.extend;
var arrayDiff = require('arraydiff');

@@ -27,14 +18,6 @@ module.exports = function (expect) {

// an identical "prototype" property.
if (a.prototype !== b.prototype) {
if (b.constructor !== a.constructor) {
return false;
}
//~~~I've managed to break Object.keys through screwy arguments passing.
// Converting to array solves the problem.
if (utils.isArguments(a)) {
if (!utils.isArguments(b)) {
return false;
}
return equal(Array.prototype.slice.call(a), Array.prototype.slice.call(b));
}
var actualKeys = utils.getKeysOfDefinedProperties(a),

@@ -69,4 +52,4 @@ expectedKeys = utils.getKeysOfDefinedProperties(b),

},
inspect: function (output, obj, inspect) {
var keys = getKeys(obj);
inspect: function (obj, depth, output, inspect) {
var keys = Object.keys(obj);
if (keys.length === 0) {

@@ -76,8 +59,8 @@ return output.text('{}');

var inspectedItems = map(keys, function (key) {
var inspectedItems = keys.map(function (key) {
var propertyOutput = output.clone();
if (key.match(/["' ]/)) {
propertyOutput.append(expect.inspect(key));
if (/^[a-z\$\_][a-z0-9\$\_]*$/i.test(key)) {
propertyOutput.key(key);
} else {
propertyOutput.key(key);
propertyOutput.append(inspect(key, depth));
}

@@ -90,3 +73,3 @@ propertyOutput.text(':');

if (hasGetter || !hasSetter) {
propertyOutput.sp().append(inspect(propertyOutput.clone(), obj[key]));
propertyOutput.sp().append(inspect(obj[key]));
}

@@ -106,3 +89,3 @@

var width = 0;
var multipleLines = some(inspectedItems, function (o) {
var multipleLines = inspectedItems.some(function (o) {
var size = o.size();

@@ -113,3 +96,3 @@ width += size.width;

forEach(inspectedItems, function (inspectedItem, index) {
inspectedItems.forEach(function (inspectedItem, index) {
var lastIndex = index === inspectedItems.length - 1;

@@ -125,3 +108,3 @@

forEach(inspectedItems, function (inspectedItem, index) {
inspectedItems.forEach(function (inspectedItem, index) {
output.i().block(inspectedItem).nl();

@@ -133,3 +116,3 @@ });

output.text('{ ');
forEach(inspectedItems, function (inspectedItem, index) {
inspectedItems.forEach(function (inspectedItem, index) {
output.append(inspectedItem);

@@ -145,12 +128,117 @@ var lastIndex = index === inspectedItems.length - 1;

},
toJSON: function (obj, toJSON) {
return reduce(getKeys(obj), function (result, key) {
result[key] = toJSON(obj[key]);
return result;
}, {});
diff: function (actual, expected, output, diff, inspect, equal) {
if (actual.constructor !== expected.constructor) {
return {
diff: output.text('Mismatching constructors ')
.text(actual.constructor.name || actual.constructor)
.text(' should be ').text(expected.constructor.name || expected.constructor),
inline: false
};
}
var result = {
diff: output,
inline: true
};
var keyIndex = {};
Object.keys(actual).concat(Object.keys(expected)).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 = !equal(actual[key], expected[key]);
var isInlineDiff = false;
if (conflicting) {
if (!(key in expected)) {
annotation.error('should be removed');
} else {
var keyDiff = diff(actual[key], expected[key]);
if (!keyDiff || (keyDiff && !keyDiff.inline)) {
annotation.error('should be: ')
.block(inspect(expected[key]));
if (keyDiff) {
annotation.nl().append(keyDiff.diff);
}
} else {
isInlineDiff = true;
valueOutput = keyDiff.diff;
}
}
}
var last = index === keys.length - 1;
if (!valueOutput) {
valueOutput = inspect(actual[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;
}
});
function structurallySimilar(a, b) {
var typeA = typeof a;
var typeB = typeof b;
if (typeA !== typeB) {
return false;
}
if (typeA === 'string') {
return utils.levenshteinDistance(a, b) < a.length / 2;
}
if (typeA !== 'object' || !a) {
return false;
}
if (utils.isArray(a) && utils.isArray(b)) {
return true;
}
var aKeys = Object.keys(a);
var bKeys = Object.keys(b);
var numberOfSimilarKeys = 0;
var requiredSimilarKeys = Math.round(Math.max(aKeys.length, bKeys.length) / 2);
return aKeys.concat(bKeys).some(function (key) {
if (key in a && key in b) {
numberOfSimilarKeys += 1;
}
return numberOfSimilarKeys >= requiredSimilarKeys;
});
}
expect.addType({
name: 'array',
base: 'object',
identify: function (arr) {

@@ -160,7 +248,7 @@ return utils.isArray(arr) || utils.isArguments(arr);

equal: function (a, b, equal) {
return a === b || (a.length === b.length && every(a, function (v, index) {
return a === b || (a.length === b.length && a.every(function (v, index) {
return equal(v, b[index]);
}));
},
inspect: function (output, arr, inspect, depth) {
inspect: function (arr, depth, output, inspect) {
if (arr.length === 0) {

@@ -174,8 +262,12 @@ return output.text('[]');

var inspectedItems = map(arr, function (v) {
return inspect(output.clone(), v);
if (utils.isArguments(arr)) {
arr = Array.prototype.slice.call(arr);
}
var inspectedItems = arr.map(function (v) {
return inspect(v);
});
var width = 0;
var multipleLines = some(inspectedItems, function (o) {
var multipleLines = inspectedItems.some(function (o) {
var size = o.size();

@@ -186,3 +278,3 @@ width += size.width;

forEach(inspectedItems, function (inspectedItem, index) {
inspectedItems.forEach(function (inspectedItem, index) {
var lastIndex = index === inspectedItems.length - 1;

@@ -197,3 +289,3 @@ if (!lastIndex) {

forEach(inspectedItems, function (inspectedItem, index) {
inspectedItems.forEach(function (inspectedItem, index) {
output.i().block(inspectedItem).nl();

@@ -205,3 +297,3 @@ });

output.text('[ ');
forEach(inspectedItems, function (inspectedItem, index) {
inspectedItems.forEach(function (inspectedItem, index) {
output.append(inspectedItem);

@@ -217,4 +309,143 @@ var lastIndex = index === inspectedItems.length - 1;

},
toJSON: function (arr, toJSON) {
return map(arr, toJSON);
diff: function (actual, expected, output, diff, inspect, equal) {
var result = {
diff: output,
inline: true
};
if (utils.isArguments(actual)) {
actual = Array.prototype.slice.call(actual);
}
if (utils.isArguments(expected)) {
expected = Array.prototype.slice.call(expected);
}
var mutatedArray = actual.map(function (v) {
return {
type: 'similar',
value: v
};
});
if (mutatedArray.length > 0) {
mutatedArray[mutatedArray.length - 1].last = true;
}
var itemsDiff = arrayDiff(actual, expected, function (a, b) {
return equal(a, b) || structurallySimilar(a, b);
});
var removeTable = [];
function offsetIndex(index) {
return index + (removeTable[index - 1] || 0);
}
var removes = itemsDiff.filter(function (diffItem) {
return diffItem.type === 'remove';
});
var removesByIndex = {};
var removedItems = 0;
removes.forEach(function (diffItem) {
var removeIndex = removedItems + diffItem.index;
mutatedArray.slice(removeIndex, diffItem.howMany + removeIndex).forEach(function (v) {
v.type = 'remove';
});
removedItems += diffItem.howMany;
removesByIndex[diffItem.index] = removedItems;
});
removedItems = 0;
actual.forEach(function (_, index) {
removedItems += removesByIndex[index] || 0;
removeTable[index] = removedItems;
});
var moves = itemsDiff.filter(function (diffItem) {
return diffItem.type === 'move';
});
var movedItems = 0;
moves.forEach(function (diffItem) {
var moveFromIndex = offsetIndex(diffItem.from);
var removed = mutatedArray.slice(moveFromIndex, diffItem.howMany + moveFromIndex);
var added = removed.map(function (v) {
return utils.extend({}, v, { type: 'insert' });
});
removed.forEach(function (v) {
v.type = 'remove';
});
Array.prototype.splice.apply(mutatedArray, [offsetIndex(diffItem.to), 0].concat(added));
movedItems += diffItem.howMany;
removesByIndex[diffItem.from] = movedItems;
});
removedItems = 0;
actual.forEach(function (_, index) {
removedItems += removesByIndex[index] || 0;
removeTable[index] = removedItems;
});
var inserts = itemsDiff.filter(function (diffItem) {
return diffItem.type === 'insert';
});
inserts.forEach(function (diffItem) {
var added = diffItem.values.map(function (v) {
return {
type: 'insert',
value: v
};
});
Array.prototype.splice.apply(mutatedArray, [offsetIndex(diffItem.index), 0].concat(added));
});
var offset = 0;
mutatedArray.forEach(function (diffItem, index) {
var type = diffItem.type;
if (type === 'remove') {
offset -= 1;
} else if (type === 'similar') {
diffItem.expected = expected[offset + index];
}
});
output.text('[').nl().indentLines();
mutatedArray.forEach(function (diffItem, index) {
output.i().block(function () {
var type = diffItem.type;
var last = !!diffItem.last;
if (type === 'insert') {
this.error('missing: ').block(inspect(diffItem.value))
.prependLinesWith('error', '// ');
} else if (type === 'remove') {
this.block(inspect(diffItem.value).text(last ? '' : ',').error(' // should be removed'));
} else if (equal(diffItem.value, diffItem.expected)) {
this.block(inspect(diffItem.value).text(last ? '' : ','));
} else {
var valueDiff = diff(diffItem.value, diffItem.expected);
if (valueDiff && valueDiff.inline) {
this.block(valueDiff.diff.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', ' // ');
});
} else {
this.block(inspect(diffItem.value).text(last ? '' : ',')).block(function () {
this.error('should be: ').append(inspect(diffItem.expected))
.prependLinesWith('error', ' // ');
});
}
}
}).nl();
});
output.outdentLines().text(']');
return result;
}

@@ -233,7 +464,14 @@ });

},
inspect: function (output, value, inspect) {
inspect: function (value, depth, output, inspect) {
var errorObject = extend({
message: value.message
}, value);
return output.text('[Error: ').append(inspect(output.clone(), errorObject)).text(']');
return output.text('[Error: ').append(inspect(errorObject, depth)).text(']');
},
diff: function (actual, expected, output, diff) {
return diff(extend({
message: actual.message
}, actual), extend({
message: expected.message
}, expected));
}

@@ -250,9 +488,4 @@ });

},
inspect: function (output, date) {
inspect: function (date, depth, output) {
return output.text('[Date ').text(date.toUTCString()).text(']');
},
toJSON: function (date) {
return {
$Date: expect.inspect(date).toString()
};
}

@@ -262,2 +495,3 @@ });

expect.addType({
base: 'object',
name: 'function',

@@ -270,14 +504,56 @@ identify: function (f) {

},
inspect: function (output, f) {
output.text('[Function');
if (f.name) {
output.text(': ').text(f.name);
inspect: function (f, depth, output) {
var source = f.toString();
var matchSource = source.match(/^function (\w*)?\s*\(([^\)]*)\)\s*\{([\s\S]*?( *)?)\}$/);
if (matchSource) {
var name = matchSource[1];
var args = matchSource[2];
var body = matchSource[3];
var bodyIndent = matchSource[4] || '';
if (!/\\\n/.test(body)) {
body = body.replace(new RegExp('^ {' + bodyIndent.length + '}', 'mg'), '');
}
if (!name || name === 'anonymous') {
name = '';
}
if (/^\s*\[native code\]\s*$/.test(body)) {
body = ' /* native code */ ';
} else {
body = body.replace(/^((?:.*\n){3}( *).*\n)[\s\S]*?\n((?:.*\n){3})$/, '$1$2// ... lines removed ...\n$3');
}
output.code('function ' + name + '(' + args + ') {' + body + '}', 'javascript');
} else {
if (f.name) {
output.sp().code(f.name, 'javascript');
}
output.text('(...) {...}');
}
output.text(']');
return output;
}
});
expect.addType({
base: 'function',
name: 'expect.it',
identify: function (f) {
return typeof f === 'function' && f._expectIt;
},
toJSON: function (f) {
return {
$Function: expect.inspect(f).toString()
};
inspect: function (f, depth, output, inspect) {
output.text('expect.it(');
f._expectations.forEach(function (expectation, index) {
if (0 < index) {
output.text(').and(');
}
var args = Array.prototype.slice.call(expectation);
args.forEach(function (arg, i) {
if (0 < i) {
output.text(', ');
}
output.append(inspect(arg));
});
});
output.text(')');
return output;
}

@@ -297,9 +573,4 @@ });

},
inspect: function (output, regExp) {
return output.text(regExp);
},
toJSON: function (regExp) {
return {
$RegExp: expect.inspect(regExp).toString()
};
inspect: function (regExp, depth, output) {
return output.regexp(regExp);
}

@@ -313,3 +584,3 @@ });

},
inspect: function (output, value) {
inspect: function (value, depth, output) {
return output.code(utils.getOuterHTML(value), 'html');

@@ -319,130 +590,135 @@ }

function getHexDumpLinesForBufferLikeObject(obj, width, digitWidth, maxLength) {
digitWidth = digitWidth || 2;
var hexDumpLines = [];
if (typeof maxLength === 'undefined') {
maxLength = obj.length;
}
if (typeof width !== 'number') {
width = 16;
} else if (width === 0) {
width = maxLength;
}
for (var i = 0 ; i < maxLength ; i += width) {
var hexChars = '',
asciiChars = ' |';
expect.addType({
name: 'binaryArray',
base: 'array',
digitWidth: 2,
hexDumpWidth: 16,
identify: function () {
return false;
},
equal: function (a, b) {
if (a === b) {
return true;
}
for (var j = 0 ; j < width ; j += 1) {
if (i + j < maxLength) {
var octet = obj[i + j];
hexChars += leftPad(octet.toString(16).toUpperCase(), digitWidth, '0') + ' ';
asciiChars += (octet >= 32 && octet < 127) ? String.fromCharCode(octet) : '.';
} else if (digitWidth === 2) {
hexChars += ' ';
}
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; i += 1) {
if (a[i] !== b[i]) return false;
}
if (digitWidth === 2) {
asciiChars += '|';
hexDumpLines.push(hexChars + asciiChars);
} else {
hexDumpLines.push(hexChars.replace(/\s+$/, ''));
return true;
},
hexDump: function (obj, maxLength) {
var hexDump = '';
if (typeof maxLength !== 'number' || maxLength === 0) {
maxLength = obj.length;
}
}
return hexDumpLines;
}
for (var i = 0 ; i < maxLength ; i += this.hexDumpWidth) {
if (hexDump.length > 0) {
hexDump += '\n';
}
var hexChars = '',
asciiChars = ' │';
function inspectBufferLikeObject(output, buffer, digitWidth, maxLength) {
if (typeof maxLength !== 'number') {
maxLength = 20;
}
if (buffer.length > maxLength) {
output.text(getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth, maxLength))
.text(' (+').text(buffer.length - maxLength).text(')');
for (var j = 0 ; j < this.hexDumpWidth ; j += 1) {
if (i + j < maxLength) {
var octet = obj[i + j];
hexChars += leftPad(octet.toString(16).toUpperCase(), this.digitWidth, '0') + ' ';
asciiChars += String.fromCharCode(octet);
} else if (this.digitWidth === 2) {
hexChars += ' ';
}
}
} else {
var hexDumpLines = getHexDumpLinesForBufferLikeObject(buffer, 0, digitWidth);
forEach(hexDumpLines, function (line, i) {
if (this.digitWidth === 2) {
hexDump += hexChars + asciiChars + '│';
} else {
hexDump += hexChars.replace(/\s+$/, '');
}
}
return hexDump;
},
inspect: function (obj, depth, output) {
var codeStr = this.name + '([';
for (var i = 0 ; i < Math.min(this.hexDumpWidth, obj.length) ; i += 1) {
if (i > 0) {
output.nl();
codeStr += ', ';
}
output.text(line);
});
var octet = obj[i];
codeStr += '0x' + leftPad(octet.toString(16).toUpperCase(), this.digitWidth, '0');
}
if (obj.length > this.hexDumpWidth) {
codeStr += ' /* ' + (obj.length - this.hexDumpWidth) + ' more */ ';
}
codeStr += '])';
return output.code(codeStr, 'javascript');
},
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;
}
}
return result;
}
return output;
}
});
function bufferLikeObjectsEqual(a, b) {
if (a === b) {
return true;
}
if (a.length !== b.length) return false;
for (var i = 0; i < a.length; i += 1) {
if (a[i] !== b[i]) return false;
}
return true;
}
if (typeof Buffer !== 'undefined') {
expect.addType({
name: 'Buffer',
identify: Buffer.isBuffer,
equal: bufferLikeObjectsEqual,
inspect: function (output, buffer) {
output.text('[Buffer ');
inspectBufferLikeObject(output, buffer);
return output.text(']');
},
toJSON: function (buffer) {
return {
$Buffer: getHexDumpLinesForBufferLikeObject(buffer)
};
}
base: 'binaryArray',
identify: Buffer.isBuffer
});
}
if (typeof Uint8Array !== 'undefined') {
expect.addType({
name: 'Uint8Array',
identify: function (obj) {
return obj && obj instanceof Uint8Array;
},
equal: bufferLikeObjectsEqual,
inspect: function (output, uint8Array) {
output.text('[Uint8Array ');
inspectBufferLikeObject(output, uint8Array);
return output.text(']');
},
toJSON: function (uint8Array) {
return {
$Uint8Array: getHexDumpLinesForBufferLikeObject(uint8Array)
};
[8, 16, 32].forEach(function (numBits) {
['Int', 'Uint'].forEach(function (intOrUint) {
var constructorName = intOrUint + numBits + 'Array',
Constructor = this[constructorName];
if (typeof Constructor !== 'undefined') {
expect.addType({
name: constructorName,
base: 'binaryArray',
hexDumpWidth: 128 / numBits,
digitWidth: numBits / 4,
identify: function (obj) {
return obj instanceof Constructor;
}
});
}
});
}
}, this);
}, this);
if (typeof Uint16Array !== 'undefined') {
expect.addType({
name: 'Uint16Array',
identify: function (obj) {
return obj && obj instanceof Uint16Array;
},
equal: bufferLikeObjectsEqual,
inspect: function (output, uint16Array) {
output.text('[Uint16Array ');
inspectBufferLikeObject(output, uint16Array, 4);
return output.text(']');
},
toJSON: function (uint16Array) {
return {
$Uint16Array: getHexDumpLinesForBufferLikeObject(uint16Array, 8, 4)
};
}
});
}
expect.addType({

@@ -453,8 +729,16 @@ name: 'string',

},
inspect: function (output, value) {
inspect: function (value, depth, output) {
return output.strings('\'')
.strings(json.stringify(value).replace(/^"|"$/g, '')
.strings(JSON.stringify(value).replace(/^"|"$/g, '')
.replace(/'/g, "\\'")
.replace(/\\"/g, '"'))
.strings('\'');
},
diff: function (actual, expected, output, diff, inspect) {
var result = {
diff: output,
inline: false
};
utils.diffStrings(actual, expected, output, {type: 'WordsWithSpace', markUpSpecialCharacters: true});
return result;
}

@@ -461,0 +745,0 @@ });

var Assertion = require('./Assertion');
var shim = require('./shim');
var utils = require('./utils');
var magicpen = require('magicpen');
var bind = shim.bind;
var forEach = shim.forEach;
var filter = shim.filter;
var map = shim.map;
var trim = shim.trim;
var reduce = shim.reduce;
var getKeys = shim.getKeys;
var indexOf = shim.indexOf;
var truncateStack = utils.truncateStack;
var extend = utils.extend;
var levenshteinDistance = utils.levenshteinDistance;
var isArray = utils.isArray;
var cloneError = utils.cloneError;
var anyType = {
name: 'any',
identify: function (value) {
identify: function () {
return true;

@@ -26,7 +17,7 @@ },

},
inspect: function (output, value) {
inspect: function (value, depth, output) {
return output.text(value);
},
toJSON: function (value) {
return value;
diff: function (actual, expected, output, diff, inspect) {
return null;
}

@@ -37,3 +28,4 @@ };

options = options || {};
this.assertions = options.assertions || {};
this.assertions = options.assertions || {any: {}};
this.typeByName = options.typeByName || {};
this.types = options.types || [anyType];

@@ -44,2 +36,43 @@ this.output = options.output || magicpen();

function createExpectIt(expect, expectations) {
function expectIt(subject) {
var evaluation = expectations.map(function (expectation) {
try {
var args = Array.prototype.slice.call(expectation);
args.unshift(subject);
expect.apply(expect, args);
return null;
} catch (e) {
return e;
}
}).filter(function (e) {
return e;
});
if (evaluation.length > 0) {
expect.fail(function (output) {
evaluation.forEach(function (failedExpectation, index) {
if (index > 0) {
output.text(' and').nl();
}
output.append(failedExpectation.output);
});
});
}
}
expectIt._expectIt = true;
expectIt._expectations = expectations;
expectIt.and = function () {
var copiedExpectations = expectations.slice();
copiedExpectations.push(arguments);
return createExpectIt(expect, copiedExpectations);
};
return expectIt;
}
Unexpected.prototype.it = function () { // ...
return createExpectIt(this.expect, [arguments]);
};
Unexpected.prototype.equal = function (actual, expected, depth, seen) {

@@ -58,20 +91,21 @@ var that = this;

var matchingCustomType = utils.findFirst(this.types || [], function (type) {
return type.identify(actual) && type.identify(expected);
return this.findTypeOf(actual, expected).equal(actual, expected, function (a, b) {
return that.equal(a, b, depth + 1, seen);
});
};
if (matchingCustomType) {
return matchingCustomType.equal(actual, expected, function (a, b) {
return that.equal(a, b, depth + 1, seen);
Unexpected.prototype.findTypeOf = function () { // ...
var objs = Array.prototype.slice.call(arguments);
return utils.findFirst(this.types || [], function (type) {
return objs.every(function (obj) {
return type.identify(obj);
});
}
return false; // we should never get there
});
};
Unexpected.prototype.inspect = function (obj, depth) {
var types = this.types;
var seen = [];
var printOutput = function (output, obj, depth) {
if (depth === 0) {
var that = this;
var printOutput = function (obj, currentDepth, output) {
if (currentDepth === 0 && typeof obj === 'object') {
return output.text('...');

@@ -81,21 +115,16 @@ }

seen = seen || [];
if (indexOf(seen, obj) !== -1) {
if (seen.indexOf(obj) !== -1) {
return output.text('[Circular]');
}
var matchingCustomType = utils.findFirst(types || [], function (type) {
return type.identify(obj);
return that.findTypeOf(obj).inspect(obj, currentDepth, output, function (v, childDepth) {
seen.push(obj);
if (typeof childDepth === 'undefined') {
childDepth = currentDepth - 1;
}
return printOutput(v, childDepth, output.clone());
});
if (matchingCustomType) {
return matchingCustomType.inspect(output, obj, function (output, v) {
seen.push(obj);
return printOutput(output, v, depth - 1);
}, depth);
} else {
return output;
}
};
return printOutput(this.output.clone(), obj, depth || 3);
return printOutput(obj, depth || 3, this.output.clone());
};

@@ -106,2 +135,5 @@

Unexpected.prototype.fail = function (arg) {
if (utils.isError(arg)) {
throw arg;
}
var output = this.output.clone();

@@ -114,3 +146,3 @@ if (typeof arg === 'function') {

var tokens = message.split(placeholderSplitRegexp);
forEach(tokens, function (token) {
tokens.forEach(function (token) {
var match = placeholderRegexp.exec(token);

@@ -134,43 +166,60 @@ if (match) {

error.output = output;
this.serializeOutputToMessage(error);
this.setErrorMessage(error);
throw error;
};
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];
var isSeenByExpandedPattern = {};
forEach(patterns, function (pattern) {
ensureValidPattern(pattern);
forEach(expandPattern(pattern), function (expandedPattern) {
if (expandedPattern.text in assertions) {
if (!isSeenByExpandedPattern[expandedPattern.text]) {
throw new Error('Cannot redefine assertion: ' + expandedPattern.text);
// addAssertion(pattern, handler)
// addAssertion([pattern, ...]], handler)
// addAssertion(typeName, pattern, handler)
// addAssertion([typeName, ...], pattern, handler)
// addAssertion([typeName, ...], [pattern, pattern...], handler)
Unexpected.prototype.addAssertion = function (types, patterns, handler) {
if (arguments.length !== 2 && arguments.length !== 3) {
throw new Error('addAssertion: Needs 2 or 3 arguments');
}
if (typeof patterns === 'function') {
handler = patterns;
patterns = types;
types = [anyType];
} else {
var typeByName = this.typeByName;
// Normalize to an array of types, but allow types to be specified by name:
types = (Array.isArray(types) ? types : [types]).map(function (type) {
if (typeof type === 'string') {
if (type in typeByName) {
return typeByName[type];
} else {
throw new Error('No such type: ' + type);
}
} else {
isSeenByExpandedPattern[expandedPattern.text] = true;
assertions[expandedPattern.text] = {
handler: handler,
flags: expandedPattern.flags
};
return type;
}
});
}
patterns = utils.isArray(patterns) ? patterns : [patterns];
var assertions = this.assertions;
types.forEach(function (type) {
var typeName = type.name;
var assertionsForType = assertions[typeName];
if (!assertionsForType) {
throw new Error('No such type: ' + typeName);
}
var isSeenByExpandedPattern = {};
patterns.forEach(function (pattern) {
ensureValidPattern(pattern);
expandPattern(pattern).forEach(function (expandedPattern) {
if (expandedPattern.text in assertionsForType) {
if (!isSeenByExpandedPattern[expandedPattern.text]) {
throw new Error('Cannot redefine assertion: ' + expandedPattern.text + (typeName === 'any' ? '' : ' for type ' + typeName));
}
} else {
isSeenByExpandedPattern[expandedPattern.text] = true;
assertionsForType[expandedPattern.text] = {
handler: handler,
flags: expandedPattern.flags,
alternations: expandedPattern.alternations
};
}
});
});
});

@@ -182,3 +231,3 @@

Unexpected.prototype.getType = function (typeName) {
return utils.findFirst(this.types, function (type) {
return utils.findFirst(this.typeNames, function (type) {
return type.name === typeName;

@@ -190,6 +239,9 @@ });

var baseType;
if (typeof type.name !== 'string' || trim(type.name) === '') {
throw new Error('A custom type must be given a non-empty name');
if (typeof type.name !== 'string' || type.name.trim() === '') {
throw new Error('A type must be given a non-empty name');
}
this.assertions[type.name] = {};
this.typeByName[type.name] = type;
if (type.base) {

@@ -233,51 +285,4 @@ baseType = utils.findFirst(this.types, function (t) {

Unexpected.prototype.sanitize = function (obj, stack) {
var that = this;
stack = stack || [];
var i;
for (i = 0 ; i < stack.length ; i += 1) {
if (stack[i] === obj) {
return obj;
}
}
stack.push(obj);
var sanitized,
matchingCustomType = utils.findFirst(this.types || [], function (type) {
return type.identify(obj);
});
if (matchingCustomType) {
sanitized = matchingCustomType.toJSON(obj, function (v) {
return that.sanitize(v, stack);
});
} else if (isArray(obj)) {
sanitized = map(obj, function (item) {
return this.sanitize(item, stack);
}, this);
} else if (typeof obj === 'object' && obj) {
sanitized = {};
forEach(getKeys(obj).sort(), function (key) {
sanitized[key] = this.sanitize(obj[key], stack);
}, this);
} else {
sanitized = obj;
}
stack.pop();
return sanitized;
};
var errorMethodBlacklist = reduce(['message', 'line', 'sourceId', 'sourceURL', 'stack', 'stackArray'], function (result, prop) {
result[prop] = true;
return result;
}, {});
function errorWithMessage(e, message) {
var newError = new Error();
forEach(getKeys(e), function (key) {
if (!errorMethodBlacklist[key]) {
newError[key] = e[key];
}
});
var newError = cloneError(e);
newError.output = message;

@@ -290,5 +295,7 @@ return newError;

case 'nested':
return errorWithMessage(e, assertion.standardErrorMessage().nl()
.indentLines()
.i().block(e.output));
var newError = errorWithMessage(e, assertion.standardErrorMessage().nl()
.indentLines()
.i().block(e.output));
delete newError.createDiff;
return newError;
case 'default':

@@ -304,15 +311,17 @@ return errorWithMessage(e, assertion.standardErrorMessage());

function installExpectMethods(unexpected, expectFunction) {
var expect = bind(expectFunction, unexpected);
expect.equal = bind(unexpected.equal, unexpected);
expect.sanitize = bind(unexpected.sanitize, unexpected);
expect.inspect = bind(unexpected.inspect, unexpected);
expect.fail = bind(unexpected.fail, unexpected);
expect.addAssertion = bind(unexpected.addAssertion, unexpected);
expect.addType = bind(unexpected.addType, unexpected);
expect.clone = bind(unexpected.clone, unexpected);
expect.toString = bind(unexpected.toString, unexpected);
var expect = expectFunction.bind(unexpected);
expect.it = unexpected.it.bind(unexpected);
expect.equal = unexpected.equal.bind(unexpected);
expect.inspect = unexpected.inspect.bind(unexpected);
expect.findTypeOf = unexpected.findTypeOf.bind(unexpected);
expect.fail = unexpected.fail.bind(unexpected);
expect.diff = unexpected.diff.bind(unexpected);
expect.addAssertion = unexpected.addAssertion.bind(unexpected);
expect.addType = unexpected.addType.bind(unexpected);
expect.clone = unexpected.clone.bind(unexpected);
expect.toString = unexpected.toString.bind(unexpected);
expect.assertions = unexpected.assertions;
expect.installPlugin = bind(unexpected.installPlugin, unexpected);
expect.installPlugin = unexpected.installPlugin.bind(unexpected);
expect.output = unexpected.output;
expect.outputFormat = bind(unexpected.outputFormat, unexpected);
expect.outputFormat = unexpected.outputFormat.bind(unexpected);
return expect;

@@ -327,9 +336,19 @@ }

Unexpected.prototype.serializeOutputToMessage = function (err) {
Unexpected.prototype.setErrorMessage = function (err) {
var outputFormat = this.outputFormat();
var message = err.output.clone().append(err.output);
if (err.createDiff) {
var comparison = err.createDiff();
if (comparison) {
message.nl(2).blue('Diff:').nl(2).append(comparison.diff);
}
}
if (outputFormat === 'html') {
outputFormat = 'text';
err.htmlMessage = err.output.toString('html');
err.htmlMessage = message.toString('html');
}
err.message = err.output.toString(outputFormat);
err.output = message;
err.message = '\n' + message.toString(outputFormat);
};

@@ -340,8 +359,15 @@

if (arguments.length < 2) {
throw new Error('The expect functions requires at least two parameters.');
throw new Error('The expect function requires at least two parameters.');
}
if (typeof testDescriptionString !== 'string') {
throw new Error('The expect functions requires second parameter to be a string.');
throw new Error('The expect function requires the second parameter to be a string.');
}
var assertionRule = this.assertions[testDescriptionString];
var matchingType = this.findTypeOf(subject);
var typeWithAssertion = matchingType;
var assertionRule = this.assertions[typeWithAssertion.name][testDescriptionString];
while (!assertionRule && typeWithAssertion !== anyType) {
// FIXME: Detect cycles?
typeWithAssertion = typeWithAssertion.baseType;
assertionRule = this.assertions[typeWithAssertion.name][testDescriptionString];
}
if (assertionRule) {

@@ -362,3 +388,3 @@ var flags = extend({}, assertionRule.flags);

var wrappedError = handleNestedExpects(e, assertion);
that.serializeOutputToMessage(wrappedError);
that.setErrorMessage(wrappedError);
throw wrappedError;

@@ -372,5 +398,5 @@ }

var wrappedExpect = function wrappedExpect(subject, testDescriptionString) {
testDescriptionString = trim(testDescriptionString.replace(/\[(!?)([^\]]+)\] ?/g, function (match, negate, flag) {
testDescriptionString = testDescriptionString.replace(/\[(!?)([^\]]+)\] ?/g, function (match, negate, flag) {
return Boolean(flags[flag]) !== Boolean(negate) ? flag + ' ' : '';
}));
}).trim();

@@ -386,4 +412,5 @@ var args = Array.prototype.slice.call(arguments, 2);

wrappedExpect.types = this.types;
wrappedExpect.sanitize = this.sanitize;
wrappedExpect.inspect = this.inspect;
wrappedExpect.diff = this.diff;
wrappedExpect.findTypeOf = this.findTypeOf;
wrappedExpect.output = this.output;

@@ -401,3 +428,4 @@ wrappedExpect.outputFormat = this.outputFormat;

args.unshift(wrappedExpect, subject);
var assertion = new Assertion(wrappedExpect, subject, testDescriptionString, flags, args.slice(2));
var assertion = new Assertion(wrappedExpect, subject, testDescriptionString,
flags, assertionRule.alternations, args.slice(2));
var handler = assertionRule.handler;

@@ -409,4 +437,2 @@ try {

if (err._isUnexpected) {
err = errorWithMessage(err, err.output);
that.serializeOutputToMessage(err);
truncateStack(err, this.expect);

@@ -417,20 +443,78 @@ }

} else {
var similarAssertions = this.findAssertionSimilarTo(testDescriptionString);
var message =
'Unknown assertion "' + testDescriptionString + '", ' +
'did you mean: "' + similarAssertions[0] + '"';
var err = new Error(message);
truncateStack(err, this.expect);
throw err;
var errorMessage;
var definedForIncompatibleTypes = this.types.filter(function (type) {
return this.assertions[type.name][testDescriptionString];
}, this);
if (definedForIncompatibleTypes.length > 0) {
errorMessage =
'The assertion "' + testDescriptionString + '" is not defined for the type "' + matchingType.name +
'", but it is defined for ';
if (definedForIncompatibleTypes.length === 1) {
errorMessage += 'the type "' + definedForIncompatibleTypes[0].name + '"';
} else {
errorMessage += 'these types: ' + definedForIncompatibleTypes.map(function (incompatibleType) {
return '"' + incompatibleType.name + '"';
}).join(', ');
}
} else {
var assertionsWithScore = [];
var bonusForNextMatchingType = 0;
[].concat(this.types).reverse().forEach(function (type) {
var typeMatchBonus = 0;
if (type.identify(subject)) {
typeMatchBonus = bonusForNextMatchingType;
bonusForNextMatchingType += 0.9;
}
Object.keys(this.assertions[type.name]).forEach(function (assertion) {
assertionsWithScore.push({
type: type,
assertion: assertion,
score: typeMatchBonus - levenshteinDistance(testDescriptionString, assertion)
});
});
}, this);
assertionsWithScore.sort(function (a, b) {
return b.score - a.score;
});
errorMessage =
'Unknown assertion "' + testDescriptionString + '", ' +
'did you mean: "' + assertionsWithScore[0].assertion + '"';
}
throw truncateStack(new Error(errorMessage), this.expect);
}
};
Unexpected.prototype.diff = function (a, b) {
var output = this.output.clone();
var that = this;
return this.findTypeOf(a, b).diff(a, b, output, 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);
});
};
Unexpected.prototype.toString = function () {
return getKeys(this.assertions).sort().join('\n');
var assertions = this.assertions;
var isSeenByExpandedPattern = {};
Object.keys(assertions).forEach(function (typeName) {
Object.keys(assertions[typeName]).forEach(function (expandedPattern) {
isSeenByExpandedPattern[expandedPattern] = true;
});
}, this);
return Object.keys(isSeenByExpandedPattern).sort().join('\n');
};
Unexpected.prototype.clone = function () {
var clonedAssertions = {};
Object.keys(this.assertions).forEach(function (typeName) {
clonedAssertions[typeName] = extend({}, this.assertions[typeName]);
}, this);
var unexpected = new Unexpected({
assertions: extend({}, this.assertions),
assertions: clonedAssertions,
types: [].concat(this.types),
typeByName: extend({}, this.typeByName),
output: this.output.clone(),

@@ -464,3 +548,3 @@ format: this.outputFormat()

function removeEmptyStrings(texts) {
return filter(texts, function (text) {
return texts.filter(function (text) {
return text !== '';

@@ -471,3 +555,3 @@ });

if (index === tokens.length) {
return [{ text: '', flags: {}}];
return [{ text: '', flags: {}, alternations: [] }];
}

@@ -479,3 +563,3 @@

var flag = token.slice(1, -1);
return map(tail, function (pattern) {
return tail.map(function (pattern) {
var flags = {};

@@ -485,5 +569,6 @@ flags[flag] = true;

text: flag + ' ' + pattern.text,
flags: extend(flags, pattern.flags)
flags: extend(flags, pattern.flags),
alternations: pattern.alternations
};
}).concat(map(tail, function (pattern) {
}).concat(tail.map(function (pattern) {
var flags = {};

@@ -493,21 +578,25 @@ flags[flag] = false;

text: pattern.text,
flags: extend(flags, pattern.flags)
flags: extend(flags, pattern.flags),
alternations: pattern.alternations
};
}));
} 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
};
}));
}, []);
return token
.substr(1, token.length - 2) // Remove parentheses
.split(/\|/)
.reduce(function (result, alternation) {
return result.concat(tail.map(function (pattern) {
return {
text: alternation + pattern.text,
flags: pattern.flags,
alternations: [alternation].concat(pattern.alternations)
};
}));
}, []);
} else {
return map(tail, function (pattern) {
return tail.map(function (pattern) {
return {
text: token + pattern.text,
flags: pattern.flags
flags: pattern.flags,
alternations: pattern.alternations
};

@@ -529,7 +618,6 @@ });

tokens.push(pattern.slice(lastIndex));
tokens = removeEmptyStrings(tokens);
var permutations = createPermutations(tokens, 0);
forEach(permutations, function (permutation) {
permutation.text = trim(permutation.text);
permutations.forEach(function (permutation) {
permutation.text = permutation.text.trim();
if (permutation.text === '') {

@@ -578,8 +666,2 @@ // This can only happen if the pattern only contains flags

}
if ((c === ')' || c === '|') && counts['('] >= counts[')']) {
if (pattern.charAt(i - 1) === '(' || pattern.charAt(i - 1) === '|') {
throw new Error("Assertion patterns must not contain empty alternations: '" + pattern + "'");
}
}
}

@@ -586,0 +668,0 @@

@@ -1,7 +0,11 @@

var shim = require('./shim');
var forEach = shim.forEach;
var filter = shim.filter;
var getKeys = shim.getKeys;
var stringDiff = require('diff');
module.exports = {
var errorMethodBlacklist = ['message', 'line', 'sourceId', 'sourceURL', 'stack', 'stackArray'].reduce(function (result, prop) {
result[prop] = true;
return result;
}, {});
var specialCharRegexp = /([\x00-\x09\x0B-\x1F\x7F-\x9F\xAD\u0378\u0379\u037F-\u0383\u038B\u038D\u03A2\u0528-\u0530\u0557\u0558\u0560\u0588\u058B-\u058E\u0590\u05C8-\u05CF\u05EB-\u05EF\u05F5-\u0605\u061C\u061D\u06DD\u070E\u070F\u074B\u074C\u07B2-\u07BF\u07FB-\u07FF\u082E\u082F\u083F\u085C\u085D\u085F-\u089F\u08A1\u08AD-\u08E3\u08FF\u0978\u0980\u0984\u098D\u098E\u0991\u0992\u09A9\u09B1\u09B3-\u09B5\u09BA\u09BB\u09C5\u09C6\u09C9\u09CA\u09CF-\u09D6\u09D8-\u09DB\u09DE\u09E4\u09E5\u09FC-\u0A00\u0A04\u0A0B-\u0A0E\u0A11\u0A12\u0A29\u0A31\u0A34\u0A37\u0A3A\u0A3B\u0A3D\u0A43-\u0A46\u0A49\u0A4A\u0A4E-\u0A50\u0A52-\u0A58\u0A5D\u0A5F-\u0A65\u0A76-\u0A80\u0A84\u0A8E\u0A92\u0AA9\u0AB1\u0AB4\u0ABA\u0ABB\u0AC6\u0ACA\u0ACE\u0ACF\u0AD1-\u0ADF\u0AE4\u0AE5\u0AF2-\u0B00\u0B04\u0B0D\u0B0E\u0B11\u0B12\u0B29\u0B31\u0B34\u0B3A\u0B3B\u0B45\u0B46\u0B49\u0B4A\u0B4E-\u0B55\u0B58-\u0B5B\u0B5E\u0B64\u0B65\u0B78-\u0B81\u0B84\u0B8B-\u0B8D\u0B91\u0B96-\u0B98\u0B9B\u0B9D\u0BA0-\u0BA2\u0BA5-\u0BA7\u0BAB-\u0BAD\u0BBA-\u0BBD\u0BC3-\u0BC5\u0BC9\u0BCE\u0BCF\u0BD1-\u0BD6\u0BD8-\u0BE5\u0BFB-\u0C00\u0C04\u0C0D\u0C11\u0C29\u0C34\u0C3A-\u0C3C\u0C45\u0C49\u0C4E-\u0C54\u0C57\u0C5A-\u0C5F\u0C64\u0C65\u0C70-\u0C77\u0C80\u0C81\u0C84\u0C8D\u0C91\u0CA9\u0CB4\u0CBA\u0CBB\u0CC5\u0CC9\u0CCE-\u0CD4\u0CD7-\u0CDD\u0CDF\u0CE4\u0CE5\u0CF0\u0CF3-\u0D01\u0D04\u0D0D\u0D11\u0D3B\u0D3C\u0D45\u0D49\u0D4F-\u0D56\u0D58-\u0D5F\u0D64\u0D65\u0D76-\u0D78\u0D80\u0D81\u0D84\u0D97-\u0D99\u0DB2\u0DBC\u0DBE\u0DBF\u0DC7-\u0DC9\u0DCB-\u0DCE\u0DD5\u0DD7\u0DE0-\u0DF1\u0DF5-\u0E00\u0E3B-\u0E3E\u0E5C-\u0E80\u0E83\u0E85\u0E86\u0E89\u0E8B\u0E8C\u0E8E-\u0E93\u0E98\u0EA0\u0EA4\u0EA6\u0EA8\u0EA9\u0EAC\u0EBA\u0EBE\u0EBF\u0EC5\u0EC7\u0ECE\u0ECF\u0EDA\u0EDB\u0EE0-\u0EFF\u0F48\u0F6D-\u0F70\u0F98\u0FBD\u0FCD\u0FDB-\u0FFF\u10C6\u10C8-\u10CC\u10CE\u10CF\u1249\u124E\u124F\u1257\u1259\u125E\u125F\u1289\u128E\u128F\u12B1\u12B6\u12B7\u12BF\u12C1\u12C6\u12C7\u12D7\u1311\u1316\u1317\u135B\u135C\u137D-\u137F\u139A-\u139F\u13F5-\u13FF\u169D-\u169F\u16F1-\u16FF\u170D\u1715-\u171F\u1737-\u173F\u1754-\u175F\u176D\u1771\u1774-\u177F\u17DE\u17DF\u17EA-\u17EF\u17FA-\u17FF\u180F\u181A-\u181F\u1878-\u187F\u18AB-\u18AF\u18F6-\u18FF\u191D-\u191F\u192C-\u192F\u193C-\u193F\u1941-\u1943\u196E\u196F\u1975-\u197F\u19AC-\u19AF\u19CA-\u19CF\u19DB-\u19DD\u1A1C\u1A1D\u1A5F\u1A7D\u1A7E\u1A8A-\u1A8F\u1A9A-\u1A9F\u1AAE-\u1AFF\u1B4C-\u1B4F\u1B7D-\u1B7F\u1BF4-\u1BFB\u1C38-\u1C3A\u1C4A-\u1C4C\u1C80-\u1CBF\u1CC8-\u1CCF\u1CF7-\u1CFF\u1DE7-\u1DFB\u1F16\u1F17\u1F1E\u1F1F\u1F46\u1F47\u1F4E\u1F4F\u1F58\u1F5A\u1F5C\u1F5E\u1F7E\u1F7F\u1FB5\u1FC5\u1FD4\u1FD5\u1FDC\u1FF0\u1FF1\u1FF5\u1FFF\u200B-\u200F\u202A-\u202E\u2060-\u206F\u2072\u2073\u208F\u209D-\u209F\u20BA-\u20CF\u20F1-\u20FF\u218A-\u218F\u23F4-\u23FF\u2427-\u243F\u244B-\u245F\u2700\u2B4D-\u2B4F\u2B5A-\u2BFF\u2C2F\u2C5F\u2CF4-\u2CF8\u2D26\u2D28-\u2D2C\u2D2E\u2D2F\u2D68-\u2D6E\u2D71-\u2D7E\u2D97-\u2D9F\u2DA7\u2DAF\u2DB7\u2DBF\u2DC7\u2DCF\u2DD7\u2DDF\u2E3C-\u2E7F\u2E9A\u2EF4-\u2EFF\u2FD6-\u2FEF\u2FFC-\u2FFF\u3040\u3097\u3098\u3100-\u3104\u312E-\u3130\u318F\u31BB-\u31BF\u31E4-\u31EF\u321F\u32FF\u4DB6-\u4DBF\u9FCD-\u9FFF\uA48D-\uA48F\uA4C7-\uA4CF\uA62C-\uA63F\uA698-\uA69E\uA6F8-\uA6FF\uA78F\uA794-\uA79F\uA7AB-\uA7F7\uA82C-\uA82F\uA83A-\uA83F\uA878-\uA87F\uA8C5-\uA8CD\uA8DA-\uA8DF\uA8FC-\uA8FF\uA954-\uA95E\uA97D-\uA97F\uA9CE\uA9DA-\uA9DD\uA9E0-\uA9FF\uAA37-\uAA3F\uAA4E\uAA4F\uAA5A\uAA5B\uAA7C-\uAA7F\uAAC3-\uAADA\uAAF7-\uAB00\uAB07\uAB08\uAB0F\uAB10\uAB17-\uAB1F\uAB27\uAB2F-\uABBF\uABEE\uABEF\uABFA-\uABFF\uD7A4-\uD7AF\uD7C7-\uD7CA\uD7FC-\uF8FF\uFA6E\uFA6F\uFADA-\uFAFF\uFB07-\uFB12\uFB18-\uFB1C\uFB37\uFB3D\uFB3F\uFB42\uFB45\uFBC2-\uFBD2\uFD40-\uFD4F\uFD90\uFD91\uFDC8-\uFDEF\uFDFE\uFDFF\uFE1A-\uFE1F\uFE27-\uFE2F\uFE53\uFE67\uFE6C-\uFE6F\uFE75\uFEFD-\uFF00\uFFBF-\uFFC1\uFFC8\uFFC9\uFFD0\uFFD1\uFFD8\uFFD9\uFFDD-\uFFDF\uFFE7\uFFEF-\uFFFB\uFFFE\uFFFF])/g;
var utils = module.exports = {
// https://gist.github.com/1044128/

@@ -65,4 +69,4 @@ getOuterHTML: function (element) {

var sources = Array.prototype.slice.call(arguments, 1);
forEach(sources, function (source) {
forEach(getKeys(source), function (key) {
sources.forEach(function (source) {
Object.keys(source).forEach(function (key) {
target[key] = source[key];

@@ -83,3 +87,3 @@ });

getKeysOfDefinedProperties: function (object) {
var keys = filter(getKeys(object), function (key) {
var keys = Object.keys(object).filter(function (key) {
return typeof object[key] !== 'undefined';

@@ -147,2 +151,3 @@ });

}
return err;
},

@@ -166,3 +171,150 @@

return str;
},
cloneError: function (e) {
var newError = new Error();
Object.keys(e).forEach(function (key) {
if (!errorMethodBlacklist[key]) {
newError[key] = e[key];
}
});
return newError;
},
escapeRegExpMetaChars: function (str) {
return str.replace(/[[\]{}()*+?.\\^$|]/g, '\\$&');
},
escapeChar: function (ch) {
if (ch === '\t') {
return '\\t';
} else if (ch === '\r') {
return '\\r';
} else {
var charCode = ch.charCodeAt(0);
var hexChars = charCode.toString(16).toUpperCase();
if (charCode < 256) {
return '\\x' + utils.leftPad(hexChars, 2, '0');
} else {
return '\\u' + utils.leftPad(hexChars, 4, '0');
}
}
},
diffStrings: function (actual, expected, output, options) {
options = options || {};
var type = options.type || 'WordsWithSpace';
function addStringToOutput(output, text, baseStyle, specialCharStyle) {
if (options.markUpSpecialCharacters) {
text.split(specialCharRegexp).forEach(function (part) {
if (specialCharRegexp.test(part)) {
output.write(specialCharStyle || baseStyle, utils.escapeChar(part));
} else {
output.write(baseStyle, part);
}
});
return output;
} else {
output.write(baseStyle, text);
}
}
var diffLines = [];
var lastPart;
stringDiff.diffLines(actual, expected).forEach(function (part) {
if (lastPart && lastPart.added && part.removed) {
diffLines.push({
oldValue: part.value,
newValue: lastPart.value,
replaced: true
});
lastPart = null;
} else if (lastPart) {
diffLines.push(lastPart);
lastPart = part;
} else {
lastPart = part;
}
});
if (lastPart) {
diffLines.push(lastPart);
}
diffLines.forEach(function (part, index) {
var endsWithNewline = /\n$/.test(part.value);
var value;
if (part.replaced) {
var oldLine = output.clone();
var newLine = output.clone();
var oldValue = part.oldValue;
var newValue = part.newValue;
var oldEndsWithNewline = oldValue.slice(-1) === '\n';
var newEndsWithNewline = newValue.slice(-1) === '\n';
if (oldEndsWithNewline) {
oldValue = oldValue.slice(0, -1);
}
if (newEndsWithNewline) {
newValue = newValue.slice(0, -1);
}
stringDiff['diff' + type](oldValue, newValue).forEach(function (part) {
if (part.added) {
addStringToOutput(newLine, part.value, 'diffAddedHighlight', 'diffAddedSpecialChar');
} else if (part.removed) {
addStringToOutput(oldLine, part.value, 'diffRemovedHighlight', 'diffRemovedSpecialChar');
} else {
newLine.diffAddedLine(part.value);
oldLine.diffRemovedLine(part.value);
}
});
oldLine.prependLinesWith(output.clone().diffRemovedLine('-'));
newLine.prependLinesWith(output.clone().diffAddedLine('+'));
if (oldEndsWithNewline && !newEndsWithNewline) {
oldLine.diffRemovedSpecialChar('\\n');
}
if (newEndsWithNewline && !oldEndsWithNewline) {
newLine.diffAddedSpecialChar('\\n');
}
output.append(oldLine).nl().append(newLine);
if (oldEndsWithNewline && index < diffLines.length - 1) {
output.nl();
}
} else if (part.added) {
value = endsWithNewline ?
part.value.slice(0, -1) :
part.value;
output.append(function () {
addStringToOutput(this, value, 'diffAddedLine').prependLinesWith(function () {
this.diffAddedLine('+');
});
});
if (endsWithNewline) {
output.nl();
}
} else if (part.removed) {
value = endsWithNewline ?
part.value.slice(0, -1) :
part.value;
output.append(function () {
addStringToOutput(this, value, 'diffRemovedLine').prependLinesWith(function () {
this.diffRemovedLine('-');
});
});
if (endsWithNewline) {
output.nl();
}
} else {
output.text(part.value.replace(/^(.)/gm, ' $1'));
}
});
return output;
}
};
{
"name": "unexpected",
"version": "4.1.6",
"version": "5.0.0-beta1",
"author": "Sune Sloth Simonsen <sune@we-knowhow.dk>",

@@ -22,3 +22,5 @@ "keywords": [

"dependencies": {
"magicpen": "=0.3.4"
"arraydiff": "0.1.1",
"diff": "=1.0.8",
"magicpen": "=2.1.0"
},

@@ -29,10 +31,11 @@ "devDependencies": {

"coveralls": "^2.11.1",
"es5-shim": "4.0.3",
"istanbul": "^0.3.0",
"jshint": "*",
"jshint": "2.5.6",
"mocha": "=1.21.4",
"mocha-phantomjs": "=3.1.0",
"mocha-phantomjs": "=3.5.0",
"mocha-slow-reporter": "*",
"phantomjs": "=1.9.1-0",
"phantomjs": "=1.9.7-15",
"serve": "*"
}
}

@@ -29,3 +29,4 @@ # Unexpected

- Single global with no prototype extensions or shims.
- Cross-browser: works on IE6+, Firefox, Safari, Chrome, Opera.
- Cross-browser: works on Chrome, Firefox, Safari, Opera, IE6+,
(IE6-IE8 with [es5-shim](https://github.com/es-shims/es5-shim)).

@@ -32,0 +33,0 @@ ## How to use

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 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

Sorry, the diff of this file is too big to display

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc