Skip to content

Commit 05f7720

Browse files
committed
(docs): add notes on mocking transitions and run linter
1 parent 9025610 commit 05f7720

File tree

1 file changed

+104
-10
lines changed

1 file changed

+104
-10
lines changed

docs/guides/common-tips.md

Lines changed: 104 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ wrapper.vm // the mounted Vue instance
2929

3030
### Writing asynchronous tests using `nextTick` (new)
3131

32-
By default, Vue batches updates to run asynchronously (on the next "tick"). This is to prevent unnecessary DOM re-renders, and watcher computations ([see the docs](https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue) for more details).
32+
By default, Vue batches updates to run asynchronously (on the next "tick"). This is to prevent unnecessary DOM re-renders, and watcher computations ([see the docs](https://vuejs.org/v2/guide/reactivity.html#Async-Update-Queue) for more details).
3333

3434
This means you **must** wait for updates to run after you set a reactive property. You can wait for updates with `Vue.nextTick()`:
3535

@@ -42,21 +42,20 @@ it('updates text', async () => {
4242
})
4343

4444
// Or if you're without async/await
45-
it('render text', (done) => {
46-
const wrapper = mount(TestComponent)
45+
it('render text', done => {
46+
const wrapper = mount(TestComponent)
47+
wrapper.trigger('click')
48+
Vue.nextTick(() => {
49+
wrapper.text().toContain('some text')
4750
wrapper.trigger('click')
4851
Vue.nextTick(() => {
49-
wrapper.text().toContain('some text')
50-
wrapper.trigger('click')
51-
Vue.nextTick(() => {
52-
wrapper.text().toContain('some different text')
53-
done()
54-
})
52+
wrapper.text().toContain('some different text')
53+
done()
5554
})
55+
})
5656
})
5757
```
5858

59-
6059
The following methods often cause watcher updates that require you to wait for the next tick:
6160

6261
- `setChecked`
@@ -177,6 +176,101 @@ _For a full list of options, please see the [mount options section](../api/optio
177176

178177
### Mocking Transitions
179178

179+
Although calling `await Vue.nexTick()` works well for most use cases, there are some situations where additional work arounds are required. These issues will be solved before the library transitions out of beta. One such example is unit testing components with the `<transition>` wrapper provided by Vue.
180+
181+
```vue
182+
<template>
183+
<div>
184+
<transition>
185+
<p v-if="show">Foo</p>
186+
</transition>
187+
</div>
188+
</template>
189+
190+
<script>
191+
export default {
192+
data() {
193+
return {
194+
show: true
195+
}
196+
}
197+
}
198+
</script>
199+
```
200+
201+
You might want to write a test that verifies that Foo is shown, then when `show` is set to `false`, Foo is no longer rendered. Such a test could be written as follows:
202+
203+
```js
204+
test('should render Foo, then hide it', async () => {
205+
const wrapper = mount(Foo)
206+
expect(wrapper.text()).toMatch(/Foo/)
207+
208+
wrapper.setData({
209+
show: false
210+
})
211+
await wrapper.vm.$nextTick()
212+
213+
expect(wrapper.text()).not.toMatch(/Foo/)
214+
})
215+
```
216+
217+
In practice, although we are calling `setData` then waiting for the `nextTick` to ensure the DOM is updated, this test fails. This is an ongoing issue that we would like to solve before version 1.0. For now, there are some work arounds:
218+
219+
1. Using a `transitionStub` helper
220+
221+
```js
222+
const transitionStub = () => ({
223+
render: function(h) {
224+
return this.$options._renderChildren
225+
}
226+
})
227+
228+
test('should render Foo, then hide it', async () => {
229+
const wrapper = mount(Foo, {
230+
stubs: {
231+
transition: transitionStub()
232+
}
233+
})
234+
expect(wrapper.text()).toMatch(/Foo/)
235+
236+
wrapper.setData({
237+
show: false
238+
})
239+
await wrapper.vm.$nextTick()
240+
241+
expect(wrapper.text()).not.toMatch(/Foo/)
242+
})
243+
```
244+
245+
This overrides the default behavior of the `<transition>` component and renders the children as soon as the relevant boolean condition changes, as opposed to applying CSS classes, which is how Vue's `<transition>` component works.
246+
247+
Another alternative is to simply avoid using `setData` by writing two tests, with the required setup performed using `mount` or `shallowMount` options:
248+
249+
```js
250+
test('should render Foo', async () => {
251+
const wrapper = mount(Foo, {
252+
data() {
253+
return {
254+
show: true
255+
}
256+
}
257+
})
258+
259+
expect(wrapper.text()).toMatch(/Foo/)
260+
})
261+
262+
test('should not render Foo', async () => {
263+
const wrapper = mount(Foo, {
264+
data() {
265+
return {
266+
show: false
267+
}
268+
}
269+
})
270+
271+
expect(wrapper.text()).not.toMatch(/Foo/)
272+
})
273+
```
180274

181275
### Applying Global Plugins and Mixins
182276

0 commit comments

Comments
 (0)