Skip to content

Commit

Permalink
fix: toBe/toEqual/toMatchObject jest compatability (#607)
Browse files Browse the repository at this point in the history
* fix: toMatchObject compatible with jest

* fix: equal/eql always use jest implementation

* fix: toBe uses Object.is instead of equal to match jest implementation

* fix: toEqual uses jest equal

* fix!: remove asymmetric matchers support for chai assertions

* chore: remove chaiEqual notices

* refactor: rename asymmetricEquals to jestEquals
  • Loading branch information
sheremet-va committed Jan 23, 2022
1 parent df6bbaf commit 75113c7
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 79 deletions.
4 changes: 0 additions & 4 deletions docs/api/index.md
Expand Up @@ -249,10 +249,6 @@ When you use `test` in the top level of file, they are collected as part of the

Also, `expect` can be used statically to access matchers functions, described later, and more.

:::warning
To provide compatibility with [asymmetric matchers](#expectany), Vitest wraps `.eql` and `.equals` chai assertions, so if you need to use chai equality, you can use `.chaiEqual` matcher.
:::

### not

TODO
Expand Down
2 changes: 0 additions & 2 deletions packages/vitest/src/index.ts
Expand Up @@ -126,8 +126,6 @@ declare global {
interface Assertion<T = any> extends VitestifyAssertion<Chai.Assertion>, JestAssertion<T> {
resolves: Promisify<Assertion<T>>
rejects: Promisify<Assertion<T>>

chaiEqual<E>(expected: E): void
}
}
}
98 changes: 40 additions & 58 deletions packages/vitest/src/integrations/chai/jest-expect.ts
Expand Up @@ -3,7 +3,7 @@ import { isMockFunction } from '../jest-mock'
import { addSerializer } from '../snapshot/port/plugins'
import type { Constructable } from '../../types'
import type { ChaiPlugin, MatcherState } from './types'
import { arrayBufferEquality, equals as asymmetricEquals, hasAsymmetric, iterableEquality, sparseArrayEquality, typeEquality } from './jest-utils'
import { arrayBufferEquality, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils'
import type { AsymmetricMatcher } from './jest-asymmetric-matchers'

const MATCHERS_OBJECT = Symbol.for('matchers-object')
Expand Down Expand Up @@ -46,14 +46,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
addMethod(name)
}

// we overrides the default `.equal`, keep original `.chaiEqual` in case need
// @ts-expect-error prototype
const chaiEqual = chai.Assertion.prototype.equal
def('chaiEqual', function(...args: any[]) {
return chaiEqual.apply(this, args)
})

;(['throw', 'throws', 'Throw'] as const).forEach((m) => {
(['throw', 'throws', 'Throw'] as const).forEach((m) => {
utils.overwriteMethod(chai.Assertion.prototype, m, (_super: any) => {
return function(this: Chai.Assertion & Chai.AssertionStatic, ...args: any[]) {
const promise = utils.flag(this, 'promise')
Expand All @@ -68,51 +61,26 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
})
})

// overrides `.equal` and `.eql` to provide custom assertion for asymmetric equality
utils.overwriteMethod(chai.Assertion.prototype, 'equal', (_super: any) => {
return function(this: Chai.Assertion & Chai.AssertionStatic, ...args: any[]) {
const expected = args[0]
const actual = utils.flag(this, 'object')
if (hasAsymmetric(expected)) {
this.assert(
asymmetricEquals(actual, expected, undefined, true),
'not match with #{act}',
'should not match with #{act}',
actual,
expected,
)
}
else {
_super.apply(this, args)
}
}
})
utils.overwriteMethod(chai.Assertion.prototype, 'eql', (_super: any) => {
return function(this: Chai.Assertion & Chai.AssertionStatic, ...args: any[]) {
const expected = args[0]
const actual = utils.flag(this, 'object')
if (hasAsymmetric(expected)) {
this.assert(
asymmetricEquals(actual, expected),
'not match with #{exp}',
'should not match with #{exp}',
expected,
actual,
)
}
else {
_super.apply(this, args)
}
}
})

def('toEqual', function(expected) {
return this.eql(expected)
const actual = utils.flag(this, 'object')
const equal = jestEquals(
actual,
expected,
[iterableEquality],
)

return this.assert(
equal,
'expected #{this} to deeply equal #{exp}',
'expected #{this} to not deeply equal #{exp}',
expected,
actual,
)
})

def('toStrictEqual', function(expected) {
const obj = utils.flag(this, 'object')
const equal = asymmetricEquals(
const equal = jestEquals(
obj,
expected,
[
Expand All @@ -133,10 +101,24 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
)
})
def('toBe', function(expected) {
return this.equal(expected)
const actual = this._obj
return this.assert(
Object.is(actual, expected),
'expected #{this} to be #{exp} // Object.is equality',
'expected #{this} not to be #{exp} // Object.is equality',
expected,
actual,
)
})
def('toMatchObject', function(expected) {
return this.containSubset(expected)
const actual = this._obj
return this.assert(
jestEquals(actual, expected, [iterableEquality, subsetEquality]),
'expected #{this} to match object #{exp}',
'expected #{this} to not match object #{exp}',
expected,
actual,
)
})
def('toMatch', function(expected: string | RegExp) {
if (typeof expected === 'string')
Expand All @@ -150,7 +132,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
def('toContainEqual', function(expected) {
const obj = utils.flag(this, 'object')
const index = Array.from(obj).findIndex((item) => {
return asymmetricEquals(item, expected)
return jestEquals(item, expected)
})

this.assert(
Expand Down Expand Up @@ -291,7 +273,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
def(['toHaveBeenCalledWith', 'toBeCalledWith'], function(...args) {
const spy = getSpy(this)
const spyName = spy.getMockName()
const pass = spy.mock.calls.some(callArg => asymmetricEquals(callArg, args, [iterableEquality]))
const pass = spy.mock.calls.some(callArg => jestEquals(callArg, args, [iterableEquality]))
return this.assert(
pass,
`expected "${spyName}" to be called with arguments: #{exp}`,
Expand Down Expand Up @@ -321,7 +303,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
const nthCall = spy.mock.calls[times - 1]

this.assert(
asymmetricEquals(nthCall, args, [iterableEquality]),
jestEquals(nthCall, args, [iterableEquality]),
`expected ${ordinalOf(times)} "${spyName}" call to have been called with #{exp}`,
`expected ${ordinalOf(times)} "${spyName}" call to not have been called with #{exp}`,
args,
Expand All @@ -334,7 +316,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
const lastCall = spy.mock.calls[spy.calls.length - 1]

this.assert(
asymmetricEquals(lastCall, args, [iterableEquality]),
jestEquals(lastCall, args, [iterableEquality]),
`expected last "${spyName}" call to have been called with #{exp}`,
`expected last "${spyName}" call to not have been called with #{exp}`,
args,
Expand Down Expand Up @@ -419,7 +401,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
def(['toHaveReturnedWith', 'toReturnWith'], function(value: any) {
const spy = getSpy(this)
const spyName = spy.getMockName()
const pass = spy.mock.results.some(({ type, value: result }) => type === 'return' && asymmetricEquals(value, result))
const pass = spy.mock.results.some(({ type, value: result }) => type === 'return' && jestEquals(value, result))
this.assert(
pass,
`expected "${spyName}" to be successfully called with #{exp}`,
Expand All @@ -431,7 +413,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
const spy = getSpy(this)
const spyName = spy.getMockName()
const { value: lastResult } = spy.mock.results[spy.returns.length - 1]
const pass = asymmetricEquals(lastResult, value)
const pass = jestEquals(lastResult, value)
this.assert(
pass,
`expected last "${spyName}" call to return #{exp}`,
Expand All @@ -450,7 +432,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
if (!isNot && callType === 'throw')
chai.assert.fail(`expected ${ordinalCall} to return #{exp}, but instead it threw an error`)

const nthCallReturn = asymmetricEquals(callResult, value)
const nthCallReturn = jestEquals(callResult, value)

this.assert(
nthCallReturn,
Expand Down
16 changes: 1 addition & 15 deletions test/core/test/jest-expect.test.ts
Expand Up @@ -95,21 +95,6 @@ describe('jest-expect', () => {
expect(['Bob', 'Eve']).toEqual(expect.not.arrayContaining(['Steve']))
})

it('asymmetric matchers (chai style)', () => {
expect({ foo: 'bar' }).equal({ foo: expect.stringContaining('ba') })
expect('bar').equal(expect.stringContaining('ba'))
expect(['bar']).equal([expect.stringContaining('ba')])
expect({ foo: 'bar', bar: 'foo', hi: 'hello' }).equal({
foo: expect.stringContaining('ba'),
bar: expect.stringContaining('fo'),
hi: 'hello',
})

expect({ foo: 'bar' }).not.equal({ foo: expect.stringContaining('zoo') })
expect('bar').not.equal(expect.stringContaining('zoo'))
expect(['bar']).not.equal([expect.stringContaining('zoo')])
})

it('object', () => {
expect({}).toEqual({})
expect({ apples: 13 }).toEqual({ apples: 13 })
Expand All @@ -126,6 +111,7 @@ describe('jest-expect', () => {
expect([complex]).toMatchObject([{ foo: 1 }])
expect(complex).not.toMatchObject({ foo: 2 })
expect(complex).toMatchObject({ bar: { bar: 100 } })
expect(complex).toMatchObject({ foo: expect.any(Number) })

expect(complex).toHaveProperty('foo')
expect(complex).toHaveProperty('foo', 1)
Expand Down

0 comments on commit 75113c7

Please sign in to comment.