Skip to content

initial data & setData for composite & class components #1880

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions src/mount.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't this be called for every component? Not only the component which has been passed to mount

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you are right, as i added this as a mixin it will be called for every component in this App.
I will have a look if there is another convenient way to set the data fields.

}

// AppConfig
if (global.config) {
for (const [k, v] of Object.entries(global.config) as [
Expand Down
10 changes: 8 additions & 2 deletions src/vueWrapper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -242,10 +242,16 @@ export class VueWrapper<
}

setData(data: Record<string, unknown>): Promise<void> {
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<string, unknown>
mergeDeep(target, data)
} else {
mergeDeep(this.componentVM.$data, data)
}
return nextTick()
}

setProps(props: Record<string, unknown>): Promise<void> {
// 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) {
Expand Down
13 changes: 13 additions & 0 deletions tests/components/SimpleData.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<template>
<div>sArray:{{dataStringArray}}|s:{{dataString}}</div>
</template>


<script setup lang="ts">

import {ref} from "vue";

const dataStringArray = ref<string[]>(['initialA','initialB'])
const dataString = ref<string>('initialC')

</script>
16 changes: 16 additions & 0 deletions tests/components/SimpleDataClassComponent.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template>
<div>sArray:{{dataStringArray}}|s:{{dataString}}</div>
</template>

<script lang="ts">
import { Options, Vue } from 'vue-class-component'

@Options({})
export default class SimpleDataClassComponent extends Vue {
dataText: string = ''
msg!: string // declared for type-checking

dataStringArray : string[]= ['initialA','initialB']
dataString: string = 'initialC'
}
</script>
53 changes: 53 additions & 0 deletions tests/mount.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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', () => {
Expand Down Expand Up @@ -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(
'<div>sArray:[\n "setA",\n "setB"\n ]|s:setC</div>'
)
})

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(
'<div>sArray:[\n "setA",\n "setB"\n ]|s:setC</div>'
)
})

it('should respect a data callback when using mount/shallowmount on inline component', async () => {
const Component = {
template: `<div>sArray:{{dataStringArray}}|s:{{dataString}}</div>`,
data: () => ({
dataStringArray: ['initialA', 'initialB'],
dataString: 'initialC'
})
}

const wrapper = mount(Component, {
data: () => {
return {
dataStringArray: ['setA', 'setB'],
dataString: 'setC'
}
}
})

expect(wrapper.html()).toEqual(
'<div>sArray:[\n "setA",\n "setB"\n ]|s:setC</div>'
)
})
})
73 changes: 57 additions & 16 deletions tests/setData.spec.ts
Original file line number Diff line number Diff line change
@@ -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 () => {
Expand Down Expand Up @@ -111,20 +112,6 @@ describe('setData', () => {
)
})

it('does not modify composition API setup data', async () => {
const Component = defineComponent({
template: `<div>Count is: {{ count }}</div>`,
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<
Expand Down Expand Up @@ -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(
'<div>sArray:[\n "initialA",\n "initialB"\n ]|s:initialC</div>'
)

await wrapper.setData({
dataStringArray: ['setA', 'setB'],
dataString: 'setC'
})

expect(wrapper.html()).toEqual(
'<div>sArray:[\n "setA",\n "setB"\n ]|s:setC</div>'
)
})

it('should handle setData on composition api component', async () => {
const wrapper = mount(SimpleData)
expect(wrapper.html()).toEqual(
'<div>sArray:[\n "initialA",\n "initialB"\n ]|s:initialC</div>'
)

await wrapper.setData({
dataStringArray: ['setA', 'setB'],
dataString: 'setC'
})

expect(wrapper.html()).toEqual(
'<div>sArray:[\n "setA",\n "setB"\n ]|s:setC</div>'
)
})

it('should handle setData on inline component component', async () => {
const Component = {
template: `<div>sArray:{{dataStringArray}}|s:{{dataString}}</div>`,
data: () => ({
dataStringArray: ['initialA', 'initialB'],
dataString: 'initialC'
})
}
const wrapper = mount(Component, {
data: () => {
return {
dataStringArray: ['setA', 'setB'],
dataString: 'setC'
}
}
})

expect(wrapper.html()).toEqual(
'<div>sArray:[\n "setA",\n "setB"\n ]|s:setC</div>'
)
})
})