selectOption() работает только с нативными элементами : вызови его на React Select или Material UI combobox собранном из div-ов и Playwright бросит ошибку. Кастомные дропдауны требуют другого подхода: клик чтобы открыть, затем клик по опции через role или видимый текст. Эта статья разбирает нативные select-ы, паттерн click-open для кастомных компонентов, дропдауны с фильтрацией по вводу, навигацию с клавиатуры, специфичную структуру React Select, мультиселект, и ассёрт который подтверждает что выбор действительно произошёл, а не просто что клик не бросил ошибку.
Нативные элементы
Простейший случай: стандартный HTML-элемент .
<label for="country">Country</label>
<select id="country">
<option value="">Select a country</option>
<option value="us">United States</option>
<option value="de">Germany</option>
<option value="pl">Poland</option>
</select>// Выбор по видимому тексту
await page.getByLabel('Country').selectOption('Germany');
// Выбор по атрибуту value
await page.getByLabel('Country').selectOption({ value: 'de' });
// Выбор по индексу (0-based, лучше избегать — хрупко)
await page.getByLabel('Country').selectOption({ index: 2 });
// Проверка выбора
await expect(page.getByLabel('Country')).toHaveValue('de');
// Мультиселект (для <select multiple>)
await page.getByLabel('Tags').selectOption(['playwright', 'typescript']);selectOption() работает только с элементами . Для кастомных дропдаунов нужен другой подход.
Кастомные dropdown-компоненты
Большинство современных приложений используют кастомные компоненты (React Select, Headless UI, Material UI) вместо нативного . Внешне они выглядят как дропдауны, но устроены иначе.
Паттерн 1: клик чтобы открыть, клик по опции
Самый распространённый паттерн кастомного дропдауна:
// Кастомный дропдаун: открыть кликом, выбрать кликом
await page.getByRole('combobox', { name: 'Country' }).click();
await page.getByRole('option', { name: 'Germany' }).click();
// Проверка
await expect(page.getByRole('combobox', { name: 'Country' })).toHaveText('Germany');Если дропдаун использует роль listbox:
await page.getByRole('button', { name: 'Country' }).click();
await page.getByRole('listbox').getByRole('option', { name: 'Germany' }).click();Паттерн 2: ввод для фильтрации, затем выбор
Многие кастомные дропдауны поддерживают поиск:
// Вводим текст для фильтрации
await page.getByRole('combobox', { name: 'Country' }).fill('Ger');
// Ждём появления отфильтрованных результатов
await page.getByRole('option', { name: 'Germany' }).waitFor();
// Кликаем по опции
await page.getByRole('option', { name: 'Germany' }).click();Паттерн 3: навигация с клавиатуры
Некоторые дропдауны управляются с клавиатуры:
await page.getByLabel('Country').focus();
await page.keyboard.press('ArrowDown'); // Открыть дропдаун
await page.keyboard.press('ArrowDown'); // Перейти к первой опции
await page.keyboard.press('ArrowDown'); // Перейти ко второй опции
await page.keyboard.press('Enter'); // ВыбратьИли ввод буквы для перехода к совпадающей опции:
await page.getByLabel('Country').focus();
await page.keyboard.type('G'); // Перейти к Germany
await page.keyboard.press('Enter');Как разобраться в реализации дропдауна
Перед написанием теста изучи элемент в DevTools:
1. Правая кнопка на дропдауне, Inspect
2. Проверь тип элемента: 3. Если кастомный, взаимодействуй с ним вручную и наблюдай изменения в DOM 4. Проверь атрибут Правильный подход к тестированию полностью зависит от реализации. Не угадывай. React Select одна из самых распространённых библиотек дропдаунов. У неё специфичное поведение: React Select использует специфичные CSS-классы. Если твоя версия генерирует другие имена классов, адаптируй соответственно. По возможности предпочитай Поля ввода даты с отдельными дропдаунами для месяца, дня и года: Для или ?
role или ARIA-ролиReact Select
// React Select combobox
const dropdown = page.locator('.react-select__control');
await dropdown.click();
// Вводим текст для фильтрации
await dropdown.locator('input').fill('Ger');
await page.locator('.react-select__option', { hasText: 'Germany' }).click();
// Проверка
await expect(page.locator('.react-select__single-value')).toHaveText('Germany');
// Сбросить выбор
await page.locator('.react-select__clear-indicator').click();getByRole:// Лучше — использует семантические роли
await page.getByRole('combobox', { name: 'Country' }).click();
await page.getByRole('option', { name: 'Germany' }).click();Мультиселект дропдауны
// Нативный <select multiple>
await page.getByLabel('Skills').selectOption(['playwright', 'typescript', 'cicd']);
// Кастомный мультиселект: кликаем несколько опций
await page.getByRole('button', { name: 'Skills' }).click();
await page.getByRole('option', { name: 'Playwright' }).click();
await page.getByRole('option', { name: 'TypeScript' }).click();
await page.getByRole('option', { name: 'CI/CD' }).click();
// Закрыть дропдаун
await page.keyboard.press('Escape');
// Проверить выбранные значения
await expect(page.getByTestId('selected-skills')).toContainText('Playwright');
await expect(page.getByTestId('selected-skills')).toContainText('TypeScript');Дропдауны дат (день, месяц, год)
await page.getByLabel('Month').selectOption('June');
await page.getByLabel('Day').selectOption('15');
await page.getByLabel('Year').selectOption('1990');:// Работает во всех браузерах
await page.getByLabel('Date of birth').fill('1990-06-15');
// Проверка
await expect(page.getByLabel('Date of birth')).toHaveValue('1990-06-15');Проверка опций дропдауна
// Проверить что все опции присутствуют
const options = await page.getByLabel('Country').locator('option').allTextContents();
expect(options).toContain('Germany');
expect(options).toContain('Poland');
// Проверить что конкретная опция задизейблена
await expect(page.getByRole('option', { name: 'Out of stock item' })).toBeDisabled();
// Проверить количество опций
const optionCount = await page.getByLabel('Category').locator('option').count();
expect(optionCount).toBeGreaterThan(0);Типичные ошибки
selectOption() на кастомном дропдауне. Работает только с . Кастомные дропдауны требуют клика или взаимодействия с клавиатурой.
Нет ожидания появления опций. После открытия дропдауна который загружает опции асинхронно, жди появления опции до клика.
Выбор по индексу. Хрупкий вариант: добавление новой опции перед твоей сломает тест. Используй текст или value.
Нет проверки что показывает дропдаун после выбора. Проверяй что выбранное значение корректно, а не просто что клик не бросил ошибку.
→ See also: Локаторы Playwright: getByRole, getByLabel, getByText, getByTestId — сравнение | Assertions в Playwright: полное руководство | Работа с несколькими вкладками, окнами и iFrame в Playwright