diff --git a/src/org/labkey/test/components/core/ProjectMenu.java b/src/org/labkey/test/components/core/ProjectMenu.java index ff029ae5c1..ecff1d476b 100644 --- a/src/org/labkey/test/components/core/ProjectMenu.java +++ b/src/org/labkey/test/components/core/ProjectMenu.java @@ -15,6 +15,7 @@ */ package org.labkey.test.components.core; +import org.apache.commons.lang3.StringUtils; import org.labkey.test.Locator; import org.labkey.test.WebDriverWrapper; import org.labkey.test.components.Component; @@ -28,6 +29,7 @@ import java.util.List; +import static org.labkey.test.Locators.loadingSpinner; import static org.labkey.test.WebDriverWrapper.WAIT_FOR_JAVASCRIPT; /** @@ -68,20 +70,27 @@ public String getCurrentProject() private boolean isOpen() { - return elementCache().menuContainer.getAttribute("class").contains("open"); + return StringUtils.trimToEmpty(elementCache().menuContainer.getAttribute("class")).contains("open") && + !loadingSpinner.existsIn(elementCache().menuContainer); } public ProjectMenu open() { if (!isOpen()) { - getWrapper().executeScript("window.scrollTo(0,0);"); - if (getWrapper().isElementPresent(Locator.css("li.dropdown.open > .lk-custom-dropdown-menu"))) - getWrapper().mouseOver(elementCache().menuToggle); // Just need to hover if another menu is already open - else - elementCache().menuToggle.click(); - WebDriverWrapper.waitFor(this::isOpen, "Project menu didn't open", 2000); - getWrapper().waitForElement(Locator.tagWithClass("div", "folder-nav")); + Runnable openMenu = () -> { + getWrapper().executeScript("window.scrollTo(0,0);"); + if (getWrapper().isElementPresent(Locator.css("li.dropdown.open > .lk-custom-dropdown-menu"))) + getWrapper().mouseOver(elementCache().menuToggle); // Just need to hover if another menu is already open + else + elementCache().menuToggle.click(); + WebDriverWrapper.waitFor(this::isOpen, "Project menu didn't open", 2000); + }; + openMenu.run(); + if (!Locator.tagWithClass("div", "folder-nav").existsIn(this)) + { + openMenu.run(); // retry + } } return this; } @@ -222,7 +231,7 @@ protected ElementCache newElementCache() return new ElementCache(); } - protected class ElementCache extends Component.ElementCache + protected class ElementCache extends Component.ElementCache { final WebElement menuContainer = Locators.menuProjectNav.refindWhenNeeded(getComponentElement()); final WebElement menuToggle = Locator.tagWithAttribute("a", "data-toggle", "dropdown").refindWhenNeeded(menuContainer); diff --git a/src/org/labkey/test/params/FieldDefinition.java b/src/org/labkey/test/params/FieldDefinition.java index 568e903161..15f92c02e9 100644 --- a/src/org/labkey/test/params/FieldDefinition.java +++ b/src/org/labkey/test/params/FieldDefinition.java @@ -590,7 +590,7 @@ public boolean isMeasureByDefault() ColumnType OntologyLookup = new ColumnTypeImpl("Ontology Lookup", "string", "http://www.labkey.org/types#conceptCode", null); ColumnType VisitId = new ColumnTypeImpl("Visit ID", "double", "http://cpas.labkey.com/Study#VisitId", null); ColumnType VisitDate = new ColumnTypeImpl("Visit Date", "dateTime", "http://cpas.labkey.com/Study#VisitId", null); - ColumnType VisitLabel = new ColumnTypeImpl("Visit Label", "string"); + ColumnType VisitLabel = new ColumnTypeImpl("Visit Label", "string", "http://cpas.labkey.com/Study#VisitId", null); ColumnType Sample = new ColumnTypeImpl("Sample", "int", "http://www.labkey.org/exp/xml#sample", new IntLookup( "exp", "Materials")); ColumnType Barcode = new ColumnTypeImpl("Unique ID", "string", "http://www.labkey.org/types#storageUniqueId", null); ColumnType TextChoice = new ColumnTypeImpl("Text Choice", "string", "http://www.labkey.org/types#textChoice", null); diff --git a/src/org/labkey/test/tests/JUnitTest.java b/src/org/labkey/test/tests/JUnitTest.java index 5cfa9cdc0e..2b785e1fee 100644 --- a/src/org/labkey/test/tests/JUnitTest.java +++ b/src/org/labkey/test/tests/JUnitTest.java @@ -36,6 +36,7 @@ import org.json.JSONException; import org.json.JSONObject; import org.junit.Assert; +import org.junit.Assume; import org.junit.experimental.categories.Category; import org.labkey.remoteapi.CommandException; import org.labkey.remoteapi.CommandResponse; @@ -69,6 +70,7 @@ import java.util.List; import java.util.Map; import java.util.Set; +import java.util.concurrent.atomic.AtomicInteger; import java.util.function.Predicate; @Category({BVT.class, UnitTests.class}) @@ -318,6 +320,7 @@ else if (responseBody.contains("Upgrade Status") || boolean addedHeader = false; for (String key : json.keySet()) { + AtomicInteger ioeCounter = new AtomicInteger(0); TestSuite testsuite = new TestSuite(key); JSONArray testClassArray = json.getJSONArray(key); // Individual tests include both the class name and the requested timeout @@ -331,7 +334,7 @@ else if (responseBody.contains("Upgrade Status") || // Timeout is represented in seconds int timeout = testClass.getInt("timeout"); if (accept.test(testClass.toMap())) - testsuite.addTest(new RemoteTest(className, timeout)); + testsuite.addTest(new RemoteTest(className, timeout, ioeCounter)); } } @@ -370,23 +373,27 @@ else if (responseBody.contains("Upgrade Status") || @SuppressWarnings("JUnitMalformedDeclaration") public static class RemoteTest extends TestCase { - String _remoteClass; + private final String _remoteClass; /** Timeout in seconds to wait for the whole testcase to finish on the server */ private final int _timeout; + // Skip tests after a certain number of IOExceptions + private final AtomicInteger _ioeCounter; /** Stash and reuse so that we can keep using the same session instead of re-authenticating with every request */ private static final Connection connection = WebTestHelper.getRemoteApiConnection(); - public RemoteTest(String remoteClass, int timeout) + public RemoteTest(String remoteClass, int timeout, AtomicInteger ioeCounter) { super(remoteClass); _remoteClass = remoteClass; _timeout = timeout; + _ioeCounter = ioeCounter; } @Override protected void runTest() { + Assume.assumeTrue("Too many consecutive test timeouts", _ioeCounter.get() < 5); long startTime = System.currentTimeMillis(); try { @@ -405,9 +412,11 @@ else if (resultJson.get("wasSuccessful") != Boolean.TRUE) WebTestHelper.logToServer(getLogTestString("successful", startTime) + ", " + dump(resultJson, false), connection); LOG.info(getLogTestString("successful", startTime)); LOG.info(dump(resultJson, true)); + _ioeCounter.set(0); } catch (SocketTimeoutException ste) { + _ioeCounter.incrementAndGet(); String timed_out = getLogTestString("timed out", startTime); LOG.error(timed_out); ArtifactCollector.dumpThreads(); @@ -415,6 +424,7 @@ else if (resultJson.get("wasSuccessful") != Boolean.TRUE) } catch (IOException ioe) { + _ioeCounter.incrementAndGet(); String message = getLogTestString("failed: " + ioe.getMessage(), startTime); LOG.error(message); throw new RuntimeException(message, ioe); diff --git a/src/org/labkey/test/util/FileBrowserHelper.java b/src/org/labkey/test/util/FileBrowserHelper.java index 72a403853c..8562caa4a7 100644 --- a/src/org/labkey/test/util/FileBrowserHelper.java +++ b/src/org/labkey/test/util/FileBrowserHelper.java @@ -248,7 +248,7 @@ private void checkFileBrowserFileCheckbox(String fileName, boolean checkTheBox) final Checkbox checkbox; try { - checkbox = Ext4Checkbox().locatedBy(Locators.gridRowCheckbox(fileName)).find(getDriver()); + checkbox = Ext4Checkbox().locatedBy(Locators.gridRowCheckbox(fileName)).timeout(1_000).find(getDriver()); } catch (NoSuchElementException nse) { diff --git a/src/org/labkey/test/util/TestLogger.java b/src/org/labkey/test/util/TestLogger.java index 827377320b..f3b5415dc3 100644 --- a/src/org/labkey/test/util/TestLogger.java +++ b/src/org/labkey/test/util/TestLogger.java @@ -138,12 +138,12 @@ public static void log(String str) } /** - * Format an elapsed time to be suitable for log messages. - * Over one minute: - * " <1m 25s>" - * Over one minute: - * " <8.059s>" - * Less than on second: + * Format an elapsed time to be suitable for log messages.
+ * Over one minute:
+ * " <1m 25s>"
+ * Over one second:
+ * " <8.059s>"
+ * Less than one second:
* " <125ms>" * @param milliseconds Elapsed time in milliseconds * @return Formatted time