diff --git a/packages/vitest/src/integrations/chai/jest-expect.ts b/packages/vitest/src/integrations/chai/jest-expect.ts index f7cfcda3849b..41f62ae29a33 100644 --- a/packages/vitest/src/integrations/chai/jest-expect.ts +++ b/packages/vitest/src/integrations/chai/jest-expect.ts @@ -7,7 +7,7 @@ import type { Constructable, Test } from '../../types' import { assertTypes } from '../../utils' import { unifiedDiff } from '../../node/diff' import type { ChaiPlugin, MatcherState } from '../../types/chai' -import { arrayBufferEquality, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils' +import { arrayBufferEquality, generateToBeMessage, iterableEquality, equals as jestEquals, sparseArrayEquality, subsetEquality, typeEquality } from './jest-utils' import type { AsymmetricMatcher } from './jest-asymmetric-matchers' import { stringify } from './jest-matcher-utils' @@ -129,9 +129,41 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { }) def('toBe', function (expected) { const actual = this._obj + const pass = Object.is(actual, expected) + + let deepEqualityName = '' + + if (!pass) { + const toStrictEqualPass = jestEquals( + actual, + expected, + [ + iterableEquality, + typeEquality, + sparseArrayEquality, + arrayBufferEquality, + ], + true, + ) + + if (toStrictEqualPass) { + deepEqualityName = 'toStrictEqual' + } + else { + const toEqualPass = jestEquals( + actual, + expected, + [iterableEquality], + ) + + if (toEqualPass) + deepEqualityName = 'toEqual' + } + } + return this.assert( - Object.is(actual, expected), - 'expected #{this} to be #{exp} // Object.is equality', + pass, + generateToBeMessage(deepEqualityName), 'expected #{this} not to be #{exp} // Object.is equality', expected, actual, diff --git a/packages/vitest/src/integrations/chai/jest-utils.ts b/packages/vitest/src/integrations/chai/jest-utils.ts index 47b831fbbb08..24a7a09c263b 100644 --- a/packages/vitest/src/integrations/chai/jest-utils.ts +++ b/packages/vitest/src/integrations/chai/jest-utils.ts @@ -508,3 +508,16 @@ export const sparseArrayEquality = ( equals(a, b, [iterableEquality, typeEquality], true) && equals(aKeys, bKeys) ) } + +export const generateToBeMessage = ( + deepEqualityName: string, + expected = '#{this}', + actual = '#{exp}', +) => { + const toBeMessage = `expected ${expected} to be ${actual} // Object.is equality` + + if (['toStrictEqual', 'toEqual'].includes(deepEqualityName)) + return `${toBeMessage}\n\nIf it should pass with deep equality, replace "toBe" with "${deepEqualityName}"\n\nExpected: ${expected}\nReceived: serializes to the same string\n` + + return toBeMessage +} diff --git a/test/core/test/jest-expect.test.ts b/test/core/test/jest-expect.test.ts index 1fca1f1a15da..9ff1ca457510 100644 --- a/test/core/test/jest-expect.test.ts +++ b/test/core/test/jest-expect.test.ts @@ -1,5 +1,7 @@ /* eslint-disable no-sparse-arrays */ +import { AssertionError } from 'assert' import { describe, expect, it, vi } from 'vitest' +import { generateToBeMessage } from 'vitest/src/integrations/chai/jest-utils' class TestError extends Error {} @@ -548,6 +550,52 @@ describe('async expect', () => { expect(error).toEqual(expectedError) } }) + + it('reminds users to use deep equality checks if they are comparing objects', () => { + const generatedToBeMessage = ( + deepEqualityName: string, + expected: string, + actual: string, + ) => new AssertionError({ + message: generateToBeMessage(deepEqualityName, expected, actual), + }) + + const actual = { key: 'value' } + class FakeClass {} + + const toStrictEqualError1 = generatedToBeMessage('toStrictEqual', '{ key: \'value\' }', '{ key: \'value\' }') + try { + expect(actual).toBe({ ...actual }) + } + catch (error) { + expect(error).toEqual(toStrictEqualError1) + } + + const toStrictEqualError2 = generatedToBeMessage('toStrictEqual', 'FakeClass{}', 'FakeClass{}') + try { + expect(new FakeClass()).toBe(new FakeClass()) + } + catch (error) { + expect(error).toEqual(toStrictEqualError2) + } + + const toEqualError1 = generatedToBeMessage('toEqual', '{}', 'FakeClass{}') + try { + expect({}).toBe(new FakeClass()) + } + catch (error) { + expect(error).toEqual(toEqualError1) + // expect(error).toEqual('1234') + } + + const toEqualError2 = generatedToBeMessage('toEqual', 'FakeClass{}', '{}') + try { + expect(new FakeClass()).toBe({}) + } + catch (error) { + expect(error).toEqual(toEqualError2) + } + }) }) it('timeout', () => new Promise(resolve => setTimeout(resolve, 500)))