NỘI DUNG BÀI HỌC

Hiểu rõ 'Actionability' ⏳✅

Từ click() đến dragTo() 🖱️🖐️

Gửi phím tắt hiệu quả ⌨️⚡



Phần 1: Làm chủ các Hành động với Chuột (Mouse Actions) trong Playwright

Playwright mô phỏng các hành động của chuột (như click, di chuột, kéo thả) giống hệt như một người dùng thật. Để làm được điều này, nó sử dụng một cơ chế tự động chờ mạnh mẽ gọi là "Actionability".

Khái niệm cốt lõi: "Actionability" (Khả năng tương tác)

Đây là điều quan trọng nhất bạn cần hiểu. Khi bạn gọi một hành động liên quan đến con trỏ (như click()), Playwright sẽ không hành động ngay lập tức. Thay vào đó, "dưới mui xe" (under the hood), nó tự động thực hiện một loạt các bước kiểm tra:

  • Chờ element có trong DOM: Đảm bảo element tồn tại.
  • Chờ element được hiển thị: Đợi cho đến khi nó không bị ẩn (display: none, visibility: hidden) và có kích thước (không phải 0x0).
  • Chờ element ngừng di chuyển: Đợi cho đến khi các hiệu ứng CSS (transition) hoặc animation kết thúc.
  • Cuộn element vào tầm nhìn (Scroll): Tự động cuộn trang cho đến khi bạn thấy được element đó.
  • Chờ element nhận được sự kiện (Pointer Events): Đợi cho đến khi element không bị che khuất bởi một element khác (ví dụ: một pop-up, một modal) tại chính điểm sẽ click.
  • Thử lại (Retry): Nếu element bị "gỡ" (detached) khỏi DOM trong bất kỳ bước nào, Playwright sẽ thử lại toàn bộ quy trình.

Chỉ khi TẤT CẢ các điều kiện này được đáp ứng, Playwright mới thực hiện hành động. Đây chính là lý do tại sao test của Playwright cực kỳ ổn định và bạn hiếm khi cần dùng waitForTimeout.


Các hành động Click cơ bản

Đây là các hành động click phổ biến nhất.

Click trái (Standard Click)

Hành động click() là một cú click chuột trái tiêu chuẩn.

// Click vào một Button

await page.getByRole('button', { name: 'Đăng nhập' }).click();

// Click vào một Link

await page.getByRole('link', { name: 'Xem chi tiết' }).click();

// Click vào một text cụ thể

await page.getByText('Bắt đầu').click();

 

Click đúp (Double Click)

Sử dụng dblclick() để mô phỏng một cú click đúp.

// Click đúp để mở một mục

await page.getByText('Item 1').dblclick();

// Click đúp để vào chế độ chỉnh sửa

await page.locator('#item-name').dblclick();


Click chuột phải (Right Click)

Sử dụng rightClick() (hoặc click({ button: 'right' })) để mở menu ngữ cảnh (context menu).

 

// Cách 1: Dùng alias (dễ đọc hơn)

await page.locator('#my-element').rightClick();

// Cách 2: Dùng options

await page.locator('#my-element').click({ button: 'right' });

// Ví dụ: Mở menu và chọn "Copy"

await page.locator('#file-explorer').rightClick();

await page.getByText('Copy').click(); // Menu ngữ cảnh xuất hiện

 

Click với các Tùy chọn (Options)

Bạn có thể tùy chỉnh hành vi click() bằng cách truyền vào một đối tượng options.

Click với Phím bổ trợ (Modifiers)

Mô phỏng việc người dùng giữ một phím trong khi click.

// Giữ phím Shift + Click (ví dụ: chọn nhiều mục trong danh sách)

await page.getByText('Mục 1').click();

await page.getByText('Mục 5').click({ modifiers: ['Shift'] });

// Giữ phím Control (hoặc Meta/Cmd trên Mac) + Click (mở link trong tab mới)

await page.getByRole('link', { name: 'Trang chủ' }).click({ modifiers: ['Control'] });

Các phím hỗ trợ: 'Shift', 'Control', 'Alt', 'Meta' (phím Command trên Mac, phím Windows trên PC).


Click vào Tọa độ (Position)

Mặc định, Playwright click vào trung tâm của element. Bạn có thể chỉ định chính xác vị trí (x, y) (tương đối so với góc trên-trái của element) để click.

Trị giá: Rất hữu ích khi click vào bản đồ (map), biểu đồ (chart), hoặc canvas.

// Click vào vị trí (x: 150, y: 100) bên trong element #heatmap

await page.locator('#heatmap').click({

  position: { x: 150, y: 100 }

});

 

Forcing the Click (Bỏ qua Actionability) ⚠️

Đôi khi, các ứng dụng web có logic phức tạp, ví dụ khi bạn di chuột vào một nút, một element khác (như tooltip) lại xuất hiện và che mất cái nút đó. Playwright sẽ coi đây là lỗi (vì element bị che khuất) và không click được.

Nếu bạn chắc chắn rằng đây là hành vi đúng của ứng dụng (chứ không phải bug), bạn có thể bỏ qua tất cả các kiểm tra "Actionability" và ép buộc click.

// Bỏ qua mọi kiểm tra (visible, stable, obscured) và click ngay lập tức

await page.getByRole('button').click({ force: true });

CẢNH BÁO: Đây là một "anti-pattern" (cách làm không tốt).

Test của bạn không còn mô phỏng người dùng thật nữa.

Nó có thể che giấu các lỗi thật (ví dụ: element bị che khuất thật).

Hãy luôn luôn ưu tiên sửa locator hoặc đợi cho element (như pop-up) biến mất, và chỉ dùng force: true như là giải pháp cuối cùng.


Các Hành động Chuột Khác

Ngoài click, đây là các hành động quan trọng khác:

Di chuột (Hover)

Sử dụng hover() để mô phỏng việc di chuột vào một element.

Trị giá: Làm hiện các menu con (dropdowns) hoặc các tooltip.

// Di chuột vào menu "Sản phẩm"

await page.getByRole('button', { name: 'Sản phẩm' }).hover();

// Chờ menu con xuất hiện và click vào "Laptop"

await page.getByRole('link', { name: 'Laptop' }).click();


Kéo và Thả (Drag and Drop)

Playwright cung cấp một hàm cấp cao dragTo() cực kỳ mạnh mẽ.

const source = page.locator('#item-to-drag');

const target = page.locator('#drop-zone');

// Tự động kéo từ tâm 'source' đến tâm 'target'

await source.dragTo(target);

// Kiểm tra kết quả

await expect(target).toContainText('Đã thả!');

Hàm dragTo() này cũng tự động thực hiện toàn bộ các bước "Actionability" (tự cuộn, chờ, v.v.) cho cả hai element.


API Chuột Cấp thấp (Low-level)

Trong trường hợp dragTo() thất bại (ví dụ với các thư viện Kéo-Thả phức tạp), bạn có thể mô phỏng chính xác từng bước:

// 1. Di chuột đến element nguồn

await page.locator('#item-to-drag').hover();

// 2. Nhấn giữ chuột trái

await page.mouse.down();

// 3. Di chuột đến element đích

await page.locator('#drop-zone').hover();

// 4. Thả chuột

await page.mouse.up();

Phần 2: Mô phỏng Bàn phím (Keys & Shortcuts)

Playwright cung cấp phương thức locator.press() để mô phỏng chính xác một lần nhấn phím (keystroke) của người dùng.

Phương thức locator.press() sẽ tự động focus vào element được chọn và tạo ra một sự kiện gõ phím duy nhất. Nó chấp nhận các tên phím logic (giống như keyboardEvent.key trong JavaScript) hoặc các tổ hợp phím tắt.


Nhấn các Phím Chức năng

Đây là các phím không phải là ký tự, dùng để điều khiển hoặc kích hoạt hành động.

Các phím phổ biến: Enter, Tab, Escape, Backspace, Delete, ArrowLeft, ArrowRight, ArrowUp, ArrowDown, Home, End, PageUp, PageDown, F1...F12.

// Dùng 'Enter' để gửi (submit) một form

await page.getByText('Submit').press('Enter');


// Dùng 'Tab' để di chuyển focus sang element tiếp theo

await page.locator('#username').press('Tab');


// Dùng 'Escape' để đóng một hộp thoại (dialog)

await page.locator('.modal-content').press('Escape');

 

Nhấn các Ký tự Đơn

Bạn cũng có thể chỉ định một ký tự duy nhất mà bạn muốn gõ.

// Gõ ký tự "$"

await page.getByRole('textbox').press('$');

// Gõ chữ 'a' thường

await page.getByRole('textbox').press('a');

 

Nhấn Tổ hợp Phím (Shortcuts)

Đây là sức mạnh thực sự của press(). Bạn có thể mô phỏng các phím bổ trợ (modifiers) bằng cách kết hợp chúng với dấu +.

Các phím bổ trợ: Shift, Control (hoặc Ctrl), Alt, Meta (phím Command ⌘ trên Mac, phím Windows trên PC).

// Di chuyển con trỏ sang phải 1 từ (Ctrl + Mũi tên phải)

await page.getByRole('textbox').press('Control+ArrowRight');

// Chọn tất cả văn bản (Ctrl + A)

await page.getByRole('textbox').press('Control+A');


// Gõ chữ 'A' hoa

await page.getByRole('textbox').press('Shift+A');


// Lưu file (Cmd + S)

await page.locator('body').press('Meta+S');

 

So sánh: press() vs. fill() vs. pressSequentially()

Việc chọn đúng phương thức là rất quan trọng để test ổn định.

locator.fill(value) ⚡ (Nhanh & Ổn định)

Hành vi: Xóa (clear) ô input và điền ngay lập tức giá trị value vào.

Mô phỏng: Nó không mô phỏng từng lần gõ phím. Nó chỉ đặt giá trị (set value).

Khi nào dùng: Hầu hết mọi lúc (99%). Khi bạn chỉ quan tâm đến kết quả là ô input có text "Hello World", hãy dùng fill(). Đây là cách nhanh và đáng tin cậy nhất

// Cách tốt nhất để điền form

await page.locator('#email').fill('example@gmail.com');

locator.press(key) ⌨️ (Kiểm tra Sự kiện)

Hành vi: Mô phỏng một lần nhấn phím duy nhất (bao gồm keyDown, keyPress, keyUp).

Khi nào dùng: Khi bạn cần kiểm tra các sự kiện (event listeners) hoặc phím tắt.

Test các phím tắt (hotkeys) của ứng dụng (ví dụ: Control+S).

Test gửi form bằng phím Enter.

Test các ô tìm kiếm tự động (auto-suggest) mà kích hoạt sự kiện onKeyDown.

locator.pressSequentially(text) 🧑‍💻 (Mô phỏng người gõ)

Hành vi: Mô phỏng việc gõ từng ký tự một trong một chuỗi, giống hệt như người dùng thật.

Khi nào dùng: Khi fill() không hoạt động, thường là với các ô input "phức tạp" (ví dụ: các code editor, các ô input được "masked" - che giấu) mà dựa vào từng sự kiện gõ phím để định dạng text.

// Mô hỏng gõ chậm từng chữ "Hello"

await page.locator('input').pressSequentially('Hello');

// Thêm độ trễ 100ms giữa mỗi lần gõ

await page.locator('input').pressSequentially('Hello', { delay: 100 });

 

Focus Element (Tập trung vào Element)

Playwright cung cấp phương thức locator.focus() để mô phỏng việc "focus" (thường là dùng phím Tab hoặc click vào) một element.

// Tập trung vào một ô input

await page.locator('#my-input').focus();

Tại sao focus() ít khi được dùng?

Bạn sẽ thấy mình rất hiếm khi cần gọi locator.focus() một cách tường minh. Lý do là:

Hầu hết các hành động đã tự động focus() cho bạn!

Các hành động cấp cao (actionability) của Playwright như click(), fill(), press() đều tự động focus vào element trước khi thực hiện hành động.

// HÀNH ĐỘNG NÀY:

await page.locator('#my-input').fill('Hello');

// ĐÃ BAO GỒM:

// 1. Tự động cuộn đến #my-input

// 2. Tự động focus() vào #my-input

// 3. Tự động điền text


Khi nào thì focus() hữu ích?

Trường hợp sử dụng duy nhất của focus() là khi bạn muốn kiểm tra một hành vi chỉ xảy ra khi element nhận được focus (mà không có hành động nào khác theo sau).

Trị giá: Kiểm tra các sự kiện onFocus hoặc onBlur.

Ví dụ: Một ô input hiển thị một thông báo "hint" (gợi ý) đặc biệt ngay khi bạn focus vào nó.

const input = page.locator('#password');

const hint = page.locator('#password-hint');

// Ban đầu, hint bị ẩn

await expect(hint).not.toBeVisible();


// 1. Chỉ focus vào ô input

await input.focus();


// 2. Kiểm tra xem hint đã xuất hiện chưa

await expect(hint).toBeVisible();

await expect(hint).toContainText('Mật khẩu phải có ít nhất 8 ký tự');

 

 

 

Teacher

Teacher

Nguyên Hoàng

Automation Engineer

With 7+ years of hands-on experience across multiple languages and frameworks. I'm here to share knowledge, helping you turn complex processes into simple and effective solutions.

Cộng đồng Automation Testing Việt Nam:

🌱 Telegram Automation Testing:   Cộng đồng Automation Testing
🌱 
Facebook Group Automation: Cộng đồng Automation Testing Việt Nam
🌱 
Facebook Fanpage: Cộng đồng Automation Testing Việt Nam - Selenium
🌱 Telegram
Manual Testing:   Cộng đồng Manual Testing
🌱 
Facebook Group Manual: Cộng đồng Manual Testing Việt Nam

Chia sẻ khóa học lên trang

Bạn có thể đăng khóa học của chính bạn lên trang Anh Tester để kiếm tiền

Danh sách bài học