diff --git a/src/org/labkey/test/ExtraSiteWrapper.java b/src/org/labkey/test/ExtraSiteWrapper.java index 39e2c7da18..6fc4840e3d 100644 --- a/src/org/labkey/test/ExtraSiteWrapper.java +++ b/src/org/labkey/test/ExtraSiteWrapper.java @@ -54,6 +54,12 @@ public WebDriver getWrappedDriver() return extraDriver.getLeft(); } + @Override + protected boolean allowTimeZoneShifting() + { + return false; + } + @Override public void pauseJsErrorChecker(){} @Override diff --git a/src/org/labkey/test/TestProperties.java b/src/org/labkey/test/TestProperties.java index 456ab1da63..c05ca8529d 100644 --- a/src/org/labkey/test/TestProperties.java +++ b/src/org/labkey/test/TestProperties.java @@ -72,7 +72,7 @@ public abstract class TestProperties } } - private static ZoneId browserTimeZone = null; + private static ZoneId browserZoneId = null; public static void load() { @@ -166,9 +166,9 @@ public static boolean isDumpBrowserConsole() return getBooleanProperty("webtest.dump.browser.console", false); } - public static ZoneId getBrowserTimeZone() + public static ZoneId getBrowserZoneId() { - if (browserTimeZone == null) + if (browserZoneId == null) { String tz = StringUtils.trimToNull(System.getProperty("webtest.browser.tz")); if (tz != null) @@ -176,14 +176,14 @@ public static ZoneId getBrowserTimeZone() String[] split = tz.split("[,\\s]+"); tz = split[LocalDateTime.now().getDayOfMonth() % split.length]; // Verify that time zone is valid - browserTimeZone = ZoneId.of(tz); + browserZoneId = ZoneId.of(tz); } else { - browserTimeZone = ZoneId.systemDefault(); + browserZoneId = ZoneId.systemDefault(); } } - return browserTimeZone; + return browserZoneId; } public static double getTimeoutMultiplier() diff --git a/src/org/labkey/test/WebDriverWrapper.java b/src/org/labkey/test/WebDriverWrapper.java index 4005c31201..e21cb5675c 100644 --- a/src/org/labkey/test/WebDriverWrapper.java +++ b/src/org/labkey/test/WebDriverWrapper.java @@ -88,7 +88,6 @@ import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeDriverService; import org.openqa.selenium.chrome.ChromeOptions; -import org.openqa.selenium.firefox.FirefoxBinary; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxDriverLogLevel; import org.openqa.selenium.firefox.FirefoxDriverService; @@ -120,6 +119,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.time.Duration; +import java.time.Instant; import java.time.ZoneId; import java.util.ArrayList; import java.util.Arrays; @@ -206,64 +206,93 @@ protected Pair createNewWebDriver(BrowserType browserT return createNewWebDriver(new ImmutablePair<>(null, null), browserType, downloadDir); } + /** + * Number of minutes to add to convert browser Date to UTC.
+ * Note: The sign of this is the opposite of Java's concept of time zone offset + * @return Actual browser time zone offset in minutes + */ + public int getWebDriverTimeZoneOffset() + { + return Math.toIntExact(executeScript("return new Date().getTimezoneOffset();", Long.class)); + } + + /** + * Test process might be running in a different time zone from the browser. This allows shifting test date to + * browser dates. + *
{@code
+     * TestDateUtils.diffFromTodaysDate(Calendar.MINUTE, getRelativeTimeZoneOffset())
+     * }
+ * @return The number of minutes to add to a system date to convert to the equivalent browser date + */ + public int getRelativeTimeZoneOffset() + { + return ZoneId.systemDefault().getRules().getOffset(Instant.now()).getTotalSeconds() / 60 - getWebDriverTimeZoneOffset(); + } + + /** + * Allow tests to disable time zone shifting. Warning: {@link TestProperties#getBrowserZoneId()} might not match actual + * browser time zone if this is false. + */ + protected boolean allowTimeZoneShifting() + { + return true; + } + protected Pair createNewWebDriver(@NotNull Pair oldDriverAndService, BrowserType browserType, File downloadDir) { WebDriver oldWebDriver = oldDriverAndService.getLeft(); - WebDriver newWebDriver = null; + WebDriver newWebDriver; DriverService oldDriverService = oldDriverAndService.getRight(); - DriverService newDriverService = null; + DriverService newDriverService; + Map browserEnv = new HashMap<>(); - ZoneId browserTimeZone = TestProperties.getBrowserTimeZone(); - if (browserTimeZone != ZoneId.systemDefault()) + ZoneId targetBrowserTimeZone = allowTimeZoneShifting() ? TestProperties.getBrowserZoneId() : ZoneId.systemDefault(); + int targetOffsetInSeconds = targetBrowserTimeZone.getRules().getOffset(Instant.now()).getTotalSeconds(); + + if (targetBrowserTimeZone != ZoneId.systemDefault()) { - TestLogger.info("Starting browser with TZ = " + browserTimeZone); - browserEnv.put("TZ", browserTimeZone.toString()); + browserEnv.put("TZ", targetBrowserTimeZone.toString()); } - else + + if (oldWebDriver != null && + (!browserType.matchesDriver(oldWebDriver) || + new WebDriverWrapperImpl(oldWebDriver).getWebDriverTimeZoneOffset() * -60 != targetOffsetInSeconds)) { - TestLogger.info("Starting browser with TZ = " + browserTimeZone + " (system default)"); + TestLogger.info("Quitting existing driver and service"); + oldWebDriver.quit(); + oldWebDriver = null; + if (oldDriverService != null && oldDriverService.isRunning()) + oldDriverService.stop(); } - switch (browserType) + final Pair result; + + if (oldWebDriver == null) { - case REMOTE: //experimental - { - try - { - newWebDriver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), new ChromeOptions()); - } - catch (MalformedURLException e) - { - throw new RuntimeException(e); - } - break; - } - case IE: //experimental + log("Starting new browser: " + browserType); + + switch (browserType) { - if (oldWebDriver != null && !(oldWebDriver instanceof InternetExplorerDriver)) + case REMOTE: //experimental { - oldWebDriver.quit(); - oldWebDriver = null; - if (oldDriverService != null && oldDriverService.isRunning()) - oldDriverService.stop(); + try + { + newDriverService = null; // Remote service runs independently of tests + newWebDriver = new RemoteWebDriver(new URL("http://localhost:4444/wd/hub"), new ChromeOptions()); + } + catch (MalformedURLException e) + { + throw new RuntimeException(e); + } + break; } - if (oldWebDriver == null) + case IE: //experimental { newDriverService = new InternetExplorerDriverService.Builder().withEnvironment(browserEnv).build(); newWebDriver = new InternetExplorerDriver((InternetExplorerDriverService) newDriverService); + break; } - break; - } - case CHROME: - { - if (oldWebDriver != null && !(oldWebDriver instanceof ChromeDriver)) - { - oldWebDriver.quit(); - oldWebDriver = null; - if (oldDriverService != null && oldDriverService.isRunning()) - oldDriverService.stop(); - } - if (oldWebDriver == null) + case CHROME: { configureChromeDriverLogging(downloadDir); ChromeOptions options = new ChromeOptions(); @@ -292,19 +321,9 @@ protected Pair createNewWebDriver(@NotNull Pair createNewWebDriver(@NotNull Pair 0) + if (!browserPath.isEmpty()) { - binary = new FirefoxBinary(new File(browserPath)); - } - else - { - binary = new FirefoxBinary(); + capabilities.setBinary(browserPath); } if (TestProperties.isRunWebDriverHeadless()) { TestLogger.warn("Launching Firefox in headless mode. This is still experimental"); - binary.addCommandLineOptions("--headless"); + capabilities.addArguments("--headless"); } - capabilities.setBinary(binary); + // Firefox 128: UnhandledAlertException doesn't include alert text. Need to leave alerts to get text manually. capabilities.setUnhandledPromptBehaviour(UnexpectedAlertBehaviour.IGNORE); FirefoxOptions firefoxOptions = new FirefoxOptions(capabilities); @@ -413,15 +427,13 @@ protected Pair createNewWebDriver(@NotNull Pair windowSize = TestProperties.getWindowSize(); if (windowSize.isPresent()) @@ -430,16 +442,21 @@ protected Pair createNewWebDriver(@NotNull Pair(newWebDriver, newDriverService); + result = Pair.of(newWebDriver, newDriverService); } else { - return oldDriverAndService; + log("Reusing existing browser"); + result = oldDriverAndService; } + + Capabilities caps = ((HasCapabilities) result.getLeft()).getCapabilities(); + String browserName = caps.getBrowserName(); + String browserVersion = caps.getBrowserVersion(); + log("Browser: " + browserName + " " + browserVersion); + log("Started browser with TZ = " + targetBrowserTimeZone + (targetBrowserTimeZone != ZoneId.systemDefault() ? "" : " (system default)")); + + return result; } private void configureChromeDriverLogging(File downloadDir) @@ -483,7 +500,7 @@ private void configureGeckoDriverLogging(File downloadDir) public boolean isFirefox() { - return getDriver().getClass().isAssignableFrom(FirefoxDriver.class); + return BrowserType.FIREFOX.matchesDriver(getDriver()); } public Object executeScript(@Language("JavaScript") String script, Object... arguments) @@ -553,11 +570,23 @@ private void setJsErrorLogging(boolean b) public enum BrowserType { - REMOTE, - FIREFOX, - IE, - CHROME, - HTML + REMOTE(null), + FIREFOX(FirefoxDriver.class), + IE(InternetExplorerDriver.class), + CHROME(ChromeDriver.class), + ; + + private final Class _driverClass; + + BrowserType(Class driverClass) + { + _driverClass = driverClass; + } + + boolean matchesDriver(@NotNull WebDriver oldDriver) + { + return _driverClass != null && oldDriver.getClass().isAssignableFrom(_driverClass); + } } public static void sleep(long ms) diff --git a/src/org/labkey/test/tests/DataViewsTest.java b/src/org/labkey/test/tests/DataViewsTest.java index 9da83a9fb9..0209cc78cc 100644 --- a/src/org/labkey/test/tests/DataViewsTest.java +++ b/src/org/labkey/test/tests/DataViewsTest.java @@ -58,6 +58,15 @@ public class DataViewsTest extends ParticipantListTest private final PortalHelper _portalHelper = new PortalHelper(this); + /** + * 52268: Data Views Webpart behaves badly when the server is in a different time zone + */ + @Override + protected boolean allowTimeZoneShifting() + { + return false; + } + @Override @LogMethod protected void doCreateSteps() {