玩笑测试通常会失败,因为Puppeteer无法单击DOM中的元素

时间:2019-08-10 02:57:19

标签: javascript angularjs jestjs puppeteer

问题摘要::我正在编写几个测试套件(使用Jest和Puppeteer)以自动化对AngularJS应用首页的测试。我要自动化的测试之一是用几个md-select字段填写表单,而在自动选择特定的md-option方面遇到了麻烦。由于某些无法解释的原因,似乎Puppeteer在单击md-select打开选项对话框后才找不到该选项。这是我的html外观,因此您可以了解我正在处理的内容:

免责声明: 为了向该社区提供minimal, reproducible example,我选择不复制并粘贴原始代码,而是写了一个简单的例子。因此,如果您发现了错字,我可以向您保证,这不是问题的根源,因为我已经非常仔细地检查了原始代码中的错字。对于以下代码中的任何拼写错误,我们深表歉意。我保持警惕,但我是人,所以我会犯错。

<!-- index.html -->
<html>
  <body ng-app="myApp" ng-controller="myCtrl">
    <form name="myForm">
      <md-dialog-content>
        <md-input-container id="animalSelect">
          <md-select>"Kittens"</md-select>
        </md-input-container>
        <md-input-container id="nameSelect">
          <md-select>"Cosmo"</md-select>
        </md-input-container>
      </md-dialog-content>
    </form>
    <!-- this dialog appears when first <md-select> is clicked -->
    <div class="md-select-menu-container">
      <md-select-menu>
        <md-content>
          <md-option value="Kittens">Kittens</md-option>
          <md-option value="Puppies">Puppies</md-option>
        </md-content>
      </md-select-menu>
    </div>
    <!-- this dialog appears when second <md-select> is clicked -->
    <div class="md-select-menu-container">
      <md-select-menu>
        <md-content>
          <md-option value="Cosmo">Cosmo</md-option>
          <md-option value="Scout">Scout</md-option>
        </md-content>
      </md-select-menu>
    </div>
  </body>
</html>

背景:我正在使用Jest(v24.8.0)作为测试框架。我正在使用Puppeteer(v1.19.0)启动并控制无头的Chromium浏览器。

我的破损代码:

// index.spec.js
test('submit form', async() => {

    let formSelector = 'form[name="myForm"]';
    let animalSelector = '#animalSelect md-select';
    let animalOptionSelector = 'md-option[value="Puppies"]';

    let nameSelector = '#nameSelect md-select';
    let nameOptionSelector = 'md-option[value="Scout"]';

    await page.waitForSelector(formSelector, {timeout: 3000}); 
    await page.waitForSelector(animalSelector, {timeout: 3000}); 
    await page.waitForSelector(nameSelector, {timeout: 3000}); 

    // this works fine
    await page.click(animalSelector);
    await page.waitForSelector(animalOptionSelector, {timeout: 3000}); 
    await page.click(animalOptionSelector);

    // this fails: Jest says nameOptionSelector is not in the DOM (see error below)
    await page.click(nameSelector);
    await page.waitForSelector(nameOptionSelector, {timeout: 3000}); 
    await page.click(nameOptionSelector);
}); 

运行测试的结果:

运行上述测试后,Jest说'submit form'失败并提供了以下错误消息:

Node is either not visible or not an HTMLElement

await page.click(nameOptionSelector);
           ^

这意味着当Puppeteer单击nameOptionSelector时,无法在DOM中找到它。但是page.waitForSelector(nameOptionSelector, {timeout: 3000})似乎已经成功了,这告诉我nameOptionSelector在调用page.click时在DOM中。

故障排除尝试:

我可以通过'submit form'的唯一方法是在page.click(nameOptionSelector)上方添加以下行:

// this line tells the test to pause for 1 second before clicking
await page.waitFor(1000);

...但是这是一个不好的解决方案,因为它不精确并且不能解决问题的根源。

我也了解了Puppeteer的page.select方法,但是由于我没有使用原生的HTML <select>元素,因此在我的情况下不起作用。

问题:你们那里的Jest / Puppeteer黑客是否知道解决方案?

2 个答案:

答案 0 :(得分:1)

不幸的是,我也遇到了类似的问题,因为您和await page.waitFor(3000)在我的情况下大多帮助了我。这是我用来不断向下滚动直到到达动态内容不断添加的页面底部的代码片段:

    let preCount = 0;
    let postCount = 0;
    do {
      preCount = await getCount(page);
      await scrollDown(page);
      await page.waitFor(3000);
      postCount = await getCount(page);
    } while (postCount > preCount);
    await page.waitFor(3000);

当我尝试将等待时间从3秒降低到1或2秒时,有时我无法进入页面底部。我知道这一点是因为我在启动puppeteer时禁用了无头模式,因此我可以查看浏览器中发生了什么:

{
  headless: false,
  defaultViewport: null,
  args: ['--window-size=800,600']
}

答案 1 :(得分:1)

考虑到它的角度应用程序,我假设有一些style disappear property之类的style="display: none"是由角度指令处理的(我不熟悉角度程序,所以我可能是错的)

在这种情况下,您的点击触发了事件,该事件会更改按钮->的状态,使其显示/消失(需要一些时间,因此page.waitFor函数似乎可以解决问题)。

page.waitForSlector(nameOptionSelector)立即解析,因为选择器已经存在(您可以通过在控制台中运行document.querySelector('md-option[value="Scout"]'),然后单击md-select并在其后检查它是否为真。在两种情况下,您都应该文档元素)

但是page.click()尝试单击不可见的元素时失败。因此,您可以尝试等待其他将正确设置angular属性的选择器。可能说明元素是否可见。

如果您的整个应用是用角度编写的,请考虑其他测试框架->例如。 protractor,因为它专用于有角度的应用程序