Большинству тестов Playwright достаточно click() и fill(), но дропдауны по наведению, мультиселект через Ctrl+click и drag-реализации которые отслеживают события mousemove по траектории требуют низкоуровневых API keyboard и mouse. page.keyboard.type() генерирует события keydown, keypress, keyup и input для каждого символа: важно для автодополнения и real-time валидации которые fill() обходит стороной. Эта статья разбирает нажатия клавиш, комбинации с модификаторами, hover, drag-and-drop через dragTo() и сырые события мыши, прокрутку и паттерн мультиселекта через Ctrl+click.
Клавиатурные события
Базовые нажатия клавиш
// нажать одну клавишу
await page.keyboard.press('Enter');
await page.keyboard.press('Tab');
await page.keyboard.press('Escape');
await page.keyboard.press('ArrowDown');
// комбинации с модификатором
await page.keyboard.press('Control+a'); // выделить всё
await page.keyboard.press('Control+c'); // копировать
await page.keyboard.press('Control+v'); // вставить
await page.keyboard.press('Shift+Tab'); // Tab в обратном направлении
await page.keyboard.press('Meta+k'); // Cmd+K на MacИмена клавиш соответствуют спецификации KeyboardEvent.key. Часто используемые: 'Enter', 'Tab', 'Escape', 'Backspace', 'Delete', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown', 'F1'–'F12'.
Ввод текста
// вводим в сфокусированный элемент
await page.getByRole('searchbox').focus();
await page.keyboard.type('playwright testing');
// ввод с задержкой между нажатиями (имитирует реальный набор)
await page.keyboard.type('slow typing', { delay: 50 });keyboard.type() генерирует события keydown, keypress, keyup и input для каждого символа. Это важно для приложений которые обрабатывают отдельные нажатия: автодополнение, real-time валидация, редакторы rich text.
Удержание клавиш-модификаторов
// Shift + клик для выделения диапазона
await page.keyboard.down('Shift');
await page.getByRole('row').nth(5).click();
await page.keyboard.up('Shift');Тестирование клавиатурной навигации
Проверка доступности приложения с клавиатуры: одновременно требование к доступности и QA-задача.
test('modal closes on Escape', async ({ page }) => {
await page.getByRole('button', { name: 'Open modal' }).click();
await expect(page.getByRole('dialog')).toBeVisible();
await page.keyboard.press('Escape');
await expect(page.getByRole('dialog')).not.toBeVisible();
});
test('dropdown navigates with arrow keys', async ({ page }) => {
await page.getByRole('combobox', { name: 'Country' }).focus();
await page.keyboard.press('ArrowDown'); // открывает дропдаун, выбирает первый
await page.keyboard.press('ArrowDown'); // выбирает второй
await page.keyboard.press('Enter'); // подтверждает выбор
await expect(page.getByRole('combobox', { name: 'Country' })).toHaveValue('Australia');
});События мыши
Варианты клика
// двойной клик
await page.getByRole('row').first().dblclick();
// правый клик (контекстное меню)
await page.getByText('Document.pdf').click({ button: 'right' });
await expect(page.getByRole('menuitem', { name: 'Download' })).toBeVisible();
// клик в конкретных координатах относительно элемента
await page.getByRole('slider').click({ position: { x: 10, y: 0 } });
// клик с зажатым модификатором
await page.getByRole('checkbox', { name: 'Item 3' }).click({ modifiers: ['Shift'] });Наведение
// наводим чтобы показать тултип или дропдаун
await page.getByRole('button', { name: 'Help' }).hover();
await expect(page.getByRole('tooltip')).toBeVisible();
await expect(page.getByRole('tooltip')).toHaveText('Click for documentation');
// наводим на строку таблицы чтобы показать скрытую кнопку действия
await page.getByRole('row', { name: 'Alice Johnson' }).hover();
await page.getByRole('button', { name: 'Edit' }).click();Движение мыши
// перемещаем мышь к абсолютным координатам страницы
await page.mouse.move(100, 200);
// перетаскиваем из одной позиции в другую
await page.mouse.move(100, 200);
await page.mouse.down();
await page.mouse.move(300, 200, { steps: 10 }); // steps делает движение плавнее
await page.mouse.up();Параметр steps в mouse.move() разбивает перемещение на промежуточные точки: важно для drag-and-drop реализаций которые отслеживают события mousemove по пути.
Drag and drop
Для стандартного HTML5 drag-and-drop:
// через dragTo — самый простой способ
await page.getByText('Card A').dragTo(page.getByText('Column B'));
// с конкретной позицией сброса внутри цели
await page.getByText('File.pdf').dragTo(page.locator('.upload-zone'), {
targetPosition: { x: 50, y: 50 },
});Для кастомных drag-реализаций которые используют события mousedown/mousemove/mouseup:
const source = page.getByTestId('draggable-card');
const target = page.getByTestId('drop-zone');
const sourceBounds = await source.boundingBox();
const targetBounds = await target.boundingBox();
await page.mouse.move(sourceBounds!.x + sourceBounds!.width / 2, sourceBounds!.y + sourceBounds!.height / 2);
await page.mouse.down();
await page.mouse.move(targetBounds!.x + targetBounds!.width / 2, targetBounds!.y + targetBounds!.height / 2, { steps: 20 });
await page.mouse.up();Прокрутка
// прокрутка страницы
await page.mouse.wheel(0, 500); // прокрутить вниз на 500px
// прокрутка внутри конкретного элемента
await page.getByRole('log').hover();
await page.mouse.wheel(0, 300);
// прокрутить к элементу (перед взаимодействием с элементом за пределами экрана)
await page.getByTestId('submit-section').scrollIntoViewIfNeeded();Комбинирование клавиатуры и мыши
Наиболее реалистичные паттерны взаимодействия комбинируют оба:
test('multi-select items with Ctrl+click', async ({ page }) => {
await page.goto('/files');
// выбираем первый элемент
await page.getByRole('row').nth(0).click();
// Ctrl+click добавляет к выделению
await page.getByRole('row').nth(2).click({ modifiers: ['Control'] });
await page.getByRole('row').nth(4).click({ modifiers: ['Control'] });
// удаляем выбранные элементы
await page.keyboard.press('Delete');
await expect(page.getByRole('row')).toHaveCount(7); // было 10, удалили 3
});Когда использовать эти API
Большинство тестов должны использовать высокоуровневые методы локаторов (click(), fill(), selectOption()). К API клавиатуры и мыши обращайся когда:
- Тестируешь горячие клавиши или доступность
- Тестируешь UI-элементы которые появляются по наведению
- Тестируешь drag-and-drop взаимодействия
- Тестируешь контекстные меню
- Тестируешь редакторы rich text
- Симулируешь сложные многошаговые взаимодействия
Прямые события клавиатуры и мыши медленнее и хрупче чем высокоуровневые действия. Используй их только когда высокоуровневый API не может выразить то что нужно.
→ See also: Локаторы Playwright: getByRole, getByLabel, getByText, getByTestId — сравнение | Тестирование доступности с Playwright: автоматизированные a11y проверки | Загрузка и скачивание файлов в тестах Playwright