diff --git a/LICENSE b/LICENSE index ff1b7cb..df21359 100644 --- a/LICENSE +++ b/LICENSE @@ -186,7 +186,7 @@ same "printed page" as the copyright notice for easier identification within third-party archives. - Copyright 2019 Aquality Automation + Copyright 2022 Aquality Automation Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/README.md b/README.md index 2f13860..a8dce51 100644 --- a/README.md +++ b/README.md @@ -13,13 +13,16 @@ Most of performed methods are logged using LOG4J, so you can easily see a histor We use interfaces where is possible, so you can implement your own version of target interface with no need to rewrite other classes. ### Quick start +To start the project using aquality.selenium framework, you can [download our template BDD project by this link.](https://github.com/aquality-automation/aquality-selenium-java-template) -1. To start work with this package, simply add the dependency to your pom.xml: +Alternatively, you can follow the steps below: + +1. Add the dependency to your pom.xml: ``` com.github.aquality-automation aquality-selenium - LATEST + 3.x.x ``` @@ -47,14 +50,37 @@ txbSearch.submit(); browser.waitForPageToLoad(); ``` -6. Quit browser at the end +6. Use BiDi functionality to handle basic authentication: +```java +browser.network().addBasicAuthentication("domain.com", "username", "password"); ``` +or intercept network requests/responses: +```java +browser.network().startNetworkInterceptor((HttpHandler) request -> new HttpResponse() + .setStatus(HttpStatus.SC_OK) + .addHeader("Content-Type", MediaType.HTML_UTF_8.toString()) + .setContent(utf8String("Some phrase"))); +``` +7. Emulate GeoLocation, Device, Touch, Media, UserAgent overrides, Disable script execution, log HTTP exchange, track Performance metrics, add initialization scripts, and more using browser.devTools() interfaces: +```java +final double latitude = 53.90772672521578; +final double longitude = 27.458060411865375; +final double accuracy = 0.97; +browser.devTools().emulation().setGeolocationOverride(latitude, longitude, accuracy); +``` +See more DevTools use cases [here](./src/test/java/tests/usecases/devtools) + +8. Quit browser at the end +```java browser.quit(); ``` -See full example [here](./src/test/java/tests/usecases/QuickStartExample.java) +See quick start example [here](./src/test/java/tests/usecases/QuickStartExample.java) ### Documentation To get more details please look at documentation: - [In English](https://github.com/aquality-automation/aquality-selenium-java/wiki/Overview-(English)) - [In Russian](https://github.com/aquality-automation/aquality-selenium-java/wiki/Overview-(Russian)) + +### License +Library's source code is made available under the [Apache 2.0 license](LICENSE). diff --git a/pom.xml b/pom.xml index 109371a..dcbd724 100644 --- a/pom.xml +++ b/pom.xml @@ -6,7 +6,7 @@ com.github.aquality-automation aquality-selenium - 3.0.0 + 3.1.0 jar Aquality Selenium Library around Selenium WebDriver @@ -81,31 +81,25 @@ com.github.aquality-automation aquality-selenium-core - 2.0.1 - - - - org.seleniumhq.selenium - selenium-java - 4.1.0 + 2.0.4 io.github.bonigarcia webdrivermanager - 5.0.2 + 5.3.0 com.fasterxml.jackson.core jackson-databind - 2.13.0 + 2.13.4 org.testng testng - 6.14.3 + 7.5 test diff --git a/src/main/java/aquality/selenium/browser/Browser.java b/src/main/java/aquality/selenium/browser/Browser.java index 18fdac2..e3dcedc 100644 --- a/src/main/java/aquality/selenium/browser/Browser.java +++ b/src/main/java/aquality/selenium/browser/Browser.java @@ -1,5 +1,8 @@ package aquality.selenium.browser; +import aquality.selenium.browser.devtools.DevToolsHandling; +import aquality.selenium.browser.devtools.JavaScriptHandling; +import aquality.selenium.browser.devtools.NetworkHandling; import aquality.selenium.configuration.IBrowserProfile; import aquality.selenium.configuration.ITimeoutConfiguration; import aquality.selenium.core.applications.IApplication; @@ -7,12 +10,12 @@ import aquality.selenium.core.localization.ILocalizedLogger; import aquality.selenium.core.waitings.IConditionalWait; import org.apache.commons.io.IOUtils; -import org.openqa.selenium.Alert; -import org.openqa.selenium.Dimension; -import org.openqa.selenium.NoAlertPresentException; -import org.openqa.selenium.OutputType; +import org.apache.commons.lang3.NotImplementedException; +import org.openqa.selenium.*; import org.openqa.selenium.WebDriver.Navigation; +import org.openqa.selenium.devtools.HasDevTools; import org.openqa.selenium.logging.LogEntries; +import org.openqa.selenium.remote.Augmenter; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.support.ui.ExpectedCondition; @@ -20,7 +23,6 @@ import java.io.IOException; import java.nio.charset.StandardCharsets; import java.time.Duration; -import java.util.concurrent.TimeUnit; import java.util.function.Supplier; public class Browser implements IApplication { @@ -32,6 +34,7 @@ public class Browser implements IApplication { private final ILocalizationManager localizationManager; private final ILocalizedLogger localizedLogger; + private DevToolsHandling devTools; private Duration implicitTimeout; public Browser(RemoteWebDriver remoteWebDriver) { @@ -262,6 +265,17 @@ public Object executeScript(final String script, Object... arguments) { return executeJavaScript(() -> getDriver().executeScript(script, arguments)); } + /** + * Executes JS (jQuery) script. + * + * @param script Script pinned with {@link this#javaScriptEngine()}. + * @param arguments Arguments for the script (web elements, values etc. + * @return Result object of script execution + */ + public Object executeScript(final ScriptKey script, Object... arguments) { + return executeJavaScript(() -> getDriver().executeScript(script, arguments)); + } + private Object executeJavaScript(Supplier executeScriptFunc) { Object result = executeScriptFunc.get(); return result instanceof Boolean ? Boolean.parseBoolean(result.toString()) : result; @@ -368,4 +382,41 @@ public final BrowserName getBrowserName() { private Duration getImplicitWaitTimeout() { return implicitTimeout; } + + /** + * Provides interface to handle DevTools for Chromium-based and Firefox drivers. + * @return an instance of {@link DevToolsHandling} + */ + public DevToolsHandling devTools() { + if (devTools != null) { + return devTools; + } + WebDriver driver = getDriver(); + if (!(driver instanceof HasDevTools)) { + driver = new Augmenter().augment(driver); + } + if (driver instanceof HasDevTools) { + devTools = new DevToolsHandling((HasDevTools) driver); + return devTools; + } + else { + throw new NotImplementedException("DevTools protocol is not supported for current browser."); + } + } + + /** + * Provides Network Handling functionality + * @return an instance of {@link NetworkHandling} + */ + public NetworkHandling network() { + return devTools().network(); + } + + /** + * Provides JavaScript Monitoring functionality. + * @return an instance of {@link JavaScriptHandling} + */ + public JavaScriptHandling javaScriptEngine() { + return devTools().javaScript(); + } } diff --git a/src/main/java/aquality/selenium/browser/BrowserName.java b/src/main/java/aquality/selenium/browser/BrowserName.java index 3b45822..8c77326 100644 --- a/src/main/java/aquality/selenium/browser/BrowserName.java +++ b/src/main/java/aquality/selenium/browser/BrowserName.java @@ -5,5 +5,8 @@ public enum BrowserName { EDGE, FIREFOX, IEXPLORER, - SAFARI + OPERA, + OTHER, + SAFARI, + YANDEX } diff --git a/src/main/java/aquality/selenium/browser/JavaScript.java b/src/main/java/aquality/selenium/browser/JavaScript.java index d8bfecc..b9a4c19 100644 --- a/src/main/java/aquality/selenium/browser/JavaScript.java +++ b/src/main/java/aquality/selenium/browser/JavaScript.java @@ -33,6 +33,7 @@ public enum JavaScript { SELECT_COMBOBOX_VALUE_BY_TEXT("selectComboboxValueByText.js"), SET_FOCUS("setFocus.js"), SET_VALUE("setValue.js"), + SET_ATTRIBUTE("setAttribute.js"), SCROLL_BY("scrollBy.js"), IS_PAGE_LOADED("isPageLoaded.js"), SCROLL_WINDOW_BY("scrollWindowBy.js"), @@ -40,7 +41,8 @@ public enum JavaScript { GET_VIEWPORT_COORDINATES("getViewPortCoordinates.js"), GET_SCREEN_OFFSET("getScreenOffset.js"), OPEN_IN_NEW_TAB("openInNewTab.js"), - OPEN_NEW_TAB("openNewTab.js"); + OPEN_NEW_TAB("openNewTab.js"), + EXPAND_SHADOW_ROOT("expandShadowRoot.js"); private final String filename; @@ -55,11 +57,12 @@ public enum JavaScript { */ public String getScript() { URL scriptFile = getClass().getResource("/js/" + filename); - try { - InputStream stream = scriptFile.openStream(); - return IOUtils.toString(stream, StandardCharsets.UTF_8.name()); - } catch (IOException e) { - Logger.getInstance().fatal(format("Couldn't find the script \"%s\"", filename), e); + if (scriptFile != null) { + try (InputStream stream = scriptFile.openStream()) { + return IOUtils.toString(stream, StandardCharsets.UTF_8.name()); + } catch (IOException e) { + Logger.getInstance().fatal(format("Couldn't find the script \"%s\"", filename), e); + } } return ""; } diff --git a/src/main/java/aquality/selenium/browser/LocalBrowserFactory.java b/src/main/java/aquality/selenium/browser/LocalBrowserFactory.java index 97a7b20..555600f 100644 --- a/src/main/java/aquality/selenium/browser/LocalBrowserFactory.java +++ b/src/main/java/aquality/selenium/browser/LocalBrowserFactory.java @@ -8,11 +8,17 @@ import io.github.bonigarcia.wdm.config.Architecture; import org.openqa.selenium.Capabilities; import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.edge.EdgeDriver; +import org.openqa.selenium.edge.EdgeOptions; import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; import org.openqa.selenium.ie.InternetExplorerDriver; +import org.openqa.selenium.ie.InternetExplorerOptions; +import org.openqa.selenium.remote.AbstractDriverOptions; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.safari.SafariDriver; +import org.openqa.selenium.safari.SafariOptions; public class LocalBrowserFactory extends BrowserFactory { @@ -32,35 +38,32 @@ protected RemoteWebDriver getDriver() { Architecture systemArchitecture = driverSettings.getSystemArchitecture(); switch (browserName) { case CHROME: + case YANDEX: WebDriverManager.chromedriver().driverVersion(webDriverVersion).setup(); - driver = getDriver(ChromeDriver.class, driverSettings.getCapabilities()); + driver = new ChromeDriver((ChromeOptions) driverSettings.getDriverOptions()); + break; + case OPERA: + WebDriverManager.operadriver().driverVersion(webDriverVersion).setup(); + driver = new ChromeDriver((ChromeOptions) driverSettings.getDriverOptions()); break; case FIREFOX: WebDriverManager.firefoxdriver().driverVersion(webDriverVersion).setup(); - driver = getDriver(FirefoxDriver.class, driverSettings.getCapabilities()); + driver = new FirefoxDriver((FirefoxOptions) driverSettings.getDriverOptions()); break; case IEXPLORER: WebDriverManager.iedriver().architecture(systemArchitecture).driverVersion(webDriverVersion).setup(); - driver = getDriver(InternetExplorerDriver.class, driverSettings.getCapabilities()); + driver = new InternetExplorerDriver((InternetExplorerOptions) driverSettings.getDriverOptions()); break; case EDGE: WebDriverManager.edgedriver().driverVersion(webDriverVersion).setup(); - driver = getDriver(EdgeDriver.class, driverSettings.getCapabilities()); + driver = new EdgeDriver((EdgeOptions) driverSettings.getDriverOptions()); break; case SAFARI: - driver = getDriver(SafariDriver.class, driverSettings.getCapabilities()); + driver = new SafariDriver((SafariOptions) driverSettings.getDriverOptions()); break; default: throw new IllegalArgumentException(String.format("Browser [%s] is not supported.", browserName)); } return driver; } - - private T getDriver(Class driverClass, Capabilities capabilities) { - try { - return driverClass.getDeclaredConstructor(Capabilities.class).newInstance(capabilities); - } catch (ReflectiveOperationException e) { - throw new UnsupportedOperationException(String.format("Cannot instantiate driver with type '%1$s'.", driverClass), e); - } - } } diff --git a/src/main/java/aquality/selenium/browser/RemoteBrowserFactory.java b/src/main/java/aquality/selenium/browser/RemoteBrowserFactory.java index b672265..0ddf5ef 100644 --- a/src/main/java/aquality/selenium/browser/RemoteBrowserFactory.java +++ b/src/main/java/aquality/selenium/browser/RemoteBrowserFactory.java @@ -35,7 +35,7 @@ public RemoteBrowserFactory(IActionRetrier actionRetrier, IBrowserProfile browse @Override protected RemoteWebDriver getDriver() { - Capabilities capabilities = browserProfile.getDriverSettings().getCapabilities(); + Capabilities capabilities = browserProfile.getDriverSettings().getDriverOptions(); localizedLogger.info("loc.browser.grid"); ClientFactory clientFactory = new ClientFactory(); diff --git a/src/main/java/aquality/selenium/browser/devtools/DevToolsHandling.java b/src/main/java/aquality/selenium/browser/devtools/DevToolsHandling.java new file mode 100644 index 0000000..387eb9c --- /dev/null +++ b/src/main/java/aquality/selenium/browser/devtools/DevToolsHandling.java @@ -0,0 +1,239 @@ +package aquality.selenium.browser.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.core.localization.ILocalizedLogger; +import org.apache.commons.lang3.NotImplementedException; +import org.openqa.selenium.chromium.ChromiumDriver; +import org.openqa.selenium.devtools.Command; +import org.openqa.selenium.devtools.DevTools; +import org.openqa.selenium.devtools.Event; +import org.openqa.selenium.devtools.HasDevTools; +import org.openqa.selenium.devtools.v85.performance.Performance; +import org.openqa.selenium.devtools.v85.performance.model.Metric; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.stream.Collectors; + +/** + * Wrapper for Selenium {@link DevTools} functionality. + */ +public class DevToolsHandling { + private final HasDevTools devToolsProvider; + private final ILocalizedLogger logger; + + private DevTools session; + private NetworkHandling network; + private EmulationHandling emulation; + private JavaScriptHandling javaScript; + + /** + * Initializes an instance of {@link DevToolsHandling} + * @param devToolsProvider Instance of {@link org.openqa.selenium.WebDriver} which supports CDP protocol. + */ + public DevToolsHandling(HasDevTools devToolsProvider) { + this.devToolsProvider = devToolsProvider; + this.logger = AqualityServices.getLocalizedLogger(); + } + + HasDevTools getDevToolsProvider() { + return devToolsProvider; + } + + private DevTools getDevTools() { + return getDevTools("default"); + } + + private DevTools getDevTools(String handleToLog) { + if (session == null) { + logger.info("loc.browser.devtools.session.get", handleToLog); + session = devToolsProvider.getDevTools(); + } + return session; + } + + private void logCommand(String commandName, Map commandParameters) { + if (!commandParameters.isEmpty()) { + logger.info("loc.browser.devtools.command.execute.withparams", commandName, commandParameters); + } + else { + logger.info("loc.browser.devtools.command.execute", commandName); + } + } + + private void logCommandResult(Object result) { + if (result != null) { + if (result instanceof Map && ((Map) result).isEmpty()) { + return; + } + logger.info("loc.browser.devtools.command.execute.result", result); + } + } + + /** + * Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol, if there is not one. + * If the session already exist, returns existing session. + * Defaults to autodetect the protocol version for Chromium, V85 for Firefox. + * @return The active session to use to communicate with the Chromium Developer Tools debugging protocol. + */ + public DevTools getDevToolsSession() { + getDevTools().createSessionIfThereIsNotOne(); + return session; + } + + /** + * Creates a session to communicate with a browser using the Chromium Developer Tools debugging protocol, if there is not one, on given window/tab (aka target). + * If the session already exist, returns existing session. + * If windowHandle is null, then the first "page" type will be selected. + * Pass the windowHandle if you have multiple windows/tabs opened to connect to the expected window/tab. + * Defaults to autodetect the protocol version for Chromium, V85 for Firefox. + * @param windowHandle result of {@link org.openqa.selenium.WebDriver#getWindowHandle()}, optional. * + * @return The active session to use to communicate with the Chromium Developer Tools debugging protocol. + */ + public DevTools getDevToolsSession(String windowHandle) { + getDevTools(windowHandle).createSessionIfThereIsNotOne(windowHandle); + return session; + } + + /** + * Gets a value indicating whether a DevTools session is active. + * @return true if there is an active session, false otherwise. + */ + public boolean hasActiveDevToolsSession() { + logger.info("loc.browser.devtools.session.isactive"); + boolean result = devToolsProvider.getDevTools().getCdpSession() != null; + logger.info("loc.browser.devtools.session.isactive.result", result); + return result; + } + + /** + * Closes a DevTools session. + */ + public void closeDevToolsSession() { + logger.info("loc.browser.devtools.session.close"); + getDevTools().disconnectSession(); + } + + /** + * Executes a custom Chromium Dev Tools Protocol Command. + * Note: works only if current driver is instance of {@link ChromiumDriver}. + * @param commandName Name of the command to execute. + * @param commandParameters Parameters of the command to execute. + * @return An object representing the result of the command, if applicable. + */ + public Map executeCdpCommand(String commandName, Map commandParameters) { + if (devToolsProvider instanceof ChromiumDriver) { + logCommand(commandName, commandParameters); + ChromiumDriver driver = (ChromiumDriver) devToolsProvider; + Map result = driver.executeCdpCommand(commandName, commandParameters); + logCommandResult(result); + return result; + } + else { + throw new NotImplementedException("Execution of CDP command directly is not supported for current browser. Try sendCommand method instead."); + } + } + + /** + * Sends the specified command and returns the associated command response. + * @param command An instance of the {@link Command} to send. + * @param The type of the command's result. For most commands it's {@link Void} + * @return the result of the command, if applicable + */ + public X sendCommand(Command command) { + logCommand(command.getMethod(), command.getParams()); + X result = getDevToolsSession().send(command); + logCommandResult(result); + return result; + } + + /** + * Adds a listener for a specific event with the handler for it. + * @param event Event to listen for + * @param handler Handler to call + * @param The type of the event's result. + */ + public void addListener(Event event, Consumer handler) { + logger.info("loc.browser.devtools.listener.add", event.getMethod()); + getDevToolsSession().addListener(event, handler); + } + + /** + * Removes all the listeners, and disables all the domains. + */ + public void clearListeners() { + logger.info("loc.browser.devtools.listener.clear"); + getDevToolsSession().clearListeners(); + } + + /** + * Version-independent emulation DevTools commands. + * @return an instance of {@link EmulationHandling}. + */ + public EmulationHandling emulation() { + if (emulation == null) { + emulation = new EmulationHandling(this); + } + return emulation; + } + + /** + * DevTools commands for network requests interception. + * @return an instance of {@link NetworkHandling}. + */ + public NetworkHandling network() { + if (network == null) { + network = new NetworkHandling(this); + } + return network; + } + + /** + * Provides JavaScript Monitoring functionality. + * @return an instance of {@link JavaScriptHandling} + */ + public JavaScriptHandling javaScript() { + if (javaScript == null) { + javaScript = new JavaScriptHandling(this); + } + return javaScript; + } + + /** + * Disable collecting and reporting metrics. + */ + public void disablePerformanceMonitoring() { + sendCommand(Performance.disable()); + } + + /** + * Enable collecting and reporting metrics. + * @param timeDomain Time domain to use for collecting and reporting duration metrics. + * Allowed Values: timeTicks, threadTicks. + */ + public void enablePerformanceMonitoring(String timeDomain) { + sendCommand(Performance.enable(Optional.of(Performance.EnableTimeDomain.fromString(timeDomain)))); + } + + /** + * Enable collecting and reporting metrics. + */ + public void enablePerformanceMonitoring() { + sendCommand(Performance.enable(Optional.empty())); + } + + /** + * Retrieve current values of run-time metrics. + * @return Current values for run-time metrics + */ + public Map getPerformanceMetrics() { + Command> command = Performance.getMetrics(); + logCommand(command.getMethod(), command.getParams()); + List metrics = getDevToolsSession().send(command); + Map result = metrics.stream().collect(Collectors.toMap(Metric::getName, Metric::getValue)); + logCommandResult(result.isEmpty() ? "empty" : result); + return result; + } +} diff --git a/src/main/java/aquality/selenium/browser/devtools/EmulationHandling.java b/src/main/java/aquality/selenium/browser/devtools/EmulationHandling.java new file mode 100644 index 0000000..df982cf --- /dev/null +++ b/src/main/java/aquality/selenium/browser/devtools/EmulationHandling.java @@ -0,0 +1,258 @@ +package aquality.selenium.browser.devtools; + +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.devtools.Command; +import org.openqa.selenium.devtools.v85.dom.model.RGBA; +import org.openqa.selenium.devtools.v85.emulation.Emulation; +import org.openqa.selenium.devtools.v85.emulation.model.MediaFeature; +import org.openqa.selenium.devtools.v85.emulation.model.ScreenOrientation; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * Implementation of version-independent emulation DevTools commands. + * Currently, only non-experimental extensions are implemented. + * For more information, @see DevTools Emulation documentation. + */ +@SuppressWarnings("OptionalUsedAsFieldOrParameterType") +public class EmulationHandling { + private final DevToolsHandling tools; + + /** + * Initializes a new instance of the {@link NetworkHandling} class. + * @param tools Instance of {@link DevToolsHandling}. + */ + public EmulationHandling(DevToolsHandling tools) { + this.tools = tools; + } + + /** + * Tells whether emulation is supported. + * @return true if the emulation is supported, false otherwise. + */ + public boolean canEmulate() { + return tools.sendCommand(Emulation.canEmulate()); + } + + /** + * Overrides the GeoLocation Position or Error. Omitting any of the parameters emulates position unavailable. + * @param latitude Latitude of location + * @param longitude Longitude of location + * @param accuracy Accuracy of the location + */ + public void setGeolocationOverride(double latitude, double longitude, double accuracy) { + setGeolocationOverride(Optional.of(latitude), Optional.of(longitude), Optional.of(accuracy)); + } + + /** + * Overrides the GeoLocation Position. Accuracy of the geoLocation is set to 1 meaning 100% accuracy. + * Omitting any of the parameters emulates position unavailable. + * @param latitude Latitude of location + * @param longitude Longitude of location + */ + public void setGeolocationOverride(double latitude, double longitude) { + setGeolocationOverride(latitude, longitude, 1); + } + + /** + * Overrides the GeoLocation Position or Error. Omitting any of the parameters emulates position unavailable. + * @param latitude Latitude of location + * @param longitude Longitude of location + * @param accuracy Accuracy of the location + */ + public void setGeolocationOverride(Optional latitude, Optional longitude, Optional accuracy) { + tools.sendCommand(Emulation.setGeolocationOverride(latitude, longitude, accuracy)); + } + + /** + * Clears the overridden GeoLocation Position and Error. + */ + public void clearGeolocationOverride() { + tools.sendCommand(Emulation.clearGeolocationOverride()); + } + + /** + * Overrides the values of device screen dimensions. + * @param params Version-specific set of parameters. For example, take a look at {@link Emulation#setDeviceMetricsOverride} + */ + public void setDeviceMetricsOverride(Map params) { + tools.sendCommand(new Command("Emulation.setDeviceMetricsOverride", params)); + } + + /** + * Overrides the values of device screen dimensions. + * @param width Value to override window.screen.width + * @param height Value to override window.screen.height + * @param deviceScaleFactor Overriding device scale factor value. 0 disables the override. + * @param mobile Whether to emulate mobile device. This includes viewport meta tag, overlay scrollbars, text auto-sizing and more. + */ + public void setDeviceMetricsOverride(Integer width, Integer height, Number deviceScaleFactor, Boolean mobile) { + setDeviceMetricsOverride(width, height, deviceScaleFactor, mobile, Optional.empty(), Optional.empty()); + } + + /** + * Overrides the values of device screen dimensions. + * @param width Value to override window.screen.width + * @param height Value to override window.screen.height + * @param deviceScaleFactor Overriding device scale factor value. 0 disables the override. + * @param mobile Whether to emulate mobile device. This includes viewport meta tag, overlay scrollbars, text auto-sizing and more. + * @param screenOrientationType Orientation type. + * Allowed Values (in any case): portraitPrimary, portraitSecondary, landscapePrimary, landscapeSecondary. + * @param screenOrientationAngle Orientation angle. Set only if orientation type was set. + */ + public void setDeviceMetricsOverride(Integer width, Integer height, Number deviceScaleFactor, Boolean mobile, + Optional screenOrientationType, Optional screenOrientationAngle) { + Optional screenOrientation = Optional.empty(); + if (screenOrientationType.isPresent() && StringUtils.isNotEmpty(screenOrientationType.get())) { + Integer angle = 0; + if (screenOrientationAngle.isPresent()) { + angle = screenOrientationAngle.get(); + } + screenOrientation = Optional.of(new ScreenOrientation(ScreenOrientation.Type.fromString(screenOrientationType.get()), angle)); + } + tools.sendCommand(Emulation.setDeviceMetricsOverride(width, height, deviceScaleFactor, mobile, Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty(), Optional.empty(), screenOrientation, Optional.empty())); + } + + /** + * Clears the overridden device metrics. + */ + public void clearDeviceMetricsOverride() { + tools.sendCommand(Emulation.clearDeviceMetricsOverride()); + } + + /** + * Overrides the values of user agent. + * @param params Version-specific set of parameters. + * For example, take a look at {@link Emulation#setUserAgentOverride} + */ + public void setUserAgentOverride(Map params) { + tools.sendCommand(new Command("Emulation.setUserAgentOverride", params)); + } + + /** + * Overrides the values of user agent. + * @param userAgent User agent to use. + */ + public void setUserAgentOverride(String userAgent) { + setUserAgentOverride(userAgent, Optional.empty(), Optional.empty()); + } + + /** + * Overrides the values of user agent. + * @param userAgent User agent to use. + * @param acceptLanguage Browser language to emulate. + * @param platform The platform navigator.platform should return. + */ + public void setUserAgentOverride(String userAgent, Optional acceptLanguage, Optional platform) { + tools.sendCommand(Emulation.setUserAgentOverride(userAgent, acceptLanguage, platform, Optional.empty())); + } + + /** + * Disables script execution in the page. + */ + public void setScriptExecutionDisabled() { + setScriptExecutionDisabled(true); + } + + /** + * Switches script execution in the page. + * @param value Whether script execution should be disabled in the page. + */ + public void setScriptExecutionDisabled(boolean value) { + tools.sendCommand(Emulation.setScriptExecutionDisabled(value)); + } + + /** + * Enables touch on platforms which do not support them. + */ + public void setTouchEmulationEnabled() { + setTouchEmulationEnabled(true); + } + + /** + * Enables touch on platforms which do not support them. + * @param enabled Whether the touch event emulation should be enabled. + */ + public void setTouchEmulationEnabled(boolean enabled) { + setTouchEmulationEnabled(enabled, Optional.empty()); + } + + /** + * Enables touch on platforms which do not support them. + * @param enabled Whether the touch event emulation should be enabled. + * @param maxTouchPoints Maximum touch points supported. Defaults to one. + */ + public void setTouchEmulationEnabled(boolean enabled, Optional maxTouchPoints) { + tools.sendCommand(Emulation.setTouchEmulationEnabled(enabled, maxTouchPoints)); + } + + /** + * Emulates the given media type or media feature for CSS media queries. + * @param params Version-specific set of parameters. For example, take a look at {@link Emulation#setEmulatedMedia} + */ + public void setEmulatedMedia(Map params) { + tools.sendCommand(new Command("Emulation.setEmulatedMedia", params)); + } + + /** + * Emulates the given media type or media feature for CSS media queries. + * @param media Media type to emulate. Empty string disables the override. + * Possible values: braille, embossed, handheld, print, projection, screen, speech, tty, tv. + * @param mediaFeatures Media features to emulate. + */ + public void setEmulatedMedia(String media, Map mediaFeatures) { + setEmulatedMedia(Optional.of(media), Optional.of(mediaFeatures)); + } + + /** + * Emulates the given media type or media feature for CSS media queries. + * @param media Media type to emulate. Empty string disables the override. + * Possible values: braille, embossed, handheld, print, projection, screen, speech, tty, tv. + * @param mediaFeatures Media features to emulate. + */ + public void setEmulatedMedia(Optional media, Optional> mediaFeatures) { + List featureList = mediaFeatures.orElse(Collections.emptyMap()).entrySet().stream() + .map((Map.Entry entry) -> new MediaFeature(entry.getKey(), entry.getValue())).collect(Collectors.toList()); + tools.sendCommand(Emulation.setEmulatedMedia(media, Optional.of(featureList))); + } + + /** + * Disables emulated media override. + */ + public void disableEmulatedMediaOverride() { + setEmulatedMedia(Collections.singletonMap("media", "")); + } + + /** + * Sets an override of the default background color of the frame. This override is used if the content does not specify one. + * @param red The red component, in the [0-255] range. + * @param green The green component, in the [0-255] range. + * @param blue The blue component, in the [0-255] range. + */ + public void setDefaultBackgroundColorOverride(int red, int green, int blue) { + setDefaultBackgroundColorOverride(red, green, blue, Optional.empty()); + } + + /** + * Sets an override of the default background color of the frame. This override is used if the content does not specify one. + * @param red The red component, in the [0-255] range. + * @param green The green component, in the [0-255] range. + * @param blue The blue component, in the [0-255] range. + * @param alpha The alpha component, in the [0-1] range (default: 1). + */ + public void setDefaultBackgroundColorOverride(int red, int green, int blue, Optional alpha) { + tools.sendCommand(Emulation.setDefaultBackgroundColorOverride(Optional.of(new RGBA(red, green, blue, alpha)))); + } + + /** + * Clears an override of the default background color of the frame. + */ + public void clearDefaultBackgroundColorOverride() { + tools.sendCommand(Emulation.setDefaultBackgroundColorOverride(Optional.empty())); + } +} diff --git a/src/main/java/aquality/selenium/browser/devtools/InitializationScript.java b/src/main/java/aquality/selenium/browser/devtools/InitializationScript.java new file mode 100644 index 0000000..688aabc --- /dev/null +++ b/src/main/java/aquality/selenium/browser/devtools/InitializationScript.java @@ -0,0 +1,48 @@ +package aquality.selenium.browser.devtools; + +import org.openqa.selenium.devtools.idealized.ScriptId; + +/** + * Represents a JavaScript script that is loaded and run on every document load. + */ +public class InitializationScript { + private final ScriptId scriptId; + private final String scriptName; + private final String scriptSource; + + /** + * Initializes a new instance of initialization script. + * @param scriptId the internal ID of the initialization script. + * @param scriptName the friendly name of the initialization script. + * @param scriptSource the JavaScript source of the initialization script. + */ + public InitializationScript(ScriptId scriptId, String scriptName, String scriptSource) { + this.scriptId = scriptId; + this.scriptName = scriptName; + this.scriptSource = scriptSource; + } + + /** + * Gets the internal ID of the initialization script. + * @return the internal ID of the initialization script. + */ + public ScriptId getScriptId() { + return scriptId; + } + + /** + * Gets the friendly name of the initialization script. + * @return the friendly name of the initialization script. + */ + public String getScriptName() { + return scriptName; + } + + /** + * Gets the JavaScript source of the initialization script + * @return the JavaScript source of the initialization script + */ + public String getScriptSource() { + return scriptSource; + } +} diff --git a/src/main/java/aquality/selenium/browser/devtools/JavaScriptHandling.java b/src/main/java/aquality/selenium/browser/devtools/JavaScriptHandling.java new file mode 100644 index 0000000..051067f --- /dev/null +++ b/src/main/java/aquality/selenium/browser/devtools/JavaScriptHandling.java @@ -0,0 +1,280 @@ +package aquality.selenium.browser.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.core.localization.ILocalizedLogger; +import org.apache.commons.lang3.NotImplementedException; +import org.openqa.selenium.JavascriptException; +import org.openqa.selenium.JavascriptExecutor; +import org.openqa.selenium.ScriptKey; +import org.openqa.selenium.WebDriver; +import org.openqa.selenium.devtools.events.ConsoleEvent; +import org.openqa.selenium.devtools.events.DomMutationEvent; +import org.openqa.selenium.devtools.idealized.Events; +import org.openqa.selenium.devtools.idealized.Javascript; +import org.openqa.selenium.devtools.idealized.ScriptId; +import org.openqa.selenium.devtools.v85.page.Page; +import org.openqa.selenium.devtools.v85.page.model.ScriptIdentifier; +import org.openqa.selenium.devtools.v85.runtime.Runtime; +import org.openqa.selenium.logging.EventType; +import org.openqa.selenium.logging.HasLogEvents; +import org.openqa.selenium.remote.Augmenter; + +import java.lang.reflect.Field; +import java.util.*; +import java.util.function.Consumer; + +import static org.openqa.selenium.devtools.events.CdpEventTypes.domMutation; + +/** + * DevTools commands for version-independent network interception. + * For more information, see {@link Javascript}. + */ +public class JavaScriptHandling { + private final DevToolsHandling tools; + private final Javascript engine; + private final Events events; + private final ILocalizedLogger logger = AqualityServices.getLocalizedLogger(); + private final Set bindings = new HashSet<>(); + private final Set initializationScripts = new HashSet<>(); + + /** + * Initializes a new instance of the {@link JavaScriptHandling} class. + * @param tools Instance of {@link DevToolsHandling}. + */ + public JavaScriptHandling(DevToolsHandling tools) { + this.tools = tools; + this.engine = tools.getDevToolsSession().getDomains().javascript(); + this.events = tools.getDevToolsSession().getDomains().events(); + } + + /** + * Adds a binding to a callback method that will raise an event when the named binding is called by JavaScript + * executing in the browser. + * @param scriptName The name of the callback that will trigger events. + */ + public void addScriptCallbackBinding(String scriptName) { + logger.info("loc.browser.javascript.scriptcallbackbinding.add", scriptName); + bindings.add(scriptName); + tools.sendCommand(Runtime.addBinding(scriptName, Optional.empty())); + } + + /** + * Removes a binding to a JavaScript callback. + * @param scriptName The name of the callback to be removed. + */ + public void removeScriptCallbackBinding(String scriptName) { + logger.info("loc.browser.javascript.scriptcallbackbinding.remove", scriptName); + bindings.remove(scriptName); + tools.sendCommand(Runtime.removeBinding(scriptName)); + } + + /** + * Gets the read-only list of binding callbacks added for this JavaScript engine. + * @return list of binding callbacks added for this JavaScript engine. + */ + public List getScriptCallbackBindings() { + logger.info("loc.browser.javascript.scriptcallbackbindings.get"); + return new ArrayList<>(bindings); + } + + /** + * Removes all bindings to JavaScript callbacks. + */ + public void clearScriptCallbackBindings() { + logger.info("loc.browser.javascript.scriptcallbackbindings.clear"); + bindings.forEach(scriptName -> { + bindings.remove(scriptName); + tools.sendCommand(Runtime.removeBinding(scriptName)); + }); + } + + /** + * Adds JavaScript to be loaded on every document load, and adds a binding to a callback method + * that will raise an event when the script with that name is called. + * @param scriptName The friendly name by which to refer to this initialization script. + * @param script The JavaScript to be loaded on every page. + * @return Initialization script. + */ + public InitializationScript addInitializationScript(String scriptName, String script) { + logger.info("loc.browser.javascript.initializationscript.add", scriptName); + logger.info("loc.browser.javascript.scriptcallbackbinding.add", scriptName); + ScriptId scriptId = engine.pin(scriptName, script); + InitializationScript initializationScript = new InitializationScript(scriptId, scriptName, script); + bindings.add(scriptName); + initializationScripts.add(initializationScript); + return initializationScript; + } + + private void removeInitializationScriptCore(InitializationScript script) { + tools.sendCommand(Page.removeScriptToEvaluateOnNewDocument(new ScriptIdentifier(script.getScriptId().getActualId().toString()))); + try { + final Field pinnedScripts = Javascript.class.getDeclaredField("pinnedScripts"); + pinnedScripts.setAccessible(true); + //noinspection unchecked + ((Map)pinnedScripts.get(engine)).remove(script.getScriptSource()); + pinnedScripts.setAccessible(false); + } catch (ReflectiveOperationException e) { + AqualityServices.getLogger().fatal("Error while removing initialization script", e); + } + initializationScripts.remove(script); + removeScriptCallbackBinding(script.getScriptName()); + } + + /** + * Removes JavaScript from being loaded on every document load, and removes a callback binding for it. + * @param script an instance of script to be removed. + */ + public void removeInitializationScript(InitializationScript script) { + logger.info("loc.browser.javascript.initializationscript.remove", script.getScriptName()); + removeInitializationScriptCore(script); + } + + /** + * Gets the read-only list of initialization scripts added for this JavaScript engine. + * @return the list of added initialization scripts. + */ + public List getInitializationScripts() { + logger.info("loc.browser.javascript.initializationscripts.get"); + return new ArrayList<>(initializationScripts); + } + + /** + * Removes all initialization scripts from being loaded on every document load. + */ + public void clearInitializationScripts() { + logger.info("loc.browser.javascript.initializationscripts.clear"); + initializationScripts.forEach(this::removeInitializationScriptCore); + } + + private JavascriptExecutor getJavascriptExecutor() { + return (JavascriptExecutor) tools.getDevToolsProvider(); + } + + /** + * Pins a JavaScript snippet for execution in the browser without transmitting the entire script across the wire for every execution. + * @param script The JavaScript to pin. + * @return object to use to execute the script. + */ + public ScriptKey pinScript(String script) { + logger.info("loc.browser.javascript.snippet.pin"); + return getJavascriptExecutor().pin(script); + } + + /** + * Unpins a previously pinned script from the browser. + * @param pinnedScript The {@link ScriptKey} object to unpin. + */ + public void unpinScript(ScriptKey pinnedScript) { + logger.info("loc.browser.javascript.snippet.unpin"); + getJavascriptExecutor().unpin(pinnedScript); + } + + /** + * Gets list of previously pinned scripts. + * @return a list of previously pinned scripts. + */ + public Set getPinnedScripts() { + logger.info("loc.browser.javascript.snippets.get"); + return getJavascriptExecutor().getPinnedScripts(); + } + + /** + * Unpins previously pinned scripts from being loaded on every document load. + */ + public void clearPinnedScripts() { + logger.info("loc.browser.javascript.snippets.clear"); + getJavascriptExecutor().getPinnedScripts().forEach(getJavascriptExecutor()::unpin); + } + + /** + * Starts monitoring for events from the browser's JavaScript engine. + */ + public void startEventMonitoring() { + logger.info("loc.browser.javascript.event.monitoring.start"); + tools.sendCommand(Runtime.enable()); + } + + /** + * Stops monitoring for events from the browser's JavaScript engine, and clears JavaScript console event listeners. + */ + public void stopEventMonitoring() { + logger.info("loc.browser.javascript.event.monitoring.stop"); + events.disable(); + } + + /** + * Adds a listener for events that occur when a JavaScript callback with a named binding is executed. + * To add a binding, use {@link this.addJsBinding}. + * @param listener a listener to add, consuming a name of exposed script. + */ + public void addBindingCalledListener(Consumer listener) { + logger.info("loc.browser.javascript.event.callbackexecuted.add"); + engine.addBindingCalledListener(listener); + } + + private HasLogEvents getDriverThatHasLogEvents() { + WebDriver driver = (WebDriver) tools.getDevToolsProvider(); + if (!(driver instanceof HasLogEvents)) { + Augmenter augmenter = new Augmenter(); + String browserName = AqualityServices.getBrowserProfile().getBrowserName().name().toLowerCase(); + driver = augmenter.addDriverAugmentation(browserName, HasLogEvents.class, (caps, exec) -> new HasLogEvents() { + @Override + public void onLogEvent(EventType kind) { + kind.initializeListener((WebDriver) tools.getDevToolsProvider()); + } + }).augment(driver); + if (!(driver instanceof HasLogEvents)) { + throw new NotImplementedException( + String.format("Driver for the current browser [%s] doesn't implement HasLogEvents", browserName)); + } + } + return (HasLogEvents) driver; + } + + /** + * Adds a listener for events that occur when a value of an attribute in an element is being changed. + * @param listener a listener to add, consuming a dom mutation event. + */ + public void addDomMutatedListener(Consumer listener) { + logger.info("loc.browser.javascript.event.dommutated.add"); + getDriverThatHasLogEvents().onLogEvent(domMutation(listener)); + } + + /** + * Adds a listener for events that occur when methods on the JavaScript console are called. + * @param listener a listener to add, consuming a {@link ConsoleEvent}. + */ + public void addJavaScriptConsoleApiListener(Consumer listener) { + logger.info("loc.browser.javascript.event.consoleapicalled.add"); + events.addConsoleListener(listener); + } + + /** + * Adds a listener for events that occur when an exception is thrown by JavaScript being executed in the browser. + * @param listener a listener to add, consuming a javascript exception. + */ + public void addJavaScriptExceptionThrownListener(Consumer listener) { + logger.info("loc.browser.javascript.event.exceptionthrown.add"); + events.addJavascriptExceptionListener(listener); + } + + /** + * Removes all bindings to JavaScript callbacks and all initialization scripts from being loaded for each document. + */ + public void clearAll() { + logger.info("loc.browser.javascript.clearall"); + clearInitializationScripts(); + clearScriptCallbackBindings(); + } + + /** + * Removes all bindings to JavaScript callbacks and all initialization scripts from being loaded for each document, + * and stops listening for events. + */ + public void reset() { + logger.info("loc.browser.javascript.reset"); + engine.disable(); + clearInitializationScripts(); + clearScriptCallbackBindings(); + } +} diff --git a/src/main/java/aquality/selenium/browser/devtools/NetworkHandling.java b/src/main/java/aquality/selenium/browser/devtools/NetworkHandling.java new file mode 100644 index 0000000..985eda3 --- /dev/null +++ b/src/main/java/aquality/selenium/browser/devtools/NetworkHandling.java @@ -0,0 +1,316 @@ +package aquality.selenium.browser.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.core.localization.ILocalizedLogger; +import aquality.selenium.logging.HttpExchangeLoggingOptions; +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.Credentials; +import org.openqa.selenium.UsernameAndPassword; +import org.openqa.selenium.devtools.NetworkInterceptor; +import org.openqa.selenium.devtools.idealized.Network; +import org.openqa.selenium.devtools.v85.network.model.*; +import org.openqa.selenium.remote.http.*; + +import java.net.URI; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.Predicate; +import java.util.function.Supplier; + +import static aquality.selenium.browser.AqualityServices.getBrowser; +import static aquality.selenium.logging.LocalizedLoggerUtility.logByLevel; +import static org.openqa.selenium.devtools.v85.network.Network.*; + +/** + * DevTools commands for version-independent network interception. + * For more information, see {@link org.openqa.selenium.devtools.v85.network.Network} and {@link Network}. + */ +public class NetworkHandling { + public static final String LOC_NETWORK_INTERCEPTOR_START = "loc.browser.network.interceptor.start"; + private final DevToolsHandling tools; + private final Network network; + private final ILocalizedLogger logger = AqualityServices.getLocalizedLogger(); + + /** + * Initializes a new instance of the {@link NetworkHandling} class. + * + * @param tools Instance of {@link DevToolsHandling}. + */ + public NetworkHandling(DevToolsHandling tools) { + this.tools = tools; + this.network = tools.getDevToolsSession().getDomains().network(); + } + + /** + * Starts network monitoring. + */ + public void startMonitoring() { + logger.info("loc.browser.network.monitoring.start"); + network.prepareToInterceptTraffic(); + } + + /** + * Stops network monitoring. + */ + public void stopMonitoring() { + logger.info("loc.browser.network.monitoring.stop"); + network.disable(); + } + + /** + * Overrides the values of user agent. + * @param userAgent User agent to use. + */ + public void setUserAgent(String userAgent) { + logger.info("loc.browser.network.useragent.set", userAgent); + network.setUserAgent(userAgent); + } + + /** + * Overrides the values of user agent. + * @param userAgent User agent to use. + */ + public void setUserAgent(Network.UserAgent userAgent) { + logger.info("loc.browser.network.useragent.set", userAgent); + network.setUserAgent(userAgent); + } + + /** + * Add basic authentication handler. + * @param useTheseCredentials parameters, such as URI matcher and credentials. + */ + public void addAuthHandler(Predicate whenThisMatches, Supplier useTheseCredentials) { + logger.info("loc.browser.network.authentication.add"); + network.addAuthHandler(whenThisMatches, useTheseCredentials); + } + + /** + * Add basic authentication handler. + * @param hostPart part of the host name for URI matcher. + * @param username authentication username. + * @param password authentication password. + */ + public void addBasicAuthentication(String hostPart, String username, String password) { + Predicate uriPredicate = uri -> uri.getHost().contains(hostPart); + addAuthHandler(uriPredicate, UsernameAndPassword.of(username, password)); + } + + /** + * Clears basic authentication handler. + */ + public void clearBasicAuthentication() { + logger.info("loc.browser.network.authentication.clear"); + stopMonitoring(); + } + + /** + * Clears the most recent network filter. + */ + public void resetNetworkFilter() { + logger.info("loc.browser.network.filter.clear"); + network.resetNetworkFilter(); + } + + /** + * Starts traffic interception with specified filter. + * @param filter HTTP filter. + */ + public void interceptTrafficWith(Filter filter) { + logger.info("loc.browser.network.filter.set"); + network.interceptTrafficWith(filter); + } + + /** + * Adds listener to network request sent event. + * @param listener a listener to add. + */ + public void addRequestListener(Consumer listener) { + logger.info("loc.browser.network.event.requestsent.add"); + tools.sendCommand(enable(Optional.empty(), Optional.empty(), Optional.empty())); + tools.addListener(requestWillBeSent(), listener); + } + + /** + * Adds listener to network response received event. + * @param listener a listener to add. + */ + public void addResponseListener(Consumer listener) { + logger.info("loc.browser.network.event.responsereceived.add"); + tools.sendCommand(enable(Optional.empty(), Optional.empty(), Optional.empty())); + tools.addListener(responseReceived(), listener); + } + + /** + * Clears network event listeners. + */ + public void clearListeners() { + tools.clearListeners(); + } + + /** + * Enables HTTP Request/Response logging with default {@link HttpExchangeLoggingOptions}. + */ + public void enableHttpExchangeLogging() { + enableHttpExchangeLogging(new HttpExchangeLoggingOptions()); + } + + private String formatHeaders(Headers headers) { + List formattedHeaders = new ArrayList<>(); + headers.forEach((key, value) -> formattedHeaders.add(String.format("%s\t%s: %s", System.lineSeparator(), key, value))); + return String.join(",", formattedHeaders); + } + + private Consumer getRequestLogger(HttpExchangeLoggingOptions loggingOptions) { + return requestWillBeSent -> { + Request request = requestWillBeSent.getRequest(); + if (loggingOptions.getRequestInfo().isEnabled()) { + logByLevel(loggingOptions.getRequestInfo().getLogLevel(), + "loc.browser.network.event.requestsent.log.info", + request.getMethod(), request.getUrl() + request.getUrlFragment().orElse(""), requestWillBeSent.getRequestId()); + } + if (loggingOptions.getRequestHeaders().isEnabled() && !request.getHeaders().isEmpty()) { + logByLevel(loggingOptions.getRequestHeaders().getLogLevel(), + "loc.browser.network.event.requestsent.log.headers", + formatHeaders(request.getHeaders())); + } + if (loggingOptions.getRequestPostData().isEnabled() && request.getHasPostData().orElse(false)) { + logByLevel(loggingOptions.getRequestPostData().getLogLevel(), + "loc.browser.network.event.requestsent.log.data", + request.getPostData().orElse(null)); + } + }; + } + + private Consumer getResponseLogger(HttpExchangeLoggingOptions loggingOptions) { + return responseReceived -> { + Response response = responseReceived.getResponse(); + RequestId requestId = responseReceived.getRequestId(); + if (loggingOptions.getResponseInfo().isEnabled()) { + logByLevel(loggingOptions.getResponseInfo().getLogLevel(), + "loc.browser.network.event.responsereceived.log.info", + response.getStatus(), response.getUrl(), responseReceived.getType().toString(), requestId); + } + if (loggingOptions.getResponseHeaders().isEnabled() && !response.getHeaders().isEmpty()) { + logByLevel(loggingOptions.getResponseHeaders().getLogLevel(), + "loc.browser.network.event.responsereceived.log.headers", + formatHeaders(response.getHeaders())); + } + if (loggingOptions.getResponseBody().isEnabled()) { + String responseBody = tools.sendCommand(org.openqa.selenium.devtools.v85.network.Network.getResponseBody(requestId)).getBody(); + if (StringUtils.isNotEmpty(responseBody)) { + logByLevel(loggingOptions.getResponseBody().getLogLevel(), + "loc.browser.network.event.responsereceived.log.body", + responseBody); + } + } + }; + } + + /** + * Enables HTTP Request/Response logging. + * @param loggingOptions logging parameters {@link HttpExchangeLoggingOptions}. + */ + public void enableHttpExchangeLogging(HttpExchangeLoggingOptions loggingOptions) { + addRequestListener(getRequestLogger(loggingOptions)); + addResponseListener(getResponseLogger(loggingOptions)); + } + + /** + * Starts network interceptor. + * @param httpHandler HTTP handler. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor startNetworkInterceptor(HttpHandler httpHandler) { + logger.info(LOC_NETWORK_INTERCEPTOR_START); + return new NetworkInterceptor(getBrowser().getDriver(), httpHandler); + } + + /** + * Starts network interceptor. + * @param filter network filter. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor startNetworkInterceptor(Filter filter) { + logger.info(LOC_NETWORK_INTERCEPTOR_START); + return new NetworkInterceptor(getBrowser().getDriver(), filter); + } + + /** + * Starts network interceptor. + * @param routable a filter with matcher. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor startNetworkInterceptor(Routable routable) { + logger.info(LOC_NETWORK_INTERCEPTOR_START); + return new NetworkInterceptor(getBrowser().getDriver(), routable); + } + + /** + * Starts network interceptor. + * @param requestMatcher predicate to match the request. + * @param handler handler for matched requests. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor startNetworkInterceptor(Predicate requestMatcher, Supplier handler) { + return startNetworkInterceptor(Route.matching(requestMatcher).to(handler)); + } + + /** + * Intercepts any request with predefined response. + * @param response HTTP response. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor interceptAllRequests(HttpResponse response) { + return startNetworkInterceptor(req -> true, () -> req -> response); + } + + /** + * Adds request transformer. + * @param requestMatcher predicate to match the request. + * @param requestTransformer function to transform the request. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor addRequestTransformer(Predicate requestMatcher, Function requestTransformer) { + return startNetworkInterceptor((Filter) next -> req -> + requestMatcher.test(req) ? next.execute(requestTransformer.apply(req)) : next.execute(req)); + } + + /** + * Adds request handler. + * @param requestMatcher predicate to match the request. + * @param requestHandler handler for matched requests. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor addRequestHandler(Predicate requestMatcher, Function requestHandler) { + return startNetworkInterceptor( + Route.matching(requestMatcher).to(() -> requestHandler::apply)); + } + + /** + * Adds response handler. + * @param responseMatcher predicate to match the response. + * @param responseTransformer function to transform the response. + * @return an instance of {@link NetworkInterceptor}. + */ + public NetworkInterceptor addResponseHandler(Predicate responseMatcher, Function responseTransformer) { + return startNetworkInterceptor( + (Filter) next -> req -> { + HttpResponse response = next.execute(req); + return responseMatcher.test(response) ? responseTransformer.apply(response) : response; + }); + } + + /** + * Clears the latest network interceptor. + * Currently, Selenium supports only a single network interceptor. Any new Network interceptor will override the previous one. + * And on {@link NetworkInterceptor#close()} the NetworkInterceptor class just calls the {@link Network#resetNetworkFilter()}, so it's enough to call it. + * If multiple network interceptors at the same time would be allowed, we may want to store all + * {@link NetworkInterceptor} classes to call {@link NetworkInterceptor::close()} for each. + */ + public void clearNetworkInterceptor() { + resetNetworkFilter(); + } +} diff --git a/src/main/java/aquality/selenium/configuration/BrowserProfile.java b/src/main/java/aquality/selenium/configuration/BrowserProfile.java index 80f6919..0dc583a 100644 --- a/src/main/java/aquality/selenium/configuration/BrowserProfile.java +++ b/src/main/java/aquality/selenium/configuration/BrowserProfile.java @@ -49,9 +49,15 @@ public IDriverSettings getDriverSettings() { case IEXPLORER: driverSettings = new IExplorerSettings(settingsFile); break; + case OPERA: + driverSettings = new OperaSettings(settingsFile); + break; case SAFARI: driverSettings = new SafariSettings(settingsFile); break; + case YANDEX: + driverSettings = new YandexSettings(settingsFile); + break; default: throw new IllegalArgumentException("There are no assigned behaviour for retrieving driver driversettings for browser " + getBrowserName()); } diff --git a/src/main/java/aquality/selenium/configuration/driversettings/ChromeSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/ChromeSettings.java index 57db97e..d71708b 100644 --- a/src/main/java/aquality/selenium/configuration/driversettings/ChromeSettings.java +++ b/src/main/java/aquality/selenium/configuration/driversettings/ChromeSettings.java @@ -3,6 +3,7 @@ import aquality.selenium.browser.BrowserName; import aquality.selenium.core.utilities.ISettingsFile; import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.AbstractDriverOptions; import java.util.HashMap; import java.util.Map; @@ -14,7 +15,7 @@ public ChromeSettings(ISettingsFile settingsFile){ } @Override - public ChromeOptions getCapabilities() { + public AbstractDriverOptions getDriverOptions() { ChromeOptions chromeOptions = new ChromeOptions(); setChromePrefs(chromeOptions); setCapabilities(chromeOptions); diff --git a/src/main/java/aquality/selenium/configuration/driversettings/DriverSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/DriverSettings.java index e72fdce..102238a 100644 --- a/src/main/java/aquality/selenium/configuration/driversettings/DriverSettings.java +++ b/src/main/java/aquality/selenium/configuration/driversettings/DriverSettings.java @@ -5,7 +5,7 @@ import aquality.selenium.core.logging.Logger; import aquality.selenium.core.utilities.ISettingsFile; import io.github.bonigarcia.wdm.config.Architecture; -import org.apache.commons.lang.StringUtils; +import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.PageLoadStrategy; import java.io.File; @@ -69,6 +69,15 @@ protected List getBrowserStartArguments() { return startArguments; } + protected String getBinaryLocation(String defaultBinaryLocation) { + String value = (String) getSettingsFile().getValueOrDefault(getDriverSettingsPath("binaryLocation"), defaultBinaryLocation); + int varStartIndex = value.indexOf('%'); + int varEndIndex = value.lastIndexOf('%'); + return varStartIndex == 0 && varStartIndex != varEndIndex + ? System.getenv(value.substring(varStartIndex + 1, varEndIndex)) + value.substring(varEndIndex + 1) + : getAbsolutePath(value); + } + @SafeVarargs private final void logCollection(String messageKey, final T... elements) { if (elements.length == 1 && @@ -129,7 +138,7 @@ public String getDownloadDir() { private enum CapabilityType { CAPABILITIES("capabilities"), OPTIONS("options"), START_ARGS("startArguments"); - private String key; + private final String key; CapabilityType(String key) { this.key = key; @@ -140,7 +149,7 @@ public String getKey() { } } - private String getAbsolutePath(String path) { + protected String getAbsolutePath(String path) { try { return new File(path).getCanonicalPath(); } catch (IOException e) { diff --git a/src/main/java/aquality/selenium/configuration/driversettings/EdgeSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/EdgeSettings.java index 967b03a..33ec50c 100644 --- a/src/main/java/aquality/selenium/configuration/driversettings/EdgeSettings.java +++ b/src/main/java/aquality/selenium/configuration/driversettings/EdgeSettings.java @@ -2,7 +2,12 @@ import aquality.selenium.browser.BrowserName; import aquality.selenium.core.utilities.ISettingsFile; +import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.edge.EdgeOptions; +import org.openqa.selenium.remote.AbstractDriverOptions; + +import java.util.HashMap; +import java.util.Map; public class EdgeSettings extends DriverSettings { @@ -11,16 +16,31 @@ public EdgeSettings(ISettingsFile settingsFile){ } @Override - public EdgeOptions getCapabilities() { + public AbstractDriverOptions getDriverOptions() { EdgeOptions edgeOptions = new EdgeOptions(); setCapabilities(edgeOptions); + setPrefs(edgeOptions); + getBrowserStartArguments().forEach(edgeOptions::addArguments); edgeOptions.setPageLoadStrategy(getPageLoadStrategy()); return edgeOptions; } @Override public String getDownloadDirCapabilityKey() { - throw new IllegalArgumentException("Download directory for EDGE profiles is not supported"); + return "download.default_directory"; + } + + private void setPrefs(EdgeOptions options){ + HashMap prefs = new HashMap<>(); + Map configOptions = getBrowserOptions(); + configOptions.forEach((key, value) -> { + if (key.equals(getDownloadDirCapabilityKey())) { + prefs.put(key, getDownloadDir()); + } else { + prefs.put(key, value); + } + }); + options.setExperimentalOption("prefs", prefs); } @Override diff --git a/src/main/java/aquality/selenium/configuration/driversettings/FirefoxSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/FirefoxSettings.java index ed757a0..55ff88f 100644 --- a/src/main/java/aquality/selenium/configuration/driversettings/FirefoxSettings.java +++ b/src/main/java/aquality/selenium/configuration/driversettings/FirefoxSettings.java @@ -3,6 +3,7 @@ import aquality.selenium.browser.BrowserName; import aquality.selenium.core.utilities.ISettingsFile; import org.openqa.selenium.firefox.FirefoxOptions; +import org.openqa.selenium.remote.AbstractDriverOptions; import java.util.Map; @@ -13,7 +14,7 @@ public FirefoxSettings(ISettingsFile settingsFile){ } @Override - public FirefoxOptions getCapabilities() { + public AbstractDriverOptions getDriverOptions() { FirefoxOptions firefoxOptions = new FirefoxOptions(); setCapabilities(firefoxOptions); setFirefoxPrefs(firefoxOptions); diff --git a/src/main/java/aquality/selenium/configuration/driversettings/IDriverSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/IDriverSettings.java index 3a63184..912d4b8 100644 --- a/src/main/java/aquality/selenium/configuration/driversettings/IDriverSettings.java +++ b/src/main/java/aquality/selenium/configuration/driversettings/IDriverSettings.java @@ -4,6 +4,7 @@ import io.github.bonigarcia.wdm.config.Architecture; import org.openqa.selenium.Capabilities; import org.openqa.selenium.PageLoadStrategy; +import org.openqa.selenium.remote.AbstractDriverOptions; /** * Describes web driver settings. @@ -14,7 +15,7 @@ public interface IDriverSettings { * Gets web driver capabilities. * @return initialized {@link Capabilities} */ - Capabilities getCapabilities(); + AbstractDriverOptions getDriverOptions(); /** * Gets WebDriver page load strategy. diff --git a/src/main/java/aquality/selenium/configuration/driversettings/IExplorerSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/IExplorerSettings.java index 065b6cf..9269c80 100644 --- a/src/main/java/aquality/selenium/configuration/driversettings/IExplorerSettings.java +++ b/src/main/java/aquality/selenium/configuration/driversettings/IExplorerSettings.java @@ -3,6 +3,7 @@ import aquality.selenium.browser.BrowserName; import aquality.selenium.core.utilities.ISettingsFile; import org.openqa.selenium.ie.InternetExplorerOptions; +import org.openqa.selenium.remote.AbstractDriverOptions; public class IExplorerSettings extends DriverSettings { @@ -11,7 +12,7 @@ public IExplorerSettings(ISettingsFile settingsFile){ } @Override - public InternetExplorerOptions getCapabilities() { + public AbstractDriverOptions getDriverOptions() { InternetExplorerOptions internetExplorerOptions = new InternetExplorerOptions(); setCapabilities(internetExplorerOptions); internetExplorerOptions.setPageLoadStrategy(getPageLoadStrategy()); diff --git a/src/main/java/aquality/selenium/configuration/driversettings/OperaSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/OperaSettings.java new file mode 100644 index 0000000..30ca499 --- /dev/null +++ b/src/main/java/aquality/selenium/configuration/driversettings/OperaSettings.java @@ -0,0 +1,27 @@ +package aquality.selenium.configuration.driversettings; + +import aquality.selenium.browser.BrowserName; +import aquality.selenium.core.utilities.ISettingsFile; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.AbstractDriverOptions; + +public class OperaSettings extends ChromeSettings { + private static final String DEFAULT_BINARY_LOCATION = "%USERPROFILE%\\AppData\\Local\\Programs\\Opera\\launcher.exe"; + + public OperaSettings(ISettingsFile settingsFile) { + super(settingsFile); + } + + @Override + public BrowserName getBrowserName() { + return BrowserName.OPERA; + } + + @Override + public AbstractDriverOptions getDriverOptions() { + ChromeOptions options = (ChromeOptions) super.getDriverOptions(); + options.setExperimentalOption("w3c", true); + options.setBinary(getBinaryLocation(DEFAULT_BINARY_LOCATION)); + return options; + } +} diff --git a/src/main/java/aquality/selenium/configuration/driversettings/SafariSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/SafariSettings.java index c87aec1..3acd382 100644 --- a/src/main/java/aquality/selenium/configuration/driversettings/SafariSettings.java +++ b/src/main/java/aquality/selenium/configuration/driversettings/SafariSettings.java @@ -2,6 +2,7 @@ import aquality.selenium.browser.BrowserName; import aquality.selenium.core.utilities.ISettingsFile; +import org.openqa.selenium.remote.AbstractDriverOptions; import org.openqa.selenium.safari.SafariOptions; public class SafariSettings extends DriverSettings { @@ -11,7 +12,7 @@ public SafariSettings(ISettingsFile settingsFile) { } @Override - public SafariOptions getCapabilities() { + public AbstractDriverOptions getDriverOptions() { SafariOptions safariOptions = new SafariOptions(); setCapabilities(safariOptions); return safariOptions; diff --git a/src/main/java/aquality/selenium/configuration/driversettings/YandexSettings.java b/src/main/java/aquality/selenium/configuration/driversettings/YandexSettings.java new file mode 100644 index 0000000..7fb48b0 --- /dev/null +++ b/src/main/java/aquality/selenium/configuration/driversettings/YandexSettings.java @@ -0,0 +1,25 @@ +package aquality.selenium.configuration.driversettings; + +import aquality.selenium.browser.BrowserName; +import aquality.selenium.core.utilities.ISettingsFile; +import org.openqa.selenium.chrome.ChromeOptions; +import org.openqa.selenium.remote.AbstractDriverOptions; + +public class YandexSettings extends ChromeSettings { + private static final String DEFAULT_BINARY_LOCATION = "%USERPROFILE%\\AppData\\Local\\Yandex\\YandexBrowser\\Application\\browser.exe"; + public YandexSettings(ISettingsFile settingsFile) { + super(settingsFile); + } + + @Override + public AbstractDriverOptions getDriverOptions() { + ChromeOptions options = (ChromeOptions) super.getDriverOptions(); + options.setBinary(getBinaryLocation(DEFAULT_BINARY_LOCATION)); + return options; + } + + @Override + public BrowserName getBrowserName() { + return BrowserName.YANDEX; + } +} diff --git a/src/main/java/aquality/selenium/elements/Element.java b/src/main/java/aquality/selenium/elements/Element.java index bd85f5e..ea58d39 100644 --- a/src/main/java/aquality/selenium/elements/Element.java +++ b/src/main/java/aquality/selenium/elements/Element.java @@ -20,6 +20,7 @@ import org.openqa.selenium.By; import org.openqa.selenium.Keys; import org.openqa.selenium.NoSuchElementException; +import org.openqa.selenium.SearchContext; import org.openqa.selenium.remote.RemoteWebElement; import java.time.Duration; @@ -28,6 +29,8 @@ * Abstract class, describing wrapper of WebElement. */ public abstract class Element extends aquality.selenium.core.elements.Element implements IElement { + private IElementFinder elementFinder; + /** * The main constructor * @@ -51,7 +54,14 @@ protected IElementFactory getElementFactory() { @Override protected IElementFinder getElementFinder() { - return AqualityServices.get(IElementFinder.class); + if (elementFinder == null) { + elementFinder = AqualityServices.get(IElementFinder.class); + } + return elementFinder; + } + + void setElementFinder(IElementFinder elementFinder) { + this.elementFinder = elementFinder; } @Override @@ -176,4 +186,10 @@ public void sendKeys(Keys key) { logElementAction("loc.text.sending.key", Keys.class.getSimpleName().concat(".").concat(key.name())); doWithRetry(() -> getElement().sendKeys(key)); } + + @Override + public SearchContext expandShadowRoot() { + logElementAction("loc.shadowroot.expand"); + return getElement().getShadowRoot(); + } } diff --git a/src/main/java/aquality/selenium/elements/ElementFactory.java b/src/main/java/aquality/selenium/elements/ElementFactory.java index e22cf52..f63d5ec 100644 --- a/src/main/java/aquality/selenium/elements/ElementFactory.java +++ b/src/main/java/aquality/selenium/elements/ElementFactory.java @@ -3,6 +3,7 @@ import aquality.selenium.browser.AqualityServices; import aquality.selenium.browser.JavaScript; import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.elements.interfaces.IElementSupplier; import aquality.selenium.core.localization.ILocalizationManager; import aquality.selenium.core.waitings.IConditionalWait; import aquality.selenium.elements.interfaces.*; @@ -19,9 +20,12 @@ public class ElementFactory extends aquality.selenium.core.elements.ElementFactory implements IElementFactory { + private final IElementFinder elementFinder; + @Inject public ElementFactory(IConditionalWait conditionalWait, IElementFinder elementFinder, ILocalizationManager localizationManager) { super(conditionalWait, elementFinder, localizationManager); + this.elementFinder = elementFinder; } private static Map, String> getLocatorToXPathTemplateMap() { @@ -92,4 +96,16 @@ protected String extractXPathFromLocator(By locator) { ? String.format(getLocatorToXPathTemplateMap().get(locatorClass), locValuableString) : super.extractXPathFromLocator(locator); } + + @Override + protected IElementSupplier getDefaultElementSupplier(Class clazz) { + IElementSupplier baseSupplier = super.getDefaultElementSupplier(clazz); + return (locator, name, state) -> { + T element = baseSupplier.get(locator, name, state); + if (element instanceof Element) { + ((Element)element).setElementFinder(elementFinder); + } + return element; + }; + } } diff --git a/src/main/java/aquality/selenium/elements/actions/CheckBoxJsActions.java b/src/main/java/aquality/selenium/elements/actions/CheckBoxJsActions.java index 507989e..6b612cf 100644 --- a/src/main/java/aquality/selenium/elements/actions/CheckBoxJsActions.java +++ b/src/main/java/aquality/selenium/elements/actions/CheckBoxJsActions.java @@ -60,6 +60,6 @@ private void setState(boolean state) { * @return state of checkbox using .checked property of element */ private boolean getState() { - return Boolean.valueOf(executeScript(JavaScript.GET_CHECKBOX_STATE, element).toString()); + return Boolean.parseBoolean(executeScript(JavaScript.GET_CHECKBOX_STATE).toString()); } } \ No newline at end of file diff --git a/src/main/java/aquality/selenium/elements/actions/ComboBoxJsActions.java b/src/main/java/aquality/selenium/elements/actions/ComboBoxJsActions.java index 7b1d1b2..5558476 100644 --- a/src/main/java/aquality/selenium/elements/actions/ComboBoxJsActions.java +++ b/src/main/java/aquality/selenium/elements/actions/ComboBoxJsActions.java @@ -17,9 +17,10 @@ public ComboBoxJsActions(IComboBox comboBox, String elementType) { * * @return texts of options from ComboBox */ + @SuppressWarnings("unchecked") public List getTexts() { logElementAction("loc.combobox.get.texts.js"); - List values = (List) executeScript(JavaScript.GET_COMBOBOX_TEXTS, element); + List values = (List) executeScript(JavaScript.GET_COMBOBOX_TEXTS); logElementAction("loc.combobox.texts", values.stream().map(value -> String.format("'%s'", value)).collect(Collectors.joining(", "))); return values; @@ -32,7 +33,7 @@ public List getTexts() { */ public String getSelectedText() { logElementAction("loc.combobox.get.text.js"); - String text = (String) executeScript(JavaScript.GET_COMBOBOX_SELECTED_TEXT, element); + String text = (String) executeScript(JavaScript.GET_COMBOBOX_SELECTED_TEXT); logElementAction("loc.combobox.selected.text", text); return text; } @@ -44,6 +45,6 @@ public String getSelectedText() { */ public void selectValueByText(final String text) { logElementAction("loc.combobox.select.by.text.js", text); - executeScript(JavaScript.SELECT_COMBOBOX_VALUE_BY_TEXT, element, text); + executeScript(JavaScript.SELECT_COMBOBOX_VALUE_BY_TEXT, text); } } \ No newline at end of file diff --git a/src/main/java/aquality/selenium/elements/actions/JsActions.java b/src/main/java/aquality/selenium/elements/actions/JsActions.java index 675ab53..69f662e 100644 --- a/src/main/java/aquality/selenium/elements/actions/JsActions.java +++ b/src/main/java/aquality/selenium/elements/actions/JsActions.java @@ -6,12 +6,16 @@ import aquality.selenium.core.utilities.IElementActionRetrier; import aquality.selenium.elements.HighlightState; import aquality.selenium.elements.interfaces.IElement; +import aquality.selenium.elements.interfaces.IShadowRootExpander; import org.openqa.selenium.Point; +import org.openqa.selenium.ScriptKey; +import org.openqa.selenium.SearchContext; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; -public class JsActions { +public class JsActions implements IShadowRootExpander { protected IElement element; protected String type; @@ -23,13 +27,29 @@ public JsActions(IElement element, String type) { this.name = element.getName(); } + @Override + public SearchContext expandShadowRoot() { + logElementAction("loc.shadowroot.expand.js"); + return (SearchContext) executeScript(JavaScript.EXPAND_SHADOW_ROOT); + } + + /** + * Setting attribute value. + * @param name Attribute name + * @param value Value to set + */ + public void setAttribute(String name, String value) { + logElementAction("loc.el.attr.set", name, value); + executeScript(JavaScript.SET_ATTRIBUTE, name, value); + } + /** * Click via JS. */ public void click() { logElementAction("loc.clicking.js"); highlightElement(); - executeScript(JavaScript.CLICK_ELEMENT, element); + executeScript(JavaScript.CLICK_ELEMENT); } /** @@ -52,7 +72,7 @@ public void highlightElement() { */ public void highlightElement(HighlightState highlightState) { if (AqualityServices.getBrowserProfile().isElementHighlightEnabled() || highlightState.equals(HighlightState.HIGHLIGHT)) { - executeScript(JavaScript.BORDER_ELEMENT, element); + executeScript(JavaScript.BORDER_ELEMENT); } } @@ -61,7 +81,7 @@ public void highlightElement(HighlightState highlightState) { */ public void scrollIntoView() { logElementAction("loc.scrolling.js"); - executeScript(JavaScript.SCROLL_TO_ELEMENT, element, true); + executeScript(JavaScript.SCROLL_TO_ELEMENT, true); } /** @@ -72,7 +92,7 @@ public void scrollIntoView() { */ public void scrollBy(int x, int y) { logElementAction("loc.scrolling.js"); - executeScript(JavaScript.SCROLL_BY, element, x, y); + executeScript(JavaScript.SCROLL_BY, x, y); } /** @@ -80,7 +100,7 @@ public void scrollBy(int x, int y) { */ public void scrollToTheCenter() { logElementAction("loc.scrolling.center.js"); - executeScript(JavaScript.SCROLL_TO_ELEMENT_CENTER, element); + executeScript(JavaScript.SCROLL_TO_ELEMENT_CENTER); } /** @@ -90,7 +110,7 @@ public void scrollToTheCenter() { */ public void setValue(final String value) { logElementAction("loc.setting.value", value); - executeScript(JavaScript.SET_VALUE, element, value); + executeScript(JavaScript.SET_VALUE, value); } /** @@ -98,7 +118,7 @@ public void setValue(final String value) { */ public void setFocus() { logElementAction("loc.focusing"); - executeScript(JavaScript.SET_FOCUS, element); + executeScript(JavaScript.SET_FOCUS); } /** @@ -108,7 +128,7 @@ public void setFocus() { */ public boolean isElementOnScreen() { logElementAction("loc.is.present.js"); - boolean value = (boolean) executeScript(JavaScript.ELEMENT_IS_ON_SCREEN, element); + boolean value = (boolean) executeScript(JavaScript.ELEMENT_IS_ON_SCREEN); logElementAction("loc.is.present.value", value); return value; } @@ -120,7 +140,7 @@ public boolean isElementOnScreen() { */ public String getElementText() { logElementAction("loc.get.text.js"); - return (String) executeScript(JavaScript.GET_ELEMENT_TEXT, element); + return (String) executeScript(JavaScript.GET_ELEMENT_TEXT); } /** @@ -128,7 +148,7 @@ public String getElementText() { */ public void hoverMouse() { logElementAction("loc.hover.js"); - executeScript(JavaScript.MOUSE_HOVER, element); + executeScript(JavaScript.MOUSE_HOVER); } /** @@ -138,7 +158,7 @@ public void hoverMouse() { */ @SuppressWarnings("unchecked") public Point getViewPortCoordinates() { - List coordinates = (ArrayList) executeScript(JavaScript.GET_VIEWPORT_COORDINATES, element); + List coordinates = (ArrayList) executeScript(JavaScript.GET_VIEWPORT_COORDINATES); return new Point(Math.round(coordinates.get(0).floatValue()), Math.round(coordinates.get(1).floatValue())); } @@ -149,17 +169,38 @@ public Point getViewPortCoordinates() { */ public String getXPath() { logElementAction("loc.get.xpath.js"); - String value = (String) executeScript(JavaScript.GET_ELEMENT_XPATH, element); + String value = (String) executeScript(JavaScript.GET_ELEMENT_XPATH); logElementAction("loc.xpath.value", value); return value; } - protected Object executeScript(JavaScript javaScript, IElement element) { + private Object[] resolveArguments(Object... args) { + List arguments = new ArrayList<>(); + arguments.add(element.getElement()); + arguments.addAll(Arrays.asList(args)); + return arguments.toArray(); + } + + /** + * Executes pinned JavaScript against the element and gets result value. + * @param pinnedScript Instance of script pinned with {@link Browser#javaScriptEngine()} + * @return Script execution result. + */ + public Object executeScript(ScriptKey pinnedScript, Object... args) { + logElementAction("loc.el.execute.pinnedjs"); + Object result = getActionRetrier().doWithRetry(() -> getBrowser().executeScript(pinnedScript, resolveArguments(args))); + if (result != null) { + logElementAction("loc.el.execute.pinnedjs.result", result); + } + return result; + } + + protected Object executeScript(JavaScript javaScript) { return getActionRetrier().doWithRetry(() -> getBrowser().executeScript(javaScript, element.getElement())); } - protected Object executeScript(JavaScript javaScript, IElement element, Object... args) { - return getActionRetrier().doWithRetry(() -> getBrowser().executeScript(javaScript, element.getElement(), args)); + protected Object executeScript(JavaScript javaScript, Object... args) { + return getActionRetrier().doWithRetry(() -> getBrowser().executeScript(javaScript, resolveArguments(args))); } /** diff --git a/src/main/java/aquality/selenium/elements/actions/MouseActions.java b/src/main/java/aquality/selenium/elements/actions/MouseActions.java index 9ea0d7f..6f399d8 100644 --- a/src/main/java/aquality/selenium/elements/actions/MouseActions.java +++ b/src/main/java/aquality/selenium/elements/actions/MouseActions.java @@ -8,12 +8,12 @@ import java.util.function.UnaryOperator; -public class MouseActions { - - private IElement element; - private String type; - private String name; +import static aquality.selenium.browser.AqualityServices.getBrowser; +public class MouseActions { + private final IElement element; + private final String type; + private final String name; public MouseActions(IElement element, String type) { this.element = element; @@ -56,12 +56,10 @@ public void moveMouseFromElement() { } /** - * Double click on the item. Waiting for the end of renderiga + * Performs double-click on the element. */ public void doubleClick() { infoLoc("loc.clicking.double"); - AqualityServices.get(IElementActionRetrier.class).doWithRetry( - () -> (getBrowser().getDriver()).getMouse().mouseMove(element.getElement().getCoordinates())); performAction(actions -> actions.doubleClick(element.getElement())); } @@ -79,8 +77,4 @@ private void performAction(UnaryOperator function) { private void infoLoc(String key) { AqualityServices.getLocalizedLogger().infoElementAction(type, name, key); } - - private Browser getBrowser() { - return AqualityServices.getBrowser(); - } } diff --git a/src/main/java/aquality/selenium/elements/interfaces/IElement.java b/src/main/java/aquality/selenium/elements/interfaces/IElement.java index 7047874..d0fec8e 100644 --- a/src/main/java/aquality/selenium/elements/interfaces/IElement.java +++ b/src/main/java/aquality/selenium/elements/interfaces/IElement.java @@ -11,7 +11,7 @@ import java.util.List; -public interface IElement extends aquality.selenium.core.elements.interfaces.IElement { +public interface IElement extends aquality.selenium.core.elements.interfaces.IElement, IShadowRootExpander { /** * Send keys. diff --git a/src/main/java/aquality/selenium/elements/interfaces/IShadowRootExpander.java b/src/main/java/aquality/selenium/elements/interfaces/IShadowRootExpander.java new file mode 100644 index 0000000..db87ab2 --- /dev/null +++ b/src/main/java/aquality/selenium/elements/interfaces/IShadowRootExpander.java @@ -0,0 +1,347 @@ +package aquality.selenium.elements.interfaces; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.core.elements.ElementState; +import aquality.selenium.core.elements.ElementsCount; +import aquality.selenium.core.elements.RelativeElementFinder; +import aquality.selenium.core.elements.interfaces.IElementFinder; +import aquality.selenium.core.elements.interfaces.IElementSupplier; +import aquality.selenium.core.localization.ILocalizationManager; +import aquality.selenium.elements.ElementFactory; +import org.openqa.selenium.By; +import org.openqa.selenium.SearchContext; + +import java.util.List; + +import static aquality.selenium.browser.AqualityServices.getConditionalWait; +import static aquality.selenium.browser.AqualityServices.getLocalizedLogger; + +public interface IShadowRootExpander { + /** + * Expands shadow root. + * @return ShadowRoot search context. + */ + SearchContext expandShadowRoot(); + + /** + * Provides {@link IElementFactory} to find elements in the shadow root of the current element. + * @return instance of ElementFactory for the shadow root. + */ + default IElementFactory getShadowRootElementFactory() { + IElementFinder elementFinder = new RelativeElementFinder(getLocalizedLogger(), getConditionalWait(), this::expandShadowRoot); + return new ElementFactory(getConditionalWait(), elementFinder, AqualityServices.get(ILocalizationManager.class)); + } + + /** + * Finds an element in the shadow root of the current element. + * + * @param locator shadowed element locator + * @param name output name in logs + * @param clazz class or interface of the element to be obtained + * @param state visibility state of target element + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, String name, Class clazz, ElementState state) { + return getShadowRootElementFactory().getCustomElement(clazz, locator, name, state); + } + + /** + * Finds an element in the shadow root of the current element with DISPLAYED state + * + * @param locator shadowed element locator + * @param name output name in logs + * @param clazz class or interface of the element to be obtained + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, String name, Class clazz) { + return findElementInShadowRoot(locator, name, clazz, ElementState.DISPLAYED); + } + + /** + * Finds an element in the shadow root of the current element. + * + * @param locator shadowed element locator + * @param clazz class or interface of the element to be obtained + * @param state visibility state of target element + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, Class clazz, ElementState state) { + return findElementInShadowRoot(locator, null, clazz, state); + } + + /** + * Finds an element in the shadow root of the current element with DISPLAYED state + * + * @param locator shadowed element locator + * @param clazz class or interface of the element to be obtained + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, Class clazz) { + return findElementInShadowRoot(locator, null, clazz, ElementState.DISPLAYED); + } + + /** + * Finds an element in the shadow root of the current element. + * + * @param locator Child element locator + * @param name output name in logs + * @param supplier required element's supplier + * @param state visibility state of target element + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, String name, IElementSupplier supplier, ElementState state) { + return getShadowRootElementFactory().getCustomElement(supplier, locator, name, state); + } + + /** + * Finds an element in the shadow root of the current element with DISPLAYED state + * + * @param locator shadowed element locator + * @param name output name in logs + * @param supplier required element's supplier + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, String name, IElementSupplier supplier) { + return findElementInShadowRoot(locator, name, supplier, ElementState.DISPLAYED); + } + + /** + * Finds an element in the shadow root of the current element. + * + * @param locator shadowed element locator + * @param supplier required element's supplier + * @param state visibility state of target element + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, IElementSupplier supplier, ElementState state) { + return findElementInShadowRoot(locator, null, supplier, state); + } + + /** + * Finds an element in the shadow root of the current element with DISPLAYED state + * + * @param locator shadowed element locator + * @param supplier required element's supplier + * @param the type of the element to be obtained + * @return found shadowed element + */ + default T findElementInShadowRoot(By locator, IElementSupplier supplier) { + return findElementInShadowRoot(locator, null, supplier, ElementState.DISPLAYED); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param clazz Class or interface of the elements to be obtained. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, Class clazz) { + return findElementsInShadowRoot(locator, clazz, ElementsCount.ANY); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param clazz Class or interface of the elements to be obtained. + * @param count Expected number of elements that have to be found (zero, more than zero, any). + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, Class clazz, ElementsCount count) { + return findElementsInShadowRoot(locator, clazz, ElementState.DISPLAYED, count); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param clazz Class or interface of the elements to be obtained. + * @param state Visibility state of shadowed elements. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, Class clazz, ElementState state) { + return findElementsInShadowRoot(locator, clazz, state, ElementsCount.ANY); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param clazz Class or interface of the elements to be obtained. + * @param state Visibility state of shadowed elements. + * @param count Expected number of elements that have to be found (zero, more than zero, any). + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, Class clazz, ElementState state, + ElementsCount count) { + return findElementsInShadowRoot(locator, null, clazz, state, count); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param clazz Class or interface of the elements to be obtained. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, Class clazz) { + return findElementsInShadowRoot(locator, name, clazz, ElementsCount.ANY); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param clazz Class or interface of the elements to be obtained. + * @param count Expected number of elements that have to be found (zero, more than zero, any). + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, Class clazz, ElementsCount count) { + return findElementsInShadowRoot(locator, name, clazz, ElementState.DISPLAYED, count); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param clazz Class or interface of the elements to be obtained. + * @param state Visibility state of shadowed elements. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, Class clazz, ElementState state) { + return findElementsInShadowRoot(locator, name, clazz, state, ElementsCount.ANY); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param Type of the target elements. + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param clazz Class or interface of the elements to be obtained. + * @param state Visibility state of target elements. + * @param count Expected number of elements that have to be found (zero, more than zero, any). + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, Class clazz, ElementState state, + ElementsCount count) { + return getShadowRootElementFactory().findElements(locator, name, clazz, count, state); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param supplier Required elements' supplier. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, IElementSupplier supplier) { + return findElementsInShadowRoot(locator, supplier, ElementsCount.ANY); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param supplier Required elements' supplier. + * @param count Expected number of elements that have to be found (zero, more than zero, any). + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, IElementSupplier supplier, + ElementsCount count) { + return findElementsInShadowRoot(locator, supplier, ElementState.DISPLAYED, count); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param supplier Required elements' supplier. + * @param state Visibility state of shadowed elements. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, IElementSupplier supplier, + ElementState state) { + return findElementsInShadowRoot(locator, supplier, state, ElementsCount.ANY); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param supplier Required elements' supplier. + * @param state Visibility state of shadowed elements. + * @param count Expected number of elements that have to be found (zero, more than zero, any). + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, IElementSupplier supplier, ElementState state, + ElementsCount count) { + return findElementsInShadowRoot(locator, null, supplier, state, count); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param supplier Required elements' supplier. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, IElementSupplier supplier) { + return findElementsInShadowRoot(locator, name, supplier, ElementsCount.ANY); + } + + /** + * Finds displayed shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param supplier Required elements' supplier. + * @param count Expected number of elements that have to be found (zero, more than zero, any). + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, IElementSupplier supplier, + ElementsCount count) { + return findElementsInShadowRoot(locator, name, supplier, ElementState.DISPLAYED, count); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param supplier Required elements' supplier. + * @param state Visibility state of shadowed elements. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, IElementSupplier supplier, + ElementState state) { + return findElementsInShadowRoot(locator, name, supplier, state, ElementsCount.ANY); + } + + /** + * Finds shadowed elements by their locator relative to shadow root of the current element. + * + * @param Type of the target elements. + * @param locator Locator of shadowed elements relative to shadow root. + * @param name Child elements name. + * @param supplier Required elements' supplier. + * @param state Visibility state of shadowed elements. + * @return List of shadowed elements. + */ + default List findElementsInShadowRoot(By locator, String name, IElementSupplier supplier, + ElementState state, ElementsCount count) { + return getShadowRootElementFactory().findElements(locator, name, supplier, count, state); + } +} diff --git a/src/main/java/aquality/selenium/logging/HttpExchangeLoggingOptions.java b/src/main/java/aquality/selenium/logging/HttpExchangeLoggingOptions.java new file mode 100644 index 0000000..3cf5ac1 --- /dev/null +++ b/src/main/java/aquality/selenium/logging/HttpExchangeLoggingOptions.java @@ -0,0 +1,103 @@ +package aquality.selenium.logging; + +/** + * HTTP Request/Response logging options. + */ +public class HttpExchangeLoggingOptions { + private LoggingParameters requestInfo = new LoggingParameters(true, LogLevel.INFO); + private LoggingParameters requestHeaders = new LoggingParameters(true, LogLevel.DEBUG); + private LoggingParameters requestPostData = new LoggingParameters(false, LogLevel.DEBUG); + private LoggingParameters responseInfo = new LoggingParameters(true, LogLevel.INFO); + private LoggingParameters responseHeaders = new LoggingParameters(true, LogLevel.DEBUG); + private LoggingParameters responseBody = new LoggingParameters(false, LogLevel.DEBUG); + + /** + * Gets logging parameters of general request info: Method, URL, Request ID. + * @return request info logging parameters. + */ + public LoggingParameters getRequestInfo() { + return requestInfo; + } + + /** + * Sets logging parameters of general request info: Method, URL, Request ID. + */ + public void setRequestInfo(LoggingParameters requestInfo) { + this.requestInfo = requestInfo; + } + + /** + * Gets logging parameters of request headers. + * @return logging parameters of request headers. + */ + public LoggingParameters getRequestHeaders() { + return requestHeaders; + } + + /** + * Sets logging parameters of request headers. + */ + public void setRequestHeaders(LoggingParameters requestHeaders) { + this.requestHeaders = requestHeaders; + } + + /** + * Gets logging parameters of request POST data. + * @return logging parameters of request POST data. + */ + public LoggingParameters getRequestPostData() { + return requestPostData; + } + + /** + * Sets logging parameters of request POST data. + */ + public void setRequestPostData(LoggingParameters requestPostData) { + this.requestPostData = requestPostData; + } + + /** + * Gets logging parameters of general response info: Status code, URL, Resource type, Request ID. + * @return logging parameters of general response info. + */ + public LoggingParameters getResponseInfo() { + return responseInfo; + } + + /** + * Sets logging parameters of general response info: Status code, URL, Resource type, Request ID. + */ + public void setResponseInfo(LoggingParameters responseInfo) { + this.responseInfo = responseInfo; + } + + /** + * Gets logging parameters of response headers. + * @return logging parameters of response headers. + */ + public LoggingParameters getResponseHeaders() { + return responseHeaders; + } + + /** + * Sets logging parameters of response headers. + */ + public void setResponseHeaders(LoggingParameters responseHeaders) { + this.responseHeaders = responseHeaders; + } + + /** + * Gets logging parameters of response body. + * @return logging parameters of response body. + */ + public LoggingParameters getResponseBody() { + return responseBody; + } + + /** + * Sets logging parameters of response body. + */ + public void setResponseBody(LoggingParameters responseBody) { + this.responseBody = responseBody; + } +} diff --git a/src/main/java/aquality/selenium/logging/LocalizedLoggerUtility.java b/src/main/java/aquality/selenium/logging/LocalizedLoggerUtility.java new file mode 100644 index 0000000..47a72fb --- /dev/null +++ b/src/main/java/aquality/selenium/logging/LocalizedLoggerUtility.java @@ -0,0 +1,45 @@ +package aquality.selenium.logging; + +import static aquality.selenium.browser.AqualityServices.getLocalizedLogger; + +/** + * Wrapper over the {@link aquality.selenium.core.localization.ILocalizedLogger}. + */ +public class LocalizedLoggerUtility { + private LocalizedLoggerUtility() throws InstantiationException { + throw new InstantiationException("Utility class"); + } + + /** + * Logging of localized messages with a specific log level. + * @param logLevel logging level. + * @param messageKey localized message key. + * @param args arguments for the localized message. + */ + public static void logByLevel(LogLevel logLevel, String messageKey, Object... args) { + switch (logLevel) { + case DEBUG: { + getLocalizedLogger().debug(messageKey, args); + break; + } + case INFO: { + getLocalizedLogger().info(messageKey, args); + break; + } + case WARN: { + getLocalizedLogger().warn(messageKey, args); + break; + } + case ERROR: { + getLocalizedLogger().error(messageKey, args); + break; + } + case FATAL: { + getLocalizedLogger().fatal(messageKey, new Throwable(), args); + break; + } + default: + throw new IllegalArgumentException(String.format("Localized logging at level [ %s] is not supported.", logLevel)); + } + } +} diff --git a/src/main/java/aquality/selenium/logging/LogLevel.java b/src/main/java/aquality/selenium/logging/LogLevel.java new file mode 100644 index 0000000..b5d223d --- /dev/null +++ b/src/main/java/aquality/selenium/logging/LogLevel.java @@ -0,0 +1,12 @@ +package aquality.selenium.logging; + +/** + * Logging level for specific features, such as HTTP Exchange. + */ +public enum LogLevel { + DEBUG, + INFO, + WARN, + ERROR, + FATAL +} diff --git a/src/main/java/aquality/selenium/logging/LoggingParameters.java b/src/main/java/aquality/selenium/logging/LoggingParameters.java new file mode 100644 index 0000000..177d5fc --- /dev/null +++ b/src/main/java/aquality/selenium/logging/LoggingParameters.java @@ -0,0 +1,34 @@ +package aquality.selenium.logging; + +/** + * Logging parameters for specific features, such as HTTP Exchange. + */ +public class LoggingParameters { + private final boolean enabled; + private final LogLevel logLevel; + + /** + * Initializes logging parameters. + */ + public LoggingParameters(boolean enabled, LogLevel logLevel) { + + this.enabled = enabled; + this.logLevel = logLevel; + } + + /** + * Logging level for the specific feature. + * @return logging level for the specific feature. + */ + public LogLevel getLogLevel() { + return logLevel; + } + + /** + * Whether feature logging should be enabled or not. + * @return true if the feature is enabled, false otherwise. + */ + public boolean isEnabled() { + return enabled; + } +} diff --git a/src/main/resources/js/expandShadowRoot.js b/src/main/resources/js/expandShadowRoot.js new file mode 100644 index 0000000..af2e83e --- /dev/null +++ b/src/main/resources/js/expandShadowRoot.js @@ -0,0 +1 @@ +return arguments[0].shadowRoot; diff --git a/src/main/resources/js/setAttribute.js b/src/main/resources/js/setAttribute.js new file mode 100644 index 0000000..8d040b7 --- /dev/null +++ b/src/main/resources/js/setAttribute.js @@ -0,0 +1 @@ +arguments[0].setAttribute(arguments[1], arguments[2]); diff --git a/src/main/resources/localization/be.json b/src/main/resources/localization/be.json index e9a20dd..3efc60e 100644 --- a/src/main/resources/localization/be.json +++ b/src/main/resources/localization/be.json @@ -1,82 +1,132 @@ { - "loc.browser.arguments" : "Атрымалі стартавыя аргументы браўзэра з settings файла: %s", - "loc.browser.back" : "Вяртаемся да папярэдняй старонкі", - "loc.browser.forward" : "Пераходзім да наступнай старонкі", - "loc.browser.capabilities" : "Атрымалі capabilities браўзэра з settings файла: %s", - "loc.browser.options" : "Атрымалі опцыі профіля браўзэра з settings файла: %s", - "loc.browser.driver.quit" : "Закрываем браўзэр", - "loc.browser.getUrl" : "Атрымліваем адрас бягучай старонкі", + "loc.browser.arguments": "Атрымалі стартавыя аргументы браўзэра з settings файла: %s", + "loc.browser.back": "Вяртаемся да папярэдняй старонкі", + "loc.browser.forward": "Пераходзім да наступнай старонкі", + "loc.browser.capabilities": "Атрымалі capabilities браўзэра з settings файла: %s", + "loc.browser.options": "Атрымалі опцыі профіля браўзэра з settings файла: %s", + "loc.browser.driver.quit": "Закрываем браўзэр", + "loc.browser.getUrl": "Атрымліваем адрас бягучай старонкі", "loc.browser.url.value": "Адрас бягучай старонкі: [%s]", - "loc.browser.grid" : "Усталёўваем драйвэр для браўзэра з Selenium Grid hub", - "loc.browser.grid.fail" : "Не ўдалося ўсталяваць драйвэр браўзэра з Selenium Grid hub", - "loc.browser.maximize" : "Разгортваем акно браўзэра на ўвесь экран", - "loc.browser.navigate" : "Пераходзім па адрасе - '%s'", + "loc.browser.grid": "Усталёўваем драйвэр для браўзэра з Selenium Grid hub", + "loc.browser.grid.fail": "Не ўдалося ўсталяваць драйвэр браўзэра з Selenium Grid hub", + "loc.browser.maximize": "Разгортваем акно браўзэра на ўвесь экран", + "loc.browser.navigate": "Пераходзім па адрасе - '%s'", "loc.browser.page.wait": "Чакаем загрузкі старонкі", - "loc.browser.page.timeout" : "Таймаўт загрузкі старонкі", - "loc.browser.ready" : "Браўзэр '%1$s' гатовы...", - "loc.browser.refresh" : "Абнаўляем старонку", - "loc.browser.page.load.timeout" : "Задаем таймаўт загрузкі старонкі: '%1$s' сек.", - "loc.browser.implicit.timeout" : "Задаем implicit(няяўны) таймаўт: '%1$s' сек.", - "loc.browser.script.timeout" : "Задаем таймаўт на выкананне асінхронных javascript камандаў: '%1$s' сек.", - "loc.browser.alert.accept" : "Пацвярджаем дзеянне ў акне апавяшчэння", - "loc.browser.alert.decline" : "Адхіляем дзеянне ў акне апавяшчэння", - "loc.browser.alert.fail" : "Памылка пры ўзаемадзеянні з акном апавяшчэння", - "loc.button" : "Кнопка", - "loc.checkbox" : "Чэкбокс", - "loc.checkable.get.state" : "Атрымліваем стан", + "loc.browser.page.timeout": "Таймаўт загрузкі старонкі", + "loc.browser.ready": "Браўзэр '%1$s' гатовы...", + "loc.browser.refresh": "Абнаўляем старонку", + "loc.browser.page.load.timeout": "Задаем таймаўт загрузкі старонкі: '%1$s' сек.", + "loc.browser.implicit.timeout": "Задаем implicit(няяўны) таймаўт: '%1$s' сек.", + "loc.browser.script.timeout": "Задаем таймаўт на выкананне асінхронных javascript камандаў: '%1$s' сек.", + "loc.browser.alert.accept": "Пацвярджаем дзеянне ў акне апавяшчэння", + "loc.browser.alert.decline": "Адхіляем дзеянне ў акне апавяшчэння", + "loc.browser.alert.fail": "Памылка пры ўзаемадзеянні з акном апавяшчэння", + "loc.button": "Кнопка", + "loc.checkbox": "Чэкбокс", + "loc.checkable.get.state": "Атрымліваем стан", "loc.checkable.state": "Стан: [%s]", - "loc.clicking" : "Націскаем", - "loc.clicking.double" : "Падвойна націскаем", - "loc.clicking.js" : "Націскаем праз Javascript", - "loc.clicking.right" : "Націскаем правай кнопкай", - "loc.combobox" : "Камбабокс", + "loc.clicking": "Націскаем", + "loc.clicking.double": "Падвойна націскаем", + "loc.clicking.js": "Націскаем праз Javascript", + "loc.clicking.right": "Націскаем правай кнопкай", + "loc.combobox": "Камбабокс", "loc.combobox.getting.selected.text": "Атрымліваем выбраны тэкст", "loc.combobox.getting.selected.value": "Атрымліваем выбранае значэнне", "loc.combobox.selected.text": "Выбраны тэкст: [%s]", "loc.combobox.selected.value": "Выбранае значэнне: [%s]", - "loc.combobox.select.by.text" : "Выбіраем значэнне з тэкстам '%s'", + "loc.combobox.select.by.text": "Выбіраем значэнне з тэкстам '%s'", "loc.combobox.select.by.text.js": "Выбіраем значэнне з тэкстам '%s' праз JavaScript", "loc.combobox.get.texts": "Атрымліваем спіс тэкстаў опцыяў", "loc.combobox.get.texts.js": "Атрымліваем спіс тэкстаў опцыяў праз JavaScript", - "loc.combobox.get.values" : "Атрымліваем спіс значэнняў", + "loc.combobox.get.values": "Атрымліваем спіс значэнняў", "loc.combobox.get.text.js": "Атрымліваем выбраны тэкст праз JavaScript", "loc.combobox.texts": "Спіс тэкстаў опцыяў: [%s]", "loc.combobox.values": "Спіс значэнняў: [%s]", - "loc.combobox.impossible.to.select.contain.value.or.text" : "Немагчыма выбраць опцыю, якая змяшчае значэнне/тэкст '%1$s' у камбабоксе '%2$s'", - "loc.el.getattr" : "Атрымліваем атрыбут '%1$s'", + "loc.combobox.impossible.to.select.contain.value.or.text": "Немагчыма выбраць опцыю, якая змяшчае значэнне/тэкст '%1$s' у камбабоксе '%2$s'", + "loc.el.getattr": "Атрымліваем атрыбут '%1$s'", "loc.el.attr.value": "Значэнне атрыбута '%1$s': [%2$s]", - "loc.el.cssvalue" : "Атрымліваем значэнне css '%1$s'", - "loc.file.reading_exception" : "Памылка пры чытанні файла: '%s'", - "loc.focusing" : "Факусуемся", - "loc.get.text" : "Атрымліваем тэкст элемента", + "loc.el.attr.set": "Задаем значэнне атрыбута '%1$s': [%2$s]", + "loc.el.cssvalue": "Атрымліваем значэнне css '%1$s'", + "loc.el.execute.pinnedjs": "Выконваем замацаваны JavaScript", + "loc.el.execute.pinnedjs.result": "Вынік выканання замацаванага JavaScript: [%1$s]", + "loc.file.reading_exception": "Памылка пры чытанні файла: '%s'", + "loc.focusing": "Факусуемся", + "loc.get.text": "Атрымліваем тэкст элемента", "loc.text.value": "Тэкст элемента: [%1$s]", - "loc.get.text.js" : "Атрымліваем тэкст элемента праз Javascript", - "loc.hover.js" : "Наводзім курсор мышы на элемент праз JavaScript", - "loc.is.present.js" : "Вызначаем, ці прысутны элемент на экране, праз JavaScript", + "loc.get.text.js": "Атрымліваем тэкст элемента праз Javascript", + "loc.hover.js": "Наводзім курсор мышы на элемент праз JavaScript", + "loc.is.present.js": "Вызначаем, ці прысутны элемент на экране, праз JavaScript", "loc.is.present.value": "Ці прысутны элемент на экране: [%s]", "loc.get.xpath.js": "Атрымліваем XPath лакатар элемента праз JavaScript", "loc.xpath.value": "XPath лакатар: [%s]", - "loc.label" : "Надпіс", - "loc.link" : "Спасылка", - "loc.moving" : "Наводзім курсор мышы на элемент", - "loc.movingFrom" : "Адводзім курсор мышы ад элемента", - "loc.radio" : "Радыёкнопка", - "loc.scrolling.center.js" : "Пракручваем старонку да цэнтра элемента праз JavaScript", - "loc.scrolling.js" : "Пракручваем старонку праз JavaScript", - "loc.selecting.value" : "Выбіраем значэнне - '%s'", - "loc.send.text" : "Задаем тэкст - '%s'", - "loc.setting.value" : "Задаем значэнне - '%s'", - "loc.text.clearing" : "Ачышчаем", + "loc.label": "Надпіс", + "loc.link": "Спасылка", + "loc.moving": "Наводзім курсор мышы на элемент", + "loc.movingFrom": "Адводзім курсор мышы ад элемента", + "loc.radio": "Радыёкнопка", + "loc.scrolling.center.js": "Пракручваем старонку да цэнтра элемента праз JavaScript", + "loc.scrolling.js": "Пракручваем старонку праз JavaScript", + "loc.selecting.value": "Выбіраем значэнне - '%s'", + "loc.send.text": "Задаем тэкст - '%s'", + "loc.setting.value": "Задаем значэнне - '%s'", + "loc.text.clearing": "Ачышчаем", "loc.text.submitting": "Адпраўляем", - "loc.text.field" : "Тэкставае поле", - "loc.text.sending.key" : "Націскаем клавішу '%1$s'", - "loc.text.typing" : "Уводзім '%s'", - "loc.text.masked_value" : "********", - "loc.browser.switch.to.tab.handle": "Пераключэнне на новую ўкладку па дэскрыптару '%1$s'", - "loc.browser.switch.to.tab.index": "Пераключэнне на новую ўкладку па індэксе '%1$s'", + "loc.text.field": "Тэкставае поле", + "loc.text.sending.key": "Націскаем клавішу '%1$s'", + "loc.text.typing": "Уводзім '%s'", + "loc.text.masked_value": "********", + "loc.browser.switch.to.tab.handle": "Пераключэнне на ўкладку па дэскрыптару '%1$s'", + "loc.browser.switch.to.tab.index": "Пераключэнне на ўкладку па індэксе '%1$s'", "loc.browser.switch.to.new.tab": "Пераключэнне на новую ўкладку", "loc.browser.get.tab.handles": "Атрыманне спісу дэскрыптараў адкрытых укладак", "loc.browser.get.tab.handle": "Атрыманне дэскрыптара бягучай укладкі", "loc.browser.tab.open.new": "Адкрыццё новай укладкі", - "loc.browser.tab.close": "Закрыццё ўкладкі" -} \ No newline at end of file + "loc.browser.tab.close": "Закрыццё ўкладкі", + "loc.browser.devtools.session.isactive": "Правяраем, ці актыўная DevTools сэсія", + "loc.browser.devtools.session.isactive.result": "Ці актыўная DevTools сэсія: [%1$s]", + "loc.browser.devtools.session.close": "Завяршаем сэсію DevTools", + "loc.browser.devtools.session.get": "Атрымліваем сэсію DevTools з дэскрыптарам ўкладкі '%1$s'", + "loc.browser.devtools.command.execute": "Выконваем DevTools каманду [%1$s]", + "loc.browser.devtools.command.execute.withparams": "Выконваем DevTools каманду [%1$s] з параметрамі [%2$s]", + "loc.browser.devtools.command.execute.result": "Вынік DevTools каманды: [%1$s]", + "loc.browser.devtools.listener.add": "Падпісваемся на DevTools падзею [%1$s]", + "loc.browser.devtools.listener.clear": "Адпісваемся ад DevTools падзеяў", + "loc.browser.network.monitoring.start": "Пачынаем сеткавы маніторынг", + "loc.browser.network.monitoring.stop": "Спыняем сеткавы маніторынг", + "loc.browser.network.useragent.set": "Задаем значэнне агента карыстальніка: [%1$s]", + "loc.browser.network.authentication.add": "Дадаем апрацоўшчык базавай аўтэнтыфікацыі", + "loc.browser.network.authentication.clear": "Ачышчаем апрацоўшчык базавай аўтэнтыфікацыі", + "loc.browser.network.filter.clear": "Ачышчаем фільтр сеткавых падзеяў", + "loc.browser.network.filter.set": "Задаем фільтр сеткавых падзеяў", + "loc.browser.network.event.requestsent.add": "Падпісваемся на падзею адпраўкі сеткавага запыта", + "loc.browser.network.event.requestsent.log.info": "Адпраўлены HTTP запыт:\r\nМетад: \t\t%1$s, \r\nURL: \t\t\t%2$s, \r\nID запыта: \t%3$s", + "loc.browser.network.event.requestsent.log.headers": "Загалоўкі запыта:\r\n{{%s\r\n}}", + "loc.browser.network.event.requestsent.log.data": "POST дадзеныя запыта:\r\n{%s}", + "loc.browser.network.event.responsereceived.log.info": "Атрыманы HTTP адказ:\r\nСтатус-код: \t%1$s, \r\nURL: \t\t\t%2$s, \r\nТып рэсурса: \t%3$s, \r\nID запыта: \t%4$s", + "loc.browser.network.event.responsereceived.log.headers": "Загалоўкі адказа:\r\n{{%s\r\n}}", + "loc.browser.network.event.responsereceived.log.body": "Цела адказа:\r\n{%s}", + "loc.browser.network.event.responsereceived.add": "Падпісваемся на падзею атрымання сеткавага адказа", + "loc.browser.network.interceptor.start": "Пачынаем перахоп сеткавых падзеяў", + "loc.browser.javascript.initializationscripts.get": "Атрымліваем ініцыялізацыйныя скрыпты", + "loc.browser.javascript.scriptcallbackbindings.get": "Атрымліваем прывязкі зваротнага выкліку JavaScript", + "loc.browser.javascript.event.callbackexecuted.add": "Падпісваемся на падзею выканання іменаванай прывязкі зваротнага выкліку JavaScript", + "loc.browser.javascript.event.exceptionthrown.add": "Падпісваемся на падзею падзення памылкі JavaScript", + "loc.browser.javascript.event.consoleapicalled.add": "Падпісваемся на падзею выкліку API кансолі JavaScript", + "loc.browser.javascript.event.dommutated.add": "Падпісваемся на падзею зменаў у DOM", + "loc.browser.javascript.initializationscript.add": "Дадаем ініцыялізацыйны JavaScript з прыязным іменем '%1$s'", + "loc.browser.javascript.initializationscript.remove": "Выдаляем ініцыялізацыйны JavaScript з прыязным іменем '%1$s'", + "loc.browser.javascript.initializationscripts.clear": "Выдаляем усе ініцыялізацыйныя скрыпты", + "loc.browser.javascript.scriptcallbackbinding.add": "Дадаем прывязку зваротнага выкліку JavaScript з іменем '%1$s'", + "loc.browser.javascript.scriptcallbackbinding.remove": "Выдаляем прывязку зваротнага выкліку JavaScript з іменем '%1$s'", + "loc.browser.javascript.scriptcallbackbindings.clear": "Выдаляем усе прывязкі зваротнага выкліку JavaScript", + "loc.browser.javascript.snippet.pin": "Замацоўваем фрагмент JavaScript", + "loc.browser.javascript.snippet.unpin": "Адмацоўваем фрагмент JavaScript", + "loc.browser.javascript.snippets.get": "Атрымліваем замацаваныя фрагменты JavaScript", + "loc.browser.javascript.snippets.clear": "Выдаляем усе замацаваныя фрагменты JavaScript", + "loc.browser.javascript.event.monitoring.start": "Пачынаем маніторынг падзеяў JavaScript", + "loc.browser.javascript.event.monitoring.stop": "Спыняем маніторынг падзеяў JavaScript", + "loc.browser.javascript.clearall": "Выдаляем усе прывязкі зваротнага выкліку JavaScript і ініцыялізацыйныя скрыпты", + "loc.browser.javascript.reset": "Выдаляем усе прывязкі зваротнага выкліку JavaScript і ініцыялізацыйныя скрыпты, і спыняем чаканне падзеяў", + "loc.shadowroot.expand": "Разварочваем дрэва схаваных элементаў", + "loc.shadowroot.expand.js": "Разварочваем дрэва схаваных элементаў праз JavaScript" +} diff --git a/src/main/resources/localization/en.json b/src/main/resources/localization/en.json index 36c9e8c..c6cef7c 100644 --- a/src/main/resources/localization/en.json +++ b/src/main/resources/localization/en.json @@ -1,82 +1,132 @@ { - "loc.browser.arguments" : "Got browser start arguments from settings file: %s", - "loc.browser.back" : "Return to previous page", - "loc.browser.forward" : "Proceed to the next page", - "loc.browser.capabilities" : "Got browser capabilities from settings file: %s", - "loc.browser.options" : "Got browser profile options from settings file: %s", - "loc.browser.driver.quit" : "Closing browser", - "loc.browser.getUrl" : "Getting current url", + "loc.browser.arguments": "Got browser start arguments from settings file: %s", + "loc.browser.back": "Return to previous page", + "loc.browser.forward": "Proceed to the next page", + "loc.browser.capabilities": "Got browser capabilities from settings file: %s", + "loc.browser.options": "Got browser profile options from settings file: %s", + "loc.browser.driver.quit": "Closing browser", + "loc.browser.getUrl": "Getting current url", "loc.browser.url.value": "Current url: [%s]", - "loc.browser.grid" : "Setting webdriver from selenium grid hub", - "loc.browser.grid.fail" : "Driver setting from Selenium Grid hub was failed", - "loc.browser.maximize" : "Maximizing browser window", - "loc.browser.navigate" : "Navigate to url - '%s'", + "loc.browser.grid": "Setting webdriver from selenium grid hub", + "loc.browser.grid.fail": "Driver setting from Selenium Grid hub was failed", + "loc.browser.maximize": "Maximizing browser window", + "loc.browser.navigate": "Navigate to url - '%s'", "loc.browser.page.wait": "Waiting for page to load", - "loc.browser.page.timeout" : "Page loading timed out", - "loc.browser.ready" : "Browser '%1$s' ready...", - "loc.browser.refresh" : "Refreshing the page", - "loc.browser.page.load.timeout" : "Set '%1$s' timeout in seconds for loading of pages", - "loc.browser.implicit.timeout" : "Set implicit timeout '%1$s' in seconds", - "loc.browser.script.timeout" : "Set async javascript execution timeout '%1$s' in seconds", - "loc.browser.alert.accept" : "Accepting browser alert", - "loc.browser.alert.decline" : "Dismissing browser alert", - "loc.browser.alert.fail" : "Failed while handling alert", - "loc.button" : "Button", - "loc.checkbox" : "CheckBox", - "loc.checkable.get.state" : "Getting state", + "loc.browser.page.timeout": "Page loading timed out", + "loc.browser.ready": "Browser '%1$s' ready...", + "loc.browser.refresh": "Refreshing the page", + "loc.browser.page.load.timeout": "Set '%1$s' timeout in seconds for loading of pages", + "loc.browser.implicit.timeout": "Set implicit timeout '%1$s' in seconds", + "loc.browser.script.timeout": "Set async javascript execution timeout '%1$s' in seconds", + "loc.browser.alert.accept": "Accepting browser alert", + "loc.browser.alert.decline": "Dismissing browser alert", + "loc.browser.alert.fail": "Failed while handling alert", + "loc.button": "Button", + "loc.checkbox": "CheckBox", + "loc.checkable.get.state": "Getting state", "loc.checkable.state": "State: [%s]", - "loc.clicking" : "Clicking", - "loc.clicking.double" : "Clicking double", - "loc.clicking.js" : "Clicking via Javascript", - "loc.clicking.right" : "Clicking right", - "loc.combobox" : "ComboBox", + "loc.clicking": "Clicking", + "loc.clicking.double": "Clicking double", + "loc.clicking.js": "Clicking via Javascript", + "loc.clicking.right": "Clicking right", + "loc.combobox": "ComboBox", "loc.combobox.getting.selected.text": "Getting selected text", "loc.combobox.getting.selected.value": "Getting selected value", "loc.combobox.selected.text": "Selected text: [%s]", "loc.combobox.selected.value": "Selected value: [%s]", - "loc.combobox.select.by.text" : "Selecting value by text '%s'", + "loc.combobox.select.by.text": "Selecting value by text '%s'", "loc.combobox.select.by.text.js": "Selecting value by text '%s' via JavaScript", "loc.combobox.get.texts": "Getting option texts array", "loc.combobox.get.texts.js": "Getting option texts array via JavaScript", - "loc.combobox.get.values" : "Getting values array", + "loc.combobox.get.values": "Getting values array", "loc.combobox.get.text.js": "Getting selected text via JavaScript", "loc.combobox.texts": "Option texts: [%s]", "loc.combobox.values": "Option values: [%s]", - "loc.combobox.impossible.to.select.contain.value.or.text" : "It is impossible to select option that contains value/text '%1$s' from combobox '%2$s'", - "loc.el.getattr" : "Getting attribute '%1$s'", + "loc.combobox.impossible.to.select.contain.value.or.text": "It is impossible to select option that contains value/text '%1$s' from combobox '%2$s'", + "loc.el.getattr": "Getting attribute '%1$s'", "loc.el.attr.value": "Value of attribute '%1$s': [%2$s]", - "loc.el.cssvalue" : "Getting css value '%1$s'", - "loc.file.reading_exception" : "Exception while reading file: '%s'", - "loc.focusing" : "Focusing", - "loc.get.text" : "Getting text from element", + "loc.el.attr.set": "Setting value of attribute '%1$s': [%2$s]", + "loc.el.cssvalue": "Getting css value '%1$s'", + "loc.el.execute.pinnedjs": "Executing pinned JavaScript", + "loc.el.execute.pinnedjs.result": "Result of pinned JavaScript execution: [%1$s]", + "loc.file.reading_exception": "Exception while reading file: '%s'", + "loc.focusing": "Focusing", + "loc.get.text": "Getting text from element", "loc.text.value": "Element's text: [%1$s]", - "loc.get.text.js" : "Getting text from element via Javascript", - "loc.hover.js" : "Hover mouse over element via JavaScript", - "loc.is.present.js" : "Is present via JavaScript", + "loc.get.text.js": "Getting text from element via Javascript", + "loc.hover.js": "Hover mouse over element via JavaScript", + "loc.is.present.js": "Is present via JavaScript", "loc.is.present.value": "Is present on screen: [%s]", "loc.get.xpath.js": "Getting XPath locator of element via JavaScript", "loc.xpath.value": "XPath locator: [%s]", - "loc.label" : "Label", - "loc.link" : "Link", - "loc.moving" : "Moving mouse to element", - "loc.movingFrom" : "Moving mouse from element", - "loc.radio" : "RadioButton", - "loc.scrolling.center.js" : "Scrolling to the center via JavaScript", - "loc.scrolling.js" : "Scrolling via JavaScript", - "loc.selecting.value" : "Selecting value - '%s'", - "loc.send.text" : "Setting text - '%s'", - "loc.setting.value" : "Setting value - '%s'", - "loc.text.clearing" : "Clearing", + "loc.label": "Label", + "loc.link": "Link", + "loc.moving": "Moving mouse to element", + "loc.movingFrom": "Moving mouse from element", + "loc.radio": "RadioButton", + "loc.scrolling.center.js": "Scrolling to the center via JavaScript", + "loc.scrolling.js": "Scrolling via JavaScript", + "loc.selecting.value": "Selecting value - '%s'", + "loc.send.text": "Setting text - '%s'", + "loc.setting.value": "Setting value - '%s'", + "loc.text.clearing": "Clearing", "loc.text.submitting": "Submitting", - "loc.text.field" : "Text Field", - "loc.text.sending.key" : "Sending key '%s'", - "loc.text.typing" : "Typing '%s'", - "loc.text.masked_value" : "********", + "loc.text.field": "Text Field", + "loc.text.sending.key": "Sending key '%s'", + "loc.text.typing": "Typing '%s'", + "loc.text.masked_value": "********", "loc.browser.switch.to.tab.handle": "Switching to tab by handle '%1$s'", "loc.browser.switch.to.tab.index": "Switching to tab by index '%1$s'", "loc.browser.switch.to.new.tab": "Switching to new tab", "loc.browser.get.tab.handles": "Getting tab handles", "loc.browser.get.tab.handle": "Getting current tab handle", "loc.browser.tab.open.new": "Opening new tab", - "loc.browser.tab.close": "Closing tab" -} \ No newline at end of file + "loc.browser.tab.close": "Closing tab", + "loc.browser.devtools.session.isactive": "Checking if DevTools session is active", + "loc.browser.devtools.session.isactive.result": "Is DevTools session active: [%1$s]", + "loc.browser.devtools.session.close": "Closing DevTools session", + "loc.browser.devtools.session.get": "Getting DevTools session by tab handle '%1$s'", + "loc.browser.devtools.command.execute": "Executing DevTools command [%1$s]", + "loc.browser.devtools.command.execute.withparams": "Executing DevTools command [%1$s] with parameters [%2$s]", + "loc.browser.devtools.command.execute.result": "DevTools command result: [%1$s]", + "loc.browser.devtools.listener.add": "Subscribing to DevTools event [%1$s]", + "loc.browser.devtools.listener.clear": "Unsubscribing from DevTools events", + "loc.browser.network.monitoring.start": "Starting Network Monitoring", + "loc.browser.network.monitoring.stop": "Stopping Network Monitoring", + "loc.browser.network.useragent.set": "Setting user agent: [%1$s]", + "loc.browser.network.authentication.add": "Adding Basic Authentication handler", + "loc.browser.network.authentication.clear": "Clearing Basic Authentication handler", + "loc.browser.network.filter.clear": "Clearing the network events filter", + "loc.browser.network.filter.set": "Setting the network events filter", + "loc.browser.network.event.requestsent.add": "Subscribing to Network Request Sent event", + "loc.browser.network.event.requestsent.log.info": "Send HTTP Request:\r\nMethod: \t\t%1$s, \r\nURL: \t\t\t%2$s, \r\nRequest ID: \t%3$s", + "loc.browser.network.event.requestsent.log.headers": "Request headers:\r\n{{%s\r\n}}", + "loc.browser.network.event.requestsent.log.data": "Request POST data:\r\n{%s}", + "loc.browser.network.event.responsereceived.log.info": "Received HTTP Response:\r\nStatus code: \t%1$s, \r\nURL: \t\t\t%2$s, \r\nResource type: \t%3$s, \r\nRequest ID: \t%4$s", + "loc.browser.network.event.responsereceived.log.headers": "Response headers:\r\n{{%s\r\n}}", + "loc.browser.network.event.responsereceived.log.body": "Response body:\r\n{%s}", + "loc.browser.network.event.responsereceived.add": "Subscribing to Network Response Received event", + "loc.browser.network.interceptor.start": "Starting network events interception", + "loc.browser.javascript.initializationscripts.get": "Getting initialization JavaScripts", + "loc.browser.javascript.scriptcallbackbindings.get": "Getting JavaScript callback bindings", + "loc.browser.javascript.event.callbackexecuted.add": "Subscribing to JavaScript Callback With A Named Binding Is Executed event", + "loc.browser.javascript.event.exceptionthrown.add": "Subscribing to JavaScript Exception Is Thrown event", + "loc.browser.javascript.event.consoleapicalled.add": "Subscribing to JavaScript Console API Is Called event", + "loc.browser.javascript.event.dommutated.add": "Subscribing to DOM mutated event", + "loc.browser.javascript.initializationscript.add": "Adding initialization JavaScript with friendly name '%1$s'", + "loc.browser.javascript.initializationscript.remove": "Removing initialization JavaScript by friendly name '%1$s'", + "loc.browser.javascript.initializationscripts.clear": "Removing all initialization JavaScripts", + "loc.browser.javascript.scriptcallbackbinding.add": "Adding JavaScript callback binding with the name '%1$s'", + "loc.browser.javascript.scriptcallbackbinding.remove": "Removing JavaScript callback binding with the name '%1$s'", + "loc.browser.javascript.scriptcallbackbindings.clear": "Removing all JavaScript callback bindings", + "loc.browser.javascript.snippet.pin": "Pinning JavaScript snippet", + "loc.browser.javascript.snippet.unpin": "Unpinning JavaScript snippet", + "loc.browser.javascript.event.monitoring.start": "Starting JavaScript Event Monitoring", + "loc.browser.javascript.event.monitoring.stop": "Stopping JavaScript Event Monitoring", + "loc.browser.javascript.snippets.get": "Getting pinned JavaScript snippets", + "loc.browser.javascript.snippets.clear": "Removing all pinned JavaScript snippets", + "loc.browser.javascript.clearall": "Removing all JavaScript callback bindings and initialization JavaScripts", + "loc.browser.javascript.reset": "Removing all JavaScript callback bindings and initialization JavaScripts, and stopping listening for events", + "loc.shadowroot.expand": "Expanding the Shadow Root", + "loc.shadowroot.expand.js": "Expanding the Shadow Root via JavaScript" +} diff --git a/src/main/resources/localization/pl.json b/src/main/resources/localization/pl.json new file mode 100644 index 0000000..145bad4 --- /dev/null +++ b/src/main/resources/localization/pl.json @@ -0,0 +1,132 @@ +{ + "loc.browser.arguments": "Pobrano argumenty uruchamiania przeglądarki z pliku ustawień: %s", + "loc.browser.back": "Powrót do poprzedniej strony", + "loc.browser.forward": "Przejście do następnej strony", + "loc.browser.capabilities": "Pobrano możliwości przeglądarki z pliku ustawień: %s", + "loc.browser.options": "Pobrano opcje profilu przeglądarki z pliku ustawień: %s", + "loc.browser.driver.quit": "Zamykanie przeglądarki", + "loc.browser.getUrl": "Pobieranie aktualnego adresu URL", + "loc.browser.url.value": "Aktualny adres URL: [%s]", + "loc.browser.grid": "Ustawianie WebDriver z Selenium Grid hub", + "loc.browser.grid.fail": "Ustawianie sterownika z Selenium Grid hub nie powiodło się", + "loc.browser.maximize": "Maksymalizacja okna przeglądarki", + "loc.browser.navigate": "Przejdź do adresu URL - '%s'", + "loc.browser.page.wait": "Oczekiwanie na załadowanie strony", + "loc.browser.page.timeout": "Przekroczono limit czasu ładowania strony", + "loc.browser.ready": "Przeglądarka '%1$s' gotowa...", + "loc.browser.refresh": "Odświeżanie strony", + "loc.browser.page.load.timeout": "Ustawianie limitu czasu ładowania strony: '%1$s' sek.", + "loc.browser.implicit.timeout": "Ustawianie implicit(niejawnego) limitu czasu: '%1$s' sek.", + "loc.browser.script.timeout": "Ustawianie limitu czasu wykonywania asynchronicznych poleceń javascript: '%1$s' sek.", + "loc.browser.alert.accept": "Akceptowanie alertu przeglądarki", + "loc.browser.alert.decline": "Odrzucanie alertu przeglądarki", + "loc.browser.alert.fail": "Niepowodzenie podczas obsługi alertu", + "loc.button": "Przycisk", + "loc.checkbox": "Pole wyboru", + "loc.checkable.get.state": "Uzyskiwanie stanu", + "loc.checkable.state": "Stan: [%s]", + "loc.clicking": "Kliknięcie", + "loc.clicking.double": "Podwójne kliknięcie", + "loc.clicking.js": "Kliknięcie przez JavaScript", + "loc.clicking.right": "Kliknięcie prawym przyciskiem", + "loc.combobox": "Pole kombi", + "loc.combobox.getting.selected.text": "Pobieranie wybranego tekstu", + "loc.combobox.getting.selected.value": "Pobieranie wybranej wartości", + "loc.combobox.selected.text": "Wybrany tekst: [%s]", + "loc.combobox.selected.value": "Wybrana wartość: [%s]", + "loc.combobox.select.by.text": "Wybieranie wartości z tekstem '%s'", + "loc.combobox.select.by.text.js": "Wybieranie wartości z tekstem '%s' przez JavaScript", + "loc.combobox.get.texts": "Pobieranie listy tekstów opcji", + "loc.combobox.get.texts.js": "Pobieranie listy tekstów opcji przez JavaScript", + "loc.combobox.get.values": "Pobieranie listy wartości", + "loc.combobox.get.text.js": "Pobieranie wybranego tekstu przez JavaScript", + "loc.combobox.texts": "Lista tekstów opcji: [%s]", + "loc.combobox.values": "Lista wartości: [%s]", + "loc.combobox.impossible.to.select.contain.value.or.text": "Wybieranie wartości ze znaczeniem/tekstem '%1$s' w polu kombi '%2$s' nie jest możliwe", + "loc.el.getattr": "Pobieranie atrybutu '%1$s'", + "loc.el.attr.value": "Wartość atrybutu '%1$s': [%2$s]", + "loc.el.attr.set": "Ustawienie wartości atrybutu '%1$s': [%2$s]", + "loc.el.cssvalue": "Pobieranie wartości css '%1$s'", + "loc.el.execute.pinnedjs": "Wykonywanie przypiętego JavaScript", + "loc.el.execute.pinnedjs.result": "Wynik wykonania przypiętego JavaScript: [%1$s]", + "loc.file.reading_exception": "Błąd podczas odczytu pliku: '%s'", + "loc.focusing": "Skupienie", + "loc.get.text": "Pobieranie tekstu z elementu", + "loc.text.value": "Tekst elementu: [%1$s]", + "loc.get.text.js": "Pobieranie tekstu z elementu przez JavaScript", + "loc.hover.js": "Najechanie kursorem na element przez JavaScript", + "loc.is.present.js": "Określiwanie, czy element jest obecny na ekranie, przez JavaScript", + "loc.is.present.value": "Czy element jest obecny na ekranie: [%s]", + "loc.get.xpath.js": "Pobieranie lokalizatora XPath elementu przez JavaScript", + "loc.xpath.value": "Lokalizator XPath: [%s]", + "loc.label": "Etykieta", + "loc.link": "Link", + "loc.moving": "Przesuwanie myszy na element", + "loc.movingFrom": "Przesuwanie myszy z elementu", + "loc.radio": "RadioButton", + "loc.scrolling.center.js": "Przewijanie do centrum przez JavaScript", + "loc.scrolling.js": "Przewijanie przez JavaScript", + "loc.selecting.value": "Wybieranie wartości - '%s'", + "loc.send.text": "Ustawianie tekstu - '%s'", + "loc.setting.value": "Ustawienie wartości - '%s'", + "loc.text.clearing": "Oczyszczenie", + "loc.text.submitting": "Przesyłanie", + "loc.text.field": "Pole tekstowe", + "loc.text.sending.key": "Wysyłanie klawisz '%1$s'", + "loc.text.typing": "Wpisywanie '%s'", + "loc.text.masked_value": "********", + "loc.browser.switch.to.tab.handle": "Przełączanie na kartę według deskryptora '%1$s'", + "loc.browser.switch.to.tab.index": "Przełączanie na kartę według indeksu '%1$s'", + "loc.browser.switch.to.new.tab": "Przełączanie na nową kartę", + "loc.browser.get.tab.handles": "Pobieranie listy deskryptorów otwartych kart", + "loc.browser.get.tab.handle": "Pobieranie deskryptora bieżącej karty", + "loc.browser.tab.open.new": "Otwieranie nowej karty", + "loc.browser.tab.close": "Zamknięcie karty", + "loc.browser.devtools.session.isactive": "Sprawdzanie, czy sesja DevTools jest aktywna", + "loc.browser.devtools.session.isactive.result": "Czy sesja DevTools jest aktywna: [%1$s]", + "loc.browser.devtools.session.close": "Zamykanie sesji DevTools", + "loc.browser.devtools.session.get": "Pobieranie sesji DevTools według deskryptora karty '%1$s'", + "loc.browser.devtools.command.execute": "Wykonywanie polecenia DevTools [%1$s]", + "loc.browser.devtools.command.execute.withparams": "Wykonywanie polecenia DevTools [%1$s] z parametrami [%2$s]", + "loc.browser.devtools.command.execute.result": "Wynik polecenia DevTools: [%1$s]", + "loc.browser.devtools.listener.add": "Subskrybowanie do wydarzenia DevTools [%1$s]", + "loc.browser.devtools.listener.clear": "Anulowanie subskrypcje do wydarzeń DevTools", + "loc.browser.network.monitoring.start": "Uruchamianie monitorowania sieci", + "loc.browser.network.monitoring.stop": "Zatrzymywanie monitorowania sieci", + "loc.browser.network.useragent.set": "Ustawianie klienta użytkownika: [%1$s]", + "loc.browser.network.authentication.add": "Dodawanie programu obsługi uwierzytelniania podstawowego", + "loc.browser.network.authentication.clear": "Usuwanie programów obsługi uwierzytelniania podstawowego", + "loc.browser.network.filter.clear": "Usuwanie filtru do wydarzeń sieciowych", + "loc.browser.network.filter.set": "Ustawianie filtru do wydarzeń sieciowych", + "loc.browser.network.event.requestsent.add": "Subskrybowanie do zdarzenia Wysłanie Żądania Sieciowego", + "loc.browser.network.event.requestsent.log.info": "Wysłano żądanie HTTP:\r\nMetoda: \t\t%1$s, \r\nURL: \t\t\t%2$s, \r\nID żądania: \t%3$s", + "loc.browser.network.event.requestsent.log.headers": "Nagłówki żądania:\r\n{{%s\r\n}}", + "loc.browser.network.event.requestsent.log.data": "POST dane żądania:\r\n{%s}", + "loc.browser.network.event.responsereceived.log.info": "Otrzymana odpowiedź HTTP:\r\nKod stanu: \t%1$s, \r\nURL: \t\t\t%2$s, \r\nTyp zasobu: \t%3$s, \r\nID żądania: \t%4$s", + "loc.browser.network.event.responsereceived.log.headers": "Nagłówki odpowiedzi:\r\n{{%s\r\n}}", + "loc.browser.network.event.responsereceived.log.body": "Treść odpowiedzi:\r\n{%s}", + "loc.browser.network.event.responsereceived.add": "Subskrybowanie do zdarzenia Otrzymano odpowiedź sieciową", + "loc.browser.network.interceptor.start": "Rozpoczęcie przechwytywania zdarzeń sieciowych", + "loc.browser.javascript.initializationscripts.get": "Pobieranie inicjujących skryptów JavaScript", + "loc.browser.javascript.scriptcallbackbindings.get": "Pobieranie powiązań wywołań zwrotnych JavaScript", + "loc.browser.javascript.event.callbackexecuted.add": "Subskrybowanie do zdarzenia Wykonywane jest nazwane powiązanie wywołania zwrotnego", + "loc.browser.javascript.event.exceptionthrown.add": "Subskrybowanie do zdarzenia wyjątek JavaScript jest rzucany", + "loc.browser.javascript.event.consoleapicalled.add": "Subskrybowanie do zdarzenia wywołania API konsoli JavaScript", + "loc.browser.javascript.event.dommutated.add": "Subskrybowanie do zdarzenia mutacji DOM", + "loc.browser.javascript.initializationscript.add": "Dodawanie inicjującego JavaScript o przyjaznej nazwie '%1$s'", + "loc.browser.javascript.initializationscript.remove": "Usuwanie inicjującego JavaScriptu według przyjaznej nazwy '%1$s'", + "loc.browser.javascript.initializationscripts.clear": "Usuwanie wszystkich inicjujących skryptów JavaScript", + "loc.browser.javascript.scriptcallbackbinding.add": "Dodanie powiązania wywołania zwrotnego JavaScript o nazwie '%1$s'", + "loc.browser.javascript.scriptcallbackbinding.remove": "Usuwanie powiązania wywołania zwrotnego JavaScript o nazwie '%1$s'", + "loc.browser.javascript.scriptcallbackbindings.clear": "Usuwanie wszystkich powiązań wywołań zwrotnych JavaScript", + "loc.browser.javascript.snippet.pin": "Przypinanie fragmentu kodu JavaScript", + "loc.browser.javascript.snippet.unpin": "Odpinanie fragmentu kodu JavaScript", + "loc.browser.javascript.snippets.get": "Pobieranie przypiętych fragmentów kodu JavaScript", + "loc.browser.javascript.snippets.clear": "Usuwanie wszystkich przypiętych fragmentów kodu JavaScript", + "loc.browser.javascript.event.monitoring.start": "Uruchamianie monitorowania zdarzeń JavaScript", + "loc.browser.javascript.event.monitoring.stop": "Zatrzymywanie monitorowania zdarzeń JavaScript", + "loc.browser.javascript.clearall": "Usuwanie wszystkich powiązań wywołań zwrotnych JavaScript i skrypty inicjujące", + "loc.browser.javascript.reset": "Usunięcie wszystkich powiązań wywołań zwrotnych JavaScript i skrypty inicjujące oraz zatrzymanie nasłuchiwania zdarzeń", + "loc.shadowroot.expand": "Rozwidnienie drzewa ukrytych elementów", + "loc.shadowroot.expand.js": "Rozwidnienie drzewa ukrytych elementów przez JavaScript" +} diff --git a/src/main/resources/localization/ru.json b/src/main/resources/localization/ru.json index 1c1bfa7..339708e 100644 --- a/src/main/resources/localization/ru.json +++ b/src/main/resources/localization/ru.json @@ -1,82 +1,132 @@ { - "loc.browser.arguments" : "Получили стартовые аргументы браузера из settings файла: %s", - "loc.browser.back" : "Возврат на предыдущую страницу", - "loc.browser.forward" : "Перейти на следующую страницу", - "loc.browser.capabilities" : "Получили capabilities браузера из settings файла: %s", - "loc.browser.options" : "Получили опции профиля браузера из settings файла: %s", - "loc.browser.driver.quit" : "Закрытие драйвера браузера", - "loc.browser.getUrl" : "Получение адреса текущей страницы", + "loc.browser.arguments": "Получили стартовые аргументы браузера из settings файла: %s", + "loc.browser.back": "Возврат на предыдущую страницу", + "loc.browser.forward": "Перейти на следующую страницу", + "loc.browser.capabilities": "Получили capabilities браузера из settings файла: %s", + "loc.browser.options": "Получили опции профиля браузера из settings файла: %s", + "loc.browser.driver.quit": "Закрытие драйвера браузера", + "loc.browser.getUrl": "Получение адреса текущей страницы", "loc.browser.url.value": "Адрес текущей страницы: [%s]", - "loc.browser.grid" : "Установка драйвера браузера из Selenium Grid хаба", - "loc.browser.grid.fail" : "Ошибка при получении драйвера из Selenium Grid хаба", - "loc.browser.maximize" : "Открытие браузера на всё окно", - "loc.browser.navigate" : "Переход на страницу - '%s'", + "loc.browser.grid": "Установка драйвера браузера из Selenium Grid хаба", + "loc.browser.grid.fail": "Ошибка при получении драйвера из Selenium Grid хаба", + "loc.browser.maximize": "Открытие браузера на всё окно", + "loc.browser.navigate": "Переход на страницу - '%s'", "loc.browser.page.wait": "Ожидание загрузки страницы", - "loc.browser.page.timeout" : "Таймаут загрузки страницы", - "loc.browser.ready" : "Браузер '%1$s' готов...", - "loc.browser.refresh" : "Обновление страницы", - "loc.browser.page.load.timeout" : "Установка таймаута для загрузки страниц: '%1$s' секунд", - "loc.browser.implicit.timeout" : "Установка implicit(неявного) таймаута: '%1$s' секунд", - "loc.browser.script.timeout" : "Установка таймаута на выполнение асинхронных javascript вызовов: '%1$s' секунд", - "loc.browser.alert.accept" : "Подтверждение действия во всплывающем окне", - "loc.browser.alert.decline" : "Отклонение действия во всплывающем окне", - "loc.browser.alert.fail" : "Не удалось обработать всплывающее окно", - "loc.button" : "Кнопка", - "loc.checkbox" : "Чекбокс", - "loc.checkable.get.state" : "Получение состояния", + "loc.browser.page.timeout": "Таймаут загрузки страницы", + "loc.browser.ready": "Браузер '%1$s' готов...", + "loc.browser.refresh": "Обновление страницы", + "loc.browser.page.load.timeout": "Установка таймаута для загрузки страниц: '%1$s' секунд", + "loc.browser.implicit.timeout": "Установка implicit(неявного) таймаута: '%1$s' секунд", + "loc.browser.script.timeout": "Установка таймаута на выполнение асинхронных javascript вызовов: '%1$s' секунд", + "loc.browser.alert.accept": "Подтверждение действия во всплывающем окне", + "loc.browser.alert.decline": "Отклонение действия во всплывающем окне", + "loc.browser.alert.fail": "Не удалось обработать всплывающее окно", + "loc.button": "Кнопка", + "loc.checkbox": "Чекбокс", + "loc.checkable.get.state": "Получение состояния", "loc.checkable.state": "Состояние: [%s]", - "loc.clicking" : "Клик", - "loc.clicking.double" : "Двойной Клик", - "loc.clicking.js" : "Клик через Javascript", - "loc.clicking.right" : "Клик правой кнопкой", - "loc.combobox" : "Комбобокс", + "loc.clicking": "Клик", + "loc.clicking.double": "Двойной Клик", + "loc.clicking.js": "Клик через Javascript", + "loc.clicking.right": "Клик правой кнопкой", + "loc.combobox": "Комбобокс", "loc.combobox.getting.selected.text": "Получаем выбранный текст", "loc.combobox.getting.selected.value": "Получаем выбранное значение", "loc.combobox.selected.text": "Выбранный текст: [%s]", "loc.combobox.selected.value": "Выбранное значение: [%s]", - "loc.combobox.select.by.text" : "Выбор значения с текстом '%s'", + "loc.combobox.select.by.text": "Выбор значения с текстом '%s'", "loc.combobox.select.by.text.js": "Выбор значения с текстом '%s' посредством JavaScript", "loc.combobox.get.texts": "Получение списка текстов опций", "loc.combobox.get.texts.js": "Получение списка текстов опций посредством JavaScript", - "loc.combobox.get.values" : "Получение списка значений", + "loc.combobox.get.values": "Получение списка значений", "loc.combobox.get.text.js": "Получение текста выбранного значения посредством JavaScript", "loc.combobox.texts": "Список текстов опций: [%s]", "loc.combobox.values": "Список значений: [%s]", - "loc.combobox.impossible.to.select.contain.value.or.text" : "Не удаётся выбрать значение которое содержит значение/текст '%1$s' в выпадающем списке '%2$s'", - "loc.el.getattr" : "Получение аттрибута '%1$s'", + "loc.combobox.impossible.to.select.contain.value.or.text": "Не удаётся выбрать значение которое содержит значение/текст '%1$s' в выпадающем списке '%2$s'", + "loc.el.getattr": "Получение аттрибута '%1$s'", "loc.el.attr.value": "Значение аттрибута '%1$s'': [%2$s]", - "loc.el.cssvalue" : "Получение значения css '%1$s'", - "loc.file.reading_exception" : "Исключение при чтении файла: '%s'%n", - "loc.focusing" : "Взятие элемента в фокус", - "loc.get.text" : "Получение текста элемента", + "loc.el.attr.set": "Установка значения атрибута '%1$s': [%2$s]", + "loc.el.cssvalue": "Получение значения css '%1$s'", + "loc.el.execute.pinnedjs": "Исполнение закреплённого JavaScript", + "loc.el.execute.pinnedjs.result": "Результат исполнения закреплённого JavaScript: [%1$s]", + "loc.file.reading_exception": "Исключение при чтении файла: '%s'%n", + "loc.focusing": "Взятие элемента в фокус", + "loc.get.text": "Получение текста элемента", "loc.text.value": "Текст элемента: [%1$s]", - "loc.get.text.js" : "Получение текста элемента (посредством JavaScript)", - "loc.hover.js" : "Наведение курсора на элемент (посредством JavaScript)", - "loc.is.present.js" : "Присутствует ли (посредством JavaScript)", + "loc.get.text.js": "Получение текста элемента (посредством JavaScript)", + "loc.hover.js": "Наведение курсора на элемент (посредством JavaScript)", + "loc.is.present.js": "Присутствует ли (посредством JavaScript)", "loc.is.present.value": "Присутствует ли на экране: [%s]", "loc.get.xpath.js": "Получение XPath локатора элемента (посредством JavaScript)", "loc.xpath.value": "XPath локатор: [%s]", - "loc.label" : "Надпись", - "loc.link" : "Ссылка", - "loc.moving" : "Наведение курсора на элемент", - "loc.movingFrom" : "Сдвиг курсора с элемента", - "loc.radio" : "Радиокнопка", - "loc.scrolling.center.js" : "Скроллинг в центр (посредством JavaScript)", - "loc.scrolling.js" : "Скроллинг посредством JavaScript", - "loc.selecting.value" : "Выбор значения - '%s'", - "loc.send.text" : "Ввод текста - '%s'", - "loc.setting.value" : "Установка значения - '%s'", - "loc.text.clearing" : "Очистка", + "loc.label": "Надпись", + "loc.link": "Ссылка", + "loc.moving": "Наведение курсора на элемент", + "loc.movingFrom": "Сдвиг курсора с элемента", + "loc.radio": "Радиокнопка", + "loc.scrolling.center.js": "Скроллинг в центр (посредством JavaScript)", + "loc.scrolling.js": "Скроллинг посредством JavaScript", + "loc.selecting.value": "Выбор значения - '%s'", + "loc.send.text": "Ввод текста - '%s'", + "loc.setting.value": "Установка значения - '%s'", + "loc.text.clearing": "Очистка", "loc.text.submitting": "Отправка", - "loc.text.field" : "Текстовое поле", - "loc.text.sending.key" : "Нажатие клавиши '%s'", - "loc.text.typing" : "Ввод текста '%s'", - "loc.text.masked_value" : "********", - "loc.browser.switch.to.tab.handle": "Переключение на новую вкладку по дескриптору '%1$s'", - "loc.browser.switch.to.tab.index": "Переключение на новую вкладку по индексу '%1$s'", + "loc.text.field": "Текстовое поле", + "loc.text.sending.key": "Нажатие клавиши '%s'", + "loc.text.typing": "Ввод текста '%s'", + "loc.text.masked_value": "********", + "loc.browser.switch.to.tab.handle": "Переключение на вкладку по дескриптору '%1$s'", + "loc.browser.switch.to.tab.index": "Переключение на вкладку по индексу '%1$s'", "loc.browser.switch.to.new.tab": "Переключение на новую вкладку", "loc.browser.get.tab.handles": "Получение списка дескрипторов открытых вкладок", "loc.browser.get.tab.handle": "Получение дескриптора текущей вкладки", "loc.browser.tab.open.new": "Открытие новой вкладки", - "loc.browser.tab.close": "Закрытие вкладки" -} \ No newline at end of file + "loc.browser.tab.close": "Закрытие вкладки", + "loc.browser.devtools.session.isactive": "Проверка, активна ли сессия DevTools", + "loc.browser.devtools.session.isactive.result": "Активна ли DevTools сессия: [%1$s]", + "loc.browser.devtools.session.close": "Завершение DevTools сессии", + "loc.browser.devtools.session.get": "Получение DevTools сессии с дескриптором вкладки '%1$s'", + "loc.browser.devtools.command.execute": "Исполняем DevTools команду [%1$s]", + "loc.browser.devtools.command.execute.withparams": "Исполняем DevTools команду [%1$s] с параметрами [%2$s]", + "loc.browser.devtools.command.execute.result": "Результат DevTools команды: [%1$s]", + "loc.browser.devtools.listener.add": "Подписываемся на событие DevTools [%1$s]", + "loc.browser.devtools.listener.clear": "Отписываемся от событий DevTools", + "loc.browser.network.monitoring.start": "Начинаем сетевой мониторинг", + "loc.browser.network.monitoring.stop": "Останавливаем сетевой мониторинг", + "loc.browser.network.useragent.set": "Задание значения агента пользователя: [%1$s]", + "loc.browser.network.authentication.add": "Добавление обработчика базовой аутентификации", + "loc.browser.network.authentication.clear": "Очистка обработчика базовой аутентификации", + "loc.browser.network.filter.clear": "Очистка фильтра сетевый событий", + "loc.browser.network.filter.set": "Задание фильтра сетевых событий", + "loc.browser.network.event.requestsent.add": "Подписываемся на событие отправки сетевого запроса", + "loc.browser.network.event.requestsent.log.info": "Отправлен HTTP запрос:\r\nМетод: \t\t%1$s, \r\nURL: \t\t\t%2$s, \r\nID запроса: \t%3$s", + "loc.browser.network.event.requestsent.log.headers": "Заголовки запроса:\r\n{{%s\r\n}}", + "loc.browser.network.event.requestsent.log.data": "POST данные запроса:\r\n{%s}", + "loc.browser.network.event.responsereceived.log.info": "Получен HTTP ответ:\r\nСтатус-код: \t%1$s, \r\nURL: \t\t\t%2$s, \r\nТип ресурса: \t%3$s, \r\nID запроса: \t%4$s", + "loc.browser.network.event.responsereceived.log.headers": "Заголовки ответа:\r\n{{%s\r\n}}", + "loc.browser.network.event.responsereceived.log.body": "Тело ответа:\r\n{%s}", + "loc.browser.network.event.responsereceived.add": "Подписываемся на событие получения сетевого ответа", + "loc.browser.network.interceptor.start": "Начинаем перехват сетевых событий", + "loc.browser.javascript.initializationscripts.get": "Получение инициализационных скриптов", + "loc.browser.javascript.scriptcallbackbindings.get": "Получение привязок обратного вызова JavaScript", + "loc.browser.javascript.event.callbackexecuted.add": "Подписываемся на событие исполнения именованной привязки обратного вызова JavaScript", + "loc.browser.javascript.event.exceptionthrown.add": "Подписываемся на событие падения ошибки JavaScript", + "loc.browser.javascript.event.consoleapicalled.add": "Подписываемся на событие вызова API консоли JavaScript", + "loc.browser.javascript.event.dommutated.add": "Подписываемся на событие изменений в DOM", + "loc.browser.javascript.initializationscript.add": "Добавление инициализационного JavaScript с дружелюбным именем '%1$s'", + "loc.browser.javascript.initializationscript.remove": "Удаление инициализационного JavaScript с дружелюбным именем '%1$s'", + "loc.browser.javascript.initializationscripts.clear": "Удаление всех инициализационных скриптов", + "loc.browser.javascript.scriptcallbackbinding.add": "Добавление привязки обратного вызова JavaScript с именем '%1$s'", + "loc.browser.javascript.scriptcallbackbinding.remove": "Удаление привязки обратного вызова JavaScript с именем '%1$s'", + "loc.browser.javascript.scriptcallbackbindings.clear": "Удаление всех привязок обратного вызова JavaScript", + "loc.browser.javascript.snippet.pin": "Закрепление фрагмента JavaScript", + "loc.browser.javascript.snippet.unpin": "Открепление фрагмента JavaScript", + "loc.browser.javascript.snippets.get": "Получение закрепённых фрагментов JavaScript", + "loc.browser.javascript.snippets.clear": "Удаление всех закрепённых фрагментов JavaScript", + "loc.browser.javascript.event.monitoring.start": "Начинаем мониторинг событий JavaScript", + "loc.browser.javascript.event.monitoring.stop": "Останавливаем мониторинг событий JavaScript", + "loc.browser.javascript.clearall": "Удаляем все привязки обратного вызова JavaScript и инициализационные скрипты", + "loc.browser.javascript.reset": "Удаляем все привязки обратного вызова JavaScript и инициализационные скрипты, и останавливаем прослушивание событий", + "loc.shadowroot.expand": "Разворачиваем дерево скрытых элементов", + "loc.shadowroot.expand.js": "Разворачиваем дерево скрытых элементов посредством JavaScript" +} diff --git a/src/main/resources/localization/uk.json b/src/main/resources/localization/uk.json new file mode 100644 index 0000000..8bebccb --- /dev/null +++ b/src/main/resources/localization/uk.json @@ -0,0 +1,132 @@ +{ + "loc.browser.arguments": "Отримані аргументи запуску браузера з файлу налаштувань: %s", + "loc.browser.back": "Повертання до попередньої сторінки", + "loc.browser.forward": "Перехід до наступної сторінки", + "loc.browser.capabilities": "Отримані можливості браузера з файлу налаштувань: %s", + "loc.browser.options": "Отримані параметри профілю браузера з файлу налаштувань: %s", + "loc.browser.driver.quit": "Закриття браузера", + "loc.browser.getUrl": "Отримання адреси поточної сторінки", + "loc.browser.url.value": "Адреса поточної сторінки: [%s]", + "loc.browser.grid": "Встановлення драйвера для браузера з Selenium Grid hub", + "loc.browser.grid.fail": "Не вдалося встановити драйвер браузера з Selenium Grid hub", + "loc.browser.maximize": "Розгортання вікно браузера на весь екран", + "loc.browser.navigate": "Перехід до '%s'", + "loc.browser.page.wait": "Очікування завантаження сторінки", + "loc.browser.page.timeout": "Вичерпано час очікування завантаження сторінки", + "loc.browser.ready": "Браузер '%1$s' готовий...", + "loc.browser.refresh": "Оновлення сторінки", + "loc.browser.page.load.timeout": "Встановлення ліміту часу завантаження сторінки: '%1$s' сек.", + "loc.browser.implicit.timeout": "Встановлення implicit(неявного) ліміту часу: '%1$s' сек.", + "loc.browser.script.timeout": "Встановлення ліміту часу виконання асинхронних javascript команд: '%1$s' сек.", + "loc.browser.alert.accept": "Підтвердження дії у вікні сповіщення", + "loc.browser.alert.decline": "Відхилення дії у вікні сповіщення", + "loc.browser.alert.fail": "Помилка взаємодії з вікном сповіщень", + "loc.button": "Кнопка", + "loc.checkbox": "Чекбокс", + "loc.checkable.get.state": "Отримання стану", + "loc.checkable.state": "Стан: [%s]", + "loc.clicking": "Натискання", + "loc.clicking.double": "Двійне натискання", + "loc.clicking.js": "Натискання за допомогою Javascript", + "loc.clicking.right": "Натискання правою кнопкою миші", + "loc.combobox": "Комбобокс", + "loc.combobox.getting.selected.text": "Отримання вибраного тексту", + "loc.combobox.getting.selected.value": "Отримання вибраного значення", + "loc.combobox.selected.text": "Вибраний текст: [%s]", + "loc.combobox.selected.value": "Вибране значення: [%s]", + "loc.combobox.select.by.text": "Вибір значення за текстом '%s'", + "loc.combobox.select.by.text.js": "Вибір значення за текстом '%s' за допомогою JavaScript", + "loc.combobox.get.texts": "Отримання масиву текстів опцій", + "loc.combobox.get.texts.js": "Отримання масиву текстів опцій за допомогою JavaScript", + "loc.combobox.get.values": "Отримання масиву значень", + "loc.combobox.get.text.js": "Отримання вибраного тексту за допомогою JavaScript", + "loc.combobox.texts": "Тексти опцій: [%s]", + "loc.combobox.values": "Значення опцій: [%s]", + "loc.combobox.impossible.to.select.contain.value.or.text": "Неможливо вибрати опцію, що зміщує значення/текст '%1$s' у комбобоксі '%2$s'", + "loc.el.getattr": "Отримання атрибута '%1$s'", + "loc.el.attr.value": "Значення атрибута '%1$s': [%2$s]", + "loc.el.attr.set": "Встановлення значення атрибута '%1$s': [%2$s]", + "loc.el.cssvalue": "Отримання значення css '%1$s'", + "loc.el.execute.pinnedjs": "Виконання закріпленого JavaScript", + "loc.el.execute.pinnedjs.result": "Результат виконання закріпленого JavaScript: [%1$s]", + "loc.file.reading_exception": "Помилка підчас читання файлу: '%s'", + "loc.focusing": "Фокусування", + "loc.get.text": "Отримання тексту з елемента", + "loc.text.value": "Текст елемента: [%1$s]", + "loc.get.text.js": "Отримання тексту з елемента за допомогою Javascript", + "loc.hover.js": "Наведення курсору миші на елемент за допомогою JavaScript", + "loc.is.present.js": "Визначення, чи є елемент на екрані за допомогою JavaScript", + "loc.is.present.value": "Чи є елемент на екрані: [%s]", + "loc.get.xpath.js": "Отримання XPath локатора елемента за допомогою JavaScript", + "loc.xpath.value": "Локатор XPath: [%s]", + "loc.label": "Мітка", + "loc.link": "Посилання", + "loc.moving": "Переміщення миші до елемента", + "loc.movingFrom": "Переміщення миші від елемента", + "loc.radio": "Радіокнопка", + "loc.scrolling.center.js": "Прокрутка до центру за допомогою JavaScript", + "loc.scrolling.js": "Прокрутка за допомогою JavaScript", + "loc.selecting.value": "Вибір значення - '%s'", + "loc.send.text": "Встановлення тексту - '%s'", + "loc.setting.value": "Встановлення значення - '%s'", + "loc.text.clearing": "Очищення", + "loc.text.submitting": "Надсилання", + "loc.text.field": "Текстове поле", + "loc.text.sending.key": "Надсилання клавіш '%1$s'", + "loc.text.typing": "Введення '%s'", + "loc.text.masked_value": "********", + "loc.browser.switch.to.tab.handle": "Перехід на вкладку за дескриптором '%1$s'", + "loc.browser.switch.to.tab.index": "Перехід на вкладку за індексом '%1$s'", + "loc.browser.switch.to.new.tab": "Перехід на нову вкладку", + "loc.browser.get.tab.handles": "Отримання дескрипторів вкладок", + "loc.browser.get.tab.handle": "Отримання дескриптора поточної вкладки", + "loc.browser.tab.open.new": "Відкриття нової вкладки", + "loc.browser.tab.close": "Закриття вкладки", + "loc.browser.devtools.session.isactive": "Перевірка, чи активний сеанс DevTools", + "loc.browser.devtools.session.isactive.result": "Чи активний сеанс DevTools: [%1$s]", + "loc.browser.devtools.session.close": "Закриття сеансу DevTools", + "loc.browser.devtools.session.get": "Отримання сеансу DevTools з дескриптовом вкладки '%1$s'", + "loc.browser.devtools.command.execute": "Виконання команди DevTools [%1$s]", + "loc.browser.devtools.command.execute.withparams": "Виконання команди DevTools [%1$s] з параметрами [%2$s]", + "loc.browser.devtools.command.execute.result": "Результат команди DevTools: [%1$s]", + "loc.browser.devtools.listener.add": "Підписка на подію DevTools [%1$s]", + "loc.browser.devtools.listener.clear": "Скасування підписки на події DevTools", + "loc.browser.network.monitoring.start": "Початок моніторингу мережі", + "loc.browser.network.monitoring.stop": "Зупинення моніторингу мережі", + "loc.browser.network.useragent.set": "Встановлення значення агента користувача: [%1$s]", + "loc.browser.network.authentication.add": "Додавання обробника базової автентифікації", + "loc.browser.network.authentication.clear": "Очищення обробників базової автентифікації", + "loc.browser.network.filter.clear": "Очищення фільтру мережевих подій", + "loc.browser.network.filter.set": "Встановлення фільтру мережевих подій", + "loc.browser.network.event.requestsent.add": "Підписка на подію надісланого запиту мережі", + "loc.browser.network.event.requestsent.log.info": "HTTP-запит надіслано:\r\nМетод: \t\t%1$s, \r\nURL: \t\t\t%2$s, \r\nID запиту: \t%3$s", + "loc.browser.network.event.requestsent.log.headers": "Заголовки запиту:\r\n{{%s\r\n}}", + "loc.browser.network.event.requestsent.log.data": "POST дані запиту:\r\n{%s}", + "loc.browser.network.event.responsereceived.log.info": "Отримана відповідь HTTP:\r\nСтатус-код: \t%1$s, \r\nURL: \t\t\t%2$s, \r\nТип ресурсу: \t%3$s, \r\nID запиту: \t%4$s", + "loc.browser.network.event.responsereceived.log.headers": "Заголовки відповіді:\r\n{{%s\r\n}}", + "loc.browser.network.event.responsereceived.log.body": "Тіло відповіді:\r\n{%s}", + "loc.browser.network.event.responsereceived.add": "Підписка на подію відповіді мережі", + "loc.browser.network.interceptor.start": "Початок перехопу мережевих подій", + "loc.browser.javascript.initializationscripts.get": "Отримання ініціалізаційних скриптів", + "loc.browser.javascript.scriptcallbackbindings.get": "Отримання прив'язок зворотного виклику JavaScript", + "loc.browser.javascript.event.callbackexecuted.add": "Підписка на подію іменованої прив'язки зворотного виклику JavaScript", + "loc.browser.javascript.event.exceptionthrown.add": "Підписка на подію викидування помилки JavaScript", + "loc.browser.javascript.event.consoleapicalled.add": "Підписка на подію виклику API консолі JavaScript", + "loc.browser.javascript.event.dommutated.add": "Підписка на подію змін у DOM", + "loc.browser.javascript.initializationscript.add": "Додавання JavaScript ініціалізації зі зручною назвою '%1$s'", + "loc.browser.javascript.initializationscript.remove": "Видалення JavaScript ініціалізації зі зручною назвою '%1$s'", + "loc.browser.javascript.initializationscripts.clear": "Видалення всіх ініціалізаційних скриптів", + "loc.browser.javascript.scriptcallbackbinding.add": "Додавання прив'язки зворотного виклику JavaScript з ім'ям '%1$s'", + "loc.browser.javascript.scriptcallbackbinding.remove": "Видалення прив'язки зворотного виклику JavaScript з ім'ям '%1$s'", + "loc.browser.javascript.scriptcallbackbindings.clear": "Видалення всіх прив'язок зворотного виклику JavaScript", + "loc.browser.javascript.snippet.pin": "Закріплення фрагменту JavaScript", + "loc.browser.javascript.snippet.unpin": "Відкріплення фрагменту JavaScript", + "loc.browser.javascript.snippets.get": "Отримання всіх закріплених фрагментів JavaScript", + "loc.browser.javascript.snippets.clear": "Видалення всіх закріплених фрагментів JavaScript", + "loc.browser.javascript.event.monitoring.start": "Початок моніторингу подій JavaScript", + "loc.browser.javascript.event.monitoring.stop": "Припинення моніторингу подій JavaScript", + "loc.browser.javascript.clearall": "Видалення усіх прив'язок зворотного виклику JavaScript та ініціалізаційних скриптів", + "loc.browser.javascript.reset": "Видалення усіх прив'язок зворотного виклику JavaScript та ініціалізаційних скриптів, та скасування підписок на події", + "loc.shadowroot.expand": "Розгорнення дерева прихованих елементів", + "loc.shadowroot.expand.js": "Розгорнення дерева прихованих елементів за допомогою JavaScript" +} diff --git a/src/main/resources/log4j.properties b/src/main/resources/log4j.properties deleted file mode 100644 index 7ce938d..0000000 --- a/src/main/resources/log4j.properties +++ /dev/null @@ -1,13 +0,0 @@ -log4j.rootLogger=DEBUG, stdout, file - -log4j.appender.stdout=org.apache.log4j.ConsoleAppender -log4j.appender.stdout.encoding=UTF-8 -log4j.appender.stdout.target=System.out -log4j.appender.stdout.layout=org.apache.log4j.PatternLayout -log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n - -log4j.appender.file = org.apache.log4j.RollingFileAppender -log4j.appender.file.File = target/log/log.log -log4j.appender.file.MaxFileSize=10MB -log4j.appender.file.layout=org.apache.log4j.PatternLayout -log4j.appender.file.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n \ No newline at end of file diff --git a/src/main/resources/log4j2.properties b/src/main/resources/log4j2.properties new file mode 100644 index 0000000..800ac4a --- /dev/null +++ b/src/main/resources/log4j2.properties @@ -0,0 +1,28 @@ +status = info +rootLogger.level = debug +property.filepath = ./target/log/ +property.filename = log.log +appenders = file, console + +appender.console.type = Console +appender.console.name = STDOUT +appender.console.layout.type = PatternLayout +appender.console.layout.pattern =%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n +appender.console.filter.threshold.type = ThresholdFilter +appender.console.filter.threshold.level = info + +appender.file.type = RollingFile +appender.file.name = File +appender.file.fileName = ${filepath}${filename} +appender.file.filePattern = ${filepath}${filename}.%i +appender.file.layout.type = PatternLayout +appender.file.layout.pattern =%d{yyyy-MM-dd HH:mm:ss} %-5p - %m%n +appender.file.policies.type = Policies +appender.file.policies.size.type = SizeBasedTriggeringPolicy +appender.file.policies.size.size=10MB +appender.file.filter.threshold.type = ThresholdFilter +appender.file.filter.threshold.level = debug + +rootLogger.appenderRefs = file, console +rootLogger.appenderRef.console.ref = STDOUT +rootLogger.appenderRef.file.ref = File \ No newline at end of file diff --git a/src/main/resources/settings.json b/src/main/resources/settings.json index 7b34b72..75a3f3e 100644 --- a/src/main/resources/settings.json +++ b/src/main/resources/settings.json @@ -8,7 +8,6 @@ "chrome": { "webDriverVersion": "latest", "capabilities": { - "enableVNC": true }, "options": { "intl.accept_languages": "en", @@ -21,10 +20,23 @@ "pageLoadStrategy": "Normal", "startArguments": [] }, + "edge": { + "webDriverVersion": "latest", + "capabilities": { + }, + "options": { + "intl.accept_languages": "en", + "safebrowsing.enabled": "true", + "profile.default_content_settings.popups": "0", + "disable-popup-blocking": "true", + "download.prompt_for_download": "false", + "download.default_directory": "//home//selenium//downloads" + }, + "startArguments": [] + }, "firefox": { "webDriverVersion": "latest", "capabilities": { - "enableVNC": true }, "options": { "intl.accept_languages": "en", @@ -50,8 +62,38 @@ "ignoreProtectedModeSettings": true } }, + "opera": { + "webDriverVersion": "latest", + "binaryLocation": "%USERPROFILE%\\AppData\\Local\\Programs\\Opera\\launcher.exe", + "capabilities": { + }, + "options": { + "intl.accept_languages": "en", + "safebrowsing.enabled": "true", + "profile.default_content_settings.popups": "0", + "disable-popup-blocking": "true", + "download.prompt_for_download": "false", + "download.default_directory": "./downloads" + }, + "startArguments": ["--remote-debugging-port=9222", "--no-sandbox", "--disable-dev-shm-usage"] + }, "safari": { "downloadDir": "/Users/username/Downloads" + }, + "yandex": { + "webDriverVersion": "102.0.5005.61", + "binaryLocation": "%USERPROFILE%\\AppData\\Local\\Yandex\\YandexBrowser\\Application\\browser.exe", + "capabilities": { + }, + "options": { + "intl.accept_languages": "en", + "safebrowsing.enabled": "true", + "profile.default_content_settings.popups": "0", + "disable-popup-blocking": "true", + "download.prompt_for_download": "false", + "download.default_directory": "./downloads" + }, + "startArguments": [] } }, "timeouts": { diff --git a/src/test/java/automationpractice/Constants.java b/src/test/java/automationpractice/Constants.java index 6879e30..a8cd59d 100644 --- a/src/test/java/automationpractice/Constants.java +++ b/src/test/java/automationpractice/Constants.java @@ -5,4 +5,5 @@ private Constants(){ } public static final String URL_AUTOMATIONPRACTICE = "http://automationpractice.com/index.php"; + public static final String URL_MYLOCATIONORG = "https://my-location.org"; } diff --git a/src/test/java/forms/ChromeDownloadsForm.java b/src/test/java/forms/ChromeDownloadsForm.java new file mode 100644 index 0000000..28ff07e --- /dev/null +++ b/src/test/java/forms/ChromeDownloadsForm.java @@ -0,0 +1,48 @@ +package forms; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.elements.interfaces.ILabel; +import aquality.selenium.forms.Form; +import org.openqa.selenium.By; +import org.openqa.selenium.SearchContext; + +public class ChromeDownloadsForm extends Form { + private static final String ADDRESS = "chrome://downloads/"; + public static final By NESTED_SHADOW_ROOT_LOCATOR = By.id("moreActionsMenu"); + + private final ILabel lblDownloadsToolbarFromJs = getFormLabel().getJsActions().findElementInShadowRoot(By.cssSelector("downloads-toolbar"), "Downloads toolbar", ILabel.class); + private final ILabel lblMainContainerFromJs = getFormLabel().getJsActions().findElementInShadowRoot(By.id("mainContainer"), "Main container", ILabel.class); + + + public ChromeDownloadsForm() { + super(By.tagName("downloads-manager"), "Chrome downloads manager"); + } + + public static void open() { + AqualityServices.getBrowser().goTo(ADDRESS); + } + + public SearchContext expandShadowRoot() { + return getFormLabel().expandShadowRoot(); + } + + public SearchContext expandShadowRootViaJs() { + return getFormLabel().getJsActions().expandShadowRoot(); + } + + public ILabel getDownloadsToolbarLabel() { + return getFormLabel().findElementInShadowRoot(By.cssSelector("downloads-toolbar"), "Downloads toolbar", ILabel.class); + } + + public ILabel getMainContainerLabel() { + return getFormLabel().findElementInShadowRoot(By.id("mainContainer"), "main container", ILabel.class); + } + + public ILabel getDownloadsToolbarLabelFromJs() { + return lblDownloadsToolbarFromJs; + } + + public ILabel getMainContainerLabelFromJs() { + return lblMainContainerFromJs; + } +} diff --git a/src/test/java/forms/MyLocationForm.java b/src/test/java/forms/MyLocationForm.java new file mode 100644 index 0000000..39f7007 --- /dev/null +++ b/src/test/java/forms/MyLocationForm.java @@ -0,0 +1,23 @@ +package forms; + +import aquality.selenium.elements.interfaces.ILabel; +import aquality.selenium.forms.Form; +import org.openqa.selenium.By; + +public class MyLocationForm extends Form { + + private final ILabel lblLatitude = getElementFactory().getLabel(By.id("latitude"), "Latitude"); + private final ILabel lblLongitude = getElementFactory().getLabel(By.id("longitude"), "Longitude"); + + public MyLocationForm() { + super(By.xpath("//h1[contains(text(),'My Location')]"), "My Location"); + } + + public double getLatitude() { + return Double.parseDouble(lblLatitude.getText()); + } + + public double getLongitude() { + return Double.parseDouble(lblLongitude.getText()); + } +} diff --git a/src/test/java/manytools/BrowserLanguageForm.java b/src/test/java/manytools/BrowserLanguageForm.java new file mode 100644 index 0000000..6c39d4a --- /dev/null +++ b/src/test/java/manytools/BrowserLanguageForm.java @@ -0,0 +1,12 @@ +package manytools; + +public class BrowserLanguageForm extends ManyToolsForm { + public BrowserLanguageForm() { + super("Browser language"); + } + + @Override + protected String getUrlPart() { + return "http-html-text/browser-language/"; + } +} diff --git a/src/test/java/manytools/ManyToolsForm.java b/src/test/java/manytools/ManyToolsForm.java new file mode 100644 index 0000000..5eda112 --- /dev/null +++ b/src/test/java/manytools/ManyToolsForm.java @@ -0,0 +1,28 @@ +package manytools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.elements.interfaces.ILabel; +import aquality.selenium.forms.Form; +import org.openqa.selenium.By; + +public abstract class ManyToolsForm> extends Form { + private static final String BASE_URL = "https://manytools.org/"; + private final ILabel lblValue = getFormLabel().findChildElement(By.xpath(".//code"), getName(), ILabel.class); + + protected ManyToolsForm(String name) { + super(By.id("maincontent"), name); + } + + protected abstract String getUrlPart(); + + @SuppressWarnings("unchecked") + public T open() { + AqualityServices.getBrowser().goTo(BASE_URL + getUrlPart()); + AqualityServices.getBrowser().waitForPageToLoad(); + return (T) this; + } + + public String getValue() { + return lblValue.getText(); + } +} diff --git a/src/test/java/manytools/RequestHeadersForm.java b/src/test/java/manytools/RequestHeadersForm.java new file mode 100644 index 0000000..5cfa3b8 --- /dev/null +++ b/src/test/java/manytools/RequestHeadersForm.java @@ -0,0 +1,31 @@ +package manytools; + +import aquality.selenium.elements.interfaces.ILabel; +import org.apache.commons.lang3.NotImplementedException; +import org.openqa.selenium.By; + +import java.util.function.Function; + +public class RequestHeadersForm extends ManyToolsForm { + private final Function getHeaderValueLabel = name -> getElementFactory().getLabel( + By.xpath(String.format("//td[.='%s']/following-sibling::td", name)), "Header " + name); + public RequestHeadersForm() { + super("Request headers"); + } + + @Override + protected String getUrlPart() { + return "http-html-text/http-request-headers/"; + } + + @Override + public String getValue() { + throw new NotImplementedException("Please call the method getNullableValue with parameter instead"); + } + + public String getNullableValue(String headerName) { + getFormLabel().state().waitForDisplayed(); + ILabel valueLabel = getHeaderValueLabel.apply(headerName); + return valueLabel.state().isDisplayed() ? valueLabel.getText() : null; + } +} diff --git a/src/test/java/manytools/UserAgentForm.java b/src/test/java/manytools/UserAgentForm.java new file mode 100644 index 0000000..139d33b --- /dev/null +++ b/src/test/java/manytools/UserAgentForm.java @@ -0,0 +1,12 @@ +package manytools; + +public class UserAgentForm extends ManyToolsForm { + public UserAgentForm() { + super("User agent"); + } + + @Override + protected String getUrlPart() { + return "http-html-text/user-agent-string/"; + } +} diff --git a/src/test/java/tests/BaseTest.java b/src/test/java/tests/BaseTest.java index 643a6d8..02fdfa9 100644 --- a/src/test/java/tests/BaseTest.java +++ b/src/test/java/tests/BaseTest.java @@ -8,6 +8,10 @@ import org.testng.annotations.BeforeMethod; import theinternet.TheInternetPage; +import java.io.IOException; + +import static utils.FileUtil.getResourceFileByName; + public abstract class BaseTest { private static final String DEFAULT_URL = TheInternetPage.LOGIN.getAddress(); @@ -38,4 +42,13 @@ protected void navigate(TheInternetPage page) { protected Browser getBrowser() { return AqualityServices.getBrowser(); } + + @SuppressWarnings("unchecked") + protected T getScriptResultOrDefault(String scriptName, T defaultValue) { + try { + return ((T) getBrowser().executeScript(getResourceFileByName(scriptName))); + } catch (IOException e) { + return defaultValue; + } + } } diff --git a/src/test/java/tests/usecases/BrowserFactoryTests.java b/src/test/java/tests/usecases/BrowserFactoryTests.java index 48f544d..038b2cd 100644 --- a/src/test/java/tests/usecases/BrowserFactoryTests.java +++ b/src/test/java/tests/usecases/BrowserFactoryTests.java @@ -14,6 +14,7 @@ import org.openqa.selenium.TimeoutException; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.firefox.FirefoxDriver; +import org.openqa.selenium.firefox.FirefoxOptions; import org.openqa.selenium.remote.UnreachableBrowserException; import org.testng.Assert; import org.testng.annotations.AfterMethod; @@ -53,7 +54,7 @@ private IBrowserFactory getCustomFactory() { FirefoxSettings firefoxSettings = new FirefoxSettings(AqualityServices.get(ISettingsFile.class)); WebDriverManager.firefoxdriver().setup(); FirefoxDriver driver = AqualityServices.get(IActionRetrier.class).doWithRetry( - () -> new FirefoxDriver(firefoxSettings.getCapabilities().setHeadless(true)), + () -> new FirefoxDriver(((FirefoxOptions) firefoxSettings.getDriverOptions()).setHeadless(true)), Arrays.asList( SessionNotCreatedException.class, UnreachableBrowserException.class, @@ -73,6 +74,7 @@ public void testShouldBePossibleToOverrideDownloadDirectory() throws IOException String downloadDir = AqualityServices.getBrowser().getDownloadDirectory(); if (new File(downloadDirInitialized).exists()) { try (Stream walk = Files.walk(Paths.get(downloadDir))) { + //noinspection ResultOfMethodCallIgnored walk.sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); @@ -89,7 +91,7 @@ public void testShouldBePossibleToOverrideDownloadDirectory() throws IOException Assert.assertTrue(isFileDownloaded, "Downloaded file exists"); } - private class CustomBrowserModule extends BrowserModule { + private static class CustomBrowserModule extends BrowserModule { CustomBrowserModule(Provider applicationProvider) { super(applicationProvider); diff --git a/src/test/java/tests/usecases/ShadowRootTests.java b/src/test/java/tests/usecases/ShadowRootTests.java new file mode 100644 index 0000000..3422ced --- /dev/null +++ b/src/test/java/tests/usecases/ShadowRootTests.java @@ -0,0 +1,36 @@ +package tests.usecases; + +import aquality.selenium.elements.interfaces.ILabel; +import forms.ChromeDownloadsForm; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import tests.BaseTest; + +public class ShadowRootTests extends BaseTest { + private static final ChromeDownloadsForm form = new ChromeDownloadsForm(); + + @BeforeMethod + @Override + public void beforeMethod() { + ChromeDownloadsForm.open(); + } + + @Test + public void testExpandShadowRoot() { + Assert.assertNotNull(form.expandShadowRoot(), "Should be possible to expand shadow root and get Selenium native ShadowRoot object"); + Assert.assertNotNull(form.getDownloadsToolbarLabel().getElement(), "Should be possible do get the element hidden under the shadow"); + Assert.assertNotNull(form.getDownloadsToolbarLabel().findElementInShadowRoot(ChromeDownloadsForm.NESTED_SHADOW_ROOT_LOCATOR, "More actions menu", ILabel.class).getElement(), + "Should be possible to expand the nested shadow root and get the element from it"); + Assert.assertTrue(form.getMainContainerLabel().state().isDisplayed(), "Should be possible to check that element under the shadow is displayed"); + } + + @Test + public void testExpandShadowRootViaJs() { + Assert.assertNotNull(form.expandShadowRootViaJs(), "Should be possible to expand shadow root and get Selenium native ShadowRoot object"); + Assert.assertNotNull(form.getDownloadsToolbarLabelFromJs().getElement(), "Should be possible do get the element hidden under the shadow"); + Assert.assertNotNull(form.getDownloadsToolbarLabelFromJs().findElementInShadowRoot(ChromeDownloadsForm.NESTED_SHADOW_ROOT_LOCATOR, "More actions menu", ILabel.class).getElement(), + "Should be possible to expand the nested shadow root and get the element from it"); + Assert.assertTrue(form.getMainContainerLabelFromJs().state().isDisplayed(), "Should be possible to check that element under the shadow is displayed"); + } +} diff --git a/src/test/java/tests/usecases/devtools/DevToolsSessionTest.java b/src/test/java/tests/usecases/devtools/DevToolsSessionTest.java new file mode 100644 index 0000000..dc686f5 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/DevToolsSessionTest.java @@ -0,0 +1,25 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.DevToolsHandling; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; + +public class DevToolsSessionTest extends BaseTest { + private static DevToolsHandling devTools() { + return AqualityServices.getBrowser().devTools(); + } + + @Test + public void getAndCloseDevToolsSessionTest() { + String windowHandle = getBrowser().tabs().getCurrentTabHandle(); + Assert.assertFalse(devTools().hasActiveDevToolsSession(), "No DevTools session should be running initially"); + Assert.assertNotNull(devTools().getDevToolsSession(windowHandle), "Should be possible to get DevTools session"); + Assert.assertTrue(devTools().hasActiveDevToolsSession(), "DevTools session should be indicated as active after getting"); + devTools().closeDevToolsSession(); + Assert.assertFalse(devTools().hasActiveDevToolsSession(), "DevTools session should be indicated as not active after close"); + Assert.assertNotNull(devTools().getDevToolsSession(), "Should be possible to get a new DevTools session after close"); + Assert.assertTrue(devTools().hasActiveDevToolsSession(), "DevTools session should be indicated as active after getting for a second time"); + } +} diff --git a/src/test/java/tests/usecases/devtools/DeviceEmulationTest.java b/src/test/java/tests/usecases/devtools/DeviceEmulationTest.java new file mode 100644 index 0000000..3fe0bec --- /dev/null +++ b/src/test/java/tests/usecases/devtools/DeviceEmulationTest.java @@ -0,0 +1,77 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.EmulationHandling; +import com.google.common.collect.ImmutableMap; +import org.openqa.selenium.devtools.v104.emulation.Emulation; +import org.openqa.selenium.devtools.v104.emulation.model.DisplayFeature; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.TheInternetPage; + +import java.util.Optional; +import java.util.function.Supplier; + +public class DeviceEmulationTest extends BaseTest { + + private static final int WIDTH = 600; + private static final int HEIGHT = 1000; + private static final boolean IS_MOBILE = true; + private static final double SCALE_FACTOR = 50; + private static final DisplayFeature DISPLAY_FEATURE = new DisplayFeature(DisplayFeature.Orientation.HORIZONTAL, 0, 0); + + @Override + @BeforeMethod + protected void beforeMethod() { + getBrowser().maximize(); + } + + private static EmulationHandling emulation() { + return AqualityServices.getBrowser().devTools().emulation(); + } + + @Test + public void setAndClearDeviceMetricsOverrideTest() { + checkDeviceMetricsOverride(() -> emulation().setDeviceMetricsOverride( + WIDTH, HEIGHT, SCALE_FACTOR, IS_MOBILE)); + } + + @Test + public void setAndClearDeviceMetricsOverrideWithVersionSpecificParametersTest() { + checkDeviceMetricsOverride(() -> { + AqualityServices.getBrowser().devTools().sendCommand(Emulation.setDeviceMetricsOverride( + WIDTH, HEIGHT, SCALE_FACTOR, IS_MOBILE, Optional.empty(), Optional.empty(), Optional.empty(), + Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), Optional.empty(), + Optional.of(DISPLAY_FEATURE) + )); + AqualityServices.getLogger().info("success"); + }); + } + + @Test + public void setAndClearDeviceMetricsOverrideWithParametersMapTest() { + ImmutableMap.Builder params = ImmutableMap.builder(); + params.put("width", WIDTH); + params.put("height", HEIGHT); + params.put("deviceScaleFactor", SCALE_FACTOR); + params.put("mobile", IS_MOBILE); + params.put("displayFeature", DISPLAY_FEATURE); + checkDeviceMetricsOverride(() -> emulation().setDeviceMetricsOverride(params.build())); + } + + private void checkDeviceMetricsOverride(Runnable setAction) { + Supplier getWindowHeight = () -> getScriptResultOrDefault("getWindowSize.js", 0L); + final int initialValue = getWindowHeight.get().intValue(); + if (initialValue == HEIGHT) { + throw new IllegalArgumentException("To check that override works, initial value should differ from the new one"); + } + setAction.run(); + navigate(TheInternetPage.TABLES); + Assert.assertEquals(getWindowHeight.get(), HEIGHT, "Browser height should match to override value"); + emulation().clearDeviceMetricsOverride(); + AqualityServices.getBrowser().refresh(); + Assert.assertEquals(getWindowHeight.get(), initialValue, "Browser height should match to initial value after clear"); + } +} diff --git a/src/test/java/tests/usecases/devtools/DomMonitoringTest.java b/src/test/java/tests/usecases/devtools/DomMonitoringTest.java new file mode 100644 index 0000000..5749d3a --- /dev/null +++ b/src/test/java/tests/usecases/devtools/DomMonitoringTest.java @@ -0,0 +1,48 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.JavaScriptHandling; +import org.openqa.selenium.devtools.events.DomMutationEvent; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.forms.WelcomeForm; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeoutException; + +import static aquality.selenium.browser.AqualityServices.getConditionalWait; + +public class DomMonitoringTest extends BaseTest { + private static final Duration NEGATIVE_CONDITION_TIMEOUT = Duration.ofSeconds(5); + + private static JavaScriptHandling javaScriptEngine() { + return AqualityServices.getBrowser().javaScriptEngine(); + } + + @Test + public void testSubscribeToDomMutationEventAndDisableMonitoring() throws TimeoutException { + final String attributeName = "cheese"; + final String attributeValue = "Gouda"; + WelcomeForm welcomeForm = new WelcomeForm(); + + List attributeValueChanges = new ArrayList<>(); + javaScriptEngine().addDomMutatedListener(attributeValueChanges::add); + getBrowser().goTo(welcomeForm.getUrl()); + + welcomeForm.getSubTitleLabel().getJsActions().setAttribute(attributeName, attributeValue); + getConditionalWait().waitForTrue(() -> !attributeValueChanges.isEmpty(), + "Some mutation events should be found, should be possible to subscribe to DOM mutation event"); + Assert.assertEquals(attributeValueChanges.size(), 1, "Exactly one change in DOM is expected"); + DomMutationEvent record = attributeValueChanges.get(0); + Assert.assertEquals(record.getAttributeName(), attributeName, "Attribute name should match to expected"); + Assert.assertEquals(record.getCurrentValue(), attributeValue, "Attribute value should match to expected"); + + javaScriptEngine().reset(); + welcomeForm.getSubTitleLabel().getJsActions().setAttribute(attributeName, attributeName); + getConditionalWait().waitFor(() -> !attributeValueChanges.isEmpty(), NEGATIVE_CONDITION_TIMEOUT); + Assert.assertEquals(attributeValueChanges.size(), 1, "No more changes in DOM is expected, should be possible to unsubscribe from DOM mutation event"); + } +} diff --git a/src/test/java/tests/usecases/devtools/EmulationTests.java b/src/test/java/tests/usecases/devtools/EmulationTests.java new file mode 100644 index 0000000..7c05d72 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/EmulationTests.java @@ -0,0 +1,84 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AlertActions; +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.EmulationHandling; +import org.openqa.selenium.NoAlertPresentException; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.TheInternetPage; +import theinternet.forms.JavaScriptAlertsForm; + +import java.util.Collections; +import java.util.function.BooleanSupplier; +import java.util.function.Supplier; + +public class EmulationTests extends BaseTest { + + private static EmulationHandling emulation() { + return AqualityServices.getBrowser().devTools().emulation(); + } + + @Override + @BeforeMethod + protected void beforeMethod() { + } + + @Test + public void browserCanEmulateTest() { + Assert.assertTrue(emulation().canEmulate(), "Emulation should be supported in browser"); + } + + @Test + public void setScriptExecutionDisabledAndEnableAgainTest() { + AqualityServices.getBrowser().goTo(TheInternetPage.JAVASCRIPT_ALERTS.getAddress()); + JavaScriptAlertsForm alertsForm = new JavaScriptAlertsForm(); + alertsForm.getBtnJsAlert().click(); + AqualityServices.getBrowser().handleAlert(AlertActions.ACCEPT); + + emulation().setScriptExecutionDisabled(); + alertsForm.getBtnJsAlert().click(); + Assert.assertThrows(NoAlertPresentException.class, () -> AqualityServices.getBrowser().handleAlert(AlertActions.ACCEPT)); + + emulation().setScriptExecutionDisabled(false); + alertsForm.getBtnJsAlert().click(); + AqualityServices.getBrowser().handleAlert(AlertActions.ACCEPT); + } + + @Test + public void setTouchEmulationEnabledAndDisabledTest() { + BooleanSupplier isTouchEnabled = () -> getScriptResultOrDefault("isTouchEnabled.js", false); + if (isTouchEnabled.getAsBoolean()) { + throw new IllegalStateException("Touch should be initially disabled"); + } + emulation().setTouchEmulationEnabled(); + Assert.assertTrue(isTouchEnabled.getAsBoolean(), "Touch should be enabled"); + emulation().setTouchEmulationEnabled(false); + Assert.assertFalse(isTouchEnabled.getAsBoolean(), "Touch should be disabled"); + } + + @Test + public void setEmulatedMediaTest() { + final String emulatedMedia = "projection"; + final String width = "600"; + Supplier getMediaType = () -> getScriptResultOrDefault("getMediaType.js", ""); + String initialValue = getMediaType.get(); + if (initialValue.contains(emulatedMedia)) { + throw new IllegalStateException("Initial media type should differ from value to be set"); + } + emulation().setEmulatedMedia(emulatedMedia, Collections.singletonMap("width", width)); + Assert.assertEquals(getMediaType.get(), emulatedMedia, "Media type should equal to emulated"); + emulation().disableEmulatedMediaOverride(); + Assert.assertEquals(getMediaType.get(), initialValue, "Media type should equal to initial after disabling the override"); + emulation().setEmulatedMedia(Collections.singletonMap("media", emulatedMedia)); + Assert.assertEquals(getMediaType.get(), emulatedMedia, "Media type should equal to emulated"); + } + + @Test + public void settingDefaultBackgroundColorOverrideDoesNotThrowTest() { + emulation().setDefaultBackgroundColorOverride(0, 255, 38); + emulation().clearDefaultBackgroundColorOverride(); + } +} diff --git a/src/test/java/tests/usecases/devtools/InitializationScriptsTests.java b/src/test/java/tests/usecases/devtools/InitializationScriptsTests.java new file mode 100644 index 0000000..bf2a675 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/InitializationScriptsTests.java @@ -0,0 +1,84 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AlertActions; +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.InitializationScript; +import aquality.selenium.browser.devtools.JavaScriptHandling; +import org.openqa.selenium.NoAlertPresentException; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.TimeoutException; + +import static aquality.selenium.browser.AqualityServices.getConditionalWait; + +public class InitializationScriptsTests extends BaseTest { + private static final String SCRIPT = "confirm('Hello world')"; + private static final String SCRIPT_NAME = "alert"; + private static JavaScriptHandling javaScriptEngine() { + return AqualityServices.getBrowser().javaScriptEngine(); + } + + @Test + public void testAddInitializationScriptGetItThenRemoveOrClear() { + InitializationScript initScript = javaScriptEngine().addInitializationScript(SCRIPT_NAME, SCRIPT); + Assert.assertNotNull(initScript, "Some initialization script model should be returned"); + Assert.assertEquals(SCRIPT, initScript.getScriptSource(), "Saved script source should match to expected"); + Assert.assertEquals(SCRIPT_NAME, initScript.getScriptName(), "Saved script name should match to expected"); + getBrowser().refresh(); + getBrowser().handleAlert(AlertActions.ACCEPT); + getBrowser().refreshPageWithAlert(AlertActions.ACCEPT); + + Assert.assertTrue(javaScriptEngine().getInitializationScripts().contains(initScript),"Should be possible to read initialization scripts"); + + javaScriptEngine().removeInitializationScript(initScript); + getBrowser().refresh(); + Assert.assertThrows(NoAlertPresentException.class, () -> getBrowser().handleAlert(AlertActions.ACCEPT)); + Assert.assertTrue(javaScriptEngine().getInitializationScripts().isEmpty(), "Should be possible to read initialization scripts after remove"); + javaScriptEngine().addInitializationScript(SCRIPT_NAME, SCRIPT); + getBrowser().refreshPageWithAlert(AlertActions.ACCEPT); + Assert.assertEquals(javaScriptEngine().getInitializationScripts().size(), 1, "Exactly one script should be among initialization scripts"); + + javaScriptEngine().clearInitializationScripts(); + Assert.assertThrows(NoAlertPresentException.class, () -> getBrowser().handleAlert(AlertActions.ACCEPT)); + Assert.assertTrue(javaScriptEngine().getInitializationScripts().isEmpty(), "Should be possible to read initialization scripts after remove"); + + javaScriptEngine().addInitializationScript(SCRIPT_NAME, SCRIPT); + getBrowser().refreshPageWithAlert(AlertActions.ACCEPT); + javaScriptEngine().clearAll(); + Assert.assertThrows(NoAlertPresentException.class, () -> getBrowser().handleAlert(AlertActions.ACCEPT)); + Assert.assertTrue(javaScriptEngine().getInitializationScripts().isEmpty(), "Should be possible to read initialization scripts after clear all"); + } + + @Test(enabled = false) + public void testAddScriptCallbackBindingSubscribeAndUnsubscribeGetItThenRemoveOrClear() throws TimeoutException { + List executedBindings = new ArrayList<>(); + javaScriptEngine().startEventMonitoring(); + javaScriptEngine().addInitializationScript(SCRIPT_NAME, SCRIPT); + javaScriptEngine().addBindingCalledListener(executedBindings::add); + getBrowser().refreshPageWithAlert(AlertActions.ACCEPT); + + javaScriptEngine().addScriptCallbackBinding(SCRIPT_NAME); + Assert.assertThrows(NoAlertPresentException.class, () -> getBrowser().handleAlert(AlertActions.ACCEPT)); + getConditionalWait().waitForTrue(() -> executedBindings.contains(SCRIPT_NAME), "Subscription to JavaScriptCallbackExecuted event should work"); + int oldCount = executedBindings.size(); + getBrowser().refresh(); + Assert.assertTrue(executedBindings.size() > oldCount, "Another event should be noticed"); + Assert.assertTrue(javaScriptEngine().getScriptCallbackBindings().contains(SCRIPT_NAME), "Should be possible to read script callback bindings"); + oldCount = executedBindings.size(); + + javaScriptEngine().removeScriptCallbackBinding(SCRIPT_NAME); + Assert.assertTrue(javaScriptEngine().getScriptCallbackBindings().isEmpty(), "Should be possible to read script callback bindings after remove"); + javaScriptEngine().addScriptCallbackBinding(SCRIPT_NAME); + Assert.assertTrue(javaScriptEngine().getScriptCallbackBindings().contains(SCRIPT_NAME), "Should be possible to read script callback bindings"); + javaScriptEngine().clearScriptCallbackBindings(); + Assert.assertTrue(javaScriptEngine().getScriptCallbackBindings().isEmpty(), "Should be possible to read script callback bindings after remove"); + + javaScriptEngine().reset(); + getBrowser().refresh(); + Assert.assertEquals(executedBindings.size(), oldCount, "Another event should not be noticed, should be possible to unsubscribe from JavaScriptCallbackExecuted event"); + } +} diff --git a/src/test/java/tests/usecases/devtools/JavaScriptEventTests.java b/src/test/java/tests/usecases/devtools/JavaScriptEventTests.java new file mode 100644 index 0000000..86f0228 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/JavaScriptEventTests.java @@ -0,0 +1,58 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.JavaScriptHandling; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.forms.WelcomeForm; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; + +import static aquality.selenium.browser.AqualityServices.getConditionalWait; + +public class JavaScriptEventTests extends BaseTest { + private static final Duration NEGATIVE_CONDITION_TIMEOUT = Duration.ofSeconds(5); + private static JavaScriptHandling javaScriptEngine() { + return AqualityServices.getBrowser().javaScriptEngine(); + } + + @Test + public void testSubscribeToJavaScriptConsoleApiCalledEventAndUnsubscribeFromIt() { + final String consoleApiScript = "console.log('Hello world!')"; + List apiCalledMessages = new ArrayList<>(); + javaScriptEngine().addJavaScriptConsoleApiListener(consoleEvent -> apiCalledMessages.addAll(consoleEvent.getMessages())); + getBrowser().executeScript(consoleApiScript); + + boolean hasCountIncreased = getConditionalWait().waitFor(() -> !apiCalledMessages.isEmpty()); + Assert.assertTrue(hasCountIncreased, "Some JS console API events should have been recorded, should be possible to subscribe to JS Console API called event"); + + int previousCount = apiCalledMessages.size(); + + javaScriptEngine().stopEventMonitoring(); + getBrowser().executeScript(consoleApiScript); + getConditionalWait().waitFor(() -> apiCalledMessages.size() > previousCount, NEGATIVE_CONDITION_TIMEOUT); + Assert.assertEquals(apiCalledMessages.size(), previousCount, "No more JS console API events should be recorded, should be possible to unsubscribe from JS Console API called event"); + } + + @Test + public void testSubscribeToJavaScriptExceptionThrownEventAndUnsubscribeFromIt() { + WelcomeForm welcomeForm = new WelcomeForm(); + List errorMessages = new ArrayList<>(); + javaScriptEngine().addJavaScriptExceptionThrownListener(exception -> errorMessages.add(exception.getMessage())); + + getBrowser().goTo(welcomeForm.getUrl()); + welcomeForm.getSubTitleLabel().getJsActions().setAttribute("onclick", "throw new Error('Hello, world!')"); + welcomeForm.getSubTitleLabel().click(); + boolean isNotEmpty = getConditionalWait().waitFor(() -> !errorMessages.isEmpty()); + Assert.assertTrue(isNotEmpty, "Some JS exceptions events should have been recorded, should be possible to subscribe to JS Exceptions thrown event"); + + int previousCount = errorMessages.size(); + javaScriptEngine().stopEventMonitoring(); + welcomeForm.getSubTitleLabel().click(); + getConditionalWait().waitFor(() -> errorMessages.size() > previousCount, NEGATIVE_CONDITION_TIMEOUT); + Assert.assertEquals(previousCount, errorMessages.size(), "No more JS exceptions should be recorded, should be possible to unsubscribe from JS Exceptions thrown event"); + } +} diff --git a/src/test/java/tests/usecases/devtools/JavaScriptSnippetsTests.java b/src/test/java/tests/usecases/devtools/JavaScriptSnippetsTests.java new file mode 100644 index 0000000..b424109 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/JavaScriptSnippetsTests.java @@ -0,0 +1,57 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.JavaScript; +import aquality.selenium.browser.devtools.JavaScriptHandling; +import org.apache.commons.lang3.StringUtils; +import org.openqa.selenium.JavascriptException; +import org.openqa.selenium.ScriptKey; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.forms.FormAuthenticationForm; +import theinternet.forms.WelcomeForm; + +public class JavaScriptSnippetsTests extends BaseTest { + private static JavaScriptHandling javaScriptEngine() { + return AqualityServices.getBrowser().javaScriptEngine(); + } + + @Test + public void testPinScriptAndUnpinIt() { + String script = JavaScript.GET_ELEMENT_XPATH.getScript(); + WelcomeForm welcomeForm = new WelcomeForm(); + ScriptKey pinnedScript = javaScriptEngine().pinScript(script); + Assert.assertTrue(javaScriptEngine().getPinnedScripts().contains(pinnedScript), "Should be possible to read pinned scripts"); + getBrowser().goTo(welcomeForm.getUrl()); + String xpath = (String) welcomeForm.getSubTitleLabel().getJsActions().executeScript(pinnedScript); + Assert.assertTrue(StringUtils.isNotEmpty(xpath), "Pinned script should be possible to execute"); + String expectedValue = welcomeForm.getSubTitleLabel().getJsActions().getXPath(); + Assert.assertEquals(xpath, expectedValue, "Pinned script should return the same value"); + + javaScriptEngine().unpinScript(pinnedScript); + Assert.assertThrows(JavascriptException.class, + () -> welcomeForm.getSubTitleLabel().getJsActions().executeScript(pinnedScript)); + javaScriptEngine().reset(); + } + + @Test + public void testPinScriptWithoutReturnedValueAndUnpinAll() { + String valueToSet = "username"; + String script = JavaScript.SET_VALUE.getScript(); + ScriptKey pinnedScript = javaScriptEngine().pinScript(script); + + FormAuthenticationForm authenticationForm = new FormAuthenticationForm(); + getBrowser().goTo(authenticationForm.getUrl()); + getBrowser().waitForPageToLoad(); + + authenticationForm.getTxbUsername().getJsActions().executeScript(pinnedScript, valueToSet); + Assert.assertEquals(authenticationForm.getTxbUsername().getValue(), valueToSet, + String.format("Text should be '%s' after setting value via pinned JS", valueToSet)); + + javaScriptEngine().clearPinnedScripts(); + Assert.assertThrows(JavascriptException.class, + () -> authenticationForm.getTxbUsername().getJsActions().executeScript(pinnedScript)); + javaScriptEngine().reset(); + } +} diff --git a/src/test/java/tests/usecases/devtools/LogHttpExchangeTest.java b/src/test/java/tests/usecases/devtools/LogHttpExchangeTest.java new file mode 100644 index 0000000..88e4a80 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/LogHttpExchangeTest.java @@ -0,0 +1,38 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.TheInternetPage; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.List; + +public class LogHttpExchangeTest extends BaseTest { + private final Path logFilePath = new File("target/log/log.log").toPath(); + + private String getLastMessage() { + try { + List lines = Files.readAllLines(logFilePath); + return lines.isEmpty() ? null : lines.get(lines.size() - 1); + } catch (IOException e) { + return null; + } + } + + @Test + public void testEnablingHttpExchangeLogging() { + navigate(TheInternetPage.DROPDOWN); + String logMessage1 = getLastMessage(); + Assert.assertNotNull(logMessage1, "Some message should appear in log file and should not be empty"); + AqualityServices.getBrowser().network().enableHttpExchangeLogging(); + AqualityServices.getBrowser().getDriver().navigate().refresh(); + String logMessage2 = getLastMessage(); + Assert.assertNotNull(logMessage2, "Some message should appear in log file and should not be empty"); + Assert.assertNotEquals(logMessage2, logMessage1, "HTTP logging message should be in file, although no Aquality-actions performed"); + } +} diff --git a/src/test/java/tests/usecases/devtools/NetworkInterceptionTests.java b/src/test/java/tests/usecases/devtools/NetworkInterceptionTests.java new file mode 100644 index 0000000..e8bada1 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/NetworkInterceptionTests.java @@ -0,0 +1,163 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.NetworkHandling; +import com.google.common.net.MediaType; +import manytools.RequestHeadersForm; +import org.apache.hc.core5.http.HttpStatus; +import org.openqa.selenium.devtools.NetworkInterceptor; +import org.openqa.selenium.remote.http.HttpHandler; +import org.openqa.selenium.remote.http.HttpRequest; +import org.openqa.selenium.remote.http.HttpResponse; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.TheInternetPage; +import theinternet.forms.WelcomeForm; + +import java.io.ByteArrayInputStream; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Function; + +import static org.openqa.selenium.remote.http.Contents.utf8String; + +public class NetworkInterceptionTests extends BaseTest { + + private static final String SOME_PHRASE = "Creamy, delicious cheese!"; + + private static NetworkHandling network() { + return AqualityServices.getBrowser().network(); + } + + @Override + @BeforeMethod + protected void beforeMethod() { + } + + @Test + public void testAllRequestsInterception() { + WelcomeForm welcomeForm = new WelcomeForm(); + + NetworkInterceptor interceptor = network().interceptAllRequests(new HttpResponse() + .setStatus(HttpStatus.SC_OK) + .addHeader("Content-Type", MediaType.HTML_UTF_8.toString()) + .setContent(utf8String(SOME_PHRASE))); + Assert.assertNotNull(interceptor, "Network interceptor must not be null"); + getBrowser().goTo(welcomeForm.getUrl()); + Assert.assertTrue(getBrowser().getDriver().getPageSource().contains(SOME_PHRASE)); + + getBrowser().network().clearNetworkInterceptor(); + getBrowser().goTo(welcomeForm.getUrl()); + Assert.assertFalse(getBrowser().getDriver().getPageSource().contains(SOME_PHRASE)); + } + + @Test + public void testRequestsInterception() { + WelcomeForm welcomeForm = new WelcomeForm(); + NetworkInterceptor interceptor = network().startNetworkInterceptor((HttpHandler) request -> new HttpResponse() + .setStatus(HttpStatus.SC_OK) + .addHeader("Content-Type", MediaType.HTML_UTF_8.toString()) + .setContent(utf8String(SOME_PHRASE))); + Assert.assertNotNull(interceptor, "Network interceptor must not be null"); + getBrowser().goTo(welcomeForm.getUrl()); + Assert.assertTrue(getBrowser().getDriver().getPageSource().contains(SOME_PHRASE)); + + getBrowser().network().clearNetworkInterceptor(); + getBrowser().goTo(welcomeForm.getUrl()); + Assert.assertFalse(getBrowser().getDriver().getPageSource().contains(SOME_PHRASE)); + } + + @Test + public void testRequestsFilter() { + final String paramName = "Test"; + final String paramValue = "delicious cheese!"; + Function requestTransformer = request -> { + request.addHeader(paramName, paramValue); + return request; + }; + network().interceptTrafficWith(next -> req -> next.execute(requestTransformer.apply(req))); + Assert.assertEquals(new RequestHeadersForm().open().getNullableValue(paramName), paramValue, "Request should be modified"); + network().resetNetworkFilter(); + Assert.assertNotEquals(new RequestHeadersForm().open().getNullableValue(paramName), paramValue, "Request should not be modified"); + } + + @Test + public void testAddAndClearRequestHandler() + { + final String somePhrase = "delicious cheese!"; + NetworkInterceptor networkInterceptor = network().addRequestHandler(request -> true, request -> { + HttpResponse response = new HttpResponse(); + response.setContent(() -> new ByteArrayInputStream(somePhrase.getBytes())); + response.setStatus(200); + return response; + }); + Assert.assertNotNull(networkInterceptor, "Created network interceptor must not be null"); + + navigate(TheInternetPage.LOGIN); + Assert.assertTrue(getBrowser().getDriver().getPageSource().contains(somePhrase), "Request should be intercepted"); + network().clearNetworkInterceptor(); + navigate(TheInternetPage.LOGIN); + Assert.assertFalse(getBrowser().getDriver().getPageSource().contains(somePhrase), "Request should not be intercepted"); + } + + @Test + public void testAddAndClearResponseHandler() + { + final String somePhrase = "delicious cheese!"; + NetworkInterceptor networkInterceptor = network().addResponseHandler(response -> true, oldResponse -> { + HttpResponse response = new HttpResponse(); + response.setContent(() -> new ByteArrayInputStream(somePhrase.getBytes())); + response.setStatus(200); + return response; + }); + Assert.assertNotNull(networkInterceptor, "Created network interceptor must not be null"); + + navigate(TheInternetPage.LOGIN); + Assert.assertTrue(getBrowser().getDriver().getPageSource().contains(somePhrase), "Request should be intercepted"); + network().clearNetworkInterceptor(); + navigate(TheInternetPage.LOGIN); + Assert.assertFalse(getBrowser().getDriver().getPageSource().contains(somePhrase), "Request should not be intercepted"); + } + + @Test + public void testAddAndClearRequestTransformer() + { + final String paramName = "Test"; + final String paramValue = "delicious cheese!"; + NetworkInterceptor networkInterceptor = network().addRequestTransformer(request -> true, request -> { + request.addHeader(paramName, paramValue); + return request; + }); + Assert.assertNotNull(networkInterceptor, "Created network interceptor must not be null"); + Assert.assertEquals(new RequestHeadersForm().open().getNullableValue(paramName), paramValue, "Request should be modified"); + network().clearNetworkInterceptor(); + Assert.assertNotEquals(new RequestHeadersForm().open().getNullableValue(paramName), paramValue, "Request should not be modified"); + } + + @Test + public void testSubscribeToRequestSentEventAndUnsubscribeFromEvents() { + navigate(TheInternetPage.LOGIN); + AtomicInteger counter = new AtomicInteger(); + network().addRequestListener(requestWillBeSent -> counter.incrementAndGet()); + navigate(TheInternetPage.LOGIN); + Assert.assertTrue(counter.get() > 0, "Should be possible to listen to Request Sent events"); + int oldValue = counter.get(); + network().clearListeners(); + navigate(TheInternetPage.LOGIN); + Assert.assertEquals(counter.get(), oldValue, "Should be possible to clear event listeners"); + } + + @Test + public void testSubscribeToResponseReceivedEventAndUnsubscribeFromEvents() { + navigate(TheInternetPage.LOGIN); + AtomicInteger counter = new AtomicInteger(); + network().addResponseListener(responseReceived -> counter.incrementAndGet()); + navigate(TheInternetPage.LOGIN); + Assert.assertTrue(counter.get() > 0, "Should be possible to listen to Response Received events"); + int oldValue = counter.get(); + network().clearListeners(); + navigate(TheInternetPage.LOGIN); + Assert.assertEquals(counter.get(), oldValue, "Should be possible to clear event listeners"); + } +} diff --git a/src/test/java/tests/usecases/devtools/OverrideGeolocationTest.java b/src/test/java/tests/usecases/devtools/OverrideGeolocationTest.java new file mode 100644 index 0000000..c878eb2 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/OverrideGeolocationTest.java @@ -0,0 +1,37 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import forms.MyLocationForm; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; + +import static automationpractice.Constants.URL_MYLOCATIONORG; + +public class OverrideGeolocationTest extends BaseTest { + + private static final double LAT_FOR_OVERRIDE = 53.90772672521578; + private static final double LNG_FOR_OVERRIDE = 27.458060411865375; + + @Test + public void overrideGeolocationTest() { + + AqualityServices.getBrowser().goTo(URL_MYLOCATIONORG); + MyLocationForm form = new MyLocationForm(); + Assert.assertTrue(form.state().waitForDisplayed()); + double latDefault = form.getLatitude(); + double lngDefault = form.getLongitude(); + + AqualityServices.getBrowser().devTools().emulation().setGeolocationOverride(LAT_FOR_OVERRIDE, LNG_FOR_OVERRIDE); + AqualityServices.getBrowser().refresh(); + + Assert.assertEquals(form.getLatitude(), LAT_FOR_OVERRIDE); + Assert.assertEquals(form.getLongitude(), LNG_FOR_OVERRIDE); + + AqualityServices.getBrowser().devTools().emulation().clearGeolocationOverride(); + AqualityServices.getBrowser().refresh(); + + Assert.assertEquals(form.getLatitude(), latDefault); + Assert.assertEquals(form.getLongitude(), lngDefault); + } +} diff --git a/src/test/java/tests/usecases/devtools/OverrideUserAgentTest.java b/src/test/java/tests/usecases/devtools/OverrideUserAgentTest.java new file mode 100644 index 0000000..40ce67d --- /dev/null +++ b/src/test/java/tests/usecases/devtools/OverrideUserAgentTest.java @@ -0,0 +1,91 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.EmulationHandling; +import manytools.BrowserLanguageForm; +import manytools.UserAgentForm; +import org.openqa.selenium.devtools.idealized.Network; +import org.openqa.selenium.devtools.v104.emulation.Emulation; +import org.testng.Assert; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; +import tests.BaseTest; + +import java.util.Collections; +import java.util.Map; +import java.util.Optional; + +public class OverrideUserAgentTest extends BaseTest { + + private static final String CUSTOM_USER_AGENT = "Mozilla/5.0 (iPhone; CPU iPhone OS 6_1_4 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B350 Safari/8536.25"; + private static final String CUSTOM_ACCEPT_LANGUAGE = "be-BY"; + + private static EmulationHandling emulation() { + return AqualityServices.getBrowser().devTools().emulation(); + } + + @BeforeMethod + @Override + protected void beforeMethod() { + String defaultUserAgent = new UserAgentForm().open().getValue(); + if (CUSTOM_USER_AGENT.equals(defaultUserAgent)) { + throw new IllegalStateException("Default user agent header should be different from the custom one to check override"); + } + } + + @Test + public void overrideUserAgentTest() { + emulation().setUserAgentOverride(CUSTOM_USER_AGENT); + Assert.assertEquals(new UserAgentForm().open().getValue(), CUSTOM_USER_AGENT, "User agent should match to value set"); + } + + @Test + public void overrideUserAgentViaNetworkTest() { + AqualityServices.getBrowser().network().setUserAgent(CUSTOM_USER_AGENT); + Assert.assertEquals(new UserAgentForm().open().getValue(), CUSTOM_USER_AGENT, "User agent should match to value set"); + } + + @Test + public void overrideUserAgentViaNetworkModelTest() { + AqualityServices.getBrowser().network().setUserAgent(new Network.UserAgent(CUSTOM_USER_AGENT)); + Assert.assertEquals(new UserAgentForm().open().getValue(), CUSTOM_USER_AGENT, "User agent should match to value set"); + } + + @Test + public void overrideUserAgentByParametersMapTest() { + Map params = Collections.singletonMap("userAgent", CUSTOM_USER_AGENT); + emulation().setUserAgentOverride(params); + Assert.assertEquals(new UserAgentForm().open().getValue(), CUSTOM_USER_AGENT, "User agent should match to value set"); + } + + @Test + public void overrideUserAgentByVersionSpecificCommandTest() { + AqualityServices.getBrowser().devTools().sendCommand(Emulation.setUserAgentOverride(CUSTOM_USER_AGENT, + Optional.of(CUSTOM_ACCEPT_LANGUAGE), Optional.empty(), + Optional.empty())); + Assert.assertEquals(new UserAgentForm().open().getValue(), CUSTOM_USER_AGENT, "User agent should match to value set"); + } + + @Test + public void overrideUserAgentByCdpCommandTest() { + Map params = Collections.singletonMap("userAgent", CUSTOM_USER_AGENT); + AqualityServices.getBrowser().devTools().executeCdpCommand("Emulation.setUserAgentOverride", params); + Assert.assertEquals(new UserAgentForm().open().getValue(), CUSTOM_USER_AGENT, "User agent should match to value set"); + } + + @Test + public void overrideUserAgentAndLanguageTest() { + String defaultLanguage = new BrowserLanguageForm().open().getValue(); + String defaultUserAgent = new UserAgentForm().open().getValue(); + if (defaultLanguage.contains(CUSTOM_ACCEPT_LANGUAGE)) { + throw new IllegalStateException("Default accept-language header should be different from the custom one to check override"); + } + if (CUSTOM_USER_AGENT.equals(defaultUserAgent)) { + throw new IllegalStateException("Default user agent header should be different from the custom one to check override"); + } + + emulation().setUserAgentOverride(CUSTOM_USER_AGENT, Optional.of(CUSTOM_ACCEPT_LANGUAGE), Optional.empty()); + Assert.assertTrue(new BrowserLanguageForm().open().getValue().contains(CUSTOM_ACCEPT_LANGUAGE), "Accept-language header should match to value set"); + Assert.assertEquals(new UserAgentForm().open().getValue(), CUSTOM_USER_AGENT, "User agent should match to value set"); + } +} diff --git a/src/test/java/tests/usecases/devtools/PerformanceMetricsTest.java b/src/test/java/tests/usecases/devtools/PerformanceMetricsTest.java new file mode 100644 index 0000000..22aeb61 --- /dev/null +++ b/src/test/java/tests/usecases/devtools/PerformanceMetricsTest.java @@ -0,0 +1,36 @@ +package tests.usecases.devtools; + +import aquality.selenium.browser.AqualityServices; +import aquality.selenium.browser.devtools.DevToolsHandling; +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.TheInternetPage; + +import java.util.Map; + +public class PerformanceMetricsTest extends BaseTest { + private static DevToolsHandling devTools() { + return AqualityServices.getBrowser().devTools(); + } + + @Test + public void getAndCloseDevToolsSessionTest() { + devTools().enablePerformanceMonitoring(); + navigate(TheInternetPage.REDIRECTOR); + Map metrics = devTools().getPerformanceMetrics(); + Assert.assertFalse(metrics.isEmpty(), "Some metrics should be returned"); + devTools().disablePerformanceMonitoring(); + getBrowser().refresh(); + Assert.assertTrue(devTools().getPerformanceMetrics().isEmpty(), + "Metrics should have not been collected after performance monitoring have been disabled"); + devTools().enablePerformanceMonitoring("timeTicks"); + getBrowser().refresh(); + metrics = devTools().getPerformanceMetrics(); + Assert.assertFalse(metrics.isEmpty(), "Some metrics should be returned"); + getBrowser().refresh(); + Map otherMetrics = devTools().getPerformanceMetrics(); + Assert.assertFalse(otherMetrics.isEmpty(), "Some metrics should be returned"); + Assert.assertNotEquals(otherMetrics, metrics, "Some additional metrics should have been collected"); + } +} diff --git a/src/test/java/tests/usecases/devtools/RegisterBasicAuthTest.java b/src/test/java/tests/usecases/devtools/RegisterBasicAuthTest.java new file mode 100644 index 0000000..94836ce --- /dev/null +++ b/src/test/java/tests/usecases/devtools/RegisterBasicAuthTest.java @@ -0,0 +1,26 @@ +package tests.usecases.devtools; + +import org.testng.Assert; +import org.testng.annotations.Test; +import tests.BaseTest; +import theinternet.forms.BasicAuthForm; + +public class RegisterBasicAuthTest extends BaseTest { + + @Test + public void setBasicAuth() { + BasicAuthForm basicAuthForm = new BasicAuthForm(); + getBrowser().network().addBasicAuthentication(BasicAuthForm.getDomain(), BasicAuthForm.getUsername(), BasicAuthForm.getPassword()); + getBrowser().goTo(basicAuthForm.getUrl()); + Assert.assertTrue(basicAuthForm.isCongratulationsDisplayed()); + } + + @Test + public void clearBasicAuth() { + BasicAuthForm basicAuthForm = new BasicAuthForm(); + getBrowser().network().addBasicAuthentication(BasicAuthForm.getDomain(), BasicAuthForm.getUsername(), BasicAuthForm.getPassword()); + getBrowser().network().clearBasicAuthentication(); + getBrowser().goTo(basicAuthForm.getUrl()); + Assert.assertFalse(basicAuthForm.isCongratulationsDisplayed()); + } +} diff --git a/src/test/java/theinternet/forms/BasicAuthForm.java b/src/test/java/theinternet/forms/BasicAuthForm.java new file mode 100644 index 0000000..38bb5e9 --- /dev/null +++ b/src/test/java/theinternet/forms/BasicAuthForm.java @@ -0,0 +1,37 @@ +package theinternet.forms; + +import aquality.selenium.elements.interfaces.ILabel; +import org.openqa.selenium.By; + +public class BasicAuthForm extends TheInternetForm { + + private static final String USER_AND_PASS= "admin"; + private static final String DOMAIN = "the-internet.herokuapp.com"; + + private final ILabel lblCongratulationsLabel = getElementFactory().getLabel(By.xpath("//p[contains(., 'Congratulations')]"), "Congratulations"); + + public BasicAuthForm() { + super(By.xpath("//h3[contains(text(),'Basic Auth')]"), "Basic Auth"); + } + + public boolean isCongratulationsDisplayed() { + return lblCongratulationsLabel.state().isDisplayed(); + } + + @Override + protected String getUri() { + return "/basic_auth"; + } + + public static String getUsername() { + return USER_AND_PASS; + } + + public static String getPassword() { + return USER_AND_PASS; + } + + public static String getDomain() { + return DOMAIN; + } +} diff --git a/src/test/java/theinternet/forms/TheInternetForm.java b/src/test/java/theinternet/forms/TheInternetForm.java index b4ef8c9..67b8603 100644 --- a/src/test/java/theinternet/forms/TheInternetForm.java +++ b/src/test/java/theinternet/forms/TheInternetForm.java @@ -10,11 +10,11 @@ public abstract class TheInternetForm extends Form { super(locator, name); } - private final String theInternetFormUrl = "http://the-internet.herokuapp.com"; - private ILink elementalSeleniumLink = getElementFactory().getLink(By.xpath("//a[contains(@href,'elementalselenium')]"), "Elemental Selenium"); + private static final String THE_INTERNET_FORM_URL = "http://the-internet.herokuapp.com"; + private final ILink elementalSeleniumLink = getElementFactory().getLink(By.xpath("//a[contains(@href,'elementalselenium')]"), "Elemental Selenium"); public String getUrl() { - return theInternetFormUrl + getUri(); + return THE_INTERNET_FORM_URL + getUri(); } public void clickElementalSelenium() { diff --git a/src/test/java/theinternet/forms/WelcomeForm.java b/src/test/java/theinternet/forms/WelcomeForm.java index 62bc40b..be7733a 100644 --- a/src/test/java/theinternet/forms/WelcomeForm.java +++ b/src/test/java/theinternet/forms/WelcomeForm.java @@ -1,8 +1,10 @@ package theinternet.forms; +import aquality.selenium.elements.interfaces.ILabel; import org.openqa.selenium.By; public class WelcomeForm extends TheInternetForm { + private final ILabel lblSubtitle = getElementFactory().getLabel(By.xpath("//h2"), "Sub title"); public WelcomeForm() { super(By.xpath("//h1[contains(.,'Welcome to the-internet')]"), "Welcome to the-internet"); @@ -12,4 +14,8 @@ public WelcomeForm() { protected String getUri() { return ""; } + + public ILabel getSubTitleLabel() { + return lblSubtitle; + } } diff --git a/src/test/resources/TestSuite.xml b/src/test/resources/TestSuite.xml index 0883b71..3b190c1 100644 --- a/src/test/resources/TestSuite.xml +++ b/src/test/resources/TestSuite.xml @@ -23,6 +23,24 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/test/resources/getMediaType.js b/src/test/resources/getMediaType.js new file mode 100644 index 0000000..d2ed690 --- /dev/null +++ b/src/test/resources/getMediaType.js @@ -0,0 +1 @@ +return window.styleMedia.type; diff --git a/src/test/resources/getWindowSize.js b/src/test/resources/getWindowSize.js new file mode 100644 index 0000000..ffbc1b9 --- /dev/null +++ b/src/test/resources/getWindowSize.js @@ -0,0 +1 @@ +return document.documentElement.clientHeight; \ No newline at end of file diff --git a/src/test/resources/isTouchEnabled.js b/src/test/resources/isTouchEnabled.js new file mode 100644 index 0000000..055149d --- /dev/null +++ b/src/test/resources/isTouchEnabled.js @@ -0,0 +1,3 @@ +return ( 'ontouchstart' in window ) || + ( navigator.maxTouchPoints > 0 ) || + ( navigator.msMaxTouchPoints > 0 ); diff --git a/src/test/resources/settings.json b/src/test/resources/settings.json index 05739f9..e7080c9 100644 --- a/src/test/resources/settings.json +++ b/src/test/resources/settings.json @@ -8,7 +8,20 @@ "chrome": { "webDriverVersion": "latest", "capabilities": { - "enableVNC": true + }, + "options": { + "intl.accept_languages": "en", + "safebrowsing.enabled": "true", + "profile.default_content_settings.popups": "0", + "disable-popup-blocking": "true", + "download.prompt_for_download": "false", + "download.default_directory": "//home//selenium//downloads" + }, + "startArguments": [] + }, + "edge": { + "webDriverVersion": "latest", + "capabilities": { }, "options": { "intl.accept_languages": "en", @@ -23,7 +36,6 @@ "firefox": { "webDriverVersion": "latest", "capabilities": { - "enableVNC": true }, "options": { "intl.accept_languages": "en", @@ -49,8 +61,38 @@ "ignoreProtectedModeSettings": true } }, + "opera": { + "webDriverVersion": "latest", + "binaryLocation": "%USERPROFILE%\\AppData\\Local\\Programs\\Opera\\launcher.exe", + "capabilities": { + }, + "options": { + "intl.accept_languages": "en", + "safebrowsing.enabled": "true", + "profile.default_content_settings.popups": "0", + "disable-popup-blocking": "true", + "download.prompt_for_download": "false", + "download.default_directory": "./downloads" + }, + "startArguments": ["--remote-debugging-port=9222", "--no-sandbox", "--disable-dev-shm-usage"] + }, "safari": { "downloadDir": "/Users/username/Downloads" + }, + "yandex": { + "webDriverVersion": "102.0.5005.61", + "binaryLocation": "%USERPROFILE%\\AppData\\Local\\Yandex\\YandexBrowser\\Application\\browser.exe", + "capabilities": { + }, + "options": { + "intl.accept_languages": "en", + "safebrowsing.enabled": "true", + "profile.default_content_settings.popups": "0", + "disable-popup-blocking": "true", + "download.prompt_for_download": "false", + "download.default_directory": "//home//selenium//downloads" + }, + "startArguments": [] } }, "timeouts": { diff --git a/src/test/resources/settings.local.json b/src/test/resources/settings.local.json index a0bfaac..2bbe616 100644 --- a/src/test/resources/settings.local.json +++ b/src/test/resources/settings.local.json @@ -57,7 +57,7 @@ "timeoutImplicit" : 0, "timeoutCondition" : 30, "timeoutScript" : 10, - "timeoutPageLoad" : 30, + "timeoutPageLoad" : 60, "timeoutPollingInterval": 300, "timeoutCommand": 120 },