vue-test-utils-compat
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
Table of Contents
⏩ Quickstart
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(VTU, fullCompatConfig, compatH);
✔️ Upgrade workflow
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
as vue
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.
[example commit]
- 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 using setupFilesAfterEnv
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)
-
At this point, you (theoretically) have a green suite and can start working on upgrading your code to Vue 3
-
Switch fullCompatConfig
from step 3 with the detailed configuration 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!
🌐 Global API
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 if MOUNT_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
🏁 Compatibility flags
Tests cover all compatibility flags. If the flag description is unclear, check the relevant test in tests
folder.
EXPORT_CREATE_LOCAL_VUE
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
EXPORT_CREATE_WRAPPER
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
MOUNTARGS_CONTEXT*
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
converts context.children
to the default slot of the component. It is not a complete implementation of old context.children
behavior but should be sufficient for most cases.
➡️ Migration strategy: rewrite your mount args as follows:
context.props
, context.attrs
, and context.class
go directly to props
children
are replaced with slots.default
context.on
become corresponding props
: (click
→ onClick
, etc.)
MOUNT_ARGS_LISTENERS
Allow passing { listeners }
field in mount
arguments
➡️ Migration strategy: replace listeners
with props
: (click
→ onClick
, etc.)
MOUNT_ARGS_MOCKS
Enable passing mocks
to the component from mount
arguments
➡️ Migration strategy: move mocks
mount arg to global.mocks
MOUNT_ARGS_PROVIDE
Allow passing relevant provide
to the component
⚠️ @vue/test-utils
v2 does not support passing provide
as function. It means that your provide()
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.
MOUNT_ARGS_SCOPED_SLOTS
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>
MOUNT_ARGS_SCOPED_SLOTS_THIS
Allows scopedSlots
declared as functions to receive this
which contains $createElement
and $set
⚠️⚠️⚠️ Requires MOUNT_ARGS_SCOPED_SLOTS
to be enabled and third argument (vueH
) a for installCompat
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
MOUNT_ARGS_STUBS
Enable stubs
to be passed to mount arguments
➡️ Migration strategy: move stubs
mount arg to global.stubs
WRAPPER_ATTRIBUTES_DISABLED
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
WRAPPER_ATTRIBUTES_VALUE
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
WRAPPER_DESTROY
Enables wrapper.destroy
calls and enableAutoDestroy
calls
➡️ Migration strategy: replace all wrapper.destroy
calls with wrapper.unmount
and enableAutoDestroy
with `enableAutoUnmount
WRAPPER_DO_NOT_INCLUDE_NATIVE_EVENTS_IN_EMITTED
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
WRAPPER_FIND_ALL
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
WRAPPER_FIND_BY_CSS_SELECTOR_RETURNS_COMPONENTS (added in v0.0.2)
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
🚧 Work in progress
These compat rules are a work in progress and will be included soon
Known issues
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;