How can change the value and fire the onValueChange() event of a Single Selection using Selenium?

I am trying to write tests against our Ignition Platform and having a very difficult time. Seems that the Single Selection component is not a real HTML select element when rendered by rather a series of divs and inputs as show below.

<div class="ia_dropdown iaDropdownCommon iaDropdownCommon_single-select iaDropdownCommon_search_enabled" data-component="ia.input.dropdown" data-component-path="C.0:1:14" id="LineSelectionId" style="flex: 0 1 250px;">
<div tabindex="0" style="">
<div class="iaDropdownCommon_container iaDropdownCommon_value-container" data-children-count="1">
<div class="ia_dropdown__valueSingle">
<div class="iaDropdownCommon_value ia_dropdown__valuePill__value" data-children-count="0">CNC 06</div>
</div>
<input class="iaDropdownCommon_search ia_dropdown__search empty" spellcheck="false" size="1" tabindex="-1" type="text" value="">
</div>
<svg class="material-icons md-24 iaDropdownCommon_expandIcon ia_dropdown__expandIcon"><symbol viewBox="0 0 24 24" id="expand_more"><path d="M16.59 8.59L12 13.17 7.41 8.59 6 10l6 6 6-6z"></path></symbol><use xlink:href="#expand_more"></use></svg>
</div>
</div>

installing the React Developer tools in Chrome has allowed me to look at the props and event defined so I can see that it has an onValueChange() event which I believe gets triggered when the value changes:

allowCustomOptions:false
className:undefined
clientScope:"C"
data-component:"ia.input.dropdown"
data-component-path:"C.0:1:14"
enabled:true
id:"LineSelectionId"
isPreviewing:true
multiSelect:false
onValueChange:Ć’ bound onValueChange() {}
optionStyle:{className: undefined, style: undefined}
options:[{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, …]
placeholder:{color: "", icon: {…}, text: "Select Line..."}
search:{enabled: true, matching: "any", noResultsText: "No…}
showClearIcon:false
style:{flexBasis: "250px", flexGrow: 0, flexShrink: 1}
value:{isDisabled: undefined, label: "CNC 06", value: "CN…}
wrapMultiSelectValues:true

I can change the value in the .iaDropdownCommon_value div by using the following “execute script” command in Selenium:

document.querySelector(".iaDropdownCommon_value").innerHTML = 'ZZTest'

But my question is, how do I then trigger the onValueChange() event on the component in order for the corresponding onActionPerformed event to be fired on the component?

I have tried using the following to no avail :frowning:

LineSelectionId.dispatchEvent(new Event("valueChange", {bubbles: true}))
    ...
    _OPTION_LOCATOR = 'a.iaDropdownCommon_option'
    ...

    def select_by_label_text(web_element, label_text):
        """
        We do NOT have the option to use "Select(ia_element).select_by_visible_text(label_text)" because the
        dropdown HTML element is NOT a select - it is a div with data-component="ia.input.dropdown".
        :param web_element: WebElement - The singular element which is the target dropdown
        :param label_text: str - the label you'd like to select
        """
        label_text = str(label_text)
        if label_text not in Dropdown.get_selected_options(web_element):
            Dropdown.expand_if_collapsed(web_element=web_element)
            labels = WebDriverWait(web_element.parent, 2).until(
                ec.presence_of_all_elements_located((By.CSS_SELECTOR, Dropdown._OPTION_LOCATOR)))
            option = list(filter(lambda e: e.text == label_text, labels))[0]
            Dropdown.wait_on_binding(0.25)
            option.click()
            Dropdown.wait_on_binding(0.5)

We have a couple of hard-coded waits in our own testing (wait_on_binding() is just a dressed-up call to sleep())because we found that we could more reliably check values if we gave bindings time to evaluate - which makes sense because Dropdowns are ALWAYS driving bindings.

1 Like

thank you for the reply. So I have to fire an onclick event on the options?

I absolutely recommend that you use Selenium/WebDriver to click elements where possible because it’s what a person would do, and will therefore give you more reliable results. I’ve had bad experiences with trying to bubble up click/touch/focus events in JavaScript.

I trying to use the Selenium IDE to write the tests cause it will be easier for my other teammates to use. I’m just trying to navigate around the HTML markup that Perspective outputs. The options seem to be an entire separate node that I cannot seems to look at since they disappear everytime I try to inspect them in the dev tools. Needless to say, this is very frustrating.

Would you be willing to post the code for this? Still having issues with Selenium leaving the options opened :frowning:

On the bright side... using the Sources F8 trick I was able to finally get the list of options for a dropdown... Posting in case future Googlers need it

<div class="ia_componentModal component-modal-large-viewport component-modal-below hide-arrow iaDropdownCommon_options_modal ia_dropdown__optionsModal"
    style="width: 217px; left: 1701.86px; top: 67.2344px; z-index: 12500;">
    <div>
        <div class="iaDropdownCommon_options"><a class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">CNC
                04</a><a class="iaDropdownCommon_option ia_dropdown__option ia_dropdown__option--selected selected"
                tabindex="0">CNC 06</a><a class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">CNC 07</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">CNC 08</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 01</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 02</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 03</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 04</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 05</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 06</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 07</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 10</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 11</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 12</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">DCM 13</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">Ford ML LH</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">Ford ML RH</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">Heat Treat</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">Melt Room</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">Restrike 01</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">Restrike 02</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">SUV CNC 13A</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">T1XX</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">TLX</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">X-Ray 01</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">X-Ray 03</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">Zerv CNC</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">ZF</a><a
                class="iaDropdownCommon_option ia_dropdown__option" tabindex="0">ZZTest</a></div>
    </div>
</div>
    _ARROW_LOCATOR = 'svg.iaDropdownCommon_expandIcon'
    _MENU_LIST_LOCATOR = 'div.iaDropdownCommon_options'

    @staticmethod
    def expand_if_collapsed(web_element: WebElement):
        if not Dropdown._any_dropdown_is_displaying_options(driver=web_element.parent):
            Dropdown._get_expand_collapse_icon(web_element=web_element).click()

    @classmethod
    def _any_dropdown_is_displaying_options(cls, driver):
        try:
            return driver.find_element_by_css_selector(Dropdown._MENU_LIST_LOCATOR) is not None
        except NoSuchElementException:
            return False

    @classmethod
    def _get_expand_collapse_icon(cls, web_element):
        return web_element.find_element_by_css_selector(Dropdown._ARROW_LOCATOR)

You could also place a breakpoint on the <body> of the page and set it to break on subtree modifications. This will result in the page pausing anytime something changes. Once the "menu list" is visible you can inspect it and examine the internal elements.

That is a really good trick :slight_smile: Thank you!!!

1 Like