Skip to content

Commit 93c1967

Browse files
bakasmariusmrazauskasMarius Bakas
authored
fix(snapshot): bring back support for array property matchers (#14025)
Co-authored-by: Tom Mrazauskas <[email protected]> Co-authored-by: Marius Bakas <[email protected]>
1 parent c40ef21 commit 93c1967

File tree

6 files changed

+142
-69
lines changed

6 files changed

+142
-69
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
### Features
44

5+
- `[jest-snapshot]` Support arrays as property matchers ([#14025](https://github.com/facebook/jest/pull/14025))
6+
57
### Fixes
68

79
- `[jest-config]` Handle frozen config object ([#14054](https://github.com/facebook/jest/pull/14054))

packages/jest-snapshot/src/__tests__/__snapshots__/printSnapshot.test.ts.snap

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,5 @@
11
// Jest Snapshot v1, https://goo.gl/fbAQLP
22

3-
exports[`matcher error toMatchInlineSnapshot Expected properties must be an object (array) with snapshot 1`] = `
4-
<d>expect(</><r>received</><d>).</>toMatchInlineSnapshot<d>(</><g>properties</><d>, </>snapshot<d>)</>
5-
6-
<b>Matcher error</>: Expected <g>properties</> must be an object
7-
8-
Expected properties has type: array
9-
Expected properties has value: <g>[]</>
10-
`;
11-
123
exports[`matcher error toMatchInlineSnapshot Expected properties must be an object (non-null) without snapshot 1`] = `
134
<d>expect(</><r>received</><d>).</>toMatchInlineSnapshot<d>(</><g>properties</><d>)</>
145

@@ -41,15 +32,6 @@ exports[`matcher error toMatchInlineSnapshot Snapshot matchers cannot be used wi
4132
<b>Matcher error</>: Snapshot matchers cannot be used with <b>not</>
4233
`;
4334

44-
exports[`matcher error toMatchSnapshot Expected properties must be an object (array) 1`] = `
45-
<d>expect(</><r>received</><d>).</>toMatchSnapshot<d>(</><g>properties</><d>)</>
46-
47-
<b>Matcher error</>: Expected <g>properties</> must be an object
48-
49-
Expected properties has type: array
50-
Expected properties has value: <g>[]</>
51-
`;
52-
5335
exports[`matcher error toMatchSnapshot Expected properties must be an object (non-null) 1`] = `
5436
<d>expect(</><r>received</><d>).</>toMatchSnapshot<d>(</><g>properties</><d>)</>
5537

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

Lines changed: 60 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -246,19 +246,6 @@ describe('matcher error', () => {
246246
}).toThrowErrorMatchingSnapshot();
247247
});
248248

249-
test('Expected properties must be an object (array) with snapshot', () => {
250-
const context = {
251-
isNot: false,
252-
promise: '',
253-
} as Context;
254-
const properties: Array<unknown> = [];
255-
const snapshot = '';
256-
257-
expect(() => {
258-
toMatchInlineSnapshot.call(context, received, properties, snapshot);
259-
}).toThrowErrorMatchingSnapshot();
260-
});
261-
262249
test('Inline snapshot must be a string', () => {
263250
const context = {
264251
isNot: false,
@@ -333,18 +320,6 @@ describe('matcher error', () => {
333320
}).toThrowErrorMatchingSnapshot();
334321
});
335322

336-
test('Expected properties must be an object (array)', () => {
337-
const context = {
338-
isNot: false,
339-
promise: '',
340-
} as Context;
341-
const properties: Array<unknown> = [];
342-
343-
expect(() => {
344-
toMatchSnapshot.call(context, received, properties);
345-
}).toThrowErrorMatchingSnapshot();
346-
});
347-
348323
describe('received value must be an object', () => {
349324
const context = {
350325
currentTestName: '',
@@ -785,6 +760,66 @@ describe('pass true', () => {
785760
) as SyncExpectationResult;
786761
expect(pass).toBe(true);
787762
});
763+
764+
test('array', () => {
765+
const context = {
766+
equals: () => true,
767+
isNot: false,
768+
promise: '',
769+
snapshotState: {
770+
match() {
771+
return {
772+
expected: [],
773+
pass: true,
774+
};
775+
},
776+
},
777+
utils: {
778+
iterableEquality: () => [],
779+
subsetEquality: () => [],
780+
},
781+
} as unknown as Context;
782+
const received: Array<unknown> = [];
783+
const properties: Array<unknown> = [];
784+
785+
const {pass} = toMatchSnapshot.call(
786+
context,
787+
received,
788+
properties,
789+
) as SyncExpectationResult;
790+
expect(pass).toBe(true);
791+
});
792+
});
793+
794+
describe('toMatchInlineSnapshot', () => {
795+
test('array', () => {
796+
const context = {
797+
equals: () => true,
798+
isNot: false,
799+
promise: '',
800+
snapshotState: {
801+
match() {
802+
return {
803+
expected: [],
804+
pass: true,
805+
};
806+
},
807+
},
808+
utils: {
809+
iterableEquality: () => [],
810+
subsetEquality: () => [],
811+
},
812+
} as unknown as Context;
813+
const received: Array<unknown> = [];
814+
const properties: Array<unknown> = [];
815+
816+
const {pass} = toMatchInlineSnapshot.call(
817+
context,
818+
received,
819+
properties,
820+
) as SyncExpectationResult;
821+
expect(pass).toBe(true);
822+
});
788823
});
789824
});
790825

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

Lines changed: 72 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -303,7 +303,12 @@ describe('removeLinesBeforeExternalMatcherTrap', () => {
303303
});
304304

305305
describe('DeepMerge with property matchers', () => {
306-
const matcher = expect.any(String);
306+
const matcherString = expect.any(String);
307+
const matcherNumber = expect.any(Number);
308+
const matcherObject = expect.any(Object);
309+
const matcherArray = expect.any(Array);
310+
const matcherBoolean = expect.any(Boolean);
311+
const matcherAnything = expect.anything();
307312

308313
it.each(
309314
/* eslint-disable sort-keys */
@@ -321,14 +326,14 @@ describe('DeepMerge with property matchers', () => {
321326
// Matchers
322327
{
323328
data: {
324-
two: matcher,
329+
two: matcherString,
325330
},
326331
},
327332
// Expected
328333
{
329334
data: {
330335
one: 'one',
331-
two: matcher,
336+
two: matcherString,
332337
},
333338
},
334339
],
@@ -358,32 +363,32 @@ describe('DeepMerge with property matchers', () => {
358363
data: {
359364
one: [
360365
{
361-
two: matcher,
366+
two: matcherString,
362367
},
363368
],
364369
six: [
365-
{seven: matcher},
370+
{seven: matcherString},
366371
// Include an array element not present in the target
367-
{eight: matcher},
372+
{eight: matcherString},
368373
],
369-
nine: [[{ten: matcher}]],
374+
nine: [[{ten: matcherString}]],
370375
},
371376
},
372377
// Expected
373378
{
374379
data: {
375380
one: [
376381
{
377-
two: matcher,
382+
two: matcherString,
378383
three: 'three',
379384
},
380385
{
381386
four: 'four',
382387
five: 'five',
383388
},
384389
],
385-
six: [{seven: matcher}, {eight: matcher}],
386-
nine: [[{ten: matcher}]],
390+
six: [{seven: matcherString}, {eight: matcherString}],
391+
nine: [[{ten: matcherString}]],
387392
},
388393
},
389394
],
@@ -402,18 +407,18 @@ describe('DeepMerge with property matchers', () => {
402407
// Matchers
403408
{
404409
data: {
405-
one: [matcher],
410+
one: [matcherString],
406411
two: ['two'],
407-
three: [matcher],
412+
three: [matcherString],
408413
five: 'five',
409414
},
410415
},
411416
// Expected
412417
{
413418
data: {
414-
one: [matcher],
419+
one: [matcherString],
415420
two: ['two'],
416-
three: [matcher, 'four'],
421+
three: [matcherString, 'four'],
417422
five: 'five',
418423
},
419424
},
@@ -424,19 +429,68 @@ describe('DeepMerge with property matchers', () => {
424429
// Target
425430
[{name: 'one'}, {name: 'two'}, {name: 'three'}],
426431
// Matchers
427-
[{name: 'one'}, {name: matcher}, {name: matcher}],
432+
[{name: 'one'}, {name: matcherString}, {name: matcherString}],
428433
// Expected
429-
[{name: 'one'}, {name: matcher}, {name: matcher}],
434+
[{name: 'one'}, {name: matcherString}, {name: matcherString}],
435+
],
436+
437+
[
438+
'an array of different types',
439+
// Target
440+
[
441+
5,
442+
'some words',
443+
[],
444+
{},
445+
true,
446+
false,
447+
5,
448+
'some words',
449+
[],
450+
{},
451+
true,
452+
false,
453+
],
454+
// Matchers
455+
[
456+
matcherNumber,
457+
matcherString,
458+
matcherArray,
459+
matcherObject,
460+
matcherBoolean,
461+
matcherBoolean,
462+
matcherAnything,
463+
matcherAnything,
464+
matcherAnything,
465+
matcherAnything,
466+
matcherAnything,
467+
matcherAnything,
468+
],
469+
// Expected
470+
[
471+
matcherNumber,
472+
matcherString,
473+
matcherArray,
474+
matcherObject,
475+
matcherBoolean,
476+
matcherBoolean,
477+
matcherAnything,
478+
matcherAnything,
479+
matcherAnything,
480+
matcherAnything,
481+
matcherAnything,
482+
matcherAnything,
483+
],
430484
],
431485

432486
[
433487
'an array of arrays',
434488
// Target
435489
[['one'], ['two'], ['three']],
436490
// Matchers
437-
[['one'], [matcher], [matcher]],
491+
[['one'], [matcherString], [matcherString]],
438492
// Expected
439-
[['one'], [matcher], [matcher]],
493+
[['one'], [matcherString], [matcherString]],
440494
],
441495
],
442496
/* eslint-enable sort-keys */

packages/jest-snapshot/src/index.ts

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -163,11 +163,7 @@ export const toMatchSnapshot: MatcherFunctionWithContext<
163163
if (length === 2 && typeof propertiesOrHint === 'string') {
164164
hint = propertiesOrHint;
165165
} else if (length >= 2) {
166-
if (
167-
Array.isArray(propertiesOrHint) ||
168-
typeof propertiesOrHint !== 'object' ||
169-
propertiesOrHint === null
170-
) {
166+
if (typeof propertiesOrHint !== 'object' || propertiesOrHint === null) {
171167
const options: MatcherHintOptions = {
172168
isNot: this.isNot,
173169
promise: this.promise,
@@ -234,7 +230,6 @@ export const toMatchInlineSnapshot: MatcherFunctionWithContext<
234230
}
235231

236232
if (
237-
Array.isArray(propertiesOrSnapshot) ||
238233
typeof propertiesOrSnapshot !== 'object' ||
239234
propertiesOrSnapshot === null
240235
) {

packages/jest-snapshot/src/utils.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -220,15 +220,20 @@ export const saveSnapshotFile = (
220220
);
221221
};
222222

223+
const isAnyOrAnything = (input: object) =>
224+
'$$typeof' in input &&
225+
input.$$typeof === Symbol.for('jest.asymmetricMatcher') &&
226+
['Any', 'Anything'].includes(input.constructor.name);
227+
223228
const deepMergeArray = (target: Array<any>, source: Array<any>) => {
224229
const mergedOutput = Array.from(target);
225230

226231
source.forEach((sourceElement, index) => {
227232
const targetElement = mergedOutput[index];
228233

229-
if (Array.isArray(target[index])) {
234+
if (Array.isArray(target[index]) && Array.isArray(sourceElement)) {
230235
mergedOutput[index] = deepMergeArray(target[index], sourceElement);
231-
} else if (isObject(targetElement)) {
236+
} else if (isObject(targetElement) && !isAnyOrAnything(sourceElement)) {
232237
mergedOutput[index] = deepMerge(target[index], sourceElement);
233238
} else {
234239
// Source does not exist in target or target is primitive and cannot be deep merged

0 commit comments

Comments
 (0)