Closed dhoko closed 2 years ago
Your options may have duplicate labels, but they have different values so you can use the value, rather than the label text in select()
and you should get your expected option.
You could select the first instance of "France" with cy.get('#country').select('object:47')
or the second with cy.get('#country').select('object:123')
.
Using the value here to be more specific about which match you want makes your test deterministic.
Hope this helps!
Of course but the list is generated via Angular. The value might change.
That does present a bit of a problem. Here is a workaround you can use for now:
cy.get('#country')
.find('option[label="France"]') // this will return both elements
.then($els => $els.get(1).setAttribute('selected', "selected")) // set the selected attribute on the desired option
.parent() // change the subject back to the select
.trigger('change') // trigger change event so event handlers will pick it up
I did a quick test where the drop down selection lead to another UI change with a handler for the select
's change
event and this did the trick. You may also wish to remove the selected
attribute beforehand just so your DOM represents a more realistic scenario before the change
even it triggered (though it made no difference in my sample code).
I did that with:
cy.get('#country')
.find('option[selected="selected"]')
.then($el => $el.get(0).removeAttribute('selected'))
So everything together looks like:
// remove current selection
cy.get('#country')
.find('option[selected="selected"]')
.then($el => $el.get(0).removeAttribute('selected'))
// Manually set desired selection
cy.get('#country')
.find('option[label="France"]')
.then($els => $els.get(1).setAttribute('selected', "selected"))
.parent()
.trigger('change')
Haven't tried this but you could probably clean this up by doing...
// Manually set desired selection
cy.get('#country').then(($select) => {
const opt = $select.find('option[label="France"]')
$select.val(opt.attr('value'))
return $select
}).trigger('change')
Hey @dhoko, did the suggestions above solve your issue?
I didn't debug my tests yet, I will try monday. I think it should solve it yes. I can create a custom command from the snippet.
I tried this one -> https://github.com/cypress-io/cypress/issues/757#issuecomment-337676399 it worked. Thx.
Even though this isssue is closed. I still think a select by index is required. Double list entries (both in value as label) is quite common as for long lists people tend to add the most used options as preferred option on top as well (same as in the example).
@brian-mann your answer is an absolute lifesaver. I'm using Vue with Cypress and it's been nearly impossible to figure out how to use my selects - it's due to my selects having objects as their values in the code. Works great for everything but testing - until your answer.
Sorta wish Cypress just let us tell it to act like a normal user and "click here" and then "click here". It doesn't let you do that on selects, and it makes things pretty tough.
Thanks for your answer.
Will reopen as feature to add 'select by index' to .select()
command.
:+1: for visibility
That does present a bit of a problem. Here is a workaround you can use for now:
cy.get('#country') .find('option[label="France"]') // this will return both elements .then($els => $els.get(1).setAttribute('selected', "selected")) // set the selected attribute on the desired option .parent() // change the subject back to the select .trigger('change') // trigger change event so event handlers will pick it up
I did a quick test where the drop down selection lead to another UI change with a handler for the
select
'schange
event and this did the trick. You may also wish to remove theselected
attribute beforehand just so your DOM represents a more realistic scenario before thechange
even it triggered (though it made no difference in my sample code).I did that with:
cy.get('#country') .find('option[selected="selected"]') .then($el => $el.get(0).removeAttribute('selected'))
So everything together looks like:
// remove current selection cy.get('#country') .find('option[selected="selected"]') .then($el => $el.get(0).removeAttribute('selected')) // Manually set desired selection cy.get('#country') .find('option[label="France"]') .then($els => $els.get(1).setAttribute('selected', "selected")) .parent() .trigger('change')
That workaround helped me, thanks!
This definitely feels like it shouldn't need a workaround. There is nothing wrong with having options with duplicate values and labels.
cy.get('#country')
.find('option[label="France"]').then(elements => {
const france = elements[0].getAttribute('value');
cy.get('#country')
.select(france);
});
Hey @jennifer-shehane, the lack of select by index has been annoying me so I'd like to take this issue. Would we want to add an option for index, and then default to 0? It looks like the check at line 109 could be changed to handle this pretty easily.
@jennifer-shehane Some select tags fields on closed platforms are auto generated and out of developer control. The platform I am testing list the same values twice in select fields causing an error below. Whats the status on index based selection? I believe @tylerlinquata is willing to fix. @tylerlinquata have you opened a pull request yet?
+1 to the select by index feature. I'm testing a select that is populated with data from an API that changes every call, so I can't select by value.
I think this would basically accept a new argument index
, so that the arguments accepted would be:
.select(value)
.select(values)
.select(index)
.select(value, options)
.select(values, options)
.select(index, options)
index (Number)
A number indicating the index to find the <option>
at within the <select>
.
We would be open to a PR (that is fully tested). The code can be found here: https://github.com/cypress-io/cypress/blob/develop/packages/driver/src/cy/commands/actions/select.coffee#L12
Check out our contributing doc.
I share implemented solution
selectDropDownList(cy.get('#selectedRoomNo'),4)
function selectDropDownList(dropDownList, value) {
dropDownList.then(($select) => {
debugger
$select.find('option[value="' + $select.val() + '"]')[0].removeAttribute('selected')
const opt = $select.find('option[value="' + value + '"]')
$select.val(opt[0].value)
opt[0].setAttribute('selected', '')
return $select
}).trigger('change')
}
This is especially useful for my current usecase:
a component has select options with the same value but when selected it uses the option's data-name attribute
Take a look at https://glebbahmutov.com/cypress-examples/7.4.0/recipes/selected-value.html#selecting-option-by-index where I use the following to select by index
// every child of <select> is an <option> element
cy.get('select')
.children()
.eq(1)
.then(($option) => {
const value = $option.attr('value')
// if we want to select the oranges,
// let's use the value we got
cy.get('select').select(value)
})
This worked for me
cy.get('select').find('option[data-name="sort"]').then($el =>
$el.get(0).setAttribute("selected", "selected")
).parent().trigger("change")
What is the reason that cy.select() does not support duplicate options? If text and value are the same, then Cypress should just select the first instance.
As mentioned before, duplicating entries is a common pattern. E.g. in a countries list where the most used countries appear on top and in the full list below.
Selecting by index would then be the workaround but it would be flaky because the order or number of options might be different.
The workaround with the jQuery selectors did not work for me in a React project. Maybe because React uses its own event system.
I agree with sbaechler. I think the solution is not to add index selection, but rather allow select() to specify if it is expecting duplicates and which duplicate should be chosen.
I'm writing against a select box where the selected option is shown as the default selected element, but is disabled. The same value is shown selectable lower down in the list. In order to select the option, I need to somehow specify that I want the non-disabled one, otherwise I get an error.
cy.select() failed because this <option> you are trying to select is currently disabled
Released in 8.5.0
.
This comment thread has been locked. If you are still experiencing this issue after upgrading to Cypress v8.5.0, please open a new issue.
4239
"_from": "cypress@^1.0.1", "_id": "cypress@1.0.2",
Current behavior:
Desired behavior:
I can select this option, via an index inside the argument option.
How to reproduce:
Test code:
Additional Info (images, stack traces, etc)
I tried to create the patch myself (it seems the error is here -> https://github.com/cypress-io/cypress/blob/0e0f75cfe90d24d9b0ed52b3c6a5a986baefcf8a/packages/driver/src/cy/commands/actions/select.coffee#L50) but I cannot find the file inside node_modules :/
PS: Cypress is awesome :heart: