pax_global_header00006660000000000000000000000064140231526450014514gustar00rootroot0000000000000052 comment=9b21a19c14bc206406e23376ff96f439bfc30171 concordance-5.0.4/000077500000000000000000000000001402315264500140005ustar00rootroot00000000000000concordance-5.0.4/.c8rc.json000066400000000000000000000002021402315264500156020ustar00rootroot00000000000000{ "all": true, "exclude": [ "{coverage,test}/**", ".eslintrc.js", "index.d.ts" ], "reporter": [ "html", "lcov" ] } concordance-5.0.4/.eslintrc.js000066400000000000000000000004241402315264500162370ustar00rootroot00000000000000'use strict' module.exports = { plugins: ['@novemberborn/as-i-preach'], extends: ['plugin:@novemberborn/as-i-preach/nodejs'], overrides: [ { files: ['test/**/*.js'], rules: { 'no-new-object': 'off', strict: 'off', }, }, ], } concordance-5.0.4/.github/000077500000000000000000000000001402315264500153405ustar00rootroot00000000000000concordance-5.0.4/.github/workflows/000077500000000000000000000000001402315264500173755ustar00rootroot00000000000000concordance-5.0.4/.github/workflows/ci.yml000066400000000000000000000012071402315264500205130ustar00rootroot00000000000000name: Test on: push: branches: - main pull_request: paths-ignore: - '*.md' jobs: nodejs: name: Node.js runs-on: ubuntu-latest strategy: fail-fast: false matrix: node-version: [^10.18.0, ^12.14.0, ^14] steps: - uses: actions/checkout@v1 with: fetch-depth: 1 - uses: actions/setup-node@v1 with: node-version: ${{ matrix.node-version }} - run: npm install --no-audit - run: npm test - uses: codecov/codecov-action@v1 with: file: coverage/lcov.info name: ${{ matrix.os }}/${{ matrix.node-version }} concordance-5.0.4/.gitignore000066400000000000000000000000271402315264500157670ustar00rootroot00000000000000/node_modules coverage concordance-5.0.4/.npmrc000066400000000000000000000000231402315264500151130ustar00rootroot00000000000000package-lock=false concordance-5.0.4/LICENSE000066400000000000000000000014271402315264500150110ustar00rootroot00000000000000ISC License (ISC) Copyright (c) 2017, Mark Wubben (novemberborn.net) Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. concordance-5.0.4/README.md000066400000000000000000000063061402315264500152640ustar00rootroot00000000000000# concordance Compare, format, diff and serialize any JavaScript value. Built for Node.js 10 and above. ## Behavior Concordance recursively describes JavaScript values, whether they're booleans or complex object structures. It recurses through all enumerable properties, list items (e.g. arrays) and iterator entries. The same algorithm is used when comparing, formatting or diffing values. This means Concordance's behavior is consistent, no matter how you use it. ### Comparison details * [Object wrappers](https://github.com/getify/You-Dont-Know-JS/blob/1st-ed/types%20%26%20grammar/ch3.md#boxing-wrappers) are compared both as objects and unwrapped values. Thus Concordance always treats `Object(1)` as different from `1`. * `-0` is distinct from `0`. * `NaN` equals `NaN`. * The `Argument` values can be compared to a regular array. * `Error` names and messages are always compared, even if these are not enumerable properties. * `Function` values are compared by identity only. Names are always formatted and serialized. * `Global` objects are considered equal. * `Map` keys and `Set` items are compared in-order. * `Object` string properties are compared according to the [traversal order](http://2ality.com/2015/10/property-traversal-order-es6.html). Symbol properties are compared by identity. * `Promise` values are compared by identity only. * `Symbol` values are compared by identity only. * Recursion stops whenever a circular reference is encountered. If the same cycle is present in the actual and expected values they're considered equal, but they're unequal otherwise. ### Formatting details Concordance strives to format every aspect of a value that is used for comparisons. Formatting is optimized for human legibility. Strings enjoy special formatting: * When used as keys, line break characters are escaped * Otherwise, multi-line strings are formatted using backticks, and line break characters are replaced by [control pictures](http://graphemica.com/blocks/control-pictures). Similarly, line breaks in symbol descriptions are escaped. ### Diffing details Concordance tries to minimize diff lines. This is difficult with object values, which may have similar properties but a different constructor. Multi-line strings are compared line-by-line. ### Serialization details Concordance can serialize any value for later use. Deserialized values can be compared to each other or to regular JavaScript values. The deserialized value should be passed as the **actual** value to the comparison and diffing methods. Certain value comparisons behave differently when the **actual** value is deserialized: * `Argument` values can only be compared to other `Argument` values. * `Function` values are compared by name. * `Promise` values are compared by their constructor and additional enumerable properties, but not by identity. * `Symbol` values are compared by their string serialization. [Registered](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Shared_symbols_in_the_global_symbol_registry) and [well-known symbols](https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol#Well-known_symbols) will never equal symbols with similar descriptions. concordance-5.0.4/index.js000066400000000000000000000011021402315264500154370ustar00rootroot00000000000000'use strict' const compare = require('./lib/compare') const describe = require('./lib/describe') const diff = require('./lib/diff') const format = require('./lib/format') const serialize = require('./lib/serialize') exports.compare = compare.compare exports.compareDescriptors = compare.compareDescriptors exports.describe = describe exports.diff = diff.diff exports.diffDescriptors = diff.diffDescriptors exports.format = format.format exports.formatDescriptor = format.formatDescriptor exports.serialize = serialize.serialize exports.deserialize = serialize.deserialize concordance-5.0.4/lib/000077500000000000000000000000001402315264500145465ustar00rootroot00000000000000concordance-5.0.4/lib/Circular.js000066400000000000000000000014211402315264500166460ustar00rootroot00000000000000'use strict' class Circular { constructor () { this.stack = new Map() } add (descriptor) { if (this.stack.has(descriptor)) throw new Error('Already in stack') if (descriptor.isItem !== true && descriptor.isMapEntry !== true && descriptor.isProperty !== true) { this.stack.set(descriptor, this.stack.size + 1) } return this } delete (descriptor) { if (this.stack.has(descriptor)) { if (this.stack.get(descriptor) !== this.stack.size) throw new Error('Not on top of stack') this.stack.delete(descriptor) } return this } has (descriptor) { return this.stack.has(descriptor) } get (descriptor) { return this.stack.has(descriptor) ? this.stack.get(descriptor) : 0 } } module.exports = Circular concordance-5.0.4/lib/Indenter.js000066400000000000000000000005501402315264500166540ustar00rootroot00000000000000'use strict' class Indenter { constructor (level, step) { this.level = level this.step = step this.value = step.repeat(level) } increase () { return new Indenter(this.level + 1, this.step) } decrease () { return new Indenter(this.level - 1, this.step) } toString () { return this.value } } module.exports = Indenter concordance-5.0.4/lib/Registry.js000066400000000000000000000006231402315264500167150ustar00rootroot00000000000000'use strict' class Registry { constructor () { this.counter = 0 this.map = new WeakMap() } has (value) { return this.map.has(value) } get (value) { return this.map.get(value).descriptor } alloc (value) { const index = ++this.counter const pointer = { descriptor: null, index } this.map.set(value, pointer) return pointer } } module.exports = Registry concordance-5.0.4/lib/compare.js000066400000000000000000000064631402315264500165430ustar00rootroot00000000000000'use strict' const Circular = require('./Circular') const constants = require('./constants') const describe = require('./describe') const recursorUtils = require('./recursorUtils') const shouldCompareDeep = require('./shouldCompareDeep') const symbolProperties = require('./symbolProperties') const AMBIGUOUS = constants.AMBIGUOUS const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function shortcircuitPrimitive (value) { if (value === null || value === undefined || value === true || value === false) return true const type = typeof value if (type === 'string' || type === 'symbol') return true // Don't shortcircuit NaN values if (type === 'number') return !isNaN(value) return false } function compareDescriptors (lhs, rhs) { const lhsCircular = new Circular() const rhsCircular = new Circular() const lhsStack = [] const rhsStack = [] let topIndex = -1 do { let result if (lhsCircular.has(lhs)) { result = lhsCircular.get(lhs) === rhsCircular.get(rhs) ? DEEP_EQUAL : UNEQUAL } else if (rhsCircular.has(rhs)) { result = UNEQUAL } else { result = lhs.compare(rhs) } if (result === UNEQUAL) return false if (result !== DEEP_EQUAL) { if (!shouldCompareDeep(result, lhs, rhs)) return false if (result === AMBIGUOUS && lhs.isProperty === true) { // Replace both sides by a pseudo-descriptor which collects symbol // properties instead. lhs = new symbolProperties.Collector(lhs, lhsStack[topIndex].recursor) rhs = new symbolProperties.Collector(rhs, rhsStack[topIndex].recursor) // Replace the current recursors so they can continue correctly after // the collectors have been "compared". This is necessary since the // collectors eat the first value after the last symbol property. lhsStack[topIndex].recursor = recursorUtils.unshift(lhsStack[topIndex].recursor, lhs.collectAll()) rhsStack[topIndex].recursor = recursorUtils.unshift(rhsStack[topIndex].recursor, rhs.collectAll()) } lhsCircular.add(lhs) rhsCircular.add(rhs) lhsStack.push({ subject: lhs, recursor: lhs.createRecursor() }) rhsStack.push({ subject: rhs, recursor: rhs.createRecursor() }) topIndex++ } while (topIndex >= 0) { lhs = lhsStack[topIndex].recursor() rhs = rhsStack[topIndex].recursor() if (lhs !== null && rhs !== null) { break } if (lhs === null && rhs === null) { const lhsRecord = lhsStack.pop() const rhsRecord = rhsStack.pop() lhsCircular.delete(lhsRecord.subject) rhsCircular.delete(rhsRecord.subject) topIndex-- } else { return false } } } while (topIndex >= 0) return true } exports.compareDescriptors = compareDescriptors function compare (actual, expected, options) { if (Object.is(actual, expected)) return { pass: true } // Primitive values should be the same, so if actual or expected is primitive // then the values will never compare. if (shortcircuitPrimitive(actual) || shortcircuitPrimitive(expected)) return { pass: false } actual = describe(actual, options) expected = describe(expected, options) const pass = compareDescriptors(actual, expected) return { actual, expected, pass } } exports.compare = compare concordance-5.0.4/lib/complexValues/000077500000000000000000000000001402315264500173755ustar00rootroot00000000000000concordance-5.0.4/lib/complexValues/arguments.js000066400000000000000000000025511402315264500217430ustar00rootroot00000000000000'use strict' const constants = require('../constants') const object = require('./object') const AMBIGUOUS = constants.AMBIGUOUS const UNEQUAL = constants.UNEQUAL function describe (props) { return new DescribedArgumentsValue(Object.assign({ // Treat as an array, to allow comparisons with arrays isArray: true, isList: true, }, props, { ctor: 'Arguments' })) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedArgumentsValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('ArgumentsValue') exports.tag = tag class ArgumentsValue extends object.ObjectValue { compare (expected) { if (expected.isComplex !== true) return UNEQUAL // When used on the left-hand side of a comparison, argument values may be // compared to arrays. if (expected.stringTag === 'Array') return AMBIGUOUS return super.compare(expected) } } Object.defineProperty(ArgumentsValue.prototype, 'tag', { value: tag }) const DescribedArgumentsValue = object.DescribedMixin(ArgumentsValue) class DeserializedArgumentsValue extends object.DeserializedMixin(ArgumentsValue) { compare (expected) { // Deserialized argument values may only be compared to argument values. return expected.isComplex === true && expected.stringTag === 'Array' ? UNEQUAL : super.compare(expected) } } concordance-5.0.4/lib/complexValues/arrayBuffer.js000066400000000000000000000017121402315264500222040ustar00rootroot00000000000000'use strict' const typedArray = require('./typedArray') function describe (props) { return new DescribedArrayBufferValue(Object.assign({ buffer: Buffer.from(props.value), // Set isArray and isList so the property recursor excludes the byte accessors isArray: true, isList: true, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedArrayBufferValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('ArrayBufferValue') exports.tag = tag // ArrayBuffers can be represented as regular Buffers, allowing them to be // treated as TypedArrays for the purposes of this package. class ArrayBufferValue extends typedArray.TypedArrayValue {} Object.defineProperty(ArrayBufferValue.prototype, 'tag', { value: tag }) const DescribedArrayBufferValue = typedArray.DescribedMixin(ArrayBufferValue) const DeserializedArrayBufferValue = typedArray.DeserializedMixin(ArrayBufferValue) concordance-5.0.4/lib/complexValues/boxed.js000066400000000000000000000024141402315264500210350ustar00rootroot00000000000000'use strict' const stringPrimitive = require('../primitiveValues/string').tag const recursorUtils = require('../recursorUtils') const object = require('./object') function describe (props) { return new DescribedBoxedValue(props) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedBoxedValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('BoxedValue') exports.tag = tag class BoxedValue extends object.ObjectValue {} Object.defineProperty(BoxedValue.prototype, 'tag', { value: tag }) class DescribedBoxedValue extends object.DescribedMixin(BoxedValue) { constructor (props) { super(props) this.unboxed = props.unboxed } createListRecursor () { return recursorUtils.NOOP_RECURSOR } createPropertyRecursor () { if (this.unboxed.tag !== stringPrimitive) return super.createPropertyRecursor() // Just so that createPropertyRecursor() skips the index-based character // properties. try { this.isList = true return super.createPropertyRecursor() } finally { this.isList = false } } createRecursor () { return recursorUtils.unshift(super.createRecursor(), this.unboxed) } } const DeserializedBoxedValue = object.DeserializedMixin(BoxedValue) concordance-5.0.4/lib/complexValues/dataView.js000066400000000000000000000016651402315264500215070ustar00rootroot00000000000000'use strict' const typedArray = require('./typedArray') function describe (props) { return new DescribedDataViewValue(Object.assign({ buffer: typedArray.getBuffer(props.value), // Set isArray and isList so the property recursor excludes the byte accessors isArray: true, isList: true, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedDataViewValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('DataViewValue') exports.tag = tag // DataViews can be represented as regular Buffers, allowing them to be treated // as TypedArrays for the purposes of this package. class DataViewValue extends typedArray.TypedArrayValue {} Object.defineProperty(DataViewValue.prototype, 'tag', { value: tag }) const DescribedDataViewValue = typedArray.DescribedMixin(DataViewValue) const DeserializedDataViewValue = typedArray.DeserializedMixin(DataViewValue) concordance-5.0.4/lib/complexValues/date.js000066400000000000000000000050651402315264500206560ustar00rootroot00000000000000'use strict' const dateTime = require('date-time') const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const object = require('./object') const SHALLOW_EQUAL = constants.SHALLOW_EQUAL const UNEQUAL = constants.UNEQUAL function describe (props) { const date = props.value const invalid = isNaN(date.valueOf()) return new DescribedDateValue(Object.assign({}, props, { invalid })) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedDateValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('DateValue') exports.tag = tag function formatDate (date) { // Always format in UTC. The local timezone shouldn't be used since it's most // likely different from that of CI servers. return dateTime({ date, local: false, showTimeZone: true, showMilliseconds: true, }) } class DateValue extends object.ObjectValue { constructor (props) { super(props) this.invalid = props.invalid } compare (expected) { const result = super.compare(expected) if (result !== SHALLOW_EQUAL) return result return (this.invalid && expected.invalid) || Object.is(this.value.getTime(), expected.value.getTime()) ? SHALLOW_EQUAL : UNEQUAL } formatShallow (theme, indent) { const string = formatUtils.formatCtorAndStringTag(theme, this) + ' ' + (this.invalid ? theme.date.invalid : formatUtils.wrap(theme.date.value, formatDate(this.value))) + ' ' + theme.object.openBracket return super.formatShallow(theme, indent).customize({ finalize (innerLines) { return innerLines.isEmpty ? lineBuilder.single(string + theme.object.closeBracket) : lineBuilder.first(string) .concat(innerLines.withFirstPrefixed(indent.increase()).stripFlags()) .append(lineBuilder.last(indent + theme.object.closeBracket)) }, maxDepth () { return lineBuilder.single(string + ' ' + theme.maxDepth + ' ' + theme.object.closeBracket) }, }) } serialize () { const iso = this.invalid ? null : this.value.toISOString() return [this.invalid, iso, super.serialize()] } } Object.defineProperty(DateValue.prototype, 'tag', { value: tag }) const DescribedDateValue = object.DescribedMixin(DateValue) class DeserializedDateValue extends object.DeserializedMixin(DateValue) { constructor (state, recursor) { super(state[2], recursor) this.invalid = state[0] this.value = new Date(this.invalid ? NaN : state[1]) } } concordance-5.0.4/lib/complexValues/error.js000066400000000000000000000070251402315264500210700ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const isEnumerable = require('../isEnumerable') const lineBuilder = require('../lineBuilder') const NOOP_RECURSOR = require('../recursorUtils').NOOP_RECURSOR const object = require('./object') const UNEQUAL = constants.UNEQUAL function describe (props) { const error = props.value return new DescribedErrorValue(Object.assign({ nameIsEnumerable: isEnumerable(error, 'name'), name: error.name, messageIsEnumerable: isEnumerable(error, 'message'), message: error.message, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedErrorValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('ErrorValue') exports.tag = tag class ErrorValue extends object.ObjectValue { constructor (props) { super(props) this.name = props.name } compare (expected) { return this.tag === expected.tag && this.name === expected.name ? super.compare(expected) : UNEQUAL } formatShallow (theme, indent) { const name = this.name || this.ctor let string = name ? formatUtils.wrap(theme.error.name, name) : formatUtils.wrap(theme.object.stringTag, this.stringTag) if (this.ctor && this.ctor !== name) { string += ' ' + formatUtils.wrap(theme.error.ctor, this.ctor) } if (this.stringTag && this.stringTag !== this.ctor && this.name && !this.name.includes(this.stringTag)) { string += ' ' + formatUtils.wrap(theme.object.secondaryStringTag, this.stringTag) } string += ' ' + theme.object.openBracket return super.formatShallow(theme, indent).customize({ finalize (innerLines) { return innerLines.isEmpty ? lineBuilder.single(string + theme.object.closeBracket) : lineBuilder.first(string) .concat(innerLines.withFirstPrefixed(indent.increase()).stripFlags()) .append(lineBuilder.last(indent + theme.object.closeBracket)) }, maxDepth () { return lineBuilder.single(string + ' ' + theme.maxDepth + ' ' + theme.object.closeBracket) }, }) } serialize () { return [this.name, super.serialize()] } } Object.defineProperty(ErrorValue.prototype, 'tag', { value: tag }) class DescribedErrorValue extends object.DescribedMixin(ErrorValue) { constructor (props) { super(props) this.nameIsEnumerable = props.nameIsEnumerable this.messageIsEnumerable = props.messageIsEnumerable this.message = props.message } createPropertyRecursor () { const recursor = super.createPropertyRecursor() let skipName = this.nameIsEnumerable let emitMessage = !this.messageIsEnumerable let size = recursor.size if (skipName && size > 0) { size -= 1 } if (emitMessage) { size += 1 } if (size === 0) return NOOP_RECURSOR let done = false const next = () => { if (done) return null const property = recursor.next() if (property) { if (skipName && property.key.value === 'name') { skipName = false return next() } return property } if (emitMessage) { emitMessage = false return this.describeProperty('message', this.describeAny(this.message)) } done = true return null } return { size, next } } } class DeserializedErrorValue extends object.DeserializedMixin(ErrorValue) { constructor (state, recursor) { super(state[1], recursor) this.name = state[0] } } concordance-5.0.4/lib/complexValues/function.js000066400000000000000000000063101402315264500215600ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const isEnumerable = require('../isEnumerable') const lineBuilder = require('../lineBuilder') const NOOP_RECURSOR = require('../recursorUtils').NOOP_RECURSOR const object = require('./object') const UNEQUAL = constants.UNEQUAL const SHALLOW_EQUAL = constants.SHALLOW_EQUAL function describe (props) { const fn = props.value return new DescribedFunctionValue(Object.assign({ nameIsEnumerable: isEnumerable(fn, 'name'), name: typeof fn.name === 'string' ? fn.name : null, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedFunctionValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('FunctionValue') exports.tag = tag class FunctionValue extends object.ObjectValue { constructor (props) { super(props) this.name = props.name } formatShallow (theme, indent) { const string = formatUtils.wrap(theme.function.stringTag, this.stringTag) + (this.name ? ' ' + formatUtils.wrap(theme.function.name, this.name) : '') + ' ' + theme.object.openBracket return super.formatShallow(theme, indent).customize({ finalize (innerLines) { return innerLines.isEmpty ? lineBuilder.single(string + theme.object.closeBracket) : lineBuilder.first(string) .concat(innerLines.withFirstPrefixed(indent.increase()).stripFlags()) .append(lineBuilder.last(indent + theme.object.closeBracket)) }, maxDepth () { return lineBuilder.single(string + ' ' + theme.maxDepth + ' ' + theme.object.closeBracket) }, }) } } Object.defineProperty(FunctionValue.prototype, 'tag', { value: tag }) class DescribedFunctionValue extends object.DescribedMixin(FunctionValue) { constructor (props) { super(props) this.nameIsEnumerable = props.nameIsEnumerable } compare (expected) { if (this.tag !== expected.tag) return UNEQUAL if (this.name !== expected.name) return UNEQUAL if (this.value && expected.value && this.value !== expected.value) return UNEQUAL return super.compare(expected) } createPropertyRecursor () { const recursor = super.createPropertyRecursor() const skipName = this.nameIsEnumerable if (!skipName) return recursor let size = recursor.size if (skipName) { size -= 1 } if (size === 0) return NOOP_RECURSOR const next = () => { const property = recursor.next() if (property) { if (skipName && property.key.value === 'name') { return next() } return property } return null } return { size, next } } serialize () { return [this.name, super.serialize()] } } class DeserializedFunctionValue extends object.DeserializedMixin(FunctionValue) { constructor (state, recursor) { super(state[1], recursor) this.name = state[0] } compare (expected) { if (this.tag !== expected.tag) return UNEQUAL if (this.name !== expected.name) return UNEQUAL if (this.stringTag !== expected.stringTag) return UNEQUAL return SHALLOW_EQUAL } serialize () { return [this.name, super.serialize()] } } concordance-5.0.4/lib/complexValues/global.js000066400000000000000000000014661402315264500212020ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe () { return new GlobalValue() } exports.describe = describe exports.deserialize = describe const tag = Symbol('GlobalValue') exports.tag = tag class GlobalValue { compare (expected) { return this.tag === expected.tag ? DEEP_EQUAL : UNEQUAL } formatDeep (theme) { return lineBuilder.single( formatUtils.wrap(theme.global, 'Global') + ' ' + theme.object.openBracket + theme.object.closeBracket) } } Object.defineProperty(GlobalValue.prototype, 'isComplex', { value: true }) Object.defineProperty(GlobalValue.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/complexValues/map.js000066400000000000000000000034651402315264500205200ustar00rootroot00000000000000'use strict' const constants = require('../constants') const recursorUtils = require('../recursorUtils') const object = require('./object') const SHALLOW_EQUAL = constants.SHALLOW_EQUAL const UNEQUAL = constants.UNEQUAL function describe (props) { return new DescribedMapValue(Object.assign({ size: props.value.size, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedMapValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('MapValue') exports.tag = tag class MapValue extends object.ObjectValue { constructor (props) { super(props) this.size = props.size } compare (expected) { const result = super.compare(expected) if (result !== SHALLOW_EQUAL) return result return this.size === expected.size ? SHALLOW_EQUAL : UNEQUAL } prepareDiff (expected) { // Maps should be compared, even if they have a different number of entries. return { compareResult: super.compare(expected) } } serialize () { return [this.size, super.serialize()] } } Object.defineProperty(MapValue.prototype, 'tag', { value: tag }) class DescribedMapValue extends object.DescribedMixin(MapValue) { createIterableRecursor () { const size = this.size if (size === 0) return recursorUtils.NOOP_RECURSOR let index = 0 let entries const next = () => { if (index === size) return null if (!entries) { entries = Array.from(this.value) } const entry = entries[index++] return this.describeMapEntry(this.describeAny(entry[0]), this.describeAny(entry[1])) } return { size, next } } } class DeserializedMapValue extends object.DeserializedMixin(MapValue) { constructor (state, recursor) { super(state[1], recursor) this.size = state[0] } } concordance-5.0.4/lib/complexValues/object.js000066400000000000000000000160211402315264500212010ustar00rootroot00000000000000'use strict' const constants = require('../constants') const ObjectFormatter = require('../formatUtils').ObjectFormatter const getObjectKeys = require('../getObjectKeys') const hasLength = require('../hasLength') const stats = require('../metaDescriptors/stats') const recursorUtils = require('../recursorUtils') const DEEP_EQUAL = constants.DEEP_EQUAL const SHALLOW_EQUAL = constants.SHALLOW_EQUAL const UNEQUAL = constants.UNEQUAL function describe (props) { const isArray = props.stringTag === 'Array' const object = props.value return new DescribedObjectValue(Object.assign({ isArray, isIterable: object[Symbol.iterator] !== undefined, isList: isArray || hasLength(object), }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedObjectValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('ObjectValue') exports.tag = tag class ObjectValue { constructor (props) { this.ctor = props.ctor this.pointer = props.pointer this.stringTag = props.stringTag this.isArray = props.isArray === true this.isIterable = props.isIterable === true this.isList = props.isList === true } compare (expected) { if (this.tag !== expected.tag) return UNEQUAL if (this.stringTag !== expected.stringTag || !this.hasSameCtor(expected)) return UNEQUAL return SHALLOW_EQUAL } hasSameCtor (expected) { return this.ctor === expected.ctor } formatShallow (theme, indent) { return new ObjectFormatter(this, theme, indent) } serialize () { return [ this.ctor, this.pointer, this.stringTag, this.isArray, this.isIterable, this.isList, ] } } Object.defineProperty(ObjectValue.prototype, 'isComplex', { value: true }) Object.defineProperty(ObjectValue.prototype, 'tag', { value: tag }) exports.ObjectValue = ObjectValue const DescribedObjectValue = DescribedMixin(ObjectValue) const DeserializedObjectValue = DeserializedMixin(ObjectValue) function DescribedMixin (base) { return class extends base { constructor (props) { super(props) this.value = props.value this.describeAny = props.describeAny this.describeItem = props.describeItem this.describeMapEntry = props.describeMapEntry this.describeProperty = props.describeProperty this.iterableState = null this.listState = null this.propertyState = null } compare (expected) { return this.value === expected.value ? DEEP_EQUAL : super.compare(expected) } createPropertyRecursor () { const objectKeys = getObjectKeys(this.value, this.isList ? this.value.length : 0) const size = objectKeys.size if (size === 0) return recursorUtils.NOOP_RECURSOR let index = 0 const next = () => { if (index === size) return null const key = objectKeys.keys[index++] return this.describeProperty(key, this.describeAny(this.value[key])) } return { size, next } } createListRecursor () { if (!this.isList) return recursorUtils.NOOP_RECURSOR const size = this.value.length if (size === 0) return recursorUtils.NOOP_RECURSOR let index = 0 const next = () => { if (index === size) return null const current = index index++ return this.describeItem(current, this.describeAny(this.value[current])) } return { size, next } } createIterableRecursor () { if (this.isArray || !this.isIterable) return recursorUtils.NOOP_RECURSOR const iterator = this.value[Symbol.iterator]() let first = iterator.next() let done = false let size = -1 if (first.done) { if (first.value === undefined) { size = 0 done = true } else { size = 1 } } let index = 0 const next = () => { if (done) return null while (!done) { const current = first || iterator.next() if (current === first) { first = null } if (current.done) { done = true } const item = current.value if (done && item === undefined) return null if (this.isList && this.value[index] === item) { index++ } else { return this.describeItem(index++, this.describeAny(item)) } } } return { size, next } } createRecursor () { let recursedProperty = false let recursedList = false let recursedIterable = false let recursor = null return () => { let retval = null do { if (recursor !== null) { retval = recursor.next() if (retval === null) { recursor = null } } while (recursor === null && (!recursedList || !recursedProperty || !recursedIterable)) { // Prioritize recursing lists if (!recursedList) { const replay = recursorUtils.replay(this.listState, () => this.createListRecursor()) this.listState = replay.state recursor = replay.recursor recursedList = true if (recursor !== recursorUtils.NOOP_RECURSOR) { retval = stats.describeListRecursor(recursor) } } else if (!recursedProperty) { const replay = recursorUtils.replay(this.propertyState, () => this.createPropertyRecursor()) this.propertyState = replay.state recursor = replay.recursor recursedProperty = true if (recursor !== recursorUtils.NOOP_RECURSOR) { retval = stats.describePropertyRecursor(recursor) } } else if (!recursedIterable) { const replay = recursorUtils.replay(this.iterableState, () => this.createIterableRecursor()) this.iterableState = replay.state recursor = replay.recursor recursedIterable = true if (recursor !== recursorUtils.NOOP_RECURSOR) { retval = stats.describeIterableRecursor(recursor) } } } } while (recursor !== null && retval === null) return retval } } } } exports.DescribedMixin = DescribedMixin function DeserializedMixin (base) { return class extends base { constructor (state, recursor) { super({ ctor: state[0], pointer: state[1], stringTag: state[2], isArray: state[3], isIterable: state[4], isList: state[5], }) this.deserializedRecursor = recursor this.replayState = null } createRecursor () { if (!this.deserializedRecursor) return () => null const replay = recursorUtils.replay(this.replayState, () => ({ size: -1, next: this.deserializedRecursor })) this.replayState = replay.state return replay.recursor.next } hasSameCtor (expected) { return this.ctor === expected.ctor } } } exports.DeserializedMixin = DeserializedMixin concordance-5.0.4/lib/complexValues/promise.js000066400000000000000000000020361402315264500214120ustar00rootroot00000000000000'use strict' const constants = require('../constants') const object = require('./object') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe (props) { return new DescribedPromiseValue(props) } exports.describe = describe function deserialize (props) { return new DeserializedPromiseValue(props) } exports.deserialize = deserialize const tag = Symbol('PromiseValue') exports.tag = tag class PromiseValue extends object.ObjectValue {} Object.defineProperty(PromiseValue.prototype, 'tag', { value: tag }) class DescribedPromiseValue extends object.DescribedMixin(PromiseValue) { compare (expected) { // When comparing described promises, require them to be the exact same // object. return super.compare(expected) === DEEP_EQUAL ? DEEP_EQUAL : UNEQUAL } } class DeserializedPromiseValue extends object.DeserializedMixin(PromiseValue) { compare (expected) { // Deserialized promises can never be compared using object references. return super.compare(expected) } } concordance-5.0.4/lib/complexValues/regexp.js000066400000000000000000000052551402315264500212340ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const object = require('./object') const UNEQUAL = constants.UNEQUAL function describe (props) { const regexp = props.value return new DescribedRegexpValue(Object.assign({ flags: getSortedFlags(regexp), source: regexp.source, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedRegexpValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('RegexpValue') exports.tag = tag function getSortedFlags (regexp) { const flags = regexp.flags || String(regexp).slice(regexp.source.length + 2) return flags.split('').sort().join('') } class RegexpValue extends object.ObjectValue { constructor (props) { super(props) this.flags = props.flags this.source = props.source } compare (expected) { return this.tag === expected.tag && this.flags === expected.flags && this.source === expected.source ? super.compare(expected) : UNEQUAL } formatShallow (theme, indent) { const ctor = this.ctor || this.stringTag const regexp = formatUtils.wrap(theme.regexp.source, this.source) + formatUtils.wrap(theme.regexp.flags, this.flags) return super.formatShallow(theme, indent).customize({ finalize: innerLines => { if (ctor === 'RegExp' && innerLines.isEmpty) return lineBuilder.single(regexp) const innerIndentation = indent.increase() const header = lineBuilder.first(formatUtils.formatCtorAndStringTag(theme, this) + ' ' + theme.object.openBracket) .concat(lineBuilder.line(innerIndentation + regexp)) if (!innerLines.isEmpty) { header.append(lineBuilder.line(innerIndentation + theme.regexp.separator)) header.append(innerLines.withFirstPrefixed(innerIndentation).stripFlags()) } return header.append(lineBuilder.last(indent + theme.object.closeBracket)) }, maxDepth: () => { return lineBuilder.single( formatUtils.formatCtorAndStringTag(theme, this) + ' ' + theme.object.openBracket + ' ' + regexp + ' ' + theme.maxDepth + ' ' + theme.object.closeBracket) }, }) } serialize () { return [this.flags, this.source, super.serialize()] } } Object.defineProperty(RegexpValue.prototype, 'tag', { value: tag }) const DescribedRegexpValue = object.DescribedMixin(RegexpValue) class DeserializedRegexpValue extends object.DeserializedMixin(RegexpValue) { constructor (state, recursor) { super(state[2], recursor) this.flags = state[0] this.source = state[1] } } concordance-5.0.4/lib/complexValues/set.js000066400000000000000000000034271402315264500205340ustar00rootroot00000000000000'use strict' const constants = require('../constants') const recursorUtils = require('../recursorUtils') const object = require('./object') const SHALLOW_EQUAL = constants.SHALLOW_EQUAL const UNEQUAL = constants.UNEQUAL function describe (props) { return new DescribedSetValue(Object.assign({ size: props.value.size, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedSetValue(state, recursor) } exports.deserialize = deserialize const tag = Symbol('SetValue') exports.tag = tag class SetValue extends object.ObjectValue { constructor (props) { super(props) this.size = props.size } compare (expected) { const result = super.compare(expected) if (result !== SHALLOW_EQUAL) return result return this.size === expected.size ? SHALLOW_EQUAL : UNEQUAL } prepareDiff (expected) { // Sets should be compared, even if they have a different number of items. return { compareResult: super.compare(expected) } } serialize () { return [this.size, super.serialize()] } } Object.defineProperty(SetValue.prototype, 'tag', { value: tag }) class DescribedSetValue extends object.DescribedMixin(SetValue) { createIterableRecursor () { const size = this.size if (size === 0) return recursorUtils.NOOP_RECURSOR let index = 0 let members const next = () => { if (index === size) return null if (!members) { members = Array.from(this.value) } const value = members[index] return this.describeItem(index++, this.describeAny(value)) } return { size, next } } } class DeserializedSetValue extends object.DeserializedMixin(SetValue) { constructor (state, recursor) { super(state[1], recursor) this.size = state[0] } } concordance-5.0.4/lib/complexValues/typedArray.js000066400000000000000000000105271402315264500220640ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const propertyStatsTag = require('../metaDescriptors/stats').propertyTag const recursorUtils = require('../recursorUtils') const object = require('./object') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function getBuffer (value) { const buffer = Buffer.from(value.buffer) return value.byteLength !== value.buffer.byteLength ? buffer.slice(value.byteOffset, value.byteOffset + value.byteLength) : buffer } exports.getBuffer = getBuffer function describe (props) { return new DescribedTypedArrayValue(Object.assign({ buffer: getBuffer(props.value), // Set isArray and isList so the property recursor excludes the byte accessors isArray: true, isList: true, }, props)) } exports.describe = describe function deserialize (state, recursor) { return new DeserializedTypedArrayValue(state, recursor) } exports.deserialize = deserialize function deserializeBytes (buffer) { return new Bytes(buffer) } exports.deserializeBytes = deserializeBytes const bytesTag = Symbol('Bytes') exports.bytesTag = bytesTag const tag = Symbol('TypedArrayValue') exports.tag = tag class Bytes { constructor (buffer) { this.buffer = buffer } compare (expected) { return expected.tag === bytesTag && this.buffer.equals(expected.buffer) ? DEEP_EQUAL : UNEQUAL } formatDeep (theme, indent) { const indentation = indent const lines = lineBuilder.buffer() // Display 4-byte words, 8 per line let string = '' let isFirst = true for (let offset = 0; offset < this.buffer.length; offset += 4) { if (offset > 0) { if (offset % 32 === 0) { if (isFirst) { lines.append(lineBuilder.first(string)) isFirst = false } else { lines.append(lineBuilder.line(string)) } string = String(indentation) } else { string += ' ' } } string += formatUtils.wrap(theme.typedArray.bytes, this.buffer.toString('hex', offset, offset + 4)) } return isFirst ? lineBuilder.single(string) : lines.append(lineBuilder.last(string)) } serialize () { return this.buffer } } Object.defineProperty(Bytes.prototype, 'tag', { value: bytesTag }) class TypedArrayValue extends object.ObjectValue { constructor (props) { super(props) this.buffer = props.buffer } formatShallow (theme, indent) { return super.formatShallow(theme, indent).customize({ shouldFormat (subject) { if (subject.tag === propertyStatsTag) return subject.size > 1 if (subject.isProperty === true) return subject.key.value !== 'byteLength' if (subject.tag === bytesTag) return subject.buffer.byteLength > 0 return true }, }) } } Object.defineProperty(TypedArrayValue.prototype, 'tag', { value: tag }) exports.TypedArrayValue = TypedArrayValue function DescribedMixin (base) { return class extends object.DescribedMixin(base) { // The list isn't recursed. Instead a Bytes instance is returned by the main // recursor. createListRecursor () { return recursorUtils.NOOP_RECURSOR } createPropertyRecursor () { const recursor = super.createPropertyRecursor() const size = recursor.size + 1 let done = false const next = () => { if (done) return null const property = recursor.next() if (property) return property done = true return this.describeProperty('byteLength', this.describeAny(this.buffer.byteLength)) } return { size, next } } createRecursor () { return recursorUtils.unshift(super.createRecursor(), new Bytes(this.buffer)) } } } exports.DescribedMixin = DescribedMixin const DescribedTypedArrayValue = DescribedMixin(TypedArrayValue) function DeserializedMixin (base) { return class extends object.DeserializedMixin(base) { constructor (state, recursor) { super(state, recursor) // Get the Bytes descriptor from the recursor. It contains the buffer. const bytesDescriptor = this.createRecursor()() this.buffer = bytesDescriptor.buffer } } } exports.DeserializedMixin = DeserializedMixin const DeserializedTypedArrayValue = DeserializedMixin(TypedArrayValue) concordance-5.0.4/lib/constants.js000066400000000000000000000003711402315264500171210ustar00rootroot00000000000000'use strict' const AMBIGUOUS = Symbol('AMBIGUOUS') const DEEP_EQUAL = Symbol('DEEP_EQUAL') const SHALLOW_EQUAL = Symbol('SHALLOW_EQUAL') const UNEQUAL = Symbol('UNEQUAL') module.exports = { AMBIGUOUS, DEEP_EQUAL, SHALLOW_EQUAL, UNEQUAL, } concordance-5.0.4/lib/describe.js000066400000000000000000000135151402315264500166710ustar00rootroot00000000000000'use strict' const Registry = require('./Registry') const argumentsValue = require('./complexValues/arguments') const arrayBufferValue = require('./complexValues/arrayBuffer') const boxedValue = require('./complexValues/boxed') const dataViewValue = require('./complexValues/dataView') const dateValue = require('./complexValues/date') const errorValue = require('./complexValues/error') const functionValue = require('./complexValues/function') const globalValue = require('./complexValues/global') const mapValue = require('./complexValues/map') const objectValue = require('./complexValues/object') const promiseValue = require('./complexValues/promise') const regexpValue = require('./complexValues/regexp') const setValue = require('./complexValues/set') const typedArrayValue = require('./complexValues/typedArray') const getCtor = require('./getCtor') const getStringTag = require('./getStringTag') const itemDescriptor = require('./metaDescriptors/item') const mapEntryDescriptor = require('./metaDescriptors/mapEntry') const propertyDescriptor = require('./metaDescriptors/property') const pluginRegistry = require('./pluginRegistry') const bigIntValue = require('./primitiveValues/bigInt') const booleanValue = require('./primitiveValues/boolean') const nullValue = require('./primitiveValues/null') const numberValue = require('./primitiveValues/number') const stringValue = require('./primitiveValues/string') const symbolValue = require('./primitiveValues/symbol') const undefinedValue = require('./primitiveValues/undefined') const SpecializedComplexes = new Map([ ['Arguments', argumentsValue.describe], ['ArrayBuffer', arrayBufferValue.describe], ['DataView', dataViewValue.describe], ['Date', dateValue.describe], ['Error', errorValue.describe], ['Float32Array', typedArrayValue.describe], ['Float64Array', typedArrayValue.describe], ['Function', functionValue.describe], ['GeneratorFunction', functionValue.describe], ['global', globalValue.describe], ['Int16Array', typedArrayValue.describe], ['Int32Array', typedArrayValue.describe], ['Int8Array', typedArrayValue.describe], ['Map', mapValue.describe], ['Promise', promiseValue.describe], ['RegExp', regexpValue.describe], ['Set', setValue.describe], ['Uint16Array', typedArrayValue.describe], ['Uint32Array', typedArrayValue.describe], ['Uint8Array', typedArrayValue.describe], ['Uint8ClampedArray', typedArrayValue.describe], ]) function describePrimitive (value) { if (value === null) return nullValue.describe() if (value === undefined) return undefinedValue.describe() if (value === true || value === false) return booleanValue.describe(value) const type = typeof value if (type === 'bigint') return bigIntValue.describe(value) if (type === 'number') return numberValue.describe(value) if (type === 'string') return stringValue.describe(value) if (type === 'symbol') return symbolValue.describe(value) return null } function unboxComplex (tag, complex) { // Try to unbox by calling `valueOf()`. `describePrimitive()` will return // `null` if the resulting value is not a primitive, in which case it's // ignored. if (typeof complex.valueOf === 'function') { const value = complex.valueOf() if (value !== complex) return describePrimitive(value) } return null } function registerPlugins (plugins) { if (!Array.isArray(plugins) || plugins.length === 0) return () => null const tryFns = pluginRegistry.getTryDescribeValues(plugins) return (value, stringTag, ctor) => { for (const tryDescribeValue of tryFns) { const describeValue = tryDescribeValue(value, stringTag, ctor) if (describeValue) return describeValue } return null } } function describeComplex (value, registry, tryPlugins, describeAny, describeItem, describeMapEntry, describeProperty) { if (registry.has(value)) return registry.get(value) const stringTag = getStringTag(value) const ctor = getCtor(stringTag, value) const pointer = registry.alloc(value) let unboxed let describeValue = tryPlugins(value, stringTag, ctor) if (describeValue === null) { if (SpecializedComplexes.has(stringTag)) { describeValue = SpecializedComplexes.get(stringTag) } else { unboxed = unboxComplex(stringTag, value) if (unboxed !== null) { describeValue = boxedValue.describe } else { describeValue = objectValue.describe } } } const descriptor = describeValue({ ctor, describeAny, describeItem, describeMapEntry, describeProperty, pointer: pointer.index, stringTag, unboxed, value, }) pointer.descriptor = descriptor return descriptor } const describeItem = (index, valueDescriptor) => { return valueDescriptor.isPrimitive === true ? itemDescriptor.describePrimitive(index, valueDescriptor) : itemDescriptor.describeComplex(index, valueDescriptor) } const describeMapEntry = (keyDescriptor, valueDescriptor) => { return mapEntryDescriptor.describe(keyDescriptor, valueDescriptor) } function describe (value, options) { const primitive = describePrimitive(value) if (primitive !== null) return primitive const registry = new Registry() const tryPlugins = registerPlugins(options && options.plugins) const curriedComplex = c => { return describeComplex(c, registry, tryPlugins, describeAny, describeItem, describeMapEntry, describeProperty) } const describeAny = any => { const descriptor = describePrimitive(any) return descriptor !== null ? descriptor : curriedComplex(any) } const describeProperty = (key, valueDescriptor) => { const keyDescriptor = describePrimitive(key) return valueDescriptor.isPrimitive === true ? propertyDescriptor.describePrimitive(keyDescriptor, valueDescriptor) : propertyDescriptor.describeComplex(keyDescriptor, valueDescriptor) } return curriedComplex(value) } module.exports = describe concordance-5.0.4/lib/diff.js000066400000000000000000000327551402315264500160300ustar00rootroot00000000000000'use strict' const Circular = require('./Circular') const Indenter = require('./Indenter') const constants = require('./constants') const describe = require('./describe') const lineBuilder = require('./lineBuilder') const recursorUtils = require('./recursorUtils') const shouldCompareDeep = require('./shouldCompareDeep') const symbolProperties = require('./symbolProperties') const themeUtils = require('./themeUtils') const AMBIGUOUS = constants.AMBIGUOUS const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL const SHALLOW_EQUAL = constants.SHALLOW_EQUAL const NOOP = Symbol('NOOP') const alwaysFormat = () => true function compareComplexShape (lhs, rhs) { let result = lhs.compare(rhs) if (result === DEEP_EQUAL) return DEEP_EQUAL if (result === UNEQUAL || !shouldCompareDeep(result, lhs, rhs)) return UNEQUAL let collectedSymbolProperties = false let lhsRecursor = lhs.createRecursor() let rhsRecursor = rhs.createRecursor() do { lhs = lhsRecursor() rhs = rhsRecursor() if (lhs === null && rhs === null) return SHALLOW_EQUAL if (lhs === null || rhs === null) return UNEQUAL result = lhs.compare(rhs) if (result === UNEQUAL) return UNEQUAL if ( result === AMBIGUOUS && lhs.isProperty === true && !collectedSymbolProperties && shouldCompareDeep(result, lhs, rhs) ) { collectedSymbolProperties = true const lhsCollector = new symbolProperties.Collector(lhs, lhsRecursor) const rhsCollector = new symbolProperties.Collector(rhs, rhsRecursor) lhsRecursor = recursorUtils.sequence( lhsCollector.createRecursor(), recursorUtils.unshift(lhsRecursor, lhsCollector.collectAll())) rhsRecursor = recursorUtils.sequence( rhsCollector.createRecursor(), recursorUtils.unshift(rhsRecursor, rhsCollector.collectAll())) } } while (true) } function diffDescriptors (lhs, rhs, options) { const theme = themeUtils.normalize(options) const invert = options ? options.invert === true : false const lhsCircular = new Circular() const rhsCircular = new Circular() const maxDepth = (options && options.maxDepth) || 0 let indent = new Indenter(0, ' ') const lhsStack = [] const rhsStack = [] let topIndex = -1 const buffer = lineBuilder.buffer() const diffStack = [] let diffIndex = -1 const isCircular = descriptor => lhsCircular.has(descriptor) || rhsCircular.has(descriptor) const format = (builder, subject, circular, depthOffset = 0) => { if (diffIndex >= 0 && !diffStack[diffIndex].shouldFormat(subject)) return if (circular.has(subject)) { diffStack[diffIndex].formatter.append(builder.single(theme.circular)) return } const formatStack = [] let formatIndex = -1 do { if (circular.has(subject)) { formatStack[formatIndex].formatter.append(builder.single(theme.circular), subject) } else { let didFormat = false if (typeof subject.formatDeep === 'function') { let formatted = subject.formatDeep(themeUtils.applyModifiers(subject, theme), indent) if (formatted !== null) { didFormat = true if (formatIndex === -1) { formatted = builder.setDefaultGutter(formatted) if (diffIndex === -1) { buffer.append(formatted) } else { diffStack[diffIndex].formatter.append(formatted, subject) } } else { formatStack[formatIndex].formatter.append(formatted, subject) } } } if (!didFormat && typeof subject.formatShallow === 'function') { const formatter = subject.formatShallow(themeUtils.applyModifiers(subject, theme), indent) const recursor = subject.createRecursor() if (formatter.increaseIndent && maxDepth > 0 && indent.level === (maxDepth + depthOffset)) { const isEmpty = recursor() === null let formatted = !isEmpty && typeof formatter.maxDepth === 'function' ? formatter.maxDepth() : formatter.finalize() if (formatIndex === -1) { formatted = builder.setDefaultGutter(formatted) diffStack[diffIndex].formatter.append(formatted, subject) } else { formatStack[formatIndex].formatter.append(formatted, subject) } } else { formatStack.push({ formatter, recursor, decreaseIndent: formatter.increaseIndent, shouldFormat: formatter.shouldFormat || alwaysFormat, subject, }) formatIndex++ if (formatter.increaseIndent) indent = indent.increase() circular.add(subject) } } } while (formatIndex >= 0) { do { subject = formatStack[formatIndex].recursor() } while (subject && !formatStack[formatIndex].shouldFormat(subject)) if (subject) { break } const record = formatStack.pop() formatIndex-- if (record.decreaseIndent) indent = indent.decrease() circular.delete(record.subject) let formatted = record.formatter.finalize() if (formatIndex === -1) { formatted = builder.setDefaultGutter(formatted) if (diffIndex === -1) { buffer.append(formatted) } else { diffStack[diffIndex].formatter.append(formatted, record.subject) } } else { formatStack[formatIndex].formatter.append(formatted, record.subject) } } } while (formatIndex >= 0) } do { let compareResult = NOOP if (lhsCircular.has(lhs)) { compareResult = lhsCircular.get(lhs) === rhsCircular.get(rhs) ? DEEP_EQUAL : UNEQUAL } else if (rhsCircular.has(rhs)) { compareResult = UNEQUAL } let firstPassSymbolProperty = false if (lhs.isProperty === true) { compareResult = lhs.compare(rhs) if (compareResult === AMBIGUOUS) { const parent = lhsStack[topIndex].subject firstPassSymbolProperty = parent.isSymbolPropertiesCollector !== true && parent.isSymbolPropertiesComparable !== true } } let didFormat = false let mustRecurse = false if (compareResult !== DEEP_EQUAL && !firstPassSymbolProperty && typeof lhs.prepareDiff === 'function') { const lhsRecursor = topIndex === -1 ? null : lhsStack[topIndex].recursor const rhsRecursor = topIndex === -1 ? null : rhsStack[topIndex].recursor const instructions = lhs.prepareDiff( rhs, lhsRecursor, rhsRecursor, compareComplexShape, isCircular) if (instructions !== null) { if (topIndex >= 0) { if (typeof instructions.lhsRecursor === 'function') { lhsStack[topIndex].recursor = instructions.lhsRecursor } if (typeof instructions.rhsRecursor === 'function') { rhsStack[topIndex].recursor = instructions.rhsRecursor } } if (instructions.compareResult) { compareResult = instructions.compareResult } if (instructions.mustRecurse === true) { mustRecurse = true } else { if (instructions.actualIsExtraneous === true) { format(lineBuilder.actual, lhs, lhsCircular) didFormat = true } else if (instructions.multipleAreExtraneous === true) { for (const extraneous of instructions.descriptors) { format(lineBuilder.actual, extraneous, lhsCircular) } didFormat = true } else if (instructions.expectedIsMissing === true) { format(lineBuilder.expected, rhs, rhsCircular) didFormat = true } else if (instructions.multipleAreMissing === true) { for (const missing of instructions.descriptors) { format(lineBuilder.expected, missing, rhsCircular) } didFormat = true } else if (instructions.isUnequal === true) { format(lineBuilder.actual, lhs, lhsCircular) format(lineBuilder.expected, rhs, rhsCircular) didFormat = true } else if (!instructions.compareResult) { // TODO: Throw a useful, custom error throw new Error('Illegal result of prepareDiff()') } } } } if (!didFormat) { if (compareResult === NOOP) { compareResult = lhs.compare(rhs) } if (!mustRecurse) { mustRecurse = shouldCompareDeep(compareResult, lhs, rhs) } if (compareResult === DEEP_EQUAL) { format(lineBuilder, lhs, lhsCircular) } else if (mustRecurse) { if (compareResult === AMBIGUOUS && lhs.isProperty === true) { // Replace both sides by a pseudo-descriptor which collects symbol // properties instead. lhs = new symbolProperties.Collector(lhs, lhsStack[topIndex].recursor) rhs = new symbolProperties.Collector(rhs, rhsStack[topIndex].recursor) // Replace the current recursors so they can continue correctly after // the collectors have been "compared". This is necessary since the // collectors eat the first value after the last symbol property. lhsStack[topIndex].recursor = recursorUtils.unshift(lhsStack[topIndex].recursor, lhs.collectAll()) rhsStack[topIndex].recursor = recursorUtils.unshift(rhsStack[topIndex].recursor, rhs.collectAll()) } if (typeof lhs.diffShallow === 'function') { const formatter = lhs.diffShallow(rhs, themeUtils.applyModifiers(lhs, theme), indent) diffStack.push({ formatter, origin: lhs, decreaseIndent: formatter.increaseIndent, exceedsMaxDepth: formatter.increaseIndent && maxDepth > 0 && indent.level >= maxDepth, shouldFormat: formatter.shouldFormat || alwaysFormat, }) diffIndex++ if (formatter.increaseIndent) indent = indent.increase() } else if (typeof lhs.formatShallow === 'function') { const formatter = lhs.formatShallow(themeUtils.applyModifiers(lhs, theme), indent) diffStack.push({ formatter, decreaseIndent: formatter.increaseIndent, exceedsMaxDepth: formatter.increaseIndent && maxDepth > 0 && indent.level >= maxDepth, shouldFormat: formatter.shouldFormat || alwaysFormat, subject: lhs, }) diffIndex++ if (formatter.increaseIndent) indent = indent.increase() } lhsCircular.add(lhs) rhsCircular.add(rhs) lhsStack.push({ diffIndex, subject: lhs, recursor: lhs.createRecursor() }) rhsStack.push({ diffIndex, subject: rhs, recursor: rhs.createRecursor() }) topIndex++ } else { const diffed = typeof lhs.diffDeep === 'function' ? lhs.diffDeep(rhs, themeUtils.applyModifiers(lhs, theme), indent, invert) : null if (diffed === null) { format(lineBuilder.actual, lhs, lhsCircular) format(lineBuilder.expected, rhs, rhsCircular) } else { if (diffIndex === -1) { buffer.append(diffed) } else { diffStack[diffIndex].formatter.append(diffed, lhs) } } } } while (topIndex >= 0) { lhs = lhsStack[topIndex].recursor() rhs = rhsStack[topIndex].recursor() if (lhs !== null && rhs !== null) { break } if (lhs === null && rhs === null) { const lhsRecord = lhsStack.pop() const rhsRecord = rhsStack.pop() lhsCircular.delete(lhsRecord.subject) rhsCircular.delete(rhsRecord.subject) topIndex-- if (lhsRecord.diffIndex === diffIndex) { const record = diffStack.pop() diffIndex-- if (record.decreaseIndent) indent = indent.decrease() let formatted = record.formatter.finalize() if (record.exceedsMaxDepth && !formatted.hasGutter) { // The record exceeds the max depth, but contains no actual diff. // Discard the potentially deep formatting and format just the // original subject. const subject = lhsRecord.subject const formatter = subject.formatShallow(themeUtils.applyModifiers(subject, theme), indent) const isEmpty = subject.createRecursor()() === null formatted = !isEmpty && typeof formatter.maxDepth === 'function' ? formatter.maxDepth() : formatter.finalize() } if (diffIndex === -1) { buffer.append(formatted) } else { diffStack[diffIndex].formatter.append(formatted, record.subject) } } } else { let builder, circular, stack, subject if (lhs === null) { builder = lineBuilder.expected circular = rhsCircular stack = rhsStack subject = rhs } else { builder = lineBuilder.actual circular = lhsCircular stack = lhsStack subject = lhs } do { format(builder, subject, circular, indent.level) subject = stack[topIndex].recursor() } while (subject !== null) } } } while (topIndex >= 0) return buffer.toString({ diff: true, invert, theme }) } exports.diffDescriptors = diffDescriptors function diff (actual, expected, options) { return diffDescriptors(describe(actual, options), describe(expected, options), options) } exports.diff = diff concordance-5.0.4/lib/encoder.js000066400000000000000000000225421402315264500165300ustar00rootroot00000000000000'use strict' const flattenDeep = require('lodash/flattenDeep') // Indexes are hexadecimal to make reading the binary output easier. const valueTypes = { zero: 0x00, int8: 0x01, // Note that the hex value equals the number of bytes required int16: 0x02, // to store the integer. int24: 0x03, int32: 0x04, int40: 0x05, int48: 0x06, numberString: 0x07, negativeZero: 0x08, notANumber: 0x09, infinity: 0x0A, negativeInfinity: 0x0B, bigInt: 0x0C, undefined: 0x0D, null: 0x0E, true: 0x0F, false: 0x10, utf8: 0x11, bytes: 0x12, list: 0x13, descriptor: 0x14, } const descriptorSymbol = Symbol('descriptor') exports.descriptorSymbol = descriptorSymbol function encodeInteger (type, value) { const encoded = Buffer.alloc(type) encoded.writeIntLE(value, 0, type) return [type, encoded] } function encodeValue (value) { if (Object.is(value, 0)) return valueTypes.zero if (Object.is(value, -0)) return valueTypes.negativeZero if (Object.is(value, NaN)) return valueTypes.notANumber if (value === Infinity) return valueTypes.infinity if (value === -Infinity) return valueTypes.negativeInfinity if (value === undefined) return valueTypes.undefined if (value === null) return valueTypes.null if (value === true) return valueTypes.true if (value === false) return valueTypes.false const type = typeof value if (type === 'number') { if (Number.isInteger(value)) { // The integer types are signed, so int8 can only store 7 bits, int16 // only 15, etc. if (value >= -0x80 && value < 0x80) return encodeInteger(valueTypes.int8, value) if (value >= -0x8000 && value < 0x8000) return encodeInteger(valueTypes.int16, value) if (value >= -0x800000 && value < 0x800000) return encodeInteger(valueTypes.int24, value) if (value >= -0x80000000 && value < 0x80000000) return encodeInteger(valueTypes.int32, value) if (value >= -0x8000000000 && value < 0x8000000000) return encodeInteger(valueTypes.int40, value) if (value >= -0x800000000000 && value < 0x800000000000) return encodeInteger(valueTypes.int48, value) // Fall through to encoding the value as a number string. } const encoded = Buffer.from(String(value), 'utf8') return [valueTypes.numberString, encodeValue(encoded.length), encoded] } if (type === 'string') { const encoded = Buffer.from(value, 'utf8') return [valueTypes.utf8, encodeValue(encoded.length), encoded] } if (type === 'bigint') { const encoded = Buffer.from(String(value), 'utf8') return [valueTypes.bigInt, encodeValue(encoded.length), encoded] } if (Buffer.isBuffer(value)) { return [valueTypes.bytes, encodeValue(value.byteLength), value] } if (Array.isArray(value)) { return [ value[descriptorSymbol] === true ? valueTypes.descriptor : valueTypes.list, encodeValue(value.length), value.map(x => encodeValue(x)), ] } const hex = `0x${type.toString(16).toUpperCase()}` throw new TypeError(`Unexpected value with type ${hex}`) } function decodeValue (buffer, byteOffset) { const type = buffer.readUInt8(byteOffset) byteOffset += 1 if (type === valueTypes.zero) return { byteOffset, value: 0 } if (type === valueTypes.negativeZero) return { byteOffset, value: -0 } if (type === valueTypes.notANumber) return { byteOffset, value: NaN } if (type === valueTypes.infinity) return { byteOffset, value: Infinity } if (type === valueTypes.negativeInfinity) return { byteOffset, value: -Infinity } if (type === valueTypes.undefined) return { byteOffset, value: undefined } if (type === valueTypes.null) return { byteOffset, value: null } if (type === valueTypes.true) return { byteOffset, value: true } if (type === valueTypes.false) return { byteOffset, value: false } if ( type === valueTypes.int8 || type === valueTypes.int16 || type === valueTypes.int24 || type === valueTypes.int32 || type === valueTypes.int40 || type === valueTypes.int48 ) { const value = buffer.readIntLE(byteOffset, type) byteOffset += type return { byteOffset, value } } if (type === valueTypes.numberString || type === valueTypes.utf8 || type === valueTypes.bytes || type === valueTypes.bigInt) { const length = decodeValue(buffer, byteOffset) const start = length.byteOffset const end = start + length.value if (type === valueTypes.numberString) { const value = Number(buffer.toString('utf8', start, end)) return { byteOffset: end, value } } if (type === valueTypes.utf8) { const value = buffer.toString('utf8', start, end) return { byteOffset: end, value } } if (type === valueTypes.bigInt) { const value = BigInt(buffer.toString('utf8', start, end)) // eslint-disable-line no-undef return { byteOffset: end, value } } const value = buffer.slice(start, end) return { byteOffset: end, value } } if (type === valueTypes.list || type === valueTypes.descriptor) { const length = decodeValue(buffer, byteOffset) byteOffset = length.byteOffset const value = new Array(length.value) if (type === valueTypes.descriptor) { value[descriptorSymbol] = true } for (let index = 0; index < length.value; index++) { const item = decodeValue(buffer, byteOffset) byteOffset = item.byteOffset value[index] = item.value } return { byteOffset, value } } const hex = `0x${type.toString(16).toUpperCase()}` throw new TypeError(`Could not decode type ${hex}`) } function buildBuffer (numberOrArray) { if (typeof numberOrArray === 'number') { const byte = Buffer.alloc(1) byte.writeUInt8(numberOrArray) return byte } const array = flattenDeep(numberOrArray) const buffers = new Array(array.length) let byteLength = 0 for (const [index, element] of array.entries()) { if (typeof element === 'number') { byteLength += 1 const byte = Buffer.alloc(1) byte.writeUInt8(element) buffers[index] = byte } else { byteLength += element.byteLength buffers[index] = element } } return Buffer.concat(buffers, byteLength) } function encode (serializerVersion, rootRecord, usedPlugins) { const buffers = [] let byteOffset = 0 const versionHeader = Buffer.alloc(2) versionHeader.writeUInt16LE(serializerVersion) buffers.push(versionHeader) byteOffset += versionHeader.byteLength const rootOffset = Buffer.alloc(4) buffers.push(rootOffset) byteOffset += rootOffset.byteLength const numPlugins = buildBuffer(encodeValue(usedPlugins.size)) buffers.push(numPlugins) byteOffset += numPlugins.byteLength for (const name of usedPlugins.keys()) { const plugin = usedPlugins.get(name) const record = buildBuffer([ encodeValue(name), encodeValue(plugin.serializerVersion), ]) buffers.push(record) byteOffset += record.byteLength } const queue = [rootRecord] const pointers = [rootOffset] while (queue.length > 0) { pointers.shift().writeUInt32LE(byteOffset, 0) const record = queue.shift() const recordHeader = buildBuffer([ encodeValue(record.pluginIndex), encodeValue(record.id), encodeValue(record.children.length), ]) buffers.push(recordHeader) byteOffset += recordHeader.byteLength // Add pointers before encoding the state. This allows, if it ever becomes // necessary, for records to be extracted from a buffer without having to // parse the (variable length) state field. for (const child of record.children) { queue.push(child) const pointer = Buffer.alloc(4) pointers.push(pointer) buffers.push(pointer) byteOffset += 4 } const state = buildBuffer(encodeValue(record.state)) buffers.push(state) byteOffset += state.byteLength } return Buffer.concat(buffers, byteOffset) } exports.encode = encode function decodePlugins (buffer) { const $numPlugins = decodeValue(buffer, 0) let byteOffset = $numPlugins.byteOffset const usedPlugins = new Map() const lastIndex = $numPlugins.value for (let index = 1; index <= lastIndex; index++) { const $name = decodeValue(buffer, byteOffset) const name = $name.value byteOffset = $name.byteOffset const serializerVersion = decodeValue(buffer, byteOffset).value usedPlugins.set(index, { name, serializerVersion }) } return usedPlugins } exports.decodePlugins = decodePlugins function decodeRecord (buffer, byteOffset) { const $pluginIndex = decodeValue(buffer, byteOffset) const pluginIndex = $pluginIndex.value byteOffset = $pluginIndex.byteOffset const $id = decodeValue(buffer, byteOffset) const id = $id.value byteOffset = $id.byteOffset const $numPointers = decodeValue(buffer, byteOffset) const numPointers = $numPointers.value byteOffset = $numPointers.byteOffset const pointerAddresses = new Array(numPointers) for (let index = 0; index < numPointers; index++) { pointerAddresses[index] = buffer.readUInt32LE(byteOffset) byteOffset += 4 } const state = decodeValue(buffer, byteOffset).value return { id, pluginIndex, state, pointerAddresses } } exports.decodeRecord = decodeRecord function extractVersion (buffer) { return buffer.readUInt16LE(0) } exports.extractVersion = extractVersion function decode (buffer) { const rootOffset = buffer.readUInt32LE(2) const pluginBuffer = buffer.slice(6, rootOffset) const rootRecord = decodeRecord(buffer, rootOffset) return { pluginBuffer, rootRecord } } exports.decode = decode concordance-5.0.4/lib/format.js000066400000000000000000000060031402315264500163730ustar00rootroot00000000000000'use strict' const Circular = require('./Circular') const Indenter = require('./Indenter') const describe = require('./describe') const lineBuilder = require('./lineBuilder') const themeUtils = require('./themeUtils') const alwaysFormat = () => true const fixedIndent = new Indenter(0, ' ') function formatDescriptor (subject, options) { const theme = themeUtils.normalize(options) if (subject.isPrimitive === true) { const formatted = subject.formatDeep(themeUtils.applyModifiers(subject, theme), fixedIndent) return formatted.toString({ diff: false }) } const circular = new Circular() const maxDepth = (options && options.maxDepth) || 0 let indent = fixedIndent const buffer = lineBuilder.buffer() const stack = [] let topIndex = -1 do { if (circular.has(subject)) { stack[topIndex].formatter.append(lineBuilder.single(theme.circular), subject) } else { let didFormat = false if (typeof subject.formatDeep === 'function') { const formatted = subject.formatDeep(themeUtils.applyModifiers(subject, theme), indent) if (formatted !== null) { didFormat = true if (topIndex === -1) { buffer.append(formatted) } else { stack[topIndex].formatter.append(formatted, subject) } } } if (!didFormat && typeof subject.formatShallow === 'function') { const formatter = subject.formatShallow(themeUtils.applyModifiers(subject, theme), indent) const recursor = subject.createRecursor() if (formatter.increaseIndent && maxDepth > 0 && indent.level === maxDepth) { const isEmpty = recursor() === null const formatted = !isEmpty && typeof formatter.maxDepth === 'function' ? formatter.maxDepth() : formatter.finalize() stack[topIndex].formatter.append(formatted, subject) } else { stack.push({ formatter, recursor, decreaseIndent: formatter.increaseIndent, shouldFormat: formatter.shouldFormat || alwaysFormat, subject, }) topIndex++ if (formatter.increaseIndent) indent = indent.increase() circular.add(subject) } } } while (topIndex >= 0) { do { subject = stack[topIndex].recursor() } while (subject && !stack[topIndex].shouldFormat(subject)) if (subject) { break } const record = stack.pop() topIndex-- if (record.decreaseIndent) indent = indent.decrease() circular.delete(record.subject) const formatted = record.formatter.finalize() if (topIndex === -1) { buffer.append(formatted) } else { stack[topIndex].formatter.append(formatted, record.subject) } } } while (topIndex >= 0) return buffer.toString({ diff: false }) } exports.formatDescriptor = formatDescriptor function format (value, options) { return formatDescriptor(describe(value, options), options) } exports.format = format concordance-5.0.4/lib/formatUtils.js000066400000000000000000000062741402315264500174260ustar00rootroot00000000000000'use strict' const lineBuilder = require('./lineBuilder') function wrap (fromTheme, value) { return fromTheme.open + value + fromTheme.close } exports.wrap = wrap function formatCtorAndStringTag (theme, object) { if (!object.ctor) return wrap(theme.object.stringTag, object.stringTag) let retval = wrap(theme.object.ctor, object.ctor) if (object.stringTag && object.stringTag !== object.ctor && object.stringTag !== 'Object') { retval += ' ' + wrap(theme.object.secondaryStringTag, object.stringTag) } return retval } exports.formatCtorAndStringTag = formatCtorAndStringTag class ObjectFormatter { constructor (object, theme, indent) { this.object = object this.theme = theme this.indent = indent this.increaseIndent = true this.innerLines = lineBuilder.buffer() this.pendingStats = null } append (formatted, origin) { if (origin.isStats === true) { this.pendingStats = formatted } else { if (this.pendingStats !== null) { if (!this.innerLines.isEmpty) { this.innerLines.append(this.pendingStats) } this.pendingStats = null } this.innerLines.append(formatted) } } finalize () { const variant = this.object.isList ? this.theme.list : this.theme.object const ctor = this.object.ctor const stringTag = this.object.stringTag const prefix = (ctor === 'Array' || ctor === 'Object') && ctor === stringTag ? '' : formatCtorAndStringTag(this.theme, this.object) + ' ' if (this.innerLines.isEmpty) { return lineBuilder.single(prefix + variant.openBracket + variant.closeBracket) } return lineBuilder.first(prefix + variant.openBracket) .concat(this.innerLines.withFirstPrefixed(this.indent.increase()).stripFlags()) .append(lineBuilder.last(this.indent + variant.closeBracket)) } maxDepth () { const variant = this.object.isList ? this.theme.list : this.theme.object return lineBuilder.single( formatCtorAndStringTag(this.theme, this.object) + ' ' + variant.openBracket + ' ' + this.theme.maxDepth + ' ' + variant.closeBracket) } shouldFormat () { return true } customize (methods) { if (methods.finalize) { this.finalize = () => methods.finalize(this.innerLines) } if (methods.maxDepth) { this.maxDepth = methods.maxDepth } if (methods.shouldFormat) { this.shouldFormat = methods.shouldFormat } return this } } exports.ObjectFormatter = ObjectFormatter class SingleValueFormatter { constructor (theme, finalizeFn, increaseIndent) { this.theme = theme this.finalizeFn = finalizeFn this.hasValue = false this.increaseIndent = increaseIndent === true this.value = null } append (formatted) { if (this.hasValue) throw new Error('Formatter buffer can only take one formatted value.') this.hasValue = true this.value = formatted } finalize () { if (!this.hasValue) throw new Error('Formatter buffer never received a formatted value.') return this.finalizeFn(this.value) } maxDepth () { return this.finalizeFn(lineBuilder.single(this.theme.maxDepth)) } } exports.SingleValueFormatter = SingleValueFormatter concordance-5.0.4/lib/getCtor.js000066400000000000000000000023731402315264500165200ustar00rootroot00000000000000'use strict' const hop = Object.prototype.hasOwnProperty function getCtor (stringTag, value) { if (value.constructor) { const name = value.constructor.name return typeof name === 'string' && name !== '' ? name : null } if (value.constructor === undefined) { if (stringTag !== 'Object' || value instanceof Object) return null // Values without a constructor, that do not inherit from `Object`, but are // tagged as objects, may come from `Object.create(null)`. Or they can come // from a different realm, e.g.: // // ``` // require('vm').runInNewContext(` // const Foo = function () {} // Foo.prototype.constructor = undefined // return new Foo() // `) // ``` // // Treat such objects as if they came from `Object.create(null)` (in the // current realm) only if they do not have inherited properties. This allows // these objects to be compared with object literals. // // This means `Object.create(null)` is not differentiated from `{}`. // Using `const` prevents Crankshaft optimizations for (var p in value) { // eslint-disable-line no-var if (!hop.call(value, p)) return null } return stringTag } return null } module.exports = getCtor concordance-5.0.4/lib/getObjectKeys.js000066400000000000000000000017041402315264500176500ustar00rootroot00000000000000'use strict' function getObjectKeys (obj, excludeListItemAccessorsBelowLength) { const keys = [] let size = 0 // Sort property names, they should never be order-sensitive const nameCandidates = Object.getOwnPropertyNames(obj).sort() // Comparators should verify symbols in an order-insensitive manner if // possible. const symbolCandidates = Object.getOwnPropertySymbols(obj) for (const name of nameCandidates) { let accept = true if (excludeListItemAccessorsBelowLength > 0) { const index = Number(name) accept = !Number.isInteger(index) || index < 0 || index >= excludeListItemAccessorsBelowLength } if (accept && Object.getOwnPropertyDescriptor(obj, name).enumerable) { keys[size++] = name } } for (const symbol of symbolCandidates) { if (Object.getOwnPropertyDescriptor(obj, symbol).enumerable) { keys[size++] = symbol } } return { keys, size } } module.exports = getObjectKeys concordance-5.0.4/lib/getStringTag.js000066400000000000000000000013211402315264500175030ustar00rootroot00000000000000'use strict' const ts = Object.prototype.toString function getStringTag (value) { return ts.call(value).slice(8, -1) } const fts = Function.prototype.toString const promiseCtorString = fts.call(Promise) const isPromise = value => { if (!value.constructor) return false try { return fts.call(value.constructor) === promiseCtorString } catch { return false } } if (getStringTag(Promise.resolve()) === 'Promise') { module.exports = getStringTag } else { const getStringTagWithPromiseWorkaround = value => { const stringTag = getStringTag(value) return stringTag === 'Object' && isPromise(value) ? 'Promise' : stringTag } module.exports = getStringTagWithPromiseWorkaround } concordance-5.0.4/lib/hasLength.js000066400000000000000000000004571402315264500170270ustar00rootroot00000000000000'use strict' const isLength = require('lodash/isLength') const hop = Object.prototype.hasOwnProperty function hasLength (obj) { return ( Array.isArray(obj) || (hop.call(obj, 'length') && isLength(obj.length) && (obj.length === 0 || '0' in obj)) ) } module.exports = hasLength concordance-5.0.4/lib/isEnumerable.js000066400000000000000000000002531402315264500175170ustar00rootroot00000000000000'use strict' function isEnumerable (obj, key) { const desc = Object.getOwnPropertyDescriptor(obj, key) return desc && desc.enumerable } module.exports = isEnumerable concordance-5.0.4/lib/lineBuilder.js000066400000000000000000000157631402315264500173560ustar00rootroot00000000000000'use strict' const ACTUAL = Symbol('lineBuilder.gutters.ACTUAL') const EXPECTED = Symbol('lineBuilder.gutters.EXPECTED') function translateGutter (theme, invert, gutter) { if (invert) { if (gutter === ACTUAL) return theme.diffGutters.expected if (gutter === EXPECTED) return theme.diffGutters.actual } else { if (gutter === ACTUAL) return theme.diffGutters.actual if (gutter === EXPECTED) return theme.diffGutters.expected } return theme.diffGutters.padding } class Line { constructor (isFirst, isLast, gutter, stringValue) { this.isFirst = isFirst this.isLast = isLast this.gutter = gutter this.stringValue = stringValue } * [Symbol.iterator] () { yield this } get isEmpty () { return false } get hasGutter () { return this.gutter !== null } get isSingle () { return this.isFirst && this.isLast } append (other) { return this.concat(other) } concat (other) { return new Collection() .append(this) .append(other) } toString (options) { if (options.diff === false) return this.stringValue return translateGutter(options.theme, options.invert, this.gutter) + this.stringValue } mergeWithInfix (infix, other) { if (other.isLine !== true) { return new Collection() .append(this) .mergeWithInfix(infix, other) } return new Line(this.isFirst, other.isLast, other.gutter, this.stringValue + infix + other.stringValue) } withFirstPrefixed (prefix) { if (!this.isFirst) return this return new Line(true, this.isLast, this.gutter, prefix + this.stringValue) } withLastPostfixed (postfix) { if (!this.isLast) return this return new Line(this.isFirst, true, this.gutter, this.stringValue + postfix) } stripFlags () { return new Line(false, false, this.gutter, this.stringValue) } decompose () { return new Collection() .append(this) .decompose() } } Object.defineProperty(Line.prototype, 'isLine', { value: true }) class Collection { constructor () { this.buffer = [] } * [Symbol.iterator] () { for (const appended of this.buffer) { for (const line of appended) yield line } } get isEmpty () { return this.buffer.length === 0 } get hasGutter () { for (const line of this) { if (line.hasGutter) return true } return false } get isSingle () { const iterator = this[Symbol.iterator]() iterator.next() return iterator.next().done === true } append (lineOrLines) { if (!lineOrLines.isEmpty) this.buffer.push(lineOrLines) return this } concat (other) { return new Collection() .append(this) .append(other) } toString (options) { let lines = this if (options.invert) { lines = new Collection() let buffer = new Collection() let prev = null for (const line of this) { if (line.gutter === ACTUAL) { if (prev !== null && prev.gutter !== ACTUAL && !buffer.isEmpty) { lines.append(buffer) buffer = new Collection() } buffer.append(line) } else if (line.gutter === EXPECTED) { lines.append(line) } else { if (!buffer.isEmpty) { lines.append(buffer) buffer = new Collection() } lines.append(line) } prev = line } lines.append(buffer) } return Array.from(lines, line => line.toString(options)).join('\n') } mergeWithInfix (infix, from) { if (from.isEmpty) throw new Error('Cannot merge, `from` is empty.') const otherLines = Array.from(from) if (!otherLines[0].isFirst) throw new Error('Cannot merge, `from` has no first line.') const merged = new Collection() let seenLast = false for (const line of this) { if (seenLast) throw new Error('Cannot merge line, the last line has already been seen.') if (!line.isLast) { merged.append(line) continue } seenLast = true for (const other of otherLines) { if (other.isFirst) { merged.append(line.mergeWithInfix(infix, other)) } else { merged.append(other) } } } return merged } withFirstPrefixed (prefix) { return new Collection() .append(Array.from(this, line => line.withFirstPrefixed(prefix))) } withLastPostfixed (postfix) { return new Collection() .append(Array.from(this, line => line.withLastPostfixed(postfix))) } stripFlags () { return new Collection() .append(Array.from(this, line => line.stripFlags())) } decompose () { const first = { actual: new Collection(), expected: new Collection() } const last = { actual: new Collection(), expected: new Collection() } const remaining = new Collection() for (const line of this) { if (line.isFirst && line.gutter === ACTUAL) { first.actual.append(line) } else if (line.isFirst && line.gutter === EXPECTED) { first.expected.append(line) } else if (line.isLast && line.gutter === ACTUAL) { last.actual.append(line) } else if (line.isLast && line.gutter === EXPECTED) { last.expected.append(line) } else { remaining.append(line) } } return { first, last, remaining } } } Object.defineProperty(Collection.prototype, 'isCollection', { value: true }) function setDefaultGutter (iterable, gutter) { return new Collection() .append(Array.from(iterable, line => { return line.gutter === null ? new Line(line.isFirst, line.isLast, gutter, line.stringValue) : line })) } module.exports = { buffer () { return new Collection() }, first (stringValue) { return new Line(true, false, null, stringValue) }, last (stringValue) { return new Line(false, true, null, stringValue) }, line (stringValue) { return new Line(false, false, null, stringValue) }, single (stringValue) { return new Line(true, true, null, stringValue) }, setDefaultGutter (lineOrCollection) { return lineOrCollection }, actual: { first (stringValue) { return new Line(true, false, ACTUAL, stringValue) }, last (stringValue) { return new Line(false, true, ACTUAL, stringValue) }, line (stringValue) { return new Line(false, false, ACTUAL, stringValue) }, single (stringValue) { return new Line(true, true, ACTUAL, stringValue) }, setDefaultGutter (lineOrCollection) { return setDefaultGutter(lineOrCollection, ACTUAL) }, }, expected: { first (stringValue) { return new Line(true, false, EXPECTED, stringValue) }, last (stringValue) { return new Line(false, true, EXPECTED, stringValue) }, line (stringValue) { return new Line(false, false, EXPECTED, stringValue) }, single (stringValue) { return new Line(true, true, EXPECTED, stringValue) }, setDefaultGutter (lineOrCollection) { return setDefaultGutter(lineOrCollection, EXPECTED) }, }, } concordance-5.0.4/lib/metaDescriptors/000077500000000000000000000000001402315264500177165ustar00rootroot00000000000000concordance-5.0.4/lib/metaDescriptors/item.js000066400000000000000000000172101402315264500212130ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const recursorUtils = require('../recursorUtils') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describeComplex (index, value) { return new ComplexItem(index, value) } exports.describeComplex = describeComplex function deserializeComplex (index, recursor) { const value = recursor() return new ComplexItem(index, value) } exports.deserializeComplex = deserializeComplex function describePrimitive (index, value) { return new PrimitiveItem(index, value) } exports.describePrimitive = describePrimitive function deserializePrimitive (state) { const index = state[0] const value = state[1] return new PrimitiveItem(index, value) } exports.deserializePrimitive = deserializePrimitive const complexTag = Symbol('ComplexItem') exports.complexTag = complexTag const primitiveTag = Symbol('PrimitiveItem') exports.primitiveTag = primitiveTag class ComplexItem { constructor (index, value) { this.index = index this.value = value } createRecursor () { return recursorUtils.singleValue(this.value) } compare (expected) { return expected.tag === complexTag && this.index === expected.index ? this.value.compare(expected.value) : UNEQUAL } formatShallow (theme, indent) { const increaseValueIndent = theme.item.increaseValueIndent === true return new formatUtils.SingleValueFormatter(theme, value => { if (typeof theme.item.customFormat === 'function') { return theme.item.customFormat(theme, indent, value) } return value.withLastPostfixed(theme.item.after) }, increaseValueIndent) } prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape, isCircular) { // Circular values cannot be compared. They must be treated as being unequal when diffing. if (isCircular(this.value) || isCircular(expected.value)) return { compareResult: UNEQUAL } // Try to line up this or remaining items with the expected items. const lhsFork = recursorUtils.fork(lhsRecursor) const rhsFork = recursorUtils.fork(rhsRecursor) const initialExpected = expected let expectedIsMissing = false while (!expectedIsMissing && expected !== null && expected.isItem === true) { if (expected.tag === complexTag) { expectedIsMissing = compareComplexShape(this.value, expected.value) !== UNEQUAL } expected = rhsFork.shared() } let actualIsExtraneous = false if (initialExpected.tag === complexTag) { let actual = this while (!actualIsExtraneous && actual !== null && actual.isItem === true) { if (actual.tag === complexTag) { actualIsExtraneous = compareComplexShape(actual.value, initialExpected.value) !== UNEQUAL } actual = lhsFork.shared() } } else if (initialExpected.tag === primitiveTag) { let actual = this while (!actualIsExtraneous && actual !== null && actual.isItem === true) { if (actual.tag === primitiveTag) { actualIsExtraneous = initialExpected.value.compare(actual.value) === DEEP_EQUAL } actual = lhsFork.shared() } } if (actualIsExtraneous && !expectedIsMissing) { return { actualIsExtraneous: true, lhsRecursor: lhsFork.recursor, rhsRecursor: recursorUtils.map( recursorUtils.unshift(rhsFork.recursor, initialExpected), next => { if (next.isItem !== true) return next next.index++ return next }), } } if (expectedIsMissing && !actualIsExtraneous) { return { expectedIsMissing: true, lhsRecursor: recursorUtils.map( recursorUtils.unshift(lhsFork.recursor, this), next => { if (next.isItem !== true) return next next.index++ return next }), rhsRecursor: rhsFork.recursor, } } const mustRecurse = this.tag === complexTag && initialExpected.tag === complexTag && this.value.compare(initialExpected.value) !== UNEQUAL return { mustRecurse, isUnequal: !mustRecurse, lhsRecursor: lhsFork.recursor, rhsRecursor: rhsFork.recursor, } } serialize () { return this.index } } Object.defineProperty(ComplexItem.prototype, 'isItem', { value: true }) Object.defineProperty(ComplexItem.prototype, 'tag', { value: complexTag }) class PrimitiveItem { constructor (index, value) { this.index = index this.value = value } compare (expected) { return expected.tag === primitiveTag && this.index === expected.index ? this.value.compare(expected.value) : UNEQUAL } formatDeep (theme, indent) { const increaseValueIndent = theme.item.increaseValueIndent === true const valueIndent = increaseValueIndent ? indent.increase() : indent // Since the value is formatted directly, modifiers are not applied. Apply // modifiers to the item descriptor instead. const formatted = this.value.formatDeep(theme, valueIndent) if (typeof theme.item.customFormat === 'function') { return theme.item.customFormat(theme, indent, formatted) } return formatted.withLastPostfixed(theme.item.after) } prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape, isCircular) { const compareResult = this.compare(expected) // Short-circuit when values are deeply equal. if (compareResult === DEEP_EQUAL) return { compareResult } // Short-circut when values can be diffed directly. if ( expected.tag === primitiveTag && this.value.tag === expected.value.tag && typeof this.value.diffDeep === 'function' ) { return { compareResult } } // Try to line up this or remaining items with the expected items. const rhsFork = recursorUtils.fork(rhsRecursor) const initialExpected = expected do { if (expected === null || expected.isItem !== true) { return { actualIsExtraneous: true, rhsRecursor: recursorUtils.map( recursorUtils.unshift(rhsFork.recursor, initialExpected), next => { if (next.isItem !== true) return next next.index++ return next }), } } if (this.value.compare(expected.value) === DEEP_EQUAL) { return { expectedIsMissing: true, lhsRecursor: recursorUtils.map( recursorUtils.unshift(lhsRecursor, this), next => { if (next.isItem !== true) return next next.index++ return next }), rhsRecursor: rhsFork.recursor, } } expected = rhsFork.shared() } while (true) } diffDeep (expected, theme, indent, invert) { // Verify a diff can be returned. if (this.tag !== expected.tag || typeof this.value.diffDeep !== 'function') return null const increaseValueIndent = theme.property.increaseValueIndent === true const valueIndent = increaseValueIndent ? indent.increase() : indent // Since the value is diffed directly, modifiers are not applied. Apply // modifiers to the item descriptor instead. const diff = this.value.diffDeep(expected.value, theme, valueIndent, invert) if (diff === null) return null if (typeof theme.item.customFormat === 'function') { return theme.item.customFormat(theme, indent, diff) } return diff.withLastPostfixed(theme.item.after) } serialize () { return [this.index, this.value] } } Object.defineProperty(PrimitiveItem.prototype, 'isItem', { value: true }) Object.defineProperty(PrimitiveItem.prototype, 'tag', { value: primitiveTag }) concordance-5.0.4/lib/metaDescriptors/mapEntry.js000066400000000000000000000162111402315264500220540ustar00rootroot00000000000000'use strict' const constants = require('../constants') const lineBuilder = require('../lineBuilder') const recursorUtils = require('../recursorUtils') const themeUtils = require('../themeUtils') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL const SHALLOW_EQUAL = constants.SHALLOW_EQUAL function describe (keyDescriptor, valueDescriptor) { const keyIsPrimitive = keyDescriptor.isPrimitive === true const valueIsPrimitive = valueDescriptor.isPrimitive === true return new MapEntry(keyDescriptor, valueDescriptor, keyIsPrimitive, valueIsPrimitive) } exports.describe = describe function deserialize (state, recursor) { const keyIsPrimitive = state[0] const valueIsPrimitive = state[1] const keyDescriptor = recursor() const valueDescriptor = recursor() return new MapEntry(keyDescriptor, valueDescriptor, keyIsPrimitive, valueIsPrimitive) } exports.deserialize = deserialize const tag = Symbol('MapEntry') exports.tag = tag function mergeWithKey (theme, key, values) { const lines = lineBuilder.buffer() const keyRemainder = lineBuilder.buffer() for (const line of key) { if (!line.isLast && !line.hasGutter) { lines.append(line) } else { keyRemainder.append(line) } } for (const value of values) { lines.append(keyRemainder.mergeWithInfix(theme.mapEntry.separator, value).withLastPostfixed(theme.mapEntry.after)) } return lines } class MapEntry { constructor (key, value, keyIsPrimitive, valueIsPrimitive) { this.key = key this.value = value this.keyIsPrimitive = keyIsPrimitive this.valueIsPrimitive = valueIsPrimitive } createRecursor () { let emitKey = true let emitValue = true return () => { if (emitKey) { emitKey = false return this.key } if (emitValue) { emitValue = false return this.value } return null } } compare (expected) { if (this.tag !== expected.tag) return UNEQUAL if (this.keyIsPrimitive !== expected.keyIsPrimitive) return UNEQUAL if (this.valueIsPrimitive !== expected.valueIsPrimitive) return UNEQUAL if (!this.keyIsPrimitive) return SHALLOW_EQUAL const keyResult = this.key.compare(expected.key) if (keyResult !== DEEP_EQUAL) return keyResult if (!this.valueIsPrimitive) return SHALLOW_EQUAL return this.value.compare(expected.value) } formatDeep (theme, indent) { // Verify the map entry can be formatted directly. if (!this.keyIsPrimitive || typeof this.value.formatDeep !== 'function') return null // Since formatShallow() would result in theme modifiers being applied // before the key and value are formatted, do the same here. const value = this.value.formatDeep(themeUtils.applyModifiersToOriginal(this.value, theme), indent) if (value === null) return null const key = this.key.formatDeep(themeUtils.applyModifiersToOriginal(this.key, theme), indent) return mergeWithKey(theme, key, [value]) } formatShallow (theme, indent) { let key = null const values = [] return { append: (formatted, origin) => { if (this.key === origin) { key = formatted } else { values.push(formatted) } }, finalize () { return mergeWithKey(theme, key, values) }, } } diffDeep (expected, theme, indent, invert) { // Verify a diff can be returned. if (this.tag !== expected.tag || typeof this.value.diffDeep !== 'function') return null // Only use this logic to format value diffs when the keys are primitive and equal. if (!this.keyIsPrimitive || !expected.keyIsPrimitive || this.key.compare(expected.key) !== DEEP_EQUAL) { return null } // Since formatShallow() would result in theme modifiers being applied // before the key and value are formatted, do the same here. const diff = this.value.diffDeep(expected.value, themeUtils.applyModifiersToOriginal(this.value, theme), indent, invert) if (diff === null) return null const key = this.key.formatDeep(themeUtils.applyModifiersToOriginal(this.key, theme), indent, '') return mergeWithKey(theme, key, [diff]) } prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape, isCircular) { // Circular values cannot be compared. They must be treated as being unequal when diffing. if (isCircular(this.value) || isCircular(expected.value)) return { compareResult: UNEQUAL } const compareResult = this.compare(expected) const keysAreEqual = this.tag === expected.tag && this.key.compare(expected.key) === DEEP_EQUAL // Short-circuit when keys and/or values are deeply equal. if (compareResult === DEEP_EQUAL || keysAreEqual) return { compareResult } // Try to line up this or remaining map entries with the expected entries. const lhsFork = recursorUtils.fork(lhsRecursor) const rhsFork = recursorUtils.fork(rhsRecursor) const initialExpected = expected let expectedIsMissing = false while (!expectedIsMissing && expected !== null && this.tag === expected.tag) { if (expected.keyIsPrimitive) { expectedIsMissing = this.key.compare(expected.key) !== UNEQUAL } else { expectedIsMissing = compareComplexShape(this.key, expected.key) !== UNEQUAL } expected = rhsFork.shared() } let actualIsExtraneous = false if (this.tag === initialExpected.tag) { if (initialExpected.keyIsPrimitive) { let actual = this while (!actualIsExtraneous && actual !== null && this.tag === actual.tag) { if (actual.keyIsPrimitive) { actualIsExtraneous = initialExpected.key.compare(actual.key) === DEEP_EQUAL } actual = lhsFork.shared() } } else { let actual = this while (!actualIsExtraneous && actual !== null && this.tag === actual.tag) { if (!actual.keyIsPrimitive) { actualIsExtraneous = compareComplexShape(actual.key, initialExpected.key) !== UNEQUAL } actual = lhsFork.shared() } } } if (actualIsExtraneous && !expectedIsMissing) { return { actualIsExtraneous: true, lhsRecursor: lhsFork.recursor, rhsRecursor: recursorUtils.unshift(rhsFork.recursor, initialExpected), } } if (expectedIsMissing && !actualIsExtraneous) { return { expectedIsMissing: true, lhsRecursor: recursorUtils.unshift(lhsFork.recursor, this), rhsRecursor: rhsFork.recursor, } } let mustRecurse = false if (!this.keyIsPrimitive && !initialExpected.keyIsPrimitive) { if (this.valueIsPrimitive || initialExpected.valueIsPrimitive) { mustRecurse = this.value.compare(initialExpected.value) !== UNEQUAL } else { mustRecurse = compareComplexShape(this.value, initialExpected.value) !== UNEQUAL } } return { mustRecurse, isUnequal: !mustRecurse, lhsRecursor: lhsFork.recursor, rhsRecursor: rhsFork.recursor, } } serialize () { return [this.keyIsPrimitive, this.valueIsPrimitive] } } Object.defineProperty(MapEntry.prototype, 'isMapEntry', { value: true }) Object.defineProperty(MapEntry.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/metaDescriptors/pointer.js000066400000000000000000000011511402315264500217320ustar00rootroot00000000000000'use strict' const UNEQUAL = require('../constants').UNEQUAL function describe (index) { return new Pointer(index) } exports.describe = describe exports.deserialize = describe const tag = Symbol('Pointer') exports.tag = tag class Pointer { constructor (index) { this.index = index } // Pointers cannot be compared, and are not expected to be part of the // comparisons. compare (expected) { return UNEQUAL } serialize () { return this.index } } Object.defineProperty(Pointer.prototype, 'isPointer', { value: true }) Object.defineProperty(Pointer.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/metaDescriptors/property.js000066400000000000000000000140251402315264500221420ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const symbolPrimitive = require('../primitiveValues/symbol').tag const recursorUtils = require('../recursorUtils') const AMBIGUOUS = constants.AMBIGUOUS const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describeComplex (key, value) { return new ComplexProperty(key, value) } exports.describeComplex = describeComplex function deserializeComplex (key, recursor) { const value = recursor() return new ComplexProperty(key, value) } exports.deserializeComplex = deserializeComplex function describePrimitive (key, value) { return new PrimitiveProperty(key, value) } exports.describePrimitive = describePrimitive function deserializePrimitive (state) { const key = state[0] const value = state[1] return new PrimitiveProperty(key, value) } exports.deserializePrimitive = deserializePrimitive const complexTag = Symbol('ComplexProperty') exports.complexTag = complexTag const primitiveTag = Symbol('PrimitiveProperty') exports.primitiveTag = primitiveTag class Property { constructor (key) { this.key = key } compareKeys (expected) { const result = this.key.compare(expected.key) // Return AMBIGUOUS if symbol keys are unequal. It's likely that properties // are compared in order of declaration, which is not the desired strategy. // Returning AMBIGUOUS allows compare() and diff() to recognize this // situation and sort the symbol properties before comparing them. return result === UNEQUAL && this.key.tag === symbolPrimitive && expected.key.tag === symbolPrimitive ? AMBIGUOUS : result } prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape, isCircular) { // Circular values cannot be compared. They must be treated as being unequal when diffing. if (isCircular(this.value) || isCircular(expected.value)) return { compareResult: UNEQUAL } // Try to line up this or remaining properties with the expected properties. const rhsFork = recursorUtils.fork(rhsRecursor) const initialExpected = expected do { if (expected === null || expected.isProperty !== true) { return { actualIsExtraneous: true, rhsRecursor: recursorUtils.unshift(rhsFork.recursor, initialExpected), } } else if (this.key.compare(expected.key) === DEEP_EQUAL) { if (expected === initialExpected) { return null } else { return { expectedIsMissing: true, lhsRecursor: recursorUtils.unshift(lhsRecursor, this), rhsRecursor: rhsFork.recursor, } } } expected = rhsFork.shared() } while (true) } } Object.defineProperty(Property.prototype, 'isProperty', { value: true }) class ComplexProperty extends Property { constructor (key, value) { super(key) this.value = value } createRecursor () { return recursorUtils.singleValue(this.value) } compare (expected) { if (expected.isProperty !== true) return UNEQUAL const keyResult = this.compareKeys(expected) if (keyResult !== DEEP_EQUAL) return keyResult return this.tag === expected.tag ? this.value.compare(expected.value) : UNEQUAL } formatShallow (theme, indent) { const increaseValueIndent = theme.property.increaseValueIndent === true return new formatUtils.SingleValueFormatter(theme, value => { if (typeof theme.property.customFormat === 'function') { return theme.property.customFormat(theme, indent, this.key, value) } return value .withFirstPrefixed(this.key.formatAsKey(theme) + theme.property.separator) .withLastPostfixed(theme.property.after) }, increaseValueIndent) } serialize () { return this.key } } Object.defineProperty(ComplexProperty.prototype, 'tag', { value: complexTag }) class PrimitiveProperty extends Property { constructor (key, value) { super(key) this.value = value } compare (expected) { if (expected.isProperty !== true) return UNEQUAL const keyResult = this.compareKeys(expected) if (keyResult !== DEEP_EQUAL) return keyResult return this.tag !== expected.tag ? UNEQUAL : this.value.compare(expected.value) } formatDeep (theme, indent) { const increaseValueIndent = theme.property.increaseValueIndent === true const valueIndent = increaseValueIndent ? indent.increase() : indent // Since the key and value are formatted directly, modifiers are not // applied. Apply modifiers to the property descriptor instead. const formatted = this.value.formatDeep(theme, valueIndent) if (typeof theme.property.customFormat === 'function') { return theme.property.customFormat(theme, indent, this.key, formatted) } return formatted .withFirstPrefixed(this.key.formatAsKey(theme) + theme.property.separator) .withLastPostfixed(theme.property.after) } diffDeep (expected, theme, indent, invert) { // Verify a diff can be returned. if (this.tag !== expected.tag || typeof this.value.diffDeep !== 'function') return null // Only use this logic to diff values when the keys are the same. if (this.key.compare(expected.key) !== DEEP_EQUAL) return null const increaseValueIndent = theme.property.increaseValueIndent === true const valueIndent = increaseValueIndent ? indent.increase() : indent // Since the key and value are diffed directly, modifiers are not // applied. Apply modifiers to the property descriptor instead. const diff = this.value.diffDeep(expected.value, theme, valueIndent, invert) if (diff === null) return null if (typeof theme.property.customFormat === 'function') { return theme.property.customFormat(theme, indent, this.key, diff) } return diff .withFirstPrefixed(this.key.formatAsKey(theme) + theme.property.separator) .withLastPostfixed(theme.property.after) } serialize () { return [this.key, this.value] } } Object.defineProperty(PrimitiveProperty.prototype, 'tag', { value: primitiveTag }) concordance-5.0.4/lib/metaDescriptors/stats.js000066400000000000000000000073741402315264500214250ustar00rootroot00000000000000'use strict' const constants = require('../constants') const lineBuilder = require('../lineBuilder') const recursorUtils = require('../recursorUtils') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describeIterableRecursor (recursor) { return new IterableStats(recursor.size) } exports.describeIterableRecursor = describeIterableRecursor function describeListRecursor (recursor) { return new ListStats(recursor.size) } exports.describeListRecursor = describeListRecursor function describePropertyRecursor (recursor) { return new PropertyStats(recursor.size) } exports.describePropertyRecursor = describePropertyRecursor function deserializeIterableStats (size) { return new IterableStats(size) } exports.deserializeIterableStats = deserializeIterableStats function deserializeListStats (size) { return new ListStats(size) } exports.deserializeListStats = deserializeListStats function deserializePropertyStats (size) { return new PropertyStats(size) } exports.deserializePropertyStats = deserializePropertyStats const iterableTag = Symbol('IterableStats') exports.iterableTag = iterableTag const listTag = Symbol('ListStats') exports.listTag = listTag const propertyTag = Symbol('PropertyStats') exports.propertyTag = propertyTag class Stats { constructor (size) { this.size = size } formatDeep (theme) { return lineBuilder.single(theme.stats.separator) } prepareDiff (expected, lhsRecursor, rhsRecursor, compareComplexShape) { if (expected.isStats !== true || expected.tag === this.tag) return null // Try to line up stats descriptors with the same tag. const rhsFork = recursorUtils.fork(rhsRecursor) const initialExpected = expected const missing = [] while (expected !== null && this.tag !== expected.tag) { missing.push(expected) expected = rhsFork.shared() } if (expected !== null && missing.length > 0) { return { multipleAreMissing: true, descriptors: missing, lhsRecursor: recursorUtils.unshift(lhsRecursor, this), // Use original `rhsRecursor`, not `rhsFork`, since the consumed // descriptors are returned with the `missing` array. rhsRecursor: recursorUtils.unshift(rhsRecursor, expected), } } const lhsFork = recursorUtils.fork(lhsRecursor) let actual = this const extraneous = [] while (actual !== null && actual.tag !== initialExpected.tag) { extraneous.push(actual) actual = lhsFork.shared() } if (actual !== null && extraneous.length > 0) { return { multipleAreExtraneous: true, descriptors: extraneous, // Use original `lhsRecursor`, not `lhsFork`, since the consumed // descriptors are returned with the `extraneous` array. lhsRecursor: recursorUtils.unshift(lhsRecursor, actual), rhsRecursor: recursorUtils.unshift(rhsFork.recursor, initialExpected), } } return null } serialize () { return this.size } } Object.defineProperty(Stats.prototype, 'isStats', { value: true }) class IterableStats extends Stats { compare (expected) { return expected.tag === iterableTag && this.size === expected.size ? DEEP_EQUAL : UNEQUAL } } Object.defineProperty(IterableStats.prototype, 'tag', { value: iterableTag }) class ListStats extends Stats { compare (expected) { return expected.tag === listTag && this.size === expected.size ? DEEP_EQUAL : UNEQUAL } } Object.defineProperty(ListStats.prototype, 'tag', { value: listTag }) class PropertyStats extends Stats { compare (expected) { return expected.tag === propertyTag && this.size === expected.size ? DEEP_EQUAL : UNEQUAL } } Object.defineProperty(PropertyStats.prototype, 'tag', { value: propertyTag }) concordance-5.0.4/lib/pluginRegistry.js000066400000000000000000000137731402315264500201460ustar00rootroot00000000000000'use strict' const semver = require('semver') const pkg = require('../package.json') const object = require('./complexValues/object') const constants = require('./constants') const formatUtils = require('./formatUtils') const lineBuilder = require('./lineBuilder') const itemDescriptor = require('./metaDescriptors/item') const propertyDescriptor = require('./metaDescriptors/property') const stringDescriptor = require('./primitiveValues/string') const recursorUtils = require('./recursorUtils') const themeUtils = require('./themeUtils') const API_VERSION = 1 const CONCORDANCE_VERSION = pkg.version const descriptorRegistry = new Map() const registry = new Map() class PluginError extends Error { constructor (message, plugin) { super(message) this.name = 'PluginError' this.plugin = plugin } } class PluginTypeError extends TypeError { constructor (message, plugin) { super(message) this.name = 'PluginTypeError' this.plugin = plugin } } class UnsupportedApiError extends PluginError { constructor (plugin) { super('Plugin requires an unsupported API version', plugin) this.name = 'UnsupportedApiError' } } class UnsupportedError extends PluginError { constructor (plugin) { super('Plugin does not support this version of Concordance', plugin) this.name = 'UnsupportedError' } } class DuplicateDescriptorTagError extends PluginError { constructor (tag, plugin) { super(`Could not add descriptor: tag ${String(tag)} has already been registered`, plugin) this.name = 'DuplicateDescriptorTagError' this.tag = tag } } class DuplicateDescriptorIdError extends PluginError { constructor (id, plugin) { const printed = typeof id === 'number' ? `0x${id.toString(16).toUpperCase()}` : String(id) super(`Could not add descriptor: id ${printed} has already been registered`, plugin) this.name = 'DuplicateDescriptorIdError' this.id = id } } function verify (plugin) { if (typeof plugin.name !== 'string' || !plugin.name) { throw new PluginTypeError('Plugin must have a `name`', plugin) } if (plugin.apiVersion !== API_VERSION) { throw new UnsupportedApiError(plugin) } if ('minimalConcordanceVersion' in plugin) { if (!semver.valid(plugin.minimalConcordanceVersion)) { throw new PluginTypeError('If specified, `minimalConcordanceVersion` must be a valid SemVer version', plugin) } const range = `>=${plugin.minimalConcordanceVersion}` if (!semver.satisfies(CONCORDANCE_VERSION, range)) { throw new UnsupportedError(plugin) } } } // Selectively expose descriptor tags. const publicDescriptorTags = Object.freeze({ complexItem: itemDescriptor.complexTag, primitiveItem: itemDescriptor.primitiveTag, primitiveProperty: propertyDescriptor.primitiveTag, string: stringDescriptor.tag, }) // Don't expose `setDefaultGutter()`. const publicLineBuilder = Object.freeze({ buffer: lineBuilder.buffer, first: lineBuilder.first, last: lineBuilder.last, line: lineBuilder.line, single: lineBuilder.single, actual: Object.freeze({ buffer: lineBuilder.actual.buffer, first: lineBuilder.actual.first, last: lineBuilder.actual.last, line: lineBuilder.actual.line, single: lineBuilder.actual.single, }), expected: Object.freeze({ buffer: lineBuilder.expected.buffer, first: lineBuilder.expected.first, last: lineBuilder.expected.last, line: lineBuilder.expected.line, single: lineBuilder.expected.single, }), }) function modifyTheme (descriptor, modifier) { themeUtils.addModifier(descriptor, modifier) return descriptor } function add (plugin) { verify(plugin) const name = plugin.name if (registry.has(name)) return registry.get(name) const id2deserialize = new Map() const tag2id = new Map() const addDescriptor = (id, tag, deserialize) => { if (id2deserialize.has(id)) throw new DuplicateDescriptorIdError(id, plugin) if (descriptorRegistry.has(tag) || tag2id.has(tag)) throw new DuplicateDescriptorTagError(tag, plugin) id2deserialize.set(id, deserialize) tag2id.set(tag, id) } const tryDescribeValue = plugin.register({ // Concordance makes assumptions about when AMBIGUOUS occurs. Do not expose // it to plugins. UNEQUAL: constants.UNEQUAL, SHALLOW_EQUAL: constants.SHALLOW_EQUAL, DEEP_EQUAL: constants.DEEP_EQUAL, ObjectValue: object.ObjectValue, DescribedMixin: object.DescribedMixin, DeserializedMixin: object.DeserializedMixin, addDescriptor, applyThemeModifiers: themeUtils.applyModifiers, descriptorTags: publicDescriptorTags, lineBuilder: publicLineBuilder, mapRecursor: recursorUtils.map, modifyTheme, wrapFromTheme: formatUtils.wrap, }) const registered = { id2deserialize, serializerVersion: plugin.serializerVersion, name, tag2id, theme: plugin.theme || {}, tryDescribeValue, } registry.set(name, registered) for (const tag of tag2id.keys()) { descriptorRegistry.set(tag, registered) } return registered } exports.add = add function getDeserializers (plugins) { return plugins.map(plugin => { const registered = add(plugin) return { id2deserialize: registered.id2deserialize, name: registered.name, serializerVersion: registered.serializerVersion, } }) } exports.getDeserializers = getDeserializers function getThemes (plugins) { return plugins.map(plugin => { const registered = add(plugin) return { name: registered.name, theme: registered.theme, } }) } exports.getThemes = getThemes function getTryDescribeValues (plugins) { return plugins.map(plugin => add(plugin).tryDescribeValue) } exports.getTryDescribeValues = getTryDescribeValues function resolveDescriptorRef (tag) { if (!descriptorRegistry.has(tag)) return null const registered = descriptorRegistry.get(tag) return { id: registered.tag2id.get(tag), name: registered.name, serialization: { serializerVersion: registered.serializerVersion, }, } } exports.resolveDescriptorRef = resolveDescriptorRef concordance-5.0.4/lib/primitiveValues/000077500000000000000000000000001402315264500177365ustar00rootroot00000000000000concordance-5.0.4/lib/primitiveValues/bigInt.js000066400000000000000000000016121402315264500215100ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe (value) { return new BigIntValue(value) } exports.describe = describe exports.deserialize = describe const tag = Symbol('BigIntValue') exports.tag = tag class BigIntValue { constructor (value) { this.value = value } compare (expected) { return expected.tag === tag && Object.is(this.value, expected.value) ? DEEP_EQUAL : UNEQUAL } formatDeep (theme) { return lineBuilder.single(formatUtils.wrap(theme.bigInt, `${this.value}n`)) } serialize () { return this.value } } Object.defineProperty(BigIntValue.prototype, 'isPrimitive', { value: true }) Object.defineProperty(BigIntValue.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/primitiveValues/boolean.js000066400000000000000000000016431402315264500217170ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe (value) { return new BooleanValue(value) } exports.describe = describe exports.deserialize = describe const tag = Symbol('BooleanValue') exports.tag = tag class BooleanValue { constructor (value) { this.value = value } compare (expected) { return this.tag === expected.tag && this.value === expected.value ? DEEP_EQUAL : UNEQUAL } formatDeep (theme) { return lineBuilder.single(formatUtils.wrap(theme.boolean, this.value === true ? 'true' : 'false')) } serialize () { return this.value } } Object.defineProperty(BooleanValue.prototype, 'isPrimitive', { value: true }) Object.defineProperty(BooleanValue.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/primitiveValues/null.js000066400000000000000000000013411402315264500212450ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe () { return new NullValue() } exports.describe = describe exports.deserialize = describe const tag = Symbol('NullValue') exports.tag = tag class NullValue { compare (expected) { return expected.tag === tag ? DEEP_EQUAL : UNEQUAL } formatDeep (theme) { return lineBuilder.single(formatUtils.wrap(theme.null, 'null')) } } Object.defineProperty(NullValue.prototype, 'isPrimitive', { value: true }) Object.defineProperty(NullValue.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/primitiveValues/number.js000066400000000000000000000017111402315264500215640ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe (value) { return new NumberValue(value) } exports.describe = describe exports.deserialize = describe const tag = Symbol('NumberValue') exports.tag = tag class NumberValue { constructor (value) { this.value = value } compare (expected) { return expected.tag === tag && Object.is(this.value, expected.value) ? DEEP_EQUAL : UNEQUAL } formatDeep (theme) { const string = Object.is(this.value, -0) ? '-0' : String(this.value) return lineBuilder.single(formatUtils.wrap(theme.number, string)) } serialize () { return this.value } } Object.defineProperty(NumberValue.prototype, 'isPrimitive', { value: true }) Object.defineProperty(NumberValue.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/primitiveValues/string.js000066400000000000000000000262461402315264500216140ustar00rootroot00000000000000'use strict' const keyword = require('esutils').keyword const fastDiff = require('fast-diff') const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe (value) { return new StringValue(value) } exports.describe = describe exports.deserialize = describe const tag = Symbol('StringValue') exports.tag = tag // TODO: Escape invisible characters (e.g. zero-width joiner, non-breaking space), // ambiguous characters (other kinds of spaces, combining characters). Use // http://graphemica.com/blocks/control-pictures where applicable. function basicEscape (string) { return string.replace(/\\/g, '\\\\') } const CRLF_CONTROL_PICTURE = '\u240D\u240A' const LF_CONTROL_PICTURE = '\u240A' const CR_CONTROL_PICTURE = '\u240D' const MATCH_CONTROL_PICTURES = new RegExp(`${CR_CONTROL_PICTURE}|${LF_CONTROL_PICTURE}|${CR_CONTROL_PICTURE}`, 'g') function escapeLinebreak (string) { if (string === '\r\n') return CRLF_CONTROL_PICTURE if (string === '\n') return LF_CONTROL_PICTURE if (string === '\r') return CR_CONTROL_PICTURE return string } function themeControlPictures (theme, resetWrap, str) { return str.replace(MATCH_CONTROL_PICTURES, picture => { return resetWrap.close + formatUtils.wrap(theme.string.controlPicture, picture) + resetWrap.open }) } const MATCH_SINGLE_QUOTE = /'/g const MATCH_DOUBLE_QUOTE = /"/g const MATCH_BACKTICKS = /`/g function escapeQuotes (line, string) { const quote = line.escapeQuote if (quote === '\'') return string.replace(MATCH_SINGLE_QUOTE, "\\'") if (quote === '"') return string.replace(MATCH_DOUBLE_QUOTE, '\\"') if (quote === '`') return string.replace(MATCH_BACKTICKS, '\\`') return string } function includesLinebreaks (string) { return string.includes('\r') || string.includes('\n') } function diffLine (theme, actual, expected, invert) { const outcome = fastDiff(actual, expected) // TODO: Compute when line is mostly unequal (80%? 90%?) and treat it as being // completely unequal. const isPartiallyEqual = !( (outcome.length === 2 && outcome[0][1] === actual && outcome[1][1] === expected) || // Discount line ending control pictures, which will be equal even when the // rest of the line isn't. ( outcome.length === 3 && outcome[2][0] === fastDiff.EQUAL && MATCH_CONTROL_PICTURES.test(outcome[2][1]) && outcome[0][1] + outcome[2][1] === actual && outcome[1][1] + outcome[2][1] === expected ) ) let stringActual = '' let stringExpected = '' const noopWrap = { open: '', close: '' } let deleteWrap = isPartiallyEqual ? theme.string.diff.delete : noopWrap let insertWrap = isPartiallyEqual ? theme.string.diff.insert : noopWrap const equalWrap = isPartiallyEqual ? theme.string.diff.equal : noopWrap if (invert) { [deleteWrap, insertWrap] = [insertWrap, deleteWrap] } for (const diff of outcome) { if (diff[0] === fastDiff.DELETE) { stringActual += formatUtils.wrap(deleteWrap, diff[1]) } else if (diff[0] === fastDiff.INSERT) { stringExpected += formatUtils.wrap(insertWrap, diff[1]) } else { const string = formatUtils.wrap(equalWrap, themeControlPictures(theme, equalWrap, diff[1])) stringActual += string stringExpected += string } } if (!isPartiallyEqual) { const deleteLineWrap = invert ? theme.string.diff.insertLine : theme.string.diff.deleteLine const insertLineWrap = invert ? theme.string.diff.deleteLine : theme.string.diff.insertLine stringActual = formatUtils.wrap(deleteLineWrap, stringActual) stringExpected = formatUtils.wrap(insertLineWrap, stringExpected) } return [stringActual, stringExpected] } const LINEBREAKS = /\r\n|\r|\n/g function gatherLines (string) { const lines = [] let prevIndex = 0 for (let match; (match = LINEBREAKS.exec(string)); prevIndex = match.index + match[0].length) { lines.push(string.slice(prevIndex, match.index) + escapeLinebreak(match[0])) } lines.push(string.slice(prevIndex)) return lines } class StringValue { constructor (value) { this.value = value } compare (expected) { return expected.tag === tag && this.value === expected.value ? DEEP_EQUAL : UNEQUAL } get includesLinebreaks () { return includesLinebreaks(this.value) } formatDeep (theme, indent) { // Escape backslashes let escaped = basicEscape(this.value) if (!this.includesLinebreaks) { escaped = escapeQuotes(theme.string.line, escaped) return lineBuilder.single(formatUtils.wrap(theme.string.line, formatUtils.wrap(theme.string, escaped))) } escaped = escapeQuotes(theme.string.multiline, escaped) const lineStrings = gatherLines(escaped).map(string => { return formatUtils.wrap(theme.string, themeControlPictures(theme, theme.string, string)) }) const lastIndex = lineStrings.length - 1 const indentation = indent return lineBuilder.buffer() .append( lineStrings.map((string, index) => { if (index === 0) return lineBuilder.first(theme.string.multiline.start + string) if (index === lastIndex) return lineBuilder.last(indentation + string + theme.string.multiline.end) return lineBuilder.line(indentation + string) })) } formatAsKey (theme) { const key = this.value if (keyword.isIdentifierNameES6(key, true) || String(parseInt(key, 10)) === key) { return key } const escaped = basicEscape(key) .replace(/\n/g, '\\n') .replace(/\r/g, '\\r') .replace(/'/g, "\\'") return formatUtils.wrap(theme.string.line, formatUtils.wrap(theme.string, escaped)) } diffDeep (expected, theme, indent, invert) { if (expected.tag !== tag) return null const escapedActual = basicEscape(this.value) const escapedExpected = basicEscape(expected.value) if (!includesLinebreaks(escapedActual) && !includesLinebreaks(escapedExpected)) { const result = diffLine(theme, escapeQuotes(theme.string.line, escapedActual), escapeQuotes(theme.string.line, escapedExpected), invert, ) return lineBuilder.actual.single(formatUtils.wrap(theme.string.line, result[0])) .concat(lineBuilder.expected.single(formatUtils.wrap(theme.string.line, result[1]))) } const actualLines = gatherLines(escapeQuotes(theme.string.multiline, escapedActual)) const expectedLines = gatherLines(escapeQuotes(theme.string.multiline, escapedExpected)) const indentation = indent const lines = lineBuilder.buffer() const lastActualIndex = actualLines.length - 1 const lastExpectedIndex = expectedLines.length - 1 let actualBuffer = [] let expectedBuffer = [] let mustOpenNextExpected = false for (let actualIndex = 0, expectedIndex = 0, extraneousOffset = 0; actualIndex < actualLines.length;) { if (actualLines[actualIndex] === expectedLines[expectedIndex]) { lines.append(actualBuffer) lines.append(expectedBuffer) actualBuffer = [] expectedBuffer = [] let string = actualLines[actualIndex] string = themeControlPictures(theme, theme.string.diff.equal, string) string = formatUtils.wrap(theme.string.diff.equal, string) if (actualIndex === 0) { lines.append(lineBuilder.first(theme.string.multiline.start + string)) } else if (actualIndex === lastActualIndex && expectedIndex === lastExpectedIndex) { lines.append(lineBuilder.last(indentation + string + theme.string.multiline.end)) } else { lines.append(lineBuilder.line(indentation + string)) } actualIndex++ expectedIndex++ continue } let expectedIsMissing = false { const compare = actualLines[actualIndex] for (let index = expectedIndex; !expectedIsMissing && index < expectedLines.length; index++) { expectedIsMissing = compare === expectedLines[index] } } let actualIsExtraneous = (actualIndex - extraneousOffset) > lastExpectedIndex || expectedIndex > lastExpectedIndex if (!actualIsExtraneous) { const compare = expectedLines[expectedIndex] for (let index = actualIndex; !actualIsExtraneous && index < actualLines.length; index++) { actualIsExtraneous = compare === actualLines[index] } if (!actualIsExtraneous && (actualIndex - extraneousOffset) === lastExpectedIndex && actualIndex < lastActualIndex) { actualIsExtraneous = true } } if (actualIsExtraneous && !expectedIsMissing) { const wrap = invert ? theme.string.diff.insertLine : theme.string.diff.deleteLine const string = formatUtils.wrap(wrap, actualLines[actualIndex]) if (actualIndex === 0) { actualBuffer.push(lineBuilder.actual.first(theme.string.multiline.start + string)) mustOpenNextExpected = true } else if (actualIndex === lastActualIndex) { actualBuffer.push(lineBuilder.actual.last(indentation + string + theme.string.multiline.end)) } else { actualBuffer.push(lineBuilder.actual.line(indentation + string)) } actualIndex++ extraneousOffset++ } else if (expectedIsMissing && !actualIsExtraneous) { const wrap = invert ? theme.string.diff.deleteLine : theme.string.diff.insertLine const string = formatUtils.wrap(wrap, expectedLines[expectedIndex]) if (mustOpenNextExpected) { expectedBuffer.push(lineBuilder.expected.first(theme.string.multiline.start + string)) mustOpenNextExpected = false } else if (expectedIndex === lastExpectedIndex) { expectedBuffer.push(lineBuilder.expected.last(indentation + string + theme.string.multiline.end)) } else { expectedBuffer.push(lineBuilder.expected.line(indentation + string)) } expectedIndex++ } else { const result = diffLine(theme, actualLines[actualIndex], expectedLines[expectedIndex], invert) if (actualIndex === 0) { actualBuffer.push(lineBuilder.actual.first(theme.string.multiline.start + result[0])) mustOpenNextExpected = true } else if (actualIndex === lastActualIndex) { actualBuffer.push(lineBuilder.actual.last(indentation + result[0] + theme.string.multiline.end)) } else { actualBuffer.push(lineBuilder.actual.line(indentation + result[0])) } if (mustOpenNextExpected) { expectedBuffer.push(lineBuilder.expected.first(theme.string.multiline.start + result[1])) mustOpenNextExpected = false } else if (expectedIndex === lastExpectedIndex) { expectedBuffer.push(lineBuilder.expected.last(indentation + result[1] + theme.string.multiline.end)) } else { expectedBuffer.push(lineBuilder.expected.line(indentation + result[1])) } actualIndex++ expectedIndex++ } } lines.append(actualBuffer) lines.append(expectedBuffer) return lines } serialize () { return this.value } } Object.defineProperty(StringValue.prototype, 'isPrimitive', { value: true }) Object.defineProperty(StringValue.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/primitiveValues/symbol.js000066400000000000000000000053311402315264500216030ustar00rootroot00000000000000'use strict' const stringEscape = require('js-string-escape') const wellKnownSymbols = require('well-known-symbols') const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe (value) { let stringCompare = null const key = Symbol.keyFor(value) if (key !== undefined) { stringCompare = `Symbol.for(${stringEscape(key)})` } else if (wellKnownSymbols.isWellKnown(value)) { stringCompare = wellKnownSymbols.getLabel(value) } return new SymbolValue({ stringCompare, value, }) } exports.describe = describe function deserialize (state) { const stringCompare = state[0] const string = state[1] || state[0] return new DeserializedSymbolValue({ string, stringCompare, value: null, }) } exports.deserialize = deserialize const tag = Symbol('SymbolValue') exports.tag = tag class SymbolValue { constructor (props) { this.stringCompare = props.stringCompare this.value = props.value } compare (expected) { if (expected.tag !== tag) return UNEQUAL if (this.stringCompare !== null) { return this.stringCompare === expected.stringCompare ? DEEP_EQUAL : UNEQUAL } return this.value === expected.value ? DEEP_EQUAL : UNEQUAL } formatString () { if (this.stringCompare !== null) return this.stringCompare return stringEscape(this.value.toString()) } formatDeep (theme) { return lineBuilder.single(formatUtils.wrap(theme.symbol, this.formatString())) } formatAsKey (theme) { return formatUtils.wrap(theme.property.keyBracket, formatUtils.wrap(theme.symbol, this.formatString())) } serialize () { const string = this.formatString() return this.stringCompare === string ? [this.stringCompare] : [this.stringCompare, string] } } Object.defineProperty(SymbolValue.prototype, 'isPrimitive', { value: true }) Object.defineProperty(SymbolValue.prototype, 'tag', { value: tag }) class DeserializedSymbolValue extends SymbolValue { constructor (props) { super(props) this.string = props.string } compare (expected) { if (expected.tag !== tag) return UNEQUAL if (this.stringCompare !== null) { return this.stringCompare === expected.stringCompare ? DEEP_EQUAL : UNEQUAL } // Symbols that are not in the global symbol registry, and are not // well-known, cannot be compared when deserialized. Treat symbols // as equal if they are formatted the same. return this.string === expected.formatString() ? DEEP_EQUAL : UNEQUAL } formatString () { return this.string } } concordance-5.0.4/lib/primitiveValues/undefined.js000066400000000000000000000014041402315264500222340ustar00rootroot00000000000000'use strict' const constants = require('../constants') const formatUtils = require('../formatUtils') const lineBuilder = require('../lineBuilder') const DEEP_EQUAL = constants.DEEP_EQUAL const UNEQUAL = constants.UNEQUAL function describe () { return new UndefinedValue() } exports.describe = describe exports.deserialize = describe const tag = Symbol('UndefinedValue') exports.tag = tag class UndefinedValue { compare (expected) { return expected.tag === tag ? DEEP_EQUAL : UNEQUAL } formatDeep (theme) { return lineBuilder.single(formatUtils.wrap(theme.undefined, 'undefined')) } } Object.defineProperty(UndefinedValue.prototype, 'isPrimitive', { value: true }) Object.defineProperty(UndefinedValue.prototype, 'tag', { value: tag }) concordance-5.0.4/lib/recursorUtils.js000066400000000000000000000043131402315264500177720ustar00rootroot00000000000000'use strict' const NOOP_RECURSOR = { size: 0, next () { return null }, } exports.NOOP_RECURSOR = NOOP_RECURSOR function fork (recursor) { const buffer = [] return { shared () { const next = recursor() if (next !== null) buffer.push(next) return next }, recursor () { if (buffer.length > 0) return buffer.shift() return recursor() }, } } exports.fork = fork function consumeDeep (recursor) { const stack = [recursor] while (stack.length > 0) { const subject = stack[stack.length - 1]() if (subject === null) { stack.pop() continue } if (typeof subject.createRecursor === 'function') { stack.push(subject.createRecursor()) } } } exports.consumeDeep = consumeDeep function map (recursor, mapFn) { return () => { const next = recursor() if (next === null) return null return mapFn(next) } } exports.map = map function replay (state, create) { if (!state) { const recursor = create() if (recursor === NOOP_RECURSOR) { state = recursor } else { state = Object.assign({ buffer: [], done: false, }, recursor) } } if (state === NOOP_RECURSOR) return { state, recursor: state } let done = false let index = 0 const next = () => { if (done) return null let retval = state.buffer[index] if (retval === undefined) { retval = state.buffer[index] = state.next() } index++ if (retval === null) { done = true } return retval } return { state, recursor: { next, size: state.size } } } exports.replay = replay function sequence (first, second) { let fromFirst = true return () => { if (fromFirst) { const next = first() if (next !== null) return next fromFirst = false } return second() } } exports.sequence = sequence function singleValue (value) { let done = false return () => { if (done) return null done = true return value } } exports.singleValue = singleValue function unshift (recursor, value) { return () => { if (value !== null) { const next = value value = null return next } return recursor() } } exports.unshift = unshift concordance-5.0.4/lib/serialize.js000066400000000000000000000302061402315264500170740ustar00rootroot00000000000000'use strict' const md5hex = require('md5-hex') const argumentsValue = require('./complexValues/arguments') const arrayBufferValue = require('./complexValues/arrayBuffer') const boxedValue = require('./complexValues/boxed') const dataViewValue = require('./complexValues/dataView') const dateValue = require('./complexValues/date') const errorValue = require('./complexValues/error') const functionValue = require('./complexValues/function') const globalValue = require('./complexValues/global') const mapValue = require('./complexValues/map') const objectValue = require('./complexValues/object') const promiseValue = require('./complexValues/promise') const regexpValue = require('./complexValues/regexp') const setValue = require('./complexValues/set') const typedArrayValue = require('./complexValues/typedArray') const encoder = require('./encoder') const itemDescriptor = require('./metaDescriptors/item') const mapEntryDescriptor = require('./metaDescriptors/mapEntry') const pointerDescriptor = require('./metaDescriptors/pointer') const propertyDescriptor = require('./metaDescriptors/property') const statsDescriptors = require('./metaDescriptors/stats') const pluginRegistry = require('./pluginRegistry') const bigIntValue = require('./primitiveValues/bigInt') const booleanValue = require('./primitiveValues/boolean') const nullValue = require('./primitiveValues/null') const numberValue = require('./primitiveValues/number') const stringValue = require('./primitiveValues/string') const symbolValue = require('./primitiveValues/symbol') const undefinedValue = require('./primitiveValues/undefined') const recursorUtils = require('./recursorUtils') // Increment if encoding layout, descriptor IDs, or value types change. Previous // Concordance versions will not be able to decode buffers generated by a newer // version, so changing this value will require a major version bump of // Concordance itself. The version is encoded as an unsigned 16 bit integer. const VERSION = 3 // Adding or removing mappings or changing an index requires the version in // encoder.js to be bumped, which necessitates a major version bump of // Concordance itself. Indexes are hexadecimal to make reading the binary // output easier. const mappings = [ [0x01, bigIntValue.tag, bigIntValue.deserialize], [0x02, booleanValue.tag, booleanValue.deserialize], [0x03, nullValue.tag, nullValue.deserialize], [0x04, numberValue.tag, numberValue.deserialize], [0x05, stringValue.tag, stringValue.deserialize], [0x06, symbolValue.tag, symbolValue.deserialize], [0x07, undefinedValue.tag, undefinedValue.deserialize], [0x08, objectValue.tag, objectValue.deserialize], [0x09, statsDescriptors.iterableTag, statsDescriptors.deserializeIterableStats], [0x0A, statsDescriptors.listTag, statsDescriptors.deserializeListStats], [0x0B, itemDescriptor.complexTag, itemDescriptor.deserializeComplex], [0x0C, itemDescriptor.primitiveTag, itemDescriptor.deserializePrimitive], [0x0D, statsDescriptors.propertyTag, statsDescriptors.deserializePropertyStats], [0x0E, propertyDescriptor.complexTag, propertyDescriptor.deserializeComplex], [0x0F, propertyDescriptor.primitiveTag, propertyDescriptor.deserializePrimitive], [0x10, pointerDescriptor.tag, pointerDescriptor.deserialize], [0x11, mapValue.tag, mapValue.deserialize], [0x12, mapEntryDescriptor.tag, mapEntryDescriptor.deserialize], [0x13, argumentsValue.tag, argumentsValue.deserialize], [0x14, arrayBufferValue.tag, arrayBufferValue.deserialize], [0x15, boxedValue.tag, boxedValue.deserialize], [0x16, dataViewValue.tag, dataViewValue.deserialize], [0x17, dateValue.tag, dateValue.deserialize], [0x18, errorValue.tag, errorValue.deserialize], [0x19, functionValue.tag, functionValue.deserialize], [0x1A, globalValue.tag, globalValue.deserialize], [0x1B, promiseValue.tag, promiseValue.deserialize], [0x1C, regexpValue.tag, regexpValue.deserialize], [0x1D, setValue.tag, setValue.deserialize], [0x1E, typedArrayValue.tag, typedArrayValue.deserialize], [0x1F, typedArrayValue.bytesTag, typedArrayValue.deserializeBytes], ] const tag2id = new Map(mappings.map(mapping => [mapping[1], mapping[0]])) const id2deserialize = new Map(mappings.map(mapping => [mapping[0], mapping[2]])) class DescriptorSerializationError extends Error { constructor (descriptor) { super('Could not serialize descriptor') this.name = 'DescriptorSerializationError' this.descriptor = descriptor } } class MissingPluginError extends Error { constructor (pluginName) { super(`Could not deserialize buffer: missing plugin ${JSON.stringify(pluginName)}`) this.name = 'MissingPluginError' this.pluginName = pluginName } } class PointerLookupError extends Error { constructor (index) { super(`Could not deserialize buffer: pointer ${index} could not be resolved`) this.name = 'PointerLookupError' this.index = index } } class UnsupportedPluginError extends Error { constructor (pluginName, serializerVersion) { super(`Could not deserialize buffer: plugin ${JSON.stringify(pluginName)} expects a different serialization`) this.name = 'UnsupportedPluginError' this.pluginName = pluginName this.serializerVersion = serializerVersion } } class UnsupportedVersion extends Error { // eslint-disable-line unicorn/custom-error-definition constructor (serializerVersion) { super('Could not deserialize buffer: a different serialization was expected') this.name = 'UnsupportedVersion' this.serializerVersion = serializerVersion } } function shallowSerializeDescriptor (descriptor, resolvePluginRef) { if (!descriptor.serialize) return undefined return serializeState(descriptor.serialize(), resolvePluginRef) } function serializeState (state, resolvePluginRef) { if (Array.isArray(state)) return state.map(x => serializeState(x)) if (state && state.tag) { let id, pluginIndex if (tag2id.has(state.tag)) { id = tag2id.get(state.tag) pluginIndex = 0 } else { const ref = resolvePluginRef(state.tag) if (ref) { id = ref.id pluginIndex = ref.pluginIndex } } if (id !== undefined) { const serialized = [pluginIndex, id, shallowSerializeDescriptor(state, resolvePluginRef)] serialized[encoder.descriptorSymbol] = true return serialized } } return state } function serialize (descriptor) { const usedPlugins = new Map() const resolvePluginRef = tag => { const ref = pluginRegistry.resolveDescriptorRef(tag) if (!ref) return null if (!usedPlugins.has(ref.name)) { // Start at 1, since 0 is reserved for Concordance's descriptors. const index = usedPlugins.size + 1 usedPlugins.set(ref.name, Object.assign({ index }, ref.serialization)) } return { id: ref.id, pluginIndex: usedPlugins.get(ref.name).index, } } const seen = new Set() const stack = [] let topIndex = -1 let rootRecord do { if (descriptor.isComplex === true) { if (seen.has(descriptor.pointer)) { descriptor = pointerDescriptor.describe(descriptor.pointer) } else { seen.add(descriptor.pointer) } } let id let pluginIndex = 0 if (tag2id.has(descriptor.tag)) { id = tag2id.get(descriptor.tag) } else { const ref = resolvePluginRef(descriptor.tag) if (!ref) throw new DescriptorSerializationError(descriptor) id = ref.id pluginIndex = ref.pluginIndex } const record = { id, pluginIndex, children: [], state: shallowSerializeDescriptor(descriptor, resolvePluginRef), } if (!rootRecord) { rootRecord = record } else { stack[topIndex].children.push(record) } if (descriptor.createRecursor) { stack.push({ recursor: descriptor.createRecursor(), children: record.children }) topIndex++ } while (topIndex >= 0) { descriptor = stack[topIndex].recursor() if (descriptor === null) { stack.pop() topIndex-- } else { break } } } while (topIndex >= 0) return encoder.encode(VERSION, rootRecord, usedPlugins) } exports.serialize = serialize function deserializeState (state, getDescriptorDeserializer) { if (state && state[encoder.descriptorSymbol] === true) { return shallowDeserializeDescriptor(state, getDescriptorDeserializer) } return Array.isArray(state) ? state.map(item => deserializeState(item, getDescriptorDeserializer)) : state } function shallowDeserializeDescriptor (entry, getDescriptorDeserializer) { const deserializeDescriptor = getDescriptorDeserializer(entry[0], entry[1]) return deserializeDescriptor(entry[2]) } function deserializeRecord (record, getDescriptorDeserializer, buffer) { const deserializeDescriptor = getDescriptorDeserializer(record.pluginIndex, record.id) const state = deserializeState(record.state, getDescriptorDeserializer) if (record.pointerAddresses.length === 0) { return deserializeDescriptor(state) } const endIndex = record.pointerAddresses.length let index = 0 const recursor = () => { if (index === endIndex) return null const recursorRecord = encoder.decodeRecord(buffer, record.pointerAddresses[index++]) return deserializeRecord(recursorRecord, getDescriptorDeserializer, buffer) } return deserializeDescriptor(state, recursor) } function buildPluginMap (buffer, options) { const cache = options && options.deserializedPluginsCache const cacheKey = md5hex(buffer) if (cache && cache.has(cacheKey)) return cache.get(cacheKey) const decodedPlugins = encoder.decodePlugins(buffer) if (decodedPlugins.size === 0) { const pluginMap = new Map() if (cache) cache.set(cacheKey, pluginMap) return pluginMap } const deserializerLookup = new Map() if (Array.isArray(options && options.plugins)) { for (const deserializer of pluginRegistry.getDeserializers(options.plugins)) { deserializerLookup.set(deserializer.name, deserializer) } } const pluginMap = new Map() for (const index of decodedPlugins.keys()) { const used = decodedPlugins.get(index) const pluginName = used.name const serializerVersion = used.serializerVersion // TODO: Allow plugin author to encode a helpful message in its serialization if (!deserializerLookup.has(pluginName)) { throw new MissingPluginError(pluginName) } if (serializerVersion !== deserializerLookup.get(pluginName).serializerVersion) { throw new UnsupportedPluginError(pluginName, serializerVersion) } pluginMap.set(index, deserializerLookup.get(pluginName).id2deserialize) } if (cache) cache.set(cacheKey, pluginMap) return pluginMap } function deserialize (buffer, options) { const version = encoder.extractVersion(buffer) if (version !== VERSION) throw new UnsupportedVersion(version) const decoded = encoder.decode(buffer) const pluginMap = buildPluginMap(decoded.pluginBuffer, options) const descriptorsByPointerIndex = new Map() const mapPointerDescriptor = descriptor => { if (descriptor.isPointer === true) { if (descriptorsByPointerIndex.has(descriptor.index)) { return descriptorsByPointerIndex.get(descriptor.index) } if (typeof rootDescriptor.createRecursor === 'function') { // The descriptor we're pointing to may be elsewhere in the serialized // structure. Consume the entire structure and check again. recursorUtils.consumeDeep(rootDescriptor.createRecursor()) if (descriptorsByPointerIndex.has(descriptor.index)) { return descriptorsByPointerIndex.get(descriptor.index) } } throw new PointerLookupError(descriptor.index) } if (descriptor.isComplex === true) { descriptorsByPointerIndex.set(descriptor.pointer, descriptor) } return descriptor } const getDescriptorDeserializer = (pluginIndex, id) => { return (state, recursor) => { const deserializeDescriptor = pluginIndex === 0 ? id2deserialize.get(id) : pluginMap.get(pluginIndex).get(id) return mapPointerDescriptor(deserializeDescriptor(state, recursor)) } } const rootDescriptor = deserializeRecord(decoded.rootRecord, getDescriptorDeserializer, buffer) return rootDescriptor } exports.deserialize = deserialize concordance-5.0.4/lib/shouldCompareDeep.js000066400000000000000000000010751402315264500205120ustar00rootroot00000000000000'use strict' const argumentsObject = require('./complexValues/arguments').tag const constants = require('./constants') const AMBIGUOUS = constants.AMBIGUOUS const SHALLOW_EQUAL = constants.SHALLOW_EQUAL function shouldCompareDeep (result, lhs, rhs) { if (result === SHALLOW_EQUAL) return true if (result !== AMBIGUOUS) return false // Properties are only ambiguous if they have symbol keys. These properties // must be compared in an order-insensitive manner. return lhs.tag === argumentsObject || lhs.isProperty === true } module.exports = shouldCompareDeep concordance-5.0.4/lib/symbolProperties.js000066400000000000000000000053071402315264500204730ustar00rootroot00000000000000'use strict' const constants = require('./constants') const recursorUtils = require('./recursorUtils') const DEEP_EQUAL = constants.DEEP_EQUAL const SHALLOW_EQUAL = constants.SHALLOW_EQUAL const UNEQUAL = constants.UNEQUAL class Comparable { constructor (properties) { this.properties = properties this.ordered = properties.slice() } createRecursor () { const length = this.ordered.length let index = 0 return () => { if (index === length) return null return this.ordered[index++] } } compare (expected) { if (this.properties.length !== expected.properties.length) return UNEQUAL // Compare property keys, reordering the expected properties in the process // so values can be compared if all keys are equal. const ordered = [] const processed = new Set() for (const property of this.properties) { let extraneous = true for (const other of expected.properties) { if (processed.has(other.key)) continue if (property.key.compare(other.key) === DEEP_EQUAL) { extraneous = false processed.add(other.key) ordered.push(other) break } } if (extraneous) return UNEQUAL } expected.ordered = ordered return SHALLOW_EQUAL } prepareDiff (expected) { // Reorder the expected properties before recursion starts. const missingProperties = [] const ordered = [] const processed = new Set() for (const other of expected.properties) { let missing = true for (const property of this.properties) { if (processed.has(property.key)) continue if (property.key.compare(other.key) === DEEP_EQUAL) { missing = false processed.add(property.key) ordered.push(other) break } } if (missing) { missingProperties.push(other) } } expected.ordered = ordered.concat(missingProperties) return { mustRecurse: true } } } Object.defineProperty(Comparable.prototype, 'isSymbolPropertiesComparable', { value: true }) exports.Comparable = Comparable class Collector { constructor (firstProperty, recursor) { this.properties = [firstProperty] this.recursor = recursor this.remainder = null } collectAll () { do { const next = this.recursor() if (next && next.isProperty === true) { // All properties will have symbol keys this.properties.push(next) } else { return next } } while (true) } createRecursor () { return recursorUtils.singleValue(new Comparable(this.properties)) } } Object.defineProperty(Collector.prototype, 'isSymbolPropertiesCollector', { value: true }) exports.Collector = Collector concordance-5.0.4/lib/themeUtils.js000066400000000000000000000122051402315264500172270ustar00rootroot00000000000000'use strict' const cloneDeep = require('lodash/cloneDeep') const merge = require('lodash/merge') const pluginRegistry = require('./pluginRegistry') function freezeTheme (theme) { const queue = [theme] while (queue.length > 0) { const object = queue.shift() Object.freeze(object) for (const key of Object.keys(object)) { const value = object[key] if (value !== null && typeof value === 'object') { queue.push(value) } } } return theme } const defaultTheme = freezeTheme({ bigInt: { open: '', close: '' }, boolean: { open: '', close: '' }, circular: '[Circular]', date: { invalid: 'invalid', value: { open: '', close: '' }, }, diffGutters: { actual: '- ', expected: '+ ', padding: ' ', }, error: { ctor: { open: '(', close: ')' }, name: { open: '', close: '' }, }, function: { name: { open: '', close: '' }, stringTag: { open: '', close: '' }, }, global: { open: '', close: '' }, item: { after: ',', customFormat: null, increaseValueIndent: false, }, list: { openBracket: '[', closeBracket: ']' }, mapEntry: { after: ',', separator: ' => ', }, maxDepth: '…', null: { open: '', close: '' }, number: { open: '', close: '' }, object: { openBracket: '{', closeBracket: '}', ctor: { open: '', close: '' }, stringTag: { open: '@', close: '' }, secondaryStringTag: { open: '@', close: '' }, }, property: { after: ',', customFormat: null, keyBracket: { open: '[', close: ']' }, separator: ': ', increaseValueIndent: false, }, regexp: { source: { open: '/', close: '/' }, flags: { open: '', close: '' }, separator: '---', }, stats: { separator: '---' }, string: { open: '', close: '', line: { open: "'", close: "'", escapeQuote: "'" }, multiline: { start: '`', end: '`', escapeQuote: '`' }, controlPicture: { open: '', close: '' }, diff: { insert: { open: '', close: '' }, delete: { open: '', close: '' }, equal: { open: '', close: '' }, insertLine: { open: '', close: '' }, deleteLine: { open: '', close: '' }, }, }, symbol: { open: '', close: '' }, typedArray: { bytes: { open: '', close: '' }, }, undefined: { open: '', close: '' }, }) const pluginRefs = new Map() pluginRefs.count = 0 const normalizedPluginThemes = new Map() function normalizePlugins (plugins) { if (!Array.isArray(plugins) || plugins.length === 0) return null const refs = [] const themes = [] for (const fromPlugin of pluginRegistry.getThemes(plugins)) { if (!pluginRefs.has(fromPlugin.name)) { pluginRefs.set(fromPlugin.name, pluginRefs.count++) } refs.push(pluginRefs.get(fromPlugin.name)) themes.push(fromPlugin.theme) } const ref = refs.join('.') if (normalizedPluginThemes.has(ref)) { return { ref, theme: normalizedPluginThemes.get(ref), } } const theme = freezeTheme(themes.reduce((acc, pluginTheme) => { return merge(acc, pluginTheme) }, cloneDeep(defaultTheme))) normalizedPluginThemes.set(ref, theme) return { ref, theme } } const normalizedCache = new WeakMap() function normalize (options) { options = Object.assign({ plugins: [], theme: null }, options) const normalizedPlugins = normalizePlugins(options.plugins) if (!options.theme) { return normalizedPlugins ? normalizedPlugins.theme : defaultTheme } const entry = normalizedCache.get(options.theme) || { theme: null, withPlugins: new Map() } if (!normalizedCache.has(options.theme)) normalizedCache.set(options.theme, entry) if (normalizedPlugins) { if (entry.withPlugins.has(normalizedPlugins.ref)) { return entry.withPlugins.get(normalizedPlugins.ref) } const theme = freezeTheme(merge(cloneDeep(normalizedPlugins.theme), options.theme)) entry.withPlugins.set(normalizedPlugins.ref, theme) return theme } if (!entry.theme) { entry.theme = freezeTheme(merge(cloneDeep(defaultTheme), options.theme)) } return entry.theme } exports.normalize = normalize const modifiers = new WeakMap() function addModifier (descriptor, modifier) { if (modifiers.has(descriptor)) { modifiers.get(descriptor).add(modifier) } else { modifiers.set(descriptor, new Set([modifier])) } } exports.addModifier = addModifier const modifierCache = new WeakMap() const originalCache = new WeakMap() function applyModifiers (descriptor, theme) { if (!modifiers.has(descriptor)) return theme return Array.from(modifiers.get(descriptor)).reduce((prev, modifier) => { const cache = modifierCache.get(modifier) || new WeakMap() if (!modifierCache.has(modifier)) modifierCache.set(modifier, cache) if (cache.has(prev)) return cache.get(prev) const modifiedTheme = cloneDeep(prev) modifier(modifiedTheme) freezeTheme(modifiedTheme) cache.set(prev, modifiedTheme) originalCache.set(modifiedTheme, theme) return modifiedTheme }, theme) } exports.applyModifiers = applyModifiers function applyModifiersToOriginal (descriptor, theme) { return applyModifiers(descriptor, originalCache.get(theme) || theme) } exports.applyModifiersToOriginal = applyModifiersToOriginal concordance-5.0.4/package.json000066400000000000000000000025121402315264500162660ustar00rootroot00000000000000{ "name": "concordance", "version": "5.0.4", "description": "Compare, format, diff and serialize any JavaScript value", "main": "index.js", "files": [ "lib", "index.js" ], "engines": { "node": ">=10.18.0 <11 || >=12.14.0 <13 || >=14" }, "scripts": { "test": "as-i-preach && c8 ava" }, "repository": { "type": "git", "url": "git+https://github.com/concordancejs/concordance.git" }, "author": "Mark Wubben (https://novemberborn.net/)", "license": "ISC", "bugs": { "url": "https://github.com/concordancejs/concordance/issues" }, "homepage": "https://github.com/concordancejs/concordance#readme", "dependencies": { "date-time": "^3.1.0", "esutils": "^2.0.3", "fast-diff": "^1.2.0", "js-string-escape": "^1.0.1", "lodash": "^4.17.15", "md5-hex": "^3.0.1", "semver": "^7.3.2", "well-known-symbols": "^2.0.0" }, "devDependencies": { "@novemberborn/eslint-plugin-as-i-preach": "^12.0.0", "ava": "^3.15.0", "c8": "^7.1.2", "eslint": "^6.8.0", "eslint-plugin-ava": "^10.3.0", "eslint-plugin-import": "^2.20.2", "eslint-plugin-node": "^11.1.0", "eslint-plugin-promise": "^4.2.1", "eslint-plugin-security": "^1.4.0", "eslint-plugin-standard": "^4.0.1", "eslint-plugin-unicorn": "^17.2.0", "proxyquire": "^2.1.3" } } concordance-5.0.4/test/000077500000000000000000000000001402315264500147575ustar00rootroot00000000000000concordance-5.0.4/test/_instrumentedTheme.js000066400000000000000000000020571402315264500211640ustar00rootroot00000000000000const { normalize } = require('../lib/themeUtils') const unused = new Set() const freeze = Object.freeze const createAccessors = (object, path = '') => { for (const key of Object.keys(object)) { const value = object[key] const keyPath = path ? `${path}.${key}` : key if (value && typeof value === 'object') { createAccessors(value, keyPath) } else if (typeof value === 'string') { unused.add(keyPath) Object.defineProperty(object, key, { get () { unused.delete(keyPath) return `%${keyPath}${value ? '#' + value : ''}%` }, }) } } freeze.call(Object, object) } const theme = {} // normalize() caches the result, so this is just a cache key Object.freeze = obj => obj // Stub out so accessors can be created const normalized = normalize({ theme }) createAccessors(normalized) Object.freeze = freeze exports.theme = theme exports.normalizedTheme = normalized exports.checkThemeUsage = t => { t.deepEqual(unused, new Set(), 'All theme properties should be accessed at least once') } concordance-5.0.4/test/compare.js000066400000000000000000000032071402315264500167450ustar00rootroot00000000000000const test = require('ava') const { compare } = require('../lib/compare') test('compare functions by reference', t => { function a () { return 1 + 2 } // eslint-disable-line unicorn/consistent-function-scoping const a_ = (() => { return function a () { return 1 + 2 } // eslint-disable-line no-shadow })() t.false(compare(a, a_).pass) }) test('compare function by names', t => { function a () { return 1 + 2 } // eslint-disable-line unicorn/consistent-function-scoping function b () { return 1 + 2 } // eslint-disable-line unicorn/consistent-function-scoping t.false(compare(a, b).pass) }) test('objects compare even if symbol properties are out of order', t => { const s1 = Symbol('s1') const s2 = Symbol('s2') const o1 = { [s1]: 1, [s2]: 2 } const o2 = { [s2]: 2, [s1]: 1 } t.true(compare(o1, o2).pass) const a1 = new Set([1, 2]) a1[s1] = 1 a1[s2] = 2 const a2 = new Set([1, 2]) a2[s2] = 2 a2[s1] = 1 t.true(compare(a1, a2).pass) }) test('-0 is not equal to +0', t => { t.false(compare(-0, +0).pass) t.false(compare({ zero: -0 }, { zero: +0 }).pass) }) test('NaN is equal to NaN', t => { t.true(compare(NaN, NaN).pass) t.true(compare({ notANumber: NaN }, { notANumber: NaN }).pass) }) test('survives odd circular references', t => { const foo = { foo: {} } foo.foo.foo = foo const foo2 = {} foo2.foo = foo t.false(compare(foo, foo2).pass) }) test('arrays are also compared by property', t => { const a1 = [1, 2, 3] a1.p = 'a1' const a2 = [1, 2, 3] a2.p = 'a2' t.false(compare(a1, a2).pass) const a3 = [1, 2, 3] const a4 = [1, 2, 3] a4[-1] = -1 t.false(compare(a3, a4).pass) }) concordance-5.0.4/test/diff.js000066400000000000000000000361271402315264500162360ustar00rootroot00000000000000const test = require('ava') const concordance = require('..') const { diff: _diff } = require('../lib/diff') const { theme, normalizedTheme, checkThemeUsage } = require('./_instrumentedTheme') const diff = (actual, expected, { invert } = {}) => _diff(actual, expected, { invert, theme }) test.after(checkThemeUsage) void ( // Tested separately normalizedTheme.maxDepth ) if (typeof BigInt === 'undefined') { void ( normalizedTheme.bigInt.open, normalizedTheme.bigInt.close ) } { const diffsPrimitives = (t, lhs, rhs) => t.snapshot(diff(lhs, rhs)) diffsPrimitives.title = (_, lhs, rhs, lhsRepresentation = String(lhs), rhsRepresentation = String(rhs)) => { return `diffs primitives: ${lhsRepresentation} versus ${rhsRepresentation}` } for (const [lhs, rhs] of [ [null, undefined], [null, false], [null, true], [null, ''], [null, 42], [null, Symbol()], // eslint-disable-line symbol-description [Symbol(), Symbol()], // eslint-disable-line symbol-description [null, {}], ]) { test(diffsPrimitives, lhs, rhs) } if (typeof BigInt === 'function') { test(diffsPrimitives, null, BigInt(42), 'null', '42n') // eslint-disable-line no-undef } } { const diffsBoxedPrimitives = (t, lhs, rhs) => t.snapshot(diff(new Object(lhs), new Object(rhs))) diffsBoxedPrimitives.title = (_, lhs, rhs, lhsRepresentation = String(lhs), rhsRepresentation = String(rhs)) => { return `diffs primitives: ${lhsRepresentation} versus ${rhsRepresentation}` } for (const [lhs, rhs] of [ [true, false], [-42, 42], ['foo', 'bar'], ]) { test(diffsBoxedPrimitives, lhs, rhs) } if (typeof BigInt === 'function') { test(diffsBoxedPrimitives, BigInt(-42), BigInt(42), '-42n', '42n') // eslint-disable-line no-undef } } test('diffs boxed primitives with extra properties', t => { t.snapshot(diff(new Object('foo'), Object.assign(new Object('foo'), { bar: 'baz' }))) }) test('diffs single line strings', t => { const actual1 = diff('foo', 'bar') t.snapshot(actual1) const actual2 = diff('bar', 'baz') t.snapshot(actual2) }) test('diffs multiline strings', t => { const actual1 = diff('foo\nbar', 'baz\nbar') t.snapshot(actual1) const actual2 = diff('foo\nbar', 'foo\nbaz') t.snapshot(actual2) const actual3 = diff('foo\nbar\nbaz', 'foo\nbaz') t.snapshot(actual3) const actual4 = diff('foo\nbaz', 'foo\nbar\nbaz') t.snapshot(actual4) const actual5 = diff('foo\n', 'foo\nbaz') t.snapshot(actual5) const actual6 = diff('foo\nbaz', 'foo\n') t.snapshot(actual6) const actual7 = diff('foo\nbar\nbaz', 'foo\n') t.snapshot(actual7) const actual8 = diff(`foo bar baz qux quux`, `corge grault baz garply quux`) t.snapshot(actual8) const actual9 = diff('foo\nbar\ncorge\nbaz\nqux\nquux\n', 'foo\nbar\nbaz\ngrault\nqux\nquux') t.snapshot(actual9) }) test('diffs diverging complex types', t => { const actual = diff({ foo: 'bar' }, ['baz']) t.snapshot(actual) }) { const mapArray = arr => arr const mapArguments = arr => { let args; (function () { args = arguments })(...arr); return args } const mapSet = arr => new Set(arr) const equalLength = (t, map) => { const actual1 = diff(map([1, 2, 4]), map([1, 3, 4])) t.snapshot(actual1) const actual2 = diff( map([1, { foo: 'bar' }, 2]), map([1, { baz: 'qux' }, 2])) t.snapshot(actual2) class Foo { constructor () { this.foo = 'foo' } } class Bar { constructor () { this.bar = 'bar' } } const actual3 = diff( map([new Foo()]), map([new Bar()])) t.snapshot(actual3) const actual4 = diff( map([Buffer.alloc(0)]), map([new Uint8Array()])) t.snapshot(actual4) const actual5 = diff(map([1, 2]), map([1])) t.snapshot(actual5) } test('diffs arrays', equalLength, mapArray) test('diffs arguments', equalLength, mapArguments) test('diffs sets', equalLength, mapSet) const extraneous = (t, map) => { const actual1 = diff(map([1, 3, 2]), map([1, 2])) t.snapshot(actual1) const actual2 = diff(map([1, {}, 2]), map([1, 2])) t.snapshot(actual2) const actual3 = diff( map([1, { foo: 'bar' }, { baz: 'qux' }, 2]), map([1, { baz: 'qux' }, 2])) t.snapshot(actual3) const s1 = Symbol('s1') const s2 = Symbol('s2') const actual4 = diff( map([1, { [s1]: 'bar' }, { [s2]: 'qux' }, 2]), map([1, { [s2]: 'qux' }, 2])) t.snapshot(actual4) } test('detects extraneous array items', extraneous, mapArray) test('detects extraneous arguments items', extraneous, mapArguments) test('detects extraneous set items', extraneous, mapSet) const missing = (t, map) => { const actual1 = diff(map([1, 2]), map([1, 3, 2])) t.snapshot(actual1) const actual2 = diff(map([1, 2]), map([1, {}, 2])) t.snapshot(actual2) const actual3 = diff( map([1, { baz: 'qux' }, 2]), map([1, { foo: 'bar' }, { baz: 'qux' }, 2])) t.snapshot(actual3) const s1 = Symbol('s1') const s2 = Symbol('s2') const actual4 = diff( map([1, { [s2]: 'qux' }, 2]), map([1, { [s1]: 'bar' }, { [s2]: 'qux' }, 2])) t.snapshot(actual4) } test('detects missing array items', missing, mapArray) test('detects missing arguments items', missing, mapArguments) test('detects missing set items', missing, mapSet) } test('detects extraneous name properties', t => { const actual1 = diff( { a: 1, b: 2, c: 3 }, { a: 1, c: 3 }) t.snapshot(actual1) const actual2 = diff( { a: 1, b: {}, c: 3 }, { a: 1, c: 3 }) t.snapshot(actual2) }) test('detects missing name properties', t => { const actual1 = diff( { a: 1, c: 3 }, { a: 1, b: 2, c: 3 }) t.snapshot(actual1) const actual2 = diff( { a: 1, c: 3 }, { a: 1, b: {}, c: 3 }) t.snapshot(actual2) }) test('detects extraneous symbol properties', t => { const s1 = Symbol('s1') const s2 = Symbol('s2') const s3 = Symbol('s3') const actual1 = diff( { [s1]: 1, [s2]: 2, [s3]: 3 }, { [s1]: 1, [s3]: 3 }) t.snapshot(actual1) const actual2 = diff( { [s1]: 1, [s2]: {}, [s3]: 3 }, { [s1]: 1, [s3]: 3 }) t.snapshot(actual2) }) test('detects missing symbol properties', t => { const s1 = Symbol('s1') const s2 = Symbol('s2') const s3 = Symbol('s3') const actual1 = diff( { [s1]: 1, [s3]: 3 }, { [s1]: 1, [s2]: 2, [s3]: 3 }) // Note that when symbol properties are sorted, they're sorted according to // their order in the actual value. Missing properties will end up at the // bottom. t.snapshot(actual1) const actual2 = diff( { [s1]: 1, [s3]: 3 }, { [s1]: 1, [s2]: {}, [s3]: 3 }) t.snapshot(actual2) }) test('diffs maps', t => { const actual1 = diff( new Map([[1, 1], [2, 2], [4, 4]]), new Map([[1, 1], [3, 3], [4, 4]])) t.snapshot(actual1) const actual2 = diff( new Map([[1, 1], [{ foo: 'bar' }, 2], [4, 4]]), new Map([[1, 1], [{ baz: 'qux' }, 2], [4, 4]])) t.snapshot(actual2) const actual3 = diff( new Map([[1, 1], [{ foo: 'bar' }, 2], [4, 4]]), new Map([[1, 1], [{ baz: 'qux' }, 3], [4, 4]])) t.snapshot(actual3) }) test('detects extraneous map entries', t => { const actual1 = diff( new Map([[1, 1], [3, 3], [2, 2]]), new Map([[1, 1], [2, 2]])) t.snapshot(actual1) const actual2 = diff( new Map([[1, 1], [{}, 3], [2, 2]]), new Map([[1, 1], [2, 2]])) t.snapshot(actual2) const actual3 = diff( new Map([[1, 1], [{ foo: 'bar' }, 4], [{ baz: 'qux' }, 2], [3, 3]]), new Map([[1, 1], [{ baz: 'qux' }, 2], [3, 3]])) t.snapshot(actual3) const s1 = Symbol('s1') const s2 = Symbol('s2') const actual4 = diff( new Map([[1, 1], [{ [s1]: 'bar' }, 4], [{ [s2]: 'qux' }, 2], [3, 3]]), new Map([[1, 1], [{ [s2]: 'qux' }, 2], [3, 3]])) t.snapshot(actual4) }) test('detects missing map entries', t => { const actual1 = diff( new Map([[1, 1], [2, 2]]), new Map([[1, 1], [3, 3], [2, 2]])) t.snapshot(actual1) const actual2 = diff( new Map([[1, 1], [2, 2]]), new Map([[1, 1], [{}, 3], [2, 2]])) t.snapshot(actual2) const actual3 = diff( new Map([[1, 1], [{ baz: 'qux' }, 2], [3, 3]]), new Map([[1, 1], [{ foo: 'bar' }, 4], [{ baz: 'qux' }, 2], [3, 3]])) t.snapshot(actual3) const s1 = Symbol('s1') const s2 = Symbol('s2') const actual4 = diff( new Map([[1, 1], [{ [s2]: 'qux' }, 2], [3, 3]]), new Map([[1, 1], [{ [s1]: 'bar' }, 4], [{ [s2]: 'qux' }, 2], [3, 3]])) t.snapshot(actual4) }) test('diffs maps with extra properties', t => { const actual1 = diff(new Map([['foo', 'bar']]), Object.assign(new Map([['foo', 'bar']]), { baz: 'qux' })) t.snapshot(actual1) const actual2 = diff(Object.assign(new Map([['foo', 'bar']]), { baz: 'qux' }), new Map([['foo', 'bar']])) t.snapshot(actual2) }) test('diffs multiline string values in objects', t => { const actual1 = diff({ foo: 'bar\nbaz' }, { foo: 'qux\nbaz' }) t.snapshot(actual1) const actual2 = diff({ foo: 'bar\nbaz\nqux' }, { foo: 'bar\nqux\nbaz' }) t.snapshot(actual2) const s1 = Symbol('s1') const actual3 = diff({ [s1]: 'bar\nbaz' }, { [s1]: 'qux\nbaz' }) t.snapshot(actual3) }) test('diffs multiline string values in arrays', t => { const actual = diff(['foo\nbar'], ['baz\nbar']) t.snapshot(actual) }) test('diffs multiline string values in sets', t => { const actual = diff(new Set(['foo\nbar']), new Set(['baz\nbar'])) t.snapshot(actual) }) test('diffs multiline string values in maps when key is primitive', t => { const actual1 = diff(new Map([[1, 'foo\nbar']]), new Map([[1, 'baz\nbar']])) t.snapshot(actual1) const actual2 = diff(new Map([['foo\nbar', 'foo\nbar']]), new Map([['foo\nbar', 'baz\nbar']])) t.snapshot(actual2) const actual3 = diff(new Map([['foo', 'bar\nbaz\nqux']]), new Map([['foo', 'bar\nqux\nbaz']])) t.snapshot(actual3) }) test('does not diff multiline string values in maps when key is complex', t => { const actual = diff(new Map([[{}, 'foo\nbar']]), new Map([[{}, 'baz\nbar']])) t.snapshot(actual) }) test('diffs properties with different values', t => { class Foo { constructor () { this.value = 42 } } class Bar { constructor () { this.value = 42 } } const actual1 = diff({ value: new Foo() }, { value: new Bar() }) t.snapshot(actual1) const actual2 = diff({ value: new Foo() }, { value: 42 }) t.snapshot(actual2) const actual3 = diff({ value: new Foo() }, { value: 'foo\nbar' }) t.snapshot(actual3) const actual4 = diff({ foo: 'bar' }, { foo: 'not bar' }) t.snapshot(actual4) }) test('diffs map keys with different values', t => { class Foo { constructor () { this.value = 42 } } class Bar { constructor () { this.value = 42 } } const actual1 = diff(new Map([['key', new Foo()]]), new Map([['key', new Bar()]])) t.snapshot(actual1) const actual2 = diff(new Map([['key', new Foo()]]), new Map([['key', 42]])) t.snapshot(actual2) const actual3 = diff(new Map([['key', new Foo()]]), new Map([['key', 'foo\nbar']])) t.snapshot(actual3) const actual4 = diff(new Map([['key\nline', new Foo()]]), new Map([['key\nline', new Bar()]])) t.snapshot(actual4) }) test('diffs circular references', t => { const obj1 = { obj: {} } obj1.obj.obj = obj1 const obj2 = {} obj2.obj = obj2 t.snapshot(diff(obj1, obj2)) obj2.obj = obj1 t.snapshot(diff(obj1, obj2)) const arr1 = [[]] arr1[0][0] = arr1 const arr2 = [] arr2[0] = arr1 t.snapshot(diff(arr1, arr2)) const map1 = new Map([['map', new Map()]]) map1.get('map').set('map', map1) const map2 = new Map() map2.set('map', map1) t.snapshot(diff(map1, map2)) const key = { key: true } const map3 = new Map([[key, new Map()]]) map3.get(key).set(key, map3) const map4 = new Map() map4.set(key, map3) t.snapshot(diff(map3, map4)) }) test('diff invalid dates', t => { t.snapshot(diff(new Date('🚀🌔🛬'), new Date('🚀💥💧'))) }) test('diff dates with extra properties', t => { const actual = diff(new Date('1969-07-20T20:17:40.000Z'), Object.assign(new Date('1969-07-21T20:17:40.000Z'), { foo: 'bar', })) t.snapshot(actual) }) test('diffs errors', t => { class Custom extends Error {} // eslint-disable-line unicorn/custom-error-definition t.snapshot(diff(new Custom(), new Error())) }) test('diffs functions', t => { t.snapshot(diff(function foo () {}, function bar () {})) }) test('diffs globals', t => { t.snapshot(diff(global, global)) }) test('diffs objects without constructor', t => { const obj = {} Object.defineProperty(obj, 'constructor', {}) t.snapshot(diff(obj, {})) }) test('diffs builtin subclasses', t => { class Foo extends Array {} t.snapshot(diff(new Foo(), [])) }) test('diffs regexps', t => { t.snapshot(diff(/foo/, /foo/g)) t.snapshot(diff(/foo/, Object.assign(/foo/, { bar: 'baz' }))) }) test('diffs buffers', t => { t.snapshot(diff(Buffer.from('decafbad', 'hex'), Buffer.from('flat white', 'utf8'))) }) test('inverted diffs', t => { t.snapshot(diff({ foo: 'bar', baz: 'qux\nquux\ncorge', }, { foo: 'BAR', baz: 'qux\ncorge\nquux', }, { invert: true })) }) test('inverts string diffs', t => { t.snapshot(diff('foo', 'bar', { invert: true })) t.snapshot(diff('foo bar baz', 'foo baz quux', { invert: true })) t.snapshot(diff('foo\nbar\nbaz', 'foobarbaz', { invert: true })) }) test('inverts string diffs in containers', t => { const actual = 'foo\nbar\nbaz' const expected = 'foo\nbaz\nquux' t.snapshot(diff({ a: actual }, { a: expected }, { invert: true })) t.snapshot(diff([actual], [expected], { invert: true })) t.snapshot(diff(new Map().set('a', actual), new Map().set('a', expected), { invert: true })) t.snapshot(diff(new Set().add(actual), new Set().add(expected), { invert: true })) t.snapshot(diff(new String(actual), new String(expected), { invert: true })) // eslint-disable-line unicorn/new-for-builtins, no-new-wrappers t.snapshot(diff({ a: { b: actual } }, { a: { b: expected } }, { invert: true })) }) test('lists: effectively resets depth when formatting differences', t => { const l1 = [ { b: 'b', }, { d: 'bar', e: { f: 'f', }, }, ] const l2 = [ { b: 'b', }, ] t.snapshot(_diff(l1, l2, { maxDepth: 1, theme })) }) test('objects: effectively resets depth when formatting differences', t => { const o1 = { a: { b: 'b', }, c: { d: 'bar', e: { f: 'f', }, }, } const o2 = { a: { b: 'b', }, } t.snapshot(_diff(o1, o2, { maxDepth: 1, theme })) }) // See . test('diff pointers hidden behind maxDepth', t => { const value = {} const descriptor = concordance.describe({ // `value` is encoded in the serialization of `a.b`. `c` is encoded as a // pointer to the encoded `value`. a: { b: value, }, c: value, }) const serialized = concordance.serialize(descriptor) t.notThrows(() => { // `maxDepth: 1` means that `a.b` is not normally deserialized, and so the // `c` pointer cannot be resolved, unless the resolution logic first // deserializes the descriptor in its entirety. concordance.diffDescriptors(concordance.deserialize(serialized), concordance.describe(undefined), { maxDepth: 1 }) }) }) concordance-5.0.4/test/fixtures/000077500000000000000000000000001402315264500166305ustar00rootroot00000000000000concordance-5.0.4/test/fixtures/customErrorPlugin.js000066400000000000000000000021061402315264500226700ustar00rootroot00000000000000'use strict' class CustomError extends Error { constructor (message, code) { super(message) this.code = code this.name = 'CustomError' } } exports.CustomError = CustomError exports.factory = function ({ DescribedMixin, DeserializedMixin, ObjectValue }) { const tag = Symbol.for('customError') class DescribedErrorValue extends DescribedMixin(ObjectValue) { createPropertyRecursor () { let i = 0 return { size: 1, next: () => { if (i === 1) { return null } i++ return this.describeProperty('code', this.describeAny(this.value.code)) }, } } } Object.defineProperty(DescribedErrorValue.prototype, 'tag', { value: tag }) const DeserializedErrorValue = DeserializedMixin(ObjectValue) Object.defineProperty(DeserializedErrorValue.prototype, 'tag', { value: tag }) return { describe (props) { return new DescribedErrorValue(props) }, deserialize (state, recursor) { return new DeserializedErrorValue(state, recursor) }, tag, } } concordance-5.0.4/test/fixtures/pointerSerialization.bin000066400000000000000000000003101402315264500235320ustar00rootroot0000000000000028MObjectObject bbarfooObjectObject fooObjectObjectconcordance-5.0.4/test/fixtures/pointerSerialization.js000066400000000000000000000006511402315264500234060ustar00rootroot00000000000000'use strict' const fs = require('fs') const path = require('path') const concordance = require('../..') const foo = {} const tree = { foo, bar: { foo }, } const binFile = path.join(__dirname, path.basename(__filename, '.js') + '.bin') if (require.main === module) { fs.writeFileSync(binFile, concordance.serialize(concordance.describe(tree))) } exports.tree = tree exports.serialization = fs.readFileSync(binFile) concordance-5.0.4/test/format.js000066400000000000000000000317241402315264500166140ustar00rootroot00000000000000const test = require('ava') const concordance = require('..') const { format: _format } = require('../lib/format') const { theme, normalizedTheme, checkThemeUsage } = require('./_instrumentedTheme') const customErrorPlugin = require('./fixtures/customErrorPlugin') const format = value => _format(value, { theme }) test.after(checkThemeUsage) // "Use" diff themes void ( normalizedTheme.diffGutters.actual, normalizedTheme.diffGutters.expected, normalizedTheme.diffGutters.padding, normalizedTheme.string.diff.insert.open, normalizedTheme.string.diff.insert.close, normalizedTheme.string.diff.delete.open, normalizedTheme.string.diff.delete.close, normalizedTheme.string.diff.equal.open, normalizedTheme.string.diff.equal.close, normalizedTheme.string.diff.insertLine.open, normalizedTheme.string.diff.insertLine.close, normalizedTheme.string.diff.deleteLine.open, normalizedTheme.string.diff.deleteLine.close, // Tested separately normalizedTheme.maxDepth ) if (typeof BigInt === 'undefined') { void ( normalizedTheme.bigInt.open, normalizedTheme.bigInt.close ) } { const formatsPrimitive = (t, value) => t.snapshot(format(value)) formatsPrimitive.title = (valueRepresentation, value) => { const str = Object.is(value, -0) ? '-0' : String(value).replace(/\r/g, '\\r').replace(/\n/g, '\\n') return `formats primitive: ${valueRepresentation || str}` } for (const value of [ null, undefined, false, true, '', 'foo', '\\ -- \' -- "', 'foo\nbar\\baz\'"', 'qux\r\nquux', 'qux\rquux', 42, -42, -0, +0, Infinity, -Infinity, NaN, Symbol(), // eslint-disable-line symbol-description Symbol('foo'), Symbol('foo\nbar'), Symbol.for('bar'), Symbol.for('bar\nbaz'), Symbol.iterator, ]) { test(formatsPrimitive, value) } if (typeof BigInt === 'function') { test('42n', formatsPrimitive, BigInt(42)) // eslint-disable-line no-undef test('-42n', formatsPrimitive, BigInt(-42)) // eslint-disable-line no-undef } } { const escapesQuote = (t, escapeQuote) => { const testTheme = { string: { line: { open: '<', close: '>', escapeQuote }, multiline: { start: '<', end: '>', escapeQuote }, }, } t.snapshot(_format(escapeQuote, { theme: testTheme })) t.snapshot(_format(escapeQuote + '\n', { theme: testTheme })) } escapesQuote.title = (_, quote) => `escapes ${quote} according to theme` test(escapesQuote, "'") test(escapesQuote, '"') test(escapesQuote, '`') } test('escapes singlequotes in one-line strings with the default theme', t => { t.snapshot(_format("'"), 'should be escaped') t.snapshot(_format("'\n"), 'should not be escaped') }) // Regression test for #36 test('escapes backticks in multi-line strings with the default theme', t => { t.snapshot(_format('`'), 'should not be escaped') t.snapshot(_format('`\n'), 'should be escaped') }) test('formats a simple object', t => { const obj = { foo: 'bar', baz: 'qux' } const actual = format(obj) t.snapshot(actual) }) test('formats a simple, nested object', t => { const obj = { foo: { baz: 'qux' } } const actual = format(obj) t.snapshot(actual) }) test('formats multiline strings inside an object', t => { const actual = format({ 'foo\nbar': 'baz\nqux' }) t.snapshot(actual) }) test('formats symbol keys', t => { t.snapshot(format({ [Symbol('')]: 'bar' })) }) test('formats registered symbols differently from normal symbols with same description', t => { t.true(format(Symbol('foo')) !== format(Symbol.for('foo'))) }) { const formatsBoxedPrimitive = (t, value) => t.snapshot(format(new Object(value))) formatsBoxedPrimitive.title = (valueRepresentation, value) => { return `formats boxed primitive: ${valueRepresentation || (Object.is(value, -0) ? '-0' : String(value))}` } for (const value of [ false, true, 42, -42, -0, +0, Infinity, -Infinity, NaN, 'foo', ]) { test(formatsBoxedPrimitive, value) } if (typeof BigInt === 'function') { test('42n', formatsBoxedPrimitive, BigInt(42)) // eslint-disable-line no-undef test('-42n', formatsBoxedPrimitive, BigInt(-42)) // eslint-disable-line no-undef } } test('formats boxed primitives with extra properties', t => { t.snapshot(format(Object.assign(new Object('foo'), { bar: 'baz' }))) }) test('formats a simple array', t => { const arr = ['foo', 'bar'] const actual = format(arr) t.snapshot(actual) }) test('formats a simple, nested array', t => { const arr = [['foo', 'bar']] const actual = format(arr) t.snapshot(actual) }) test('formats an array with additional properties', t => { const arr1 = ['foo', 'bar'] arr1.baz = 'qux' t.snapshot(format(arr1)) const arr2 = [1, 2, 3] arr2[-1] = -1 t.snapshot(format(arr2)) }) test('formats a multiline string inside an array', t => { const actual = format(['bar\nbaz']) t.snapshot(actual) }) test('formats maps', t => { const map = new Map() map.set('foo', 'bar') map.set({ baz: 'qux' }, 'quux') map.set('corge', { grault: 'garply' }) const actual = format(map) t.snapshot(actual) }) test('formats multiline strings inside a map', t => { const actual = format(new Map([['foo\nbar', 'baz\nqux']])) t.snapshot(actual) }) test('formats maps with additional properties', t => { const map = new Map() map.set('foo', 'bar') map.baz = 'qux' const actual = format(map) t.snapshot(actual) }) test('formats sets', t => { const set = new Set() set.add('foo') set.add({ bar: 'baz' }) const actual = format(set) t.snapshot(actual) }) test('formats a multiline string inside sets', t => { const actual = format(new Set(['bar\nbaz'])) t.snapshot(actual) }) test('formats sets with additional properties', t => { const set = new Set() set.add('foo') set.bar = 'baz' const actual = format(set) t.snapshot(actual) }) test('formats funky objects that are lists and have an iterator', t => { const funky = { 0: 'first', 1: 'second', foo: 'bar', } Object.defineProperty(funky, 'length', { value: 2 }) Object.defineProperty(funky, Symbol.iterator, { * value () { yield 'baz' } }) const actual = format(funky) t.snapshot(actual) }) test('formats regular expressions', t => { const actual = format(/foo/gim) t.snapshot(actual) }) test('formats regular expressions with additional properties', t => { const actual = format(Object.assign(/foo/gim, { bar: 'baz' })) t.snapshot(actual) }) test('formats anonymous functions', t => { const actual = format(() => {}) t.snapshot(actual) }) test('formats named functions', t => { const actual = format(function foo () {}) t.snapshot(actual) }) test('formats functions with additional properties', t => { const actual = format(Object.assign(function foo () {}, { bar: 'baz' })) t.snapshot(actual) }) test('formats anonymous generator functions', t => { const actual = format(function * () {}) // eslint-disable-next-line max-len t.is(actual, '%function.stringTag.open%GeneratorFunction%function.stringTag.close% %object.openBracket#{%%object.closeBracket#}%') }) test('formats named generator functions', t => { const actual = format(function * foo () {}) // eslint-disable-next-line max-len t.is(actual, '%function.stringTag.open%GeneratorFunction%function.stringTag.close% %function.name.open%foo%function.name.close% %object.openBracket#{%%object.closeBracket#}%') }) test('formats generator functions with additional properties', t => { const actual = format(Object.assign(function * foo () {}, { bar: 'baz' })) // eslint-disable-next-line max-len t.is(actual, `%function.stringTag.open%GeneratorFunction%function.stringTag.close% %function.name.open%foo%function.name.close% %object.openBracket#{% bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,% %object.closeBracket#}%`) }) test('formats arguments', t => { (function (a, b, c) { const actual = format(arguments) t.snapshot(actual) })('foo', 'bar', 'baz') }) test('formats simple errors', t => { const actual = format(new TypeError('Test message')) t.snapshot(actual) }) test('formats simple errors with a modified name', t => { const err = new TypeError('Test message') err.name = 'FooError' const actual = format(err) t.snapshot(actual) }) test('formats errors with a name that does not include Error and does not match the constructor', t => { class Foo extends Error { // eslint-disable-line unicorn/custom-error-definition constructor (message) { super(message) this.name = 'Bar' // eslint-disable-line unicorn/custom-error-definition } } const actual = format(new Foo('Test message')) t.snapshot(actual) }) test('formats errors with additional properties', t => { const actual = format(Object.assign(new TypeError('Test message'), { foo: 'bar' })) t.snapshot(actual) }) test('formats promises', t => { const actual = format(Promise.resolve()) t.snapshot(actual) }) test('formats promises with additional properties', t => { const actual = format(Object.assign(Promise.resolve(), { foo: 'bar' })) t.snapshot(actual) }) test('formats pointers', t => { const obj = { foo: 'bar' } const actual = format({ baz: obj, qux: { quux: obj } }) t.snapshot(actual) }) test('formats circular references', t => { const obj = {} obj.circular = obj const actual = format(obj) t.snapshot(actual) }) { const plain = (t, value, tag) => { const actual = format(value) t.snapshot(actual) } plain.title = (_, __, tag) => `formats ${tag}` const withProperties = (t, value, tag) => { const actual = format(Object.assign(value, { foo: 'bar' })) t.snapshot(actual) } withProperties.title = (_, __, tag) => `formats ${tag} with additional properties` const buffer = Buffer.from('decafbad'.repeat(12), 'hex') const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) for (const [tag, value, valueForProps] of [ ['ArrayBuffer', arrayBuffer], ['Buffer @Uint8Array', buffer], ['DataView', new DataView(arrayBuffer), new DataView(arrayBuffer)], ['Float32Array', new Float32Array(arrayBuffer)], ['Float64Array', new Float64Array(arrayBuffer)], ['Int16Array', new Int16Array(arrayBuffer)], ['Int32Array', new Int32Array(arrayBuffer)], ['Int8Array', new Int8Array(arrayBuffer)], ['Uint16Array', new Uint16Array(arrayBuffer)], ['Uint32Array', new Uint32Array(arrayBuffer)], ['Uint8Array', new Uint8Array(arrayBuffer)], ['Uint8ClampedArray', new Uint8ClampedArray(arrayBuffer)], ]) { test(plain, value, tag) test(withProperties, valueForProps || value.slice(), tag) } } test('formats dates', t => { const actual1 = format(new Date('1969-07-20T20:17:40.012Z')) t.snapshot(actual1) const actual2 = format(new Date('🚀🌔🛬')) t.snapshot(actual2) }) test('formats dates with additional properties', t => { const actual = format(Object.assign(new Date('1969-07-20T20:17:40Z'), { foo: 'bar' })) t.snapshot(actual) }) test('shows non-Object tag if constructor name is different', t => { class Foo {} const actual1 = format(new Foo()) t.snapshot(actual1) class Bar extends Array {} const actual2 = format(new Bar()) t.snapshot(actual2) class Baz extends Date {} const actual3 = format(new Baz('1969-07-20T20:17:40Z')) t.snapshot(actual3) class Qux extends RegExp {} const actual4 = format(new Qux('foo')) t.snapshot(actual4) class Quux extends Int16Array {} const actual5 = format(new Quux()) t.snapshot(actual5) }) test('shows string tag if object has no constructor', t => { const obj = {} Object.defineProperty(obj, 'constructor', {}) t.snapshot(format(obj)) }) test('formats global', t => { t.snapshot(format(global)) }) test('format with given plugin', t => { const plugins = [ { name: 'CustomError', apiVersion: 1, register: props => { const { describe } = customErrorPlugin.factory(props) return function (value) { if (value.name === 'CustomError') { return describe } } }, }, ] const actual1 = _format(new customErrorPlugin.CustomError('plugin formatter', 'PLUGIN'), { plugins, theme }) t.snapshot(actual1) const actual2 = _format(new Error('error'), { plugins, theme }) t.snapshot(actual2) }) // See . test('format pointers hidden behind maxDepth', t => { const value = {} const descriptor = concordance.describe({ // `value` is encoded in the serialization of `a.b`. `c` is encoded as a // pointer to the encoded `value`. a: { b: value, }, c: value, }) const serialized = concordance.serialize(descriptor) t.notThrows(() => { // `maxDepth: 1` means that `a.b` is not normally deserialized, and so the // `c` pointer cannot be resolved, unless the resolution logic first // deserializes the descriptor in its entirety. concordance.formatDescriptor(concordance.deserialize(serialized), { maxDepth: 1 }) }) }) concordance-5.0.4/test/lodash-isequal-comparison.js000066400000000000000000000376161402315264500224150ustar00rootroot00000000000000/* Copyright JS Foundation and other contributors Based on Underscore.js, copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors This software consists of voluntary contributions made by many individuals. For exact contribution history, see the revision history available at https://github.com/lodash/lodash ==== Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. === Tests adopted from https://github.com/lodash/lodash/blob/3967c1e1197b726463246b47521a4099ab74cb35/test/test.js#L9477:L10291> */ /* eslint-disable unicorn/consistent-function-scoping */ const vm = require('vm') const test = require('ava') const { compare } = require('../lib/compare') const isEqual = (actual, expected) => compare(actual, expected).pass const realm = vm.runInNewContext('(function () { return this })()') const symbol1 = Symbol ? Symbol('a') : true const symbol2 = Symbol ? Symbol('b') : false test('compare primitives', t => { const pairs = [ [1, 1, true], [1, new Object(1), false], [1, '1', false], [1, 2, false], [-0, -0, true], [0, 0, true], [0, new Object(0), false], [new Object(0), new Object(0), true], [-0, 0, false], [0, '0', false], [0, null, false], // eslint-disable-line max-len [NaN, NaN, true], [NaN, new Object(NaN), false], [new Object(NaN), new Object(NaN), true], [NaN, 'a', false], [NaN, Infinity, false], // eslint-disable-line max-len ['a', 'a', true], ['a', new Object('a'), false], [new Object('a'), new Object('a'), true], ['a', 'b', false], ['a', ['a'], false], // eslint-disable-line max-len [true, true, true], [true, new Object(true), false], [new Object(true), new Object(true), true], [true, 1, false], [true, 'a', false], // eslint-disable-line max-len [false, false, true], [false, new Object(false), false], [new Object(false), new Object(false), true], [false, 0, false], [false, '', false], // eslint-disable-line max-len [symbol1, symbol1, true], [symbol1, new Object(symbol1), false], [new Object(symbol1), new Object(symbol1), true], [symbol1, symbol2, false], // eslint-disable-line max-len [null, null, true], [null, undefined, false], [null, {}, false], [null, '', false], [undefined, undefined, true], [undefined, null, false], [undefined, '', false], ] for (const [lhs, rhs, result] of pairs) { t.is(isEqual(lhs, rhs), result) } }) test('compare arrays', t => { let array1 = [true, null, 1, 'a', undefined] let array2 = [true, null, 1, 'a', undefined] t.true(isEqual(array1, array2)) array1 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { e: 1 }] array2 = [[1, 2, 3], new Date(2012, 4, 23), /x/, { e: 1 }] t.true(isEqual(array1, array2)) array1 = [1] array1[2] = 3 array2 = [1] array2[1] = undefined array2[2] = 3 t.true(isEqual(array1, array2)) array1 = [new Object(1), false, new Object('a'), /x/, new Date(2012, 4, 23), ['a', 'b', [new Object('c')]], { a: 1 }] array2 = [1, new Object(false), 'a', /x/, new Date(2012, 4, 23), ['a', new Object('b'), ['c']], { a: 1 }] t.false(isEqual(array1, array2)) array1 = [1, 2, 3] array2 = [3, 2, 1] t.false(isEqual(array1, array2)) array1 = [1, 2] array2 = [1, 2, 3] t.false(isEqual(array1, array2)) }) test('treat arrays with identical values but different non-index properties as unequal', t => { let array1 = [1, 2, 3] let array2 = [1, 2, 3] array1.every = array1.filter = array1.forEach = array1.indexOf = array1.lastIndexOf = array1.map = array1.some = array1.reduce = array1.reduceRight = null array2.concat = array2.join = array2.pop = array2.reverse = array2.shift = array2.slice = array2.sort = array2.splice = array2.unshift = null t.false(isEqual(array1, array2)) array1 = [1, 2, 3] array1.a = 1 array2 = [1, 2, 3] array2.b = 1 t.false(isEqual(array1, array2)) array1 = /c/.exec('abcde') array2 = ['c'] t.false(isEqual(array1, array2)) }) test('compare sparse arrays', t => { const array = new Array(1) t.true(isEqual(array, new Array(1))) t.true(isEqual(array, [undefined])) t.false(isEqual(array, new Array(2))) }) test('compare plain objects', t => { let object1 = { a: true, b: null, c: 1, d: 'a', e: undefined } let object2 = { a: true, b: null, c: 1, d: 'a', e: undefined } t.true(isEqual(object1, object2)) object1 = { a: [1, 2, 3], b: new Date(2012, 4, 23), c: /x/, d: { e: 1 } } object2 = { a: [1, 2, 3], b: new Date(2012, 4, 23), c: /x/, d: { e: 1 } } t.true(isEqual(object1, object2)) object1 = { a: 1, b: 2, c: 3 } object2 = { a: 3, b: 2, c: 1 } t.false(isEqual(object1, object2)) object1 = { a: 1, b: 2, c: 3 } object2 = { d: 1, e: 2, f: 3 } t.false(isEqual(object1, object2)) object1 = { a: 1, b: 2 } object2 = { a: 1, b: 2, c: 3 } t.false(isEqual(object1, object2)) }) test('compare objects regardless of key order', t => { const object1 = { a: 1, b: 2, c: 3 } const object2 = { c: 3, a: 1, b: 2 } t.true(isEqual(object1, object2)) }) test('compare nested objects', t => { const noop = () => {} const object1 = { a: [1, 2, 3], b: true, c: 1, d: 'a', e: { f: ['a', 'b', 'c'], g: false, h: new Date(2012, 4, 23), i: noop, j: 'a', }, } const object2 = { a: [1, 2, 3], b: true, c: 1, d: 'a', e: { f: ['a', 'b', 'c'], g: false, h: new Date(2012, 4, 23), i: noop, j: 'a', }, } t.true(isEqual(object1, object2)) }) test('compare object instances', t => { function Foo () { this.a = 1 } Foo.prototype.a = 1 function Bar () { this.a = 1 } Bar.prototype.a = 2 t.true(isEqual(new Foo(), new Foo())) t.false(isEqual(new Foo(), new Bar())) t.false(isEqual({ a: 1 }, new Foo())) t.false(isEqual({ a: 2 }, new Bar())) }) test('compare objects with constructor properties', t => { t.true(isEqual({ constructor: 1 }, { constructor: 1 })) t.false(isEqual({ constructor: 1 }, { constructor: '1' })) t.true(isEqual({ constructor: [1] }, { constructor: [1] })) t.false(isEqual({ constructor: [1] }, { constructor: ['1'] })) t.false(isEqual({ constructor: Object }, {})) }) test('compare arrays with circular references', t => { let array1 = [] let array2 = [] array1.push(array1) array2.push(array2) t.true(isEqual(array1, array2)) array1.push('b') array2.push('b') t.true(isEqual(array1, array2)) array1.push('c') array2.push('d') t.false(isEqual(array1, array2)) array1 = ['a', 'b', 'c'] array1[1] = array1 array2 = ['a', ['a', 'b', 'c'], 'c'] t.false(isEqual(array1, array2)) }) test('have transitive equivalence for circular references of arrays', t => { const array1 = [] const array2 = [array1] const array3 = [array2] array1[0] = array1 t.true(isEqual(array1, array2)) t.true(isEqual(array2, array3)) // Concordance detects a different circular reference in array1 before it does // in array3, making them unequal. t.false(isEqual(array1, array3)) }) test('compare objects with circular references', t => { let object1 = {} let object2 = {} object1.a = object1 object2.a = object2 t.true(isEqual(object1, object2)) object1.b = 0 object2.b = 0 t.true(isEqual(object1, object2)) object1.c = new Object(1) object2.c = new Object(2) t.false(isEqual(object1, object2)) object1 = { a: 1, b: 2, c: 3 } object1.b = object1 object2 = { a: 1, b: { a: 1, b: 2, c: 3 }, c: 3 } t.false(isEqual(object1, object2)) }) test('have transitive equivalence for circular references of objects', t => { const object1 = {} const object2 = { a: object1 } const object3 = { a: object2 } object1.a = object1 t.true(isEqual(object1, object2)) t.true(isEqual(object2, object3)) // Concordance detects a different circular reference in object1 before it // does in object3, making them unequal. t.false(isEqual(object1, object3)) }) test('compare objects with multiple circular references', t => { const array1 = [{}] const array2 = [{}]; (array1[0].a = array1).push(array1); (array2[0].a = array2).push(array2) t.true(isEqual(array1, array2)) array1[0].b = 0 array2[0].b = 0 t.true(isEqual(array1, array2)) array1[0].c = new Object(1) array2[0].c = new Object(2) t.false(isEqual(array1, array2)) }) test('compare objects with complex circular references', t => { const object1 = { foo: { b: { c: { d: {} } } }, bar: { a: 2 }, } const object2 = { foo: { b: { c: { d: {} } } }, bar: { a: 2 }, } object1.foo.b.c.d = object1 object1.bar.b = object1.foo.b object2.foo.b.c.d = object2 object2.bar.b = object2.foo.b t.true(isEqual(object1, object2)) }) test('compare objects with shared property values', t => { const object1 = { a: [1, 2], } const object2 = { a: [1, 2], b: [1, 2], } object1.b = object1.a t.true(isEqual(object1, object2)) }) test('treat objects created by `Object.create(null)` like plain objects', t => { function Foo () { this.a = 1 } Foo.prototype.constructor = null const object1 = Object.create(null) object1.a = 1 const object2 = { a: 1 } t.true(isEqual(object1, object2)) t.false(isEqual(new Foo(), object2)) }) test('avoid common type coercions', t => { t.false(isEqual(true, new Object(false))) t.false(isEqual(new Object(false), new Object(0))) t.false(isEqual(false, new Object(''))) t.false(isEqual(new Object(36), new Object('36'))) t.false(isEqual(0, '')) t.false(isEqual(1, true)) t.false(isEqual(1337756400000, new Date(2012, 4, 23))) t.false(isEqual('36', 36)) t.false(isEqual(36, '36')) }) test('compare `arguments` objects', t => { const args1 = (function () { return arguments }()) const args2 = (function () { return arguments }()) const args3 = (function () { return arguments }(1, 2)) t.true(isEqual(args1, args2)) t.false(isEqual(args1, args3)) }) test('actual `arguments` objects may be compared to expected arrays', t => { const array = [1, 2, 3] const args = (function () { return arguments })(1, 2, 3) t.true(isEqual(args, array)) t.false(isEqual(array, args)) }) test('compare array buffers', t => { const buffer = new Int8Array([-1]).buffer t.true(isEqual(buffer, new Uint8Array([255]).buffer)) t.false(isEqual(buffer, new ArrayBuffer(1))) }) test('compare array views', t => { const arrayViews = [ 'Float32Array', 'Float64Array', 'Int8Array', 'Int16Array', 'Int32Array', 'Uint8Array', 'Uint8ClampedArray', 'Uint16Array', 'Uint32Array', 'DataView', ] const namespaces = [global, realm] for (const ns of namespaces) { arrayViews.forEach((type, viewIndex) => { const otherType = arrayViews[(viewIndex + 1) % arrayViews.length] const CtorA = ns[type] const CtorB = ns[otherType] const bufferA = new ns.ArrayBuffer(8) const bufferB = new ns.ArrayBuffer(8) const bufferC = new ns.ArrayBuffer(16) t.true(isEqual(new CtorA(bufferA), new CtorA(bufferA))) t.false(isEqual(new CtorA(bufferA), new CtorB(bufferB))) t.false(isEqual(new CtorB(bufferB), new CtorB(bufferC))) }) } }) test('compare buffers', t => { const buffer = Buffer.from([1]) t.true(isEqual(buffer, Buffer.from([1]))) t.false(isEqual(buffer, Buffer.from([2]))) t.false(isEqual(buffer, new Uint8Array([1]))) }) test('compare date objects', t => { const date = new Date(2012, 4, 23) t.true(isEqual(date, new Date(2012, 4, 23))) t.true(isEqual(new Date('a'), new Date('b'))) t.false(isEqual(date, new Date(2013, 3, 25))) t.false(isEqual(date, { getTime () { return +date } })) }) test('compare error objects', t => { const errorTypes = [ 'Error', 'EvalError', 'RangeError', 'ReferenceError', 'SyntaxError', 'TypeError', 'URIError', ] errorTypes.forEach((type, index) => { const otherType = errorTypes[++index % errorTypes.length] const CtorA = global[type] const CtorB = global[otherType] t.true(isEqual(new CtorA('a'), new CtorA('a'))) t.false(isEqual(new CtorA('a'), new CtorB('a'))) t.false(isEqual(new CtorB('a'), new CtorB('b'))) }) }) test('compare functions', t => { function a () { return 1 + 2 } function b () { return 1 + 2 } t.true(isEqual(a, a)) t.false(isEqual(a, b)) }) test('compare maps', t => { const map1 = new Map() for (const map2 of [new Map(), new realm.Map()]) { map1.set('a', 1) map2.set('b', 2) t.false(isEqual(map1, map2)) map1.set('b', 2) map2.set('a', 1) t.false(isEqual(map1, map2)) map1.delete('a') map1.set('a', 1) t.true(isEqual(map1, map2)) map2.delete('a') t.false(isEqual(map1, map2)) map1.clear() map2.clear() } }) test('compare maps with circular references', t => { const map1 = new Map() const map2 = new Map() map1.set('a', map1) map2.set('a', map2) t.true(isEqual(map1, map2)) map1.set('b', 1) map2.set('b', 2) t.false(isEqual(map1, map2)) }) test('compare promises by reference', t => { const promise1 = Promise.resolve(1) for (const promise2 of [Promise.resolve(1), realm.Promise.resolve(1)]) { t.false(isEqual(promise1, promise2)) t.true(isEqual(promise1, promise1)) } }) test('compare regexes', t => { t.true(isEqual(/x/gim, /x/gim)) t.true(isEqual(/x/gim, /x/gim)) t.false(isEqual(/x/gi, /x/g)) t.false(isEqual(/x/, /y/)) t.false(isEqual(/x/g, { global: true, ignoreCase: false, multiline: false, source: 'x' })) }) test('compare sets', t => { const set1 = new Set() for (const set2 of [new Set(), new realm.Set()]) { set1.add(1) set2.add(2) t.false(isEqual(set1, set2)) set1.add(2) set2.add(1) t.false(isEqual(set1, set2)) set1.delete(1) set1.add(1) t.true(isEqual(set1, set2)) set2.delete(1) t.false(isEqual(set1, set2)) set1.clear() set2.clear() } }) test('compare sets with circular references', t => { const set1 = new Set() const set2 = new Set() set1.add(set1) set2.add(set2) t.true(isEqual(set1, set2)) set1.add(1) set2.add(2) t.false(isEqual(set1, set2)) }) test('compare symbol properties', t => { const object1 = { a: 1 } const object2 = { a: 1 } object1[symbol1] = { a: { b: 2 } } object2[symbol1] = { a: { b: 2 } } Object.defineProperty(object2, symbol2, { configurable: true, enumerable: false, writable: true, value: 2, }) t.true(isEqual(object1, object2)) object2[symbol1] = { a: 1 } t.false(isEqual(object1, object2)) delete object2[symbol1] object2[Symbol('a')] = { a: { b: 2 } } t.false(isEqual(object1, object2)) }) test('return `true` for like-objects from different realms', t => { const array = new realm.Array() array.push(1) t.true(isEqual([1], array)) t.false(isEqual([2], array)) const object = new realm.Object() object.a = 1 t.true(isEqual({ a: 1 }, object)) t.false(isEqual({ a: 2 }, object)) }) test('return `false` for objects with custom `toString` methods', t => { let primitive const object = { 'toString' () { return primitive } } for (const value of [true, null, 1, 'a', undefined]) { primitive = value t.false(isEqual(object, value)) } }) concordance-5.0.4/test/max-depth.js000066400000000000000000000027421402315264500172110ustar00rootroot00000000000000const test = require('ava') const { format, diff } = require('..') const deep = { one: { two: { three: { four: { five: 'fin', }, }, arr: [], arr2: [[]], date: new Date('1969-07-20T20:17:40Z'), date2: Object.assign(new Date('1969-07-20T20:17:40Z'), { foo: 'bar' }), error: new Error(), fn () {}, fn2: Object.assign(() => {}, { foo: 'bar' }), map: new Map(), map2: new Map([['foo', 'bar']]), obj: {}, regexp: /foo/, regexp2: Object.assign(/foo/, { foo: 'bar' }), set: new Set(), set2: new Set(['foo']), }, arr: [{ three: { four: {} } }], map: new Map([[{ three: { four: {} } }, { three: { four: {} } }]]), set: new Set([{ three: { four: {} } }]), }, } test('format() respects maxDepth option', t => { t.snapshot(format(deep, { maxDepth: 3 })) }) test('diff() respects maxDepth option for equal parts', t => { t.snapshot(diff(Object.assign({ unequal: 1 }, deep), Object.assign({ unequal: 2 }, deep), { maxDepth: 3 })) t.snapshot(diff({ one: { two: { three: 'baz', three2: ['quux'] } } }, { one: { two: { three: 'qux', three2: ['quux'] } } }, { maxDepth: 1 })) // eslint-disable-line max-len }) test('properties with increased indentation respect the maxDepth when formatted', t => { t.snapshot(format({ foo: { bar: {}, }, }, { maxDepth: 1, theme: { property: { increaseValueIndent: true, }, }, })) }) concordance-5.0.4/test/pluginRegistry.js000066400000000000000000000040201402315264500203400ustar00rootroot00000000000000const test = require('ava') const proxyquire = require('proxyquire') const pluginRegistry = proxyquire('../lib/pluginRegistry', { '../package.json': { version: '1.0.0' } }) test('registration should fail when plugin name invalid', t => { t.throws(() => pluginRegistry.add({ name: { for: 'complex' } }), { name: 'PluginTypeError' }) }) test('registration should fail when api version unsupported', t => { t.throws(() => pluginRegistry.add({ name: 'complex', apiVersion: 2 }), { name: 'UnsupportedApiError' }) }) test('registration should fail when installed concordance version below minimal version required by plugin', t => { t.throws(() => pluginRegistry.add({ name: 'complex', apiVersion: 1, minimalConcordanceVersion: '2.0.0' }), { name: 'UnsupportedError' }) }) test('registration should fail when descriptor id used twice', t => { const plugin = { name: 'complex', apiVersion: 1, register: concordance => { concordance.addDescriptor(1, 'complexCustomValue') concordance.addDescriptor(1, 'complexCustomValue') }, } t.throws(() => pluginRegistry.add(plugin), { name: 'DuplicateDescriptorIdError' }) }) test('registration should fail when descriptor tag used twice', t => { const plugin = { name: 'complex', apiVersion: 1, register: concordance => { concordance.addDescriptor(1, 'complexCustomValue') concordance.addDescriptor(2, 'complexCustomValue') }, } t.throws(() => pluginRegistry.add(plugin), { name: 'DuplicateDescriptorTagError' }) }) test('registration should be successful', t => { t.plan(2) const tryDescribeValue = () => { } // eslint-disable-line unicorn/consistent-function-scoping const plugin = { name: 'complex', apiVersion: 1, serializerVersion: 1, register: concordance => { t.pass() concordance.addDescriptor(1, 'complexCustomObject', 'objectDeserializer') concordance.addDescriptor(2, 'complexCustomArray', 'arrayDeserializer') return tryDescribeValue }, } t.snapshot(pluginRegistry.add(plugin)) }) concordance-5.0.4/test/serialization-fixtures.js000066400000000000000000000004631402315264500220440ustar00rootroot00000000000000const test = require('ava') const { compareDescriptors, deserialize, describe } = require('..') const { serialization, tree } = require('./fixtures/pointerSerialization') test('pointer serialization equals the same tree', t => { t.true(compareDescriptors(deserialize(serialization), describe(tree))) }) concordance-5.0.4/test/serialize-and-encode.js000066400000000000000000000214611402315264500213030ustar00rootroot00000000000000const test = require('ava') const { compareDescriptors, describe, diffDescriptors, formatDescriptor } = require('..') const { deserialize, serialize } = require('../lib/serialize') const customErrorPlugin = require('./fixtures/customErrorPlugin') test('serializes a descriptor into a buffer', t => { const result = serialize(describe({ foo: 'bar' })) t.true(Buffer.isBuffer(result)) }) const plugins = [ { name: 'CustomError', apiVersion: 1, serializerVersion: 1, register: props => { const custom = customErrorPlugin.factory(props) props.addDescriptor(1, custom.tag, custom.deserialize) return function (value) { if (value.name === 'CustomError') { return custom.describe } } }, }, ] const useDeserialized = (t, value, options) => { const original = describe(value, options) const buffer = serialize(original) const deserialized = deserialize(buffer, options) t.true( compareDescriptors(deserialized, original), 'the deserialized descriptor equals the original') t.is( formatDescriptor(deserialized), formatDescriptor(original), 'the deserialized descriptor is formatted like the original') const redeserialized = deserialize(serialize(deserialized), options) t.true( compareDescriptors(redeserialized, original), 'after serializing and deserializing it again, the deserialized descriptor equals the original') t.is( formatDescriptor(redeserialized), formatDescriptor(original), 'after serializing and deserializing it again, the deserialized descriptor is formatted like the original') t.true( compareDescriptors(redeserialized, deserialized), 'deserialized descriptors equal each other') } useDeserialized.title = (desc, value) => `deserialized ${desc || String(value)} is equivalent to the original` // Primitives test(useDeserialized, true) test(useDeserialized, false) test(useDeserialized, null) test(useDeserialized, 0) test('-0', useDeserialized, -0) test(useDeserialized, NaN) test(useDeserialized, Infinity) test(useDeserialized, -Infinity) test('-0x80 (int8)', useDeserialized, -0x80) test('0x7F (int8)', useDeserialized, 0x7F) test('-0x8000 (int16)', useDeserialized, -0x8000) test('0x7FFF (int16)', useDeserialized, 0x7FFF) test('-0x800000 (int24)', useDeserialized, -0x800000) test('0x7FFFFF (int24)', useDeserialized, 0x7FFFFF) test('-0x80000000 (int32)', useDeserialized, -0x80000000) test('0x7FFFFFFF (int32)', useDeserialized, 0x7FFFFFFF) test('-0x8000000000 (int40)', useDeserialized, -0x8000000000) test('0x7FFFFFFFFF (int40)', useDeserialized, 0x7FFFFFFFFF) test('-0x800000000000 (int48)', useDeserialized, -0x800000000000) test('0x7FFFFFFFFFFF (int48)', useDeserialized, 0x7FFFFFFFFFFF) test('-0x800000000001 (larger than int48)', useDeserialized, -0x80000000001) test('0x800000000000 (larger than int48)', useDeserialized, 0x800000000000) test('Number.MIN_VALUE', useDeserialized, Number.MIN_VALUE) test('Number.MAX_VALUE', useDeserialized, Number.MAX_VALUE) test('0.1 + 0.2 (float)', useDeserialized, 0.1 + 0.2) test('\'foo\' (ascii string)', useDeserialized, 'foo') test('\'🚀\' (unicode string)', useDeserialized, '🚀') test('Symbol.iterator', useDeserialized, Symbol.iterator) test('Symbol.for(\'foo\')', useDeserialized, Symbol.for('foo')) test('Symbol(\'foo\')', useDeserialized, Symbol('foo')) test(useDeserialized, undefined) if (typeof BigInt === 'function') { test('42n', useDeserialized, BigInt(42)) // eslint-disable-line no-undef } // Objects test('object with primitive property', useDeserialized, { foo: 'bar' }) test('object with complex property', useDeserialized, { foo: {} }) test('object with well known symbol key', useDeserialized, { [Symbol.unscopables]: 'bar' }) test('object with registered symbol key', useDeserialized, { [Symbol.for('foo')]: 'bar' }) test('object with arbitrary symbol key', useDeserialized, { [Symbol('foo')]: 'bar' }) test('object with length property', useDeserialized, { length: 12345678 }) test('object with negative length property', useDeserialized, { length: -12345678 }) test('object with NaN length property', useDeserialized, { length: NaN }) test('object with infinite length property', useDeserialized, { length: Infinity }) test('object with fractional length property', useDeserialized, { length: 1.5 }) test('symbol properties are reordered despite serialization', t => { const s1 = Symbol('s1') const s2 = Symbol('s2') const original = describe({ [s1]: 1, [s2]: 2 }) const expected = describe({ [s2]: 2, [s1]: 1 }) t.true(compareDescriptors(deserialize(serialize(original)), expected)) t.snapshot(diffDescriptors(deserialize(serialize(original)), expected)) t.true(compareDescriptors(deserialize(serialize(original)), deserialize(serialize(expected)))) t.snapshot(diffDescriptors(deserialize(serialize(original)), deserialize(serialize(expected)))) }) // Arrays test('array with primitive item', useDeserialized, ['bar']) test('array with complex item', useDeserialized, [{}]) // Iterators test('iterator with primitive item', useDeserialized, Object.create({}, { [Symbol.iterator]: { enumerable: false, * value () { return 'bar' }, }, })) test('iterator with complex item', useDeserialized, Object.create({}, { [Symbol.iterator]: { enumerable: false, * value () { return {} }, }, })) // Maps test('map with primitive key and value', useDeserialized, new Map([['foo', 'bar']])) test('map with complex key and primitive value', useDeserialized, new Map([[{}, 'bar']])) test('map with complex key and value', useDeserialized, new Map([[{}, {}]])) test('map with primitive key and complex value', useDeserialized, new Map([['foo', {}]])) // Sets test('set with primitive value', useDeserialized, new Set(['foo'])) test('set with complex value', useDeserialized, new Set([{}])) // Pointers { const obj = {} obj.self = obj test('object with pointer to itself', useDeserialized, obj) } // Other complex values test('arguments', useDeserialized, (function () { return arguments })('foo', {})) { const buffer = Buffer.from('decafbad'.repeat(12), 'hex') const arrayBuffer = buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) for (const [tag, value] of [ ['ArrayBuffer', arrayBuffer], ['Buffer @Uint8Array', buffer], ['DataView', new DataView(arrayBuffer)], ['Float32Array', new Float32Array(arrayBuffer)], ['Float64Array', new Float64Array(arrayBuffer)], ['Int16Array', new Int16Array(arrayBuffer)], ['Int32Array', new Int32Array(arrayBuffer)], ['Int8Array', new Int8Array(arrayBuffer)], ['Uint16Array', new Uint16Array(arrayBuffer)], ['Uint32Array', new Uint32Array(arrayBuffer)], ['Uint8Array', new Uint8Array(arrayBuffer)], ['Uint8ClampedArray', new Uint8ClampedArray(arrayBuffer)], ]) { test(tag, useDeserialized, value) } } test('date', useDeserialized, new Date('1969-07-20T20:17:40Z')) test('error', useDeserialized, new Error('foo')) test('function', useDeserialized, function foo () {}) test('compare functions with different names', t => { function a () { return 1 + 2 } // eslint-disable-line unicorn/consistent-function-scoping function b () { return 1 + 2 } // eslint-disable-line unicorn/consistent-function-scoping const original = describe(a) const deserialized = deserialize(serialize(original)) t.false(compareDescriptors(deserialized, describe(b))) t.not(formatDescriptor(deserialized), formatDescriptor(describe(b))) }) test('compare deserialized function with object', t => { function a () { return 1 + 2 } // eslint-disable-line unicorn/consistent-function-scoping const b = { bar: 'b' } const original = describe(a) const deserialized = deserialize(serialize(original)) t.false(compareDescriptors(deserialized, describe(b))) t.not(formatDescriptor(deserialized), formatDescriptor(describe(b))) }) test('generator function', useDeserialized, function * foo () {}) test('global', useDeserialized, global) test('promise', useDeserialized, Promise.resolve()) test('regexp', useDeserialized, /foo/gi) test('plugin', useDeserialized, new customErrorPlugin.CustomError('custom error', 'PLUGIN', 1), { plugins }) test('should fail when plugin for deserialization missing', t => { const deserializationPlugins = [ { name: 'CustomError_v2', apiVersion: 1, serializerVersion: 2, register: props => { const custom = customErrorPlugin.factory(props) props.addDescriptor(1, Symbol('CustomError_v2'), custom.deserialize) return function (value) { if (value.name === 'CustomError') { return custom.describe } } }, }, ] t.throws(() => { const serialized = serialize(describe(new customErrorPlugin.CustomError('custom error', 'PLUGIN', 1), { plugins })) deserialize(serialized, { plugins: deserializationPlugins }) }, { name: 'MissingPluginError' }) }) concordance-5.0.4/test/snapshots/000077500000000000000000000000001402315264500170015ustar00rootroot00000000000000concordance-5.0.4/test/snapshots/diff.js.md000066400000000000000000002503601402315264500206540ustar00rootroot00000000000000# Snapshot report for `test/diff.js` The actual snapshot is saved in `diff.js.snap`. Generated by [AVA](https://avajs.dev). ## diffs primitives: null versus undefined > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%undefined.open%undefined%undefined.close%` ## diffs primitives: null versus false > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%boolean.open%false%boolean.close%` ## diffs primitives: null versus true > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%boolean.open%true%boolean.close%` ## diffs primitives: null versus > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%string.line.open#'%%string.open%%string.close%%string.line.close#'%` ## diffs primitives: null versus 42 > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%number.open%42%number.close%` ## diffs primitives: null versus Symbol() > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%symbol.open%Symbol()%symbol.close%` ## diffs primitives: Symbol() versus Symbol() > Snapshot 1 `%diffGutters.actual#- %%symbol.open%Symbol()%symbol.close%␊ %diffGutters.expected#+ %%symbol.open%Symbol()%symbol.close%` ## diffs primitives: null versus [object Object] > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%object.openBracket#{%%object.closeBracket#}%` ## diffs primitives: null versus 42n > Snapshot 1 `%diffGutters.actual#- %%null.open%null%null.close%␊ %diffGutters.expected#+ %%bigInt.open%42n%bigInt.close%` ## diffs primitives: true versus false > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Boolean%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %boolean.open%true%boolean.close%␊ %diffGutters.expected#+ % %boolean.open%false%boolean.close%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs primitives: -42 versus 42 > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %number.open%-42%number.close%␊ %diffGutters.expected#+ % %number.open%42%number.close%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs primitives: foo versus bar > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%String%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %string.line.open#'%%string.diff.deleteLine.open%foo%string.diff.deleteLine.close%%string.line.close#'%␊ %diffGutters.expected#+ % %string.line.open#'%%string.diff.insertLine.open%bar%string.diff.insertLine.close%%string.line.close#'%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs primitives: -42n versus 42n > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%BigInt%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %bigInt.open%-42n%bigInt.close%␊ %diffGutters.expected#+ % %bigInt.open%42n%bigInt.close%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs boxed primitives with extra properties > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%String%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%␊ %diffGutters.expected#+ % %stats.separator#---%␊ %diffGutters.expected#+ % bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs single line strings > Snapshot 1 `%diffGutters.actual#- %%string.line.open#'%%string.diff.deleteLine.open%foo%string.diff.deleteLine.close%%string.line.close#'%␊ %diffGutters.expected#+ %%string.line.open#'%%string.diff.insertLine.open%bar%string.diff.insertLine.close%%string.line.close#'%` > Snapshot 2 `%diffGutters.actual#- %%string.line.open#'%%string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%r%string.diff.delete.close%%string.line.close#'%␊ %diffGutters.expected#+ %%string.line.open#'%%string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%z%string.diff.insert.close%%string.line.close#'%` ## diffs multiline strings > Snapshot 1 `%diffGutters.actual#- %%string.multiline.start#`%%string.diff.deleteLine.open%foo%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ %%string.multiline.start#`%%string.diff.insertLine.open%baz%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# %%string.diff.equal.open%bar%string.diff.equal.close%%string.multiline.end#`%` > Snapshot 2 `%diffGutters.padding# %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%r%string.diff.delete.close%%string.multiline.end#`%␊ %diffGutters.expected#+ %%string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%z%string.diff.insert.close%%string.multiline.end#`%` > Snapshot 3 `%diffGutters.padding# %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.deleteLine.open%bar␊%string.diff.deleteLine.close%␊ %diffGutters.padding# %%string.diff.equal.open%baz%string.diff.equal.close%%string.multiline.end#`%` > Snapshot 4 `%diffGutters.padding# %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ %%string.diff.insertLine.open%bar␊%string.diff.insertLine.close%␊ %diffGutters.padding# %%string.diff.equal.open%baz%string.diff.equal.close%%string.multiline.end#`%` > Snapshot 5 `%diffGutters.padding# %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.multiline.end#`%␊ %diffGutters.expected#+ %%string.diff.insert.open%baz%string.diff.insert.close%%string.multiline.end#`%` > Snapshot 6 `%diffGutters.padding# %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.delete.open%baz%string.diff.delete.close%%string.multiline.end#`%␊ %diffGutters.expected#+ %%string.multiline.end#`%` > Snapshot 7 `%diffGutters.padding# %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.deleteLine.open%bar␊%string.diff.deleteLine.close%␊ %diffGutters.actual#- %%string.diff.delete.open%baz%string.diff.delete.close%%string.multiline.end#`%␊ %diffGutters.expected#+ %%string.multiline.end#`%` > Snapshot 8 `%diffGutters.actual#- %%string.multiline.start#`%%string.diff.delete.open%fo%string.diff.delete.close%%string.diff.equal.open%o%string.diff.equal.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.delete.open%b%string.diff.delete.close%%string.diff.equal.open%a%string.diff.equal.close%%string.diff.delete.open%r%string.diff.delete.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ %%string.multiline.start#`%%string.diff.insert.open%c%string.diff.insert.close%%string.diff.equal.open%o%string.diff.equal.close%%string.diff.insert.open%rge%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ %%string.diff.insert.open%gr%string.diff.insert.close%%string.diff.equal.open%a%string.diff.equal.close%%string.diff.insert.open%ult%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.padding# %%string.diff.equal.open%baz%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.deleteLine.open%qux%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ %%string.diff.insertLine.open%garply%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# %%string.diff.equal.open%quux%string.diff.equal.close%%string.multiline.end#`%` > Snapshot 9 `%diffGutters.padding# %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.padding# %%string.diff.equal.open%bar%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.deleteLine.open%corge␊%string.diff.deleteLine.close%␊ %diffGutters.padding# %%string.diff.equal.open%baz%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ %%string.diff.insertLine.open%grault␊%string.diff.insertLine.close%␊ %diffGutters.padding# %%string.diff.equal.open%qux%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- %%string.diff.equal.open%quux%string.diff.equal.close%%string.diff.delete.open%␊%string.diff.delete.close%␊ %diffGutters.actual#- %%string.diff.deleteLine.open%%string.diff.deleteLine.close%%string.multiline.end#`%␊ %diffGutters.expected#+ %%string.diff.equal.open%quux%string.diff.equal.close%%string.multiline.end#`%` ## diffs diverging complex types > Snapshot 1 `%diffGutters.actual#- %%object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- %%object.closeBracket#}%␊ %diffGutters.expected#+ %%list.openBracket#[%␊ %diffGutters.expected#+ % %string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%item.after#,%␊ %diffGutters.expected#+ %%list.closeBracket#]%` ## diffs arrays > Snapshot 1 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%2%number.close%%item.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%4%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 2 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 3 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.actual#- % %object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.expected#+ % %object.ctor.open%Bar%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % bar%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 4 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.actual#- % %object.ctor.open%Buffer%object.ctor.close% %object.secondaryStringTag.open#@%Uint8Array%object.secondaryStringTag.close% %list.openBracket#[%%list.closeBracket#]%%item.after#,%␊ %diffGutters.expected#+ % %object.ctor.open%Uint8Array%object.ctor.close% %list.openBracket#[%%list.closeBracket#]%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 5 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## diffs arguments > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%2%number.close%%item.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%4%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.actual#- % %object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.expected#+ % %object.ctor.open%Bar%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % bar%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.actual#- % %object.ctor.open%Buffer%object.ctor.close% %object.secondaryStringTag.open#@%Uint8Array%object.secondaryStringTag.close% %list.openBracket#[%%list.closeBracket#]%%item.after#,%␊ %diffGutters.expected#+ % %object.ctor.open%Uint8Array%object.ctor.close% %list.openBracket#[%%list.closeBracket#]%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 5 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## diffs sets > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%2%number.close%%item.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%4%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.expected#+ % %object.ctor.open%Bar%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % bar%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %object.ctor.open%Buffer%object.ctor.close% %object.secondaryStringTag.open#@%Uint8Array%object.secondaryStringTag.close% %list.openBracket#[%%list.closeBracket#]%%item.after#,%␊ %diffGutters.expected#+ % %object.ctor.open%Uint8Array%object.ctor.close% %list.openBracket#[%%list.closeBracket#]%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 5 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects extraneous array items > Snapshot 1 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 2 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%%object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 3 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 4 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## detects extraneous arguments items > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%%object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## detects extraneous set items > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%%object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects missing array items > Snapshot 1 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 2 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%%object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 3 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 4 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## detects missing arguments items > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%%object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## detects missing set items > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%%object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%item.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects extraneous name properties > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % a%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.actual#- % b%property.separator#: %%number.open%2%number.close%%property.after#,%␊ %diffGutters.padding# % c%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % a%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.actual#- % b%property.separator#: %%object.openBracket#{%%object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# % c%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects missing name properties > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % a%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.expected#+ % b%property.separator#: %%number.open%2%number.close%%property.after#,%␊ %diffGutters.padding# % c%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % a%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.expected#+ % b%property.separator#: %%object.openBracket#{%%object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# % c%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects extraneous symbol properties > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.actual#- % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%2%number.close%%property.after#,%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s3)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.actual#- % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%object.openBracket#{%%object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s3)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects missing symbol properties > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s3)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.expected#+ % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%2%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%1%number.close%%property.after#,%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s3)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%number.open%3%number.close%%property.after#,%␊ %diffGutters.expected#+ % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%object.openBracket#{%%object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs maps > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.actual#- % %number.open%2%number.close%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%4%number.close%%mapEntry.separator# => %%number.open%4%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%4%number.close%%mapEntry.separator# => %%number.open%4%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%4%number.close%%mapEntry.separator# => %%number.open%4%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects extraneous map entries > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.actual#- % %number.open%3%number.close%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%%object.closeBracket#}%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.separator# => %%number.open%4%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%3%number.close%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.separator# => %%number.open%4%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%3%number.close%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## detects missing map entries > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.expected#+ % %number.open%3%number.close%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%%object.closeBracket#}%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%2%number.close%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.separator# => %%number.open%4%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%3%number.close%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %number.open%1%number.close%%mapEntry.separator# => %%number.open%1%number.close%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.separator# => %%number.open%4%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % %property.keyBracket.open#[%%symbol.open%Symbol(s2)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.separator# => %%number.open%2%number.close%%mapEntry.after#,%␊ %diffGutters.padding# % %number.open%3%number.close%%mapEntry.separator# => %%number.open%3%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs maps with extra properties > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %stats.separator#---%␊ %diffGutters.padding# % %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%mapEntry.separator# => %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# % %stats.separator#---%␊ %diffGutters.padding# % %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%mapEntry.separator# => %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs multiline string values in objects > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.multiline.start#`%%string.diff.deleteLine.open%bar%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.multiline.start#`%%string.diff.insertLine.open%qux%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# % %string.diff.equal.open%baz%string.diff.equal.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % foo%property.separator#: %%string.multiline.start#`%%string.diff.equal.open%bar%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%baz%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%qux%string.diff.deleteLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%qux%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.actual#- % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.multiline.start#`%%string.diff.deleteLine.open%bar%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ % %property.keyBracket.open#[%%symbol.open%Symbol(s1)%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.multiline.start#`%%string.diff.insertLine.open%qux%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# % %string.diff.equal.open%baz%string.diff.equal.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs multiline string values in arrays > Snapshot 1 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.actual#- % %string.multiline.start#`%%string.diff.deleteLine.open%foo%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ % %string.multiline.start#`%%string.diff.insertLine.open%baz%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# % %string.diff.equal.open%bar%string.diff.equal.close%%string.multiline.end#`%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## diffs multiline string values in sets > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %string.multiline.start#`%%string.diff.deleteLine.open%foo%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ % %string.multiline.start#`%%string.diff.insertLine.open%baz%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# % %string.diff.equal.open%bar%string.diff.equal.close%%string.multiline.end#`%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs multiline string values in maps when key is primitive > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %number.open%1%number.close%%mapEntry.separator# => %%string.multiline.start#`%%string.diff.deleteLine.open%foo%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ % %number.open%1%number.close%%mapEntry.separator# => %%string.multiline.start#`%%string.diff.insertLine.open%baz%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# % %string.diff.equal.open%bar%string.diff.equal.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.multiline.start#`%%string.open%foo%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %diffGutters.actual#- % %string.open%bar%string.close%%string.multiline.end#`%%mapEntry.separator# => %%string.multiline.start#`%%string.diff.deleteLine.open%foo%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.expected#+ % %string.open%bar%string.close%%string.multiline.end#`%%mapEntry.separator# => %%string.multiline.start#`%%string.diff.insertLine.open%baz%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.padding# % %string.diff.equal.open%bar%string.diff.equal.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%mapEntry.separator# => %%string.multiline.start#`%%string.diff.equal.open%bar%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%baz%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%qux%string.diff.deleteLine.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%qux%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## does not diff multiline string values in maps when key is complex > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %object.openBracket#{%%object.closeBracket#}%%mapEntry.separator# => %%string.multiline.start#`%%string.open%foo%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %diffGutters.actual#- % %string.open%bar%string.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%%object.closeBracket#}%%mapEntry.separator# => %%string.multiline.start#`%%string.open%baz%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %diffGutters.expected#+ % %string.open%bar%string.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs properties with different values > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%property.after#,%␊ %diffGutters.expected#+ % value%property.separator#: %%object.ctor.open%Bar%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%property.after#,%␊ %diffGutters.expected#+ % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%property.after#,%␊ %diffGutters.expected#+ % value%property.separator#: %%string.multiline.start#`%%string.open%foo%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %diffGutters.expected#+ % %string.open%bar%string.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.diff.equal.open%bar%string.diff.equal.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.line.open#'%%string.diff.insert.open%not %string.diff.insert.close%%string.diff.equal.open%bar%string.diff.equal.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs map keys with different values > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %string.line.open#'%%string.open%key%string.close%%string.line.close#'%%mapEntry.separator# => %%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.expected#+ % %string.line.open#'%%string.open%key%string.close%%string.line.close#'%%mapEntry.separator# => %%object.ctor.open%Bar%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %string.line.open#'%%string.open%key%string.close%%string.line.close#'%%mapEntry.separator# => %%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.expected#+ % %string.line.open#'%%string.open%key%string.close%%string.line.close#'%%mapEntry.separator# => %%number.open%42%number.close%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %string.line.open#'%%string.open%key%string.close%%string.line.close#'%%mapEntry.separator# => %%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.expected#+ % %string.line.open#'%%string.open%key%string.close%%string.line.close#'%%mapEntry.separator# => %%string.multiline.start#`%%string.open%foo%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %diffGutters.expected#+ % %string.open%bar%string.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.multiline.start#`%%string.open%key%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %diffGutters.actual#- % %string.open%line%string.close%%string.multiline.end#`%%mapEntry.separator# => %%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.expected#+ % %string.open%line%string.close%%string.multiline.end#`%%mapEntry.separator# => %%object.ctor.open%Bar%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % value%property.separator#: %%number.open%42%number.close%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs circular references > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.actual#- % obj%property.separator#: %%object.openBracket#{%␊ %diffGutters.actual#- % obj%property.separator#: %%circular#[Circular]%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%property.after#,%␊ %diffGutters.expected#+ % obj%property.separator#: %%circular#[Circular]%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % obj%property.separator#: %%object.openBracket#{%␊ %diffGutters.actual#- % obj%property.separator#: %%circular#[Circular]%%property.after#,%␊ %diffGutters.expected#+ % obj%property.separator#: %%object.openBracket#{%␊ %diffGutters.expected#+ % obj%property.separator#: %%circular#[Circular]%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 3 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %list.openBracket#[%␊ %diffGutters.actual#- % %circular#[Circular]%%item.after#,%␊ %diffGutters.expected#+ % %list.openBracket#[%␊ %diffGutters.expected#+ % %circular#[Circular]%%item.after#,%␊ %diffGutters.expected#+ % %list.closeBracket#]%%item.after#,%␊ %diffGutters.padding# % %list.closeBracket#]%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.line.open#'%%string.open%map%string.close%%string.line.close#'%%mapEntry.separator# => %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %string.line.open#'%%string.open%map%string.close%%string.line.close#'%%mapEntry.separator# => %%circular#[Circular]%%mapEntry.after#,%␊ %diffGutters.expected#+ % %string.line.open#'%%string.open%map%string.close%%string.line.close#'%%mapEntry.separator# => %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % %string.line.open#'%%string.open%map%string.close%%string.line.close#'%%mapEntry.separator# => %%circular#[Circular]%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 5 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %object.openBracket#{%␊ %diffGutters.padding# % key%property.separator#: %%boolean.open%true%boolean.close%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.separator# => %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % key%property.separator#: %%boolean.open%true%boolean.close%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%mapEntry.separator# => %%circular#[Circular]%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % key%property.separator#: %%boolean.open%true%boolean.close%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.separator# => %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.expected#+ % %object.openBracket#{%␊ %diffGutters.expected#+ % key%property.separator#: %%boolean.open%true%boolean.close%%property.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.separator# => %%circular#[Circular]%%mapEntry.after#,%␊ %diffGutters.expected#+ % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diff invalid dates > Snapshot 1 '%diffGutters.padding# %%object.ctor.open%Date%object.ctor.close% %date.invalid#invalid% %object.openBracket#{%%object.closeBracket#}%' ## diff dates with extra properties > Snapshot 1 `%diffGutters.actual#- %%object.ctor.open%Date%object.ctor.close% %date.value.open%1969-07-20 20:17:40 UTC%date.value.close% %object.openBracket#{%%object.closeBracket#}%␊ %diffGutters.expected#+ %%object.ctor.open%Date%object.ctor.close% %date.value.open%1969-07-21 20:17:40 UTC%date.value.close% %object.openBracket#{%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ %%object.closeBracket#}%` ## diffs errors > Snapshot 1 `%diffGutters.actual#- %%error.name.open%Error%error.name.close% %error.ctor.open#(%Custom%error.ctor.close#)% %object.openBracket#{%␊ %diffGutters.actual#- % message%property.separator#: %%string.line.open#'%%string.open%%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- %%object.closeBracket#}%␊ %diffGutters.expected#+ %%error.name.open%Error%error.name.close% %object.openBracket#{%␊ %diffGutters.expected#+ % message%property.separator#: %%string.line.open#'%%string.open%%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ %%object.closeBracket#}%` ## diffs functions > Snapshot 1 `%diffGutters.actual#- %%function.stringTag.open%Function%function.stringTag.close% %function.name.open%foo%function.name.close% %object.openBracket#{%%object.closeBracket#}%␊ %diffGutters.expected#+ %%function.stringTag.open%Function%function.stringTag.close% %function.name.open%bar%function.name.close% %object.openBracket#{%%object.closeBracket#}%` ## diffs globals > Snapshot 1 '%diffGutters.padding# %%global.open%Global%global.close% %object.openBracket#{%%object.closeBracket#}%' ## diffs objects without constructor > Snapshot 1 `%diffGutters.actual#- %%object.stringTag.open#@%Object%object.stringTag.close% %object.openBracket#{%%object.closeBracket#}%␊ %diffGutters.expected#+ %%object.openBracket#{%%object.closeBracket#}%` ## diffs builtin subclasses > Snapshot 1 `%diffGutters.actual#- %%object.ctor.open%Foo%object.ctor.close% %object.secondaryStringTag.open#@%Array%object.secondaryStringTag.close% %list.openBracket#[%%list.closeBracket#]%␊ %diffGutters.expected#+ %%list.openBracket#[%%list.closeBracket#]%` ## diffs regexps > Snapshot 1 `%diffGutters.actual#- %%regexp.source.open#/%foo%regexp.source.close#/%%regexp.flags.open%%regexp.flags.close%␊ %diffGutters.expected#+ %%regexp.source.open#/%foo%regexp.source.close#/%%regexp.flags.open%g%regexp.flags.close%` > Snapshot 2 `%diffGutters.padding# %%object.ctor.open%RegExp%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %regexp.source.open#/%foo%regexp.source.close#/%%regexp.flags.open%%regexp.flags.close%␊ %diffGutters.padding# % %regexp.separator#---%␊ %diffGutters.expected#+ % bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## diffs buffers > Snapshot 1 `%diffGutters.padding# %%object.ctor.open%Buffer%object.ctor.close% %object.secondaryStringTag.open#@%Uint8Array%object.secondaryStringTag.close% %list.openBracket#[%␊ %diffGutters.actual#- % %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %diffGutters.expected#+ % %typedArray.bytes.open%666c6174%typedArray.bytes.close% %typedArray.bytes.open%20776869%typedArray.bytes.close% %typedArray.bytes.open%7465%typedArray.bytes.close%␊ %diffGutters.padding# %%list.closeBracket#]%` ## inverted diffs > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % baz%property.separator#: %%string.multiline.start#`%%string.diff.equal.open%qux%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%corge%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.deleteLine.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%quux%string.diff.deleteLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%quux%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.insertLine.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%corge%string.diff.insertLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.actual#- % foo%property.separator#: %%string.line.open#'%%string.diff.deleteLine.open%BAR%string.diff.deleteLine.close%%string.line.close#'%%property.after#,%␊ %diffGutters.expected#+ % foo%property.separator#: %%string.line.open#'%%string.diff.insertLine.open%bar%string.diff.insertLine.close%%string.line.close#'%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## inverts string diffs > Snapshot 1 `%diffGutters.actual#- %%string.line.open#'%%string.diff.deleteLine.open%bar%string.diff.deleteLine.close%%string.line.close#'%␊ %diffGutters.expected#+ %%string.line.open#'%%string.diff.insertLine.open%foo%string.diff.insertLine.close%%string.line.close#'%` > Snapshot 2 `%diffGutters.actual#- %%string.line.open#'%%string.diff.equal.open%foo ba%string.diff.equal.close%%string.diff.delete.open%z%string.diff.delete.close%%string.diff.equal.open% %string.diff.equal.close%%string.diff.delete.open%quux%string.diff.delete.close%%string.line.close#'%␊ %diffGutters.expected#+ %%string.line.open#'%%string.diff.equal.open%foo ba%string.diff.equal.close%%string.diff.insert.open%r%string.diff.insert.close%%string.diff.equal.open% %string.diff.equal.close%%string.diff.insert.open%baz%string.diff.insert.close%%string.line.close#'%` > Snapshot 3 `%diffGutters.actual#- %%string.multiline.start#`%%string.diff.delete.open%foobar%string.diff.delete.close%%string.diff.equal.open%baz%string.diff.equal.close%␊ %diffGutters.expected#+ %%string.multiline.start#`%%string.diff.insertLine.open%foo␊%string.diff.insertLine.close%␊ %diffGutters.expected#+ %%string.diff.insertLine.open%bar␊%string.diff.insertLine.close%␊ %diffGutters.expected#+ %%string.diff.equal.open%baz%string.diff.equal.close%%string.multiline.end#`%` ## inverts string diffs in containers > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % a%property.separator#: %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%z%string.diff.delete.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%quux%string.diff.deleteLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.expected#+ % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%r%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 2 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%z%string.diff.delete.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%quux%string.diff.deleteLine.close%%string.multiline.end#`%%item.after#,%␊ %diffGutters.expected#+ % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%r%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` > Snapshot 3 `%diffGutters.padding# %%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.line.open#'%%string.open%a%string.close%%string.line.close#'%%mapEntry.separator# => %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%z%string.diff.delete.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%quux%string.diff.deleteLine.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.expected#+ % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%r%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%%mapEntry.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 4 `%diffGutters.padding# %%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%z%string.diff.delete.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%quux%string.diff.deleteLine.close%%string.multiline.end#`%%item.after#,%␊ %diffGutters.expected#+ % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%r%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%%item.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 5 `%diffGutters.padding# %%object.ctor.open%String%object.ctor.close% %object.openBracket#{%␊ %diffGutters.padding# % %string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%z%string.diff.delete.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%quux%string.diff.deleteLine.close%%string.multiline.end#`%␊ %diffGutters.expected#+ % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%r%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%␊ %diffGutters.padding# %%object.closeBracket#}%` > Snapshot 6 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % a%property.separator#: %%object.openBracket#{%␊ %diffGutters.padding# % b%property.separator#: %%string.multiline.start#`%%string.diff.equal.open%foo%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.delete.open%z%string.diff.delete.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.actual#- % %string.diff.deleteLine.open%quux%string.diff.deleteLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.expected#+ % %string.diff.equal.open%ba%string.diff.equal.close%%string.diff.insert.open%r%string.diff.insert.close%%string.diff.equal.open%%string.diff.equal.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.diff.equal.open%%string.diff.equal.close%␊ %diffGutters.expected#+ % %string.diff.insertLine.open%baz%string.diff.insertLine.close%%string.multiline.end#`%%property.after#,%␊ %diffGutters.padding# % %object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` ## lists: effectively resets depth when formatting differences > Snapshot 1 `%diffGutters.padding# %%list.openBracket#[%␊ %diffGutters.padding# % %object.ctor.open%Object%object.ctor.close% %object.openBracket#{% %maxDepth#…% %object.closeBracket#}%%item.after#,%␊ %diffGutters.actual#- % %object.openBracket#{%␊ %diffGutters.actual#- % d%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % e%property.separator#: %%object.ctor.open%Object%object.ctor.close% %object.openBracket#{% %maxDepth#…% %object.closeBracket#}%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%item.after#,%␊ %diffGutters.padding# %%list.closeBracket#]%` ## objects: effectively resets depth when formatting differences > Snapshot 1 `%diffGutters.padding# %%object.openBracket#{%␊ %diffGutters.padding# % a%property.separator#: %%object.ctor.open%Object%object.ctor.close% %object.openBracket#{% %maxDepth#…% %object.closeBracket#}%%property.after#,%␊ %diffGutters.actual#- % c%property.separator#: %%object.openBracket#{%␊ %diffGutters.actual#- % d%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %diffGutters.actual#- % e%property.separator#: %%object.ctor.open%Object%object.ctor.close% %object.openBracket#{% %maxDepth#…% %object.closeBracket#}%%property.after#,%␊ %diffGutters.actual#- % %object.closeBracket#}%%property.after#,%␊ %diffGutters.padding# %%object.closeBracket#}%` concordance-5.0.4/test/snapshots/diff.js.snap000066400000000000000000000125641402315264500212170ustar00rootroot00000000000000AVA Snapshot v2 Ꝝᄻw7R4= xE9,7r찐@B$K9B~Τ' ̑`µuAd%\\"Q@wիz^5j @?@tx 9hr[~bs\~&o˭9Yy Nkaw,B8›] Nū+r]+Wp- RUt<0ٱm}͙1ȄPBJD<0jJG ;4`ڥ41qLp؉996O6#O6{JwY&c(9JC -;y>dWx`n:s\X[3tȴܕ]/py# |4®Qyl'sZے*gE?DpدX?c^fgEqXc`-nԥmMM aXR_Y=TLq۷p,!nS )p @'$^q]ſ'8 Q6ø֦w[Raj;|m~&⋏礔}flm=„>yC)~a@8rBaXSMծ7=|P4%  KEnTȗZgΚBpw ?3?7gۼ֠-Ļ*IWN_ҩI975kۼF Ah|›W W>f-_vB]8~T%maGMؒ{X:3/tp~Ec8lxB8{;X{BcTuVvk pӤnL|WiDX[c-75_;n=׻{3b?k+ǣ8lҒ SňWv(^/u55?S^H( !A7u-`*Z,]daQo29 Qf&8dg10e[r o9])Wl4OY&͌%$ś `I}R!UZM&OSB_S!1harZ Pu} vfŃ ]좊Id_h\ ,1VA%f4<}]r!ejxs{YduCYPqpDV2 ?pQ4s# ș>ңobِ*a\?Ze?r~`MH zSt@ZII1[ l, u J+z$A]Dt 2BxOeDPq ^+ST_%\ز*VxQypKUU^*l1f<ЉJQ93eM5P͕sO%eL:dD.'Y2a`өнg?^1ܿzG}Pۛh ED2.Ymji;5I\KB. |, > MY4MI#WNV8&J.x4&hR!sh5H}:PTt6iˢ(g"R%BIHc'1?DUpMUɡ!IyZ.%AyK&'IʿW3k蔥/Y*?|o bFUjꘋNmPK8 z.*"cXذLbAf̴8cGGpPDŶ2lR(q{F-c´_U1;ٹ"8 N<&YſG.[',XjUZNUn`+RT8J*Pve!j^Ҕ">T: ":}QxpE?j$F4S4͗ʼn衖 FSZ׊甄5O#U njcORsyS)T֭η oְ+ɁRõ3\Q}r ːǏpxǒ.2}?nx_He;6t)%3jt{76tkD&vJ~TSf%.6@&‘*]*DŽ8h]f"AnfgVLn $*#swXչe#3E$d'KQԄ3On2 bgҩ,>0^*0T(.$(sn[dW/loWOE %șBk'f6kaBHޠ;<)ZWKQL]M[t<옮<mwScPO"\u}AV&cOGzJر:r]<잜@P%{EVDWiR"abY*)D֐7U! CZ',t9llF6V~lM`V/VZ) 2ĭH)8~W[h=:uQ"G3JKzb$y(^,`EF} C@qs3QP03V+4/z5mX$F^zԫ 4u1n^VuSٟc"їJ6NϩD,ꉴUy|<޽i>\G.Dvc!mcC%/e;ۨ^`DsvR2N8G%Z]P֐B4sqxJ]/,Հ*W C[^CcBS2 N1pjА&j`SWR,ȒLhO}_2N5Ռ\Ew>eq"NIE(5%p'^"K2 }TM U3* 4k t~%Ti4oB6F0K=ޏHQ7bzLrܨl ] X #(ZhFfjX4$On&xet1 2g ALOᑨ$'Q;PT@F5!!sU8(R[/(bi:4ܣFW ) "EK -!Dz"r0.iʸS<;5H, BF,҈EbT#JAhcĕ t]ރ"z_ZJ#"QR4V7Et[?EnE(sab mlm\H(^S⽟KQi:\,o#\b>HI~|{Ô(X5p:ϪmG(L#%X;Id2,Gi%!f5pɾ_]U'<;pXɯcl͸B7WEYc%+tJeIl9BE-UXMҲX}]+$2%!_Me'-r; 9Ri{ht4䫈 -4G\nY(Y OhW(g&\+hғr2¡]xKCXȫr5*׭DpEP4F^0oOlQ NVBwS8 z),Anĺ~_1 dzU&S&$.Ykٍ/C`!jF[K bl)Pox6K?!B*Rp2B B2- I& -o[Tn75մZ`G߰Vx)0u*#_Qʌ:F 1 =1)Wf);bi}9- h =FSE,}]N7m^?ܫY[Pn rFSEK1 1NI&<concordance-5.0.4/test/snapshots/format.js.md000066400000000000000000001234171402315264500212360ustar00rootroot00000000000000# Snapshot report for `test/format.js` The actual snapshot is saved in `format.js.snap`. Generated by [AVA](https://avajs.dev). ## formats primitive: null > Snapshot 1 '%null.open%null%null.close%' ## formats primitive: undefined > Snapshot 1 '%undefined.open%undefined%undefined.close%' ## formats primitive: false > Snapshot 1 '%boolean.open%false%boolean.close%' ## formats primitive: true > Snapshot 1 '%boolean.open%true%boolean.close%' ## formats primitive: > Snapshot 1 '%string.line.open#\'%%string.open%%string.close%%string.line.close#\'%' ## formats primitive: foo > Snapshot 1 '%string.line.open#\'%%string.open%foo%string.close%%string.line.close#\'%' ## formats primitive: \ -- ' -- " > Snapshot 1 '%string.line.open#\'%%string.open%\\\\ -- \' -- "%string.close%%string.line.close#\'%' ## formats primitive: foo\nbar\baz'" > Snapshot 1 `%string.multiline.start#`%%string.open%foo%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %string.open%bar\\\\baz'"%string.close%%string.multiline.end#`%` ## formats primitive: qux\r\nquux > Snapshot 1 `%string.multiline.start#`%%string.open%qux%string.close%%string.controlPicture.open%␍%string.controlPicture.close%%string.open%%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %string.open%quux%string.close%%string.multiline.end#`%` ## formats primitive: qux\rquux > Snapshot 1 `%string.multiline.start#`%%string.open%qux%string.close%%string.controlPicture.open%␍%string.controlPicture.close%%string.open%%string.close%␊ %string.open%quux%string.close%%string.multiline.end#`%` ## formats primitive: 42 > Snapshot 1 '%number.open%42%number.close%' ## formats primitive: -42 > Snapshot 1 '%number.open%-42%number.close%' ## formats primitive: -0 > Snapshot 1 '%number.open%-0%number.close%' ## formats primitive: 0 > Snapshot 1 '%number.open%0%number.close%' ## formats primitive: Infinity > Snapshot 1 '%number.open%Infinity%number.close%' ## formats primitive: -Infinity > Snapshot 1 '%number.open%-Infinity%number.close%' ## formats primitive: NaN > Snapshot 1 '%number.open%NaN%number.close%' ## formats primitive: Symbol() > Snapshot 1 '%symbol.open%Symbol()%symbol.close%' ## formats primitive: Symbol(foo) > Snapshot 1 '%symbol.open%Symbol(foo)%symbol.close%' ## formats primitive: Symbol(foo\nbar) > Snapshot 1 '%symbol.open%Symbol(foo\\nbar)%symbol.close%' ## formats primitive: Symbol(bar) > Snapshot 1 '%symbol.open%Symbol.for(bar)%symbol.close%' ## formats primitive: Symbol(bar\nbaz) > Snapshot 1 '%symbol.open%Symbol.for(bar\\nbaz)%symbol.close%' ## formats primitive: Symbol(Symbol.iterator) > Snapshot 1 '%symbol.open%Symbol.iterator%symbol.close%' ## formats primitive: 42n > Snapshot 1 '%bigInt.open%42n%bigInt.close%' ## formats primitive: -42n > Snapshot 1 '%bigInt.open%-42n%bigInt.close%' ## escapes ' according to theme > Snapshot 1 '<\\\'>' > Snapshot 2 `<\\'␊␊ >` ## escapes " according to theme > Snapshot 1 '<\\">' > Snapshot 2 `<\\"␊␊ >` ## escapes ` according to theme > Snapshot 1 '<\\`>' > Snapshot 2 `<\\`␊␊ >` ## escapes singlequotes in one-line strings with the default theme > should be escaped '\'\\\'\'' > should not be escaped ``'␊␊ `` ## escapes backticks in multi-line strings with the default theme > should not be escaped '\'`\'' > should be escaped ``\\`␊␊ `` ## formats a simple object > Snapshot 1 `%object.openBracket#{%␊ baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats a simple, nested object > Snapshot 1 `%object.openBracket#{%␊ foo%property.separator#: %%object.openBracket#{%␊ baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%%property.after#,%␊ %object.closeBracket#}%` ## formats multiline strings inside an object > Snapshot 1 `%object.openBracket#{%␊ %string.line.open#'%%string.open%foo\\nbar%string.close%%string.line.close#'%%property.separator#: %%string.multiline.start#`%%string.open%baz%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %string.open%qux%string.close%%string.multiline.end#`%%property.after#,%␊ %object.closeBracket#}%` ## formats symbol keys > Snapshot 1 `%object.openBracket#{%␊ %property.keyBracket.open#[%%symbol.open%Symbol()%symbol.close%%property.keyBracket.close#]%%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats boxed primitive: false > Snapshot 1 `%object.ctor.open%Boolean%object.ctor.close% %object.openBracket#{%␊ %boolean.open%false%boolean.close%␊ %object.closeBracket#}%` ## formats boxed primitive: true > Snapshot 1 `%object.ctor.open%Boolean%object.ctor.close% %object.openBracket#{%␊ %boolean.open%true%boolean.close%␊ %object.closeBracket#}%` ## formats boxed primitive: 42 > Snapshot 1 `%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %number.open%42%number.close%␊ %object.closeBracket#}%` ## formats boxed primitive: -42 > Snapshot 1 `%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %number.open%-42%number.close%␊ %object.closeBracket#}%` ## formats boxed primitive: -0 > Snapshot 1 `%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %number.open%-0%number.close%␊ %object.closeBracket#}%` ## formats boxed primitive: 0 > Snapshot 1 `%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %number.open%0%number.close%␊ %object.closeBracket#}%` ## formats boxed primitive: Infinity > Snapshot 1 `%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %number.open%Infinity%number.close%␊ %object.closeBracket#}%` ## formats boxed primitive: -Infinity > Snapshot 1 `%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %number.open%-Infinity%number.close%␊ %object.closeBracket#}%` ## formats boxed primitive: NaN > Snapshot 1 `%object.ctor.open%Number%object.ctor.close% %object.openBracket#{%␊ %number.open%NaN%number.close%␊ %object.closeBracket#}%` ## formats boxed primitive: foo > Snapshot 1 `%object.ctor.open%String%object.ctor.close% %object.openBracket#{%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%␊ %object.closeBracket#}%` ## formats boxed primitive: 42n > Snapshot 1 `%object.ctor.open%BigInt%object.ctor.close% %object.openBracket#{%␊ %bigInt.open%42n%bigInt.close%␊ %object.closeBracket#}%` ## formats boxed primitive: -42n > Snapshot 1 `%object.ctor.open%BigInt%object.ctor.close% %object.openBracket#{%␊ %bigInt.open%-42n%bigInt.close%␊ %object.closeBracket#}%` ## formats boxed primitives with extra properties > Snapshot 1 `%object.ctor.open%String%object.ctor.close% %object.openBracket#{%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%␊ %stats.separator#---%␊ bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats a simple array > Snapshot 1 `%list.openBracket#[%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%item.after#,%␊ %string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%item.after#,%␊ %list.closeBracket#]%` ## formats a simple, nested array > Snapshot 1 `%list.openBracket#[%␊ %list.openBracket#[%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%item.after#,%␊ %string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%item.after#,%␊ %list.closeBracket#]%%item.after#,%␊ %list.closeBracket#]%` ## formats an array with additional properties > Snapshot 1 `%list.openBracket#[%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%item.after#,%␊ %string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%item.after#,%␊ %stats.separator#---%␊ baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` > Snapshot 2 `%list.openBracket#[%␊ %number.open%1%number.close%%item.after#,%␊ %number.open%2%number.close%%item.after#,%␊ %number.open%3%number.close%%item.after#,%␊ %stats.separator#---%␊ -1%property.separator#: %%number.open%-1%number.close%%property.after#,%␊ %list.closeBracket#]%` ## formats a multiline string inside an array > Snapshot 1 `%list.openBracket#[%␊ %string.multiline.start#`%%string.open%bar%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %string.open%baz%string.close%%string.multiline.end#`%%item.after#,%␊ %list.closeBracket#]%` ## formats maps > Snapshot 1 `%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%mapEntry.separator# => %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%mapEntry.after#,%␊ %object.openBracket#{%␊ baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%%mapEntry.separator# => %%string.line.open#'%%string.open%quux%string.close%%string.line.close#'%%mapEntry.after#,%␊ %string.line.open#'%%string.open%corge%string.close%%string.line.close#'%%mapEntry.separator# => %%object.openBracket#{%␊ grault%property.separator#: %%string.line.open#'%%string.open%garply%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%%mapEntry.after#,%␊ %object.closeBracket#}%` ## formats multiline strings inside a map > Snapshot 1 `%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ %string.multiline.start#`%%string.open%foo%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %string.open%bar%string.close%%string.multiline.end#`%%mapEntry.separator# => %%string.multiline.start#`%%string.open%baz%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %string.open%qux%string.close%%string.multiline.end#`%%mapEntry.after#,%␊ %object.closeBracket#}%` ## formats maps with additional properties > Snapshot 1 `%object.ctor.open%Map%object.ctor.close% %object.openBracket#{%␊ baz%property.separator#: %%string.line.open#'%%string.open%qux%string.close%%string.line.close#'%%property.after#,%␊ %stats.separator#---%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%mapEntry.separator# => %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%mapEntry.after#,%␊ %object.closeBracket#}%` ## formats sets > Snapshot 1 `%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%item.after#,%␊ %object.openBracket#{%␊ bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%%item.after#,%␊ %object.closeBracket#}%` ## formats a multiline string inside sets > Snapshot 1 `%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ %string.multiline.start#`%%string.open%bar%string.close%%string.controlPicture.open%␊%string.controlPicture.close%%string.open%%string.close%␊ %string.open%baz%string.close%%string.multiline.end#`%%item.after#,%␊ %object.closeBracket#}%` ## formats sets with additional properties > Snapshot 1 `%object.ctor.open%Set%object.ctor.close% %object.openBracket#{%␊ bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,%␊ %stats.separator#---%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%item.after#,%␊ %object.closeBracket#}%` ## formats funky objects that are lists and have an iterator > Snapshot 1 `%list.openBracket#[%␊ %string.line.open#'%%string.open%first%string.close%%string.line.close#'%%item.after#,%␊ %string.line.open#'%%string.open%second%string.close%%string.line.close#'%%item.after#,%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %stats.separator#---%␊ %string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%item.after#,%␊ %list.closeBracket#]%` ## formats regular expressions > Snapshot 1 '%regexp.source.open#/%foo%regexp.source.close#/%%regexp.flags.open%gim%regexp.flags.close%' ## formats regular expressions with additional properties > Snapshot 1 `%object.ctor.open%RegExp%object.ctor.close% %object.openBracket#{%␊ %regexp.source.open#/%foo%regexp.source.close#/%%regexp.flags.open%gim%regexp.flags.close%␊ %regexp.separator#---%␊ bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats anonymous functions > Snapshot 1 '%function.stringTag.open%Function%function.stringTag.close% %object.openBracket#{%%object.closeBracket#}%' ## formats named functions > Snapshot 1 '%function.stringTag.open%Function%function.stringTag.close% %function.name.open%foo%function.name.close% %object.openBracket#{%%object.closeBracket#}%' ## formats functions with additional properties > Snapshot 1 `%function.stringTag.open%Function%function.stringTag.close% %function.name.open%foo%function.name.close% %object.openBracket#{%␊ bar%property.separator#: %%string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats arguments > Snapshot 1 `%object.ctor.open%Arguments%object.ctor.close% %list.openBracket#[%␊ %string.line.open#'%%string.open%foo%string.close%%string.line.close#'%%item.after#,%␊ %string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%item.after#,%␊ %string.line.open#'%%string.open%baz%string.close%%string.line.close#'%%item.after#,%␊ %list.closeBracket#]%` ## formats simple errors > Snapshot 1 `%error.name.open%TypeError%error.name.close% %object.openBracket#{%␊ message%property.separator#: %%string.line.open#'%%string.open%Test message%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats simple errors with a modified name > Snapshot 1 `%error.name.open%FooError%error.name.close% %error.ctor.open#(%TypeError%error.ctor.close#)% %object.openBracket#{%␊ message%property.separator#: %%string.line.open#'%%string.open%Test message%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats errors with a name that does not include Error and does not match the constructor > Snapshot 1 `%error.name.open%Bar%error.name.close% %error.ctor.open#(%Foo%error.ctor.close#)% %object.secondaryStringTag.open#@%Error%object.secondaryStringTag.close% %object.openBracket#{%␊ message%property.separator#: %%string.line.open#'%%string.open%Test message%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats errors with additional properties > Snapshot 1 `%error.name.open%TypeError%error.name.close% %object.openBracket#{%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ message%property.separator#: %%string.line.open#'%%string.open%Test message%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats promises > Snapshot 1 '%object.ctor.open%Promise%object.ctor.close% %object.openBracket#{%%object.closeBracket#}%' ## formats promises with additional properties > Snapshot 1 `%object.ctor.open%Promise%object.ctor.close% %object.openBracket#{%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## formats pointers > Snapshot 1 `%object.openBracket#{%␊ baz%property.separator#: %%object.openBracket#{%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%%property.after#,%␊ qux%property.separator#: %%object.openBracket#{%␊ quux%property.separator#: %%object.openBracket#{%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%%property.after#,%␊ %object.closeBracket#}%%property.after#,%␊ %object.closeBracket#}%` ## formats circular references > Snapshot 1 `%object.openBracket#{%␊ circular%property.separator#: %%circular#[Circular]%%property.after#,%␊ %object.closeBracket#}%` ## formats ArrayBuffer > Snapshot 1 `%object.ctor.open%ArrayBuffer%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats ArrayBuffer with additional properties > Snapshot 1 `%object.ctor.open%ArrayBuffer%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Buffer @Uint8Array > Snapshot 1 `%object.ctor.open%Buffer%object.ctor.close% %object.secondaryStringTag.open#@%Uint8Array%object.secondaryStringTag.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Buffer @Uint8Array with additional properties > Snapshot 1 `%object.ctor.open%Buffer%object.ctor.close% %object.secondaryStringTag.open#@%Uint8Array%object.secondaryStringTag.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats DataView > Snapshot 1 `%object.ctor.open%DataView%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats DataView with additional properties > Snapshot 1 `%object.ctor.open%DataView%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Float32Array > Snapshot 1 `%object.ctor.open%Float32Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Float32Array with additional properties > Snapshot 1 `%object.ctor.open%Float32Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Float64Array > Snapshot 1 `%object.ctor.open%Float64Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Float64Array with additional properties > Snapshot 1 `%object.ctor.open%Float64Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Int16Array > Snapshot 1 `%object.ctor.open%Int16Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Int16Array with additional properties > Snapshot 1 `%object.ctor.open%Int16Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Int32Array > Snapshot 1 `%object.ctor.open%Int32Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Int32Array with additional properties > Snapshot 1 `%object.ctor.open%Int32Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Int8Array > Snapshot 1 `%object.ctor.open%Int8Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Int8Array with additional properties > Snapshot 1 `%object.ctor.open%Int8Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Uint16Array > Snapshot 1 `%object.ctor.open%Uint16Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Uint16Array with additional properties > Snapshot 1 `%object.ctor.open%Uint16Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Uint32Array > Snapshot 1 `%object.ctor.open%Uint32Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Uint32Array with additional properties > Snapshot 1 `%object.ctor.open%Uint32Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Uint8Array > Snapshot 1 `%object.ctor.open%Uint8Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Uint8Array with additional properties > Snapshot 1 `%object.ctor.open%Uint8Array%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats Uint8ClampedArray > Snapshot 1 `%object.ctor.open%Uint8ClampedArray%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %list.closeBracket#]%` ## formats Uint8ClampedArray with additional properties > Snapshot 1 `%object.ctor.open%Uint8ClampedArray%object.ctor.close% %list.openBracket#[%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close% %typedArray.bytes.open%decafbad%typedArray.bytes.close%␊ %stats.separator#---%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %list.closeBracket#]%` ## formats dates > Snapshot 1 '%object.ctor.open%Date%object.ctor.close% %date.value.open%1969-07-20 20:17:40 12ms UTC%date.value.close% %object.openBracket#{%%object.closeBracket#}%' > Snapshot 2 '%object.ctor.open%Date%object.ctor.close% %date.invalid#invalid% %object.openBracket#{%%object.closeBracket#}%' ## formats dates with additional properties > Snapshot 1 `%object.ctor.open%Date%object.ctor.close% %date.value.open%1969-07-20 20:17:40 UTC%date.value.close% %object.openBracket#{%␊ foo%property.separator#: %%string.line.open#'%%string.open%bar%string.close%%string.line.close#'%%property.after#,%␊ %object.closeBracket#}%` ## shows non-Object tag if constructor name is different > Snapshot 1 '%object.ctor.open%Foo%object.ctor.close% %object.openBracket#{%%object.closeBracket#}%' > Snapshot 2 '%object.ctor.open%Bar%object.ctor.close% %object.secondaryStringTag.open#@%Array%object.secondaryStringTag.close% %list.openBracket#[%%list.closeBracket#]%' > Snapshot 3 '%object.ctor.open%Baz%object.ctor.close% %object.secondaryStringTag.open#@%Date%object.secondaryStringTag.close% %date.value.open%1969-07-20 20:17:40 UTC%date.value.close% %object.openBracket#{%%object.closeBracket#}%' > Snapshot 4 `%object.ctor.open%Qux%object.ctor.close% %object.secondaryStringTag.open#@%RegExp%object.secondaryStringTag.close% %object.openBracket#{%␊ %regexp.source.open#/%foo%regexp.source.close#/%%regexp.flags.open%%regexp.flags.close%␊ %object.closeBracket#}%` > Snapshot 5 '%object.ctor.open%Quux%object.ctor.close% %object.secondaryStringTag.open#@%Int16Array%object.secondaryStringTag.close% %list.openBracket#[%%list.closeBracket#]%' ## shows string tag if object has no constructor > Snapshot 1 '%object.stringTag.open#@%Object%object.stringTag.close% %object.openBracket#{%%object.closeBracket#}%' ## formats global > Snapshot 1 '%global.open%Global%global.close% %object.openBracket#{%%object.closeBracket#}%' ## format with given plugin > Snapshot 1 `CustomError @Error {␊ code: 'PLUGIN',␊ }` > Snapshot 2 `Error {␊ message: 'error',␊ }` concordance-5.0.4/test/snapshots/format.js.snap000066400000000000000000000110521402315264500215660ustar00rootroot00000000000000AVA Snapshot v2 R"f@v-]\S֟ 6 %!tT "@ phH KV+X׆늸V>}SPAykX7%&X$gΔsfΜi `fɓK3f٢Jx%oZnA(Jq2qPAhb>9lKyY]{R2y;%|^g08@ۖܣ9vm7{ʁ~h]7aiDk9tv{-VמFt*O}Be}:L:oڢ^zt^ɡ#\nѠ:P0n~Eӝl1h;^l^!FtmŇ -M{8:3/(8=HIy{ܾn 1N8O7#-_KSz>w2>\yB+x;{{ybzcTQ̱+L2^.M|YcY]ToKwb!Fǘ^u~쏥NJmf ;>a_W؍!F>s㋝{JZ`p1Ť" |'!Fc,6pб;ɟ>.|sbF!_cݤw10s}Ed KbԲCn7KoWf3Q%Ĩ@Cz5e.yÚS4T7Z 俬yz~zQW7f͝&sMf9}QBs)>qN߂f\{9CrKOxxt8&BP&|&'^Vxeh+u3{ }Sztwp؊;b4d }Я1CgBBaYN9w$ayi ,_m3E󈽅 u h(V%;98otXHOP}?8Lֺa5`}3-3Vbz F3Qzhn~jo v{81l[FGeVL-}=,k⫣*G߱r! ,IX[VϤhQFW7+E$g{BUF^-ɧz,ҋɃ `aakFrw:)#w߬%AV {Oa.f^ 16i6<9G/{_ʇu0X-GsȂycyHGn= zwm+[p b|{.ݶō tn?rŢJrJb7Y~_+ܶ@6Y쮩Z,fsϙNA=T:iA.pQEň4*lB3F;ʛ+\mO'n TCJ!-RlJ[Ԅ JL<8@6Wǝg%JʼnK38l|V4Bh1K31D Kq^D8+2!][S Z(;P<#r 3͍㰹.ƂåUsH!a芇NP5BFH77e @1O͚Z!bVDPȗ"wH%S+IU9%ԓhr^]~҂msN$v)n:cfm*,jRSόX|E(~ <;\K&99Ҵ8tj$)7/%J3f,3:NEltt١ mQ)խuc]s^+"$*[I6̎MIlC;dK}Ht=93 ՗(> -TAI7)(B!*^w+3'ȿQo1qUhyi4ellƬC>RQh}Wso|U))MXSH*ED?Mywϴѩ).r_pÑqAH&JCS_B1P 57;?o~*橴1 fJ|kʔdUFlt1 *O67Fڠ Ecg̍$w('4sT̿Sm:ȿ!_u sX5;vL$ѩRconcordance-5.0.4/test/snapshots/max-depth.js.md000066400000000000000000000043671402315264500216370ustar00rootroot00000000000000# Snapshot report for `test/max-depth.js` The actual snapshot is saved in `max-depth.js.snap`. Generated by [AVA](https://avajs.dev). ## format() respects maxDepth option > Snapshot 1 `{␊ one: {␊ arr: [␊ Object { … },␊ ],␊ map: Map {␊ Object { … } => Object { … },␊ },␊ set: Set {␊ Object { … },␊ },␊ two: {␊ arr: [],␊ arr2: Array [ … ],␊ date: Date 1969-07-20 20:17:40 UTC {},␊ date2: Date 1969-07-20 20:17:40 UTC { … },␊ error: Error { … },␊ fn: Function fn {},␊ fn2: Function { … },␊ map: Map {},␊ map2: Map { … },␊ obj: {},␊ regexp: /foo/,␊ regexp2: RegExp { /foo/ … },␊ set: Set {},␊ set2: Set { … },␊ three: Object { … },␊ },␊ },␊ }` ## diff() respects maxDepth option for equal parts > Snapshot 1 ` {␊ one: {␊ arr: [␊ Object { … },␊ ],␊ map: Map {␊ Object { … } => Object { … },␊ },␊ set: Set {␊ Object { … },␊ },␊ two: {␊ arr: [],␊ arr2: Array [ … ],␊ date: Date 1969-07-20 20:17:40 UTC {},␊ date2: Date 1969-07-20 20:17:40 UTC { … },␊ error: Error { … },␊ fn: Function fn {},␊ fn2: Function { … },␊ map: Map {},␊ map2: Map { … },␊ obj: {},␊ regexp: /foo/,␊ regexp2: RegExp { /foo/ … },␊ set: Set {},␊ set2: Set { … },␊ three: Object { … },␊ },␊ },␊ - unequal: 1,␊ + unequal: 2,␊ }` > Snapshot 2 ` {␊ one: {␊ two: {␊ - three: 'baz',␊ + three: 'qux',␊ three2: Array [ … ],␊ },␊ },␊ }` ## properties with increased indentation respect the maxDepth when formatted > Snapshot 1 `{␊ foo: …,␊ }` concordance-5.0.4/test/snapshots/max-depth.js.snap000066400000000000000000000010301402315264500221600ustar00rootroot00000000000000AVA Snapshot v2 T~z|?Qպ{:`ca```fb闟Zqwb@`7;330\my~^yc-۪]*%rJaϧ3Ee ȴIAKRLĢ"+h(GA?)+5DZQ2Zx,X`X׎CpFqjBpj N30t[!8&cQQbB4 $ɔw]BhBu-r#BQݩZTt+”M˳Rp+K.rPK3BЊoTA#(,+E@3хV +@7UFPQt%E@JH&X)pi#@j.c֔ Mv'%VCD-,PGs'$p';@v#9%`5PlC\RZQd2 Y_=f,Yg@X3QYKdHBMxiR wconcordance-5.0.4/test/snapshots/pluginRegistry.js.md000066400000000000000000000010331402315264500227620ustar00rootroot00000000000000# Snapshot report for `test/pluginRegistry.js` The actual snapshot is saved in `pluginRegistry.js.snap`. Generated by [AVA](https://avajs.dev). ## registration should be successful > Snapshot 1 { id2deserialize: Map { 1 => 'objectDeserializer', 2 => 'arrayDeserializer', }, name: 'complex', serializerVersion: 1, tag2id: Map { 'complexCustomObject' => 1, 'complexCustomArray' => 2, }, theme: {}, tryDescribeValue: Function tryDescribeValue {}, } concordance-5.0.4/test/snapshots/pluginRegistry.js.snap000066400000000000000000000006271402315264500233330ustar00rootroot00000000000000AVA Snapshot v2 117;@71mQ;N@Y;A"B((@(QR@qV(#%8 \! PQP" N3on~i.6>;{2lmݧHx!hyh]n f뺀k`ѢhV<V_B|`jeP,ۏPLi,Yvh#&~Ve$ MO YGľ zˇRxh,HDxF5.Cg6Yzi|%((6U?d2Y9:o֑}LwPifݜɔmJJN Snapshot 1 ` {␊ [Symbol(s1)]: 1,␊ [Symbol(s2)]: 2,␊ }` > Snapshot 2 ` {␊ [Symbol(s1)]: 1,␊ [Symbol(s2)]: 2,␊ }` concordance-5.0.4/test/snapshots/serialize-and-encode.js.snap000066400000000000000000000002021402315264500242530ustar00rootroot00000000000000AVA Snapshot v2 Qg}",-Mh````dpq|kY?KZP\ `1FVAFCj. M(6ԌR0A4 k)&52