diff --git a/packages/expect/src/jest-utils.ts b/packages/expect/src/jest-utils.ts index 218c2759a4d6..da895b9399fb 100644 --- a/packages/expect/src/jest-utils.ts +++ b/packages/expect/src/jest-utils.ts @@ -783,8 +783,12 @@ export function getObjectSubset( }) } - for (const key of getObjectKeys(object)) { - if (hasPropertyInObject(subset, key)) { + // Iterate subset's keys (not object's own enumerable keys) so that + // inherited/accessor properties -- e.g. DOM element properties + // like `tagName`, which live on the prototype chain -- are still + // picked up via the `in` operator. See #6939. + for (const key of getObjectKeys(subset)) { + if (key in object) { trimmed[key] = seenReferences.has(object[key]) ? seenReferences.get(object[key]) : getObjectSubsetWithContext(seenReferences)( @@ -792,18 +796,20 @@ export function getObjectSubset( subset[key], ) } - else { - if (!seenReferences.has(object[key])) { - stripped += 1 - if (isObject(object[key])) { - stripped += getObjectKeys(object[key]).length - } - - getObjectSubsetWithContext(seenReferences)( - object[key], - subset[key], - ) + } + + // Preserve "N properties omitted from actual" messaging by still + // counting object's own extra keys not present in subset. + for (const key of getObjectKeys(object)) { + if (!hasPropertyInObject(subset, key) && !seenReferences.has(object[key])) { + stripped += 1 + if (isObject(object[key])) { + stripped += getObjectKeys(object[key]).length } + getObjectSubsetWithContext(seenReferences)( + object[key], + subset[key], + ) } } diff --git a/test/unit/test/jest-expect.test.ts b/test/unit/test/jest-expect.test.ts index c550e5c72ca0..52e6087829c8 100644 --- a/test/unit/test/jest-expect.test.ts +++ b/test/unit/test/jest-expect.test.ts @@ -1747,6 +1747,37 @@ it('toMatchObject error diff', () => { }", ] `) + // https://github.com/vitest-dev/vitest/issues/6939 + // properties inherited via the prototype chain (e.g. DOM elements) should + // still be picked up when building the toMatchObject diff, instead of + // falling back to showing the raw object. + { + const proto = { + get tagName() { + return 'DIV' + }, + } + const domLike = Object.create(proto) + domLike.id = 'root' + + expect(domLike).toMatchObject({ tagName: 'DIV', id: 'root' }) + + expect(getError(() => + expect(domLike).toMatchObject({ tagName: 'SPAN', id: 'root' }), + )).toMatchInlineSnapshot(` + [ + "expected { id: 'root' } to match object { tagName: 'SPAN', id: 'root' }", + "- Expected + + Received + + { + "id": "root", + - "tagName": "SPAN", + + "tagName": "DIV", + }", + ] + `) + } }) it('toHaveProperty error diff', () => {