We have a React Native app, and have trouble in Jest testing this:
import React from 'react';
import { render } from '#testing-library/react-native';
import MyScreen from '../../../../../src/screens/MyScreen/index';
import Provider from '../../../../__helpers__/Provider';
import { t } from 'core/utils';
import '#testing-library/jest-dom';
jest.mock('#react-navigation/native', () => {
return {
...jest.requireActual('#react-navigation/native'),
useNavigation: () => ({
navigate: jest.fn(),
}),
};
});
jest.mock('#react-navigation/core', () => {
return {
...jest.requireActual('#react-navigation/core'),
useFocusEffect: () => ({
navigate: jest.fn(),
}),
};
});
describe('<AddEditSchedulable />', () => {
it('tests a button is disabled', () => {
const myProperty = {
myData: 'myData'
};
const myRender = render(
Provider(() => <MyScreen myProperty={myProperty} />),
);
const button = myRender.getByText(t('common.buttons.save')); // Returns a complex "_fiber" object.
expect(button).toBeDisabled(); // Expects an HTML element.
});
});
The button returned by getByText contains an object we dont understand containing lots of "_fiber" objects. From this I think we need to get HTML elements to correctly use the toBeDisabled function, but this is React Native and I dont think it uses HTML elements under the hood.
So can we either, get HTML elements from React Native, or can we get functions that understand React Native elements that have the functionality we need (at least accessing properties, ie "disabled")?
We are in circles because standard React seems very different to React Native in Jest tests.
The library #testing-library/jest-dom is meant only for #testing-library/react. Since you are using React Native, the custom matcher toBeDisabled() doesn't understand the element. The import for react native is below.
import '#testing-library/jest-native/extend-expect';
Remove the import and remove it from the project.
import '#testing-library/jest-dom';
Also, please make sure this returns a react native component.
Provider(() => <MyScreen myProperty={myProperty} />),
Related
I'm trying to get translations from i18n files in my unit testing, I've seen other answers but they work with just one i18n file, My problem is that, I have 2 files and the folder structure is like this,
i18n/en/translation.json
i18n/es/translation.json
and translation.json file is written like this
{... "info":"information", "name":"Name", ...}
doesn't have an export default.
and here is my test file,
import React from 'react'
import '#testing-library/jest-dom'
import {render} from '#testing-library/react'
import AddUsers from '../../components/AddUsers'
test('Render OK',()=>{
const menuLinkUp =false
const component =render(
<AddUsers/>
)
component.getByText(" how can i call my i18n?")
})
I'm using react testing library and jest for doing this.
There is a section in the documentation: https://react.i18next.com/misc/testing.
I would probably mock the react-i18next module, as it requires the least amount of changes.
jest.mock('react-i18next', () => ({
// this mock makes sure any components using the translate HoC receive the t function as a prop
withTranslation: () => Component => {
Component.defaultProps = { ...Component.defaultProps, t: () => "" };
return Component;
},
}));
(If you actually want to "inject" the translations: https://react.i18next.com/misc/testing#example-test-using-this-configuration)
Trying to test the Material-UI Slider with Reat-Test-Renderer gets an error: Uncaught [TypeError: Cannot read property 'addEventListener' of null]
Codesandbox Link
import React from "react";
import { Slider } from "#material-ui/core";
import renderer from "react-test-renderer";
it("should render", () => {
renderer.create(<Slider />);
});
This is not the case with any other Material UI components that I know of.
It seems to be related to forwardRef as described here, but I could not figure out a way to get it to work.
EDIT Unfortunately switching over to #testing-library/react is not an option this project I'm working on.
EDIT 2 The reason I am doing this is because I am trying to render and test a more complex component of my own which contains the Slider. It took me a while to figure out that this is what's causing the issue, and the code above is the minimal amount of code to replicate the issue.
EDIT 3 Error message screenshot
React Test Renderer only renders the components as a JS object rather than to a browser environment. Hence, anything that uses dom based refs will have this issue. Related jest issue: https://github.com/facebook/jest/issues/5462.
There is a section in the React Docs for a way to workaround it: https://reactjs.org/docs/test-renderer.html#ideas
EDIT:
Here's a working test. It likely makes a lot of the functionality of the slider not work, since we are setting the internal refs to null. But it does run. The "Next" branch on MUI doesn't have the findDOMNode function there, so that might be easier to work with.
I wasn't able to get these to work on CodeSandbox because jest was undefined, and I couldn't find how to fix that.
import React from 'react';
import { Slider } from '#material-ui/core';
import renderer from 'react-test-renderer';
jest.mock('react-dom', () => ({
// the useIsFocusVisible function in Slider.js (MUI component) uses findDOMNOde
// Luckily it checks if there's nulls, so we can return a null
// https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/utils/useIsFocusVisible.js#L136
findDOMNode: () => null,
}));
it('should render', () => {
renderer.create(<Slider />);
});
If you want to try and mock the full functionality (sort of), you can use this implementation of the findDOMNode mock, which will return the values that are needed for the useIsFocusVisible to run successfully including adding the event listener:
import React from 'react';
import { Slider } from '#material-ui/core';
import renderer from 'react-test-renderer';
jest.mock('react-dom', () => ({
// the useIsFocusVisible function in Slider.js (MUI component) uses findDOMNOde
// https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/utils/useIsFocusVisible.js#L136
findDOMNode: (instance) => {
return { ownerDocument: instance };
},
}));
it('should render', () => {
let eventListenerFn = jest.fn();
renderer.create(<Slider />, {
createNodeMock: (element) => {
if (element.type === 'span') {
return {
addEventListener: eventListenerFn,
};
}
},
});
});
In order to figure out how to get these tests to succesfully run, I had to step through the error stacks/MUI source code to find out what's going wrong and what values were needed to be mocked in order to not throw errors.
Original, showing how much better #testing-library is for this:
However, may I suggest using #testing-library? It's nice to work with and uses jsdom to render the components so it'll work with this component.
I went ahead and made a couple of random tests for the Slider using the testing library to show that it works with Slider and throws no errors:
import React from "react";
import { Slider } from "#material-ui/core";
import { render } from "#testing-library/react";
it("should render", () => {
render(<Slider />);
});
it("should render an input", () => {
const component = render(<Slider />);
const input = component.baseElement.querySelector("input");
expect(input).toBeDefined();
});
it("Should have the correct value", () => {
const component = render(<Slider value={50} />);
const input = component.baseElement.querySelector("input");
expect(input.value).toBe("50");
});
CodeSandbox
I am writing a test for a component that is wrapped in a withStyles() from Material UI using Jest. I need to test the children elements, but my wrapper is undefined.
I've seen another post similar to writing tests with withStyles(), but the undefined error still persists.
Test File:
import { shallow } from 'enzyme';
import { TempComponent } from '../../../../src/components/helpers/temp';
describe('temp', () => {
let wrapper;
const renderComponent = () => shallow(<TempComponent />);
beforeEach(() => {
wrapper = renderComponent();
});
it('render correctly', () => {
expect(wrapper.type()).toEqual('div');
});
});
Component:
import React from 'react';
import { withStyles } from '#material-ui/core/styles';
const TempComponent = () => <button>Click Me!</button>;
export default withStyles({})(TempComponent);
I get this error for my test:
Invariant Violation: ReactShallowRenderer render(): Shallow rendering works only with custom components, but the provided element type was undefined.
I am wanting the wrapper component to behave the same way as a wrapper without the withStyles() component. Any help would be appreciated!
Note: I am not doing snapchat testing with jest
This appears to be the issue and solution:
https://github.com/mui-org/material-ui/issues/9266#issuecomment-349447137
The issue isn't about the children elements but with the intermediary
element, it's creating. The shallow() API of enzyme only render one
level depth. Any higher-order component is going to obfuscate the
children. You have different workarounds. I would encourage you to
refer to the enzyme documentation. Still, here they are:
use mount()
use .dive()
use our createShallow() public API
use our internal until() helper
I am using jest and enzyme to test my react component which relies on antd - a 3rd react UI library.
For illustrative purpose, I produce the minimal code that is fair enough to show my question.
See my test component as follow:
import React from 'react';
import { Button } from 'antd';
function Test({onSubmit}) {
return (
<div>
<Button htmlType="submit" type="primary" />
</div>
);
}
export default Test;
Q1: I mock up the Button as below since unit test requires us to isolate the target and mock up any other dependencies.
Is that correct?
__mocks__
antd.js
antd.js
import mockComponent from '../mockComponent';
const list = [
'Form',
'Input',
'Button',
'Spin',
'Icon'
];
const mockups = list.reduce((prev, next) => {
prev[next] = mockComponent(next);
return prev;
}, {});
mockups.Form.Item = mockComponent('Form.Item');
export const Form = mockups.Form;
export const Input = mockups.Input;
export const Button = mockups.Button;
export const Spin = mockups.Spin;
export const Icon = mockups.Icon;
mockComponent.js
import React from 'react';
export default function mockComponent (name) {
return props => React.createElement(name, props, props.children);
}
Q2. I have got the following warning even if the test passes. How can I resolve that?
test.spec.js
import React from 'react';
import { shallow, mount } from 'enzyme';
import renderer from 'react-test-renderer';
import Test from '../components/question';
describe('mount test', () => {
let wrapper;
let props;
let mountedLogin;
const test = () => {
if (!mountedLogin) {
mountedLogin = mount(<Test {...props} />);
}
return mountedLogin;
};
beforeEach(() => {
mountedLogin = null;
});
it('always render test as the root', () => {
const divs = test().find('div');
expect(divs.length).toBeGreaterThan(0);
});
});
warning
console.error node_modules/fbjs/lib/warning.js:36
Warning: Unknown prop `htmlType` on <Button> tag. Remove this prop from the
element. For details, see
in Button (created by Unknown)
in Unknown (created by Test)
in div (created by Test)
in Test (created by WrapperComponent)
in WrapperComponent
A side note, I haven't got any jest configs in my package.json.
Can anybody help me out with these.
Many thanks
Q1: I mock up the Button as below since unit test requires us to
isolate the target and mock up any other dependencies. Is that
correct?
Currently, the recommended approach for React unit testing is shallow rendering. It basically renders the given component one level deep. If we shallow render your Test component, it will call render method of Test component, but not the render method of Button component. Even though Button is 3rd party component and dependency, we don't need to mock it. Because we don't render it. But still, we will be able to see whether it's in the component tree and it has got the correct props. This is what essentially we will assert with the mocking approach also. However, shallow rendering has few limitations also, but usually, it works very well for most of the scenarios.
But you can obviously mock children and render the whole component also. But it's time-consuming and problematic, at least with my experience.
Q2: I have got the following warning even if the test passes. How can
I resolve that?
Since you pass a string as name for React.createElement, React think you want to create a normal HTML element, and not a React component. But since there is a no HTML element call Button (case sensitive) and it doesn't know prop called htmlType, you get this unknown prop type warning. To prevent this warning, either you can stop passing props to React.createElement or pass a mock component to React.createElement instead of the name string.
import React from 'react';
function Mock(){
retun (<div/>);
}
export default function mockComponent (name) {
return props => {
return React.createElement(Mock, {...props, name}, props.children);
}
}
If you need to read more about react unit testing, I suggest you to go through this thread from react discuss forum.
Hope this helps!
I am testing a React component that uses i18next for internationalization.
The component:
import * as React from "react";
import { t } from "i18next";
export function Hello(_props) {
return <div className="widget-header">
{t("Hello, world!")}
</div>;
}
The test:
import * as React from "react";
import { render } from "enzyme";
import { Hello } from "./hello";
describe("<Hello />", () => {
it("says hi", () => {
let hi = render(<Hello />);
// FAILS!
// Expected "" to contain "Hello"
expect(hi.text()).toContain("Hello");
});
})
My suspicion is that Jest is stubbing i18next.t() to return undefined rather than "Hello, world!", but I am unsure of that.
I have tried to unmock("i18next") without any luck.
How do I write tests for i18next components in Jest?
UPDATE: I was using Typescript as my compiler. It appears that there is a hoisting issue with the loader I use for Typescript (ES6 hoists imports, jest.unmock is not- Babel users have a plugin to handle this).
Not sure why its not working but you can just mock put i18next like this:
jest.mock('i18next', () => ({
t: (i) => i
}))