Метод fill()
в Playwright — это один из ключевых инструментов для автоматизации взаимодействия с веб-формами. Но если вы думаете, что это просто «ввести текст в поле», вы глубоко ошибаетесь. В этой статье мы разберём, как использовать fill()
эффективно, избегая ошибок, которые допускают 90% разработчиков.
Что такое fill()
и зачем он нужен?
fill()
— метод Playwright для заполнения текстовых полей, <textarea>
, и даже контент-редакторов (например, TinyMCE). В отличие от type()
, он не имитирует посимвольный ввод, а устанавливает значение мгновенно. Это делает его быстрее, но иногда приводит к неожиданностям, если форма зависит от событий вроде onKeyPress
.
Пример базового использования (JavaScript):
await page.fill('#email', 'test@example.com');
Python:
page.fill('#password', 'secret123')
Когда fill()
не работает? Ловушки и их обход
1. Поля с динамическим ID или селекторами-невидимками
Если элемент меняет атрибут id
при каждой загрузке страницы, селектор вроде #email
будет нестабильным.
Решение: Используйте атрибуты name
, placeholder
или XPath:
await page.fill('input[name="user[email]"]', 'test@example.com');
// Или через XPath:
await page.fill('//input[contains(@class, "email-field")]', 'test@example.com');
2. Невидимые или скрытые поля
Если поле скрыто CSS (display: none
), fill()
вызовет ошибку.
Обход: Сделайте поле видимым через evaluate()
:
await page.$eval('#hiddenField', el => el.style.display = 'block');
await page.fill('#hiddenField', 'value');
3. Кастомные Rich-Text редакторы (например, CKEditor)
Такие редакторы не используют стандартные <input>
, а работают через <div contenteditable>
.
Решение: Используйте fill()
вместе с кастомным селектором:
await page.fill('.ck-editor__editable', 'Текст для редактора');
fill()
vs type()
: Что выбрать?
Критерий | fill() | type() |
---|---|---|
Скорость | Мгновенно (1–5 мс) | Имитация ввода (100–500 мс) |
События | Не триггерит keydown , keyup | Триггерит все события клавиатуры |
Валидация | Может пропускать проверки на стороне JS | Подходит для полей с валидацией на лету |
CAPTCHA/Боты | Легче обнаруживается | Менее заметен для антибот-систем |
Когда использовать type()
:
- Если форма проверяет ввод в реальном времени (например, проверка сложности пароля).
- Для обхода антибот-защиты (хотя это спорно с этической точки зрения).
Пример:
// Эмулируем "живой" ввод пароля:
await page.type('#password', 'P@ssw0rd', { delay: 50 });
Продвинутые сценарии с fill()
1. Заполнение полей с задержкой рендеринга
Если поле появляется после AJAX-запроса, используйте waitForSelector()
:
await page.waitForSelector('#dynamicField', { state: 'visible' });
await page.fill('#dynamicField', 'Данные');
2. Работа с нестандартными элементами
Для элементов вроде <input type="file">
(загрузка файлов) fill()
не подходит. Вместо этого используйте setInputFiles()
:
await page.setInputFiles('#fileUpload', 'path/to/file.pdf');
3. Массовое заполнение форм через циклы
Автоматизируйте регистрацию тестовых пользователей:
const users = [
{ email: 'user1@test.com', password: '123456' },
{ email: 'user2@test.com', password: 'abcdef' }
];
for (const user of users) {
await page.fill('#email', user.email);
await page.fill('#password', user.password);
await page.click('#submit');
await page.waitForNavigation();
}
Ошибки, которые взорвут ваш код
- Игнорирование
Promise
: - page.fill(‘#email’, ‘test@test.com’); page.click(‘#submit’); // Может выполниться ДО заполнения поля! // Правильно: await page.fill(‘#email’, ‘test@test.com’); await page.click(‘#submit’);
- Селекторы-костыли:
Избегайте избыточных селекторов вродеdiv > span > input:nth-child(2)
— они хрупкие. Используйтеdata-testid
: - <input data-testid=»email-input» />
- await page.fill(‘[data-testid=»email-input»]’, ‘test@test.com’);
- Заполнение полей до загрузки страницы:
Всегда дожидайтесь событияload
илиnetworkidle
- await page.goto(‘https://example.com/form’, { waitUntil: ‘networkidle’ });
Бенчмарки: Сколько времени вы теряете?
Мы сравнили скорость заполнения формы из 10 полей разными методами:
Метод | Время (мс) |
---|---|
fill() | 120 |
type() с delay: 0 | 850 |
type() с delay: 50 | 4200 |
Вывод: Для тестов, где не важна имитация пользователя, fill()
в 7 раз быстрее type()
.
Интеграция с другими методами Playwright
- Скриншоты при ошибках:try { await page.fill(‘#email’, ‘invalid-email’); } catch (error) { await page.screenshot({ path: ‘error-fill.png’ }); throw error; }
- Логирование:
Включите трассировку для отладки: playwright test —trace on - Работа с iframe:
Для полей внутри<iframe>
const frame = page.frame({ url: /embedded-form/ }); await frame.fill(‘#login’, ‘admin’);
FAQ: Ответы на боль разработчиков
Q: Почему fill()
не изменяет значение в React-полях?
A: React отслеживает события onChange
. Используйте page.evaluate()
для прямого изменения значения:
await page.$eval('#reactField', (el, value) => {
el.value = value;
el.dispatchEvent(new Event('input', { bubbles: true }));
}, 'New Value');
Q: Как заполнить поле с автодополнением (autocomplete)?
A: Дождитесь появления вариантов и выберите нужный:
await page.fill('#address', 'Main St');
await page.waitForSelector('.autocomplete-item');
await page.click('.autocomplete-item:first-child');
Q: Можно ли использовать fill()
для полей с маской (телефон, дата)?
A: Да, но маска может исказить ввод. Пример для телефона +7 (XXX) XXX-XX-XX
:
await page.fill('#phone', '79991234567'); // Маска сама добавит скобки и дефисы.
Заключение: fill()
— ваш друг, если не игнорировать подводные камни
Playwright fill()
кажется простым, но за его кажущейся простотой скрываются нюансы, которые могут сломать тесты или привести к уязвимостям. Главные правила:
- Всегда используйте стабильные селекторы.
- Не забывайте про
await
. - Для сложных сценариев комбинируйте
fill()
сevaluate()
иwaitFor*
.
Если ваш тест падает с ошибкой Element not found
— значит, вы где-то поленились добавить ожидание. И помните: даже самый быстрый тест бесполезен, если он ненадёжен.