Site logo
Tác giả
  • avatar Nguyễn Đức Xinh
    Name
    Nguyễn Đức Xinh
    Twitter
Ngày xuất bản
Ngày xuất bản

Tổng Hợp Các Method Của Locator Trong Playwright - Hướng Dẫn Chi Tiết

Giới Thiệu

Locator là thành phần cốt lõi trong Playwright, cung cấp khả năng auto-waiting và retry tự động. Locator đại diện cho cách tìm element trên page tại bất kỳ thời điểm nào. Các method của Locator đóng vai trò quan trọng trong việc tương tác với trang web, cho phép bạn thực hiện mọi thao tác từ cơ bản đến phức tạp như click, điền form, kiểm tra trạng thái, và xử lý các tình huống đặc biệt. Hiểu và sử dụng thành thạo các method này là chìa khóa để viết test automation hiệu quả và ổn định.

1. Các Method Tương Tác Với Element

1.1. Click và Double Click

Method Mô Tả Ví Dụ
click() Click vào element await page.getByRole('button').click()
dblclick() Double-click element await locator.dblclick()
tap() Thao tác tap (cho thiết bị cảm ứng) await locator.tap()

Ví dụ click với options:

// Click với modifier key
await page.locator('canvas').click({
  button: 'right',
  modifiers: ['Shift'],
  position: { x: 23, y: 32 }
});

1.2. Nhập Liệu (Input)

Method Mô Tả Sử Dụng Khi
fill() Điền giá trị vào input field Nhập văn bản nhanh vào form
type() (deprecated) Gõ từng ký tự Không nên dùng, dùng pressSequentially()
pressSequentially() Gõ từng ký tự (như người dùng thật) Cần trigger keyboard events
press() Nhấn một phím cụ thể Nhấn Enter, Backspace, v.v.

Ví dụ:

// Fill - Cách nhanh nhất
await page.getByRole('textbox').fill('example value');

// Press sequentially - Gõ từng ký tự
await locator.pressSequentially('Hello', { delay: 100 });

// Press - Nhấn phím đặc biệt
await page.getByRole('textbox').press('Backspace');
await locator.press('Control+A'); // Shortcut

1.3. Xóa và Clear

// Clear input field
await page.getByRole('textbox').clear();

// Blur - Remove focus
await locator.blur();

1.4. Hover và Focus

// Hover over element
await page.getByRole('link').hover();

// Focus vào element
await locator.focus();

// Scroll element vào view
await locator.scrollIntoViewIfNeeded();

2. Checkbox và Radio Button

Method Mô Tả Return
check() Đảm bảo checkbox/radio được checked Promise<void>
uncheck() Đảm bảo checkbox/radio không được checked Promise<void>
setChecked(checked) Set trạng thái checked (true/false) Promise<void>
isChecked() Kiểm tra xem có được checked không Promise<boolean>

Ví dụ:

// Check checkbox
await page.getByRole('checkbox').check();

// Uncheck
await page.getByRole('checkbox').uncheck();

// Set checked state
await locator.setChecked(true);

// Kiểm tra trạng thái
const checked = await page.getByRole('checkbox').isChecked();

3. Select Options và File Upload

3.1. Select Option

// Select single option bằng value
await locator.selectOption('blue');

// Select bằng label
await locator.selectOption({ label: 'Blue' });

// Select multiple options
await locator.selectOption(['red', 'green', 'blue']);

// Select bằng index
await locator.selectOption({ index: 0 });

3.2. File Upload

// Upload một file
await page.getByLabel('Upload file').setInputFiles(
  path.join(__dirname, 'myfile.pdf')
);

// Upload nhiều files
await page.getByLabel('Upload files').setInputFiles([
  path.join(__dirname, 'file1.txt'),
  path.join(__dirname, 'file2.txt')
]);

// Upload từ buffer
await page.getByLabel('Upload file').setInputFiles({
  name: 'file.txt',
  mimeType: 'text/plain',
  buffer: Buffer.from('this is test')
});

// Clear files
await page.getByLabel('Upload file').setInputFiles([]);

4. Lấy Thông Tin Từ Element

4.1. Text Content

Method Return Ghi Chú
textContent() Promise<string | null> Lấy node.textContent
innerText() Promise<string> Lấy element.innerText
innerHTML() Promise<string> Lấy element.innerHTML
allTextContents() Promise<string[]> Lấy textContent của tất cả matching elements
allInnerTexts() Promise<string[]> Lấy innerText của tất cả matching elements

Ví dụ:

const text = await locator.textContent();
const inner = await locator.innerText();
const html = await locator.innerHTML();

// Lấy text của tất cả elements
const texts = await page.getByRole('link').allTextContents();

4.2. Input Value và Attributes

// Lấy value của input/textarea/select
const value = await page.getByRole('textbox').inputValue();

// Lấy attribute
const href = await locator.getAttribute('href');
const title = await locator.getAttribute('title');

// Lấy bounding box
const box = await locator.boundingBox();
// Returns: { x, y, width, height } hoặc null

5. Kiểm Tra Trạng Thái Element

5.1. Visibility và Display

Method Kiểm Tra Return
isVisible() Element có visible không Promise<boolean>
isHidden() Element có hidden không Promise<boolean>
isEnabled() Element có enabled không Promise<boolean>
isDisabled() Element có disabled không Promise<boolean>
isEditable() Element có editable không Promise<boolean>

Ví dụ:

const visible = await page.getByRole('button').isVisible();
const disabled = await page.getByRole('button').isDisabled();
const editable = await page.getByRole('textbox').isEditable();

// Lưu ý: isVisible() và isHidden() không wait
// Nên dùng expect() cho assertions
await expect(page.getByRole('button')).toBeVisible();

5.2. Count và Bounding Box

// Đếm số lượng matching elements
const count = await page.getByRole('listitem').count();

// Lấy bounding box coordinates
const box = await page.getByRole('button').boundingBox();
if (box) {
  await page.mouse.click(box.x + box.width / 2, box.y + box.height / 2);
}

6. Filter và Chọn Element

6.1. Filter Locators

const rows = page.locator('tr');

// Filter theo text
await rows
  .filter({ hasText: 'text in column 1' })
  .filter({ has: page.getByRole('button', { name: 'column 2 button' }) })
  .screenshot();

// Filter theo điều kiện phủ định
await rows.filter({ hasNotText: 'excluded text' });
await rows.filter({ hasNot: page.locator('.hidden') });

// Filter theo visibility
await rows.filter({ visible: true });

6.2. Chọn Element Cụ Thể

Method Mô Tả Ví Dụ
first() Element đầu tiên locator.first()
last() Element cuối cùng await page.getByRole('listitem').last()
nth(index) Element thứ n (index từ 0) await page.getByRole('listitem').nth(2)

Ví dụ:

// Lấy element đầu tiên
const firstButton = await page.getByRole('button').first();

// Lấy element cuối
const lastItem = await page.getByRole('listitem').last();

// Lấy element thứ 3 (index = 2)
const thirdItem = await page.getByRole('listitem').nth(2);

7. Locator Nâng Cao

7.1. Kết Hợp Locators

// AND - Kết hợp 2 locators
const button = page.getByRole('button').and(page.getByTitle('Subscribe'));

// OR - Match một trong hai
const newEmail = page.getByRole('button', { name: 'New' });
const dialog = page.getByText('Confirm security settings');
await expect(newEmail.or(dialog).first()).toBeVisible();

7.2. Nested Locators

// Locator trong locator
const article = page.locator('article');
const content = article.locator('.content');

// Frame locator
const frameLocator = page.frameLocator('iframe');
await frameLocator.getByRole('button').click();

// Content frame (cho iframe)
const locator = page.locator('iframe[name="embedded"]');
const frameLocator = locator.contentFrame();
await frameLocator.getByRole('button').click();

8. Wait và Assertions

8.1. Wait For Element

// Wait for element state
await locator.waitFor({ state: 'visible' });
await locator.waitFor({ state: 'hidden' });
await locator.waitFor({ state: 'attached' });
await locator.waitFor({ state: 'detached' });

// Wait for element state (các state: visible, hidden, stable, enabled, disabled, editable)
await locator.waitForElementState('visible');
await locator.waitForElementState('enabled');

8.2. Wait For Selector

await page.setContent(`<div><span></span></div>`);
const div = await page.$('div');
const span = await div.waitForSelector('span', { state: 'attached' });

9. Advanced Operations

9.1. Drag and Drop

const source = page.locator('#source');
const target = page.locator('#target');

await source.dragTo(target);

// Với positions cụ thể
await source.dragTo(target, {
  sourcePosition: { x: 34, y: 7 },
  targetPosition: { x: 10, y: 20 }
});

9.2. Screenshot

// Screenshot element
await page.getByRole('link').screenshot();

// Screenshot với options
await page.getByRole('link').screenshot({
  animations: 'disabled',
  path: 'link.png',
  mask: [page.locator('.sensitive-data')],
  maskColor: '#FF0000'
});

9.3. Evaluate JavaScript

// Execute JS trên element
const result = await page.getByTestId('myId').evaluate((element, [x, y]) => {
  return element.textContent + ' ' + x * y;
}, [7, 8]);

// Evaluate all matching elements
const moreThanTen = await locator.evaluateAll(
  (divs, min) => divs.length > min,
  10
);

// Evaluate và return JSHandle
const handle = await locator.evaluateHandle(element => element);

9.4. Dispatch Events

// Dispatch custom event
await locator.dispatchEvent('click');

// Với event init properties
await locator.dispatchEvent('dragstart', { dataTransfer });

10. Locator Selectors

10.1. Get By Methods

Method Mô Tả Ví Dụ
getByRole() Tìm theo ARIA role page.getByRole('button', { name: 'Submit' })
getByText() Tìm theo text content page.getByText('Hello')
getByLabel() Tìm theo label text page.getByLabel('Username')
getByPlaceholder() Tìm theo placeholder page.getByPlaceholder('name@example.com')
getByAltText() Tìm theo alt text page.getByAltText('Playwright logo')
getByTitle() Tìm theo title attribute page.getByTitle('Issues count')
getByTestId() Tìm theo test id page.getByTestId('directions')

Ví dụ Get By Role:

// Basic role
await page.getByRole('button', { name: 'Submit' }).click();

// Với các options
await page.getByRole('heading', { name: 'Sign up' });
await page.getByRole('checkbox', { name: 'Subscribe' }).check();
await page.getByRole('button', { name: /submit/i }).click();

// Role với state
await page.getByRole('button', {
  name: 'Save',
  disabled: false,
  pressed: true
});

11. Utility Methods

11.1. Các Method Hỗ Trợ

// Lấy page từ locator
const page = locator.page();

// Highlight element (debugging)
await locator.highlight();

// Get all matching locators
const allButtons = await page.getByRole('button').all();
for (const button of allButtons) {
  await button.click();
}

// Mô tả locator (cho trace/report)
const button = page.getByTestId('btn-sub').describe('Subscribe button');
await button.click();

// Select text
await locator.selectText();

// Aria snapshot
const snapshot = await page.getByRole('link').ariaSnapshot();

12. Bảng Tổng Hợp Nhanh

12.1. Các Method Thường Dùng Nhất

Category Methods Frequency
Click click(), dblclick() ⭐⭐⭐⭐⭐
Input fill(), press(), pressSequentially() ⭐⭐⭐⭐⭐
Get Text textContent(), innerText() ⭐⭐⭐⭐⭐
Checkbox check(), uncheck(), isChecked() ⭐⭐⭐⭐
Visibility isVisible(), isHidden() ⭐⭐⭐⭐
Select first(), last(), nth() ⭐⭐⭐⭐
Filter filter(), locator() ⭐⭐⭐
Wait waitFor(), waitForElementState() ⭐⭐⭐

12.2. So Sánh Text Methods

Method Khi Nào Dùng Assertion Tốt Hơn
textContent() Lấy raw text content expect(locator).toHaveText()
innerText() Lấy visible text (như user thấy) expect(locator).toHaveText({ useInnerText: true })
innerHTML() Lấy HTML content Ít khi cần assert

12.3. So Sánh Input Methods

Method Tốc Độ Use Case
fill() ⚡⚡⚡ Nhanh nhất Điền form thông thường
pressSequentially() ⚡⚡ Chậm hơn Cần trigger keyboard events
press() ⚡⚡⚡ Nhấn phím đặc biệt, shortcuts

13. Best Practices

13.1. Nên Dùng

  • ✅ Dùng fill() thay vì type() hoặc pressSequentially() cho input thông thường
  • ✅ Dùng expect() assertions thay vì isVisible(), isDisabled() để tránh flakiness
  • ✅ Dùng role-based selectors (getByRole) khi có thể
  • ✅ Dùng waitFor() thay vì hard-coded timeout

13.2. Không Nên

  • ❌ Không dùng type() - đã deprecated, dùng pressSequentially() hoặc fill()
  • ❌ Không dùng elementHandle() - racy, dùng locator
  • ❌ Không dùng isVisible() cho assertions - dùng expect(locator).toBeVisible()
  • ❌ Không quên set timeout cho các operations lâu

13.3. Tips và Tricks

// 1. Chain operations
await page.getByRole('textbox')
  .fill('test')
  .press('Enter');

// 2. Reuse locators
const submitButton = page.getByRole('button', { name: 'Submit' });
await expect(submitButton).toBeVisible();
await submitButton.click();

// 3. Wait cho element trước khi interact
await locator.waitFor({ state: 'visible' });
await locator.click();

// 4. Dùng force cho edge cases
await locator.click({ force: true }); // Bypass actionability checks

14. Xử Lý Các Trường Hợp Đặc Biệt

14.1. Element Trong Frame

// Cách 1: Frame locator
const frame = page.frameLocator('iframe');
await frame.getByRole('button').click();

// Cách 2: Content frame
const iframeLocator = page.locator('iframe[name="embedded"]');
const frame = iframeLocator.contentFrame();
await frame.getByRole('button').click();

14.2. Dynamic Content

// Wait for element appear
await page.getByText('Loading...').waitFor({ state: 'hidden' });
await page.getByText('Content loaded').waitFor({ state: 'visible' });

// Wait for text change
await expect(locator).toHaveText('New text');

14.3. Multiple Elements

// Get all và iterate
const buttons = await page.getByRole('button').all();
for (const button of buttons) {
  if (await button.isVisible()) {
    await button.click();
  }
}

// Count before processing
const count = await page.getByRole('listitem').count();
for (let i = 0; i < count; i++) {
  await page.getByRole('listitem').nth(i).click();
}

Kết Luận

Playwright Locator cung cấp một bộ API phong phú và mạnh mẽ để tương tác với web elements. Hiểu rõ và sử dụng đúng các method sẽ giúp bạn:

  • Viết test code ngắn gọn, dễ đọc hơn
  • Giảm flakiness trong tests
  • Tận dụng auto-waiting và retry mechanism
  • Xử lý các trường hợp phức tạp một cách dễ dàng

Hãy bookmark bài viết này để tra cứu khi cần và thực hành thường xuyên để thành thạo Playwright!

Tài Liệu Tham Khảo