Wiki

← Tất cả bài wiki

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ọi console.log/warn/error trong trang emit ra test dưới dạng ConsoleMessage — 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ặp Promise.all vớ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 qua await 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ỘC dialog.accept() hoặc dismiss() trong handler, không thì trang treo.
  • Event hữu ích khác: 'download', 'popup', 'close', 'crash', 'filechooser', 'weberror' (service worker error — trên browserContext).

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.
  • waitForResponse cho 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 — check response.status(); failed là net::ERR_*, abort, timeout.
  • pageerror chỉ 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ải await 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.off nếu gắn lâu dài.
  • Service worker lỗi không qua pageerror: dùng browserContext.on('weberror') cho lỗi worker.
  • Dialog không handle → treo: page.on('dialog') nhận được event nhưng quên accept()/dismiss() = trang chờ mãi → test timeout khó hiểu.