Upgrade your Vue components first, deal with tests later
A migration tool to help you with migration from Vue 2 to Vue3
Vue 3 introduced migration build to help teams with gradual migration. vue-test-utils-next is playing well with migration build, but there are many differences between v1 vue-test-utils and v2.
This package provides a compatibility layer, which allows you to run old v1 test suites on vue-test-utils v2 and Vue 3
-
🏁 Compatibility flags
- EXPORT_CREATE_LOCAL_VUE
- EXPORT_CREATE_WRAPPER
- MOUNT_ARGS_COMPONENTS
- MOUNT_ARGS_CONTEXT_*
- MOUNT_ARGS_DIRECTIVES
- MOUNT_ARGS_LISTENERS
- MOUNT_ARGS_MOCKS
- MOUNT_ARGS_PROVIDE
- MOUNT_ARGS_SCOPED_SLOTS
- MOUNT_ARGS_SCOPED_SLOTS_THIS
- MOUNT_ARGS_STUBS
- WRAPPER_ATTRIBUTES_DISABLED
- WRAPPER_ATTRIBUTES_VALUE
- WRAPPER_DESTROY
- WRAPPER_DO_NOT_INCLUDE_NATIVE_EVENTS_IN_EMITTED
- WRAPPER_DO_NOT_INCLUDE_HOOK_EVENTS_IN_EMITTED
- WRAPPER_FIND_ALL
- WRAPPER_FIND_BY_CSS_SELECTOR_RETURNS_COMPONENTS
- WRAPPER_FIND_COMPONENT_BY_REF_RETURNS_DOM
- WRAPPER_SET_VALUE_DOES_NOT_TRIGGER_CHANGE
- WRAPPER_VUE_SET_VALUE_USES_DOM
-
⚠️ Known issues
npm install --save-dev vue-test-utils-compatVue 3:
const VueTestUtils = require('vue@/test-utils');
const { h } = require('vue');
const {
installCompat as installVTUCompat,
fullCompatConfig
} = require('vue-test-utils-compat');
installVTUCompat(VueTestUtils, fullCompatConfig, h)Vue 3 migration build (in Vue 2 mode):
const VueTestUtils = require("vue@/test-utils");
const Vue = require("vue");
let compatH;
Vue.createApp({
compatConfig: {
MODE: 3,
RENDER_FUNCTION: "suppress-warning",
},
render(h) {
compatH = h;
},
}).mount(document.createElement("div"));
installVTUCompat(VueTestUtils, fullCompatConfig, compatH);This upgrade workflow is demonstrated in bootstrap-vue migration to Vue 3.
- Before you start make sure you are using latest version of
@vue/test-utilsv1 in your project and fix all deprecations reported - Follow Vue3 migration build upgrade workflow to set up Vue build infrastructure [example commit]
- Make sure your test infrastructure uses
@vue/compatasvuealias. Example (using jest -jest.config.js):
module.exports = {
// ...
moduleNameMapper: {
"^vue$": "@vue/compat",
},
// ...
};Hint: it might be a good idea to set up your environment to use Vue 2 or Vue 3 conditionally. It greatly simplifies the migration process.
- Install
vue-test-utils-compat. Please take a note that your test environment might reset modules between files (jestdo this), so make sure to do this in the proper place (we're usingsetupFilesAfterEnvin jest):
const compatH = new Vue({}).$createElement;
installVTUCompat(VTU, fullCompatConfig, compatH);-
Run your tests and fix failing ones. Typical failures usually include:
- using private Vue API (like
__vue__) [example commit] - wrong usage of
findvs.findComponent[example commit] - snapshots (they might differ between Vue 2 and Vue 3)
- using private Vue API (like
-
At this point, you (theoretically) have a green suite and can start working on upgrading your code to Vue 3
-
Replace
fullCompatConfigfrom step 3 with the detailed list of compat flags. You can copy-paste the full list of flags below or take a look at the source code to figure all flags:
const compatConfig = {
EXPORT_CREATE_LOCAL_VUE: true,
EXPORT_CREATE_WRAPPER: true,
GLOBAL_STUBS: true,
MOUNT_ARGS_CONTEXT_ATTRS: true,
MOUNT_ARGS_CONTEXT_CHILDREN: true,
MOUNT_ARGS_CONTEXT_CLASS: true,
MOUNT_ARGS_CONTEXT_ON: true,
MOUNT_ARGS_CONTEXT_PROPS: true,
MOUNT_ARGS_LISTENERS: true,
MOUNT_ARGS_MOCKS: true,
MOUNT_ARGS_PROVIDE: true,
MOUNT_ARGS_SCOPED_SLOTS: true,
MOUNT_ARGS_SCOPED_SLOTS_THIS: true,
MOUNT_ARGS_STUBS: true,
WRAPPER_ATTRIBUTES_DISABLED: true,
WRAPPER_ATTRIBUTES_VALUE: true,
WRAPPER_DESTROY: true,
WRAPPER_DO_NOT_INCLUDE_NATIVE_EVENTS_IN_EMITTED: true,
WRAPPER_FIND_ALL: true,
};- 🔁 Turn off one compatibility flag. Fix failing tests. Repeat.
- As soon as you turn off the last compatibility flag - throw away and uninstall this package. You are awesome! 🎉
installCompat(VueTestUtilsModule, compatConfig, vueH)VueTestUtilsModule- module, which will be patchedcompatConfig: Record<string, boolean>- list of compatibility flagsvueH- function which will be used to create Vue VNodes. Required only ifMOUNT_ARGS_SCOPED_SLOTS_THIScompatibility flag is used, could be omitted otherwise
compatFlags- object with all available compatibilityfullCompatConfig- config object with all compatibility flags enabled
Tests cover all compatibility flags. If the flag description is unclear, check the relevant test in tests folder.
Adds createLocalVue to @vue/test-utils module and support for { localVue } mount option.
⚠️ localVueprovides.extend, which is no-op operation. It is sufficient for most of the code but might require special handling
➡️ Migration strategy: available in @vue/test-utils v2 docs
Adds createWrapper to @vue/test-utils module
➡️ Migration strategy: replace createWrapper with new DOMWrapper(), new VueWrapper() which are available as exports in @vue/test-utils v2
Enable support for components field in mount args of @vue/test-utils
➡️ Migration strategy: Move components mount arg to global.components
Flags:
MOUNT_ARGS_CONTEXT_ATTRSMOUNT_ARGS_CONTEXT_CHILDRENMOUNT_ARGS_CONTEXT_CLASSMOUNT_ARGS_CONTEXT_ONMOUNT_ARGS_CONTEXT_PROPS
Enable support for context field in mount args of @vue/test-utils (used to test functional components)
⚠️ MOUNT_ARGS_CONTEXT_CHILDRENconvertscontext.childrento the default slot of the component. It is not a complete implementation of oldcontext.childrenbehavior but should be sufficient for most cases.
➡️ Migration strategy: rewrite your mount args as follows:
context.props,context.attrs, andcontext.classgo directly topropschildrenare replaced withslots.defaultcontext.onbecome correspondingprops: (click→onClick, etc.)
Enable support for components field in mount args of @vue/test-utils
➡️ Migration strategy: Move directives mount arg to global.directives
Allow passing { listeners } field in mount arguments
➡️ Migration strategy: replace listeners with props: (click → onClick, etc.)
Enable passing mocks to the component from mount arguments
➡️ Migration strategy: move mocks mount arg to global.mocks
Allow passing relevant provide to the component
⚠️ @vue/test-utilsv2 does not support passingprovideas function. It means that yourprovide()function might be invoked earlier than you think
➡️ Migration strategy: move provide mount arg to global.provide. If your provide is a function - replace it with an object.
Enable scopedSlots support in mount args
➡️ Migration strategy: merge scopedSlots mount arg to slots. If your scoped slot is using raw string - wrap it with <template #default="props">${your string here}</template>
Allows scopedSlots declared as functions to receive this which contains $createElement and $set
⚠️ ⚠️ ⚠️ RequiresMOUNT_ARGS_SCOPED_SLOTSto be enabled and third argument (vueH) a forinstallCompatcall
️
⚠️ $createElementprovided by this flag is not context-aware and will not be able to render components as a string. Refer to Vue docs for details
➡️ Migration strategy: ❌ rewrite such slots in your tests
Enable stubs to be passed to mount arguments
➡️ Migration strategy: move stubs mount arg to global.stubs
Adds special handling when retrieving the disabled attribute on wrapper. Previously Vue always normalized such values (Vue 3 migration guide has more details on this)
➡️ Migration strategy: update your .attributes("disabled") assertions to relevant values
Adds special handling when retrieving the value attribute on wrapper. Previously Vue always set value as DOM node attribute, which is no more the case
➡️ Migration strategy: ❌ rewrite your value retrieval in another way
Enables wrapper.destroy calls and enableAutoDestroy calls
➡️ Migration strategy: replace all wrapper.destroy calls with wrapper.unmount and enableAutoDestroy with `enableAutoUnmount
Makes sure that native events will not be captured in .emitted()
➡️ Migration strategy: rewrite your event-related assertions to take into account that native events are also captured, or (preferred) use emits option on your components
Makes sure that hook: events (which happen when using @vue/compat) will not be captured in .emitted()
➡️ Migration strategy: rewrite your event-related assertions to take into account that such events are also captured, or just upgrade to Vue 3 build without compat
Implements old behavior of .findAll / .findAllComponents when results were wrapped with special object with .wrappers field and various methods (.at, .filter, .trigger, etc.)
➡️ Migration strategy: rewrite your tests, assuming that .findAll and .findAllComponents return a simple array instead
Implements old behavior of .find / .findAll when results will be Vue components if they matches. So potentially, you can receive mixed array of DOM/Vue wrappers when using .findAll with this compat flag
➡️ Migration strategy: replace .find with .findComponent and .findAll with .findAllComponents where appropriate. Please take a note that if your tests rely on having a mixed array of DOM/Vue wrappers - you need to rewrite them
Implements old behavior when using .findComponent with ref will return DOM wrapper if ref is pointing to one.
➡️ Migration strategy: replace .findComponent with .find by ref (when vuejs/test-utils#1110 will be merged)
Implements old behavior when using .trigger on DOM Wrapper did not trigger change event, so you should trigger it manually (important for lazy v-models)
➡️ Migration strategy: rewrite relevant tests
Implements old VTU v1 behavior when using .setValue on Vue component actually used same logic, as setting value on DOM node (checking element type of Vue component, etc.)
➡️ Migration strategy: fix your components to use new setValue (which respects v-model) or rewrite relevant tests
This package monkey-patches @vue/test-utils package. Depending on your setup this might not work (for example you are using real imports). In that case you can create a mutable wrapper around VTU and replace all your imports from @vue/test-utils to this helper module:
import * as VueTestUtils from '@vue/test-utils';
import { h } from 'vue';
import { installCompat, fullCompatConfig } from `@vue/test-utils/compat`
const PatchedVTU = { ...VueTestUtils };
installCompat(PatchedVTU, fullCompatConfig, h);
export PatchedVTU;