Unit Testing dynamically imported React Component - reactjs

I have a very simple React component that uses react-loadable to dynamically import another component. The code looks something akin to the following:
import React from 'react';
import Loadable from 'react-loadable';
import LoaderComponent from 'path/to/LoaderComponent';
export default outerLoadableComponent = Loadable({
loader: () => import('path/to/innerComponent'),
loading() {
return <LoaderComponent />
}
});
I am attempting to test this component by using Enzyme to mount outerLoadableComponent, which creates a wrapper around outerLoadableComponent where I can see that the LoaderComponent wrapping it has the loadingState set to true. However, I am stuck at the point where the inner import does not resolve. It seems to be a promise that would only resolve should the import actually go through, however even with some timeouts, it does not work:
const expect = chai.expect;
chai.use(sinonChai);
it('should render the loading state, and innerComponent', (done) => {
const wrapper = mount(
<outerLoadableComponent />
);
expect(wrapper.loading).to.be.true;
setTimeout(() => {
expect(wrapper.loading).to.be.false;
expect(wrapper.loaded).to.exist; // loaded state returns a function
expect(wrapper.find(innerComponent)).to.exist;
done();
}, 500);
});
My babel-rc has dynamic-import-node so running this outside of the test works all fine. But there seems to be no clear/documented way of mocking (with sinon) the results of an import promise. Any ideas?

Related

how can i ise i18n in unit test from react testing library

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)

React lazy load for a function, not a component

I am working on a React v16 app and need to load a heavy (IMO) library for xlsx export of data.
I'm using functional components/hooks.
I understand and have used the <Suspense /> component and lazy for lazy loading modules. but since this item is not a component, it is simply a library function i need to run with an onClick event, i can't use lazy/suspense.
How can i lazy load this function only when needed? (writeXlsxFile)
import React, { useEffect, useState } from "react";
import { connect } from "react-redux";
//...
import writeXlsxFile from "write-excel-file";
//...
const fileCreate = async () => {
await writeXlsxFile(exportData, {
schema,
fileName: `file.xlsx`,
});
};
return(//...
JavaScript, and by direct association, React, supports Dynamic Imports.
So,
const fileCreate = async () => {
const {default: writeXlsxFile} = await import ('write-excel-file')
void await writeXlsxFile(exportData, {
schema,
fileName: `file.xlsx`,
});
}

Testing react component that is dependent on Stateful Context Providers chain in React Testing Library

I'm aware of this, but it's not my case.
Example:
<AuthProvider>
<SessionProvider>
<AnotherProvider>
<OneMoreProvider>
<MyComponent />
All of these providers are actually regular React Components with state and effects, that fetch some data via GraphQL and pass that data as a 'value' prop to MyContext.Provider in return statement.
This enforces me to create lots of mocks for modules that are being used in all of these providers, just to render my own component in testing env.
Any thoughts about what can be done to avoid creating so many mocks?
You can create a helper test lib with a custom render function that wrap your component with the contexts then export all react testing library methods from there
- test/lib.jsx
import React from 'react';
import { render as reactRender } from '#testing-library/react';
export * from '#testing-library/react';
export const render = (MyComponent, options) => {
return reactRender(
<AuthProvider>
<SessionProvider>
<AnotherProvider>
<OneMoreProvider>
{MyComponent}
</OneMoreProvider>
</AnotherProvider>
</SessionProvider>
</AuthProvider>,
options
)
}
Then use this helper lib to import test functions instead of using #testing-library/react directly
import { render } from 'test/lib'
import MyComponent from './MyComponent';
test("My component", () => {
const { getByTestId, ... } = render(<MyComponent>);
...
});

Jest cannot find module from file in the same folder

I'm trying to set up a very basic test with Jest which tests whether App.js renders correctly. I am getting the error
Cannot find module './App' from 'App.test.js'
However, Jest was able to find:
'./App.js'
'./App.test.js'
However, if I try to write import App from "./App.js"; instead of ... from "./App";, I get
Cannot find module './App.js' from 'App.test.js'
How can I make Jest find modules properly?
The project was set up using Create React App, and App.js and App.test.js are located within the same folder (src/components).
App.js
import React, { Component } from "react";
class App extends Component {
render() {
return <div />;
}
}
export default App;
App.test.js
import React from "react";
import { shallow } from "enzyme";
import App from "./App.js";
const app = shallow(<App />);
it("renders correctly", () => {
expect(app).toMatchSnapshot();
});
I think Jest needs to set up itself before rendering components (so don't call <App /> outside of test cases):
it("renders correctly", () => {
const app = shallow(<App />);
expect(app).toMatchSnapshot();
});
However, if the problem is on the import line, assuming you use an up-to-date version and don't pass any CLI options yourself, I would recommend to replace whole content of App.test.js with:
it('', () => console.log(process.env))
and search for the listed environment variables in https://jestjs.io/docs/en/configuration to see if any can affect Jest.

The correct way to unit test react component

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!

Resources