React testing: test that component renders child component - reactjs

I have a react component that renders a bunch of child components.
I am trying to test that this component indeeds renders these components.
I rather avoid Enzyme, so I am trying to use #testing-library/react and react-test-renderer.
I seem to be be able to get the child component via findByType, but I can't find any assertion in the library to check the element is rendered. I've tried checking for nullity but that doesn't work when the component is not there.
I don't want to use react-test-renderer's getByRole because that would be testing the role of the root element of the child component, and I think that belongs to the child component's test. I just want to test the child components are instantiated.
Am I approching this the wrong way? Is it not general practice to test a parent renders its children correctly? Should I be using snapshot testing instead?
App.tsx
...
return (
<Navbar className={styles.navbar} />
<Header />
<Footer />
);
App.test.ts
import { render, screen } from '#testing-library/react';
import Footer from './footer/Footer';
import Navbar from './navbar/Navbar';
describe('App', () => {
let component;
let instance;
beforeEach(() => {
// render(<App />); // Unsure if I should use react-testing-lib or react-test-renderer
component = renderer.create(<App />);
instance = component.root;
});
it('should render a navbar', () => {
expect(instance.findByType(Navbar)).not.toBeNull; ??
expect(instance.findByType('section')).toBeNull; // Just playing with the null assertion here, this causes the test to fail
});
it('should define the footer', () => {
expect(instance.findByType(Footer)).not.toBeNull; ??
});
});

Related

How to test for a global Toast/Snackbar with React Testing Library

I have a material-ui Snackbar component in my root pages/_app file in my project. If a suitable event happens on any component in my project, it dispatches an action to redux to update the "alert" state of my store, which triggers the Snackbar to appear.
What source of approach can I use to run tests on a component level to track that relevant (and important) toast/snackbar messages appear due to events? Is this possible within the confines of react-testing-library?
tests for a given component interacting with snackbar
import { render } from "#testing-library/react";
import user from "#testing-library/user-event";
describe("if the url is invalid", () => {
beforeEach(() => {
render(<SelectMedia />);
user.click(screen.getByRole("button", { name: /search/i }));
});
test("the VideoPlayer does not activate", () => {
expect(screen.getByTestId())
});
test.todo("a warning toast message appears");
// ^ what can I do to make tests like these work?
});
root app
const myApp = () => {
...
return (
<Provider store={store}>
<StyledSnackBar />
<Component {...pageProps} />
// ^ all components (like SelectMedia) exist within <Component ... />
</Provider>
)
}
toast/snackbar does not exist within the <SelectMedia /> component
toast/snackbar exists on a root page as a sibling and uses redux to trigger behavior

React Enzyme Test - DOM manipulation with componentDidMount

I am manipulating the DOM with componentDidMount based on element positions. It works perfectly in React, but I would like to create an unit test for that.
Basically, I have a container with a bunch of children elements and I want to add a class for children that are wrapped (not in the first row). I do that logic in componentDidMount.
How can I test with enzyme unit tests?
I thought to use mount but it does not result. Any idea?
class Container extends Component {
componentDidMount () {
this.updateChildren()
}
updateChildren () {
// logic to add a class if element position is different than 1st line
// calls render again with state update
}
...
}
Unit test:
import React from 'react'
import { mount } from 'enzyme'
import Container from '../Container'
describe('<Container />', () => {
it('renders and hides second row elements'), () => {
const container = mount(
<div style={{ width: 200 }}>
<Container> ...childrens </Container>
</div>
)
expect(wrapper.render()).toMatchSnapshot()
})
})
ComponentDidMount is just like a method in your class.
So, you can directly call the method in your unit test this way
wrapper = shallow(<Your Component />);
// call the method
wrapper.instance().componentDidMount()
You can write a mock implementation for that method if you need.

How test a React Loadable component

I have this component:
import React from 'react';
const UploadAsync = Loadable({
loader: () => import('components/Upload'),
loading: () => <LoadingComponent full />
});
const Component = () => {
return <UploadAsync />
}
export default Component
And the test:
import React from 'react';
import Component from './index';
describe('Component', () => {
it('should render correctly with upload component', () => {
const tree = create(<Component />).toJSON();
expect(tree).toMatchSnapshot();
});
});
How I can see the Upload component and not the Loading component in the snapshot?
exports[`Content should render correctly with form component 1`] = `
<div>
<Loading
full={true}
/>
</div>
`;
So far I have tried setTimeOut and Promises.
Use Loadable.preloadAll() before the tests then you can access the Component you need.
Docs
Simple example:
all imports here
Loadable.preloadAll()
describe('TestName', () => {
//tests
})
I haven't been able to figure this out either, but here are a couple of workarounds that, alone or together, may work reasonably well:
Snapshot test the components that are passed to Loadable
In your case, the test would look something like this:
import React from 'react';
import Component from './components/Upload';
describe('Component', () => {
it('should render correctly with upload component', () => {
const tree = create(<Component />).toJSON();
expect(tree).toMatchSnapshot();
});
});
You could also test <LoadingComponent full /> in a similar fashion. No, this doesn't assure you that the Loadable component is working, but you may find it satisfactory to assume that the react-loadable library is well tested and will work as long as you pass to it your own, properly tested components.
End-to-end browser testing
Using a framework such as Selenium or TestCafe you can write tests that run against your site as it runs in a real browser.
It seems like there is no proper solution but if your test is actually rendering the component in browser inside iframe then you can get your react component by Jquery contentDocument
$('#component-iframe')[0].contentDocument
you can find for some specific element in your component by class or id using
$('#component-iframe')[0].contentDocument.getElementById('componentID')
I have a solution, which I found accidentally, but I don't understand how it works (maybe if we could figure out, we could solve this problem). For now, it's good for a workaround.
If you call mount once outside the test, the dynamic module will load magically:
function mountApp() {
return mount(
<ApolloProvider client={apolloClient}>
<MemoryRouter initialEntries={['/']}>
<App />
</MemoryRouter>
</ApolloProvider>
);
}
mountApp();
// Tests
test('react-loadable module loads', () => {
const wrapper = mountApp();
console.log(wrapper.debug());
});
Here, the App, which contains react-loadable modules loads correctly with all its content available. When I remove the first mountApp, it doesn't work anymore (it loads only the loading string).
Edit:
Actually it works inside the test too, but this way you only need to do this once for every test to work.
I was trying to test a component which had Loadable-components inside of it, and run into a similar problem. I managed to mock those components and that made the parent-component mount (enzyme) as I wanted it to.
I'm leaving out the mocked store and props, as they aren't relevant to the solution
const component = () => {
return mount(
<Provider store={store}>
<DoubleMatrix {...props} />
</Provider>
)
}
// These are the Loadable-components, import { Grid, GridColumn } from 'App/Components/Tables/Grid' in the parent component which I am testing
jest.mock('App/Components/Tables/Grid', () => ({
Grid: () => <div />, // eslint-disable-line
GridColumn: () => <div />, // eslint-disable-line
}))
it('renders', () => {
const wrapper = component()
expect(wrapper).toMatchSnapshot()
})
Here is how you test the loaded component:
import {LoadableFoo} from './loadable-foo';
import {Foo} from './Foo';
it('should load the Foo component', async () => {
// usual shallow render
const component = shallow(<LoadableFoo/>);
// prerender the Loadable component
const loadedComponent = await component.preload();
expect(loadedComponent.Foo).toEqual(Foo);
});

Testing react component enclosed in withRouter (preferably using jest/enzyme)

I have a React component which is enclosed within Higher Order Component withRouter as below:
module.exports = withRouter(ManageProfilePage);
My routes are as below:
<Route path="/" component={AdrApp}>
<IndexRoute component={Login}/>
<Route component={CheckLoginStatus}>
<Route path="manage-profiles/:profileId" component=
{ManageProfilesPage}/>
</Route>
<Route path="*" component={notFoundPage}/>
</Route>
I need to use once of the Router lifecycle methods, that is why I need withRouter:
class ManageProfilePage extends React.Component {
componentDidMount() {
this.props.router.setRouteLeaveHook(this.props.route, () => {
...
})
render(){
...
}
}
I need to test this component using Jest/Enzyme and I wrote the test case as below:
describe('manage profile page test suite', () => {
it('snapshot test', () => {
const setRouteLeaveHook =jest.fn();
let wrapper = shallow(
<ManageProfilePage params={{id : 25, router:
setRouteLeaveHook}}/>
);
expect(wrapper).toMatchSnapshot();
})
})
The issue is it is not rendering one level deep. I am pasting the snapshot below:
exports[`manage drug term page test suites snapshot test 1`] = `
<ManageProfilePage
params={
Object {
"id": 25,
"router": [Function],
}
}
/>
`;
Is there any different way I can write my test case so that I am able to render ManageProfilePage atleast 1 level deep? It is not able to render as it is enclosed within WithRouter? How do we test these type of components?
Normally if we try to test such components we won’t be able to render it as it is wrapped within WithRouter (WithRouter is a wrapper over a component which provides
Router props like match, route and history to be directly used within the component).
module.exports = withRouter(ManageProfilePage);
To render such components, we have to explicitly tell it to render the wrapped component using WrappedComponent keyword.
For Eg. we will use below code for snapshot test:
describe('manage profile page test suite', () => {
it('snapshot test', () => {
const setRouteLeaveHook =jest.fn();
let wrapper = shallow(
<ManageProfilePage.WrappedComponent params={{id : 25, router:
setRouteLeaveHook}}/>
);
expect(wrapper).toMatchSnapshot();
})
})
This will tell enzyme to do shallow rendering (Shallow Rendering renders only that particular component and skips child components) for ManageProfilePage which is wrapped component within WithRouter.
Shallow rendering will only render one level, that's part of the specs for it.
you can use Mount which will render the whole tree, but I don't think you can limit how many levels deep it will render.
In any case, when using High Order Components I usually just export the base component as well(before wrapping it), this way I can do all my tests without the wrapper, and simply pass mocks for the required providers.
same thing with a Connect component with redux, you export your regular component and test the different props on that, instead of the connected one.
also note that some with... wrappers do not expose the inner instance(some do, but some don't) , so testing on your own component instead of the wrapper helps there as well.
I think you should try this
describe('manage profile page test suite', () => {
it('snapshot test', () => {
let wrapper = shallow(
<ManageProfilePage.WrappedComponent/>
);
expect(wrapper).toMatchSnapshot();
})
})
What I did which fixed the problem:
In "this.props.match.params.id" is used the post component
In the test case
const miniProps = {
otherProps,
match:{params:{id:'112'}}
};
const wrapper = shallow();

Enzyme/Mocha: How to test a react component function by firing an onChange event from a child component

I'm using enzyme/mocha to test my react component.
I have a parent component which I am testing.
let wrapper = mount(<Parent />);
and this parent has a child component in it's render function
render: function() {
<Child onChange={this.foo} />
},
foo: function() {
console.log("I was called");
}
I would like the child's onChange function to fire so that I can test my parents foo function.
So far, I have found no way to do this - I've read about sinon and stubbing but that's mostly about intercepting functions and not firing them.
The following test
shallow(<Parent />).instance().foo();
is a weak test because it doesn't test the line of code connecting my child and parent, and in case I haven't written a unit test for my child, it doesn't test the child's onChange functionality either. IMHO - If breaking up my components to parents/children means less testability - then something is wrong with this framework
Any help will be appreciated, thanks
This is something that I do in a lot of my tests. The method that I've found that works best for me is to manually invoke the child component's onChange handler, and make your assertions based on the behavior you expect to happen as a result.
So let's say you have a Parent component that looks like this:
import React from 'react';
import Child from './child';
export default class extends React.Component {
render() {
return (
<div>
<Child onChange={this.foo} />
</div>
);
}
foo() {
console.log('bar');
}
}
The onChange prop passed to child will log the string 'bar' when invoked. This is the behavior we want to test. To do this, we'll need to take the following steps:
Stub console.log using your mocking library of choice (I'll be using Sinon for this example)
Create a shallow instance of the Parent component, and obtain a reference to its Child.
Manually invoke the Child's onChange prop.
Assert that console.log was called once, and with a single argument: 'bar'
And here is how I would do that (using mocha and chai):
import Foo from './foo';
import React from 'react';
import {shallow} from 'enzyme';
import sinon from 'sinon';
import sinonChai from 'sinon-chai';
import chai, {expect} from 'chai';
describe('Foo', () => {
let renderedElement;
function renderComponent() {
const componentElement = React.createElement(Foo);
renderedElement = shallow(componentElement);
}
before(() => {
chai.use(sinonChai);
});
it('should log the string "bar" when the child component is changed', () => {
//step 1
sinon.stub(console, 'log');
//step 2
renderComponent();
const childComponent = renderedElement.props().children;
//step 3
childComponent.props.onChange();
//step 4
expect(console.log).to.have.callCount(1);
expect(console.log).to.be.calledWith('bar');
//clean up
console.log.restore();
});
});
The reason I like this approach is because it's testing component behavior as opposed to simply testing that it was passed a function as a prop that happens to be equal to another function.
Try breaking your tests into parts. For example...
Firstly, test that the expected function was passed to your child component:
import { shallow } from 'enzyme';
const actual = shallow(<Parent />);
const expected = <Child onChange={actual.instance().foo} />
expect(actual.matchesElement(expected)).true;
I like to use the simple matchesElement approach above when there is little being rendered in the Component I am testing, but you could also use a find selector to find the Child instance and then test it.
Then test your foo function seperately:
import { shallow } from 'enzyme';
const actual = shallow(<Parent />).instance().foo();
const expected = 'expected return of foo'
expect(actual).equals(expected);
You can test your Child component separately and how it handles it's onChange prop.
Some of the enzyme API's being used here:
.instance()
.matchesElement(node)
Also look at:
.containsMatchingElement(node)
.find(selector)

Resources