Events on Vue components call associated methods, but don't update DOM unless awaited
See original GitHub issueHello! I’m a big fan of user-event in my React projects, and I know that’s the primary framework for this library! I’ve started learning Vue and want to use userEvent in my component tests here too, but weird stuff happens. I’ll do my best to describe what I think is going on; please be patient with me!
@testing-library/user-eventversion: 13.1.5- Testing Framework and version: Jest 26.6.3 (with jest-dom matchers 5.11.10)
- DOM Environment: jsdom 16.5.2
- Vue version: 2.6.12
Relevant code or config
Component
// Header.vue
<template>
<div>
<h1 v-if="checked">
Checked
</h1>
<h1 v-else>
Unchecked
</h1>
<fieldset>
<legend class="question">Check this box to make the header change!</legend>
<input
id="box"
type="checkbox"
v-on:change="toggleBox()"
>
<label for="box">Click me!</label>
</fieldset>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
data() {
return {
checked: false
}
},
methods: {
toggleBox() {
this.checked = !this.checked
}
}
})
</script>
Failing test
// Header.test.ts
import { render, screen } from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import Header from './Header.vue'
test('changes the header when checkbox checked', () => {
render(Header)
userEvent.click(screen.getByLabelText('Click me!'))
// passes
expect(screen.getByLabelText('Click me!')).toBeChecked()
// fails
expect(screen.getByText('Checked')).toBeInTheDocument()
})
// screen.debug() output
<body>
<div>
<div>
<h1>
Unchecked!
</h1>
<fieldset>
<legend
class="question"
>
Check this box to make the header change!
</legend>
<input
id="box"
type="checkbox"
/>
<label
for="box"
>
Click me!
</label>
</fieldset>
</div>
</div>
</body>
Working test without user-event
// Header.test.ts
import { fireEvent, render, screen } from '@testing-library/vue'
import Header from './Header.vue'
test('changes the header when checkbox checked', async () => {
render(Header)
await fireEvent.click(screen.getByLabelText('Click me!'))
// passes
expect(screen.getByLabelText('Click me!')).toBeChecked()
expect(screen.getByText('Checked')).toBeInTheDocument()
})
// screen.debug
<body>
<div>
<div>
<h1>
Checked!
</h1>
<fieldset>
<legend
class="question"
>
Check this box to make the header change!
</legend>
<input
id="box"
type="checkbox"
/>
<label
for="box"
>
Click me!
</label>
</fieldset>
</div>
</div>
</body>
Working test, unexpectedly, with user-event
// Header.test.ts
import { render, screen } from '@testing-library/vue'
import userEvent from '@testing-library/user-event'
import Header from './Header.vue'
test('changes the header when checkbox checked', async () => {
render(Header)
// user-event should be synchronous, so why does this work?
await userEvent.click(screen.getByLabelText('Click me!'))
// passes
expect(screen.getByLabelText('Click me!')).toBeChecked()
expect(screen.getByText('Checked')).toBeInTheDocument()
})
// screen.debug output same as previous test
Reproduction repository: I can try to get one together if the above code is not enough! It’ll take me some time, though.
Problem description:
user-event properly interacts with DOM elements and triggers a Vue component’s methods, but testing-library’s representation of the DOM after that method is called doesn’t change unless userEvent.[method] is awaited, despite the click method here being synchronous.
I’m not sure how to keep exploring this, and fireEvent is a fine substitute for now, but I figured you’d like to know this is happening with Vue!
Issue Analytics
- State:
- Created 2 years ago
- Reactions:3
- Comments:7 (1 by maintainers)

Top Related StackOverflow Question
@rydash You may be able to get a clearer idea of how the problem arises from here.
For your immediate purposes, I’d recommend using
waitForinstead. There are peculiar circumstances whereVue.nextTickdoesn’t do what one would expect or hope for. By contrast,waitForis guaranteed to work in all circumstances (because you’re waiting for a condition to be met). And the last assertion in your test that appears in awaitFordoesn’t need to beawaited – if that makes sense.For anyone reading this issue later, here’s
waitForused in the test in the initial example. Both expectations pass!