React Junior
207 subscribers
37 photos
462 links
Изучение React с нуля
加入频道
Testing Library. Расширение и песочница

Есть прикольное расширение, которое помогает выбрать подходящий метод для поиска элемента на странице: https://chromewebstore.google.com/detail/testing-playground/hejbmebodbijjdhflfknehhcgaklhano
Устанавливаем его, в DevTools появляется новая вкладка Testing Playground.
Открываем ее, наводим курсор на нужный элемент, и расширение пытается подобрать самый подходящий метод с учетом всех best practices.

А еще есть песочница, где можно ввести любой кусок HTML и поиграться с запросами - https://testing-playground.com/

#тестирование #testinglibrary #документация
👍3
byRole

Итак, начнем с подробного разбора самого главного запроса в Testing Library: byRole. Это методы getByRole, getAllByRole, queryByRole, queryAllByRole, findByRole, findAllByRole.

Документация: https://testing-library.com/docs/queries/byrole

Собственно роль

Первым параметром метод принимает собственно роль нужного элемента в виде строки. Это может быть button, heading, switch и так далее.

Речь идет не об атрибуте role, а прежде всего о дефолтной (встроенной) роли элементов. Например, элемент button имеет роль button, даже без явного указания на это. Вот тут можно почитать про роли элементов.

Опции

Вторым параметром можно передать объект с кучей дополнительных опций, которые помогут найти нужный элемент. Например, если у вас на странице куча кнопок, а нужно найти конкретную.

Тут будет много опций для поиска по aria- атрибутам.

🔸hidden: boolean

По умолчанию значение равно false - из-за этого в поиск не включаются "недоступные" элементы (display: none, aria-hidden, role=none).

🔸name: TextMatch

Для элемента формы - это текст лейбла, для кнопки - собственно текст кнопки. Также может использоваться значение атрибута aria-label.

Что за тип такой TextMatch? Это составной тип, который может быть обычной строкой, регулярным выражением или даже функцией.

Функция принимает два аргумента: content: string (собственно текстовый контент, по которому производится поиск) и element (DOMElement) и должна вернуть true или false.


screen.getByText((content, element) => content.startsWith('Hello'))


🔸description: TextMatch

Это для атрибута aria-describedby.

🔸selected: boolean

Отбор по значению атрибута aria-selected.

🔸busy: boolean

Отбор по значению атрибута aria-busy.

🔸checked: boolean

Отбор по значению атрибута aria-checked.

🔸pressed: boolean

Отбор по значению атрибута aria-pressed.

🔸suggest: boolean

Эта настройка для того, чтобы Testing Library предлагала вам запросы получше, чем тот, что написали вы.

🔸current: boolean | string

Отбор по значению атрибута aria-current.

🔸expanded: boolean

Отбор по значению атрибута aria-expanded.

🔸queryFallbacks: boolean

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

🔸level: number

Уровень заголовка для роли heading. Учитывает как семантику тега, так и атрибут aria-level.

🔸value

Это для группы атрибутов aria-value, например, aria-valuemin, aria-valuetext. Указывается в виде объекта:


screen.getByRole('spinbutton', { value: { min: 5, max: 10 }})


#тестирование #testinglibrary #документация
👍2
ByLabelText, ByPlaceholderText

Два запроса, предназначенных в основном для интерактивных элементов форм: поиск по тексту лейбла и по плейсхолдеру.

ByLabelText

Поле ввода может быть связано с лейблом разными способами:

- через атрибуты for и id
- через атрибут aria-labelledby
- если поле находится внутри label
- лейбл можно указать в атрибуте aria-label

Сигнатура:


screen.getByLabelText(
text: TextMatch,
options: {
selector?: string = '*',
exact?: boolean = true,
normalizer?: NormalizeFn
}
)


Текст лейбла можно задать первым параметром в виде строки, регулярки или функции (TextMatch).

Вторым параметром идет объект настроект:
- selector - можно дополнительно указать селектор нужного элемента
- exact - если текст задан в виде строки, то этот параметр определяет должен ли поиск быть точным (по полной строке, с учетом регистра символов) или нет
- normalizer - по умолчанию Testing Library нормализует текст (убирает лишние пробелы). Можно передать собственный нормализатор

ByPlaceholderText

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

Сигнатура:


screen.getByPlaceholderText(
text: TextMatch,
options: {
exact?: boolean = true,
normalizer?: NormalizerFn
}
)


#тестирование #testinglibrary #документация #примерыкода
👍2
ByDisplayValue

Еще один запрос для интерактивных элементов, у которых может быть value (input, textarea, select). Соответственно поиск происходит по текущему значению value - это текст, который отображается в инпуте. Настроек у запроса немного:


screen.getByDisplayValue(
value: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
}
)


#тестирование #testinglibrary #документация #примерыкода
👍1
ByText

Есть возможность искать элементы по текстовому контенту. Это сработает для всех элементов, у которых есть textContent, а также для инпутов с типом submit или button.

Настройки у запроса стандартные:


getByText(
text: TextMatch,
options?: {
selector?: string = '*',
exact?: boolean = true,
ignore?: string|boolean = 'script, style',
normalizer?: NormalizerFn,
})

Новая для нас настройка - ignore, она указывает, какие селекторы игнорировать при поиске.

#тестирование #testinglibrary #документация #примерыкода
👍1
ByAltText, ByTitle

Еще два запроса для поиска по значению атрибутов:

- alt - в основном для изображений
- title


screen.getByAltText(
text: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
})

screen.getByTitle(
title: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
})


#тестирование #testinglibrary #документация #примерыкода
👍1
ByTestId

И наконец последний, запасной запрос, который рекомендуется использовать только в самом крайнем случае, если ничего другое вам не подошло - поиск по атрибуту data-testid (название атрибута можно изменить в настройках).


screen.getByTestId(
text: TextMatch,
options?: {
exact?: boolean = true,
normalizer?: NormalizerFn,
})


#тестирование #testinglibrary #документация #примерыкода
👍1
Testing Library. Вызов событий. fireEvent

В тестах часто требуется проверить как элементы реагируют на действия пользователя, например, что при клике на кнопку отправились данные формы.

Testing Library предоставляет несколько способов эмулировать такие события. Самый простой - метод fireEvent из пакета @testing-library/dom.

Документация нам говорит, что в большинстве случаев мы будем использовать другой пакет - @testing-library/user-event, но пока посмотрим на этот.

Метод fireEvent принимает первым аргументом элемент, на котором необходимо вызвать событие. Вторым - объект события.


fireEvent(input, new MouseEvent('click'))


Также есть несколько готовых методов для конкретных событий, которым можно передать объект с настройками:


fireEvent.change(input, { target: { value: 'hello' } })
fireEvent.keyDown(domNode, {key: 'Enter', code: 'Enter', charCode: 13})


Дока тут: https://testing-library.com/docs/dom-testing-library/api-events

Это особенно полезно для сложных событий ввода, например, для инпута с файлами, которому мы не можем вручную установить значение свойства files.

Пример: https://codesandbox.io/p/devbox/testing-library-global-jsdom-screen-react-junior-forked-7t9cmt

#тестирование #testinglibrary #документация #примерыкода
👍2
Testing Library. Асинхронщина. waitFor

В ряде случаев для тестирования нам нужно подождать, когда на странице что-то произойдет: появится/пропадет конкретный элемент, закончится какой-то таймер, юзер что-то кликнет и так далее. Библиотека предоставляет несколько способов для такого ожидания.

waitFor

Основная механика ожидания - это метод waitFor. Первым аргументом он принимает коллбэк, который собственно и должен проверить, произошло ли то, что нужно. Если произошло, нужно вернуть true, если нет - выбросить исключение. В этом случае коллбэк будет вызван для проверки снова спустя некоторое время, и так до тех пор, пока он не будет удовлетворен.

Мы можем даже использовать expect для проверки условий, так как при несоответствии он как раз выбрасывает ошибку:


await waitFor(() => expect(mockAPI).toHaveBeenCalledTimes(1))


Вместо коллбэка-функции можно передать промис, он не будет вызываться повторно.

Вторым аргументом можно передать объект с настройками:

- container: HTMLElement - по умолчанию document
Если мы ждем появления элемента внутри конкретного контейра, то можно передать его
- timeout - время ожидания
- interval - как часто вызывать коллбэк
- onTimeout: (error: Error) => Error - по умолчанию добавляет к ошибке текущее состояние элемента container
- mutationObsereverOptions - для настройки вызова коллбэка при изменениях контейнера

#тестирование #testinglibrary #документация #примерыкода
👍2👏1
Testing Library. Асинхронщина. findBy

Мы помним, что у нас есть три вида запросов - getBy, queryBy и findBy. Так вот findBy - это комбинация getBy и уже рассмотренного выше метода waitFor. Таким образом, запрос findBy по умолчанию ожидает, когда искомый элемент появится на странице.

Можно использовать в комбинации с await:


await screen.findByText('Clicked once')


#тестирование #testinglibrary #документация #примерыкода
👍1
Testing Library. Асинхронщина. waitForElementToBeRemoved

Есть еще одна утилита, позволяющая дождаться, когда конкретный элемент исчезнет со страницы: waitForElementToBeRemoved.

Первым параметром она принимает элемент или массив элементов. Кроме того, можно передать функцию-коллбэк, которая вернет элемент или массив элементов. Если элемент null или массив пустой, будет ошибка.


const el = document.querySelector('div.getOuttaHere')
await waitForElementToBeRemoved(el)


Вторым параметром можно передать объект с настройками, такой же как у функции waitFor.

#тестирование #testinglibrary #документация #примерыкода
👍1
Testing Library. Размышления о fireEvent

Хорошая статья в документации про особенности эмуляции событий: https://testing-library.com/docs/guide-events

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

Например, если мы вызываем метод fireEvent.click(element), он задиспатчит событие клика и сработает обработчик клика, если он есть. В большинстве случаев нам этого более чем достаточно. Однако когда настоящий юзер кликает на настоящий элемент, мы получаем гораздо больше событий: mouseOver, mouseMove, mouseDown, focus (если элемент focusable), mouseUp и только теперь, наконец, click.

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

Статья предлагает пару паттернов для эмуляции событий с помощью fireEvent. Например, вместо прямого вызова события keyDown на элементе, лучше сначала сфокусироваться на нем, а затем вызвать событие на document.activeElement:


getByText('click me')
fireEvent.keyDown(document.activeElement || document.body)


#тестирование #testinglibrary #документация #примерыкода
👍2
Testing Library. Тестирование доступности

Если возникла необходимость затестить доступность вашего приложения, Testing Library предлагает пару полезных утилит:

getRoles

Находит на странице все элементы, имеющие роли, и возвращает их в виде объекта

logRoles

То же самое, только выводит данные в консоль.

isInaccessible

Проверяет, исключен ли элемент из дерева доступности браузера.

Подробнее в документации: https://testing-library.com/docs/dom-testing-library/api-accessibility

#тестирование #testinglibrary #документация
👍1
Testing Library. Debugging

Библиотека предоставляет ряд возможностей для отладки

- Если запросы get или find не находят элементов, то они выбрасывают ошибку, а в консоль выводится DOM корневого элемента (screen или container)
- Утилита prettyDOM принимает элемент и возвращает его структуру
- Метод screen.debug() или screen.debug(element) также получает DOM элемента и выводит его в консоль
- Метод screen.logTestingPlaygroundURL() выводит урл песочницы, в которой уже будет сохранен ваш документ
- Утилита logRoles выводит все элементы, имеющие роли

Подробнее в документации: https://testing-library.com/docs/dom-testing-library/api-debugging

#тестирование #testinglibrary #документация
👍1
Testing Library. within и кастомные запросы

Библиотека также предоставляет ряд низкоуровневых функций для построения более точных/сложных запросов: https://testing-library.com/docs/dom-testing-library/api-custom-queries

И еще есть полезная функция within(element). Она оборачивает полученный элемент и возвращает объект со всеми уже известными нам методами (как у объекта screen). И все эти методы работают "в контексте" полученного элемента: https://testing-library.com/docs/dom-testing-library/api-within

#тестирование #testinglibrary #документация
👍3