Skip to content

Commit 8457d8b

Browse files
authoredFeb 12, 2022
fix(runtime-core): allow spying on proxy methods (#4216)
Since Jest v26.6.1, the mock method changed (see this commit jestjs/jest@30e8020) to rely on `Object.defineProperty` in some cases. This breaks spying on proxy's methods, because even if Jest is properly calling `Object.defineProperty`, the cached value in the `get` section of the proxy is never updated, and the spy is in fact never used. This is easily reproducible as vue-next already uses a version of jest with these changes. This is blocking projects (like vue-test-utils-next and vue-cli) to update to recent Jest versions. This commit adds a `defineProperty` method to the proxy handler, that properly updates the defined value in the cache.
1 parent 436c500 commit 8457d8b

File tree

2 files changed

+83
-0
lines changed

2 files changed

+83
-0
lines changed
 

‎packages/runtime-core/__tests__/componentPublicInstance.spec.ts

+68
Original file line numberDiff line numberDiff line change
@@ -214,6 +214,74 @@ describe('component: proxy', () => {
214214
])
215215
})
216216

217+
test('allow updating proxy with Object.defineProperty', () => {
218+
let instanceProxy: any
219+
const Comp = {
220+
render() {},
221+
setup() {
222+
return {
223+
isDisplayed: true
224+
}
225+
},
226+
mounted() {
227+
instanceProxy = this
228+
}
229+
}
230+
231+
const app = createApp(Comp)
232+
233+
app.mount(nodeOps.createElement('div'))
234+
235+
Object.defineProperty(instanceProxy, 'isDisplayed', { value: false })
236+
237+
expect(instanceProxy.isDisplayed).toBe(false)
238+
239+
Object.defineProperty(instanceProxy, 'isDisplayed', { value: true })
240+
241+
expect(instanceProxy.isDisplayed).toBe(true)
242+
243+
Object.defineProperty(instanceProxy, 'isDisplayed', {
244+
get() {
245+
return false
246+
}
247+
})
248+
249+
expect(instanceProxy.isDisplayed).toBe(false)
250+
251+
Object.defineProperty(instanceProxy, 'isDisplayed', {
252+
get() {
253+
return true
254+
}
255+
})
256+
257+
expect(instanceProxy.isDisplayed).toBe(true)
258+
})
259+
260+
test('allow spying on proxy methods', () => {
261+
let instanceProxy: any
262+
const Comp = {
263+
render() {},
264+
setup() {
265+
return {
266+
toggle() {}
267+
}
268+
},
269+
mounted() {
270+
instanceProxy = this
271+
}
272+
}
273+
274+
const app = createApp(Comp)
275+
276+
app.mount(nodeOps.createElement('div'))
277+
278+
const spy = jest.spyOn(instanceProxy, 'toggle')
279+
280+
instanceProxy.toggle()
281+
282+
expect(spy).toHaveBeenCalled()
283+
})
284+
217285
// #864
218286
test('should not warn declared but absent props', () => {
219287
const Comp = {

‎packages/runtime-core/src/componentPublicInstance.ts

+15
Original file line numberDiff line numberDiff line change
@@ -397,8 +397,10 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
397397
const { data, setupState, ctx } = instance
398398
if (setupState !== EMPTY_OBJ && hasOwn(setupState, key)) {
399399
setupState[key] = value
400+
return true
400401
} else if (data !== EMPTY_OBJ && hasOwn(data, key)) {
401402
data[key] = value
403+
return true
402404
} else if (hasOwn(instance.props, key)) {
403405
__DEV__ &&
404406
warn(
@@ -445,6 +447,19 @@ export const PublicInstanceProxyHandlers: ProxyHandler<any> = {
445447
hasOwn(publicPropertiesMap, key) ||
446448
hasOwn(appContext.config.globalProperties, key)
447449
)
450+
},
451+
452+
defineProperty(
453+
target: ComponentRenderContext,
454+
key: string,
455+
descriptor: PropertyDescriptor
456+
) {
457+
if (descriptor.get != null) {
458+
this.set!(target, key, descriptor.get(), null)
459+
} else if (descriptor.value != null) {
460+
this.set!(target, key, descriptor.value, null)
461+
}
462+
return Reflect.defineProperty(target, key, descriptor)
448463
}
449464
}
450465

1 commit comments

Comments
 (1)

juddy-star commented on Feb 16, 2022

@juddy-star

Will lines 400 and 403 affect the logic between lines 421 and 429 @cexbrayat

Please sign in to comment.