diff --git a/src/org/labkey/test/Locator.java b/src/org/labkey/test/Locator.java index c4b3e88e05..5fcf6137c7 100644 --- a/src/org/labkey/test/Locator.java +++ b/src/org/labkey/test/Locator.java @@ -20,8 +20,8 @@ import org.apache.commons.lang3.mutable.MutableObject; import org.intellij.lang.annotations.Language; import org.jetbrains.annotations.Contract; -import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import org.labkey.test.selenium.LazyWebElement; import org.labkey.test.selenium.ReclickingWebElement; import org.labkey.test.selenium.RefindingWebElement; @@ -1019,6 +1019,41 @@ public static String cq(String value) return "\"" + value.replace("\\", "\\\\").replace("\"", "\\\"") + "\""; } + public static Locator union(Locator... locators) + { + if (locators.length < 1) + throw new IllegalArgumentException("Specify at least one locator"); + if (locators.length == 1) + return locators[0]; + + List cssLocators = Arrays.stream(locators).map(Locator::asRawCssLocator) + .filter(Objects::nonNull).toList(); + if (cssLocators.size() == locators.length) + return CssLocator.union(cssLocators.toArray(new CssLocator[0])); + + List xPathLocators = Arrays.stream(locators).map(locator -> { + if (locator instanceof XPathLocator) + return (XPathLocator) locator; + else + return null; + }).filter(Objects::nonNull).toList(); + if (xPathLocators.size() == locators.length) + return XPathLocator.union(xPathLocators.toArray(new XPathLocator[0])); + + throw new IllegalArgumentException("Locators should be all CSS or all XPath"); + } + + private static CssLocator asRawCssLocator(Locator locator) + { + CssLocator cssLocator = null; + if (locator instanceof CssLocator cssLoc) + cssLocator = cssLoc; + else if (locator instanceof XPathCSSLocator xcLoc) + cssLocator = xcLoc.getCssLoc(); + + return cssLocator != null && cssLocator.isRawCssLocator() ? cssLocator : null; + } + private static class XPathCSSLocator extends XPathLocator { private final XPathLocator _xLoc; @@ -1670,9 +1705,9 @@ public static Locator union(CssLocator... locators) if (locators.length == 0) throw new IllegalArgumentException("Specify one or more locators to union"); - for (Locator loc : locators) + for (CssLocator loc : locators) { - if (loc._contains != null || loc._text != null || loc._index != null) + if (!loc.isRawCssLocator()) throw new IllegalArgumentException("Only able to union raw CSS selectors"); } @@ -1687,6 +1722,11 @@ public static Locator union(CssLocator... locators) return new WrappedLocator(new CssLocator(unionedLocators.toString())); } + private boolean isRawCssLocator() + { + return _contains == null && _text == null && _index == null; + } + @Override public Locator containing(String contains) { diff --git a/src/org/labkey/test/WebTestHelper.java b/src/org/labkey/test/WebTestHelper.java index c5c08a8a84..0313b07300 100644 --- a/src/org/labkey/test/WebTestHelper.java +++ b/src/org/labkey/test/WebTestHelper.java @@ -75,6 +75,7 @@ import java.net.URI; import java.net.URISyntaxException; import java.net.URL; +import java.net.URLDecoder; import java.net.UnknownHostException; import java.nio.charset.StandardCharsets; import java.security.KeyManagementException; @@ -544,6 +545,11 @@ public static Map parseUrlQuery(URL url) } public static Map parseUrlQueryString(String query) + { + return parseUrlQueryString(query, true); + } + + public static Map parseUrlQueryString(String query, boolean decode) { if (query != null) { @@ -556,7 +562,7 @@ public static Map parseUrlQueryString(String query) for (String arg : queryArgs) { String[] split = arg.split("=", 2); - parsedQuery.put(split[0], split.length > 1 ? split[1] : null); + parsedQuery.put(maybeDecode(split[0], decode), split.length > 1 ? maybeDecode(split[1], decode) : null); } return parsedQuery; @@ -565,6 +571,30 @@ public static Map parseUrlQueryString(String query) return Collections.emptyMap(); } + private static String maybeDecode(String value, boolean decode) + { + return decode + ? URLDecoder.decode(value, StandardCharsets.UTF_8) + : value; + } + + public static Map parseAppQuery(URL url) + { + String ref = url.getRef(); + + if (ref != null) + { + int queryIndex = ref.indexOf("?"); + + if (queryIndex > -1) + { + return parseUrlQueryString(ref.substring(queryIndex), true); + } + } + + return Collections.emptyMap(); + } + public static Connection getRemoteApiConnection() { return getRemoteApiConnection(true); diff --git a/src/org/labkey/test/components/ui/grids/ManageViewsDialog.java b/src/org/labkey/test/components/ui/grids/ManageViewsDialog.java index 42856d1caa..df3b654286 100644 --- a/src/org/labkey/test/components/ui/grids/ManageViewsDialog.java +++ b/src/org/labkey/test/components/ui/grids/ManageViewsDialog.java @@ -25,6 +25,7 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; +import org.openqa.selenium.support.ui.ExpectedConditions; import java.util.List; import java.util.stream.Collectors; @@ -158,7 +159,9 @@ public String getDeleteConfirmationText() */ public ManageViewsDialog confirmDelete() { - elementCache().deleteYesButton.click(); + WebElement deleteYesButton = elementCache().deleteYesButton; + deleteYesButton.click(); + getWrapper().quickWait().until(ExpectedConditions.stalenessOf(deleteYesButton)); return this; } @@ -169,7 +172,9 @@ public ManageViewsDialog confirmDelete() */ public ManageViewsDialog cancelDelete() { - elementCache().deleteNoButton.click(); + WebElement deleteNoButton = elementCache().deleteNoButton; + deleteNoButton.click(); + getWrapper().quickWait().until(ExpectedConditions.stalenessOf(deleteNoButton)); return this; } diff --git a/src/org/labkey/test/tests/BaseAppTest.java b/src/org/labkey/test/tests/BaseAppTest.java new file mode 100644 index 0000000000..58ef66abf4 --- /dev/null +++ b/src/org/labkey/test/tests/BaseAppTest.java @@ -0,0 +1,8 @@ +package org.labkey.test.tests; + +import org.labkey.test.BaseWebDriverTest; + +public abstract class BaseAppTest extends BaseWebDriverTest +{ + public abstract String getAppControllerName(); +}