diff --git a/packages/jest-snapshot/src/__tests__/utils.test.ts b/packages/jest-snapshot/src/__tests__/utils.test.ts
index 93193ff5977c..9283eaaf9838 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 {
@@ -199,16 +200,137 @@ test('serialize handles \\r\\n', () => {
expect(serializedData).toBe('\n"
\n
"\n');
});
-describe('DeepMerge', () => {
- it('Correctly merges objects with property matchers', () => {
- const target = {data: {bar: 'bar', foo: 'foo'}};
+describe('DeepMerge with property matchers', () => {
+ const matcher = expect.any(String);
- const matcher = expect.any(String);
- const propertyMatchers = {data: {foo: matcher}};
+ /* eslint-disable sort-keys */
+ // to keep keys in numerical order rather than alphabetical
+ const cases = [
+ [
+ 'a nested object',
+ // Target
+ {
+ data: {
+ one: 'one',
+ two: 'two',
+ },
+ },
+ // Matchers
+ {
+ data: {
+ two: matcher,
+ },
+ },
+ // Expected
+ {
+ data: {
+ one: 'one',
+ two: matcher,
+ },
+ },
+ ],
- const mergedOutput = deepMerge(target, propertyMatchers);
+ [
+ 'an object with an array of objects',
+ // Target
+ {
+ data: {
+ one: [
+ {
+ two: 'two',
+ three: 'three',
+ },
+ // Include an array element not present in the propertyMatchers
+ {
+ four: 'four',
+ five: 'five',
+ },
+ ],
+ six: [{seven: 'seven'}],
+ nine: [[{ten: 'ten'}]],
+ },
+ },
+ // Matchers
+ {
+ data: {
+ one: [
+ {
+ two: matcher,
+ },
+ ],
+ six: [
+ {seven: matcher},
+ // Include an array element not present in the target
+ {eight: matcher},
+ ],
+ nine: [[{ten: matcher}]],
+ },
+ },
+ // Expected
+ {
+ data: {
+ one: [
+ {
+ two: matcher,
+ three: 'three',
+ },
+ {
+ four: 'four',
+ five: 'five',
+ },
+ ],
+ six: [{seven: matcher}, {eight: matcher}],
+ nine: [[{ten: matcher}]],
+ },
+ },
+ ],
- expect(mergedOutput).toStrictEqual({data: {bar: 'bar', foo: matcher}});
- expect(target).toStrictEqual({data: {bar: 'bar', foo: 'foo'}});
- });
+ [
+ 'an object with an array of strings',
+ // Target
+ {
+ data: {
+ one: ['one'],
+ two: ['two'],
+ three: ['three', 'four'],
+ five: ['five'],
+ },
+ },
+ // Matchers
+ {
+ data: {
+ one: [matcher],
+ two: ['two'],
+ three: [matcher],
+ five: 'five',
+ },
+ },
+ // Expected
+ {
+ data: {
+ one: [matcher],
+ two: ['two'],
+ three: [matcher, 'four'],
+ five: 'five',
+ },
+ },
+ ],
+ ];
+ /* eslint-enable sort-keys */
+
+ it.each(cases)(
+ 'Correctly merges %s',
+ (_case, target, propertyMatchers, expected) => {
+ const originalTarget = JSON.parse(JSON.stringify(target));
+ const mergedOutput = deepMerge(target, propertyMatchers);
+
+ // 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, expected);
+
+ // Ensure original target is not modified
+ expect(target).toStrictEqual(originalTarget);
+ },
+ );
});
diff --git a/packages/jest-snapshot/src/utils.ts b/packages/jest-snapshot/src/utils.ts
index f1f7009b1115..1a1524d2e039 100644
--- a/packages/jest-snapshot/src/utils.ts
+++ b/packages/jest-snapshot/src/utils.ts
@@ -178,6 +178,25 @@ export const saveSnapshotFile = (
);
};
+const deepMergeArray = (target: Array, source: Array) => {
+ const mergedOutput = Array.from(target);
+
+ source.forEach((sourceElement, index) => {
+ const targetElement = mergedOutput[index];
+
+ if (Array.isArray(target[index])) {
+ mergedOutput[index] = deepMergeArray(target[index], sourceElement);
+ } else if (isObject(targetElement)) {
+ mergedOutput[index] = deepMerge(target[index], sourceElement);
+ } else {
+ // Source does not exist in target or target is primitive and cannot be deep merged
+ mergedOutput[index] = sourceElement;
+ }
+ });
+
+ return mergedOutput;
+};
+
export const deepMerge = (target: any, source: any) => {
const mergedOutput = {...target};
if (isObject(target) && isObject(source)) {
@@ -185,6 +204,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]});
}