Тестирование

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

Я попробовал оба ответа, перечисленных выше, и все еще не повезло

Что сработало для меня:

  1. Добавьте classNamePrefix prop - т.е. list (как упомянуто в других ответах):

    <Select
       classNamePrefix='list'
       options={[
         { label: 'one', value: 'one' },
         { label: 'two', value: 'two' }
    ]}/>
    
  2. выберите индикатор выпадающего меню и смоделируйте mouseDown => открытый раскрывающийся список:

    wrapper
      .find('.list__dropdown-indicator')
      .simulate('mouseDown', {
        button: 0 
      });
    
  3. ожидать, что что-то произойдет, т.е. в моем случае я проверял количество выпадающих опций

    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

Это сработало для меня. Что это делает, вкратце:

  1. Загрузите подключенный к редукту компонент (AddEduPlans), который показывает компонентact-select.
  2. Устанавливает состояние AddEduPlans так, чтобы компонент реагировать на выбор получил 2 варианта
  3. Перезагрузка компонента.
  4. Использует решение Кита, предоставленное в этом вопросе, чтобы изменить значение (откройте меню со стрелкой вниз и выберите новое значение).
  5. Перезагрузка компонента.
  6. Проверяет, изменил ли выбор реагирования свое свойство.

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'});