diff --git a/docs/guides/common-tips.md b/docs/guides/common-tips.md index 5ac100e84..7f4766707 100644 --- a/docs/guides/common-tips.md +++ b/docs/guides/common-tips.md @@ -27,11 +27,11 @@ const wrapper = shallowMount(Component) wrapper.vm // the mounted Vue instance ``` -### Using `nextTick` +### Writing asynchronous tests using `nextTick` (new) -Vue batches watcher updates and runs them asynchronously on the "next tick". +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). -In practice, this means you must wait for updates to run after you set a reactive property. You can wait for updates with `Vue.nextTick()`: +This means you **must** wait for updates to run after you set a reactive property. You can wait for updates with `Vue.nextTick()`: ```js it('updates text', async () => { @@ -40,6 +40,20 @@ it('updates text', async () => { await Vue.nextTick() expect(wrapper.text()).toContain('updated') }) + +// Or if you're without async/await +it('render text', done => { + const wrapper = mount(TestComponent) + wrapper.trigger('click') + Vue.nextTick(() => { + wrapper.text().toContain('some text') + wrapper.trigger('click') + Vue.nextTick(() => { + wrapper.text().toContain('some different text') + done() + }) + }) +}) ``` The following methods often cause watcher updates that require you to wait for the next tick: @@ -160,6 +174,106 @@ You can also update the props of an already-mounted component with the `wrapper. _For a full list of options, please see the [mount options section](../api/options.md) of the docs._ +### Mocking Transitions + +Although calling `await Vue.nextTick()` works well for most use cases, there are some situations where additional workarounds are required. These issues will be solved before the `vue-test-utils` library moves out of beta. One such example is unit testing components with the `` wrapper provided by Vue. + +```vue + + + +``` + +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: + +```js +test('should render Foo, then hide it', async () => { + const wrapper = mount(Foo) + expect(wrapper.text()).toMatch(/Foo/) + + wrapper.setData({ + show: false + }) + await wrapper.vm.$nextTick() + + expect(wrapper.text()).not.toMatch(/Foo/) +}) +``` + +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 related to show Vue implements the `` component, that we would like to solve before version 1.0. For now, there are some workarounds: + +#### Using a `transitionStub` helper + +```js +const transitionStub = () => ({ + render: function(h) { + return this.$options._renderChildren + } +}) + +test('should render Foo, then hide it', async () => { + const wrapper = mount(Foo, { + stubs: { + transition: transitionStub() + } + }) + expect(wrapper.text()).toMatch(/Foo/) + + wrapper.setData({ + show: false + }) + await wrapper.vm.$nextTick() + + expect(wrapper.text()).not.toMatch(/Foo/) +}) +``` + +This overrides the default behavior of the `` component and renders the children as soon as the relevant boolean condition changes, as opposed to applying CSS classes, which is how Vue's `` component works. + +#### Avoid `setData` + +Another alternative is to simply avoid using `setData` by writing two tests, with the required setup performed using `mount` or `shallowMount` options: + +```js +test('should render Foo', async () => { + const wrapper = mount(Foo, { + data() { + return { + show: true + } + } + }) + + expect(wrapper.text()).toMatch(/Foo/) +}) + +test('should not render Foo', async () => { + const wrapper = mount(Foo, { + data() { + return { + show: false + } + } + }) + + expect(wrapper.text()).not.toMatch(/Foo/) +}) +``` + ### Applying Global Plugins and Mixins Some of the components may rely on features injected by a global plugin or mixin, for example `vuex` and `vue-router`.