From fcbb47bb3fbb3dda8b97c9b2cee74ea9758101fe Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Mon, 3 Nov 2025 15:53:04 -0800 Subject: [PATCH 01/15] Fix various intermittent test issues --- src/org/labkey/test/TestScrubber.java | 11 +++++++++++ .../components/domain/DomainDesigner.java | 2 +- .../components/domain/DomainFormPanel.java | 2 +- .../pipeline/PipelineTriggerWizard.java | 19 +++++++++++++++++++ .../components/ui/grids/EditableGrid.java | 2 ++ 5 files changed, 34 insertions(+), 2 deletions(-) diff --git a/src/org/labkey/test/TestScrubber.java b/src/org/labkey/test/TestScrubber.java index 82f19b110a..4c12237c11 100644 --- a/src/org/labkey/test/TestScrubber.java +++ b/src/org/labkey/test/TestScrubber.java @@ -20,6 +20,7 @@ import org.labkey.remoteapi.Connection; import org.labkey.remoteapi.SimplePostCommand; import org.labkey.test.components.html.Checkbox; +import org.labkey.test.components.pipeline.PipelineTriggerWizard; import org.labkey.test.pages.core.admin.AllowedFileExtensionAdminPage; import org.labkey.test.pages.core.admin.BaseSettingsPage; import org.labkey.test.pages.core.admin.ConfigureFileSystemAccessPage; @@ -160,6 +161,16 @@ public void cleanSiteSettings() TestLogger.error("Failed to reset site look and feel properties after test.", e); } + try + { + // Disable all pipeline triggers so that they don't show up as memory leaks in subsequent tests. + PipelineTriggerWizard.disableAllPipelineTriggers(createDefaultConnection()); + } + catch (Exception e) + { + TestLogger.error("Failed to disable pipeline triggers after test", e); + } + } @LogMethod(quiet = true) diff --git a/src/org/labkey/test/components/domain/DomainDesigner.java b/src/org/labkey/test/components/domain/DomainDesigner.java index 4c67a177da..dbdb95fe6b 100644 --- a/src/org/labkey/test/components/domain/DomainDesigner.java +++ b/src/org/labkey/test/components/domain/DomainDesigner.java @@ -48,7 +48,7 @@ public class ElementCache extends BaseDomainDesigner.ElementCache protected final DomainPanel propertiesPanel = new DomainPanel.DomainPanelFinder(getDriver()).index(0) .timeout(WAIT_FOR_JAVASCRIPT).findWhenNeeded(this); protected final DomainFormPanel fieldsPanel = new DomainFormPanel.DomainFormPanelFinder(getDriver()) - .index(getFieldPanelIndex()).timeout(1000).findWhenNeeded(); + .index(getFieldPanelIndex()).timeout(2_000).findWhenNeeded(); protected int getFieldPanelIndex() { diff --git a/src/org/labkey/test/components/domain/DomainFormPanel.java b/src/org/labkey/test/components/domain/DomainFormPanel.java index 615622a4df..7130c081e4 100644 --- a/src/org/labkey/test/components/domain/DomainFormPanel.java +++ b/src/org/labkey/test/components/domain/DomainFormPanel.java @@ -47,7 +47,7 @@ private DomainFormPanel(WebElement element, WebDriver driver) @Override protected void waitForReady() { - waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 2_000); + waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 5_000); } public static List advancedSettingsFromFieldDefinition(FieldDefinition def) diff --git a/src/org/labkey/test/components/pipeline/PipelineTriggerWizard.java b/src/org/labkey/test/components/pipeline/PipelineTriggerWizard.java index 6cb734f92d..65dd4320e0 100644 --- a/src/org/labkey/test/components/pipeline/PipelineTriggerWizard.java +++ b/src/org/labkey/test/components/pipeline/PipelineTriggerWizard.java @@ -16,6 +16,10 @@ package org.labkey.test.components.pipeline; import org.jetbrains.annotations.NotNull; +import org.labkey.remoteapi.CommandException; +import org.labkey.remoteapi.Connection; +import org.labkey.remoteapi.query.ContainerFilter; +import org.labkey.remoteapi.query.Filter; import org.labkey.test.Locator; import org.labkey.test.WebDriverWrapper; import org.labkey.test.WebTestHelper; @@ -24,11 +28,14 @@ import org.labkey.test.components.html.Checkbox; import org.labkey.test.components.html.Input; import org.labkey.test.components.html.OptionSelect; +import org.labkey.test.util.query.QueryApiHelper; import org.openqa.selenium.Alert; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; +import java.io.IOException; +import java.util.List; import java.util.Map; import static org.junit.Assert.assertTrue; @@ -61,6 +68,18 @@ public static PipelineTriggerWizard beginAt(WebDriverWrapper driver, String cont return new PipelineTriggerWizard(driver.getDriver()); } + public static void disableAllPipelineTriggers(Connection connection) throws IOException, CommandException + { + QueryApiHelper queryApiHelper = new QueryApiHelper(connection, "/", "pipeline", "TriggerConfigurations"); + List> triggers = queryApiHelper.selectRows(List.of("rowId", "enabled"), + List.of(new Filter("enabled", true)), List.of(), ContainerFilter.AllFolders).getRows(); + if (!triggers.isEmpty()) + { + triggers.forEach(trigger -> trigger.put("enabled", false)); + queryApiHelper.updateRows(triggers); + } + } + @Override public WebElement getComponentElement() { diff --git a/src/org/labkey/test/components/ui/grids/EditableGrid.java b/src/org/labkey/test/components/ui/grids/EditableGrid.java index 2e7dba6535..a2915b9445 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -1160,6 +1160,8 @@ public String getErrorPopoverText(int row, CharSequence columnIdentifier) */ public String getCellPopoverText(int row, CharSequence columnIdentifier) { + dismissPopover(); // Other popovers can block the target cell + getWrapper().mouseOver(Locator.tag("td").findElement(getRow(row))); // Avoid passing over any header cells on the way to the target cell WebElement cellDiv = Locator.tagWithClass("div", "cellular-display").findElement(getCell(row, columnIdentifier)); getWrapper().mouseOver(cellDiv); // cause the tooltip to be present return Optional.ofNullable(WebDriverWrapper.waitFor(()-> Locators.popover.findElementOrNull(getDriver()), 1000)) From 7eaef99f76ca68817f28f84606e76c8f2c95edf5 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 4 Nov 2025 09:41:14 -0800 Subject: [PATCH 02/15] QueryGrid synchronization improvements --- .../test/components/ui/grids/QueryGrid.java | 42 +++++++++++++------ .../components/ui/grids/ResponsiveGrid.java | 27 +++++++----- 2 files changed, 45 insertions(+), 24 deletions(-) diff --git a/src/org/labkey/test/components/ui/grids/QueryGrid.java b/src/org/labkey/test/components/ui/grids/QueryGrid.java index 6ede621b9d..a529b103e6 100644 --- a/src/org/labkey/test/components/ui/grids/QueryGrid.java +++ b/src/org/labkey/test/components/ui/grids/QueryGrid.java @@ -221,17 +221,17 @@ public QueryGrid waitForRecordCount(int expectedCount, int milliseconds) @Override public void doAndWaitForUpdate(Runnable func) { - waitForLoaded(); - Optional optionalStatus = elementCache().selectionStatusContainerLoc.findOptionalElement(elementCache()); + WebElement status = hasSelectColumn() ? Locators.selectionStatusContainerLoc.waitForElement(this, 5_000) : null; func.run(); - optionalStatus.ifPresent(el -> { - getWrapper().shortWait().until(ExpectedConditions.stalenessOf(el)); - elementCache().selectionStatusContainerLoc.waitForElement(this, 5_000); - }); + if (status != null) + { + getWrapper().shortWait().until(ExpectedConditions.stalenessOf(status)); + Locators.selectionStatusContainerLoc.waitForElement(this, 5_000); + } - waitForLoaded(); + elementCache().waitForLoaded(); clearElementCache(); } @@ -789,19 +789,35 @@ protected ElementCache elementCache() return (ElementCache) super.elementCache(); } + protected static class Locators + { + static final Locator.XPathLocator selectionStatusContainerLoc = Locator.byClass("selection-status"); + } + protected class ElementCache extends ResponsiveGrid.ElementCache { + + @Override + public Boolean isLoaded() + { + return isSelectionLoaded() && super.isLoaded(); + } + + private boolean isSelectionLoaded() + { + return Locators.selectionStatusContainerLoc.isDisplayed(this); + } + final GridBar gridBar = new GridBar.GridBarFinder().findWhenNeeded(QueryGrid.this); - WebElement saveViewButton = Locator.button("Save").findWhenNeeded(getDriver()); + final WebElement saveViewButton = Locator.button("Save").findWhenNeeded(getDriver()); final BootstrapMenu viewMenu = new MultiMenu.MultiMenuFinder(getDriver()).withText("Views").findWhenNeeded(this); - final Locator.XPathLocator selectionStatusContainerLoc = Locator.tagWithClass("div", "selection-status"); - final Locator selectAllBtnLoc = selectionStatusContainerLoc.append(Locator.tagWithClass("span", "selection-status__select-all") - .child(Locator.buttonContainingText("Select"))); - final Locator clearBtnLoc = selectionStatusContainerLoc.append(Locator.byClass("selection-status__clear-all") - .child(Locator.tag("button"))); + final Locator selectAllBtnLoc = Locators.selectionStatusContainerLoc.append(Locator.byClass("selection-status__select-all") + .childTag("button")); + final Locator clearBtnLoc = Locators.selectionStatusContainerLoc.append(Locator.byClass("selection-status__clear-all") + .childTag("button")); final WebElement filterStatusPanel = Locator.css("div.grid-panel__filter-status").findWhenNeeded(this); diff --git a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java index e8bfad7634..398bffc517 100644 --- a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java +++ b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java @@ -67,16 +67,7 @@ public WebElement getComponentElement() public Boolean isLoaded() { - return getComponentElement().isDisplayed() && - !Locators.loadingGrid.existsIn(this) && - !Locators.spinner.existsIn(this) && - (Locator.tag("td").existsIn(this) || - getGridEmptyMessage().isPresent()); - } - - protected void waitForLoaded() - { - WebDriverWrapper.waitFor(this::isLoaded, "Grid still loading", 30000); + return elementCache().isLoaded(); } @Override @@ -85,7 +76,7 @@ public void doAndWaitForUpdate(Runnable func) // Look at WebDriverWrapper.doAndWaitForElementToRefresh for an example. func.run(); - waitForLoaded(); + elementCache().waitForLoaded(); clearElementCache(); } @@ -828,6 +819,20 @@ public ElementCache() waitForLoaded(); } + protected void waitForLoaded() + { + WebDriverWrapper.waitFor(this::isLoaded, "Grid still loading", 30000); + } + + public Boolean isLoaded() + { + return getComponentElement().isDisplayed() && + !Locators.loadingGrid.existsIn(this) && + !Locators.spinner.existsIn(this) && + (Locator.tag("td").existsIn(this) || + getGridEmptyMessage().isPresent()); + } + private Boolean hasSelectColumn = null; protected boolean hasSelectColumn() { From db8d3a7906565a721c85c79bd2a26f2497ec3d48 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 4 Nov 2025 14:16:39 -0800 Subject: [PATCH 03/15] Make refinding a bit less fragile --- src/org/labkey/test/util/DataRegion.java | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/util/DataRegion.java b/src/org/labkey/test/util/DataRegion.java index 83304f6299..0bf85649a5 100644 --- a/src/org/labkey/test/util/DataRegion.java +++ b/src/org/labkey/test/util/DataRegion.java @@ -37,6 +37,7 @@ import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.TreeMap; import java.util.regex.Pattern; @@ -190,7 +191,7 @@ protected String getTableId() { if (_tableId == null) { - String id = _el.getAttribute("id"); + String id = Objects.requireNonNull(_el.getAttribute("id"), "Table has no ID"); if (id.endsWith("-form")) _tableId = id.replace("-form", ""); else @@ -334,7 +335,7 @@ public static Locator.XPathLocator form(String regionName) } } - public class ElementCache extends Component.ElementCache + public class ElementCache extends Component.ElementCache { protected ElementCache() { @@ -418,7 +419,7 @@ protected BootstrapMenu getHeaderMenu(String text) private abstract class BaseDataRegionApi { - final String regionJS = "LABKEY.DataRegions['" + getDataRegionName().replaceAll("'", "\\\\'") + "']"; + final CachingSupplier regionJS = new CachingSupplier<>(() -> "LABKEY.DataRegions['" + getDataRegionName().replaceAll("'", "\\\\'") + "']"); public void executeScript(String methodWithArgs, Object... args) { @@ -427,7 +428,7 @@ public void executeScript(String methodWithArgs, Object... args) public T executeScript(String methodWithArgs, Class expectedResultType, Object... args) { - return getWrapper().executeScript((expectedResultType != null ? "return " : "") + regionJS + "." + methodWithArgs, expectedResultType, args); + return getWrapper().executeScript((expectedResultType != null ? "return " : "") + regionJS.get() + "." + methodWithArgs, expectedResultType, args); } public void callMethod(String apiMethodName, Object... args) @@ -465,7 +466,7 @@ public T executeScript(String methodWithArgs, Class expectedResultType, O { MutableObject result = new MutableObject<>(); doAndWaitForUpdate(() -> result.setValue(super.executeScript(methodWithArgs, expectedResultType, args))); - return result.getValue(); + return result.get(); } } } From be55dbdf1a5d999e87e76d7ef0790a16a216de4f Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 4 Nov 2025 14:24:09 -0800 Subject: [PATCH 04/15] Selection count might not be visible --- src/org/labkey/test/components/ui/grids/QueryGrid.java | 2 +- src/org/labkey/test/components/ui/grids/ResponsiveGrid.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/org/labkey/test/components/ui/grids/QueryGrid.java b/src/org/labkey/test/components/ui/grids/QueryGrid.java index a529b103e6..94a89bcca6 100644 --- a/src/org/labkey/test/components/ui/grids/QueryGrid.java +++ b/src/org/labkey/test/components/ui/grids/QueryGrid.java @@ -805,7 +805,7 @@ public Boolean isLoaded() private boolean isSelectionLoaded() { - return Locators.selectionStatusContainerLoc.isDisplayed(this); + return !hasSelectColumn() || Locators.selectionStatusContainerLoc.existsIn(this); } final GridBar gridBar = new GridBar.GridBarFinder().findWhenNeeded(QueryGrid.this); diff --git a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java index d4ad64762a..40e35e36b2 100644 --- a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java +++ b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java @@ -824,7 +824,7 @@ protected void waitForLoaded() WebDriverWrapper.waitFor(this::isLoaded, "Grid still loading", 30000); } - public Boolean isLoaded() + protected Boolean isLoaded() { return getComponentElement().isDisplayed() && !Locators.loadingGrid.existsIn(this) && @@ -843,7 +843,7 @@ protected boolean hasSelectColumn() return hasSelectColumn; } - ReactCheckBox selectAllCheckbox = new ReactCheckBox(Locator.xpath("//th/input[@type='checkbox']").findWhenNeeded(this)) + final ReactCheckBox selectAllCheckbox = new ReactCheckBox(Locator.xpath("//th/input[@type='checkbox']").findWhenNeeded(this)) { @Override public void toggle() From 667faff7848268d6203f026a4ece74917ffd7dbf Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 4 Nov 2025 14:42:23 -0800 Subject: [PATCH 05/15] 'attemptSignIn' might trigger a page load --- src/org/labkey/test/LabKeySiteWrapper.java | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index 2abd3f59fc..86e1c27efd 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -405,11 +405,14 @@ public void attemptSignIn(String email, String password) setFormElement(Locator.id("email"), email); setFormElement(Locator.id("password"), password); WebElement signInButton = Locator.lkButton("Sign In").findElement(getDriver()); - signInButton.click(); - shortWait().until(ExpectedConditions.invisibilityOfElementLocated(Locator.byClass("signing-in-msg"))); - shortWait().until(ExpectedConditions.or( - ExpectedConditions.stalenessOf(signInButton), // Successful login - ExpectedConditions.presenceOfElementLocated(Locators.labkeyError.withText()))); // Error during sign-in + doAndMaybeWaitForPageToLoad(10_000, () -> { + signInButton.click(); + shortWait().until(ExpectedConditions.invisibilityOfElementLocated(Locator.byClass("signing-in-msg"))); + shortWait().until(ExpectedConditions.or( + ExpectedConditions.stalenessOf(signInButton), // Successful login + ExpectedConditions.presenceOfElementLocated(Locators.labkeyError.withText()))); // Error during sign-in + return ExpectedConditions.stalenessOf(signInButton).apply(null); + }); } public void signInShouldFail(String email, String password, String... expectedMessages) From 5c3cc1f6570102f01f9195761d03112368486bd7 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 5 Nov 2025 08:50:39 -0800 Subject: [PATCH 06/15] Rhino JavaScript executor doesn't know 'endsWith' --- .../resources/scripts/validationTest/actionUrlTest.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js b/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js index d26732f654..0957ee01ed 100644 --- a/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js +++ b/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js @@ -21,7 +21,7 @@ function doTest() if (contextPath.length > 0) { var baseUrl = LABKEY.ActionURL.getBaseURL(); - if (!baseUrl.endsWith(contextPath + "/")) + if (baseUrl.indexOf(contextPath + "/") !== baseUrl.length - (contextPath.length + 1)) errors[errors.length] = new Error("ActionURL.getBaseURL() = " + baseUrl); } From 284a15a0904b357a7ad29a88464f7854463cba23 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 5 Nov 2025 09:15:44 -0800 Subject: [PATCH 07/15] Make GitHub code scan happy --- .../resources/scripts/validationTest/actionUrlTest.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js b/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js index 0957ee01ed..22cdfe5a6f 100644 --- a/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js +++ b/modules/simpletest/resources/scripts/validationTest/actionUrlTest.js @@ -21,7 +21,9 @@ function doTest() if (contextPath.length > 0) { var baseUrl = LABKEY.ActionURL.getBaseURL(); - if (baseUrl.indexOf(contextPath + "/") !== baseUrl.length - (contextPath.length + 1)) + var suffix = contextPath + "/"; + var idx = baseUrl.indexOf(suffix); + if (idx === -1 || idx !== baseUrl.length - suffix.length) errors[errors.length] = new Error("ActionURL.getBaseURL() = " + baseUrl); } From 1bcf523fcd5b48c0fce3bdb8499c02e5cfc1997d Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Wed, 5 Nov 2025 09:22:02 -0800 Subject: [PATCH 08/15] Add a wiggle to mouseOut --- src/org/labkey/test/WebDriverWrapper.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index 7d1d4b6e63..1f157bad08 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -3007,7 +3007,12 @@ public void mouseOut() try { scrollToTop(); - new Actions(getDriver()).moveToLocation(0, 0).perform(); + new Actions(getDriver()) + .moveToLocation(0, 0) + // Add a little wiggle to make sure tooltips notice + .moveByOffset(4, 4) + .moveByOffset(-2, -2) + .perform(); } catch (WebDriverException ignore) { } } From 6a3c6cbb049691598da8f97d6fc0cb8bb948845d Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Thu, 6 Nov 2025 17:22:09 -0800 Subject: [PATCH 09/15] Fix grid loading --- .../components/domain/DomainFormPanel.java | 2 +- .../test/components/ui/grids/QueryGrid.java | 31 ++++++------------- .../components/ui/grids/ResponsiveGrid.java | 29 ++++++++--------- 3 files changed, 24 insertions(+), 38 deletions(-) diff --git a/src/org/labkey/test/components/domain/DomainFormPanel.java b/src/org/labkey/test/components/domain/DomainFormPanel.java index 7130c081e4..615622a4df 100644 --- a/src/org/labkey/test/components/domain/DomainFormPanel.java +++ b/src/org/labkey/test/components/domain/DomainFormPanel.java @@ -47,7 +47,7 @@ private DomainFormPanel(WebElement element, WebDriver driver) @Override protected void waitForReady() { - waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 5_000); + waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 2_000); } public static List advancedSettingsFromFieldDefinition(FieldDefinition def) diff --git a/src/org/labkey/test/components/ui/grids/QueryGrid.java b/src/org/labkey/test/components/ui/grids/QueryGrid.java index 94a89bcca6..b1ad592b0e 100644 --- a/src/org/labkey/test/components/ui/grids/QueryGrid.java +++ b/src/org/labkey/test/components/ui/grids/QueryGrid.java @@ -221,18 +221,18 @@ public QueryGrid waitForRecordCount(int expectedCount, int milliseconds) @Override public void doAndWaitForUpdate(Runnable func) { - WebElement status = hasSelectColumn() ? Locators.selectionStatusContainerLoc.waitForElement(this, 5_000) : null; - - func.run(); - - if (status != null) + super.doAndWaitForUpdate(() -> { - getWrapper().shortWait().until(ExpectedConditions.stalenessOf(status)); - Locators.selectionStatusContainerLoc.waitForElement(this, 5_000); - } + WebElement status = hasSelectColumn() ? Locators.selectionStatusContainerLoc.waitForElement(this, 5_000) : null; + + func.run(); - elementCache().waitForLoaded(); - clearElementCache(); + if (status != null) + { + getWrapper().shortWait().until(ExpectedConditions.stalenessOf(status)); + Locators.selectionStatusContainerLoc.waitForElement(this, 5_000); + } + }); } @@ -797,17 +797,6 @@ protected static class Locators protected class ElementCache extends ResponsiveGrid.ElementCache { - @Override - public Boolean isLoaded() - { - return isSelectionLoaded() && super.isLoaded(); - } - - private boolean isSelectionLoaded() - { - return !hasSelectColumn() || Locators.selectionStatusContainerLoc.existsIn(this); - } - final GridBar gridBar = new GridBar.GridBarFinder().findWhenNeeded(QueryGrid.this); final WebElement saveViewButton = Locator.button("Save").findWhenNeeded(getDriver()); diff --git a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java index 40e35e36b2..5242306ec2 100644 --- a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java +++ b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java @@ -67,16 +67,27 @@ public WebElement getComponentElement() public Boolean isLoaded() { - return elementCache().isLoaded(); + return getComponentElement().isDisplayed() && + !Locators.loadingGrid.existsIn(this) && + !Locators.spinner.existsIn(this) && + (Locator.tag("td").existsIn(this) || + getGridEmptyMessage().isPresent()); + } + + protected void waitForLoaded() + { + WebDriverWrapper.waitFor(this::isLoaded, "Grid still loading", 30000); } @Override public void doAndWaitForUpdate(Runnable func) { + waitForLoaded(); + // Look at WebDriverWrapper.doAndWaitForElementToRefresh for an example. func.run(); - elementCache().waitForLoaded(); + waitForLoaded(); clearElementCache(); } @@ -819,20 +830,6 @@ public ElementCache() waitForLoaded(); } - protected void waitForLoaded() - { - WebDriverWrapper.waitFor(this::isLoaded, "Grid still loading", 30000); - } - - protected Boolean isLoaded() - { - return getComponentElement().isDisplayed() && - !Locators.loadingGrid.existsIn(this) && - !Locators.spinner.existsIn(this) && - (Locator.tag("td").existsIn(this) || - getGridEmptyMessage().isPresent()); - } - private Boolean hasSelectColumn = null; protected boolean hasSelectColumn() { From e69419c32a28450b292bf0d8780875f727662a55 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 7 Nov 2025 12:41:14 -0800 Subject: [PATCH 10/15] Don't scroll with mouseOut --- src/org/labkey/test/WebDriverWrapper.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index 1f157bad08..b51b78bfc1 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -3000,13 +3000,11 @@ public void selectFolderTreeItem(String folderName) /** * Move mouse to the upper left corner of the document to dismiss tooltips and the like - * Will scroll page if necessary */ public void mouseOut() { try { - scrollToTop(); new Actions(getDriver()) .moveToLocation(0, 0) // Add a little wiggle to make sure tooltips notice From 9c8d6ae9690ed67ec42b7b23b98fc127c8a6df22 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 7 Nov 2025 13:44:33 -0800 Subject: [PATCH 11/15] Wait for LineageGraph to load --- src/org/labkey/test/Locators.java | 1 + .../ui/grids/FieldSelectionDialog.java | 1 + .../components/ui/lineage/LineageGraph.java | 28 +++++++++++++------ 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/org/labkey/test/Locators.java b/src/org/labkey/test/Locators.java index ec7ec8fdb4..9140833f14 100644 --- a/src/org/labkey/test/Locators.java +++ b/src/org/labkey/test/Locators.java @@ -30,6 +30,7 @@ private Locators() { } public static final Locator.XPathLocator folderTab = Locator.tagWithClass("div", "lk-nav-tabs-ct").append(Locator.tagWithClass("ul", "lk-nav-tabs")).childTag("li"); public static final Locator.XPathLocator panelWebpartTitle = Locator.byClass("labkey-wp-title-text"); public static final Locator.XPathLocator folderTitle = Locator.tagWithClass("a", "lk-body-title-folder"); + public static final Locator.XPathLocator loadingSpinner = Locator.byClass("fa-spinner"); public static Locator.XPathLocator headerContainer() { diff --git a/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java b/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java index 994ee13f92..35d0a8140c 100644 --- a/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java +++ b/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java @@ -400,6 +400,7 @@ public FieldSelectionDialog removeAllSelectedFields() continue; } + getWrapper().mouseOver(removeIcon); removeIcon.click(); } diff --git a/src/org/labkey/test/components/ui/lineage/LineageGraph.java b/src/org/labkey/test/components/ui/lineage/LineageGraph.java index 2dd8384866..7f25c466ed 100644 --- a/src/org/labkey/test/components/ui/lineage/LineageGraph.java +++ b/src/org/labkey/test/components/ui/lineage/LineageGraph.java @@ -1,6 +1,8 @@ package org.labkey.test.components.ui.lineage; import org.labkey.test.Locator; +import org.labkey.test.Locators; +import org.labkey.test.WebDriverWrapper; import org.labkey.test.components.Component; import org.labkey.test.components.WebDriverComponent; import org.labkey.test.components.react.Tabs; @@ -48,6 +50,16 @@ public static LineageGraph showLineageGraph(WebDriver driver) return lineageGraph; } + @Override + protected void waitForReady() + { + WebDriverWrapper.waitFor(()-> + elementCache().visGraphContainer.isDisplayed() && + elementCache().nodeDetailContainer.isDisplayed() && + !Locators.loadingSpinner.existsIn(this), + "lineage graph did not load in time", 5_000); + } + public Map getCurrentNodeData() { return elementCache().detailTable().getTableDataByLabel(); @@ -178,16 +190,16 @@ final WebElement lineageItem(String name, WebElement tabPanel) // container for the details of the currently-selected node final WebElement nodeDetailContainer = Locator.tagWithClass("div", "lineage-node-detail-container") .findWhenNeeded(this).withTimeout(4000); - WebElement componentDetailImage = Locator.tagWithClass("i", "component-detail--child--img") + final WebElement componentDetailImage = Locator.tagWithClass("i", "component-detail--child--img") .child(Locator.tag("img")).findWhenNeeded(nodeDetailContainer); - WebElement nodeDetailName = Locator.tagWithClass("h4", "lineage-name-data") + final WebElement nodeDetailName = Locator.tagWithClass("h4", "lineage-name-data") .findWhenNeeded(nodeDetailContainer); - WebElement nodeDetailLinksContainer = Locator.tagWithClass("div", "lineage-node-detail") + final WebElement nodeDetailLinksContainer = Locator.tagWithClass("div", "lineage-node-detail") .findWhenNeeded(nodeDetailContainer); - WebElement nodeOverviewLink = Locator.linkWithSpan("Overview").withClass("lineage-data-link--text") + final WebElement nodeOverviewLink = Locator.linkWithSpan("Overview").withClass("lineage-data-link--text") .findWhenNeeded(nodeDetailLinksContainer); - Locator lineageLinkLoc = Locator.linkWithSpan("Lineage").withClass("lineage-data-link--text"); - WebElement nodeLineageLink = lineageLinkLoc.findWhenNeeded(nodeDetailLinksContainer); + final Locator lineageLinkLoc = Locator.linkWithSpan("Lineage").withClass("lineage-data-link--text"); + final WebElement nodeLineageLink = lineageLinkLoc.findWhenNeeded(nodeDetailLinksContainer); Tabs nodeDetailsTabs() { return new Tabs.TabsFinder(getDriver()).findWhenNeeded(nodeDetailContainer); @@ -198,9 +210,9 @@ DetailTable detailTable() return new DetailTable.DetailTableFinder(getDriver()).waitFor(nodeDetailContainer); } - WebElement nodeDetails = Locator.tagWithClass("div", "lineage-node-detail") + final WebElement nodeDetails = Locator.tagWithClass("div", "lineage-node-detail") .findWhenNeeded(nodeDetailContainer).withTimeout(3000); - WebElement nodeDetailsName = Locator.tagWithClass("div", "lineage-name-data") + final WebElement nodeDetailsName = Locator.tagWithClass("div", "lineage-name-data") .findWhenNeeded(nodeDetails); NodeDetailGroup summaryList(String nodeLabel) From 0a911f9cbeb05ab08df14bba61be85d8ee82a89e Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Fri, 7 Nov 2025 16:08:32 -0800 Subject: [PATCH 12/15] More tries to dismiss popover --- 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 a2915b9445..15a33ee79e 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -1172,7 +1172,9 @@ public String getCellPopoverText(int row, CharSequence columnIdentifier) public void dismissPopover() { Locators.popover.findOptionalElement(getDriver()).ifPresent(popover -> { + getWrapper().mouseOver(popover); getWrapper().mouseOut(); + getWrapper().mouseOver(elementCache().getGridHeaderManager().getColumnHeader(0).getElement()); getWrapper().shortWait().until(ExpectedConditions.invisibilityOf(popover)); }); } From f8fb1cf0a9327d98e671966f6e91abf6bf6fb4b1 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 11 Nov 2025 08:38:18 -0800 Subject: [PATCH 13/15] Switch domain creation logging to debug --- src/org/labkey/test/params/property/DomainProps.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/org/labkey/test/params/property/DomainProps.java b/src/org/labkey/test/params/property/DomainProps.java index 6b5ddc3193..34027f712d 100644 --- a/src/org/labkey/test/params/property/DomainProps.java +++ b/src/org/labkey/test/params/property/DomainProps.java @@ -36,10 +36,9 @@ public DomainResponse execute(Connection connection, String folderPath) throws I DomainResponse response = super.execute(connection, folderPath); - TestLogger.log("Successfully created domain, '%s':\n%s" - .formatted( - response.getDomain().getName(), - response.getDomain().toJSONObject().toString(2))); + TestLogger.log().debug("Successfully created domain, '{}':\n{}", + () -> response.getDomain().getName(), + () -> response.getDomain().toJSONObject().toString(2)); return response; } From 32e86c71e73d13bf31a27953ec921a5edef12084 Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 11 Nov 2025 09:29:06 -0800 Subject: [PATCH 14/15] Wait longer for metadata designer --- src/org/labkey/test/components/domain/DomainFormPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/components/domain/DomainFormPanel.java b/src/org/labkey/test/components/domain/DomainFormPanel.java index 615622a4df..7130c081e4 100644 --- a/src/org/labkey/test/components/domain/DomainFormPanel.java +++ b/src/org/labkey/test/components/domain/DomainFormPanel.java @@ -47,7 +47,7 @@ private DomainFormPanel(WebElement element, WebDriver driver) @Override protected void waitForReady() { - waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 2_000); + waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 5_000); } public static List advancedSettingsFromFieldDefinition(FieldDefinition def) From acbccdd9a19497572cfbcd7e6109c423e1ece90b Mon Sep 17 00:00:00 2001 From: labkey-tchad Date: Tue, 11 Nov 2025 09:58:13 -0800 Subject: [PATCH 15/15] Fix waits --- src/org/labkey/test/components/domain/DomainFormPanel.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/components/domain/DomainFormPanel.java b/src/org/labkey/test/components/domain/DomainFormPanel.java index 7130c081e4..615622a4df 100644 --- a/src/org/labkey/test/components/domain/DomainFormPanel.java +++ b/src/org/labkey/test/components/domain/DomainFormPanel.java @@ -47,7 +47,7 @@ private DomainFormPanel(WebElement element, WebDriver driver) @Override protected void waitForReady() { - waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 5_000); + waitFor(() -> !BootstrapLocators.loadingSpinner.existsIn(this), "Loading spinner still present", 2_000); } public static List advancedSettingsFromFieldDefinition(FieldDefinition def)