Skip to content

Commit 62ca17c

Browse files
timtrinidadSimenB
authored andcommitted
Reapply #7089 and handle arrays of primitives/arrays (#8408)
1 parent 43d1cf6 commit 62ca17c

File tree

2 files changed

+152
-9
lines changed

2 files changed

+152
-9
lines changed

packages/jest-snapshot/src/__tests__/utils.test.ts

Lines changed: 131 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ jest.mock('fs');
99

1010
import fs from 'fs';
1111
import path from 'path';
12+
import assert from 'assert';
1213
import chalk from 'chalk';
1314

1415
import {
@@ -199,16 +200,137 @@ test('serialize handles \\r\\n', () => {
199200
expect(serializedData).toBe('\n"<div>\n</div>"\n');
200201
});
201202

202-
describe('DeepMerge', () => {
203-
it('Correctly merges objects with property matchers', () => {
204-
const target = {data: {bar: 'bar', foo: 'foo'}};
203+
describe('DeepMerge with property matchers', () => {
204+
const matcher = expect.any(String);
205205

206-
const matcher = expect.any(String);
207-
const propertyMatchers = {data: {foo: matcher}};
206+
/* eslint-disable sort-keys */
207+
// to keep keys in numerical order rather than alphabetical
208+
const cases = [
209+
[
210+
'a nested object',
211+
// Target
212+
{
213+
data: {
214+
one: 'one',
215+
two: 'two',
216+
},
217+
},
218+
// Matchers
219+
{
220+
data: {
221+
two: matcher,
222+
},
223+
},
224+
// Expected
225+
{
226+
data: {
227+
one: 'one',
228+
two: matcher,
229+
},
230+
},
231+
],
208232

209-
const mergedOutput = deepMerge(target, propertyMatchers);
233+
[
234+
'an object with an array of objects',
235+
// Target
236+
{
237+
data: {
238+
one: [
239+
{
240+
two: 'two',
241+
three: 'three',
242+
},
243+
// Include an array element not present in the propertyMatchers
244+
{
245+
four: 'four',
246+
five: 'five',
247+
},
248+
],
249+
six: [{seven: 'seven'}],
250+
nine: [[{ten: 'ten'}]],
251+
},
252+
},
253+
// Matchers
254+
{
255+
data: {
256+
one: [
257+
{
258+
two: matcher,
259+
},
260+
],
261+
six: [
262+
{seven: matcher},
263+
// Include an array element not present in the target
264+
{eight: matcher},
265+
],
266+
nine: [[{ten: matcher}]],
267+
},
268+
},
269+
// Expected
270+
{
271+
data: {
272+
one: [
273+
{
274+
two: matcher,
275+
three: 'three',
276+
},
277+
{
278+
four: 'four',
279+
five: 'five',
280+
},
281+
],
282+
six: [{seven: matcher}, {eight: matcher}],
283+
nine: [[{ten: matcher}]],
284+
},
285+
},
286+
],
210287

211-
expect(mergedOutput).toStrictEqual({data: {bar: 'bar', foo: matcher}});
212-
expect(target).toStrictEqual({data: {bar: 'bar', foo: 'foo'}});
213-
});
288+
[
289+
'an object with an array of strings',
290+
// Target
291+
{
292+
data: {
293+
one: ['one'],
294+
two: ['two'],
295+
three: ['three', 'four'],
296+
five: ['five'],
297+
},
298+
},
299+
// Matchers
300+
{
301+
data: {
302+
one: [matcher],
303+
two: ['two'],
304+
three: [matcher],
305+
five: 'five',
306+
},
307+
},
308+
// Expected
309+
{
310+
data: {
311+
one: [matcher],
312+
two: ['two'],
313+
three: [matcher, 'four'],
314+
five: 'five',
315+
},
316+
},
317+
],
318+
];
319+
/* eslint-enable sort-keys */
320+
321+
it.each(cases)(
322+
'Correctly merges %s',
323+
(_case, target, propertyMatchers, expected) => {
324+
const originalTarget = JSON.parse(JSON.stringify(target));
325+
const mergedOutput = deepMerge(target, propertyMatchers);
326+
327+
// Use assert.deepStrictEqual() instead of expect().toStrictEqual()
328+
// since we want to actually validate that we got the matcher
329+
// rather than treat it specially the way that expect() does
330+
assert.deepStrictEqual(mergedOutput, expected);
331+
332+
// Ensure original target is not modified
333+
expect(target).toStrictEqual(originalTarget);
334+
},
335+
);
214336
});

packages/jest-snapshot/src/utils.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,13 +178,34 @@ export const saveSnapshotFile = (
178178
);
179179
};
180180

181+
const deepMergeArray = (target: Array<any>, source: Array<any>) => {
182+
const mergedOutput = Array.from(target);
183+
184+
source.forEach((sourceElement, index) => {
185+
const targetElement = mergedOutput[index];
186+
187+
if (Array.isArray(target[index])) {
188+
mergedOutput[index] = deepMergeArray(target[index], sourceElement);
189+
} else if (isObject(targetElement)) {
190+
mergedOutput[index] = deepMerge(target[index], sourceElement);
191+
} else {
192+
// Source does not exist in target or target is primitive and cannot be deep merged
193+
mergedOutput[index] = sourceElement;
194+
}
195+
});
196+
197+
return mergedOutput;
198+
};
199+
181200
export const deepMerge = (target: any, source: any) => {
182201
const mergedOutput = {...target};
183202
if (isObject(target) && isObject(source)) {
184203
Object.keys(source).forEach(key => {
185204
if (isObject(source[key]) && !source[key].$$typeof) {
186205
if (!(key in target)) Object.assign(mergedOutput, {[key]: source[key]});
187206
else mergedOutput[key] = deepMerge(target[key], source[key]);
207+
} else if (Array.isArray(source[key])) {
208+
mergedOutput[key] = deepMergeArray(target[key], source[key]);
188209
} else {
189210
Object.assign(mergedOutput, {[key]: source[key]});
190211
}

0 commit comments

Comments
 (0)