diff --git a/src/calculateDeepEqualDiffs.js b/src/calculateDeepEqualDiffs.js index da2b0d9..c4df51b 100644 --- a/src/calculateDeepEqualDiffs.js +++ b/src/calculateDeepEqualDiffs.js @@ -6,6 +6,7 @@ import { isError, isFunction, isSet, + isMap, has, uniq, } from 'lodash'; @@ -75,17 +76,78 @@ function accumulateDeepEqualDiffs(a, b, diffsAccumulator, pathString = '', { det } if (isSet(a) && isSet(b)) { - if (a.size !== b.size) { + const setLength = a.size; + if (setLength !== b.size) { return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.different); } - for (const valA of a) { - if (!b.has(valA)) { - return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.different); + const setItemDiffs = []; + let numberOfDeepEqualsValues = 0; + const aValues = [...a.values()]; + const bValues = [...b.values()]; + for (let i = setLength; i--; i > 0) { + const diffEquals = accumulateDeepEqualDiffs(aValues[i], bValues[i], setItemDiffs, `${pathString}.values()[${i}]`, { detailed }); + if (diffEquals) { + numberOfDeepEqualsValues++; + } + } + + if (detailed || numberOfDeepEqualsValues !== setLength) { + diffsAccumulator.push(...setItemDiffs); + } + + if (numberOfDeepEqualsValues === setLength) { + return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.deepEquals); + } + + return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.different); + } + + if (isMap(a) && isMap(b)) { + const mapLength = a.size; + if (mapLength !== b.size) { + return trackDiff(new Map(a), new Map(b), diffsAccumulator, pathString, diffTypes.different); + } + + const mapKeysDiffs = []; + let numberOfDeepEqualsKeys = 0; + const aKeys = [...a.keys()]; + const bKeys = [...b.keys()]; + for (let i = mapLength; i--; i > 0) { + const diffEquals = accumulateDeepEqualDiffs(aKeys[i], bKeys[i], mapKeysDiffs, `${pathString}.keys()[${i}]`, { detailed }); + if (diffEquals) { + numberOfDeepEqualsKeys++; } } - return trackDiff(new Set(a), new Set(b), diffsAccumulator, pathString, diffTypes.deepEquals); + if (detailed || numberOfDeepEqualsKeys !== mapLength) { + diffsAccumulator.push(...mapKeysDiffs); + } + + if (numberOfDeepEqualsKeys !== mapLength) { + return trackDiff(new Map(a), new Map(b), diffsAccumulator, pathString, diffTypes.different); + } + + const mapValuesDiffs = []; + let numberOfDeepEqualsValues = 0; + const aValues = [...a.values()]; + const bValues = [...b.values()]; + for (let i = mapLength; i--; i > 0) { + const diffEquals = accumulateDeepEqualDiffs(aValues[i], bValues[i], mapValuesDiffs, `${pathString}.values()[${i}]`, { detailed }); + if (diffEquals) { + numberOfDeepEqualsValues++; + } + } + + if (detailed || numberOfDeepEqualsValues !== mapLength) { + diffsAccumulator.push(...mapValuesDiffs); + } + + if (numberOfDeepEqualsValues === mapLength) { + return trackDiff(new Map(a), new Map(b), diffsAccumulator, pathString, diffTypes.deepEquals); + } + + return trackDiff(new Map(a), new Map(b), diffsAccumulator, pathString, diffTypes.different); } if (isDate(a) && isDate(b)) { @@ -140,7 +202,7 @@ function accumulateDeepEqualDiffs(a, b, diffsAccumulator, pathString = '', { det if (typeof a === 'object' && typeof b === 'object' && Object.getPrototypeOf(a) === Object.getPrototypeOf(b)) { const aKeys = Object.getOwnPropertyNames(a); const bKeys = Object.getOwnPropertyNames(b); - + const allKeys = uniq([...aKeys, ...bKeys]); const clonedA = isPlainObject(a) ? { ...a } : a; diff --git a/tests/calculateDeepEqualDiffs.test.js b/tests/calculateDeepEqualDiffs.test.js index 9876d65..d13043c 100644 --- a/tests/calculateDeepEqualDiffs.test.js +++ b/tests/calculateDeepEqualDiffs.test.js @@ -423,22 +423,106 @@ test('sets', () => { a: new Set(['a']), b: new Set(['a', 1]), c: new Set(['a', 1]), + d: new Set([{ foo: 'bar' }]), }; const nextValue = { a: new Set(['a']), b: new Set(['a', 2]), c: new Set(['a', 1, 'c']), + d: new Set([{ foo: 'bar' }]), }; const diffs = calculateDeepEqualDiffs(prevValue, nextValue); expect(diffs).toEqual([ + { + pathString: '.d', + prevValue: prevValue.d, + nextValue: nextValue.d, + diffType: diffTypes.deepEquals, + }, + { + pathString: '.c', + prevValue: prevValue.c, + nextValue: nextValue.c, + diffType: diffTypes.different, + }, + { + pathString: '.b.values()[1]', + prevValue: [...prevValue.b.values()][1], + nextValue: [...nextValue.b.values()][1], + diffType: diffTypes.different, + }, + { + pathString: '.b', + prevValue: prevValue.b, + nextValue: nextValue.b, + diffType: diffTypes.different, + }, + { + pathString: '.a', + prevValue: prevValue.a, + nextValue: nextValue.a, + diffType: diffTypes.deepEquals, + }, + { + pathString: '', + prevValue: prevValue, + nextValue: nextValue, + diffType: diffTypes.different, + }, + ]); +}); + +test('maps', () => { + const prevValue = { + a: new Map([['a', 'b']]), + b: new Map([['a', 'b'], [1, 2]]), + c: new Map([['a', 'b'], [1, 2]]), + d: new Map([[{ foo: 'bar' }, { bar: 'foo' }]]), + e: new Map([['a', 'b']]), + }; + + const nextValue = { + a: new Map([['a', 'b']]), + b: new Map([['a', 'b'], [2, 2]]), + c: new Map([['a', 'b'], [1, 2], ['c', 3]]), + d: new Map([[{ foo: 'bar' }, { bar: 'foo' }]]), + e: new Map([['a', 'e']]), + }; + + const diffs = calculateDeepEqualDiffs(prevValue, nextValue); + expect(diffs).toEqual([ + { + pathString: '.e.values()[0]', + prevValue: [...prevValue.e.values()][0], + nextValue: [...nextValue.e.values()][0], + diffType: diffTypes.different, + }, + { + pathString: '.e', + prevValue: prevValue.e, + nextValue: nextValue.e, + diffType: diffTypes.different, + }, + { + pathString: '.d', + prevValue: prevValue.d, + nextValue: nextValue.d, + diffType: diffTypes.deepEquals, + }, { pathString: '.c', prevValue: prevValue.c, nextValue: nextValue.c, diffType: diffTypes.different, }, + { + pathString: '.b.keys()[1]', + prevValue: [...prevValue.b.keys()][1], + nextValue: [...nextValue.b.keys()][1], + diffType: diffTypes.different, + }, { pathString: '.b', prevValue: prevValue.b, @@ -489,7 +573,7 @@ describe('calculateDeepEqualDiffs - Errors', () => { }, ]); }); - + test('Different Native Errors', () => { const prevValue = new Error('message'); const nextValue = new Error('Second message'); @@ -568,7 +652,7 @@ test('Equal class instances', () => { this.name = name; } } - + const prevValue = new Person('Jon Snow'); const nextValue = new Person('Jon Snow'); const diffs = calculateDeepEqualDiffs(prevValue, nextValue); @@ -588,7 +672,7 @@ test('Different class instances', () => { this.name = name; } } - + const prevValue = new Person('Jon Snow'); const nextValue = new Person('Aria Stark'); const diffs = calculateDeepEqualDiffs(prevValue, nextValue); diff --git a/tests/findObjectsDifferences.test.js b/tests/findObjectsDifferences.test.js index de2385d..4d16eff 100644 --- a/tests/findObjectsDifferences.test.js +++ b/tests/findObjectsDifferences.test.js @@ -166,8 +166,8 @@ describe('findObjectsDifferences not shallow', () => { }); test('For sets with same values', () => { - const prev = new Set([1, 2, 3]); - const next = new Set([1, 2, 3]); + const prev = new Set([1, 2, 3, { foo: 'bar' }]); + const next = new Set([1, 2, 3, { foo: 'bar' }]); const diffs = findObjectsDifferences(prev, next, { shallow: false }); expect(diffs).toEqual([{ pathString: '', @@ -178,10 +178,30 @@ describe('findObjectsDifferences not shallow', () => { }); test('For sets with different values', () => { - const prev = new Set([1, 2, 3]); - const next = new Set([4, 5, 6]); + const prevValues = [1, 2, 3]; + const nextValues = [4, 5, 6]; + const prev = new Set(prevValues); + const next = new Set(nextValues); const diffs = findObjectsDifferences(prev, next, { shallow: false }); expect(diffs).toEqual([ + { + pathString: '.values()[2]', + diffType: diffTypes.different, + prevValue: prevValues[2], + nextValue: nextValues[2], + }, + { + pathString: '.values()[1]', + diffType: diffTypes.different, + prevValue: prevValues[1], + nextValue: nextValues[1], + }, + { + pathString: '.values()[0]', + diffType: diffTypes.different, + prevValue: prevValues[0], + nextValue: nextValues[0], + }, { pathString: '', diffType: diffTypes.different, @@ -191,7 +211,7 @@ describe('findObjectsDifferences not shallow', () => { ]); }); - test('For sets with different value length', () => { + test('For sets with different length', () => { const prev = new Set([1, 2, 3]); const next = new Set([1, 2, 3, 4]); const diffs = findObjectsDifferences(prev, next, { shallow: false }); @@ -204,4 +224,98 @@ describe('findObjectsDifferences not shallow', () => { }, ]); }); + + test('For maps with same entries', () => { + const prev = new Map([[1, 'a'], [2, 'b'], [3, 'c'], [{ foo: 'bar' }, { bar: 'foo' }]]); + const next = new Map([[1, 'a'], [2, 'b'], [3, 'c'], [{ foo: 'bar' }, { bar: 'foo' }]]); + const diffs = findObjectsDifferences(prev, next, { shallow: false }); + expect(diffs).toEqual([{ + pathString: '', + diffType: diffTypes.deepEquals, + prevValue: prev, + nextValue: next, + }]); + }); + + test('For maps with different keys', () => { + const prevEntries = [[1, ''], [2, ''], [3, '']]; + const nextEntries = [[4, ''], [5, ''], [6, '']]; + const prev = new Map(prevEntries); + const next = new Map(nextEntries); + const diffs = findObjectsDifferences(prev, next, { shallow: false }); + expect(diffs).toEqual([ + { + pathString: '.keys()[2]', + diffType: diffTypes.different, + prevValue: prevEntries[2][0], + nextValue: nextEntries[2][0], + }, + { + pathString: '.keys()[1]', + diffType: diffTypes.different, + prevValue: prevEntries[1][0], + nextValue: nextEntries[1][0], + }, + { + pathString: '.keys()[0]', + diffType: diffTypes.different, + prevValue: prevEntries[0][0], + nextValue: nextEntries[0][0], + }, + { + pathString: '', + diffType: diffTypes.different, + prevValue: prev, + nextValue: next, + }, + ]); + }); + + test('For maps with different values', () => { + const prevEntries = [[1, 'a'], [2, 'b'], [3, 'c']]; + const nextEntries = [[1, 'd'], [2, 'e'], [3, 'f']]; + const prev = new Map(prevEntries); + const next = new Map(nextEntries); + const diffs = findObjectsDifferences(prev, next, { shallow: false }); + expect(diffs).toEqual([ + { + pathString: '.values()[2]', + diffType: diffTypes.different, + prevValue: prevEntries[2][1], + nextValue: nextEntries[2][1], + }, + { + pathString: '.values()[1]', + diffType: diffTypes.different, + prevValue: prevEntries[1][1], + nextValue: nextEntries[1][1], + }, + { + pathString: '.values()[0]', + diffType: diffTypes.different, + prevValue: prevEntries[0][1], + nextValue: nextEntries[0][1], + }, + { + pathString: '', + diffType: diffTypes.different, + prevValue: prev, + nextValue: next, + }, + ]); + }); + + test('For maps with different length', () => { + const prev = new Map([[1, 1], [2, 2], [3, 3]]); + const next = new Map([[1, 1], [2, 2], [3, 3], [4, 4]]); + const diffs = findObjectsDifferences(prev, next, { shallow: false }); + expect(diffs).toEqual([ + { + pathString: '', + diffType: diffTypes.different, + prevValue: prev, + nextValue: next, + }, + ]); + }); });