aboutsummaryrefslogtreecommitdiff
path: root/node_modules/concordance/lib/complexValues
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/concordance/lib/complexValues')
-rw-r--r--node_modules/concordance/lib/complexValues/arguments.js48
-rw-r--r--node_modules/concordance/lib/complexValues/arrayBuffer.js30
-rw-r--r--node_modules/concordance/lib/complexValues/boxed.js51
-rw-r--r--node_modules/concordance/lib/complexValues/dataView.js29
-rw-r--r--node_modules/concordance/lib/complexValues/date.js89
-rw-r--r--node_modules/concordance/lib/complexValues/error.js133
-rw-r--r--node_modules/concordance/lib/complexValues/function.js159
-rw-r--r--node_modules/concordance/lib/complexValues/global.js33
-rw-r--r--node_modules/concordance/lib/complexValues/map.js78
-rw-r--r--node_modules/concordance/lib/complexValues/object.js283
-rw-r--r--node_modules/concordance/lib/complexValues/promise.js40
-rw-r--r--node_modules/concordance/lib/complexValues/regexp.js90
-rw-r--r--node_modules/concordance/lib/complexValues/set.js78
-rw-r--r--node_modules/concordance/lib/complexValues/typedArray.js162
14 files changed, 1303 insertions, 0 deletions
diff --git a/node_modules/concordance/lib/complexValues/arguments.js b/node_modules/concordance/lib/complexValues/arguments.js
new file mode 100644
index 000000000..446407222
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/arguments.js
@@ -0,0 +1,48 @@
+'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)
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/arrayBuffer.js b/node_modules/concordance/lib/complexValues/arrayBuffer.js
new file mode 100644
index 000000000..1053b893e
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/arrayBuffer.js
@@ -0,0 +1,30 @@
+'use strict'
+
+const typedArray = require('./typedArray')
+
+function describe (props) {
+ return new DescribedArrayBufferValue(Object.assign({
+ // Assume at least Node.js 4.5.0, which introduces Buffer.from()
+ 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)
diff --git a/node_modules/concordance/lib/complexValues/boxed.js b/node_modules/concordance/lib/complexValues/boxed.js
new file mode 100644
index 000000000..3736ad137
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/boxed.js
@@ -0,0 +1,51 @@
+'use strict'
+
+const recursorUtils = require('../recursorUtils')
+const stringPrimitive = require('../primitiveValues/string').tag
+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)
diff --git a/node_modules/concordance/lib/complexValues/dataView.js b/node_modules/concordance/lib/complexValues/dataView.js
new file mode 100644
index 000000000..0dd6199f6
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/dataView.js
@@ -0,0 +1,29 @@
+'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)
diff --git a/node_modules/concordance/lib/complexValues/date.js b/node_modules/concordance/lib/complexValues/date.js
new file mode 100644
index 000000000..b437456b8
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/date.js
@@ -0,0 +1,89 @@
+'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])
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/error.js b/node_modules/concordance/lib/complexValues/error.js
new file mode 100644
index 000000000..781dd7e87
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/error.js
@@ -0,0 +1,133 @@
+'use strict'
+
+const constants = require('../constants')
+const isEnumerable = require('../isEnumerable')
+const formatUtils = require('../formatUtils')
+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]
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/function.js b/node_modules/concordance/lib/complexValues/function.js
new file mode 100644
index 000000000..4ddd38f39
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/function.js
@@ -0,0 +1,159 @@
+'use strict'
+
+const functionNameSupport = require('function-name-support')
+
+const constants = require('../constants')
+const getStringTag = require('../getStringTag')
+const isEnumerable = require('../isEnumerable')
+const formatUtils = require('../formatUtils')
+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
+
+// Node.js 4 provides Function, more recent versions use GeneratorFunction
+const generatorsHaveGeneratorTag = getStringTag(function * () {}) === 'GeneratorFunction'
+
+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, generatorsHaveGeneratorTag, super.serialize()]
+ }
+}
+
+class DeserializedFunctionValue extends object.DeserializedMixin(FunctionValue) {
+ constructor (state, recursor) {
+ super(state[2], recursor)
+ this.name = state[0]
+ this.trustStringTag = state[1]
+ }
+
+ compare (expected) {
+ if (this.tag !== expected.tag) return UNEQUAL
+
+ if (this.name !== expected.name) {
+ if (this.functionNameSupportFlags === functionNameSupport.bitFlags) {
+ // The engine used to create the serialization supports the same
+ // function name inference as the current engine. That said, unless
+ // the engine has full support for name inference, it's possible that
+ // names were lost simply due to refactoring. Names are unequal if
+ // the engine has full support, or if names were inferred.
+ if (functionNameSupport.hasFullSupport === true || (this.name !== '' && expected.name !== '')) return UNEQUAL
+ } else if (functionNameSupport.isSubsetOf(this.functionNameSupportFlags)) {
+ // The engine used to create the serialization could infer more function
+ // names than the current engine. Assume `expected.name` comes from the
+ // current engine and treat the names as unequal only if the current
+ // engine could infer a name.
+ if (expected.name !== '') return UNEQUAL
+ } else {
+ /* istanbul ignore else */
+ if (functionNameSupport.isSupersetOf(this.functionNameSupportFlags)) {
+ // The engine used to create the serialization could infer fewer
+ // function names than the current engine. Treat the names as unequal
+ // only if a name was in the serialization.
+ if (this.name !== '') return UNEQUAL
+ }
+ }
+ }
+
+ // Assume `stringTag` is either 'Function' or 'GeneratorFunction', and that
+ // it always equals `ctor`. Since Node.js 4 only provides 'Function', even
+ // for generator functions, only compare `stringTag` if the serialized value
+ // legitimately would have been `Function`, and the expected `stringTag` can
+ // reliably be inferred.
+ if (this.trustStringTag && generatorsHaveGeneratorTag && this.stringTag !== expected.stringTag) return UNEQUAL
+
+ return SHALLOW_EQUAL
+ }
+
+ serialize () {
+ return [this.name, this.trustStringTag, super.serialize()]
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/global.js b/node_modules/concordance/lib/complexValues/global.js
new file mode 100644
index 000000000..6acd66d7a
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/global.js
@@ -0,0 +1,33 @@
+'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 })
diff --git a/node_modules/concordance/lib/complexValues/map.js b/node_modules/concordance/lib/complexValues/map.js
new file mode 100644
index 000000000..7ff13b217
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/map.js
@@ -0,0 +1,78 @@
+'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]
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/object.js b/node_modules/concordance/lib/complexValues/object.js
new file mode 100644
index 000000000..97a59285c
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/object.js
@@ -0,0 +1,283 @@
+'use strict'
+
+const functionNameSupport = require('function-name-support')
+
+const constants = require('../constants')
+const getObjectKeys = require('../getObjectKeys')
+const ObjectFormatter = require('../formatUtils').ObjectFormatter
+const hasLength = require('../hasLength')
+const recursorUtils = require('../recursorUtils')
+const stats = require('../metaDescriptors/stats')
+
+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,
+ functionNameSupport.bitFlags
+ ]
+ }
+}
+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.functionNameSupportFlags = state[6]
+ 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) {
+ if (this.ctor === expected.ctor) return true
+
+ if (this.functionNameSupportFlags === functionNameSupport.bitFlags) {
+ // The engine used to create the serialization supports the same
+ // function name inference as the current engine. That said, unless
+ // the engine has full support for name inference, it's possible that
+ // names were lost simply due to refactoring. Ctors are not the same
+ // only if the engine has full support, or if ctors were inferred.
+ if (functionNameSupport.hasFullSupport === true || (this.ctor !== null && expected.ctor !== null)) return false
+ } else if (functionNameSupport.isSubsetOf(this.functionNameSupportFlags)) {
+ // The engine used to create the serialization could infer more function
+ // names than the current engine. Assume `expected.ctor` comes from the
+ // current engine and treat the ctors as unequal only if the current
+ // engine could infer a ctor.
+ if (expected.ctor !== null) return false
+ } else {
+ /* istanbul ignore else */
+ if (functionNameSupport.isSupersetOf(this.functionNameSupportFlags)) {
+ // The engine used to create the serialization could infer fewer
+ // function names than the current engine. Treat the ctors as unequal
+ // only if a ctor was in the serialization.
+ if (this.ctor !== null) return false
+ }
+ }
+
+ return true
+ }
+ }
+}
+exports.DeserializedMixin = DeserializedMixin
diff --git a/node_modules/concordance/lib/complexValues/promise.js b/node_modules/concordance/lib/complexValues/promise.js
new file mode 100644
index 000000000..823d45386
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/promise.js
@@ -0,0 +1,40 @@
+'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)
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/regexp.js b/node_modules/concordance/lib/complexValues/regexp.js
new file mode 100644
index 000000000..1c66a2fd8
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/regexp.js
@@ -0,0 +1,90 @@
+'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]
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/set.js b/node_modules/concordance/lib/complexValues/set.js
new file mode 100644
index 000000000..fc3623ace
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/set.js
@@ -0,0 +1,78 @@
+'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]
+ }
+}
diff --git a/node_modules/concordance/lib/complexValues/typedArray.js b/node_modules/concordance/lib/complexValues/typedArray.js
new file mode 100644
index 000000000..5c482ff1f
--- /dev/null
+++ b/node_modules/concordance/lib/complexValues/typedArray.js
@@ -0,0 +1,162 @@
+'use strict'
+
+const constants = require('../constants')
+const formatUtils = require('../formatUtils')
+const lineBuilder = require('../lineBuilder')
+const recursorUtils = require('../recursorUtils')
+const propertyStatsTag = require('../metaDescriptors/stats').propertyTag
+const object = require('./object')
+
+const DEEP_EQUAL = constants.DEEP_EQUAL
+const UNEQUAL = constants.UNEQUAL
+
+function getBuffer (value) {
+ // Assume at least Node.js 4.5.0, which introduces Buffer.from()
+ 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)