How to choose option of select control in Cypress based on default value?

Viewed 29

Below is a select control in my app:

<select id="answer">
    <option value="yes"> Yes <option>
    <option value="no"> No <option>
    <option value="maybe"> Maybe <option>
</select>

One of the above values will always be selected by default when the page loads.

A problem I have is that I won't know what that default value will be each time.

Here is an example of the Cypress test I'm trying to write:

Given the select control is visible
When I select a different option
Then the selected option is updated

Here's what I am trying to do in pseudo-code:

cy.get('select').select(~~ any option that isn't the default value ~~)

Is it possible to write something like the above? I just want to update the selected option to something other than the default value (but I won't know that default value until during the test, as it will be random)

Example of how it would work during a test run:

  • The page loads & the default selected option is 'No'
  • Cypress should get the default selected value ('no')
  • Then select an option that is not equal to 'no'
  • Validate the selected option value is 'no'

Example of how the test might run a second time:

  • The page loads & the default selected option is 'Maybe'
  • Cypress should get the default selected value ('maybe')
  • Then select an option that is not equal to 'maybe'
  • Validate the selected option value is 'maybe'
2 Answers

You can grab the value of the element on load, and use the option underneath the select to find one that does not equal the initial value.

cy.get('select')
  .invoke('val')
  .then((val) => {
    cy.get('select').find('option').then(($options) => {
      const notSelected = $options.filter((x) => $options[x].value !== val);
      const ranNum = Math.floor(Math.random() * notSelected.length);
      cy.get('select').select(notSelected[ranNum].value);
    });
  });

The random starting value is a bit of an anti-pattern for testing.

Maybe try to force the initial value to be the 1st, then cycle through the options.

const options = ['yes', 'no', 'maybe']

function nextOption(current) {
  return (options.indexOf(current) + 1) % options.length
} 

function selectAnotherOption() {
  cy.get('select')
    .invoke('val')
    .then(current => {
      cy.get('select').select(nextOption(current))
    }) 
}

cy.get('select').select('Yes')
cy.get('select').should('have.value', 'yes')

selectAnotherOption()
cy.get('select').should('have.value', 'no')

selectAnotherOption()
cy.get('select').should('have.value', 'maybe')

selectAnotherOption()
cy.get('select').should('have.value', 'yes')

I had a crack at making the above test "blind" (starting from any random value) but frankly the test gets awfully complicated and it's hard to know if you're testing anything of value.

Related