Действия с элементами
Урок: Действия с элементами (click, fill и др.)
Введение
Когда пользователь работает с сайтом, он постоянно выполняет действия: нажимает кнопки, вводит текст, выбирает пункты в списке, отправляет формы.
Если посмотреть на это со стороны автоматизации, задача теста — повторить эти действия.
Представь, что ты вручную проверяешь форму логина:
- вводишь email;
- вводишь пароль;
- нажимаешь кнопку «Войти»;
- проверяешь результат.
Playwright делает ровно то же самое, только через код.
И ключевая часть этой работы — действия с элементами. Это те команды, с помощью которых мы «управляем» страницей: кликаем, вводим текст, выбираем значения.
Важно не просто знать названия методов, а понимать, как они работают и почему иногда тесты с ними ведут себя нестабильно.
Официальный обзор действий с полями, кликами, клавиатурой, файлами и перетаскиванием: Actions | Playwright. Про проверки «можно ли кликнуть» (видимость, перекрытие, стабильность позиции) — Actionability.
Базовая идея: действие = взаимодействие с элементом
Любое действие в Playwright выглядит примерно так:
await page.getByRole('button', { name: 'Войти' }).click();
Разберём:
- сначала мы находим элемент (локатор);
- затем вызываем метод (
click); - Playwright выполняет действие.
Важно: действие всегда выполняется над локатором.
Текст и значения в полях (fill)
locator.fill() — основной способ заполнить поле: фокус, ввод текста, событие input. Подходит для <input>, <textarea> и элементов с [contenteditable].
await page.getByRole('textbox', { name: 'Имя' }).fill('Пётр');
// типы input из документации
await page.getByLabel('Дата рождения').fill('2020-02-02');
await page.getByLabel('Время визита').fill('13:15');
await page.getByLabel('Локальная дата и время').fill('2020-03-02T05:15');
fill полностью подменяет значение (как «выделил всё и вставил»). Для обычных форм этого достаточно.
Очистить поле отдельно:
await page.getByLabel('Email').clear();
Чаще clear не нужен — перед заполнением fill сам приводит поле к новому значению.
Чекбоксы и радиокнопки (check, uncheck, setChecked)
Для input[type=checkbox], input[type=radio] и роли checkbox:
await page.getByLabel('Согласен с условиями').check();
await page.getByLabel('Размер: XL').check(); // радиокнопка
await expect(page.getByLabel('Подписка на рассылку')).toBeChecked();
await page.getByRole('checkbox', { name: 'Опция' }).uncheck();
Явно выставить состояние «вкл/выкл»:
await page.getByRole('checkbox', { name: 'Уведомления' }).setChecked(true);
await page.getByRole('checkbox', { name: 'Уведомления' }).setChecked(false);
Выпадающий список (selectOption)
Работай через локатор на <select>, а не через сырой селектор страницы.
<select aria-label="Язык">
<option value="ru">Русский</option>
<option value="en">English</option>
</select>
const lang = page.getByLabel('Язык');
await lang.selectOption('ru'); // по value или по видимой подписи option
await lang.selectOption({ label: 'English' }); // точно по тексту option
// несколько выбранных значений (multiple)
await page.getByLabel('Теги').selectOption(['bug', 'docs', 'feature']);
Клик, наведение, модификаторы (click, dblclick, hover)
Обычный клик:
await page.getByRole('button', { name: 'Войти' }).click();
Дополнительные варианты из документации:
await page.getByText('Карточка').dblclick();
await page.getByText('Элемент').click({ button: 'right' }); // правый клик
await page.getByText('Элемент').click({ modifiers: ['Shift'] });
// Ctrl на Windows/Linux или Cmd на macOS
await page.getByText('Ссылка').click({ modifiers: ['ControlOrMeta'] });
await page.getByText('Пункт меню').hover();
// клик в конкретной точке элемента (например, угол)
await page.getByText('Область').click({ position: { x: 10, y: 10 } });
Под капотом Playwright ждёт появления в DOM, видимости, окончания движения (например, после CSS-перехода), прокрутки в зону видимости и того, что в точке клика нет перекрытия другим элементом — см. Actionability.
Если перекрытие осознанное и нужно обойти проверки (осторожно):
await page.getByRole('button', { name: 'OK' }).click({ force: true });
«Программный» клик без реального указателя (имитация HTMLElement.click()):
await page.getByRole('button', { name: 'Скрытая кнопка' }).dispatchEvent('click');
Посимвольный ввод (pressSequentially)
Если на странице важна обработка каждого нажатия клавиши (автодополнение, маска, live-поиск), используй посимвольный ввод — в документации рекомендуется pressSequentially:
await page.locator('#search').pressSequentially('playwright', { delay: 50 });
Для обычных полей по-прежнему предпочитай fill — он проще и быстрее.
Клавиши и сочетания (press)
Один вызов — одно нажатие после фокуса на элементе:
await page.getByRole('textbox', { name: 'Чат' }).press('Enter');
await page.getByRole('textbox').press('Control+ArrowRight');
await page.getByRole('textbox').press('$'); // символ с клавиатуры
await page.locator('#name').press('Shift+A');
Имена клавиш совместимы с тем, что описано в KeyboardEvent.key (Backspace, Escape, KeyA …). Модификаторы: Shift, Control, Alt, Meta. Комбинации вроде Control+o поддерживаются.
Загрузка файлов (setInputFiles)
Для <input type="file">:
await page.getByLabel('Файл').setInputFiles('path/to/report.pdf');
await page.getByLabel('Несколько файлов').setInputFiles(['fixtures/a.txt', 'fixtures/b.txt']);
await page.getByLabel('Папка').setInputFiles('path/to/dir');
// сброс выбора
await page.getByLabel('Файл').setInputFiles([]);
// из памяти (без файла на диске)
await page.getByLabel('Файл').setInputFiles({
name: 'note.txt',
mimeType: 'text/plain',
buffer: Buffer.from('текст для теста'),
});
Если диалог выбора файла открывается не через обычный input, а через кастомную кнопку:
const fileChooserPromise = page.waitForEvent('filechooser');
await page.getByText('Выбрать файл').click();
const fileChooser = await fileChooserPromise;
await fileChooser.setFiles('path/to/doc.pdf');
Фокус (focus)
Для сценариев, где важны события фокуса:
await page.getByLabel('Пароль').focus();
Перетаскивание (dragTo и мышь)
Готовый сценарий «потянуть — отпустить»:
await page.locator('#drag').dragTo(page.locator('#drop'));
Точный контроль — hover, затем page.mouse.down() / mouse.move() / mouse.up() (в документации есть нюансы с dragover: иногда нужен второй move или повторный hover на цель перед mouse.up()).
Прокрутка
Перед действием Playwright обычно сам прокручивает элемент в видимую область. Явно прокрутить к элементу:
await page.getByText('Подвал страницы').scrollIntoViewIfNeeded();
Колёсико мыши или программная прокрутка контейнера — см. mouse.wheel и locator.evaluate в разделе Scrolling той же страницы.
Чтение значений (вспомогательно для сценария)
Для проверок чаще используй expect, но иногда нужно прочитать данные:
const title = await page.getByRole('heading').textContent();
const value = await page.getByLabel('Логин').inputValue();
Проверка через expect
Предпочтительный способ дождаться состояния и проверить его:
await expect(page.getByText('Успешно')).toBeVisible();
await expect(page.getByRole('checkbox', { name: 'Согласен' })).toBeChecked();
expect автоматически ретраит проверку в течение таймаута — надёжнее, чем одноразовое чтение textContent без ожидания.
Комбинирование действий
Рассмотрим полный сценарий:
await page.goto('https://example.com/login');
await page.getByLabel('Email').fill('test@mail.com');
await page.getByLabel('Пароль').fill('secret');
await page.getByRole('button', { name: 'Войти' }).click();
await expect(page.getByText('Добро пожаловать')).toBeVisible();
Что происходит:
- открываем страницу;
- вводим данные;
- нажимаем кнопку;
- проверяем результат.
Это уже полноценный пользовательский сценарий.
Как Playwright делает действия стабильными
Когда ты вызываешь действие у локатора, Playwright перед выполнением проверяет цепочку условий (элемент в DOM, виден, получает события указателя в точке действия, при необходимости не двигается и т.д.) — см. Actionability.
await page.getByRole('button', { name: 'Отправить' }).click();
Поэтому не нужно вручную выставлять паузы «на глаз»:
await page.waitForTimeout(2000); // хуже: дольше и нестабильнее
Встроенные ожидания в действиях и в expect обычно достаточны.
Частые ошибки
Ошибка 1 — клик по неуникальному селектору или слишком общему локатору:
await page.locator('button').click(); // если кнопок несколько — неоднозначно
Лучше уточнять через getByRole, getByTestId или filter.
Ошибка 2 — лишние ожидания:
await page.waitForTimeout(3000);
Это замедляет тесты и делает их нестабильными.
Ошибка 3 — разовое чтение DOM без ожидания вместо expect:
const text = await page.locator('.message').textContent();
if (text !== 'OK') throw new Error('fail');
Лучше: await expect(page.getByRole('status')).toHaveText('OK') — с ретраями и понятным сообщением при таймауте.
Где это используется на практике
Действия с элементами — это основа любых тестов.
Они используются:
- в тестировании форм;
- в e2e сценариях;
- в автоматизации пользовательских потоков;
- в CI/CD.
Любой тест — это последовательность действий + проверка результата.
Итоговое понимание
Действия с элементами — это способ «управлять» страницей через код.
Ключевая идея в том, что ты не просто вызываешь методы (click, fill, selectOption…), а воспроизводишь поведение пользователя там, где это важно для проверки.
Playwright даёт единый набор операций из документации по действиям: ввод и очистка, чекбоксы и списки, клики и наведение, клавиатура, файлы, фокус, drag-and-drop и прокрутка — с auto-waiting и проверками actionability.
Хорошо написанные действия — это понятный сценарий, стабильный тест и предсказуемый результат. Именно через них e2e-тест превращается из набора строк в проверку реального потока по интерфейсу.
Веб-страница из видео
Демо для отработки действий с элементами: "Экшены" — демо-страница.