artooras
artooras

Reputation: 6805

Handling React re-renders with cypress

I have two react-select Selects side-by-side. Selecting one updates state and determines the values in the second Select.

My cypress test does this:

// select an option in the first select - this works
cy.get('div[class*="container"]').contains('Select 1').click()
cy.get('div[class*="option"]').contains('Select 1 Option 1').click()

// select an option in the second select - this doesn't work
cy.get('div[class*="container"]').contains('Select 2').click() // <<< error occurs here
cy.get('div[class*="option"]').contains('Select 2 Option 1').click()

So, the option in the first Select is selected successfully. But the second isn't - I get this error:

Timed out retrying: cy.click() failed because this element is detached from the DOM.

I tried a cy.wait(10) before getting the second Select, which, yes, is bad practice, but even so, it didn't solve the problem. How do I do this correctly?

Edit:

Here's an abstraction of the implementation of the Selects:

const [state, setState] = useState([
  {
    select: '', option: ''
  }
])

const options = {
  "Select 1": [
    "Select 1 Option 1",
    "Select 1 Option 2"
  ],
  "Select 2": [
    "Select 2 Option 1",
    "Select 2 Option 2"
  ]
}

return (
  {state.map((row, index) => {
    <Select
      options={Object.keys(options)}
      value={row.select}
      onValueChange={value => setState({
        ...state,
        select: value
      })}
    />
    <Select
      options={options[row.select]}
      value={row.option}
      onValueChange={value => setState({
        ...state,
        option: value
      })}
    />
  })}
)

Of course, react-select cannot accept an array of strings as options, so in my real implementation, I have a function that converts each string into a {label: '', value: ''} object, but this is not necessary for this illustration.

The idea above is that when I select the value of the first Select, the view is re-rendered because the state changes.

Upvotes: 2

Views: 2805

Answers (1)

Renato Nouman
Renato Nouman

Reputation: 31

it is a bit hard to guess from your abstraction, but it seems like you are not updating the value of state.option that sets the value of the second select. I'm not sure what your default state is, but from your snippet above cypress would not find either select, because when you do this cy.get('div[class*="container"]').contains('Select 1') none of the DOM elements would actually contain the matching string Select 1 yet.

Because you mentioned the first cy command works, I'm not sure how exactly the Select is initialising, so this is a bit of a guess.

Take a look here: https://codesandbox.io/s/compassionate-moon-ss4z9?file=/src/App.js

Upvotes: 1

Related Questions