재료 UI 변경 시 반응 테스트 라이브러리 구성 요소 선택
테스트하려고 합니다.onChange
react-testing-library를 사용하여 Select 컴포넌트의 이벤트.
다음 방법으로 요소를 잡습니다.getByTestId
그 후 요소의 값을 설정하고 호출합니다.fireEvent.change(select);
하지만onChange
는 호출되지 않으며 상태는 갱신되지 않습니다.
선택한 컴포넌트 자체와 기본 컴포넌트에 대한 참조를 모두 사용해 보았습니다.input
둘 다 작동하지 않습니다.
해결방법은?아니면 이미 알고 있는 문제인가요?
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";
import Typography from "@material-ui/core/Typography";
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);
});
Material-UI를 사용하면 이 작업이 매우 복잡해집니다.Select
와 함께native={false}
(이것은 디폴트입니다.이는 렌더링된 입력에 심지어<select>
HTML 요소는 대신 div, 숨겨진 입력 및 일부 svgs를 혼합한 것입니다.그런 다음 선택을 클릭하면 프레젠테이션 레이어(모달과 비슷함)가 모든 옵션과 함께 표시됩니다(모달과 비슷하지 않음).<option>
HTML 요소). 그리고 이 옵션들 중 하나를 클릭하는 것이 당신이 통과시킨 모든 것을 트리거한다고 생각합니다.onChange
원래의 Material-UI로의 콜백<Select>
이 모든 것을 말할 수 있습니다. 만약 당신이 이 모든 것을 사용할 의향이 있다면<Select native={true}>
그럼 실제로,<select>
그리고.<option>
HTML 요소에서 작업할 수 있습니다.또, 변경 이벤트를 기동할 수 있습니다.<select>
예상하신 대로입니다.
다음은 동작하는 Code Sandbox의 테스트 코드입니다.
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);
});
노드를 파헤쳐 실제 위치를 찾아야 합니다.<select>
Material-UI가 렌더링되면<Select>
하지만 일단 그걸 찾으면fireEvent.change
그 위에 올려놔요.
CodeSandbox는 다음 사이트에서 찾을 수 있습니다.
사용.*ByLabelText()
요소
// demo.js
import * as React from "react";
import Box from "@mui/material/Box";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";
import Typography from "@mui/material/Typography";
export default function BasicSelect() {
const [theThing, setTheThing] = React.useState("None");
const handleChange = (event) => {
setTheThing(event.target.value);
};
return (
<Box sx={{ minWidth: 120 }}>
<FormControl fullWidth>
<InputLabel id="demo-simple-select-label">Choose a thing</InputLabel>
<Select
labelId="demo-simple-select-label"
id="demo-simple-select"
value={theThing}
label="Choose a thing"
onChange={handleChange}
>
<MenuItem value={"None"}>None</MenuItem>
<MenuItem value={"Meerkat"}>Meerkat</MenuItem>
<MenuItem value={"Marshmallow"}>Marshmallow</MenuItem>
</Select>
</FormControl>
<Box sx={{ padding: 2 }}>
<Typography>The thing is: {theThing}</Typography>
</Box>
</Box>
);
}
시험
// demo.test.js
import "@testing-library/jest-dom";
import { render, screen, within } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import Demo from "./demo";
test("When I choose a thing, then the thing changes", async () => {
render(<Demo />);
// Confirm default state.
expect(await screen.findByText(/the thing is: none/i)).toBeInTheDocument();
// Click on the MUI "select" (as found by the label).
const selectLabel = /choose a thing/i;
const selectEl = await screen.findByLabelText(selectLabel);
expect(selectEl).toBeInTheDocument();
userEvent.click(selectEl);
// Locate the corresponding popup (`listbox`) of options.
const optionsPopupEl = await screen.findByRole("listbox", {
name: selectLabel
});
// Click an option in the popup.
userEvent.click(within(optionsPopupEl).getByText(/marshmallow/i));
// Confirm the outcome.
expect(
await screen.findByText(/the thing is: marshmallow/i)
).toBeInTheDocument();
});
코드 및 상자 주의:테스트는 코드 앤 박스에서는 실행되지 않지만 로컬에서는 실행 및 통과됩니다.
다음으로 Select 옵션이 있는 MUI TextField의 작업 예를 나타냅니다.
샌드박스 : https://codesandbox.io/s/stupefied-chandrasekhar-vq2x0?file = / src / _ tests _ = Text Select . test . tsx : 0 - 1668
텍스트 필드:
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>
);
}
테스트:
import { fireEvent, render, screen } from "@testing-library/react";
import React from "react";
import { act } from "react-dom/test-utils";
import TextSelect, { sampleData } from "../MuiTextSelect/TextSelect";
import "@testing-library/jest-dom";
describe("Tests TextField Select change", () => {
test("Changes the selected value", () => {
const { getAllByRole, getByRole, container } = render(<TextSelect />);
//CHECK DIV CONTAINER
let vatSelectTextField = container.querySelector(
"#vatSelectTextField"
) as HTMLDivElement;
expect(vatSelectTextField).toBeInTheDocument();
//CHECK DIV CONTAINER
let vatSelectInput = container.querySelector(
"#vatSelectInput"
) as HTMLInputElement;
expect(vatSelectInput).toBeInTheDocument();
expect(vatSelectInput.value).toEqual(sampleData[0].name);
// OPEN
fireEvent.mouseDown(vatSelectTextField);
//CHECKO OPTIONS
expect(getByRole("listbox")).not.toEqual(null);
// screen.debug(getByRole("listbox"));
//CHANGE
act(() => {
const options = getAllByRole("option");
// screen.debug(getAllByRole("option"));
fireEvent.mouseDown(options[1]);
options[1].click();
});
//CHECK CHANGED
vatSelectInput = container.querySelector(
"#vatSelectInput"
) as HTMLInputElement;
expect(vatSelectInput.value).toEqual(sampleData[1].name);
});
});
/**
* HAVE A LOOK AT
*
*
* https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/Select/Select.test.js
* (ll. 117-121)
*
* https://github.com/mui-org/material-ui/blob/master/packages/material-ui/src/TextField/TextField.test.js
*
*
*/
머티리얼 UI 5.10.3을 사용하여 다음 방법으로 클릭을 시뮬레이션합니다.Select
컴포넌트 및 그 후에 아이템 값을 취득/잠금하고 그 중 하나를 클릭하여 기본 변경 이벤트를 트리거하려면 다음 절차를 따릅니다.
import { fireEvent, render, screen, within } from '@testing-library/react';
import { MenuItem, Select } from '@mui/material';
describe('MUI Select Component', () => {
it('should have correct options an handle change', () => {
const spyOnSelectChange = jest.fn();
const { getByTestId } = render(
<div>
<Select
data-testid={'component-under-test'}
value={''}
onChange={(evt) => spyOnSelectChange(evt.target.value)}
>
<MenuItem value="menu-a">OptionA</MenuItem>
<MenuItem value="menu-b">OptionB</MenuItem>
</Select>
</div>
);
const selectCompoEl = getByTestId('component-under-test');
const button = within(selectCompoEl).getByRole('button');
fireEvent.mouseDown(button);
const listbox = within(screen.getByRole('presentation')).getByRole(
'listbox'
);
const options = within(listbox).getAllByRole('option');
const optionValues = options.map((li) => li.getAttribute('data-value'));
expect(optionValues).toEqual(['menu-a', 'menu-b']);
fireEvent.click(options[1]);
expect(spyOnSelectChange).toHaveBeenCalledWith('menu-b');
});
});
여기도 있어요.
이게 MUI 5를 사용할 때 효과가 있었던 거예요.
userEvent.click(screen.getByLabelText(/^foo/i));
userEvent.click(screen.getByRole('option', {name: /^bar/i}));
import * as React from "react";
import ReactDOM from 'react-dom';
import * as TestUtils from 'react-dom/test-utils';
import { } from "mocha";
import Select from "@material-ui/core/Select";
import MenuItem from "@material-ui/core/MenuItem";
let container;
beforeEach(() => {
container = document.createElement('div');
document.body.appendChild(container);
});
afterEach(() => {
document.body.removeChild(container);
container = null;
});
describe("Testing Select component", () => {
test('start empty, open and select second option', (done) => {
//render the component
ReactDOM.render(<Select
displayEmpty={true}
value={""}
onChange={(e) => {
console.log(e.target.value);
}}
disableUnderline
classes={{
root: `my-select-component`
}}
>
<MenuItem value={""}>All</MenuItem>
<MenuItem value={"1"}>1</MenuItem>
<MenuItem value={"2"}>2</MenuItem>
<MenuItem value={"3"}>3</MenuItem>
</Select>, container);
//open filter
TestUtils.Simulate.click(container.querySelector('.my-select-component'));
const secondOption = container.ownerDocument.activeElement.parentElement.querySelectorAll('li')[1];
TestUtils.Simulate.click(secondOption);
done();
});
});
it('Set min zoom', async () => {
const minZoomSelect = await waitForElement( () => component.getByTestId('min-zoom') );
fireEvent.click(minZoomSelect.childNodes[0]);
const select14 = await waitForElement( () => component.getByText('14') );
expect(select14).toBeInTheDocument();
fireEvent.click(select14);
});
Material UI select 요소에 문제가 있었지만 결국 이 간단한 해결책을 찾았습니다.
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)
})
})
})
한 페이지에 여러 Select를 입력했습니다.이것부터 시험해 보세요.
import { render, fireEvent, within } from '@testing-library/react'
it('Should trigger select-xxx methiod', () => {
const { getByTestId, getByRole: getByRoleParent } = component
const element = getByTestId('select-xxx');
const { getByRole } = within(element)
const select = getByRole('button')
fireEvent.mouseDown(select);
const list = within(getByRoleParent('listbox')) // get list opened by trigger fireEvent
fireEvent.click(list.getByText(/just try/i)); //select by text
})
항목이 여러 인 경우 항목을 .name
<SelectDropdown
name="date_range"
...
>
...
</SelectDropdown>
<SelectDropdown
name="company"
...
>
...
</SelectDropdown>
// date filter
const date_range_dropdown = getByLabelText('Date Range');
fireEvent.mouseDown(date_range_dropdown);
await screen.findByRole('listbox');
fireEvent.click(
within(screen.getByRole('listbox')).getByText(/Last 30 Days/)
);
// // company filter
const company_dropdown = getByLabelText('Company');
fireEvent.mouseDown(company_dropdown);
fireEvent.click(within(getByRole('listbox')).getByText(/Uber/));
언급URL : https://stackoverflow.com/questions/55184037/react-testing-library-on-change-for-material-ui-select-component
'programing' 카테고리의 다른 글
응답 useReducer 비동기 데이터 가져오기 (0) | 2023.04.05 |
---|---|
JS index.js 파일에 접속하는 index.html을 ID 참조용으로 어떻게 반응합니까? (0) | 2023.04.05 |
BS Modal에서의 WooCommerce 고객 주문 상세 (0) | 2023.04.05 |
Angularjs $http 투고 파일 및 폼 데이터 (0) | 2023.04.05 |
Typescript에서 인터페이스 또는 클래스를 사용하는 경우 (0) | 2023.04.05 |