web-dev-qa-db-ja.com

puppeteer element.click()が機能せず、エラーをスローしません

フォーム上で、アニメーション表示されるボタンが表示される状況があります。アニメーションの進行中にelement.click()が発生すると、ボタンが機能しません。

element.click()はエラーをスローせず、失敗ステータスを返しません(未定義を返します)だけでサイレントに動作しません。

クリックされている要素が無効ではなく、表示されている(表示されている)ことを確認しようとしましたが、これらのテストが両方とも成功しても、クリックは失敗します。

クリックする前に0.4秒待つと、アニメーションが終了したので機能します。

クリックがいつ機能したかを検出でき、自動的に再試行しない場合は、遅延(信頼性が低く、率直に言うと、遅延)を追加する必要はありません。

Click()が実際に実行されたかどうかを検出する一般的な方法はありますか?そうなるまで再試行ループを使用できますか?

9
Austin France

何が起こっているのか、なぜエラーが発生しないのか、問題を回避する方法を決定しました。

主な問題は、element.click()の動作方法にあります。 _DEBUG="puppeteer:*"_を使用して、内部で何が行われているのかを確認できました。 element.click()が実際に行うことは:-

_const box = element.boundingBox();
const x = box.x + (box.width/2);
const y = box.y + (box.height/2);
page.mouse.move(x,y);
page.mouse.down();
sleep(delay);
page.mouse.up();
_

問題は、ビュー(div)が要素のboundingBox()をアニメーション化しているため、ボックスの位置を要求してからclick()を完了するまでの間に、要素が移動したかクリックできないということです。

エラーはスローされません(プロミスは拒否されます)。これは、ビューポート内のポイントをマウスでクリックするだけで、要素にリンクされていないためです。マウスイベントが送信されますが、何も応答しません。

回避策の1つは、アニメーションが終了するのに十分な遅延を追加することです。もう1つは、テスト中にアニメーションを無効にすることです。

私の解決策は、要素の位置が目的の位置に落ち着くのを待つことでした。つまり、boundingBox()のクエリをスピンし、x、yが以前に決定した要素を報告するのを待ちます。

私の場合、これはクリックの直前にテストスクリプトに_at 10,10_を追加するのと同じくらい簡単です。

_test-id "form1.button3" at 10,10 click
_

そして、実際には次のように機能します。この場合、ビューは左からアニメーション表示されています。

 00.571 [selector.test、61] at 8,410 
 test-id "main.add" info tag button displayed at -84,410 size 116,33 enabled not selected check "Add" 
 test-id "main.add"情報タグボタンが-11,410サイズで表示されます。116,33が選択されていません。選択されていません "Add" 
 test-id "main.add"情報タグボタンが8,410サイズで表示されました116,33有効選択されていませんチェック "追加" 
 00.947 [selector.test、61]クリック

継続的に移動している要素や、​​何かで覆われている要素には機能しません。そのような場合は、page.evaluate(el => el.click(), element)を試してください。

11
Austin France

Page.click()はpromiseを返します。promiseとして処理するようにしてください。また、xpathとして参照していない場合は問題が発生する可能性があることに注意してください。それが機能するために私がしなければならなかったことです。 querySelectorsを使用してそのようにオブジェクトを操作しようとしましたが、問題が発生しました。

page.evaluate(()=>{
await Promise.all([
    page.click("a[id=tab-default-2__item]"),
    //The page.waitFor is set to 15000 for my personal use. 
    //Feel free to play around with this.
    page.waitFor(15000)
]);
});    

これがお役に立てば幸いです。

1
ElementCR

アンドレアの答えに触発されたタイムアウト機能付きの一般的なクリック。これは要素がクリック可能になるとすぐに戻るため、テストの速度が低下することはありません。

click: async function (page, selector, timeout = 30000) {

    await page.waitForSelector(selector, { visible: true, timeout })

    let error;
    while (timeout > 0) {
        try {
            await page.click(selector);
            return;
        } catch (e) {
            await page.waitFor(100);
            timeout -= 100;
            error = e;
        }
    }
    throw err;
}
0
Austin France

ヘルパー関数を使用してクリックを処理します


    click: async function (page, selector) {

        //selector must to exists
        await page.waitForSelector(selector, {visible: true, timeout: 30000})
        //give time to extra rendering time
        await page.waitFor(500)

        try {
            await page.click(selector)
        } catch (error) {
          console.log("error clicking " + selector + " : " + error ;
        }
    }

page.waitFor(500)の使用は理論的な世界では非常に悪い慣行ですが、複雑なインターフェースを備えた実用的な方法では、多くの誤検知を排除します。

誤検知を取得するよりも500ミリ秒待機することを好みます。

0
Andrea Bisello