diff --git a/src/org/labkey/test/BaseWebDriverTest.java b/src/org/labkey/test/BaseWebDriverTest.java index 96bc04b333..57b530e61b 100644 --- a/src/org/labkey/test/BaseWebDriverTest.java +++ b/src/org/labkey/test/BaseWebDriverTest.java @@ -216,8 +216,6 @@ public abstract class BaseWebDriverTest extends LabKeySiteWrapper implements Cle public static final double DELTA = 10E-10; - @Deprecated // Going away soon - public static final String[] ILLEGAL_QUERY_KEY_CHARACTERS = FieldKey.getIllegalChars().toArray(new String[0]); public static final String ALL_ILLEGAL_QUERY_KEY_CHARACTERS = StringUtils.join(FieldKey.getIllegalChars(), ""); // See TSVWriter.shouldQuote. Generally we are not able to use the tab and new line characters when creating field names in the UI, but including here for completeness public static final String[] TRICKY_IMPORT_FIELD_CHARACTERS = {"\\", "\"", "\\t", ",", "\\n", "\\r"}; @@ -300,7 +298,7 @@ public WebDriver getWrappedDriver() return SingletonWebDriver.getInstance().getWebDriver(); } - protected abstract @Nullable String getProjectName(); + protected abstract String getProjectName(); public final @Nullable String getPrimaryTestProject() { diff --git a/src/org/labkey/test/components/ui/grids/EditableGrid.java b/src/org/labkey/test/components/ui/grids/EditableGrid.java index f0ff60f2a2..aa4c8633d8 100644 --- a/src/org/labkey/test/components/ui/grids/EditableGrid.java +++ b/src/org/labkey/test/components/ui/grids/EditableGrid.java @@ -44,6 +44,7 @@ import java.util.LinkedHashSet; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import java.util.TimeZone; import java.util.function.Function; @@ -1114,6 +1115,20 @@ public String getCellError(int row, CharSequence columnIdentifier) return null; } + /** + * @param row row index + * @param columnIdentifier fieldKey, name, or label of column + * @return error popover text in the specified cell or 'null' if there is no error + */ + public String getErrorPopoverText(int row, CharSequence columnIdentifier) + { + WebElement gridCell = getCell(row, columnIdentifier); + + if (cellHasError(gridCell)) + return getCellPopoverText(row, columnIdentifier); + return null; + } + /** * @param row row index * @param columnIdentifier fieldKey, name, or label of column @@ -1123,11 +1138,17 @@ public String getCellPopoverText(int row, CharSequence columnIdentifier) { WebElement cellDiv = Locator.tagWithClass("div", "cellular-display").findElement(getCell(row, columnIdentifier)); getWrapper().mouseOver(cellDiv); // cause the tooltip to be present - if (WebDriverWrapper.waitFor(()-> null != Locator.byClass("popover").findElementOrNull(getDriver()), 1000)) - { - return Locator.byClass("popover").findElement(getDriver()).getText(); - } - return null; + return Optional.ofNullable(WebDriverWrapper.waitFor(()-> Locators.popover.findElementOrNull(getDriver()), 1000)) + .map(WebElement::getText) + .orElse(null); + } + + public void dismissPopover() + { + Locators.popover.findOptionalElement(getDriver()).ifPresent(popover -> { + getWrapper().mouseOut(); + getWrapper().shortWait().until(ExpectedConditions.invisibilityOf(popover)); + }); } public List getCellErrors() @@ -1286,6 +1307,7 @@ private Locators() static final Locator.XPathLocator rows = Locator.tag("tbody").childTag("tr").withoutClass("grid-empty").withoutClass("grid-loading"); static final Locator headerCells = Locator.css("thead tr th"); static final Locator inputCell = Locator.css(".eg-input-cell"); + static final Locator popover = Locator.byClass("popover"); } public static class EditableGridFinder extends WebDriverComponent.WebDriverComponentFinder diff --git a/src/org/labkey/test/components/ui/grids/GridFilterModal.java b/src/org/labkey/test/components/ui/grids/GridFilterModal.java index 2ffd46c9ad..0f1eb1892d 100644 --- a/src/org/labkey/test/components/ui/grids/GridFilterModal.java +++ b/src/org/labkey/test/components/ui/grids/GridFilterModal.java @@ -7,7 +7,9 @@ import org.labkey.test.components.react.Tabs; import org.labkey.test.components.ui.search.FilterExpressionPanel; import org.labkey.test.components.ui.search.FilterFacetedPanel; +import org.labkey.test.params.WrapsFieldKey; import org.labkey.test.util.selenium.WebElementUtils; +import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedConditions; @@ -36,12 +38,13 @@ protected void waitForReady() /** * Select field to configure filters for - * @param fieldLabel Field's label + * @param fieldIdentifier fieldKey or field label * @return this component */ - public GridFilterModal selectField(String fieldLabel) + public GridFilterModal selectField(CharSequence fieldIdentifier) { - WebElement fieldItem = elementCache().findFieldOption(fieldLabel); + WebElement fieldItem = elementCache().findFieldOption(fieldIdentifier); + String fieldLabel = WebElementUtils.getTextContent(fieldItem); fieldItem.click(); Locator.byClass("field-modal__col-sub-title").withText("Find values for " + fieldLabel) .waitForElement(elementCache().filterPanel, 10_000); @@ -182,15 +185,25 @@ protected ElementCache newElementCache() protected class ElementCache extends ModalDialog.ElementCache { - public final Locator listItemLoc = Locator.byClass("list-group-item"); + public final Locator.XPathLocator listItemLoc = Locator.byClass("list-group-item"); // Fields column public final WebElement fieldsSelectionPanel = Locator.byClass("filter-modal__col_fields") .findWhenNeeded(this); - protected WebElement findFieldOption(String queryName) + protected WebElement findFieldOption(CharSequence queryName) { - return listItemLoc.withText(queryName).findElement(elementCache().fieldsSelectionPanel); + try + { + return listItemLoc.withChild(Locator.tagWithAttribute("span", "data-fieldkey", queryName.toString())).findElement(fieldsSelectionPanel); + } + catch (NoSuchElementException nse) + { + if (!(queryName instanceof WrapsFieldKey)) + return listItemLoc.withText(queryName.toString()).findElement(elementCache().fieldsSelectionPanel); + else + throw nse; + } } protected List findFieldOptions() { diff --git a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java index 992e940d0d..4ffc4d2151 100644 --- a/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java +++ b/src/org/labkey/test/components/ui/grids/ResponsiveGrid.java @@ -27,6 +27,7 @@ import org.openqa.selenium.support.ui.ExpectedConditions; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.List; @@ -449,6 +450,17 @@ public T selectRows(CharSequence columnIdentifier, Collection texts, boo return getThis(); } + /** + * Checks the specified rows' selector checkboxes + * @param columnIdentifier fieldKey, name, or label of column + * @param texts Text to search for in the specified column + * @return this grid + */ + public T selectRows(CharSequence columnIdentifier, String... texts) + { + return selectRows(columnIdentifier, Arrays.asList(texts), true); + } + /** * Is the row at the selected index selected * @param index Row index (zero-based) diff --git a/src/org/labkey/test/tests/AdminConsoleNavigationTest.java b/src/org/labkey/test/tests/AdminConsoleNavigationTest.java index 0becdfcc6f..26e796b7e8 100644 --- a/src/org/labkey/test/tests/AdminConsoleNavigationTest.java +++ b/src/org/labkey/test/tests/AdminConsoleNavigationTest.java @@ -87,7 +87,7 @@ public void testAdminNavTrails() { linkHrefs.put(link.getText(), link.getAttribute("href")); } - Assertions.assertThat(linkHrefs.keySet()).as("Expected links").containsAll(ignoredLinks); + Assertions.assertThat(linkHrefs.keySet()).as("Expected links").containsAll(ignoredLinks.stream().map(String::toUpperCase).toList()); List pagesMissingNavTrail = new ArrayList<>(); diff --git a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java index 5f6eb4ce17..07597b6f68 100644 --- a/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java +++ b/src/org/labkey/test/tests/SampleTypeNameExpressionTest.java @@ -896,7 +896,7 @@ public void testNameExpressionPreview() throws IOException, CommandException mouseOver(createPage.getComponentElement()); log("Use a name expression using a field from the named parent, with parent type not encoded."); - nameExpressionBad = String.format("SNP_${genId}_${%s/$s}_${materialInputs/%s/%s}", parentAlias, PARENT_FIELD_CURLY_RIGHT_INT.getExpName(), PARENT_SAMPLE_TYPE, PARENT_FIELD_CURLY_LEFT.getName()); + nameExpressionBad = String.format("SNP_${genId}_${%s/%s}_${materialInputs/%s/%s}", parentAlias, PARENT_FIELD_CURLY_RIGHT_INT.getExpName(), PARENT_SAMPLE_TYPE, PARENT_FIELD_CURLY_LEFT.getName()); createPage.setNameExpression(nameExpressionBad); actualMsg = createPage.getNameExpressionPreview(); checker().withScreenshot("Parent_Fields_Preview_Error") diff --git a/src/org/labkey/test/util/DeferredErrorCollector.java b/src/org/labkey/test/util/DeferredErrorCollector.java index 1fc34cc028..0ae19ae8ab 100644 --- a/src/org/labkey/test/util/DeferredErrorCollector.java +++ b/src/org/labkey/test/util/DeferredErrorCollector.java @@ -4,6 +4,7 @@ import org.awaitility.core.ConditionTimeoutException; import org.hamcrest.Matcher; import org.hamcrest.MatcherAssert; +import org.jetbrains.annotations.Contract; import org.jetbrains.annotations.NotNull; import org.junit.Assert; import org.labkey.junit.LabKeyAssert; @@ -283,6 +284,7 @@ public final boolean verifyEqualsSorted(String message, Collection expect * @see Assert#assertTrue(String, boolean) * @return true if condition is true */ + @Contract("_, true -> true; _, false -> false") public final boolean verifyTrue(String message, boolean condition) { return wrapAssertion(() -> Assert.assertTrue(message, condition)); @@ -296,6 +298,7 @@ public final boolean verifyTrue(String message, boolean condition) * @see Assert#assertFalse(String, boolean) * @return true if condition is false */ + @Contract("_, false -> true; _, true -> false") public final boolean verifyFalse(String message, boolean condition) { return wrapAssertion(() -> Assert.assertFalse(message, condition)); @@ -309,6 +312,7 @@ public final boolean verifyFalse(String message, boolean condition) * @see Assert#assertNull(String, Object) * @return true if object is null */ + @Contract("_, null -> true; _, !null -> false") public final boolean verifyNull(String message, Object object) { return wrapAssertion(() -> Assert.assertNull(message, object)); @@ -322,6 +326,7 @@ public final boolean verifyNull(String message, Object object) * @see Assert#assertNotNull(String, Object) * @return true if object is not null */ + @Contract("_, !null -> true; _, null -> false") public final boolean verifyNotNull(String message, Object object) { return wrapAssertion(() -> Assert.assertNotNull(message, object)); @@ -349,6 +354,7 @@ public final boolean verifyThat(String reason, T actual, Matcher * @see Assert#fail() * @return Always returns false */ + @Contract("_ -> false") public final boolean error(String message) { return wrapAssertion(() -> Assert.fail(message)); diff --git a/src/org/labkey/test/util/data/TestDataUtils.java b/src/org/labkey/test/util/data/TestDataUtils.java index e4c7fbc4b8..8c0a71d50c 100644 --- a/src/org/labkey/test/util/data/TestDataUtils.java +++ b/src/org/labkey/test/util/data/TestDataUtils.java @@ -621,10 +621,4 @@ protected boolean shouldQuote(String value) return StringUtils.containsAny(value, _escapedChars); } } - - @Deprecated // Going away soon - public static String getEscapedNameExpression(String name) - { - return EscapeUtil.escapeForNameExpression(name); - } }