Selenium DeselectAll 不适用于具有多个属性的 HTML SELECT

Selenium DeselectAll not working for HTML SELECT with multiple attribute

我正在使用 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>

我手动 select 一些项目。然后我想 deselect 所有这些(我正在使用 C# 但那没关系):

var carsElement = BrowserDriver.FindElementByName("cars");
var carsSelect = new SelectElement(carsElement);
carsSelect.DeselectAll();

会发生什么:第一个 selected 选项保留 selected,其他未selected。

查看代码,这是 必须 发生的,因为 DeselectAll() 为所有 selected 选项调用 Click()。您可以在浏览器中尝试。这永远不会取消 select all 选项(除非您在单击时按住 CTRL,但 Selenium 代码不会这样做)。因此,正确的方法是更改​​ DeselectAll 以在单击时按 CTRL,如

所示

最重要的是,我知道如何解决这个问题;我的问题是:我错过了什么吗?有没有更简单的方法? SelectElement 不是 HTML SELECT 多个吗?

您肯定可以通过

取消选择这些
browser.execute_script("[...document.querySelectorAll('[name=cars] option')].map(o => o.selected = false)")

Select class 可以处理多选下拉。它甚至在使用 DeselectAll() 时检查下拉列表是否为多项选择。来自 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();
    }
}

当您单击第一个 select 选项而不按控制键时,此选项仍然 selected 但所有其他选项都被取消 selected,因此单击是实际上没有对其余选项执行,因为 isSelectedselect 都是 SetSelected.

中的 false

解决方案是按照您的问题中的建议实施您自己的 DeselectAll(),或者 select 下拉列表中的第一个选项(这将自动取消 select 所有其他选项)然后 deselect 使用 control.

这个选项

即使我没有询问如何设置选择状态的代码,我还是会 post 为遇到同样问题的任何人提供代码。这是 C# 代码。您将需要 NuGet 包 Selenium.WebDriver and Selenium.SupportBrowserDriver 是我的助手 class 的成员变量,其中包含此方法。

/// <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();
    }
}

我已经通过 Selenium Python Client 验证了您使用 deselect_all() 方法的 usecase 并且它似乎工作 perfecto.

deselect_all()

deselect_all() 方法清除所有选定条目。这仅在 SELECT 支持多选时有效。如果 SELECT 不支持多选,则抛出 NotImplementedError。


插图

注意:正如你在问题中提到的,我还模拟了所有项目的选择手动

  • 代码块:

    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()
    
  • 浏览器快照: