I have a very simple component relying on two fetch calls :
<template>
<div>
<div ng-if="this.foo">
{{ foo.name }}
</div>
<div ng-if="this.bar">
{{ bar.address }}
</div>
</div>
</template>
<script>
export default {
name: 'identity-card',
data() {
return {
foo:undefined,
bar:undefined
}
}
created() {
Promise.all([
fetch('http://ul/to/api/foo'),
fetch('http://ul/to/api/bar')
]).then(async (response) => {
this.foo = await response[0].json();
this.bar = await response[1].json();
})
}
}
</script>
I'm trying to test that component with Jest.
While I found how to mock a Promise with Jest, I couldn't find a way to mock both fetch responses.
How can I do it without adding an external lib and without potentially refactoring my code?
You could set global.fetch to a jest.fn() that uses the mockReturnValueOnce() API for each expected fetch call:
const makeFetchResp = value => ({ json: async() => value }) // mock fetch().json()
const mockFetch = jest.fn()
.mockReturnValueOnce(makeFetchResp(true)) // 1st fetch() returns true
.mockReturnValueOnce(makeFetchResp(false)) // 2nd fetch() returns false
global.fetch = mockFetch
Before asserting any changes from the fetches, the test needs to flush their Promises for their then callbacks to be invoked. This can be done with:
const flushPromises = () => new Promise(r => setTimeout(r))
await flushPromises()
Your test would look similar to this:
it('fetches foo bar', async () => {
const makeFetchResponse = value => ({ json: async() => value })
const mockFetch = jest.fn()
.mockReturnValueOnce(makeFetchResponse(true))
.mockReturnValueOnce(makeFetchResponse(false))
global.fetch = mockFetch
const wrapper = shallowMount(MyComponent)
await flushPromises()
expect(mockFetch).toHaveBeenCalledTimes(2)
expect(wrapper.vm.foo).toBeTruthy()
expect(wrapper.vm.bar).toBeFalsy()
})
Related
I have the following files:
Component.vue
<template>
...
</template>
<script setup lang=ts>
...
const model = reactive([]);
watch(model, () => {
foo();
});
const foo = () => {
// do something
};
</script>
Component.spec.ts
describe("some test suite", () => {
it("calls 'foo' when model changes", async () => {
const wrapper = mount(Component);
const spyFoo = jest.spyOn(wrapper.vm, "foo");
wrapper.vm.model.push("bar");
expect(spyFoo).toHaveBeenCalledTimes(1);
});
});
What I want to test with this is, if foo() is called when i change the model. But when I do it like this I get the following error:
TypeError: object.hasOwnProperty is not a function
What is the proper way to implement test cases like that?
I am fetching som data from my backend and iterate over a component, that uses that data in the child component. The name of each does always render though the image only renderes sometimes and somestimes not. I'm guessing the reason behind this is the fact that Vue iterates over the data before it's all fetched.
onMounted(() => {
const getGradients = async () => {
const {data: usersLovedGradients } = await supabase.from("users").select().eq('username', user.value.username).single()
lovedArr = usersLovedGradients.loved
const {data: progradients } = await supabase.from("progradients").select("gradient_id")
const arrGradients = []
progradients.forEach( async (progradient, index) => {
if (lovedArr.includes(progradient.gradient_id)) {
const {data: gradients } = await supabase.from("progradients").select().eq('gradient_id', progradient.gradient_id).single()
arrGradients.push(gradients)
}
if (lovedArr.length === arrGradients.length) {
lovedGradients.value = arrGradients
}
})
}
getGradients()
});
THIS IS HOW I AM ITERATIONG OVER THE COMPONENT
<div class="cards-container">
<GradientCard
v-for="(lovedGradient, index) in lovedGradients"
:expertGradient="lovedGradient"
:index="index"
:id="lovedGradient.gradient_id"
/>
</div>
Is there any way to not mount/render the cards before all data has been retrieved? or is this caused by something else?
I am working on a project built on Vue3 and composition API and writing test cases.
The component I want to test is like below.
Home.vue
<template>
<div>
<Child #onChangeValue="onChangeValue" />
</div>
</template>
<script lang="ts>
...
const onChangeValue = (value: string) => {
store.dispatch("changeValueAction", {
value: value,
});
};
</scirpt>
Now I want to test if changeValueAction has been called.
Home.spec.ts
...
import { key, store } from '#/store';
describe("Test Home component", () => {
const wrapper = mount(Home, {
global: {
plugins: [[store, key]],
},
});
it("Test onChangeValue", () => {
const child = wrapper.findComponent(Child);
child.vm.$emit("onChangeValue", "Hello, world");
// I want to check changeValueAction has been called.
expect(wrapper.vm.store.state.moduleA.value).toBe("Hello, world");
});
});
I can confirm the state has actually been updated successfully in the test case above but I am wondering how I can mock action and check if it has been called.
How can I do it?
I have sort of a similar setup.
I don't want to test the actual store just that the method within the component is calling dispatch with a certain value.
This is what I've done.
favorite.spec.ts
import {key} from '#/store';
let storeMock: any;
beforeEach(async () => {
storeMock = createStore({});
});
test(`Should remove favorite`, async () => {
const wrapper = mount(Component, {
propsData: {
item: mockItemObj
},
global: {
plugins: [[storeMock, key]],
}
});
const spyDispatch = jest.spyOn(storeMock, 'dispatch').mockImplementation();
await wrapper.find('.remove-favorite-item').trigger('click');
expect(spyDispatch).toHaveBeenCalledTimes(1);
expect(spyDispatch).toHaveBeenCalledWith("favoritesState/deleteFavorite", favoriteId);
});
This is the Component method:
setup(props) {
const store = useStore();
function removeFavorite() {
store.dispatch("favoritesState/deleteFavorite", favoriteId);
}
return {
removeFavorite
}
}
Hope this will help you further :)
I want to make several API calls to get data into a component. I created a PostService.ts that looks like this:
const apiClient = axios.create({
baseURL: '/api/v1',
})
export default {
async getPosts() {
const { data }: { data: Post[] } = await apiClient.get('/posts')
// transform data ...
return data
},
async getTags() {
const { data }: { data: Tag[] } = await apiClient.get('/tags')
return data
},
async getComments() {
const { data }: { data: Comment[] } = await apiClient.get('/comments')
return data
},
}
This is my posts.vue:
<template>
<div>
<div v-if="dataLoaded">
content
</div>
<div v-else>
loading...
</div>
</div>
</template>
<script>
finishedApiCalls = 0
get dataLoaded() {
return this.finishedApiCalls === 3
}
created() {
PostService.getPosts()
.then((posts) => {
this.posts = posts
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
PostService.getTags()
.then((tags) => {
this.tags = tags
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
PostService.getComments()
.then((comments) => {
this.comments = comments
this.finishedApiCalls++
})
.catch((error) => {
console.log('There was an error:', error)
})
}
</script>
The key point is that I want to display a loading spinner as long as the data has not been loaded. Is it recommended to make the API calls from created()? What would be a more elegant way to find out when all calls are finished? It does not feel right to use the finishedApiCalls variable.
I recommend using Nuxt's fetch method along with Promise.all() on all your async PostService fetches:
// MyComponent.vue
export default {
fetch() {
return Promise.all([
PostService.getPosts().then((posts) => ...).catch((error) => ...),
PostService.getTags().then((tags) => ...).catch((error) => ...),
PostService.getComments().then((comments) => ...).catch((error) => ...)
])
}
}
Nuxt provides a $fetchState.pending prop that you could use for conditionally rendering a loader:
<template>
<div>
<Loading v-if="$fetchState.pending" />
<div v-else>My component data<div>
</div>
</template>
You can use Promise.all for this kind of requirements.
this.loading = true
Promise.all([PostService.getPosts(), PostService.getTags(), PostService.getComments()])
.then(values => {
let [posts, tags, comments] = values
this.posts = posts
this.tags = tags
this.comments = comments
//Here you can toggle your fetching flag like below
this.loading = false
})
You can use Promise.all(). This will wait till all resolves or if 1 fails.
With async / await you can make it "synchronous"
data() {
return {
loaded: false
}
},
async created() {
let [posts, tags, comments] = await Promise.all([PostService.getPosts(), PostService.getTags(), PostService.getComments()])
this.posts = posts;
this.tags = tags;
this.comments = comments;
this.loaded = true;
}
I'm trying to create a test in an application with jest and this is some lines of my code:
import React, { Component } from 'react';
import {...} from 'react-native';
import jwt_decode from 'jwt-decode';
class CreateProduct extends Component {
constructor(props) {
super(props);
this.keyboardHeight = new Animated.Value(0);
this.imageHeight = new Animated.Value(199);
this.state = {
isButtonsHidden: false,
title: '',
price: '',
description: '',
isDialogVisible: false,
messageError: '',
};
}
_goBack = async () => {
const {state} = this.props.navigation;
var token = state.params ? state.params.token : undefined;
this.props.navigation.navigate('MyProducts', {token:token});
}
I want to test the navigation:
this.props.navigation.navigate('MyProducts', {token:token});
Now this is the attempt to test:
describe('Testing navigation', () =>{
let wrapper = null
const spyNavigate = jest.fn()
const props = {
navigation:{
navigate: spyNavigate
}
}
const params = {
token: 'randomToken'
}
beforeEach(() => {
wrapper = shallow(<CreateProduct {...props}/>)
wrapper.setState({params: params})
})
it('should test navigation', () => {
wrapper.instance()._goBack(params)
expect(spyNavigate).toHaveBeenCalled()
})
})
But I'm receiving this error.
I'm assuming that there is an error with the way I'm passing the const params. Can you help me telling what's the best way I can do this to simulate a token and that way I can navigate in the screen?
Thanks.
Rootcause is your _goBack is async. But you don't await till it ends before running expect. Even more: jest also does not wait _goBack to finish so you don't even see an error
Cannot read property 'params' of undefined
that happens because you don't mock state in navigation.params.
To work with async code there are 2 different approaches in Jest: either returning Promise from the it() or running done() callback manually(it's passed as 1st argument in it()).
I'll picking 2nd since it allows us also await until goBack is finished before running expect:
describe('Testing navigation', () => {
let wrapper = null
const spyNavigate = jest.fn()
const props = {
navigation: {
navigate: spyNavigate,
state: {}
}
}
const params = {
token: 'randomToken'
}
beforeEach(() => {
wrapper = shallow(<CreateProduct {...props} />)
wrapper.setState({ params: params })
})
it('should test navigation', async () => {
await wrapper.instance()._goBack(params)
expect(spyNavigate).toHaveBeenCalled()
})
})
Or without using async/await it would look like
it('should test navigation', () => {
return wrapper.
instance()._goBack(params).
then(() => expect(spyNavigate).toHaveBeenCalled());
})
that looks messy
Or using done() callback
it('should test navigation', (done) => {
wrapper.
instance()._goBack(params).
then(() => expect(spyNavigate).toHaveBeenCalled()).
then(done);
})