From e536f3d309909a374db2a26e910fbf219d032676 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Tue, 25 Aug 2026 16:39:08 -0700 Subject: [PATCH 1/3] Change impersonate to API in DataViewsPermissionsTest (#3169) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Rationale The DataViewsPermissionsTest sometimes fails intermittently due to issues with UI impersonation. ## Related Pull Requests - ## Changes - changed impersonation to API - small fix for click editDataViews ---- Build: [https://teamcity.labkey.org/buildConfiguration/LabKey_267Release_Community_DailySuites_DailyAPostgres/4150155](https://teamcity.labkey.org/buildConfiguration/LabKey_267Release_Community_DailySuites_DailyAPostgres/4150155) --- .../test/tests/DataViewsPermissionsTest.java | 46 +++++++++++-------- 1 file changed, 27 insertions(+), 19 deletions(-) diff --git a/src/org/labkey/test/tests/DataViewsPermissionsTest.java b/src/org/labkey/test/tests/DataViewsPermissionsTest.java index 183b2ed313..10169df09e 100644 --- a/src/org/labkey/test/tests/DataViewsPermissionsTest.java +++ b/src/org/labkey/test/tests/DataViewsPermissionsTest.java @@ -24,6 +24,7 @@ import org.labkey.test.components.html.BootstrapMenu; import org.labkey.test.util.Ext4Helper; import org.labkey.test.util.PortalHelper; +import org.labkey.test.util.TestUser; import static org.junit.Assert.assertTrue; import static org.labkey.test.util.PermissionsHelper.AUTHOR_ROLE; @@ -33,8 +34,8 @@ @BaseWebDriverTest.ClassTimeout(minutes = 10) public class DataViewsPermissionsTest extends StudyBaseTest { - public static final String AUTHOR_USER = "dvp_author@dataviews.test"; - public static final String EDITOR_USER = "dvp_editor@dataviews.test"; + public static final TestUser AUTHOR_USER = new TestUser("dvp_author@dataviews.test"); + public static final TestUser EDITOR_USER = new TestUser("dvp_editor@dataviews.test"); private final PortalHelper portalHelper = new PortalHelper(this); @Override @@ -61,6 +62,9 @@ protected void doCreateSteps() { importStudy(); + AUTHOR_USER.create(this); + EDITOR_USER.create(this); + clickFolder(getFolderName()); portalHelper.addWebPart("Data Views"); portalHelper.enterAdminMode(); @@ -79,13 +83,13 @@ protected void doCreateSteps() _permissionsHelper.createPermissionsGroup("Editor Group"); _permissionsHelper.assertPermissionSetting("Editor Group", "No Permissions"); _permissionsHelper.setPermissions("Editor Group", EDITOR_ROLE); - createUserInProjectForGroup(EDITOR_USER, "StudyVerifyProject", "Editor Group", false); + addUserToProjectGroup(EDITOR_USER, "StudyVerifyProject", "Editor Group", false); clickFolder(getFolderName()); _permissionsHelper.enterPermissionsUI(); _permissionsHelper.createPermissionsGroup("Author Group"); _permissionsHelper.assertPermissionSetting("Author Group", "No Permissions"); _permissionsHelper.setPermissions("Author Group", AUTHOR_ROLE); - createUserInProjectForGroup(AUTHOR_USER, "StudyVerifyProject", "Author Group", false); + addUserToProjectGroup(AUTHOR_USER, "StudyVerifyProject", "Author Group", false); clickFolder(getFolderName()); clickTab("Manage"); @@ -113,7 +117,6 @@ protected void doCreateSteps() clickButton("Create View", defaultWaitForPage); clickFolder(getFolderName()); portalHelper.removeWebPart("Views"); - click(Locator.tag("a").withAttributeContaining("href", "editDataViews")); openEditPanel("Report 1"); //_ext4Helper.selectRadioButton ("Visibility","Hidden"); _ext4Helper.uncheckCheckbox("Shared"); @@ -121,17 +124,15 @@ protected void doCreateSteps() _ext4Helper.clickWindowButton("Report 1","Save",0,0); _ext4Helper.waitForMaskToDisappear(); clickFolder(getFolderName()); - click(Locator.tag("a").withAttributeContaining("href", "editDataViews")); openEditPanel("Report 2"); - _ext4Helper.selectComboBoxItem("Author", _userHelper.getDisplayNameForEmail(AUTHOR_USER)); + _ext4Helper.selectComboBoxItem("Author", AUTHOR_USER.getUserDisplayName()); _ext4Helper.checkCheckbox("Shared"); sleep(1000); _ext4Helper.clickWindowButton("Report 2","Save",0,0); _ext4Helper.waitForMaskToDisappear(); clickFolder(getFolderName()); - click(Locator.tag("a").withAttributeContaining("href", "editDataViews")); openEditPanel("Report 3"); - _ext4Helper.selectComboBoxItem("Author", _userHelper.getDisplayNameForEmail(EDITOR_USER)); + _ext4Helper.selectComboBoxItem("Author", EDITOR_USER.getUserDisplayName()); _ext4Helper.checkCheckbox("Shared"); sleep(1000); _ext4Helper.clickWindowButton("Report 3","Save",0,0); @@ -139,19 +140,17 @@ protected void doCreateSteps() } @Override - protected void doVerifySteps() + protected void doVerifySteps() throws Exception { - impersonate(EDITOR_USER); - click(Locator.tag("a").withAttributeContaining("href", "editDataViews")); + EDITOR_USER.impersonate(true); openEditPanel("Report 4"); sleep(1000); _ext4Helper.clickWindowButton("Report 4", "Save", 0, 0); _ext4Helper.waitForMaskToDisappear(); - stopImpersonating(); + EDITOR_USER.stopImpersonating(true); navigateToFolder("StudyVerifyProject", getFolderName()); - sleep(500); - impersonate(AUTHOR_USER); + AUTHOR_USER.impersonate(true); PortalHelper portalHelper1 = new PortalHelper(this); portalHelper1.clickWebpartMenuItem("Data Views", true, "Add Report", "Link Report"); setFormElement(Locator.name("viewName"), "Report 5"); @@ -159,7 +158,6 @@ protected void doVerifySteps() sleep(1000); clickButton("Save", defaultWaitForPage); - click(Locator.tag("a").withAttributeContaining("href", "editDataViews")); openEditPanel("Report 5"); sleep(1000); _ext4Helper.clickWindowButton("Report 5", "Save", 0, 0); @@ -168,8 +166,13 @@ protected void doVerifySteps() verifyMineCheckbox(); } + /** + * Put the Data Views webpart into edit mode and open the edit dialog for a single view. + */ private void openEditPanel(String itemName) { + waitForElement(Locators.itemLink()); + click(Locator.tag("a").withAttributeContaining("href", "editDataViews")); waitAndClick(Locators.editViewsLink(itemName)); waitForElement(Ext4Helper.Locators.window(itemName)); } @@ -199,11 +202,16 @@ private void waitForItemCount(String errMsg, int expectedCount) private int visibleItemCount() { - return getElementCount(Locator.tagWithClass("a", "x4-tree-node-text").notHidden()); + return getElementCount(Locators.itemLink().notHidden()); } public static class Locators { + static Locator.XPathLocator itemLink() + { + return Locator.tagWithClass("a", "x4-tree-node-text"); + } + static Locator.XPathLocator editViewsLink(String dataset) { return Locator.tag("tr").withClass("x4-grid-tree-node-leaf").withDescendant(Locator.xpath("td/div/a[normalize-space()="+Locator.xq(dataset)+"]")).append("//span").withClass("edit-views-link"); @@ -221,7 +229,7 @@ static Locator.XPathLocator expanderForUncategorized() } } - private void createUserInProjectForGroup(String userName, String projectName, String groupName, boolean sendEmail) + private void addUserToProjectGroup(TestUser user, String projectName, String groupName, boolean sendEmail) { if (isElementPresent(Locator.permissionRendered())) { @@ -230,7 +238,7 @@ private void createUserInProjectForGroup(String userName, String projectName, St } _permissionsHelper.enterPermissionsUI(); _permissionsHelper.clickManageGroup(groupName); - setFormElement(Locator.name("names"), userName); + setFormElement(Locator.name("names"), user.getEmail()); if (!sendEmail) uncheckCheckbox(Locator.checkboxByName("sendEmail")); clickButton("Update Group Membership"); From a2651b78e0342328e3c43bc41a7bdcb795896614 Mon Sep 17 00:00:00 2001 From: Daria Bodiakova <70635654+DariaBod@users.noreply.github.com> Date: Thu, 3 Sep 2026 14:33:51 -0700 Subject: [PATCH 2/3] Fix for AuthenticationProviderReorderTest.testReorderConfigurations (#3183) ## Rationale A single-jump Selenium drag can lift and drop having registered no movement, so the reorder silently does nothing. Some of the changes in this PR are already in develop due to previous test fixes. ## Related Pull Requests - https://github.com/LabKey/platform/pull/8015 ## Changes - `WebDriverWrapper.keyboardDragAndDrop(WebElement, int)` drives the library's keyboard sensor instead of the mouse: Space to lift, one Arrow per step, Space to drop. - Overload taking a target element derives the step count from the two handles' sibling indexes, so call sites read like the existing `dragAndDrop(from, to)`. - `FieldSelectionDialog.repositionField` delegates to the shared helper instead of its own copy. - `LoginConfigRow.getDragHandle()` exposes a row's drag handle. On the authentication configuration page `dragHandleProps` sit on the row wrapper rather than the visible `.domain-row-handle` icon, so the handle is an ancestor of the row's component element. --- src/org/labkey/test/WebDriverWrapper.java | 29 +++++++ .../ui/grids/FieldSelectionDialog.java | 80 ++++++++++--------- .../test/pages/core/login/LoginConfigRow.java | 8 ++ 3 files changed, 79 insertions(+), 38 deletions(-) diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index b742bde15c..4ea14429f2 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -3086,6 +3086,35 @@ public void dragAndDrop(WebElement fromEl, int xOffset, int yOffset) builder.clickAndHold(fromEl).moveByOffset(xOffset + 1, yOffset + 1).release().build().perform(); } + /** + * Reorder a drag-and-drop list with the keyboard instead of the mouse: focus the drag handle, Space to lift, + * one Arrow per step, Space to drop. Required for lists built on '@hello-pangea/dnd' (auth configurations, + * domain designer, field selection). + * + * @param dragHandle Element carrying the library's drag handle props; must be focusable. + * @param steps Positions to move; negative moves up, positive moves down. + */ + public void keyboardDragAndDrop(WebElement dragHandle, int steps) + { + dragHandle.sendKeys(Keys.SPACE); + + Actions drag = new Actions(getDriver()).pause(Duration.ofMillis(400)); + Keys arrow = steps < 0 ? Keys.ARROW_UP : Keys.ARROW_DOWN; + for (int i = 0; i < Math.abs(steps); i++) + drag.sendKeys(arrow).pause(Duration.ofMillis(300)); + drag.sendKeys(Keys.SPACE).perform(); + } + + /** + * @see #keyboardDragAndDrop(WebElement, int) + * @param dragHandle Drag handle of the element to move. + * @param target Drag handle of a sibling element to move it to. + */ + public void keyboardDragAndDrop(WebElement dragHandle, WebElement target) + { + keyboardDragAndDrop(dragHandle, getElementIndex(target) - getElementIndex(dragHandle)); + } + // This is useful when making a draggin selection in a plot, and there may be many elements ontop of the one you want. public void dragAndDrop(int xOffset, int yOffset) { diff --git a/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java b/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java index 73b5c143e6..a7b4cf5eca 100644 --- a/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java +++ b/src/org/labkey/test/components/ui/grids/FieldSelectionDialog.java @@ -522,49 +522,48 @@ private List getSelectedListItems(String fieldLabel) */ public FieldSelectionDialog repositionField(FieldKey fieldToMove, FieldKey targetField, boolean beforeTarget) { - WebElement elementToMove = elementCache().findSelectedField(fieldToMove); - WebElement elementTarget = elementCache().findSelectedField(targetField); + List order = selectedFieldKeys(); + int from = indexOfFieldKey(order, fieldToMove); + int target = indexOfFieldKey(order, targetField); + int to = beforeTarget ? (from < target ? target - 1 : target) : (from < target ? target : target + 1); - int yBefore = elementToMove.getRect().getY(); + getWrapper().keyboardDragAndDrop(elementCache().findDragHandle(fieldToMove), to - from); - int offset; + WebDriverWrapper.waitFor(() -> { + List now = selectedFieldKeys(); + return indexOfFieldKey(now, fieldToMove) - indexOfFieldKey(now, targetField) == (beforeTarget ? -1 : 1); + }, "Field '" + fieldToMove + "' was not repositioned as expected", 5_000); - if(beforeTarget) - { - if(elementTarget.getRect().getY() < elementToMove.getRect().getY()) - { - // If the target is above the field being moved. - offset = -1 * elementTarget.getSize().getHeight(); - } - else - { - // If the target is below the field being moved. - offset = -1 * elementTarget.getSize().getHeight() / 2; - } - } - else - { - offset = elementTarget.getSize().getHeight() / 2 + 10; - } - - WebElement dragHandle = Locator.tagWithAttribute("div", "role", "button").findWhenNeeded(elementToMove); - getWrapper().mouseOver(dragHandle); - new Actions(getDriver()) - .clickAndHold(dragHandle) - .moveToElement(elementTarget) - .moveByOffset(2, offset) - .release() - .perform(); - - // Maybe I don't need to wait? - WebDriverWrapper.sleep(1_000); - - int yAfter = elementToMove.getRect().getY(); + return this; + } - WebDriverWrapper.waitFor(()-> yAfter != yBefore, "I don't think I repositioned the field in the list.", - 1_000); + /** + * Get the 'data-fieldkey' values of the selected fields, in display order. + * + * @return The encoded field keys. + */ + private List selectedFieldKeys() + { + return elementCache().getListItemElements(elementCache().selectedFieldsPanel).stream() + .map(el -> el.getDomAttribute("data-fieldkey")) + .collect(Collectors.toList()); + } - return this; + /** + * Get the index of a field in the given list, matched case-insensitively (as findFieldRow locates rows). + * + * @param fieldKeys Encoded field keys, in display order. + * @param fieldKey Field to locate. + * @return The index, or -1 if not present. + */ + private static int indexOfFieldKey(List fieldKeys, FieldKey fieldKey) + { + for (int i = 0; i < fieldKeys.size(); i++) + { + if (fieldKey.toString().equalsIgnoreCase(fieldKeys.get(i))) + return i; + } + return -1; } /** @@ -662,6 +661,11 @@ protected WebElement findSelectedField(FieldKey fieldKey) return findFieldRow(fieldKey, selectedFieldsPanel); } + protected WebElement findDragHandle(FieldKey fieldKey) + { + return Locator.tagWithAttribute("div", "role", "button").findElement(findSelectedField(fieldKey)); + } + protected WebElement findAvailableField(FieldKey fieldKey) { return findFieldRow(fieldKey, availableFieldsPanel); diff --git a/src/org/labkey/test/pages/core/login/LoginConfigRow.java b/src/org/labkey/test/pages/core/login/LoginConfigRow.java index 2f840381e8..42059113e0 100644 --- a/src/org/labkey/test/pages/core/login/LoginConfigRow.java +++ b/src/org/labkey/test/pages/core/login/LoginConfigRow.java @@ -72,6 +72,11 @@ public boolean canEdit() return elementCache().editButtonLoc.existsIn(this); } + public WebElement getDragHandle() + { + return elementCache().dragHandle; + } + @Override public WebElement getComponentElement() { @@ -93,6 +98,9 @@ protected ElementCache newElementCache() protected class ElementCache extends Component.ElementCache { + final WebElement dragHandle = Locator.xpath("ancestor::div") + .withAttribute("data-rfd-drag-handle-draggable-id").findWhenNeeded(this); + final WebElement baseFieldsElement = Locator.tagWithClass("div", "domain-row-base-fields").findWhenNeeded(this); final WebElement description = Locator.tagWithClass("div", "description").findWhenNeeded(baseFieldsElement); final WebElement details = Locator.tagWithClass("div", "details").findWhenNeeded(baseFieldsElement); From aebacefec82a2fbd8171f1033acbcc93e30373be Mon Sep 17 00:00:00 2001 From: Adam Rauch Date: Thu, 3 Sep 2026 14:49:16 -0700 Subject: [PATCH 3/3] Update productKeys used in tests (#3184) (#3185) ## Rationale Update product keys backport Co-authored-by: Susan Hert --- src/org/labkey/test/LabKeySiteWrapper.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index e0a9c51281..62b26da729 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -117,9 +117,9 @@ public enum ProductKey { sampleManagerStarter, sampleManagerProfessional, - labkeyLims, limsStarter, limsEnterprise, + biologics, } public boolean isGuestModeTest()