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

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)

Related

React Native: platform specific import

Got this weird situation. Building for react native and using a native package from Intercom. Importing it works fine when android or ios. But for the web (or node jest) it throws an error.
So have to do some face-pattern "hacking" like this
utilities/Intercom/index.ios.ts
export { default } from '#intercom/intercom-react-native'
utilities/Intercom/index.web.ts
export default function Intercom() {}
some file that uses Intercom
// #ts-ignore
import Intercom from '~/utilities/Intercom' // Cannot find module '~/utilities/Intercom' or its corresponding type declarations.ts(2307)
...
Intercom.logout() // no TS support
Not only does TS complain, but I also loses all types 😭
Is there any other way to do platform specific import and keep the native types?
The error in jest (node) is Cannot read property 'UNREAD_CHANGE_NOTIFICATION' of undefined which is also described in their docs. Problem is that I can't mock it out when using react-native-web that comes with expo.
I think this is what you're looking for https://stackoverflow.com/a/43531355/1868008
In your utilities directory, create a file named Intercom.d.ts and there place the following
import DefaultIos from "./Intercom/index.ios";
import * as ios from "./Intercom/index.ios";
import DefaultWeb from "./Intercom/index.web";
import * as web from "./Intercom/index.web";
declare var _test: typeof ios;
declare var _test: typeof web;
declare var _testDefault: typeof DefaultIos;
declare var _testDefault: typeof DefaultWeb;
export * from "./Intercom/index.ios";
export default DefaultIos;
Not sure what all those are. Maybe something used in typescript internals.
And for the tests, it seems you'll need to mock every method you use in the code you're testing, e.g., in this App component; I'm using the logEvent method, so I return it in the mock object of the library
import React from "react";
import renderer from "react-test-renderer";
import App from "./App";
jest.mock("#intercom/intercom-react-native", () => ({ logEvent: jest.fn() }));
describe("<App />", () => {
it("has 1 child", () => {
const tree = renderer.create(<App />).toJSON();
expect(tree.children.length).toBe(1);
});
});
App.tsx
...
import Intercom from "./utilities/Intercom";
export default function App() {
Intercom.logEvent("test", {});
...
}
For the web implementation, you could import the type to ensure compliance with the library interface
import type { IntercomType } from "#intercom/intercom-react-native";
const webImplementation: IntercomType = {
// here implement all methods
};
export default webImplementation;
https://github.com/diedu89/expo-isomorphic-import-ts

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.

Unit Testing dynamically imported React Component

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?

Typescript, Jest and i18next: Can't see text in React component

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
}))

Resources