Page Events: console, pageerror, requestfailed (Bắt tín hiệu và lỗi từ trang)
automationplaywright
page là EventEmitter: console (log của trang), pageerror (uncaught exception), requestfailed, response, dialog... Lắng nghe bằng page.on() để bắt lỗi JS âm thầm — những lỗi trang 'trông OK' trên UI nhưng script đã crash, thứ mà UI assertion không bao giờ thấy.
Khái niệm & Bản chất
page.on('console'): mọiconsole.log/warn/errortrong trang emit ra test dưới dạngConsoleMessage— inspect log trang mà không mở DevTools.page.on('pageerror'): uncaught exception + unhandled promise rejection trong trang — lỗi này KHÔNG hiện UI, trang vẫn render nhưng tính năng đã chết ngầm.page.on('requestfailed'): request fail ở tầng network (DNS lỗi, TLS, abort, timeout) — LƯU Ý: HTTP 4xx/5xx KHÔNG phải 'failed', chúng là response thành công; check status qua'response'event.- Hai pattern:
page.on= lắng nghe liên tục (audit/log/fail-on-error);page.waitForEvent/waitForResponse= đợi 1 event cụ thể, hay đi cặpPromise.allvới action gây ra event.
Cách hoạt động & API chính
- Lifecycle listener:
page.on(event, handler),page.once,page.off/removeListener— listener sống theo page, đăng ký trong fixture thì áp cho cả test. - ConsoleMessage API:
msg.type()('log'|'warning'|'error'|...),msg.text(),msg.location()→{ url, lineNumber, columnNumber },msg.args()→JSHandle[](giá trị thật quaawait arg.jsonValue()). - Response/Request events:
page.on('response', r => r.status())audit API;page.on('requestfailed', r => r.failure()?.errorText)lấy lý do fail. page.on('dialog'): alert/confirm/prompt — BẮT BUỘCdialog.accept()hoặcdismiss()trong handler, không thì trang treo.- Event hữu ích khác:
'download','popup','close','crash','filechooser','weberror'(service worker error — trênbrowserContext).
Ví dụ & Cú pháp thực tế
- Fail test nếu trang có BẤT KỲ JS error nào:
``
ts test('trang không ném exception', async ({ page }) => { const pageErrors: Error[] = []; page.on('pageerror', e => pageErrors.push(e)); await page.goto('/checkout'); await page.getByRole('button', { name: 'Thanh toán' }).click(); await page.waitForLoadState('networkidle'); expect(pageErrors).toEqual([]); // trang đẹp nhưng JS crash vẫn fail });`` - Lọc console error, whitelist nguồn 3rd-party:
``
ts page.on('console', msg => { const { url } = msg.location(); if (msg.type() === 'error' && !url.includes('analytics.js')) { console.error('PAGE ERROR:', msg.text(), 'at', url); } });`` - Promise.all — đợi response do chính action gây ra:
``
ts const [resp] = await Promise.all([ page.waitForResponse(r => r.url().includes('/api/order') && r.status() === 200), page.getByRole('button', { name: 'Đặt hàng' }).click(), ]); const body = await resp.json(); expect(body.orderId).toBeTruthy();`` - Bắt dialog confirm khi xoá:
``
ts page.on('dialog', d => void d.accept()); // không handle = treo trang await page.getByRole('button', { name: 'Xoá' }).click();``
Best practices & Khi nào dùng
- Fixture 'fail on pageerror' cho toàn bộ suite: đặt trong
test.extend— mọi test tự động bị bắt khi app ném uncaught error, không cần lặp code. - Whitelist có chọn lọc: error từ analytics/ads/extension đã biết → log warning thay vì im lặng hoặc fail cả test.
- Promise.all cho event do action gây ra: click → response/download/popup; gắn listener SAU click là race — event fire trước khi kịp đợi.
waitForResponsecho assertion 1 lần,page.on('response')cho audit suốt flow: hai công cụ hai việc — đừng dùng on('response') + flag để làm việc của waitForResponse.- Log console ra artifact: đẩy pageErrors/console errors vào
test.info().attach()— khi test fail trên CI có ngay bằng chứng.
Gotchas, So sánh & Lưu ý
requestfailed≠ HTTP 500: 500 vẫn 'response thành công' tầng network — checkresponse.status(); failed lànet::ERR_*, abort, timeout.pageerrorchỉ bắt uncaught: lỗi đã try/catch trong app sẽ im — muốn thấy thì phải đọc console hoặc expose binding từ app.msg.args()là JSHandle, không phải value:msg.text()cho repr string; cần object thật phảiawait arg.jsonValue()— quên await là bug khó thấy.- Listener leak giữa test: listener gắn vào page sống hết vòng đời page — buffer array nên tạo local per test, cleanup bằng
page.offnếu gắn lâu dài. - Service worker lỗi không qua
pageerror: dùngbrowserContext.on('weberror')cho lỗi worker. - Dialog không handle → treo:
page.on('dialog')nhận được event nhưng quênaccept()/dismiss()= trang chờ mãi → test timeout khó hiểu.
