Как тестировать React-компоненты с Chakra UI: Полное руководство
Узнайте, как правильно тестировать React-компоненты с использованием Chakra UI. Исправьте ошибки тестирования доступности и напишите надежные юнит-тесты для ваших Chakra UI-компонентов с помощью этого подробного руководства.
Как правильно тестировать React компонент с Chakra UI, когда в юнит-тестах не удается найти доступный элемент по роли и имени?
У меня возникают проблемы с написанием юнит-теста для моего React компонента, который использует Chakra UI. Тест завершается с ошибкой: “TestingLibraryElementError: Unable to find an accessible element with the role ‘button’ and name ‘Process payment rejection’”
Вот мой родительский компонент:
import { useState } from 'react';
import VButton from 'ui-components/src/v2/VButton.jsx';
import { Provider } from 'ui-components/src/v2/providers/chakra.jsx';
import { ProcessPaymentRejectionModal } from './ProcessPaymentRejectionModal.jsx';
export const PaymentRejection = () => {
const [openModal, setOpenModal] = useState(false);
return (
<Provider>
<VButton
ml="8px"
h="32px"
fontSize="12px"
textTransform="uppercase"
onClick={() => setOpenModal(true)}
>
Process payment rejection
</VButton>
<ProcessPaymentRejectionModal
openModal={openModal}
onClose={() => setOpenModal(false)}
/>
</Provider>
);
};
И дочерний компонент:
import { useState } from 'react';
import VDialog from 'ui-components/src/v2/VDialog.jsx';
import { ProcessPaymentRejectionModalContent } from './ProcessPaymentRejectionModalContent.jsx';
export const ProcessPaymentRejectionModal = ({ openModal, onClose }) => {
const paymentsData = [
{ value: 'payment 1', label: 'Payment 1' },
{ value: 'payment 2', label: 'Payment 2' },
{ value: 'payment 3', label: 'Payment 3' },
];
const [payments, setPayments] = useState(paymentsData);
const [formData, setFormData] = useState({
selectedPayment: '',
comment: '',
});
const handleFormData = (dataFromChild) => {
setFormData(dataFromChild);
};
const handleSave = () => {
// to be removed when data fetched fro endpoint
console.log('formData:', formData);
onClose();
};
return (
<VDialog
size="lg"
title="Process Payment Rejection"
saveBtnText="Process Rejection"
body={
<ProcessPaymentRejectionModalContent
payments={payments}
onDataChange={handleFormData}
/>
}
open={openModal}
onOpenChange={onClose}
onCancel={onClose}
onOk={handleSave}
/>
);
};
Вот мой текущий юнит-тест:
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { PaymentRejection } from './PaymentRejection.jsx';
describe('Payment Rejection flow', () => {
test('opens modal on button click', () => {
render(<PaymentRejection />);
screen.debug(undefined, 30000);
const openButton = screen.getByRole('button', {
name: 'Process payment rejection',
});
fireEvent.click(openButton);
});
});
Компонент VButton рендерится как:
<button type="button" class="chakra-button root-thz07t">Process payment rejection</button>
Я считаю, что проблема связана с компонентами Chakra UI, но я не могу найти конкретных указаний по их правильному тестированию. Как мне следует изменить мой тест, чтобы правильно находить и взаимодействовать с кнопкой компонента Chakra?
При тестировании React-компонентов с Chakra UI часто возникают сложности с запросами getByRole из-за требований доступности и обертывания компонентов. Ошибка, которую вы видите, обычно указывает на то, что кнопка не распознается как доступный элемент библиотекой тестирования.
Содержание
- Распространенные причины и решения
- Исправление конфигурации провайдера
- Методы отладки доступности
- Альтернативные методы запросов
- Лучшие практики для тестирования Chakra UI
- Полный пример теста
Распространенные причины и решения
Ошибка “Unable to find an accessible element with the role ‘button’ and name ‘Process payment rejection’” обычно возникает в следующих случаях:
-
Кнопка не имеет должной доступности: Кнопки Chakra UI должны быть доступны по умолчанию, но иногда CSS или другие атрибуты могут мешать источник
-
Отсутствует обертка провайдера: Компоненты Chakra UI требуют контекста
Providerдля правильной работы в тестах -
Проблемы с таймингом: Компонент может быть не полностью отрисован в момент выполнения запроса
Первым шагом является обеспечение правильной конфигурации вашего тестового файла с оберткой провайдера Chakra UI. Как показано в официальной документации Chakra UI, вам нужно создать функцию кастомного рендеринга, которая оборачивает компоненты в Provider.
Исправление конфигурации провайдера
Создайте утилиту для тестов для обработки обертки провайдера:
// ./testing/render.tsx
import { Provider } from 'ui-components/src/v2/providers/chakra.jsx';
import { render as rtlRender } from '@testing-library/react';
export function render(ui) {
return rtlRender(
<Provider>{ui}</Provider>
);
}
Затем измените ваш тест для использования этой функции кастомного рендеринга:
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { PaymentRejection } from './PaymentRejection.jsx';
describe('Процесс отклонения платежа', () => {
test('открывает модальное окно при нажатии на кнопку', () => {
render(<PaymentRejection />);
const openButton = screen.getByRole('button', {
name: 'Process payment rejection',
});
fireEvent.click(openButton);
});
});
Методы отладки доступности
Когда getByRole не работает, это часто происходит потому, что элемент не распознается как доступный. Используйте эти методы отладки:
1. Используйте screen.debug() для проверки отрендеренного вывода
Как вы уже сделали, screen.debug() неоценим для отладки. Добавьте его перед вашим неработающим запросом, чтобы увидеть, что фактически отрисовано:
render(<PaymentRejection />);
screen.debug(undefined, 30000);
const openButton = screen.getByRole('button', {
name: 'Process payment rejection',
});
2. Проверяйте скрытые элементы
Если кнопка появляется в выводе отладки, но не находится с помощью getByRole, она может быть скрыта. Как упоминается в ответе на Stack Overflow, CSS или aria-hidden="true" могут помешать нахождению элементов:
// Попробуйте выполнить запрос с опцией hidden, чтобы проверить, существует ли элемент, но скрыт
const hiddenButton = screen.getByRole('button', {
name: 'Process payment rejection',
hidden: true
});
Альтернативные методы запросов
Когда getByRole не работает, рассмотрите эти альтернативы:
1. Используйте getByText для простого соответствия тексту
Поскольку кнопка содержит текст, вы можете сначала запросить по тексту:
const openButton = screen.getByText('Process payment rejection');
2. Используйте getByLabelText, если у кнопки есть связанный метка
3. Добавьте test ID как запасной вариант
Если запросы доступности все еще не работают, добавьте data-testid в крайнем случае:
// В вашем компоненте
<VButton
data-testid="process-payment-rejection-btn"
// ... другие свойства
>
Process payment rejection
</VButton>
// В вашем тесте
const openButton = screen.getByTestId('process-payment-rejection-btn');
Лучшие практики для тестирования Chakra UI
1. Всегда используйте обертку с провайдером
Компоненты Chakra UI зависят от контекста Theme Provider. Всегда оборачивайте ваши тестируемые компоненты:
function renderWithProvider(ui) {
return render(
<Provider>{ui}</Provider>
);
}
2. Используйте семантические запросы в первую очередь
Как рекомендует Kent C. Dodds, отдавайте предпочтение семантическим запросам:
// ✅ Предпочтительно
screen.getByRole('button', { name: /submit/i })
// ❌ Избегайте
screen.getByTestId('submit-button')
3. Правильно обрабатывайте асинхронные операции
Если ваше модальное окно появляется после асинхронного действия, используйте waitFor:
import { waitFor } from '@testing-library/react';
test('открывает модальное окно при нажатии на кнопку', async () => {
render(<PaymentRejection />);
const openButton = screen.getByRole('button', {
name: 'Process payment rejection',
});
fireEvent.click(openButton);
// Ожидание появления модального окна
await waitFor(() => {
expect(screen.getByRole('dialog')).toBeInTheDocument();
});
});
4. Тестируйте функции доступности
Компоненты Chakra UI разработаны с учетом доступности. Тестируйте их правильную работу с программами чтения с экрана и навигацией с клавиатуры.
Полный пример теста
Вот полный, надежный тест для вашего компонента:
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
import '@testing-library/jest-dom';
import { PaymentRejection } from './PaymentRejection.jsx';
describe('Процесс отклонения платежа', () => {
test('открывает модальное окно при нажатии на кнопку', async () => {
// Arrange
render(<PaymentRejection />);
// Проверка существования кнопки и ее доступности
const openButton = screen.getByRole('button', {
name: 'Process payment rejection',
});
// Act
fireEvent.click(openButton);
// Assert - ожидание появления модального окна
await waitFor(() => {
expect(screen.getByRole('dialog')).toBeInTheDocument();
expect(screen.getByRole('dialog')).toHaveTextContent('Process Payment Rejection');
});
});
test('закрывает модальное окно при отмене', async () => {
render(<PaymentRejection />);
// Сначала открываем модальное окно
const openButton = screen.getByRole('button', {
name: 'Process payment rejection',
});
fireEvent.click(openButton);
// Ожидание появления модального окна
await waitFor(() => {
expect(screen.getByRole('dialog')).toBeInTheDocument();
});
// Находим и нажимаем кнопку отмены
const cancelButton = screen.getByRole('button', {
name: /cancel/i
});
fireEvent.click(cancelButton);
// Проверка закрытия модального окна
await waitFor(() => {
expect(screen.queryByRole('dialog')).not.toBeInTheDocument();
});
});
});
Если у вас все еще возникают проблемы, рассмотрите временное использование опции hidden, чтобы проверить, существует ли элемент, но скрыт, или проверьте, нет ли переопределений CSS, которые могут влиять на видимость кнопки в тестовой среде.