I'm trying to declare a const to be re-used for a number of tests.
For example:
describe('Component.vue', () => {
const householdData = [ "here", "is", "some", "data" ]
it('does stuff', () => {
const wrapper = mount(HouseholdsComponent, {
propsData: {
original_household: householdData,
}
});
expect(original_household).toContain("here");
})
it('does stuff', () => {
const wrapper = mount(HouseholdsComponent, {
propsData: {
original_household: householdData,
}
});
expect(original_household).toContain("is");
})
});
The problem is that householdData does not seem to be getting set.
When I console.log householdData, I get this:
{ clients: [Getter/Setter], networth: [Getter/Setter] }
I've tried setting the data within the component like this as well:
wrapper.vm.someVariable = householdData
and that also gives me this:
{ clients: [Getter/Setter], networth: [Getter/Setter] }
However, it does work when I do this.
wrapper.vm.someVariable = [ "here", "is", "some", "data" ]
I would hate to have to keep setting this data in each test.
What am I doing wrong?
I figured it out. As opposed to setting the data as a const, I had to return it from a function.
function householdData() {
return [ "here", "is", "some", "data" ]
}
Then I pass it to component props like this:
const wrapper = mount(HouseholdsComponent, {
propsData: {
original_household: householdData(),
}
});
Voila!
Related
I have the my code working in a sandbox and now I am trying to write the test. However, When I try this...
test("Hello World", async () => {
let list = [
{
name: "foo"
}
];
var data = {
list
};
const wrapper = mount(MyComponent, data);
await wrapper.vm.$nextTick();
expect(wrapper.html()).toContain("foo");
expect(wrapper.html()).not.toContain("bar");
list.push({
name: "bar"
});
await wrapper.setProps({ list });
await wrapper.vm.$nextTick();
expect(wrapper.html()).toContain("foo");
expect(wrapper.html()).toContain("bar");
});
However, expect(wrapper.html()).toContain("bar"); fails because it cannot fine the text. I can see it work using setTimeout so I am not sure what I am missing.
How do I see the prop changes in the html?
Your component is not expecting any props. When you mounting your component you are setting component's data property. And if you want to change it later in test after mounting you should call setData.
Also there is a mistake in your test: according to docs data must be a function.
With all being said your test should look like that:
test("Hello World", async () => {
const list = [
{
name: "foo"
}
];
const data = () => {
list
};
const wrapper = mount(MyComponent, {
data
});
expect(wrapper.html()).toContain("foo");
expect(wrapper.html()).not.toContain("bar");
list.push({
name: "bar"
});
await wrapper.setData({ list });
expect(wrapper.html()).toContain("foo");
expect(wrapper.html()).toContain("bar");
});
In my VueJS 2 component below, I can add the imgdata property to each question in the area.questions array. It works - I can see from the console.log that there are questions where imgdata has a value. But despite using $set it still isn't reactive, and the imgdata isn't there in the view! How can I make this reactive?
var componentOptions = {
props: ['area'],
data: function() {
return {
qIndex: 0,
};
},
mounted: function() {
var that = this;
that.init();
},
methods: {
init: function() {
var that = this;
if (that.area.questions.length > 0) {
that.area.questions.forEach(function(q) {
Util.HTTP('GET', '/api/v1/photos/' + q.id + '/qimage').then(function(response) {
var thisIndex = (that.area.questions.findIndex(entry => entry.id === q.id));
var thisQuestion = (that.area.questions.find(entry => entry.id === q.id));
thisQuestion.imgdata = response.data;
that.$set(that.area.questions, thisIndex, thisQuestion);
})
});
}
console.log("area.questions", that.area.questions);
},
Since area is a prop, you should not be attempting to make changes to it within this component.
The general idea is to emit an event for the parent component to listen to in order to update the data passed in.
For example
export default {
name: "ImageLoader",
props: {
area: Object
},
data: () => ({ qIndex: 0 }), // are you actually using this?
mounted () {
this.init()
},
methods: {
async init () {
const questions = await Promise.all(this.area.questions.map(async q => {
const res = await Util.HTTP("GET", `/api/v1/photos/${encodeURIComponent(q.id)}/qimage`)
return {
...q,
imgdata: res.data
}
}))
this.$emit("loaded", questions)
}
}
}
And in the parent
<image-loader :area="area" #loaded="updateAreaQuestions"/>
export default {
data: () => ({
area: {
questions: [/* questions go here */]
}
}),
methods: {
updateAreaQuestions(questions) {
this.area.questions = questions
}
}
}
Here that variable has a value of this but it's bound under the scope of function. So, you can create reactive property in data as below :
data: function() {
return {
qIndex: 0,
questions: []
};
}
Props can't be reactive so use :
that.$set(this.questions, thisIndex, thisQuestion);
And assign your API output to directly questions using this.questions.
I'm getting this error while trying to mutate the local state in apollo.
errInvariant Violation: Expecting a parsed GraphQL document. Perhaps you need to wrap the query string in a "gql" tag? http://docs.apollostack.com/apollo-client/core.html#gql
Initial state
registration: {
__typename: 'Registration',
tempMerchantId: '',
authorizeProfile: {
__typename: 'AuthorizePersonProfile',
nid_front: '',
nid_back: '',
authorized_person_photo: ''
}
}
My mutation
export const setAuthorizePersonQuery = gql`
mutation setAuthorizePersonProfileInfo($authorizePerosnData: Object!){
setAuthorizePersonProfileInfo(authorizePersonData: $authorizePerosnData) #client
}
`;
My resolver
export const setAuthorizePersonProfileInfo = (
_, { authorizePersonData }, { cache }
) => {
try {
const prevData = cache.readQuery({ getAuthorizePersonProfileQuery });
cache.writeQuery({
getAuthorizePersonProfileQuery,
data: {
registration: {
__typename: 'Registration',
authorizeProfile: {
__typename: 'AuthorizePersonProfile',
...prevData.registration.authorizeProfile,
...authorizePersonData
}
}
}
});
} catch (e) {
console.log(`err${e}`);
}
return null;
};
I'm trying to mutate the local state on button press, the function is
const handlePressedNext = () => {
Promise.all([
setAuthorizePersonProfileInfo({
variables: { authorizePersonData: generateNidData() }
})
])
.then(() => {
navigation.navigate('Photograph');
});
};
generateNidData function is like bellow
const generateNidData = () => ({
nid_front: nidFrontImage,
nid_back: nidBackImage
});
I'm new to apollo client. I can not understand what I'm doing wrong. Can anyone help me figure out the problem?
getAuthorizePersonProfileQuery is not a valid option for readQuery. Presumably, you meant use query instead.
I am using vue-head in website because of I have to pass the name of the program to the html head, and the inf. it is coming from an API, so I make the request but every time I try to pass the name it send me error this the code:
export default {
data: () => ({
errors: [],
programs: [],
firstVideo: {},
vidProgram: {}
}),
},
created() {
//do something after creating vue instance
this.api = new ApiCanal({})
this.getProgram()
},
methods: {
getProgram() {
this.api.http.get(`videos/program/${this.programSlug}`)
.then(response => {
this.programs = response.data
this.firstVideo = response.data[0]
this.vidProgram = response.data[0]['program']
})
.catch(error => {
this.errors = error
});
}
},
head: {
//this is the inf. for the head
title: {
inner: this.programs.name,
separator: '-',
complement: this.programs.info
}
}
}
I will really appreciate if you can help me with this issue
If you want to use properties of your Vue object/component in the title there, you need to make it a function, as currently this refers to the object creating your Vue component (probably the global window object).
head: {
title: function() {
return {
inner: this.programs.name,
separator: '-',
complement: this.programs.info
};
}
}
I've played around with vue-i18n and Vue.compile() and found a very static solution to my problem. While searching for a solution I've tried to dynamically set the render functions during runtime. Unfortunately without any success.
Out of curiosity: Is it possible to exchange the render functions of Components during runtime?
I try to do something like this:
{
props: {
toCompile: {
type: String,
required: true
},
callbackFn: {
type: Function,
default: () => {}
}
},
created (){
let res = Vue.compile(this.toCompile);
this.render = res.render;
this.staticRenderFns = res.staticRenderFns;
}
}
The following approach is working for me:
{
...
methods: {
render: function () {
var createElement = this.$createElement;
return (this._self._c || createElement)("div", {
staticClass: "element"
});
}
},
beforeCreate: function() {
this.$vnode.componentOptions.Ctor.options.render = this.$vnode.componentOptions.Ctor.options.methods.render.bind(this);
}
}
If your want slots as well, use the following render method:
render: function () {
var that = this,
createElement = (this._self._c || this.$createElement),
children = Object.keys(that.$slots).map(function(slot) {
return createElement('template', { slot }, that.$slots[slot]);
});
return createElement('div', [
createElement('component-element, {
attrs: that.$attrs,
on: that.$listeners,
scopedSlots: that.$scopedSlots,
}, children)
]);
}