How to test assignment lines in saga function using jest - reactjs

I have a function which consists of saga effect calls i want to coverage the full function with out missing any line of code how can i test if condition here
export function* fetchFromSource() {
const dataTypeName = mapDataTypes(dataType);
Iif (dataTypeName.length === 0) {
return;
}
yield put(sourceActions.onRdsmSourcePlantRequestStarted());
}
how i test the dataTypeName.length using jest
this is my mapDataTypes unit test method
it('should return appropriate dataType when mapDataTypes triggered', () => {
const expected = 'Items';
const actionDataType = action.payload.name;
expect(expected).toEqual(saga.mapDataTypes(actionDataType));
});
this is my next put test method
it('should return onRdsmSourcePlantRequestStarted action', () => {
const expectedAction = {
type: 'rdsm/sourceView/ON_RDSM_SOURCE_PLANT_REQUEST_STARTED',
};
const dataTypeName = '';
const genNext = generator.next(dataTypeName);
expect(genNext.value).toEqual(put(expectedAction));
});
here test is passing to verify the put call without entering to if block.
my question is how to verify the if block

Probably you should change the implementation of your saga, and make mapDataTypes call declarative:
const dataTypeName = yield call(mapDataTypes, dataType).
Then you can test it like this:
it('should end saga when there is no dataTypeName', () => {
const dataTypeName = '';
expect(generator.next().value).toEqual(call(mapDataTypes, dataType));
expect(generator.next(dataTypeName).done).toBeTruthy();
});
it('should return onRdsmSourcePlantRequestStarted action', () => {
const expectedAction = {
type: 'rdsm/sourceView/ON_RDSM_SOURCE_PLANT_REQUEST_STARTED',
};
const dataTypeName = 'something';
expect(generator.next().value).toEqual(call(mapDataTypes, dataType));
expect(generator.next(dataTypeName).value).toEqual(put(expectedAction));
});

to test else block
it('should return onRdsmSourcePlantRequestStarted action', () => {
const expectedAction = {
type: 'rdsm/sourceView/ON_RDSM_SOURCE_PLANT_REQUEST_STARTED',
};
const dataTypeName = 'test';
expect(generator.next(dataTypeName).value).toEqual(put(expectedAction));
});
test for if block
it('should return undefined ', () => {
const dataTypeName = '';
expect(generator.next(dataTypeName).value).toBe(undefined));
});

Related

How to test for return result from callback in RTL?

I expect callback to return either true or false. I know how to test for function being called, but how to test if that function returned specific value? How can I do that using react testing libraries?
My code:
it('sends back error callback', () => {
const isError = (val) => val;
render(<Component callback={isError}/>);
const input = screen.getAllByRole('textbox')[0];
fireEvent.change(input, {target: {value: '100.98'}});
expect(isError).toHaveBeenCalledTimes(1);
})
If you utilize a Mock (jest.fn()), you can simply use toHaveBeenCalledWith.
So in your case:
it("sends back error callback", () => {
const isError = jest.fn();
render(<Component callback={isError} />);
const input = screen.getAllByRole("textbox")[0];
fireEvent.change(input, { target: { value: "100.98" } });
expect(isError).toHaveBeenCalledWith(false);
});
Or use this alternative, especially if you have issue with the asynchronous nature of the code:
it("sends back error callback", (done) => {
const isError = (val) => {
expect(val).toBe(false);
done();
};
render(<Component callback={isError} />);
const input = screen.getAllByRole("textbox")[0];
fireEvent.change(input, { target: { value: "100.98" } });
});
Read more about async code: https://jestjs.io/docs/asynchronous.
If you have any questions to my answer/or the answer is not working, please get back to me in the comments 😊.

Why am i getting an undefined output when I try to access value returned from async method

I have the following method which returns an object with 3 fields inside a different file named localStorage:
const getUserProfileData = async () => {
try {
await AsyncStorage.getItem(CONSTANTS.USER_PROFILE).then((item) => {
let retrievedProfile = JSON.parse(item);
return retrievedProfile;
});
} catch (e) {
throw e;
}
};
here is my file profile.js:
useEffect(() => {
const retrieveProfileData = async () => {
let retProfile = await localStorage.getUserProfileData();
console.log("check what: ",retProfile);
};
retrieveProfileData();
}, []);
inside the use effect, when I attempt to log out the result I get an output of:
check what: undefined
I have read other forums on similar problems to this but I can't seem to notice where I'm going wrong?
I think it has to do with you mixing async and .then(). Try this way:
const getUserProfileData = async () => {
try {
const result = await AsyncStorage.getItem(CONSTANTS.USER_PROFILE)
const retrievedProfile = JSON.parse(result);
return retrievedProfile;
} catch (e) {
throw e;
}
};
const getUserProfileData = async () => {
return AsyncStorage.getItem(CONSTANTS.USER_PROFILE);
};
useEffect(() => {
const retrieveProfileData = async () => {
try {
let retProfile = JSON.parse(await localStorage.getUserProfileData());
console.log("check what: ",retProfile);
} catch (error) {
// handle error
}
};
retrieveProfileData();
}, []);

How to fire an event React Testing Library

I have some code, in a hook, to detect whether the browser is online / offline:
export function useConnectivity() {
const [isOnline, setNetwork] = useState(window.navigator.onLine);
const updateNetwork = () => {
setNetwork(window.navigator.onLine);
};
useEffect(() => {
window.addEventListener('offline', updateNetwork);
window.addEventListener('online', updateNetwork);
return () => {
window.removeEventListener('offline', updateNetwork);
window.removeEventListener('online', updateNetwork);
};
});
return isOnline;
}
I have this basic test:
test('hook should detect offline state', () => {
let internetState = jest.spyOn(window.navigator, 'onLine', 'get');
internetState.mockReturnValue(false);
const { result } = renderHook(() => useConnectivity());
expect(result.current.valueOf()).toBe(false);
});
However, I want to run a test to see whether it returns the correct value when an offline event is triggered, not just after the mocking of the returned value on render. What is the best way to approach this? Where I have got so far is this:
test('hook should detect offline state then online state', async () => {
const { result, waitForNextUpdate } = renderHook(() => useConnectivity());
act(() => {
const goOffline = new window.Event('offline');
window.dispatchEvent(goOffline);
});
await waitForNextUpdate();
expect(result.current).toBe(false);
});
I'm not sure about 'best', but this is one way: change the mock response halfway through the test, and tweak some of the async code:
test('hook should detect online state then offline state', async () => {
const onLineSpy = jest.spyOn(window.navigator, 'onLine', 'get');
// Pretend we're initially online:
onLineSpy.mockReturnValue(true);
const { result, waitForNextUpdate } = renderHook(() => useConnectivity());
await act(async () => {
const goOffline = new window.Event('offline');
// Pretend we're offline:
onLineSpy.mockReturnValue(false);
window.dispatchEvent(goOffline);
await waitForNextUpdate();
});
expect(result.current).toBe(false);
});

How to mock variables inside redux saga generator functions?

how can I test the saga below?
export function* getSnapShotFromUserAuth(userAuth, additionalData) {
try {
const userRef = yield call(
createUserProfileDocument,
userAuth,
additionalData
);
const userSnapshot = yield userRef.get();
yield put(signInSuccess({ id: userSnapshot.id, ...userSnapshot.data() }));
} catch (error) {
yield put(signInFailure(error));
}
}
I was only able to get it working up until the first line:
describe("getSnapShotFromUserAuth", () => {
const mockUserAuth = {};
const mockAdditionalData = {};
const generator = getSnapShotFromUserAuth(mockUserAuth, mockAdditionalData);
it("should get snapshot from user auth", () => {
expect(generator.next().value).toEqual(
call(createUserProfileDocument, mockUserAuth, mockAdditionalData)
);
});
});
How can I verify the next line? const userSnapshot = yield userRef.get();
I keep getting error TypeError: Cannot read property 'get' of undefined when calling trying to test the next line as it cannot find userRef. Is there a way to be able to mock the next line?
You can dictate what the result of the yield is by what you pass in when you call next(). So for example, after doing the first generator.next, you can do:
const mockUserRef = {
get: jest.fn();
}
expect(generator.next(mockUserRef).value).toEqual(/* whatever */);
Answer -
it("should check for signInSuccess", () => {
const myMock = jest.fn();
let userRef = {
get: myMock.mockReturnValue({
id: 1,
data: () => {},
}),
};
let userSnapshot = {
id: 1,
data: () => {},
};
generator.next(userRef);
expect(generator.next(userSnapshot).value).toEqual(
put(signInSuccess({ id: userSnapshot.id, ...userSnapshot.data() }))
);
});

React State is not getting update before assertion even acting inside act function

Why below unit test case is failing? I have a similar situation in real-world, but here, I am testing a simplified version of it.
I am expecting that assertions should execute after act complete the state update of hook, but its not behaving that way. Though state is getting update, after assertions executes.
Please suggest if there is any other way to test such situation.
Code
const HookForTest = () => {
const [data, setData] = useState('');
const updateDataFromOutside = toData => {
setData(toData);
};
return [data, updateDataFromOutside];
};
Unit Test
describe('HookForTest', () => {
test('should change state on calling updateDataFromOutside function ', async () => {
let hookData;
testHook(() => {
hookData = HookForTest();
});
let [data, updateDataFromOutside] = hookData;
await act(async () => {
updateDataFromOutside('testData');
});
expect(data).toEqual('testData');
});
});
I am using a few utility functions for testing custom hook, below is code:
export const TestHook = ({callback}) => {
callback();
return null;
};
export const testHook = callback => {
mount(<TestHook callback={callback} />);
};
Test Result
● HookForTest › should change state on calling updateDataFromOutside function
expect(received).toEqual(expected) // deep equality
Expected: "testData"
Received: ""
178 | });
179 |
> 180 | expect(data).toEqual('testData');
The problem here is that let [data, updateDataFromOutside] = hookData; locks the value of data to be what its original value was. Even if something like hookData[0] = 'something else' was called, the value of data would still be ''.
Changing the test to something like this should work
describe('HookForTest', () => {
test('should change state on calling updateDataFromOutside function ', async () => {
let data;
let updateDataFromOutside;
testHook(() => {
let hookData = HookForTest();
data = hookData[0]
updateDataFromOutside = hookData[1]
});
await act(async () => {
updateDataFromOutside('testData');
});
expect(data).toEqual('testData');
});
});

Resources