Seems like when using the withStyles HOC it'll actually break my Jest test. It creates children elements where it shouldn't.
Here is the code to reproduce it:
import React from 'react';
import ReactDOM from 'react-dom';
import { shallow } from 'enzyme';
import withStyles from 'material-ui/styles/withStyles';
import LoadingWrapper from './LoadingWrapper';
const ReproBug = ({ loading, children }) => {
if (loading) {
return <span>Loading...</span>;
}
return children;
}
const ReproBugWithStyles = withStyles({})(ReproBug);
const children = <span>Dummy Children!</span>;
it('should not render children if loading is true', () => {
const wrapper = shallow(
<ReproBug loading={true}>{children}</ReproBug>
);
expect(wrapper.contains(children)).toEqual(false);
});
it('should not render children if loading is true', () => {
const wrapper = shallow(
// using ReproBugWithStyles instead of ReproBug
<ReproBugWithStyles loading={true}>{children}</ReproBugWithStyles>
);
expect(wrapper.contains(children)).toEqual(false); //<--- this check fails, it is actually true
});
I use the create-react-test
and simply run yarn test
. (they use Jest and enzyme IIRC)
I also notice this in "real world" usage beside the jest test. For example I use this kind of logic on my <LoadingWrapper>
for my Apollo requests and Apollo set the loading
and data
(and some more) variable. So I could make a simple Wrapper which avoid loading the children elements as long as data isn't recieved.
<LoadingWrapper loading={loading}>
<UseDataElement data={data} />
</LoadingWrapper>
With this bug the <UseDataElement>
element is actually "rendered" (it is not) or at least triggered so I receive propTypes warning for missing data
.
Mh I'll get similar errors when trying some of your intern testing code. For example when trying this:
https://github.com/mui-org/material-ui/blob/930937827c58f9468299e9760ac9f88e2b34ebcd/src/Divider/Divider.spec.js#L17-L20
I'll get this error:
Expected value to equal:
"hr"
Received:
"Divider"
Maybe I should use your testing scripts instead of the create-react-app ones.
@Skaronator I was having a ton of issues testing my application. Once I found Mui's docs on testing and used their Shallow and Mount I had no issues.
It creates children elements where it shouldn't
@Skaronator The issue isn't about the children elements but with the intermediary element, it's creating. The shallow()
API of enzyme only render one level depth. Any higher-order component is going to obfuscate the children. You have different workarounds. I would encourage you to refer to the enzyme documentation. Still, here they are:
mount()
.dive()
createShallow()
public APIuntil()
helper@codepressd how did you manage to get testing working? I keep getting issues like find isn't a function. I cannot target my clickable objects for the life of me.
@FahdW I'm using create-react-app
and therefore Jest for the test. find()
works fine for me but I use childAt(<int>)
or at(<int>)
most of the time. Here is a click test I'm using:
import React from 'react';
import ReactDOM from 'react-dom';
import { createShallow, getClasses } from 'material-ui/test-utils';
import AppDrawerElements from './AppDrawerElements';
describe('<AppDrawerElements />', () => {
let shallow;
const TestIcon = () => <span>Not a Icon</span>;
const URL = {
display: {
url: '/theUrl/with/more',
name: 'Testing...',
display: true,
icon: <TestIcon />,
},
};
beforeAll(() => {
shallow = createShallow({ dive: true });
});
it('should fire onClose when click the close button', () => {
const onCloseMock = jest.fn();
const onClose = jest.fn(() => {
onCloseMock();
});
const wrapper = shallow(<AppDrawerElements url={URL} onClose={onClose} />);
wrapper
.childAt(0)
.childAt(0)
.childAt(0)
.simulate('click');
expect(onCloseMock).toBeCalled();
});
});
@Skaronator Thanks for this! How would you test if the function itself was contained in the component? I am guessing you call a spy, so far jest's spy has been underwhelming and I have yet to get it to work. I was going to send a spy onto my dispatch to make sure that some of the internal functions are firing off dispatch calls. But this is good to point me in the right direction.
@FahdW You might find it helpful to look at some of Material-UI's own tests.
@mbrookes Thanks will take a look at your source, hopefully find something to help me.
I had some trouble getting my head around why shallow
wasn't working for some tests when I started using withStyles
, so I broke it down into a few self-contained examples at https://gist.github.com/a-h/21df0f432ae02a6dfed941debb0e5950
Once I did that, I realised that I needed to switch from shallow
to mount
since withStyles
is a higher order component and 'wraps' the other component.
I found that material-ui was not breaking my tests even when using withStyles and that this worked just fine using plain old enzyme:
import { configure, shallow } from 'enzyme';
import Adapter from 'enzyme-adapter-react-16';
configure({ adapter: new Adapter() });
// CODE OMITTED HERE
it('renders title matches it with containsMatchingElement', () => {
const wrapper = shallow(<ElfHeader />);
const target = <Typography>Address Maven</Typography>;
expect(wrapper.dive().containsMatchingElement(target)).toBe(true);
});
Here was the JSX I wanted to match:
<Typography variant="title" color="inherit" className={classes.flex}>
Address Maven
</Typography>
And here was the messy code material-ui produced:
<WithStyles(Typography) variant="title" color="inherit" className="ElfHeader-flex-100">
Address Maven
</WithStyles(Typography)>
Nevertheless, my test passed.
I say this only because this thread is a bit misleading for those who get errors and don't know their source. I guess my point is simply that in most cases, your tests should pass. Note this line from the docs: "You can take advantage of them if you so choose." Not that you have to use them, but use them if you find them convenient.
The shallow = createShallow({ dive: true });
totally made my day... 馃巻
Why this issue is closed?
None of those is working for me,
Still get displayed with
<WithStyles(Component)
Cannot go past through it...
I'm having the same issue as Matteo. When testing the output in Jest i get For example if I want to see if a Drawer component is present I have to test for Is there a way to get the test render to be the unwrapped component? or is the only way to test for the
It shouldn't be too long before we start/try using hooks over higher-order component internally for the styles. I'm not sure what is the implication for shallow rendering. It will most likely solve this class of issues.
I solved my issue, was an obvious fix. I was using the component name in quotes eg .find('Avatar') instead of importing it into the test and calling it directly .find(Avatar) - no quotes. That worked.
@jondbm Solved the same way
I am able to proceed with createShallow and it works fine for Components using WithStyles but I couldn't make it work with Components which needs redux.
Works:
import PropTypes from "prop-types";
import React, { Component } from "react";
import { connect } from "react-redux";
import { makeStyles, withStyles } from '@material-ui/core/styles';
import CircularProgress from '@material-ui/core/LinearProgress';
const useStyles = (theme) => ({
root: {
flexGrow: 1,
},
extendedIcon: {
marginRight: theme.spacing(1),
}
});
class LogoutSuccess extends Component {
render() {
const { classes } = this.props;
return (
<p>
<CircularProgress size={24} className={classes.extendedIcon} />
</p>
);
};
}
LogoutSuccess.propTypes = {
classes: PropTypes.object.isRequired,
};
export default (withStyles(useStyles)(LogoutSuccess));
import { Provider } from "react-redux";
import React from "react";
import Enzyme, { mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import LogoutSuccess from "../../../src/client/components/logoutsuccess";
import sinon from "sinon";
import configureMockStore from "redux-mock-store";
import axios from "axios";
import thunk from "redux-thunk";
import * as mockData from '../mock';
import { createShallow } from '@material-ui/core/test-utils';
Enzyme.configure({ adapter: new Adapter() });
const getStore = props => {
const intialState = props;
const middlewares = [thunk.withExtraArgument(axios)];
const mockstore = configureMockStore(middlewares);
const store = mockstore(intialState);
return store;
};
describe("Home Page ", () => {
let sandbox;
beforeEach(() =>
sandbox = sinon.createSandbox()
);
afterEach(() => sandbox.restore());
it('renders title matches it with containsMatchingElement', () => {
const shallow = createShallow();
const wrapper = shallow(<LogoutSuccess />);
expect(wrapper.dive().find(CircularProgress)).toHaveLength(1);
});
});
Not working:
import PropTypes from "prop-types";
import React, { Component } from "react";
import { connect } from "react-redux";
import { makeStyles, withStyles } from '@material-ui/core/styles';
import CircularProgress from '@material-ui/core/LinearProgress';
const useStyles = (theme) => ({
root: {
flexGrow: 1,
},
extendedIcon: {
marginRight: theme.spacing(1),
}
});
class LogoutSuccess extends Component {
render() {
const { classes } = this.props;
return (
<p>
<CircularProgress size={24} className={classes.extendedIcon} />
</p>
);
};
}
const mapStateToProps = state => ({
countryCodes: state.formatbanner.countryCodes
});
LogoutSuccess.propTypes = {
classes: PropTypes.object.isRequired,
};
export default connect(mapStateToProps, null, null, { withRef: true })(withStyles(useStyles)(LogoutSuccess));
import { Provider } from "react-redux";
import React from "react";
import Enzyme, { mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import LogoutSuccess from "../../../src/client/components/logoutsuccess";
import sinon from "sinon";
import configureMockStore from "redux-mock-store";
import axios from "axios";
import thunk from "redux-thunk";
import * as mockData from '../mock';
import { createShallow } from '@material-ui/core/test-utils';
Enzyme.configure({ adapter: new Adapter() });
const getStore = props => {
const intialState = props;
const middlewares = [thunk.withExtraArgument(axios)];
const mockstore = configureMockStore(middlewares);
const store = mockstore(intialState);
return store;
};
describe("Home Page ", () => {
let sandbox;
beforeEach(() =>
sandbox = sinon.createSandbox()
);
afterEach(() => sandbox.restore());
it('renders title matches it with containsMatchingElement', () => {
const shallow = createShallow();
const mockstore = configureMockStore();
const store = mockstore(mockData.data);
const wrapper = shallow(<Provider store={store}><LogoutSuccess /></Provider>);
expect(wrapper.dive().find(CircularProgress)).toHaveLength(1);
});
});
@a-h @Skaronator @MatteoGioioso @jondbm Can someone help me if you have written test cases for components having dependent on redux?
Most helpful comment
I had some trouble getting my head around why
shallow
wasn't working for some tests when I started usingwithStyles
, so I broke it down into a few self-contained examples at https://gist.github.com/a-h/21df0f432ae02a6dfed941debb0e5950Once I did that, I realised that I needed to switch from
shallow
tomount
sincewithStyles
is a higher order component and 'wraps' the other component.