13. Queries и селекторы

Приоритет queries
Заголовок раздела «Приоритет queries»RTL рекомендует использовать queries в следующем порядке (от лучшего к худшему):
1. getByRole ← семантика, доступность2. getByLabelText ← форма3. getByPlaceholderText4. getByText5. getByDisplayValue6. getByAltText ← изображения7. getByTitle8. getByTestId ← последний resortgetByRole (приоритет №1)
Заголовок раздела «getByRole (приоритет №1)»Ищет по ARIA роли — соответствует тому, что видит screen reader.
// Пример разметки<button>Submit</button><a href="/about">About</a><h1>Page Title</h1><input type="checkbox" /><img alt="Cat photo" /><nav>Navigation</nav><form>Form</form>
// Queriesscreen.getByRole('button', { name: /submit/i });screen.getByRole('link', { name: /about/i });screen.getByRole('heading', { name: /page title/i, level: 1 });screen.getByRole('checkbox');screen.getByRole('img', { name: /cat photo/i });screen.getByRole('navigation');
// Все роли: button, link, heading, textbox, checkbox, radio,// combobox, listbox, option, img, navigation, dialog,// alert, progressbar, tab, tabpanel, list, listitem...getByLabelText (форма)
Заголовок раздела «getByLabelText (форма)»// Разметка<label htmlFor="email">Email</label><input id="email" type="email" />
// aria-label<input aria-label="Search" type="search" />
// aria-labelledby<span id="username-label">Username</span><input aria-labelledby="username-label" />
// Queriesscreen.getByLabelText('Email');screen.getByLabelText(/email/i);screen.getByLabelText('Search');screen.getByLabelText('Username');getByText
Заголовок раздела «getByText»<p>Welcome, Alice!</p><button>Click me</button>
screen.getByText('Welcome, Alice!');screen.getByText(/welcome/i);screen.getByText(/alice/i, { exact: false }); // частичное совпадениеВарианты: getBy, queryBy, findBy
Заголовок раздела «Варианты: getBy, queryBy, findBy»// getBy*: выбрасывает если не найден / найдено несколькоscreen.getByRole('button') // ✅ нашёл один, ❌ если 0 или 2+
// queryBy*: возвращает null если не найденscreen.queryByRole('dialog') // null если нет диалогаexpect(screen.queryByText('Error')).not.toBeInTheDocument()
// findBy*: async, ждёт появления элементаconst element = await screen.findByRole('alert') // ждёт до 1000мс
// getAllBy* / queryAllBy* / findAllBy* — множественные результатыconst buttons = screen.getAllByRole('button')expect(buttons).toHaveLength(3)Когда использовать каждый
Заголовок раздела «Когда использовать каждый»// getBy: элемент должен быть в DOM прямо сейчасconst submitBtn = screen.getByRole('button', { name: /submit/i })
// queryBy: элемент может отсутствоватьif (screen.queryByRole('dialog')) { // диалог открыт}expect(screen.queryByText('Error')).not.toBeInTheDocument()
// findBy: элемент появится асинхронно (после загрузки данных)const loadedContent = await screen.findByText('Loaded!')ByTestId — последний resort
Заголовок раздела «ByTestId — последний resort»// Используй только когда нет семантического способа<div data-testid="custom-widget">...</div>
screen.getByTestId('custom-widget')
// Лучше добавь роль через aria<div role="region" aria-label="Stats Widget">...</div>screen.getByRole('region', { name: /stats widget/i })within — поиск внутри контейнера
Заголовок раздела «within — поиск внутри контейнера»<ul> <li data-testid="user-1"> <span>Alice</span> <button>Edit</button> </li> <li data-testid="user-2"> <span>Bob</span> <button>Edit</button> </li></ul>import { within } from '@testing-library/react';
// Найти кнопку Edit только для Aliceconst aliceRow = screen.getByTestId('user-1');const editButton = within(aliceRow).getByRole('button', { name: /edit/i });await userEvent.click(editButton);screen.debug() — отладка
Заголовок раздела «screen.debug() — отладка»test('debug example', () => { render(<MyComponent />);
screen.debug(); // вывести весь DOM screen.debug(screen.getByRole('button')); // только элемент});Практические задания
Заголовок раздела «Практические задания»- Найди 5 разных элементов на странице используя getByRole
- Протестируй условное отображение с queryBy
- Протестируй динамически появляющийся элемент (setTimeout) с findBy
- Приоритет: Role > LabelText > Text > TestId
- getBy — синхронный, обязательный
- queryBy — синхронный, опциональный
- findBy — асинхронный, ждёт появления
- within() — поиск в конкретном контейнере