Skip to content

Commit ad9d21a

Browse files
committed
Fix setData() not detecting object-api components correctly in dependent projects
We were checking reference equality with the default vue EMPTY_OBJ placeholder, however in the production bundle of test-utils, this was inlined, so in effect there were suddenly two EMPTY_OBJ definitions, and the references would no longer match. Instead, detect whether the object is frozen, which should always hold for the EMPTY_OBJ, no matter if it's inlined or not
1 parent e2f4360 commit ad9d21a

File tree

2 files changed

+14
-17
lines changed

2 files changed

+14
-17
lines changed

src/vueWrapper.ts

+13-16
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { nextTick, App, ComponentPublicInstance, VNode } from 'vue'
2-
import { EMPTY_OBJ as VUE_UNWRITABLE_PLACEHOLDER_OBJECT } from '@vue/shared'
32
import * as reactivity from '@vue/reactivity'
43

54
import { config } from './config'
@@ -249,29 +248,27 @@ export class VueWrapper<
249248
}
250249

251250
setData(data: Record<string, unknown>): Promise<void> {
252-
// setupState is always defined,
253-
// Even when using the object api without defining a setup() function
254-
// Check if the setupState actually originates from <script setup>
251+
/*
252+
Depending on how the component was defined, data can live in different places
253+
Vue sets some default placeholder in all the locations however, so we cannot just check
254+
if the data object exists or not.
255+
When using <script setup>, data lives in the setupState object, which is then marked with __isScriptSetup
256+
When using the setup() function, data lives in the setupState object, but is not marked with __isScriptSetup
257+
When using the object api, data lives in the data object, proxied through $data, HOWEVER
258+
the setupState object will also exist, and be frozen.
259+
*/
255260
// @ts-ignore
256261
if (this.componentVM.$.setupState.__isScriptSetup) {
257-
// result from <script setup>
262+
// data from <script setup>
258263
// @ts-ignore
259264
mergeDeep(this.componentVM.$.setupState, data)
260-
} else if (
261265
// @ts-ignore
262-
this.componentVM.$.setupState != null &&
263-
// Vue always defined the setupState property
264-
// However, it uses a shared readonly singleton if the component doesn't define a setup()
265-
// Check this and don't use it.
266-
// @ts-ignore
267-
this.componentVM.$.setupState !== VUE_UNWRITABLE_PLACEHOLDER_OBJECT
268-
) {
269-
// is the return value of the setup() function when using the object api,
270-
// i.e. defineComponent({ setup() {} })
266+
} else if (!Object.isFrozen(this.componentVM.$.setupState)) {
267+
// data from setup() function when using the object api
271268
// @ts-ignore
272269
mergeDeep(reactivity.proxyRefs(this.componentVM.$.setupState), data)
273270
} else {
274-
// is the data object when using the object api
271+
// data when using data: {...} in the object api
275272
mergeDeep(this.componentVM.$data, data)
276273
}
277274
return nextTick()

tests/setData.spec.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -284,7 +284,7 @@ describe('setData', () => {
284284
expect(wrapper.html()).toContain('updated value')
285285
})
286286

287-
it('updates data on an scf using <script setup>', async () => {
287+
it('updates data on a component using <script setup>', async () => {
288288
const wrapper = shallowMount(ScriptSetup)
289289
await wrapper.setData({ count: 20 })
290290
expect(wrapper.html()).toContain('20')

0 commit comments

Comments
 (0)