@@ -640,8 +640,26 @@ function setShouldKill (value) {
640640 } )
641641}
642642
643- // we use our own assertObjectContains, to account for any types
644- const assertObjectContains = function assertObjectContains ( actual , expected , msg ) {
643+ // Check if value contains any asymmetric matchers (ANY_STRING, ANY_NUMBER, ANY_VALUE)
644+ function containsAsymmetricMatchers ( value ) {
645+ if ( value === ANY_STRING || value === ANY_NUMBER || value === ANY_VALUE ) {
646+ return true
647+ }
648+ if ( value !== null && typeof value === 'object' ) {
649+ for ( const val of Object . values ( value ) ) {
650+ if ( containsAsymmetricMatchers ( val ) ) return true
651+ }
652+ }
653+ if ( Array . isArray ( value ) ) {
654+ for ( const item of value ) {
655+ if ( containsAsymmetricMatchers ( item ) ) return true
656+ }
657+ }
658+ return false
659+ }
660+
661+ // Custom assertObjectContains that supports asymmetric matchers (ANY_STRING, ANY_NUMBER, ANY_VALUE)
662+ function assertObjectContainsWithMatchers ( actual , expected , msg ) {
645663 if ( Array . isArray ( expected ) ) {
646664 assert . ok ( Array . isArray ( actual ) , `${ msg ?? '' } Expected array but got ${ inspect ( actual ) } ` )
647665 let startIndex = 0
@@ -651,7 +669,7 @@ const assertObjectContains = function assertObjectContains (actual, expected, ms
651669 const actualItem = actual [ i ]
652670 try {
653671 if ( expectedItem !== null && typeof expectedItem === 'object' ) {
654- assertObjectContains ( actualItem , expectedItem , msg )
672+ assertObjectContainsWithMatchers ( actualItem , expectedItem )
655673 } else {
656674 assert . strictEqual ( actualItem , expectedItem , msg )
657675 }
@@ -676,17 +694,40 @@ const assertObjectContains = function assertObjectContains (actual, expected, ms
676694 } else if ( val === ANY_VALUE ) {
677695 assert . ok ( actual [ key ] !== undefined , `Expected ${ key } to be present but it was undefined` )
678696 } else if ( val !== null && typeof val === 'object' ) {
679- assert . ok ( Object . hasOwn ( actual , key ) )
680- assert . notStrictEqual ( actual [ key ] , null )
681- assert . strictEqual ( typeof actual [ key ] , 'object' )
682- assertObjectContains ( actual [ key ] , val )
697+ assert . ok ( Object . hasOwn ( actual , key ) , `Expected object to have key ' ${ key } '` )
698+ assert . notStrictEqual ( actual [ key ] , null , `Expected ${ key } to not be null` )
699+ assert . strictEqual ( typeof actual [ key ] , 'object' , `Expected ${ key } to be an object but got ${ typeof actual [ key ] } ` )
700+ assertObjectContainsWithMatchers ( actual [ key ] , val )
683701 } else {
684702 assert . ok ( actual , msg )
685- assert . strictEqual ( actual [ key ] , expected [ key ] , msg )
703+ assert . strictEqual ( actual [ key ] , expected [ key ] , `Expected ${ key } to be ${ expected [ key ] } but got ${ actual [ key ] } ` )
686704 }
687705 }
688706}
689707
708+ // Use native partialDeepStrictEqual when available and no asymmetric matchers,
709+ // otherwise fall back to custom implementation for better asymmetric matcher support
710+ // @ts -expect-error assert.partialDeepStrictEqual does not exist on older Node.js versions
711+ // eslint-disable-next-line n/no-unsupported-features/node-builtins
712+ const nativePartialDeepStrictEqual = assert . partialDeepStrictEqual
713+
714+ const assertObjectContains = function assertObjectContains ( actual , expected , msg ) {
715+ // If expected contains asymmetric matchers, use our custom implementation
716+ if ( containsAsymmetricMatchers ( expected ) ) {
717+ assertObjectContainsWithMatchers ( actual , expected , msg )
718+ return
719+ }
720+
721+ // Use native partialDeepStrictEqual if available (better error output)
722+ if ( nativePartialDeepStrictEqual ) {
723+ nativePartialDeepStrictEqual ( actual , expected , msg )
724+ return
725+ }
726+
727+ // Fallback for older Node.js versions without asymmetric matchers
728+ assertObjectContainsWithMatchers ( actual , expected , msg )
729+ }
730+
690731/**
691732 * @param {string } actual
692733 * @param {string } [msg]
0 commit comments