@@ -257,13 +257,17 @@ describe('component: proxy', () => {
257257 expect ( instanceProxy . isDisplayed ) . toBe ( true )
258258 } )
259259
260- test ( 'allow spying on proxy methods' , ( ) => {
260+
261+ test ( 'allow jest spying on proxy methods with Object.defineProperty' , ( ) => {
262+ // #5417
261263 let instanceProxy : any
262264 const Comp = {
263265 render ( ) { } ,
264266 setup ( ) {
265267 return {
266- toggle ( ) { }
268+ toggle ( ) {
269+ return 'a'
270+ }
267271 }
268272 } ,
269273 mounted ( ) {
@@ -275,13 +279,154 @@ describe('component: proxy', () => {
275279
276280 app . mount ( nodeOps . createElement ( 'div' ) )
277281
278- const spy = jest . spyOn ( instanceProxy , 'toggle' )
282+ // access 'toggle' to ensure key is cached
283+ const v1 = instanceProxy . toggle ( )
284+ expect ( v1 ) . toEqual ( 'a' )
285+
286+ // reconfigure "toggle" to be getter based.
287+ let getCalledTimes = 0
288+ Object . defineProperty ( instanceProxy , 'toggle' , {
289+ get ( ) {
290+ getCalledTimes ++
291+ return ( ) => 'b'
292+ }
293+ } )
279294
295+ // getter should not be evaluated on initial definition
296+ expect ( getCalledTimes ) . toEqual ( 0 )
297+
298+ // invoke "toggle" after "defineProperty"
299+ const v2 = instanceProxy . toggle ( )
300+ expect ( v2 ) . toEqual ( 'b' )
301+ expect ( getCalledTimes ) . toEqual ( 1 )
302+
303+ // expect toggle getter not to be cached. it can't be
280304 instanceProxy . toggle ( )
305+ expect ( getCalledTimes ) . toEqual ( 2 )
281306
307+ // attaching jest spy, triggers the getter once, cache it and override the property.
308+ // also uses Object.defineProperty
309+ const spy = jest . spyOn ( instanceProxy , 'toggle' )
310+ expect ( getCalledTimes ) . toEqual ( 3 )
311+
312+ // expect getter to not evaluate the jest spy caches its value
313+ const v3 = instanceProxy . toggle ( )
314+ expect ( v3 ) . toEqual ( 'b' )
282315 expect ( spy ) . toHaveBeenCalled ( )
316+ expect ( getCalledTimes ) . toEqual ( 3 )
317+ } )
318+
319+ test ( 'defineProperty on proxy property with value descriptor' , ( ) => {
320+ // #5417
321+ let instanceProxy : any
322+ const Comp = {
323+ render ( ) { } ,
324+ setup ( ) {
325+ return {
326+ toggle : 'a'
327+ }
328+ } ,
329+ mounted ( ) {
330+ instanceProxy = this
331+ }
332+ }
333+
334+ const app = createApp ( Comp )
335+
336+ app . mount ( nodeOps . createElement ( 'div' ) )
337+
338+ const v1 = instanceProxy . toggle
339+ expect ( v1 ) . toEqual ( 'a' )
340+
341+ Object . defineProperty ( instanceProxy , 'toggle' , {
342+ value : 'b'
343+ } )
344+ const v2 = instanceProxy . toggle
345+ expect ( v2 ) . toEqual ( 'b' )
346+
347+ // expect null to be a settable value
348+ Object . defineProperty ( instanceProxy , 'toggle' , {
349+ value : null
350+ } )
351+ const v3 = instanceProxy . toggle
352+ expect ( v3 ) . toBeNull ( )
353+ } )
354+
355+ test ( 'defineProperty on public instance proxy should work with SETUP,DATA,CONTEXT,PROPS' , ( ) => {
356+ // #5417
357+ let instanceProxy : any
358+ const Comp = {
359+ props : [ 'fromProp' ] ,
360+ data ( ) {
361+ return { name : 'data.name' }
362+ } ,
363+ computed : {
364+ greet ( ) {
365+ return 'Hi ' + ( this as any ) . name
366+ }
367+ } ,
368+ render ( ) { } ,
369+ setup ( ) {
370+ return {
371+ fromSetup : true
372+ }
373+ } ,
374+ mounted ( ) {
375+ instanceProxy = this
376+ }
377+ }
378+
379+ const app = createApp ( Comp , {
380+ fromProp : true
381+ } )
382+
383+ app . mount ( nodeOps . createElement ( 'div' ) )
384+ expect ( instanceProxy . greet ) . toEqual ( 'Hi data.name' )
385+
386+ // define property on data
387+ Object . defineProperty ( instanceProxy , 'name' , {
388+ get ( ) {
389+ return 'getter.name'
390+ }
391+ } )
392+
393+ // computed is same still cached
394+ expect ( instanceProxy . greet ) . toEqual ( 'Hi data.name' )
395+
396+ // trigger computed
397+ instanceProxy . name = ''
398+
399+ // expect "greet" to evaluated and use name from context getter
400+ expect ( instanceProxy . greet ) . toEqual ( 'Hi getter.name' )
401+
402+ // defineProperty on computed ( context )
403+ Object . defineProperty ( instanceProxy , 'greet' , {
404+ get ( ) {
405+ return 'Hi greet.getter.computed'
406+ }
407+ } )
408+ expect ( instanceProxy . greet ) . toEqual ( 'Hi greet.getter.computed' )
409+
410+ // defineProperty on setupState
411+ expect ( instanceProxy . fromSetup ) . toBe ( true )
412+ Object . defineProperty ( instanceProxy , 'fromSetup' , {
413+ get ( ) {
414+ return false
415+ }
416+ } )
417+ expect ( instanceProxy . fromSetup ) . toBe ( false )
418+
419+ // defineProperty on Props
420+ expect ( instanceProxy . fromProp ) . toBe ( true )
421+ Object . defineProperty ( instanceProxy , 'fromProp' , {
422+ get ( ) {
423+ return false
424+ }
425+ } )
426+ expect ( instanceProxy . fromProp ) . toBe ( false )
283427 } )
284428
429+
285430 // #864
286431 test ( 'should not warn declared but absent props' , ( ) => {
287432 const Comp = {
0 commit comments