How to mock variables inside redux saga generator functions? - reactjs

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() }))
);
});

Related

React : waiting for a promise

i got a function that uses axios to get info from my backend as follows:
const getDoH = async () => {
const user = JSON.parse(localStorage.getItem("user"));
let config = {
headers: {
"Content-Type": "application/json",
Authorization: "Bearer " + user.accessToken,
},
params: {
username: user.username,
},
};
return await axios.get(API_URL + "get", config);
};
Now i want to save these values into a global variable on initiate so i use the initialstate hook like this:
const [initialValues, setInitialValues] = useState(() => {
const initialSwitchState = getDoH().then(
(response) => {
setInitialValues(response.data);
},
(error) => {
console.log(error);
}
);
return initialSwitchState;
});
after that i got a function that takes the values from the db and maps them onto my local variable and this function looks like this:
const setStartValues = () => {
let newValues = initialSwitchState;
let valueArray = Object.entries(newValues).map((v, index) => {
v[1] = initialValues.switchValues[index]
return v
});
newValues = Object.fromEntries(valueArray);
setValues({...newValues});
}
and i want to call this function with a final function that is another initialstate hook like this:
const [values, setValues] = useState(() => {
const initialState = setStartValues();}
but by the time it gets to the line:
v[1] = initialValues.switchValues[index]
the initialValues is still a promise. and i cant see where i have gone wrong as i have used async and wait on my initial getDoH() function.
How can i solve this (wait for the promise) before i try to use the results?
kind regards.
There are two issues here:
First, you need to await getDoH() since that's an async function.
Second, useState() is a synchronous function, so you need to do the await getDoH() inside a useEffect() before you set const [initialValues, setInitialValues] = ...
Tbh i did it with use effect and it comes with its own set of issues. found the best way to do it was:
const [values, setValues] = useState(async () => {
const initialState = await getDoH().then(
(response) => {
let newValues = switchState;
let valueArray = Object.entries(newValues).map((v, index) => {
v[1] = response.data.switchValues[index]
return v
});
newValues = Object.fromEntries(valueArray);
setValues({...newValues});
},
(error) => {
console.log(error);
}
);
return initialState;
});

Redux saga yield put unit test not working

I looking to doc and some samples online, but still not working. I use Sinon for unit test, and I keep getting this error, stuck on this one so long, can't figure it out.
expected { Object (##redux-saga/IO, combinator, ...) } to deeply equal { Object (##redux-saga/IO, combinator, ...) }
My action
export const loadingStatus = (response) => {
return { type: "LOADING_STATUS", response };
};
My saga
export function* mySampleSaga() {
try {
yield put(loadingStatus('loading'));
yield delay(1000);
const config = yield select(getConfig);
const requestCall = new SendingRequest(config);
const linkRequests = yield select(getLinks);
const response = yield call(
[requestService, requestCall.sample],
"2020-01-01",
"2020-12-21"
);
const result = get(response, 'entities.requests', {});
yield put(success(result));
yield put(loadingStatus('done'));
} catch (error) {
yield put(sendError(error));
yield put(loadingStatus('done'));
}
}
My test
describe('sample saga', () => {
const config = {
sample: "123"
};
const linkRequests = ['12345', '5678910'];
it('should update request status - happy path', () => {
const gen = mySampleSaga();
expect(gen.next().value).to.deep.equal(put(loadingStatus('loading'))); // This keep getting error below
});
it('If saga has error', () => {
const gen = mySampleSaga();
const error = new Error('error');
gen.next();
expect(gen.next().value).to.deep.equal(put(sendError(error)));
expect(gen.next().value).to.deep.equal(put(loadingStatus('done')));
expect(gen.next().done).to.equal(true);
});
});

mocking my fetch function does not work, keeps getting back undefined

I am trying to mock a simple function that uses fetch. The function in question looks like this:
export const getPokemon = async () => {
//function that makes the API call and fetches our pokemon
//getPokemon.js
const randomId = () => Math.floor(Math.random() * 151 + 1);
const pokemonApiUrl = `https://pokeapi.co/api/v2/pokemon/`;
export const getPokemon = async () => {
//function that makes the API call and fetches our pokemon
const id = randomId();
let pokemon = { name: "", image: "" };
try {
const result = await fetch(`https://pokeapi.co/api/v2/pokemon/${id}`);
console.log(result)
const data = await result.json();
pokemon.name = data.name;
pokemon.image = data.sprites.other["official-artwork"].front_default;
return pokemon;
} catch (err) {
console.error(err);
Whenever I try to mock the function in my unit tests I receive back a TypeError: Cannot read property 'json' of undefined. Basically, the result comes back as undefined and thus we cannot call our .json(). It works fine in production and the fetch calls work as expected. I am using React Testing Library and Jest.
I have tried to replaced the global fetch in the following manner:
//PokemonPage.test.js
global.fetch = jest.fn(() =>
Promise.resolve({
json: () => Promise.resolve({ data: { name: 'Charizard' } }),
})
);
I've also tried to create a fakeFetch and send it in to my function as a dependency injection, but I get the exact same error.
Lastly, I've tried to install jest-fetch-mock but yet again I am getting the same error... Has anyone encountered the same thing?
The failing function gets called in production here:
function Pokemon({ pokemonTrainer }) {
...
useEffect(() => {
async function fetchData() {
pokemonRef.current = await getPokemon();
setPokemonList((prev) => [
...prev,
{ name: pokemonRef.current.name, image: pokemonRef.current.image },
]);
}
fetchData();
}, []);
...
}

Rematch/Effects- Error running test cases written with react testing Library

I am trying to write unit test cases for my models using react testing library but i am facing some issues executing the test cases.
My effects.js
export async function getStoredData(param1, store, param2) {
try {
dispatch(setLoading(true, 'getStoredData'));
// Check if key exists in store
const inputKeyCode = getInputKeyCode([param1, param2]);
let response = getUserDataState(store)[inputKeyCode];
if (!response) {
response = await getUserApi(param1, param2);
this.setUserData({ keyCode: inputKeyCode, keyValue: response });
}
return response;
} catch (error) {
// dispatch error
} finally {
dispatch(setLoading(false, 'getStoredData'));
}
}
My reducers.js
const INITIAL_STATE = {
userData: {},
};
const setUserData = (state, { key, value }) => ({ // {key: value}
...state,
userData: {
...state.userData,
[key]: value,
},
});
effects.test.js
import { getUserApi } from '../../../api/common';
jest.mock('../../../store', () => ({ dispatch: jest.fn() }));
jest.mock('../../../api/common', () => ({ getUserApi: jest.fn() }));
describe('getStoredData', () => {
const responseData = {};
setWith(responseData, 'data.userInformation', 12345);
const setUserData = jest.fn();
test('success', async () => {
getUserApi.mockResolvedValue(responseData);
await testModel.effects().getStoredData.call({ setuserData });
expect(setuserData).toHaveBeenCalled();
expect(setuserData).toHaveBeenCalledWith(12345);
});
test('failure', async () => {
getUserApi.mockRejectedValue(errorMsg);
await testModel.effects().getStoredData.call({ setuserData });
expect(showNotification).toHaveBeenCalled();
expect(showNotification).toHaveBeenCalledWith('error');
});
});
This gives me below error-
Expected mock function to have been called, but it was not called.
At line- expect(setuserData).toHaveBeenCalled();
Can someone help me understand what i am doing wrong? I guess i am doing some mistake in calling the setuserData. Any help is much appreciated.

How to test assignment lines in saga function using jest

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));
});

Resources