Jest: Add component children elements to snapshot - javascript

When trying to run Sample.test.jsx with jest, below snapshot is created.
Sample.jsx
class Sample extends React.Component {
render() {
return (
<Link to={'/xyz'}>
<div className={cx('l-tab')}>
Click Me
</div>
</Link>
);
}
}
Sample.test.jsx
import React from 'react';
import { mount } from 'enzyme';
test('testing Component', () => {
const component = mount(
<Sample />
);
expect(component).toMatchSnapshot();
});
Snapshot
exports[`testing Component 1`] = `
<Link to="/xyz" />
`
Question- How can I get the child elements of Link in the snapshot?
Snapshot expected:
exports[`testing Component 1`] = `
<Link to="/xyz" >
<div className='l-tab'>
Click Me
</div>
</Link>
`

If you don't want to do shallow rendering, you could just try the react-test-renderer instead of enzyme:
import React from 'react';
import renderer from 'react-test-renderer';
import Sample from './Sample';
test('testing Component', () => {
const component = renderer.create(<Sample />).toJSON();
expect(component).toMatchSnapshot();
});

enzyme mount children method..
.children([selector]) => ReactWrapper
from enzyme mount docs :
const wrapper = mount(<ToDoList items={items} />);
expect(wrapper.find('ul').children()).to.have.length(items.length);
ref: http://airbnb.io/enzyme/docs/api/ReactWrapper/children.html
you can also find the element by className :
const wrapper = mount(<MyComponent />);
expect(wrapper.find('.my-button').hasClass('disabled')).to.equal(true);
ref: http://airbnb.io/enzyme/docs/api/ReactWrapper/hasClass.html

Related

Getting the error as " Cannot read property 'wait' of null" while testing my component containing i18next library using react testing library in react

This is my file which I am trying to test
So this is my about file which contains i18next library beacuse of which I am getting the error as "cannot read property wait of null
import React, { Component } from 'react';
import LoadingImg from '../../assets/images/loader.gif'
import {getChapterData,fetchChapterData,htmlDecode} from '../helpers/common'
import { Trans, withNamespaces } from 'react-i18next';
class AboutReportChapter extends Component {
constructor(props){
super(props)
this.state={
isDataLoaded:false
}
}
render() {
return (
<div className="drChapterContainer aboutChapterReport" id="pills-AboutReportChapter">
{this.state.isDataLoaded?
<div className="aboutReport">
{this.state.chapterDescription ?
<div className="aboutReportHeader flexContainer">
<div className="hideShowAll show unselectable" id="about_HS"><Trans>HIDE_ALL</Trans></div>
</div>
:null
}
</div>
:
<div className="loading" key="loading_key">
<img src={LoadingImg} alt ="Loading"/>
</div>
}
</div>
)
}
componentDidMount= async()=> {
let result = await fetchChapterData('AboutReportChapter')
this.setState({...result});
}
}
export default withNamespaces() (AboutReportChapter);
And this is my testing file
This is my testing file which I am testing using react testing library jest and I am getting null value on rendering the component
import React from 'react'
import About from '../about'
import { render,unmountComponentAtNode} from "react-dom";
import { screen,getByText,getByTestId,queryByText } from '#testing-library/dom'
import {fetchChapterData,htmlDecode} from '../../helpers/common'
import { act } from "react-dom/test-utils";
jest.mock('../../helpers/common', () => ({
fetchChapterData:jest.fn(),
htmlDecode:jest.fn()
}))
let container = null;
beforeEach(() => {
// setup a DOM element as a render target
container = document.createElement("div");
document.body.appendChild(container);
});
afterEach(() => {
// cleanup on exiting
unmountComponentAtNode(container);
container.remove();
container = null;
});
describe("Introduction component testing",() => {
const fakeData=require('../_Mock Data_/about.json');
fetchChapterData.mockResolvedValue({...fakeData})
it("check if about component is rendered or not", async () => {
await act(async () => {
render(<About t={key => key} />, container);
});
//Assertions
expect(container.getElementsByClassName('aboutReport').length).toBe(1);
expect(container.getElementsByClassName('loading').length).toBe(0);
});
})

React HOC with invoked functions; "Functions are not valid as a React child"

Please see this codesandbox. I am trying to create a react HOC that receives both a functional component and a function that gets invoked in the HOC.
To do this, I need to create a react component that returns this HOC. The function that needs to be invoked in the HOC needs to share it's scope with the react component, since it might do things like change the component's state or make api calls. My attempt at this is is trying to create a nested react component, but when I do, I get the following error:
Warning: Functions are not valid as a React child. This may happen if you return a Component instead of from render. Or maybe you meant to call this function rather than return it in SampleComponent
How can I create a react HOC that accepts both a react component and a function to be invoked? Thanks! The code can also be found below:
index.tsx:
import * as React from "react";
import { render } from "react-dom";
import SampleComponent from "./sampleComponent";
render(<SampleComponent />, document.getElementById("root"));
sampleWrapper.tsx:
import * as React from "react";
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
const Component: React.FC = () => (
<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>
);
return Component;
};
export default sampleWrapper;
sampleComponent.tsx:
import * as React from "react";
import sampleWrapper from "./sampleWrapper";
const SampleComponent: React.FC = () => {
const [title, setTitle] = React.useState("hello world");
const handleTitleChange = (): void => {
setTitle("This is a new title");
};
const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;
return sampleWrapper(SampleInnerComponent, handleTitleChange);
};
export default SampleComponent;
As your sampleWrapper returns a functional component. What you need to do is save the returned functional component into a variable and render the component the same way you do functional component. i.e
import * as React from "react";
import sampleWrapper from "./sampleWrapper";
const SampleComponent: React.FC = () => {
const [title, setTitle] = React.useState("hello world");
const handleTitleChange = (): void => {
setTitle("This is a new title");
};
const SampleInnerComponent: React.FC = () => <h1>{title}</h1>;
const ReturnedSampleComponent = sampleWrapper(SampleInnerComponent, handleTitleChange);
return <ReturnedSampleComponent />;
};
export default SampleComponent;
You can check this codesandbox
It seems you are not returning the React Element, but the Component. You want to return the element React.ReactElemnt. This is what you want i think:
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
return (<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>);
};
another alternative :
import * as React from "react";
const sampleWrapper = (
WrappedComponent: React.FC,
onButtonClick: () => void
): React.FC => {
const Component: React.FC = () => (
<div>
<button onClick={onButtonClick} type="button">
Click Me
</button>
<WrappedComponent />
</div>
);
return <Component/>;
};
export default sampleWrapper;

How to snapshot test a component with nested component?

I'm testing a component with a nested component inside which use redux. I'm using shallow test for the component.
This is my test:
describe("Header", () =>
void it("renders correctly", () => {
const renderer = new ShallowRenderer()
const tree = renderer.render(<Header />)
expect(tree).toMatchSnapshot();
})
The snapshot output is:
exports[`Header renders correctly 1`] = `
<mockConstructor
render={[Function]}
/>
`;
Is this correct? Shouldn't a snapshot show my component?
Try using shallow from the enzyme package:
import { shallow } from 'enzyme';
import Header from './Header';
describe('Header', () => {
it('should render', () => {
const wrapper = shallow(<Header />);
expect(wrapper).toMatchSnapshot();
});
});
I changed my code as you say and the snapshot output for your snippet is:
exports[`Header renders correctly 1`] = `ShallowWrapper {}`;
Looking information about this output i found Jest/Enzyme ShallowWrapper is empty when creating Snapshot
Basically i have to use enzyme-to-json, so i changed my code to:
import { shallow } from 'enzyme';
import toJson from 'enzyme-to-json';
...
describe("Header", () =>
void it("renders correctly", () => {
const tree = shallow(<Header />)
expect(toJson(tree)).toMatchSnapshot()
})
)
In the github site for enzyme-to-json there is a example that show as my test
import React, {Component} from 'react';
import {shallow} from 'enzyme';
import toJson from 'enzyme-to-json';
it('renders correctly', () => {
const wrapper = shallow(
<MyComponent className="my-component">
<strong>Hello World!</strong>
</MyComponent>
);
expect(toJson(wrapper)).toMatchSnapshot();
});
But the snapshot is:
exports[`Header renders correctly 1`] = `
<mockConstructor
render={[Function]}
/>
`;

Jest/Enzyme Class Component testing with React Suspense and React.lazy child component

So I converted an import used in a class component to React.lazy import api and wrapped it in a Suspense tag. When I test that class component, enzyme throws an error "Enzyme Internal Error: unknown node with tag 13". Is there a way to render and test the mount of the lazy loaded component rather than using shallow render?
I've already tried async await to wait until the promise of the lazy load resolved but that didn't work neither, like so:
it('async await mount', () async () => {
const wrapper = await mount(<Component />)
}
here's example code:
Component.js
import React, { PureComponent, Suspense } from 'react'
const ChildComponent = React.lazy(() => import('../ChildComponent'))
export default class Component extends PureComponent {
constructor() {
super()
this.state = {
example: null
}
}
doSomething = () => this.setState({
example: 'example'
})
render() {
return (
<div>
<p>Example</p>
<Suspense fallback={<div>...loading</div>}>
<ChildComponent
example={this.state.example}
doSomething={this.doSomething}
/>
</Suspense>
</div>
)
}
}
Component.test.js
import React from 'react'
import renderer from 'react-test-renderer'
import { mount } from 'enzyme'
import Component from '../../Component'
describe('Component', () => {
// snapshot renders loading and not children
it('example snapshot of tree', () => {
const tree = renderer.create(<Component />).toJSON()
expect(tree).toMatchSnapshot()
})
it('example mount test', () => {
// this test fails and throws error I state above
const wrapper = mount(<Component />)
wrapper.setState({ example: 'example' })
expect(wrapper.state.example).toBe('example')
})
})
I read that Enzyme does not support React 16.6 Suspense API yet but I wanted to know if there was still a way to test the mounted so I can use things like simulate and find API from Enzyme.
Solution
The GitHub issue linked by ChuckJHardy has been resolved merged and released. You are now able to use the mount API in enzyme as of 1.14.0.
References
https://github.com/airbnb/enzyme/pull/1975
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.

How can I test React component's style with Jest + Enzyme?

Alright I have a component called <TestButton />. Inside the <TestButton /> there are two Semantic UI React component, <Button /> and <Header>.
Basically, when the <Button> is clicked, it toggles display: none; to <Header>.
I want to check (I want to learn) on how to assert <Header>'s display: none; when <Button> is clicked.
TestButton.js
const TestButton = (props) => {
return (
<div id='test-button'>
<Header id='please-hide-me' size='huge'>Please Hide Me</Header>
<Button
onClick={
() => {
hiding = !hiding;
let findDOM = document.getElementById(searchForThisID);
if (findDOM) { findDOM.style.display = hiding ? 'none' : ''; }
return hiding;
}
}
>
Sample Toggle
</Button>
</div>
);
};
My unit test is based on How to test style for a React component attribute with Enzyme. It looks like this:
test(`
`, () => {
const wrapper = shallow(<TestButton />);
const button = wrapper.find('Button');
const header = wrapper.find('Header');
const headerStyle = header.get(0).style;
expect(headerStyle).to.have.property('display', '');
wrapper.find('Button').simulate('click');
expect(headerStyle).to.have.property('display', 'none');
}
);
But it has this error:
TypeError: Cannot read property 'have' of undefined
What should I do?
There are a few mistakes in your provided code:
You should not be using DOM element's style property because React does not manage it. Shift the hidden property into the state instead.
I believe headerStyle is a shallow copy of the style object. After you simulate click, it does not get updated. You will have to query the element again for the style object.
to.have.property is not valid Jest syntax. It should be toHaveProperty.
Please refer to the corrected code here. If you paste the following into create-react-app, it should just work.
app.js
import React, { Component } from 'react';
function Header(props) {
return <h1 style={props.style}>Header</h1>;
}
function Button(props) {
return <button onClick={props.onClick}>Click Me</button>;
}
export class TestButton extends React.Component {
constructor(props) {
super(props);
this.state = { hiding: false };
}
render() {
return (
<div>
<Header
id="please-hide-me"
style={{
display: this.state.hiding ? 'none' : '',
}}
>
Please Hide Me
</Header>
<Button
onClick={() => {
this.setState({
hiding: !this.state.hiding,
});
}}
>
Sample Toggle
</Button>
</div>
);
}
}
class App extends Component {
render() {
return (
<div className="App">
<TestButton />
</div>
);
}
}
export default App;
app.test.js
import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
import { TestButton } from './App';
it('renders without crashing', () => {
const wrapper = shallow(<TestButton />);
expect(wrapper.find('Header').get(0).props.style).toHaveProperty(
'display',
'',
);
wrapper.find('Button').simulate('click');
expect(wrapper.find('Header').get(0).props.style).toHaveProperty(
'display',
'none',
);
});

Categories

Resources