diff --git a/lib/utils/objects.js b/lib/utils/objects.js index 6999ec06d..edc709edd 100644 --- a/lib/utils/objects.js +++ b/lib/utils/objects.js @@ -1,122 +1,119 @@ // @flow import stableStringify from 'fast-json-stable-stringify'; import invariant from 'invariant'; -import _difference from 'lodash/fp/difference.js'; import _isEqual from 'lodash/fp/isEqual.js'; import _isPlainObject from 'lodash/fp/isPlainObject.js'; import stringHash from 'string-hash'; type ObjectMap = { +[key: K]: T }; type NestedObjectMap = { +[key: K]: T | NestedObjectMap }; function findMaximumDepth(obj: Object): ?{ path: string, depth: number } { let longestPath = null; let longestDepth = null; for (const key in obj) { const value = obj[key]; if (typeof value !== 'object' || !value) { if (!longestDepth) { longestPath = key; longestDepth = 1; } continue; } const childResult = findMaximumDepth(obj[key]); if (!childResult) { continue; } const { path, depth } = childResult; const ourDepth = depth + 1; if (longestDepth === null || ourDepth > longestDepth) { longestPath = `${key}.${path}`; longestDepth = ourDepth; } } if (!longestPath || !longestDepth) { return null; } return { path: longestPath, depth: longestDepth }; } function values(map: ObjectMap): T[] { return Object.values ? // https://github.com/facebook/flow/issues/2221 // $FlowFixMe - Object.values currently does not have good flow support Object.values(map) : Object.keys(map).map((key: K): T => map[key]); } function keys(map: ObjectMap): K[] { return Object.keys(map); } function assignValueWithKey( obj: NestedObjectMap, key: K, value: T | NestedObjectMap, ): NestedObjectMap { return { ...obj, ...Object.fromEntries([[key, value]]), }; } function hash(obj: ?Object): number { if (!obj) { return -1; } return stringHash(stableStringify(obj)); } // returns an object with properties from obj1 not included in obj2 function deepDiff( obj1: NestedObjectMap, obj2: NestedObjectMap, ): NestedObjectMap { let diff: NestedObjectMap = {}; keys(obj1).forEach((key: K) => { if (_isEqual(obj1[key], obj2[key])) { return; } if (!_isPlainObject(obj1[key]) || !_isPlainObject(obj2[key])) { diff = assignValueWithKey(diff, key, obj1[key]); return; } const nestedObj1: ObjectMap = (obj1[key]: any); const nestedObj2: ObjectMap = (obj2[key]: any); const nestedDiff = deepDiff(nestedObj1, nestedObj2); if (Object.keys(nestedDiff).length > 0) { diff = assignValueWithKey(diff, key, nestedDiff); } }); return diff; } function assertObjectsAreEqual( processedObject: ObjectMap, expectedObject: ObjectMap, message: string, ) { - const processedObjectKeys = Object.keys(processedObject); - const expectedObjectKeys = Object.keys(expectedObject); - - const inProcessedButNotExpected = - _difference(processedObjectKeys)(expectedObjectKeys); - const inExpectedButNotProcessed = - _difference(expectedObjectKeys)(processedObjectKeys); + if (_isEqual(processedObject)(expectedObject)) { + return; + } + const dataProcessedButNotExpected = deepDiff(processedObject, expectedObject); + const dataExpectedButNotProcessed = deepDiff(expectedObject, processedObject); invariant( - _isEqual(processedObject)(expectedObject), + false, `${message}: Objects should be equal.` + - ` Object keys processed but not expected:` + - ` ${inExpectedButNotProcessed.toString()}` + - ` Object keys expected but not processed:` + - ` ${inProcessedButNotExpected.toString()}`, + ` Data processed but not expected:` + + ` ${JSON.stringify(dataProcessedButNotExpected)}` + + ` Data expected but not processed:` + + ` ${JSON.stringify(dataExpectedButNotProcessed)}`, ); } export { findMaximumDepth, values, hash, assertObjectsAreEqual, deepDiff };