問題点
PuppeteerによるWebテストの自動化を行うと、HTML等のWebページ遷移が発生しないSingle Page Application(SPA)が正常に動作しない場合があります。特にGithubのPuppeteerのREADME.mdにある使用例をそのまま流用するとハマる可能性があります。
例えば、以下のようにCSSセレクターが見つからないエラーだったり
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): AssertionError [ERR_ASSERTION]: No node found for selector: ...
画面が遷移したにも関わらず以下のようにタイムアウトが発生してしまうエラーだったりします。
UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 1): Error: Navigation Timeout Exceeded: 30000ms exceeded
対処方法
Single Page ApplicationのWebページの場合、以下の点を考慮する必要があるようです。
- page.goto()の後は、動的生成されるHTMLタグのCSSセレクタが登場するまで待機する。
- page.goto()のオプションで
{waitUntil: 'networkidle'}
を指定する。 - またはpage.waitFor(), page.waitForSelector()などで待機する。
- page.goto()のオプションで
- page.click()で画面が遷移した後に待機を行う場合は、page.waitForNavigation()を使用しない。
- page.waitFor(), page.waitForSelector()などで待機する。
- page.waitForNavigation()はあるWebページから別のWebページに移動するまで待機する関数のため、Single Page Applicationのように同じWebページに留まる場合は待機タイムアウトする。
VueによるSPAデモページにアクセスして操作を行い、画像キャプチャをとるサンプルコードを以下に示します。
const puppeteer = require('puppeteer');
async function spa_access() {
const browser = await puppeteer.launch({headless: true});
const page = await browser.newPage();
await page.goto('https://skyronic.github.io/vue-spa/#/', {waitUntil: 'networkidle'});
await page.screenshot({path: 'image-spa-top.png', fullPage: true});
await page.click('body > div > div.page > div.product-container > div:nth-child(2) > a')
//await page.waitFor(1000) // waitFor()
await page.waitForSelector('body > div > div.page > div.product-item > a') // waitForSelector()
await page.screenshot({path: 'image-spa-clicked.png', fullPage: true});
await browser.close();
}
確認環境: Windows10, Node.js=v8.50, Puppeteer=v0.11.0
(おまけ) WindowsでPuppeteerのWebアクセス等がタイムアウトする場合
Chrome 60以降でWindowsもヘッドレスChromeに対応しているはずなのですが、Chromium 60以降を使用しているPuppeteerであっても何故か https://www.example.com 以外へのアクセスでタイムアウトしてしまう問題が発生する場合があるようです。その場合にはpuppeteer.launch()の関数のオプションとして {headless: false}
を追加してヘッドレスモードを無効にして動作させるとタイムアウトを回避できる場合があるようです。
ヘッドレスモードではなくてもWindowsでPuppeteerを動かしたい場合は試してください。