Playwright P11: Xử lý Dialog, iframe, upload và download

⏱︎

Read time:

2–4 minutes
Playwright P13: Dialog, iframe, upload và download

Bài này sẽ hướng dẫn xử lý những kịch bản nâng cao hơn 1 chút trong quá trình test web:

Dialog

Dưới đây là ví dụ về 1 dialog. Nó không phải HTML web, mà là do browser bắn ra ngoài màn hình, vì vậy cần có cách tiếp cận riêng:

page.once('dialog', async dialog => {
  console.log(dialog.message());    // in ra Hello word!
  await dialog.accept();    // Bấm vào OK trên dialog
});
await page.getByRole('button', { name: 'Xóa' }).click();

page.once('dialog', dialog => dialog.dismiss());
await page.getByRole('button', { name: 'Hủy tài khoản' }).click();

Phải đăng ký listener Playwright trước action mở dialog. Nếu có listener nhưng không accept/dismiss, action có thể bị kẹt. Khi không có listener, Playwright tự dismiss dialog.

Xử lý link mở tab mới với target="_blank"

Khi click vào link có target="_blank", trình duyệt mở một tab mới. Trong Playwright, tab đó là một object Page mới nhưng vẫn nằm trong cùng BrowserContext với page ban đầu.

Cần bắt đầu chờ sự kiện popup trước khi click. Nếu click trước rồi mới chờ, tab mới có thể đã mở và test sẽ bỏ lỡ sự kiện.

// Bắt đầu chờ tab mới trước khi click
const popupPromise = page.waitForEvent('popup');

await page.getByRole('link', { name: 'Mở tài liệu' }).click();

// popup là một Page mới trong cùng BrowserContext
const popup = await popupPromise;

await expect(popup).toHaveURL(/docs/);
await expect(
  popup.getByRole('heading', { name: 'Tài liệu hướng dẫn' })
).toBeVisible();

// Đóng tab mới khi không còn sử dụng
await popup.close();
Mẹo nhớ
page vẫn trỏ tới tab ban đầu; popup trỏ tới tab mới. Hai tab cùng context nên chia sẻ cookie, local storage và trạng thái đăng nhập.

Nếu không biết page nào đã tạo tab mới, có thể chờ ở cấp context bằng context.waitForEvent('page'). Với link được click trực tiếp từ page hiện tại, page.waitForEvent('popup') dễ hiểu và cụ thể hơn.

Iframe

Xử lý 1 trang web nhúng vào 1 trang web khác, hay gặp với cổng thanh toán:

// dùng page.frameLocator để chọc vào iframe:
const paymentFrame = page.frameLocator('#payment-frame');

await paymentFrame.getByLabel('Số thẻ').fill('4111111111111111');
await paymentFrame.getByRole('button', { name: 'Thanh toán' }).click();

Upload file

Sử dụng hàm setInputFiles() để trỏ đúng đến đối tượng HTML có thuộc tính type=file là xong:

import path from 'node:path';

await page.getByLabel('Chọn CV').setInputFiles(
 './data/files/cv.pdf')
);

Cả hai cách đều tương tác trực tiếp với <input type="file">, không điều khiển hộp thoại file của hệ điều hành.

Download file

// chuẩn bị hứng sự kiện download trước khi bấm vào nút Download
const downloadPromise = page.waitForEvent('download');
//bấm nút download:
await page.getByRole('button', { name: 'Tải báo cáo' }).click();
const download = await downloadPromise;

const suggestedName = download.suggestedFilename();
await download.saveAs(testInfo.outputPath(suggestedName));