備考
- 文章は人間が一生懸命タイピングしてます。
経緯
PlaywightでTurnstileを使った認証ページをテストする時に、Turnstileのiframe取得がうまくいかないことがありました。結果として、適当に数秒待つというコードを使ってテストの実行時間が無駄に伸びている時がありました。そこで、TurnstileがSuccesになるまで待機するというコードを書いたので共有します。
コード
/** * Cloudflare Turnstileのiframeが出現するまで待機する関数 */ async function waitForTurnstileFrame( page: Page, timeout = 20000, ): Promise<Frame> { console.log("Waiting for Turnstile iframe to appear (polling)..."); const startTime = Date.now(); while (Date.now() - startTime < timeout) { const frame = page.frames().find((f) => f.url().includes("challenges.cloudflare.com")); if (frame) { console.log("Found Turnstile frame via polling."); return frame; } await page.waitForTimeout(500); // 500ms待機 } throw new Error(`Turnstile frame not found within ${timeout}ms`); } /** * Turnstileのトークンが生成され、inputに格納されるまで待機して値を返す関数 */ async function waitForTurnstileToken( page: Page, timeout = 30000, ): Promise<string> { console.log("Waiting for Turnstile token to be generated..."); const tokenInput = page.locator('input[id^="cf-chl-widget-"][id$="_response"]'); // 要素がDOMに出現するまで待機 await tokenInput.waitFor({ state: "attached", timeout }); // 値が空でなくなるまで待機 await page.waitForFunction( (el) => (el as HTMLInputElement).value !== "", await tokenInput.elementHandle(), { timeout }, ); const token = await tokenInput.inputValue(); return token; } /** * Turnstileが出てきてからトークン取得までをまとめて行う関数 */ export async function getTurnstileToken(page: Page, timeout = 30000): Promise<string> { // 1. iframeが出現するまで待機 await waitForTurnstileFrame(page, timeout); // 2. トークンが生成されるまで待機して取得 return await waitForTurnstileToken(page, timeout); }
短いですが以上です。