From 252df22adcf773cb0b788639c9d2ee52003676f4 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 12 Aug 2025 16:03:24 -0700 Subject: [PATCH 01/14] Add domain name fuzzing to many tests --- src/org/labkey/test/util/DomainUtils.java | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/util/DomainUtils.java b/src/org/labkey/test/util/DomainUtils.java index 9cc20bce6b..9266495c22 100644 --- a/src/org/labkey/test/util/DomainUtils.java +++ b/src/org/labkey/test/util/DomainUtils.java @@ -95,11 +95,18 @@ public static void ensureDeleted(String containerPath, String schema, String tab } public enum DomainKind { - DataClass, - SampleSet, + Assay, + DataClass, // aka "Sources" + SampleSet, // aka "Sample Type" IntList, VarList, StudyDatasetDate, - StudyDatasetVisit + StudyDatasetVisit, + ; + + public String randomName(String namePart) + { + return TestDataGenerator.randomDomainName(namePart, this); + } } } From 8e8b93b8af8580722ff3da65615387fbc23eeda5 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Thu, 14 Aug 2025 17:36:42 -0700 Subject: [PATCH 02/14] Fuzzing fixes --- .../test/components/ui/grids/FieldReferenceManager.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java b/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java index cb2c4de07c..d70a851fd8 100644 --- a/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java +++ b/src/org/labkey/test/components/ui/grids/FieldReferenceManager.java @@ -1,5 +1,6 @@ package org.labkey.test.components.ui.grids; +import org.apache.commons.lang3.StringUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.labkey.test.params.FieldKey; @@ -132,7 +133,11 @@ else if (_fieldLabels.size() < _fieldReferences.size()) } } - return null; + String capitalized = StringUtils.capitalize(label); + if (capitalized.equals(label)) + return null; + else + return findColumnHeaderByLabel(capitalized); // Handle domain names that aren't capitalized } public static class FieldReference From 740149e68154f066249c6263c3aac465d4a24f24 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 15 Aug 2025 17:30:18 -0700 Subject: [PATCH 03/14] More fuzzing fixes --- src/org/labkey/test/AppLocators.java | 8 +++++ src/org/labkey/test/Locators.java | 2 ++ .../test/selenium/ReclickingWebElement.java | 6 ++++ .../test/tests/TextChoiceSampleTypeTest.java | 34 ++++++++++++++----- src/org/labkey/test/util/AuditLogHelper.java | 26 ++++++++++++++ 5 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 src/org/labkey/test/AppLocators.java diff --git a/src/org/labkey/test/AppLocators.java b/src/org/labkey/test/AppLocators.java new file mode 100644 index 0000000000..d61f6980e5 --- /dev/null +++ b/src/org/labkey/test/AppLocators.java @@ -0,0 +1,8 @@ +package org.labkey.test; + +public abstract class AppLocators +{ + private AppLocators() {} + + public static final Locator.XPathLocator detailHeaderName = Locator.tagWithClass("h2", "detail__header--name"); +} diff --git a/src/org/labkey/test/Locators.java b/src/org/labkey/test/Locators.java index bd688dfced..ec7ec8fdb4 100644 --- a/src/org/labkey/test/Locators.java +++ b/src/org/labkey/test/Locators.java @@ -17,6 +17,8 @@ public abstract class Locators { + private Locators() { } + public static final Locator documentRoot = Locator.css(":root"); public static final Locator.IdLocator folderMenu = Locator.id("folderBar"); public static final Locator.XPathLocator labkeyError = Locator.byClass("labkey-error"); diff --git a/src/org/labkey/test/selenium/ReclickingWebElement.java b/src/org/labkey/test/selenium/ReclickingWebElement.java index 65dc66e8e6..453f9dd5ec 100644 --- a/src/org/labkey/test/selenium/ReclickingWebElement.java +++ b/src/org/labkey/test/selenium/ReclickingWebElement.java @@ -15,6 +15,8 @@ */ package org.labkey.test.selenium; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.Strings; import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableObject; import org.jetbrains.annotations.NotNull; @@ -214,6 +216,10 @@ private void revealElement(WebElement el, String shortMessage) Locator.XPathLocator interceptingElLoc = parseInterceptingElementLoc(shortMessage); if (interceptingElLoc != null) { + if (Strings.CS.containsAny(interceptingElLoc.toString(), "popover", "tip")) + { + new Actions(getDriver()).moveToLocation(0,0).perform(); + } List interceptingElements = interceptingElLoc.findElements(getDriver()); TestLogger.debug("Found %s element(s) matching extracted locator: %s".formatted(interceptingElements.size(), shortMessage)); if (interceptingElements.size() == 1) diff --git a/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java b/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java index e14f10e49c..ff9ddaf7f5 100644 --- a/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java +++ b/src/org/labkey/test/tests/TextChoiceSampleTypeTest.java @@ -450,12 +450,29 @@ public void testUpdatingAndDeletingValuesInSampleType() throws IOException, Comm updatePage.clickSave(); log("Validate that the expected rows after the update are in the log."); - String fieldOldValues = "Name=TextChoice_Field_1&Type=String&Scale=4000&PHI=Not%20PHI&DefaultScale=Linear&Required=false&Hidden=false&MvEnabled=false&Measure=false&Dimension=false" + - "&ShownInInsert=true&ShownInDetails=true&ShownInUpdate=true&ShownInLookupView=false&RecommendedVariable=false&ExcludedFromShifting=false&Scannable=false" + - "&DefaultValueType=Editable%20default&Validator=Text%20Choice%20Validator%2C%20%C3%85%5C%7C%C3%85%7CBB%7CCC%7CDD%7CE%20E%20E%7C%C2%83%C2%83%7CGG%7CH%2C%20Text%20Choice%20Validator"; - String fieldUpdateValues = "Name=TextChoice_Field_1&Type=String&Scale=4000&PHI=Not%20PHI&DefaultScale=Linear&Required=false&Hidden=false&MvEnabled=false&Measure=false&Dimension=false" + - "&ShownInInsert=true&ShownInDetails=true&ShownInUpdate=true&ShownInLookupView=false&RecommendedVariable=false&ExcludedFromShifting=false&Scannable=false" + - "&DefaultValueType=Editable%20default&Validator=Text%20Choice%20Validator%2C%20BB%7CCC%20and%20here%20is%20an%20update%7CE%20E%20E%7CGG%7CH%7C%C2%83%C2%83%20updated%7C%C3%85%5C%7C%C3%85%2C%20Text%20Choice%20Validator"; + String fieldSharedValues = AuditLogHelper.encodeValues( + "Name", textChoiceFieldName, + "Type", "String", + "Scale", "4000", + "PHI", "Not PHI", + "DefaultScale", "Linear", + "Required", "false", + "Hidden", "false", + "MvEnabled", "false", + "Measure", "false", + "Dimension", "false", + "ShownInInsert", "true", + "ShownInDetails", "true", + "ShownInUpdate", "true", + "ShownInLookupView", "false", + "RecommendedVariable", "false", + "ExcludedFromShifting", "false", + "Scannable", "false", + "DefaultValueType", "Editable default"); + String fieldOldValues = fieldSharedValues + "&" + AuditLogHelper.encodeValues( + "Validator", "Text Choice Validator, \u00C5\\|\u00C5|BB|CC|DD|E E E|\u0083\u0083|GG|H, Text Choice Validator"); + String fieldUpdateValues = fieldSharedValues + "&" + AuditLogHelper.encodeValues( + "Validator", "Text Choice Validator, BB|CC and here is an update|E E E|GG|H|\u0083\u0083 updated|\u00C5\\|\u00C5, Text Choice Validator"); AuditLogHelper.DetailedAuditEventRow fieldEvent = new AuditLogHelper.DetailedAuditEventRow(null, textChoiceFieldName, "Modified", "The following property was updated: Validator", "", fieldOldValues, fieldUpdateValues, null); AuditLogHelper.DetailedAuditEventRow expectedDomainEvent = new AuditLogHelper.DetailedAuditEventRow(null, sampleTypeName, null, @@ -548,9 +565,8 @@ public void testUpdatingAndDeletingValuesInSampleType() throws IOException, Comm updatePage.clickSave(); log("Validate that the expected rows after the update are in the log."); - String fieldUpdateValues2 = "Name=TextChoice_Field_1&Type=String&Scale=4000&PHI=Not%20PHI&DefaultScale=Linear&Required=false&Hidden=false&MvEnabled=false&Measure=false" + - "&Dimension=false&ShownInInsert=true&ShownInDetails=true&ShownInUpdate=true&ShownInLookupView=false&RecommendedVariable=false&ExcludedFromShifting=false" + - "&Scannable=false&DefaultValueType=Editable%20default&Validator=Text%20Choice%20Validator%2C%20BB%7CCC%20and%20here%20is%20an%20update%7CE%20E%20E%7CGG%7CH%20no%20change%7C%C2%83%C2%83%20updated%7C%C3%85%5C%7C%C3%85%2C%20Text%20Choice%20Validator"; + String fieldUpdateValues2 = fieldSharedValues + "&" + AuditLogHelper.encodeValues( + "Validator", "Text Choice Validator, BB|CC and here is an update|E E E|GG|H no change|\u0083\u0083 updated|\u00C5\\|\u00C5, Text Choice Validator"); fieldEvent = new AuditLogHelper.DetailedAuditEventRow(null, textChoiceFieldName, "Modified", "The following property was updated: Validator", "", fieldUpdateValues, fieldUpdateValues2, null); pass = _auditLogHelper.validateLastDomainAuditEvents(sampleTypeName, getProjectName(), expectedDomainEvent, Map.of(textChoiceFieldName, fieldEvent)); diff --git a/src/org/labkey/test/util/AuditLogHelper.java b/src/org/labkey/test/util/AuditLogHelper.java index a91b5c377f..694d024055 100644 --- a/src/org/labkey/test/util/AuditLogHelper.java +++ b/src/org/labkey/test/util/AuditLogHelper.java @@ -453,6 +453,32 @@ public String getLogString() } } + /** + * URL-encode fields and values for {@link DetailedAuditEventRow#newValues} or {@link DetailedAuditEventRow#oldValues} + * @param pairs alternating field names and their associated values + * @return URL-encoded String for use in DetailedAuditEventRow + */ + public static String encodeValues(String... pairs) + { + if (pairs.length % 2 != 0) + { + throw new IllegalArgumentException("pairs length must be even"); + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < pairs.length; i = i + 2) + { + if (!sb.isEmpty()) + sb.append("&"); + sb.append(EscapeUtil.encode(pairs[i])).append("=").append(EscapeUtil.encode(pairs[i + 1])); + } + return sb.toString(); + } + + public static String formatDataChange(String name, String oldValue, String newValue) + { + return name + ": " + oldValue + " > " + newValue; + } + public @NotNull Map getDomainPropertyEvents(String domainName, Integer domainEventId) { if (domainEventId == null) From 62864118d4891f9f649ed11864bfd07b76fd6bdb Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 18 Aug 2025 17:02:41 -0700 Subject: [PATCH 04/14] Fix URL encoding of pipeline job notifications and fix more fuzzing failures --- .../test/selenium/ReclickingWebElement.java | 2 +- src/org/labkey/test/util/AuditLogHelper.java | 17 +++++++++- src/org/labkey/test/util/ExcelHelper.java | 31 +++++++++++++++++-- src/org/labkey/test/util/URLBuilder.java | 9 +++++- 4 files changed, 54 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/selenium/ReclickingWebElement.java b/src/org/labkey/test/selenium/ReclickingWebElement.java index 453f9dd5ec..2bcda34d9b 100644 --- a/src/org/labkey/test/selenium/ReclickingWebElement.java +++ b/src/org/labkey/test/selenium/ReclickingWebElement.java @@ -216,7 +216,7 @@ private void revealElement(WebElement el, String shortMessage) Locator.XPathLocator interceptingElLoc = parseInterceptingElementLoc(shortMessage); if (interceptingElLoc != null) { - if (Strings.CS.containsAny(interceptingElLoc.toString(), "popover", "tip")) + if (Strings.CS.containsAny(interceptingElLoc.toString(), "popover", "ws-pre-wrap", "tip")) { new Actions(getDriver()).moveToLocation(0,0).perform(); } diff --git a/src/org/labkey/test/util/AuditLogHelper.java b/src/org/labkey/test/util/AuditLogHelper.java index 694d024055..14b6601d54 100644 --- a/src/org/labkey/test/util/AuditLogHelper.java +++ b/src/org/labkey/test/util/AuditLogHelper.java @@ -469,11 +469,26 @@ public static String encodeValues(String... pairs) { if (!sb.isEmpty()) sb.append("&"); - sb.append(EscapeUtil.encode(pairs[i])).append("=").append(EscapeUtil.encode(pairs[i + 1])); + sb.append(encodeValue(pairs[i])).append("=").append(encodeValue(pairs[i + 1])); } return sb.toString(); } + /** + * Perform selective URL encoding for use {@link DetailedAuditEventRow#newValues} or + * {@link DetailedAuditEventRow#oldValues} + * @param value raw key or value + * @return Partially URL-encoded value + */ + private static String encodeValue(String value) + { + return value + .replace("%", "%25") // '%' needs to be first + .replace(" ", "%20") + .replace("&", "%26") + .replace("=", "%3D"); + } + public static String formatDataChange(String name, String oldValue, String newValue) { return name + ": " + oldValue + " > " + newValue; diff --git a/src/org/labkey/test/util/ExcelHelper.java b/src/org/labkey/test/util/ExcelHelper.java index 16b0c5c1ab..143fbf9995 100644 --- a/src/org/labkey/test/util/ExcelHelper.java +++ b/src/org/labkey/test/util/ExcelHelper.java @@ -15,6 +15,7 @@ */ package org.labkey.test.util; +import org.apache.commons.lang3.StringUtils; import org.apache.poi.ss.format.CellGeneralFormatter; import org.apache.poi.ss.formula.FormulaParseException; import org.apache.poi.ss.usermodel.Cell; @@ -28,6 +29,7 @@ import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.ss.usermodel.WorkbookFactory; +import org.apache.poi.ss.util.WorkbookUtil; import org.jetbrains.annotations.Nullable; import java.io.File; @@ -35,6 +37,7 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; +import java.util.Collections; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; @@ -266,7 +269,7 @@ public static Map>> loadData(File file) { try (Workbook workbook = ExcelHelper.create(file)) { - Map>> allData = new LinkedHashMap<>(); + Map>> allData = new WorsheetMap(); for (int s = 0; s < workbook.getNumberOfSheets(); s++) { @@ -291,11 +294,35 @@ public static Map>> loadData(File file) allData.put(sheet.getSheetName(), rowMaps); } - return allData; + return Collections.unmodifiableMap(allData); } catch (IOException e) { throw new RuntimeException(e); } } + + /** + * Truncate and make safe a proposed Excel sheet name + * @see org.labkey.api.data.ExcelWriter#cleanSheetName(String) + */ + public static String sheetName(String sheetName) + { + return WorkbookUtil.createSafeSheetName(StringUtils.truncate(sheetName, 31), '_'); + } } + +class WorsheetMap extends LinkedHashMap>> +{ + @Override + public List> get(Object key) + { + return super.get(ExcelHelper.sheetName((String) key)); + } + + @Override + public List> getOrDefault(Object key, List> defaultValue) + { + return super.getOrDefault(ExcelHelper.sheetName((String) key), defaultValue); + } +} \ No newline at end of file diff --git a/src/org/labkey/test/util/URLBuilder.java b/src/org/labkey/test/util/URLBuilder.java index 03a976c626..5a6785976f 100644 --- a/src/org/labkey/test/util/URLBuilder.java +++ b/src/org/labkey/test/util/URLBuilder.java @@ -106,11 +106,18 @@ public URLBuilder setQuery(Map query) public URLBuilder setAppResourcePath(Object... pathParts) { List encodedParts = Arrays.stream(pathParts).map(Objects::requireNonNull).map(String::valueOf) - .map(s -> EscapeUtil.encode(s).replace("+", " ")).collect(Collectors.toList()); + .map(this::encodeAppResourcePathPart).collect(Collectors.toList()); setFragment("/" + String.join("/", encodedParts)); return this; } + private String encodeAppResourcePathPart(String pathPart) + { + return EscapeUtil.encode(pathPart) + .replace("%28", "(") + .replace("%29", ")"); + } + /** * Append a fragment to the URL.
* e.g. setResourcePath("marker") will append "#marker" to the built URL From b36375e02849a1b55ad2343c6e02896dc1e4d649 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 19 Aug 2025 16:59:45 -0700 Subject: [PATCH 05/14] More test fixes --- src/org/labkey/test/WebDriverWrapper.java | 6 +++++ .../components/ui/grids/EditableGrid.java | 25 +++++++++++++++++++ .../test/selenium/ReclickingWebElement.java | 12 +++++---- src/org/labkey/test/util/AuditLogHelper.java | 1 + 4 files changed, 39 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index 2fec90445a..29de0211d1 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -1127,6 +1127,12 @@ public String call() } } + @Contract(pure = true) + public WebDriverWait quickWait() + { + return new WebDriverWait(getDriver(), Duration.ofSeconds(1)); + } + @Contract(pure = true) public WebDriverWait shortWait() { diff --git a/src/org/labkey/test/components/ui/grids/EditableGrid.java b/src/org/labkey/test/components/ui/grids/EditableGrid.java index c0a0d5442a..b9448bfc98 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -769,6 +769,31 @@ public List getFilteredDropdownListForCell(int row, CharSequence columnI return lookupSelect.getOptions(); } + /** + * Values will be quoted appropriately for pasting into editable grid lookups. + */ + public static String getPastableColumn(List values) + { + List valueList = new ArrayList<>(); + for (Object value : values) + { + String strVal = CSVFormat.DEFAULT.format(value); // Just quote commas + valueList.add(strVal); + } + return String.join("\n", valueList); + } + + /** + * Pastes text to a single column of the grid. + * @param columnIdentifier fieldKey, name, or label of column + * @param pasteValues list of values to paste + * @return A Reference to this editableGrid object. + */ + public EditableGrid pasteColumn(CharSequence columnIdentifier, List pasteValues) + { + return pasteFromCell(0, columnIdentifier, getPastable(pasteValues), false); + } + /** * Pastes delimited text to the grid, via a single target. The component is clever enough to target * text into cells based on text delimiters; thus we can paste a square of data into the grid. diff --git a/src/org/labkey/test/selenium/ReclickingWebElement.java b/src/org/labkey/test/selenium/ReclickingWebElement.java index 2bcda34d9b..cf0e34f256 100644 --- a/src/org/labkey/test/selenium/ReclickingWebElement.java +++ b/src/org/labkey/test/selenium/ReclickingWebElement.java @@ -15,7 +15,6 @@ */ package org.labkey.test.selenium; -import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.Strings; import org.apache.commons.lang3.mutable.Mutable; import org.apache.commons.lang3.mutable.MutableObject; @@ -51,7 +50,7 @@ public class ReclickingWebElement extends WebElementDecorator { // Extract the element info from ElementClickInterceptedException message. - private static final Pattern interceptingElPattern = Pattern.compile("Element .* is not clickable .*<(?[a-zA-Z]+) (?.+)> obscures it"); + private static final Pattern interceptingElPattern = Pattern.compile("Element .* is not clickable .*<(?[a-zA-Z0-9]+) (?.+)> obscures it"); private static final Pattern elAttributePattern = Pattern.compile("(?[a-zA-Z-]+)=\"(?[^\"]+)\""); public ReclickingWebElement(@NotNull WebElement decoratedElement) @@ -216,12 +215,15 @@ private void revealElement(WebElement el, String shortMessage) Locator.XPathLocator interceptingElLoc = parseInterceptingElementLoc(shortMessage); if (interceptingElLoc != null) { - if (Strings.CS.containsAny(interceptingElLoc.toString(), "popover", "ws-pre-wrap", "tip")) + List interceptingElements = interceptingElLoc.findElements(getDriver()); + TestLogger.debug("Found %s element(s) matching extracted locator: %s".formatted(interceptingElements.size(), shortMessage)); + + if (Strings.CI.containsAny(interceptingElLoc.toString(), "popover", "ws-pre-wrap", "tip")) { + // Move mouse to corner to dismiss tooltips new Actions(getDriver()).moveToLocation(0,0).perform(); } - List interceptingElements = interceptingElLoc.findElements(getDriver()); - TestLogger.debug("Found %s element(s) matching extracted locator: %s".formatted(interceptingElements.size(), shortMessage)); + if (interceptingElements.size() == 1) { //noinspection ResultOfMethodCallIgnored diff --git a/src/org/labkey/test/util/AuditLogHelper.java b/src/org/labkey/test/util/AuditLogHelper.java index 14b6601d54..080f0494f9 100644 --- a/src/org/labkey/test/util/AuditLogHelper.java +++ b/src/org/labkey/test/util/AuditLogHelper.java @@ -486,6 +486,7 @@ private static String encodeValue(String value) .replace("%", "%25") // '%' needs to be first .replace(" ", "%20") .replace("&", "%26") + .replace("/", "%2F") .replace("=", "%3D"); } From 28b5c62a8dbc9d42045e219a66a2745845c37519 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 19 Aug 2025 17:17:22 -0700 Subject: [PATCH 06/14] Fix build --- src/org/labkey/test/components/ui/grids/EditableGrid.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/components/ui/grids/EditableGrid.java b/src/org/labkey/test/components/ui/grids/EditableGrid.java index b9448bfc98..b700412119 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -791,7 +791,7 @@ public static String getPastableColumn(List values) */ public EditableGrid pasteColumn(CharSequence columnIdentifier, List pasteValues) { - return pasteFromCell(0, columnIdentifier, getPastable(pasteValues), false); + return pasteFromCell(0, columnIdentifier, getPastableColumn(pasteValues), false); } /** From 3b6dd5af15c27754d3e7113c453e6091bca46c17 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 19 Aug 2025 17:21:26 -0700 Subject: [PATCH 07/14] Minor fix --- src/org/labkey/test/components/ui/grids/EditableGrid.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/org/labkey/test/components/ui/grids/EditableGrid.java b/src/org/labkey/test/components/ui/grids/EditableGrid.java index b700412119..2e7dba6535 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -791,6 +791,8 @@ public static String getPastableColumn(List values) */ public EditableGrid pasteColumn(CharSequence columnIdentifier, List pasteValues) { + if (pasteValues.isEmpty()) + throw new IllegalArgumentException("No paste values provided"); return pasteFromCell(0, columnIdentifier, getPastableColumn(pasteValues), false); } From debaf2adf0dff167129bcf83afd7d31bf35b6ff0 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 19 Aug 2025 17:37:42 -0700 Subject: [PATCH 08/14] Pager fix --- src/org/labkey/test/components/ui/Pager.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/org/labkey/test/components/ui/Pager.java b/src/org/labkey/test/components/ui/Pager.java index f364bed03a..6d87817239 100644 --- a/src/org/labkey/test/components/ui/Pager.java +++ b/src/org/labkey/test/components/ui/Pager.java @@ -93,8 +93,7 @@ private int pageSize(String pageSize) // only works on GridPanel } else { - // Tooltip sometimes blocks button. Click active option to dismiss menu. - activeLi.click(); + elementCache().jumpToDropdown.collapse(); } return initialSize; From a355ea717eb8735f76bff03351b3aab49136d755 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 20 Aug 2025 12:13:25 -0700 Subject: [PATCH 09/14] Better fix for encoding --- src/org/labkey/test/util/AuditLogHelper.java | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/src/org/labkey/test/util/AuditLogHelper.java b/src/org/labkey/test/util/AuditLogHelper.java index 080f0494f9..0517ab041f 100644 --- a/src/org/labkey/test/util/AuditLogHelper.java +++ b/src/org/labkey/test/util/AuditLogHelper.java @@ -482,12 +482,19 @@ public static String encodeValues(String... pairs) */ private static String encodeValue(String value) { - return value - .replace("%", "%25") // '%' needs to be first - .replace(" ", "%20") - .replace("&", "%26") - .replace("/", "%2F") - .replace("=", "%3D"); + return EscapeUtil.encode(value) + .replace("%28", "(") + .replace("%29", ")"); +// return value +// .replace("%", "%25") // '%' needs to be first +// .replace(" ", "%20") +// .replace("$", "%24") +// .replace("&", "%26") +// .replace("'", "%27") +// .replace("/", "%2F") +// .replace("=", "%3D") +// .replace("{", "%7B") +// .replace("}", "%7D"); } public static String formatDataChange(String name, String oldValue, String newValue) From 7e4e8c624a1c72633ea56d2b756950062a6ae63b Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 20 Aug 2025 13:35:46 -0700 Subject: [PATCH 10/14] Remove selenium.By usages --- .../ui/notifications/ServerNotificationItem.java | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java b/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java index 16d0dec587..b668106f8e 100644 --- a/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java +++ b/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java @@ -7,6 +7,7 @@ import org.labkey.test.util.TestLogger; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; /** *

@@ -73,14 +74,7 @@ else if (status.contains("is-complete")) */ public String getMessage() { - if(elementCache().message.isDisplayed()) - { - return elementCache().message.getText(); - } - else - { - return ""; - } + return getWrapper().shortWait().until(ExpectedConditions.visibilityOf(elementCache().message)).getText(); } /** From 805f62e723fcb9a4eeb79b8aae8f9ede6796f2fd Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 20 Aug 2025 14:10:58 -0700 Subject: [PATCH 11/14] Adjust wait --- .../ui/notifications/ServerNotificationItem.java | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java b/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java index b668106f8e..298446e9a2 100644 --- a/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java +++ b/src/org/labkey/test/components/ui/notifications/ServerNotificationItem.java @@ -44,6 +44,12 @@ public WebElement getComponentElement() return componentElement; } + @Override + protected void waitForReady() + { + getWrapper().quickWait().until(ExpectedConditions.visibilityOf(getComponentElement())); + } + /** * Get the status as indicated by the icon. * @@ -74,7 +80,14 @@ else if (status.contains("is-complete")) */ public String getMessage() { - return getWrapper().shortWait().until(ExpectedConditions.visibilityOf(elementCache().message)).getText(); + if (elementCache().message.isDisplayed()) + { + return elementCache().message.getText(); + } + else + { + return ""; + } } /** From 430b04cd53496be304c6b1de40deabe2c2441646 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 20 Aug 2025 14:36:09 -0700 Subject: [PATCH 12/14] Comment --- src/org/labkey/test/util/URLBuilder.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/org/labkey/test/util/URLBuilder.java b/src/org/labkey/test/util/URLBuilder.java index 5a6785976f..4f49b5485b 100644 --- a/src/org/labkey/test/util/URLBuilder.java +++ b/src/org/labkey/test/util/URLBuilder.java @@ -114,6 +114,7 @@ public URLBuilder setAppResourcePath(Object... pathParts) private String encodeAppResourcePathPart(String pathPart) { return EscapeUtil.encode(pathPart) + // We generally don't encode parentheses in app resource paths .replace("%28", "(") .replace("%29", ")"); } From f037db3abf439b1905cd3372436c8817e8d49edb Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 20 Aug 2025 14:37:30 -0700 Subject: [PATCH 13/14] Fix EOF --- src/org/labkey/test/util/ExcelHelper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/util/ExcelHelper.java b/src/org/labkey/test/util/ExcelHelper.java index 143fbf9995..cb09a721ee 100644 --- a/src/org/labkey/test/util/ExcelHelper.java +++ b/src/org/labkey/test/util/ExcelHelper.java @@ -325,4 +325,4 @@ public List> getOrDefault(Object key, List Date: Wed, 20 Aug 2025 14:39:22 -0700 Subject: [PATCH 14/14] Fix comments --- src/org/labkey/test/util/AuditLogHelper.java | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/src/org/labkey/test/util/AuditLogHelper.java b/src/org/labkey/test/util/AuditLogHelper.java index 0517ab041f..8a5b3d8fc5 100644 --- a/src/org/labkey/test/util/AuditLogHelper.java +++ b/src/org/labkey/test/util/AuditLogHelper.java @@ -483,18 +483,9 @@ public static String encodeValues(String... pairs) private static String encodeValue(String value) { return EscapeUtil.encode(value) + // Parentheses aren't encoded .replace("%28", "(") .replace("%29", ")"); -// return value -// .replace("%", "%25") // '%' needs to be first -// .replace(" ", "%20") -// .replace("$", "%24") -// .replace("&", "%26") -// .replace("'", "%27") -// .replace("/", "%2F") -// .replace("=", "%3D") -// .replace("{", "%7B") -// .replace("}", "%7D"); } public static String formatDataChange(String name, String oldValue, String newValue)