diff --git a/lib/jasmine-core/jasmine.js b/lib/jasmine-core/jasmine.js index fa9938e6..a0ba7a6b 100644 --- a/lib/jasmine-core/jasmine.js +++ b/lib/jasmine-core/jasmine.js @@ -4925,117 +4925,116 @@ getJasmineRequireObj().toBeResolvedTo = function(j$) { }; getJasmineRequireObj().DiffBuilder = function(j$) { - return function DiffBuilder(config) { - const prettyPrinter = - (config || {}).prettyPrinter || j$.makePrettyPrinter(); - const mismatches = new j$.MismatchTree(); - let path = new j$.ObjectPath(); - let actualRoot = undefined; - let expectedRoot = undefined; + class DiffBuilder { + constructor(config) { + this.prettyPrinter_ = + (config || {}).prettyPrinter || j$.makePrettyPrinter(); + this.mismatches_ = new j$.MismatchTree(); + this.path_ = new j$.ObjectPath(); + this.actualRoot_ = undefined; + this.expectedRoot_ = undefined; + } - return { - setRoots: function(actual, expected) { - actualRoot = actual; - expectedRoot = expected; - }, + setRoots(actual, expected) { + this.actualRoot_ = actual; + this.expectedRoot_ = expected; + } - recordMismatch: function(formatter) { - mismatches.add(path, formatter); - }, + recordMismatch(formatter) { + this.mismatches_.add(this.path_, formatter); + } - getMessage: function() { - const messages = []; + getMessage() { + const messages = []; - mismatches.traverse(function(path, isLeaf, formatter) { - const { actual, expected } = dereferencePath( - path, - actualRoot, - expectedRoot, - prettyPrinter - ); - - if (formatter) { - messages.push(formatter(actual, expected, path, prettyPrinter)); - return true; - } - - const actualCustom = prettyPrinter.customFormat_(actual); - const expectedCustom = prettyPrinter.customFormat_(expected); - const useCustom = !( - j$.util.isUndefined(actualCustom) && - j$.util.isUndefined(expectedCustom) - ); - - if (useCustom) { - messages.push( - wrapPrettyPrinted(actualCustom, expectedCustom, path) - ); - return false; // don't recurse further - } - - if (isLeaf) { - messages.push( - defaultFormatter(actual, expected, path, prettyPrinter) - ); - } + this.mismatches_.traverse((path, isLeaf, formatter) => { + const { actual, expected } = this.dereferencePath_(path); + if (formatter) { + messages.push(formatter(actual, expected, path, this.prettyPrinter_)); return true; - }); + } - return messages.join('\n'); - }, + const actualCustom = this.prettyPrinter_.customFormat_(actual); + const expectedCustom = this.prettyPrinter_.customFormat_(expected); + const useCustom = !( + j$.util.isUndefined(actualCustom) && + j$.util.isUndefined(expectedCustom) + ); - withPath: function(pathComponent, block) { - const oldPath = path; - path = path.add(pathComponent); - block(); - path = oldPath; + if (useCustom) { + messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path)); + return false; // don't recurse further + } + + if (isLeaf) { + messages.push(this.defaultFormatter_(actual, expected, path)); + } + + return true; + }); + + return messages.join('\n'); + } + + withPath(pathComponent, block) { + const oldPath = this.path_; + this.path_ = this.path_.add(pathComponent); + block(); + this.path_ = oldPath; + } + + dereferencePath_(objectPath) { + let actual = this.actualRoot_; + let expected = this.expectedRoot_; + + const handleAsymmetricExpected = () => { + if ( + j$.isAsymmetricEqualityTester_(expected) && + j$.isFunction_(expected.valuesForDiff_) + ) { + const asymmetricResult = expected.valuesForDiff_( + actual, + this.prettyPrinter_ + ); + expected = asymmetricResult.self; + actual = asymmetricResult.other; + } + }; + + handleAsymmetricExpected(); + + for (const pc of objectPath.components) { + actual = actual[pc]; + expected = expected[pc]; + handleAsymmetricExpected(); } - }; - function defaultFormatter(actual, expected, path, prettyPrinter) { + return { actual: actual, expected: expected }; + } + + defaultFormatter_(actual, expected, path) { return wrapPrettyPrinted( - prettyPrinter(actual), - prettyPrinter(expected), + this.prettyPrinter_(actual), + this.prettyPrinter_(expected), path ); } - - function wrapPrettyPrinted(actual, expected, path) { - return ( - 'Expected ' + - path + - (path.depth() ? ' = ' : '') + - actual + - ' to equal ' + - expected + - '.' - ); - } - }; - - function dereferencePath(objectPath, actual, expected, pp) { - function handleAsymmetricExpected() { - if ( - j$.isAsymmetricEqualityTester_(expected) && - j$.isFunction_(expected.valuesForDiff_) - ) { - const asymmetricResult = expected.valuesForDiff_(actual, pp); - expected = asymmetricResult.self; - actual = asymmetricResult.other; - } - } - - handleAsymmetricExpected(); - - for (const pc of objectPath.components) { - actual = actual[pc]; - expected = expected[pc]; - handleAsymmetricExpected(); - } - - return { actual: actual, expected: expected }; } + + function wrapPrettyPrinted(actual, expected, path) { + return ( + 'Expected ' + + path + + (path.depth() ? ' = ' : '') + + actual + + ' to equal ' + + expected + + '.' + ); + } + + return DiffBuilder; }; getJasmineRequireObj().MatchersUtil = function(j$) { @@ -5735,49 +5734,51 @@ getJasmineRequireObj().MismatchTree = function(j$) { the expected and actual object graphs. MismatchTree maintains that context and provides it via the traverse method. */ - function MismatchTree(path) { - this.path = path || new j$.ObjectPath([]); - this.formatter = undefined; - this.children = []; - this.isMismatch = false; - } - - MismatchTree.prototype.add = function(path, formatter) { - if (path.depth() === 0) { - this.formatter = formatter; - this.isMismatch = true; - } else { - const key = path.components[0]; - path = path.shift(); - let child = this.child(key); - - if (!child) { - child = new MismatchTree(this.path.add(key)); - this.children.push(child); - } - - child.add(path, formatter); + class MismatchTree { + constructor(path) { + this.path = path || new j$.ObjectPath([]); + this.formatter = undefined; + this.children = []; + this.isMismatch = false; } - }; - MismatchTree.prototype.traverse = function(visit) { - const hasChildren = this.children.length > 0; + add(path, formatter) { + if (path.depth() === 0) { + this.formatter = formatter; + this.isMismatch = true; + } else { + const key = path.components[0]; + path = path.shift(); + let child = this.child(key); - if (this.isMismatch || hasChildren) { - if (visit(this.path, !hasChildren, this.formatter)) { - for (const child of this.children) { - child.traverse(visit); + if (!child) { + child = new MismatchTree(this.path.add(key)); + this.children.push(child); + } + + child.add(path, formatter); + } + } + + traverse(visit) { + const hasChildren = this.children.length > 0; + + if (this.isMismatch || hasChildren) { + if (visit(this.path, !hasChildren, this.formatter)) { + for (const child of this.children) { + child.traverse(visit); + } } } } - }; - MismatchTree.prototype.child = function(key) { - return this.children.find(child => { - const pathEls = child.path.components; - return pathEls[pathEls.length - 1] === key; - }); - }; + child(key) { + return this.children.find(child => { + const pathEls = child.path.components; + return pathEls[pathEls.length - 1] === key; + }); + } + } return MismatchTree; }; @@ -5817,29 +5818,31 @@ getJasmineRequireObj().NullDiffBuilder = function(j$) { }; getJasmineRequireObj().ObjectPath = function(j$) { - function ObjectPath(components) { - this.components = components || []; - } - - ObjectPath.prototype.toString = function() { - if (this.components.length) { - return '$' + this.components.map(formatPropertyAccess).join(''); - } else { - return ''; + class ObjectPath { + constructor(components) { + this.components = components || []; } - }; - ObjectPath.prototype.add = function(component) { - return new ObjectPath(this.components.concat([component])); - }; + toString() { + if (this.components.length) { + return '$' + this.components.map(formatPropertyAccess).join(''); + } else { + return ''; + } + } - ObjectPath.prototype.shift = function() { - return new ObjectPath(this.components.slice(1)); - }; + add(component) { + return new ObjectPath(this.components.concat([component])); + } - ObjectPath.prototype.depth = function() { - return this.components.length; - }; + shift() { + return new ObjectPath(this.components.slice(1)); + } + + depth() { + return this.components.length; + } + } function formatPropertyAccess(prop) { if (typeof prop === 'number' || typeof prop === 'symbol') { @@ -5850,7 +5853,7 @@ getJasmineRequireObj().ObjectPath = function(j$) { return '.' + prop; } - return "['" + prop + "']"; + return `['${prop}']`; } function isValidIdentifier(string) { @@ -6415,7 +6418,7 @@ getJasmineRequireObj().toEqual = function(j$) { var result = { pass: false }, - diffBuilder = j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }); + diffBuilder = new j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }); result.pass = matchersUtil.equals(actual, expected, diffBuilder); @@ -7521,13 +7524,300 @@ getJasmineRequireObj().NeverSkipPolicy = function(j$) { }; getJasmineRequireObj().makePrettyPrinter = function(j$) { - function SinglePrettyPrintRun(customObjectFormatters, pp) { - this.customObjectFormatters_ = customObjectFormatters; - this.ppNestLevel_ = 0; - this.seen = []; - this.length = 0; - this.stringParts = []; - this.pp_ = pp; + class SinglePrettyPrintRun { + constructor(customObjectFormatters, pp) { + this.customObjectFormatters_ = customObjectFormatters; + this.ppNestLevel_ = 0; + this.seen = []; + this.length = 0; + this.stringParts = []; + this.pp_ = pp; + } + + format(value) { + this.ppNestLevel_++; + try { + const customFormatResult = this.applyCustomFormatters_(value); + + if (customFormatResult) { + this.emitScalar(customFormatResult); + } else if (j$.util.isUndefined(value)) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === 0 && 1 / value === -Infinity) { + this.emitScalar('-0'); + } else if (value === j$.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString(this.pp_)); + } else if (j$.isString_(value)) { + this.emitString(value); + } else if (j$.isSpy(value)) { + this.emitScalar('spy on ' + value.and.identity); + } else if (j$.isSpy(value.toString)) { + this.emitScalar('spy on ' + value.toString.and.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (j$.isDomNode(value)) { + if (value.tagName) { + this.emitDomElement(value); + } else { + this.emitScalar('HTMLNode'); + } + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (j$.isSet(value)) { + this.emitSet(value); + } else if (j$.isMap(value)) { + this.emitMap(value); + } else if (j$.isTypedArray_(value)) { + this.emitTypedArray(value); + } else if ( + value.toString && + typeof value === 'object' && + !j$.isArray_(value) && + hasCustomToString(value) + ) { + try { + this.emitScalar(value.toString()); + } catch (e) { + this.emitScalar('has-invalid-toString-method'); + } + } else if (j$.util.arrayContains(this.seen, value)) { + this.emitScalar( + '' + ); + } else if (j$.isArray_(value) || j$.isA_('Object', value)) { + this.seen.push(value); + if (j$.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + this.seen.pop(); + } else { + this.emitScalar(value.toString()); + } + } catch (e) { + if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { + throw e; + } + } finally { + this.ppNestLevel_--; + } + } + + applyCustomFormatters_(value) { + return customFormat(value, this.customObjectFormatters_); + } + + iterateObject(obj, fn) { + const objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj)); + const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + + for (let i = 0; i < length; i++) { + fn(objKeys[i]); + } + + return objKeys.length > length; + } + + emitScalar(value) { + this.append(value); + } + + emitString(value) { + this.append("'" + value + "'"); + } + + emitArray(array) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Array'); + return; + } + + const length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + this.append('[ '); + + for (let i = 0; i < length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + if (array.length > length) { + this.append(', ...'); + } + + let first = array.length === 0; + const wasTruncated = this.iterateObject(array, property => { + if (first) { + first = false; + } else { + this.append(', '); + } + + this.formatProperty(array, property); + }); + + if (wasTruncated) { + this.append(', ...'); + } + + this.append(' ]'); + } + + emitSet(set) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Set'); + return; + } + this.append('Set( '); + const size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + let i = 0; + set.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format(value); + + i++; + }, this); + if (set.size > size) { + this.append(', ...'); + } + this.append(' )'); + } + + emitMap(map) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Map'); + return; + } + this.append('Map( '); + const size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + let i = 0; + map.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format([key, value]); + + i++; + }, this); + if (map.size > size) { + this.append(', ...'); + } + this.append(' )'); + } + + emitObject(obj) { + const ctor = obj.constructor; + const constructorName = + typeof ctor === 'function' && obj instanceof ctor + ? j$.fnNameFor(obj.constructor) + : 'null'; + + this.append(constructorName); + + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + return; + } + + this.append('({ '); + let first = true; + + const wasTruncated = this.iterateObject(obj, property => { + if (first) { + first = false; + } else { + this.append(', '); + } + + this.formatProperty(obj, property); + }); + + if (wasTruncated) { + this.append(', ...'); + } + + this.append(' })'); + } + + emitTypedArray(arr) { + const constructorName = j$.fnNameFor(arr.constructor); + const limitedArray = Array.prototype.slice.call( + arr, + 0, + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH + ); + let itemsString = Array.prototype.join.call(limitedArray, ', '); + + if (limitedArray.length !== arr.length) { + itemsString += ', ...'; + } + + this.append(constructorName + ' [ ' + itemsString + ' ]'); + } + + emitDomElement(el) { + const tagName = el.tagName.toLowerCase(); + let out = '<' + tagName; + + for (const attr of el.attributes) { + out += ' ' + attr.name; + + if (attr.value !== '') { + out += '="' + attr.value + '"'; + } + } + + out += '>'; + + if (el.childElementCount !== 0 || el.textContent !== '') { + out += '...'; + } + + this.append(out); + } + + formatProperty(obj, property) { + if (typeof property === 'symbol') { + this.append(property.toString()); + } else { + this.append(property); + } + + this.append(': '); + this.format(obj[property]); + } + + append(value) { + // This check protects us from the rare case where an object has overriden + // `toString()` with an invalid implementation (returning a non-string). + if (typeof value !== 'string') { + value = Object.prototype.toString.call(value); + } + + const result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); + this.length += result.value.length; + this.stringParts.push(result.value); + + if (result.truncated) { + throw new MaxCharsReachedError(); + } + } } function hasCustomToString(value) { @@ -7545,291 +7835,6 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) { } } - SinglePrettyPrintRun.prototype.format = function(value) { - this.ppNestLevel_++; - try { - const customFormatResult = this.applyCustomFormatters_(value); - - if (customFormatResult) { - this.emitScalar(customFormatResult); - } else if (j$.util.isUndefined(value)) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value === 0 && 1 / value === -Infinity) { - this.emitScalar('-0'); - } else if (value === j$.getGlobal()) { - this.emitScalar(''); - } else if (value.jasmineToString) { - this.emitScalar(value.jasmineToString(this.pp_)); - } else if (j$.isString_(value)) { - this.emitString(value); - } else if (j$.isSpy(value)) { - this.emitScalar('spy on ' + value.and.identity); - } else if (j$.isSpy(value.toString)) { - this.emitScalar('spy on ' + value.toString.and.identity); - } else if (value instanceof RegExp) { - this.emitScalar(value.toString()); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (j$.isDomNode(value)) { - if (value.tagName) { - this.emitDomElement(value); - } else { - this.emitScalar('HTMLNode'); - } - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (j$.isSet(value)) { - this.emitSet(value); - } else if (j$.isMap(value)) { - this.emitMap(value); - } else if (j$.isTypedArray_(value)) { - this.emitTypedArray(value); - } else if ( - value.toString && - typeof value === 'object' && - !j$.isArray_(value) && - hasCustomToString(value) - ) { - try { - this.emitScalar(value.toString()); - } catch (e) { - this.emitScalar('has-invalid-toString-method'); - } - } else if (j$.util.arrayContains(this.seen, value)) { - this.emitScalar( - '' - ); - } else if (j$.isArray_(value) || j$.isA_('Object', value)) { - this.seen.push(value); - if (j$.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - this.seen.pop(); - } else { - this.emitScalar(value.toString()); - } - } catch (e) { - if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { - throw e; - } - } finally { - this.ppNestLevel_--; - } - }; - - SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) { - return customFormat(value, this.customObjectFormatters_); - }; - - SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) { - const objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj)); - const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - - for (let i = 0; i < length; i++) { - fn(objKeys[i]); - } - - return objKeys.length > length; - }; - - SinglePrettyPrintRun.prototype.emitScalar = function(value) { - this.append(value); - }; - - SinglePrettyPrintRun.prototype.emitString = function(value) { - this.append("'" + value + "'"); - }; - - SinglePrettyPrintRun.prototype.emitArray = function(array) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Array'); - return; - } - - const length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - this.append('[ '); - - for (let i = 0; i < length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - if (array.length > length) { - this.append(', ...'); - } - - let first = array.length === 0; - const wasTruncated = this.iterateObject(array, property => { - if (first) { - first = false; - } else { - this.append(', '); - } - - this.formatProperty(array, property); - }); - - if (wasTruncated) { - this.append(', ...'); - } - - this.append(' ]'); - }; - - SinglePrettyPrintRun.prototype.emitSet = function(set) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Set'); - return; - } - this.append('Set( '); - const size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - let i = 0; - set.forEach(function(value, key) { - if (i >= size) { - return; - } - if (i > 0) { - this.append(', '); - } - this.format(value); - - i++; - }, this); - if (set.size > size) { - this.append(', ...'); - } - this.append(' )'); - }; - - SinglePrettyPrintRun.prototype.emitMap = function(map) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Map'); - return; - } - this.append('Map( '); - const size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - let i = 0; - map.forEach(function(value, key) { - if (i >= size) { - return; - } - if (i > 0) { - this.append(', '); - } - this.format([key, value]); - - i++; - }, this); - if (map.size > size) { - this.append(', ...'); - } - this.append(' )'); - }; - - SinglePrettyPrintRun.prototype.emitObject = function(obj) { - const ctor = obj.constructor; - const constructorName = - typeof ctor === 'function' && obj instanceof ctor - ? j$.fnNameFor(obj.constructor) - : 'null'; - - this.append(constructorName); - - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - return; - } - - this.append('({ '); - let first = true; - - const wasTruncated = this.iterateObject(obj, property => { - if (first) { - first = false; - } else { - this.append(', '); - } - - this.formatProperty(obj, property); - }); - - if (wasTruncated) { - this.append(', ...'); - } - - this.append(' })'); - }; - - SinglePrettyPrintRun.prototype.emitTypedArray = function(arr) { - const constructorName = j$.fnNameFor(arr.constructor); - const limitedArray = Array.prototype.slice.call( - arr, - 0, - j$.MAX_PRETTY_PRINT_ARRAY_LENGTH - ); - let itemsString = Array.prototype.join.call(limitedArray, ', '); - - if (limitedArray.length !== arr.length) { - itemsString += ', ...'; - } - - this.append(constructorName + ' [ ' + itemsString + ' ]'); - }; - - SinglePrettyPrintRun.prototype.emitDomElement = function(el) { - const tagName = el.tagName.toLowerCase(); - let out = '<' + tagName; - - for (const attr of el.attributes) { - out += ' ' + attr.name; - - if (attr.value !== '') { - out += '="' + attr.value + '"'; - } - } - - out += '>'; - - if (el.childElementCount !== 0 || el.textContent !== '') { - out += '...'; - } - - this.append(out); - }; - - SinglePrettyPrintRun.prototype.formatProperty = function(obj, property) { - if (typeof property === 'symbol') { - this.append(property.toString()); - } else { - this.append(property); - } - - this.append(': '); - this.format(obj[property]); - }; - - SinglePrettyPrintRun.prototype.append = function(value) { - // This check protects us from the rare case where an object has overriden - // `toString()` with an invalid implementation (returning a non-string). - if (typeof value !== 'string') { - value = Object.prototype.toString.call(value); - } - - const result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); - this.length += result.value.length; - this.stringParts.push(result.value); - - if (result.truncated) { - throw new MaxCharsReachedError(); - } - }; - function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; diff --git a/spec/core/matchers/DiffBuilderSpec.js b/spec/core/matchers/DiffBuilderSpec.js index 69753bf5..b15d0e2c 100644 --- a/spec/core/matchers/DiffBuilderSpec.js +++ b/spec/core/matchers/DiffBuilderSpec.js @@ -1,6 +1,6 @@ describe('DiffBuilder', function() { it('records the actual and expected objects', function() { - const diffBuilder = jasmineUnderTest.DiffBuilder(); + const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ x: 'actual' }, { x: 'expected' }); diffBuilder.recordMismatch(); @@ -10,7 +10,7 @@ describe('DiffBuilder', function() { }); it('prints the path at which the difference was found', function() { - const diffBuilder = jasmineUnderTest.DiffBuilder(); + const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ foo: { x: 'actual' } }, { foo: { x: 'expected' } }); diffBuilder.withPath('foo', function() { @@ -23,7 +23,7 @@ describe('DiffBuilder', function() { }); it('prints multiple messages, separated by newlines', function() { - const diffBuilder = jasmineUnderTest.DiffBuilder(); + const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ foo: 1, bar: 3 }, { foo: 2, bar: 4 }); diffBuilder.withPath('foo', function() { @@ -40,7 +40,7 @@ describe('DiffBuilder', function() { }); it('allows customization of the message', function() { - const diffBuilder = jasmineUnderTest.DiffBuilder(); + const diffBuilder = new jasmineUnderTest.DiffBuilder(); diffBuilder.setRoots({ x: 'bar' }, { x: 'foo' }); function darthVaderFormatter(actual, expected, path) { @@ -68,7 +68,7 @@ describe('DiffBuilder', function() { const prettyPrinter = function(val) { return '|' + val + '|'; }, - diffBuilder = jasmineUnderTest.DiffBuilder({ + diffBuilder = new jasmineUnderTest.DiffBuilder({ prettyPrinter: prettyPrinter }); prettyPrinter.customFormat_ = function() {}; @@ -86,7 +86,7 @@ describe('DiffBuilder', function() { it('passes the injected pretty-printer to the diff formatter', function() { const diffFormatter = jasmine.createSpy('diffFormatter'), prettyPrinter = function() {}, - diffBuilder = jasmineUnderTest.DiffBuilder({ + diffBuilder = new jasmineUnderTest.DiffBuilder({ prettyPrinter: prettyPrinter }); prettyPrinter.customFormat_ = function() {}; diff --git a/src/core/PrettyPrinter.js b/src/core/PrettyPrinter.js index 8e1dae36..f3231359 100644 --- a/src/core/PrettyPrinter.js +++ b/src/core/PrettyPrinter.js @@ -1,11 +1,298 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) { - function SinglePrettyPrintRun(customObjectFormatters, pp) { - this.customObjectFormatters_ = customObjectFormatters; - this.ppNestLevel_ = 0; - this.seen = []; - this.length = 0; - this.stringParts = []; - this.pp_ = pp; + class SinglePrettyPrintRun { + constructor(customObjectFormatters, pp) { + this.customObjectFormatters_ = customObjectFormatters; + this.ppNestLevel_ = 0; + this.seen = []; + this.length = 0; + this.stringParts = []; + this.pp_ = pp; + } + + format(value) { + this.ppNestLevel_++; + try { + const customFormatResult = this.applyCustomFormatters_(value); + + if (customFormatResult) { + this.emitScalar(customFormatResult); + } else if (j$.util.isUndefined(value)) { + this.emitScalar('undefined'); + } else if (value === null) { + this.emitScalar('null'); + } else if (value === 0 && 1 / value === -Infinity) { + this.emitScalar('-0'); + } else if (value === j$.getGlobal()) { + this.emitScalar(''); + } else if (value.jasmineToString) { + this.emitScalar(value.jasmineToString(this.pp_)); + } else if (j$.isString_(value)) { + this.emitString(value); + } else if (j$.isSpy(value)) { + this.emitScalar('spy on ' + value.and.identity); + } else if (j$.isSpy(value.toString)) { + this.emitScalar('spy on ' + value.toString.and.identity); + } else if (value instanceof RegExp) { + this.emitScalar(value.toString()); + } else if (typeof value === 'function') { + this.emitScalar('Function'); + } else if (j$.isDomNode(value)) { + if (value.tagName) { + this.emitDomElement(value); + } else { + this.emitScalar('HTMLNode'); + } + } else if (value instanceof Date) { + this.emitScalar('Date(' + value + ')'); + } else if (j$.isSet(value)) { + this.emitSet(value); + } else if (j$.isMap(value)) { + this.emitMap(value); + } else if (j$.isTypedArray_(value)) { + this.emitTypedArray(value); + } else if ( + value.toString && + typeof value === 'object' && + !j$.isArray_(value) && + hasCustomToString(value) + ) { + try { + this.emitScalar(value.toString()); + } catch (e) { + this.emitScalar('has-invalid-toString-method'); + } + } else if (j$.util.arrayContains(this.seen, value)) { + this.emitScalar( + '' + ); + } else if (j$.isArray_(value) || j$.isA_('Object', value)) { + this.seen.push(value); + if (j$.isArray_(value)) { + this.emitArray(value); + } else { + this.emitObject(value); + } + this.seen.pop(); + } else { + this.emitScalar(value.toString()); + } + } catch (e) { + if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { + throw e; + } + } finally { + this.ppNestLevel_--; + } + } + + applyCustomFormatters_(value) { + return customFormat(value, this.customObjectFormatters_); + } + + iterateObject(obj, fn) { + const objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj)); + const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + + for (let i = 0; i < length; i++) { + fn(objKeys[i]); + } + + return objKeys.length > length; + } + + emitScalar(value) { + this.append(value); + } + + emitString(value) { + this.append("'" + value + "'"); + } + + emitArray(array) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Array'); + return; + } + + const length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + this.append('[ '); + + for (let i = 0; i < length; i++) { + if (i > 0) { + this.append(', '); + } + this.format(array[i]); + } + if (array.length > length) { + this.append(', ...'); + } + + let first = array.length === 0; + const wasTruncated = this.iterateObject(array, property => { + if (first) { + first = false; + } else { + this.append(', '); + } + + this.formatProperty(array, property); + }); + + if (wasTruncated) { + this.append(', ...'); + } + + this.append(' ]'); + } + + emitSet(set) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Set'); + return; + } + this.append('Set( '); + const size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + let i = 0; + set.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format(value); + + i++; + }, this); + if (set.size > size) { + this.append(', ...'); + } + this.append(' )'); + } + + emitMap(map) { + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + this.append('Map'); + return; + } + this.append('Map( '); + const size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); + let i = 0; + map.forEach(function(value, key) { + if (i >= size) { + return; + } + if (i > 0) { + this.append(', '); + } + this.format([key, value]); + + i++; + }, this); + if (map.size > size) { + this.append(', ...'); + } + this.append(' )'); + } + + emitObject(obj) { + const ctor = obj.constructor; + const constructorName = + typeof ctor === 'function' && obj instanceof ctor + ? j$.fnNameFor(obj.constructor) + : 'null'; + + this.append(constructorName); + + if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { + return; + } + + this.append('({ '); + let first = true; + + const wasTruncated = this.iterateObject(obj, property => { + if (first) { + first = false; + } else { + this.append(', '); + } + + this.formatProperty(obj, property); + }); + + if (wasTruncated) { + this.append(', ...'); + } + + this.append(' })'); + } + + emitTypedArray(arr) { + const constructorName = j$.fnNameFor(arr.constructor); + const limitedArray = Array.prototype.slice.call( + arr, + 0, + j$.MAX_PRETTY_PRINT_ARRAY_LENGTH + ); + let itemsString = Array.prototype.join.call(limitedArray, ', '); + + if (limitedArray.length !== arr.length) { + itemsString += ', ...'; + } + + this.append(constructorName + ' [ ' + itemsString + ' ]'); + } + + emitDomElement(el) { + const tagName = el.tagName.toLowerCase(); + let out = '<' + tagName; + + for (const attr of el.attributes) { + out += ' ' + attr.name; + + if (attr.value !== '') { + out += '="' + attr.value + '"'; + } + } + + out += '>'; + + if (el.childElementCount !== 0 || el.textContent !== '') { + out += '...'; + } + + this.append(out); + } + + formatProperty(obj, property) { + if (typeof property === 'symbol') { + this.append(property.toString()); + } else { + this.append(property); + } + + this.append(': '); + this.format(obj[property]); + } + + append(value) { + // This check protects us from the rare case where an object has overriden + // `toString()` with an invalid implementation (returning a non-string). + if (typeof value !== 'string') { + value = Object.prototype.toString.call(value); + } + + const result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); + this.length += result.value.length; + this.stringParts.push(result.value); + + if (result.truncated) { + throw new MaxCharsReachedError(); + } + } } function hasCustomToString(value) { @@ -23,291 +310,6 @@ getJasmineRequireObj().makePrettyPrinter = function(j$) { } } - SinglePrettyPrintRun.prototype.format = function(value) { - this.ppNestLevel_++; - try { - const customFormatResult = this.applyCustomFormatters_(value); - - if (customFormatResult) { - this.emitScalar(customFormatResult); - } else if (j$.util.isUndefined(value)) { - this.emitScalar('undefined'); - } else if (value === null) { - this.emitScalar('null'); - } else if (value === 0 && 1 / value === -Infinity) { - this.emitScalar('-0'); - } else if (value === j$.getGlobal()) { - this.emitScalar(''); - } else if (value.jasmineToString) { - this.emitScalar(value.jasmineToString(this.pp_)); - } else if (j$.isString_(value)) { - this.emitString(value); - } else if (j$.isSpy(value)) { - this.emitScalar('spy on ' + value.and.identity); - } else if (j$.isSpy(value.toString)) { - this.emitScalar('spy on ' + value.toString.and.identity); - } else if (value instanceof RegExp) { - this.emitScalar(value.toString()); - } else if (typeof value === 'function') { - this.emitScalar('Function'); - } else if (j$.isDomNode(value)) { - if (value.tagName) { - this.emitDomElement(value); - } else { - this.emitScalar('HTMLNode'); - } - } else if (value instanceof Date) { - this.emitScalar('Date(' + value + ')'); - } else if (j$.isSet(value)) { - this.emitSet(value); - } else if (j$.isMap(value)) { - this.emitMap(value); - } else if (j$.isTypedArray_(value)) { - this.emitTypedArray(value); - } else if ( - value.toString && - typeof value === 'object' && - !j$.isArray_(value) && - hasCustomToString(value) - ) { - try { - this.emitScalar(value.toString()); - } catch (e) { - this.emitScalar('has-invalid-toString-method'); - } - } else if (j$.util.arrayContains(this.seen, value)) { - this.emitScalar( - '' - ); - } else if (j$.isArray_(value) || j$.isA_('Object', value)) { - this.seen.push(value); - if (j$.isArray_(value)) { - this.emitArray(value); - } else { - this.emitObject(value); - } - this.seen.pop(); - } else { - this.emitScalar(value.toString()); - } - } catch (e) { - if (this.ppNestLevel_ > 1 || !(e instanceof MaxCharsReachedError)) { - throw e; - } - } finally { - this.ppNestLevel_--; - } - }; - - SinglePrettyPrintRun.prototype.applyCustomFormatters_ = function(value) { - return customFormat(value, this.customObjectFormatters_); - }; - - SinglePrettyPrintRun.prototype.iterateObject = function(obj, fn) { - const objKeys = j$.MatchersUtil.keys(obj, j$.isArray_(obj)); - const length = Math.min(objKeys.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - - for (let i = 0; i < length; i++) { - fn(objKeys[i]); - } - - return objKeys.length > length; - }; - - SinglePrettyPrintRun.prototype.emitScalar = function(value) { - this.append(value); - }; - - SinglePrettyPrintRun.prototype.emitString = function(value) { - this.append("'" + value + "'"); - }; - - SinglePrettyPrintRun.prototype.emitArray = function(array) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Array'); - return; - } - - const length = Math.min(array.length, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - this.append('[ '); - - for (let i = 0; i < length; i++) { - if (i > 0) { - this.append(', '); - } - this.format(array[i]); - } - if (array.length > length) { - this.append(', ...'); - } - - let first = array.length === 0; - const wasTruncated = this.iterateObject(array, property => { - if (first) { - first = false; - } else { - this.append(', '); - } - - this.formatProperty(array, property); - }); - - if (wasTruncated) { - this.append(', ...'); - } - - this.append(' ]'); - }; - - SinglePrettyPrintRun.prototype.emitSet = function(set) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Set'); - return; - } - this.append('Set( '); - const size = Math.min(set.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - let i = 0; - set.forEach(function(value, key) { - if (i >= size) { - return; - } - if (i > 0) { - this.append(', '); - } - this.format(value); - - i++; - }, this); - if (set.size > size) { - this.append(', ...'); - } - this.append(' )'); - }; - - SinglePrettyPrintRun.prototype.emitMap = function(map) { - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - this.append('Map'); - return; - } - this.append('Map( '); - const size = Math.min(map.size, j$.MAX_PRETTY_PRINT_ARRAY_LENGTH); - let i = 0; - map.forEach(function(value, key) { - if (i >= size) { - return; - } - if (i > 0) { - this.append(', '); - } - this.format([key, value]); - - i++; - }, this); - if (map.size > size) { - this.append(', ...'); - } - this.append(' )'); - }; - - SinglePrettyPrintRun.prototype.emitObject = function(obj) { - const ctor = obj.constructor; - const constructorName = - typeof ctor === 'function' && obj instanceof ctor - ? j$.fnNameFor(obj.constructor) - : 'null'; - - this.append(constructorName); - - if (this.ppNestLevel_ > j$.MAX_PRETTY_PRINT_DEPTH) { - return; - } - - this.append('({ '); - let first = true; - - const wasTruncated = this.iterateObject(obj, property => { - if (first) { - first = false; - } else { - this.append(', '); - } - - this.formatProperty(obj, property); - }); - - if (wasTruncated) { - this.append(', ...'); - } - - this.append(' })'); - }; - - SinglePrettyPrintRun.prototype.emitTypedArray = function(arr) { - const constructorName = j$.fnNameFor(arr.constructor); - const limitedArray = Array.prototype.slice.call( - arr, - 0, - j$.MAX_PRETTY_PRINT_ARRAY_LENGTH - ); - let itemsString = Array.prototype.join.call(limitedArray, ', '); - - if (limitedArray.length !== arr.length) { - itemsString += ', ...'; - } - - this.append(constructorName + ' [ ' + itemsString + ' ]'); - }; - - SinglePrettyPrintRun.prototype.emitDomElement = function(el) { - const tagName = el.tagName.toLowerCase(); - let out = '<' + tagName; - - for (const attr of el.attributes) { - out += ' ' + attr.name; - - if (attr.value !== '') { - out += '="' + attr.value + '"'; - } - } - - out += '>'; - - if (el.childElementCount !== 0 || el.textContent !== '') { - out += '...'; - } - - this.append(out); - }; - - SinglePrettyPrintRun.prototype.formatProperty = function(obj, property) { - if (typeof property === 'symbol') { - this.append(property.toString()); - } else { - this.append(property); - } - - this.append(': '); - this.format(obj[property]); - }; - - SinglePrettyPrintRun.prototype.append = function(value) { - // This check protects us from the rare case where an object has overriden - // `toString()` with an invalid implementation (returning a non-string). - if (typeof value !== 'string') { - value = Object.prototype.toString.call(value); - } - - const result = truncate(value, j$.MAX_PRETTY_PRINT_CHARS - this.length); - this.length += result.value.length; - this.stringParts.push(result.value); - - if (result.truncated) { - throw new MaxCharsReachedError(); - } - }; - function truncate(s, maxlen) { if (s.length <= maxlen) { return { value: s, truncated: false }; diff --git a/src/core/matchers/DiffBuilder.js b/src/core/matchers/DiffBuilder.js index 50da9a69..8a6da6a4 100644 --- a/src/core/matchers/DiffBuilder.js +++ b/src/core/matchers/DiffBuilder.js @@ -1,113 +1,112 @@ getJasmineRequireObj().DiffBuilder = function(j$) { - return function DiffBuilder(config) { - const prettyPrinter = - (config || {}).prettyPrinter || j$.makePrettyPrinter(); - const mismatches = new j$.MismatchTree(); - let path = new j$.ObjectPath(); - let actualRoot = undefined; - let expectedRoot = undefined; + class DiffBuilder { + constructor(config) { + this.prettyPrinter_ = + (config || {}).prettyPrinter || j$.makePrettyPrinter(); + this.mismatches_ = new j$.MismatchTree(); + this.path_ = new j$.ObjectPath(); + this.actualRoot_ = undefined; + this.expectedRoot_ = undefined; + } - return { - setRoots: function(actual, expected) { - actualRoot = actual; - expectedRoot = expected; - }, + setRoots(actual, expected) { + this.actualRoot_ = actual; + this.expectedRoot_ = expected; + } - recordMismatch: function(formatter) { - mismatches.add(path, formatter); - }, + recordMismatch(formatter) { + this.mismatches_.add(this.path_, formatter); + } - getMessage: function() { - const messages = []; + getMessage() { + const messages = []; - mismatches.traverse(function(path, isLeaf, formatter) { - const { actual, expected } = dereferencePath( - path, - actualRoot, - expectedRoot, - prettyPrinter - ); - - if (formatter) { - messages.push(formatter(actual, expected, path, prettyPrinter)); - return true; - } - - const actualCustom = prettyPrinter.customFormat_(actual); - const expectedCustom = prettyPrinter.customFormat_(expected); - const useCustom = !( - j$.util.isUndefined(actualCustom) && - j$.util.isUndefined(expectedCustom) - ); - - if (useCustom) { - messages.push( - wrapPrettyPrinted(actualCustom, expectedCustom, path) - ); - return false; // don't recurse further - } - - if (isLeaf) { - messages.push( - defaultFormatter(actual, expected, path, prettyPrinter) - ); - } + this.mismatches_.traverse((path, isLeaf, formatter) => { + const { actual, expected } = this.dereferencePath_(path); + if (formatter) { + messages.push(formatter(actual, expected, path, this.prettyPrinter_)); return true; - }); + } - return messages.join('\n'); - }, + const actualCustom = this.prettyPrinter_.customFormat_(actual); + const expectedCustom = this.prettyPrinter_.customFormat_(expected); + const useCustom = !( + j$.util.isUndefined(actualCustom) && + j$.util.isUndefined(expectedCustom) + ); - withPath: function(pathComponent, block) { - const oldPath = path; - path = path.add(pathComponent); - block(); - path = oldPath; + if (useCustom) { + messages.push(wrapPrettyPrinted(actualCustom, expectedCustom, path)); + return false; // don't recurse further + } + + if (isLeaf) { + messages.push(this.defaultFormatter_(actual, expected, path)); + } + + return true; + }); + + return messages.join('\n'); + } + + withPath(pathComponent, block) { + const oldPath = this.path_; + this.path_ = this.path_.add(pathComponent); + block(); + this.path_ = oldPath; + } + + dereferencePath_(objectPath) { + let actual = this.actualRoot_; + let expected = this.expectedRoot_; + + const handleAsymmetricExpected = () => { + if ( + j$.isAsymmetricEqualityTester_(expected) && + j$.isFunction_(expected.valuesForDiff_) + ) { + const asymmetricResult = expected.valuesForDiff_( + actual, + this.prettyPrinter_ + ); + expected = asymmetricResult.self; + actual = asymmetricResult.other; + } + }; + + handleAsymmetricExpected(); + + for (const pc of objectPath.components) { + actual = actual[pc]; + expected = expected[pc]; + handleAsymmetricExpected(); } - }; - function defaultFormatter(actual, expected, path, prettyPrinter) { + return { actual: actual, expected: expected }; + } + + defaultFormatter_(actual, expected, path) { return wrapPrettyPrinted( - prettyPrinter(actual), - prettyPrinter(expected), + this.prettyPrinter_(actual), + this.prettyPrinter_(expected), path ); } - - function wrapPrettyPrinted(actual, expected, path) { - return ( - 'Expected ' + - path + - (path.depth() ? ' = ' : '') + - actual + - ' to equal ' + - expected + - '.' - ); - } - }; - - function dereferencePath(objectPath, actual, expected, pp) { - function handleAsymmetricExpected() { - if ( - j$.isAsymmetricEqualityTester_(expected) && - j$.isFunction_(expected.valuesForDiff_) - ) { - const asymmetricResult = expected.valuesForDiff_(actual, pp); - expected = asymmetricResult.self; - actual = asymmetricResult.other; - } - } - - handleAsymmetricExpected(); - - for (const pc of objectPath.components) { - actual = actual[pc]; - expected = expected[pc]; - handleAsymmetricExpected(); - } - - return { actual: actual, expected: expected }; } + + function wrapPrettyPrinted(actual, expected, path) { + return ( + 'Expected ' + + path + + (path.depth() ? ' = ' : '') + + actual + + ' to equal ' + + expected + + '.' + ); + } + + return DiffBuilder; }; diff --git a/src/core/matchers/MismatchTree.js b/src/core/matchers/MismatchTree.js index 9836b2d9..85176563 100644 --- a/src/core/matchers/MismatchTree.js +++ b/src/core/matchers/MismatchTree.js @@ -6,49 +6,51 @@ getJasmineRequireObj().MismatchTree = function(j$) { the expected and actual object graphs. MismatchTree maintains that context and provides it via the traverse method. */ - function MismatchTree(path) { - this.path = path || new j$.ObjectPath([]); - this.formatter = undefined; - this.children = []; - this.isMismatch = false; - } - - MismatchTree.prototype.add = function(path, formatter) { - if (path.depth() === 0) { - this.formatter = formatter; - this.isMismatch = true; - } else { - const key = path.components[0]; - path = path.shift(); - let child = this.child(key); - - if (!child) { - child = new MismatchTree(this.path.add(key)); - this.children.push(child); - } - - child.add(path, formatter); + class MismatchTree { + constructor(path) { + this.path = path || new j$.ObjectPath([]); + this.formatter = undefined; + this.children = []; + this.isMismatch = false; } - }; - MismatchTree.prototype.traverse = function(visit) { - const hasChildren = this.children.length > 0; + add(path, formatter) { + if (path.depth() === 0) { + this.formatter = formatter; + this.isMismatch = true; + } else { + const key = path.components[0]; + path = path.shift(); + let child = this.child(key); - if (this.isMismatch || hasChildren) { - if (visit(this.path, !hasChildren, this.formatter)) { - for (const child of this.children) { - child.traverse(visit); + if (!child) { + child = new MismatchTree(this.path.add(key)); + this.children.push(child); + } + + child.add(path, formatter); + } + } + + traverse(visit) { + const hasChildren = this.children.length > 0; + + if (this.isMismatch || hasChildren) { + if (visit(this.path, !hasChildren, this.formatter)) { + for (const child of this.children) { + child.traverse(visit); + } } } } - }; - MismatchTree.prototype.child = function(key) { - return this.children.find(child => { - const pathEls = child.path.components; - return pathEls[pathEls.length - 1] === key; - }); - }; + child(key) { + return this.children.find(child => { + const pathEls = child.path.components; + return pathEls[pathEls.length - 1] === key; + }); + } + } return MismatchTree; }; diff --git a/src/core/matchers/ObjectPath.js b/src/core/matchers/ObjectPath.js index f4225d5c..e87e17de 100644 --- a/src/core/matchers/ObjectPath.js +++ b/src/core/matchers/ObjectPath.js @@ -1,27 +1,29 @@ getJasmineRequireObj().ObjectPath = function(j$) { - function ObjectPath(components) { - this.components = components || []; - } - - ObjectPath.prototype.toString = function() { - if (this.components.length) { - return '$' + this.components.map(formatPropertyAccess).join(''); - } else { - return ''; + class ObjectPath { + constructor(components) { + this.components = components || []; } - }; - ObjectPath.prototype.add = function(component) { - return new ObjectPath(this.components.concat([component])); - }; + toString() { + if (this.components.length) { + return '$' + this.components.map(formatPropertyAccess).join(''); + } else { + return ''; + } + } - ObjectPath.prototype.shift = function() { - return new ObjectPath(this.components.slice(1)); - }; + add(component) { + return new ObjectPath(this.components.concat([component])); + } - ObjectPath.prototype.depth = function() { - return this.components.length; - }; + shift() { + return new ObjectPath(this.components.slice(1)); + } + + depth() { + return this.components.length; + } + } function formatPropertyAccess(prop) { if (typeof prop === 'number' || typeof prop === 'symbol') { @@ -32,7 +34,7 @@ getJasmineRequireObj().ObjectPath = function(j$) { return '.' + prop; } - return "['" + prop + "']"; + return `['${prop}']`; } function isValidIdentifier(string) { diff --git a/src/core/matchers/toEqual.js b/src/core/matchers/toEqual.js index d2db1bd8..9946d9ae 100644 --- a/src/core/matchers/toEqual.js +++ b/src/core/matchers/toEqual.js @@ -14,7 +14,7 @@ getJasmineRequireObj().toEqual = function(j$) { var result = { pass: false }, - diffBuilder = j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }); + diffBuilder = new j$.DiffBuilder({ prettyPrinter: matchersUtil.pp }); result.pass = matchersUtil.equals(actual, expected, diffBuilder);