From 01c9995c69d00205388da6d7dba2b35d9e70e5b8 Mon Sep 17 00:00:00 2001 From: Andrei Picus Date: Thu, 24 Jun 2021 12:38:17 +0200 Subject: [PATCH] feat: `thenReject` now lazily creates promise rejection Previously, `thenReject` would instantly create a `Promise.reject()` that could cause problems with code that did not immediately call the instance and capture the promise. Now, the expectation sets an internal value that will be turned into a promise rejection only when the instance is called. This should avoid the unhandled rejection warnings that you can get when either 1) the expectation is not met or 2) there is code that flushes promises before they're actually caught, such as `act` from `@testing-library/react`. Moreover, this should help avoid pausing a debugger on `thenReject` when "Pause on any exception" is checked. Fixes #238. --- src/expectation/expectation.ts | 2 +- src/expectation/repository/base-repository.ts | 51 ++++++++++------- .../expectation-repository.mocks.ts | 5 +- .../repository/expectation-repository.ts | 10 ++-- src/expectation/repository/repo.contract.ts | 57 +++++++++++-------- .../repository/strong-repository.spec.ts | 6 +- .../repository/strong-repository.ts | 4 +- .../repository/weak-repository.spec.ts | 26 ++++----- src/expectation/repository/weak-repository.ts | 4 +- src/instance/instance.spec.ts | 40 +++++++++++-- src/instance/instance.ts | 32 ++++++++--- src/return/returns.spec.ts | 24 +++----- src/return/returns.ts | 4 +- tests/e2e.spec.ts | 6 ++ 14 files changed, 165 insertions(+), 106 deletions(-) diff --git a/src/expectation/expectation.ts b/src/expectation/expectation.ts index e9576c0b..5314c793 100644 --- a/src/expectation/expectation.ts +++ b/src/expectation/expectation.ts @@ -3,7 +3,7 @@ import { Property } from '../proxy'; export type ReturnValue = { value: any; isPromise?: boolean; - promiseValue?: any; + promiseValue?: any; // TODO: remove, since value is equal to this isError?: boolean; }; diff --git a/src/expectation/repository/base-repository.ts b/src/expectation/repository/base-repository.ts index 2729931b..f4a9d7e3 100644 --- a/src/expectation/repository/base-repository.ts +++ b/src/expectation/repository/base-repository.ts @@ -1,5 +1,5 @@ import { returnOrThrow } from '../../instance/instance'; -import { ApplyProp, Expectation } from '../expectation'; +import { ApplyProp, Expectation, ReturnValue } from '../expectation'; import { CallMap, ExpectationRepository } from './expectation-repository'; import { Property } from '../../proxy'; @@ -35,7 +35,7 @@ export abstract class BaseRepository implements ExpectationRepository { this.unexpectedCallStats.clear(); } - get(property: Property): any { + get(property: Property): ReturnValue { const expectations = this.expectations.get(property); if (expectations && expectations.length) { @@ -50,45 +50,50 @@ export abstract class BaseRepository implements ExpectationRepository { if (propertyExpectation) { this.countAndConsume(propertyExpectation); - return propertyExpectation.expectation.returnValue.value; + return propertyExpectation.expectation.returnValue; } - return (...args: any[]) => { - const callExpectation = expectations.find((e) => - e.expectation.matches(args) - ); + return { + value: (...args: any[]) => { + const callExpectation = expectations.find((e) => + e.expectation.matches(args) + ); - if (callExpectation) { - this.recordExpected(property, args); - this.countAndConsume(callExpectation); + if (callExpectation) { + this.recordExpected(property, args); + this.countAndConsume(callExpectation); - return returnOrThrow(callExpectation.expectation.returnValue.value); - } + // TODO: this is duplicated in instance + return returnOrThrow(callExpectation.expectation.returnValue); + } - this.recordUnexpected(property, args); - return this.getValueForUnexpectedCall(property, args); + this.recordUnexpected(property, args); + return this.getValueForUnexpectedCall(property, args); + }, }; } switch (property) { case 'toString': - return () => 'mock'; + return { value: () => 'mock' }; case '@@toStringTag': case Symbol.toStringTag: case 'name': - return 'mock'; + return { value: 'mock' }; // pretty-format case '$$typeof': case 'constructor': case '@@__IMMUTABLE_ITERABLE__@@': case '@@__IMMUTABLE_RECORD__@@': - return null; + return { value: null }; case ApplyProp: - return (...args: any[]) => { - this.recordUnexpected(property, args); - return this.getValueForUnexpectedCall(property, args); + return { + value: (...args: any[]) => { + this.recordUnexpected(property, args); + return this.getValueForUnexpectedCall(property, args); + }, }; default: this.recordUnexpected(property, undefined); @@ -124,13 +129,15 @@ export abstract class BaseRepository implements ExpectationRepository { protected abstract getValueForUnexpectedCall( property: Property, args: any[] - ): any; + ): ReturnValue; /** * We got a property access that doesn't match any expectation, * what should we return? */ - protected abstract getValueForUnexpectedAccess(property: Property): any; + protected abstract getValueForUnexpectedAccess( + property: Property + ): ReturnValue; protected abstract consumeExpectation( expectation: CountableExpectation diff --git a/src/expectation/repository/expectation-repository.mocks.ts b/src/expectation/repository/expectation-repository.mocks.ts index 49d0cbd6..9d82caf0 100644 --- a/src/expectation/repository/expectation-repository.mocks.ts +++ b/src/expectation/repository/expectation-repository.mocks.ts @@ -10,8 +10,9 @@ export class OneIncomingExpectationRepository implements ExpectationRepository { this.expectation = expectation; } - get(): ReturnValue | undefined { - return this.expectation?.returnValue; + get(): ReturnValue { + if (!this.expectation) throw new Error(); + return this.expectation.returnValue; } getAllProperties(): Property[] { diff --git a/src/expectation/repository/expectation-repository.ts b/src/expectation/repository/expectation-repository.ts index dd3292fd..ff7fe2d6 100644 --- a/src/expectation/repository/expectation-repository.ts +++ b/src/expectation/repository/expectation-repository.ts @@ -1,4 +1,4 @@ -import { Expectation } from '../expectation'; +import { Expectation, ReturnValue } from '../expectation'; import { Property } from '../../proxy'; export type Call = { @@ -47,17 +47,17 @@ export interface ExpectationRepository { * * @example * add(new Expectation('getData', [1, 2], 23); - * get('getData')(1, 2) === 23 + * get('getData').value(1, 2) === 23 * * @example * add(new Expectation('hasData', undefined, true); - * get('hasData') === true + * get('hasData').value === true * * @example * add(new Expectation('getData', undefined, () => 42); - * get('getData')(1, 2, '3', false, NaN) === 42 + * get('getData').value(1, 2, '3', false, NaN) === 42 */ - get(property: Property): any; + get(property: Property): ReturnValue; /** * Get all the properties that have expectations. diff --git a/src/expectation/repository/repo.contract.ts b/src/expectation/repository/repo.contract.ts index b8f923f8..87eb34c1 100644 --- a/src/expectation/repository/repo.contract.ts +++ b/src/expectation/repository/repo.contract.ts @@ -24,7 +24,7 @@ export const repoContractTests: ExpectationRepositoryContract = { test: (repo) => () => { repo.add(new MatchingPropertyExpectation('foo', { value: 23 })); - expect(repo.get('foo')).toEqual(23); + expect(repo.get('foo').value).toEqual(23); }, }, { @@ -33,8 +33,8 @@ export const repoContractTests: ExpectationRepositoryContract = { repo.add(new MatchingPropertyExpectation('foo', { value: 23 })); repo.add(new MatchingPropertyExpectation('foo', { value: 42 })); - expect(repo.get('foo')).toEqual(23); - expect(repo.get('foo')).toEqual(42); + expect(repo.get('foo').value).toEqual(23); + expect(repo.get('foo').value).toEqual(42); }, }, { @@ -46,9 +46,9 @@ export const repoContractTests: ExpectationRepositoryContract = { expectation.setInvocationCount(2, 3); repo.add(expectation); - expect(repo.get('foo')).toEqual(23); - expect(repo.get('foo')).toEqual(23); - expect(repo.get('foo')).toEqual(23); + expect(repo.get('foo').value).toEqual(23); + expect(repo.get('foo').value).toEqual(23); + expect(repo.get('foo').value).toEqual(23); }, }, ], @@ -59,7 +59,7 @@ export const repoContractTests: ExpectationRepositoryContract = { test: (repo) => () => { repo.add(new MatchingCallExpectation('foo', { value: 23 })); - expect(repo.get('foo')()).toEqual(23); + expect(repo.get('foo').value()).toEqual(23); }, }, { @@ -68,18 +68,22 @@ export const repoContractTests: ExpectationRepositoryContract = { repo.add(new MatchingCallExpectation('foo', { value: 23 })); repo.add(new MatchingCallExpectation('foo', { value: 42 })); - expect(repo.get('foo')()).toEqual(23); - expect(repo.get('foo')()).toEqual(42); + expect(repo.get('foo').value()).toEqual(23); + expect(repo.get('foo').value()).toEqual(42); }, }, { name: 'should match property expectations before function expectations', test: (repo) => () => { repo.add(new MatchingCallExpectation('foo', { value: 1 })); - repo.add(new MatchingPropertyExpectation('foo', { value: () => 2 })); + repo.add( + new MatchingPropertyExpectation('foo', { + value: () => ({ value: 2 }), + }) + ); - expect(repo.get('foo')()).toEqual(2); - expect(repo.get('foo')()).toEqual(1); + expect(repo.get('foo').value()).toEqual({ value: 2 }); + expect(repo.get('foo').value()).toEqual(1); }, }, { @@ -89,9 +93,9 @@ export const repoContractTests: ExpectationRepositoryContract = { expectation.setInvocationCount(2, 3); repo.add(expectation); - expect(repo.get('foo')()).toEqual(23); - expect(repo.get('foo')()).toEqual(23); - expect(repo.get('foo')()).toEqual(23); + expect(repo.get('foo').value()).toEqual(23); + expect(repo.get('foo').value()).toEqual(23); + expect(repo.get('foo').value()).toEqual(23); }, }, { @@ -99,10 +103,11 @@ export const repoContractTests: ExpectationRepositoryContract = { test: (repo) => () => { const expectation = new MatchingCallExpectation('foo', { value: new Error(), + isError: true, }); repo.add(expectation); - expect(() => repo.get('foo')()).toThrow(); + expect(() => repo.get('foo').value()).toThrow(); }, }, ], @@ -201,7 +206,7 @@ export const repoContractTests: ExpectationRepositoryContract = { const expectation = new MatchingCallExpectation('foo', { value: 23 }); repo.add(expectation); - repo.get('foo')(1, 2); + repo.get('foo').value(1, 2); const callStats: CallStats = { expected: new Map([ @@ -233,7 +238,7 @@ export const repoContractTests: ExpectationRepositoryContract = { repo.add(new NotMatchingExpectation('foo', { value: 23 })); try { - repo.get('foo')(1, 2, 3); + repo.get('foo').value(1, 2, 3); } catch (e) {} const callStats: CallStats = { @@ -280,9 +285,9 @@ export const repoContractTests: ExpectationRepositoryContract = { { name: 'should return values for toString and friends', test: (repo) => () => { - expect(repo.get('toString')()).toBeTruthy(); - expect(repo.get('@@toStringTag')).toBeTruthy(); - expect(repo.get(Symbol.toStringTag)).toBeTruthy(); + expect(repo.get('toString').value()).toBeTruthy(); + expect(repo.get('@@toStringTag').value).toBeTruthy(); + expect(repo.get(Symbol.toStringTag).value).toBeTruthy(); }, }, { @@ -309,10 +314,12 @@ export const repoContractTests: ExpectationRepositoryContract = { }) ); - expect(repo.get('toString')()).toEqual('not a mock'); - expect(repo.get('toString')()).toEqual('I said not a mock'); - expect(repo.get('@@toStringTag')).toEqual('totally not a mock'); - expect(repo.get(Symbol.toStringTag)).toEqual('absolutely not a mock'); + expect(repo.get('toString').value()).toEqual('not a mock'); + expect(repo.get('toString').value()).toEqual('I said not a mock'); + expect(repo.get('@@toStringTag').value).toEqual('totally not a mock'); + expect(repo.get(Symbol.toStringTag).value).toEqual( + 'absolutely not a mock' + ); }, }, ], diff --git a/src/expectation/repository/strong-repository.spec.ts b/src/expectation/repository/strong-repository.spec.ts index 0868928d..bfa2c826 100644 --- a/src/expectation/repository/strong-repository.spec.ts +++ b/src/expectation/repository/strong-repository.spec.ts @@ -29,13 +29,13 @@ describe('StrongRepository', () => { const repo = new StrongRepository(); repo.add(new NotMatchingExpectation('foo', { value: 23 })); - expect(() => repo.get('foo')(3, 4)).toThrow(UnexpectedCall); + expect(() => repo.get('foo').value(3, 4)).toThrow(UnexpectedCall); }); it('should throw if no apply expectations', () => { const repo = new StrongRepository(); - expect(() => repo.get(ApplyProp)(1, 2, 3)).toThrow(UnexpectedCall); + expect(() => repo.get(ApplyProp).value(1, 2, 3)).toThrow(UnexpectedCall); }); it('should throw after a property expectation is fulfilled', () => { @@ -49,7 +49,7 @@ describe('StrongRepository', () => { it('should throw after a function expectation is fulfilled', () => { const repo = new StrongRepository(); repo.add(new MatchingCallExpectation('foo', { value: 23 })); - repo.get('foo')(1, 2); + repo.get('foo').value(1, 2); expect(() => repo.get('foo')).toThrow(UnexpectedAccess); }); diff --git a/src/expectation/repository/strong-repository.ts b/src/expectation/repository/strong-repository.ts index 62fc3d61..0704a330 100644 --- a/src/expectation/repository/strong-repository.ts +++ b/src/expectation/repository/strong-repository.ts @@ -19,11 +19,11 @@ export class StrongRepository extends BaseRepository { } } - protected getValueForUnexpectedCall(property: Property, args: any[]) { + protected getValueForUnexpectedCall(property: Property, args: any[]): never { throw new UnexpectedCall(property, args, this.getUnmet()); } - protected getValueForUnexpectedAccess(property: Property) { + protected getValueForUnexpectedAccess(property: Property): never { throw new UnexpectedAccess(property, this.getUnmet()); } } diff --git a/src/expectation/repository/weak-repository.spec.ts b/src/expectation/repository/weak-repository.spec.ts index 020e8c69..3de57db5 100644 --- a/src/expectation/repository/weak-repository.spec.ts +++ b/src/expectation/repository/weak-repository.spec.ts @@ -19,7 +19,7 @@ describe('WeakRepository', () => { it('should return a default value for everything', () => { const repo = new WeakRepository(); - expect(repo.get('whatever')()).toEqual(null); + expect(repo.get('whatever').value()).toEqual(null); }); it('should keep repeating the last met property expectation', () => { @@ -27,15 +27,15 @@ describe('WeakRepository', () => { repo.add(new MatchingPropertyExpectation('foo', { value: 23 })); - expect(repo.get('foo')).toEqual(23); - expect(repo.get('foo')).toEqual(23); + expect(repo.get('foo').value).toEqual(23); + expect(repo.get('foo').value).toEqual(23); }); it('should mark the last expectation as met', () => { const repo = new WeakRepository(); repo.add(new MatchingPropertyExpectation('foo', { value: 23 })); - expect(repo.get('foo')).toEqual(23); + expect(repo.get('foo').value).toEqual(23); expect(repo.getUnmet()).toEqual([]); }); @@ -44,11 +44,11 @@ describe('WeakRepository', () => { const repo = new WeakRepository(); repo.add(new MatchingPropertyExpectation('foo', { value: 23 })); - expect(repo.get('foo')).toEqual(23); + expect(repo.get('foo').value).toEqual(23); repo.add(new MatchingPropertyExpectation('foo', { value: 42 })); - expect(repo.get('foo')).toEqual(42); - expect(repo.get('foo')).toEqual(42); + expect(repo.get('foo').value).toEqual(42); + expect(repo.get('foo').value).toEqual(42); }); it('should keep repeating the last met call expectation', () => { @@ -56,26 +56,26 @@ describe('WeakRepository', () => { repo.add(new MatchingCallExpectation('foo', { value: 23 })); - expect(repo.get('foo')(1, 2)).toEqual(23); - expect(repo.get('foo')(1, 2)).toEqual(23); + expect(repo.get('foo').value(1, 2)).toEqual(23); + expect(repo.get('foo').value(1, 2)).toEqual(23); }); it('should add new call expectations after repeating the last', () => { const repo = new WeakRepository(); repo.add(new MatchingCallExpectation('foo', { value: 23 })); - expect(repo.get('foo')(1, 2)).toEqual(23); + expect(repo.get('foo').value(1, 2)).toEqual(23); repo.add(new MatchingCallExpectation('foo', { value: 42 })); - expect(repo.get('foo')(1, 2)).toEqual(42); - expect(repo.get('foo')(1, 2)).toEqual(42); + expect(repo.get('foo').value(1, 2)).toEqual(42); + expect(repo.get('foo').value(1, 2)).toEqual(42); }); it('should keep repeating the last met call expectation', () => { const repo = new WeakRepository(); repo.add(new MatchingCallExpectation('foo', { value: 23 })); - expect(repo.get('foo')(1, 2)).toEqual(23); + expect(repo.get('foo').value(1, 2)).toEqual(23); expect(repo.getUnmet()).toEqual([]); }); diff --git a/src/expectation/repository/weak-repository.ts b/src/expectation/repository/weak-repository.ts index 1d1035c9..19ba4b5c 100644 --- a/src/expectation/repository/weak-repository.ts +++ b/src/expectation/repository/weak-repository.ts @@ -13,9 +13,9 @@ export class WeakRepository implements ExpectationRepository { private repeating = new Map(); - protected getValueForUnexpectedCall = () => null; + protected getValueForUnexpectedCall = () => ({ value: () => null }); - protected getValueForUnexpectedAccess = () => () => null; + protected getValueForUnexpectedAccess = () => ({ value: () => null }); protected consumeExpectation(expectation: CountableExpectation): void { const { property, max } = expectation.expectation; diff --git a/src/instance/instance.spec.ts b/src/instance/instance.spec.ts index a3b20745..38e5a848 100644 --- a/src/instance/instance.spec.ts +++ b/src/instance/instance.spec.ts @@ -12,7 +12,7 @@ describe('instance', () => { const repo = SM.mock(); it('should get matching expectation for apply', () => { - SM.when(repo.get(ApplyProp) as unknown).thenReturn(() => 42); + SM.when(repo.get(ApplyProp)).thenReturn({ value: () => 42 }); const fn = mock<(x: number) => number>({ repository: SM.instance(repo) }); @@ -20,7 +20,7 @@ describe('instance', () => { }); it('should get matching expectation for method', () => { - SM.when(repo.get('bar') as unknown).thenReturn(() => 42); + SM.when(repo.get('bar')).thenReturn({ value: () => 42 }); const foo = mock<{ bar: (x: number) => number }>({ repository: SM.instance(repo), @@ -30,13 +30,41 @@ describe('instance', () => { }); it('should get matching expectation for property', () => { - SM.when(repo.get('bar') as unknown).thenReturn(42); + SM.when(repo.get('bar')).thenReturn({ value: 42 }); const foo = mock<{ bar: number }>({ repository: SM.instance(repo) }); expect(instance(foo).bar).toEqual(42); }); + it('should throw matching property error expectation', () => { + SM.when(repo.get('bar')).thenReturn({ value: 'foo', isError: true }); + + const foo = mock<{ bar: number }>({ repository: SM.instance(repo) }); + + expect(() => instance(foo).bar).toThrow('foo'); + }); + + it('should resolve matching property promise expectation', async () => { + SM.when(repo.get('bar')).thenReturn({ value: 'foo', isPromise: true }); + + const foo = mock<{ bar: number }>({ repository: SM.instance(repo) }); + + await expect(instance(foo).bar).resolves.toEqual('foo'); + }); + + it('should reject matching property error promise expectation', async () => { + SM.when(repo.get('bar')).thenReturn({ + value: new Error('foo'), + isPromise: true, + isError: true, + }); + + const foo = mock<{ bar: number }>({ repository: SM.instance(repo) }); + + await expect(instance(foo).bar).rejects.toThrow('foo'); + }); + it('should pretty print', () => { expectAnsilessEqual( printExpected(instance(mock())), @@ -49,9 +77,9 @@ describe('instance', () => { const keys = ['foo', 'bar', baz]; SM.when(repo.getAllProperties()).thenReturn(keys).times(4); - SM.when(repo.get('foo') as unknown).thenReturn(1); - SM.when(repo.get('bar') as unknown).thenReturn(2); - SM.when(repo.get(baz) as unknown).thenReturn(3); + SM.when(repo.get('foo')).thenReturn({ value: 1 }); + SM.when(repo.get('bar')).thenReturn({ value: 2 }); + SM.when(repo.get(baz)).thenReturn({ value: 3 }); const foo = mock<{ foo: number; bar: number; [baz]: number }>({ repository: SM.instance(repo), diff --git a/src/instance/instance.ts b/src/instance/instance.ts index d8db4e7a..4565cea9 100644 --- a/src/instance/instance.ts +++ b/src/instance/instance.ts @@ -1,18 +1,33 @@ -import { ApplyProp } from '../expectation/expectation'; +import { ApplyProp, ReturnValue } from '../expectation/expectation'; import { getMockState } from '../mock/map'; import { Mock } from '../mock/mock'; import { createProxy } from '../proxy'; /** - * Return the expectation's return value. If the value is an error then - * throw it. + * Return the expectation's return value. + * + * If the value is an error then throw it. + * + * If the value is a promise then resolve/reject it. */ -export const returnOrThrow = (returnValue: any) => { - if (returnValue instanceof Error) { - throw returnValue; +export const returnOrThrow = ({ isError, isPromise, value }: ReturnValue) => { + if (isError) { + if (isPromise) { + return Promise.reject(value); + } + + if (value instanceof Error) { + throw value; + } + + throw new Error(value); + } + + if (isPromise) { + return Promise.resolve(value); } - return returnValue; + return value; }; /** @@ -26,7 +41,8 @@ export const instance = (mock: Mock): T => { apply: (args: any[]) => { const fn = repository.get(ApplyProp); - return returnOrThrow(fn(...args)); + // This is not using `returnOrThrow` because the repo will use it. + return fn.value(...args); }, ownKeys: () => repository.getAllProperties(), }); diff --git a/src/return/returns.spec.ts b/src/return/returns.spec.ts index 7955956d..f5d3d42d 100644 --- a/src/return/returns.spec.ts +++ b/src/return/returns.spec.ts @@ -42,7 +42,7 @@ describe('returns', () => { }); }); - it('should set a exception message', () => { + it('should set an exception message', () => { const pendingExpectation = new SpyPendingExpectation(); createReturns(pendingExpectation).thenThrow('foobar'); @@ -70,14 +70,12 @@ describe('returns', () => { }); }); - it('should set a return promise value', async () => { + it('should set a return promise value', () => { const pendingExpectation = new SpyPendingExpectation(); createReturns>(pendingExpectation).thenResolve(23); - await expect(pendingExpectation.finishCalledWith?.value).resolves.toEqual( - 23 - ); + expect(pendingExpectation.finishCalledWith?.value).toEqual(23); expect(pendingExpectation.finishCalledWith).toMatchObject< Partial >({ @@ -87,15 +85,13 @@ describe('returns', () => { }); }); - it('should set a custom promise rejection', async () => { + it('should set a custom promise rejection', () => { const pendingExpectation = new SpyPendingExpectation(); const error = new Error(); createReturns>(pendingExpectation).thenReject(error); - await expect(pendingExpectation.finishCalledWith?.value).rejects.toEqual( - error - ); + expect(pendingExpectation.finishCalledWith?.value).toEqual(error); expect(pendingExpectation.finishCalledWith).toMatchObject< Partial >({ @@ -105,14 +101,12 @@ describe('returns', () => { }); }); - it('should set an empty promise rejection', async () => { + it('should set an empty promise rejection', () => { const pendingExpectation = new SpyPendingExpectation(); createReturns>(pendingExpectation).thenReject(); - await expect(pendingExpectation.finishCalledWith?.value).rejects.toEqual( - new Error() - ); + expect(pendingExpectation.finishCalledWith?.value).toEqual(new Error()); expect(pendingExpectation.finishCalledWith).toMatchObject< Partial >({ @@ -122,12 +116,12 @@ describe('returns', () => { }); }); - it('should set a promise rejection message', async () => { + it('should set a promise rejection message', () => { const pendingExpectation = new SpyPendingExpectation(); createReturns>(pendingExpectation).thenReject('foobar'); - await expect(pendingExpectation.finishCalledWith?.value).rejects.toEqual( + expect(pendingExpectation.finishCalledWith?.value).toEqual( new Error('foobar') ); expect(pendingExpectation.finishCalledWith).toMatchObject< diff --git a/src/return/returns.ts b/src/return/returns.ts index 3f5e725b..b8484eaa 100644 --- a/src/return/returns.ts +++ b/src/return/returns.ts @@ -140,7 +140,7 @@ export const createReturns = ( thenResolve: (promiseValue: any): InvocationCount => finishPendingExpectation( { - value: Promise.resolve(promiseValue), + value: promiseValue, promiseValue, isError: false, isPromise: true, @@ -151,7 +151,7 @@ export const createReturns = ( thenReject: (errorOrMessage?: Error | string): InvocationCount => finishPendingExpectation( { - value: Promise.reject(getError(errorOrMessage)), + value: getError(errorOrMessage), promiseValue: getError(errorOrMessage), isError: true, isPromise: true, diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts index 71706e36..a7957a2f 100644 --- a/tests/e2e.spec.ts +++ b/tests/e2e.spec.ts @@ -49,6 +49,12 @@ describe('e2e', () => { expect({ ...instance(foo) }).toEqual({ bar: 42 }); }); + it('should not throw an unhandled rejection from an unmet promise expectation', () => { + const fn = mock<() => Promise>(); + + when(fn()).thenReject('if you are seeing this it means the test failed'); + }); + describe('ignoring arguments', () => { it('should support matching anything', () => { const fn = mock();