From c00bc88258566bdf2ab5b3d7f1ffd543a782f6f8 Mon Sep 17 00:00:00 2001 From: azaleta <24407500@qq.com> Date: Thu, 20 Oct 2022 01:18:30 +0900 Subject: [PATCH] fix(vitest): return detailed error when using toHaveReturnedWith (#2163) --- .../src/integrations/chai/jest-expect.ts | 36 +++++++-- .../test/__snapshots__/mocked.test.ts.snap | 79 +++++++++++++++++++ test/core/test/mocked.test.ts | 29 +++++++ 3 files changed, 139 insertions(+), 5 deletions(-) create mode 100644 test/core/test/__snapshots__/mocked.test.ts.snap diff --git a/packages/vitest/src/integrations/chai/jest-expect.ts b/packages/vitest/src/integrations/chai/jest-expect.ts index 77a5fe0d1caf..8eb7f0d60b61 100644 --- a/packages/vitest/src/integrations/chai/jest-expect.ts +++ b/packages/vitest/src/integrations/chai/jest-expect.ts @@ -394,6 +394,20 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`) return msg } + const formatReturns = (spy: EnhancedSpy, msg: string, actualReturn?: any) => { + msg += c.gray(`\n\nReceived: \n${spy.mock.results.map((callReturn, i) => { + let methodCall = c.bold(` ${ordinalOf(i + 1)} ${spy.getMockName()} call return:\n\n`) + if (actualReturn) + methodCall += unifiedDiff(stringify(callReturn.value), stringify(actualReturn), { showLegend: false }) + else + methodCall += stringify(callReturn).split('\n').map(line => ` ${line}`).join('\n') + + methodCall += '\n' + return methodCall + }).join('\n')}`) + msg += c.gray(`\n\nNumber of calls: ${c.bold(spy.mock.calls.length)}\n`) + return msg + } def(['toHaveBeenCalledTimes', 'toBeCalledTimes'], function (number: number) { const spy = getSpy(this) const spyName = spy.getMockName() @@ -589,12 +603,24 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { const spy = getSpy(this) const spyName = spy.getMockName() 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}`, - `expected "${spyName}" to not be successfully called with #{exp}`, - value, + const isNot = utils.flag(this, 'negate') as boolean + + let msg = utils.getMessage( + this, + [ + pass, + `expected "${spyName}" to return with: #{exp} at least once`, + `expected "${spyName}" to not return with: #{exp}`, + value, + ], ) + + if ((pass && isNot) || (!pass && !isNot)) { + msg = formatReturns(spy, msg, value) + const err = new Error(msg) + err.name = 'AssertionError' + throw err + } }) def(['toHaveLastReturnedWith', 'lastReturnedWith'], function (value: any) { const spy = getSpy(this) diff --git a/test/core/test/__snapshots__/mocked.test.ts.snap b/test/core/test/__snapshots__/mocked.test.ts.snap new file mode 100644 index 000000000000..858f49ee0236 --- /dev/null +++ b/test/core/test/__snapshots__/mocked.test.ts.snap @@ -0,0 +1,79 @@ +// Vitest Snapshot v1 + +exports[`mocked function which fails on toReturnWith > just one call 1`] = ` +"expected \\"spy\\" to return with: 2 at least once + +Received: + 1st spy call return: + + 2 + 1 + + +Number of calls: 1 +" +`; + +exports[`mocked function which fails on toReturnWith > multi calls 1`] = ` +"expected \\"spy\\" to return with: 2 at least once + +Received: + 1st spy call return: + + 2 + 1 + + 2nd spy call return: + + 2 + 1 + + 3rd spy call return: + + 2 + 1 + + +Number of calls: 3 +" +`; + +exports[`mocked function which fails on toReturnWith > oject type 1`] = ` +"expected \\"spy\\" to return with: { a: '4' } at least once + +Received: + 1st spy call return: + + Object { + - \\"a\\": \\"4\\", + + \\"a\\": \\"1\\", + } + + 2nd spy call return: + + Object { + - \\"a\\": \\"4\\", + + \\"a\\": \\"1\\", + } + + 3rd spy call return: + + Object { + - \\"a\\": \\"4\\", + + \\"a\\": \\"1\\", + } + + +Number of calls: 3 +" +`; + +exports[`mocked function which fails on toReturnWith > zero call 1`] = ` +"expected \\"spy\\" to return with: 2 at least once + +Received: + + +Number of calls: 0 +" +`; diff --git a/test/core/test/mocked.test.ts b/test/core/test/mocked.test.ts index a329cdf08ff8..c92fe7a8db84 100644 --- a/test/core/test/mocked.test.ts +++ b/test/core/test/mocked.test.ts @@ -134,6 +134,35 @@ test('async functions should be mocked', () => { expect(asyncFunc()).resolves.toBe('foo') }) +describe('mocked function which fails on toReturnWith', () => { + test('zero call', () => { + const mock = vi.fn(() => 1) + expect(() => expect(mock).toReturnWith(2)).toThrowErrorMatchingSnapshot() + }) + + test('just one call', () => { + const mock = vi.fn(() => 1) + mock() + expect(() => expect(mock).toReturnWith(2)).toThrowErrorMatchingSnapshot() + }) + + test('multi calls', () => { + const mock = vi.fn(() => 1) + mock() + mock() + mock() + expect(() => expect(mock).toReturnWith(2)).toThrowErrorMatchingSnapshot() + }) + + test('oject type', () => { + const mock = vi.fn(() => { return { a: '1' } }) + mock() + mock() + mock() + expect(() => expect(mock).toReturnWith({ a: '4' })).toThrowErrorMatchingSnapshot() + }) +}) + // This is here because mocking streams previously caused some problems (#1671). test('streams', () => { expect(exportedStream).toBeDefined()