4

I need to select a value in a select. The select is a list of countries, displaying their names, with a two-letter country code as the value for each option. We surface the most selected countries to the top, while also leaving them in their alphabetical position. This means the items surfaced to the top are repeated twice.

<select>
  <option value="gb">UK</option>
  <option value="us">USA</option>
  <option value="af">Afganistan</option>
  <option value="ai">Aland Islands</option>
  ...
  <option value="us">USA</option>
  <option value="gb">UK</option>
  ...
</select>

I'm selecting a value like this:

cy.getSelect().select('gb')

However, this raises an error:

CypressError: Timed out retrying: cy.select() matched more than one option by value or text: gb

This makes sense as the value for 'UK' is gb and it appears at the top of the list and within the list in its alphabetical position.

How can I tell Cypress to ignore the duplicate value and select the first match?

Note that I cannot guarantee the index of any country and that I have lots of other tests that select different countries. I need a way to tell Cypress to select the first match.

Paolo
  • 3,530
  • 7
  • 21
Undistraction
  • 42,754
  • 56
  • 195
  • 331

3 Answers3

9

You can try dropping down to jQuery/JavaScript to manually set the field:

cy.get('select').then($country => {$country.val("gb")})

The $country above should be a jQuery object wrapping the html that you selected.

See: https://docs.cypress.io/api/commands/then.html#Syntax

jpvantuyl
  • 584
  • 10
  • 22
  • 5
    If you want to trigger the select, then you can go further with this: cy.get('select').then($country => {$country.val("gb")}) .parent() .trigger('change') – C. J. Tantay Mar 19 '21 at 22:19
2

Cypress has added some functionality to cy.select(), you can select by position now.

Using us value to test, since gb is the default selected value (it's at the top of the list)

cy.get('select')
  .find('[value="us"]')
  .eq(1)                                               // to pick the 2nd occurrence 
  .then($option => {
    const $select = $option.prevObject.prevObject      // back to <select>
    cy.wrap($select).select($option.index())           // select this option
  })

cy.get('select').should('have.value', 'us')            // confirm
Paolo
  • 3,530
  • 7
  • 21
1

Here are a couple sources to look through: https://docs.cypress.io/api/commands/select.html#Text-Content & https://docs.cypress.io/api/commands/eq.html#Syntax

What I would recommend trying would be along the lines of this: cy.get('select').select('gb').eq(0) or cy.get('select').select('gb').first()

Either of these options will grab the first item if duplicates are found. Other things you can do with eq() are eq(-1) for the last item, or eq(2) for the third item (base zero) and so on.

Edit: use cy.get('select') instead of cy.getSelect();

Porter Lyman
  • 343
  • 2
  • 10
  • 1
    I'm unsure how this got downvoted. I tested it with a native select/option element and it worked how it should. Please be helpful. If the answer is incorrect, let me know and why and we'll sort it out. Thanks! – Porter Lyman Jun 19 '19 at 16:05
  • 1
    This does not work in cypress 3.4.0. I tried both .first() and .eq(1). Both get the error: `cy.select() matched more than one option by value or text` – jpvantuyl Jul 24 '19 at 21:49
  • That's unexpected. .first() and eq(0) can only return one element. How specific is your selector? – Porter Lyman Jul 26 '19 at 14:19
  • 2
    My `cy.select()` was working against a ` – jpvantuyl Jul 27 '19 at 16:39