Search code examples
cssxpathcypress

How to select from react dropdown with Cypress


Learning Cypress: I have the following code. I'm able to get this to work, but it doesn't seem this is the correct way to do it. There are 25 similar dropdowns on this page

Can someone provide a little guidance here? thx

// choice = 'No' or 'Yes" in this case
setSellChargeOffs(choice){
  cy.get('[dt-label="sellChargeOffFlag"].ui.selection.dropdown').click()
    .xpath(`//div[@dt-label='sellChargeOffFlag']/div[contains(@class, 'menu')]/div[@role='option']/span[text()='${choice}']`)
    .click();
} 

html

<div dt-label="sellChargeOffFlag" role="listbox" aria-expanded="true" class="ui active visible selection dropdown" tabindex="0">
   <div aria-atomic="true" aria-live="polite" role="alert" class="divider default text">Yes</div>
   <i aria-hidden="true" class="dropdown icon" />
   <div class="visible menu transition">
      <div role="option" aria-checked="false" aria-selected="true" class="selected item" style="pointer-events: all;">
         <span class="text">Yes</span>
      </div>
      <div role="option" aria-checked="false" aria-selected="false" class="item" style="pointer-events: all;">
         <span class="text">No</span>
      </div>
   </div>
</div>

Solution

  • Cypress have dropped the cypress-xpath package, so you would be better to stick with the CSS syntax you started with.

    • CSS is more readable
    • virtually nothing is gained by using xpath
    • you avoid having to add the package

    Don't chain more queries after .click()

    There is a warning about that here: click()

    It is unsafe to chain further commands that rely on the subject after .click()

    so don't .xpath() or .get() after the click.

    The reason as I understand it is that click() is an action (i.e has an event handler) that may cause the DOM to re-render, and if you chain further queries in that way you are now working with an old version of the DOM.

    Don't make one function per dropdown

    setSellChargeOffs(choice) applies to just one dropdown - are you going to make 24 more just like it?

    Instead, parameterize a function that can switch any of the dropdowns

    setDropdown (name, choice) {
    
      const dropdownSelector = `[dt-label="${name}"].dropdown`; 
    
      cy.get(dropdownSelector).click()
    
      cy.get(`${dropdownSelector} div.menu div[@role='option']`)
        .find(`span:contains(${choice})`)
        .click()
    }