test mapDispatchToProps async actions - reactjs

I am trying to test my mapDispatchToProps function when an asyncronous function is dispatched. I have read Dan Abramov's suggestions on how to test mapDispatchToProps and I am trying to test my code as such.
I am getting the error...
TypeError: Cannot read property 'then' of undefined
Here is my test...
describe("mapDispatchToProps", () => {
const dispatchSpy = jest.fn();
const {signupUser} = mapDispatchToProps(dispatchSpy);
it("should dispatch signupActions.signupUser()", () => {
// mockAxios.onPost(endpoints.SIGNUP,{})
// .reply(200,'test_success');
// tried with and without mockAxios
signupUser({})
const spyLastCall = dispatchSpy.mock.calls[0][0];
expect(spyLastCall)
.toEqual(signupActions.signupUser({}));
})
})
The function that I want to test...
export const mapDispatchToProps = dispatch => {
return { signupUser: (user) => {
dispatch(signupActions.signupUser(user))
.then((response) => {
// do something on success
}, (error) => {
// do something on failure
})
}
}
I have already tested signupActions.signupUser and I know that it returns a promise. Here is the code...
export function signupUser(user) {
return (dispatch) => {
return dispatch(rest.post(SIGNUP,user))
.then((response) => {
return Promise.resolve(response);
},(error) => {
return Promise.reject(error)
}
)
}}
What am I doing wrong?
Ps: I also tried:
const dispatchSpy = jest.fn().mockImplementation( () => {
return p = new Promise((reject,resolve) => {
resolve({})
})
}
with the same result

For anyone who is interested, I ended up using mergeProps which has made my tests a lot cleaner. Now I have...
export const mapDispatchToProps = dispatch => {
return { dispatchSignupUser: (user) => {
dispatch(signupActions.signupUser(user))
}
}
export const mergeProps = (propsFromState,propsFromDispatch,ownProps) => {
return {
signupUser: (values) => {
return propsFromDispatch.dispatchSignupUser(values)
.then(() => { // do something on success },
() => { // do something on failure})
}
}
and I test them separately...
describe("signup", () => {
/// ... ownProps and propsFromState declared here
const dispatchSpy = jest.fn((x) => {});
const {
dispatchSignupUser,
} = mapDispatchToProps(dispatchSpy);
const signupUser = mergeProps(propsFromState,propsFromDispatch,ownProps);
describe("mapDispatchToProps", () => {
it("should dispatch signup user on dispatchSignupUser", () => {
const spyOn = jest.spyOn(signupActions,'signupUser');
dispatchSignupUser({test: "test"});
expect(spyOn).toHaveBeenCalledWith({test: "test"});
})
})
describe("mergeProps", () => {
it("should do something on success", () => {
propsFromDispatch.dispatchSignupUser jest.fn().mockImplementation((x) => {
return new Promise((resolve,reject) => { return resolve({})} )
});
return signupUser({}).then(() => {
expect(history.location.pathname).toEqual("/signup/thank-you")
}, (error) => {})
})
})
})
Hopefully this is helpful!

Related

How can I test useEffect with async function in Jest?

I have this function inside a helper:
export const useDAMProductImages = (imageId: string) => {
const {
app: { baseImgDomain },
} = getConfig();
const response: MutableRefObject<string[]> = useRef([]);
useEffect(() => {
const getProductImages = async (imageId: string) => {
try {
const url = new URL(FETCH_URL);
const res = await fetchJsonp(url.href, {
jsonpCallbackFunction: 'callback',
});
const jsonData = await res.json();
response.current = jsonData;
} catch (error) {
response.current = ['error'];
}
};
if (imageId) {
getProductImages(imageId);
}
}, [imageId]);
return response.current;
};
In test file:
import .....
jest.mock('fetch-jsonp', () =>
jest.fn().mockImplementation(() =>
Promise.resolve({
status: 200,
json: () => Promise.resolve({ set: { a: 'b' } }),
}),
),
);
describe('useDAMProductImages', () => {
beforeEach(() => {
jest.clearAllMocks();
cleanup();
});
it('should return empty array', async () => {
const { result: hook } = renderHook(() => useDAMProductImages('a'), {});
expect(hook.current).toMatchObject({ set: { a: 'b' } });
});
});
The problem is that hook.current is an empty array. Seems that useEffect is never called. Can someone explain to me what I'm doing wrong and how I should write the test? Thank you in advance

React JS + Axios return undefined first

I trying make an axios get from context file into function and call this from component to return data.
Context file:
const getPets = async () => {
await axios.get('http://localhost:1337/api/pets?populate=*')
.then((res) => {
return res.data
})
.catch(err => {
console.log(err)
})}
Component file:
const [pets, setPets] = useState([])
useEffect( () => {
setPets(getPets())},[])
return (console.log(pets))
The return value is undefined and i don't know why.
Can we help me please?
Tks!
Modify getPets():
const getPets = async () => {
const res = await axios.get('http://localhost:1337/api/pets? populate=*');
return res.data;
}
getPets() returns a promise
useEffect(() => {
getPets().then(res => setPets(res));
}, []);
return (
<>
{pets?.map(pet => { /* some JSX */})}
</>
);

fireEvent.click(button) causing error **Call retries were exceeded at ChildProcessWorker.initialize**

One of my unit tests is failing when I'm trying to fire a click event on a component. The component is being rendered and is enabled.
//component
import {makeEncryptedCall} from '../../foo';
const MyComponent = (props) => {
const onRedirection = async () => {
const param = {foo: 'bar'};
return await c(param)
.then((data) => {
history.push('/some-url');
});
};
return (
<>
<button
onClick={onRedirection}
data-testid='my-button'
/>
</>
)
}
// test
it('should fire redirection flow', () => {
jest.mock('../../foo', () => {
return {
makeEncryptedCall: jest.fn(() => {
const response = {
ok: true,
json: () => {
Promise.resolve({
data: 'superEncryptedStuff';
});
}
};
return Promise.resolve(response);
});
}
});
const component = screen.getByTestId('my-button');
expect(component).toBeEnabled();
fireEvent.click(component);
});
I tried finding solutions related to Call retries were exceeded posted before but they are related to setTimeouts, FakeTimers, or async-mock(which I have already implemented).
Note: The test passes when I comment out fireEvent.click. Test only fails when the event is triggered.
The issue was resolved by wrapping the fireEvent in a waitFor function.
it('should fire redirection flow', async () => {
jest.mock('../../foo', () => {
return {
makeEncryptedCall: jest.fn(() => {
const response = {
ok: true,
json: () => {
Promise.resolve({
data: 'superEncryptedStuff';
});
}
};
return Promise.resolve(response);
});
}
});
const component = screen.getByTestId('my-button');
expect(component).toBeEnabled();
await waitFor(() => fireEvent.click(component));
});

Waiting for dispatch in thunks

I need to wait until dispatch in thunks is done. After that, I have to set state of hook to true.
Here is my service:
export const loadSearchInfoAsync = (product_id: string) => {
return (dispatch: Dispatch) => {
SearchService.getSearchInfo(product_id)
.then((response) => {
if (response) {
// Wait until this dispatch is done
dispatch(searchInfoLoadSuccess(response.data));
}
})
.catch((error) => {
if (error) {
dispatch(appErrorState(error.response));
}
});
};
};
And here is state which has to be updated after that dispatch
const handleScan = (data: string | null) => {
if (!proceed && data) {
// After this dispatch make setProceed true
dispatch(loadSearchInfoAsync(data));
setProceed(true);
}
};
Maybe it will help you
const loadSearchInfoAsync = (product_id: string, onSuccess, onFailure) => {
return (dispatch: Dispatch) => {
SearchService.getSearchInfo(product_id)
.then((response) => {
if (response) {
// Wait until this dispatch is done
dispatch(searchInfoLoadSuccess(response.data));
onSuccess()
}
})
.catch((error) => {
if (error) {
dispatch(appErrorState(error.response));
onFailure()
}
});
};
};
const loadSearchInfoPromise = (product_id: string) => {
return new Promise((resolve, reject) => {
dispatch(loadSearchInfoAsync(product_id, resolve, reject))
}
}
const handleScan = async (data: string | null) => {
if (!proceed && data) {
// After this dispatch make setProceed true
await loadSearchInfoPromise(data).then(() => {
setProceed(true);
})
}
};
I think in this case you could probably just move your proceed code into an effect and wait for a response on that?
useEffect(() => {
if (data.length) { // or do whatever check here to see if it's not empty
setProceed(true);
}
}, [data])

How to test a dispatch function?

Hello I'm trying to test this function with the return of the dispatch in how many times it have been called, but really don't know how to do it correctly in order to call dispatch
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
I have these test
describe('mapDispatchToProps', () => {
test('test', () => {
const dispatch = jest.fn(() => Promise.resolve())
mapDispatchToProps(dispatch)
expect(dispatch).toHaveBeenCalledTimes(2)
})
})
Any suggestions?
Create a dispatch mock function and pass it to mapDispatchToProps.
Then call the functions defined on the result.
You can use something like toHaveBeenCalledWith to verify that the correct action was dispatched:
// Stubs for hideSidebar and settingsActions.updateArray
const hideSidebar = { type: 'hide-side-bar' };
const settingsActions = { updateArray: u => ({ type: 'update-unit', payload: u })};
export const mapDispatchToProps = (dispatch) => {
return {
hideSidebar: () => {
dispatch(hideSidebar)
},
updateUnit: (unitObject) => {
dispatch(settingsActions.updateArray(unitObject))
}
}
}
test('mapDispatchToProps', () => {
const dispatch = jest.fn();
const map = mapDispatchToProps(dispatch);
map.hideSidebar();
expect(dispatch).toHaveBeenCalledWith({ type: 'hide-side-bar' }); // Success!
map.updateUnit({ theKey: 'theVal' });
expect(dispatch).toHaveBeenCalledWith({ type: 'update-unit', payload: { theKey: 'theVal' } }); // Success!
})

Resources