I'm trying to test a React component using Mocha and Enzyme that uses a dynamic import to load a module.
When I try to test the logic that relies on the dynamic import I get incorrect results. The problem is that the async functions don't finish before the test finishes so I can never get accurate results.
How can I handle this scenario?
Component
import classNames from 'classnames';
import PropTypes from 'prop-types';
import React from 'react';
// styles
import styles from './PasswordStrengthIndicator.scss';
class PasswordStrengthIndicator extends React.Component {
static defaultProps = {
password: undefined,
onPasswordChange: undefined,
}
static propTypes = {
password: PropTypes.string,
onPasswordChange: PropTypes.func,
}
constructor() {
super();
this.state = {};
}
componentWillMount() {
this.handlePasswordChange();
}
componentWillReceiveProps(nextProps) {
const password = this.props.password;
const nextPassword = nextProps.password;
if (password !== nextPassword) {
this.handlePasswordChange();
}
}
render() {
const strength = this.state.strength || {};
const score = strength.score;
return (
<div className={ styles.passwordStrength }>
<div className={ classNames(styles.score, styles[`score-${score}`]) } />
<div className={ styles.separator25 } />
<div className={ styles.separator50 } />
<div className={ styles.separator75 } />
</div>
);
}
// private
async determineStrength() {
const { password } = this.props;
const zxcvbn = await import('zxcvbn');
let strength = {};
if (password) strength = zxcvbn(password);
return strength;
}
async handlePasswordChange() {
const { onPasswordChange } = this.props;
const strength = await this.determineStrength();
this.setState({ strength });
if (onPasswordChange) onPasswordChange(strength);
}
}
export default PasswordStrengthIndicator;
Test
describe('when `password` is bad', () => {
beforeEach(() => {
props.password = 'badpassword';
});
it.only('should display a score of 1', () => {
const score = indicator().find(`.${styles.score}`);
expect(score.props().className).to.include(styles.score1); // should pass
});
});
I was able to accomplish this with a -- something.
I switched the test that relies on the dynamic import to be asynchronous. I then created a function that renders the component and returns a promise that dynamically imports the module I'm trying to import in the component.
const render = () => {
indicator = shallow(
<PasswordStrengthIndicator { ...props } />,
);
return (
Promise.resolve()
.then(() => import('zxcvbn'))
);
};
I believe this is relying on the same concept as just waiting since import('zxcvbn') will take a similar enough amount of time to import in both places.
Here's my test code:
describe('when `password` is defined', () => {
describe('and password is bad', () => {
beforeEach(() => {
props.password = 'badpassword';
});
it('should display a score of 1', (done) => {
render()
.then(() => {
const score = indicator.find(`.${styles.score}`);
expect(score.props().className).to.include(styles.score1);
done();
});
});
});
});
This ended up working out because it prevented me from having to stub and I didn't have to change my component's implementation to better support stubbing. It is also less arbitrary than waiting x ms.
I'm going to leave this question open for now as there are probably better solutions that the community can provide.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With