#javascript #reactjs #jestjs #react-testing-library
#javascript #reactjs #jestjs #react-testing-library
Вопрос:
У меня есть компонент выбора даты, подобный этому:
const MonthPanel = ({ setMode = () => {} }) => {
return (
<div>
<button
onClick={(e) => setMode("year")}
data-testid="datepicker-year-button"
>
2021
</button>
</div>
);
};
const YearPanel = () => {
return <div data-testid="datepicker-year-panel">YearPanel</div>;
};
const DatePicker = (props) => {
const [open, setOpen] = useState(false);
const [mode, setMode] = useState("month");
const containerRef = useRef();
const handleOpen = () => {
setOpen(true);
};
const handleClose = () => {
setOpen(false);
};
const handleModeChange = (newMode) => {
setMode(newMode);
};
const generatePanel = () => {
switch (mode) {
case "month":
return <MonthPanel setMode={handleModeChange} />;
case "year":
return <YearPanel />;
default:
return null;
}
};
useEffect(() => {
const handleOutsideClick = (e) => {
if (containerRef.current amp;amp; !containerRef.current.contains(e.target)) {
handleClose();
}
};
window.addEventListener("click", handleOutsideClick);
return () => {
window.removeEventListener("click", handleOutsideClick);
};
}, []);
return (
<div
ref={containerRef}
data-testid="datepicker-container"
>
<div onClick={handleOpen} data-testid="datepicker-input">
Select a date
</div>
{open amp;amp; <div data-testid="datepicker-dialog">{generatePanel()}</div>}
</div>
);
};
И у меня есть такой тестовый файл, как этот:
it.only("should close DatePicker dialog when clicked only outside DatePicker", () => {
const { getByTestId, queryByTestId } = render(
<DatePicker />
);
userEvent.click(getByTestId("datepicker-input"));
userEvent.click(getByTestId("datepicker-year-button"));
expect(queryByTestId("datepicker-dialog")).toBeInTheDocument();
userEvent.click(document.body);
expect(queryByTestId("datepicker-dialog")).toBeNull();
});
Желаемое состояние:
Когда вы нажимаете вне DatePicker, он должен закрыться. И когда вы нажмете [data-testid="datepicker-year-button"]
на него, он должен изменить режим выбора даты на «год», чтобы была показана панель «год».
Текущее состояние:
Когда вы нажимаете [data-testid="datepicker-year-button"]
, он меняет режим выбора даты на «год», а панель месяцев (а вместе с ней и сама кнопка) удаляется. Поскольку кнопка является целью события и уже удалена, containerRef.current.contains(e.target)
условие is false
и Диалоговое окно также будут удалены. Но тест показывает, что диалоговое окно находится в документе.
Вопрос в том, как я должен правильно протестировать эту функциональность.
Комментарии:
1. вы уверены, что ваш тестировщик знает, как обращаться
document.body
?2. кажется, ваше желаемое состояние на самом деле заключается в том, что тест завершается неудачей, когда он должен. Вы могли бы обновить вопрос, чтобы отразить это.
Ответ №1:
Вы можете вызвать e.stopPropagation()
обработчик нажатия кнопки <MonthPanel>
, чтобы предотвратить всплывающее событие и его перехват window
. eventListener
<button
onClick={(e) => {
e.stopPropagation();
setMode("year");
}}
data-testid="datepicker-year-button"
>
Комментарии:
1. Я это знаю. Проблема в том, что тест должен завершиться неудачей, но он проходит. Я хочу знать, что не так с моим тестом и как я должен его протестировать.
2. Итак, вы пытаетесь сделать тест неудачным специально? Вероятно, вам следует упомянуть об этом в исходном сообщении.
3. Нет. Код работает некорректно, верно. Таким образом, тест должен завершиться неудачей, но он проходит. Я спрашиваю, что не так с моим тестом.
4. Я имею в виду, что вы специально изменили код, чтобы он не работал должным образом, чтобы тест не прошел. Но в данном случае это на самом деле не сбой.
5. Да. Это так.
Ответ №2:
Попробуйте использовать асинхронные методы, такие как waitFor
или waitForElementToBeRemoved
:
await waitFor(() => {
expect(queryByTestId("datepicker-dialog")).toBeNull();
});
React-dom
введенный act API
для переноса кода, который отображает или обновляет компоненты, это приближает выполнение теста к тому, как React работает в браузере. Библиотека тестирования React включает некоторые из своих API в функцию act, но в некоторых случаях вам все равно потребуется использовать waitFor или waitForElementToBeRemoved
Ответ №3:
Я считаю, что ваш userEvent.click(document.body);
не работает должным образом.
Скорее document.body
всего, не решается так, как должно. вероятно, вы используете пользовательское событие testing-library, и их основным примером является:
import { screen } from '@testing-library/dom'
import userEvent from '@testing-library/user-event'
test('types inside textarea', () => {
document.body.innerHTML = `<textarea />`
userEvent.type(screen.getByRole('textbox'), 'Hello, World!')
expect(screen.getByRole('textbox')).toHaveValue('Hello, World!')
})
убедитесь, что у вас document.body.innerHTML
установлено значение something