From 8ee59f0d9dff5c15e0fe27e555ed73346f465e20 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Fri, 23 Feb 2024 18:41:02 +0900 Subject: [PATCH] fix(expect): show diff on `toContain/toMatch` assertion error (#5267) --- docs/api/expect.md | 4 -- packages/expect/src/jest-expect.ts | 24 ++++++++++-- .../__snapshots__/jest-expect.test.ts.snap | 39 +++++++++++++++++++ test/core/test/jest-expect.test.ts | 39 +++++++++++-------- 4 files changed, 82 insertions(+), 24 deletions(-) diff --git a/docs/api/expect.md b/docs/api/expect.md index ecb2284ec934..09d4aaceed9f 100644 --- a/docs/api/expect.md +++ b/docs/api/expect.md @@ -544,10 +544,6 @@ test('top fruits', () => { }) ``` -::: tip -If the value in the error message is too truncated, you can increase [chaiConfig.truncateThreshold](/config/#chaiconfig-truncatethreshold) in your config file. -::: - ## toMatchObject - **Type:** `(received: object | array) => Awaitable` diff --git a/packages/expect/src/jest-expect.ts b/packages/expect/src/jest-expect.ts index c3349220d11a..1df2260926e8 100644 --- a/packages/expect/src/jest-expect.ts +++ b/packages/expect/src/jest-expect.ts @@ -170,10 +170,16 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { ) }) def('toMatch', function (expected: string | RegExp) { - if (typeof expected === 'string') - return this.include(expected) - else - return this.match(expected) + const actual = this._obj as string + return this.assert( + typeof expected === 'string' + ? actual.includes(expected) + : actual.match(expected), + `expected #{this} to match #{exp}`, + `expected #{this} not to match #{exp}`, + expected, + actual, + ) }) def('toContain', function (item) { const actual = this._obj as Iterable | string | Node | DOMTokenList @@ -203,6 +209,16 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { actual.value, ) } + // handle simple case on our own using `this.assert` to include diff in error message + if (typeof actual === 'string' && typeof item === 'string') { + return this.assert( + actual.includes(item), + `expected #{this} to contain #{exp}`, + `expected #{this} not to contain #{exp}`, + item, + actual, + ) + } // make "actual" indexable to have compatibility with jest if (actual != null && typeof actual !== 'string') utils.flag(this, 'object', Array.from(actual as Iterable)) diff --git a/test/core/test/__snapshots__/jest-expect.test.ts.snap b/test/core/test/__snapshots__/jest-expect.test.ts.snap index f9d09b20a239..d1f7b3d9b6de 100644 --- a/test/core/test/__snapshots__/jest-expect.test.ts.snap +++ b/test/core/test/__snapshots__/jest-expect.test.ts.snap @@ -310,3 +310,42 @@ exports[`asymmetric matcher error 23`] = ` "message": "expected error to be instance of MyError1", } `; + +exports[`toMatch/toContain diff 1`] = ` +{ + "actual": "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", + "diff": "- Expected ++ Received + +- world ++ hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", + "expected": "world", + "message": "expected 'hellohellohellohellohellohellohellohe…' to contain 'world'", +} +`; + +exports[`toMatch/toContain diff 2`] = ` +{ + "actual": "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", + "diff": "- Expected ++ Received + +- world ++ hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", + "expected": "world", + "message": "expected 'hellohellohellohellohellohellohellohe…' to match 'world'", +} +`; + +exports[`toMatch/toContain diff 3`] = ` +{ + "actual": "hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello", + "diff": "- Expected: +/world/ + ++ Received: +"hellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohellohello"", + "expected": "/world/", + "message": "expected 'hellohellohellohellohellohellohellohe…' to match /world/", +} +`; diff --git a/test/core/test/jest-expect.test.ts b/test/core/test/jest-expect.test.ts index 0fce503bf106..990361d41146 100644 --- a/test/core/test/jest-expect.test.ts +++ b/test/core/test/jest-expect.test.ts @@ -993,25 +993,26 @@ it('toHaveProperty error diff', () => { `) }) +function snapshotError(f: () => unknown) { + try { + f() + } + catch (error) { + const e = processError(error) + expect({ + message: e.message, + diff: e.diff, + expected: e.expected, + actual: e.actual, + }).toMatchSnapshot() + return + } + expect.unreachable() +} + it('asymmetric matcher error', () => { setupColors(getDefaultColors()) - function snapshotError(f: () => unknown) { - try { - f() - return expect.unreachable() - } - catch (error) { - const e = processError(error) - expect({ - message: e.message, - diff: e.diff, - expected: e.expected, - actual: e.actual, - }).toMatchSnapshot() - } - } - expect.extend({ stringContainingCustom(received: unknown, other: string) { return { @@ -1084,4 +1085,10 @@ it('asymmetric matcher error', () => { }).toThrow(MyError1)) }) +it('toMatch/toContain diff', () => { + snapshotError(() => expect('hello'.repeat(20)).toContain('world')) + snapshotError(() => expect('hello'.repeat(20)).toMatch('world')) + snapshotError(() => expect('hello'.repeat(20)).toMatch(/world/)) +}) + it('timeout', () => new Promise(resolve => setTimeout(resolve, 500)))