React Fragment Not Rendering in Jest Testing - javascript

I'm testing some components and having trouble rendering the rows of a material-ui table specifically when testing with React Testing Library. I'm using react fragments to loop through an array and create the table rows of a material-ui table. I've included the code in the following code sandbox. It works as expected in this case. However, when running the tests, renders properly, but does not render it's contents.
Code Sandbox
My testing code is as follows:
// Link.react.test.js
import React from 'react';
import { cleanup, fireEvent, render, waitFor, screen } from "#testing-library/react";
import { act } from 'react-dom/test-utils';
import {InvoiceRow, InvoiceBreakdown} from '../components/billing/InvoicesBreakdown';
import SongUtils from '../components/songs/api.songs';
jest.mock('../components/songs/api.songs');
// ...
describe("SongBreakdown", function() {
test('SongBreakdown renders appropriately', async () => {
var {container} = await render(< SongBreakdown />);
screen.debug();
const tablerow = await container.querySelectorAll(".SongRow-root-1");
expect(tablerow.length).toEqual(1);
});
});
"screen.debug" Shows the resulting html after rendering. It lacks any of the react.fragments that should be printed.

Update
var {container} = await render(< SongBreakdown />);
screen.debug();
To
var {container, debug} = await render(< SongBreakdown />);
debug();

Related

Reach router navigate updates URL but not component

I'm trying to get Reach Router to navigate programmatically from one of my components. The URL is updated as expected however the route is not rendered and if I look at the React developer tools I can see the original component is listed as being displayed.
If I refresh the page once at the new URL then it renders correctly.
How can I get it to render the new route?
A simplified example is shown below and I'm using #reach/router#1.2.1 (it may also be salient that I'm using Redux).
import React from 'react';
import { navigate } from '#reach/router';
const ExampleComponent = props => {
navigate('/a/different/url');
return <div />;
};
export default ExampleComponent;
I was running into the same issue with a <NotFound defualt /> route component.
This would change the URL, but React itself didn't change:
import React from "react";
import { RouteComponentProps, navigate } from "#reach/router";
interface INotFoundProps extends RouteComponentProps {}
export const NotFound: React.FC<INotFoundProps> = props => {
// For that it's worth, neither of these worked
// as I would have expected
if (props.navigate !== undefined) {
props.navigate("/");
}
// ...or...
navigate("/", { replace: true });
return null;
};
This changes the URL and renders the new route as I would expect:
...
export const NotFound: React.FC<INotFoundProps> = props => {
React.useEffect(() => {
navigate("/", { replace: true });
}, []);
return null;
};
Could it be that you use #reach/router in combination with redux-first-history? Because I had the same issue and could solve it with the following configuration of my historyContext:
import { globalHistory } from "#reach/router";
// other imports
const historyContext = createReduxHistoryContext({
// your options...
reachGlobalHistory: globalHistory // <-- this option is the important one that fixed my issue
}
More on this in the README of redux-first-history
The same issue happens to me when I'm just starting to play around with Reach Router. Luckily, found the solution not long after.
Inside Reach Router documentation for navigate, it is stated that:
Navigate returns a promise so you can await it. It resolves after React is completely finished rendering the next screen, even with React Suspense.
Hence, use await navigate() work it for me.
import React, {useEffect} from 'react';
import {useStoreState} from "easy-peasy";
import {useNavigate} from "#reach/router";
export default function Home() {
const {isAuthenticated} = useStoreState(state => state.auth)
const navigate = useNavigate()
useEffect(()=> {
async function navigateToLogin() {
await navigate('login')
}
if (!isAuthenticated) {
navigateToLogin()
}
},[navigate,isAuthenticated])
return <div>Home page</div>
}
Try and use gatsby navigate. It uses reach-router. It solved my problem
import { navigate } from 'gatsby'

styled-components - test createGlobalStyle

How is it possible to make snapshot tests with styled-components createGlobalStyle?
Tests are running with jest v22.4.4, and styled-components v4.1.2, react v16.7 and jest-styled-components v5.0.1 and react-test-renderer v16.6.3
The output of the snapshot is without the css. but I need a way to test if the css had changes...
E.g.
const BaseCSS = createGlobalStyle`
a { color: red };
`;
And a test
import React from 'react';
import renderer from 'react-test-renderer';
import 'jest-styled-components';
import { BaseCSS } from '../src';
test('test if e', () => {
const tree = renderer.create(<div><BaseCSS /> Test</div>).toJSON();
expect(tree).toMatchSnapshot();
});
edit:
The output of the snapshot looks like: (there is no css in the snapshot)
exports[`test if e 1`] = `
<div>
Test
</div>
`;
I found an answer here and it works!
The global style component will "live" in the <head> tag, not inside the <body> so "you should have aimed for the head".
Here is my working example:
import "jest-styled-components";
import React from "react";
import renderer from "react-test-renderer";
import GlobalStyle from "../../src/styles/GlobalStyle.js";
it("should have global style", () => {
renderer.create(<GlobalStyle />);
expect(document.head).toMatchSnapshot();
});

Is it possible to get the full page html when using React/Enzyme/Jest with Nextjs?

I've got a React component that looks like:
import React from 'react';
import Head from 'next/head';
export default class extends React.Component {
static defaultProps = {
language: 'en',
country: 'us'
};
...
render () {
const language = this.props.language || 'en';
const country = this.props.country || 'us';
return (
<div className="edf-header">
<div className="desktop-header"></div>
<div className="mobile-header"></div>
<Head>
<script dangerouslySetInnerHTML={{__html: `
var secure = "//local-www.hjjkashdkjfh.com";
var perfConfig = {
LOCALE: '${language}_${country}',
I want to confirm, via a test, that the perfConfig is built correctly. I'm testing it with:
import React from 'react';
import Enzyme from 'enzyme';
import Foo from '../../components/Foo';
import Adapter from 'enzyme-adapter-react-16';
import enzymify from 'expect-enzyme';
import Head from 'next/head';
const {mount, shallow, render} = Enzyme;
Enzyme.configure({adapter: new Adapter()});
expect.extend(enzymify());
...
it('renders correct nsgConfig', () => {
const foo = render(<Foo country='ca' language='fr'/>);
console.dir(foo.html());
expect(foo.find('Head')).toExist();
expect(foo.html().indexOf("LOCALE: 'fr_ca'")).toBeGreaterThan(0);
});
The problem is that the html doesn't contain a head tag. The html has the divs but that's it.
How do I get Next/Enzyme to work together here to render the full page? Tried shallow and mount with no luck.
The Head component adds its children to the actual once the page is mounted. You would have to render the full page starting from the _document component. In my tests mount seems to be using a "div" tag where it inserts the component and complains if you actually mount a "head" component so I'm not sure this is even possible.

How to get unit test coverage of components run inside loop in React and Jest

I wrote a unit test for component that spits out atomized component icons (from a custom library built from icomoon.io ) The issue I am having is that despite each component rendering correctly; the test for the component that renders each icon shows no coverage upon the check. The desired outcome is that when i 'npm run test:coverage' it shows coverage for each atomized icon component if it renders correctly in the test.
Directory
* constants
--* iconConstants.js
* components
--* iconGenerator
-----* iconGenerator.js
-----* icons
--------* icon1.js
--------* icon2.js
--------* icon3.js
* __tests__
--* iconGenerator
-----*iconGenerator.test.js
Here is the generator:
import React from 'react';
import PropTypes from 'prop-types';
import cx from 'classnames';
// Constants
import { ICONS } from '../../constants/iconConstants';
const iconGenerator = (props) => {
const { type, size } = props;
const inf = ICONS[type];
return (
<span
className={ cx('ne8-icon', inf.iconCls) }
style={ { fontSize: `${size}em` } }
title={ inf.description } />
);
};
iconGenerator.defaultProps = {
size: 3,
};
iconGenerator.propTypes = {
type: PropTypes.string.isRequired,
size: PropTypes.number,
};
export default iconGenerator;
here is the test:
import React from 'react';
import ReactDOM from 'react-dom';
import iconGenerator from '../../components/iconGenerator/iconGenerator.js';
import { ICONS } from '../../constants/iconConstants';
describe('iconGenerator', () => {
const iconKeys = Object.keys(ICONS);
for (let i = 0; i < iconKeys.length; i += 1) {
it(`renders ${iconKeys[i]} symbol without crashing`, () => {
const div = document.createElement('div');
ReactDOM.render(<iconGenerator type={ iconKeys[i] } />, div);
ReactDOM.unmountComponentAtNode(div);
});
}
});
When I run the test:coverage it shows 0 coverage for any of the icon.js files even though they are being rendered just fine AND that I get 100% coverage for the icongenerator.js file. Any thoughts?
It looks like you're not requiring or rendering any of the icon files, just the one iconGenerator component with many different types, which then change the classname. To get coverage on the various icon*.js, you'd need to require/render them, which you could also do in a loop.
Higher level, I'm not sure how your icon generator actually 'generates' icons, it looks like a regular ol' component that renders a span. Which absolutely works, of course! But it's not creating other components.

React Native jest test: TypeError: Cannot read property 'unsubscribeFromTopic' of undefined

I'm using react-native-fcm and jest to test my React Native app. I have a pretty basic test, it looks like this:
import 'react-native';
import React from 'react';
import PushController from '../app/PushController';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
it('works correctly', () => {
const tree = renderer.create(
<PushController />
);
});
And PushController is somewhat large, so here's the interesting parts
import React, { Component } from 'react';
import { AsyncStorage } from 'react-native';
import FCM from 'react-native-fcm';
export default class PushController extends Component {
(...)
componentDidMount() {
if (this.notificationListener) this.notificationListener.remove();
this.notificationListener = FCM.on('notification', (notif) => {
if (!notif.local_notification) {
this.notifyUser(notif.coffee);
}
});
FCM.unsubscribeFromTopic('/topics/coffee');
FCM.subscribeToTopic('/topics/coffee');
}
(...)
However, when running the test I get
__tests__/PushControllerTest.js
● works correctly
TypeError: Cannot read property 'unsubscribeFromTopic' of undefined
at Object.FCM.unsubscribeFromTopic (node_modules/react-native-fcm/index.js:86:15)
at PushController.componentDidMount (app/PushController.js:44:26)
at node_modules/react-test-renderer/lib/ReactCompositeComponent.js:265:25
at measureLifeCyclePerf (node_modules/react-test-renderer/lib/ReactCompositeComponent.js:75:12)
at node_modules/react-test-renderer/lib/ReactCompositeComponent.js:264:11
at CallbackQueue.notifyAll (node_modules/react-test-renderer/lib/CallbackQueue.js:76:22)
at ReactTestReconcileTransaction.ON_DOM_READY_QUEUEING.close (node_modules/react-test-renderer/lib/ReactTestReconcileTransaction.js:36:26)
at ReactTestReconcileTransaction.TransactionImpl.closeAll (node_modules/react-test-renderer/lib/Transaction.js:206:25)
at ReactTestReconcileTransaction.TransactionImpl.perform (node_modules/react-test-renderer/lib/Transaction.js:153:16)
at batchedMountComponentIntoNode (node_modules/react-test-renderer/lib/ReactTestMount.js:69:27)
I've tried including lots of stuff in the test, like jest.mock('react-native-fcm') and others, but I can't get it to work at all. I get that jest automatically mocks the library, but I don't understand why FCM is undefined. Any ideas?
I managed to solve it, finally! Simply needed to change my test to
import 'react-native';
import React from 'react';
import PushController from '../app/PushController';
// Note: test renderer must be required after react-native.
import renderer from 'react-test-renderer';
import FCM from 'react-native-fcm'; // <-- This
it('works correctly', () => {
FCM.unsubscribeFromTopic = jest.fn(); // <-- These two
FCM.subscribeToTopic = jest.fn();
const tree = renderer.create(
<PushController />
);
});
To make sure the actual calls are mocked. I did a lot of googling before this, so I'm sure this will be useful for someone.

Categories

Resources