失败测试(testing-library/react):无法找到包含文本的元素:

Failing test (testing-library/react): Unable to find an element with the text:

我正在尝试使用 testing-library/react 的 react-select (rc-select) 组件编写一些测试。测试现在是 运行,但下拉列表如下所示:

我也在控制台中收到警告: react-dom.development.js:21 Warning: Encountered two children with the same key, .

测试 运行 的变化来自

  <Option key={option.key || option} value={option.key || option}>
            {option.name || option}
          </Option>```

 <Option key={option.value} value={option.value}>
            {option.label}
          </Option>

代码使用testing-library/react:

Select测试组件:

import React from "react";
import Select from "../../packages/lab/src/select/Select.jsx";

const ReactSelectTestComponent = (props) => {
    const { options } = props;

    const onChange = (event) => {
        if (props.onChange) {
            props.onChange(event);
        }
    };

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

export default ReactSelectTestComponent;

Select.test.js:

import React from "react";
import { render, fireEvent, cleanup, waitFor } from '@testing-library/react';
import SelectTestComponent from "./SelectTestComponent";

it('should call onChange when the first option is selected', async () => {
  const mockedOptions = [
    { label: 'Mocked option 1', value: 'mocked-option-1' },
    { label: 'Mocked option 2', value: 'mocked-option-2' },
    { label: 'Mocked option 3', value: 'mocked-option-3' },
    { label: 'Mocked option 4', value: 'mocked-option-4' },
    { label: 'Mocked option 5', value: 'mocked-option-5' },
    { label: 'Mocked option 6', value: 'mocked-option-6' },
    { label: 'Mocked option 7', value: 'mocked-option-7' },
    { label: 'Mocked option 8', value: 'mocked-option-8' },
    { label: 'Mocked option 9', value: 'mocked-option-9' },
    { label: 'Mocked option 10', value: 'mocked-option-10' },
  ];

  const mockedOnChange = jest.fn();
  const { getByText, queryByTestId } = render(<SelectTestComponent
    options={mockedOptions}
    onChange={mockedOnChange} />);

  const mySelectComponent = queryByTestId('my-select-component');

  expect(mySelectComponent).toBeDefined();
  expect(mySelectComponent).not.toBeNull();
  // We trigger the right element to show autocomplete
  const input = mySelectComponent.getElementsByTagName('input')[0];
  fireEvent.keyDown(input, { key: 'ArrowDown' });

  // Leave as is
  await waitFor(() => getByText('Mocked option 1')[0]);
  fireEvent.click(getByText('Mocked option 1')[0]);

  expect(mockedOnChange).toHaveBeenCalledTimes(1);

  // Another change is to only selected value instead of entire option
  expect(mockedOnChange).toHaveBeenCalledWith('mocked-option-1');

})

错误:

Select分量:

/* eslint-disable react/forbid-prop-types */
import React from "react";
import PropTypes from "prop-types";
import styled from "styled-components";
import RcSelect, { Option } from "rc-select";
import { Icon } from "@opt-ui/core";
import { colors } from "@opt-ui/styles";
import { GlobalStyle } from "./Select.styles";

const ArrowContainer = styled.div`
  position: relative;
  top: 2px;
`;

const BaseSelect = styled(RcSelect).attrs(() => { })`
  font-family: Equinor;
`;

// Component
const Select = React.forwardRef((props, ref) => {
  const { className, options, value, menuItemSelectedIcon, ...other } = props;
  return (
    <>
      <GlobalStyle />
      <BaseSelect
        ref={ref}
        className={className}
        value={value}
        inputIcon={
          <ArrowContainer>
            <Icon type="chevron_down" color={colors.grey20} />
          </ArrowContainer>
        }
        menuItemSelectedIcon={menuItemSelectedIcon}
        {...other}
      >
        {options.map((option) => (
          <Option key={option.value} value={option.value}>
            {option.label}
          </Option>
        ))}
      </BaseSelect>
    </>
  );
});

Select.propTypes = {
  options: PropTypes.array.isRequired,
  value: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
  menuItemSelectedIcon: PropTypes.element
};

Select.defaultProps = {
  value: undefined,
  menuItemSelectedIcon: null
};

export default Select;


我检查了您的代码,发现您需要更改一些内容才能使其正常工作。

首先,您可能需要仅更改 Option 渲染 label 并且设置选项的值是按字面设置 value:

<Option key={option.value} value={option.value}> // key/value as unique key
  {option.label} // render only label
</Option>

下一步是通过 keydown 正确触发显示自动完成列表到 rc-select 呈现的 input:


// We trigger the right element to show autocomplete
const input = mySelectComponent.getElementsByTagName('input')[0];
fireEvent.keyDown(input, { key: 'ArrowDown' });

// Leave as is
await waitForElement(() => getByText('Mocked option 1'));    
fireEvent.click(getByText('Mocked option 1'));

expect(mockedOnChange).toHaveBeenCalledTimes(1);

// Another change is to only selected value instead of entire option
expect(mockedOnChange).toHaveBeenCalledWith('mocked-option-1');