UNPKG

unexpected

Version:
1,297 lines (1,160 loc) 71.2 kB
var _toConsumableArray2 = require('babel-runtime/helpers/toConsumableArray'); var _toConsumableArray3 = _interopRequireDefault(_toConsumableArray2); var _typeof2 = require('babel-runtime/helpers/typeof'); var _typeof3 = _interopRequireDefault(_typeof2); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } /*global setTimeout*/ var utils = require('./utils'); var arrayChanges = require('array-changes'); var arrayChangesAsync = require('array-changes-async'); var throwIfNonUnexpectedError = require('./throwIfNonUnexpectedError'); var objectIs = utils.objectIs; var isRegExp = utils.isRegExp; var extend = utils.extend; module.exports = function (expect) { expect.addAssertion('<any> [not] to be (ok|truthy)', function (expect, subject) { var not = !!expect.flags.not; var condition = !!subject; if (condition === not) { expect.fail(); } }); expect.addAssertion('<any> [not] to be (ok|truthy) <string>', function (expect, subject, message) { var not = !!expect.flags.not; var condition = !!subject; if (condition === not) { expect.fail({ errorMode: 'bubble', message: message }); } }); expect.addAssertion('<any> [not] to be <any>', function (expect, subject, value) { expect(objectIs(subject, value), '[not] to be truthy'); }); expect.addAssertion('<string> [not] to be <string>', function (expect, subject, value) { expect(subject, '[not] to equal', value); }); expect.addAssertion('<boolean> [not] to be true', function (expect, subject) { expect(subject, '[not] to be', true); }); expect.addAssertion('<boolean> [not] to be false', function (expect, subject) { expect(subject, '[not] to be', false); }); expect.addAssertion('<any> [not] to be falsy', function (expect, subject) { expect(subject, '[!not] to be truthy'); }); expect.addAssertion('<any> [not] to be falsy <string>', function (expect, subject, message) { var not = !!expect.flags.not; var condition = !!subject; if (condition !== not) { expect.fail({ errorMode: 'bubble', message: message }); } }); expect.addAssertion('<any> [not] to be null', function (expect, subject) { expect(subject, '[not] to be', null); }); expect.addAssertion('<any> [not] to be undefined', function (expect, subject) { expect(typeof subject === 'undefined', '[not] to be truthy'); }); expect.addAssertion('<any> to be defined', function (expect, subject) { expect(subject, 'not to be undefined'); }); expect.addAssertion('<number|NaN> [not] to be NaN', function (expect, subject) { expect(isNaN(subject), '[not] to be truthy'); }); expect.addAssertion('<number> [not] to be close to <number> <number?>', function (expect, subject, value, epsilon) { expect.errorMode = 'bubble'; if (typeof epsilon !== 'number') { epsilon = 1e-9; } expect.withError(function () { expect(Math.abs(subject - value), '[not] to be less than or equal to', epsilon); }, function (e) { expect.fail(function (output) { output.error('expected ').appendInspected(subject).sp().error(expect.testDescription).sp().appendInspected(value).sp().text('(epsilon: ').jsNumber(epsilon.toExponential()).text(')'); }); }); }); expect.addAssertion('<any> [not] to be (a|an) <type>', function (expect, subject, type) { expect.argsOutput[0] = function (output) { output.text(type.name); }; expect(type.identify(subject), '[not] to be true'); }); expect.addAssertion('<any> [not] to be (a|an) <string>', function (expect, subject, typeName) { typeName = /^reg(?:exp?|ular expression)$/.test(typeName) ? 'regexp' : typeName; expect.argsOutput[0] = function (output) { output.jsString(typeName); }; if (!expect.getType(typeName)) { expect.errorMode = 'nested'; expect.fail(function (output) { output.error('Unknown type:').sp().jsString(typeName); }); } expect(expect.subjectType.is(typeName), '[not] to be truthy'); }); expect.addAssertion('<any> [not] to be (a|an) <function>', function (expect, subject, Constructor) { var className = utils.getFunctionName(Constructor); if (className) { expect.argsOutput[0] = function (output) { output.text(className); }; } expect(subject instanceof Constructor, '[not] to be truthy'); }); expect.addAssertion('<any> [not] to be one of <array>', function (expect, subject, superset) { var found = false; for (var i = 0; i < superset.length; i += 1) { found = found || objectIs(subject, superset[i]); } if (found === expect.flags.not) { expect.fail(); } }); // Alias for common '[not] to be (a|an)' assertions expect.addAssertion('<any> [not] to be an (object|array)', function (expect, subject) { expect(subject, '[not] to be an', expect.alternations[0]); }); expect.addAssertion('<any> [not] to be a (boolean|number|string|function|regexp|regex|regular expression|date)', function (expect, subject) { expect(subject, '[not] to be a', expect.alternations[0]); }); expect.addAssertion('<string> to be (the empty|an empty|a non-empty) string', function (expect, subject) { expect(subject, expect.alternations[0] === 'a non-empty' ? 'not to be empty' : 'to be empty'); }); expect.addAssertion('<array-like> to be (the empty|an empty|a non-empty) array', function (expect, subject) { expect(subject, expect.alternations[0] === 'a non-empty' ? 'not to be empty' : 'to be empty'); }); expect.addAssertion('<string> to match <regexp>', function (expect, subject, regexp) { return expect.withError(function () { var captures = subject.match(regexp); expect(captures, 'to be truthy'); return captures; }, function (e) { e.label = 'should match'; expect.fail(e); }); }); expect.addAssertion('<string> not to match <regexp>', function (expect, subject, regexp) { return expect.withError(function () { expect(regexp.test(subject), 'to be false'); }, function (e) { expect.fail({ label: 'should not match', diff: function diff(output) { output.inline = false; 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.removedHighlight($0); }); flushUntilIndex(subject.length); return output; } }); }); }); expect.addAssertion('<object|function> [not] to have own property <string>', function (expect, subject, key) { expect(subject.hasOwnProperty(key), '[not] to be truthy'); return subject[key]; }); expect.addAssertion('<object|function> [not] to have (enumerable|configurable|writable) property <string>', function (expect, subject, key) { var descriptor = expect.alternations[0]; expect(Object.getOwnPropertyDescriptor(subject, key)[descriptor], '[not] to be truthy'); return subject[key]; }); expect.addAssertion('<object|function> [not] to have property <string>', function (expect, subject, key) { var subjectType = expect.findTypeOf(subject); var subjectKey = subjectType.is('function') ? subject[key] : subjectType.valueForKey(subject, key); expect(subjectKey, '[!not] to be undefined'); return subjectKey; }); expect.addAssertion('<object|function> to have [own] property <string> <any>', function (expect, subject, key, expectedPropertyValue) { return expect(subject, 'to have [own] property', key).then(function (actualPropertyValue) { expect.argsOutput = function () { this.appendInspected(key).sp().error('with a value of').sp().appendInspected(expectedPropertyValue); }; expect(actualPropertyValue, 'to equal', expectedPropertyValue); return actualPropertyValue; }); }); expect.addAssertion('<object|function> [not] to have [own] properties <array>', function (expect, subject, propertyNames) { var unsupportedPropertyNames = []; propertyNames.forEach(function (propertyName) { if (typeof propertyName !== 'string' && typeof propertyName !== 'number') { unsupportedPropertyNames.push(propertyName); } }); if (unsupportedPropertyNames.length > 0) { expect.errorMode = 'nested'; expect.fail(function () { this.error('All expected properties must be passed as strings or numbers, but these are not:').indentLines(); unsupportedPropertyNames.forEach(function (propertyName) { this.nl().i().appendInspected(propertyName); }, this); this.outdentLines(); }); } propertyNames.forEach(function (propertyName) { expect(subject, '[not] to have [own] property', String(propertyName)); }); }); expect.addAssertion('<object|function> to have [own] properties <object>', function (expect, subject, properties) { expect.withError(function () { Object.keys(properties).forEach(function (property) { var value = properties[property]; if (typeof value === 'undefined') { expect(subject, 'not to have [own] property', property); } else { expect(subject, 'to have [own] property', property, value); } }); }, function (e) { expect.fail({ diff: function diff(output, _diff) { output.inline = false; var expected = extend({}, properties); var actual = {}; var propertyNames = expect.findTypeOf(subject).getKeys(subject); // Might put duplicates into propertyNames, but that does not matter: for (var propertyName in subject) { if (!subject.hasOwnProperty(propertyName)) { propertyNames.push(propertyName); } } propertyNames.forEach(function (propertyName) { if ((!expect.flags.own || subject.hasOwnProperty(propertyName)) && !(propertyName in properties)) { expected[propertyName] = subject[propertyName]; } if ((!expect.flags.own || subject.hasOwnProperty(propertyName)) && !(propertyName in actual)) { actual[propertyName] = subject[propertyName]; } }); return utils.wrapConstructorNameAroundOutput(_diff(actual, expected), subject); } }); }); }); expect.addAssertion('<string|array-like> [not] to have length <number>', function (expect, subject, length) { if (!expect.flags.not) { expect.errorMode = 'nested'; } expect(subject.length, '[not] to be', length); }); expect.addAssertion('<string|array-like> [not] to be empty', function (expect, subject) { expect(subject, '[not] to have length', 0); }); expect.addAssertion('<string|array-like|object> to be non-empty', function (expect, subject) { expect(subject, 'not to be empty'); }); expect.addAssertion('<object> to [not] [only] have keys <array>', function (expect, subject, keys) { var keysInSubject = {}; var subjectType = expect.findTypeOf(subject); var subjectKeys = subjectType.getKeys(subject); subjectKeys.forEach(function (key) { keysInSubject[key] = true; }); if (expect.flags.not && keys.length === 0) { return; } var hasKeys = keys.every(function (key) { return keysInSubject[key]; }); if (expect.flags.only) { expect(hasKeys, 'to be truthy'); expect.withError(function () { expect(subjectKeys.length === keys.length, '[not] to be truthy'); }, function (err) { expect.fail({ diff: !expect.flags.not && function (output, diff, inspect, equal) { output.inline = true; var keyInValue = {}; keys.forEach(function (key) { keyInValue[key] = true; }); var subjectIsArrayLike = subjectType.is('array-like'); subjectType.prefix(output, subject); output.nl().indentLines(); subjectKeys.forEach(function (key, index) { var propertyOutput = subjectType.property(output.clone(), key, inspect(subjectType.valueForKey(subject, key)), subjectIsArrayLike); var delimiterOutput = subjectType.delimiter(output.clone(), index, subjectKeys.length); output.i().block(function () { this.append(propertyOutput).amend(delimiterOutput); if (!keyInValue[key]) { this.sp().annotationBlock(function () { this.error('should be removed'); }); } }).nl(); }); output.outdentLines(); subjectType.suffix(output, subject); return output; } }); }); } else { expect(hasKeys, '[not] to be truthy'); } }); expect.addAssertion('<object> [not] to be empty', function (expect, subject) { if (expect.flags.not && !expect.findTypeOf(subject).getKeys(subject).length) { return expect.fail(); } expect(subject, 'to [not] only have keys', []); }); expect.addAssertion('<object> not to have keys <array>', function (expect, subject, keys) { expect(subject, 'to not have keys', keys); }); expect.addAssertion('<object> not to have key <string>', function (expect, subject, value) { expect(subject, 'to not have keys', [value]); }); expect.addAssertion('<object> not to have keys <string+>', function (expect, subject, value) { expect(subject, 'to not have keys', Array.prototype.slice.call(arguments, 2)); }); expect.addAssertion('<object> to [not] [only] have key <string>', function (expect, subject, value) { expect(subject, 'to [not] [only] have keys', [value]); }); expect.addAssertion('<object> to [not] [only] have keys <string+>', function (expect, subject) { expect(subject, 'to [not] [only] have keys', Array.prototype.slice.call(arguments, 2)); }); expect.addAssertion('<string> [not] to contain <string+>', function (expect, subject) { var args = Array.prototype.slice.call(arguments, 2); args.forEach(function (arg) { if (arg === '') { throw new Error('The \'' + expect.testDescription + '\' assertion does not support the empty string'); } }); expect.withError(function () { args.forEach(function (arg) { expect(subject.indexOf(arg) !== -1, '[not] to be truthy'); }); }, function (e) { expect.fail({ diff: function diff(output) { output.inline = false; var lastIndex = 0; function flushUntilIndex(i) { if (i > lastIndex) { output.text(subject.substring(lastIndex, i)); lastIndex = i; } } if (expect.flags.not) { subject.replace(new RegExp(args.map(function (arg) { return utils.escapeRegExpMetaChars(arg); }).join('|'), 'g'), function ($0, index) { flushUntilIndex(index); lastIndex += $0.length; output.removedHighlight($0); }); flushUntilIndex(subject.length); } else { var ranges = []; args.forEach(function (arg) { var needle = arg; var partial = false; while (needle.length > 1) { var found = false; lastIndex = -1; var index = void 0; do { index = subject.indexOf(needle, lastIndex + 1); if (index !== -1) { found = true; ranges.push({ startIndex: index, endIndex: index + needle.length, partial: partial }); } lastIndex = index; } while (lastIndex !== -1); if (found) { break; } needle = arg.substr(0, needle.length - 1); partial = true; } }); lastIndex = 0; ranges.sort(function (a, b) { return a.startIndex - b.startIndex; }).forEach(function (_ref) { var startIndex = _ref.startIndex, endIndex = _ref.endIndex, partial = _ref.partial; flushUntilIndex(startIndex); var firstUncoveredIndex = Math.max(startIndex, lastIndex); if (endIndex > firstUncoveredIndex) { if (partial) { output.partialMatch(subject.substring(firstUncoveredIndex, endIndex)); } else { output.match(subject.substring(firstUncoveredIndex, endIndex)); } lastIndex = endIndex; } }); flushUntilIndex(subject.length); } return output; } }); }); }); expect.addAssertion('<array-like> [not] to contain <any+>', function (expect, subject) { var args = Array.prototype.slice.call(arguments, 2); expect.withError(function () { args.forEach(function (arg) { expect(subject && Array.prototype.some.call(subject, function (item) { return expect.equal(item, arg); }), '[not] to be truthy'); }); }, function (e) { expect.fail({ diff: expect.flags.not && function (output, diff, inspect, equal) { return diff(subject, Array.prototype.filter.call(subject, function (item) { return !args.some(function (arg) { return equal(item, arg); }); })); } }); }); }); expect.addAssertion(['<string> [not] to begin with <string>', '<string> [not] to start with <string>'], function (expect, subject, value) { if (value === '') { throw new Error('The \'' + expect.testDescription + '\' assertion does not support a prefix of the empty string'); } var isTruncated = false; var outputSubject = utils.truncateSubjectStringForBegin(subject, value); if (outputSubject === null) { outputSubject = subject; } else { isTruncated = true; } expect.subjectOutput = function (output) { output = output.jsString("'" + outputSubject.replace(/\n/g, '\\n') + "'"); if (isTruncated) { output.jsComment('...'); } }; expect.withError(function () { expect(subject.substr(0, value.length), '[not] to equal', value); }, function (err) { expect.fail({ diff: function diff(output) { output.inline = false; if (expect.flags.not) { output.removedHighlight(value).text(subject.substr(value.length)); } else { var i = 0; while (subject[i] === value[i]) { i += 1; } if (i === 0) { // No common prefix, omit diff return null; } else { output.partialMatch(subject.substr(0, i)).text(outputSubject.substr(i)).jsComment(isTruncated ? '...' : ''); } } return output; } }); }); }); expect.addAssertion('<string> [not] to end with <string>', function (expect, subject, value) { if (value === '') { throw new Error('The \'' + expect.testDescription + '\' assertion does not support a suffix of the empty string'); } var isTruncated = false; var outputSubject = utils.truncateSubjectStringForEnd(subject, value); if (outputSubject === null) { outputSubject = subject; } else { isTruncated = true; } expect.subjectOutput = function (output) { if (isTruncated) { output = output.jsComment('...'); } output.jsString("'" + outputSubject.replace(/\n/g, '\\n') + "'"); }; expect.withError(function () { expect(subject.substr(-value.length), '[not] to equal', value); }, function (err) { expect.fail({ diff: function diff(output) { output.inline = false; if (expect.flags.not) { output.text(subject.substr(0, subject.length - value.length)).removedHighlight(value); } else { var i = 0; while (outputSubject[outputSubject.length - 1 - i] === value[value.length - 1 - i]) { i += 1; } if (i === 0) { // No common suffix, omit diff return null; } output.jsComment(isTruncated ? '...' : '').text(outputSubject.substr(0, outputSubject.length - i)).partialMatch(outputSubject.substr(outputSubject.length - i, outputSubject.length)); } return output; } }); }); }); expect.addAssertion('<number> [not] to be finite', function (expect, subject) { expect(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('<number> [not] to be within <number> <number>', function (expect, subject, start, finish) { expect.argsOutput = function (output) { output.appendInspected(start).text('..').appendInspected(finish); }; expect(subject >= start && subject <= finish, '[not] to be truthy'); }); expect.addAssertion('<string> [not] to be within <string> <string>', function (expect, subject, start, finish) { expect.argsOutput = function (output) { output.appendInspected(start).text('..').appendInspected(finish); }; expect(subject >= start && subject <= finish, '[not] to be truthy'); }); expect.addAssertion('<number> [not] to be (less than|below) <number>', function (expect, subject, value) { expect(subject < value, '[not] to be truthy'); }); expect.addAssertion('<string> [not] to be (less than|below) <string>', function (expect, subject, value) { expect(subject < value, '[not] to be truthy'); }); expect.addAssertion('<number> [not] to be less than or equal to <number>', function (expect, subject, value) { expect(subject <= value, '[not] to be truthy'); }); expect.addAssertion('<string> [not] to be less than or equal to <string>', function (expect, subject, value) { expect(subject <= value, '[not] to be truthy'); }); expect.addAssertion('<number> [not] to be (greater than|above) <number>', function (expect, subject, value) { expect(subject > value, '[not] to be truthy'); }); expect.addAssertion('<string> [not] to be (greater than|above) <string>', function (expect, subject, value) { expect(subject > value, '[not] to be truthy'); }); expect.addAssertion('<number> [not] to be greater than or equal to <number>', function (expect, subject, value) { expect(subject >= value, '[not] to be truthy'); }); expect.addAssertion('<string> [not] to be greater than or equal to <string>', function (expect, subject, value) { expect(subject >= value, '[not] to be truthy'); }); expect.addAssertion('<number> [not] to be positive', function (expect, subject) { expect(subject, '[not] to be greater than', 0); }); expect.addAssertion('<number> [not] to be negative', function (expect, subject) { expect(subject, '[not] to be less than', 0); }); expect.addAssertion('<any> to equal <any>', function (expect, subject, value) { expect.withError(function () { expect(expect.equal(value, subject), 'to be truthy'); }, function (e) { expect.fail({ label: 'should equal', diff: function diff(output, _diff2) { return _diff2(subject, value); } }); }); }); expect.addAssertion('<any> not to equal <any>', function (expect, subject, value) { expect(expect.equal(value, subject), 'to be falsy'); }); expect.addAssertion('<function> to error', function (expect, subject) { return expect.promise(function () { return subject(); }).then(function () { expect.fail(); }, function (error) { return error; }); }); expect.addAssertion('<function> to error [with] <any>', function (expect, subject, arg) { return expect(subject, 'to error').then(function (error) { expect.errorMode = 'nested'; return expect.withError(function () { if (error.isUnexpected && (typeof arg === 'string' || isRegExp(arg))) { return expect(error, 'to have message', arg); } else { return expect(error, 'to satisfy', arg); } }, function (e) { e.originalError = error; throw e; }); }); }); expect.addAssertion('<function> not to error', function (expect, subject) { var threw = false; return expect.promise(function () { try { return subject(); } catch (e) { threw = true; throw e; } }).caught(function (error) { expect.errorMode = 'nested'; expect.fail({ output: function output(_output) { _output.error(threw ? 'threw' : 'returned promise rejected with').error(': ').appendErrorMessage(error); }, originalError: error }); }); }); expect.addAssertion('<function> not to throw', function (expect, subject) { var threw = false; var error = void 0; try { subject(); } catch (e) { error = e; threw = true; } if (threw) { expect.errorMode = 'nested'; expect.fail({ output: function output(_output2) { _output2.error('threw: ').appendErrorMessage(error); }, originalError: error }); } }); expect.addAssertion('<function> to (throw|throw error|throw exception)', function (expect, subject) { try { subject(); } catch (e) { return e; } expect.errorMode = 'nested'; expect.fail('did not throw'); }); expect.addAssertion('<function> to throw (a|an) <function>', function (expect, subject, value) { var constructorName = utils.getFunctionName(value); if (constructorName) { expect.argsOutput[0] = function (output) { output.jsFunctionName(constructorName); }; } expect.errorMode = 'nested'; return expect(subject, 'to throw').then(function (error) { expect(error, 'to be a', value); }); }); expect.addAssertion('<function> to (throw|throw error|throw exception) <any>', function (expect, subject, arg) { expect.errorMode = 'nested'; return expect(subject, 'to throw').then(function (error) { var isUnexpected = error && error._isUnexpected; // in the presence of a matcher an error must have been thrown. expect.errorMode = 'nested'; return expect.withError(function () { if (isUnexpected && (typeof arg === 'string' || isRegExp(arg))) { return expect(error.getErrorMessage('text').toString(), 'to satisfy', arg); } else { return expect(error, 'to satisfy', arg); } }, function (err) { err.originalError = error; throw err; }); }); }); expect.addAssertion('<function> to have arity <number>', function (expect, _ref2, value) { var length = _ref2.length; expect(length, 'to equal', value); }); expect.addAssertion(['<object> to have values [exhaustively] satisfying <any>', '<object> to have values [exhaustively] satisfying <assertion>', '<object> to be (a map|a hash|an object) whose values [exhaustively] satisfy <any>', '<object> to be (a map|a hash|an object) whose values [exhaustively] satisfy <assertion>'], function (expect, subject, nextArg) { expect.errorMode = 'nested'; expect(subject, 'not to be empty'); expect.errorMode = 'bubble'; var keys = expect.subjectType.getKeys(subject); var expected = {}; keys.forEach(function (key, index) { if (typeof nextArg === 'string') { expected[key] = function (s) { return expect.shift(s); }; } else if (typeof nextArg === 'function') { expected[key] = function (s) { return nextArg._expectIt ? nextArg(s, expect.context) : nextArg(s, index); }; } else { expected[key] = nextArg; } }); return expect.withError(function () { return expect(subject, 'to [exhaustively] satisfy', expected); }, function (err) { expect.fail({ message: function message(output) { output.append(expect.standardErrorMessage(output.clone(), { compact: err && err._isUnexpected && err.hasDiff() })); }, diff: function diff(output) { var diff = err.getDiff({ output: output }); diff.inline = true; return diff; } }); }); }); expect.addAssertion(['<array-like> to have items [exhaustively] satisfying <any>', '<array-like> to have items [exhaustively] satisfying <assertion>', '<array-like> to be an array whose items [exhaustively] satisfy <any>', '<array-like> to be an array whose items [exhaustively] satisfy <assertion>'], function (expect, subject) { for (var _len = arguments.length, rest = Array(_len > 2 ? _len - 2 : 0), _key = 2; _key < _len; _key++) { rest[_key - 2] = arguments[_key]; } // ... expect.errorMode = 'nested'; expect(subject, 'not to be empty'); expect.errorMode = 'bubble'; return expect.withError(function () { return expect.apply(undefined, [subject, 'to have values [exhaustively] satisfying'].concat(rest)); }, function (err) { expect.fail({ message: function message(output) { output.append(expect.standardErrorMessage(output.clone(), { compact: err && err._isUnexpected && err.hasDiff() })); }, diff: function diff(output) { var diff = err.getDiff({ output: output }); diff.inline = true; return diff; } }); }); }); expect.addAssertion(['<object> to have keys satisfying <any>', '<object> to have keys satisfying <assertion>', '<object> to be (a map|a hash|an object) whose (keys|properties) satisfy <any>', '<object> to be (a map|a hash|an object) whose (keys|properties) satisfy <assertion>'], function (expect, subject) { for (var _len2 = arguments.length, rest = Array(_len2 > 2 ? _len2 - 2 : 0), _key2 = 2; _key2 < _len2; _key2++) { rest[_key2 - 2] = arguments[_key2]; } expect.errorMode = 'nested'; expect(subject, 'not to be empty'); expect.errorMode = 'default'; var keys = expect.subjectType.getKeys(subject); return expect.apply(undefined, [keys, 'to have items satisfying'].concat(rest)); }); expect.addAssertion(['<object> [not] to have a value [exhaustively] satisfying <any>', '<object> [not] to have a value [exhaustively] satisfying <assertion>'], function (expect, subject, nextArg) { expect.errorMode = 'nested'; expect(subject, 'not to be empty'); expect.errorMode = 'bubble'; var subjectType = expect.findTypeOf(subject); var keys = subjectType.getKeys(subject); var not = !!expect.flags.not; var keyResults = new Array(keys.length); expect.withError(function () { return expect.promise[not ? 'all' : 'any'](keys.map(function (key, index) { var expected = void 0; if (typeof nextArg === 'string') { expected = function expected(s) { return expect.shift(s); }; } else if (typeof nextArg === 'function') { expected = function expected(s) { return nextArg(s, index); }; } else { expected = nextArg; } keyResults[key] = expect.promise(function () { return expect(subjectType.valueForKey(subject, key), '[not] to [exhaustively] satisfy', expected); }); return keyResults[key]; })); }, function (err) { expect.fail({ message: function message(output) { output.append(expect.standardErrorMessage(output.clone(), { compact: err && err._isUnexpected && err.hasDiff() })); }, diff: expect.flags.not && function (output, diff, inspect, equal) { var expectedObject = subjectType.is('array-like') ? [] : {}; keys.forEach(function (key) { if (keyResults[key].isFulfilled()) { expectedObject[key] = subjectType.valueForKey(subject, key); } }); return diff(subject, expectedObject); } }); }); }); expect.addAssertion(['<array-like> [not] to have an item [exhaustively] satisfying <any>', '<array-like> [not] to have an item [exhaustively] satisfying <assertion>'], function (expect, subject) { for (var _len3 = arguments.length, rest = Array(_len3 > 2 ? _len3 - 2 : 0), _key3 = 2; _key3 < _len3; _key3++) { rest[_key3 - 2] = arguments[_key3]; } expect.errorMode = 'nested'; expect(subject, 'not to be empty'); expect.errorMode = 'default'; return expect.apply(undefined, [subject, '[not] to have a value [exhaustively] satisfying'].concat(rest)); }); expect.addAssertion('<object> to be canonical', function (expect, subject) { var stack = []; (function traverse(obj) { var i = void 0; for (i = 0; i < stack.length; i += 1) { if (stack[i] === obj) { return; } } if (obj && (typeof obj === 'undefined' ? 'undefined' : (0, _typeof3.default)(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('<Error> to have message <any>', function (expect, subject, value) { expect.errorMode = 'nested'; return expect(subject.isUnexpected ? subject.getErrorMessage('text').toString() : subject.message, 'to satisfy', value); }); expect.addAssertion('<Error> to [exhaustively] satisfy <Error>', function (expect, subject, value) { expect(subject.constructor, 'to be', value.constructor); var unwrappedValue = expect.argTypes[0].unwrap(value); return expect.withError(function () { return expect(subject, 'to [exhaustively] satisfy', unwrappedValue); }, function (e) { expect.fail({ diff: function diff(output, _diff3) { output.inline = false; var unwrappedSubject = expect.subjectType.unwrap(subject); return utils.wrapConstructorNameAroundOutput(_diff3(unwrappedSubject, unwrappedValue), subject); } }); }); }); expect.addAssertion('<Error> to [exhaustively] satisfy <object>', function (expect, subject, value) { var valueType = expect.argTypes[0]; var subjectKeys = expect.subjectType.getKeys(subject); var valueKeys = valueType.getKeys(value); var convertedSubject = {}; subjectKeys.concat(valueKeys).forEach(function (key) { convertedSubject[key] = subject[key]; }); return expect(convertedSubject, 'to [exhaustively] satisfy', value); }); expect.addAssertion('<Error> to [exhaustively] satisfy <regexp|string>', function (expect, _ref3, value) { var message = _ref3.message; return expect(message, 'to [exhaustively] satisfy', value); }); expect.addAssertion('<Error> to [exhaustively] satisfy <any>', function (expect, _ref4, value) { var message = _ref4.message; return expect(message, 'to [exhaustively] satisfy', value); }); expect.addAssertion('<binaryArray> to [exhaustively] satisfy <expect.it>', function (expect, subject, value) { return expect.withError(function () { return value(subject, expect.context); }, function (e) { expect.fail({ diff: function diff(output, _diff4, inspect, equal) { output.inline = false; return output.appendErrorMessage(e); } }); }); }); expect.addAssertion('<UnexpectedError> to [exhaustively] satisfy <function>', function (expect, subject, value) { return expect.promise(function () { subject.serializeMessage(expect.outputFormat()); return value(subject); }); }); expect.addAssertion('<any|Error> to [exhaustively] satisfy <function>', function (expect, subject, value) { return expect.promise(function () { return value(subject); }); }); if (typeof Buffer !== 'undefined') { expect.addAssertion('<Buffer> [when] decoded as <string> <assertion?>', function (expect, subject, value) { return expect.shift(subject.toString(value)); }); } expect.addAssertion('<any> not to [exhaustively] satisfy [assertion] <any>', function (expect, subject, value) { return expect.promise(function (resolve, reject) { return expect.promise(function () { return expect(subject, 'to [exhaustively] satisfy [assertion]', value); }).then(function () { try { expect.fail(); } catch (e) { reject(e); } }).caught(function (e) { if (!e || !e._isUnexpected) { reject(e); } else { resolve(); } }); }); }); expect.addAssertion('<any> to [exhaustively] satisfy assertion <any>', function (expect, subject, value) { expect.errorMode = 'bubble'; // to satisfy assertion 'to be a number' => to be a number return expect(subject, 'to [exhaustively] satisfy', value); }); expect.addAssertion('<any> to [exhaustively] satisfy assertion <assertion>', function (expect, subject) { expect.errorMode = 'bubble'; // to satisfy assertion 'to be a number' => to be a number return expect.shift(); }); expect.addAssertion('<any> to [exhaustively] satisfy [assertion] <expect.it>', function (expect, subject, value) { return expect.withError(function () { return value(subject, expect.context); }, function (e) { expect.fail({ diff: function diff(output) { output.inline = false; return output.appendErrorMessage(e); } }); }); }); expect.addAssertion('<regexp> to [exhaustively] satisfy <regexp>', function (expect, subject, value) { expect(subject, 'to equal', value); }); expect.addAssertion('<string> to [exhaustively] satisfy <regexp>', function (expect, subject, value) { expect.errorMode = 'bubble'; return expect(subject, 'to match', value); }); expect.addAssertion('<function> to [exhaustively] satisfy <function>', function (expect, subject, value) { expect.errorMode = 'bubble'; expect(subject, 'to equal', value); }); expect.addAssertion('<binaryArray> to [exhaustively] satisfy <binaryArray>', function (expect, subject, value) { expect.errorMode = 'bubble'; expect(subject, 'to equal', value); }); expect.addAssertion('<any> to [exhaustively] satisfy <any>', function (expect, subject, value) { expect.errorMode = 'bubble'; expect(subject, 'to equal', value); }); expect.addAssertion('<array-like> to [exhaustively] satisfy <array-like>', function (expect, subject, value) { expect.errorMode = 'bubble'; var subjectType = expect.subjectType; var subjectKeys = subjectType.getKeys(subject); var valueType = expect.argTypes[0]; var valueKeys = valueType.getKeys(value).filter(function (key) { return utils.numericalRegExp.test(key) || (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'symbol' || // include keys whose value is not undefined on either LHS or RHS typeof valueType.valueForKey(value, key) !== 'undefined' || typeof subjectType.valueForKey(subject, key) !== 'undefined'; }); var keyPromises = {}; valueKeys.forEach(function (keyInValue) { keyPromises[keyInValue] = expect.promise(function () { var subjectKey = subjectType.valueForKey(subject, keyInValue); var valueKey = valueType.valueForKey(value, keyInValue); var valueKeyType = expect.findTypeOf(valueKey); if (valueKeyType.is('function')) { return valueKey(subjectKey); } else { return expect(subjectKey, 'to [exhaustively] satisfy', valueKey); } }); }); return expect.promise.all([expect.promise(function () { // create subject key presence object var remainingKeysInSubject = {}; subjectType.getKeys(subject).forEach(function (key) { remainingKeysInSubject[key] = 1; // present in subject }); // discard or mark missing each previously seen value key valueKeys.forEach(function (key) { if (!remainingKeysInSubject[key]) { remainingKeysInSubject[key] = 2; // present in value } else { delete remainingKeysInSubject[key]; } }); // filter outstanding keys which should not lead to an error var outstandingKeys = Object.keys(remainingKeysInSubject).filter(function (key) { return utils.numericalRegExp.test(key) || (typeof key === 'undefined' ? 'undefined' : (0, _typeof3.default)(key)) === 'symbol' || typeof subjectType.valueForKey(subject, key) !== 'undefined' || remainingKeysInSubject[key] === 2; }); // key checking succeeds with no outstanding keys expect(outstandingKeys.length === 0, 'to be truthy'); }), expect.promise.all(keyPromises)]).caught(function () { var i = 0; return expect.promise.settle(keyPromises).then(function () { var toSatisfyMatrix = new Array(subject.length); for (i = 0; i < subject.length; i += 1) { toSatisfyMatrix[i] = new Array(value.length); if (i < value.length) { toSatisfyMatrix[i][i] = keyPromises[i].isFulfilled() || keyPromises[i].reason(); } } if (subject.length > 10 || value.length > 10) { var indexByIndexChanges = []; for (i = 0; i < subject.length; i += 1) { var promise = keyPromises[i]; if (i < value.length) { indexByIndexChanges.push({ type: promise.isFulfilled() ? 'equal' : 'similar', value: subject[i], expected: value[i], actualIndex: i, expectedIndex: i, last: i === Math.max(subject.length, value.length) - 1 }); } else { indexByIndexChanges.push({ type: 'remove', value: subject[i], actualIndex: i, last: i === subject.length - 1 }); } } for (i = subject.length; i < value.length; i += 1) { indexByIndexChanges.push({ type: 'insert', value: value[i], expectedIndex: i }); } return failWithChanges(indexByIndexChanges); } var isAsync = false; var subjectElements = utils.duplicateArrayLikeUsingType(subject, subjectType); var valueElements = utils.duplicateArrayLikeUsingType(value, valueType); var nonNumericalKeysAndSymbols = !subjectType.numericalPropertiesOnly && utils.uniqueNonNumericalStringsAndSymbols(subjectKeys, valueKeys); var changes = arrayChanges(subjectElements, valueElements, function equal(a, b, aIndex, bIndex) { toSatisfyMatrix[aIndex] = toSatisfyMatrix[aIndex] || []; var existingResult = toSatisfyMatrix[aIndex][bIndex]; if (typeof existingResult !== 'undefined') { return existingResult === true; } var result = void 0; try { result = expect(a, 'to [exhaustively] satisfy', b); } catch (err) { throwIfNonUnexpectedError(err); toSatisfyMatrix[aIndex][bIndex] = err; return false; } result.then(function () {}, function () {}); if (result.isPending()) { isAsync = true; return false; } toSatisfyMatrix[aIndex][bIndex] = true; return true; }, function (a, b) { return subjectType.similar(a, b); }, { includeNonNumericalProperties: nonNumericalKeysAndSymbols }); if (isAsync) { return expect.promise(function (resolve, reject) { arrayChangesAsync(subject, value, function equal(a, b, aIndex, bIndex, cb) { toSatisfyMatrix[aIndex] = toSatisfyMatrix[aIndex] || []; var existingResult = toSatisfyMatrix[aIndex][bIndex]; if (typeof existingResult !== 'undefined') { return cb(existingResult === true); } expect.promise(function () { return expect(a, 'to [exhaustively] satisfy', b); }).then(function () { toSatisfyMatrix[aIndex][bIndex] = true; cb(true); }, function (err) { toSatisfyMatrix[aIndex][bIndex] = err; cb(false); }); }, function (a, b, aIndex, bIndex, cb) { cb(subjectType.similar(a, b)); }, nonNumericalKeysAndSymbols, resolve); }).then(failWithChanges); } else { return failWithChanges(changes); } function failWithChanges(changes) { expect.errorMode = 'default'; expect.fail({ diff: function diff(output, _diff5, inspect, equal) { output.inline = true; var indexOfLastNonInsert = changes.reduce(function (previousValue, _ref5, index) { var type = _ref5.type; return type === 'insert' ? previousValue : index; }, -1); var prefixOutput = subjectType.prefix(output.clone(), subject); output.append(prefixOutput).nl(prefixOutput.isEmpty() ? 0 : 1); if (subjectType.indent) { output.indentLines(); } var packing = utils.packArrows(changes); // NOTE: Will have side effects in changes if the packing results in too many arrow lanes output.arrowsAlongsideChangeOutputs(packing, changes.map(function (diffItem, index) { var delimiterOutput = subjectType.delimiter(output.clone(), index, indexOfLastNonInsert + 1); var type = diffItem.type; if (type === 'moveTarget') { return output.clone(); } else { return output.clone().block(function () { if (type === 'moveSource') { var propertyOutput = subjectType.property(output.clone(), diffItem.actualIndex, inspect(diffItem.value), true); this.append(propertyOutput).amend(delimiterOutput).sp().error('// should be moved'); } else if (type === 'insert') { this.annotationBlock(function () { if (expect.findTypeOf(diffItem.value).is('function')) { this.error('missing: ').block(function () { this.omitSubject = undefined; var promise = keyPromises[diffItem.expectedIndex]; if (promise.isRejected()) { this.appendErrorMessage(promise.reason()); } else { this.appendInspected(diffItem.value); } }); } else { var _index = typeof diffItem.actualIndex !== 'undefined' ? diffItem.actualIndex : diffItem.expectedIndex; var _propertyOutput = subjectType.property(output.clone(), _index, inspect(diffItem.value), true); this.error('missing ').append(_propertyOutput); } }); } else { var _propertyOutput2 = subjectType.property(output.clone(), diffItem.actualIndex, inspect(diffItem.value), true); this.block(function () { if (type === 'remove') { this.append(_propertyOutput2).amend(delimiterOutput).sp().error('// should be removed'); } else if (type === 'equal') { this.append(_propertyOutput2).amend(delimiterOutput); } else { var toSatisfyResult = toSatisfyMatrix[diffItem.actualIndex][diffItem.expectedIndex]; var valueDiff = toSatisfyResult && toSatisfyResult !== true && toSatisfyResult.getDiff({ output: output.clone() }); if (valueDiff && valueDiff.inline) { this.append(valueDiff).amend(delimiterOutput); } else { this.append(_propertyOutput2).amend(delimiterOutput).sp().annotationBlock(function () { this.omitSubject = diffItem.value; var label = toSatisfyResult.getLabel(); if (label) { this.error(label).sp().block(inspect(diffItem.expected)); if (valueDiff) { this.nl(2).append(valueDi