Я пытаюсь проверить компонент слайдера.
Этот компонент слайдера может быть переменным по ширине. Когда вы нажимаете "дорожку" слайдера, он должен изменить значение и вызвать обратный вызов onChange
. Значение зависит от того, где вы нажимаете на дорожку. Если вы нажмете половину точки, когда значение min 100, а максимальное значение 200, тогда оно должно сообщать значение 150.
Проблема, с которой я сталкиваюсь, заключается в том, что при рендеринге компонента с помощью ReactTest.renderIntoDocument
у компонента нет ширины, поэтому при щелчке по нему он не может вычислить новое значение.
Вот компонент Slider.js
import React, {PropTypes} from 'react';
import ReactDOM from 'react-dom';
import { noop } from 'lodash';
import style from './style.scss';
export default class Slider extends React.Component {
render() {
return (
<div
className='Slider'
onClick={this.handleClick.bind(this)}
{...this.props}
>
<div
className='handle'
style={{left: `${this.calculateLeft()}%`}}>
</div>
<div className='track'></div>
</div>
);
}
handleClick(e) {
let node = ReactDOM.findDOMNode(this);
let {clientX, clientY} = e;
let {offsetLeft, offsetWidth, clientWidth} = node;
let xPercent = (clientX - offsetLeft) / offsetWidth;
console.log(offsetLeft, offsetWidth, clientWidth, xPercent);
this.props.onChange(normalize(xPercent, this.props.min, this.props.max));
}
calculateLeft() {
let numerator = this.props.value - this.props.min;
let denominator = this.props.max - this.props.min;
return numerator / denominator * 100;
}
}
// Proptypes
// ----------------------------------------------------------------------------
Slider.propTypes = {
// Callback for when the value changes.
onChange: PropTypes.func,
// The value for when the slider is at 0%
min: PropTypes.number,
// The value for when the slider is at 100%
max: PropTypes.number,
// The starting value
value: validateValue,
}
Slider.defaultProps = {
onChange: noop,
min: 0,
max: 100,
}
// Custom Validation
// ----------------------------------------------------------------------------
function validateValue(props, propName, componentName) {
let value = props[propName];
if (typeof(value) !== 'number') {
return new Error(`value must be a number, got ${typeof(value)}`);
}
if (value > props.max || value < props.min) {
return new Error(
`value: ${value} must be between max: ${props.max}
and min: ${props.min}`
);
}
}
// Helpers
// ---------------------------------------------------------------------------
function normalize(floatValue, min, max) {
let range = max - min;
let normalizedValue = floatValue * range + min;
// cleverly restrict the value be between the min and max
return [min, normalizedValue, max].sort()[1];
}
Таблица стилей (style.scss
):
.Slider {
position: relative;
display: block;
width: 100px;
.track {
height: 4px;
background: #666;
border-radius: 2px;
}
.handle {
width: 12px;
height: 12px;
background: #fff;
border-radius: 10px;
position: absolute;
top: 50%;
transform: translate(-50%, -50%);
transition: left 100ms linear;
}
}
Вот мой тест:
import Slider from './Slider';
import React from 'react';
import {
renderIntoDocument,
findRenderedDOMComponentWithClass,
findRenderedDOMComponentWithTag,
Simulate
} from 'react-addons-test-utils';
describe('Slider', function() {
describe('click', function() {
it('triggers the onChange callback', function() {
const onChange = sinon.spy();
const component = renderIntoDocument(
<Slider
style={{width: 100, height: 40}}
min={100}
max={200}
value={150}
onChange={onChange}
/>
);
const track = findRenderedDOMComponentWithClass(component, 'track');
Simulate.click(track, {clientY: 0, clientX: 10})
expect(onChange).to.have.been.calledWith(110);
});
});
});
Тестовый выход
LOG LOG: 0, 0, 0, Infinity
click
✗ triggers the onChange callback
AssertionError: expected onChange to have been called with arguments 10
onChange(200)
at /components/Slider/test.js:99 < webpack:///src/components/Slider/test.js:55:6
Эти операторы журнала относятся к функции handleClick()
в компоненте.
Ширина равна нулю, поэтому знаменатель заканчивается равным нулю при вычислении xPercent, что приводит к его бесконечности. Это заставляет просто использовать значение max
200.
TL;DR
Как заставить компонент иметь ширину при рендеринге во время теста?