Тестирование
https://github.com/JedWatson/react-select
Я хотел бы использовать React-Select реагирующий компонент, но мне нужно добавить тесты.
Я пробовал несколько вариантов, которые я нашел с Google, но, похоже, ничего не работает. У меня есть код ниже, но он не вызывает событие изменения. Мне удалось добавить событие фокуса, которое добавляет класс "is-focus", но класс "is-open" все еще отсутствует.
Я использовал: https://github.com/JedWatson/react-select/blob/master/test/Select-test.js в качестве ссылки
Я пытался использовать событие изменения только в поле ввода, но это тоже не помогло. Я заметил, что для onInputChange={this.change}
есть onInputChange={this.change}
.
Тестовое задание
import Home from '../../src/components/home';
import { mount } from 'enzyme'
describe('Home', () => {
it("renders home", () => {
const component = mount(<Home/>);
// default class on .Select div
// "Select foobar Select--single is-searchable"
const select = component.find('.Select');
// After focus event
// "Select foobar Select--single is-searchable is-focussed"
// missing is-open
TestUtils.Simulate.focus(select.find('input'));
//this is not working
TestUtils.Simulate.keyDown(select.find('.Select-control'), { keyCode: 40, key: 'ArrowDown' });
TestUtils.Simulate.keyDown(select.find('.Select-control'), { keyCode: 13, key: 'Enter' });
// as per code below I expect the h2 to have the select value in it eg 'feaure'
});
});
Тестируемый компонент
import React, { Component } from 'react';
import Select from 'react-select';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
message: "Please select option"};
this.change = this.change.bind(this);
}
change(event) {
if(event.value) {
this.setState({message: event.label});
}
}
render () {
const options = [ {label: 'bug', value: 1} , {label: 'feature', value: 2 }, {label: 'documents', value: 3}, {label: 'discussion', value: 4}];
return (
<div className='content xs-full-height'>
<div>
<h2>{this.state.message}</h2>
<Select
name="select"
value={this.state.message}
options={options}
onInputChange={this.change}
onChange={this.change}
/>
</div>
</div>
);
}
}
export default Home;
Командная строка Для запуска теста я делаю:
>> npm run test
и в package.js у меня есть этот скрипт:
"test": "mocha --compilers js:babel-core/register -w test/browser.js ./new",
Испытательная установка
и browser.js это:
import 'babel-register';
import jsdom from 'jsdom';
const exposedProperties = ['window', 'navigator', 'document'];
global.document = jsdom.jsdom('<!doctype html><html><body></body></html>');
global.window = document.defaultView;
Object.keys(document.defaultView).forEach((property) => {
if (typeof global[property] === 'undefined') {
exposedProperties.push(property);
global[property] = document.defaultView[property];
}
});
global.navigator = {
userAgent: 'node.js'
};
Я также попытался использовать методы тестирования, описанные здесь: https://github.com/StephenGrider/ReduxSimpleStarter
Любая помощь будет оценена
Ответы
Ответ 1
От https://github.com/JedWatson/react-select/issues/1357
Только решение, которое я нашел, состояло в том, чтобы моделировать выбор путем нажатия клавиш События:
wrapper.find('.Select-control').simulate('keyDown', { keyCode: 40 });
// you can use 'input' instead of '.Select-control'
wrapper.find('.Select-control').simulate('keyDown', { keyCode: 13 });
expect(size).to.eql('your first value in the list')
Ответ 2
Я попробовал оба ответа, перечисленных выше, и все еще не повезло
Что сработало для меня:
-
Добавьте classNamePrefix
prop - т.е. list
(как упомянуто в других ответах):
<Select
classNamePrefix='list'
options={[
{ label: 'one', value: 'one' },
{ label: 'two', value: 'two' }
]}/>
-
выберите индикатор выпадающего меню и смоделируйте mouseDown => открытый раскрывающийся список:
wrapper
.find('.list__dropdown-indicator')
.simulate('mouseDown', {
button: 0
});
-
ожидать, что что-то произойдет, т.е. в моем случае я проверял количество выпадающих опций
expect(wrapper.find('.list__option').length).toEqual(2);
если у вас есть контроль над отправляемыми реквизитами, вы можете добавить menuIsOpen
prop, чтобы меню всегда было открыто (также как шаг 2 в списке).
Чтобы выбрать значение из раскрывающегося списка, после открытия раскрывающегося списка:
wrapper.find('.list__option').last().simulate('click', null);
тогда вы можете проверить либо:
expect(wrapper.find('.list__value-container').text()).toEqual('two');
или же
expect(wrapper.find('.list__single-value').text()).toEqual('two');
Ответ 3
В дополнение к тому, что сказал Кит, использование метода имитации, кажется, является единственным способом реализовать функциональность. Однако, когда я попробовал это в своем решении, это не сработало - я использую Typescript, хотя и не уверен, что это имеет отношение, но я обнаружил, что необходимо также передать свойство key при моделировании события:
wrapper.find('.Select-control').simulate('keyDown', { key: 'ArrowDown', keyCode: 40 });
wrapper.find('.Select-control').simulate('keyDown', { key: 'Enter', keyCode: 13 });
Я также обнаружил, что установка свойства classNamePrefix значительно упростила проведение простого теста, чтобы дать мне уверенность в том, что компонент правильно реагировал на смоделированные события. При установке этого префикса полезные части компонента украшаются именами классов, обеспечивающими легкий доступ к ним (вы можете определить эти полезные имена классов, проверив элементы в инструментах Google Dev). Простой тест Jest:
it('react-select will respond to events correctly', () => {
const sut = Enzyme.mount(
<Select
classNamePrefix="list"
options={[{ label: 'item 1', value: 1 }]}
/>);
// The intereactive element uses the suffix __control **note there are two underscores**
sut.find('.list__control').first().simulate('keyDown', { key: 'ArrowDown', keyCode: 40 });
sut.find('.list__control').first().simulate('keyDown', { key: 'Enter', keyCode: 13 });
// the selected value uses the suffix __single-value **note there are two underscores**
expect(sut.find('.list__single-value').first().text()).toEqual('item 1');
});
Ответ 4
Для более новой версии реакции-выбора (2. x+) вышеупомянутый метод не работает, потому что реакция-выбора использует эмоцию. Таким образом, wrapper.find('.Select-control')
или wrapper.find('.list__option')
больше не работает. response-select 2. x+ оборачивает компонент Select
внутри диспетчера состояний, но вы можете получить доступ к методу onChange
компонента Select
. Я использую следующий код для запуска выбора:
wrapper.find(Select).props().onChange({ value: ... })
Ответ 5
Просто хочу добавить, что мне действительно нужно было добавить реквизит classNamePrefix
в компонент Select
иначе я не получил никаких классов *__control
для привязки.
Ответ 6
Если кто-то использует энзим, это работает для меня:
wrapper.find('Select').simulate('change', {
target: { name: 'select', value: 1 },
});
где "select" - это имя атрибута, как определено здесь:
<Select
name="select"
...
и "значение" - это желаемое значение параметра.
Ответ 7
Это сработало для меня. Что это делает, вкратце:
- Загрузите подключенный к редукту компонент (AddEduPlans), который показывает компонентact-select.
- Устанавливает состояние AddEduPlans так, чтобы компонент реагировать на выбор получил 2 варианта
- Перезагрузка компонента.
- Использует решение Кита, предоставленное в этом вопросе, чтобы изменить значение (откройте меню со стрелкой вниз и выберите новое значение).
- Перезагрузка компонента.
- Проверяет, изменил ли выбор реагирования свое свойство.
it("handles react-select input change correctly", () => {
const newValue = [
{ value: "value in setstate (1)", label: "newLabel 1" },
{ value: "value after select menu click (2)", label: "newLabel 2" }
];
component
.find(AddEduPlans)
.children()
.first()
.setState({ schoolingYear: newValue[1] }); //set the state so that component loads the second value
component.update(); //update the component so that the select component loads its value from the newly set state
expect(
component
.find(Select)
.first()
.children()
.first()
.prop("value")
).toBe(newValue[1]); // the select component has been loaded with the second value from state.
// This also tests if the value will be reloaded after it has been changed in state
component
.find(Select)
.first()
.children()
.first()
.simulate("keyDown", { key: "ArrowDown", keyCode: 40 }); //simulate that the user has opened the menu
component
.find(Select)
.first()
.children()
.first()
.simulate("keyDown", { key: "Enter", keyCode: 13 }); //simulate that the user has chosen another value (the first one)
component.update(); //update the component as it would be after selecting new value of the select component
expect(
component
.find(Select)
.first()
.children()
.first()
.prop("value")
).toBe(newValue[1]);
});
и, конечно же, реализация "отреагируй":
<Select
options={schoolingYearOptions}
placeholder="Choose..."
onChange={value =>
this.handleSelectChange(value, "schoolingYear")
}
required="true"
value={schoolingYear}
/>
Ответ 8
Использование библиотеки тестирования и версии 2.0
Стараясь избегать использования чего-либо очень специфического, например, classNamePrefix
, или хакерства о том, как компонент работает, ища пропеллер onChange или что-то еще.
const callback = jest.fn();
const { container, getByText} = render(<Select ... onChange={callback} />);
Теперь мы притворяемся читателем экрана и фокусируемся, и нажимаем стрелку вниз.
fireEvent.focus(container.querySelector('input'));
fireEvent.keyDown(container.querySelector('input'), { key: 'ArrowDown', code: 40 });
А теперь нажмите на значение, которое вы хотите
fireEvent.click(getByText('Option Two'));
И утверждать.
expect(callback).toHaveBeenCalledWith({ value: 'two', label: 'Option Two'});