Update
This is possible several different ways. There's two issues that need to be addressed.
The variable has to be made available. You can use vue's expose function in setup (but getting the value is really messy: wrapper.__app._container._vnode.component.subTree.component.exposed😱) or just include it in the return object (accessible through wrapper.vm).
change how you mutate the data in the test.
your test has
const myObject = {myName:"DNU Bad Name", myNumber:"12345"}
let myData = reactive({myObject})
const wrapper = shallowMount(Hello)
await wrapper.setData({'myData' : myData})
even if setData was able to override the internal, it would not work.
the problem is that the setup function has this
let myData = reactive({ myObject });
const isEditable = computed(() => {
return myData.myObject.myName.startsWith("DNU") ? false : true;
});
where editable is using a computed generated from that instance of myData. If you override myData with a separate reactive, the computed will still continue to use the old one. You need to replace the contents of the reactive and not the reactive itself
To update the entire content of the reactive, you can use:
Object.assign(myReactive, myNewData)
you can make that a method in your component, or just run that from the test. If you update any value within the reactive (like myData.myObject) you can skip the Object.asign
Here are several versions of how you can test it.
Component:
<template>
<div v-if="isEditable" id="myEditDiv">
<button type="button">Edit</button>
</div>
</template>
<script>
import { computed, defineComponent, reactive } from "vue";
export default defineComponent({
setup(_, { expose }) {
const myObject = { myName: "", myNumber: "" };
let myData = reactive({ myObject });
const isEditable = computed(() => {
return myData.myObject.myName.startsWith("DNU") ? false : true;
});
const updateMyData = (data) => Object.assign(myData, data);
expose({ updateMyData });
return {
isEditable,
updateMyData,
myData
};
},
});
</script>
the test
import { shallowMount } from "@vue/test-utils";
import MyComponent from "@/components/MyComponent.vue";
const data = { myObject: { myName: "DNU Bad Name" } };
describe("MyComponent.vue", () => {
it.only("sanity test", async () => {
const wrapper = shallowMount(MyComponent);
expect(wrapper.vm.isEditable).toBe(true);
});
it.only("myData", async () => {
const wrapper = shallowMount(MyComponent);
Object.assign(wrapper.vm.myData, data);
expect(wrapper.vm.isEditable).toBe(false);
});
it.only("myData", async () => {
const wrapper = shallowMount(MyComponent);
wrapper.vm.myData.myObject = data.myObject;
expect(wrapper.vm.isEditable).toBe(false);
});
it.only("updateMyData method via return", async () => {
const wrapper = shallowMount(MyComponent);
wrapper.vm.updateMyData(data);
expect(wrapper.vm.isEditable).toBe(false);
});
it.only("updateMyData method via expose🙄", async () => {
const wrapper = shallowMount(MyComponent);
wrapper.__app._container._vnode.component.subTree.component.exposed.updateMyData(
data
);
expect(wrapper.vm.isEditable).toBe(false);
});
});
It is not possible through setData
from the docs:
setData
Updates component internal data.
Signature:
setData(data: Record<string, any>): Promise<void>
Details:
setData does not allow setting new properties that are not defined in the component.
Also, notice that setData does not modify composition API setup() data.
It seems that updating internals with composition API is incompatible with setData. See the method name setData, refers to this.data and was likely kept in the vue test utils mostly for backwards compatibility.
I suspect the theory is that it's bad practice anyway to test, what would be considered, an implementation detail and the component test should focus on validating inputs an outputs only. Fundamentally though, this is a technical issue, because the setup function doesn't expose the refs and reactives created in the setup.