Skip to content

Commit

Permalink
fix: Handle non string keys in isObject
Browse files Browse the repository at this point in the history
  • Loading branch information
NiGhTTraX committed Sep 24, 2023
1 parent 9b50fe4 commit 87cb768
Show file tree
Hide file tree
Showing 2 changed files with 65 additions and 10 deletions.
42 changes: 32 additions & 10 deletions src/expectation/it.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { printExpected } from 'jest-matcher-utils';
import {
isEqual,
isMatchWith,
isObjectLike,
isPlainObject,
isUndefined,
Expand Down Expand Up @@ -116,12 +115,35 @@ type DeepPartial<T> = T extends object
? { [K in keyof T]?: DeepPartial<T[K]> }
: T;

const isMatch = (actual: object, expected: object): boolean =>
Reflect.ownKeys(expected).every((key) => {
// @ts-expect-error
const right = expected[key];
// @ts-expect-error
const left = actual?.[key];

if (!left) {
return false;
}

if (isMatcher(right)) {
return right.matches(left);
}

if (isPlainObject(right)) {
return isMatch(left, right);
}

return isEqual(left, right);
});

/**
* Recursively match an object.
*
* Supports nested matcher.
*
* @param partial An optional subset of the expected objected.
* @param partial An optional subset of the expected object.
* Object like values, e.g. classes and arrays, will not be matched against this.
*
* @example
* const fn = mock<(foo: { x: number, y: number }) => number>();
Expand All @@ -142,15 +164,15 @@ const isObject = <T extends object, K extends DeepPartial<T>>(
return false;
}

return isMatchWith(actual, partial || {}, (argValue, partialValue) => {
if (isMatcher(partialValue)) {
return partialValue.matches(argValue);
}
if (!partial) {
return true;
}

// Let lodash handle it otherwise.
return undefined;
}),
{ toJSON: () => (partial ? `object(${printExpected(partial)})` : 'object') }
return isMatch(actual, partial);
},
{
toJSON: () => (partial ? `object(${printExpected(partial)})` : 'object'),
}
);

/**
Expand Down
33 changes: 33 additions & 0 deletions src/expectation/matcher.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,17 @@ describe('It', () => {
).toBeTruthy();
});

it('should match objects with non string keys', () => {
const foo = Symbol('foo');

expect(
It.isObject({ [foo]: 'bar' }).matches({ [foo]: 'bar' })
).toBeTruthy();
expect(
It.isObject({ [foo]: 'bar' }).matches({ [foo]: 'baz' })
).toBeFalsy();
});

it('should deep match nested objects', () => {
expect(
It.isObject({ foo: { bar: { baz: 42 } } }).matches({
Expand All @@ -630,6 +641,28 @@ describe('It', () => {
).toBeFalsy();
});

it('should not deep match object like values', () => {
class Foo {
foo = 'bar';
}
expect(It.isObject({ foo: 'bar' }).matches(new Foo())).toBeFalsy();
expect(It.isObject({ 0: 1 }).matches([1])).toBeFalsy();
});

it('should deep match nested objects with arrays', () => {
expect(
It.isObject({ foo: [1, 2, 3] }).matches({
foo: [1, 2, 3],
})
).toBeTruthy();

expect(
It.isObject({ foo: [1, 2] }).matches({
foo: [1, 2, 3],
})
).toBeFalsy();
});

it('should match against extra undefined keys', () => {
expect(It.isObject({}).matches({ key: undefined })).toBeTruthy();
});
Expand Down

0 comments on commit 87cb768

Please sign in to comment.