top of page

waiter2: Useful methods for working with dropdowns and Select

When it comes to selecting values from a classic HTML dropdown, in Selenium you need to perform a few tasks: first, define a WebElement that represents the dropdown as page element; then, using the WebElement, create a Select object; using the Select object, you can then choose an option from the dropdown. This latter option can be done by: visible text (specifying the option's label), 'value' attribute (of the <option> HTML tag found under the <select> tag representing the dropdown, corresponding to which option you want to choose), or by index (position in the list of <option> tags).

Well, in the 'waiter2' library, there is now a collection of useful methods for simplifying the way of working with dropdowns. For starters, you don't need to create the 'Select' object. It is created inside the waiter2 methods. Secondly, for each method you will have again 2 variants, as with all other methods: one with a timeout parameter in the signature; one without it and assuming a default timeout of 30 seconds. But also, there are the 2 variants for how you can specify the dropdown page element: by passing a WebElement defined through @FindBy and by passing a By selector.

Note: Do NOT define the WebElement by calling the driver.findElement() method right before using a wait method from the waiter2 library. If the element is not present or interactable when the findElement() method executes, you will get an Exception, the test will fail, and that beats the purpose of having the wait methods I implemented. That is since the test execution will not even reach the line of code from where you called them, in this case.

Additionally, in case the timeout elapses, instead of a TimeoutException you will get a RuntimeException with a dedicated message that includes: the dropdown selector, the option you tried to select, and for how long the method tried to make the selection.

Also, because all these methods contain embedded check that the condition was successfully reached, do not use an extra assertion to check that the options you wanted to select are selected. Such an assertion is redundant.

In the next paragraphs I will describe in detail these methods that include: selecting based on visible text from a single or multiple value type of dropdowns, selecting based on value, selecting based on either or the other one from a single type of dropdown, selecting by index from single and multiple value type of dropdowns, deselecting all options from a multiple type of dropdown.


selectFromDropdownByVisibleText


For selecting one or multiple values from a dropdown by visible text, the 'selectFromDropdownByVisibleText' method can be used in one of it's 8 variants as below:

- method that takes one WebElement as parameter representing the dropdown and selects one option based on visible text (also passed as parameter). There is a method having these properties that does not take a timeout parameter (and sets the default one of 30 seconds) and one which takes a timeout value as parameter

- method that takes one WebElement as parameter representing the dropdown and selects all options having the specified texts (which are the varargs parameters the method takes). Same as above regarding the timeout parameters

- same as above 2 variants for specifying the dropdown using the By selector

So basically:



Let's see some code and some usage examples.


Since version: 1.1


The code


Let's take for example a variant of this method that selects a single value from a dropdown, represented by a WebElement, using the default timeout. The code of this method is:

public void selectFromDropdownByVisibleText(WebElement dropdownElement, String text) {
    selectFromDropdownByVisibleText(dropdownElement, text, TIMEOUT);
}

which calls -->
 
public void selectFromDropdownByVisibleText(WebElement dropdownElement, String text, int specificTimeout) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {
            try {
                Select select = new Select(dropdownElement);
                select.selectByVisibleText(text);
                return select.getFirstSelectedOption().getText().equals(text);
            } catch (Exception e) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not select option with text '" + text
                + "' from dropdown '" + dropdownElement + "' within " + specificTimeout + " seconds.");
    }
}

Here, inside the method, based on the provided WebElement (as method parameter value), a new Select object is created. Then, Selenium's 'selectByVisibleText' method is called and the option with the expected label is selected. This method will exit successfully when the check that the only (well first, but this being a single type of dropdown, it is also the only) selected option has the expected visible text. In case any of these steps (creating the Select object, selecting a value, and checking the selected option's text) throws any exception, the whole process is restarted. This happens until the condition is met (that the selection was successful) or until the timeout has elapsed.


The variant of this method used for selecting multiple values from a multiple type dropdown is the following (with default timeout, which calls the variant with a timeout parameter):

public void selectFromDropdownByVisibleText(WebElement dropdownElement, String... text) {
    selectFromDropdownByVisibleText(dropdownElement, TIMEOUT, text);
}

which calls -->
 
public void selectFromDropdownByVisibleText(WebElement dropdownElement, int specificTimeout, String... text) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {
            List<String> textsToSelect = Arrays.stream(text).toList();
            List<String> selectedTexts = new ArrayList<>();
            try {
                Select select = new Select(dropdownElement);
                select.deselectAll();
                if (select.getAllSelectedOptions().size() > 0) return false;
                for (String textToSelect : textsToSelect) {
                    select.selectByVisibleText(textToSelect);
                }
                for (WebElement selectedOption : select.getAllSelectedOptions())
                    selectedTexts.add(selectedOption.getText());
                return textsToSelect.equals(selectedTexts);
            } catch (Exception e) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not select multiple options with texts '" + Arrays.toString(text)
                + "' from dropdown '" + dropdownElement + "' within " + specificTimeout + " seconds.");
    }
}

This allows for selecting more than one value by calling this method just once. All the values you want to be selected will be passed as parameters, namely as the last Strings the method call will take. Note that the values you pass to the method call will be the ONLY values selected, as previous to the selection the dropdown is cleared (deselected).


Examples


I did not post the code for the 'By' variants, as you got the hang of it already. But let's see a set of examples on how to select from single and multiple dropdown, using both WebElements and By selectors.


For the first example, the dropdown is represented by a WebElement. In the corresponding Page object class, the WebElement is defined as:

@FindBy(id = "country") public WebElement countryDropdown;

Now, to have 'Spain' as the selected option from the dropdown, the 'selectFromDropdownByVisibleText' method will help:

waiter.selectFromDropdownByVisibleText(page.countryDropdown, "Spain");

The result is as per the below image:


Now, in case your page element's selector is dynamic and you cannot create a WebElement to represent it, you can pass a By selector to the 'selectFromDropdownByVisibleText' method as per the below example:

waiter.selectFromDropdownByVisibleText(By.id("city"), "Valencia");

For selecting multiple values from a 'multiple' type of dropdown, the usage will be as follows (here the dropdown is represented as a @FindBy WebElement - @FindBy(id = "refreshment") public WebElement refreshmentDropdown;):

waiter.selectFromDropdownByVisibleText(page.refreshmentDropdown, "Rose Lemonade", "Still Water");

The result will be as per the below screenshot:



And, as a last example of selecting by visible text, below you provide a WebElement for the dropdown, select 1 option from the single type of dropdown, and wait for only 10 seconds for this action to be successful:

waiter.selectFromDropdownByVisibleText(page.teaDropdown, "Earl Grey", 10);

selectFromDropdownByValue


For selecting from a dropdown based on the option tags' 'value' attribute, there are also 8 available methods that allow: providing a WebElement/By selector; selecting from a single/multiple type of dropdown; specifying a timeout value/using the default one:



Since version: 1.1


The code


I will only show the code for 2 methods, as the others are very similar (but you can also check them out in GitHub). The first one, for selecting from a single type of dropdown using a WebElement to define the dropdown:

public void selectFromDropdownByValue(WebElement dropdownElement, String value) {
    selectFromDropdownByValue(dropdownElement, value, TIMEOUT);
}

which calls -->

public void selectFromDropdownByValue(WebElement dropdownElement, String value, int specificTimeout) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {
            try {
                Select select = new Select(dropdownElement);
                select.selectByValue(value);
                return select.getFirstSelectedOption().getAttribute("value").equals(value);
            } catch (Exception e) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not select option with value '" + value
                + "' from dropdown '" + dropdownElement + "' within " + specificTimeout + " seconds.");
    }
}

The second one, for selecting from a multiple type of dropdown represented by a By selector:

public void selectFromDropdownByValue(By dropdownElementSelector, String... value) {
    selectFromDropdownByValue(dropdownElementSelector, TIMEOUT, value);
}

which calls --> 

public void selectFromDropdownByValue(By dropdownElementSelector, int specificTimeout, String... value) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {
            List<String> valuesToSelect = Arrays.stream(value).toList();
            List<String> selectedValues = new ArrayList<>();
            try {
                Select select = new Select(driver.findElement(dropdownElementSelector));
                select.deselectAll();
                if (select.getAllSelectedOptions().size() > 0) return false;
                for (String valueToSelect : valuesToSelect) {
                    select.selectByValue(valueToSelect);
                }
                for (WebElement selectedOption : select.getAllSelectedOptions())
                    selectedValues.add(selectedOption.getAttribute("value"));
                return valuesToSelect.equals(selectedValues);
            } catch (Exception e) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not select multiple options with values '" + Arrays.toString(value)
                + "' from dropdown '" + dropdownElementSelector + "' within " + specificTimeout + " seconds.");
    }
}


Example


For example, there is a dropdown containing tea beverages as per the image below:



To define the dropdown as a WebElement, in the page object class, the following @FindBy annotated WebElement is declared:

@FindBy(id = "tea") public WebElement teaDropdown;

Then, in the test, to select the Pu Erh tea using the corresponding option tags' value attribute:

waiter.selectFromDropdownByValue(page.teaDropdown, "Pu_Erh");

When it comes to the same multiple type dropdown for selecting refreshments used in previous examples, this time I want to show what values can be chosen:



So, for example to select the Lavender Lemonade and Still Water, the values I need to provide to the select by value method are the Strings "2" and "4". Here I will also provide a By selector to represent this dropdown:

waiter.selectFromDropdownByValue(By.id("refreshment"), "2", "4");


selectFromDropdown


In the previous 2 sections, when calling a method for selecting from a dropdown, you either explicitly specified a visible text or a value, and called dedicated methods for both types of selecting. However, in case you don't want to be bound to any of the 2 selection methods (by visible text or by value), there is a set of methods called 'selectFromDropdown' in the waiter2 library. To this method you can pass either the visible text or the value. The logic inside the method will first try to select by visible text, and if that is not successful, it will try selection by value. Of course, since this is a wait based method, these actions are retried within the specified timeout, until the correct option is selected from the dropdown, or the timeout elapses. Note: this set of methods is only available for single selection type of dropdowns.

The variants of this method are as follows:



Since version: 1.1


The code


As you have seen in above image, there are variants of this method that take a WebElement or a By selector as parameter. Below I will show the code only for the variant with WebElement, as the By one is similar:

public void selectFromDropdown(WebElement dropdownElement, String text) {
    selectFromDropdown(dropdownElement, text, TIMEOUT);
}

which calls -->

public void selectFromDropdown(WebElement dropdownElement, String text, int specificTimeout) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {
            try {
                Select select = new Select(dropdownElement);
                try {
                    select.selectByVisibleText(text);
                    return select.getFirstSelectedOption().getText().equals(text);
                } catch (Exception f) {
                    try {
                        select.selectByValue(text);
                        return select.getFirstSelectedOption().getAttribute("value").equals(text);
                    } catch (Exception e) {
                        return false;
                    }
                }
            } catch (Exception g) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not select option with text or value '" + text
                + "' from dropdown '" + dropdownElement + "' within " + specificTimeout + " seconds.");
    }
}

Example


For the tea dropdown used in an example above, one of the options has a visible text of "Pu Erh" and a value of "Pu_Erh". Using the 'selectFromDropdown' method for selecting the Pu Erh type of tea from this dropdown, this can be done in either one of the following ways:

waiter.selectFromDropdown(page.teaDropdown, "Pu Erh");
OR
waiter.selectFromDropdown(page.teaDropdown, "Pu_Erh");


selectFromDropdownByIndex


A third way of selecting an option from a dropdown is based on an index: the position in the list of options. In 'waiter2' you will find helpful methods for performing this type of selecting also, as follows: for single selection and multiple selection dropdowns, using WebElements and By selectors to identify the dropdown to select from, and by specifying a timeout value or using the default one. Note: For specifying your own timeout as a method parameter, you need to pass this as the first parameter in the method call. This is due to supporting the multiple selection type of dropdown.




Since version: 1.1


The code


Similarly to the other methods I've already explained, in the 'selectFromDropdownByIndex' ones, first the 'Select' object is created. In the methods for single dropdowns, the option specified through the int position parameter is selected. Only when the subsequent check that the correct option was selected finishes successfully will the wait method exit:

public void selectFromDropdownByIndex(WebElement dropdownElement, int position) {
    selectFromDropdownByIndex(TIMEOUT, dropdownElement, position);
}

which calls -->

public void selectFromDropdownByIndex(int specificTimeout, WebElement dropdownElement, int position) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {
            try {
                Select select = new Select(dropdownElement);
                select.selectByIndex(position);
                return dropdownElement.findElements(By.cssSelector("option")).get(position).isSelected();
            } catch (Exception e) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not select option with index '" + position
                + "' from dropdown '" + dropdownElement + "' within " + specificTimeout + " seconds.");
    }
}

For the multiple selection dropdowns, first any previously selected option is unselected, and then the ones specified by the int parameters will be selected. A check is then made that only the specified options are the selected ones.


public void selectFromDropdownByIndex(WebElement dropdownElement, int... position) {
    selectFromDropdownByIndex(TIMEOUT, dropdownElement, position);
}

which calls --> 

public void selectFromDropdownByIndex(int specificTimeout, WebElement dropdownElement, int... position) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {

            List<Integer> positionsToSelect = new ArrayList<>();
            for (int onePosition : position)
                positionsToSelect.add(valueOf(onePosition));
            try {
                Select select = new Select(dropdownElement);
                select.deselectAll();
                if (select.getAllSelectedOptions().size() > 0) return false;
                for (int positionToSelect : positionsToSelect) {
                    select.selectByIndex(positionToSelect);
                }
                for (int positionToSelect : positionsToSelect) {
                    if (!dropdownElement.findElements(By.cssSelector("option")).get(positionToSelect).isSelected())
                        return false;
                }
                return true;
            } catch (Exception e) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not select option with indexes '" + Arrays.toString(position)
                + "' from dropdown '" + dropdownElement + "' within " + specificTimeout + " seconds.");
    }
}


Examples


Let's say you want to select the second option from the tea dropdown, for which you already created a @FindBy annotated WebElement in a separate class. You do it like this:


waiter.selectFromDropdownByIndex(page.teaDropdown, 1);

How about selecting the fourth option from a dropdown for which you want to specify the By selector inline?


waiter.selectFromDropdownByIndex(By.id("country"), 3);

Now, to select the second, third and fourth option from a multiple type of dropdown, specified as a WebElement:


waiter.selectFromDropdownByIndex(page.refreshmentDropdown, 1, 2, 3);

How about also providing a timeout value to the multiple dropdown selection method?


waiter.selectFromDropdownByIndex(3, By.id("refreshment"), 2, 3);


deselectAllFromDropdown


For multiple selection type of dropdowns, there is now a set of methods for deselecting all the selected options. Their variants are as below:



They are very easy to use: just pass in the WebElement or By selector representing the dropdown you want to clear of all the selected values to the method, and optionally a timeout value. And that's it.


Since version: 1.1


The code


The code behind these methods can be seen here:


public void deselectAllFromDropdown(WebElement dropdownElement) {
    deselectAllFromDropdown(dropdownElement, TIMEOUT);
}

which calls --> 

public void deselectAllFromDropdown(WebElement dropdownElement, int specificTimeout) {
    WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(specificTimeout));
    try {
        ExpectedCondition<Boolean> condition = arg -> {
            try {
                Select select = new Select(dropdownElement);
                select.deselectAll();
                return (select.getAllSelectedOptions().size() == 0);
            } catch (Exception e) {
                return false;
            }
        };
        wait.until(condition);
    } catch (TimeoutException e) {
        throw new RuntimeException("waiter2.FAILURE: Could not deselect all dropdown options from dropdown '"
                + dropdownElement + "' within " + specificTimeout + " seconds.");
    }
}

Example


For the refreshment dropdown mentioned in previous examples, to simply deselect all selected options:


waiter.deselectAllFromDropdown(page.refreshmentDropdown);

Do not use any assertion to assert that no option is selected after this method finishes successfully, since it only finishes successfully when there are 0 selected options.


Conclusion


So these are some useful methods for working with dropdown. The code of the waiter2 library methods can be found here: https://github.com/iamalittletester/waiter2/blob/main/src/main/java/utils/Waiter.java .

The examples regarding these methods can be found here (tests with Order value from 10 to 14):

https://github.com/iamalittletester/little-project/blob/master/src/test/java/com/imalittletester/_2023/waiter2/Waiter2Tests.java .



bottom of page