From 1b727f69b6e031cad849f26627014da19c32ff85 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 29 Oct 2025 11:08:39 -0700 Subject: [PATCH 1/2] Make FilteringReactSelect prefer exact matches --- .../components/react/BaseReactSelect.java | 12 ++-- .../react/FilteringReactSelect.java | 57 ++++++++----------- .../test/components/react/ReactSelect.java | 3 +- 3 files changed, 31 insertions(+), 41 deletions(-) diff --git a/src/org/labkey/test/components/react/BaseReactSelect.java b/src/org/labkey/test/components/react/BaseReactSelect.java index 43aec6e465..16ae54338f 100644 --- a/src/org/labkey/test/components/react/BaseReactSelect.java +++ b/src/org/labkey/test/components/react/BaseReactSelect.java @@ -191,7 +191,7 @@ public boolean hasOption(String value) elementCache().input.sendKeys(value); try { - var optionElement = ReactSelect.Locators.options.containing(value); + var optionElement = Locators.option.containing(value); optionElement.waitForElement(elementCache().selectMenu, 4000); elementCache().input.clear(); return true; @@ -374,7 +374,7 @@ public List getOptionElements() // Can only get the list of items once the list has been opened. if (!alreadyOpened) open(); - return Locators.listItems.findElements(getComponentElement()); + return Locators.option.findElements(getComponentElement()); } /** @@ -391,7 +391,7 @@ public List getOptions(Function optionMapper) if (!alreadyOpened) open(); - List optionElements = Locators.listItems.findElements(getComponentElement()); + List optionElements = Locators.option.findElements(getComponentElement()); List rawItems = optionElements.stream().map(optionMapper).toList(); // If it wasn't open before close it, otherwise leave it in the open state. @@ -486,13 +486,13 @@ protected class ElementCache extends WebDriverComponent.ElementCache List getOptions() { - return Locators.options.findElements(selectMenu); + return Locators.option.findElements(selectMenu); } @NotNull WebElement findOption(String option) { - return Locators.options.withText(option).findElement(selectMenu); + return Locators.option.withText(option).findElement(selectMenu); } } @@ -504,7 +504,6 @@ private Locators() } public static final Locator.XPathLocator option = Locator.tagWithClass("div", "select-input__option"); - public static final Locator options = Locator.tagWithClass("div", "select-input__option"); public static final Locator placeholder = Locator.tagWithClass("div", "select-input__placeholder"); public static final Locator clear = Locator.tagWithClass("div","select-input__clear-indicator"); public static final Locator arrow = Locator.tagWithClass("div","select-input__dropdown-indicator"); @@ -514,7 +513,6 @@ private Locators() public static final Locator.XPathLocator multiValueRemove = Locator.tagWithClass("div", "select-input__multi-value__remove"); public static final Locator.XPathLocator singleValueLabel = Locator.tagWithClass("div", "select-input__single-value"); public static final Locator loadingSpinner = Locator.tagWithClass("span", "select-input__loading-indicator"); - public static final Locator listItems = Locator.tagWithClass("div", "select-input__option"); public static final Locator.XPathLocator helpBlock = Locator.tagWithClass("span", "help-block"); public static Locator.XPathLocator selectContainer() diff --git a/src/org/labkey/test/components/react/FilteringReactSelect.java b/src/org/labkey/test/components/react/FilteringReactSelect.java index febe1df91b..4580b8e34b 100644 --- a/src/org/labkey/test/components/react/FilteringReactSelect.java +++ b/src/org/labkey/test/components/react/FilteringReactSelect.java @@ -53,7 +53,7 @@ public FilteringReactSelect typeAheadSelect(String value, String optionText, Str scrollIntoView(); open(); - var elementToClick = ReactSelect.Locators.options.containing(optionText); + var elementToClick = Locators.option.containing(optionText); var elementToWaitFor = getValueLabelLocator().containing(selectedOptionLabel); List options = setFilter(value); @@ -121,43 +121,34 @@ public FilteringReactSelect filterSelect(String value, Locator elementToWaitFor) { waitForReady(); scrollIntoView(); - WebElement success = null; - int tryCount = 0; - while (null == success && tryCount < 6) - { - tryCount++; - open(); + open(); - if (isMulti() || hasValue()) - sleep(250); - setFilter(value); + if (isMulti() || hasValue()) + sleep(250); + setFilter(value); - WebElement elemToClick = Locator.waitForAnyElement( - new FluentWait(getComponentElement()).withTimeout(Duration.ofMillis(WAIT_FOR_JAVASCRIPT)), - Locators.options.containing(value)); + WebElement elemToClick = Locator.waitForAnyElement(getWrapper().shortWait(), + Locators.option.withDescendant(Locator.tagWithText("strong", value)), // Exact match + Locators.option.containing(value)); - log("clicking item with value [" +value+"]"); - getWrapper().scrollIntoView(elemToClick); + log("clicking item with value [" + value + "]"); + getWrapper().scrollIntoView(elemToClick); - WebDriverWrapper.waitFor(()-> { - try - { - if (isExpanded()) - elemToClick.click(); - sleep(250); - return !isExpanded(); - } - catch (StaleElementReferenceException retry) - { - return false; - } - },"failed to select item "+ elemToClick.getAttribute("class") +" by clicking", WAIT_FOR_JAVASCRIPT); - - success = elementToWaitFor.findElement(getComponentElement()); - } + WebDriverWrapper.waitFor(() -> { + try + { + if (isExpanded()) + elemToClick.click(); + sleep(250); + return !isExpanded(); + } + catch (StaleElementReferenceException retry) + { + return false; + } + }, "failed to select item " + elemToClick.getAttribute("class") + " by clicking", WAIT_FOR_JAVASCRIPT); - if (success == null) - log("Expected selection was not found. Selected value(s) are:" + getSelections()); + elementToWaitFor.findElement(getComponentElement()); close(); return this; diff --git a/src/org/labkey/test/components/react/ReactSelect.java b/src/org/labkey/test/components/react/ReactSelect.java index 4663095f18..e61e0c5b33 100644 --- a/src/org/labkey/test/components/react/ReactSelect.java +++ b/src/org/labkey/test/components/react/ReactSelect.java @@ -24,7 +24,7 @@ public class ReactSelect extends BaseReactSelect { - private Function _optionLocFactory = Locators.options::withText; + private Function _optionLocFactory = Locators.option::withText; public ReactSelect(WebElement element, WebDriver driver) { @@ -34,6 +34,7 @@ public ReactSelect(WebElement element, WebDriver driver) public ReactSelect(ReactSelect wrapped) { this(wrapped.getComponentElement(), wrapped.getDriver()); + setOptionLocator(wrapped._optionLocFactory); } public static ReactSelectFinder finder(WebDriver driver) From 3d355071ccd853763ba76b99bee62e823d4eaf5b Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 29 Oct 2025 11:21:05 -0700 Subject: [PATCH 2/2] make nullable --- .../test/components/react/FilteringReactSelect.java | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/components/react/FilteringReactSelect.java b/src/org/labkey/test/components/react/FilteringReactSelect.java index 4580b8e34b..b005c4a01c 100644 --- a/src/org/labkey/test/components/react/FilteringReactSelect.java +++ b/src/org/labkey/test/components/react/FilteringReactSelect.java @@ -4,17 +4,15 @@ */ package org.labkey.test.components.react; +import org.jetbrains.annotations.Nullable; import org.labkey.test.Locator; import org.labkey.test.WebDriverWrapper; import org.openqa.selenium.NoSuchElementException; -import org.openqa.selenium.SearchContext; import org.openqa.selenium.StaleElementReferenceException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; -import org.openqa.selenium.support.ui.FluentWait; -import java.time.Duration; import java.util.List; import static org.labkey.test.BaseWebDriverTest.WAIT_FOR_JAVASCRIPT; @@ -117,7 +115,7 @@ public FilteringReactSelect filterSelect(String value) // adds text for usage in /* for use with editable instances of a reactSelect, where the options aren't shown unless type-ahead filter information is keyed in first */ - public FilteringReactSelect filterSelect(String value, Locator elementToWaitFor) + public FilteringReactSelect filterSelect(String value, @Nullable Locator elementToWaitFor) { waitForReady(); scrollIntoView(); @@ -148,7 +146,8 @@ public FilteringReactSelect filterSelect(String value, Locator elementToWaitFor) } }, "failed to select item " + elemToClick.getAttribute("class") + " by clicking", WAIT_FOR_JAVASCRIPT); - elementToWaitFor.findElement(getComponentElement()); + if (elementToWaitFor != null) + elementToWaitFor.findElement(getComponentElement()); close(); return this;