I've recently decided to start writing tests for a project I'm a part of, and we have a React component that I'm having some real trouble figuring out how to test properly.
The component is called FallbackedImage and it takes either a string or array of string and renders the first valid image, which, as you probably understand, is an asynchronous action.
I want to test if it does, in fact, render an image if supplied a valid string, but I can't just run
it("renders img if src is valid string", () => {
const wrapper = mount(<FallbackedImage src={validLink} />)
expect(wrapper.find("img")).to.have.length(1)
})
because the image will not have rendered yet, and wrapper.find("img") will therefore have a length of 0.
I have come up with two "solutions", but they're hacky and I don't want to use them:
setTimeout
This assumes that the image will have loaded within 300ms. If the image takes longer to load, the test will fail; if it takes less time, then I've wasted precious milliseconds.
it("renders img if src is valid string", done => {
const wrapper = mount(<FallbackedImage src={validLink} />)
window.setTimeout(() => {
expect(wrapper.find("img")).to.have.length(1)
done()
}, 300)
})
Using the component's onLoad prop
This assumes that the onLoad-functionality works as intended, which doesn't feel right since that is not in scope for this particular test.
it("renders img if src is valid string", done => {
const onLoad = () => {
expect(wrapper.find("img")).to.have.length(1)
done()
}
const wrapper = mount(
<FallbackedImage
onLoad={onLoad}
src={validLink} />
)
})
How am I supposed to test this? I'm using chai, Enzyme, and Mocha, if that helps. Also, here's the FallbackedImage-component so you can see what I'm working with:
import React, { Component, PropTypes } from "react"
import { noop } from "../../utils"
export default class FallbackedImage extends Component {
constructor(props) {
super(props)
this.componentDidMount = this.tryImage.bind(this)
this.testImg = new window.Image()
this.testImg.onerror = this.onError.bind(this)
this.testImg.onload = this.onLoad.bind(this)
this.photos = [].concat(props.src) // Make array from props.src if it's not
this.state = { index: 0 }
}
static propTypes = {
className: PropTypes.string,
onLoad: PropTypes.func,
src: PropTypes.oneOfType([
PropTypes.array,
PropTypes.string,
]),
}
static defaultProps = {
onLoad: noop,
}
onError() {
const nextIndex = this.state.index + 1
if (nextIndex <= this.photos.length) {
this.setState({ index: nextIndex }, this.tryImage)
}
}
onLoad() {
const { src } = this.testImg
this.setState({ src })
this.props.onLoad(src)
}
tryImage() {
this.testImg.src = this.photos[this.state.index]
}
shouldComponentUpdate(_nextProps, nextState) {
return !!nextState.src
}
render() {
const { src } = this.state
return src
? <img src={src} className={this.props.className} />
: null
}
}
Aucun commentaire:
Enregistrer un commentaire