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 



Auto-Waiting là một trong những chủ đề quan trọng và "kỳ diệu" nhất đã làm nên tên tuổi của Playwright, giúp nó vượt trội hơn hẳn so với các thế hệ công cụ kiểm thử tự động trước đây. 

Nếu bạn đã từng trải qua nỗi đau của việc viết test không ổn định (flaky test), phải chèn các lệnh sleep() hay waitForElement() ở khắp nơi, thì bài giảng này sẽ cho bạn thấy một cách tiếp cận hoàn toàn mới, giúp cho kịch bản test của bạn trở nên mạnh mẽ, đáng tin cậy và cực kỳ dễ đọc.

Phần 1: Auto-Waiting là gì? - Triết lý Cốt lõi 🧠

Hãy bắt đầu với một vấn đề kinh điển: bạn ra lệnh cho máy click vào một nút bấm, nhưng nút đó chưa kịp xuất hiện trên màn hình.

Framework cũ (Thiếu kiên nhẫn): Sẽ báo lỗi Element Not Found ngay lập tức.

Playwright (Thông minh & Kiên nhẫn): Sẽ tự nói với chính nó: "Ồ, nút bấm chưa có ở đây. Tôi sẽ đợi một chút rồi thử lại."

Auto-Waiting chính là cơ chế tích hợp sẵn của Playwright, theo đó, nó sẽ tự động chờ cho đến khi một phần tử sẵn sàng để tương tác trước khi thực hiện hành động. Nó không chỉ chờ cho phần tử tồn tại, mà là chờ cho đến khi nó "có thể hành động được" (actionable)."Actionability Checks": Sổ Tay Kiểm Tra của Playwright

"Phép thuật" của Auto-Waiting nằm ở một danh sách các bước kiểm tra (checklist) mà Playwright tự động thực hiện trước mỗi hành động. Danh sách này được gọi là Actionability Checks.

Ví dụ, trước khi thực hiện locator.click(), Playwright sẽ âm thầm kiểm tra và chờ đợi cho đến khi phần tử:

  • Đã được gắn vào cây DOM (Attached).
  • Phải hiển thị (Visible).
  • Phải ổn định (Stable), tức là không còn hiệu ứng animation.
  • Phải nhận được sự kiện (Receives Events), tức không bị phần tử khác che khuất.
  • Phải được phép tương tác (Enabled), không có thuộc tính disabled.

Playwright sẽ liên tục thử lại các bước kiểm tra này cho đến khi tất cả đều thỏa mãn, hoặc cho đến khi hết thời gian chờ (timeout). Điều này mô phỏng chính xác sự kiên nhẫn của một người dùng thực thụ.

Ba Trụ Cột của Việc Chờ Đợi trong Playwright 🏗️

Để làm chủ việc chờ đợi, bạn cần nắm vững 3 công cụ chiến lược sau:

Trụ Cột 1: Chờ Đợi Ngầm trên Hành Động (Implicit Wait on Actions)

Công cụ: Các hành động tương tác như .click(), .fill(), .check(), .press().

Triết lý: "Hãy thực hiện hành động này cho tôi."

Khi nào dùng: Dùng cho hầu hết các tương tác cơ bản của người dùng. Hãy tin tưởng vào cơ chế Actionability Checks có sẵn của chúng.

Ví dụ:

// Playwright sẽ tự chờ cho ô input sẵn sàng rồi mới điền.

await page.getByLabel('Username').fill('myuser');

// Playwright sẽ tự chờ cho nút bấm sẵn sàng rồi mới click.

await page.getByRole('button', { name: 'Login' }).click();


Trụ Cột 2: Chờ Đợi Tường Minh với "Web-First Assertions"

Công cụ: expect(locator)... ví dụ: .toBeVisible(), .toHaveText().

Triết lý: "Hãy đảm bảo trạng thái này là đúng trước khi đi tiếp."

Khi nào dùng:

Dùng như một "chốt chặn" (checkpoint) để xác nhận một trạng thái quan trọng (ví dụ: sau khi điều hướng trang).

Để chờ đợi và xác minh một kết quả bất đồng bộ xảy ra sau một hành động.

Ví dụ:

await page.goto('/dashboard');

// Dùng làm checkpoint: Chờ cho đến khi tiêu đề Dashboard xuất hiện.

await expect(page.getByRole('heading', { name: 'Dashboard' })).toBeVisible();

await searchButton.click();

// Chờ kết quả: Chờ cho đến khi danh sách kết quả có 3 mục.

await expect(page.locator('.results-item')).toHaveCount(3);

 

Trụ Cột 3: Chờ Đợi Sự Kiện Chuyên Dụng

Công cụ: Các hàm page.waitFor...() như page.waitForResponse(), page.waitForURL().

Triết lý: "Hãy chờ một sự kiện không thuộc về giao diện người dùng."

Khi nào dùng: Khi bạn cần đồng bộ hóa kịch bản test của mình với các sự kiện mạng hoặc điều hướng phức tạp.

Ví dụ:

// Chờ cho đến khi một cuộc gọi API cụ thể hoàn thành.

await page.waitForResponse('**/api/user/profile');

Ví Dụ Tổng Hợp Nâng Cao

Hãy cùng xem cả 3 trụ cột này phối hợp với nhau như thế nào trong một kịch bản phức tạp: một form có các hành động phụ thuộc lẫn nhau.

Bối cảnh:

Người dùng tick vào ô "Đồng ý điều khoản".

Hệ thống mất 1.5 giây để "xác thực", sau đó nút "Tiếp tục" mới được kích hoạt.

Người dùng nhấn "Tiếp tục".

Hệ thống mất 3 giây để "tải dữ liệu".

Thông báo chào mừng cuối cùng xuất hiện.

Mã HTML cho ví dụ:

<!DOCTYPE html><html lang="en"><head><title>Advanced Auto-Wait Demo</title></head><body><h1>Quy trình Xác nhận</h1><div id="step-1"><label><input type="checkbox" id="terms-checkbox"> Tôi đồng ý với các điều khoản và điều kiện.</label></div><div id="step-2" style="margin-top: 20px;"><button id="proceed-btn" disabled>Tiếp tục</button></div><div id="loading-section" style="margin-top: 20px; display: none;"><p id="spinner">⏳ Đang tải dữ liệu...</p></div><div id="final-step" style="margin-top: 20px;"></div><script>const termsCheckbox=document.getElementById("terms-checkbox"),proceedButton=document.getElementById("proceed-btn"),loadingSection=document.getElementById("loading-section"),finalStep=document.getElementById("final-step");termsCheckbox.addEventListener("change",()=>{termsCheckbox.checked?(proceedButton.disabled=!0,setTimeout(()=>{proceedButton.disabled=!1},1500)):proceedButton.disabled=!0}),proceedButton.addEventListener("click",()=>{document.getElementById("step-1").style.display="none",document.getElementById("step-2").style.display="none",loadingSection.style.display="block",setTimeout(()=>{loadingSection.style.display="none";const e=document.createElement("h2");e.textContent="Chào mừng bạn đã quay trở lại!",finalStep.appendChild(e)},3e3)});</script></body></html>

 

Kịch bản Test "Viết Như Người Dùng Suy Nghĩ":

import { test, expect } from '@playwright/test';
test('should handle chained asynchronous actions with ease', async ({ page }) => {
  // Chuẩn bị: Tải nội dung HTML

  const htmlContent = `...`; // Dán nội dung HTML ở trên vào đây

  await page.setContent(htmlContent);
  // Định nghĩa các locators

  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!' });

  // === Bắt đầu kịch bản ===

  // 1. Hành động người dùng: Tick vào ô.

  // SỬ DỤNG TRỤ CỘT 1: Auto-wait của .check() sẽ chờ checkbox sẵn sàng.

  await termsCheckbox.check();

  // 2. Hành động người dùng: Click nút "Tiếp tục".

  // 💥 ĐÂY LÀ SỰ KỲ DIỆU CỦA TRỤ CỘT 1 💥

  // Playwright sẽ tự động chờ cho đến khi nút này hết `disabled` rồi mới click.

  // Nó tự xử lý 1.5 giây chờ đợi mà chúng ta không cần ra lệnh.

  await proceedButton.click();

  // 3. Xác minh kết quả cuối cùng.

  // SỬ DỤNG TRỤ CỘT 2: Web-First Assertion để chờ kết quả của 3 giây tải dữ liệu.

  // Lệnh .click() ở trên không chờ 3 giây này.

  // `expect().toBeVisible()` sẽ kiên nhẫn chờ cho đến khi thông báo chào mừng xuất hiện.

  await expect(welcomeMessage).toBeVisible({ timeout: 5000 });

  console.log('Kịch bản phức tạp đã được xử lý một cách tự động và gọn gàng!');

});

Khi Chờ Đợi Thất Bại - Xử lý Timeout

Khi bạn gặp lỗi timeout, đừng vội vàng tăng thời gian chờ. Hãy xem nó là một tín hiệu để chẩn đoán:

Selector có đúng không? (Nguyên nhân phổ biến nhất)

Trạng thái phần tử có đúng mong đợi không? (Nó có bị disabled không?)

Ứng dụng có bị lỗi performance không? (Thời gian chờ có quá lâu một cách vô lý không?)

Chỉ sau khi chẩn đoán, bạn mới quyết định "phương thuốc": tăng timeout cục bộ { timeout: ... }, sửa timeout toàn cục


Kết luận - Tóm tắt Quy tắc Vàng

Hãy tin tưởng Auto-Wait trên Action cho các tương tác người dùng cơ bản (click, fill...).

Sử dụng Web-First Assertions như các "chốt chặn" để chờ đợi và xác minh các kết quả, trạng thái quan trọng.

Sử dụng page.waitFor... cho các sự kiện không thuộc UI như network.

Xem lỗi Timeout là một cơ hội để điều tra, không phải một phiền toái.

Nắm vững các chiến lược này, bạn sẽ có thể viết các kịch bản test tự động hóa không chỉ mạnh mẽ mà còn cực kỳ ổn định, dễ đọc và dễ bảo trì.

 

Phần 2: Web-First Assertions - Triết lý Giúp Test Playwright Bất Bại 🚀

 

Trong các ứng dụng web ngày nay (xây dựng bằng React, Vue, Angular...), giao diện người dùng không còn tĩnh tại. Dữ liệu được tải về bất đồng bộ, các thành phần xuất hiện, biến mất, thay đổi trạng thái liên tục. Điều này tạo ra một thách thức lớn cho kiểm thử tự động.

Cách tiếp cận cũ (Script-First):

Các framework cũ thường buộc chúng ta phải tư duy theo kiểu "script":

Bước 1 (Lấy dữ liệu): "Hãy tìm phần tử và lấy nội dung text của nó ngay bây giờ." (const text = await element.textContent();)

Bước 2 (So sánh): "Bây giờ, so sánh nội dung text đó với giá trị mong muốn." (expect(text).toBe('Success!');)

Vấn đề là gì? Nếu ở Bước 1, dữ liệu chưa kịp tải về, text sẽ là một chuỗi rỗng và test sẽ thất bại ngay lập tức, dù chỉ một mili giây sau đó dữ liệu đã hiển thị đúng.

Giải pháp của Playwright (Web-First):

Playwright giới thiệu một sự thay đổi trong tư duy. Thay vì quy trình 2 bước trên, bạn chỉ cần ra một mệnh lệnh duy nhất:

"Hãy chờ cho đến khi phần tử này có nội dung text là 'Success!', hoặc báo lỗi nếu hết thời gian chờ."

await expect(element).toHaveText('Success!');

Đây chính là triết lý Web-First: Viết test từ góc nhìn và trải nghiệm của người dùng, không phải từ góc nhìn của một kịch bản máy móc.

"Web-First" là gì? - Phép so sánh Dễ hiểu 👨‍⚕️

Hãy tưởng tượng bạn đang kiểm tra sức khỏe của một bệnh nhân.

Cách làm cũ (Script-First):

Bạn ra lệnh: "Y tá, hãy đo nhiệt độ của bệnh nhân ngay bây giờ." (Y tá đo được 38°C).

Bạn cầm kết quả và hỏi: "Bác sĩ, 38°C có phải là nhiệt độ bình thường không?" (Bác sĩ trả lời không).

Đây là quy trình 2 bước: lấy dữ liệu, sau đó kiểm tra dữ liệu.


Cách làm mới (Web-First):

Bạn chỉ cần ra một mệnh lệnh duy nhất cho vị bác sĩ chuyên gia (Playwright): "Bác sĩ, hãy theo dõi bệnh nhân này. Khi nào nhiệt độ của anh ta trở về mức bình thường, hãy báo cho tôi biết."

Đây là quy trình 1 bước: bạn giao phó cả việc chờ đợi và kiểm tra cho chuyên gia.

expect(locator)... của Playwright chính là vị bác sĩ chuyên gia đó.


Cơ Chế Hoạt Động - Vòng Lặp Thử Lại (Polling Loop)

Sức mạnh của Web-First Assertions đến từ cơ chế auto-waiting được tích hợp sẵn, với timeout mặc định là 5 giây.

Khi bạn viết await expect(myLocator).toBeVisible();, Playwright sẽ khởi động một vòng lặp thông minh:

Bắt đầu đồng hồ đếm ngược 5 giây.

Kiểm tra: myLocator có đang visible không?

Nếu CÓ -> Khẳng định thành công, thoát vòng lặp và tiếp tục test.

Nếu KHÔNG -> Không báo lỗi. Kiên nhẫn chờ một khoảng thời gian ngắn.

Kiểm tra xem 5 giây đã hết chưa. Nếu chưa, quay lại Bước 2. Nếu đã hết, báo lỗi Timeout 5000ms exceeded. với thông tin chi tiết.

Chính vòng lặp này đã biến một câu lệnh "khẳng định" đơn giản thành một cơ chế "chờ đợi và khẳng định" cực kỳ mạnh mẽ.


Các Matcher "Web-First" Phổ Biến Nhất và Khi Nào Dùng Chúng

Playwright cung cấp một bộ sưu tập phong phú các matcher được thiết kế cho web.

Nhóm Trạng thái Hiển thị & Tồn tại:

.toBeVisible(): (Dùng nhiều nhất) Chờ và kiểm tra phần tử có hiển thị trên màn hình không.

.toBeHidden(): Chờ và kiểm tra phần tử đã bị ẩn hoặc xóa khỏi DOM. Rất hữu ích để kiểm tra spinner loading đã biến mất chưa.

.toHaveCount(number): Chờ và kiểm tra một locator trả về đúng số lượng phần tử. Tuyệt vời để kiểm tra danh sách kết quả tìm kiếm đã tải đủ.


Nhóm Trạng thái Tương tác:

.toBeEnabled(): Chờ và kiểm tra một nút bấm hoặc ô input có thể tương tác được (không bị disabled).

.toBeDisabled(): Chờ và kiểm tra một phần tử đang bị vô hiệu hóa.

.toBeEditable(): Chờ và kiểm tra một ô input có thể điền dữ liệu vào.

.toBeChecked(): Chờ và kiểm tra một checkbox/radio đã được tick hay chưa.


Nhóm Nội dung & Thuộc tính:

.toHaveText(text): Chờ và kiểm tra nội dung text của phần tử khớp chính xác (hoặc một phần nếu dùng mảng/biểu thức chính quy).

.toContainText(text): Tương tự toHaveText nhưng linh hoạt hơn.

.toHaveValue(value): Chờ và kiểm tra giá trị (value) của một ô input.

.toHaveAttribute(name, value): Chờ và kiểm tra một thuộc tính của thẻ HTML (ví dụ: class, aria-label).

.toHaveCSS(name, value): Chờ và kiểm tra một thuộc tính CSS đã được tính toán (ví dụ: color, background-color).


Chiến Lược Áp Dụng Thực Tế

Dùng làm "Chốt Chặn An Toàn" (Safety Checkpoint): Sau mỗi hành động lớn gây thay đổi trang (như page.goto() hoặc submit form), hãy bắt đầu bằng một Web-First Assertion để đảm bảo trang mới đã sẵn sà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...

Dùng để Xác minh Hậu quả Bất đồng bộ: Đây là công dụng chính. Sử dụng nó để chờ đợi kết quả của một hành động.

await page.getByRole('button', { name: 'Thêm vào giỏ hàng' }).click();

// Chờ cho đến khi icon giỏ hàng cập nhật số lượng là '1'.

await expect(page.locator('#cart-badge')).toHaveText('1');

Tích hợp trực tiếp vào Logic Test: Dùng nó để kiểm tra các quy tắc nghiệp vụ của ứng dụng.

So sánh với Auto-Wait trên Action

Hai cơ chế này là đồng minh bổ sung cho nhau, không phải đối thủ.

Công cụ

Triết lý

Ví dụ

Auto-Wait trên Action (.click())

"Thực hiện hành động này ngay khi có thể."

await loginButton.click();

Web-First Assertion (expect(...))

"Đảm bảo trạng thái này là đúng, hãy chờ nếu cần."

await expect(welcomeMessage).toBeVisible();


Quy trình làm việc chuẩn:

Sử dụng một Action (.click()) để kích hoạt một sự kiện. Tin tưởng vào auto-wait của nó để xử lý việc click.

Sử dụng một Web-First Assertion (expect) để chờ đợi và xác minh kết quả của hành động đó.


Kết luận

Việc áp dụng triết lý Web-First Assertions mang lại những lợi ích khổng lồ:

Chống Flaky Test: Đây là lợi ích lớn nhất, giúp loại bỏ gần như hoàn toàn các lỗi liên quan đến thời gian.

Code Ngắn gọn & Dễ đọc: Gộp việc chờ và kiểm tra vào một dòng lệnh duy nhất, thể hiện rõ ý định.

Tập trung vào Trải nghiệm Người dùng: Buộc bạn phải suy nghĩ về những gì người dùng thực sự thấy và mong đợi, thay vì các chi tiết kỹ thuật bên trong.

Làm chủ Web-First Assertions chính là bước ngoặt để bạn chuyển từ việc "bắt chước" hành vi người dùng sang việc "kiểm thử theo đúng trải nghiệm" của họ, tạo ra những bộ test tự động hóa thực sự mạnh mẽ và đáng tin cậy.

 

Phần 3: Làm Chủ page.goto() và Chiến Lược waitUntil trong Playwright⏱️

Tùy chọn waitUntil trong page.goto() giống như một "bảng điều khiển", cho phép bạn ra lệnh cho Playwright chờ đợi đến một "cột mốc" cụ thể trong quá trình tải trang. Để dễ hình dung, chúng ta sẽ sử dụng lại phép so sánh với việc "Đặt hàng Online".

waitUntil: 'load' (Tùy chọn Mặc định)

Phép so sánh: "Lắp Ráp Hoàn Chỉnh".

Sản phẩm đã được giao đến, bạn đã lắp ráp xong tất cả các bộ phận, dán decal trang trí. Sản phẩm bây giờ trông giống hệt như hình ảnh quảng cáo.

Nó chờ đợi điều gì? Đây là tùy chọn nghiêm ngặt nhất. Nó chờ cho đến khi sự kiện load của trang được kích hoạt. Điều này có nghĩa là toàn bộ tài liệu HTML và tất cả các tài nguyên phụ thuộc (stylesheet, JavaScript, iframes và quan trọng nhất là hình ảnh) đã được tải xuống hoàn toàn.

Khi nào nên dùng?

Đây là tùy chọn mặc định, an toàn nhất cho các trang web truyền thống (server-rendered).

Khi kịch bản test của bạn phụ thuộc vào việc các hình ảnh đã được tải (ví dụ: kiểm tra kích thước của một banner) hoặc các script từ bên thứ ba đã chạy xong.

Ví dụ thực tế:

Kịch bản: Kiểm tra xem một ảnh logo có được tải và hiển thị đúng kích thước không.

Mã HTML:

<!DOCTYPE html>

<html lang="en">

<head><title>Load Demo</title></head>

<body>

    <h1>Welcome!</h1>

    <img src="https://playwright.dev/img/playwright-logo.svg" alt="logo" id="logo" width="200">

</body>

</html>

Kịch bản Test:

test('should wait for image to load with default "load" state', async ({ page }) => {

  const html = `...`; // Dán code HTML ở trên
  await page.setContent(html);
  // Không cần chỉ định waitUntil, mặc định là 'load'

  // await page.goto(...);
  const logo = page.locator('#logo');

  const boundingBox = await logo.boundingBox();

  // Vì waitUntil: 'load' đã chờ ảnh tải xong,

  // chúng ta có thể tự tin kiểm tra kích thước của nó.

  expect(boundingBox.width).toBe(200);

});

 

waitUntil: 'domcontentloaded'

Phép so sánh: "Giao Hàng & Mở Hộp".

Người giao hàng đã mang gói hàng đến. Bạn mở hộp và thấy tất cả các bộ phận (thân tủ, ốc vít...). Mọi thứ đã ở đó, nhưng chưa được lắp ráp.

Nó chờ đợi điều gì? Nó chờ cho đến khi sự kiện DOMContentLoaded được kích hoạt. Điều này có nghĩa là file HTML đã được tải xuống và phân tích cú pháp hoàn toàn thành một cây DOM. Kịch bản test có thể bắt đầu tìm và tương tác với các phần tử. Nó không chờ stylesheet, hình ảnh, hay iframes.

Khi nào nên dùng?

Để tối ưu hóa tốc độ. Đây là lựa chọn tuyệt vời cho hầu hết các kịch bản test tương tác với form.

Khi bạn cần điền vào các ô input, click vào các nút bấm ngay khi chúng tồn tại trong cấu trúc HTML, không cần đợi trang phải "đẹp".

Ví dụ thực tế:

Kịch bản: Nhanh chóng điền vào form đăng nhập trên một trang có ảnh nền nặng.

Mã HTML:

<!DOCTYPE html>

<html lang="en">

<head><title>DOM Content Loaded Demo</title></head>

<body style="background-image: url('https://source.unsplash.com/random/1920x1080');">

    <input type="text" aria-label="username">

    <button>Login</button>

</body>

</html>

Kịch bản Test:

test('should fill form quickly with "domcontentloaded"', async ({ page }) => {

  const html = `...`; // Dán code HTML ở trên

  // Chúng ta sẽ ghi đè hành vi mặc định

  await page.setContent(html, { waitUntil: 'domcontentloaded' });




  // Test sẽ bắt đầu điền form ngay lập tức,

  // không cần chờ ảnh nền 1920x1080 tải xong.

  await page.getByLabel('username').fill('Gemini');

  await page.getByRole('button', { name: 'Login' }).click();

});

 

waitUntil: 'commit'

Phép so sánh: "Nhận Email Xác Nhận".

Bạn vừa đặt hàng xong và nhận được email xác nhận ngay lập tức. Bạn chưa có sản phẩm, nhưng bạn có sự xác nhận rằng yêu cầu của bạn đã được server xử lý, cùng với các thông tin meta (mã đơn hàng, trạng thái...).

Nó chờ đợi điều gì? Đây là tùy chọn chờ ít nhất. Nó chỉ chờ cho đến khi trình duyệt nhận được các phản hồi headers từ server. Tại thời điểm này, trình duyệt "cam kết" (commit) sẽ bắt đầu render trang, nhưng gần như chưa có nội dung gì được hiển thị.

Khi nào nên dùng?

Cho các trường hợp sử dụng chuyên biệt, khi bạn không quan tâm đến UI.

Kiểm tra "sức khỏe" (health check): Chỉ cần biết server có trả về 200 OK hay không.

Kiểm tra các HTTP headers của response (ví dụ: headers bảo mật, caching).

Kiểm tra các lệnh chuyển hướng (redirects).

Ví dụ thực tế:

Kịch bản: Kiểm tra xem trang có trả về đúng mã trạng thái 200 hay không.

Kịch bản Test:

test('should verify server response status with "commit"', async ({ page }) => {

  // page.goto() trả về một đối tượng response

  const response = await page.goto('https://playwright.dev', { waitUntil: 'commit' });




  // Ngay tại thời điểm 'commit', chúng ta đã có thông tin về response.

  // Test không cần chờ cả trang render.

  expect(response.status()).toBe(200);

});

 

waitUntil: 'networkidle' (KHÔNG KHUYẾN KHÍCH)

Phép so sánh: "Chờ Thiết Bị Thông Minh Tự Cập Nhật".

Bạn đã lắp ráp xong. Bây giờ bạn cắm điện và thấy các đèn hiệu nhấp nháy liên tục khi nó kết nối mạng để tải cập nhật. Bạn đứng chờ cho đến khi tất cả các đèn hiệu ngừng nhấp nháy.

Nó chờ đợi điều gì? Nó chờ cho đến khi không có hoạt động mạng nào (network connections) diễn ra trong ít nhất 500ms.

Khi nào nên dùng?

Lưu ý: Đội ngũ Playwright không khuyến khích sử dụng tùy chọn này cho việc chờ đợi UI. Triết lý hiện đại là hãy dùng "Web-First Assertions" (expect(locator)...) để chờ trực tiếp phần tử bạn cần.

Tuy nhiên, nó vẫn hữu ích trong các trường hợp hẹp:

Kiểm thử các hành vi ngầm không có UI (gửi sự kiện analytics, tracking).

Kiểm thử tính năng tự động lưu (autosave).

Chờ một "cơn bão" request API lắng xuống trước khi chụp ảnh màn hình một trang dashboard phức tạp.

Ví dụ thực tế:

Kịch bản: Kiểm thử tính năng autosave.

Mã HTML:

<!DOCTYPE html>

<html><body>

    <textarea id="editor" placeholder="Start typing..."></textarea>

    <script>

        let timeoutId;

        document.getElementById('editor').addEventListener('input', () => {

            clearTimeout(timeoutId);

            // Giả lập gửi request 'save' sau khi người dùng ngừng gõ 1 giây

            timeoutId = setTimeout(() => {

                console.log('Saving data...');

                fetch('/api/save', { method: 'POST', body: 'data' });

            }, 1000);

        });

    </script>

</body></html>

Kịch bản Test:

test('should wait for autosave to finish with "networkidle"', async ({ page }) => {

  // Giả lập API để bắt request

  await page.route('/api/save', route => route.fulfill({ status: 200 }));

  const html = `...`; // Dán code HTML ở trên

  await page.setContent(html);
  await page.locator('#editor').fill('My important note');

  // Logic của ứng dụng là "lưu sau khi người dùng nghỉ".

  // Vì vậy, logic của test là "chờ cho đến khi mạng lưới nghỉ".

  await page.waitForLoadState('networkidle');

  // Tại đây, chúng ta có thể tự tin rằng request 'save' đã được gửi đi.

  // (Trong test thực tế, ta có thể dùng page.waitForRequest để chắc chắn hơn).

});

Triết lý Hiện đại: waitUntil kết hợp với Web-First Assertions

Cách làm hiệu quả và đáng tin cậy nhất hiện nay là:

Sử dụng một tùy chọn waitUntil nhanh, thường là 'domcontentloaded', để tải cấu trúc trang.

Ngay sau đó, sử dụng một "Web-First Assertion" (expect(locator)...) để chờ đợi một phần tử quan trọng, đại diện cho trạng thái "sẵn sàng" của trang từ góc nhìn người dùng.

test('The Modern Approach', async ({ page }) => {

  // Bước 1: Điều hướng nhanh

  await page.goto('https://my-react-app.com', { waitUntil: 'domcontentloaded' });

  // Bước 2: Chờ đợi tường minh cho một phần tử cốt lõi

  // Đây là "chốt chặn an toàn" của bạn.

  await expect(page.getByRole('main')).toBeVisible();

  // Bây giờ, trang đã thực sự sẵn sàng để tương tác.

});

Bằng cách làm chủ 4 tùy chọn waitUntil và kết hợp chúng với Web-First Assertions, bạn sẽ có toàn quyền kiểm soát luồng chạy của kịch bản test, giúp chúng trở nên nhanh, hiệu quả và cực kỳ ổn định.

 

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