Skip to content

Commit 51c3081

Browse files
committed
Add resolves() method
1 parent cda5761 commit 51c3081

File tree

5 files changed

+65
-4
lines changed

5 files changed

+65
-4
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ interface Calculator {
1818
add(a: number, b: number): number;
1919
subtract(a: number, b: number): number;
2020
divide(a: number, b: number): number;
21+
async heavyOperation(): Promise<number>;
2122

2223
isEnabled: boolean;
2324
}
@@ -53,6 +54,15 @@ console.log(calculator.add(1, 2)); //prints 9
5354
console.log(calculator.add(1, 2)); //prints undefined
5455
```
5556

57+
## Working with promises
58+
When working with promises you can also use the method `resolves()` to return a promise.
59+
60+
```typescript
61+
calculator.heavyOperation(1, 2).resolves(4);
62+
//same as calculator.heavyOperation(1, 2).returns(Promise.resolve(4));
63+
console.log(await calculator.heavyOperation(1, 2)); //prints 4
64+
```
65+
5666
## Verifying calls
5767
```typescript
5868
calculator.enabled = true;

spec/index.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,22 @@ test('returning resolved promises works', async t => {
174174
t.is(1338, await substitute.returnPromise());
175175
});
176176

177+
test('resolving other fake works', async t => {
178+
initialize();
179+
180+
const otherSubstitute = Substitute.for<Dummy>();
181+
substitute.returnPromise().resolves(otherSubstitute);
182+
t.is(otherSubstitute, await substitute.returnPromise());
183+
});
184+
185+
test('resolving promises works', async t => {
186+
initialize();
187+
188+
substitute.returnPromise().resolves(1338);
189+
190+
t.is(1338, await substitute.returnPromise());
191+
});
192+
177193
test('class void returns', t => {
178194
initialize();
179195

src/Transformations.ts

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,23 @@
11
import { AllArguments } from "./Arguments";
22

33
export type NoArgumentFunctionSubstitute<TReturnType> =
4-
(() => (TReturnType & NoArgumentMockObjectMixin<TReturnType>))
4+
TReturnType extends Promise<infer U> ?
5+
(() => (TReturnType & NoArgumentMockObjectPromise<TReturnType>)) :
6+
(() => (TReturnType & NoArgumentMockObjectMixin<TReturnType>))
57

68
export type FunctionSubstitute<TArguments extends any[], TReturnType> =
7-
((...args: TArguments) => (TReturnType & MockObjectMixin<TArguments, TReturnType>)) &
8-
((allArguments: AllArguments) => (TReturnType & MockObjectMixin<TArguments, TReturnType>))
9+
TReturnType extends Promise<infer U> ?
10+
((...args: TArguments) => (TReturnType & MockObjectPromise<TArguments, TReturnType>)) &
11+
((allArguments: AllArguments) => (TReturnType & MockObjectPromise<TArguments, TReturnType>)) :
12+
((...args: TArguments) => (TReturnType & MockObjectMixin<TArguments, TReturnType>)) &
13+
((allArguments: AllArguments) => (TReturnType & MockObjectMixin<TArguments, TReturnType>))
914

1015
export type PropertySubstitute<TReturnType> = (TReturnType & Partial<NoArgumentMockObjectMixin<TReturnType>>);
1116

17+
type Unpacked<T> =
18+
T extends Promise<infer U> ? U :
19+
T;
20+
1221
type BaseMockObjectMixin<TReturnType> = {
1322
returns: (...args: TReturnType[]) => void;
1423
throws: (exception: any) => void;
@@ -22,6 +31,14 @@ type MockObjectMixin<TArguments extends any[], TReturnType> = BaseMockObjectMixi
2231
mimicks: (func: (...args: TArguments) => TReturnType) => void;
2332
}
2433

34+
type NoArgumentMockObjectPromise<TReturnType> = NoArgumentMockObjectMixin<TReturnType> & {
35+
resolves: (...args: Unpacked<TReturnType>[]) => void;
36+
}
37+
38+
type MockObjectPromise<TArguments extends any[], TReturnType> = MockObjectMixin<TArguments, TReturnType> & {
39+
resolves: (...args: Unpacked<TReturnType>[]) => void;
40+
}
41+
2542
export type ObjectSubstitute<T extends Object, K extends Object = T> = ObjectSubstituteTransformation<T> & {
2643
received(amount?: number): TerminatingObject<K>;
2744
didNotReceive(): TerminatingObject<K>;

src/Utilities.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ export enum SubstituteMethods {
1616
didNotReceive = 'didNotReceive',
1717
mimicks = 'mimicks',
1818
throws = 'throws',
19-
returns = 'returns'
19+
returns = 'returns',
20+
resolves = 'resolves'
2021
}
2122

2223
export const Nothing = Symbol();

src/states/FunctionState.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,23 @@ export class FunctionState implements ContextState {
156156
};
157157
}
158158

159+
if (property === SubstituteMethods.resolves) {
160+
return (...returns: any[]) => {
161+
if (!this._lastArgs) {
162+
throw SubstituteException.generic('Eh, there\'s a bug, no args recorded for this return :/')
163+
}
164+
returns = returns.map(value => Promise.resolve(value))
165+
this.returns.push({
166+
returnValues: returns,
167+
returnIndex: 0,
168+
args: this._lastArgs
169+
})
170+
this._calls.pop()
171+
172+
context.state = context.initialState;
173+
};
174+
}
175+
159176
return context.proxy;
160177
}
161178
}

0 commit comments

Comments
 (0)