A maioria dos testes Playwright não precisa de nada além de click() e fill(). Mas dropdowns ativados por hover, multi-seleção com Ctrl+click e implementações de drag que rastreiam eventos mousemove ao longo do caminho requerem as APIs de nível mais baixo keyboard e mouse. page.keyboard.type() dispara eventos keydown, keypress, keyup e input por caractere, o que importa para autocomplete e validação em tempo real que fill() ignora.

Eventos de teclado

Pressionamentos básicos de teclas

// Pressiona uma tecla
await page.keyboard.press('Enter');
await page.keyboard.press('Tab');
await page.keyboard.press('Escape');
await page.keyboard.press('ArrowDown');

// Combinações de modificador + tecla
await page.keyboard.press('Control+a');  // Selecionar tudo
await page.keyboard.press('Control+c');  // Copiar
await page.keyboard.press('Control+v');  // Colar
await page.keyboard.press('Shift+Tab'); // Tab reverso
await page.keyboard.press('Meta+k');    // Cmd+K no Mac

Os nomes das teclas seguem a especificação KeyboardEvent.key. Os mais comuns: 'Enter', 'Tab', 'Escape', 'Backspace', 'Delete', 'ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Home', 'End', 'PageUp', 'PageDown', 'F1''F12'.

Digitando texto

// Digita no elemento com foco
await page.getByRole('searchbox').focus();
await page.keyboard.type('playwright testing');

// Digita com delay entre teclas (simula digitação real)
await page.keyboard.type('digitação lenta', { delay: 50 });

keyboard.type() dispara eventos keydown, keypress, keyup e input para cada caractere. Isso importa para aplicações que lidam com teclas individuais (autocomplete, validação em tempo real, editores de texto rico).

Segurando teclas modificadoras

// Shift + click para selecionar um intervalo
await page.keyboard.down('Shift');
await page.getByRole('row').nth(5).click();
await page.keyboard.up('Shift');

Testando navegação por teclado

Testar que seu app é acessível por teclado é tanto um requisito de acessibilidade quanto uma preocupação de QA:

test('modal fecha com 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 navega com teclas de seta', async ({ page }) => {
  await page.getByRole('combobox', { name: 'País' }).focus();
  await page.keyboard.press('ArrowDown'); // Abre dropdown, seleciona primeiro
  await page.keyboard.press('ArrowDown'); // Seleciona segundo
  await page.keyboard.press('Enter');     // Confirma a seleção

  await expect(page.getByRole('combobox', { name: 'País' })).toHaveValue('Brasil');
});

Eventos de mouse

Variantes de clique

// Duplo clique
await page.getByRole('row').first().dblclick();

// Clique direito (menu de contexto)
await page.getByText('Documento.pdf').click({ button: 'right' });
await expect(page.getByRole('menuitem', { name: 'Download' })).toBeVisible();

// Clique em coordenadas específicas relativas ao elemento
await page.getByRole('slider').click({ position: { x: 10, y: 0 } });

// Clique segurando tecla modificadora
await page.getByRole('checkbox', { name: 'Item 3' }).click({ modifiers: ['Shift'] });

Hover

// Hover para revelar tooltip ou dropdown
await page.getByRole('button', { name: 'Ajuda' }).hover();
await expect(page.getByRole('tooltip')).toBeVisible();
await expect(page.getByRole('tooltip')).toHaveText('Clique para documentação');

// Hover para revelar botão de ação oculto em linha de tabela
await page.getByRole('row', { name: 'Alice Johnson' }).hover();
await page.getByRole('button', { name: 'Editar' }).click();

Movimento do mouse

// Move o mouse para coordenadas absolutas da página
await page.mouse.move(100, 200);

// Arrasta de uma posição para outra
await page.mouse.move(100, 200);
await page.mouse.down();
await page.mouse.move(300, 200, { steps: 10 }); // steps deixa mais suave
await page.mouse.up();

O parâmetro steps em mouse.move() divide o movimento em pontos intermediários, o que importa para implementações de drag-and-drop que rastreiam eventos mousemove ao longo do caminho.

Drag-and-drop

Para drag-and-drop HTML5 padrão:

// Usando dragTo — abordagem mais simples
await page.getByText('Card A').dragTo(page.getByText('Coluna B'));

// Com posição específica dentro do alvo
await page.getByText('Arquivo.pdf').dragTo(page.locator('.upload-zone'), {
  targetPosition: { x: 50, y: 50 },
});

Para implementações customizadas de drag que usam 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

// Rola a página
await page.mouse.wheel(0, 500); // Rola 500px para baixo

// Rola dentro de um elemento específico
await page.getByRole('log').hover();
await page.mouse.wheel(0, 300);

// Rola até o elemento (use antes de interagir com elementos fora da tela)
await page.getByTestId('submit-section').scrollIntoViewIfNeeded();

Combinando teclado e mouse

Os padrões de interação mais realistas combinam os dois:

test('multi-seleciona itens com Ctrl+click', async ({ page }) => {
  await page.goto('/files');

  // Seleciona o primeiro item
  await page.getByRole('row').nth(0).click();

  // Ctrl+click para adicionar à seleção
  await page.getByRole('row').nth(2).click({ modifiers: ['Control'] });
  await page.getByRole('row').nth(4).click({ modifiers: ['Control'] });

  // Deleta os itens selecionados
  await page.keyboard.press('Delete');

  await expect(page.getByRole('row')).toHaveCount(7); // Começou com 10, deletou 3
});

Quando usar essas APIs

A maioria dos seus testes deve usar métodos de locator de alto nível (click(), fill(), selectOption()). Use as APIs de teclado e mouse quando:

  • Testando atalhos de teclado ou acessibilidade
  • Testando elementos de UI ativados por hover
  • Testando interações de drag-and-drop
  • Testando menus de contexto
  • Testando editores de texto rico
  • Simulando interações complexas de múltiplos steps

Eventos diretos de teclado e mouse são mais lentos e frágeis do que ações de alto nível. Use-os apenas quando a API de alto nível não conseguir expressar o que você precisa.

→ Veja também: Locators do Playwright: getByRole, getByLabel, getByText, getByTestId Comparados | Testes de Acessibilidade com Playwright: Verificações a11y Automatizadas | Upload e Download de Arquivos em Testes Playwright