From 333c490722ce03479f498bb2e50acc8482811c19 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Fri, 23 Jan 2026 13:40:28 -0800 Subject: [PATCH 1/8] initial commit for multichoice value --- .../components/domain/DomainFieldRow.java | 5 +++ .../components/domain/DomainFormPanel.java | 10 +++++ .../components/ui/grids/ResponsiveGrid.java | 11 ++++- .../ui/search/FilterFacetedPanel.java | 12 +++++ .../labkey/test/params/FieldDefinition.java | 45 +++++++++++++++++++ 5 files changed, 82 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/components/domain/DomainFieldRow.java b/src/org/labkey/test/components/domain/DomainFieldRow.java index 8c36831e33..cbb05220bf 100644 --- a/src/org/labkey/test/components/domain/DomainFieldRow.java +++ b/src/org/labkey/test/components/domain/DomainFieldRow.java @@ -935,6 +935,11 @@ public String updateLockedTextChoiceValue(String originalValue, String newValue) return alert.getText(); } + public void updateMultiChoiceValue(String originalValue, String newValue) + { + updateValue(originalValue, newValue, true); + } + /** * Select a TextChoice value and check if the edit field is enabled. * diff --git a/src/org/labkey/test/components/domain/DomainFormPanel.java b/src/org/labkey/test/components/domain/DomainFormPanel.java index bc596f1e50..145237c0f2 100644 --- a/src/org/labkey/test/components/domain/DomainFormPanel.java +++ b/src/org/labkey/test/components/domain/DomainFormPanel.java @@ -235,6 +235,16 @@ else if (validator instanceof FieldDefinition.TextChoiceValidator textChoiceVali } fieldRow.setTextChoiceValues(textChoiceValidator.getValues()); } + else if (validator instanceof FieldDefinition.MultiChoiceValidator textChoiceValidator) + { + if(!fieldRow.isExpanded()) fieldRow.expand(); + // TextChoice is a field type; implemented using a special validator. TextChoice field cannot have other validators. + if (validators.size() > 1) + { + throw new IllegalArgumentException("TextChoice fields cannot have additional validators."); + } + fieldRow.setTextChoiceValues(textChoiceValidator.getValues()); + } else { throw new IllegalArgumentException("Validator not supported: " + validator.getClass().getName()); diff --git a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java index e8ee4d0fb9..6ea369501a 100644 --- a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java +++ b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java @@ -40,6 +40,12 @@ import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; import static org.junit.Assert.assertEquals; +import static org.labkey.remoteapi.query.Filter.Operator.CONTAINS_ALL; +import static org.labkey.remoteapi.query.Filter.Operator.CONTAINS_ANY; +import static org.labkey.remoteapi.query.Filter.Operator.CONTAINS_EXACTLY; +import static org.labkey.remoteapi.query.Filter.Operator.CONTAINS_NONE; +import static org.labkey.remoteapi.query.Filter.Operator.DOES_NOT_CONTAIN_EXACTLY; +import static org.labkey.remoteapi.query.Filter.Operator.IN; import static org.labkey.test.WebDriverWrapper.waitFor; public class ResponsiveGrid> extends WebDriverComponent.ElementCache> implements UpdatingComponent @@ -234,15 +240,18 @@ public String filterColumnExpectingError(CharSequence columnIdentifier, Filter.O private GridFilterModal initFilterColumn(CharSequence columnIdentifier, Filter.Operator operator, Object value) { + List listOperators = List.of(IN, CONTAINS_ALL, CONTAINS_ANY, CONTAINS_EXACTLY, CONTAINS_NONE, + DOES_NOT_CONTAIN_EXACTLY); clickColumnMenuItem(columnIdentifier, "Filter...", false); GridFilterModal filterModal = new GridFilterModal(getDriver(), this); if (operator != null) { - if (operator.equals(Filter.Operator.IN) && value instanceof List) + if (listOperators.contains(operator) && value instanceof List) { List values = (List) value; filterModal.selectFacetTab().selectValue(values.get(0)); filterModal.selectFacetTab().checkValues(values.toArray(String[]::new)); + filterModal.selectFacetTab().selectFilter(operator.getDisplayValue()); } else filterModal.selectExpressionTab().setFilter(new FilterExpressionPanel.Expression(operator, value)); diff --git a/src/org/labkey/test/components/ui/search/FilterFacetedPanel.java b/src/org/labkey/test/components/ui/search/FilterFacetedPanel.java index 90a8c78713..1c014b7dbd 100644 --- a/src/org/labkey/test/components/ui/search/FilterFacetedPanel.java +++ b/src/org/labkey/test/components/ui/search/FilterFacetedPanel.java @@ -6,6 +6,7 @@ import org.labkey.test.components.WebDriverComponent; import org.labkey.test.components.html.Checkbox; import org.labkey.test.components.html.Input; +import org.labkey.test.components.react.ReactSelect; import org.labkey.test.components.ui.FilterStatusValue; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -48,6 +49,15 @@ public void selectValue(String value) elementCache().findCheckboxLabel(value).click(); } + /** + * Select a single facet value by clicking its label. Should replace all existing selections. + * @param value desired value + */ + public void selectFilter(String value) + { + elementCache().filterTypeSelects.select(value); + } + /** * Check single facet value by label to see if it is checked or not. * @param value desired value @@ -123,6 +133,8 @@ protected class ElementCache extends Component.ElementCache { protected final Input filterInput = Input(Locator.id("filter-faceted__typeahead-input"), getDriver()).findWhenNeeded(this); + protected final ReactSelect filterTypeSelects = + new ReactSelect.ReactSelectFinder(getDriver()).index(0).findWhenNeeded(this); protected final WebElement checkboxSection = Locator.byClass("labkey-wizard-pills").index(0).refindWhenNeeded(this); protected final Locator.XPathLocator checkboxLabelLoc diff --git a/src/org/labkey/test/params/FieldDefinition.java b/src/org/labkey/test/params/FieldDefinition.java index 78dbb89f17..acbfde8882 100644 --- a/src/org/labkey/test/params/FieldDefinition.java +++ b/src/org/labkey/test/params/FieldDefinition.java @@ -470,6 +470,13 @@ public FieldDefinition setTextChoiceValues(List values) return this; } + public FieldDefinition setMultiChoiceValues(List values) + { + Assert.assertEquals("Invalid field type for text choice values.", ColumnType.MultiChoice, getType()); + setValidators(List.of(new FieldDefinition.MultiChoiceValidator(values))); + return this; + } + public ExpSchema.DerivationDataScopeType getAliquotOption() { return _aliquotOption; @@ -606,6 +613,7 @@ public boolean isMeasureByDefault() ColumnType Sample = new ColumnTypeImpl("Sample", "int", "http://www.labkey.org/exp/xml#sample", new IntLookup( "exp", "Materials")); ColumnType Barcode = new ColumnTypeImpl("Unique ID", "string", "http://www.labkey.org/types#storageUniqueId", null); ColumnType TextChoice = new ColumnTypeImpl("Text Choice", "string", "http://www.labkey.org/types#textChoice", null); + ColumnType MultiChoice = new ColumnTypeImpl("Multi Choice", "string", "http://www.labkey.org/types#multiChoice", null); ColumnType SMILES = new ColumnTypeImpl("SMILES", "string", "http://www.labkey.org/exp/xml#smiles", null); ColumnType Calculation = new ColumnTypeImpl("Calculation", null, "http://www.labkey.org/exp/xml#calculated", null); /** @@ -1140,6 +1148,43 @@ public List getValues() } + public static class MultiChoiceValidator extends FieldValidator + { + private final List _values; + + public MultiChoiceValidator(List values) + { + // The TextChoice validator only has a name and no description or message. + // And the name is generated (not user defined). + super("Text Choice Validator", "", ""); + _values = Collections.unmodifiableList(values); + } + + @Override + protected MultiChoiceValidator getThis() + { + return this; + } + + @Override + protected String getType() + { + return "MultiChoice"; + } + + @Override + protected String getExpression() + { + return EscapeUtil.getTextChoiceValidatorExpression(_values); + } + + public List getValues() + { + return _values; + } + + } + } class ColumnTypeImpl implements FieldDefinition.ColumnType From fabe096a43a10bb6549df6fc72df8f4948c933d2 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Mon, 26 Jan 2026 15:19:27 -0800 Subject: [PATCH 2/8] some changes --- .../components/domain/DomainFieldRow.java | 15 ++++--- .../components/domain/DomainFormPanel.java | 10 +---- .../labkey/test/params/FieldDefinition.java | 41 ++++--------------- 3 files changed, 20 insertions(+), 46 deletions(-) diff --git a/src/org/labkey/test/components/domain/DomainFieldRow.java b/src/org/labkey/test/components/domain/DomainFieldRow.java index cbb05220bf..c066bbf3cd 100644 --- a/src/org/labkey/test/components/domain/DomainFieldRow.java +++ b/src/org/labkey/test/components/domain/DomainFieldRow.java @@ -767,6 +767,11 @@ public DomainFieldRow clickRemoveOntologyConcept() // behind the scenes. Because of that the validator aspect of the TextChoice field is hidden from the user (just like // it is in the product). + public void setAllowMultipleSelections(Boolean allowMultipleSelections) + { + elementCache().allowMultipleSelectionsCheckbox.set(allowMultipleSelections); + } + /** * Set the list of allowed values for a TextChoice field. * @@ -780,6 +785,7 @@ public DomainFieldRow setTextChoiceValues(List values) TextChoiceValueDialog addValuesDialog = new TextChoiceValueDialog(this); addValuesDialog.addValues(values); + return addValuesDialog.clickApply(); } @@ -935,11 +941,6 @@ public String updateLockedTextChoiceValue(String originalValue, String newValue) return alert.getText(); } - public void updateMultiChoiceValue(String originalValue, String newValue) - { - updateValue(originalValue, newValue, true); - } - /** * Select a TextChoice value and check if the edit field is enabled. * @@ -1707,6 +1708,10 @@ protected class ElementCache extends WebDriverComponent.ElementCache public final WebElement domainWarningIcon = Locator.tagWithClass("span", "domain-warning-icon") .findWhenNeeded(this); + // text choice field option + public final Checkbox allowMultipleSelectionsCheckbox = new Checkbox(Locator.inputById("domainpropertiesrow-textChoiceAllowMulti-0-0") + .refindWhenNeeded(this).withTimeout(WAIT_FOR_JAVASCRIPT)); + // lookup field options public final Select lookupContainerSelect = SelectWrapper.Select(Locator.name("domainpropertiesrow-lookupContainer")) .findWhenNeeded(this); diff --git a/src/org/labkey/test/components/domain/DomainFormPanel.java b/src/org/labkey/test/components/domain/DomainFormPanel.java index 145237c0f2..5ba00274a9 100644 --- a/src/org/labkey/test/components/domain/DomainFormPanel.java +++ b/src/org/labkey/test/components/domain/DomainFormPanel.java @@ -234,16 +234,10 @@ else if (validator instanceof FieldDefinition.TextChoiceValidator textChoiceVali throw new IllegalArgumentException("TextChoice fields cannot have additional validators."); } fieldRow.setTextChoiceValues(textChoiceValidator.getValues()); - } - else if (validator instanceof FieldDefinition.MultiChoiceValidator textChoiceValidator) - { - if(!fieldRow.isExpanded()) fieldRow.expand(); - // TextChoice is a field type; implemented using a special validator. TextChoice field cannot have other validators. - if (validators.size() > 1) + if(textChoiceValidator.getMultipleSelections()) { - throw new IllegalArgumentException("TextChoice fields cannot have additional validators."); + fieldRow.setAllowMultipleSelections(textChoiceValidator.getMultipleSelections()); } - fieldRow.setTextChoiceValues(textChoiceValidator.getValues()); } else { diff --git a/src/org/labkey/test/params/FieldDefinition.java b/src/org/labkey/test/params/FieldDefinition.java index acbfde8882..deb72adab1 100644 --- a/src/org/labkey/test/params/FieldDefinition.java +++ b/src/org/labkey/test/params/FieldDefinition.java @@ -472,8 +472,8 @@ public FieldDefinition setTextChoiceValues(List values) public FieldDefinition setMultiChoiceValues(List values) { - Assert.assertEquals("Invalid field type for text choice values.", ColumnType.MultiChoice, getType()); - setValidators(List.of(new FieldDefinition.MultiChoiceValidator(values))); + Assert.assertEquals("Invalid field type for text choice values.", ColumnType.TextChoice, getType()); + setValidators(List.of(new FieldDefinition.TextChoiceValidator(values).setMultipleSelections())); return this; } @@ -613,7 +613,6 @@ public boolean isMeasureByDefault() ColumnType Sample = new ColumnTypeImpl("Sample", "int", "http://www.labkey.org/exp/xml#sample", new IntLookup( "exp", "Materials")); ColumnType Barcode = new ColumnTypeImpl("Unique ID", "string", "http://www.labkey.org/types#storageUniqueId", null); ColumnType TextChoice = new ColumnTypeImpl("Text Choice", "string", "http://www.labkey.org/types#textChoice", null); - ColumnType MultiChoice = new ColumnTypeImpl("Multi Choice", "string", "http://www.labkey.org/types#multiChoice", null); ColumnType SMILES = new ColumnTypeImpl("SMILES", "string", "http://www.labkey.org/exp/xml#smiles", null); ColumnType Calculation = new ColumnTypeImpl("Calculation", null, "http://www.labkey.org/exp/xml#calculated", null); /** @@ -1115,6 +1114,8 @@ public static class TextChoiceValidator extends FieldValidator _values; + private Boolean multipleSelections = false; + public TextChoiceValidator(List values) { // The TextChoice validator only has a name and no description or message. @@ -1146,41 +1147,15 @@ public List getValues() return _values; } - } - - public static class MultiChoiceValidator extends FieldValidator - { - private final List _values; - - public MultiChoiceValidator(List values) - { - // The TextChoice validator only has a name and no description or message. - // And the name is generated (not user defined). - super("Text Choice Validator", "", ""); - _values = Collections.unmodifiableList(values); - } - - @Override - protected MultiChoiceValidator getThis() + public TextChoiceValidator setMultipleSelections() { + this.multipleSelections = true; return this; } - @Override - protected String getType() - { - return "MultiChoice"; - } - - @Override - protected String getExpression() - { - return EscapeUtil.getTextChoiceValidatorExpression(_values); - } - - public List getValues() + public Boolean getMultipleSelections() { - return _values; + return this.multipleSelections; } } From 349d08db85ca3998e31a0171123403d9d740e4ee Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Tue, 27 Jan 2026 16:55:02 -0800 Subject: [PATCH 3/8] add tests for: - SMProAssayCreateTest.testAddResultFields - SMSampleCreateTest.testSamplesWithMultiChoice - SMSourceTypeMultiChoiceTest - ListTest.testMultiChoiceValues --- src/org/labkey/test/WebDriverWrapper.java | 8 +++++ .../components/domain/DomainFieldRow.java | 2 +- src/org/labkey/test/tests/list/ListTest.java | 33 +++++++++++++++++++ 3 files changed, 42 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index b51b78bfc1..c571d84a13 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -3465,6 +3465,14 @@ public void setFormElement(Locator l, String text) setFormElement(el, text); } + public void setListElement(Locator l, List text) + { + List elems = l.waitForElements(new WebDriverWait(getDriver(), Duration.ofMillis(WAIT_FOR_JAVASCRIPT))); + elems.forEach(element->{ + if(text.contains(element.getAttribute("value")) ^ element.isSelected()) element.click(); + }); + } + /** * Clears and sets the text of the specified input element. * Warning: Clear unfocuses the element which causes some inputs to disappear. diff --git a/src/org/labkey/test/components/domain/DomainFieldRow.java b/src/org/labkey/test/components/domain/DomainFieldRow.java index c066bbf3cd..fff227aefc 100644 --- a/src/org/labkey/test/components/domain/DomainFieldRow.java +++ b/src/org/labkey/test/components/domain/DomainFieldRow.java @@ -1709,7 +1709,7 @@ protected class ElementCache extends WebDriverComponent.ElementCache .findWhenNeeded(this); // text choice field option - public final Checkbox allowMultipleSelectionsCheckbox = new Checkbox(Locator.inputById("domainpropertiesrow-textChoiceAllowMulti-0-0") + public final Checkbox allowMultipleSelectionsCheckbox = new Checkbox(Locator.tagWithClass("input", "domain-text-choice-multi") .refindWhenNeeded(this).withTimeout(WAIT_FOR_JAVASCRIPT)); // lookup field options diff --git a/src/org/labkey/test/tests/list/ListTest.java b/src/org/labkey/test/tests/list/ListTest.java index 41f8992f59..7921b0b0b9 100644 --- a/src/org/labkey/test/tests/list/ListTest.java +++ b/src/org/labkey/test/tests/list/ListTest.java @@ -1688,6 +1688,39 @@ public void testAutoIncrementKeyEncoded() _listHelper.deleteList(); } + @Test + public void testMultiChoiceValues() + { + // setup a list with an auto-increment key that we need to make sure is encoded in the form input + String encodedListName = "multiChoiceList"; + String keyName = "'>'"; + List tcValues = List.of("0", "1", "2"); + _listHelper.createList(PROJECT_VERIFY, encodedListName, keyName, col("MCF", ColumnType.TextChoice) + .setMultiChoiceValues(tcValues)); + _listHelper.goToList(encodedListName); + + DataRegionTable table = new DataRegionTable("query", getDriver()); + table.clickInsertNewRow(); + List valuesToChoose = List.of("0", "1"); + Locator loc = Locator.tag("select").append(Locator.tag("option")); + setListElement(loc, valuesToChoose); + + clickButton("Submit"); + table = new DataRegionTable("query", getDriver()); + checker().verifyEquals("Name value not as expected", String.join(" ", valuesToChoose), table.getDataAsText(0, "MCF")); + + table.clickEditRow(0); + valuesToChoose = List.of("1", "2"); + setListElement(loc, valuesToChoose); + clickButton("Submit"); + + // verify the name value is persisted + table = new DataRegionTable("query", getDriver()); + checker().verifyEquals("Name value not as expected", String.join(" ", valuesToChoose), table.getDataAsText(0, "MCF")); + + _listHelper.deleteList(); + } + private List getQueryFormFieldNames() { return Locator.tag("input").attributeStartsWith("name", "quf_") From fd9d70040f697c7191c0e0559299f5de747f213b Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Thu, 29 Jan 2026 11:39:00 -0800 Subject: [PATCH 4/8] new changes for test --- src/org/labkey/test/Locator.java | 5 ++++ src/org/labkey/test/WebDriverWrapper.java | 15 +++++----- .../components/domain/DomainFieldRow.java | 1 - .../components/ui/grids/EditableGrid.java | 3 ++ .../labkey/test/pages/DatasetInsertPage.java | 4 ++- .../test/pages/query/UpdateQueryRowPage.java | 5 ++-- src/org/labkey/test/tests/list/ListTest.java | 30 +++++++++++-------- 7 files changed, 38 insertions(+), 25 deletions(-) diff --git a/src/org/labkey/test/Locator.java b/src/org/labkey/test/Locator.java index 17bd5dc435..badd2c0669 100644 --- a/src/org/labkey/test/Locator.java +++ b/src/org/labkey/test/Locator.java @@ -606,6 +606,11 @@ public static XPathLocator name(String name) return tag("*").withAttribute("name", name); } + public static XPathLocator nameContaining(String name) + { + return tag("*").withAttributeContaining("name", name); + } + public static CssLocator css(String selector) { return new CssLocator(selector); diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index c571d84a13..19a4f69640 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -3465,14 +3465,6 @@ public void setFormElement(Locator l, String text) setFormElement(el, text); } - public void setListElement(Locator l, List text) - { - List elems = l.waitForElements(new WebDriverWait(getDriver(), Duration.ofMillis(WAIT_FOR_JAVASCRIPT))); - elems.forEach(element->{ - if(text.contains(element.getAttribute("value")) ^ element.isSelected()) element.click(); - }); - } - /** * Clears and sets the text of the specified input element. * Warning: Clear unfocuses the element which causes some inputs to disappear. @@ -4067,6 +4059,13 @@ public void selectOptionByText(Locator locator, String text) public void selectOptionByText(WebElement selectElement, String value) { + if(Boolean.parseBoolean(selectElement.getAttribute("multiple"))) { + List elems = selectElement.findElements(Locator.tag("option")); + elems.forEach(element->{ + if(value.contains(element.getAttribute("value")) ^ element.isSelected()) element.click(); + }); + return; + } Select select = new Select(selectElement); select.selectByVisibleText(value); } diff --git a/src/org/labkey/test/components/domain/DomainFieldRow.java b/src/org/labkey/test/components/domain/DomainFieldRow.java index fff227aefc..ef83ced7cb 100644 --- a/src/org/labkey/test/components/domain/DomainFieldRow.java +++ b/src/org/labkey/test/components/domain/DomainFieldRow.java @@ -785,7 +785,6 @@ public DomainFieldRow setTextChoiceValues(List values) TextChoiceValueDialog addValuesDialog = new TextChoiceValueDialog(this); addValuesDialog.addValues(values); - return addValuesDialog.clickApply(); } diff --git a/src/org/labkey/test/components/ui/grids/EditableGrid.java b/src/org/labkey/test/components/ui/grids/EditableGrid.java index 04aba55749..ad279022a0 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -507,11 +507,14 @@ public WebElement setCellValue(int row, CharSequence columnIdentifier, Object va if (value instanceof List) { + // If this is a list assume that it will need a lookup. List values = (List) value; ReactSelect lookupSelect = elementCache().lookupSelect(gridCell); + lookupSelect.clearSelection(); + lookupSelect.open(); for (String _value : values) diff --git a/src/org/labkey/test/pages/DatasetInsertPage.java b/src/org/labkey/test/pages/DatasetInsertPage.java index 4651210bdf..33a517ecdc 100644 --- a/src/org/labkey/test/pages/DatasetInsertPage.java +++ b/src/org/labkey/test/pages/DatasetInsertPage.java @@ -23,6 +23,8 @@ import org.openqa.selenium.WebElement; import java.util.Map; +import java.util.Objects; +import java.util.Optional; import static org.labkey.test.util.EscapeUtil.FORM_FIELD_PREFIX; @@ -82,7 +84,7 @@ private void tryInsert(Map values) { for (Map.Entry entry : values.entrySet()) { - WebElement fieldInput = Locator.name(EscapeUtil.getFormFieldName(entry.getKey())).findElement(getDriver()); + WebElement fieldInput = Locator.nameContaining(EscapeUtil.getFormFieldName(entry.getKey())).findElement(getDriver()); String type = fieldInput.getAttribute("type"); switch (type) { diff --git a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java index dcb2efa47e..c2bdd1a975 100644 --- a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java +++ b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java @@ -18,6 +18,7 @@ import java.io.File; import java.util.HashMap; +import java.util.List; import java.util.Map; public class UpdateQueryRowPage extends LabKeyPage @@ -99,7 +100,7 @@ public UpdateQueryRowPage setField(String fieldName, String value) WebElement field = elementCache().findField(fieldName); if (field.getTagName().equals("select")) { - setField(fieldName, OptionSelect.SelectOption.textOption(value)); + selectOptionByText(field, value); } else { @@ -186,7 +187,7 @@ WebElement findField(String name) { if (!fieldMap.containsKey(name)) { - fieldMap.put(name, Locator.name(EscapeUtil.getFormFieldName(name)).findElement(this)); + fieldMap.put(name, Locator.nameContaining(EscapeUtil.getFormFieldName(name)).findElement(this)); } return fieldMap.get(name); } diff --git a/src/org/labkey/test/tests/list/ListTest.java b/src/org/labkey/test/tests/list/ListTest.java index 7921b0b0b9..d942a7e643 100644 --- a/src/org/labkey/test/tests/list/ListTest.java +++ b/src/org/labkey/test/tests/list/ListTest.java @@ -84,6 +84,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.stream.Collectors; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; @@ -1691,32 +1692,35 @@ public void testAutoIncrementKeyEncoded() @Test public void testMultiChoiceValues() { - // setup a list with an auto-increment key that we need to make sure is encoded in the form input + // setup a list with an auto-increment key and multi text choice field String encodedListName = "multiChoiceList"; String keyName = "'>'"; - List tcValues = List.of("0", "1", "2"); - _listHelper.createList(PROJECT_VERIFY, encodedListName, keyName, col("MCF", ColumnType.TextChoice) + String columnName = "MultiChoiceField"; + List tcValues = List.of("~`!@#$%^&*()_+=[]{}\\|';:\"<>?,./", "1", "2"); + _listHelper.createList(PROJECT_VERIFY, encodedListName, keyName, col(columnName, ColumnType.TextChoice) .setMultiChoiceValues(tcValues)); _listHelper.goToList(encodedListName); DataRegionTable table = new DataRegionTable("query", getDriver()); table.clickInsertNewRow(); - List valuesToChoose = List.of("0", "1"); - Locator loc = Locator.tag("select").append(Locator.tag("option")); - setListElement(loc, valuesToChoose); + String valuesToChoose = tcValues.subList(1, 3).stream() + .sorted() + .collect(Collectors.joining(" ")); + Locator loc = Locator.nameContaining(EscapeUtil.getFormFieldName(columnName)); + selectOptionByText(loc, valuesToChoose); clickButton("Submit"); - table = new DataRegionTable("query", getDriver()); - checker().verifyEquals("Name value not as expected", String.join(" ", valuesToChoose), table.getDataAsText(0, "MCF")); + checker().verifyEquals("Multi choice value not as expected", valuesToChoose, table.getDataAsText(0, columnName)); table.clickEditRow(0); - valuesToChoose = List.of("1", "2"); - setListElement(loc, valuesToChoose); + valuesToChoose = tcValues.subList(1, 3).stream() + .sorted() + .collect(Collectors.joining(" ")); + selectOptionByText(loc, valuesToChoose); clickButton("Submit"); - // verify the name value is persisted - table = new DataRegionTable("query", getDriver()); - checker().verifyEquals("Name value not as expected", String.join(" ", valuesToChoose), table.getDataAsText(0, "MCF")); + // verify the multi choice value is persisted + checker().verifyEquals("Multi choice value not as expected", valuesToChoose, table.getDataAsText(0, columnName)); _listHelper.deleteList(); } From f349d82ba04436eb6077c27a54423406a7c4c085 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Mon, 2 Feb 2026 17:00:59 -0800 Subject: [PATCH 5/8] add test testMultiChoiceUpdateFromFile(), testMultiChoiceEditInGridDrag() fix test testMultiChoiceEditInBulk() --- .../ui/entities/EntityBulkUpdateDialog.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java b/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java index a5edaae738..7c9309caba 100644 --- a/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java +++ b/src/org/labkey/test/components/ui/entities/EntityBulkUpdateDialog.java @@ -124,6 +124,19 @@ public EntityBulkUpdateDialog setSelectionField(CharSequence fieldIdentifier, Li return this; } + /** + * Clear the field (fieldIdentifier). + * + * @param fieldIdentifier Identifier for the field; name ({@link String}) or fieldKey ({@link FieldKey}) + * @return this component + */ + public EntityBulkUpdateDialog clearSelection(CharSequence fieldIdentifier) + { + FilteringReactSelect reactSelect = enableSelectionField(fieldIdentifier); + reactSelect.clearSelection(); + return this; + } + /** * @param fieldIdentifier Identifier for the field; name ({@link String}) or fieldKey ({@link FieldKey}) * @param selectValue value to select From e5ec9dbc73f9ac324c0f45a36945eca9cea2e702 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:13:25 -0800 Subject: [PATCH 6/8] -delete unused imports --- src/org/labkey/test/pages/DatasetInsertPage.java | 2 -- src/org/labkey/test/pages/query/UpdateQueryRowPage.java | 3 +-- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/org/labkey/test/pages/DatasetInsertPage.java b/src/org/labkey/test/pages/DatasetInsertPage.java index 33a517ecdc..4a922f85c7 100644 --- a/src/org/labkey/test/pages/DatasetInsertPage.java +++ b/src/org/labkey/test/pages/DatasetInsertPage.java @@ -23,8 +23,6 @@ import org.openqa.selenium.WebElement; import java.util.Map; -import java.util.Objects; -import java.util.Optional; import static org.labkey.test.util.EscapeUtil.FORM_FIELD_PREFIX; diff --git a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java index c2bdd1a975..ce442e3428 100644 --- a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java +++ b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java @@ -15,10 +15,9 @@ import org.labkey.test.util.EscapeUtil; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; - +Ï import java.io.File; import java.util.HashMap; -import java.util.List; import java.util.Map; public class UpdateQueryRowPage extends LabKeyPage From 33683e091b91dff5dc9df449103c0eb5343ed782 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:25:27 -0800 Subject: [PATCH 7/8] -delete unexpected symbol --- src/org/labkey/test/pages/query/UpdateQueryRowPage.java | 1 - 1 file changed, 1 deletion(-) diff --git a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java index ce442e3428..0f16d59a0e 100644 --- a/src/org/labkey/test/pages/query/UpdateQueryRowPage.java +++ b/src/org/labkey/test/pages/query/UpdateQueryRowPage.java @@ -15,7 +15,6 @@ import org.labkey.test.util.EscapeUtil; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; -Ï import java.io.File; import java.util.HashMap; import java.util.Map; From 9a5773a0448375293c8d0fc7fd82d77cb1451bd8 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Thu, 5 Feb 2026 14:58:33 -0800 Subject: [PATCH 8/8] -some code style fixes -add new shuffleSelect -replace my method with shuffleSelect --- src/org/labkey/test/util/TestDataGenerator.java | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/org/labkey/test/util/TestDataGenerator.java b/src/org/labkey/test/util/TestDataGenerator.java index 854b2d2952..8b9431a485 100644 --- a/src/org/labkey/test/util/TestDataGenerator.java +++ b/src/org/labkey/test/util/TestDataGenerator.java @@ -60,6 +60,7 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Random; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; import java.util.function.Function; @@ -956,6 +957,12 @@ public ImportDataResponse importRows(Connection cn, List> ro return getQueryHelper(cn).importData(TestDataUtils.stringFromRows(TestDataUtils.rowListsFromMaps(rows)), lookupByAlternateKey); } + public static List shuffleSelect(List allFields) + { + int randomSize = new Random().nextInt(allFields.size()) + 1; + return shuffleSelect(allFields, randomSize); + } + public static List shuffleSelect(List allFields, int selectCount) { List shuffled = new ArrayList<>(allFields);