Cannot read property 'createEvent' of null - jestjs

I'm getting the error Cannot read property 'createEvent' of null when I use setTimeout in the code to wait for a while before navigating. The timeout is needed for a reason. The test is passing when I remove the timeout but need to make it work with the timeout really.
Here is the code I have.
useEffect(() => {
(async () => {
if (hasInternetConnection) {
...
} else {
setTimeout(() => {
navigation.dispatch(state => {
const routes = state.routes.filter(route => route.name === splashScreen)
routes.push({ name: homeNavigation, params: { screen: offlineDashboardScreen } })
return CommonActions.reset({
...state,
routes,
index: routes.length - 1
})
})
}, 1000)
}
})()
}, [hasInternetConnection, conductor])
it('navigates OfflineDashboardScreen if there is no internet connection', async () => {
useNetInfo.mockReturnValueOnce({
type: 'test', // not 'unknown'
isInternetReachable: false,
details: {},
isConnected: false
})
const component = (
<NavigationContainer>
<AppProviders>
<ThemeProvider theme={mockTheme}>
<AppNavigation/>
</ThemeProvider>
</AppProviders>
</NavigationContainer>
)
const rendered = render(component)
expect(await rendered.findByText(strings.om1)).toBeTruthy()
})
I've added jest.useFakeTimers() to setup files.

Related

Error with sending an arrays and an object in a GET API and getting them in the reducer

I'm working on a project where I need to send an arrays and an object from the backend (nodejs) through a GET api to the frontend (reactjs) and have both of those be accessible in my reducer. I have never done this, and I'm not sure if I'm going about it the right way. I am currently getting an error saying that totalPages from this line: export const orderMineListReducer = (state = {orders:[], totalPages}, action) => { is not defined. I would really appreciate any help or advice on how to go about sending a GET api with an arrays and an object and receiving an arrays and an object in the reducer. Thank you!
Below, I have included what I have tried to do so far:
Backend:
orderRouter.js
orderRouter.get(
'/mine',
isAuth,
expressAsyncHandler(async (req, res) => {
const page = req.query.page || 1;
const perPage = 20
const orders = await Order.find({ user: req.user._id }).skip(page * perPage).limit(perPage);
const total = await Order.countDocuments();
const totalPages = Math.ceil(total / perPage).toString();
res.status(200).send({
data:
[orders],
totalPages,
});
}),
);
Frontend
orderReducer.js
export const orderMineListReducer = (state = {orders:[], totalPages}, action) => {
switch (action.type) {
case ORDER_MINE_LIST_REQUEST:
return { ...state, loading: true };
case ORDER_MINE_LIST_SUCCESS:
return { ...state, loading: false, orders: action.payload.orders, totalPages: action.payload.totalPages,};
case ORDER_MINE_LIST_FAIL:
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
orderActions.js
export const listOrderMine = (page) => async (dispatch, getState) => {
dispatch({ type: ORDER_MINE_LIST_REQUEST });
const {
userSignin: { userInfo },
} = getState();
try {
const { data } = await Axios.get(`${BASE_URL}/api/orders/mine?page=${page}`, {
headers: {
Authorization: `Bearer ${userInfo.token}`,
},
});
dispatch({ type: ORDER_MINE_LIST_SUCCESS, payload: data });
} catch (error) {
const message = error.response && error.response.data.message ? error.response.data.message : error.message;
dispatch({ type: ORDER_MINE_LIST_FAIL, payload: message });
}
};
I've also tried just having
res.status(200).send({
orders,
totalPages,
});
instead of res.status(200).send({data: { orders, totalPages,}});
with my reducer like so:
export const orderMineListReducer = (state = { data: {} }, action) => {
switch (action.type) {
case ORDER_MINE_LIST_REQUEST:
return { ...state, loading: true };
case ORDER_MINE_LIST_SUCCESS:
return { ...state, loading: false, data: action.payload,};
case ORDER_MINE_LIST_FAIL:
return { ...state, loading: false, error: action.payload };
default:
return state;
}
};
however in my OrderHistoryScreen.js where I have
const orderMineList = useSelector((state) => state.orderMineList);
const { loading, data, error,} = orderMineList;
const dispatch = useDispatch();
useEffect(() => { dispatch(listOrderMine());
}, [dispatch]);
I am getting undefined for console.log(data.orders) and empty {} for console.log(data).
Your response has this scheme:
{
data: {
orders,
totalPages
}
}
Axios.get will resolve to an object with this schema:
{
data: {
data: {
orders,
totalPages
}
},
status: 200,
statusText: 'OK',
...
}
So you need to change the destructuring or dispatch data.data like this:
dispatch({ type: ORDER_MINE_LIST_SUCCESS, payload: data.data });
Check the Axios documentation on the response schema: https://axios-http.com/docs/res_schema

Jest - resetting value in module and reloading import

I'm writing a test in jest for a module which uses a constant from a different module.
I want to set a different value for it for every test case, but I don't seem to be able to do so.
The test file:
import { Request, Response } from 'express';
const activityConsumer = require('../../src/utils/activity.consumer');
const mockRequest = {
params: {
activityArn: 'activityArn'
}
} as Request;
const mockedJsonFunction = jest.fn();
const mockResponse: any = {
json: jest.fn(),
status: jest.fn().mockReturnValue({ json: mockedJsonFunction }),
} as Response;
let stopConsumerMock;
describe('consumer handler', () => {
beforeAll(() => {
stopConsumerMock = activityConsumer.stopConsumer = jest.fn().mockReturnValue(1);
});
beforeEach(() => {
jest.resetModules();
});
afterEach(() => {
stopConsumerMock.mockClear();
mockResponse.json.mockClear();
});
describe('stopConsumingHandler', () => {
it('Should return success true and not call stopConsumer when no consumer exists', () => {
activityConsumer.consumer = undefined;
const { stopConsumingHandler } = require ('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(0);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
it('Should return success true and call stopConsumer when consumer exists', () => {
activityConsumer.consumer = true;
const { stopConsumingHandler } = require ('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(1);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
});
});
I want to replace the value of activityConsumer.consumer and then reload the consumer.handlers module but the re-assignment and reload does not seem to have any effect.
Please advise on how can I write this test properly.
Try this way, using jest.mock to modify import value of activityConsumer
import { Request, Response } from 'express';
// const activityConsumer = require('../../src/utils/activity.consumer');
const mockRequest = {
params: {
activityArn: 'activityArn'
}
} as Request;
const mockedJsonFunction = jest.fn();
const mockResponse: any = {
json: jest.fn(),
status: jest.fn().mockReturnValue({ json: mockedJsonFunction }),
} as Response;
let stopConsumerMock;
describe('consumer handler', () => {
beforeAll(() => {
// stopConsumerMock = activityConsumer.stopConsumer = jest.fn().mockReturnValue(1);
stopConsumerMock = jest.fn().mockReturnValue(1);
});
beforeEach(() => {
jest.resetModules(); // important line
});
afterEach(() => {
stopConsumerMock.mockClear();
mockResponse.json.mockClear();
});
describe('stopConsumingHandler', () => {
it('Should return success true and not call stopConsumer when no consumer exists', () => {
// activityConsumer.consumer = undefined;
// mock by this way
jest.mock('../../src/utils/activity.consumer', () => ({
consumer: undefined,
stopConsumer: stopConsumerMock,
}));
const { stopConsumingHandler } = require('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(0);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
it('Should return success true and call stopConsumer when consumer exists', () => {
// activityConsumer.consumer = true;
// mock by this way
jest.mock('../../src/utils/activity.consumer', () => ({
consumer: true, // mock value for consumer
stopConsumer: stopConsumerMock,
}));
const { stopConsumingHandler } = require('../../src/handlers/consumer.handlers');
stopConsumingHandler(mockRequest, mockResponse);
expect(stopConsumerMock.mock.calls.length).toEqual(1);
expect(mockResponse.json.mock.calls.length).toEqual(1);
expect(mockResponse.json).toHaveBeenCalledWith({ success: true });
});
});
});

React | Why the defaultValue of the input isn't being updated?

I have a stateful component that calls a CEP promise, to fetch data from post offices. This data is fetched when the Zip input is fulfilled with 9 chars - 8 number and an '-' - and return an object with desired information.
Heres the function:
const handleZipCode = useCallback(
async ({ target }: ChangeEvent<HTMLInputElement>) => {
const { value } = target;
try {
if (value.length === 9 && isDigit(value[8])) {
const zip = await cep(value);
if (zip?.city) {
setZipData(zip);
} else
addressFormRef.current?.setErrors({
user_zip_code: 'CEP not found',
});
}
} catch (e) {
addressFormRef.current?.setErrors({
user_zip_code: e.message ?? 'CEP not found',
});
}
},
[isDigit]
);
Then, on the return I have some fields, example:
<fieldset>
<legend>Address</legend>
<Input
mask=''
name='user_address'
placeholder='Rua um dois trĂªs'
defaultValue={zipData.street}
/>
</fieldset>
Here's the Input component:
const Input: React.FC<InputProps> = ({ name, ...rest }) => {
const { fieldName, defaultValue, registerField, error } = useField(name);
const inputRef = useRef(null);
useEffect(() => {
registerField({
name: fieldName,
ref: inputRef.current,
path: 'value',
// eslint-disable-next-line
setValue(ref: any, value: string) {
ref.setInputValue(value);
},
// eslint-disable-next-line
clearValue(ref: any) {
ref.setInputValue('');
},
});
}, [fieldName, registerField]);
return (
<Container>
<ReactInputMask ref={inputRef} defaultValue={defaultValue} {...rest} />
{error && <Error>{error}</Error>}
</Container>
);
};
However the zipData seems to update, but the default value is not fulfilled. What I'm doing wrong?
The default value will not change, as unform is an uncontrolled form library, the defaultValue will be set on the first render of the page and then will not change anymore.
To fix your problem you can do something like:
// on your handleZipCode function
formRef.current.setData({
zipData: {
street: zipResult.street,
},
});

Rest API call from REACT APP always return isLoaded: false

import React from 'react';
import './App.css';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
error: null,
items: [],
isLoaded: false
};
}
callAPI() {
fetch("http://localhost:4000/api/process/4570")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoaded: true,
items: result.items
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
this.setState({
isLoaded: false,
error
});
}
)
}
componetDidMount() {
this.callAPI();
}
render() {
var { error, isLoaded, items } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
}
else {
return (
<div className="App">
<ul>
{items.map(item => (
<li key={item.id} >
Id: {item.id} | Name: {item.name}
</li>
))}
</ul>
</div>
);
}
}
}
export default App;
console: no error.
react-developer-tool: returns
state=
{
"error": null,
"items": [],
"isLoaded": false
}
I am very new to REACT and APIs. Please guide me through, what mistake i have done here. I am unable to get the API output.
I am always getting "Loading"
The API does return the json:
{"id":"4570","name":"AR_RESUME_CNC_ROUTING"}
you need to set isLoading to true in your callApi function before every thing else, after that you need to set it false when you get the result or when you catch some error.
callAPI() {
this.setState({
isLoading: true,
});
fetch("http://localhost:4000/api/process/4570")
.then(res => res.json())
.then(
(result) => {
this.setState({
isLoading: false,
items: result.items
});
},
(error) => {
this.setState({
isLoading: false,
error
});
}
)
}
a bit explanation about the code, you always want to show loading when your api call begins, thats why we set isLoading in the beginning of the function, then when we get the result (success or failure, does not matter) the loading state should change to false, because we have at least a result!
also as an extra point you can use try {...} catch {...} finally {...} to have better code style like below:
async callApi() {
try {
this.setState({ isLoading: true })
const result = await fetch("http://localhost:4000/api/process/4570")
const data = await result.json()
this.setState({
items: result.items
});
} catch (e) {
this.setState({ error: e })
} finally {
this.setState({ isLoading: false })
}
}
It looks to me it is some sort of scope issue. You are doing:
this.setState({
isLoaded: true,
items: result.items
});
but you are calling it within the function callback of the fetch promise. So, this is probably referencing the internal Promise object.
I recommend you try this:
callAPI() {
const self = this;
fetch("http://localhost:4000/api/process/4570")
.then(res => res.json())
.then(
(result) => {
self.setState({
isLoaded: true,
items: result.items
});
},
// Note: it's important to handle errors here
// instead of a catch() block so that we don't swallow
// exceptions from actual bugs in components.
(error) => {
self.setState({
isLoaded: false,
error
});
}
)
}
Re-reference the this, to a new variable (in this case I used self) and try your code there.
Thank you for your response, however the solution which worked for me is as below:
class GetOnlinePosts extends Component {
constructor(props) {
super(props);
this.state = {
error: null,
loading: true,
posts: null,
};
}
async componentDidMount() {
console.log("inside external_json");
const url = "http://localhost:4000/api/process/4570";
const response = await fetch(url);
const data = await response.json();
this.setState({ posts: data, loading: false })
console.log(data);
}
...........

Unit test for customPollingHook which uses apollo useLazyQuery

So I have written a custom polling hook which uses useContext and useLazyQuery hooks. I want to write a unit test for this, which should cover its returned values state and side effect.
So far I have managed to do this much but I'm not so sure how to proceed ahead. Any tips?
export const useUploadActivityPolling = (
teId: TeIdType
): UploadActivityPollingResult => {
const { dispatch, uploadActivityId }: StoreContextType = useAppContext();
const [fetchActivityStatus, { error: UploadActivityError, data: UploadActivityData, stopPolling }] = useLazyQuery(
GET_UPLOAD_ACTIVITY,
{
pollInterval: 3000,
fetchPolicy: 'network-only',
variables: { teId, activityId: uploadActivityId },
}
);
useEffect(() => {
if (UploadActivityData) {
setUploadActivityId(
UploadActivityData.getUploadActivityStatus.activity_id,
dispatch
);
updateActivityStateAction(UploadActivityData.getExcelUploadActivityStatus.status, dispatch);
}
}, [UploadActivityData]);
return { fetchActivityStatus, stopPolling, UploadActivityError };
};
import React from 'react';
import { mount } from 'enzyme';
const TestCustomHook = ({ callback }) => {
callback();
return null;
};
export const testCustomHook = callback => {
mount(<TestCustomHook callback={callback} />);
};
describe('useUploadActivityPolling', () => {
let pollingResult;
const teId = 'some id';
beforeEach(() => {
testCustomHook(() => {
pollingResult = useUploadActivityPolling(teId);
});
});
test('should have an fetchActivityStatus function', () => {
expect(pollingResult.fetchActivityStatus).toBeInstanceOf(Function);
});
});

Resources