I want to write the test cases for this file - reactjs

enter image description here
enter image description here
enter image description here
Here is my test code
import React from 'react';
import Notification from '../Notification';
import Enzyme, { shallow, mount, render } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
import { Provider } from 'react-redux';
import thunk from 'redux-thunk';
import * as redux from 'react-redux';
import { createStore, applyMiddleware } from 'redux';
import rootReducer from '../../Core/Reducers/index';
import renderer from 'react-test-renderer';
const store = createStore(rootReducer, applyMiddleware(thunk));
Enzyme.configure({ adapter: new Adapter() });
describe('Should render initial layout Notification', () => {
it('renders correctly', () => {
const prop = true;
const wrapper = shallow(<Provider store={store}><Notification {...prop} />
</Provider>
it('renders correctly', () => {
const spy = jest.spyOn(redux, 'useSelector');
spy.mockReturnValue('drafts');
});
it('renders correctly', () => {
const setdraftState = jest.fn();
jest
.spyOn(React, 'useState')
.mockImplementation(draftState => [draftState, setdraftState]);
});
it('renders correctly', () => {
const setVenueState = jest.fn();
jest
.spyOn(React, 'useState')
.mockImplementation(venueState => [venueState, setVenueState]);
});
it('renders correctly', () => {
const setAuditState = jest.fn();
jest
.spyOn(React, 'useState')
.mockImplementation(auditState => [auditState, setAuditState]);
});
it('renders correctly', () => {
const setAdminState = jest.fn();
jest
.spyOn(React, 'useState')
.mockImplementation(adminState => [adminState, setAdminState]);
});
it('renders correctly', () => {
const setAdminStateOM = jest.fn();
jest
.spyOn(React, 'useState')
.mockImplementation(adminStateOM => [adminStateOM, setAdminStateOM]);
});
it('renders correctly', () => {
const setInternalVenueState = jest.fn();
jest
.spyOn(React, 'useState')
.mockImplementation(internalVenueState => [internalVenueState, setInternalVenueState
]);
});
it('renders correctly', () => {
const prop = true;
const wrapper = shallow(<Provider store={store}><Notification {...prop} />
</Provider>); expect(wrapper.children().length).toEqual(1);
});
it('renders correctly', () => {
const wrapper = shallow(<Provider store={store}><Notification /></Provider>);
const openNotificationWithIcon = jest.fn();
expect(wrapper.instance(openNotificationWithIcon));
});
it('Render Notification', () => {
const notify = renderer.create(<Provider store={store}><Notification /></Provider>);
let tree = notify.toJSON();
expect(tree).toMatchSnapshot();
});
});
I write some test cases but it's giving me 33.36 test coverage few things as I showed you in the image want to cover I am new in jest and react I would appreciate If you assist me how can I cover all the test coverage

You need to test your all condition so your code coverage will be increased. So based on that you need to manage your API's response or your state value based on that you can cover that lines.
Example :
draftStatus you need to update status based on your condition and write test cases according to that.
Add, Delete, Failure etc...
You can use react-testing-library and react-hooks-testing-library
for testing your react component and react hooks.
Basic Hooks
Imagine we have a simple hook that we want to test:
import { useState, useCallback } from 'react'
export default function useCounter() {
const [count, setCount] = useState(0)
const increment = useCallback(() => setCount((x) => x + 1), [])
return { count, increment }
}
To test useCounter we need to render it using the renderHook function provided by react-hooks-testing-library:
import { renderHook } from '#testing-library/react-hooks'
import useCounter from './useCounter'
test('should use counter', () => {
const { result } = renderHook(() => useCounter())
expect(result.current.count).toBe(0)
expect(typeof result.current.increment).toBe('function')
})

Generally, enzyme and the "shallow" (which does not execute effects) testing mindset are not really a thing any more nowadays. (Enzyme itself has been pretty much dropped by airbnb and is maintained by a single person at this point, often lagging behind on React by months).
I would want to encourage you to look into react-testing-library instead, which will also execute your effects (the fact that they are not executed means you are not testing them - that is what coverage tells you in the end).
Also, it shifts the whole "testing mindset" a lot towards "how would a user interact with this?", so it might make more sense for you to read through their tutorials than just providing a code example here.

Related

Test dispatch action using react testing-library

Hi I have a function as shown below:
MyComponent.js-------
const somefunction = () => {
if (someCondition) {
dispatch(someActionMethod({
id: 01,
empName: 'Adam'
}).then((result) => {
setSomeState(result.data);
})
)
}
}
Tried a lot of ways to mock someActionMethod with dispatch, but all efforts are failing.
My test file:
import React from 'react';
import * as redux from 'react-redux';
import { render } from '#testing-library/react';
const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch');
const mockDispatchFn = jest.fn();
describe('<MyComponent />', () => {
const props = {};
mockDispatchFn.mockReturnValue(
[{data1},{data2}]
);
let componentRendering;
beforeEach(() => {
componentRendering = render(
<MyComponent props={ props } />
)});
it('test component', ()=> {
expect(componentRendering.container.innerHTML).not.toBeNull();
});
});
Throws me error:
Cannot read properties of undefined (reading 'then')
My assumption from the error message is that your code is expecting a promise somewhere.
The stack trace will have info about the component throwing this error at a specific line.
I would recommend you to check your code and mock all the required methods
I hope this helps in resolving the error
Have you tried to mock useDispatch instead mocking someActionMethod?
import * as redux from 'react-redux';
const useDispatchSpy = jest.spyOn(redux, 'useDispatch');
const mockDispatchFn = jest.fn();
// One of these should work
mockDispatchFn.mockReturnValue([{data1}, {data2}]);
useDispatchSpy.mockReturnValue(mockDispatchFn);
With your updated response I'd try this:
describe('<MyComponent />', () => {
it('test component', () => {
const props = {};
const useDispatchSpy = jest.spyOn(reactRedux, 'useDispatch');
const mockDispatchFn = jest.fn();
mockDispatchFn.mockReturnValue(
[{ data1 }, { data2 }],
);
render(<MyComponent {...props} />);
// your assertions here
});
});
Are you using it with middleware? I am not sure that dispatch returns a promise nor any value without using middleware.
By the way, mocking a react redux hook is not a recommended way and you could actually see it is not even possible as of version 8 of react-redux package.

Warning: An update to App inside a test was not wrapped in act(...) in enzyme and hooks

I have written this component. it fetchs data using hooks and state. Once it is fetched the loading state is changed to false and show the sidebar.
I faced a problem with Jest and Enzyme, as it does throw a warning for Act in my unit test. once I add the act to my jest and enzyme the test is failed!
// #flow
import React, { useEffect, useState } from 'react';
import Sidebar from '../components/Sidebar';
import fetchData from '../apiWrappers/fetchData';
const App = () => {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
const getData = async () => {
try {
const newData = await fetchData();
setData(newData);
setLoading(false);
}
catch (e) {
setLoading(false);
}
};
getData();
// eslint-disable-next-line
}, []);
return (
<>
{!loading
? <Sidebar />
: <span>Loading List</span>}
</>
);
};
export default App;
And, I have added a test like this which works perfectly.
import React from 'react';
import { mount } from 'enzyme';
import fetchData from '../apiWrappers/fetchData';
import data from '../data/data.json';
import App from './App';
jest.mock('../apiWrappers/fetchData');
const getData = Promise.resolve(data);
fetchData.mockReturnValue(getData);
describe('<App/> Rendering using enzyme', () => {
beforeEach(() => {
fetchData.mockClear();
});
test('After loading', async () => {
const wrapper = mount(<App />);
expect(wrapper.find('span').at(0).text()).toEqual('Loading List');
const d = await fetchData();
expect(d).toHaveLength(data.length);
wrapper.update();
expect(wrapper.find('span').exists()).toEqual(false);
expect(wrapper.html()).toMatchSnapshot();
});
});
So, I got a warning:
Warning: An update to App inside a test was not wrapped in act(...).
When testing, code that causes React state updates should be wrapped into act(...):
act(() => {
/* fire events that update state */
});
I did resolve the warning like this using { act } react-dom/test-utils.
import React from 'react';
import { act } from 'react-dom/test-utils';
import { mount } from 'enzyme';
import fetchData from '../apiWrappers/fetchData';
import data from '../data/data.json';
import App from './App';
jest.mock('../apiWrappers/fetchData');
const getData = Promise.resolve(data);
fetchData.mockReturnValue(getData);
describe('<App/> Rendering using enzyme', () => {
beforeEach(() => {
fetchData.mockClear();
});
test('After loading', async () => {
await act(async () => {
const wrapper = mount(<App />);
expect(wrapper.find('span').at(0).text()).toEqual('Loading List');
const d = await fetchData();
expect(d).toHaveLength(data.length);
wrapper.update();
expect(wrapper.find('span').exists()).toEqual(false);
expect(wrapper.html()).toMatchSnapshot();
});
});
});
But, then my test is failed.
<App/> Rendering using enzyme › After loading
expect(received).toEqual(expected) // deep equality
Expected: false
Received: true
35 |
36 | wrapper.update();
> 37 | expect(wrapper.find('span').exists()).toEqual(false);
Does anybody know why it fails? Thanks!
"react": "16.13.1",
"enzyme": "^3.11.0",
"enzyme-adapter-react-16": "^1.15.3",
This issue is not new at all. You can read the full discussion here: https://github.com/enzymejs/enzyme/issues/2073.
To sum up, currently in order to fix act warning, you have to wait a bit before update your wrapper as following:
const waitForComponentToPaint = async (wrapper) => {
await act(async () => {
await new Promise(resolve => setTimeout(resolve));
wrapper.update();
});
};
test('After loading', async () => {
const wrapper = mount(<App />);
expect(wrapper.find('span').at(0).text()).toEqual('Loading List');
// before the state updated
await waitForComponentToPaint(wrapper);
// after the state updated
expect(wrapper.find('span').exists()).toEqual(false);
expect(wrapper.html()).toMatchSnapshot();
});
You should not wrap your whole test in act, just the part that will cause state of your component to update.
Something like the below should solve your problem.
test('After loading', async () => {
await act(async () => {
const wrapper = mount(<App />);
});
expect(wrapper.find('span').at(0).text()).toEqual('Loading List');
const d = await fetchData();
expect(d).toHaveLength(data.length);
await act(async () => {
wrapper.update();
})
expect(wrapper.find('span').exists()).toEqual(false);
expect(wrapper.html()).toMatchSnapshot();
});

React test if something hasn't happened after useEffect

I'm using react-testing-library and jest to test if my API is not invoked when a certain prop is set. Currently the test succeeds immediately without finishing the useEffect() call. How do I make the test wait until useEffect has finished, so I can be certain the API has not been called?
The code:
const MyComponent = ({ dontCallApi }) => {
React.useEffect(() => {
const asyncFunction = async () => {
if (dontCallApi) {
return
}
await callApi()
}
asyncFunction
}, [])
return <h1>Hi!</h1>
}
it('should not call api when dontCallApi is set', async () => {
const apiSpy = jest.spyOn(api, 'callApi')
render(<MyComponent dontCallApi />)
expect(apiSpy).not.toHaveBeenCalled()
})
In your case, you could spy on React.useEffect and provide an alternative implementation. jest.spyOn(React, "useEffect").mockImplementation((f) => f())
so now you dont't have to care about the handling of useEffect anymore.
If you also want to test useEffect in a descent way you may extract the logic in a custom hook and use the testing library for hooks with the renderHooks function to test your use case.
I would test your Component like this:
import React from "react";
import { MyComponent } from "./Example";
import { render } from "#testing-library/react";
import { mocked } from "ts-jest/utils";
jest.mock("./api", () => ({
callApi: jest.fn(),
}));
import api from "./api";
const mockApi = mocked(api);
jest.spyOn(React, "useEffect").mockImplementation((f) => f());
describe("MyComponet", () => {
afterEach(() => {
jest.clearAllMocks();
});
it("should not call api when dontCallApi is set", async () => {
render(<MyComponent dontCallApi />);
expect(mockApi.callApi).toHaveBeenCalledTimes(0);
});
it("should call api when is not set", async () => {
render(<MyComponent />);
expect(mockApi.callApi).toHaveBeenCalledTimes(1);
});
});
Edit 03.07.2020
I recently found out that there is a possibility to query something like you wanted without mocking useEffect. You could simply use the async utilities of react testing library and get the following:
import React from "react";
import { MyComponent } from "./TestComponent";
import { render, waitFor } from "#testing-library/react";
import { api } from "./api";
const callApiSpy = jest.spyOn(api, "callApi");
beforeEach(() => {
callApiSpy.mockImplementation(() => Promise.resolve());
});
afterEach(() => {
callApiSpy.mockClear();
});
describe("MyComponet", () => {
afterEach(() => {
jest.clearAllMocks();
});
it("should not call api when dontCallApi is set", async () => {
render(<MyComponent dontCallApi />);
await waitFor(() => expect(callApiSpy).toHaveBeenCalledTimes(0));
});
it("should call api when is not set", async () => {
render(<MyComponent />);
await waitFor(() => expect(callApiSpy).toHaveBeenCalledTimes(1));
});
});
to get more information about this take a look at the async utilities docs

React Storybook showing only three unit tests

I am facing a very strange situation. I have 9 unit tests that are running perfectly well when I run it through jest command. But when I run storybook it just shows 3 tests out of 9. Storybook version is 5.1.7.
Test which is showing on storybook - Filter Search Orders -
import React from "react";
import expect from "expect";
import { mount } from "enzyme";
import _ from "lodash";
import Root from "../../root";
import { storiesOf, describe, it, specs, beforeEach, afterEach } from "../../../.storybook/facade";
import FilterSearchOrders from "../search-orders/filter-search-orders";
import ordersFilterOptions from "../search-orders/orders-filters-options";
const filterSearchOrdersInfo = {
searchedText: "",
status: "",
pageNo: 0,
};
const getElement = () => {
return (
<Root>
<FilterSearchOrders
searchedText={filterSearchOrdersInfo.searchedText}
status={filterSearchOrdersInfo.status}
pageNo={filterSearchOrdersInfo.pageNo}
/>
</Root>
);
};
storiesOf("Filter Search Orders", module).add("Filter Search Orders", () => {
specs(() =>
describe("Test case for filter search order component", () => {
let wrapper;
beforeEach(() => {
wrapper = mount(getElement());
});
afterEach(() => {
wrapper.unmount();
});
it("should render an input box for searching", () => {
expect(wrapper.find(".search-orders-input"));
});
it("should render a dropdown with options", () => {
const dropdownOptions = wrapper.find(".item").map((node) => node.text());
const items = _.map(ordersFilterOptions, _.property("text"));
expect(dropdownOptions).toEqual(items);
});
it("should render a primary button called search", () => {
expect(wrapper.find(".primary").text()).toEqual("SEARCH");
});
})
);
return getElement();
});
Test not showing on storybook - Menu story -
import React from "react";
import expect from "expect";
import { mount } from "enzyme";
import _ from "lodash";
import { DASHBOARDITEMS } from "../menu/menuItems";
import { storiesOf, describe, it, specs } from "../../../.storybook/facade";
import NavBar from "../menu";
import Root from "../../root";
const getElement = () => {
return (
<Root>
<NavBar items={DASHBOARDITEMS} />
</Root>
);
};
storiesOf("Menu", module).add("Navigation Bar component", () => {
specs(() =>
describe("Main menu", () => {
it("should render menu", () => {
const wrapper = mount(getElement());
expect(wrapper.find(".nestedMenu"));
});
it("should render an image to show logo", () => {
const wrapper = mount(getElement());
expect(
wrapper
.find(".item")
.at(0)
.find("img").length > 0
).toEqual(true);
});
it("should render all links", () => {
const wrapper = mount(<Root>{getElement()}</Root>);
const links = wrapper.find("a").map((node) => node.text());
const items = _.map(DASHBOARDITEMS, _.property("content"));
expect(links).toEqual(items);
});
})
);
return getElement();
});
Okay so I seemed to figure out what was wrong the whole time.
A very stupid mistake by me but I hope it helps anyone who is dealing with same issue.
I checked the console and seems like the fourth story (in order) that I had written had some error and as soon as I resolved that error that story itself and all other story started showing on storybook.
I upgraded my storybook version and seems like all the stories are shown on the UI with errors.

mount(Component).debug() is giving emtpy value

I tried to implement mount from enzyme in the following but found wrapper.debug() is empty.
import {mount} from 'enzyme';
const Foo = () => <div>Sairam</div>;
describe(...
it(...
const wrapper = mount(<Foo/>);
console.log(wrapper.debug()) // gives empty value
UPDATE:
import {
specs, describe, it,
beforeEach, before, after, afterEach, xdescribe
} from 'storybook-addon-specifications';
import {mount, shallow} from 'enzyme';
storiesOf('UI|Components', module)
.add('with text', () => {
const Foo = () => <div>Sairam</div>;
specs(() =>
describe('with text', () => {
it('Clicking on the showFirst button should select the first page', () => {
const wrapper = mount(<Foo/>);
console.log("WRAPPER TEST" , wrapper.debug());
// expect(wrapper.props().chipData).to.equal(ChipData);
wrapper.unmount();
expect(2).toBe(23);
});
})
);
return story;
})
The Reason why it is failed for me is because of adding decorator
addDecorator(StoryRouter()); to my .storybookconfig file

Resources