【问题标题】:puppeteer element.click() not working and not throwing an errorpuppeteer element.click() 不工作且不抛出错误
【发布时间】:2018-04-23 10:39:51
【问题描述】:

我有一种情况,表单上的按钮以动画形式显示在视图中,如果 element.click() 在动画进行时发生,则它不起作用。

element.click() 不会抛出错误,不会返回失败状态(它返回未定义),它只是默默地不起作用。

我已尝试确保被单击的元素未被禁用,并且显示(可见),但即使这两个测试都成功,单击也会失败。

如果我在点击之前等待 0.4 秒,它可以工作,因为动画已经完成。

如果我可以检测到点击何时有效,并且如果不能自动重试,我不想增加延迟(这是不可靠的,坦率地说是一个错误)。

是否有一种通用的方法来检测 click() 是否实际已被执行,以便我可以使用重试循环直到它执行?

【问题讨论】:

  • 不是 100% 相同,但相关:github.com/GoogleChrome/puppeteer/issues/2107(相关,因为该解决方案可能会解决您的情况)
  • 您在page.click('button'); 之前尝试过await page.waitForSelector('button'); 也许它有帮助。但不是很确定。
  • 另一种方法是将动画 css buster 注入页面,例如marcgg.com/blog/2015/01/05/…
  • @Bernhard 是的,代码就是这样做的。 page.waitForSelector(选择器);元素 = page.$(选择器); element.click();

标签: puppeteer


【解决方案1】:

我已经确定发生了什么,为什么我没有收到错误,以及如何解决这个问题。

主要问题在于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() 之间,元素已移动或不可点击。

不会引发错误(承诺被拒绝),因为它只是在视口中的某个点上单击鼠标,并且没有链接到任何元素。鼠标事件被发送,只是没有任何响应。

一种解决方法是添加足够的延迟以使动画完成。另一个是在测试期间禁用动画。

我的解决方案是等待元素的位置稳定在其目标位置,即我旋转查询 boundingBox() 并等待 x,y 报告元素先前确定的位置。

就我而言,这就像在点击之前将at 10,10 添加到我的测试脚本中一样简单,或者特别是

test-id "form1.button3" at 10,10 click

实际上,它的工作原理如下,在这种情况下,视图是从左侧动画回来的。

00.571 [selector.test,61] 在 8,410 test-id "main.add" 信息标签按钮显示在 -84,410 大小 116,33 启用 未选中 检查 "添加" test-id "main.add" 信息标签按钮显示在 -11,410 大小 116,33 启用 未选中 检查 "添加" test-id "main.add" 信息标签按钮显示在 8,410 大小 116,33 启用 未选中 检查 "添加" 00.947 [selector.test,61] 点击

它不适用于不断移动的元素或被其他东西覆盖的元素。对于这些情况,请尝试page.evaluate(el => el.click(), element)

【讨论】:

  • 从这个答案中,我得到了 2 个收获:page.evaluate(el => el.click(), element) 并禁用了过渡/动画。谢谢。
【解决方案2】:

受 Andrea 的回答启发的具有超时功能的通用点击。一旦元素可点击,这个就会返回,因此不会减慢测试速度。

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 error;
}

【讨论】:

    【解决方案3】:

    我刚刚遇到了一个非常相似的问题:我曾经有过 Puppeteer 脚本,但现在突然单击停止工作。罪魁祸首原来是缩放级别。一旦我将缩放切换到 100%,它就会再次开始工作。显然,Puppeteer 不会将点击坐标调整为缩放级别。

    【讨论】:

      【解决方案4】:

      我使用辅助函数来处理点击

      
          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 毫秒以上而不是获得误报。

      【讨论】:

      • 不幸的是,由于所有不必要的 1/2 秒等待,您的测试速度大大减慢。在我的例子中,我使用了有针对性的等待,因为我知道在某些点我需要等待一个 400 毫秒的动画,其他点击不需要等待,并且很容易添加到我的脚本中。等待最终位置或某个已知状态总是最好的解决方案,因为它不依赖于延迟,这通常是不好的,应该避免。
      • 澄清一下,如果这是一个非常好的解决方案,例如 anim_click 方法,那么在我看来每次点击都不是一个好主意。
      • 我同意奥斯汀的观点; Puppeteer's own readme 说“Puppeteer 具有事件驱动的架构,它消除了很多潜在的脆弱性。在 puppeteer 脚本中不需要邪恶的 sleep(1000) 调用。”
      • @AustinFrance 我完全同意你的观点,但有时在非常繁重的 web 应用程序中,使用巨大的 html 页面开发的很糟糕,这是无法完成的,需要强制休眠。
      【解决方案5】:

      page.click() 返回一个 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)
      ]);
      });    
      

      我希望这会有所帮助。

      【讨论】:

      • 我正在使用等待,因此将拒绝作为错误抛出,返回值是传递给解析的值。事实证明,这是 element.click() 工作方式的限制。 (见下面我的回答)
      【解决方案6】:

      对我来说,下面的代码成功了

      const element = await page.$('[name="submit"]')
      await this.page.evaluate(ele => ele.click(), element);
      

      【讨论】:

      • 这与this answer基本相同,只是没有解释。这提供了什么新的见解?顺便说一句,您可以使用element.evaluate(ele => ele.click())
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-10-11
      相关资源
      最近更新 更多