// Currently in sync with Node.js lib/internal/assert/assertion_error.js
|
// https://github.com/nodejs/node/commit/0817840f775032169ddd70c85ac059f18ffcc81c
|
'use strict';
|
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; }
|
|
function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
|
|
function _defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } }
|
|
function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; }
|
|
function _possibleConstructorReturn(self, call) { if (call && (_typeof(call) === "object" || typeof call === "function")) { return call; } return _assertThisInitialized(self); }
|
|
function _assertThisInitialized(self) { if (self === void 0) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return self; }
|
|
function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function"); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, writable: true, configurable: true } }); if (superClass) _setPrototypeOf(subClass, superClass); }
|
|
function _wrapNativeSuper(Class) { var _cache = typeof Map === "function" ? new Map() : undefined; _wrapNativeSuper = function _wrapNativeSuper(Class) { if (Class === null || !_isNativeFunction(Class)) return Class; if (typeof Class !== "function") { throw new TypeError("Super expression must either be null or a function"); } if (typeof _cache !== "undefined") { if (_cache.has(Class)) return _cache.get(Class); _cache.set(Class, Wrapper); } function Wrapper() { return _construct(Class, arguments, _getPrototypeOf(this).constructor); } Wrapper.prototype = Object.create(Class.prototype, { constructor: { value: Wrapper, enumerable: false, writable: true, configurable: true } }); return _setPrototypeOf(Wrapper, Class); }; return _wrapNativeSuper(Class); }
|
|
function isNativeReflectConstruct() { if (typeof Reflect === "undefined" || !Reflect.construct) return false; if (Reflect.construct.sham) return false; if (typeof Proxy === "function") return true; try { Date.prototype.toString.call(Reflect.construct(Date, [], function () {})); return true; } catch (e) { return false; } }
|
|
function _construct(Parent, args, Class) { if (isNativeReflectConstruct()) { _construct = Reflect.construct; } else { _construct = function _construct(Parent, args, Class) { var a = [null]; a.push.apply(a, args); var Constructor = Function.bind.apply(Parent, a); var instance = new Constructor(); if (Class) _setPrototypeOf(instance, Class.prototype); return instance; }; } return _construct.apply(null, arguments); }
|
|
function _isNativeFunction(fn) { return Function.toString.call(fn).indexOf("[native code]") !== -1; }
|
|
function _setPrototypeOf(o, p) { _setPrototypeOf = Object.setPrototypeOf || function _setPrototypeOf(o, p) { o.__proto__ = p; return o; }; return _setPrototypeOf(o, p); }
|
|
function _getPrototypeOf(o) { _getPrototypeOf = Object.setPrototypeOf ? Object.getPrototypeOf : function _getPrototypeOf(o) { return o.__proto__ || Object.getPrototypeOf(o); }; return _getPrototypeOf(o); }
|
|
function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function _typeof(obj) { return typeof obj; }; } else { _typeof = function _typeof(obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof(obj); }
|
|
var _require = require('util/'),
|
inspect = _require.inspect;
|
|
var _require2 = require('../errors'),
|
ERR_INVALID_ARG_TYPE = _require2.codes.ERR_INVALID_ARG_TYPE; // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/endsWith
|
|
|
function endsWith(str, search, this_len) {
|
if (this_len === undefined || this_len > str.length) {
|
this_len = str.length;
|
}
|
|
return str.substring(this_len - search.length, this_len) === search;
|
} // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/repeat
|
|
|
function repeat(str, count) {
|
count = Math.floor(count);
|
if (str.length == 0 || count == 0) return '';
|
var maxCount = str.length * count;
|
count = Math.floor(Math.log(count) / Math.log(2));
|
|
while (count) {
|
str += str;
|
count--;
|
}
|
|
str += str.substring(0, maxCount - str.length);
|
return str;
|
}
|
|
var blue = '';
|
var green = '';
|
var red = '';
|
var white = '';
|
var kReadableOperator = {
|
deepStrictEqual: 'Expected values to be strictly deep-equal:',
|
strictEqual: 'Expected values to be strictly equal:',
|
strictEqualObject: 'Expected "actual" to be reference-equal to "expected":',
|
deepEqual: 'Expected values to be loosely deep-equal:',
|
equal: 'Expected values to be loosely equal:',
|
notDeepStrictEqual: 'Expected "actual" not to be strictly deep-equal to:',
|
notStrictEqual: 'Expected "actual" to be strictly unequal to:',
|
notStrictEqualObject: 'Expected "actual" not to be reference-equal to "expected":',
|
notDeepEqual: 'Expected "actual" not to be loosely deep-equal to:',
|
notEqual: 'Expected "actual" to be loosely unequal to:',
|
notIdentical: 'Values identical but not reference-equal:'
|
}; // Comparing short primitives should just show === / !== instead of using the
|
// diff.
|
|
var kMaxShortLength = 10;
|
|
function copyError(source) {
|
var keys = Object.keys(source);
|
var target = Object.create(Object.getPrototypeOf(source));
|
keys.forEach(function (key) {
|
target[key] = source[key];
|
});
|
Object.defineProperty(target, 'message', {
|
value: source.message
|
});
|
return target;
|
}
|
|
function inspectValue(val) {
|
// The util.inspect default values could be changed. This makes sure the
|
// error messages contain the necessary information nevertheless.
|
return inspect(val, {
|
compact: false,
|
customInspect: false,
|
depth: 1000,
|
maxArrayLength: Infinity,
|
// Assert compares only enumerable properties (with a few exceptions).
|
showHidden: false,
|
// Having a long line as error is better than wrapping the line for
|
// comparison for now.
|
// TODO(BridgeAR): `breakLength` should be limited as soon as soon as we
|
// have meta information about the inspected properties (i.e., know where
|
// in what line the property starts and ends).
|
breakLength: Infinity,
|
// Assert does not detect proxies currently.
|
showProxy: false,
|
sorted: true,
|
// Inspect getters as we also check them when comparing entries.
|
getters: true
|
});
|
}
|
|
function createErrDiff(actual, expected, operator) {
|
var other = '';
|
var res = '';
|
var lastPos = 0;
|
var end = '';
|
var skipped = false;
|
var actualInspected = inspectValue(actual);
|
var actualLines = actualInspected.split('\n');
|
var expectedLines = inspectValue(expected).split('\n');
|
var i = 0;
|
var indicator = ''; // In case both values are objects explicitly mark them as not reference equal
|
// for the `strictEqual` operator.
|
|
if (operator === 'strictEqual' && _typeof(actual) === 'object' && _typeof(expected) === 'object' && actual !== null && expected !== null) {
|
operator = 'strictEqualObject';
|
} // If "actual" and "expected" fit on a single line and they are not strictly
|
// equal, check further special handling.
|
|
|
if (actualLines.length === 1 && expectedLines.length === 1 && actualLines[0] !== expectedLines[0]) {
|
var inputLength = actualLines[0].length + expectedLines[0].length; // If the character length of "actual" and "expected" together is less than
|
// kMaxShortLength and if neither is an object and at least one of them is
|
// not `zero`, use the strict equal comparison to visualize the output.
|
|
if (inputLength <= kMaxShortLength) {
|
if ((_typeof(actual) !== 'object' || actual === null) && (_typeof(expected) !== 'object' || expected === null) && (actual !== 0 || expected !== 0)) {
|
// -0 === +0
|
return "".concat(kReadableOperator[operator], "\n\n") + "".concat(actualLines[0], " !== ").concat(expectedLines[0], "\n");
|
}
|
} else if (operator !== 'strictEqualObject') {
|
// If the stderr is a tty and the input length is lower than the current
|
// columns per line, add a mismatch indicator below the output. If it is
|
// not a tty, use a default value of 80 characters.
|
var maxLength = process.stderr && process.stderr.isTTY ? process.stderr.columns : 80;
|
|
if (inputLength < maxLength) {
|
while (actualLines[0][i] === expectedLines[0][i]) {
|
i++;
|
} // Ignore the first characters.
|
|
|
if (i > 2) {
|
// Add position indicator for the first mismatch in case it is a
|
// single line and the input length is less than the column length.
|
indicator = "\n ".concat(repeat(' ', i), "^");
|
i = 0;
|
}
|
}
|
}
|
} // Remove all ending lines that match (this optimizes the output for
|
// readability by reducing the number of total changed lines).
|
|
|
var a = actualLines[actualLines.length - 1];
|
var b = expectedLines[expectedLines.length - 1];
|
|
while (a === b) {
|
if (i++ < 2) {
|
end = "\n ".concat(a).concat(end);
|
} else {
|
other = a;
|
}
|
|
actualLines.pop();
|
expectedLines.pop();
|
if (actualLines.length === 0 || expectedLines.length === 0) break;
|
a = actualLines[actualLines.length - 1];
|
b = expectedLines[expectedLines.length - 1];
|
}
|
|
var maxLines = Math.max(actualLines.length, expectedLines.length); // Strict equal with identical objects that are not identical by reference.
|
// E.g., assert.deepStrictEqual({ a: Symbol() }, { a: Symbol() })
|
|
if (maxLines === 0) {
|
// We have to get the result again. The lines were all removed before.
|
var _actualLines = actualInspected.split('\n'); // Only remove lines in case it makes sense to collapse those.
|
// TODO: Accept env to always show the full error.
|
|
|
if (_actualLines.length > 30) {
|
_actualLines[26] = "".concat(blue, "...").concat(white);
|
|
while (_actualLines.length > 27) {
|
_actualLines.pop();
|
}
|
}
|
|
return "".concat(kReadableOperator.notIdentical, "\n\n").concat(_actualLines.join('\n'), "\n");
|
}
|
|
if (i > 3) {
|
end = "\n".concat(blue, "...").concat(white).concat(end);
|
skipped = true;
|
}
|
|
if (other !== '') {
|
end = "\n ".concat(other).concat(end);
|
other = '';
|
}
|
|
var printedLines = 0;
|
var msg = kReadableOperator[operator] + "\n".concat(green, "+ actual").concat(white, " ").concat(red, "- expected").concat(white);
|
var skippedMsg = " ".concat(blue, "...").concat(white, " Lines skipped");
|
|
for (i = 0; i < maxLines; i++) {
|
// Only extra expected lines exist
|
var cur = i - lastPos;
|
|
if (actualLines.length < i + 1) {
|
// If the last diverging line is more than one line above and the
|
// current line is at least line three, add some of the former lines and
|
// also add dots to indicate skipped entries.
|
if (cur > 1 && i > 2) {
|
if (cur > 4) {
|
res += "\n".concat(blue, "...").concat(white);
|
skipped = true;
|
} else if (cur > 3) {
|
res += "\n ".concat(expectedLines[i - 2]);
|
printedLines++;
|
}
|
|
res += "\n ".concat(expectedLines[i - 1]);
|
printedLines++;
|
} // Mark the current line as the last diverging one.
|
|
|
lastPos = i; // Add the expected line to the cache.
|
|
other += "\n".concat(red, "-").concat(white, " ").concat(expectedLines[i]);
|
printedLines++; // Only extra actual lines exist
|
} else if (expectedLines.length < i + 1) {
|
// If the last diverging line is more than one line above and the
|
// current line is at least line three, add some of the former lines and
|
// also add dots to indicate skipped entries.
|
if (cur > 1 && i > 2) {
|
if (cur > 4) {
|
res += "\n".concat(blue, "...").concat(white);
|
skipped = true;
|
} else if (cur > 3) {
|
res += "\n ".concat(actualLines[i - 2]);
|
printedLines++;
|
}
|
|
res += "\n ".concat(actualLines[i - 1]);
|
printedLines++;
|
} // Mark the current line as the last diverging one.
|
|
|
lastPos = i; // Add the actual line to the result.
|
|
res += "\n".concat(green, "+").concat(white, " ").concat(actualLines[i]);
|
printedLines++; // Lines diverge
|
} else {
|
var expectedLine = expectedLines[i];
|
var actualLine = actualLines[i]; // If the lines diverge, specifically check for lines that only diverge by
|
// a trailing comma. In that case it is actually identical and we should
|
// mark it as such.
|
|
var divergingLines = actualLine !== expectedLine && (!endsWith(actualLine, ',') || actualLine.slice(0, -1) !== expectedLine); // If the expected line has a trailing comma but is otherwise identical,
|
// add a comma at the end of the actual line. Otherwise the output could
|
// look weird as in:
|
//
|
// [
|
// 1 // No comma at the end!
|
// + 2
|
// ]
|
//
|
|
if (divergingLines && endsWith(expectedLine, ',') && expectedLine.slice(0, -1) === actualLine) {
|
divergingLines = false;
|
actualLine += ',';
|
}
|
|
if (divergingLines) {
|
// If the last diverging line is more than one line above and the
|
// current line is at least line three, add some of the former lines and
|
// also add dots to indicate skipped entries.
|
if (cur > 1 && i > 2) {
|
if (cur > 4) {
|
res += "\n".concat(blue, "...").concat(white);
|
skipped = true;
|
} else if (cur > 3) {
|
res += "\n ".concat(actualLines[i - 2]);
|
printedLines++;
|
}
|
|
res += "\n ".concat(actualLines[i - 1]);
|
printedLines++;
|
} // Mark the current line as the last diverging one.
|
|
|
lastPos = i; // Add the actual line to the result and cache the expected diverging
|
// line so consecutive diverging lines show up as +++--- and not +-+-+-.
|
|
res += "\n".concat(green, "+").concat(white, " ").concat(actualLine);
|
other += "\n".concat(red, "-").concat(white, " ").concat(expectedLine);
|
printedLines += 2; // Lines are identical
|
} else {
|
// Add all cached information to the result before adding other things
|
// and reset the cache.
|
res += other;
|
other = ''; // If the last diverging line is exactly one line above or if it is the
|
// very first line, add the line to the result.
|
|
if (cur === 1 || i === 0) {
|
res += "\n ".concat(actualLine);
|
printedLines++;
|
}
|
}
|
} // Inspected object to big (Show ~20 rows max)
|
|
|
if (printedLines > 20 && i < maxLines - 2) {
|
return "".concat(msg).concat(skippedMsg, "\n").concat(res, "\n").concat(blue, "...").concat(white).concat(other, "\n") + "".concat(blue, "...").concat(white);
|
}
|
}
|
|
return "".concat(msg).concat(skipped ? skippedMsg : '', "\n").concat(res).concat(other).concat(end).concat(indicator);
|
}
|
|
var AssertionError =
|
/*#__PURE__*/
|
function (_Error) {
|
_inherits(AssertionError, _Error);
|
|
function AssertionError(options) {
|
var _this;
|
|
_classCallCheck(this, AssertionError);
|
|
if (_typeof(options) !== 'object' || options === null) {
|
throw new ERR_INVALID_ARG_TYPE('options', 'Object', options);
|
}
|
|
var message = options.message,
|
operator = options.operator,
|
stackStartFn = options.stackStartFn;
|
var actual = options.actual,
|
expected = options.expected;
|
var limit = Error.stackTraceLimit;
|
Error.stackTraceLimit = 0;
|
|
if (message != null) {
|
_this = _possibleConstructorReturn(this, _getPrototypeOf(AssertionError).call(this, String(message)));
|
} else {
|
if (process.stderr && process.stderr.isTTY) {
|
// Reset on each call to make sure we handle dynamically set environment
|
// variables correct.
|
if (process.stderr && process.stderr.getColorDepth && process.stderr.getColorDepth() !== 1) {
|
blue = "\x1B[34m";
|
green = "\x1B[32m";
|
white = "\x1B[39m";
|
red = "\x1B[31m";
|
} else {
|
blue = '';
|
green = '';
|
white = '';
|
red = '';
|
}
|
} // Prevent the error stack from being visible by duplicating the error
|
// in a very close way to the original in case both sides are actually
|
// instances of Error.
|
|
|
if (_typeof(actual) === 'object' && actual !== null && _typeof(expected) === 'object' && expected !== null && 'stack' in actual && actual instanceof Error && 'stack' in expected && expected instanceof Error) {
|
actual = copyError(actual);
|
expected = copyError(expected);
|
}
|
|
if (operator === 'deepStrictEqual' || operator === 'strictEqual') {
|
_this = _possibleConstructorReturn(this, _getPrototypeOf(AssertionError).call(this, createErrDiff(actual, expected, operator)));
|
} else if (operator === 'notDeepStrictEqual' || operator === 'notStrictEqual') {
|
// In case the objects are equal but the operator requires unequal, show
|
// the first object and say A equals B
|
var base = kReadableOperator[operator];
|
var res = inspectValue(actual).split('\n'); // In case "actual" is an object, it should not be reference equal.
|
|
if (operator === 'notStrictEqual' && _typeof(actual) === 'object' && actual !== null) {
|
base = kReadableOperator.notStrictEqualObject;
|
} // Only remove lines in case it makes sense to collapse those.
|
// TODO: Accept env to always show the full error.
|
|
|
if (res.length > 30) {
|
res[26] = "".concat(blue, "...").concat(white);
|
|
while (res.length > 27) {
|
res.pop();
|
}
|
} // Only print a single input.
|
|
|
if (res.length === 1) {
|
_this = _possibleConstructorReturn(this, _getPrototypeOf(AssertionError).call(this, "".concat(base, " ").concat(res[0])));
|
} else {
|
_this = _possibleConstructorReturn(this, _getPrototypeOf(AssertionError).call(this, "".concat(base, "\n\n").concat(res.join('\n'), "\n")));
|
}
|
} else {
|
var _res = inspectValue(actual);
|
|
var other = '';
|
var knownOperators = kReadableOperator[operator];
|
|
if (operator === 'notDeepEqual' || operator === 'notEqual') {
|
_res = "".concat(kReadableOperator[operator], "\n\n").concat(_res);
|
|
if (_res.length > 1024) {
|
_res = "".concat(_res.slice(0, 1021), "...");
|
}
|
} else {
|
other = "".concat(inspectValue(expected));
|
|
if (_res.length > 512) {
|
_res = "".concat(_res.slice(0, 509), "...");
|
}
|
|
if (other.length > 512) {
|
other = "".concat(other.slice(0, 509), "...");
|
}
|
|
if (operator === 'deepEqual' || operator === 'equal') {
|
_res = "".concat(knownOperators, "\n\n").concat(_res, "\n\nshould equal\n\n");
|
} else {
|
other = " ".concat(operator, " ").concat(other);
|
}
|
}
|
|
_this = _possibleConstructorReturn(this, _getPrototypeOf(AssertionError).call(this, "".concat(_res).concat(other)));
|
}
|
}
|
|
Error.stackTraceLimit = limit;
|
_this.generatedMessage = !message;
|
Object.defineProperty(_assertThisInitialized(_this), 'name', {
|
value: 'AssertionError [ERR_ASSERTION]',
|
enumerable: false,
|
writable: true,
|
configurable: true
|
});
|
_this.code = 'ERR_ASSERTION';
|
_this.actual = actual;
|
_this.expected = expected;
|
_this.operator = operator;
|
|
if (Error.captureStackTrace) {
|
// eslint-disable-next-line no-restricted-syntax
|
Error.captureStackTrace(_assertThisInitialized(_this), stackStartFn);
|
} // Create error message including the error code in the name.
|
|
|
_this.stack; // Reset the name.
|
|
_this.name = 'AssertionError';
|
return _possibleConstructorReturn(_this);
|
}
|
|
_createClass(AssertionError, [{
|
key: "toString",
|
value: function toString() {
|
return "".concat(this.name, " [").concat(this.code, "]: ").concat(this.message);
|
}
|
}, {
|
key: inspect.custom,
|
value: function value(recurseTimes, ctx) {
|
// This limits the `actual` and `expected` property default inspection to
|
// the minimum depth. Otherwise those values would be too verbose compared
|
// to the actual error message which contains a combined view of these two
|
// input values.
|
return inspect(this, _objectSpread({}, ctx, {
|
customInspect: false,
|
depth: 0
|
}));
|
}
|
}]);
|
|
return AssertionError;
|
}(_wrapNativeSuper(Error));
|
|
module.exports = AssertionError;
|