Reputation: 7667
I am using this HTML code:
<select name="cars" multiple>
<option value="volvo">Volvo</option>
<option value="saab">Saab</option>
<option value="opel">Opel</option>
<option value="audi">Audi</option>
</select>
Manually I select some items. Then I want to deselect all of them (I am using C# but that does not matter):
var carsElement = BrowserDriver.FindElementByName("cars");
var carsSelect = new SelectElement(carsElement);
carsSelect.DeselectAll();
What happens: The first selected options stays selected, others are unselected.
Looking at the code this is what must happen, because DeselectAll()
calls Click()
for all selected options. You can try that in your browser. This will never unselect all options (unless you hold CTRL while clicking but that is not done by the Selenium code). So the correct way would be to change DeselectAll
to press CTRL while clicking as demonstrated by How to perform Control key down in selenium webdriver?
Bottom line, I know how to fix this; my questions are: Am I missing anything? Is there an easier way? Is SelectElement
not intended from HTML SELECT multiple?
Upvotes: 1
Views: 2414
Reputation: 55002
You can for sure deselect these with
browser.execute_script("[...document.querySelectorAll('[name=cars] option')].map(o => o.selected = false)")
Upvotes: 2
Reputation: 193218
I have verified your usecase of using the deselect_all()
method through the Selenium Python Client and it seems to work perfecto.
deselect_all()
method clears all selected entries. This is only valid when the SELECT supports multiple selections. throws NotImplementedError If the SELECT does not support multiple selections.
Note: As you have mentioned in your question I have also simulated the selection of all the items manually
Code Block:
from selenium import webdriver
from selenium.webdriver.common.by import By
from selenium.webdriver.support.ui import WebDriverWait
from selenium.webdriver.support import expected_conditions as EC
from selenium.webdriver.support.ui import Select
import time
options = webdriver.ChromeOptions()
options.add_argument("start-maximized")
options.add_argument('disable-infobars')
driver=webdriver.Chrome(chrome_options=options, executable_path=r'C:\Utility\BrowserDrivers\chromedriver.exe')
driver.get('https://www.w3schools.com/tags/tryit.asp?filename=tryhtml_select_multiple')
WebDriverWait(driver, 10).until(EC.frame_to_be_available_and_switch_to_it((By.ID,"iframeResult")))
select_cars = Select(driver.find_element_by_css_selector("select[name='cars']"))
time.sleep(5) # Timeframe to Manually select all the items
select_cars.deselect_all()
Browser Snapshot:
Upvotes: 0
Reputation: 7667
Even though I did not ask for code how to set the selection state, I'll post it anyways for anybody who encountered the same problem. This is C# code. You'll need NuGet packages Selenium.WebDriver and Selenium.Support. BrowserDriver
is a member variable of my helper class which contains this method.
/// <summary>
/// Sets the selection state of <paramref name="selectElement"/>. All options specified by <paramref name="selectedOptions"/>
/// are select, all others are unselected.
/// </summary>
/// <param name="selectElement">HTML select element</param>
/// <param name="selectedOptions">options to be selected</param>
internal void SelectStateByText(OpenQA.Selenium.Support.UI.SelectElement selectElement, params string[] selectedOptions)
{
Assert.IsNotNull(selectElement);
Assert.IsNotNull(selectedOptions);
CollectionAssert.IsSubsetOf(selectedOptions, selectElement.Options.Select(o => o.Text).ToArray());
if (!selectElement.IsMultiple)
{
Assert.AreEqual(1, selectedOptions.Length);
selectElement.SelectByText(selectedOptions[0]);
}
else
{
var actions = new OpenQA.Selenium.Interactions.Actions(BrowserDriver);
actions.KeyDown(Keys.LeftControl);
foreach (var option in selectElement.Options)
{
if (selectedOptions.Contains(option.Text) && !option.Selected)
{
actions.Click(option);
}
else if (option.Selected)
{
actions.Click(option);
}
}
actions.KeyUp(Keys.LeftControl).Build().Perform();
}
}
Upvotes: 0
Reputation: 50899
Select
class can handle multiple choice dropdown. It even checks if the dropdown is multiple choice when using DeselectAll()
. From github
public void DeselectAll()
{
if (!this.IsMultiple)
{
throw new InvalidOperationException("You may only deselect all options if multi-select is supported");
}
foreach (IWebElement option in this.Options)
{
SetSelected(option, false);
}
}
private static void SetSelected(IWebElement option, bool select)
{
bool isSelected = option.Selected;
if ((!isSelected && select) || (isSelected && !select))
{
option.Click();
}
}
When you click on the first select option without pressing the control key this option remains selected but all the other options are being deselected, so the click is actually not performed on the rest of the options since both isSelected
and select
are false
in SetSelected
.
The solution is either implement your own DeselectAll()
as suggested in your question, or select the first option from the dropdown (which will automatically deselect all the other options) and then deselect this option using control.
Upvotes: 0