From 519d7759a9b082df0246795ef6b0e9863d6623ca Mon Sep 17 00:00:00 2001 From: Damien Cooke Date: Tue, 17 Aug 2021 20:34:07 +0100 Subject: [PATCH] Add performance logging through config *When user provides chrome logging options in the config the system will set the performance logs to the level specified in the config. It will also turn on verbose logging and Browser logs to the same level. * Added Power mock to mock out static members for the unit tests --- pom.xml | 13 ++++ .../configuration/WebDriverConfig.java | 24 ++++++++ .../driver/ConfiguredChromeDriver.java | 60 ++++++++++++++++++- .../configuration/WebDriverConfigTests.java | 47 ++++++++++++++- .../driver/ConfiguredChromeDriverTests.java | 60 ++++++++++++++++++- .../utils/ChromeDriverPreferenceTests.java | 33 +++++++--- ...onfig-with-chrome-logging-preferences.json | 42 +++++++++++++ 7 files changed, 266 insertions(+), 13 deletions(-) create mode 100644 src/test/resources/fixtures/sample-config-with-chrome-logging-preferences.json 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