测试React Select组件

时间:2017-02-01 22:32:58

标签: reactjs testing mocha reactjs-testutils react-select

https://github.com/JedWatson/react-select

我想使用React-Select react组件,但我需要添加测试。

我已经尝试了几种与google相关的选项,但似乎没有任何效果。我有下面的代码,但它不会导致更改事件。我已经能够添加一个焦点事件,它增加了'is-focusedsed'类,但是'is-open'类仍然缺失。

我使用过:https://github.com/JedWatson/react-select/blob/master/test/Select-test.js作为参考

我曾尝试仅在输入字段上使用更改事件,但这也没有帮助。我注意到select有一个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

非常感谢任何帮助

13 个答案:

答案 0 :(得分:14)

来自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')

答案 1 :(得分:9)

我已经尝试了上面列出的两个答案,但还是没有运气。

对我有用的是:

  1. 添加classNamePrefix属性-即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道具以始终打开菜单(又称列表中的第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');

答案 2 :(得分:8)

这是一个经常性的问题。我正在与100%通过测试的人共享我自己的代码,这些测试涵盖了我的源代码的100%。

我的组件看起来像这样

MySelectComponent({ options, onChange }) {

  return <div data-testid="my-select-component">
    <Select
      className="basic-single"
      classNamePrefix="select"
      name="myOptions"
      placeholder="Select an option"
      options={options}
      onChange={e => onChange(e)}
    />
</div>;
}

我要在Select的{​​{1}}上添加包装器的原因是,呈现的options元素将在其上可用,否则我无法检查是否存在文本选项(您将当您看到我的测试时,会更好地理解。)

这是一个live running example,渲染时会显示带有10个选项的选择组件。

enter image description here

测试1:应呈现无误

  • 我渲染组件。

  • 我搜索要显示的占位符。

测试2:选择第一个选项时应调用onChange

  • 我渲染组件。

  • 我检查我的data-testid="my-select-component"是否尚未被呼叫。

  • 模拟一个mockedOnChange事件。

  • 单击第一个选项。

  • 我检查是否使用第一个选项标签和值将ArrowDown调用了1次。

测试3:选择第一个选项,然后选择第二个选项,然后选择第9个选项时,应调用onChange

  • 我渲染组件。

  • 我模拟了第一个选项的选择。

  • 我模拟第二个选项的选择。

  • 我模拟了第9个选项的选择。

  • 我用第9个选项包和值检查mockedOnChange是否被调用3次。

测试4:按输入值进行过滤时应调用onChange

  • 我渲染组件。

  • 我通过输入“选项1”来模拟输入字段上的更改。

  • 我知道,根据我的mockedOnChange,过滤后的结果将是“模拟选项1”和“模拟选项10”。

  • 我模拟了2个mockedOptions事件。

  • 我检查是否使用带有正确标签和值的第二个过滤选项调用了ArrowDown

完整的测试文件

mockedOnChange

我希望有帮助。

答案 3 :(得分:2)

要补充Keith所说的话,使用Simulation方法似乎确实是行使功能的唯一方法。但是,当我在解决方案中尝试使用此方法时,该方法无法正常工作-我使用的是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 :(得分:1)

只需添加,我实际上必须classNamePrefix道具添加到Select组件中,否则我没有得到任何*__control类来闩锁上。

答案 5 :(得分:0)

如果有人正在使用酶,这对我有用:

  wrapper.find('Select').simulate('change', {
    target: { name: 'select', value: 1 },
  });

其中“选择”是此处定义的属性名称:

  <Select
      name="select"
      ...

“值”是所需选项的值。

答案 6 :(得分:0)

使用测试库和v2.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'});

答案 7 :(得分:0)

对于那些正在使用酶v3.11.0并进行反应选择v3.0.8的人来说,这对我有用

component.find('Select').simulate('keyDown', { key: 'ArrowDown', keyCode: 40 });

这是我的Select。我正在与redux-form

一起使用
<Select
    {...input}
    styles={customStyles}
    options={props.options}
    formatGroupLabel={formatGroupLabel}
    placeholder="Custom Search..."
    isSearchable={true}
    onBlur={handleBlur}
/>

测试样本

it('should render dropdown on keyDown', () => {
    expect(component.find('MenuList').length).toEqual(1);
});

it('should render the correct amount of options', () => {
    expect(component.find('Option').length).toEqual(optionsLength);
});

答案 8 :(得分:0)

https://stackoverflow.com/a/57699061/10786373几乎对我有用。我刚刚添加了keyDown事件来打开选择菜单。

it('my test', () => {
  const { container } = getShallow();
  const elSelector = container.querySelector('.Select-input');

  expect(propsComponent.onPageSizeChange).toHaveBeenCalledTimes(0);

  // open select menu
  fireEvent.keyDown(elSelector, { keyCode: 13 });

  // choose next option
  fireEvent.keyDown(elSelector, { key: 'ArrowDown', code: 40 });

  // send the option
  fireEvent.keyDown(elSelector, { keyCode: 13 });

  expect(propsComponent.onPageSizeChange).toHaveBeenCalledTimes(1);
});

答案 9 :(得分:0)

wrapper.find(ReactSelectComponent.instance().onChange(...params));

答案 10 :(得分:0)

带有react-testing-library:

=QUERY(importxml('url-address';"//ul[@class='xxxxxxx']");"SELECT * WHERE Col1 CONTAINS 'x' OR Col1 IS NULL";-1)

然后在我的测试中

<Select id='myId' onChange=(list: ReactSelectOption[]) => {
                        props.changeGroupItem(
                            {
                                items: list ? list.map((item) => item.value) : [],
                            }
                        );
                    }
/>

答案 11 :(得分:0)

有一个库,用于模拟react-select元素上的用户事件,供react-testing-library使用。适用于react select 2 +。

https://www.npmjs.com/package/react-select-event

像这样:

const { getByRole, getByLabelText } = render(
  <form role="form">
    <label htmlFor="food">Food</label>
    <Select options={OPTIONS} name="food" inputId="food" isMulti />
  </form>
);
expect(getByRole("form")).toHaveFormValues({ food: "" });
 
await selectEvent.select(getByLabelText("Food"), ["Strawberry", "Mango"]);
expect(getByRole("form")).toHaveFormValues({ food: ["strawberry", "mango"] });
 
await selectEvent.select(getByLabelText("Food"), "Chocolate");
expect(getByRole("form")).toHaveFormValues({
  food: ["strawberry", "mango", "chocolate"],
});

答案 12 :(得分:0)

我在这里尝试了所有解决方案 - 没有任何效果。

我能够用 user-event 库解决这个问题。查看 selectOptions 函数。