diff --git a/docs/.vitepress/config.ts b/docs/.vitepress/config.ts index 53f6f5fd7..a40d39c8a 100644 --- a/docs/.vitepress/config.ts +++ b/docs/.vitepress/config.ts @@ -86,6 +86,7 @@ export default defineConfig({ text: 'Reusability and Composition', link: '/guide/advanced/reusability-composition' }, + { text: 'Testing v-model', link: '/guide/advanced/v-model' }, { text: 'Testing Vuex', link: '/guide/advanced/vuex' }, { text: 'Testing Vue Router', link: '/guide/advanced/vue-router' }, { text: 'Testing Teleport', link: '/guide/advanced/teleport' }, diff --git a/docs/guide/advanced/v-model.md b/docs/guide/advanced/v-model.md new file mode 100644 index 000000000..50dfcbc06 --- /dev/null +++ b/docs/guide/advanced/v-model.md @@ -0,0 +1,96 @@ +# Testing `v-model` + +When writing components that rely on `v-model` interaction (`update:modelValue` event), you need to handle the `event` and `props`. + +Check ["vmodel integration" Discussion](https://github.com/vuejs/test-utils/discussions/279) for some community solutions. + +Check [VueJS VModel event documentation](https://vuejs.org/guide/components/events.html#usage-with-v-model). + +## A Simple Example + +Here a simple Editor component: + +```js +const Editor = { + props: { + label: String, + modelValue: String + }, + template: `
+ + +
` +} +``` + +This component will just behave as an input component: + +```js +const App { + components: { + Editor + }, + template: `` + data(){ + return { + text: 'test' + } + } +} +``` + +Now when we type on the input, it will update `text` on our component. + +To test this behavior: + +```js +test('modelValue should be updated', async () => { + const wrapper = mount(Editor, { + props: { + modelValue: 'initialText', + 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }) + } + }) + + await wrapper.find('input').setValue('test') + expect(wrapper.props('modelValue')).toBe('test') +}) +``` + +# Multiple `v-model` + +In some situations we can have multiple `v-model` targeting specific properties. + +Example an Money Editor, we can have `currency` and `modelValue` properties. + +```js +const MoneyEditor = { + template: `
+ + +
`, + props: ['currency', 'modelValue'] +} +``` + +We can test both by: + +```js +test('modelValue and currency should be updated', async () => { + const wrapper = mount(MoneyEditor, { + props: { + modelValue: 'initialText', + 'onUpdate:modelValue': (e) => wrapper.setProps({ modelValue: e }), + currency: '$', + 'onUpdate:currency': (e) => wrapper.setProps({ currency: e }) + } + }) + + const [currencyInput, modelValueInput] = wrapper.findAll('input') + await modelValueInput.setValue('test') + await currencyInput.setValue('£') + + expect(wrapper.props('modelValue')).toBe('test') + expect(wrapper.props('currency')).toBe('£') +}) +```