简体   繁体   中英

Cypress wait for an element to be loaded inside a foor loop

I have an array, which items are a bunch of button elements. I want to call a button then wait for an element(which is in the dom) to be loaded.

after that element loaded, then only I want to go to the next iteration (click on the next button/element)

In my current implementation, all buttons are clicked at a time. but I want one button should be clicked at a time.

  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');
        });
      }
    });
  });

@jmargolisvt is correct, if you change the .click() operation to a Cypress command, you should get the correct sequence of actions.

Command execution sequence and test loops

Think of it like this - the test code above runs as normal javascript, and each cy.X() command puts a command into the queue. Commands are guaranteed to run sequentially in the queue order, but not synchronously with the test code.

So, the the cy.wait(2000) is not slowing down the for-loop, it's pausing the queue execution.

Waiting for assertions

Note also that commands have built-in waits for their assertions, so you probably don't need either the cy.wait() or the cy.waitUntil() in this scenario. You can control the maximum time for waiting with a timeout option.

Looping

The for loop will work ok if you convert the inner parts to commands, but you can also convert the loop itself to a cy.get().each() command which IMO is much neater.

Testing async content

Lastly, prefer cy.contains('mySelector', 'myContent') command to cy.get('mySelector').contains('myContent') command when waiting for async content.

The reason is cy.get('mySelector').contains('myContent') only waits on the element mySelector , but it's already in the DOM. If the content is asynchronously changing, this command will test the old content (presume empty) immediately and fail the test.

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
    );  

});

NOTE jQuery's :nth-child(index) selector starts at index 1, but .each(($el, i) => has zero-based indexes, so must use i+1 within these selectors.


This is the mock DOM I used to test with. In a clean Cypress project, put it in folder <project root>/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>

Note the nesting above is simplified, so I changed the selector contants appropriately.

Testing the above HTML fragment

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}`))

  });
})

Cypress log

This is what the Cypress log looks like.
All green, and can see the clicks only happen after the Finished text appears.

  1. VISIT app/iterate-table-buttons.html
  2. GET .instances__action
  3. GET tbody > tr:nth-child(1) > td:nth-child(1) > button
  4. CLICK
  5. CONTAINS tbody > tr:nth-child(1) > td:nth-child(2) > span, Finished
  6. GET tbody > tr:nth-child(2) > td:nth-child(1) > button
  7. CLICK
  8. CONTAINS tbody > tr:nth-child(2) > td:nth-child(2) > span, Finished

Console log

This is what the console logs look like.

The loop executes to completion before the command queue even begins, but that's ok because the commands are still executing in correct order.

starting #0
starting #1
clicking #0
asserting #0
clicking #1
asserting #1

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM