diff --git a/CHANGELOG.md b/CHANGELOG.md index d4d5fe700a5b..63707a66008c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,7 @@ - `[jest-haste-map]` Fix the `mapper` option which was incorrectly ignored ([#8299](https://github.com/facebook/jest/pull/8299)) - `[jest-jasmine2]` Fix describe return value warning being shown if the describe function throws ([#8335](https://github.com/facebook/jest/pull/8335)) - `[jest-environment-jsdom]` Re-declare global prototype of JSDOMEnvironment ([#8352](https://github.com/facebook/jest/pull/8352)) +- `[jest-snapshot]` Handle arrays when merging snapshots ([#7089](https://github.com/facebook/jest/pull/7089)) ### Chore & Maintenance diff --git a/packages/jest-snapshot/src/__tests__/utils.test.ts b/packages/jest-snapshot/src/__tests__/utils.test.ts index 981070691777..f28efd12128d 100644 --- a/packages/jest-snapshot/src/__tests__/utils.test.ts +++ b/packages/jest-snapshot/src/__tests__/utils.test.ts @@ -9,6 +9,7 @@ jest.mock('fs'); import fs from 'fs'; import path from 'path'; +import assert from 'assert'; import chalk from 'chalk'; import { @@ -201,12 +202,85 @@ test('serialize handles \\r\\n', () => { describe('DeepMerge', () => { it('Correctly merges objects with property matchers', () => { - const target = {data: {bar: 'bar', foo: 'foo'}}; + /* eslint-disable sort-keys */ + // to keep keys in numerical order rather than alphabetical + const target = { + data: { + one: 'one', + two: 'two', + three: [ + { + four: 'four', + five: 'five', + }, + // Include an array element not present in the propertyMatchers + { + six: 'six', + seven: 'seven', + }, + ], + eight: [{nine: 'nine'}], + }, + }; + const matcher = expect.any(String); - const propertyMatchers = {data: {foo: matcher}}; + const propertyMatchers = { + data: { + two: matcher, + three: [ + { + four: matcher, + }, + ], + eight: [ + {nine: matcher}, + // Include an array element not present in the target + {ten: matcher}, + ], + }, + }; + const mergedOutput = deepMerge(target, propertyMatchers); - expect(mergedOutput).toStrictEqual({data: {bar: 'bar', foo: matcher}}); - expect(target).toStrictEqual({data: {bar: 'bar', foo: 'foo'}}); + // Use assert.deepStrictEqual() instead of expect().toStrictEqual() + // since we want to actually validate that we got the matcher + // rather than treat it specially the way that expect() does + assert.deepStrictEqual(mergedOutput, { + data: { + one: 'one', + two: matcher, + three: [ + { + four: matcher, + five: 'five', + }, + { + six: 'six', + seven: 'seven', + }, + ], + eight: [{nine: matcher}, {ten: matcher}], + }, + }); + + // Ensure original target is not modified + expect(target).toStrictEqual({ + data: { + one: 'one', + two: 'two', + three: [ + { + four: 'four', + five: 'five', + }, + { + six: 'six', + seven: 'seven', + }, + ], + eight: [{nine: 'nine'}], + }, + }); + /* eslint-enable sort-keys */ }); }); diff --git a/packages/jest-snapshot/src/utils.ts b/packages/jest-snapshot/src/utils.ts index 992fa72926d0..d0e4b20eb049 100644 --- a/packages/jest-snapshot/src/utils.ts +++ b/packages/jest-snapshot/src/utils.ts @@ -178,6 +178,21 @@ export const saveSnapshotFile = ( ); }; +const deepMergeArray = (target: Array, source: Array) => { + // Clone target + const mergedOutput = target.slice(); + + source.forEach((element, index) => { + if (typeof mergedOutput[index] === 'undefined') { + mergedOutput[index] = element; + } else { + mergedOutput[index] = deepMerge(target[index], element); + } + }); + + return mergedOutput; +}; + export const deepMerge = (target: any, source: any) => { const mergedOutput = {...target}; if (isObject(target) && isObject(source)) { @@ -185,6 +200,8 @@ export const deepMerge = (target: any, source: any) => { if (isObject(source[key]) && !source[key].$$typeof) { if (!(key in target)) Object.assign(mergedOutput, {[key]: source[key]}); else mergedOutput[key] = deepMerge(target[key], source[key]); + } else if (Array.isArray(source[key])) { + mergedOutput[key] = deepMergeArray(target[key], source[key]); } else { Object.assign(mergedOutput, {[key]: source[key]}); }