使用Selenium webdriver处理Select2

27

我一直在试图使用Selenium WebDriver从一个启用了ajax的select2选择列表中选择选项。我已经设法通过IE WebDriver使其工作,但是在Firefox中无法实现。这是我的IE的临时解决方案:

 public static void SetSelect2Option(this IWebDriver driver, By locator, string subContainerClass, string searchTerm, TimeSpan? ajaxWaitTimeSpan = null)
    {
        var select2Product = driver.FindElement(locator);
        select2Product.Click();
        var searchBox = driver.FindElement(By.CssSelector(subContainerClass + " .select2-input"));
        searchBox.SendKeys(searchTerm);
        if (ajaxWaitTimeSpan != null)
        {
            driver.Manage().Timeouts().ImplicitlyWait(ajaxWaitTimeSpan.Value);
        }
        var selectedItem = driver.FindElements(By.CssSelector(subContainerClass + " .select2-results li")).First();
        selectedItem.Click();
        selectedItem.SendKeys(Keys.Enter);
    }

在Firefox中,这个解决方案可以一直工作到SendKeys调用的那一点,然后就挂起并继续进行下一步,而没有实际触发select2的事件来填充所选项目。

我还尝试使用http://code.google.com/p/selenium/wiki/AdvancedUserInteractions API,结果类似。

有人之前遇到过类似问题吗?

8个回答

30

请问您能否同时展示一下定位器?以下是我测试过没有任何问题的内容。

注意

  1. 要打开选择框,请使用CSS选择器#s2id_e1 .select2-choice或者等效XPath。
  2. 请确保可见的是#select2-drop,可以使用CSS选择器#select2-drop:not([style*='display: none'])或者等效XPath。
  3. 请使用subContainerClass + .select2-results li.select2-result-selectable,或者等效XPath来点击可选择的项目。
var driver = new FirefoxDriver();
driver.Url = "http://ivaynberg.github.io/select2/";

var select2Product = driver.FindElement(By.CssSelector("#s2id_e1 .select2-choice"));
select2Product.Click();

string subContainerClass = "#select2-drop:not([style*='display: none'])";
var searchBox = driver.FindElement(By.CssSelector(subContainerClass + " .select2-input"));
searchBox.SendKeys("Ohio");

var selectedItem = driver.FindElements(By.CssSelector(subContainerClass + " .select2-results li.select2-result-selectable")).First();
selectedItem.Click();

2
顺便说一句,我不得不使用'ClickAt' 5,5而不是点击才能使其正常工作。 - Daniel
@Daniel:更有可能是您的其他元素重叠了。使用的是哪个版本?哪个浏览器? - Yi Zeng
这是在Selenium 2.4.0中在FF中的操作。我正在使用IDE,但预计行为应该是类似的。我对Selenium还很陌生,但无法想象是什么原因导致click无法工作,而clickAt可以。点击的行为是select2下拉菜单不会出现。您的答案有助于让我找到正确的元素... - Daniel
我也必须等待我的情况下对应于 select2Product 的元素被显示。否则,测试将随机失败并出现 Elementnotvisibleexception - Kristian Johannessen

6

我花了一些时间让它在Firefox、Chrome和IE8-11中运行。

  1. 点击下拉箭头
  2. 点击所需的li元素

这是我的简化代码:

[FindsBy(How = How.ClassName, Using = "select2-arrow")]
private IWebElement Selector { get; set; }

private void selectItem(string itemText)
{
    Selector.Click();  // open the drop
    var drop = Driver.FindElement(By.Id("select2-drop"));    // exists when open only
    var item = drop.FindElement(By.XPath(String.Format("//li[contains(translate(., '{0}', '{1}'), '{1}')]", itemText.ToUpper(), itemText.ToLower())));
    item.Click();
}

4

我使用以下代码选择所需选项,并且它有效。这种方法也比多次点击更快。

String script = "$('select#yearSelector').trigger($.Event('change',{val:'" + year + "'}))";
((JavascriptExecutor) driver).executeScript(script);

在Python中,如果这个一行代码不能正常工作,尝试将其拆分为其组件:

 value = ['a', 'b', 'c']
 script = "var elem = $('select#tradingMarketSelect'); "
 script += "elem.val(%s); " % value
 script += "elem.change();"
 self.driver.execute_script(script)

3

这是我的代码(获取/显示):

获取 select2 可用元素(结果):

public List<WebElement> getDataFromSelect2(String elementXpath)
{       
    WebElement select2Element = driver.findElement(By.xpath(elementXpath));
    select2Element.click();     

    WebDriverWait webDriverWait = new WebDriverWait(driver, 90);
    webDriverWait.until(ExpectedConditions.presenceOfElementLocated(By.xpath("//ul[@class='select2-results']//div")));

    WebElement select2ElementResults=driver.findElement(By.xpath("//div[@id='select2-drop']/ul[@class='select2-results']"));
    List<WebElement> selectResultsAsListCollection = select2ElementResults.findElements(By.tagName("div"));

    return selectResultsAsListCollection; 
}

展示select2可选择元素(结果)

使用id(attribute)为s2id_autogen1select2:

List<WebElement> select2Results = getDataFromSelect2("//input[@id='s2id_autogen1']");

for(WebElement item: select2Results)
{
    System.out.println(item.getText());
}

2
protected void SelectOptionForSelect2(IWebDriver driver, string id, string text)
{
  var element = driver.FindElement(By.Id(id)).FindElement(By.XPath("following-sibling::*[1]"));
  element.Click();

  element = driver.FindElement(By.CssSelector("input[type=search]"));
  element.SendKeys(text);

  Thread.Sleep(1000);
  element.SendKeys(Keys.Enter);
}

2
这是一个可靠的、可重用的解决方案,可以处理与一个页面上的多个select2下拉框交互的附加问题。由于某种原因,webdriver并未将要发送搜索值的元素视为可见,尽管您可以在屏幕上看到它并且光标位于其中。这就是'if displayed'测试所检查的内容。然后它使用不同的选择器。
这是一个函数,您可以向其发送要与之交互的字段的ID(去掉标准的s2id_),以及要选择的值(或至少足够的值来进行选择)。
额外的thread.sleep()仅是为了帮助我观察它。我认为它们不会影响结果。
public void SelectDropDownOption(string dropDownID, string option)
    {
        for (int second = 0; ; second++)
        {
            if (second >= 60) Assert.Fail("timeout");
            try
            {
                if (driver.FindElement(By.CssSelector("div[ID^=s2id_" + dropDownID + "]>a.select2-choice")).Displayed) break;
            }
            catch (Exception)
            { }
            Thread.Sleep(1000);
        }

        driver.FindElement(By.CssSelector("div[ID^=s2id_" + dropDownID + "]>a.select2-choice")).Click();
        Thread.Sleep(1000);

        if (driver.FindElement(By.CssSelector("input.select2-input.select2-focused")).Displayed == true)
        {
            driver.FindElement(By.CssSelector("input.select2-input.select2-focused")).SendKeys(option);
            Thread.Sleep(500);
            driver.FindElement(By.CssSelector("input.select2-input.select2-focused")).SendKeys(Keys.Enter);
            Thread.Sleep(500);
        }
        else
        {
            driver.FindElement(By.CssSelector("input.select2-focusser.select2-offscreen")).SendKeys(option);
            Thread.Sleep(500);
            driver.FindElement(By.CssSelector("input.select2-focusser.select2-offscreen")).SendKeys(Keys.Enter);
            Thread.Sleep(500);
        }

    }

3
@HappyBird,我们都在这里学习。你能提出一些改进意见吗?我的解决方案可能并不完美,但我觉得它提供了很多有用的信息。 - KyleK
对不起,我并不是想无礼。我的意思是尽可能避免使用 Thread.Sleep() - Happy Bird
1
啊,感谢您的回复。我同意。正如原帖中所指出的那样,它们只是故障排除过程中剩余的部分,而不是实际解决方案的一部分。我想我应该把它们删除并保持简单。再次感谢,祝编码愉快。 - KyleK

0

针对2022年的读者... 使用SelectElement Class与select2进行交互: (csharp)


IWebElement select2Element = _webDriver.FindElement(By.Id("select2"));

var select2 = new SelectElement(select2Element);

select2.SelectByText("foo");

//example of getting an item from the list
var selectOptions= select2.Options.Select(x => x.Text).ToList();


0

尝试选择webdriver javascript执行。以下是一个方便的C#方法。

// usings
using System;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;


// method
public static void SetSelect2Option_JSExample(this IWebDriver driver, string select2Id, string value)
{       
    IJavaScriptExecutor jsExecutor = (IJavaScriptExecutor)driver;
    string js = "$('#" + select2Id + "').val('" + value + "').trigger('change');";
    string jsOutput = (string)jsExecutor.ExecuteScript(js);
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接