diff --git a/src/mount.ts b/src/mount.ts index 27735767c..4d765ef42 100644 --- a/src/mount.ts +++ b/src/mount.ts @@ -516,6 +516,24 @@ export function mount( app.mixin(mixin) } + // mixin for initial data of script setup composition components + if (options?.data) { + const providedData = options.data() + const mixin = defineComponent({ + beforeCreate() { + if (hasSetupState(this)) { + for (const [k, v] of Object.entries( + providedData as { [key: string]: any } + )) { + this.$.setupState[k] = v + } + } + } + }) + + app.mixin(mixin) + } + // AppConfig if (global.config) { for (const [k, v] of Object.entries(global.config) as [ diff --git a/src/vueWrapper.ts b/src/vueWrapper.ts index 1d5fb40a0..8062d5d72 100644 --- a/src/vueWrapper.ts +++ b/src/vueWrapper.ts @@ -242,10 +242,16 @@ export class VueWrapper< } setData(data: Record): Promise { - mergeDeep(this.componentVM.$data, data) + //this.componentVM.$data is not defined on all composition api components + //script setup components store data in root node instead of $data + if (hasSetupState(this.vm)) { + const target = this.componentVM as Record + mergeDeep(target, data) + } else { + mergeDeep(this.componentVM.$data, data) + } return nextTick() } - setProps(props: Record): Promise { // if this VM's parent is not the root or if setProps does not exist, error out if (this.vm.$parent !== this.rootVM || !this.__setProps) { diff --git a/tests/components/SimpleData.vue b/tests/components/SimpleData.vue new file mode 100644 index 000000000..be5d801fd --- /dev/null +++ b/tests/components/SimpleData.vue @@ -0,0 +1,13 @@ + + + + diff --git a/tests/components/SimpleDataClassComponent.vue b/tests/components/SimpleDataClassComponent.vue new file mode 100644 index 000000000..71c8d3ec6 --- /dev/null +++ b/tests/components/SimpleDataClassComponent.vue @@ -0,0 +1,16 @@ + + + diff --git a/tests/mount.spec.ts b/tests/mount.spec.ts index 7c77d66c4..77037f55d 100644 --- a/tests/mount.spec.ts +++ b/tests/mount.spec.ts @@ -2,6 +2,8 @@ import { describe, expect, it, vi } from 'vitest' import { defineComponent } from 'vue' import { mount } from '../src' import HelloFromVitestPlayground from './components/HelloFromVitestPlayground.vue' +import SimpleData from './components/SimpleData.vue' +import SimpleDataClassComponent from './components/SimpleDataClassComponent.vue' describe('mount: general tests', () => { it('correctly handles component, throwing on mount', () => { @@ -32,4 +34,55 @@ describe('mount: general tests', () => { expect(spy).not.toHaveBeenCalled() }) + + it('should respect a data callback when using mount/shallowmount on ClassComponent api component', async () => { + const wrapper = mount(SimpleDataClassComponent, { + data: () => { + return { + dataStringArray: ['setA', 'setB'], + dataString: 'setC' + } + } + }) + expect(wrapper.html()).toEqual( + '
sArray:[\n "setA",\n "setB"\n ]|s:setC
' + ) + }) + + it('should respect a data callback when using mount/shallowmount on composition api component', async () => { + const wrapper = mount(SimpleData, { + data: () => { + return { + dataStringArray: ['setA', 'setB'], + dataString: 'setC' + } + } + }) + expect(wrapper.html()).toEqual( + '
sArray:[\n "setA",\n "setB"\n ]|s:setC
' + ) + }) + + it('should respect a data callback when using mount/shallowmount on inline component', async () => { + const Component = { + template: `
sArray:{{dataStringArray}}|s:{{dataString}}
`, + data: () => ({ + dataStringArray: ['initialA', 'initialB'], + dataString: 'initialC' + }) + } + + const wrapper = mount(Component, { + data: () => { + return { + dataStringArray: ['setA', 'setB'], + dataString: 'setC' + } + } + }) + + expect(wrapper.html()).toEqual( + '
sArray:[\n "setA",\n "setB"\n ]|s:setC
' + ) + }) }) diff --git a/tests/setData.spec.ts b/tests/setData.spec.ts index 30cfabd55..e00106bb5 100644 --- a/tests/setData.spec.ts +++ b/tests/setData.spec.ts @@ -1,7 +1,8 @@ import { describe, expect, it, vi } from 'vitest' -import { defineComponent, ref } from 'vue' - +import { defineComponent } from 'vue' import { mount } from '../src' +import SimpleData from './components/SimpleData.vue' +import SimpleDataClassComponent from './components/SimpleDataClassComponent.vue' describe('setData', () => { it('sets component data', async () => { @@ -111,20 +112,6 @@ describe('setData', () => { ) }) - it('does not modify composition API setup data', async () => { - const Component = defineComponent({ - template: `
Count is: {{ count }}
`, - setup: () => ({ count: ref(1) }) - }) - const wrapper = mount(Component) - - expect(wrapper.html()).toContain('Count is: 1') - - expect(() => wrapper.setData({ count: 2 })).toThrowError( - 'Cannot add property count' - ) - }) - // https://github.com/vuejs/test-utils/issues/538 it('updates data set via data mounting option using setData', async () => { const Comp = defineComponent< @@ -214,4 +201,58 @@ describe('setData', () => { expect(wrapper.vm.value).toBeInstanceOf(Date) expect(wrapper.vm.value!.toISOString()).toBe('2022-08-11T12:15:54.000Z') }) + + it('should handle setData on ClassComponent api component', async () => { + const wrapper = mount(SimpleDataClassComponent) + expect(wrapper.html()).toEqual( + '
sArray:[\n "initialA",\n "initialB"\n ]|s:initialC
' + ) + + await wrapper.setData({ + dataStringArray: ['setA', 'setB'], + dataString: 'setC' + }) + + expect(wrapper.html()).toEqual( + '
sArray:[\n "setA",\n "setB"\n ]|s:setC
' + ) + }) + + it('should handle setData on composition api component', async () => { + const wrapper = mount(SimpleData) + expect(wrapper.html()).toEqual( + '
sArray:[\n "initialA",\n "initialB"\n ]|s:initialC
' + ) + + await wrapper.setData({ + dataStringArray: ['setA', 'setB'], + dataString: 'setC' + }) + + expect(wrapper.html()).toEqual( + '
sArray:[\n "setA",\n "setB"\n ]|s:setC
' + ) + }) + + it('should handle setData on inline component component', async () => { + const Component = { + template: `
sArray:{{dataStringArray}}|s:{{dataString}}
`, + data: () => ({ + dataStringArray: ['initialA', 'initialB'], + dataString: 'initialC' + }) + } + const wrapper = mount(Component, { + data: () => { + return { + dataStringArray: ['setA', 'setB'], + dataString: 'setC' + } + } + }) + + expect(wrapper.html()).toEqual( + '
sArray:[\n "setA",\n "setB"\n ]|s:setC
' + ) + }) })