Automation testing

[Playwright] Hướng dẫn tương tác với checkbox

Phong Do Phong Do · 11/09/2025 · 16 phút đọc

Checkbox là một trong những element phổ biến nhất trong web testing, nhưng không phải lúc nào việc tương tác với chúng cũng đơn giản như ta tưởng. Bài viết này sẽ hướng dẫn chi tiết cách làm việc với checkbox trong Playwright TypeScript, từ cơ bản đến nâng cao.

Các phương thức cơ bản để làm việc với Checkbox

Playwright cung cấp ba phương thức chính để tương tác với checkbox. Tất cả các ví dụ dưới đây sử dụng page.locator() – cách được khuyến khích trong Playwright hiện đại.

1. Phương thức check()

Phương thức check() được sử dụng để đánh dấu checkbox nếu nó chưa được chọn. Nếu checkbox đã được chọn, phương thức này không thực hiện hành động gì.

import { test, expect } from '@playwright/test';

test('Check a checkbox', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  // Check checkbox bằng id
  await page.locator('#terms-checkbox').check();
  
  // Check checkbox bằng label text
  await page.locator('text=I agree to terms and conditions').check();
  
  // Check checkbox bằng CSS selector
  await page.locator('input[type="checkbox"][name="newsletter"]').check();
  
  // Check checkbox bằng role
  await page.locator('role=checkbox[name="Subscribe to newsletter"]').check();
  
  // Verify checkbox đã được check
  await expect(page.locator('#terms-checkbox')).toBeChecked();
});

2. Phương thức uncheck()

Phương thức uncheck() dùng để bỏ chọn checkbox nếu nó đang được chọn. Tương tự như check(), nếu checkbox chưa được chọn, phương thức này không làm gì.

test('Uncheck a checkbox', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  const newsletterCheckbox = page.locator('#newsletter-checkbox');
  
  // Giả sử checkbox ban đầu đã được check
  await newsletterCheckbox.check();
  
  // Uncheck checkbox
  await newsletterCheckbox.uncheck();
  
  // Verify checkbox đã được uncheck
  await expect(newsletterCheckbox).not.toBeChecked();
});

3. Phương thức setChecked()

Phương thức setChecked() là phương thức linh hoạt nhất, cho phép bạn đặt trạng thái của checkbox một cách rõ ràng thông qua tham số boolean.

test('Set checkbox state with setChecked', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  const notificationCheckbox = page.locator('#notification-checkbox');
  
  // Set checkbox thành checked
  await notificationCheckbox.setChecked(true);
  
  // Set checkbox thành unchecked
  await notificationCheckbox.setChecked(false);
  
  // Toggle checkbox state
  const currentState = await notificationCheckbox.isChecked();
  await notificationCheckbox.setChecked(!currentState);
  
  // Verify với assertion
  await expect(notificationCheckbox).toBeChecked();
});

Options chi tiết cho check(), uncheck() và setChecked()

Cả ba phương thức đều hỗ trợ các options giống nhau để xử lý các tình huống phức tạp.

Option: force

Option force cho phép bỏ qua các kiểm tra actionability. Hữu ích khi element bị che khuất hoặc có custom styling phức tạp.

test('Using force option', async ({ page }) => {
  await page.goto('https://example.com/custom-form');
  
  // Checkbox bị overlay che phủ
  const hiddenCheckbox = page.locator('#hidden-checkbox');
  
  // Sẽ fail nếu checkbox bị che
  try {
    await hiddenCheckbox.check({ timeout: 3000 });
  } catch (error) {
    console.log('Cannot check hidden checkbox normally');
  }
  
  // Force check bỏ qua visibility checks
  await hiddenCheckbox.check({ force: true });
  
  // Force uncheck
  await hiddenCheckbox.uncheck({ force: true });
  
  // Force với setChecked
  await hiddenCheckbox.setChecked(true, { force: true });
  
  // Verify
  await expect(hiddenCheckbox).toBeChecked();
});

Option: position

Option position cho phép click vào vị trí cụ thể là vị trí tương đối tới góc trái trên của element padding box.

test('Using position option', async ({ page }) => {
  await page.goto('https://example.com/large-checkbox');
  
  const largeCheckbox = page.locator('#large-custom-checkbox');
  
  // Click vào vị trí cụ thể của checkbox
  await largeCheckbox.check({ 
    position: { x: 10, y: 10 } 
  });
  
  // Useful cho custom checkbox với clickable area lớn
  await page.locator('.checkbox-container').check({
    position: { x: 20, y: 15 }
  });
  
  // Uncheck với position
  await largeCheckbox.uncheck({
    position: { x: 10, y: 10 }
  });
  
  // setChecked với position
  await largeCheckbox.setChecked(false, {
    position: { x: 5, y: 5 }
  });
});

Option: timeout

Option timeout đặt thời gian chờ tối đa cho action. Mặc định là 30 giây (có thể config trong playwright.config.ts).

test('Using timeout option', async ({ page }) => {
  await page.goto('https://example.com/slow-loading');
  
  // Checkbox load chậm
  const slowCheckbox = page.locator('#slow-checkbox');
  
  // Tăng timeout cho element load chậm
  await slowCheckbox.check({ timeout: 60_000 }); // 60 seconds
  
  // Giảm timeout cho fast fail
  try {
    await page.locator('#non-existent').check({ timeout: 1000 });
  } catch (error) {
    console.log('Element not found within 1 second');
  }
  
  // Timeout với uncheck
  await slowCheckbox.uncheck({ timeout: 45000 });
  // Thêm "_" để phân tách phần nghìn cho dễ nhìn. Vẫn đúng syntax
  await slowCheckbox.uncheck({ timeout: 45_000 });
  
  // Timeout với setChecked
  await slowCheckbox.setChecked(true, { timeout: 30000 });
});

Option: trial

Option trial chỉ thực hiện actionability checks mà không thực sự perform action. Hữu ích để verify element có thể tương tác được.

test('Using trial option', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  const checkbox = page.locator('#terms-checkbox');
  
  // Kiểm tra xem có thể check được không (dry run)
  await checkbox.check({ trial: true });
  console.log('Checkbox is checkable');
  
  // Checkbox vẫn chưa được check thực sự
  expect(await checkbox.isChecked()).toBeFalsy();
  
  // Bây giờ thực sự check
  await checkbox.check();
  expect(await checkbox.isChecked()).toBeTruthy();
  
  // Trial với uncheck
  await checkbox.uncheck({ trial: true });
  // Checkbox vẫn còn checked
  expect(await checkbox.isChecked()).toBeTruthy();
  
  // Trial với setChecked
  await checkbox.setChecked(false, { trial: true });
  // State không thay đổi
  expect(await checkbox.isChecked()).toBeTruthy();
  
  // Thực sự uncheck
  await checkbox.uncheck();
});

Kết hợp nhiều options

test('Combining multiple options', async ({ page }) => {
  await page.goto('https://example.com/complex-form');
  
  const complexCheckbox = page.locator('#complex-checkbox');
  
  // Kết hợp force và timeout
  await complexCheckbox.check({
    force: true,
    timeout: 45000
  });
  
  // Kết hợp position và timeout
  await complexCheckbox.uncheck({
    position: { x: 15, y: 15 },
    timeout: 30000
  });
  
  // Trial run với timeout ngắn
  try {
    await complexCheckbox.check({
      trial: true,
      timeout: 5000
    });
    console.log('Element is actionable');
  } catch (error) {
    console.log('Element not actionable within 5 seconds');
  }
  
  // setChecked với multiple options
  await complexCheckbox.setChecked(true, {
    force: true,
    position: { x: 10, y: 10 },
    timeout: 20000
  });
});

Xử lý các tình huống phức tạp với Options

Custom checkbox với overlay

test('Handle checkbox with overlay using options', async ({ page }) => {
  await page.goto('https://example.com/modal-form');
  
  // Modal overlay che checkbox
  await page.locator('#open-modal').click();
  
  const modalCheckbox = page.locator('.modal-content #agreement');
  
  // Thử check normal - có thể fail
  try {
    await modalCheckbox.check({ timeout: 3000 });
  } catch {
    // Nếu fail, dùng force
    await modalCheckbox.check({ force: true });
  }
  
  // Hoặc check với specific position để avoid overlay
  await modalCheckbox.check({
    position: { x: 5, y: 5 },
    timeout: 10000
  });
});

Dynamic loading checkboxes

test('Handle dynamically loaded checkboxes', async ({ page }) => {
  await page.goto('https://example.com/lazy-load');
  
  // Trigger lazy loading
  await page.locator('#load-more').click();
  
  // Wait và check với extended timeout
  const dynamicCheckboxes = page.locator('.dynamic-item input[type="checkbox"]');
  
  // Check all với different timeouts
  const count = await dynamicCheckboxes.count();
  for (let i = 0; i < count; i++) {
    await dynamicCheckboxes.nth(i).check({
      timeout: 20000 // Extended timeout cho mỗi checkbox
    });
  }
  
  // Verify với trial option trước
  const criticalCheckbox = page.locator('#critical-option');
  await criticalCheckbox.check({ trial: true, timeout: 30000 });
  
  // Nếu trial pass, thực sự check
  await criticalCheckbox.check({ timeout: 30000 });
});

Checkbox trong scrollable container

test('Handle checkbox in scrollable area', async ({ page }) => {
  await page.goto('https://example.com/long-form');
  
  const checkboxInScroll = page.locator('#checkbox-at-bottom');
  
  // Auto scroll và check
  await checkboxInScroll.check(); // Playwright auto scrolls
  
  // Hoặc explicit scroll và check với position
  await checkboxInScroll.scrollIntoViewIfNeeded();
  await checkboxInScroll.check({
    position: { x: 10, y: 10 },
    timeout: 15000
  });
  
  // Force check nếu scroll có vấn đề
  await checkboxInScroll.check({
    force: true,
    timeout: 20000
  });
});

Best Practices với Options

1. Progressive enhancement strategy

test('Progressive enhancement with options', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  const checkbox = page.locator('#important-checkbox');
  
  // Bước 1: Thử normal check
  try {
    await checkbox.check({ timeout: 5000 });
  } catch {
    // Bước 2: Thử với extended timeout
    try {
      await checkbox.check({ timeout: 15000 });
    } catch {
      // Bước 3: Force as last resort
      await checkbox.check({ force: true });
      console.warn('Had to use force option for checkbox');
    }
  }
});

2. Validation before action

test('Validate before action using trial', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  async function safeCheck(locator: Locator) {
    // Validate với trial first
    try {
      await locator.check({ trial: true, timeout: 5000 });
      // Nếu trial pass, proceed với actual check
      await locator.check();
      return true;
    } catch (error) {
      console.error(`Cannot check element: ${error.message}`);
      return false;
    }
  }
  
  const checkbox1 = page.locator('#checkbox1');
  const checkbox2 = page.locator('#checkbox2');
  
  await safeCheck(checkbox1);
  await safeCheck(checkbox2);
});

3. Config-driven timeouts

// test-config.ts
export const TIMEOUTS = {
  fast: 5000,
  normal: 15000,
  slow: 30000,
  verySlow: 60000
};

// test file
import { TIMEOUTS } from './test-config';

test('Use configured timeouts', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  // Fast elements
  await page.locator('#quick-checkbox').check({ 
    timeout: TIMEOUTS.fast 
  });
  
  // Normal elements
  await page.locator('#normal-checkbox').check({ 
    timeout: TIMEOUTS.normal 
  });
  
  // Slow loading elements
  await page.locator('#slow-checkbox').check({ 
    timeout: TIMEOUTS.slow 
  });
  
  // Very slow or problematic elements
  await page.locator('#problematic-checkbox').setChecked(true, {
    timeout: TIMEOUTS.verySlow,
    force: true
  });
});

4. Helper functions với default options

// helpers/checkbox-helper.ts
import { Locator } from '@playwright/test';

export class CheckboxHelper {
  static async safeCheck(
    locator: Locator, 
    options = { timeout: 15000, force: false }
  ) {
    try {
      await locator.check(options);
    } catch (error) {
      console.warn(`Check failed with options, trying force: ${error.message}`);
      await locator.check({ ...options, force: true });
    }
  }
  
  static async checkWithRetry(
    locator: Locator,
    maxRetries = 3,
    options = { timeout: 10000 }
  ) {
    for (let i = 0; i < maxRetries; i++) {
      try {
        await locator.check(options);
        return;
      } catch (error) {
        if (i === maxRetries - 1) throw error;
        console.log(`Retry ${i + 1}/${maxRetries}`);
        await locator.page().waitForTimeout(1000);
      }
    }
  }
  
  static async checkAtPosition(
    locator: Locator,
    x: number,
    y: number,
    timeout = 15000
  ) {
    await locator.check({
      position: { x, y },
      timeout
    });
  }
}

// Usage in test
test('Use checkbox helper', async ({ page }) => {
  await page.goto('https://example.com/form');
  
  const checkbox = page.locator('#my-checkbox');
  
  await CheckboxHelper.safeCheck(checkbox);
  await CheckboxHelper.checkWithRetry(checkbox, 5);
  await CheckboxHelper.checkAtPosition(checkbox, 10, 10);
});

Tìm hiểu thêm

Phong Do
Tác giả
Phong Do