对Material UI Select组件的更改进行测试库

时间:2019-03-15 13:47:06

标签: reactjs material-ui react-testing-library

我正在尝试使用Select component测试react-testing-libraryonChange事件。

我使用getByTestId来抓取元素,效果很好,然后设置元素的值,然后调用fireEvent.change(select);,但是从未调用onChange并且从未更新状态。 / p>

我尝试使用select组件本身,也尝试通过获取对基础input元素的引用,但均无效。

有解决方案吗?还是这是一个已知问题?

6 个答案:

答案 0 :(得分:31)

material-ui的select组件使用mouseDown事件触发弹出菜单的出现。如果使用fireEvent.mouseDown应该触发弹出窗口,然后可以在出现的列表框中单击您的选择。参见下面的示例。

import React from "react";
import { render, fireEvent, within } from "react-testing-library";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";

it('selects the correct option', () => {
  const {getByRole} = render(
     <>  
       <Select fullWidth value={selectedTab} onChange={onTabChange}>
         <MenuItem value="privacy">Privacy</MenuItem>
         <MenuItem value="my-account">My Account</MenuItem>
       </Select>
       <Typography variant="h1">{/* value set in state */}</Typography>
     </>
  );

  fireEvent.mouseDown(getByRole('button'));

  const listbox = within(getByRole('listbox'));

  fireEvent.click(listbox.getByText(/my account/i));

  expect(getByRole('heading').toHaveTextContent(/my account/i);
});

答案 1 :(得分:2)

当您将Material-UI的Selectnative={false}(默认)一起使用时,这会变得非常复杂。这是因为呈现的输入甚至没有<select> HTML元素,而是div,隐藏输入和某些svg的混合。然后,当您单击选择时,将显示一个表示层(有点像模式),其中包含所有选项(顺便说一下,它们不是<option> HTML元素),我相信这是对这些选项之一将触发您通过onChange回调传递到原始Material-UI <Select>

总而言之,如果您愿意使用<Select native={true}>,那么您将拥有实际的<select><option> HTML元素,并且可以触发更改事件如您所愿,在<select>上显示。

以下是可运行的代码沙箱中的测试代码:

import React from "react";
import { render, cleanup, fireEvent } from "react-testing-library";
import Select from "@material-ui/core/Select";

beforeEach(() => {
  jest.resetAllMocks();
});

afterEach(() => {
  cleanup();
});

it("calls onChange if change event fired", () => {
  const mockCallback = jest.fn();
  const { getByTestId } = render(
    <div>
      <Select
        native={true}
        onChange={mockCallback}
        data-testid="my-wrapper"
        defaultValue="1"
      >
        <option value="1">Option 1</option>
        <option value="2">Option 2</option>
        <option value="3">Option 3</option>
      </Select>
    </div>
  );
  const wrapperNode = getByTestId("my-wrapper")
  console.log(wrapperNode)
  // Dig deep to find the actual <select>
  const selectNode = wrapperNode.childNodes[0].childNodes[0];
  fireEvent.change(selectNode, { target: { value: "3" } });
  expect(mockCallback.mock.calls).toHaveLength(1);
});

您会注意到,一旦Material-UI渲染了其<select>,您就必须深入研究节点以查找实际<Select>的位置。但是一旦找到它,就可以对其进行fireEvent.change

可在此处找到CodeSandbox:

Edit firing change event for material-ui select

答案 2 :(得分:1)

这是带有 Select 选项的 MUI TextField 的工作示例。

沙盒:https://codesandbox.io/s/stupefied-chandrasekhar-vq2x0?file=/src/__tests__/TextSelect.test.tsx:0-1668

文本字段:

int socket(int domain, int type, int protocol);

测试:

import { TextField, MenuItem, InputAdornment } from "@material-ui/core";
import { useState } from "react";

export const sampleData = [
  {
    name: "Vat-19",
    value: 1900
  },
  {
    name: "Vat-0",
    value: 0
  },
  {
    name: "Vat-7",
    value: 700
  }
];

export default function TextSelect() {
  const [selected, setSelected] = useState(sampleData[0].name);

  return (
    <TextField
      id="vatSelectTextField"
      select
      label="#ExampleLabel"
      value={selected}
      onChange={(evt) => {
        setSelected(evt.target.value);
      }}
      variant="outlined"
      color="secondary"
      inputProps={{
        id: "vatSelectInput"
      }}
      InputProps={{
        startAdornment: <InputAdornment position="start">%</InputAdornment>
      }}
      fullWidth
    >
      {sampleData.map((vatOption) => (
        <MenuItem key={vatOption.name} value={vatOption.name}>
          {vatOption.name} - {vatOption.value / 100} %
        </MenuItem>
      ))}
    </TextField>
  );
}

答案 3 :(得分:0)

    for (x in cards) {

        const parameters = [

            content[data][squads][0]["cards"][x]['identifier'],
            content[data][squads][0]["cards"][x]['title'],
            content[data][squads][0]["cards"][x]['description'],
            content[data][squads][0]["cards"][x]['status'],
            content[data][squads][0]["cards"][x]['priority'],
            content[data][squads][0]["cards"][x]['assignees'][0]['fullname'],
            content[data][squads][0]["cards"][x]['assignees'][0]['email'],
            content[data][squads][0]["cards"][x]['primaryLabels'],
            content[data][squads][0]["cards"][x]['secondaryLabel'],
            content[data][squads][0]["cards"][x]['swimlane'],
            content[data][squads][0]["cards"][x]['workstate'],
            content[data][squads][0]["cards"][x]['createdAt']

        ];

         res = await client.query(query, parameters);

         client.query(res, () => {

            try {

                console.log("Insert card: " + content[data]["squads"][0]["cards"][x]['title']);

            } catch (error) {

                console.error("Error: " + error + " - " + content[data]["squads"][0]["cards"][x]['title']);

            }

        })

    }

答案 4 :(得分:0)

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-2-c64481d4eddd> in <module>
     10 state_dict = torch.load("GAN_agent.pth", map_location = device)
     11 
---> 12 torch.onnx.export(state_dict, dummy_input, "GAN_agent.onnx")

~\anaconda3\envs\Basemap_upres\lib\site-packages\torch\onnx\__init__.py in export(model, args, f, export_params, verbose, training, input_names, output_names, aten, export_raw_ir, operator_export_type, opset_version, _retain_param_name, do_constant_folding, example_outputs, strip_doc_string, dynamic_axes, keep_initializers_as_inputs)
    146                         operator_export_type, opset_version, _retain_param_name,
    147                         do_constant_folding, example_outputs,
--> 148                         strip_doc_string, dynamic_axes, keep_initializers_as_inputs)
    149 
    150 

~\anaconda3\envs\Basemap_upres\lib\site-packages\torch\onnx\utils.py in export(model, args, f, export_params, verbose, training, input_names, output_names, aten, export_raw_ir, operator_export_type, opset_version, _retain_param_name, do_constant_folding, example_outputs, strip_doc_string, dynamic_axes, keep_initializers_as_inputs)
     64             _retain_param_name=_retain_param_name, do_constant_folding=do_constant_folding,
     65             example_outputs=example_outputs, strip_doc_string=strip_doc_string,
---> 66             dynamic_axes=dynamic_axes, keep_initializers_as_inputs=keep_initializers_as_inputs)
     67 
     68 

~\anaconda3\envs\Basemap_upres\lib\site-packages\torch\onnx\utils.py in _export(model, args, f, export_params, verbose, training, input_names, output_names, operator_export_type, export_type, example_outputs, propagate, opset_version, _retain_param_name, do_constant_folding, strip_doc_string, dynamic_axes, keep_initializers_as_inputs, fixed_batch_size)
    414                                                         example_outputs, propagate,
    415                                                         _retain_param_name, do_constant_folding,
--> 416                                                         fixed_batch_size=fixed_batch_size)
    417 
    418         # TODO: Don't allocate a in-memory string for the protobuf

~\anaconda3\envs\Basemap_upres\lib\site-packages\torch\onnx\utils.py in _model_to_graph(model, args, verbose, training, input_names, output_names, operator_export_type, example_outputs, propagate, _retain_param_name, do_constant_folding, _disable_torch_constant_prop, fixed_batch_size)
    277             model.graph, tuple(in_vars), False, propagate)
    278     else:
--> 279         graph, torch_out = _trace_and_get_graph_from_model(model, args, training)
    280         state_dict = _unique_state_dict(model)
    281         params = list(state_dict.values())

~\anaconda3\envs\Basemap_upres\lib\site-packages\torch\onnx\utils.py in _trace_and_get_graph_from_model(model, args, training)
    226     # A basic sanity check: make sure the state_dict keys are the same
    227     # before and after running the model.  Fail fast!
--> 228     orig_state_dict_keys = _unique_state_dict(model).keys()
    229 
    230     # By default, training=False, which is good because running a model in

~\anaconda3\envs\Basemap_upres\lib\site-packages\torch\jit\__init__.py in _unique_state_dict(module, keep_vars)
    283     # id(v) doesn't work with it. So we always get the Parameter or Buffer
    284     # as values, and deduplicate the params using Parameters and Buffers
--> 285     state_dict = module.state_dict(keep_vars=True)
    286     filtered_dict = type(state_dict)()
    287     seen_ids = set()

AttributeError: 'collections.OrderedDict' object has no attribute 'state_dict'

答案 5 :(得分:0)

我在使用 Material UI 选择元素时遇到了一些问题,但最后我找到了这个简单的解决方案。

const handleSubmit = jest.fn()

const renderComponent = (args?: any) => {
  const defaultProps = {
    submitError: '',
    allCurrencies: [{ name: 'CAD' }, { name: 'EUR' }],
    setSubmitError: () => jest.fn(),
    handleSubmit,
    handleClose,
  }

  const props = { ...defaultProps, ...args }
  return render(<NewAccontForm {...props} />)
}

afterEach(cleanup)

// TEST

describe('New Account Form tests', () => {
  it('submits form with corret data', async () => {
    const expectedSubmitData = {
      account_type: 'Personal',
      currency_type: 'EUR',
      name: 'MyAccount',
    }
    const { getByRole, getAllByDisplayValue } = renderComponent()
    const inputs = getAllByDisplayValue('')
    fireEvent.change(inputs[0], { target: { value: 'Personal' } })
    fireEvent.change(inputs[1], { target: { value: 'EUR' } })
    fireEvent.change(inputs[2], { target: { value: 'MyAccount' } })
    userEvent.click(getByRole('button', { name: 'Confirm' }))
    await waitFor(() => {
      expect(handleSubmit).toHaveBeenCalledWith(expectedSubmitData)
      expect(handleSubmit).toHaveBeenCalledTimes(1)
    })
  })
})