Skip to content

Commit b6ce426

Browse files
committed
Merge remote-tracking branch 'origin/develop' into fb_appHeaderCleanup
2 parents 806c33c + ef5c203 commit b6ce426

5 files changed

Lines changed: 108 additions & 39 deletions

File tree

src/org/labkey/test/Locator.java

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
import org.apache.commons.lang3.mutable.MutableObject;
2121
import org.intellij.lang.annotations.Language;
2222
import org.jetbrains.annotations.Contract;
23-
import org.jetbrains.annotations.Nullable;
2423
import org.jetbrains.annotations.NotNull;
24+
import org.jetbrains.annotations.Nullable;
2525
import org.labkey.test.selenium.LazyWebElement;
2626
import org.labkey.test.selenium.ReclickingWebElement;
2727
import org.labkey.test.selenium.RefindingWebElement;
@@ -1019,6 +1019,41 @@ public static String cq(String value)
10191019
return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\"";
10201020
}
10211021

1022+
public static Locator union(Locator... locators)
1023+
{
1024+
if (locators.length < 1)
1025+
throw new IllegalArgumentException("Specify at least one locator");
1026+
if (locators.length == 1)
1027+
return locators[0];
1028+
1029+
List<CssLocator> cssLocators = Arrays.stream(locators).map(Locator::asRawCssLocator)
1030+
.filter(Objects::nonNull).toList();
1031+
if (cssLocators.size() == locators.length)
1032+
return CssLocator.union(cssLocators.toArray(new CssLocator[0]));
1033+
1034+
List<XPathLocator> xPathLocators = Arrays.stream(locators).map(locator -> {
1035+
if (locator instanceof XPathLocator)
1036+
return (XPathLocator) locator;
1037+
else
1038+
return null;
1039+
}).filter(Objects::nonNull).toList();
1040+
if (xPathLocators.size() == locators.length)
1041+
return XPathLocator.union(xPathLocators.toArray(new XPathLocator[0]));
1042+
1043+
throw new IllegalArgumentException("Locators should be all CSS or all XPath");
1044+
}
1045+
1046+
private static CssLocator asRawCssLocator(Locator locator)
1047+
{
1048+
CssLocator cssLocator = null;
1049+
if (locator instanceof CssLocator cssLoc)
1050+
cssLocator = cssLoc;
1051+
else if (locator instanceof XPathCSSLocator xcLoc)
1052+
cssLocator = xcLoc.getCssLoc();
1053+
1054+
return cssLocator != null && cssLocator.isRawCssLocator() ? cssLocator : null;
1055+
}
1056+
10221057
private static class XPathCSSLocator extends XPathLocator
10231058
{
10241059
private final XPathLocator _xLoc;
@@ -1670,9 +1705,9 @@ public static Locator union(CssLocator... locators)
16701705
if (locators.length == 0)
16711706
throw new IllegalArgumentException("Specify one or more locators to union");
16721707

1673-
for (Locator loc : locators)
1708+
for (CssLocator loc : locators)
16741709
{
1675-
if (loc._contains != null || loc._text != null || loc._index != null)
1710+
if (!loc.isRawCssLocator())
16761711
throw new IllegalArgumentException("Only able to union raw CSS selectors");
16771712
}
16781713

@@ -1687,6 +1722,11 @@ public static Locator union(CssLocator... locators)
16871722
return new WrappedLocator(new CssLocator(unionedLocators.toString()));
16881723
}
16891724

1725+
private boolean isRawCssLocator()
1726+
{
1727+
return _contains == null && _text == null && _index == null;
1728+
}
1729+
16901730
@Override
16911731
public Locator containing(String contains)
16921732
{

src/org/labkey/test/WebTestHelper.java

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@
7575
import java.net.URI;
7676
import java.net.URISyntaxException;
7777
import java.net.URL;
78+
import java.net.URLDecoder;
7879
import java.net.UnknownHostException;
7980
import java.nio.charset.StandardCharsets;
8081
import java.security.KeyManagementException;
@@ -544,6 +545,11 @@ public static Map<String, String> parseUrlQuery(URL url)
544545
}
545546

546547
public static Map<String, String> parseUrlQueryString(String query)
548+
{
549+
return parseUrlQueryString(query, true);
550+
}
551+
552+
public static Map<String, String> parseUrlQueryString(String query, boolean decode)
547553
{
548554
if (query != null)
549555
{
@@ -556,7 +562,7 @@ public static Map<String, String> parseUrlQueryString(String query)
556562
for (String arg : queryArgs)
557563
{
558564
String[] split = arg.split("=", 2);
559-
parsedQuery.put(split[0], split.length > 1 ? split[1] : null);
565+
parsedQuery.put(maybeDecode(split[0], decode), split.length > 1 ? maybeDecode(split[1], decode) : null);
560566
}
561567

562568
return parsedQuery;
@@ -565,6 +571,30 @@ public static Map<String, String> parseUrlQueryString(String query)
565571
return Collections.emptyMap();
566572
}
567573

574+
private static String maybeDecode(String value, boolean decode)
575+
{
576+
return decode
577+
? URLDecoder.decode(value, StandardCharsets.UTF_8)
578+
: value;
579+
}
580+
581+
public static Map<String, String> parseAppQuery(URL url)
582+
{
583+
String ref = url.getRef();
584+
585+
if (ref != null)
586+
{
587+
int queryIndex = ref.indexOf("?");
588+
589+
if (queryIndex > -1)
590+
{
591+
return parseUrlQueryString(ref.substring(queryIndex), true);
592+
}
593+
}
594+
595+
return Collections.emptyMap();
596+
}
597+
568598
public static Connection getRemoteApiConnection()
569599
{
570600
return getRemoteApiConnection(true);

src/org/labkey/test/components/ui/grids/EditableGrid.java

Lines changed: 19 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1034,39 +1034,9 @@ public String copyCurrentSelection() throws IOException, UnsupportedFlavorExcept
10341034
return getWrapper().getClipboardContent();
10351035
}
10361036

1037-
/**
1038-
* Select a cell range and drag-fill from the end of that selection to {@code dragEnd}.
1039-
* Because this overload owns the {@link #selectCellRange} step, it can fully restore state
1040-
* and retry if the first drag extended the selection without applying the fill.
1041-
*
1042-
* @param selectStart first cell of the selection (passed to {@link #selectCellRange})
1043-
* @param selectEnd last cell of the selection; also the source of the fill value
1044-
* @param dragEnd destination cell for the fill drag
1045-
*/
1046-
public void dragFill(WebElement selectStart, WebElement selectEnd, WebElement dragEnd)
1047-
{
1048-
Locator.XPathLocator selectionHandleLoc = Locator.byClass("cell-selection-handle");
1049-
selectCellRange(selectStart, selectEnd);
1050-
selectEnd.click();
1051-
String fillValue = getCellValue(selectEnd);
1052-
WebElement selectionHandle = selectionHandleLoc.waitForElement(getComponentElement(), 2_000);
1053-
dragToCell(selectionHandle, dragEnd);
1054-
if (!WebDriverWrapper.waitFor(() -> fillValue.equals(getCellValue(dragEnd)), 3_000))
1055-
{
1056-
// Fill didn't complete — the drag likely extended the selection without triggering the fill.
1057-
selectCellRange(selectStart, selectEnd);
1058-
selectEnd.click();
1059-
selectionHandle = selectionHandleLoc.waitForElement(getComponentElement(), 2_000);
1060-
dragToCell(selectionHandle, dragEnd);
1061-
WebDriverWrapper.waitFor(() -> fillValue.equals(getCellValue(dragEnd)),
1062-
"Drag fill did not populate end cell with value: " + fillValue, 5_000);
1063-
}
1064-
}
1065-
10661037
/**
10671038
* Drag-fill from {@code startCell} (which must already be selected / part of the current
1068-
* selection) to {@code endCell}. Prefer {@link #dragFill(WebElement, WebElement, WebElement)}
1069-
* when the selection range is known — that overload can retry reliably.
1039+
* selection) to {@code endCell}.
10701040
*/
10711041
public void dragFill(WebElement startCell, WebElement endCell)
10721042
{
@@ -1077,6 +1047,21 @@ public void dragFill(WebElement startCell, WebElement endCell)
10771047
selectionHandleLoc.waitForElement(endCell, 5_000);
10781048
}
10791049

1050+
/**
1051+
* Select {@code selectStart} through {@code dragEnd} and fill down from {@code selectEnd}'s row using Ctrl/Cmd+D.
1052+
* @param selectStart top-left cell of the range to select
1053+
* @param selectEnd cell whose value will be filled down (must be in {@code selectStart}'s row)
1054+
* @param dragEnd bottom-right cell of the range to select and fill down to
1055+
*/
1056+
public void fillDown(WebElement selectStart, WebElement selectEnd, WebElement dragEnd)
1057+
{
1058+
selectCellRange(selectStart, dragEnd);
1059+
String fillValue = getCellValue(selectEnd);
1060+
new Actions(getDriver()).keyDown(MODIFIER_KEY).sendKeys("d").keyUp(MODIFIER_KEY).build().perform();
1061+
WebDriverWrapper.waitFor(() -> fillValue.equals(getCellValue(dragEnd)),
1062+
"Fill-down did not populate end cell with value: " + fillValue, 3_000);
1063+
}
1064+
10801065
public void selectCellRange(WebElement startCell, WebElement endCell)
10811066
{
10821067
dragToCell(startCell, endCell);
@@ -1195,8 +1180,9 @@ private boolean isInSelection(WebElement cell) // 'in selection' shows as blue
11951180
{
11961181
// Should not need to add code for a reactSelect here. A selection involves clicking/dragging, which closes the reactSelect.
11971182
return Locator.tagWithClass("div", "cellular-display")
1198-
.findElement(cell)
1199-
.getDomAttribute("class").contains("cell-selection");
1183+
.findOptionalElement(cell)
1184+
.map(el -> el.getDomAttribute("class").contains("cell-selection"))
1185+
.orElse(false);
12001186
}
12011187

12021188
/**

src/org/labkey/test/components/ui/grids/ManageViewsDialog.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
import org.openqa.selenium.WebDriver;
2626
import org.openqa.selenium.WebElement;
2727
import org.openqa.selenium.interactions.Actions;
28+
import org.openqa.selenium.support.ui.ExpectedConditions;
2829

2930
import java.util.List;
3031
import java.util.stream.Collectors;
@@ -158,7 +159,9 @@ public String getDeleteConfirmationText()
158159
*/
159160
public ManageViewsDialog confirmDelete()
160161
{
161-
elementCache().deleteYesButton.click();
162+
WebElement deleteYesButton = elementCache().deleteYesButton;
163+
deleteYesButton.click();
164+
getWrapper().quickWait().until(ExpectedConditions.stalenessOf(deleteYesButton));
162165
return this;
163166
}
164167

@@ -169,7 +172,9 @@ public ManageViewsDialog confirmDelete()
169172
*/
170173
public ManageViewsDialog cancelDelete()
171174
{
172-
elementCache().deleteNoButton.click();
175+
WebElement deleteNoButton = elementCache().deleteNoButton;
176+
deleteNoButton.click();
177+
getWrapper().quickWait().until(ExpectedConditions.stalenessOf(deleteNoButton));
173178
return this;
174179
}
175180

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package org.labkey.test.tests;
2+
3+
import org.labkey.test.BaseWebDriverTest;
4+
5+
public abstract class BaseAppTest extends BaseWebDriverTest
6+
{
7+
public abstract String getAppControllerName();
8+
}

0 commit comments

Comments
 (0)