NỘI DUNG BÀI HỌC

🛡️ Tin tưởng Auto-Waiting trên Hành Động (Actions) 

🎯 Dùng Web-First Assertions làm "Chốt Chặn An Toàn" 

🚀 Làm chủ page.goto() với waitUntil



⏳ Phần 1: Auto-Waiting là gì?

 

Khác với framework cũ báo lỗi ngay khi phần tử chưa xuất hiện, Playwright tự động chờ cho tới khi phần tử sẵn sàng tương tác (actionable), chứ không chỉ tồn tại trong DOM. Nhờ vậy bạn gần như không cần rải sleep() khắp nơi.

Trước mỗi hành động, Playwright chạy các "Actionability Checks" và chờ cho tới khi phần tử: gắn vào DOM (Attached), hiển thị (Visible), ổn định không animation (Stable), nhận được sự kiện không bị che (Receives Events), và được phép tương tác (Enabled).

 

🔹 1. Ba trụ cột chờ đợi

 

Trụ cột 1 - Chờ ngầm trên hành động (.click().fill().check().press()). Bạn không cần làm gì, Playwright tự chờ.

 

// Tự chờ ô input sẵn sàng rồi mới điền
await page.getByLabel('Username').fill('myuser');
// Tự chờ nút bấm sẵn sàng (hết disabled) rồi mới click
await page.getByRole('button', { name: 'Login' }).click();

 

Trụ cột 2 - Chờ tường minh với Web-First Assertions, dùng làm checkpoint xác minh kết quả.

 

await page.goto('/dashboard');
// Checkpoint: chờ đến khi tiêu đề Dashboard xuất hiện
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
await searchButton.click();
// Chờ danh sách kết quả có đúng 3 mục
await expect(page.locator('.results-item')).toHaveCount(3);

 

Trụ cột 3 - Chờ sự kiện chuyên dụng (mạng, điều hướng), dùng cho những thứ nằm ngoài UI.

 

// Chờ đến khi một cuộc gọi API cụ thể hoàn thành
await page.waitForResponse('**/api/user/profile');

 

🔹 2. Ví dụ tổng hợp: chuỗi hành động bất đồng bộ

 

Kịch bản: tick checkbox → hệ thống chờ 1.5s rồi bật nút "Tiếp tục" → click → chờ 3s tải dữ liệu → hiện thông báo chào mừng. Điều kỳ diệu là ta không cần ra lệnh chờ 1.5s hay 3s.

 

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

test('should handle chained asynchronous actions with ease', async ({ page }) => {
  const termsCheckbox = page.getByLabel('Tôi đồng ý với các điều khoản và điều kiện.');
  const proceedButton = page.getByRole('button', { name: 'Tiếp tục' });
  const welcomeMessage = page.getByRole('heading', { name: 'Chào mừng bạn đã quay trở lại!' });

  // 1. Tick vào ô - auto-wait của .check() chờ checkbox sẵn sàng
  await termsCheckbox.check();

  // 2. Click nút "Tiếp tục"
  // Playwright TỰ ĐỘNG chờ nút hết `disabled` rồi mới click,
  // tự xử lý 1.5 giây chờ mà ta không cần ra lệnh.
  await proceedButton.click();

  // 3. Web-First Assertion chờ kết quả của 3 giây tải dữ liệu
  // .click() ở trên không chờ 3 giây này, nên expect sẽ kiên nhẫn chờ tiếp.
  await expect(welcomeMessage).toBeVisible({ timeout: 5000 });
});

 

⚠️ Lưu ý: Khi gặp timeout, đừng vội tăng thời gian chờ. Hãy chẩn đoán trước: selector có đúng không, trạng thái phần tử có như mong đợi không, ứng dụng có vấn đề hiệu năng không. Coi mỗi lỗi timeout là một cơ hội điều tra.

 

✅ Phần 2: Web-First Assertions

 

Cách cũ (Script-First) tách thành 2 bước: lấy text rồi so sánh. Nếu dữ liệu chưa kịp tải, test fail ngay dù chỉ mili giây sau nó đã đúng. Playwright gộp "chờ và kiểm tra" thành một lệnh duy nhất.

 

// Tương đương ra lệnh: "Hãy chờ đến khi phần tử này có text là 'Success!'"
await expect(element).toHaveText('Success!');

Cơ chế bên trong là vòng lặp polling với timeout mặc định 5 giây: kiểm tra điều kiện, nếu đúng thì pass, nếu sai thì chờ chút rồi thử lại cho đến khi hết giờ.

 

🔹 1. Dùng làm "chốt chặn an toàn" và xác minh hậu quả

 

// Chốt chặn sau khi điều hướng
await page.goto('/dashboard');
await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();
// Bây giờ mới tự tin thực hiện các bước tiếp theo...

// Xác minh hậu quả bất đồng bộ sau một hành động
await page.getByRole('button', { name: 'Thêm vào giỏ hàng' }).click();
await expect(page.locator('#cart-badge')).toHaveText('1'); // Chờ icon giỏ hàng cập nhật

Auto-Wait trên action và Web-First Assertion bổ sung cho nhau: dùng action để kích hoạt sự kiện, dùng expect để chờ và xác minh kết quả.

 

🚦 Phần 3: page.goto() và waitUntil

 

Tùy chọn waitUntil cho phép chỉ định "cột mốc" chờ trong quá trình tải trang. Có 4 mức từ nghiêm ngặt đến lỏng nhất.

 

🔹 1. 'load' và 'domcontentloaded'

 

'load' (mặc định) chờ toàn bộ HTML và tài nguyên phụ (CSS, JS, ảnh, iframe). 'domcontentloaded' chỉ chờ HTML được parse thành DOM, không đợi ảnh - nhanh hơn cho test tương tác form.

 

// Ghi đè mặc định để bắt đầu điền form ngay,
// không cần chờ ảnh nền nặng tải xong.
await page.goto('/login', { waitUntil: 'domcontentloaded' });
await page.getByLabel('username').fill('Gemini');
await page.getByRole('button', { name: 'Login' }).click();

 

🔹 2. 'commit' và 'networkidle'

 

'commit' chờ ít nhất, chỉ đến khi trình duyệt nhận response headers - hợp cho health check, kiểm tra status/redirect. 'networkidle' chờ mạng "im" 500ms, không được khuyến khích nhưng vẫn hữu ích cho autosave.

 

// 'commit': có ngay thông tin response mà không cần render cả trang
const response = await page.goto('https://playwright.dev', { waitUntil: 'commit' });
expect(response.status()).toBe(200);

 

💡 Cách hiện đại được khuyên dùng: điều hướng nhanh bằng 'domcontentloaded', rồi chờ phần tử cốt lõi bằng assertion thay vì dựa vào 'networkidle'.

 

// Bước 1: Điều hướng nhanh
await page.goto('https://my-react-app.com', { waitUntil: 'domcontentloaded' });
// Bước 2: Chờ tường minh cho phần tử cốt lõi - "chốt chặn an toàn"
await expect(page.getByRole('main')).toBeVisible();

 

📋 Phần 4: Các Web-First Assertions phổ biến

 

Luôn nhớ đặt await trước mỗi assertion vì chúng bất đồng bộ, với timeout mặc định 5 giây.

 

🔹 1. Nhóm trạng thái & tồn tại

 

await expect(page.getByRole('button', { name: 'Login' })).toBeVisible();
await expect(page.locator('#spinner')).toBeHidden({ timeout: 10000 }); // Chờ tối đa 10s
await expect(page.getByRole('button', { name: 'Submit' })).toBeEnabled();
await expect(page.getByRole('button', { name: 'Submit' })).toBeDisabled();
await expect(page.getByLabel('I agree')).toBeChecked();
await expect(page.getByPlaceholder('Search')).toBeFocused();
await expect(page.locator('.search-result-item')).toHaveCount(5); // Đúng 5 mục
await expect(page.locator('#cart-items')).toBeEmpty();

 

🔹 2. Nhóm nội dung & thuộc tính

 

await expect(page.locator('#status')).toHaveText('Tải thành công!'); // Khớp chính xác
await expect(page.locator('.error-message')).toContainText('không hợp lệ'); // Chứa chuỗi con
await expect(page.getByLabel('Email')).toHaveValue('test@example.com');
await expect(page.getByRole('link', { name: 'Tài liệu' })).toHaveAttribute('href', '/docs');
await expect(page.locator('#myButton')).toHaveClass(/active/); // Chờ có class 'active'
await expect(page.locator('.error-text')).toHaveCSS('color', 'rgb(255, 0, 0)');

 

🔹 3. Nhóm trang

 

await expect(page).toHaveURL(/.*\/login/); // Dùng Regex cho linh hoạt
await expect(page).toHaveTitle('Dashboard - My App');

 

🔹 Ghi nhớ nhanh cho Tester

 

  • Tin tưởng Auto-Wait cho tương tác cơ bản (clickfill), đừng rải sleep().
  • Dùng Web-First Assertions (expect) làm chốt chặn xác minh kết quả vì chúng tự polling đến khi đúng.
  • Dùng page.waitFor... cho sự kiện ngoài UI như mạng, điều hướng.
  • Điều hướng bằng 'domcontentloaded' rồi chờ phần tử cốt lõi, thay vì dựa vào 'networkidle'.
  • Luôn đặt await trước mọi assertion, mặc định timeout là 5 giây.
  • Coi mỗi lỗi timeout là cơ hội điều tra selector/trạng thái, đừng chỉ tăng thời gian chờ.

 

✅ Kết luận: Bài 8 là nền tảng để viết test hết flaky. Khi bạn hiểu Auto-Waiting lo phần tương tác, Web-First Assertions lo phần xác minh, và waitUntil lo phần tải trang, bạn sẽ có những test ổn định, dễ đọc và dễ bảo trì mà không cần chờ đợi thủ công.

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