Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 25 additions & 9 deletions packages/expect/src/jest-asymmetric-matchers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,7 @@ export class Anything extends AsymmetricMatcher<void> {
}

export class ObjectContaining extends AsymmetricMatcher<
Record<string, unknown>
Record<string | symbol | number, unknown>
> {
constructor(sample: Record<string, unknown>, inverse = false) {
super(sample, inverse)
Expand All @@ -126,18 +126,27 @@ export class ObjectContaining extends AsymmetricMatcher<
return obj.constructor.prototype
}

hasProperty(obj: object | null, property: string): boolean {
hasProperty(obj: object | null, property: string | symbol): boolean {
if (!obj) {
return false
}

if (Object.prototype.hasOwnProperty.call(obj, property)) {
if (Object.hasOwn(obj, property)) {
return true
}

return this.hasProperty(this.getPrototype(obj), property)
}

getProperties(obj: object): (string | symbol)[] {
return [
...Object.keys(obj),
...Object.getOwnPropertySymbols(obj).filter(
s => Object.getOwnPropertyDescriptor(obj, s)?.enumerable,
),
]
}

asymmetricMatch(other: any): boolean {
if (typeof this.sample !== 'object') {
throw new TypeError(
Expand All @@ -149,14 +158,21 @@ export class ObjectContaining extends AsymmetricMatcher<
let result = true

const matcherContext = this.getMatcherContext()
for (const property in this.sample) {
const properties = this.getProperties(this.sample)
for (const property of properties) {
if (
!this.hasProperty(other, property)
|| !equals(
this.sample[property],
other[property],
matcherContext.customTesters,
)
) {
result = false
break
}
const value = Object.getOwnPropertyDescriptor(this.sample, property)?.value ?? this.sample[property]
const otherValue = Object.getOwnPropertyDescriptor(other, property)?.value ?? other[property]
if (!equals(
value,
otherValue,
matcherContext.customTesters,
)
) {
result = false
break
Expand Down
4 changes: 4 additions & 0 deletions test/core/test/jest-expect.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,10 @@ describe('jest-expect', () => {
expect(undefined).not.toEqual(expect.anything())
expect({ a: 0, b: 0 }).toEqual(expect.objectContaining({ a: 0 }))
expect({ a: 0, b: 0 }).not.toEqual(expect.objectContaining({ z: 0 }))
// objectContaining with symbol key
const symbolForObjectContaining = Symbol('symbolForObjectContaining')
expect({ [symbolForObjectContaining]: 0 }).toEqual(expect.objectContaining({ [symbolForObjectContaining]: 0 }))
expect({ [symbolForObjectContaining]: 0 }).not.toEqual(expect.objectContaining({ [symbolForObjectContaining]: 1 }))
expect(0).toEqual(expect.any(Number))
expect('string').toEqual(expect.any(String))
expect('string').not.toEqual(expect.any(Number))
Expand Down
Loading