Skip to content

Commit 5a11a57

Browse files
authored
Merge pull request #239 from sgtoj/improve-ts
Improve TS Support
2 parents 73f27b8 + 6c9009f commit 5a11a57

File tree

2 files changed

+229
-52
lines changed

2 files changed

+229
-52
lines changed

index.d.ts

Lines changed: 205 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,82 +1,235 @@
1-
export type TestDouble = Function;
1+
//
2+
// types and interfaces
3+
// ----------------------------------------------------------------------------
24

3-
export type DoubledObjectWithKey<Key extends string> = {};
5+
export type DoubledObject<T> = T;
46

5-
export type DoubledObject<Subject> = Subject;
7+
export type DoubledObjectWithKey<T extends string> = { [K in T] };
68

7-
export interface TestdoubleConfig {
8-
promiseConstructor?: any;
9-
ignoreWarnings?: boolean;
10-
suppressErrors?: boolean;
11-
}
12-
export function config(config: TestdoubleConfig): void;
13-
14-
declare function functionDouble(name?: string): TestDouble;
15-
export { functionDouble as function };
9+
export type TestDouble<T> = T;
1610

17-
// When passed class or constructor function
18-
export function object<T>(constructor: { new (...args: any[]): T }): DoubledObject<T>;
11+
export type TestDoubleConstructor<T> = Constructor<T>;
1912

20-
// When passed array of props
21-
export function object<Key extends string>(props: Key[]): DoubledObjectWithKey<Key>;
22-
23-
// When passed class or constuctor function name as string value
24-
export function object<T>(object: string): DoubledObject<T>;
25-
26-
// When passed general object
27-
export function object<T>(object: T): DoubledObject<T>;
13+
interface Call {
14+
context: {};
15+
args: any[];
16+
}
2817

29-
export interface Stubber {
30-
thenReturn(...args: any[]): TestDouble;
31-
thenDo(f: Function): TestDouble;
32-
thenThrow(e: Error): TestDouble;
33-
thenResolve(...args: any[]): TestDouble;
34-
thenReject(e: Error): TestDouble;
35-
thenCallback(...args: any[]): TestDouble;
18+
interface Constructor<T> {
19+
new (...args: any[]): T
3620
}
3721

38-
export function callback(...args: any[]): void;
22+
export interface Captor {
23+
capture(): any;
24+
value?: any;
25+
values?: any[];
26+
}
3927

40-
export function when(...args: any[]): Stubber;
28+
export interface Explanation {
29+
callCount: number;
30+
calls: Call[];
31+
description: string;
32+
}
4133

4234
export interface Matchers {
4335
anything(): any;
4436
isA(type: Function): any;
45-
contains(a: string|any[]|{}): any;
37+
contains(a: string | any[] | {}): any;
4638
argThat(matcher: Function): any;
4739
not(v: any): any;
4840
captor(): Captor
4941
}
5042

51-
export interface Captor {
52-
capture(): any;
53-
value?: any;
54-
values?: any[];
55-
}
56-
5743
export const matchers: Matchers;
5844

59-
export function replace(path: string, f?: any): any;
60-
export function replace(path: {}, property: string, f?: any): any;
45+
export interface Stubber {
46+
thenReturn<T>(...args: any[]): TestDouble<T>;
47+
thenDo<T>(f: Function): TestDouble<T>;
48+
thenThrow<T>(e: Error): TestDouble<T>;
49+
thenResolve<T>(...args: any[]): TestDouble<T>;
50+
thenReject<T>(e: Error): TestDouble<T>;
51+
thenCallback<T>(...args: any[]): TestDouble<T>;
52+
}
6153

62-
export function reset(): void;
54+
export interface TestdoubleConfig {
55+
promiseConstructor?: any;
56+
ignoreWarnings?: boolean;
57+
suppressErrors?: boolean;
58+
}
6359

6460
export interface VerificationConfig {
6561
ignoreExtraArgs?: boolean;
6662
times?: number;
6763
}
6864

69-
export function verify(a: any, check?: VerificationConfig): void;
65+
//
66+
// general
67+
// ----------------------------------------------------------------------------
68+
69+
/**
70+
* Update the configuration. Configuration will perist through the lifetime of
71+
* of the entire test. If you need to change a configuration property for a
72+
* single test, you'll need to manage undoing the change yourself (e.g. in
73+
* beforeEach and afterEach hooks).
74+
*
75+
* @export
76+
* @param {TestdoubleConfig} config
77+
*/
78+
export function config(config: TestdoubleConfig): void;
7079

71-
interface Call {
72-
context: {};
73-
args: any[];
74-
}
80+
/**
81+
* Reset the state.
82+
*
83+
* @export
84+
*/
85+
export function reset(): void;
7586

76-
export interface Explanation {
77-
callCount: number;
78-
calls: Call[];
79-
description: string;
80-
}
87+
/**
88+
* Takes a test double function as an argument and will describe the current
89+
* configuration and state of the test double.
90+
*
91+
* @export
92+
* @template T
93+
* @param {TestDouble<T>} f
94+
* @returns {Explanation}
95+
*/
96+
export function explain<T>(f: TestDouble<T>): Explanation;
97+
98+
99+
//
100+
// fake: constructors
101+
// ----------------------------------------------------------------------------
102+
103+
/**
104+
* Create a fake object constructor the given class.
105+
*
106+
* @export
107+
* @template T
108+
* @param {{ new (...args: any[]): T }} constructor
109+
* @returns {DoubledObject<T>}
110+
*/
111+
export function constructor<T>(constructor: Constructor<T>): TestDoubleConstructor<T>;
112+
113+
//
114+
// fake: functions
115+
// ----------------------------------------------------------------------------
116+
117+
/**
118+
* Create a fake function.
119+
*
120+
* @param {string} [name] Name of function for better messages.
121+
* @returns {TestDouble<Function>}
122+
*/
123+
declare function functionDouble(name?: string): TestDouble<Function>;
124+
125+
/**
126+
* Create a fake function.
127+
*
128+
* @template T
129+
* @param {T} [name] Name of function to copy.
130+
* @returns {TestDouble<T>}
131+
*/
132+
declare function functionDouble<T>(name?: T): TestDouble<T>;
81133

82-
export function explain(f: TestDouble): Explanation;
134+
export { functionDouble as function };
135+
export { functionDouble as func };
136+
137+
//
138+
// fake: objects
139+
// ----------------------------------------------------------------------------
140+
141+
/**
142+
* Create a fake object that is deep copy of the given object.
143+
*
144+
* @export
145+
* @template T
146+
* @param {{ new (...args: any[]): T }} constructor
147+
* @returns {DoubledObject<T>}
148+
*/
149+
export function object<T>(constructor: Constructor<T> ): DoubledObject<T>;
150+
151+
/**
152+
* Create a fake object that has the given list of properties.
153+
*
154+
* @export
155+
* @template Key
156+
* @param {Key[]} props Array of properties.
157+
* @returns {DoubledObjectWithKey<Key>}
158+
*/
159+
export function object<T extends string>(props: T[]): DoubledObjectWithKey<T>;
160+
161+
/**
162+
* Create a fake empty object that is cast as the generic using a Proxy object.
163+
*
164+
* @export
165+
* @template T
166+
* @param {T} object Name of object.
167+
* @returns {DoubledObject<T>}
168+
*/
169+
export function object<T>(object: string): DoubledObject<T>;
170+
171+
/**
172+
* Create a fake object that is deep copy of the given object.
173+
*
174+
* @export
175+
* @template T
176+
* @param {T} object Object to copy.
177+
* @returns {DoubledObject<T>}
178+
*/
179+
export function object<T>(object: T): DoubledObject<T>;
180+
181+
//
182+
// stubbing
183+
// ----------------------------------------------------------------------------
184+
185+
186+
/**
187+
* Callback marker.
188+
*
189+
* @export
190+
* @param {...any[]} args
191+
*/
192+
export function callback(...args: any[]): void;
193+
194+
195+
/**
196+
* Swap out real dependenencies with fake one. Intercept calls to `require`
197+
* that dependency module and ensure your subject is handed a fake instead.
198+
*
199+
* @export
200+
* @param {string} path
201+
* @param {*} [f]
202+
* @returns {*}
203+
*/
204+
export function replace(path: string, f?: any): any;
205+
206+
/**
207+
* Swap out real dependenencies with fake one. Reference to the property will
208+
* be replace it during your test.
209+
*
210+
* @export
211+
* @param {{}} path
212+
* @param {string} property
213+
* @param {*} [f]
214+
* @returns {*}
215+
*/
216+
export function replace(path: {}, property: string, f?: any): any;
217+
218+
219+
/**
220+
* Stub a specific function call.
221+
*
222+
* @export
223+
* @param {...any[]} args
224+
* @returns {Stubber}
225+
*/
226+
export function when(...args: any[]): Stubber;
227+
228+
/**
229+
* Verify a specific function call to a stubbed function.
230+
*
231+
* @export
232+
* @param {...any[]} args
233+
* @returns {Stubber}
234+
*/
235+
export function verify(a: any, check?: VerificationConfig): void;

regression/typescript/test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,26 @@ class Dog {
88
bark() {}
99
}
1010

11+
class Cat {
12+
constructor() {};
13+
meow(): string { return "meow! meow!" }
14+
}
15+
16+
function sum (first: number, second: number): number {
17+
return first + second
18+
}
19+
20+
1121
const dog = td.constructor(Dog);
1222
td.when(dog.prototype.bark()).thenReturn("woof!");
1323

1424
const bird = td.object({ fly: function(){} });
1525
td.when(bird.fly()).thenReturn("swoosh!");
1626

27+
const kitty = td.object(["scratch","meow"]);
28+
td.when(kitty.scratch()).thenReturn("scratch!");
29+
td.when(kitty.meow()).thenReturn("meow!");
30+
1731
if (eval("typeof Proxy") !== "undefined") {
1832
class Bear { constructor() {}; sleep() {}; };
1933
const bear = td.object<Bear>("A bear");
@@ -36,12 +50,22 @@ td.when(f(td.matchers.isA(String))).thenDo(function(s: string) { return s; });
3650
td.when(f(td.matchers.not(true))).thenResolve("value1", "value2");
3751
td.when(f(td.matchers.not(false))).thenReject(new Error("rejected"));
3852

53+
const fakeSum = td.function(sum);
54+
td.when(fakeSum(1, 2)).thenReturn(3);
55+
56+
const fakestSum = td.function("sum");
57+
td.when(fakestSum(1, 2)).thenReturn(3);
58+
3959
f()
4060
td.verify(f());
4161
td.verify(f(), { times: 1 });
4262
td.verify(f(), { ignoreExtraArgs: false });
4363
td.verify(f(), { ignoreExtraArgs: true, times: 1 });
4464

65+
const CatFake = td.constructor(Cat);
66+
const cat = new CatFake()
67+
td.when(cat.meow()).thenReturn("moo!");
68+
4569
const explanation = td.explain(f);
4670

4771
console.log(

0 commit comments

Comments
 (0)