I am testing my React components using Jest/Enzyme.
In some of the test cases, the test fails with some exception.
What is a sane/standard way to handle error in Jest/Enzyme instead of breaking the test case?
e.g - the following case should pass if the 'Grid' component does not get any data as parameter.However, it fails with an exception which is being thrown out of the Grid component.
test('Grid does not render without data',()=>{
const wrapper=shallow(<Grid/>);
expect(wrapper.length.toBe(0));
})
You should not handle error in your test cases. Instead you should expect the code to throw errors.
You code should look something like this
test('Grid should throw when data is not passed',()=>{
expect(() => shallow(<Grid/>)).toThrow();
})
But ideally, when the right prop is not there, the component should not throw, instead, it should not render.
Related
I'm using the Select component from patternfly and attempting to do a test that involves making changes to the selection. I feel like I'm in a catch 22 as I can make the tests pass without the use act() messages if I do this:
await waitFor(() => {
userEvent.click(screen.getByLabelText(labelText));
});
userEvent.click(screen.getAllByRole('option')[optionNumber]);
however, I then get a lint error saying Avoid using side effects within 'waitFor' callback
If I remove the waitFor(), and do
userEvent.click(screen.getByLabelText(labelText));
userEvent.click(screen.getAllByRole('option')[optionNumber]);
My tests give me the console errors saying Warning: An update to Popper inside a test was not wrapped in act(...).
One point worth noting, I'm only having this issue if I'm appending what ends up being the "Popper" menu (where the options reside) to document.body (or anywhere other than "inline") using the Select component's menuAppendTo prop. I have to do this to prevent some clipping issues with the menu being in a modal.
For now I'm just ignoring the lint issue:
await waitFor(() => {
// eslint-disable-next-line testing-library/no-wait-for-side-effects
userEvent.click(screen.getByLabelText(labelText));
});
userEvent.click(screen.getAllByRole('option')[optionNumber]);
How can I satisfy both the test and the lint?
I was able to fix this issue by putting an await on a findBy for the options prior to clicking them, like so:
userEvent.click(screen.getByLabelText(labelText));
const options = await screen.findAllByRole('option');
userEvent.click(options[optionNumber]);
Solved
Issue is tracked on github
I was attempting to test custom hooks using the react testing library, and the hook I was trying to put under test requires multiple context providers to work correctly. (authentication and notification)
The documentation here only outlines creating a wrapper with a single provider like so:
const wrapper = ({ children }) => <ContextProvider>{children}</ContextProvider>
However, my implementation needed something more complex, like so:
const wrapper = ({ children }) => (
<ToastProvider>
<NotificationProvider>
<AuthProvider>{children}</AuthProvider>
</NotificationProvider>
</ToastProvider>
);
This was was failing at every attempt with the errors:
TypeError: parentInstance.children.indexOf is not a function
OR
Invariant Violation: Drop(...): Nothing was returned from render. This usually means a return statement is missing. Or, to render nothing, return null.
Leading me to believe there was no clear way to provide the right context without abandoning renderHook all together and building a test component that could fire off the necessary behaviors manually.
After a lot more digging I found this error buried in the logs:
Warning: An invalid container has been provided. This may indicate that another renderer is being used in addition to the test renderer. (For example, ReactDOM.createPortal inside of a ReactTestRenderer tree.) This is not supported.
Surely enough, it turns out there is a conflict with react-test-renderer and react-dom which causes calls to ReactDOM.createPortal to fail when under test. I assume this is somewhere in the ToastProvider, but the workaround is pretty simple.
Solved by adding this to the top of my test:
ReactDOM.createPortal = node => node
The pattern to check if a React component contains something with Enzyme + Jest seems to be this one:
expect(shallow(<Field title="hello" />).contains(<p id="hello"></p>)).toBe(true);
The main problem I face with this kind of code is that I have no clue what the problem is if the expectation fails. Because it uses the pattern "expect boolean to be boolean", the only error I get is:
expect(received).toBe(expected) // Object.is equality
Expected: true
Received: false
It does not really help. Is there a way to get more precise error when testing if a React element contains something?
Yes, there are more ways of checking if the element is present within the component. And if your test case fails you can debug the wrapper to check what went wrong. .debug() won't pinpoint the error but I think it will make debugging very easy since you get what is being rendered by shallow/mount.
The following command will help you debug a wrapper.
const wrapper = shallow(<Field title="hello" />);
console.log(wrapper.debug());
I have created a sandbox to demonstrate the same there I use 'contains' and 'find' on the wrapper to check if p is present with id as "hello" and also on the console you can see the output of wrapper.debug()
https://codesandbox.io/s/886799791j
I'm trying to write Jest tests for a React component which contains a DashJS media player. I'm using Enzyme's mount method to try and test the component, but it seems that the DashJS media player fails to mount properly.
In my componentDidMount method, I have the following code:
this.videoManager = dashjs.MediaPlayer().create();
this.videoManager.initialize(this.videoPlayer, videoUrl, true);
// Where this.videoPlayer is a reference to an HTML <video> element
this.videoManager.preload();
The last line (this.videoManager.preload();) produces the following error:
You must first call attachSource() with a valid source before calling this method thrown
When I run the component it works normally - it's only the testing I'm having issues with. I haven't been able to find any related issues/solutions online.
I'm using the following versions of each relevant package:
react: "16.2.0"
dashjs: "2.6.7"
jest: "22.3.0"
enzyme: "3.3.0"
enzyme-adapter-react-16: "1.1.1"
Any help will be appreciated!
That error implies that there was some issue with videoUrl, which caused the value passed in initialize not to be set. When preload checks that a valid source has been set, the error is thrown.
At a guess, is videoUrl an empty string in your test but non-zero length when the component is used normally?
Looking at this again, the problem is probably that you are (presumably) using JSDOM to provide the DOM for your tests, and JSDOM does not provide MediaSource or WebKitMediaSource in window. This causes dash.js to fail to initialise. dash.js should throw a capability error which can be caught using player.on('error', () => {}).
As a side note, you are providing a video object to initialize, as well as setting autoplay to true. Doing the first will cause preload do nothing since it will just load segments in to the SourceBuffer instead, which probably isn't what you wanted.
When I mock a react component like this jest.mock('react-slick', () => 'div'); I get console errors for unknown props passed to tag. I know these errors don't mean anything in this case, but it's quite annoying to see them in the output:
Is there a way to disable these errors? Maybe just don't pass any props to the component when it is mocked?
You can just mock the console.error, like so:
console.error = jest.genMockFn()
Jest will still fail the tests regardless of the above.