Handling Select2 with Selenium webdriver
Asked Answered
M

8

29

I've been banging my head against the wall trying to select a option from a ajax enabled select2 select list with the selenium webdriver. I've managed to get it working with the IE webdriver but not firefox. Here is my hacky solution for 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);
    }

In Firefox, this solution works up until the point of the SendKeys call where it just hangs and moves on to the next step without actually firing select2's events to populate the selected item.

I've also tired using the http://code.google.com/p/selenium/wiki/AdvancedUserInteractions api with similar results.

Has anyone run into a similar issue before?

Maltose answered 19/7, 2013 at 19:26 Comment(0)
S
30

Could you please show us the locators as well? Here is what I tested without any issues.

Note

  1. To open select box, use css selector #s2id_e1 .select2-choice, or equivalent XPath.
  2. Make sure #select2-drop is the visible one, by css selector #select2-drop:not([style*='display: none']), or equivalent XPath.
  3. Make sure to click the selectable item using subContainerClass + .select2-results li.select2-result-selectable, or equivalent 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();
Shoshana answered 20/7, 2013 at 1:30 Comment(4)
FWIW, I had to use 'ClickAt' 5,5 instead of the clicks to get this to work.Foin
@Daniel: That's more likely you have other elements overlapping. What version? Which browser?Shoshana
This is in Selenium 2.4.0 in FF. I am using the IDE, but would expect the behavior to be similar. I'm new to Selenium, but can't imagine what would be causing click to not work, when clickAt does. Behavior with click is that the select2 dropdown does not appear. Your answer was helpful in getting me to the right elements though...Foin
I also had to wait for the element in my case corresponding to the select2Product to be displayed. Otherwise, the test failed randomly with an Elementnotvisibleexception.Hochstetler
L
7

I've spent some time to get it working in FF, Chrome and IE8-11.

  1. Click the drop arrow
  2. Click the required li

Here is my simplified code:

[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();
}
Laggard answered 8/5, 2015 at 4:19 Comment(0)
B
4

I used the below code to select the desired option and it worked. This must also be faster than performing multiple clicks.

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

And, in Python, if this one-liner doesn't work, try splitting it into it's components:

 value = ['a', 'b', 'c']
 script = "var elem = $('select#tradingMarketSelect'); "
 script += "elem.val(%s); " % value
 script += "elem.change();"
 self.driver.execute_script(script)
Burlington answered 19/4, 2016 at 9:5 Comment(0)
S
3

Here's my code(Getting/Displaying):

Getting select2 available elements(Results):

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; 
}

Displaying select2 available elements(Results)

Using select2 with id(attribute) of: s2id_autogen1:

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

for(WebElement item: select2Results)
{
    System.out.println(item.getText());
}
Sensibility answered 2/11, 2015 at 14:48 Comment(0)
R
2

Here's a solid, reusable solution that handles the additional problem of interacting with multiple select2 dropdowns on one page.

For some reason, the webdriver was not considering the element to send the search value to as visible, even though you could see it on screen and the cursor was in it. That's what the 'if displayed' test checks for. Then it uses a different selector.

Its a function that you can send the ID of the field you want to interact with (less the standard s2id_) and the value to select (or at least enough of it to make the selection.)

The extra thread.sleep()s were just to help me watch it. I don't think they affect the outcome.

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

    }
Raffaello answered 22/4, 2015 at 17:4 Comment(3)
@HappyBird, We're all here to learn. Can you suggest improvements? My solution might not be perfect but I feel it offers a lot of helpful information.Raffaello
Sorry, I don't need to be rude. What I mean is that Thread.Sleep() should be avoided as much as possible.Chlamydate
Ahh, I appreciate your response. I agree. As noted in the original post, those were just a leftover from troubleshooting and not part of the actual solution. I guess I should have just removed them and kept it simple. Thanks again and happy coding.Raffaello
C
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);
}
Conductivity answered 4/6, 2017 at 13:39 Comment(0)
N
0

Try opting for webdriver javascript execution. Below is a handy C# method.

// 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);
}
Narda answered 5/8, 2020 at 11:57 Comment(0)
S
0

For 2022 readers... Use the SelectElement Class to interact with select2's: (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();

Sodalite answered 9/11, 2022 at 0:39 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.