Change: Unify `nextTick` in our Jest Vue tests

Change pattern proposal: wrapper.vm.$nextTick() / Vue.nextTick() to one of the options

I would like to say thank you to @dmishunov for raising this concern in gitlab-org/gitlab-ui#560 (closed)

This is a broader version of this discussion

Old Pattern

In our codebase there are multiple approaches to managing nextTick:

New Pattern

Unify this one across our codebase

Proposal 1: Use wrapper.vm.$nextTick() everywhere

Advantages:

  1. No extra import of Vue

Disadvantages:

  1. Sometimes usage is not obvious

Special thanks for @pslaughter for asking the right questions in the right time.

Sometimes usage of wrapper.vm.$nextTick() is not obvious. For example (from real codebase):

Is it safe to use wrapper.vm.$nextTick() after calling wrapper.destroy()?

If I have two components (either created by mount / shallowMount of found via .find - which one should I nextTick()?

  1. Longer to type :)
  2. Accessing anything inside wrapper.vm feels like a code smell for me. Let me explain:
  • wrapper.vm.someState.foo - we should use wrapper.setData() instead
  • wrapper.vm.someMethod() - triggering vm methods does not align with our guidelines on testing Vue components and @vue/test-utils guidelines
  • jest.spyOn(wrapper.vm, 'something') - we should treat component as black box, and spy on external activities. For example for vuex-enabled components such activity is calling dispatch`
  • Testing and accessing computeds is wrong way of testing Vue components - they are implementation details

The only inevitable use-case for wrapper.vm is wrapper.vm.$emit which will not be on the table with Vue3 release, since there will be no difference between trigger & emit. I would prefer (in the future) to get rid of as many wrapper.vm.something calls as possible :)

Proposal 2: Use import { nextTick } from 'vue'; nextTick() everywhere

First of all, you should know one important thing:

wrapper.vm.$nextTick === Vue.nextTick // true
createLocalVue().nextTick === Vue.nextTick // true

So we're actually always calling the same function :) Also additional import does not add anything (tested) to tests execution time, since (obviously) @vue/test-utils also import Vue under the hood.

Advantages:

  1. No-brainer! No time to think, just use nextTick() everywhere
  2. Shorter :)

Disadvantages:

  1. Extra import in each file.

Summary

Why no "extra utility function" approach on the table? From my point of view, I see 0 benefits compared to nextTick() - still one import, and I really don't want to introduce new things unless really needed.

All the tests I've written were actually wrapper.vm.$nextTick(). But having a large codebase and a big FE department, I really think that we already have too many things to understand & remember - so no-brainer solution like nextTick() really shines for me

Migration effort

  • We can easily migrate our entire codebase with codemod (I have them ready)
  • ESLint rule for linting yet to be developed, but is pretty trivial to implement

Risks

None

Voting

  • use 🚲 emoji for keeping wrapper.vm.nextTick() thing :)
  • use 🌐 emoji for global solution of nextTick()
  • raise your voice in comments if you have other proposals
Edited by Denys Mishunov