107 lines
2.7 KiB
JavaScript
107 lines
2.7 KiB
JavaScript
|
'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
|