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

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

Related

React Testing With Jest - Mock components on the same file except one

I have a tsx file which contains three react components:
import {FC} from 'react';
export const ComponentA: FC<{booleanProp: boolean}> = ({booleanProp}) => {
return (
<>
{booleanProp ? (
<ComponentB />
) : (
<ComponentC />
)}
</>
);
};
export const ComponentB: FC = () => {
return <span>ComponentB</span>;
};
export const ComponentC: FC = () => {
return <span>ComponentC</span>;
};
I want to test ComponentA and mock ComponentB and ComponentC.
This is my test file:
import {FC} from 'react';
import {createRoot, Root} from 'react-dom/client';
import {act} from 'react-dom/test-utils';
import {ComponentA} from './my-components';
jest.mock('./my-components', () => {
const ComponentBMock: FC = () => {
return <span>ComponentB Mock</span>;
};
const ComponentCMock: FC = () => {
return <span>ComponentC Mock</span>;
};
return {
...jest.requireActual('./my-components'),
ComponentB: ComponentBMock,
ComponentC: ComponentCMock,
};
});
describe('ComponentA', () => {
let container: Element | null = null;
let root: Root | null = null;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => {
root?.unmount();
root = null;
});
container?.remove();
container = null;
});
it('should render "ComponentB" when booleanProp is true', () => {
act(() => {
root?.render(<ComponentA booleanProp={true}/>);
});
expect(container?.textContent).toEqual('ComponentB Mock');
});
it('should render "ComponentC" when booleanProp is false', () => {
act(() => {
root?.render(<ComponentA booleanProp={false}/>);
});
expect(container?.textContent).toEqual('ComponentC Mock');
});
});
The problem is that the mocks doesn't seem to take effect and these are the tests result:
Expected: "ComponentB Mock"
Received: "ComponentB"
Expected: "ComponentC Mock"
Received: "ComponentC"
When I debugged the jest mock callback it appears to be called twice. In the first time the requireActual returned undefined for every component, and in the second time it has the real components values.
What am I missing?
Thanks for your help!
So after playing with it and read more about mocking this solution solved my problem.
The difference is the way I import my components and used jest.spyOn to mock them:
import * as MyComponents from './my-components';
const ComponentA = MyComponents.ComponentA;
jest.spyOn(MyComponents, 'ComponentB').mockReturnValue(<span>ComponentB Mock</span>);
jest.spyOn(MyComponents, 'ComponentC').mockReturnValue(<span>ComponentC Mock</span>);
Of course that if you you need to remove the created mock you can restore the mock by calling the spy.mockRestore function.
The full test file:
import {createRoot, Root} from 'react-dom/client';
import {act} from 'react-dom/test-utils';
import * as MyComponents from './my-components';
const ComponentA = MyComponents.ComponentA;
jest.spyOn(MyComponents, 'ComponentB').mockReturnValue(<span>ComponentB Mock</span>);
jest.spyOn(MyComponents, 'ComponentC').mockReturnValue(<span>ComponentC Mock</span>);
describe('ComponentA', () => {
let container: Element | null = null;
let root: Root | null = null;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
root = createRoot(container);
});
afterEach(() => {
act(() => {
root?.unmount();
root = null;
});
container?.remove();
container = null;
});
it('should render "ComponentB" when booleanProp is true', () => {
act(() => {
root?.render(<ComponentA booleanProp={true}/>);
});
expect(container?.textContent).toEqual('ComponentB Mock');
});
it('should render "ComponentC" when booleanProp is false', () => {
act(() => {
root?.render(<ComponentA booleanProp={false}/>);
});
expect(container?.textContent).toEqual('ComponentC Mock');
});
});

Mocking the content of an iframe with React Testing Library

I've got a React component that returns an iframe and handles sending messages to and receiving messages from the iframe. I am totally stumped as to how I can mock the iframe and simulate a message being posted in the iframe with React Testing Library. Any ideas or examples would be very helpful.
Component:
const ContextualHelpClient: React.VFC<CHCProps> = ({ onAction, data }) => {
const [iframe, setIframe] = React.useState<HTMLIFrameElement | null>(null);
const handleMessage = React.useCallback((event: MessageEvent<ContextualHelpAction>) => {
onAction(event.data);
}, [onAction])
const contentWindow = iframe?.contentWindow;
React.useEffect(() => {
if (contentWindow) {
contentWindow.addEventListener('message', handleMessage);
return () => contentWindow.removeEventListener('message', handleMessage);
}
}, [handleMessage, contentWindow]);
React.useEffect(() => {
if (contentWindow) {
contentWindow.postMessage({ type: CONTEXTUAL_HELP_CLIENT_DATA, data }, "/");
}
}, [data, contentWindow]);
return <iframe src={IFRAME_SOURCE} width={300} height={300} ref={setIframe} title={IFRAME_TITLE} />;
};
Test:
import React from 'react';
import { render, screen, fireEvent } from '#testing-library/react';
import ContextualHelpClient from '../ContextualHelpClient';
import { IFRAME_SOURCE, IFRAME_TITLE } from '../constants';
describe('<ContextualHelpClient />', () => {
it('should call onAction when a message is posted', () => {
const handleAction = jest.fn();
const content = render(<ContextualHelpClient onAction={handleAction} />);
const iframe = content.getByTitle(IFRAME_TITLE);
fireEvent(iframe.contentWindow, new MessageEvent('data'));
expect(handleAction).toBeCalled(); // fails
});
});
In my case, using the message type on fireEvent was enough to test this.
import React from 'react';
import { render, screen, fireEvent } from '#testing-library/react';
import ContextualHelpClient from '../ContextualHelpClient';
import { IFRAME_SOURCE, IFRAME_TITLE } from '../constants';
describe('<ContextualHelpClient />', () => {
it('should call onAction when a message is posted', () => {
const handleAction = jest.fn();
const content = render(<ContextualHelpClient onAction={handleAction} />);
const iframe = content.getByTitle(IFRAME_TITLE);
fireEvent(
window,
new MessageEvent('message', {origin: window.location.origin}),
);
expect(handleAction).toBeCalled(); // fails
});
});
Solved!
it('should call onAction when a message is posted', async () => {
const handleAction = jest.fn();
const content = render(<ContextualHelpClient onAction={handleAction} />);
const iframe = content.getByTitle(IFRAME_TITLE) as HTMLIFrameElement;
const TEST_MESSAGE = 'TEST_MESSAGE';
// https://github.com/facebook/jest/issues/6765
iframe.contentWindow?.postMessage(TEST_MESSAGE, window.location.origin);
await waitFor(() => expect(handleAction).toBeCalledWith(TEST_MESSAGE));
});

I want to write the test cases for this file

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.

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.

How to test functions used in a stateless component that's defined outside of the component's scope in Jest/Enzyme?

I'm trying to write a test for the following:
import React from 'react'
import Popup from 'some-library'
const popupConfig = {
home: {
popupValue: 'Hello World',
popupValue: 'action',
popupMessage: 'Get Started'
},
settings: {
popupValue: 'Hello World',
popupValue: 'action',
popupMessage: 'Get Started'
}
}
const closePopup = () => {
Popup.closePopup()
}
const toggleNewPopup = () => {
Popup.togglePopup('some-popup')
}
const GetStartedPopup = ({ moduleName }) => {
if (!Object.keys(popupConfig).includes(moduleName)) return null
const {
popupValue = 'Hi there!',
popupStyle = 'warning',
popupMessage = 'Get Started',
popupBtnFunction = toggleNewPopup
} = popupConfig[moduleName]
return (
<Popup
popupValue={popupValue}
popupStyle={popupStyle}
popupBtnValue={popupMessage}
popupBtnStyle="neutral"
popupBtnFunction={popupBtnFunction}
xPopup={closePopup}
/>
)
}
export default GetStartedPopup
The objective of the test is to make sure that the closePopup and toggleNewPopup functions are called. I'm doing the following to do that for the closePopup function:
import React from 'react'
import { mount } from 'enzyme'
import { Popup } from 'some-library'
import GetStartedPopup from 'widgets/getStartedPopup'
describe('<GetStartedPopup/>', () => {
let wrapper
let props
beforeEach(() => {
props = {
page: 'home'
}
wrapper = mount(<GetStartedPopup {...props}/>)
})
it('should render the component without crashing', () => {
expect(wrapper).toBeDefined();
})
it('should call closePopup', () => {
const spy = jest.spyOn(wrapper.instance(), 'closePopup');
wrapper.instance().closePopup();
expect(spy).toHaveBeenCalledTimes(1);
})
afterEach(() => {
wrapper.unmount()
})
})
I went through the docs for spyOn and other SO threads that tackle issues like this but couldn't resolve how to test the closePopup and toggleNewPopup functions for my case here. When I run the test case written above I get this: TypeError: Cannot read property 'closePopup' of null. What would be the correct way to write the test to make sure that the two functions are called?
Funny that I ran into this myself at work in regards to wrapper.instance() doc
To return the props for the entire React component, use wrapper.instance().props. This is valid for stateful or stateless components in React 15.. But, wrapper.instance() will return null for stateless React component in React 16., so wrapper.instance().props will cause an error in this case.
As for the 3rd party library. You should be mocking any collaborators that your component uses.
import { Popup } from 'some-library';
describe('<GetStartedPopup />', () => {
let wrapper;
jest.mock('some-library', () => {
Popup: jest.fn(),
});
const initialProps = {
page: 'home'
};
const getStartedPopup = () => {
return mount(<GetStartedPopup {...initialProps});
};
beforeEach(() => {
Popup.mockClear()
wrapper = getStartedPopup();
};
it('should call closePopup', () => {
expect(Popup.closePopup()).toHaveBeenCalledTimes(1);
});
...
});

Resources