Skip to content

Commit 8ac9d68

Browse files
Ilham-HabibullinffMathy
authored andcommitted
Fix 51 issue (#70)
* added enum for object member types * prettified error message * attempt to fix 51 & 45 issues * receivedProxy.set does same thing as _proxy.set, throwing error like i was doing before was breaking test called "class method received" * All code from initialState.get except checking that property is special method like "received" moved into Get unitlity, added method initialState.recordedGetPropertyState & initialState.recordedSetPropertyState * 1. Get, set and apply of Context now expect full list of arguments that that expects same method of proxy. 2. Recording getState on call special method like "received" if disableFor was used on substituted object * changed 51 issue test, works as supposed to now * throwing error on calls to setPropertyState.get & setPropertyState.apply
1 parent 92f7e60 commit 8ac9d68

File tree

8 files changed

+101
-56
lines changed

8 files changed

+101
-56
lines changed

spec/issues/51.test.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,7 @@ test('issue 51 - All functions shares the same state', async t => {
1414
t.is(result, 4);
1515
try {
1616
calculator.received().divide(1, 2);
17-
t.fail('Expected to have failed.');
1817
} catch (e) {
19-
t.regex(e.toString(), /Expected 1 or more calls to the property divide with no arguments/);
18+
t.regex(e.toString(), /Error: there are no mock for property: divide/);
2019
}
2120
});

src/Context.ts

Lines changed: 26 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,51 +7,51 @@ export class Context {
77

88
private _proxy: any;
99
private _rootProxy: any;
10+
private _receivedProxy: any;
1011

1112
private _state: ContextState;
13+
private _receivedState: ContextState;
1214

1315
constructor() {
1416
this._initialState = new InitialState();
1517
this._state = this._initialState;
1618

1719
this._proxy = new Proxy(() => { }, {
18-
apply: (_target, _this, args) => {
19-
return this.apply(args);
20-
},
21-
set: (_target, property, value) => {
22-
this.set(property, value);
23-
return true;
24-
},
25-
get: (_target, property) => {
26-
return this.get(property);
27-
}
20+
apply: (_target, _this, args) => this.apply(_target, _this, args),
21+
set: (_target, property, value) => (this.set(_target, property, value), true),
22+
get: (_target, property) => this.get(_target, property)
2823
});
2924

3025
this._rootProxy = new Proxy(() => { }, {
31-
apply: (_target, _this, args) => {
32-
return this.initialState.apply(this, args);
33-
},
34-
set: (_target, property, value) => {
35-
this.initialState.set(this, property, value);
36-
return true;
37-
},
26+
apply: (_target, _this, args) => this.initialState.apply(this, args),
27+
set: (_target, property, value) => (this.initialState.set(this, property, value), true),
28+
get: (_target, property) => this.initialState.get(this, property)
29+
});
30+
31+
this._receivedProxy = new Proxy(() => { }, {
32+
apply: (_target, _this, args) => this._receivedState.apply(this, args),
33+
set: (_target, property, value) => (this.set(_target, property, value), true),
3834
get: (_target, property) => {
39-
return this.initialState.get(this, property);
35+
const state = this.initialState.getPropertyStates.find(getPropertyState => getPropertyState.property === property)
36+
if (state === void 0) throw new Error(`there are no mock for property: ${String(property)}`)
37+
this._receivedState = state
38+
return this.receivedProxy;
4039
}
4140
});
4241
}
4342

44-
apply(args: any[]) {
43+
apply(_target: any, _this: any, args: any[]) {
4544
return this._state.apply(this, args);
4645
}
4746

48-
set(property: PropertyKey, value: any) {
47+
set(_target: any, property: PropertyKey, value: any) {
4948
return this._state.set(this, property, value);
5049
}
5150

52-
get(property: PropertyKey) {
53-
if(property === HandlerKey)
51+
get(_target: any, property: PropertyKey) {
52+
if(property === HandlerKey) {
5453
return this;
54+
}
5555

5656
return this._state.get(this, property);
5757
}
@@ -64,6 +64,10 @@ export class Context {
6464
return this._rootProxy;
6565
}
6666

67+
public get receivedProxy() {
68+
return this._receivedProxy;
69+
}
70+
6771
public get initialState() {
6872
return this._initialState;
6973
}

src/Substitute.ts

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { Context } from "./Context";
22
import { ObjectSubstitute, OmitProxyMethods, DisabledSubstituteObject } from "./Transformations";
3+
import { Get } from './Utilities'
34

45
export const HandlerKey = Symbol();
56
export const AreProxiesDisabledKey = Symbol();
@@ -13,22 +14,29 @@ export class Substitute {
1314
}
1415

1516
static disableFor<T extends ObjectSubstitute<OmitProxyMethods<any>>>(substitute: T): DisabledSubstituteObject<T> {
16-
const thisProxy = substitute as any;
17-
const thisExposedProxy = thisProxy[HandlerKey];
17+
const thisProxy = substitute as any; // rootProxy
18+
const thisExposedProxy = thisProxy[HandlerKey]; // Context
1819

1920
const disableProxy = <K extends Function>(f: K): K => {
2021
return function() {
21-
thisProxy[AreProxiesDisabledKey] = true;
22+
thisProxy[AreProxiesDisabledKey] = true; // for what reason need to do this?
2223
const returnValue = f.call(thisExposedProxy, ...arguments);
2324
thisProxy[AreProxiesDisabledKey] = false;
2425
return returnValue;
2526
} as any;
2627
};
2728

2829
return new Proxy(() => { }, {
29-
apply: disableProxy(thisExposedProxy.apply),
30-
set: disableProxy(thisExposedProxy.set),
31-
get: disableProxy(thisExposedProxy.get)
30+
apply: function (_target, _this, args) {
31+
return disableProxy(thisExposedProxy.apply)(...arguments)
32+
},
33+
set: function (_target, property, value) {
34+
return disableProxy(thisExposedProxy.set)(...arguments)
35+
},
36+
get: function (_target, property) {
37+
Get(thisExposedProxy._initialState, thisExposedProxy, property)
38+
return disableProxy(thisExposedProxy.get)(...arguments)
39+
}
3240
}) as any;
3341
}
3442
}

src/Utilities.ts

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,16 @@
11
import { Argument, AllArguments } from "./Arguments";
2+
import { GetPropertyState } from './states/GetPropertyState'
3+
import { InitialState } from './states/InitialState'
4+
import { Context } from './Context'
25
import util = require('util')
36

47
export type Call = any[] // list of args
58

9+
export enum Type {
10+
method = 'method',
11+
property = 'property'
12+
}
13+
614
export function stringifyArguments(args: any[]) {
715
args = args.map(x => util.inspect(x));
816
return args && args.length > 0 ? 'arguments [' + args.join(', ') + ']' : 'no arguments';
@@ -51,4 +59,19 @@ export function areArgumentsEqual(a: any, b: any) {
5159
return b.matches(a);
5260

5361
return a === b;
54-
};
62+
};
63+
64+
export function Get(recorder: InitialState, context: Context, property: PropertyKey) {
65+
const existingGetState = recorder.getPropertyStates.find(state => state.property === property);
66+
if (existingGetState) {
67+
context.state = existingGetState;
68+
return context.get(void 0, property);
69+
}
70+
71+
const getState = new GetPropertyState(property);
72+
context.state = getState;
73+
74+
recorder.recordGetPropertyState(property, getState);
75+
76+
return context.get(void 0, property);
77+
}

src/states/FunctionState.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
11
import { ContextState, PropertyKey } from "./ContextState";
22
import { Context } from "src/Context";
3-
import { stringifyArguments, stringifyCalls, areArgumentsEqual, areArgumentArraysEqual, Call } from "../Utilities";
3+
import { areArgumentArraysEqual, Call, Type } from "../Utilities";
44
import { GetPropertyState } from "./GetPropertyState";
5-
import { Argument, Arg } from "../Arguments";
65

76
const Nothing = Symbol()
87

@@ -48,7 +47,7 @@ export class FunctionState implements ContextState {
4847
context.initialState.assertCallCountMatchesExpectations(
4948
this._calls,
5049
this.getCallCount(args),
51-
'method',
50+
Type.method,
5251
this.property,
5352
args);
5453

src/states/GetPropertyState.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { ContextState, PropertyKey } from "./ContextState";
22
import { Context } from "src/Context";
33
import { FunctionState } from "./FunctionState";
4+
import { Type } from "../Utilities";
45

56
const Nothing = Symbol();
67

@@ -45,7 +46,7 @@ export class GetPropertyState implements ContextState {
4546
context.state = functionState;
4647
this._functionState = functionState
4748

48-
return context.apply(args);
49+
return context.apply(void 0, void 0, args);
4950
}
5051

5152
set(context: Context, property: PropertyKey, value: any) {
@@ -99,7 +100,7 @@ export class GetPropertyState implements ContextState {
99100
context.initialState.assertCallCountMatchesExpectations(
100101
[[]], // I'm not sure what this was supposed to mean
101102
this.callCount,
102-
'property',
103+
Type.property,
103104
this.property,
104105
[]);
105106

src/states/InitialState.ts

Lines changed: 27 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { ContextState, PropertyKey } from "./ContextState";
22
import { Context } from "src/Context";
33
import { GetPropertyState } from "./GetPropertyState";
44
import { SetPropertyState } from "./SetPropertyState";
5-
import { stringifyArguments, stringifyCalls, Call } from "../Utilities";
5+
import { stringifyArguments, stringifyCalls, Call, Type, Get } from "../Utilities";
66
import { AreProxiesDisabledKey } from "../Substitute";
77

88
export class InitialState implements ContextState {
@@ -13,6 +13,8 @@ export class InitialState implements ContextState {
1313
private _areProxiesDisabled: boolean;
1414

1515
public get expectedCount() {
16+
// expected count of calls,
17+
// being assigned with received() method call
1618
return this._expectedCount;
1719
}
1820

@@ -28,6 +30,14 @@ export class InitialState implements ContextState {
2830
return [...this.recordedGetPropertyStates.values()];
2931
}
3032

33+
public recordGetPropertyState(property: PropertyKey, getState: GetPropertyState) {
34+
this.recordedGetPropertyStates.set(property, getState);
35+
}
36+
37+
public recordSetPropertyState(setState: SetPropertyState) {
38+
this.recordedSetPropertyStates.push(setState);
39+
}
40+
3141
constructor() {
3242
this.recordedGetPropertyStates = new Map();
3343
this.recordedSetPropertyStates = [];
@@ -36,14 +46,26 @@ export class InitialState implements ContextState {
3646
this._expectedCount = void 0;
3747
}
3848

39-
assertCallCountMatchesExpectations(calls: Call[], callCount: number, type: string, property: PropertyKey, args: any[]) {
49+
assertCallCountMatchesExpectations(
50+
calls: Call[], // list of arguments
51+
callCount: number,
52+
type: Type, // method or property
53+
property: PropertyKey,
54+
args: any[]
55+
) {
4056
const expectedCount = this._expectedCount;
4157

4258
this.clearExpectations();
4359
if(this.doesCallCountMatchExpectations(expectedCount, callCount))
4460
return;
4561

46-
throw new Error('Expected ' + (expectedCount === null ? '1 or more' : expectedCount) + ' call' + (expectedCount === 1 ? '' : 's') + ' to the ' + type + ' ' + property.toString() + ' with ' + stringifyArguments(args) + ', but received ' + (callCount === 0 ? 'none' : callCount) + ' of such call' + (callCount === 1 ? '' : 's') + '.\nAll calls received to ' + type + ' ' + property.toString() + ':' + stringifyCalls(calls));
62+
throw new Error(
63+
'Expected ' + (expectedCount === null ? '1 or more' : expectedCount) +
64+
' call' + (expectedCount === 1 ? '' : 's') + ' to the ' + type + ' ' + property.toString() +
65+
' with ' + stringifyArguments(args) + ', but received ' + (callCount === 0 ? 'none' : callCount) +
66+
' of such call' + (callCount === 1 ? '' : 's') +
67+
'.\nAll calls received to ' + type + ' ' + property.toString() + ':' + stringifyCalls(calls)
68+
);
4769
}
4870

4971
private doesCallCountMatchExpectations(expectedCount: number|undefined|null, actualCount: number) {
@@ -116,22 +138,11 @@ export class InitialState implements ContextState {
116138
if (property === 'received') {
117139
return (count?: number) => {
118140
this._expectedCount = count === void 0 ? null : count;
119-
return context.proxy;
141+
return context.receivedProxy;
120142
};
121143
}
122144

123-
const existingGetState = this.recordedGetPropertyStates.get(property);
124-
if (existingGetState) {
125-
context.state = existingGetState;
126-
return context.get(property);
127-
}
128-
129-
const getState = new GetPropertyState(property);
130-
context.state = getState;
131-
132-
this.recordedGetPropertyStates.set(property, getState);
133-
134-
return context.get(property);
145+
return Get(this, context, property)
135146
}
136147

137148
private clearExpectations() {

src/states/SetPropertyState.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { ContextState, PropertyKey } from "./ContextState";
22
import { Context } from "src/Context";
3-
import { areArgumentsEqual } from "../Utilities";
3+
import { areArgumentsEqual, Type } from "../Utilities";
44

55
const Nothing = Symbol();
66

@@ -27,7 +27,7 @@ export class SetPropertyState implements ContextState {
2727
}
2828

2929
apply(context: Context): undefined {
30-
return void 0;
30+
throw new Error('Calling apply of setPropertyState is not normal behaviour, something gone wrong')
3131
}
3232

3333
set(context: Context, property: PropertyKey, value: any) {
@@ -44,7 +44,7 @@ export class SetPropertyState implements ContextState {
4444
context.initialState.assertCallCountMatchesExpectations(
4545
[[]], // not sure what this was supposed to do
4646
callCount,
47-
'property',
47+
Type.property,
4848
this.property,
4949
this.arguments);
5050

@@ -54,6 +54,6 @@ export class SetPropertyState implements ContextState {
5454
}
5555

5656
get(context: Context, property: PropertyKey): undefined {
57-
return void 0;
57+
throw new Error('Calling get of setPropertyState is not normal behaviour, something gone wrong')
5858
}
5959
}

0 commit comments

Comments
 (0)