La mayoría de los tests de Playwright no necesitan nada más que click() y fill(), pero los dropdowns que se activan con hover, el multi-select con Ctrl+click y las implementaciones de drag que rastrean eventos mousemove a lo largo del recorrido requieren las APIs de más bajo nivel de keyboard y mouse. page.keyboard.type() dispara eventos keydown, keypress, keyup e input por cada carácter, lo que importa para los autocompletados y la validación en tiempo real que fill() omite. Este artículo cubre pulsaciones de teclas, combinaciones con teclas modificadoras, hover, drag-and-drop con dragTo() y eventos de mouse directos, scroll, y el patrón de multi-select con Ctrl+click.
Eventos de teclado
Pulsaciones básicas de teclas
// Presionar una tecla individual
await page.keyboard.press('Enter');
await page.keyboard.press('Tab');
await page.keyboard.press('Escape');
await page.keyboard.press('ArrowDown');
// Combinaciones con teclas modificadoras
await page.keyboard.press('Control+a'); // Seleccionar todo
await page.keyboard.press('Control+c'); // Copiar
await page.keyboard.press('Control+v'); // Pegar
await page.keyboard.press('Shift+Tab'); // Tab en reversa
await page.keyboard.press('Meta+k'); // Cmd+K en MacLos nombres de teclas siguen la especificación KeyboardEvent.key. Los más comunes: 'Enter', 'Tab', 'Escape', 'Backspace', 'Delete', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown', 'F1' a 'F12'.
Escribir texto
// Escribir en el elemento enfocado
await page.getByRole('searchbox').focus();
await page.keyboard.type('playwright testing');
// Escribir con retraso entre teclas (simula escritura real)
await page.keyboard.type('escritura lenta', { delay: 50 });keyboard.type() dispara eventos keydown, keypress, keyup e input por cada carácter. Esto importa para aplicaciones que manejan pulsaciones individuales (autocompletado, validación en tiempo real, editores de texto enriquecido).
Mantener teclas modificadoras presionadas
// Shift + clic para seleccionar un rango
await page.keyboard.down('Shift');
await page.getByRole('row').nth(5).click();
await page.keyboard.up('Shift');Testear navegación por teclado
Testear que tu aplicación es accesible por teclado es tanto un requisito de accesibilidad como una preocupación de QA:
test('el modal se cierra con Escape', async ({ page }) => {
await page.getByRole('button', { name: 'Abrir modal' }).click();
await expect(page.getByRole('dialog')).toBeVisible();
await page.keyboard.press('Escape');
await expect(page.getByRole('dialog')).not.toBeVisible();
});
test('el dropdown navega con las teclas de flecha', async ({ page }) => {
await page.getByRole('combobox', { name: 'País' }).focus();
await page.keyboard.press('ArrowDown'); // Abre el dropdown, selecciona el primero
await page.keyboard.press('ArrowDown'); // Selecciona el segundo
await page.keyboard.press('Enter'); // Confirma la selección
await expect(page.getByRole('combobox', { name: 'País' })).toHaveValue('Argentina');
});Eventos de mouse
Variantes de clic
// Doble clic
await page.getByRole('row').first().dblclick();
// Clic derecho (menú contextual)
await page.getByText('Documento.pdf').click({ button: 'right' });
await expect(page.getByRole('menuitem', { name: 'Descargar' })).toBeVisible();
// Clic en coordenadas específicas relativas al elemento
await page.getByRole('slider').click({ position: { x: 10, y: 0 } });
// Clic mientras se mantiene una tecla modificadora
await page.getByRole('checkbox', { name: 'Ítem 3' }).click({ modifiers: ['Shift'] });Hover
// Hover para revelar un tooltip o dropdown
await page.getByRole('button', { name: 'Ayuda' }).hover();
await expect(page.getByRole('tooltip')).toBeVisible();
await expect(page.getByRole('tooltip')).toHaveText('Clic para ver la documentación');
// Hover para revelar un botón de acción oculto en una fila de tabla
await page.getByRole('row', { name: 'Ana García' }).hover();
await page.getByRole('button', { name: 'Editar' }).click();Movimiento del mouse
// Mover el mouse a coordenadas absolutas de la página
await page.mouse.move(100, 200);
// Arrastrar de una posición a otra
await page.mouse.move(100, 200);
await page.mouse.down();
await page.mouse.move(300, 200, { steps: 10 }); // steps lo hace más suave
await page.mouse.up();El parámetro steps de mouse.move() divide el movimiento en puntos intermedios, lo que importa para implementaciones de drag-and-drop que rastrean eventos mousemove a lo largo del recorrido.
Drag and drop
Para drag-and-drop estándar de HTML5:
// Usando dragTo: el enfoque más simple
await page.getByText('Tarjeta A').dragTo(page.getByText('Columna B'));
// Con posición específica de drop dentro del objetivo
await page.getByText('Archivo.pdf').dragTo(page.locator('.upload-zone'), {
targetPosition: { x: 50, y: 50 },
});Para implementaciones de drag personalizadas que usan eventos 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();Scroll
// Hacer scroll en la página
await page.mouse.wheel(0, 500); // Scroll hacia abajo 500px
// Scroll dentro de un elemento específico
await page.getByRole('log').hover();
await page.mouse.wheel(0, 300);
// Scroll hasta un elemento (usalo antes de interactuar con elementos fuera de pantalla)
await page.getByTestId('submit-section').scrollIntoViewIfNeeded();Combinar teclado y mouse
Los patrones de interacción más realistas combinan ambos:
test('multi-seleccionar ítems con Ctrl+click', async ({ page }) => {
await page.goto('/files');
// Seleccionar el primer ítem
await page.getByRole('row').nth(0).click();
// Ctrl+click para agregar a la selección
await page.getByRole('row').nth(2).click({ modifiers: ['Control'] });
await page.getByRole('row').nth(4).click({ modifiers: ['Control'] });
// Eliminar los ítems seleccionados
await page.keyboard.press('Delete');
await expect(page.getByRole('row')).toHaveCount(7); // Empezó con 10, eliminó 3
});Cuándo usar estas APIs
La mayoría de tus tests deberían usar métodos de locator de alto nivel (click(), fill(), selectOption()). Recurre a las APIs de teclado y mouse cuando:
- Testeas atajos de teclado o accesibilidad
- Testeas elementos de UI que se activan con hover
- Testeas interacciones de drag-and-drop
- Testeas menús contextuales
- Testeas editores de texto enriquecido
- Simulas interacciones complejas de múltiples pasos
Los eventos directos de teclado y mouse son más lentos y más frágiles que las acciones de alto nivel. Úsalos solo cuando la API de alto nivel no puede expresar lo que necesitas.
→ See also: Locators de Playwright: getByRole, getByLabel, getByText, getByTestId Comparados | Testing de Accesibilidad con Playwright: Verificaciones a11y Automatizadas | Carga y Descarga de Archivos en Tests de Playwright