Testing fetch using Jest- React Native - reactjs

I have a common api class that i use for handling api calls in React Native. It will make the call and get the json/ error and return it. See the code below.
// General api to acces data from web
import ApiConstants from './ApiConstants';
export default function api(path,params,method, sssid){
let options;
options = Object.assign({headers: {
'Accept': 'application/json',
'Content-Type': 'application/json'
}},{ method: method }, params ? { body: JSON.stringify(params) } : null );
return fetch(ApiConstants.BASE_URL+path, options).then( resp => {
let json = resp.json();
if (resp.ok) {
return json;
}
return json.then(err => {
throw err;
}).then( json => json );
});
}
But when i write the jest test to mock the api as folllows in tests folder.
test('Should login',() => {
global.fetch = jest.fn(() => new Promise((resolve) => {
resolve( { status: 201, json: () => (mock_data_login) });
}));
return Api(ApiConstants.LOGIN,{'un':'test1','pwd':'1234'},'post', null).then((data1)=>{
expect(data1).toBeDefined();
expect(data1.success).toEqual(true);
expect(data1.message).toEqual('Login Success');
});
});
it fails with:
TypeError: json.then is not a function
When I change the fetch return to this, the test passes:
return fetch(ApiConstants.BASE_URL+path, options).then( resp => {
let json = resp.json();
return json
});
}
Why is this type error error popping up? I can't change the API module, because that will my redux saga code to change. What should I do?

In your code, json is just an Object and not a Promise, so then is undefined. That's the complain you are getting because you are trying to use undefined as a function. The problem is not in the test but in your code that ha san error. Try the following instead.
return fetch(ApiConstants.BASE_URL+path, options)
.then(resp => resp.json())
.then( json => json)
.catch((error) => error);
});

Edit: oh, just read you can't make changes to the component where the error occurs?
Try converting your fetch like this:
return fetch(ApiConstants.BASE_URL+path, options)
.then(resp => {
let json = resp.json();
if (resp.ok) {
return json;
} else {
throw Error(resp.error) // assuming you have some kind of error from endpoint?
}
})
.then(/*handle your ok response*/)
.catch(/*handle your error response*/);

I faced the same issue, The problem is that you are mocking only response.json as function but it should be a Promise, Like this,
global.fetch = jest.fn(() => new Promise((resolve) => {
resolve( { status: 201, json: () => {
return Promise.resolve(mock_data_login);
}
});
}));
This will return a Promise for you json function.
Hope this fix your problem.

Related

React class component issue in order of execution

I have the following code in my React class component.
For some reason, I am observing that, inside componentDidMount, despite having the keyword await before the call to this.getKeyForNextRequest(), the execution is jumping to the next call, this.loadGrids().
Am I doing something wrong here?
async componentDidMount() {
await this.getKeyForNextRequest();
await this.loadGrids();
}
getKeyForNextRequest = async () => {
const dataRequester = new DataRequester({
dataSource: `${URL}`,
requestType: "POST",
params: {
},
successCallback: response => {
console.log(response);
}
});
dataRequester.requestData();
}
loadGrids = async () => {
await this.loadGrid1ColumnDefs();
this.loadGrid1Data();
await this.loadGrid2ColumnDefs();
this.loadGrid2Data();
}
You can try using the Promise constructor:
getKeyForNextRequest = () => {
return new Promise((resolve, reject) => {
const dataRequester = new DataRequester({
dataSource: `${URL}`,
requestType: "POST",
params: {},
successCallback: response => {
console.log(response);
resolve(response);
}
});
});
}
This ensures you're waiting for a relevant promise, one that resolves only upon successCallback completing, rather than one that resolves instantly to undefined as you have it currently.
This is called "promisifying" the callback.
If DataRequester offers a promise-based mode, use that instead of promisifying the callback.

nuxt generate payload undefined

I have a Nuxt project, in nuxt.config.js file, I have a function like this:
generate: {
async routes() {
function postRoutes() {
return axios
.post('https://my-server.com/api/posts')
.then((r) => r.data.map((post) => {
// I log post data here, it exist
console.log(post)
return {
route: `post/${post.id}`,
payload: 'post'
}
}))
}
const response = await axios
.all([postRoutes()])
.then(function (results) {
const merged = [].concat(...results)
return merged
})
return response
}
},
Then, in pages/post/_slug.vue, in asyncData, when receiving payload object, it returns undefined
async asyncData({ params, error, payload }) { {
console.log(payoad) // return undefined
}
I don't know whether I pass the payload the wrong way or something wrong with Nuxt payload, please help !! thank you in advance

React query mutation: getting the response from the server with onError callback when the API call fails

I am working on a React JS project. In my project, I am using React query, https://react-query.tanstack.com/docs/guides/mutations. I am using mutation to make the post request to the server. But I am trying the get the response returns from the server when the API call fails with the onError call back.
This is my code.
let [ createItem ] = useMutation(payload => createItem(payload), {
onSuccess: (response) => {
},
onError: (error) => {
// here I am trying to get the response. In axios, we can do something like error.data.server_error_code
},
onMutate: () => {
}
})
As you can see in the comment, I am trying to read a field returned from the server within the onError callback. How can I do that?
let [ createItem ] = useMutation(payload => createItem(payload), {
onSuccess: (response) => {
},
onError: (error) => {
console.log(error.response.data);
console.log(error.response.status);
},
onMutate: () => {
}
})
It's not entirely clear when just doing console.log(error) inside onError, but error.response should be available.
It should work as it is. Make sure that your HTTP client (probably, Axios) is configured to throw an error. For example:
import axios from 'axios'
import { useMutation } from 'react-query'
import { BASE_URL } from 'constants/api'
const client = axios.create({
baseURL: BASE_URL,
})
const request = (options) => {
const onSuccess = (response) => response
const onError = (error) => {
// Throwing an error here
throw error
}
return client(options).then(onSuccess).catch(onError)
}
const { mutate } = useMutation(
async (data) =>
await request({
url: '/someUrl',
method: 'post',
data
}),
{ onError: (e) => console.log(e) }
)
And of course, it's better to store your Axios settings within a separate file, and then just import the 'request' variable where mutations are using.
If you are using fetch, you have to know that fetch does not throw any error unless is a network problem (as read here)
My solution was just to change to axios (which throws error when 400 or 500), but if you still need to use fetch, you need to find a way to make it throw errors instead.
I think the issue with NOT having an error.response in the callback depends on how the API is failing. If you look at the react-query documentation it shows that most HTTP libs like axios will throw if there is a non 2xx response. However it's up to the underlying API function how it handles that.
For example axios https://axios-http.com/docs/handling_errors will return the response object if there is a response from the server. They will return the request if the call has timed out and return just a message if the previous two don't fit the error
axios.get('/user/12345')
.catch(function (error) {
if (error.response) {
// The request was made and the server responded with a status code
// that falls out of the range of 2xx
console.log(error.response.data);
console.log(error.response.status);
console.log(error.response.headers);
} else if (error.request) {
// The request was made but no response was received
// `error.request` is an instance of XMLHttpRequest in the browser and an instance of
// http.ClientRequest in node.js
console.log(error.request);
} else {
// Something happened in setting up the request that triggered an Error
console.log('Error', error.message);
}
console.log(error.config);
});
However, if you're using the Fetch API you have handle this yourself. Taken straight from react-query's docs: https://react-query.tanstack.com/guides/query-functions#usage-with-fetch-and-other-clients-that-do-not-throw-by-default
useQuery(['todos', todoId], async () => {
const response = await fetch('/todos/' + todoId)
if (!response.ok) {
throw new Error('Network response was not ok')
}
return response.json()
})

Dispatching an action from a service in Redux

I have a logging function which logs errors. When an Ajax Request fails with a non JSON data type, the log method should log it, however, we are getting the mutated error as the attached screenshot shows. I am trying to call this log action within a service.
Code
...
import {log} from '../actions/LoggingActions';
...
export default function request(url, opts, dispatch, type = 'application/x-www-form-urlencoded') {
...
return new Promise((resolve, reject) => {
$.ajax(args).then((data) => {
dispatch(httpEndRequest([url, opts, dispatch]));
resolve(data);
}).fail((jqXHR, textStatus, errorThrown) => {
const error = (jqXHR && jqXHR.responseJSON) ?
jqXHR.responseJSON.message :
'Error Making Request';
dispatch(httpFailRequest([url, opts, dispatch], error));
try {
reject(JSON.parse(jqXHR.responseText));
} catch (e) {
console.log(jqXHR.responseText, jqXHR, error);
reject(error);
dispatch(log('Received data is not in JSON format', {requestUrl: url}, {result: e, response: jqXHR, status: textStatus, error: errorThrown}, 'error'));
}
});
});
}
Instead of using jQuery with React, Use axios or fetch (Promise based HTTP clients). I personally prefer axios.
To use axios, do
npm install axios --save. Then
import axios from 'axios';
return new Promise((resolve, reject) => {
axios.get(url, {
params: params
})
.then((response) => {
resolve(response.data);
})
.catch((error) => {
// error.response.status
dispatch(log(error));
reject(error);
});
});

How to get results into called js from service calls js in react native ?

Im new to react native, and to make service calls Im creating a different file say 'NetworkCalls.js', the file is as follows :
var NetworkCalls = {
callUsingGETRequest: function(URL){
fetch(URL, {method: "GET"})
.then((response) => response.json())
.then((responseData) => {
return responseData.ip;
})
.done();
}
}
Now Im calling this function from index.ios.js file as
NetworkCalls.callUsingGETRequest(('http://ip.jsontest.com/'),(response) => {
console.log(response);
})
}
importing is done , and even im receiving response, but the result never came back into this function block and it never gets printed.
This may be noob question, but where Im working wrong ? Any suggestions ?
You can return Promise from callUsingGETRequest
var NetworkCalls = {
callUsingGETRequest: function (URL) {
return fetch(URL, { method: "GET" })
.then(response => response.json())
.then(responseData => responseData.ip)
}
}
and then use it, like this
NetworkCalls
.callUsingGETRequest('http://ip.jsontest.com/')
.then(ip => {
console.log(ip);
});

Resources