Skip to content

Commit 87cb768

Browse files
committedSep 24, 2023
fix: Handle non string keys in isObject
1 parent 9b50fe4 commit 87cb768

File tree

2 files changed

+65
-10
lines changed

2 files changed

+65
-10
lines changed
 

‎src/expectation/it.ts

+32-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import { printExpected } from 'jest-matcher-utils';
22
import {
33
isEqual,
4-
isMatchWith,
54
isObjectLike,
65
isPlainObject,
76
isUndefined,
@@ -116,12 +115,35 @@ type DeepPartial<T> = T extends object
116115
? { [K in keyof T]?: DeepPartial<T[K]> }
117116
: T;
118117

118+
const isMatch = (actual: object, expected: object): boolean =>
119+
Reflect.ownKeys(expected).every((key) => {
120+
// @ts-expect-error
121+
const right = expected[key];
122+
// @ts-expect-error
123+
const left = actual?.[key];
124+
125+
if (!left) {
126+
return false;
127+
}
128+
129+
if (isMatcher(right)) {
130+
return right.matches(left);
131+
}
132+
133+
if (isPlainObject(right)) {
134+
return isMatch(left, right);
135+
}
136+
137+
return isEqual(left, right);
138+
});
139+
119140
/**
120141
* Recursively match an object.
121142
*
122143
* Supports nested matcher.
123144
*
124-
* @param partial An optional subset of the expected objected.
145+
* @param partial An optional subset of the expected object.
146+
* Object like values, e.g. classes and arrays, will not be matched against this.
125147
*
126148
* @example
127149
* const fn = mock<(foo: { x: number, y: number }) => number>();
@@ -142,15 +164,15 @@ const isObject = <T extends object, K extends DeepPartial<T>>(
142164
return false;
143165
}
144166

145-
return isMatchWith(actual, partial || {}, (argValue, partialValue) => {
146-
if (isMatcher(partialValue)) {
147-
return partialValue.matches(argValue);
148-
}
167+
if (!partial) {
168+
return true;
169+
}
149170

150-
// Let lodash handle it otherwise.
151-
return undefined;
152-
}),
153-
{ toJSON: () => (partial ? `object(${printExpected(partial)})` : 'object') }
171+
return isMatch(actual, partial);
172+
},
173+
{
174+
toJSON: () => (partial ? `object(${printExpected(partial)})` : 'object'),
175+
}
154176
);
155177

156178
/**

‎src/expectation/matcher.spec.ts

+33
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,17 @@ describe('It', () => {
616616
).toBeTruthy();
617617
});
618618

619+
it('should match objects with non string keys', () => {
620+
const foo = Symbol('foo');
621+
622+
expect(
623+
It.isObject({ [foo]: 'bar' }).matches({ [foo]: 'bar' })
624+
).toBeTruthy();
625+
expect(
626+
It.isObject({ [foo]: 'bar' }).matches({ [foo]: 'baz' })
627+
).toBeFalsy();
628+
});
629+
619630
it('should deep match nested objects', () => {
620631
expect(
621632
It.isObject({ foo: { bar: { baz: 42 } } }).matches({
@@ -630,6 +641,28 @@ describe('It', () => {
630641
).toBeFalsy();
631642
});
632643

644+
it('should not deep match object like values', () => {
645+
class Foo {
646+
foo = 'bar';
647+
}
648+
expect(It.isObject({ foo: 'bar' }).matches(new Foo())).toBeFalsy();
649+
expect(It.isObject({ 0: 1 }).matches([1])).toBeFalsy();
650+
});
651+
652+
it('should deep match nested objects with arrays', () => {
653+
expect(
654+
It.isObject({ foo: [1, 2, 3] }).matches({
655+
foo: [1, 2, 3],
656+
})
657+
).toBeTruthy();
658+
659+
expect(
660+
It.isObject({ foo: [1, 2] }).matches({
661+
foo: [1, 2, 3],
662+
})
663+
).toBeFalsy();
664+
});
665+
633666
it('should match against extra undefined keys', () => {
634667
expect(It.isObject({}).matches({ key: undefined })).toBeTruthy();
635668
});

0 commit comments

Comments
 (0)
Please sign in to comment.