diff --git a/packages/expect/src/assertions/index.ts b/packages/expect/src/assertions/index.ts new file mode 100644 index 000000000000..d826ed47796f --- /dev/null +++ b/packages/expect/src/assertions/index.ts @@ -0,0 +1,5 @@ +import toMatchObject from './toMatchObject' + +export default [ + toMatchObject, +] diff --git a/packages/expect/src/assertions/toMatchObject.ts b/packages/expect/src/assertions/toMatchObject.ts new file mode 100644 index 000000000000..70bb362b74ba --- /dev/null +++ b/packages/expect/src/assertions/toMatchObject.ts @@ -0,0 +1,13 @@ +import { defineAssertion } from '../utils' +import { iterableEquality, equals as jestEquals, subsetEquality } from '../jest-utils' + +export default defineAssertion('toMatchObject', function (expected: unknown) { + const actual = this._obj as unknown + this.assert( + jestEquals(actual, expected, [iterableEquality, subsetEquality]), + 'expected #{this} to match object #{exp}', + 'expected #{this} to not match object #{exp}', + expected, + actual, + ) +}) diff --git a/packages/expect/src/jest-expect.ts b/packages/expect/src/jest-expect.ts index 0434c3705233..56e4ef2c8085 100644 --- a/packages/expect/src/jest-expect.ts +++ b/packages/expect/src/jest-expect.ts @@ -4,12 +4,14 @@ import type { MockInstance } from '@vitest/spy' import { isMockFunction } from '@vitest/spy' import type { Test } from '@vitest/runner' import type { Assertion, ChaiPlugin } from './types' -import { arrayBufferEquality, generateToBeMessage, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils' +import { arrayBufferEquality, generateToBeMessage, iterableEquality, equals as jestEquals, sparseArrayEquality, typeEquality } from './jest-utils' import type { AsymmetricMatcher } from './jest-asymmetric-matchers' import { diff, stringify } from './jest-matcher-utils' import { JEST_MATCHERS_OBJECT } from './constants' import { recordAsyncExpect, wrapSoft } from './utils' +import matchers from './assertions/index' + // polyfill globals because expect can be used in node environment declare class Node { contains(item: unknown): boolean @@ -19,11 +21,17 @@ declare class DOMTokenList { contains(item: unknown): boolean } +const defineJestMatchers: ChaiPlugin = (chai, utils) => { + matchers.forEach(define => define(chai, utils)) +} + // Jest Expect Compact export const JestChaiExpect: ChaiPlugin = (chai, utils) => { const { AssertionError } = chai const c = () => getColors() + defineJestMatchers(chai, utils) + function def(name: keyof Assertion | (keyof Assertion)[], fn: ((this: Chai.AssertionStatic & Assertion, ...args: any[]) => any)) { const addMethod = (n: keyof Assertion) => { const softWrapper = wrapSoft(utils, fn) @@ -156,16 +164,6 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { actual, ) }) - def('toMatchObject', function (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') return this.include(expected) diff --git a/packages/expect/src/utils.ts b/packages/expect/src/utils.ts index aa1754d57f78..e9c12afc9caa 100644 --- a/packages/expect/src/utils.ts +++ b/packages/expect/src/utils.ts @@ -1,8 +1,8 @@ import { processError } from '@vitest/utils/error' import type { Test } from '@vitest/runner/types' -import { GLOBAL_EXPECT } from './constants' +import { GLOBAL_EXPECT, JEST_MATCHERS_OBJECT } from './constants' import { getState } from './state' -import type { Assertion, MatcherState } from './types' +import type { Assertion, ChaiPlugin, MatcherState } from './types' export function recordAsyncExpect(test: any, promise: Promise | PromiseLike) { // record promise for test, that resolves before test ends @@ -49,3 +49,18 @@ export function wrapSoft(utils: Chai.ChaiUtils, fn: (this: Chai.AssertionStatic } } } + +export function defineAssertion(name: keyof Assertion, fn: (this: Omit & { _obj: T }, ...args: any) => any): ChaiPlugin { + return (chai, utils) => { + const addMethod = (n: keyof Assertion) => { + const softWrapper = wrapSoft(utils, fn) + utils.addMethod(chai.Assertion.prototype, n, softWrapper) + utils.addMethod((globalThis as any)[JEST_MATCHERS_OBJECT].matchers, n, softWrapper) + } + + if (Array.isArray(name)) + name.forEach(n => addMethod(n)) + else + addMethod(name) + } +}