Migration Vue 2 to Vue 3 with Composition API, Typescript and script setup: Challenge wrapper.setData() in unit tests
In one of our projects, we are migrating the Vue 2 components to Vue 3 with script setup and Typescript step by step according to the Boy Scout rule. Occasionally, the unit tests also have to be adapted.
Migration is usually uncomplicated. However, in a unit test wrapper.setData() was called. This no longer works with the Composition API.
The Vue 2 component to be migrated contained this code:
data() {
return {
isDirty : false
}
}
The unit test included this test:
test (`saving changes allowed`, async () => {
await wrapper.setData ({isDirty: true})
expect (…)…
}
The challenges:
- wrapper.setData() no longer works on script setup components.
- console.log(wrapper.vm) does not show any refs and functions. The IDE also does not recognize anything in the code completion and even displays the call as a warning.
The solution:
The code of the component with Vue 3 and script setup
const isDirty = ref(false)
function foo() {}
In the migrated unit test, you can access the Ref via vm:
test.each(allowedRoles)(`saving changes allowed for %s`, async (role) => {
wrapper.vm.isDirty = true
await nextTick()
})
Aha moments here:
-
- Although isDirty is now a Ref, you must not write wrapper.vm.isDirty.value in the unit test.
- A ref is recognized by the IDE if it is there but not in the code completion. (There is no warning marker, and after it is written, the type is displayed correctly in the popup).
- A function, e.g. wrapper.vm.foo() is never recognized by the IDE and receives a warning marker "unknown function". BUT: If you run the test, the function is called correctly. The next point still applies.
- Without await nextTick(), the Vue 3 test code still does not work. This is the most unusual thing about this migration. If you are testing a Vue 2 component, you do not need the nextTick(). Changing the internal state of the data properties has an immediate effect on the template code. Vue behaves differently when using refs.
- defineExpose() has no influence on console.log(wrapper.vm) or the IDE code completion in unit tests.
- If you call a function, the unit test works, but the build fails because the vue-tsc command does not find the function and aborts the build. As the problematic code is in the unit test, you can use a configuration file to tell vue-tsc to ignore unit tests during the build:
tsconfig.build-dts.json neben tsconfig.json
{
„extends“: „,./tsconfig.json“,
„exclude“: [
„**/*.spec.ts“,
„**/*.test.ts“,
]
}