diff --git a/src/org/labkey/test/LabKeySiteWrapper.java b/src/org/labkey/test/LabKeySiteWrapper.java index 03f757301c..de4a1135f7 100644 --- a/src/org/labkey/test/LabKeySiteWrapper.java +++ b/src/org/labkey/test/LabKeySiteWrapper.java @@ -431,7 +431,7 @@ public void signInShouldFail(String email, String password, String... expectedMe errorText = getText(Locator.tagWithClass("div", "auth-form-body").childTag("p")); } - List missingErrors = getMissingTexts(new TextSearcher(errorText), expectedMessages); + List missingErrors = new TextSearcher(errorText).getMissingTexts(expectedMessages); assertTrue(String.format("Wrong errors.\nExpected: ['%s']\nActual: '%s'", String.join("',\n'", expectedMessages), errorText), missingErrors.isEmpty()); } diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index 2fec90445a..5a53c34bd7 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -18,7 +18,6 @@ import org.apache.commons.collections4.MultiValuedMap; import org.apache.commons.collections4.multimap.HashSetValuedHashMap; import org.apache.commons.lang3.StringUtils; -import org.apache.commons.lang3.mutable.MutableBoolean; import org.apache.commons.lang3.mutable.MutableInt; import org.apache.commons.lang3.tuple.ImmutablePair; import org.apache.commons.lang3.tuple.Pair; @@ -62,6 +61,7 @@ import org.labkey.test.util.RelativeUrl; import org.labkey.test.util.TestLogger; import org.labkey.test.util.TextSearcher; +import org.labkey.test.util.TextSearcher.TextTransformers; import org.labkey.test.util.Timer; import org.labkey.test.util.selenium.ScrollUtils; import org.labkey.test.util.selenium.WebDriverUtils; @@ -149,6 +149,7 @@ import java.util.concurrent.Executors; import java.util.concurrent.Future; import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Supplier; import java.util.logging.Level; import java.util.logging.Logger; @@ -168,6 +169,7 @@ import static org.labkey.test.TestProperties.isWebDriverLoggingEnabled; import static org.labkey.test.WebTestHelper.makeRelativeUrl; import static org.labkey.test.components.html.RadioButton.RadioButton; +import static org.labkey.test.util.LabKeyExpectedConditions.windowIsPresent; import static org.openqa.selenium.chrome.ChromeDriverService.CHROME_DRIVER_LOG_PROPERTY; import static org.openqa.selenium.chrome.ChromeDriverService.CHROME_DRIVER_VERBOSE_LOG_PROPERTY; import static org.openqa.selenium.firefox.GeckoDriverService.GECKO_DRIVER_LOG_PROPERTY; @@ -1621,29 +1623,27 @@ public void assertLabKeyErrorPresent() fail("No errors found"); } - public static String encodeText(String unencodedText) + /** + * Check whether all the specified text is present on the page + * @param texts un-encoded text to search for + * @return true if all the specified texts are present on the page + */ + public boolean isTextPresent(String... texts) { - return unencodedText - .replace("&", "&") - .replace("<", "<") - .replace(">", ">"); + return new TextSearcher(this).areAllTextsPresent(texts); } - public boolean isTextPresent(String... texts) + /** + * Check whether the HTML-encoded text is in the page source + * @param htmlFragments encoded html fragments to search for + * @return true if all the specified texts are present on the page + */ + public boolean isHtmlPresent(String... htmlFragments) { - final MutableBoolean present = new MutableBoolean(true); - - TextSearcher.TextHandler handler = (htmlSource, text) -> { - // Not found... stop enumerating and return false - if (htmlSource == null || !htmlSource.contains(text)) - present.setFalse(); - - return present.get(); - }; TextSearcher searcher = new TextSearcher(this); - searcher.searchForTexts(handler, Arrays.asList(texts)); + searcher.setSearchTransformer(TextTransformers.IDENTITY); - return present.get(); + return searcher.areAllTextsPresent(htmlFragments); } public List getTextOrder(TextSearcher searcher, String... texts) @@ -1666,11 +1666,6 @@ public List getTextOrder(TextSearcher searcher, String... texts) return orderedTexts; } - public List getMissingTexts(TextSearcher searcher, String... texts) - { - return searcher.getMissingTexts(Arrays.asList(texts)); - } - public String getText(Locator elementLocator) { WebElement el = elementLocator.findElement(getDriver()); @@ -1699,7 +1694,7 @@ public void assertTextPresent(String... texts) public void assertTextPresent(TextSearcher searcher, String... texts) { - List missingTexts = getMissingTexts(searcher, texts); + List missingTexts = searcher.getMissingTexts(texts); if (!missingTexts.isEmpty()) { @@ -1716,7 +1711,7 @@ public void assertTextPresentCaseInsensitive(String... texts) { TextSearcher searcher = new TextSearcher(this); - searcher.setSearchTransformer((text) -> encodeText(text).toLowerCase()); + searcher.setSearchTransformer(TextTransformers.ENCODE_HTML.andThen(String::toLowerCase)); searcher.setSourceTransformer(String::toLowerCase); @@ -1728,18 +1723,7 @@ public void assertTextPresentCaseInsensitive(String... texts) */ public boolean isAnyTextPresent(String... texts) { - final MutableBoolean found = new MutableBoolean(false); - - TextSearcher.TextHandler handler = (htmlSource, text) -> { - if (htmlSource.contains(text)) - found.setTrue(); - - return !found.get(); // stop searching if any value is found - }; - TextSearcher searcher = new TextSearcher(this); - searcher.searchForTexts(handler, Arrays.asList(texts)); - - return found.get(); + return new TextSearcher(this).isAnyTextPresent(texts); } /** @@ -1899,7 +1883,7 @@ public static void assertTextNotPresent(TextSearcher searcher, String... texts) public void assertTextNotPresent(String... texts) { TextSearcher searcher = new TextSearcher(this); - searcher.setSearchTransformer((text) -> encodeText(text).replace(" ", " ")); + searcher.setSearchTransformer(TextTransformers.ENCODE_HTML.andThen(t -> t.replace(" ", " "))); assertTextNotPresent(searcher, texts); } @@ -2141,24 +2125,29 @@ public long doAndMaybeWaitForPageToLoad(int msWait, Supplier action) public long doAndWaitForWindow(Runnable action, String windowName) { + String initialWindow = getDriver().getWindowHandle(); + AtomicBoolean targetWindowExists = new AtomicBoolean(false); + try + { + getDriver().switchTo().window(windowName); + targetWindowExists.set(true); + } + catch (NoSuchWindowException e) + { + targetWindowExists.set(false); + } + + // Call doAndMaybeWaitForPageToLoad with target window in focus (if present) + // Then it will correctly detect the page load in that window return doAndMaybeWaitForPageToLoad(10_000, () -> { - String initialWindow = getDriver().getWindowHandle(); - boolean targetWindowExists; - try - { - getDriver().switchTo().window(windowName); + if (targetWindowExists.get()) getDriver().switchTo().window(initialWindow); - targetWindowExists = true; - } - catch (NoSuchWindowException e) - { - targetWindowExists = false; - } action.run(); - getDriver().switchTo().window(windowName); - return targetWindowExists; + new WebDriverWait(getDriver(), Duration.ofSeconds(5)).until(windowIsPresent(windowName)); + + return targetWindowExists.get(); }); } diff --git a/src/org/labkey/test/pages/reports/ManageViewsPage.java b/src/org/labkey/test/pages/reports/ManageViewsPage.java index 810b70f5eb..6ed6bbb3a9 100644 --- a/src/org/labkey/test/pages/reports/ManageViewsPage.java +++ b/src/org/labkey/test/pages/reports/ManageViewsPage.java @@ -17,11 +17,17 @@ import org.labkey.test.Locator; import org.labkey.test.components.ChartQueryDialog; +import org.labkey.test.components.ext4.Window; import org.labkey.test.components.html.BootstrapMenu; import org.labkey.test.pages.LabKeyPage; import org.labkey.test.util.LogMethod; +import org.openqa.selenium.NoSuchElementException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; +import org.openqa.selenium.support.ui.ExpectedConditions; +import org.openqa.selenium.support.ui.WebDriverWait; + +import java.time.Duration; import static org.labkey.test.components.ext4.Window.Window; @@ -34,7 +40,7 @@ public ManageViewsPage(WebDriver driver) public void clickAddReport(String reportType) { - BootstrapMenu.find(getDriver(),"Add Report").clickSubMenu(true,reportType); + new BootstrapMenu.BootstrapMenuFinder(getDriver()).withButtonTextContaining("Add Report").find().clickSubMenu(true,reportType); } public ChartQueryDialog clickAddChart() @@ -89,4 +95,42 @@ public void viewReport(String reportName) mouseOver(reportLink); clickAndWait(reportLink); } + + public void createCategory(String categoryName) + { + createCategories(categoryName); + } + + public void createCategories(String... categoryNames) + { + Locator.linkWithText("Manage Categories").findElement(getDriver()).click(); + Window categoryWindow = new Window.WindowFinder(getDriver()).withTitle("Manage Categories").waitFor(); + for (String categoryName : categoryNames) + addCategory(categoryName, categoryWindow); + clickButton("Done", 0); + categoryWindow.waitForClose(); + } + + private void addCategory(String categoryName, Window categoryWindow) + { + int attempt = 0; + while (true) + { + categoryWindow.clickButton("New Category", 0); + WebElement newCategoryField = Locator.input("label").withAttributeContaining("id", "textfield").notHidden().waitForElement(categoryWindow, WAIT_FOR_JAVASCRIPT); + setFormElementJS(newCategoryField, categoryName); + fireEvent(newCategoryField, SeleniumEvent.blur); + new WebDriverWait(getDriver(), Duration.ofSeconds(2)).until(ExpectedConditions.invisibilityOf(newCategoryField)); + try + { + Locator.tagWithText("div", categoryName).waitForElement(categoryWindow, 2_000); + break; + } + catch (NoSuchElementException e) + { + if (++attempt >= 3) + throw e; + } + } + } } diff --git a/src/org/labkey/test/tests/ClientAPITest.java b/src/org/labkey/test/tests/ClientAPITest.java index a9b4549d91..3a6ceea1d1 100644 --- a/src/org/labkey/test/tests/ClientAPITest.java +++ b/src/org/labkey/test/tests/ClientAPITest.java @@ -57,6 +57,7 @@ import org.labkey.test.util.PermissionsHelper; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.StudyHelper; +import org.labkey.test.util.TestDataGenerator; import org.labkey.test.util.data.TestDataUtils; import org.labkey.test.util.UIUserHelper; import org.labkey.test.util.WikiHelper; @@ -77,9 +78,12 @@ import static org.hamcrest.CoreMatchers.containsString; import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.endsWith; +import static org.hamcrest.Matchers.startsWith; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.labkey.test.WebTestHelper.getHttpResponse; import static org.labkey.test.params.FieldDefinition.DOMAIN_TRICKY_CHARACTERS; @@ -93,8 +97,10 @@ public class ClientAPITest extends BaseWebDriverTest private static final String PROJECT_NAME = "ClientAPITestProject"; private static final String OTHER_PROJECT = "OtherClientAPITestProject"; // for cross-project query test - protected static final String FOLDER_NAME = "api folder"; + protected static final String API_FOLDER_NAME = "api folder"; + private static final String API_FOLDER_PATH = PROJECT_NAME + "/" + API_FOLDER_NAME; private static final String SUBFOLDER_NAME = "subfolder"; + private static final String SUBFOLDER_PATH = API_FOLDER_PATH + "/" + SUBFOLDER_NAME; private static final String TIME_STUDY_FOLDER = "timeStudyFolder"; private static final String TIME_STUDY_NAME = "timeStudyName"; private static final String VISIT_STUDY_FOLDER = "visitStudyFolder"; @@ -129,10 +135,6 @@ public class ClientAPITest extends BaseWebDriverTest public static final String TEST_DIV_NAME = "testDiv"; - private static final String GRIDTEST_GRIDTITLE = "ClientAPITest Grid Title"; - - private static final int PAGE_SIZE = 4; - public static final String SRC_PREFIX = "Loading XSS")); } + @Test // Issue 53699 + public void testLargeMultibytePayload() + { + for (int i = 0; i < 10; i++) + { + // Arrange + int characterCount = 5_000; + // We had problems fetching the JSON content server-side due to Tomcat's InputBuffer not dealing with + // characters that span multiple "pages" of the byte buffer. Test with different numbers of single-byte + // UTF-8 characters before the 4-byte characters to ensure we get different boundary conditions + String tooManyMultibyteCharacters = "a".repeat(i) + "\uD83D\uDC7E".repeat(characterCount); + + TestDataGenerator dataGenerator = new TestDataGenerator("lists", LIST_NAME, API_FOLDER_PATH) + .addCustomRow(Map.of("FirstName", tooManyMultibyteCharacters, "LastName", "Chaplin", "Age", 42)); + + // Act + // Prior to fix for Issue 53699 this would throw a BufferUnderflowException when processing the JSON payload + Exception exception = assertThrows(Exception.class, dataGenerator::insertRows); + + // Assert + // Prior to fix length validation incorrectly checked for string length rather than character code point length + String expectedPrefix = "Value is too long for column 'FirstName', a maximum length of 4000 is allowed."; + String expectedSuffix = String.format("was %d characters long.", characterCount + i); + + assertThat(exception.getMessage(), startsWith(expectedPrefix)); + assertThat(exception.getMessage(), endsWith(expectedSuffix)); + } + } + @Override public BrowserType bestBrowser() { diff --git a/src/org/labkey/test/tests/SampleTypeLinkToStudyTest.java b/src/org/labkey/test/tests/SampleTypeLinkToStudyTest.java index c90256bcc3..f115cfb9b0 100644 --- a/src/org/labkey/test/tests/SampleTypeLinkToStudyTest.java +++ b/src/org/labkey/test/tests/SampleTypeLinkToStudyTest.java @@ -26,12 +26,10 @@ import org.labkey.test.params.FieldDefinition; import org.labkey.test.params.experiment.SampleTypeDefinition; import org.labkey.test.util.DataRegionTable; -import org.labkey.test.util.Ext4Helper; import org.labkey.test.util.PortalHelper; import org.labkey.test.util.SampleTypeHelper; import org.labkey.test.util.StudyHelper; import org.labkey.test.util.TestDataGenerator; -import org.openqa.selenium.WebElement; import java.io.File; import java.io.IOException; @@ -783,17 +781,7 @@ private void createNewVisits(String label, String startRange, String endRange) private void createDatasetCategory(String projectName, String name) { goToProjectHome(projectName); - goToManageViews(); - Locator.linkWithText("Manage Categories").findElement(getDriver()).click(); - _extHelper.waitForExtDialog("Manage Categories"); - Window categoryWindow = new Window.WindowFinder(getDriver()).withTitle("Manage Categories").waitFor(); - categoryWindow.clickButton("New Category", 0); - WebElement newCategoryField = Locator.input("label").withAttributeContaining("id", "textfield").notHidden().waitForElement(getDriver(), WAIT_FOR_JAVASCRIPT); - setFormElementJS(newCategoryField, name); - fireEvent(newCategoryField, SeleniumEvent.blur); - waitForElement(Ext4Helper.Locators.window("Manage Categories").append("//div").withText(name)); - clickButton("Done", 0); - _extHelper.waitForExtDialogToDisappear("Manage Categories"); + goToManageViews().createCategory(name); } private String getCategory(String projectName, String datasetName) diff --git a/src/org/labkey/test/tests/announcements/ModeratorReviewTest.java b/src/org/labkey/test/tests/announcements/ModeratorReviewTest.java index d79161b04e..70fdb06842 100644 --- a/src/org/labkey/test/tests/announcements/ModeratorReviewTest.java +++ b/src/org/labkey/test/tests/announcements/ModeratorReviewTest.java @@ -163,7 +163,7 @@ private String addResponse(String user, String title, boolean expectAutoApproved // commonmark-java auto-linking turns all email addresses into mailto: links String formattedResponse = replaceEmailAddressesWithMailToLinks(response); - boolean responseAdded = isTextPresent(formattedResponse); + boolean responseAdded = isHtmlPresent(formattedResponse); if (expectAutoApproved && !responseAdded) { checker().fatal().error(String.format("Expected response '%s' was not present on the thread page.", formattedResponse)); diff --git a/src/org/labkey/test/util/EscapeUtil.java b/src/org/labkey/test/util/EscapeUtil.java index 71093031f0..ad88def79c 100644 --- a/src/org/labkey/test/util/EscapeUtil.java +++ b/src/org/labkey/test/util/EscapeUtil.java @@ -199,4 +199,5 @@ public static String escapeForNameExpression(String name) { return nameExpressionNeedsEscaping.matcher(name).replaceAll("\\\\$1"); } + } diff --git a/src/org/labkey/test/util/LabKeyExpectedConditions.java b/src/org/labkey/test/util/LabKeyExpectedConditions.java index fc87aedf33..89a0960d32 100644 --- a/src/org/labkey/test/util/LabKeyExpectedConditions.java +++ b/src/org/labkey/test/util/LabKeyExpectedConditions.java @@ -21,8 +21,8 @@ import org.openqa.selenium.Dimension; import org.openqa.selenium.JavascriptExecutor; import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.NoSuchWindowException; import org.openqa.selenium.Point; -import org.openqa.selenium.SearchContext; import org.openqa.selenium.StaleElementReferenceException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; @@ -33,6 +33,7 @@ import java.util.Collections; import java.util.List; +import java.util.Objects; import java.util.function.Function; public class LabKeyExpectedConditions @@ -42,32 +43,6 @@ private LabKeyExpectedConditions() // Utility class } - /** - * An expectation for checking child WebElement as a part of parent element to be present - * - * @param context SearchContext to find element within - * @param childLocator used to find child element. For example Locator.xpath("./tr/td") - * @return subelement - */ - public static ExpectedCondition presenceOfNestedElementLocatedBy(final SearchContext context, final By childLocator) - { - return new ExpectedCondition<>() - { - - @Override - public WebElement apply(WebDriver webDriver) - { - return context.findElement(childLocator); - } - - @Override - public String toString() - { - return String.format("visibility of element located by %s", childLocator); - } - }; - } - /** * An expectation for checking that an element has stopped moving * @@ -101,7 +76,7 @@ public String toString() /** * Another expectation for checking that an element has stopped moving * - * @param el the element who's position changes + * @param el the element whose position changes * @return the element when animation is complete */ public static ExpectedCondition animationIsDone(final WebElement el) { @@ -165,7 +140,7 @@ public WebElement apply(WebDriver driver) return null; } - if (el.isEnabled() && !el.getAttribute("class").contains("disabled")) + if (el.isEnabled() && !Objects.requireNonNullElse(el.getAttribute("class"), "").contains("disabled")) return el; else return null; @@ -202,43 +177,7 @@ public Boolean apply(WebDriver ignored) @Override public String toString() { - return staleCheck.toString() + " after clicking"; - } - }; - } - - /** - * Wraps {@link ExpectedConditions#visibilityOfAllElements(WebElement...)} - * This expectations accounts for the behavior of LabKey WebElement wrappers, which will throw if you attempt to - * inspect them before the element has appeared. - * - * @param elements list of WebElements - * @return the list of WebElements once they are located - * @see org.labkey.test.selenium.LazyWebElement - */ - public static ExpectedCondition> visibilityOfAllElements(WebElement... elements) - { - return new ExpectedCondition<>() - { - final ExpectedCondition> wrapped = ExpectedConditions.visibilityOfAllElements(elements); - - @Override - public List apply(WebDriver driver) - { - try - { - return wrapped.apply(driver); - } - catch (StaleElementReferenceException | NoSuchElementException ignore) - { - return null; - } - } - - @Override - public String toString() - { - return wrapped.toString(); + return staleCheck + " after clicking"; } }; } @@ -246,10 +185,10 @@ public String toString() /** * Wraps {@link ExpectedConditions#stalenessOf(WebElement)} * Firefox occasionally throws "NoSuchElementException: Web element reference not seen before" - * for short lived elements. + * for short-lived elements. * * @param element WebElement that should go stale. - * @return false if the element is still attached to the DOM, true otherwise. + * @return ExpectedCondition that returns false if the element is still attached to the DOM, true otherwise. */ public static ExpectedCondition stalenessOf(WebElement element) { @@ -292,6 +231,31 @@ public static ExpectedCondition visibilityOf(WebElement element, boolean visi : ExpectedConditions.invisibilityOf(element); } + /** + * An expectation for checking whether a window or tab with the give name is present. + * If the window/tab is available it switches the given driver to the specified window/tab. + * @param windowName The name of the window + * @return An expected condition that returns the driver with focus switched to the specified window or tab. + * @see WebDriver.TargetLocator#window(String) + */ + public static ExpectedCondition windowIsPresent(final String windowName) { + return new ExpectedCondition<>() { + @Override + public WebDriver apply(WebDriver driver) { + try { + return driver.switchTo().window(windowName); + } catch (NoSuchWindowException e) { + return null; + } + } + + @Override + public String toString() { + return "window named " + windowName; + } + }; + } + /** * Wraps a 'Wait' to terminate after function return a non-null value. * Normally, 'Wait' expects a non-null, non-false return value diff --git a/src/org/labkey/test/util/TestDataGenerator.java b/src/org/labkey/test/util/TestDataGenerator.java index c5c5074976..27f0f41cfb 100644 --- a/src/org/labkey/test/util/TestDataGenerator.java +++ b/src/org/labkey/test/util/TestDataGenerator.java @@ -59,6 +59,7 @@ import java.util.Objects; import java.util.Set; import java.util.concurrent.ThreadLocalRandom; +import java.util.function.Function; import java.util.function.Supplier; import java.util.regex.Pattern; @@ -417,7 +418,7 @@ private Map generateRow() { if (!_dataSuppliers.containsKey(columnName)) { - _dataSuppliers.put(columnName, getDefaultDataSupplier(_columns.get(columnName).getRangeURI())); + _dataSuppliers.put(columnName, getDefaultDataSupplier(_columns.get(columnName))); } if (_autoGeneratedFields.contains(columnName)) @@ -431,31 +432,54 @@ private Map generateRow() return newRow; } - private Supplier getDefaultDataSupplier(String columnType) + private Supplier getDefaultDataSupplier(PropertyDescriptor propertyDescriptor) { - switch (columnType.substring(columnType.indexOf('#') + 1).toLowerCase()) + Map allProperties = Objects.requireNonNullElse(propertyDescriptor.getAllProperties(), Collections.emptyMap()); + Function getType = s -> s == null ? "" : s.substring(s.indexOf('#') + 1).toLowerCase().trim(); + String conceptUriName = getType.apply((String) allProperties.get("conceptURI")); + String rangeUriName = getType.apply(propertyDescriptor.getRangeURI()); + + switch (conceptUriName) { - case "string": - return ()-> randomString(20, _excludedChars, _alphaNumericStr ? ALPHANUMERIC_STRING : CHARSET_STRING); - case "int": - return ()-> randomInt(0, 20); - case "float": - return ()-> randomFloat(0, 20); - case "double": - return ()-> randomDouble(0, 20); - case "boolean": - return this::randomBoolean; - case "date": - case "datetime": - return ()-> randomDateString(DateUtils.addWeeks(new Date(), -39), new Date()); - case "time": - return ()-> - randomInt(0, 23) + ":" + // hour - StringUtils.leftPad(String.valueOf(randomInt(0, 59)), 2, "0") + ":" + // minute - StringUtils.leftPad(String.valueOf(randomInt(0, 59)), 2, "0") + "." + // second - StringUtils.leftPad(String.valueOf(randomInt(0, 999)), 3, "0"); // millisecond + case "textchoice": + List textChoices = ((FieldDefinition) propertyDescriptor) + .getValidators().stream() + .map(v -> { + if (v instanceof FieldDefinition.TextChoiceValidator tcv && !tcv.getValues().isEmpty()) + return tcv.getValues(); + else + return null; + }) + .filter(Objects::nonNull) + .findFirst() + .orElseThrow(() -> new IllegalStateException("No choices defined for textChoice field : " + propertyDescriptor.getName())); + + return () -> randomChoice(textChoices); default: - throw new IllegalArgumentException("ColumnType " + columnType + " isn't implemented yet"); + switch (rangeUriName) + { + case "string": + return () -> randomString(20, _excludedChars, _alphaNumericStr ? ALPHANUMERIC_STRING : CHARSET_STRING); + case "int": + return () -> randomInt(0, 20); + case "float": + return () -> randomFloat(0, 20); + case "double": + return () -> randomDouble(0, 20); + case "boolean": + return this::randomBoolean; + case "date": + case "datetime": + return () -> randomDateString(DateUtils.addWeeks(new Date(), -39), new Date()); + case "time": + return () -> + randomInt(0, 23) + ":" + // hour + StringUtils.leftPad(String.valueOf(randomInt(0, 59)), 2, "0") + ":" + // minute + StringUtils.leftPad(String.valueOf(randomInt(0, 59)), 2, "0") + "." + // second + StringUtils.leftPad(String.valueOf(randomInt(0, 999)), 3, "0"); // millisecond + default: + throw new IllegalArgumentException("ColumnType " + conceptUriName + " isn't implemented yet"); + } } } @@ -655,7 +679,7 @@ private static boolean isDomainAndFieldNameInvalid(DomainUtils.DomainKind domain } } - public static String randomChoice(List choices) + public static T randomChoice(List choices) { return choices.get(randomInt(0, choices.size() - 1)); } diff --git a/src/org/labkey/test/util/TextSearcher.java b/src/org/labkey/test/util/TextSearcher.java index 3ed4a79f06..a36368ba1b 100644 --- a/src/org/labkey/test/util/TextSearcher.java +++ b/src/org/labkey/test/util/TextSearcher.java @@ -15,12 +15,13 @@ */ package org.labkey.test.util; -import org.labkey.test.BaseWebDriverTest; +import org.apache.commons.lang3.mutable.MutableBoolean; import org.labkey.test.TestFileUtils; import org.labkey.test.WebDriverWrapper; import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.function.Function; @@ -98,6 +99,11 @@ public final void searchForTexts(TextHandler textHandler, List texts) } } + public List getMissingTexts(String... texts) + { + return getMissingTexts(Arrays.asList(texts)); + } + public List getMissingTexts(List texts) { final List missingTexts = new ArrayList<>(); @@ -113,6 +119,46 @@ public List getMissingTexts(List texts) return missingTexts; } + /** + * Checks whether any of the texts are present + * @return true if any of the specified text is found + */ + public boolean isAnyTextPresent(String... texts) + { + final MutableBoolean found = new MutableBoolean(false); + + TextSearcher.TextHandler handler = (htmlSource, text) -> { + if (htmlSource.contains(text)) + found.setTrue(); + + return !found.get(); // stop searching if any value is found + }; + searchForTexts(handler, Arrays.asList(texts)); + + return found.get(); + } + + /** + * Checks whether all the specified texts are present + * @return true if all the specified texts are present on the page + */ + public boolean areAllTextsPresent(String... texts) + { + final MutableBoolean present = new MutableBoolean(true); + + TextSearcher.TextHandler handler = (htmlSource, text) -> { + // Not found... stop enumerating and return false + if (htmlSource == null || !htmlSource.contains(text)) + present.setFalse(); + + return present.get(); + }; + + searchForTexts(handler, Arrays.asList(texts)); + + return present.get(); + } + /** * @return source text from the last search attempt */ @@ -129,7 +175,10 @@ public interface TextHandler public static abstract class TextTransformers { - public static final Function ENCODE_HTML = BaseWebDriverTest::encodeText; + public static final Function ENCODE_HTML = t -> t + .replace("&", "&") + .replace("<", "<") + .replace(">", ">"); public static final Function IDENTITY = text -> text; //Inserts spaces between camel-cased words