navigateWithRedirectWait.mjs

//等待轉址與networkidle之最長毫秒
let REDIRECT_WAIT_MS = 10000
let NETWORK_IDLE_WAIT_MS = 10000


/**
 * 導航至網址並等待JS轉址完成
 *
 * 先以domcontentloaded導航,再等待網址host脫離原host(代表已轉址),最後等待networkidle;
 * 等待轉址與networkidle皆為盡力而為,逾時不視為失敗
 *
 * @param {Object} page 輸入Playwright之page物件
 * @param {String} url 輸入待導航網址字串
 * @param {Integer} navTimeout 輸入導航最長等待毫秒整數
 * @returns {Promise} 回傳Promise,resolve回傳首次導航之Response物件(供呼叫端檢核HTTP狀態),同頁錨點導航等情形可能為null
 * @example
 *
 * import { chromium } from 'playwright'
 * import navigateWithRedirectWait from './src/navigateWithRedirectWait.mjs'
 *
 * let test = async () => {
 *
 *     let browser = await chromium.launch({ headless: true, channel: 'chrome' })
 *     let page = await browser.newPage()
 *     await navigateWithRedirectWait(page, 'https://news.google.com/articles/xxxxxx', 15000)
 *     console.log(page.url())
 *     // => 'https://www.example-news.com/real-article'
 *     await browser.close()
 *
 * }
 * await test()
 *     .catch((err) => {
 *         console.log(err)
 *     })
 *
 */
async function navigateWithRedirectWait(page, url, navTimeout) {

    let originalHost = new URL(url).hostname

    //回傳首次導航之Response供呼叫端檢核HTTP狀態
    //轉址後之最終頁狀態不在此物件內: 本函數之轉址由頁面內JS發動, 其請求不經此次導航。
    //故本值代表「所要求之網址本身回了什麼」, 這正是要檢核的對象
    let resp = await page.goto(url, { waitUntil: 'domcontentloaded', timeout: navTimeout })

    await page
        .waitForURL((u) => !u.href.includes(originalHost), { timeout: REDIRECT_WAIT_MS })
        .catch(() => {})

    await page
        .waitForLoadState('networkidle', { timeout: NETWORK_IDLE_WAIT_MS })
        .catch(() => {})

    return resp
}


export default navigateWithRedirectWait