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-compat
Vue 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-utils
v1 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/compat
asvue
alias. 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 (jest
do this), so make sure to do this in the proper place (we're usingsetupFilesAfterEnv
in 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
find
vs.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
fullCompatConfig
from 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_THIS
compatibility 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.
⚠️ localVue
provides.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_ATTRS
MOUNT_ARGS_CONTEXT_CHILDREN
MOUNT_ARGS_CONTEXT_CLASS
MOUNT_ARGS_CONTEXT_ON
MOUNT_ARGS_CONTEXT_PROPS
Enable support for context
field in mount
args of @vue/test-utils
(used to test functional components)
⚠️ MOUNT_ARGS_CONTEXT_CHILDREN
convertscontext.children
to the default slot of the component. It is not a complete implementation of oldcontext.children
behavior but should be sufficient for most cases.
➡️ Migration strategy: rewrite your mount args as follows:
context.props
,context.attrs
, andcontext.class
go directly toprops
children
are replaced withslots.default
context.on
become 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-utils
v2 does not support passingprovide
as 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_SLOTS
to be enabled and third argument (vueH
) a forinstallCompat
call
️
⚠️ $createElement
provided 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;