How do I mock a promise in reactjs? - reactjs

I want to write a test which mocks a promise in reactjs
I just need a mocked implementation of getHeaders() to return a string
export const loadAllProjects = () => {
return (dispatch) => {
getHeaders()
.then(headers => {
...do stuff
})
}
}
to clarify my original function was...
export const loadAllProjects = () => {
return (dispatch) => {
...do stuff
}
}
...and my test was...
it('should create SET_ALL_PROJECTS action when fetching projects', () => {
fetchMock
.getOnce('http://test.projects.api/api/projects',
{
body: [{name: "x"}],
headers: { 'content-type': 'application/json' }
}).spy()
const expectedActions = [
{ type: "SET_ALL_PROJECTS", json: [{name:"x"}] },
]
checkAsyncActionsWereDispatched(expectedActions, actions.loadAllProjects)
});
I want the test to work with the mocked header

const getHeaders = () => {
return new Promise((resolve, reject) => {
resolve("some string");
});
};
a = await getHeaders(); //some string

Use Promise.resolve
return Promise.resolve("your headers here");

You can use jest to mock a promise for testing
Example for the eventual completion:
const mockPostSpy = jest
.spyOn(axios, 'post')
.mockImplementation(() => {
return new Promise((resolve) => {
return resolve({
data: {},
});
});
});
Example for the operation failed:
const mockPostSpy = jest
.spyOn(axios, 'post')
.mockImplementation(() => {
return new Promise((resolve) => {
return reject({});
});
});
Good luck to you ^^

Related

Unable to mock CognitoIdentityServiceProvider functions from aws-sdk in jest

Aws.config.js
import AWS from 'aws-sdk';
AWS.config.update({
region: process.env.REACT_APP_AWS_REGION,
credentials: new AWS.CognitoIdentityCredentials({
IdentityPoolId: process.env.REACT_APP_IDENTITY_POOL_ID
})
});
const AwsCognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider({ apiVersion: '2016-04-18' });
export default AwsCognitoIdentityServiceProvider;
I have a function in ListUser.js
import AwsCognitoIdentityServiceProvider from 'components/aws/AwsConfig';
const userList = () =>{
var params = {
UserPoolId: process.env.REACT_APP_USERPOOL_ID,
AttributesToGet: null,
Filter:""
};
AwsCognitoIdentityServiceProvider.listUsers(params,function (err, data) {
if(data) {
//fetching data here successfully
} else {
console.log("error",err);
}
})
}
My test file ListUser.test.js
const mockListUsers = jest.fn((params) => {
return {
promise() {
return Promise.resolve('mock response');
}
};
});
jest.mock('aws-sdk', () => {
return {
CognitoIdentityServiceProvider: jest.fn(() => ({
listUsers: mockListUsers
})),
config: {
update: jest.fn()
}
};
});
describe('ListUser', () => {
test('renders ListUser component', () => {
act(() => {
render(<ListUser />);
});
});
});
I am not able to mock this function and return response I am getting error below:
TypeError: _AwsConfig.default.listUsers is not a function
90 | Filter:""
91 | };
> 92 | AwsCognitoIdentityServiceProvider.listUsers(params,function (err, data) {
I have also tried with keeping aws.sdk.js file inside mocks folder but no luck
_mocks_/aws.sdk.js
class AWS {
CognitoIdentityServiceProvider = class {
listUsers = jest.fn(() =>{
return { promise: ()=> Promise.resolve({mockresponse})}
});
};
}
module.exports = AWS;
I need to mock listUsers function but not able to do so. I have followed so many links but no luck :(
This one works for me
const AWS = require('aws-sdk');
jest.mock('aws-sdk');
AWS.CognitoIdentityServiceProvider.prototype.listUsers = jest.fn().mockReturnValue({
promise: jest.fn().mockResolvedValue({})
});
I have tried this way and worked for me.
jest.mock("aws-sdk", () => {
const cognito = { listUsers: jest.fn() };
return {
CognitoIdentityServiceProvider: jest.fn(() => cognito),
config: {
update: jest.fn(),
},
};
});
const mCognitoIdentityServiceProvider = new AWS.CognitoIdentityServiceProvider();
mCognitoIdentityServiceProvider.listUsers.mockImplementationOnce(() => {
return {
promise() {
return Promise.resolve('your mock data');
},
};
});

How to test custom hooks with event listener inside useEffect?

I'm using react native and jest to create my tests. I'm facing problems to test an event listener that listens to url events from expo-linking. This event listenner is inside an useEffect hook.
Below is the code from my custom hook with my useEffect and an event listener inside:
useEffect(() => {
isMounted.current = true;
Linking.addEventListener('url', async () => {
try {
if (!navigation.isFocused() || !isMounted.current) return;
setIsLoading(true);
const response = await api.get('sessions/auth/success');
if (!response.data) return;
console.log('aqui');
const { notRegisteredUser, token } = response.data;
api.defaults.headers.authorization = `Bearer ${token}`;
if (notRegisteredUser && token) {
setIsLoading(false);
navigation.navigate('BirthDateScreen');
dispatch(
updateUser({
...notRegisteredUser,
}),
);
}
} catch (err) {
Alert.alert('Error', `${translate('loginRegisterError')}: `, err);
}
});
return () => {
isMounted.current = false;
};
}, [dispatch, navigation]);
In my test file I have the following mocks:
jest.mock('expo-linking', () => {
return {
addEventListener: (event: string, callback: () => void) => callback(),
};
});
jest.mock('#react-navigation/native', () => {
return {
useNavigation: () => ({
isFocused: mockedNavigationFocus,
navigate: mockedNavigation,
}),
};
});
jest.mock('react-redux', () => {
return {
useDispatch: jest.fn(),
};
});
jest.mock('../../../store/modules/user/actions', () => {
return {
updateUser: jest.fn(),
};
});
jest.mock('i18n-js', () => {
return {
locale: 'en',
t: () => 'any key',
};
});
Finally this is how my test looks in my first try:
it('should pass the test', async done => {
mockedNavigationFocus.mockImplementation(() => true);
apiMock.onGet('sessions/auth/success').reply(200, {
notRegisteredUser: { name: 'Logan' },
token: '123',
});
render(<LoginScreen />);
await waitFor(() =>
expect(mockedNavigation).toHaveBeenCalledWith('BirthDateScreen'),
);
done();
});
In my second try this is how my test looked (I used renderHooks from #testing-library/react-hooks):
it('should pass the test', async () => {
mockedNavigationFocus.mockImplementation(() => true);
apiMock.onGet('sessions/auth/success').reply(200, {
notRegisteredUser: { name: 'Logan' },
token: '123',
});
const { result, waitForValueToChange } = renderHook(() => useLoginButton());
const { isLoading } = result.current;
await waitForValueToChange(() => isLoading);
await waitForValueToChange(() => isLoading);
expect(mockedNavigation).toHaveBeenCalledWith('BirthDateScreen');
});
With both tests I get the following error:
test error
Another error I get is that my callback function inside useEffect runs many times before it stops and this does not happen when I am not testing.
Does anyone knows how can I write this test?

Calling axios request one after the other?

I have tow functions in my ReactJs application called,
AuthService.addUser(newUser);
AuthService.userCategories(usercategories);
I want to run these two functions separately, which means the Axios request of the second function should be called after the Axios request of the first function when clicked the submit button. How do I approach the solution? Thanks in advance.
I tried in this way. Is this correct?
const handleSubmit = (e) => {
e.preventDefault();
AuthService.addUser(newUser);
AuthService.userCategories(usercategories);
};
Here are my two functions
addUser: (user) => {
//console.log(post);
axios
.post(CONSTANTS.HOSTNAME + "/api/users/register", user)
.then((res) => {
//save to local storage
const { token } = res.data;
localStorage.setItem("jwtToken", token);
isAuthenticated.next(true);
setAuthToken(token);
Swal.fire({
icon: "success",
title: "Signup Successful!",
showConfirmButton: false,
timer: 1500,
}).then(() => {
window.location.href = "/";
//decode token to get user data
const decoded = jwt_decode(token);
currentUser.next(decoded);
console.log(decoded);
});
})
.catch((err) => {
console.log(err.response.data);
Swal.fire({
icon: "error",
title: "Oops...",
text: err.response.data,
});
// alert(JSON.stringify(err.response.data));
});
},
userCategories: (userCategories) => {
axios
.post(CONSTANTS.HOSTNAME + "/api/users/usercategories", userCategories)
.then((res) => {
console.log(res.data);
});
},
just use promise if function return promise:
const handleSubmit = async (e) => {
e.preventDefault();
await AuthService.addUser();
await AuthService.userCategories();
};
or make promise from function and run async
function one() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('resolve one')
return resolve("i am after five seconds")
},
2000);
});
}
function two() {
return new Promise((resolve, reject) => {
console.log('resolve two')
return resolve("i am after three seconds")
});
}
const handleSubmit = async () => {
console.log('run handleSubmit')
await one();
await two();
}
handleSubmit()

I'm getting following error in Jest ReferenceError: Response is not defined

First I had a typescript issue that my mocked data doesn't match to Response type. Then I tried to create mock data with Response constructor and got that error.
I have the following code
const HttpRequest = async function (
url: string,
options: RequestInit,
): Promise<Response> {
try {
return await fetch(url, options);
} catch (error) {
return null;
}
};
export default HttpRequest;
And following test:
const res = new Response();
window.fetch = jest.fn(
() => new Promise((resolve) => {
return resolve(res);
})
);
describe('HttpService', () => {
it('fetchWithFeedback', async () => {
const data = await HttpRequest('/api', { method: 'GET' });
expect(data).toEqual(res);
});
});
Change
const changeField = (field, id, value) => {
const newPropertyData = { ...propertyData };
if (newPropertyData.id === id) {
newPropertyData.field = value;
}
};
to
const changeField = (field, id, value) => {
if (propertyData.id === id) {
setPropertyData({ ...propertyData, [field]: value })
}
};
You're not updating your state in your onChange, so it's never able to update the value, which is still "".

How to mock a fetch call that is within an arrow function?

I'm trying to test the invocation of a function that deletes specific data saved in a database in React. The problem is I want to only mock the fetch call and have everything else run as usual because right now whenever tests are run the data gets deleted in the database.
Here is my code for the delete function:
deleteEvent = async () => {
try {
await fetch(
"api url",
{
method: "DELETE",
}
)
.then((res) => res.json())
.then(
(result) => {
console.log(result);
},
(error) => {
console.log(error);
}
);
} catch (error) {
console.error(error);
}
this.props.history.push("/eventList");
};
And here is my test code:
test("deleteEvent function works", (done) => {
const mockSuccessResponse = {};
const mockJsonPromise = Promise.resolve(mockSuccessResponse);
const mockFetchPromise = Promise.resolve({
json: () => mockJsonPromise,
});
jest.spyOn(global, "fetch").mockImplementation(() => mockFetchPromise);
const historyMock = { push: jest.fn() };
const wrapper = shallow(<EventState history={historyMock} />);
wrapper.instance().deleteEvent();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
global.fetch.mockClear();
done();
});
I get number times called: 0 for the expect(global.fetch).toHaveBeenCalledTimes(1);
and a Received: undefined for the expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
Any help would be great
Instead of using spyOn(global, fetch), try this:
global.fetch = jest.fn().mockImplementation(() => mockFetchPromise);
const historyMock = { push: jest.fn() };
const wrapper = shallow(<EventState history={historyMock} />);
wrapper.instance().deleteEvent();
expect(global.fetch).toHaveBeenCalledTimes(1);
expect(historyMock.push.mock.calls[0]).toEqual(["/eventList"]);
global.fetch.mockClear();
done();
});

Resources