programing

재료 UI 변경 시 반응 테스트 라이브러리 구성 요소 선택

testmans 2023. 4. 5. 21:25
반응형

재료 UI 변경 시 반응 테스트 라이브러리 구성 요소 선택

테스트하려고 합니다.onChangereact-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는 다음 사이트에서 찾을 수 있습니다.

material-ui 선택에 대한 부팅 변경 이벤트 편집

사용.*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

반응형