How to rewrite test with jest? - javascript

Hi I had simple snapshot test but I needed to save data from API with redux toolkit and after that it's always failing.
DashboardScreen.tsx
const DashboardScreen = () => {
// added block of code
const dispatch = useDispatch();
const { data: userData } = useGetUserDataQuery();
useEffect(() => {
if (userData) dispatch(setCurrentUser(userData));
}, [dispatch, userData]);
// end of added block of code
return (
<View style={styles.container}>
<View style={styles.containerWidth}>
<Image
style={styles.logo}
source={require('../../assets/images/KonektoSmart-logo.png')}
/>
</View>
</View>
);
};
and the test DashboardScreen-test-tsx
test('renders correctly', () => {
const tree = create(<DashboardScreen />).toJSON();
expect(tree).toMatchSnapshot();
});
● Test suite failed to run - but I tried some of the under and doesn't work.
[#RNC/AsyncStorage]: NativeModule: AsyncStorage is null.
To fix this issue try these steps:
• Run `react-native link #react-native-async-storage/async-storage` in the project root.
• Rebuild and restart the app.
• Run the packager with `--reset-cache` flag.
• If you are using CocoaPods on iOS, run `pod install` in the `ios` directory and then rebuild and re-run the app.
• If this happens while testing with Jest, check out docs how to integrate AsyncStorage with it: https://react-native-async-storage.github.io/async-storage/docs/advanced/jest

To solve this problem I needed to use Provider. For me docs are unclear doing that.
Full code:
import React from 'react';
import { create } from 'react-test-renderer';
import DashboardScreen from '../DashboardScreen';
import { Provider } from 'react-redux';
import store from '../../redux/store';
test('renders correctly', async () => {
const tree = create(<Provider store={store}><DashboardScreen /></Provider>).toJSON();
expect(tree).toMatchSnapshot();
});

Related

import npm modules asynchronously in react.js

I am using parcel as a bundler in React.js project.
How to load npm modules asynchronously in react.js?
There is only one page that uses one specific npm module so I didn't need to load it at first loading.
By avoiding this, I would like to reduce the bundle size.
Could you let me the proper way to do this?
========================
And also, if I understood anything wrongly about the bundle size optimization and lazy loading, please let me know.
By using Dynamic Import you may import the package when you really need the package.
You can use a dynamic import inside an useEffect hook like:
const Page = (props) => {
useEffect(
() => {
const [momentjsPromise, cancel] = makeCancelable(import("moment"));
momentjsPromise.then((momentjs) => {
// handle states here
});
return () => {
cancel?.();
};
},
[
/* don't forget the dependencies */
],
);
};
You can use dynamic imports.
Let's say you want to import my-module:
const Component = () => {
useEffect(() => {
import('my-module').then(mod => {
// my-module is ready
console.log(mod);
});
}, []);
return <div>my app</div>
}
Another way is to code-splitt Component itself:
// ./Component.js
import myModule from 'my-module';
export default () => <div>my app</div>
// ./App.js
const OtherComponent = React.lazy(() => import('./Component'));
const App = () => (
<Suspense>
<OtherComponent />
<Suspense>
);
my-module will be splitted along with Component.
These two patterns should work with any bundler, but it will work client side only.

React app Jest unit test failing with Microsoft application insights react plugin

I'm trying to use the Microsoft's Application Insights JavaScript SDK React Plugin in my React application, and although it's working successfully, I'm having trouble getting my Jest and Enzyme unit test to pass with it.
I've set up my unit test like the following:
import React from 'react';
import {act} from 'react-dom/test-utils';
import ReactDOM from 'react-dom';
import App from '../App.view';
jest.mock('#microsoft/applicationinsights-react-js', () => ({
ReactPlugin: Object,
}));
jest.mock('#microsoft/applicationinsights-web', () => ({
ApplicationInsights: Object,
loadAppInsights: () => ({}),
}));
describe('App View', () => {
it('renders without crashing', () => {
const div = document.createElement('div');
act(() => {
ReactDOM.render(<App />, div);
});
ReactDOM.unmountComponentAtNode(div);
});
});
With my application insights service as:
import {ApplicationInsights} from '#microsoft/applicationinsights-web';
import {ReactPlugin, withAITracking} from '#microsoft/applicationinsights-react-js';
import {createBrowserHistory} from 'history';
// Set up AppInsights connection
const browserHistory = createBrowserHistory({basename: ''});
const reactPlugin = new ReactPlugin();
const ai = new ApplicationInsights({
config: {
instrumentationKey:
process.env.REACT_APP_APPINSIGHTS_KEY || 'xxxxxxxxxxx-xxxxxxxxxx-xxxxxxx-xxxxx',
extensions: [reactPlugin],
extensionConfig: {
[reactPlugin.identifier]: {history: browserHistory},
},
},
});
ai.loadAppInsights();
export default Component => withAITracking(reactPlugin, Component);
export const appInsights = ai.appInsights;
When I run my tests, I keep getting the error, TypeError: ai.loadAppInsights is not a function
Since I'm using jest mocking for the application insights modules, I've tried to mock this method as well in several ways, but with no luck. Any ideas on what I'm missing here? I'm also unable to find any good documentation on how to properly integrate application insights into a React application, which includes proper unit testing.
Thanks in advance!

How to ignore generated Relay query files in Jest tests using create-react-app?

I created a React app using create-react-app and added Relay. I want to test my components with Jest, but the Relay compiler generates files that Jest reads as test files. I can't ignore the files because I'm using create-react-app.
For example a test might look like:
// src/components/MyComponent/__tests__/MyComponent.test.js
import React from 'react';
import graphql from 'babel-plugin-relay/macro';
import { QueryRenderer } from 'react-relay';
import renderer from 'react-test-renderer';
import { MockPayloadGenerator, createMockEnvironment } from 'relay-test-utils';
import MyComponent from '../MyComponent';
const query = graphql`
query MyComponentQuery #relay_test_operation {
myComponent: node(id: "test-id") {
...MyComponent_myComponent
}
}
`;
const rootRender = ({ props }) => <MyComponent myComponent={props.myComponent} />;
it('renders without crashing', () => {
const environment = createMockEnvironment();
const component = renderer.create(
<QueryRenderer
environment={environment}
query={query}
variables={{}}
render={rootRender}
/>
);
environment.mock.resolveMostRecentOperation((operation) =>
MockPayloadGenerator.generate(operation));
expect(component.toJSON()).toMatchSnapshot();
});
relay-compiler will generate a file src/components/MyComponent/__tests__/__generated__/MyComponentQuery.graphql.js for the query.
When running the tests, I get:
FAIL src/components/MyComponent/__tests__/__generated__/MyComponentQuery.graphql.js
● Test suite failed to run
Your test suite must contain at least one test.
How do I get Jest to ignore the generated query files? Is there a way to do this without ejecting from create-react-app?
Try to add this to your package.json
...,
"jest": {
"testPathIgnorePatterns": [
".graphql.js"
]
},
...

Mock out imported Lazy React component

Here's my lazy component:
const LazyBones = React.lazy(() => import('#graveyard/Bones')
.then(module => ({default: module.BonesComponent}))
export default LazyBones
I'm importing it like this:
import Bones from './LazyBones'
export default () => (
<Suspense fallback={<p>Loading bones</p>}>
<Bones />
</Suspense>
)
And in my test I have this kind of thing:
import * as LazyBones from './LazyBones';
describe('<BoneYard />', function() {
let Bones;
let wrapper;
beforeEach(function() {
Bones = sinon.stub(LazyBones, 'default');
Bones.returns(() => (<div />));
wrapper = shallow(<BoneYard />);
});
afterEach(function() {
Bones.restore();
});
it('renders bones', function() {
console.log(wrapper)
expect(wrapper.exists(Bones)).to.equal(true);
})
})
What I expect is for the test to pass, and the console.log to print out:
<Suspense fallback={{...}}>
<Bones />
</Suspense>
But instead of <Bones /> I get <lazy /> and it fails the test.
How can I mock out the imported Lazy React component, so that my simplistic test passes?
I'm not sure this is the answer you're looking for, but it sounds like part of the problem is shallow. According to this thread, shallow won't work with React.lazy.
However, mount also doesn't work when trying to stub a lazy component - if you debug the DOM output (with console.log(wrapper.debug())) you can see that Bones is in the DOM, but it's the real (non-stubbed-out) version.
The good news: if you're only trying to check that Bones exists, you don't have to mock out the component at all! This test passes:
import { Bones } from "./Bones";
import BoneYard from "./app";
describe("<BoneYard />", function() {
it("renders bones", function() {
const wrapper = mount(<BoneYard />);
console.log(wrapper.debug());
expect(wrapper.exists(Bones)).to.equal(true);
wrapper.unmount();
});
});
If you do need to mock the component for a different reason, jest will let you do that, but it sounds like you're trying to avoid jest. This thread discusses some other options in the context of jest (e.g.
mocking Suspense and lazy) which may also work with sinon.
You don't need to resolve lazy() function by using .then(x => x.default) React already does that for you.
React.lazy takes a function that must call a dynamic import(). This must return a Promise which resolves to a module with a default export containing a React component. React code splitting
Syntax should look something like:
const LazyBones = React.lazy(() => import("./LazyBones"))
Example:
// LazyComponent.js
import React from 'react'
export default () => (
<div>
<h1>I'm Lazy</h1>
<p>This component is Lazy</p>
</div>
)
// App.js
import React, { lazy, Suspense } from 'react'
// This will import && resolve LazyComponent.js that located in same path
const LazyComponent = lazy(() => import('./LazyComponent'))
// The lazy component should be rendered inside a Suspense component
function App() {
return (
<div className="App">
<Suspense fallback={<p>Loading...</p>}>
<LazyComponent />
</Suspense>
</div>
)
}
As for Testing, you can follow the React testing example that shipped by default within create-react-app and change it a little bit.
Create a new file called LazyComponent.test.js and add:
// LazyComponent.test.js
import React, { lazy, Suspense } from 'react'
import { render, screen } from '#testing-library/react'
const LazyComponent = lazy(() => import('./LazyComponent'))
test('renders lazy component', async () => {
// Will render the lazy component
render(
<Suspense fallback={<p>Loading...</p>}>
<LazyComponent />
</Suspense>
)
// Match text inside it
const textToMatch = await screen.findByText(/I'm Lazy/i)
expect(textToMatch).toBeInTheDocument()
})
Live Example: Click on the Tests Tab just next to Browser tab. if it doesn't work, just reload the page.
You can find more react-testing-library complex examples at their Docs website.
I needed to test my lazy component using Enzyme. Following approach worked for me to test on component loading completion:
const myComponent = React.lazy(() =>
import('#material-ui/icons')
.then(module => ({
default: module.KeyboardArrowRight
})
)
);
Test Code ->
//mock actual component inside suspense
jest.mock("#material-ui/icons", () => {
return {
KeyboardArrowRight: () => "KeyboardArrowRight",
}
});
const lazyComponent = mount(<Suspense fallback={<div>Loading...</div>}>
{<myComponent>}
</Suspense>);
const componentToTestLoaded = await componentToTest.type._result; // to get actual component in suspense
expect(componentToTestLoaded.text())`.toEqual("KeyboardArrowRight");
This is hacky but working well for Enzyme library.
To mock you lazy component first think is to transform the test to asynchronous and wait till component exist like:
import CustomComponent, { Bones } from './Components';
it('renders bones', async () => {
const wrapper = mount(<Suspense fallback={<p>Loading...</p>}>
<CustomComponent />
</Suspense>
await Bones;
expect(wrapper.exists(Bones)).toBeTruthy();
}

Testing react-loadable components

I'm having trouble testing my React components that use react-loadable. Say, I have a Button component that, depending on whether it receives an icon prop, loads an Icon component like so:
Button.js
const LoadableIcon = Loadable({
loader: () => import('./Icon'),
loading: () => <div>Loading...</div>
})
function Button(props) {
return (
<button
onClick={props.onClick}>
{props.icon &&
<LoadableIcon name={props.icon} />}
{props.children}
</button>
)
}
When I test this component, however, the Icon had not loaded yet, and instead the test only finds the <div>Loading...</div> element...
Button.test.js
import React from 'react'
import {render} from 'react-testing-library'
import Button from '../Button'
describe('Button', () => {
it('renders icon correctly', () => {
const {getByText} = render(
<Button
icon='add'
/>
)
expect(getByText('add')).toBeInTheDocument()
})
})
Is there an elegant way to handle this situation without using actual setTimeouts?
So, the answer is to read the docs - note to self! The solution based on docs was the following:
describe('Button', () => {
it('renders icon correctly', async () => {
const {getByText} = render(
<Button
icon='add'
/>
)
const icon = await waitForElement(() => getByText('add'))
expect(icon).toBeInTheDocument()
})
})
Also, note that async needs to be used together with await.
I don't have personal experience using react-loadable, but I have implemented a similar component that handles code splitting via the dynamic import() syntax.
To get Jest to work with 'loadable' / 'async' components, I had to configure my .babel-rc config for Jest to include the dynamic-import-node babel plugin that way the modules can be properly resolved even when the import is async.

Categories