From 0b4f19eff93ac95365160035735f50bcfdbb547b Mon Sep 17 00:00:00 2001 From: Ivan Demchuk Date: Wed, 29 Dec 2021 15:54:17 +0200 Subject: [PATCH] feat: add spy name to assertion messages (#361) --- .../src/integrations/chai/jest-expect.ts | 59 +++++++++++-------- packages/vitest/src/integrations/jest-mock.ts | 4 +- 2 files changed, 38 insertions(+), 25 deletions(-) diff --git a/packages/vitest/src/integrations/chai/jest-expect.ts b/packages/vitest/src/integrations/chai/jest-expect.ts index b2d59cc87a09..abef793474fc 100644 --- a/packages/vitest/src/integrations/chai/jest-expect.ts +++ b/packages/vitest/src/integrations/chai/jest-expect.ts @@ -1,4 +1,4 @@ -import type { Spy } from 'tinyspy' +import type { EnhancedSpy } from '../jest-mock' import type { ChaiPlugin } from './types' import { arrayBufferEquality, equals as asymmetricEquals, hasAsymmetric, iterableEquality, sparseArrayEquality, typeEquality } from './jest-utils' @@ -219,45 +219,49 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { } const getSpy = (assertion: any) => { assertIsMock(assertion) - return assertion._obj as Spy + return assertion._obj as EnhancedSpy } def(['toHaveBeenCalledTimes', 'toBeCalledTimes'], function(number: number) { const spy = getSpy(this) + const spyName = spy.getMockName() return this.assert( spy.callCount === number, - 'expected spy to be called #{exp} times', - 'expected spy to not be called #{exp} times', + `expected "${spyName}" to be called #{exp} times`, + `expected "${spyName}" to not be called #{exp} times`, number, spy.callCount, ) }) def('toHaveBeenCalledOnce', function() { const spy = getSpy(this) + const spyName = spy.getMockName() return this.assert( spy.callCount === 1, - 'expected spy to be called once', - 'expected spy to not be called once', + `expected "${spyName}" to be called once`, + `expected "${spyName}" to not be called once`, 1, spy.callCount, ) }) def(['toHaveBeenCalled', 'toBeCalled'], function() { const spy = getSpy(this) + const spyName = spy.getMockName() return this.assert( spy.called, - 'expected spy to be called at least once', - 'expected spy to not be called at all', + `expected "${spyName}" to be called at least once`, + `expected "${spyName}" to not be called at all`, true, spy.called, ) }) def(['toHaveBeenCalledWith', 'toBeCalledWith'], function(...args) { const spy = getSpy(this) + const spyName = spy.getMockName() const pass = spy.calls.some(callArg => asymmetricEquals(callArg, args)) return this.assert( pass, - 'expected spy to be called with arguments: #{exp}', - 'expected spy to not be called with arguments: #{exp}', + `expected "${spyName}" to be called with arguments: #{exp}`, + `expected "${spyName}" to not be called with arguments: #{exp}`, args, spy.calls, ) @@ -279,24 +283,26 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { } def(['toHaveBeenNthCalledWith', 'nthCalledWith'], function(times: number, ...args: any[]) { const spy = getSpy(this) + const spyName = spy.getMockName() const nthCall = spy.calls[times - 1] this.assert( asymmetricEquals(nthCall, args), - `expected ${ordinalOf(times)} spy call to have been called with #{exp}`, - `expected ${ordinalOf(times)} spy call to not have been called with #{exp}`, + `expected ${ordinalOf(times)} "${spyName}" call to have been called with #{exp}`, + `expected ${ordinalOf(times)} "${spyName}" call to not have been called with #{exp}`, args, nthCall, ) }) def(['toHaveBeenLastCalledWith', 'lastCalledWith'], function(...args: any[]) { const spy = getSpy(this) + const spyName = spy.getMockName() const lastCall = spy.calls[spy.calls.length - 1] this.assert( asymmetricEquals(lastCall, args), - 'expected last spy call to have been called with #{exp}', - 'expected last spy call to not have been called with #{exp}', + `expected last "${spyName}" call to have been called with #{exp}`, + `expected last "${spyName}" call to not have been called with #{exp}`, args, lastCall, ) @@ -311,50 +317,55 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { }) def(['toHaveReturned', 'toReturn'], function() { const spy = getSpy(this) + const spyName = spy.getMockName() const calledAndNotThrew = spy.called && !spy.results.some(([type]) => type === 'error') this.assert( calledAndNotThrew, - 'expected spy to be successfully called at least once', - 'expected spy to not be successfully called', + `expected "${spyName}" to be successfully called at least once`, + `expected "${spyName}" to not be successfully called`, calledAndNotThrew, !calledAndNotThrew, ) }) def(['toHaveReturnedTimes', 'toReturnTimes'], function(times: number) { const spy = getSpy(this) + const spyName = spy.getMockName() const successfullReturns = spy.results.reduce((success, [type]) => type === 'error' ? success : ++success, 0) this.assert( successfullReturns === times, - `expected spy to be successfully called ${times} times`, - `expected spy to not be successfully called ${times} times`, + `expected "${spyName}" to be successfully called ${times} times`, + `expected "${spyName}" to not be successfully called ${times} times`, `expected number of returns: ${times}`, `received number of returns: ${successfullReturns}`, ) }) def(['toHaveReturnedWith', 'toReturnWith'], function(value: any) { const spy = getSpy(this) + const spyName = spy.getMockName() const pass = spy.results.some(([type, result]) => type === 'ok' && asymmetricEquals(value, result)) this.assert( pass, - 'expected spy to be successfully called with #{exp}', - 'expected spy to not be successfully called with #{exp}', + `expected "${spyName}" to be successfully called with #{exp}`, + `expected "${spyName}" to not be successfully called with #{exp}`, value, ) }) def(['toHaveLastReturnedWith', 'lastReturnedWith'], function(value: any) { const spy = getSpy(this) + const spyName = spy.getMockName() const lastResult = spy.returns[spy.returns.length - 1] const pass = asymmetricEquals(lastResult, value) this.assert( pass, - 'expected last spy call to return #{exp}', - 'expected last spy call to not return #{exp}', + `expected last "${spyName}" call to return #{exp}`, + `expected last "${spyName}" call to not return #{exp}`, value, lastResult, ) }) def(['toHaveNthReturnedWith', 'nthReturnedWith'], function(nthCall: number, value: any) { const spy = getSpy(this) + const spyName = spy.getMockName() const isNot = utils.flag(this, 'negate') as boolean const [callType, callResult] = spy.results[nthCall - 1] const ordinalCall = `${ordinalOf(nthCall)} call` @@ -366,8 +377,8 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => { this.assert( nthCallReturn, - `expected ${ordinalCall} spy call to return #{exp}`, - `expected ${ordinalCall} spy call to not return #{exp}`, + `expected ${ordinalCall} "${spyName}" call to return #{exp}`, + `expected ${ordinalCall} "${spyName}" call to not return #{exp}`, value, callResult, ) diff --git a/packages/vitest/src/integrations/jest-mock.ts b/packages/vitest/src/integrations/jest-mock.ts index 4985af2db183..1a690b3513d7 100644 --- a/packages/vitest/src/integrations/jest-mock.ts +++ b/packages/vitest/src/integrations/jest-mock.ts @@ -94,6 +94,8 @@ export type MaybeMocked = T extends MockableFunction ? MockedObject : T +export type EnhancedSpy = JestMockCompat & SpyImpl + export interface MockWithArgs extends JestMockCompatFn, ReturnType> { new (...args: ConstructorArgumentsOf): T @@ -124,7 +126,7 @@ type Awaited = T extends Promise ? R : never function enhanceSpy( spy: SpyImpl, ): JestMockCompat { - const stub = spy as unknown as JestMockCompat & SpyImpl + const stub = spy as unknown as EnhancedSpy let implementation: ((...args: TArgs) => TReturns) | undefined