Тестирование фронтенд компонентов с jest-dom на видимость пользователю

f35919e71b9f8ebf739d7797861ed015.png

Решаем проблему с тестирование элемента что он виден пользователю или не виден.

Предпосылки

Разработчики react-testing-library рекомендуют нам писать тесты так, как если бы код работал как у пользователя. А это значит что нужно вызывать пользовательские события, проверять что пользователь видит тот компонент, который должен.

Для тестирования компонентов мы используем библиотеку react-testing-library с testing-library/jest-dom, в рамках которого мы проверяем показывается тот или иной компонент на странице или нет.

Варианты решения

  1. Проверять с помощью метод toHaveClass то, что у элемента есть тот или иной класс. Не является хорошей практикой, т.к. на скрытие элемента также влияют его родители. Если родительский элемент не виден пользователь, соответственно и его дочерние тоже не будут.

  2. Использовать метод toBeVisible, который под капотом использует getComputedStyle, на расчет которого как раз влияют родительские компоненты и примененные классы к ним и к самому элементу. Но мы чаще всего css файлы стабаем (заменяем пустым объектом), чтобы тесты быстрее проходили, т.к. нам в принципе не важно, как выглядят компоненты у пользователя. Но есть проблема, как нам дать знать тестовому окружению, что у нас есть классы которые влияют на скрытие элемента.

Решение

Можно конечно попробовать каким-то спосом загрузить все стили приложения при выполнении тестов, но это негативно может повлиять на скорость загрузки. Поэтому такой вариант нас не устраивает, такой вариант описан здесь.

Самый простое и легкое решение, это загрузить только те классы, которые у нас вызывает сокрытие у пользователя компонента. Если мы говорим про tailwindcss, то это классы hidden и invisible.

Добавим в файл jest.setup.js небольшой код, который решает нашу проблему:

import '@testing-library/jest-dom';

beforeEach(() => {
    const style = document.createElement('style');
    style.innerHTML = `
    .hidden {
        display: none;
    }
    .invisible {
        visibility: hidden;
    }
    `;
    document.head.appendChild(style);
});

Пример теста:

...
test('Check element is show and hidden', async () => {
  render();
  const searchInput = screen.getByPlaceholderText(/Найти/i);

  await userEvent.click(searchInput);

  const listbox = screen.getByRole('listbox');

  expect(listbox).toBeVisible();

  await userEvent.click(document.body);

  // если вы вдруг будете запрашивать элемент когда он скрыт используется getByRole с hidden: true, иначе элемент не найдет, т.к. для пользователя он скрыт, но присутствует в DOM-дереве
  // const listbox = screen.getByRole('listbox', { hidden: true });

  expect(listbox).not.toBeVisible();
});
...

В данном тесте мы находим элемент у которого placeholder со значением «Найти», после мы имитируем нажатие на этот элемент. Компонент реализован у нас таким образом, что при нажатии на него, у нас показывается список элементов.

Затем с помощью getByRole ищем семантический тег список, проверяем его что он показывает на экране. Затем мы имитируем нажатие на тело документа, в приложении у нас компонент список скрывается на это действие и мы проверяем это с помощью expect (listbox).not.toBeVisible ();

Но если бы мы в файле jest.setup.js не добавили описание для этих css стилей, которые скрывают элемент, то у нас тест бы не прошел, т.к. jest-dom думал бы, что элемент показывается, т.к. он не знает «реализацию» класса.

P.S. Также есть метод toBeInTheDocument, он проверяет если ли элемент в DOM-дереве элементов, но он не проверяет что видит пользователь или нет его на экране.

© Habrahabr.ru