Home > other >  cypress .next at end of list yields '' but I need to break instead
cypress .next at end of list yields '' but I need to break instead

Time:02-17

I am writing Cypress tests for an application that has a dynamic group/list of items. Each item has a details link that when clicked creates a popup with said details. I need to close that popup and move to the next item in the group.

I first tried to use the .each() command on the group and then would .click({multiple:true}) the details but the popup would cover the the next click. Adding {foce:true} does allow all of the popups to display but I don't think that is in the spirit of how the application should function.

My latest attempt has been to create a custom command using .next() to iterate through the group. This works but when .next() reaches the end of the group it yields "" and so the test ultimately fails. The actual error I get is: Expected to find element: ``, but never found it. Queried from element: <div.groups.ng-star-inserted>

the .spec.ts

describe('Can select incentives and view details', () => {
  it('Views incentive details', () => {
    cy.optionPop('section#Incentives div.groups:first')
  })
})

the index.ts

Cypress.Commands.add('optionPop', (clickable) => {
    
    cy.get(clickable).find('[ng-reflect-track="Estimator, open_selection_dial"]').click()
    cy.get('mat-dialog-container i.close').click()
    cy.get(clickable).next().as('clicked').then(($clicked) => {
        //fails at .next ^ because '' is yielded at end of list
        cy.wrap($clicked).should('not.eq','')
    })
    cy.optionPop('@clicked')
})

CodePudding user response:

You basically have the right idea, but it might work better in a plain JS function rather than a custom command.

function openPopups(clickables) {

  if (clickables.length === 0) return   // exit when array is empty

  const clickable = clickables.pop()    // extract one and reduce array

  cy.wrap(clickable)
    .find('[ng-reflect-track="Estimator, open_selection_dial"]').click()

  cy.get('mat-dialog-container i.close')
    .should('be.visible')                // in case popup is slow
    .click()

  // wait for this popup to go, then proceed to next
  cy.get('mat-dialog-container')
    .should('not.be.visible')        
    .then(() => {
      openPopups(clickables)   // clickables now has one less item
    })        
}

cy.get('section#Incentives div.groups')  // get all the popups
  .then($popups => {
    const popupsArray = Array.from($popups)  // convert jQuery result to array
    openPopups(popupsArray)
  })

Some extra notes:

  • Using Array.from($popups) because we don't know how many in the list, and want to use array.pop() to grab each item and at the same time reduce the array (it's length will control the loop exit).

  • clickables is a list of raw elements, so cy.wrap(clickable) makes the individual element usable with Cypress commands like .find()

  • .should('be.visible') - when dealing with popup, the DOM is often altered by the click event that opens it, which can be slow relative to the speed the test runs at. Adding .should('be.visible') is a guard to make sure the test is not flaky on some runs (e.g if using CI)

  • .should('not.be.visible').then(() => ... - since you have some problems with multiple overlapping popups this will ensure each popup has gone before testing the next one.

  • Related