Trouble with multiselect drop down automation

Hi there,

I’m very new to all of this so please excuse me if this is a very basic question.

I am trying to automate form entry from a CSV file for a surgical log and have a problem with one field. This is a drop down box with multiple selections. On inspecting this, it is a DIV class with LI class elements, e.g. li[1], li[2] etc.. I know how to SELECT these elements with the click command, but I need it to be able to pull the performed procedure from a CSV file, and select the right one from this drop down list accordingly.

On using Katalon recorder, I discovered that the target element when I click anything in the list is xpath=//article[@id=‘log-entry-form’]/div/div/div[3]/div/div/div[3]/ul/li[X]/span

My question is: How do I code this to choose a SPECIFIC li number based on my CSV file, or is there a simpler way to do this where the code searches for the right variable and selects it? E.g. for option 6, recorder gives me: selenium.click(“xpath=//article[@id=‘log-entry-form’]/div/div/div[3]/div/div/div[3]/ul/li[6]/span”)

Thank you so much!

See

1 Like

Thank you, I will have a read through that!

I figured out that the website I’m creating a test case for uses Vue Multiselect. The main page has a demo of a drop down box, see: https://vue-multiselect.js.org/

Would it be possible for you to give me an example code of how to select between the various options on that drop down?

Thank you.

A sample from that site could be

Something like:
import org.openqa.selenium.By as By
import org.openqa.selenium.Keys as Keys
import org.openqa.selenium.WebDriver as WebDriver
import org.openqa.selenium.WebElement as WebElement

import com.kms.katalon.core.webui.driver.DriverFactory as DriverFactory


WebUI.openBrowser('')

// like it better when see all the items
WebUI.maximizeWindow()
WebUI.navigateToUrl('https://vue-multiselect.js.org/')
WebUI.waitForPageLoad(10)

def driver = DriverFactory.getWebDriver();

List<WebElement> list = driver.findElements(By.xpath('//span[@class="badge__name"]'))

for (int cnt = 0; cnt < list.size(); cnt++) {
	dropdownButton = driver.findElement(By.xpath('id("listbox-null")/../preceding-sibling::div[@class="multiselect__select"]'))
	dropdownButton.click();
	WebUI.delay(1)

	mySelectedChoice = driver.findElement(By.xpath("id('null-${cnt}')/span/span"))
	mySelectedChoice.click()
	WebUI.delay(2)
}

Right click on the first drop-down item and select Inspect (you might have to do this twice). That should give you an idea of where you should be looking for a pathway for any elements.

1 Like

This description is ambiguous. I do not understand what you want us, the guys in this forum, to do.

Do not say “various options”. Please do not ask others to guess what you want. Please ask a concrete question that others can think about.

You specified https://vue-multiselect.js.org/ as a subject to work with. But it contains a lot of sentences and sample codes. I do not see which one you want us to look at.

Okay, I hope this isn’t as ambiguous:

  1. On the website https://vue-multiselect.js.org/, there is a demo drop down box on the very first page containing 5 list items.
  2. The private web form I will be working with uses the same style of drop down box, which contains nested elements and isn’t very easy to work with.
  3. I was looking to get a demo of code which is able to automate selection of one of the list items from the drop down box, based on parametrized data from a CSV file.

-Example: Imagine the list was a list of car manufacturers, e.g. BMW, Volkswagen, Honda, Toyota, Porsche.
-My example CSV file would contain other variables, e.g. Date, Time, Damage, Owner Name but also manufacturer
-I would want my code to be able to pull the manufacturer from the CSV file and be able to SELECT the right option from the drop down.

I amended my earlier post to display the items in the drop-down just as an exercise.

However, if you want to MATCH to a manufacturer, I think you would have to either add the manufacturer’s name to the pathway to “just” select that specific one (or adding that specific one to the list of already selected items) or run through all the items in the drop-down comparing until you match. Personally, try the first option.

1 Like

Perhaps I’m missing something, but I have to say this…

What is this test meant to prove? I’m pretty sure there is no user of the app you’re testing that is going to dig out “names” from a CSV file and choose corresponding elements from a dropdown list.

My advice, for what it’s worth…

Pick a few yourself, add them to your test, check they can be selected in the list. Move on and write your next test.

Just saying…

This isn’t so much for testing, it’s to develop an automation process to retrofill data which was previously manually collected into Excel and import it into an online web application.

Let’s look at that HTML element:

<div class="multiselect-example__container">
  <div>
    <div
      tabindex="-1"
      role="combobox"
      aria-owns="listbox-null"
      class="multiselect"
    >
      <div class="multiselect__select"></div>
      <div class="multiselect__tags">
        <div class="multiselect__tags-wrap" style="display: none;"></div>
        <!---->
        <div class="multiselect__spinner" style="display: none;"></div>
        <input
          name=""
          type="text"
          autocomplete="off"
          spellcheck="false"
          placeholder="Pick badges"
          tabindex="0"
          aria-controls="listbox-null"
          class="multiselect__input"
          style="width: 0px; position: absolute; padding: 0px;"
          aria-activedescendant="null-2"
        />
        <!---->
        <span class="multiselect__placeholder"> Pick badges </span>
      </div>
      <div
        tabindex="-1"
        class="multiselect__content-wrapper"
        style="max-height: 300px; display: none;"
      >
        <ul
          role="listbox"
          id="listbox-null"
          class="multiselect__content"
          style="display: inline-block;"
        >
          <!---->
          <li id="null-0" role="option" class="multiselect__element">
            <span
              data-select=""
              data-selected=""
              data-deselect=""
              class="multiselect__option"
              ><span class="badge__name">License</span
              ><img
                src="https://camo.githubusercontent.com/d0e25b09a82bc4bfde9f1e048a092752eebbb4f3/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c75652e7376673f7374796c653d666c6174"
                alt="License"
                class="badge__img"
            /></span>
            <!---->
          </li>
          <li id="null-1" role="option" class="multiselect__element">
            <span
              data-select=""
              data-selected=""
              data-deselect=""
              class="multiselect__option"
              ><span class="badge__name">GitHub Stars</span
              ><img
                src="https://img.shields.io/github/stars/shentao/vue-multiselect.svg?label=Stars"
                alt="GitHub Stars"
                class="badge__img"
            /></span>
            <!---->
          </li>
          <li id="null-2" role="option" class="multiselect__element">
            <span
              data-select=""
              data-selected=""
              data-deselect=""
              class="multiselect__option multiselect__option--highlight"
              ><span class="badge__name">Npm Monthly Downloads</span
              ><img
                src="https://camo.githubusercontent.com/64f9a2333bb303d34b1587e1436b24dee6a8e134/68747470733a2f2f696d672e736869656c64732e696f2f6e706d2f646d2f7675652d6d756c746973656c6563742e737667"
                alt="Npm Monthly Downloads"
                class="badge__img"
            /></span>
            <!---->
          </li>
          <li id="null-3" role="option" class="multiselect__element">
            <span
              data-select=""
              data-selected=""
              data-deselect=""
              class="multiselect__option"
              ><span class="badge__name">Full Test Coverage</span
              ><img
                src="https://camo.githubusercontent.com/47ff0923e959e736113988e900268dfc7a601d3b/68747470733a2f2f636972636c6563692e636f6d2f67682f6d6f6e74657261696c2f7675652d6d756c746973656c6563742f747265652f6d61737465722e7376673f7374796c653d736869656c6426636972636c652d746f6b656e3d35633933316666323866643132353837363130663833353437326265636464353134643039636566"
                alt="Full Test Coverage"
                class="badge__img"
            /></span>
            <!---->
          </li>
          <li id="null-4" role="option" class="multiselect__element">
            <span
              data-select=""
              data-selected=""
              data-deselect=""
              class="multiselect__option"
              ><span class="badge__name">NO Dependencies</span
              ><img
                src="https://img.shields.io/badge/dependencies-none-brightgreen.svg?style=flat"
                alt="NO Dependencies"
                class="badge__img"
            /></span>
            <!---->
          </li>
          <li style="display: none;">
            <span class="multiselect__option"
              ><span
                >Badge not found. Suggest a badge
                <a
                  href="https://github.com/shentao/vue-multiselect/issues"
                  target="_blank"
                  class="typo__link"
                  >here</a
                >.</span
              ></span
            >
          </li>
          <li style="display: none;">
            <span class="multiselect__option">List is empty.</span>
          </li>
        </ul>
      </div>
    </div>
  </div>
  <div class="grid__row start__list">
    <div class="grid__column grid__unit--md-6 list">
      <ul class="list__ul">
        <li class="typo__li">Single / multiple select</li>
        <li class="typo__li">Dropdowns</li>
        <li class="typo__li">
          <a href="#sub-select-with-search" class="typo__link">Searchable</a>
        </li>
        <li class="typo__li">
          <a href="#sub-tagging" class="typo__link">Tagging</a>
        </li>
        <li class="typo__li">Server-side Rendering support</li>
      </ul>
    </div>
    <div class="grid__column grid__unit--md-6 list">
      <ul class="list__ul">
        <li class="typo__li">
          <a href="#sub-vuex-support" class="typo__link"
            >Vuex support by default</a
          >
        </li>
        <li class="typo__li">
          <a href="#sub-asynchronous-select" class="typo__link">Ajax support</a>
        </li>
        <li class="typo__li">
          <a href="#sub-custom-configuration" class="typo__link"
            >Fully configurable</a
          >
        </li>
        <li class="typo__li">+99% test coverage</li>
        <li class="typo__li">No dependencies</li>
      </ul>
    </div>
  </div>
</div>

Given that the multi-select for your project is going to look like this, the question is: how should we handle it?

Breakdown of the action steps, in plain English

Given the example, on that page, we know the following should happen:

  • for each option to click:
    • click the dropdown button
    • wait for dropdown list to spawn
    • click on the right dropdown option
    • wait for dropdown option to disappear
    • wait for the tag to appear

Knowing that, we can now start writing code for it…

Writing code for it?! I want the Katalon Recorder!

Bear with me. You’re going to hate this initially, but if you trust the process, and get good at writing code, you’re going to end up loving it, and having a solid system in place…

NOTE: this code is going to be in Katalon Studio, not Selenium… We’re going to be using the WebUI and TestObjects, not the driver methods and WebElements.

Page Objects

I will not give you full breakdown of that on here, instead I’ll refer you to this question.

Let’s implement this concept:

public class YourPage { 
    public final String MultiSelectPartDesc = "Your Page/Multi-Select Dropdown";

    public void doMultiSelect(List<String> options) { 
        for (String option : options) { 
            final TestObject firstDropdownOption = findTestObject("${this.MultiSelectPartDesc}/First dropdown option");

            WebUI.click(findTestObject("${this.MultiSelectPartDesc}/Dropdown button"));

            WebUI.waitForElementVisible(firstDropdownOption ,
                2);

            WebUI.click(this.getDropdownOption(option));

            WebUI.waitForElementNotVisible(firstDropdownOption ,
                2);

            WebUI.waitForElementPresent(this.getOptionTag(option),
                3);
        }
    }

    public TestObject getDropdownOption(String option) { 
        throw new cucumber.api.PendingException();
    }

    public TestObject getOptionTag(String option) { 
        throw new cucumber.api.PendingException();
    }
}

This is what our Page Object class should look like…

What are those Test Objects?

Yea, so about those… We’re either going to create those ourselves (this means NOT using the Recorder any more, also we write our own xpath for them), or find the ones that the Recorder generated and rename them…

Dropdown button

Recorder should have the dropdown button already. It’s whatever we click on to spawn the dropdown list.

Rename it to, for the sake of this question, Your Page/Multi-Select Dropdown/Dropdown button. This means making a folder called Your Page, inside which create a folder called Multi-Select Dropdown, inside which go this and all the other Test Objects for this multi-select!

First dropdown option

Recorder should have it already, however I would still change its xpath to something like:

(//div[contains(concat(' ', @class, ' '), ' multiselect ')]//span[contains(concat(' ', @class, ' '), ' multiselect__option ')])[1]

NOTE: this will NOT work if you have multiple multiselect widgets on the page. For the example you gave, I had to change the first part of that xpath to :

((//div[contains(concat(' ', @class, ' '), ' multiselect ')])[1]

Alternatively, you can use CSS selector for this one to simplify things:

div.multiselect:first-child span.multiselect__option:first-child

Save this Test Object (or the existing Recorder Test Object) as Your Page/Multi-Select Dropdown/First dropdown option.

The getDropdownOption() method

If we look at the xpath for the first dropdown option (this stuff is why xpath is so commonplace in web automation testing!), we see that we can easily adapt it for selecting dropdown option based on ANY key words:

((//div[contains(concat(' ', @class, ' '), ' multiselect ')])[1]//span[contains(concat(' ', @class, ' '), ' multiselect__option ')])[.//text() = 'License']

is the xpath for the ‘License’ dropdown option for your example

We just add a [.//text() = 'License'] to get that.

Knowing this, we can now implement the method:

    public TestObject getDropdownOption(String option) { 
        return new TestObject("${this.MultiSelectPartDesc}/'${option}' Dropdown option")
            .addProperty("xpath",
                ConditionType.EQUALS,
                "(//div[contains(concat(' ', @class, ' '), ' multiselect ')]//span[contains(concat(' ', @class, ' '), ' multiselect__option ')])[.//text() = '${option}']");
    }

The getOptionTag() method

Let’s select an option and take a look at the DOM for the option tag (we select the ‘License’ option):

<div class="multiselect__tags">
  <div class="multiselect__tags-wrap" style="">
    <span class="multiselect__tag"
      ><span>License</span> <i tabindex="1" class="multiselect__tag-icon"></i
    ></span>
  </div>
  <!---->
  <div class="multiselect__spinner" style="display: none;"></div>
  <input
    name=""
    type="text"
    autocomplete="off"
    spellcheck="false"
    placeholder="Pick badges"
    tabindex="0"
    aria-controls="listbox-null"
    class="multiselect__input"
    style="width: 0px; position: absolute; padding: 0px;"
    aria-activedescendant="null-0"
  />
  <!---->
  <!---->
</div>

The “License” tag is a span, with class multiselect__tag. It’s pretty straightforward what the xpath, and the method, should look like:

    public TestObject getOptionTag(String option) { 
        return new TestObject("${this.MultiSelectPartDesc}/'${option}' Tag")
            .addProperty("xpath",
                ConditionType.EQUALS,
                "(//div[contains(concat(' ', @class, ' '), ' multiselect ')]//span[contains(concat(' ', @class, ' '), ' multiselect__tag ')])[.//text() = '${option}']");
    }

OK this is great, now how do I use all this?

Simple:

  • change your data store to just pass the names (no need for the indices any more)
  • create an instance of the page object (let’s call it page)
  • page.doMultiSelect(yourList)

Lmk if you have any questions