【问题标题】:Cypress wait for an element to be loaded inside a foor loop赛普拉斯等待在 for 循环中加载元素
【发布时间】:2020-07-02 10:58:27
【问题描述】:

我有一个数组,其中的项目是一堆按钮元素。我想调用一个按钮,然后等待加载一个元素(在 dom 中)。

加载该元素后,我只想进行下一次迭代(单击下一个按钮/元素)

在我当前的实现中,一次单击所有按钮。但我希望一次单击一个按钮。

  cy.document().then(document => {
      const arra = [...document.querySelectorAll('.instances__action')];

      for (let i = 1; i <= arra.length; i++) {
        let state = document.querySelector(
          `#root > section > section > main > div > div > section.instances > div > div > div > div > div > div > table > tbody > tr:nth-child(${i}) > td:nth-child(3) > span`
        ).innerText;

        document
          .querySelector(
            `#root > section > section > main > div > div > section.instances > div > div > div > div > div > div > table > tbody > tr:nth-child(${i}) > td:nth-child(7) > div > button.ant-btn.ant-btn-primary.ant-btn-sm`
          )
          .click();
        cy.wait(2000);
        cy.waitUntil(() => {
          cy.get(
            `#root > section > section > main > div > div > section.instances > div > div > div > div > div > div > table > tbody > tr:nth-child(${i +
              1}) > td:nth-child(3) > span`
          ).contains('Finished');
        });
      }
    });
  });

【问题讨论】:

  • 这能回答你的问题吗? Cypress IO- Writing a For Loop
  • 好吧,它不会确保在第一个按钮之后调用第二个按钮。因为它本质上是异步的
  • 总的来说,我认为如果你使用 Cypress 提供的结构而不是普通的 javascript,你会取得更大的成功。

标签: javascript for-loop cypress browser-automation ui-testing


【解决方案1】:

@jmargolisvt 是正确的,如果您将.click() 操作更改为赛普拉斯命令,您应该得到正确的操作顺序。

命令执行顺序和测试循环

可以这样想——上面的测试代码像普通的javascript一样运行,每个cy.X()命令都会将一个命令放入队列中。命令保证按队列顺序顺序运行,但不与测试代码同步。

所以,cy.wait(2000) 并没有减慢 for 循环,而是暂停了队列执行。

等待断言

还要注意,命令具有内置的等待断言,因此在这种情况下您可能不需要cy.wait()cy.waitUntil()。您可以使用timeout 选项控制最大等待时间。

循环

如果您将内部部分转换为命令,for 循环将正常工作,但您也可以将循环本身转换为 IMO 更简洁的 cy.get().each() 命令。

测试异步内容

最后,在等待异步内容时,首选cy.contains('mySelector', 'myContent') 命令而不是cy.get('mySelector').contains('myContent') 命令。

原因是cy.get('mySelector').contains('myContent') 只等待元素mySelector,但它已经在DOM 中。如果内容是异步变化的,这个命令会立即测试旧的内容(假设为空)并且测试失败。

const tbodySelector = '#root > section > section > main > div > div > section.instances > div > div > div > div > div > div > table > tbody';
const buttonSelector = 'td:nth-child(7) > div > button.ant-btn.ant-btn-primary.ant-btn-sm';
const spanSelector = 'td:nth-child(3) > span'

cy.get('.instances__action').each(($el, i) => {

    cy.get(`${tbodySelector} > tr:nth-child(${i+1}) > ${buttonSelector}`)
      .click();

    cy.contains(
      `${tbodySelector} > tr:nth-child(${i+1}) > ${spanSelector}`, 
      'Finished', 
      { timeout: 2000 }  // increase timeout if a longer wait is required
    );  

});

注意 jQuery 的 :nth-child(index) 选择器从索引 1 开始,但 .each(($el, i) =&gt; 具有从零开始的索引,因此必须在这些选择器中使用 i+1


这是我用来测试的模拟 DOM。在一个干净的 Cypress 项目中,将其放入文件夹 &lt;project root&gt;/app

<table>
  <tbody>
    <tr class="instances__action">
      <td>
        <button id="myBtn1"></button>
      </td>
      <td>
        <span id="mySpan1"></span>
      </td>
    </tr>

    <tr class="instances__action">
      <td>
        <button id="myBtn2"></button>
      </td>
      <td>
        <span id="mySpan2"></span>
      </td>
    </tr>
  </tbody>
</table>
<script>
  document.getElementById("myBtn1").addEventListener("click", function() {
    setTimeout(() => {
      document.getElementById("mySpan1").innerHTML = "Finished";
    }, 1000)
  });
  document.getElementById("myBtn2").addEventListener("click", function() {
    setTimeout(() => {
      document.getElementById("mySpan2").innerHTML = "Finished";
    }, 500)
  });
</script>

注意上面的嵌套是简化的,所以我适当地改变了选择器内容。

测试上面的 HTML 片段

it('clicks buttons and waits for finished flag', () => {

  cy.visit('app/iterate-table-buttons.html')

  const tbodySelector = 'tbody';
  const buttonSelector = 'td:nth-child(1) > button';
  const spanSelector = 'td:nth-child(2) > span'

  cy.get('.instances__action').each(($el, i) => {

      console.log(`starting #${i}`)

      cy.get(`${tbodySelector} > tr:nth-child(${i+1}) > ${buttonSelector}`)
        .then(() => console.log(`clicking #${i}`))
        .click();

      cy.contains(
        `${tbodySelector} > tr:nth-child(${i+1}) > ${spanSelector}`, 
        'Finished',
        { timeout: 2000 }  // increase timeout if a longer wait is required
      )
      .then(() => console.log(`asserting #${i}`))

  });
})

赛普拉斯日志

这就是赛普拉斯日志的样子。
全部为绿色,并且只有在 Finished 文本出现后才能看到点击。

  1. 访问 app/iterate-table-buttons.html
  2. GET .instances__action
  3. 获取 tbody > tr:nth-child(1) > td:nth-child(1) > 按钮
  4. 点击
  5. 包含 tbody > tr:nth-child(1) > td:nth-child(2) > span,已完成
  6. 获取 tbody > tr:nth-child(2) > td:nth-child(1) > 按钮
  7. 点击
  8. 包含 tbody > tr:nth-child(2) > td:nth-child(2) > span,已完成

控制台日志

这就是控制台日志的样子。

循环在命令队列开始之前执行完成,但这没关系,因为命令仍在以正确的顺序执行。

从#0开始
开始 #1
点击 #0
断言 #0
点击 #1
断言 #1

【讨论】:

  • 我试过这个,但同样的问题仍然存在。它单击开始按钮,然后在完成操作之前单击下一个开始按钮。我的意思是选择器不包含finished,但它仍然会转到下一个开始按钮并单击它!
  • 更奇怪的是,如果我在 contains 中输入错误的文本,例如我用 Finishes 代替 finished,它会单击第一个按钮,然后查找 @987654342 @它没有找到并测试崩溃。但是当我使用正确的文本时,它并没有等到 finished 被加载到 dom 中!而是转到循环的下一次迭代
  • 它在我认为你拥有的 DOM 模型中对我有用。我会将其发布在答案下方,您可以将其与您拥有的现场场景进行比较。
猜你喜欢
  • 1970-01-01
  • 2022-10-02
  • 2019-03-15
  • 2022-12-19
  • 1970-01-01
  • 1970-01-01
  • 2019-02-12
  • 2020-05-30
相关资源
最近更新 更多