无法通过Jest中的“提交"按钮触发Vuetify表单提交
我正在尝试使用Jest和Avoriaz验证基于Vuetify组件的Vue表单的行为.
我可以在表单上触发 submit.prevent
,从而导致预期的行为.
但是触发提交按钮上的 click
无效.
I'm trying to verify the behavior of a Vue form, based on Vuetify components, using Jest and Avoriaz.
I can trigger submit.prevent
on the form, resulting in expected behavior.
But triggering click
on the submit button does not work.
组件:
<template>
<v-form
ref="form"
data-cy="form"
@submit.prevent="login"
>
<v-text-field
id="email"
v-model="email"
label="Email"
name="email"
prepend-icon="mdi-at"
type="text"
required
autofocus
data-cy="email-text"
/>
<v-btn
color="primary"
type="submit"
data-cy="login-btn"
>
Login
</v-btn>
</v-form>
</template>
<script>
export default {
data () {
return {
email: 'test@test.com',
}
},
computed: {},
methods: {
login: function () {
console.log('Logging in')
}
}
}
</script>
测试设置:
import vuetify from '@/plugins/vuetify'
import { mount } from 'avoriaz'
import Form from '@/views/Form'
describe('Form', () => {
const mountFunction = options => {
return mount(Form, {
vuetify,
...options
})
}
Vue&Vuetify设置在 @/plugins/vuetify
中完成:
Where the Vue & Vuetify setup is done in @/plugins/vuetify
:
import Vue from 'vue'
import Vuetify from 'vuetify/lib'
Vue.use(Vuetify)
export default new Vuetify({
})
以下测试成功(因此该模拟有效):
The following test succeeds (thus the mock works):
it('can trigger form directly', () => {
const login = jest.fn()
const wrapper = mountFunction()
wrapper.setData({ 'email': 'test@com' })
wrapper.setMethods({ login })
let element = wrapper.first('[data-cy=form]')
element.trigger('submit.prevent')
expect(login).toHaveBeenCalledTimes(1)
})
但是实际上测试提交按钮失败:
But actually testing the submit button, fails:
it('can trigger form through button', () => {
const login = jest.fn()
const wrapper = mountFunction()
wrapper.setData({ 'email': 'test@test.com' })
wrapper.setMethods({ login })
const button = wrapper.first('[type=submit]')
button.trigger('click')
expect(login).toHaveBeenCalledTimes(1)
})
更新:也许 package.json
中的一些相关依赖项:
Update:
Perhaps some relevant dependencies in package.json
:
{
..
"dependencies": {
"axios": "^0.19.1",
"core-js": "^3.4.4",
"vue": "^2.6.11",
"vue-router": "^3.1.3",
"vuetify": "^2.1.0",
"vuex": "^3.1.2"
},
"devDependencies": {
..
"avoriaz": "^6.3.0",
"vue-jest": "^3.0.5",
"vuetify-loader": "^1.3.0"
}
}
更新:使用test-utils,非Vuetify组件(< form>
和< btn
)时,以下测试成功:
Update:
When using test-utils, non Vuetify components (<form>
and <btn
), the following test succeeds:
const localVue = createLocalVue()
localVue.use(Vuetify)
describe('Form', () => {
const mountFunction = options => {
return shallowMount(Form, {
localVue,
vuetify,
...options
})
}
it('can trigger form through button alternative', async () => {
const login = jest.fn()
const wrapper = mountFunction({ attachToDocument: true })
try {
wrapper.setData({ 'email': 'test@test.com' })
wrapper.setMethods({ login })
const button = wrapper.find('[type=submit]')
expect(button).toBeDefined()
button.trigger('click')
await Vue.nextTick()
expect(login).toHaveBeenCalledTimes(1)
} finally {
wrapper.destroy()
}
})
})
然后切换到Vuetify组件会导致测试失败.
Then switching to Vuetify components cause the test to fail.
显然,在使用Vuetify组件(< v-form>
和< v-btn&
>),要使其正常工作,需要一些关键要素:
Apparently, when using Vuetify components (<v-form>
and <v-btn>
), there are some key ingredients necessary to make it work:
- Vue test-utils 代替avoriaz
-
mount
而不是shallowMount
- 包括
attachToDocument
,这也需要随后进行清理(wrapper.destroy()
) 正如@Husam Ibrahim也提到的, - 需要
异步
严格地等待Vue.nextTick()
- 要使setData完全生效,您可能需要额外地
等待Vue.nextTick()
.在我的完整代码中,我进行了表单验证(在输入上使用:rules
,在表单上使用v-model
,以及按钮的:disabled
绑定到v-model
数据元素,这需要两个nextTick()
s
- Vue test-utils instead of avoriaz
-
mount
instead ofshallowMount
- include
attachToDocument
, which also requires cleaning up afterwards (wrapper.destroy()
) - as @Husam Ibrahim also mentioned, need to
async
hronouslyawait Vue.nextTick()
- To let the setData have full effect, you might have to extra
await Vue.nextTick()
. In my full code I have form validation (with:rules
on the input, andv-model
on the form, and the button's:disabled
bound to thev-model
data element. This required twonextTick()
s
以下工作方式(与问题中的'@/plugins/vuetify'
相同:
The following works (with '@/plugins/vuetify'
the same as in the question:
import vuetify from '@/plugins/vuetify'
import Vue from 'vue'
import { createLocalVue, mount } from '@vue/test-utils'
import Form from '@/views/Form'
import Vuetify from 'vuetify/lib'
const localVue = createLocalVue()
localVue.use(Vuetify)
describe('Form', () => {
const mountFunction = options => {
return mount(Form, {
localVue,
vuetify,
...options
})
}
it('can trigger form through button alternative', async () => {
const login = jest.fn()
const wrapper = mountFunction({ attachToDocument: true })
try {
wrapper.setData({ 'email': 'test@test.com' })
await Vue.nextTick() # might require multiple
wrapper.setMethods({ login })
const button = wrapper.find('[type=submit]')
button.trigger('click')
await Vue.nextTick()
expect(login).toHaveBeenCalledTimes(1)
} finally {
wrapper.destroy()
}
})
})