我正在尝试使用Select component测试react-testing-library的onChange
事件。
我使用getByTestId
来抓取元素,效果很好,然后设置元素的值,然后调用fireEvent.change(select);
,但是从未调用onChange
并且从未更新状态。 / p>
我尝试使用select组件本身,也尝试通过获取对基础input
元素的引用,但均无效。
有解决方案吗?还是这是一个已知问题?
答案 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的Select
与native={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:
答案 2 :(得分:1)
这是带有 Select 选项的 MUI TextField 的工作示例。
文本字段:
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)
})
})
})