Branch coverage zero percent in jest - reactjs

I have written some test cases and everything seems fine except the following one. I am getting zero branch cover for one file. I have googled couple of blog and I came to understand if the statement cab be executed in multiple scenario that call branch coverage. But I don't find my code can be executed in multiple way.
request.js
import axios from 'axios';
export default async (request, httpService = axios) => {
const {
method, url, data, headers,
} = request;
return httpService.request({
method,
url,
headers: Object.assign({}, headers),
data,
});
};
reqeust.test.js
describe('requestServie', () => {
it('should have a valid request object', async () => {
const requestObj = {
method: 'POST',
url: 'http://mock.url',
data: {},
};
const mockRequest = jest.fn(() => Promise.resolve({}));
const httpService = {
request: mockRequest,
};
await request(requestObj, httpService);
expect(mockRequest).toHaveBeenCalledWith({
method: requestObj.method,
url: requestObj.url,
headers: {},
data: requestObj.data,
});
});
it('should return a valid response (empty)', async () => {
const response = {
data: {
},
status: 200,
statusText: 'OK',
headers: {},
config: {},
request: {},
};
const mockRequest = jest.fn(() => Promise.resolve(response));
const httpService = {
request: mockRequest,
};
const res = await request({ url: 'http://mock.url' }, httpService);
expect(res).not.toBe(null);
expect(res).toMatchObject(
{
status: response.status,
},
);
});
});
Edit
rquest.js
export default async (request, httpService = axios) => {
const {
method, url, data, headers,
} = request;
return httpService.request({
method,
url,
headers: Object.assign({}, headers),
data,
}).then(successResponse, (error) => {
throwHttpError(error);
});
};
request.test.js
import HttpError from 'standard-http-error';
import axios from 'axios';
import request, { successResponse, throwHttpError } from './requestService';
describe('requestService', () => {
jest.mock('axios', () => ({
request: jest.fn(() => Promise.resolve({})),
}));
describe('successResponse', () => {
const mockRes = {
status: 9001,
data: {
stuff: 'stuff',
},
};
it('should returns an object with only status and data properties', () => {
const responseKeys = Object.keys(successResponse(mockRes));
expect(responseKeys).toMatchObject(['status', 'data']);
expect(responseKeys.length).toBe(2);
});
it('should map the status of the reponse to the status property', () => {
expect(successResponse(mockRes).status)
.toBe(mockRes.status);
});
it('should map the data of the reponse to the data property', () => {
expect(successResponse(mockRes).data)
.toMatchObject(mockRes.data);
});
it('should have a valid request object', async () => {
const requestObj = {
method: 'POST',
url: 'http://mock.url',
data: {},
headers: {},
};
const mockRequest = jest.fn(() => Promise.resolve({}));
const httpService = {
request: mockRequest,
};
await request(requestObj, httpService);
expect(mockRequest).toHaveBeenCalledWith({
method: requestObj.method,
url: requestObj.url,
headers: {},
data: requestObj.data,
});
});
});
describe('httpThrowError', () => {
const mockErr = {
response: {
status: 9001,
statusText: 'error message goes here',
},
};
it('should map the status of the reponse to the error.status property', () => {
try {
throwHttpError(mockErr);
} catch (e) {
expect(e).not.toBe(null);
expect(e.status).toBe(mockErr.response.status);
expect(e.message).toBe(mockErr.response.statusText);
}
});
it('should map the data of the reponse to the error.data property', () => {
const mockErrWithData = Object.assign({}, mockErr);
mockErrWithData.response.data = {};
try {
throwHttpError(mockErrWithData);
} catch (e) {
expect(e).not.toBe(null);
expect(e.data).toBe(mockErrWithData.response.data);
}
});
});
describe('request', () => {
const testCases = [
['should return error response on server error', 500],
['should return error response on bad request', 400],
['should return error response on unauthorised', 401],
];
testCases.forEach(([testName, errorStatus]) => {
it(testName, async () => {
const errorResponse = {
response: {
status: errorStatus,
},
};
const mockRequest = jest.fn(() => Promise.reject(errorResponse));
const httpService = {
request: mockRequest,
};
try {
await request({ url: 'http://mock.url' }, httpService);
throw new Error('Expected an exception, but none was thrown');
} catch (err) {
expect(err).not.toBe(null);
expect(err).toMatchObject(
new HttpError(errorResponse.response.status,
errorResponse.response.statusText),
);
}
});
});
it('should return an valid response (empty)', async () => {
const response = {
data: {
meta: {},
results: [],
},
status: 200,
statusText: 'OK',
headers: {},
config: {},
request: {},
};
const mockRequest = jest.fn(() => Promise.resolve(response));
const httpService = {
request: mockRequest,
};
const res = await request({ url: 'http://mock.url' }, httpService);
expect(res).not.toBe(null);
expect(res).toMatchObject(
{
status: response.status,
data: response.data,
},
);
});
it('should use axios by default', async () => {
const req = { url: 'http://mock.url', method: 'get' };
await request(req);
expect(axios.request).toHaveBeenCalled();
});
});
});
Error
Updated 15/Nov/18
"jest": "^23.6.0",
import HttpError from 'standard-http-error';
import axios from 'axios';
import request, { successResponse, throwHttpError } from './requestService';
jest.mock('axios', () => ({
request: jest.fn(),
}));
Error

To see what is not covered you can go to coverage/Iconv-report and open index.html. Those are created once you run jest with --coverage option.
It looks like uncovered branch is: httpService = axios. So you need to check if default axios is used.
To cover that you may run request without httpService argument - you can mock axios globally for that, i.e.:
import axios from 'axios';
// note that mock goes before any describe block
jest.mock('axios', () => {
return {
request: jest.fn(() => Promise.resolve({})),
}
});
describe('requestService', () => {
// ... your other tests
it('should use axios by default', async () => {
const opts = { url: 'http://mock.url', method: 'get' };
const res = await request(opts);
expect(axios.request).toHaveBeenCalled();
});
});
Note that jest.mock have some buggy behavior when running inside a spec.

Related

axios returns "fullfilled" promise

Can someone please tell me why my Axios service function returns this:
The object looks fine in the service function.
Promise {<fulfilled>: {…}}
[[Prototype]]: Promise
[[PromiseState]]: "fulfilled"
[[PromiseResult]]: Object
Here is the call:
useEffect(() => {
setData(playlistService.getPlaylists);
}, []);
console.log(data);
And the function:
const config = {
headers: {
'Content-Type': 'application/json',
},
withCredentials: true,
};
const getPlaylists = async () => {
try {
const res = await axios.get(`${API_SONGS_URL}/`, config);
console.log('RES ', res);
return res;
} catch (error) {
if (error.response) {
return error.response.data;
}
}
};
this could work
useEffect(() => {
const fetchPlayListAsync = async () => {
const response = await playlistService.getPlaylists();
setData(response)
}
fetchPlayListAsync()
}, []);
you can add appropriate checks of fetched data

How can i change from promise to async/await

How I can change the arrow function in my code to async/await syntax in google drive integration.
const listFiles = () => {
setLoading(true)
gapi.client.drive.files
.list({
pageSize: 500,
fields: 'nextPageToken, files(id, name, mimeType, modifiedTime)',
})
.then(function (response) {
setLoading(false)
const res = JSON.parse(response.body);
setDocuments(res.files);
});
};
the another one function I want to add async/await:
const initClient = () => {
setLoading(true)
gapi.client
.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
})
.then(
function () {
setConnectStorage(true);
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
setLoading(false)
},
function (error) {
alert(error)
}
);
};
const listFiles = () => {
setLoading(true)
return new Promise((resolve, reject) => {
gapi.client.drive.files
.list({
pageSize: 500,
fields: 'nextPageToken, files(id, name, mimeType, modifiedTime)',
})
.then(function (response) {
setLoading(false)
const res = JSON.parse(response.body);
resolve(res.files)
});
})
};
const initClient = () => {
setLoading(true)
return new Promise ((resolve, reject) => {
gapi.client
.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
})
.then(
function () {
setConnectStorage(true);
// gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
// updateSigninStatus(gapi.auth2.getAuthInstance().isSignedIn.get());
resolve({status: gapi.auth2.getAuthInstance().isSignedIn.get()})
setLoading(false)
},
function (error) {
reject({status: false})
}
);
})
};
Then run those functions:
(async () => {
const files = await listFiles()
const status = await initClient()
})()

react-query customHook refetchOnWindowFocus

I am creating individual hooks using react-query. Where would I add in refetchOnWindowFocus: false ? as it is not currently being read and the data is being re-fetched when returning to the window.
const useFetchOverview = () => {
return useQuery(["userData", { refetchOnWindowFocus: false }],
async () => {
const { data } = await axios({
url: useFetchOverviewUrl,
headers: { ...getHeaders(reduxState) },
method: "get"
});
return data;
});
};
const { isLoading, data: userOverviewData } = useFetchOverview();
this should be third parameter after fuinction:
return useQuery(["userData"],
async () => {
const { data } = await axios({
url: useFetchOverviewUrl,
headers: { ...getHeaders(reduxState) },
method: "get"
});
return data;
}, { refetchOnWindowFocus: false });
ex: useQuery(queryKey, queryFn?, options)
check this for your reference: https://tanstack.com/query/v4/docs/reference/useQuery?from=reactQueryV3&original=https://react-query-v3.tanstack.com/reference/useQuery
OR you can write it for global:
const queryClient = new QueryClient({
defaultOptions: {
queries: {
refetchOnWindowFocus: false,
},
},
})

How do I resolve the issue of TypeError: Cannot read property 'then' of undefined?

I have a reactjs app that should be returning data from a WepAPI. The dispatch I call on a function seems to be giving me this error: TypeError: Cannot read property 'then' of undefined
I have used other functions through dispatch and it worked fine but this one still sticks out.
The intended result is for the data to get back to the initial dispatch. At the moment the data comes through but is stuck when returning to the initial call.
import React from 'react';
import { connect } from 'react-redux';
import { jobActions } from '../../actions/job.actions';
import Popup from 'reactjs-popup'
import JwPagination from 'jw-react-pagination';
class LoadTable extends React.Component {
constructor(props) {
super(props);
this.state = {
data: [],
pagination: [],
Search: "Search",
sort: {
column: null,
direction: 'desc',
},
}
this.clearSearch = this.clearSearch.bind(this);
this.doSearch = this.doSearch.bind(this);
this.doSort = this.doSort.bind(this);
this.runLog = this.runLog.bind(this);
this.openRunLog = this.openRunLog.bind(this);
this.onChangePage = this.onChangePage.bind(this);
}
componentDidMount() {
this.props.getJobs()
.then((res) => {
this.setState({
data: res.results.response || []
})
});
}
clearSearch() {
this.props.getJobs()
.then((res) => {
this.setState({
data: res.results.response || [], Search: "Search",
sort: {
column: null,
direction: 'desc',
}
})
});
}
doSearch(e) {
const { name, value } = e.target;
this.setState({ [name]: value });
this.props.doSearch(value)<----Initial Call
.then((res) => {
this.setState({
data: res.results.response || [],
sort: {
column: null,
direction: 'desc',
}
})
});
}
render() {
return (
use data
)}
const mapDispatchToProps = dispatch => ({
getJobs: () => dispatch(jobActions.getJobs()),
doSearch(value) {
dispatch(jobActions.doSearch(value));<----dispatch
},
});
export default connect(mapStateToProps, mapDispatchToProps)(LoadTable);
==========================================
Action being called:
function doSearch(value) {
return (dispatch) => {
dispatch({ type: jobConstants.JOB_REQUEST });
return jobService.doSearch(value)
.then(
results => {
dispatch({ type: jobConstants.JOB_SUCCESS, user });
//Ran console logs and seen the results here
return { results };
},
error => {
dispatch({ type: jobConstants.JOB_FAILURE, error });
}
);
}
}
=========================
Services
function doSearch(SearchValue) {
const requestOptions = {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json; charset=utf-8'
}),
body: JSON.stringify({SearchValue})
};
const requestPath = 'http://localhost:53986/api/jobs/postsearch';
return fetch(requestPath, requestOptions)
.then(handleResponseToJson)
.then(response => {
if (response) {
return { response };
}
}).catch(function (error) {
return Promise.reject(error);
});
}
You need an async function for your service, which returns a promise. Like this
async function doSearch(val) {
const requestOptions = {
method: 'POST',
headers: new Headers({
'Content-Type': 'application/json; charset=utf-8'
}),
body: JSON.stringify({SearchValue})
};
const requestPath = 'http://localhost:53986/api/jobs/postsearch';
const data = fetch(requestPath, requestOptions);
const jsonData = await data.json();
return jsonData;
}
Then you can call like so:
doSearch(val).then() // and so on...
This is the pattern your looking for in this case.

Put error response interceptor on redux-axios-middleware

I have a problem with https://github.com/svrcekmichal/redux-axios-middleware.
I want to set the interceptor response (error). But can't successfully set it up.
Here is my code:
function interceptorResponse({ dispatch, getState, getAction }, response) {
console.log(response);
}
export const client = axios.create({
baseURL: API_URL,
headers: {
Accept: 'application/json',
},
});
export const clientOptions = {
interceptors: {
request: [interceptorRequest],
response: [interceptorResponse],
},
};
the console.log(response) only respond if the response is 200. How can I set it to accept an error response?
I've tried set it like this
function interceptorResponse({ dispatch, getState, getAction }) {
return response => response.data, (error) => {
const meta = error.response.data.meta;
const { code, status } = meta;
console.log(meta);
};
}
but still never show anything.
Any soluion?
Here is an example usage with ES6 :
import axios from 'axios'
import axiosMiddleware from 'redux-axios-middleware'
const options = {
// not required, but use-full configuration option
returnRejectedPromiseOnError: true,
interceptors: {
request: [
({ getState, dispatch }, config) => {
// Request interception
return config
}
],
response: [
{
success: ({ dispatch }, response) => {
// Response interception
return response
},
error: ({ dispatch }, error) => {
// Response Error Interception
return Promise.reject(error)
}
}
]
}
}
export default axiosMiddleware(axios, options)
Note that the created middleware should be passed to createStore()

Resources