Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(expect): objectContaining traversing non vanilla Objects #10720

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
11 changes: 11 additions & 0 deletions packages/expect/src/__tests__/asymmetricMatchers.test.ts
Expand Up @@ -169,6 +169,17 @@ test('ObjectContaining matches', () => {
objectContaining({first: objectContaining({second: {}})}).asymmetricMatch({
first: {second: {}},
}),
objectContaining({foo: Buffer.from('foo')}).asymmetricMatch({
foo: Buffer.from('foo'),
jest: 'jest',
}),
objectContaining({foo: {bar: [Buffer.from('foo')]}}).asymmetricMatch({
foo: {
bar: [Buffer.from('foo'), 1],
qux: 'qux',
},
jest: 'jest',
}),
].forEach(test => {
jestExpect(test).toEqual(true);
});
Expand Down
20 changes: 15 additions & 5 deletions packages/expect/src/asymmetricMatchers.ts
Expand Up @@ -178,11 +178,21 @@ class ObjectContaining extends AsymmetricMatcher<Record<string, unknown>> {
return true;
} else {
for (const property in this.sample) {
const expected =
typeof this.sample[property] === 'object' &&
!(this.sample[property] instanceof AsymmetricMatcher)
? objectContaining(this.sample[property] as Record<string, unknown>)
: this.sample[property];
let expected = this.sample[property];

if (typeof this.sample[property] === 'object') {
const samplePropertyPrototype = Object.getPrototypeOf(
this.sample[property],
);
if (
samplePropertyPrototype === Object.prototype ||
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this might break for cases hit by #2549, but 🤞 it does not

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it does break, afaik the next best ways to check for "plain objects" are Object.getPrototypeOf(sample)?.constructor?.name === 'Object' and Object.prototype.toString.call(sample) .... These are imprecise, but at least they work across realms.

Array.isArray() could be substituted for the Array.prototype check to fix that case.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I can confirm that this is broken for cross-realm inputs, like in #2549. ninevra@0553005 adds a failing test on top of this PR to demonstrate the issue.

samplePropertyPrototype === Array.prototype
) {
expected = objectContaining(
this.sample[property] as Record<string, unknown>,
);
}
}

if (
!hasProperty(other, property) ||
Expand Down