Skip to content

Commit

Permalink
Reapply #7089 and handle arrays of primitives/arrays (#8408)
Browse files Browse the repository at this point in the history
  • Loading branch information
timtrinidad authored and SimenB committed Jun 13, 2019
1 parent 43d1cf6 commit 62ca17c
Show file tree
Hide file tree
Showing 2 changed files with 152 additions and 9 deletions.
140 changes: 131 additions & 9 deletions packages/jest-snapshot/src/__tests__/utils.test.ts
Expand Up @@ -9,6 +9,7 @@ jest.mock('fs');

import fs from 'fs';
import path from 'path';
import assert from 'assert';
import chalk from 'chalk';

import {
Expand Down Expand Up @@ -199,16 +200,137 @@ test('serialize handles \\r\\n', () => {
expect(serializedData).toBe('\n"<div>\n</div>"\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);
},
);
});
21 changes: 21 additions & 0 deletions packages/jest-snapshot/src/utils.ts
Expand Up @@ -178,13 +178,34 @@ export const saveSnapshotFile = (
);
};

const deepMergeArray = (target: Array<any>, source: Array<any>) => {
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)) {
Object.keys(source).forEach(key => {
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]});
}
Expand Down

0 comments on commit 62ca17c

Please sign in to comment.