Skip to content

Commit 7ae1bf8

Browse files
authored
Update index.ts
1 parent b610bfd commit 7ae1bf8

File tree

1 file changed

+146
-73
lines changed

1 file changed

+146
-73
lines changed

src/index.ts

Lines changed: 146 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -1,80 +1,153 @@
1-
export type FunctionSubstitute<T> = (...args: any[]) => (T & {
2-
returns: (...args: T[]) => void;
3-
})
4-
5-
export type PropertySubstitute<T> = {
6-
returns: (...args: T[]) => void;
7-
}
8-
9-
export type ObjectSubstitute<T extends Object> = {
10-
[P in keyof T]:
11-
T[P] extends (...args: any[]) => infer R ? FunctionSubstitute<R> :
12-
PropertySubstitute<T[P]>;
13-
}
14-
15-
export class Substitute {
16-
static for<T>(instance: T): ObjectSubstitute<T> {
17-
const lastRecord = {
18-
type: null as string,
19-
target: null as T,
20-
metadata: null as any
21-
};
1+
export type FunctionSubstitute<F extends any[], T> = (...args: F) => (T & {
2+
returns: (...args: T[]) => void;
3+
})
224

23-
const record = (type: string, target: T, metadata: any) => {
24-
lastRecord.type = type;
25-
lastRecord.target = target;
26-
lastRecord.metadata = metadata;
27-
};
5+
export type PropertySubstitute<T> = {
6+
returns: (...args: T[]) => void;
7+
}
288

29-
const setupRecording = (localTarget: any) => {
30-
const proxy = new Proxy(localTarget, {
31-
get: (target, property) => {
32-
record('get', target, property);
33-
return target[property];
34-
},
35-
apply: (target, thisArg, argumentsList) => {
36-
record('apply', target, argumentsList);
37-
return target
38-
}
39-
});
9+
export type ObjectSubstitute<T extends Object> = {
10+
[P in keyof T]:
11+
T[P] extends (...args: infer F) => infer R ? FunctionSubstitute<F, R> :
12+
PropertySubstitute<T[P]>;
13+
}
14+
15+
export class Substitute {
16+
static for<T>(): ObjectSubstitute<T> {
17+
let lastRecord: {
18+
arguments: Array<any>,
19+
shouldReturn: Array<any>,
20+
currentReturnOffset: number,
21+
proxy: any
22+
};
4023

41-
return proxy;
24+
const createRecord = () => {
25+
lastRecord = {
26+
arguments: null,
27+
shouldReturn: [],
28+
proxy: null,
29+
currentReturnOffset: 0
4230
};
31+
32+
return lastRecord;
33+
};
34+
35+
const equals = (a: any, b: any) => {
36+
if((!a || !b) && a !== b)
37+
return false;
38+
39+
if(typeof a !== typeof b)
40+
return false;
41+
42+
if(Array.isArray(a) !== Array.isArray(b))
43+
return false;
44+
45+
if(Array.isArray(a) && Array.isArray(b)) {
46+
if(a.length !== b.length)
47+
return false;
48+
49+
for(let i=0;i<a.length;i++) {
50+
if(!equals(a[i], b[i]))
51+
return false;
52+
}
53+
54+
return true;
55+
}
56+
57+
return a === b;
4358
}
59+
60+
const createProxy = (r: any = null) => {
61+
let localRecord: typeof lastRecord = r;
62+
63+
let thisProxy: any;
64+
return thisProxy = new Proxy(() => {}, {
65+
apply: (_target, _thisArg, argumentsList) => {
66+
if(localRecord.arguments) {
67+
if(!equals(localRecord.arguments, argumentsList))
68+
return localRecord.proxy || (localRecord.proxy = createProxy());
69+
70+
return localRecord.shouldReturn[localRecord.currentReturnOffset++];
71+
}
72+
73+
localRecord.arguments = argumentsList;
74+
return thisProxy;
75+
},
76+
get: (target, property) => {
77+
if(typeof property === 'symbol')
78+
return void 0;
79+
80+
if(property === 'valueOf')
81+
return void 0;
82+
83+
if(property === 'toString')
84+
return target[property].toString();
85+
86+
if(property === 'inspect')
87+
return () => "{SubstituteJS fake}";
88+
89+
if(property === 'constructor')
90+
return () => thisProxy;
91+
92+
if(property === 'returns') {
93+
return (...args: any[]) => {
94+
localRecord.shouldReturn = args;
95+
};
96+
}
97+
98+
if(localRecord) {
99+
if(localRecord.arguments)
100+
return thisProxy;
101+
102+
return localRecord.shouldReturn[localRecord.currentReturnOffset];
103+
}
104+
105+
localRecord = createRecord();
106+
return thisProxy;
107+
}
108+
});
109+
};
110+
111+
return createProxy() as any;
44112
}
45-
46-
class Example {
47-
a = "1337";
48-
b = 1337;
49-
50-
c(arg1: string, arg2: string) {
51-
return "hello " + arg1 + " world (" + arg2 + ")";
52-
}
53-
54-
get d() {
55-
return 1337;
56-
}
57-
58-
set v(x) {
59-
console.log('define: ' + x);
60-
}
113+
}
114+
115+
class Example {
116+
a = "1337";
117+
b = 1337;
118+
119+
c(arg1: string, arg2: string) {
120+
return "hello " + arg1 + " world (" + arg2 + ")";
61121
}
62-
63-
console.log('start');
64-
65-
var ex = new Example();
66-
var exFake = Substitute.for(ex);
67-
68-
exFake.a.returns("foo", "bar");
69-
exFake.b.returns(10, 30);
70-
exFake.c("hi", "there").returns("blah", "haha");
71-
exFake.d.returns(9);
72-
73-
console.log(exFake.a);
74-
console.log(exFake.b);
75-
76-
console.log(exFake.c("hi", "there"));
77-
console.log(exFake.c("hi", "there"));
78-
console.log(exFake.c("something", "there"));
79-
80-
console.log(exFake.d);
122+
123+
get d() {
124+
return 1337;
125+
}
126+
127+
set v(x) {
128+
console.log('define: ' + x);
129+
}
130+
}
131+
132+
var exFake = Substitute.for<Example>();
133+
134+
exFake.a.returns("foo", "bar");
135+
console.log('returned', exFake.a);
136+
console.log('returned', exFake.a);
137+
138+
exFake.b.returns(10, 30);
139+
exFake.c("hi", "there").returns("blah", "haha");
140+
exFake.d.returns(9);
141+
142+
console.log(exFake.a);
143+
console.log(exFake.b);
144+
145+
console.log('assert');
146+
147+
console.log(exFake.c("hi", "there"));
148+
console.log(exFake.c("hi", "the1re"));
149+
console.log(exFake.c("hi", "there"));
150+
console.log(exFake.c("hi", "there"));
151+
console.log(exFake.c("something", "there"));
152+
153+
console.log(exFake.d);

0 commit comments

Comments
 (0)