Keyboard & Mouse Input (Nhập liệu bàn phím và chuột)
automationplaywright
Keyboard & Mouse input là nhóm API của Playwright mô phỏng thao tác bàn phím và chuột ở mức thấp (page.keyboard, page.mouse), bên cạnh các action mức cao như pressSequentially và dragTo. Dùng khi cần kiểm soát chính xác từng phím/từng pixel — ví dụ test phím tắt, nhập liệu giống người thật, hoặc kéo-thả trong app phức tạp.
Khái niệm & Bản chất
- Hai tầng input: Playwright có 2 lớp — (1) action mức cao gắn với Locator (
locator.click(),locator.fill(),locator.press(),locator.dragTo()) tự chờ element sẵn sàng, và (2) input mức thấp theo trang (page.keyboard,page.mouse) bắn sự kiện thô vào tọa độ/focus hiện tại, không auto-wait. page.keyboardmô phỏng bàn phím vật lý:press()(nhấn+thả một phím, hỗ trợ tổ hợp nhưControl+A),type()(gõ chuỗi ký tự, mỗi ký tự là một keydown/keyup),insertText()(chèn text không phát key event),down()/up()(giữ/thả phím).page.mousemô phỏng chuột theo tọa độ viewport:move(x, y),click(x, y),dblclick(),down()/up()để giữ kéo,wheel(deltaX, deltaY)để cuộn.pressSequentially(trên Locator) là bản 'gõ từng ký tự' củafill()— phát đủ keydown/keypress/keyup cho từng chữ, hữu ích khi input có listener theo phím (autocomplete, mask số điện thoại).- Mọi action mức cao đều đi qua bộ máy auto-wait + actionability check của Playwright; input mức thấp thì không — bạn tự chịu trách nhiệm về timing.
Cách hoạt động & API chính
- Keyboard:
page.keyboard.press('Enter'),page.keyboard.type('text', { delay: 50 }),page.keyboard.down('Shift')+ click +up('Shift')để chọn nhiều item,page.keyboard.insertText('')cho nội dung không gõ được. - Tên phím theo chuẩn
KeyboardEvent.key:Enter,Tab,Escape,ArrowLeft,Backspace,F5,ControlOrMeta(tự map Ctrl trên Win/Linux, Cmd trên macOS — quan trọng khi chạy CI đa nền tảng). - Mouse:
page.mouse.click(100, 200)bắn vào tọa độ viewport;down()/move()/up()kết hợp để custom drag;wheel()cho infinite scroll / map canvas. - Locator actions:
locator.press('KeyA')focus vào element rồi press;locator.pressSequentially('0901234567', { delay: 30 });locator.dragTo(target)cho drag & drop HTML5. - Modifier:
locator.click({ modifiers: ['Control'] })để Ctrl+Click;page.click('a', { position: { x, y } })click lệch tâm.
Ví dụ & Cú pháp thực tế
- Test phím tắt và gõ từng ký tự:
``
ts import { test, expect } from '@playwright/test'; test('shortcut Ctrl+S lưu form', async ({ page }) => { await page.goto('/editor'); const editor = page.getByRole('textbox'); await editor.click(); await page.keyboard.insertText('Noi dung bai viet'); await page.keyboard.press('ControlOrMeta+S'); // Cmd+S trên macOS await expect(page.getByText('Đã lưu')).toBeVisible(); });`` pressSequentiallycho input có mask / autocomplete: ``ts const phone = page.getByLabel('Số điện thoại'); await phone.pressSequentially('0901234567', { delay: 50 }); // gõ chậm để mask xử lý kịp từng ký tự await expect(phone).toHaveValue('090 123 4567');``- Drag & drop HTML5 bằng
dragTo: ``ts await page.getByText('Task A').dragTo(page.locator('#column-done')); // hoặc kéo đến vị trí cụ thể trong target await page.locator('.card').first().dragTo(page.locator('#board'), { targetPosition: { x: 50, y: 200 }, });`` - Kéo chuột thủ công (slider, canvas vẽ):
``
ts const box = await page.locator('.slider').boundingBox(); await page.mouse.move(box!.x + 10, box!.y + box!.height / 2); await page.mouse.down(); await page.mouse.move(box!.x + 200, box!.y + box!.height / 2, { steps: 10 }); await page.mouse.up();`` - Giữ Shift để multi-select:
``
ts await page.keyboard.down('Shift'); await page.getByRole('row', { name: 'Item 1' }).click(); await page.getByRole('row', { name: 'Item 5' }).click(); await page.keyboard.up('Shift');``
Best practices & Khi nào dùng
- Ưu tiên action mức cao trước:
fill()/pressSequentially()/dragTo()đủ cho 90% case; chỉ rớt xuốngpage.keyboard/page.mousekhi cần tổ hợp phím, giữ phím, hoặc tọa độ thô. - Dùng
pressSequentiallythayfillkhi input có keyup/keydown listener (search suggest, tag input, OTP field) —fill()set value một phát không phát key event. - Dùng
insertTextkhi chỉ cần đổ text vào (giống paste) mà không quan tâm key event — nhanh và không bị mask cản. - Luôn
ControlOrMetathay vì hardcodeControlcho shortcut — test chạy local trên macOS lẫn CI Linux vẫn đúng. - Drag & drop: thử
dragTo()trước; nếu app dùng lib custom (dnd-kit, react-beautiful-dnd) màdragTokhông trigger, fallback sangmouse.down/move/upvớistepsđể mô phỏng quỹ đạo.
Gotchas, So sánh & Lưu ý
page.keyboard/page.mousekhông auto-wait — gọipage.mouse.click(x, y)khi element chưa render là click vào hư không. Luônawait locator.waitFor()hoặc assert trước.type()vspressSequentially():typeở cấp page (gõ vào element đang focus),pressSequentiallyở cấp locator (tự focus đúng element) — bản mới khuyên dùngpressSequentially.- Focus là vua:
page.keyboard.press('Tab')chỉ đúng nếu focus đang ở đúng chỗ; mất focus do re-render là test flake — nên click/focus element trước. - HTML5 drag & drop (event
dragstart/drop) khác với drag bằng mouse events —dragTo()xử lý được cả hai nhưng một số lib chỉ nghe mouse event, phải dùngmousethô vớistepsđủ lớn (ít nhất 2-3 bước) để lib nhận 'drag'. page.mouse.wheel()cuộn cả trang; nếu muốn cuộn 1 container thì dùnglocator.evaluate(el => el.scrollTop = ...)hoặclocator.hover()rồi wheel.- Trên headless, một số sự kiện IME/compose không đầy đủ — test nhập tiếng Việt có dấu nên dùng
insertTexthoặcpressSequentiallyvới ký tự đã compose sẵn.
