diff --git a/pom.xml b/pom.xml index 0e8c8e7..9317c21 100644 --- a/pom.xml +++ b/pom.xml @@ -97,6 +97,7 @@ 2.2 2.12.1 0.4.2 + 2.0.2 1.3.0 2.7 0.8.5 @@ -219,6 +220,18 @@ 4.1.2 compile + + org.powermock + powermock-module-junit4 + ${powermock.version} + test + + + org.powermock + powermock-api-mockito2 + ${powermock.version} + test + diff --git a/src/main/java/uk/co/evoco/webdriver/configuration/WebDriverConfig.java b/src/main/java/uk/co/evoco/webdriver/configuration/WebDriverConfig.java index fa6f01e..0d52e3d 100644 --- a/src/main/java/uk/co/evoco/webdriver/configuration/WebDriverConfig.java +++ b/src/main/java/uk/co/evoco/webdriver/configuration/WebDriverConfig.java @@ -4,6 +4,7 @@ import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; +import org.openqa.selenium.json.Json; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -25,6 +26,7 @@ public class WebDriverConfig { private GridConfig gridConfig; private RunType runType; private Map browserPreferences; + private ObjectNode chromeLoggingPreferences; private TolerantActionExceptions tolerantActionExceptions; private MetricsConfig metricsConfig; private boolean takeScreenshotOnError; @@ -162,6 +164,19 @@ public ObjectNode getBrowserPreferences(BrowserType browserType) { .orElse(JsonNodeFactory.instance.objectNode()); } + /** + * + * @return the ChromeLoggingPreferences configuration + */ + public Optional getChromeLoggingPreferences() { + return Optional.ofNullable(chromeLoggingPreferences) + .map(configObject -> configObject.retain("logLevel")) + .filter(s -> !s.isEmpty()) + .map(logLevelNode -> logLevelNode.get("logLevel").asText()) + .stream() + .findFirst(); + } + /** * * @param browserPreferences the configuration properties for the various browsers supported by the webdriver @@ -171,6 +186,15 @@ public void setBrowserPreferences(Map browserPreferences) { this.browserPreferences = browserPreferences; } + /** + * + * @param chromeLoggingPreferences the configuration logging level for the various browsers supported by the webdriver + */ + @JsonProperty("chromeLoggingPreferences") + public void setChromeLoggingPreferences(ObjectNode chromeLoggingPreferences) { + this.chromeLoggingPreferences = chromeLoggingPreferences; + } + /** * * @return the run type diff --git a/src/main/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriver.java b/src/main/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriver.java index 3e6cb2f..acefbb8 100644 --- a/src/main/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriver.java +++ b/src/main/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriver.java @@ -5,26 +5,40 @@ import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeDriver; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.logging.LogType; +import org.openqa.selenium.logging.LoggingPreferences; +import org.openqa.selenium.remote.CapabilityType; +import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.RemoteWebDriver; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import uk.co.evoco.tests.BaseAbstractTest; import uk.co.evoco.webdriver.configuration.BrowserType; import uk.co.evoco.webdriver.configuration.TestConfigHelper; -import java.io.File; import java.io.IOException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; +import java.util.logging.Level; public class ConfiguredChromeDriver implements ConfiguredDriver { + private static final Logger logger = LoggerFactory.getLogger(ConfiguredChromeDriver.class); + /** * * * @return WebDriver representing RemoteWebDriver grid */ public WebDriver getRemoteDriver() throws IOException { + ChromeOptions chromeOptions = this.getOptions(); + LoggingPreferences loggingOptions = this.getLoggerPrefs(); + DesiredCapabilities capabilities = DesiredCapabilities.chrome(); + capabilities.setCapability("goog:loggingPrefs", loggingOptions); + chromeOptions.merge(capabilities); return new RemoteWebDriver( - TestConfigHelper.get().getGridConfig().getGridUrl(), this.getOptions()); + TestConfigHelper.get().getGridConfig().getGridUrl(), chromeOptions); } /** @@ -33,10 +47,16 @@ public WebDriver getRemoteDriver() throws IOException { * @throws IOException if log directory doesn't exist */ public WebDriver getLocalDriver() throws IOException { + ChromeOptions chromeOptions = this.getOptions(); + LoggingPreferences loggingOptions = this.getLoggerPrefs(); + DesiredCapabilities capabilities = DesiredCapabilities.chrome(); + capabilities.setCapability("goog:loggingPrefs", loggingOptions); + chromeOptions.merge(capabilities); + createLogDirectory(); System.setProperty("webdriver.chrome.logfile", "logs/chrome-driver.log"); WebDriverManager.chromedriver().setup(); - return new ChromeDriver(this.getOptions()); + return new ChromeDriver(chromeOptions); } /** @@ -68,8 +88,42 @@ public ChromeOptions getOptions() throws IOException { } } } + + if(!getLoggerPrefs().getLevel(LogType.PERFORMANCE).getName().equals("OFF")) { + Map perfLogPrefs = new HashMap<>(); + perfLogPrefs.put("traceCategories", "browser,devtools.timeline,devtools"); + chromeOptions.setExperimentalOption("perfLoggingPrefs", perfLogPrefs); + } + chromeOptions.setExperimentalOption("prefs", chromePrefs); chromeOptions.setHeadless(TestConfigHelper.get().isHeadless()); return chromeOptions; } + + public LoggingPreferences getLoggerPrefs() { + LoggingPreferences loggingPreferences = new LoggingPreferences(); + TestConfigHelper.get() + .getChromeLoggingPreferences() + .ifPresent(logLevel -> { + try { + System.setProperty("webdriver.chrome.verboseLogging", "true"); + loggingPreferences.enable(LogType.PERFORMANCE, parseLogLevel(logLevel)); + loggingPreferences.enable(LogType.BROWSER, parseLogLevel(logLevel)); + } catch (Exception e) { + e.printStackTrace(); + } + }); + + return loggingPreferences; + } + + private Level parseLogLevel(String logLevel) { + try { + return Level.parse(logLevel); + } + catch (IllegalArgumentException exception) { + logger.warn("Incorrect Level provided so performance logging set to OFF"); + return Level.OFF; + } + } } diff --git a/src/test/java/uk/co/evoco/webdriver/configuration/WebDriverConfigTests.java b/src/test/java/uk/co/evoco/webdriver/configuration/WebDriverConfigTests.java index 5a5fd48..82c8634 100644 --- a/src/test/java/uk/co/evoco/webdriver/configuration/WebDriverConfigTests.java +++ b/src/test/java/uk/co/evoco/webdriver/configuration/WebDriverConfigTests.java @@ -10,6 +10,7 @@ import java.io.IOException; import java.net.MalformedURLException; +import java.util.Optional; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -51,7 +52,7 @@ public void testGetBrowserPreferencesReturnsAnEmptyNodeIfSpecifiedBrowserTypeIsN public void testGetBrowserPreferencesReturnsTheCorrectBrowserOptions() throws JsonProcessingException { String preferenceKey = "browser.download.dir"; String preferenceValue = "docs/chrome/"; - String inputConfigJson = String.format("{ \"browserPreferences\": { \"chrome\": {\"%s\": \"%s\"}}}", preferenceKey, preferenceValue); + String inputConfigJson = String.format("{ \"browserPreferences\": { \"chrome\": {\"%s\": \"%s\"} }}", preferenceKey, preferenceValue); WebDriverConfig webDriverConfig = JsonUtils.fromString(inputConfigJson, WebDriverConfig.class); JsonNode actualPreferences = webDriverConfig.getBrowserPreferences(BrowserType.CHROME); @@ -75,6 +76,50 @@ public void testGetBrowserPropertiesReturnsTheCorrectBrowserOptionsIrrespectiveO assertThat(actualPreferences, is(expectedPreferences)); } + @Test + public void testGetLoggingPreferencesGetsTheRightOptions() throws IOException { + WebDriverConfig webDriverConfig = JsonUtils.fromFile( + ClassLoader.getSystemResourceAsStream("fixtures/sample-config-with-chrome-logging-preferences.json"), + WebDriverConfig.class); + + assertThat(webDriverConfig.getChromeLoggingPreferences(), is(Optional.of("ALL"))); + } + + @Test + public void testWrongLogKeyReturnsEmptyOptional() throws IOException { + String logKey = "wrongLogKey"; + String logValue = "ALL"; + String inputConfigJson = String.format("{ \"chromeLoggingPreferences\": {\"%s\": \"%s\"}}}", logKey, logValue); + + WebDriverConfig webDriverConfig = JsonUtils.fromString(inputConfigJson, WebDriverConfig.class); + + Optional actualPreferences = webDriverConfig.getChromeLoggingPreferences(); + assertThat(actualPreferences, is(Optional.empty())); + } + + @Test + public void testBlankLogLevelAndValueReturnsEmptyOptional() throws IOException { + String logKey = ""; + String logValue = ""; + String inputConfigJson = String.format("{ \"chromeLoggingPreferences\": {\"%s\": \"%s\"}}}", logKey, logValue); + + WebDriverConfig webDriverConfig = JsonUtils.fromString(inputConfigJson, WebDriverConfig.class); + + Optional actualPreferences = webDriverConfig.getChromeLoggingPreferences(); + assertThat(actualPreferences, is(Optional.empty())); + } + + @Test + public void testMultipleLogLevelsReturnsFirstElement() throws IOException { + + String inputConfigJson = "{ \"chromeLoggingPreferences\": {\"logLevel\": \"All\", \"logLevel\": \"FINE\"}}}"; + + WebDriverConfig webDriverConfig = JsonUtils.fromString(inputConfigJson, WebDriverConfig.class); + + Optional actualPreferences = webDriverConfig.getChromeLoggingPreferences(); + assertThat(actualPreferences, is(Optional.of("FINE"))); + } + @Test public void testConstructionFromJsonFileWithBadBaseUrlFails() { assertThrows(JsonMappingException.class, () -> { diff --git a/src/test/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriverTests.java b/src/test/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriverTests.java index b051e04..9a1aade 100644 --- a/src/test/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriverTests.java +++ b/src/test/java/uk/co/evoco/webdriver/configuration/driver/ConfiguredChromeDriverTests.java @@ -2,29 +2,48 @@ import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.Test; +import org.mockito.Mock; import org.openqa.selenium.WebDriver; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.logging.LoggingPreferences; import org.openqa.selenium.support.events.EventFiringWebDriver; +import org.powermock.api.mockito.PowerMockito; +import uk.co.evoco.webdriver.configuration.TestConfigHelper; +import uk.co.evoco.webdriver.configuration.WebDriverConfig; import java.io.File; import java.io.IOException; +import java.lang.reflect.Field; import java.util.Map; +import java.util.Optional; +import java.util.logging.Level; import static org.hamcrest.CoreMatchers.instanceOf; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; public class ConfiguredChromeDriverTests { + @Mock + WebDriverConfig webDriverConfigMock = mock(WebDriverConfig.class); + @Test - public void testReturnsLocalWebDriver() throws IOException { + public void testReturnsLocalWebDriver() throws IOException, IllegalAccessException { + Field testConfigHelperStaticVariable = PowerMockito.field(TestConfigHelper.class, "testConfigHelper"); + testConfigHelperStaticVariable.set(TestConfigHelper.class, null); + ConfiguredDriver configuredChromeDriver = new ConfiguredChromeDriver(); WebDriver webDriver = configuredChromeDriver.getDriver(FileUtils.getTempDirectory()); assertThat(webDriver, instanceOf(EventFiringWebDriver.class)); } @Test - public void testGetOptionsReturnsOptionsIncludedInChromeConfig() throws IOException { + public void testGetOptionsReturnsOptionsIncludedInChromeConfig() throws IOException, IllegalAccessException { + Field testConfigHelperStaticVariable = PowerMockito.field(TestConfigHelper.class, "testConfigHelper"); + testConfigHelperStaticVariable.set(TestConfigHelper.class, null); + ConfiguredChromeDriver configuredChromeDriver = new ConfiguredChromeDriver(); Map chromeOptions = getOptions(configuredChromeDriver.getOptions()); String expectedFileDownLoadPath = new File("run-generated-files/chrome/downloads").getCanonicalPath(); @@ -33,8 +52,45 @@ public void testGetOptionsReturnsOptionsIncludedInChromeConfig() throws IOExcept assertThat(chromeOptions.get("safebrowsing.enabled"), is(true)); } + @Test + public void testGetLoggingPreferencesReturnsEnabledLogLevel() throws Exception { + + Optional mockConfigReturn = Optional.of("FINE"); + mockTestConfig(); + when(webDriverConfigMock.getChromeLoggingPreferences()).thenReturn(mockConfigReturn); + + + ConfiguredChromeDriver configuredChromeDriver = new ConfiguredChromeDriver(); + LoggingPreferences loggingPreferences = configuredChromeDriver.getLoggerPrefs(); + + Level googleChromeLoggingPreferences = loggingPreferences.getLevel("performance"); + + assertThat(googleChromeLoggingPreferences, is(Level.FINE)); + } + + @Test + public void testEmptyLoggingPreferencesReturnsOffLevel() throws IllegalAccessException { + Optional mockConfigReturn = Optional.of(""); + mockTestConfig(); + when(webDriverConfigMock.getChromeLoggingPreferences()).thenReturn(mockConfigReturn); + + ConfiguredChromeDriver configuredChromeDriver = new ConfiguredChromeDriver(); + LoggingPreferences loggingPreferences = configuredChromeDriver.getLoggerPrefs(); + + Level googleChromeLoggingPreferences = loggingPreferences.getLevel("performance"); + + assertThat(googleChromeLoggingPreferences, is(Level.OFF)); + } + private Map getOptions(ChromeOptions options) { Map googleChromeOptions = (Map) options.asMap().get("goog:chromeOptions"); return (Map) googleChromeOptions.get("prefs"); } + + private void mockTestConfig() throws IllegalAccessException { + Field testConfigHelperStaticVariable = PowerMockito.field(TestConfigHelper.class, "testConfigHelper"); + testConfigHelperStaticVariable.set(TestConfigHelper.class, mock(TestConfigHelper.class)); + Field webdriverConfigStaticVariable = PowerMockito.field(TestConfigHelper.class, "webDriverConfig"); + webdriverConfigStaticVariable.set(TestConfigHelper.class, webDriverConfigMock); + } } diff --git a/src/test/java/uk/co/evoco/webdriver/utils/ChromeDriverPreferenceTests.java b/src/test/java/uk/co/evoco/webdriver/utils/ChromeDriverPreferenceTests.java index 66352d6..84fc736 100644 --- a/src/test/java/uk/co/evoco/webdriver/utils/ChromeDriverPreferenceTests.java +++ b/src/test/java/uk/co/evoco/webdriver/utils/ChromeDriverPreferenceTests.java @@ -3,10 +3,15 @@ import org.apache.commons.io.FileUtils; import org.junit.jupiter.api.*; import org.openqa.selenium.By; +import org.openqa.selenium.InvalidArgumentException; import org.openqa.selenium.WebDriver; +import org.openqa.selenium.logging.LogType; +import org.powermock.api.mockito.PowerMockito; +import uk.co.evoco.webdriver.configuration.TestConfigHelper; import uk.co.evoco.webdriver.configuration.driver.ConfiguredChromeDriver; import java.io.File; +import java.lang.reflect.Field; import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.MatcherAssert.assertThat; @@ -17,8 +22,8 @@ public class ChromeDriverPreferenceTests { private static EmbeddedJetty embeddedJetty; private WebDriver webDriver; - @BeforeAll - public static void webDriverSetup() throws Exception { + @BeforeEach + public void webDriverSetup() throws Exception { embeddedJetty = new EmbeddedJetty(); embeddedJetty.start(); baseUrl = "http://localhost:" + embeddedJetty.getPort() + "/index.html"; @@ -36,13 +41,27 @@ public void testChromeBrowserPreferencesApplied() throws Exception { new File(expectedFile).exists(), is(true)); } - @AfterEach - public void tearDown() { - this.webDriver.quit(); + @Test + public void testChromeBrowserLoggingPreferencesApplied() throws Exception { + Field testConfigHelperStaticVariable = PowerMockito.field(TestConfigHelper.class, "testConfigHelper"); + testConfigHelperStaticVariable.set(TestConfigHelper.class, null); + + System.setProperty("config", "fixtures/sample-config-with-chrome-logging-preferences.json"); + assertThat(System.getProperty("config"), is("fixtures/sample-config-with-chrome-logging-preferences.json")); + webDriver = new ConfiguredChromeDriver().getDriver(FileUtils.getTempDirectory()); + webDriver.get(baseUrl); + webDriver.findElement(By.xpath("//a[text()='clickHereToDownLoadAFile']")); + try { + Assertions.assertTrue(webDriver.manage().logs().get(LogType.PERFORMANCE).getAll().size() > 0); + } catch (InvalidArgumentException e) { + Assertions.fail("There are no Performance Logs that have been found"); + } } - @AfterAll - public static void webDriverTearDown() throws Exception { + + @AfterEach + public void tearDown() throws Exception { + this.webDriver.quit(); System.setProperty("config", "DEFAULT"); embeddedJetty.stop(); } diff --git a/src/test/resources/fixtures/sample-config-with-chrome-logging-preferences.json b/src/test/resources/fixtures/sample-config-with-chrome-logging-preferences.json new file mode 100644 index 0000000..43d4ab4 --- /dev/null +++ b/src/test/resources/fixtures/sample-config-with-chrome-logging-preferences.json @@ -0,0 +1,42 @@ +{ + "browser": "chrome", + "baseUrl": "https://www.google.com", + "timeout": "30", + "headless": true, + "runType": "LOCAL", + "takeScreenshotOnError": true, + "testConfig": { + "sample": "sample text" + }, + "gridConfig": { + "url": "http://localhost:4444/wd/hub" + }, + "browserPreferences": { + "chrome": { + "profile.default_content_settings.popups": 0, + "download.default_directory": "run-generated-files/chrome/downloads", + "safebrowsing.enabled": true + } + }, + "chromeLoggingPreferences": { + "logLevel": "ALL" + }, + "tolerantActionExceptions": { + "waitTimeoutInSeconds": 5, + "exceptionsToHandle": [ + "StaleElementReferenceException", + "ElementClickInterceptedException", + "ElementNotInteractableException" + ] + }, + "metrics": { + "jmx": { + "enabled": false + }, + "graphite": { + "enabled": false, + "host": "localhost", + "port": 2003 + } + } +} \ No newline at end of file