How can i test localstorage properly? - reactjs

how can i test localstorage? i wanna create a test a simple component who is using localstorage, i want to compare an atribute like this
test("data is added into local storage", () => {
const mockId = "111";
const mockJson = { data: "json data" };
expect(localStorage.getItem('STORE')).toEqual(JSON.stringify(mockJson));
});
but it say me :
localStorage.getItem(mockId) is undefined
i mocked like this
beforeAll(() => {
const localStorageMock = {
getItem: jest.fn(),
setItem: jest.fn(),
clear: jest.fn()
};
Object.defineProperty(window, 'localStorage', { value: localStorageMock });
})
btw this test passed, but it does not tell nothing to me, i would like to compare the values
it('renders correctly', () => {
console.log('112233',localStorage.getItem('STORE'))
expect(localStorage.getItem).toHaveBeenCalledWith('STORE')
})

You can mock it like this:
const mockGetItem = jest.fn();
Object.defineProperty(window, "localStorage", {
value: {
getItem: () => {
return mockGetItem();
},
},
});
test("data is added into local storage", () => {
const mockJson = { data: "json data" };
mockGetItem.mockReturnValueOnce(JSON.stringify(mockJson));
expect(window.localStorage.getItem("STORE")).toEqual(
JSON.stringify(mockJson)
);
});
The test itself only tests if mock is working ;) Maybe it will help you with your test.
Here you can read my post on mocking window object Mocking window object with Jest

Related

How can i test downloading excel file with jest?

I am new to testing react components with jest and enzyme. I have this example
generateExcelFile = () => {
const {actions, state} = this.props;
const dateFrom = state.getIn(['config', 'marketingQuestionReport', 'dateFrom']);
const dateTo = state.getIn(['config', 'marketingQuestionReport', 'dateTo']);
this.setState({isLoading: true, isLoadingFinished: false});
fetch(`${env.MARKETING_QUESTION_REPORT}?dateFrom=${dateFrom}&dateTo=${dateTo}`)
.then((resp) => resp.blob())
.then((blob) => {
if (typeof window.navigator.msSaveBlob !== 'undefined') {
window.navigator.msSaveBlob(
blob,
`Marketing Question Report from ${dateFrom} to ${dateTo}.xlsx`
);
}
const url = window.URL.createObjectURL(blob);
const tempLink = document.createElement('a');
tempLink.style.display = 'none';
tempLink.href = url;
tempLink.setAttribute(
'download',
`Marketing Question Report from ${dateFrom} to ${dateTo}.xlsx`
);
if (typeof tempLink.download === 'undefined') {
tempLink.setAttribute('target', '_blank');
}
document.body.appendChild(tempLink);
tempLink.click();
window.URL.revokeObjectURL(url);
toastr.success('Sucessfully generated marketing question report');
this.setState({isLoading: false, isLoadingFinished: true});
actions.clearMarketingQuestionReportDates();
})
.catch(() =>
toastr.error('An error occurred while generating marketing question report')
);
};
I am struck on .then part i don't know how to test after that the whole fetch call
What i have sofar
describe('<MarketingQuestionReportPage />', () => {
beforeEach(() => {
const fecthSpy = jest.spyOn(window, 'fetch').mockReturnValue(() =>
Promise.resolve({
blob: () =>
Promise.resolve({
size: 6682,
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}),
})
);
});
test('generateExcelFile', () => {
const props = {
state: fromJS({
config: {
marketingQuestionReport: {
dateFrom: '2019-01-01',
dateTo: '2021-03-21',
},
},
}),
actions: {
clearMarketingQuestionReportDates: jest.fn(),
},
};
const tree = shallowSetup(props);
tree.instance().generateExcelFile();
tree.fecthSpy.toHaveBeenCalled();
});
});
i maked a instance of my component - i got prepaired the needed props for that call to work and i am sending them to my instance.After that i call my method - generateExcelFile();
On my coverage everything is freen except the fetch call. I don't know how to fix this. Please help
You need to create a spy of fetch before rendering the component with jest.spyOn(object, methodName):
test('generateExcelFile', () => {
const fetchSpy = jest.spyOn(window, "fetch").mockReturnValue(Promise.resolve({
blob: () =>
Promise.resolve({
size: 6682,
type:
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
}),
})
);
//...
const tree = shallowSetup(props);
tree.instance().generateExcelFile();
expect(fetchSpy).toHaveBeenCalled();
});
In your test you can check if fecthSpy have been called

Mock window.close() in React-testing-library

I have searched online but I didn't find a way to mock window.close() in react testing library or even in jest.
const handleClose = () => {
window.opener.location.reload()
window.close()
}
<Button
data-test-id="close"
onClick={handleClose}
/>
How do I achieve test case coverage for the onclick of button and window.close() and window.opener.location.reload() is covered
My test case is as follows:
const wrapper = render( <CloseButton />);
const windowSpy = jest.spyOn(global, 'window', 'get');
const { queryByTestId } = wrapper;
fireEvent.click(queryByTestId('close');
expect(windowSpy.opener.location.relaod).toHaveBeenCalled();
expect(windowSpy.close).toHaveBeenCalled();
for this last line of code
expect(windowSpy.close).toHaveBeenCalled();
I am getting an error that says
received value must be a mock or spy function. received has value
undefined
For
expect(windowSpy.opener.location.relaod).toHaveBeenCalled();
it says:
windowSpy.opener is not defined.
You're only mocking window but you're not providing any implementation.
This should help:
windowSpy.mockImplementation(() => ({
close: jest.fn(),
opener: {
location: {
reload: jest.fn(),
}
}
}));
If you don't actually need a spy, just a mock, I've had luck with simply reassigning the particular window function:
global.window.opener.location.reload = jest.fn()
Remarkable Mark provides a solution to this that avoids all the above pitfalls, as well as the reload is not declared configurable error that you may get when you try to modify reload() directly. See:
https://remarkablemark.org/blog/2018/11/17/mock-window-location/
Code is copied here directly from this ^ page:
describe('window.location', () => {
const { location } = window;
beforeAll(() => {
delete window.location;
window.location = { reload: jest.fn() };
});
afterAll(() => {
window.location = location;
});
it('mocks `reload`', () => {
expect(jest.isMockFunction(window.location.reload)).toBe(true);
});
it('calls `reload`', () => {
window.location.reload();
expect(window.location.reload).toHaveBeenCalled();
});
});
Thanks Mark!
https://remarkablemark.org/support/

Jest testing service call responses witth promises, useEffect and useState hooks

I'm having some difficulty testing this useEffect in jest. The following piece of code is within a react functional component and I want to return some mock values when the serviceFn is called. The returned data is written back to state.
//from service-factory.js
const serviceFn = () => (
({ personId }) => (
ionXHR.request(
`/persons/${personId}/`,
'GET',
null,
'json',
)
)
);
//from Component.jsx
const service = useRef(serviceFn());
useEffect(() => {
service.current({ personId:123456 }).then((response) => {
if (response.data) {
setData(response.data);
setLoadingState('SUCCESS');
} else {
setLoadingState('FAILED');
}
});
}, [personId]);
I have the following, but not sure what else I would need.
function mockReturnFn() { return 'Test'; }
const wrapper = mount(<Component/>);
const somethingSpy = jest.spyOn(wrapper, 'serviceFn').mockImplementation(mockReturnFn);
Update:
So, I think I'm getting close.
In my test file I had to import the function
import { serviceFn } from './service-factory';
jest.mock('./service-factory', () => ({ serviceFn : jest.fn() }));
In my test I have
serviceFn.mockImplementation(() => Promise.resolve('test1234'));
The issue now with this is I am getting service.current is not a function
I tried to do this but now getting _serviceFactory.serviceFn.mockImplementation is not a function
jest.mock('./service-factory', () => (
{
serviceFn: {
current: jest.fn(),
},
}
));
serviceFn is factory function, it returns a function that makes requests.
Considering it's named export, it should be initially stubbed as:
jest.mock('./service-factory', () => {
const mockService = jest.fn();
return {
__esModule: true,
mockService,
serviceFn: jest.fn().mockReturnValue(mockService)
}
});
mockService is exposed and allows to mock specific responses:
mockService.mockResolvedValue({ data: ... });
Since it's basic wrapper over ionXHR, it's also possible to mock responses one level higher on ionXHR.request calls.

Is there a way to mock refetch in MockedProvider - Apollo Client?

Here is how I am using MockedProvider. How can I mock refetch in mocks array?
const mocks = [{
request: {
query: GET_USERS_BY_FACILITY,
variables: {
facility: 300
}
},
result: {
data: {
GetUsersByFacility: [{
nuId: 'Q916983',
userName: faker.internet.userName(),
profileKey: 'testKey',
profileValue: 'testValue',
__typename: 'FacilityUser'
}]
}
},
refetch: () => {
return {
data: {
GetUsersByFacility: [{
nuId: 'Q916983',
userName: faker.internet.userName(),
profileKey: 'testKey',
profileValue: 'testValue',
__typename: 'FacilityUser'
}]
}
}
}
}
This test case calls refetch function when delete event is triggered.
it('should be able to click on delete user', async () => {
const {getByTestId} = render(
<MockedProvider mocks={mocks}>
<Users selectedFacility={300}/>
</MockedProvider>)
await wait(0)
fireEvent.click(getByTestId('btnDelete'))
})
I have been trying different ways, none seems to work. I get error message as TypeError: Cannot read property 'refetch' of undefined.
Thank you very much in hope of an answer.
Regards,
--Rajani
Maybe it's a bit late to answer, but if you have't got any answers yet, you would refer to the way I solved.
Please note that this might not be the correct answer.
You can find this code in react-apollo docs
const mocks = [
{
request: {
query: GET_DOG_QUERY,
variables: {
name: 'Buck',
},
},
result: () => {
// do something, such as recording that this function has been called
// ...
return {
data: {
dog: { id: '1', name: 'Buck', breed: 'bulldog' },
},
}
},
},
];
I make my refetch testcode based on this phrase // do something, such as recording that this function has been called
This is my mock example.
let queryCalled = false
const testingData = (value) => ({
data: {....}
})
const TESTING_MOCK = {
request: {
query: TESTING_QUERY,
variables: { some: "variables" },
},
result: () => {
if (queryCalled) return testingData("refetched");
else {
queryCalled = true;
return testingData("first fetched");
}
},
};
This component refetches data when the button is clicked. I designed my test code in this order
when it's rendered for the first time, it fetches the mock data .
=> In the code above, queryCalled is false so, it reassigns queryCalled as true and return the "first fetched" mock data,
when a click event occurs, refetch occurs too.
=> On the same principle the mock data returns "refetched" mock data.
My testcode example is here
it("refetch when clicked save button.", async () => {
const mocks = [TESTING_MOCK];
let utils: RenderResult = render(<SomeTestingComponent mocks={mocks} />);
await waitForNextTick(); //for getting a data, not loading
const titleInput = utils.getByDisplayValue("first fetched");
const saveBtn = utils.getByText("save");
fireEvent.click(saveBtn);
await waitForElement(() => utils.getByDisplayValue("refetched"));
})
Please let me know if you have any other suggestions!
For anyone that might still run into this, the solution to make refetch work in your tests is to use the newData method while keeping track of the query having been called.
I don't know if this is a bug in the MockedProvider implementation, but I was banging my head against the wall trying to make newData work together with result, but it turns out that newData completely overrides result.
A working solution (tested with useQuery and Apollo Client 3) would be something like this:
let queryCalled = false;
const refetchMock = {
request: {
query: YOUR_QUERY
},
newData: () => {
if (queryCalled) {
return {
data: {
// your refetched data
}
};
} else {
queryCalled = true;
return {
data: {
// your first fetched data
}
};
}
}
};
The newData solution didn't work for me with apollo client #2.6.
As a workaround, for the few tests that utilize refetch I had to physically mock the useQuery function and provide mock functions for the return of refetch; for our custom hook (where an overridden useQuery hook is exported as default), it looked something like this:
import * as useQueryModule from '~/hooks/useQuery';
describe('test the thing', () => {
let useQuerySpy;
beforeEach(() => {
// Spy on the `useQuery` function so we can optionally provide alternate behaviour.
useQuerySpy = jest.spyOn(useQueryModule, 'default');
})
afterEach(() => {
// Restore any mocked behaviour
useQuerySpy.mockRestore();
});
it('does a thing', async () => {
const refetchedApolloResponse = buildResponse(refetchData) // some function to build the shape / data of apollo response
const initialApolloResponse = buildResponse(initialData) // some function to build the shape / data of apollo response
const mockRefetch = jest.fn().mockResolvedValue({ data: refetchedApolloResponse });
useQuerySpy.mockReturnValue({ data: initialApolloResponse, refetch: mockRefetch });
// Assert stuff
}
})
This solution did not work for me and not sure whether it will work or not because it didn't work in my case.
let queryCalled = false
const testingData = (value) => ({
data: {....}
})
const TESTING_MOCK = {
request: {
query: TESTING_QUERY,
variables: { some: "variables" },
},
result: () => {
if (queryCalled) return testingData("refetched");
else {
queryCalled = true;
return testingData("first fetched");
}
},
};
So I solved it by another way which is:
const mocks = [
{
request: {
query: GET_DOG_QUERY,
variables: {
name: 'Buck',
},
},
result: {
data: {
dog: { id: '1', name: 'Buck', breed: 'bulldog' },
},
},
newData: jest.fn(() => ({
data: {
dog: { id: '1', name: 'Refetched-Buck', breed: 'refetched-bulldog' },
},
})),
},
];
It worked like a charm for me.

Jest mock module resolve with variable value

Assuming I have a module which returns a promise.
I want to mock different outcomes of this promise to test the function where this module is part of. I mock the module like this:
jest.mock('../request', () => {
return () => new Promise((resolve, reject) => {
return resolve({
response: { ok: true }
});
});
});
My first test is running
test("The function resolves", () => {
const initialState = { apiData: getState("postData", {}, "ready", "POST") };
const store: any = mockStore(initialState);
return expect(
performApiRequest("postData/", {}, { data: "json" })(dispatch, () =>
store.getState()
)
).resolves.toBeUndefined();
});
The problem is now with testing an other function where the value that resolves is supposed to be different, for instance {response: { ok: false } }.
I already tried different things. First wrapping the mock in a function and give the response as an argument. --> fails for mocks can't take out of scope variables.
I tried to call jest.doMock within the test but this does not trigger the request correctly.
I tried to do
const mockResponse = jest.fn();
jest.mock("../request", () => {
return () =>
new Promise((resolve, reject) => {
return resolve({
mockResponse
});
});
});
And then call mockResponse.mockReturnValueOnce(value).
No success yet. Anybody sees a way out?
You can create a default mock function at the top level with jest.fn. Once you create the mock you can override the implementation of the function within the test case with mockImplementation or mockImplementationOnce. You can find more information about this in the Jest documentation.
import request from '../request';
jest.mock("../request", () =>
jest.fn(() =>
Promise.resolve({
response: {
ok: true
}
})
)
);
test("MyTest", () => {
request.mockImplementationOnce(() =>
Promise.resolve({
response: {
ok: false
}
})
);
});
answer with typescript would be:
import request from '../request';
jest.mock("../request", () =>
jest.fn(() =>
Promise.resolve({
response: {
ok: true
}
})
)
);
test("MyTest", () => {
(request as jest.Mock).mockImplementationOnce(() =>
Promise.resolve({
response: {
ok: true
}
})
);
});

Resources