Другое

Как тестировать 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’”

Вот мой родительский компонент:

jsx
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>
  );
};

И дочерний компонент:

jsx
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}
    />
  );
};

Вот мой текущий юнит-тест:

jsx
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 рендерится как:

html
<button type="button" class="chakra-button root-thz07t">Process payment rejection</button>

Я считаю, что проблема связана с компонентами Chakra UI, но я не могу найти конкретных указаний по их правильному тестированию. Как мне следует изменить мой тест, чтобы правильно находить и взаимодействовать с кнопкой компонента Chakra?

При тестировании React-компонентов с Chakra UI часто возникают сложности с запросами getByRole из-за требований доступности и обертывания компонентов. Ошибка, которую вы видите, обычно указывает на то, что кнопка не распознается как доступный элемент библиотекой тестирования.

Содержание

Распространенные причины и решения

Ошибка “Unable to find an accessible element with the role ‘button’ and name ‘Process payment rejection’” обычно возникает в следующих случаях:

  1. Кнопка не имеет должной доступности: Кнопки Chakra UI должны быть доступны по умолчанию, но иногда CSS или другие атрибуты могут мешать источник

  2. Отсутствует обертка провайдера: Компоненты Chakra UI требуют контекста Provider для правильной работы в тестах

  3. Проблемы с таймингом: Компонент может быть не полностью отрисован в момент выполнения запроса

Первым шагом является обеспечение правильной конфигурации вашего тестового файла с оберткой провайдера Chakra UI. Как показано в официальной документации Chakra UI, вам нужно создать функцию кастомного рендеринга, которая оборачивает компоненты в Provider.


Исправление конфигурации провайдера

Создайте утилиту для тестов для обработки обертки провайдера:

jsx
// ./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>
  );
}

Затем измените ваш тест для использования этой функции кастомного рендеринга:

jsx
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() неоценим для отладки. Добавьте его перед вашим неработающим запросом, чтобы увидеть, что фактически отрисовано:

jsx
render(<PaymentRejection />);
screen.debug(undefined, 30000);

const openButton = screen.getByRole('button', {
  name: 'Process payment rejection',
});

2. Проверяйте скрытые элементы

Если кнопка появляется в выводе отладки, но не находится с помощью getByRole, она может быть скрыта. Как упоминается в ответе на Stack Overflow, CSS или aria-hidden="true" могут помешать нахождению элементов:

jsx
// Попробуйте выполнить запрос с опцией hidden, чтобы проверить, существует ли элемент, но скрыт
const hiddenButton = screen.getByRole('button', {
  name: 'Process payment rejection',
  hidden: true
});

Альтернативные методы запросов

Когда getByRole не работает, рассмотрите эти альтернативы:

1. Используйте getByText для простого соответствия тексту

Поскольку кнопка содержит текст, вы можете сначала запросить по тексту:

jsx
const openButton = screen.getByText('Process payment rejection');

2. Используйте getByLabelText, если у кнопки есть связанный метка

3. Добавьте test ID как запасной вариант

Если запросы доступности все еще не работают, добавьте data-testid в крайнем случае:

jsx
// В вашем компоненте
<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. Всегда оборачивайте ваши тестируемые компоненты:

jsx
function renderWithProvider(ui) {
  return render(
    <Provider>{ui}</Provider>
  );
}

2. Используйте семантические запросы в первую очередь

Как рекомендует Kent C. Dodds, отдавайте предпочтение семантическим запросам:

jsx
// ✅ Предпочтительно
screen.getByRole('button', { name: /submit/i })

// ❌ Избегайте
screen.getByTestId('submit-button')

3. Правильно обрабатывайте асинхронные операции

Если ваше модальное окно появляется после асинхронного действия, используйте waitFor:

jsx
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 разработаны с учетом доступности. Тестируйте их правильную работу с программами чтения с экрана и навигацией с клавиатуры.


Полный пример теста

Вот полный, надежный тест для вашего компонента:

jsx
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, которые могут влиять на видимость кнопки в тестовой среде.

Источники

  1. Документация по тестированию Chakra UI
  2. Stack Overflow - Не могу найти кнопку с помощью Test Library
  3. Kent C. Dodds - Распространенные ошибки с React Testing Library
  4. Testing Library - документация getByRole
Авторы
Проверено модерацией
Модерация