@@ -46,32 +46,30 @@ export class Mocker {
4646 return new Proxy ( this . mock , this . createCatchAllHandlerForRemainingPropertiesWithoutGetters ( "expectation" ) ) ;
4747 }
4848
49- public createCatchAllHandlerForRemainingPropertiesWithoutGetters ( origin : "instance" | "expectation" ) : any {
49+ public createCatchAllHandlerForRemainingPropertiesWithoutGetters ( origin : "instance" | "expectation" ) : any {
5050 return {
5151 get : ( target : any , name : PropertyKey ) => {
5252 const hasMethodStub = name in target ;
5353 if ( ! hasMethodStub ) {
54- if ( this . mock . __policy === MockPropertyPolicy . StubAsMethod ) {
55- if ( origin !== "instance" || name !== "then" ) {
56- // Don't make this mock object instance look like a Promise instance by mistake, if someone is checking
57- this . createMethodStub ( name . toString ( ) ) ;
58- this . createInstanceActionListener ( name . toString ( ) , { } ) ;
59- }
60- } else if ( this . mock . __policy === MockPropertyPolicy . StubAsProperty ) {
61- this . createPropertyStub ( name . toString ( ) ) ;
62- this . createInstancePropertyDescriptorListener ( name . toString ( ) , { } , this . clazz . prototype ) ;
63- } else if ( this . mock . __policy === MockPropertyPolicy . Throw ) {
64- if ( origin === "instance" ) {
65- throw new Error ( "Trying to read property " + name . toString ( ) + " from a mock object, which was not expected." ) ;
66- } else {
67- // TODO: Assuming it is a property, not a function. Fix this...
54+ if ( origin === "instance" ) {
55+ if ( this . mock . __policy === MockPropertyPolicy . StubAsMethod ) {
56+ if ( name !== "then" ) {
57+ // Don't make this mock object instance look like a Promise instance by mistake, if someone is checking
58+ this . createMethodStub ( name . toString ( ) ) ;
59+ this . createInstanceActionListener ( name . toString ( ) , { } ) ;
60+ }
61+ } else if ( this . mock . __policy === MockPropertyPolicy . StubAsProperty ) {
6862 this . createPropertyStub ( name . toString ( ) ) ;
6963 this . createInstancePropertyDescriptorListener ( name . toString ( ) , { } , this . clazz . prototype ) ;
64+ } else if ( this . mock . __policy === MockPropertyPolicy . Throw ) {
65+ throw new Error ( "Trying to read property " + name . toString ( ) + " from a mock object, which was not expected." ) ;
66+ } else {
67+ throw new Error ( "Invalid MockPolicy value" ) ;
7068 }
71- } else {
72- throw new Error ( "Invalid MockPolicy value" ) ;
69+ } else if ( origin === "expectation" ) {
70+ this . createMixedStub ( name . toString ( ) ) ;
7371 }
74- }
72+ }
7573 return target [ name ] ;
7674 } ,
7775 } ;
@@ -112,7 +110,6 @@ export class Mocker {
112110 if ( descriptor . get ) {
113111 this . createPropertyStub ( name ) ;
114112 this . createInstancePropertyDescriptorListener ( name , descriptor , obj ) ;
115- this . createInstanceActionListener ( name , obj ) ;
116113 } else if ( typeof descriptor . value === "function" ) {
117114 this . createMethodStub ( name ) ;
118115 this . createInstanceActionListener ( name , obj ) ;
@@ -178,6 +175,54 @@ export class Mocker {
178175 } ) ;
179176 }
180177
178+ private createMixedStub ( key : string ) : void {
179+ if ( this . mock . hasOwnProperty ( key ) ) {
180+ return ;
181+ }
182+
183+ // Assume it is a property stub, until proven otherwise
184+ let isProperty = true ;
185+
186+ Object . defineProperty ( this . instance , key , {
187+ get : ( ) => {
188+ if ( isProperty ) {
189+ return this . createActionListener ( key ) ( ) ;
190+ } else {
191+ return this . createActionListener ( key ) ;
192+ }
193+ }
194+ } ) ;
195+
196+ let methodMock = ( ...args ) => {
197+ isProperty = false ;
198+
199+ const matchers : Matcher [ ] = [ ] ;
200+
201+ for ( const arg of args ) {
202+ if ( ! ( arg instanceof Matcher ) ) {
203+ matchers . push ( strictEqual ( arg ) ) ;
204+ } else {
205+ matchers . push ( arg ) ;
206+ }
207+ }
208+
209+ return new MethodToStub ( this . methodStubCollections [ key ] , matchers , this , key ) ;
210+ } ;
211+
212+ let propertyMock = ( ) => {
213+ if ( ! this . methodStubCollections [ key ] ) {
214+ this . methodStubCollections [ key ] = new MethodStubCollection ( ) ;
215+ }
216+
217+ // Return a mix of a method stub and a property invocation, which works as both
218+ return Object . assign ( methodMock , new MethodToStub ( this . methodStubCollections [ key ] , [ ] , this , key ) ) ;
219+ } ;
220+
221+ Object . defineProperty ( this . mock , key , {
222+ get : propertyMock ,
223+ } ) ;
224+ }
225+
181226 private createPropertyStub ( key : string ) : void {
182227 if ( this . mock . hasOwnProperty ( key ) ) {
183228 return ;
0 commit comments