/** * Copyright 2017 Alfa Laboratory * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * http://www.apache.org/licenses/LICENSE-2.0 * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package ru.alfabank.tests.core.drivers; import com.codeborne.selenide.Configuration; import com.codeborne.selenide.WebDriverProvider; import lombok.extern.slf4j.Slf4j; import net.lightbody.bmp.BrowserMobProxy; import net.lightbody.bmp.BrowserMobProxyServer; import net.lightbody.bmp.client.ClientUtil; import net.lightbody.bmp.proxy.BlacklistEntry; import net.lightbody.bmp.proxy.CaptureType; import org.openqa.selenium.Dimension; import org.openqa.selenium.MutableCapabilities; import org.openqa.selenium.Proxy; import org.openqa.selenium.WebDriver; 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.opera.OperaDriver; import org.openqa.selenium.opera.OperaOptions; import org.openqa.selenium.remote.CapabilityType; import org.openqa.selenium.remote.DesiredCapabilities; import org.openqa.selenium.remote.LocalFileDetector; import org.openqa.selenium.remote.RemoteWebDriver; import org.openqa.selenium.safari.SafariDriver; import org.openqa.selenium.safari.SafariOptions; import ru.alfabank.alfatest.cucumber.api.AkitaScenario; import ru.alfabank.tests.core.helpers.BlackList; import ru.alfabank.tests.core.helpers.PropertyLoader; import java.net.MalformedURLException; import java.net.URI; import java.util.HashMap; import java.util.List; import java.util.Map; import static com.codeborne.selenide.WebDriverRunner.*; import static org.openqa.selenium.remote.CapabilityType.*; import static ru.alfabank.tests.core.helpers.PropertyLoader.loadProperty; import static ru.alfabank.tests.core.helpers.PropertyLoader.loadSystemPropertyOrDefault; /** * Провайдер драйверов, который позволяет запускать тесты локально или удаленно, используя Selenoid * Параметры запуска можно задавать, как системные переменные. * * Например, можно указать браузер, версию браузера, remote Url(где будут запущены тесты), ширину и высоту окна браузера, * при удаленном запуске имя сессии в Selenoid UI: * -Dbrowser=chrome -DbrowserVersion=63.0 -DremoteUrl=http://some/url -Dwidth=1200 -Dheight=800 * -DselenoidSessionName=MyProjectName -Doptions=--load-extension=my-custom-extension * Если параметр remoteUrl не указан - тесты будут запущены локально в заданном браузере последней версии. * Все необходимые опции можно прописывать в переменную options, разделяя их пробелом. * Если указан параметр remoteUrl и browser, но версия браузера не указана, * по умолчанию будет установлена версия latest * Если браузер не указан - по умолчанию будет запущен chrome * По умолчанию размер окна браузера при remote запуске равен 1920x1080 * Предусмотрена возможность запуска в режиме мобильного браузера (-Dbrowser=mobile) * Если selenoidSessionName не указан - имя сессии в Selenoid UI отображаться не будет * С указанием устройства, на котором будем эмулироваться запуск мобильного chrome браузера (-Ddevice=iPhone 6) * Если указан параметр headless, то браузеры firefox и chrome будут запускаться без GUI (-Dheadless=true) */ @Slf4j public class CustomDriverProvider implements WebDriverProvider { public final static String MOBILE_DRIVER = "mobile"; public final static String BROWSER = "browser"; public final static String REMOTE_URL = "remoteUrl"; public final static String HEADLESS = "headless"; public final static String WINDOW_WIDTH = "width"; public final static String WINDOW_HEIGHT = "height"; public final static String VERSION_LATEST = "latest"; public final static String LOCAL = "local"; public final static String TRUST_ALL_SERVERS = "trustAllServers"; public final static String NEW_HAR = "har"; public final static String SELENOID = "selenoid"; private final static String SELENOID_SESSION_NAME = "selenoidSessionName"; public final static int DEFAULT_WIDTH = 1920; public final static int DEFAULT_HEIGHT = 1080; private static BrowserMobProxy proxy = new BrowserMobProxyServer(); private String[] options = loadSystemPropertyOrDefault("options", "").split(" "); public static BrowserMobProxy getProxy() { return proxy; } /** * если установлен -Dproxy=true стартует прокси * har для прослушки указывается в application.properties * @param capabilities */ private void enableProxy(DesiredCapabilities capabilities) { proxy.setTrustAllServers(Boolean.valueOf(loadProperty(TRUST_ALL_SERVERS, "true"))); proxy.start(); Proxy seleniumProxy = ClientUtil.createSeleniumProxy(proxy); capabilities.setCapability(PROXY, seleniumProxy); capabilities.setCapability(ACCEPT_SSL_CERTS, Boolean.valueOf(loadProperty(ACCEPT_SSL_CERTS, "true"))); capabilities.setCapability(SUPPORTS_JAVASCRIPT, Boolean.valueOf(loadProperty(SUPPORTS_JAVASCRIPT, "true"))); proxy.enableHarCaptureTypes(CaptureType.REQUEST_CONTENT, CaptureType.REQUEST_HEADERS, CaptureType.RESPONSE_CONTENT, CaptureType.RESPONSE_HEADERS); proxy.newHar(loadProperty(NEW_HAR)); } @Override public WebDriver createDriver(DesiredCapabilities capabilities) { Configuration.browserSize = String.format("%sx%s", loadSystemPropertyOrDefault(WINDOW_WIDTH, DEFAULT_WIDTH), loadSystemPropertyOrDefault(WINDOW_HEIGHT, DEFAULT_HEIGHT)); String expectedBrowser = loadSystemPropertyOrDefault(BROWSER, CHROME); String remoteUrl = loadSystemPropertyOrDefault(REMOTE_URL, LOCAL); BlackList blackList = new BlackList(); boolean isProxyMode = loadSystemPropertyOrDefault(PROXY, false); if (isProxyMode) { enableProxy(capabilities); } log.info("remoteUrl=" + remoteUrl + " expectedBrowser= " + expectedBrowser + " BROWSER_VERSION=" + System.getProperty(CapabilityType.BROWSER_VERSION)); switch (expectedBrowser.toLowerCase()) { case (FIREFOX): return LOCAL.equalsIgnoreCase(remoteUrl) ? createFirefoxDriver(capabilities) : getRemoteDriver(getFirefoxDriverOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); case (MOBILE_DRIVER): return LOCAL.equalsIgnoreCase(remoteUrl) ? new ChromeDriver(getMobileChromeOptions(capabilities)) : getRemoteDriver(getMobileChromeOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); case (OPERA): return LOCAL.equalsIgnoreCase(remoteUrl) ? createOperaDriver(capabilities) : getRemoteDriver(getOperaRemoteDriverOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); case (SAFARI): return LOCAL.equalsIgnoreCase(remoteUrl) ? createSafariDriver(capabilities) : getRemoteDriver(getSafariDriverOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); case (INTERNET_EXPLORER): return LOCAL.equalsIgnoreCase(remoteUrl) ? createIEDriver(capabilities) : getRemoteDriver(getIEDriverOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); case (IE): return LOCAL.equalsIgnoreCase(remoteUrl) ? createIEDriver(capabilities) : getRemoteDriver(getIEDriverOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); case (EDGE): return LOCAL.equalsIgnoreCase(remoteUrl) ? createEdgeDriver(capabilities) : getRemoteDriver(getEdgeDriverOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); default: return LOCAL.equalsIgnoreCase(remoteUrl) ? createChromeDriver(capabilities) : getRemoteDriver(getChromeDriverOptions(capabilities), remoteUrl, blackList.getBlacklistEntries()); } } /** * Задает capabilities для запуска Remote драйвера для Selenoid * * @param capabilities - capabilities для установленного браузера * @param remoteUrl - url для запуска тестов, например http://remoteIP:4444/wd/hub * @return WebDriver */ private WebDriver getRemoteDriver(MutableCapabilities capabilities, String remoteUrl) { log.info("---------------run Remote Driver---------------------"); Boolean isSelenoidRun = loadSystemPropertyOrDefault(SELENOID, true); if (isSelenoidRun) { capabilities.setCapability("enableVNC", true); capabilities.setCapability("screenResolution", String.format("%sx%s", loadSystemPropertyOrDefault(WINDOW_WIDTH, DEFAULT_WIDTH), loadSystemPropertyOrDefault(WINDOW_HEIGHT, DEFAULT_HEIGHT))); String sessionName = loadSystemPropertyOrDefault(SELENOID_SESSION_NAME, ""); if (!sessionName.isEmpty()) { capabilities.setCapability("name", String.format("%s %s", sessionName, AkitaScenario.getInstance().getScenario().getName())); } } try { RemoteWebDriver remoteWebDriver = new RemoteWebDriver( URI.create(remoteUrl).toURL(), capabilities ); remoteWebDriver.setFileDetector(new LocalFileDetector()); return remoteWebDriver; } catch (MalformedURLException e) { throw new RuntimeException(e); } } /** * Задает capabilities для запуска Remote драйвера для Selenoid * со списком соответствующих URL, которые добавляются в Blacklist * URL для добавления в Blacklist могут быть указаны в формате регулярных выражений * * @param capabilities * @param remoteUrl * @param blacklistEntries - список url для добавления в Blacklist * @return WebDriver */ private WebDriver getRemoteDriver(MutableCapabilities capabilities, String remoteUrl, List<BlacklistEntry> blacklistEntries) { proxy.setBlacklist(blacklistEntries); return getRemoteDriver(capabilities, remoteUrl); } /** * Устанавливает ChromeOptions для запуска google chrome эмулирующего работу мобильного устройства (по умолчанию nexus 5) * Название мобильного устройства (device) может быть задано через системные переменные * * @return ChromeOptions */ private ChromeOptions getMobileChromeOptions(DesiredCapabilities capabilities) { log.info("---------------run CustomMobileDriver---------------------"); String mobileDeviceName = loadSystemPropertyOrDefault("device", "Nexus 5"); ChromeOptions chromeOptions = new ChromeOptions().addArguments("disable-extensions", "test-type", "no-default-browser-check", "ignore-certificate-errors"); Map<String, String> mobileEmulation = new HashMap<>(); chromeOptions.setHeadless(getHeadless()); mobileEmulation.put("deviceName", mobileDeviceName); chromeOptions.setExperimentalOption("mobileEmulation", mobileEmulation); chromeOptions.merge(capabilities); return chromeOptions; } /** * Задает options для запуска Chrome драйвера * options можно передавать, как системную переменную, например -Doptions=--load-extension=my-custom-extension * * @return ChromeOptions */ private ChromeOptions getChromeDriverOptions(DesiredCapabilities capabilities) { log.info("---------------Chrome Driver---------------------"); ChromeOptions chromeOptions = !options[0].equals("") ? new ChromeOptions().addArguments(options) : new ChromeOptions(); chromeOptions.setCapability(CapabilityType.BROWSER_VERSION, loadSystemPropertyOrDefault(CapabilityType.BROWSER_VERSION, VERSION_LATEST)); chromeOptions.setHeadless(getHeadless()); chromeOptions.merge(capabilities); return chromeOptions; } /** * Задает options для запуска Firefox драйвера * options можно передавать, как системную переменную, например -Doptions=--load-extension=my-custom-extension * * @return FirefoxOptions */ private FirefoxOptions getFirefoxDriverOptions(DesiredCapabilities capabilities) { log.info("---------------Firefox Driver---------------------"); FirefoxOptions firefoxOptions = !options[0].equals("") ? new FirefoxOptions().addArguments(options) : new FirefoxOptions(); capabilities.setVersion(loadSystemPropertyOrDefault(CapabilityType.BROWSER_VERSION, VERSION_LATEST)); firefoxOptions.setHeadless(getHeadless()); firefoxOptions.merge(capabilities); return firefoxOptions; } /** * Задает options для запуска Opera драйвера * options можно передавать, как системную переменную, например -Doptions=--load-extension=my-custom-extension * * @return operaOptions */ private OperaOptions getOperaDriverOptions(DesiredCapabilities capabilities) { log.info("---------------Opera Driver---------------------"); OperaOptions operaOptions = !options[0].equals("") ? new OperaOptions().addArguments(options) : new OperaOptions(); operaOptions.setCapability(CapabilityType.BROWSER_VERSION, loadSystemPropertyOrDefault(CapabilityType.BROWSER_VERSION, VERSION_LATEST)); operaOptions.merge(capabilities); return operaOptions; } /** * Задает options для запуска Opera драйвера в контейнере Selenoid * options можно передавать, как системную переменную, например -Doptions=--load-extension=my-custom-extension * * @return operaOptions */ private OperaOptions getOperaRemoteDriverOptions(DesiredCapabilities capabilities) { log.info("---------------Opera Driver---------------------"); OperaOptions operaOptions = !this.options[0].equals("") ? (new OperaOptions()).addArguments(this.options) : new OperaOptions(); operaOptions.setCapability("browserVersion", PropertyLoader.loadSystemPropertyOrDefault("browserVersion", "latest")); operaOptions.setCapability("browserName", "opera"); operaOptions.setBinary(PropertyLoader.loadSystemPropertyOrDefault("webdriver.opera.driver", "/usr/bin/opera")); operaOptions.merge(capabilities); return operaOptions; } /** * Задает options для запуска IE драйвера * options можно передавать, как системную переменную, например -Doptions=--load-extension=my-custom-extension * * @return internetExplorerOptions */ private InternetExplorerOptions getIEDriverOptions(DesiredCapabilities capabilities) { log.info("---------------IE Driver---------------------"); InternetExplorerOptions internetExplorerOptions = !options[0].equals("") ? new InternetExplorerOptions().addCommandSwitches(options) : new InternetExplorerOptions(); internetExplorerOptions.setCapability(CapabilityType.BROWSER_VERSION, loadSystemPropertyOrDefault(CapabilityType.BROWSER_VERSION, VERSION_LATEST)); internetExplorerOptions.setCapability("ie.usePerProcessProxy", "true"); internetExplorerOptions.setCapability("requireWindowFocus", "false"); internetExplorerOptions.setCapability("ie.browserCommandLineSwitches", "-private"); internetExplorerOptions.setCapability("ie.ensureCleanSession", "true"); internetExplorerOptions.merge(capabilities); return internetExplorerOptions; } /** * Задает options для запуска Edge драйвера * options можно передавать, как системную переменную, например -Doptions=--load-extension=my-custom-extension * * @return edgeOptions */ private EdgeOptions getEdgeDriverOptions(DesiredCapabilities capabilities) { log.info("---------------Edge Driver---------------------"); EdgeOptions edgeOptions = new EdgeOptions(); edgeOptions.setCapability(CapabilityType.BROWSER_VERSION, loadSystemPropertyOrDefault(CapabilityType.BROWSER_VERSION, VERSION_LATEST)); edgeOptions.merge(capabilities); return edgeOptions; } /** * Задает options для запуска Safari драйвера * options можно передавать, как системную переменную, например -Doptions=--load-extension=my-custom-extension * * @return SafariOptions */ private SafariOptions getSafariDriverOptions(DesiredCapabilities capabilities) { log.info("---------------Safari Driver---------------------"); SafariOptions safariOptions = new SafariOptions(); safariOptions.setCapability(CapabilityType.BROWSER_VERSION, loadSystemPropertyOrDefault(CapabilityType.BROWSER_VERSION, VERSION_LATEST)); safariOptions.merge(capabilities); return safariOptions; } /** * Создает экземпляр ChromeDriver с переданными capabilities и window dimensions * * @return WebDriver */ private WebDriver createChromeDriver(DesiredCapabilities capabilities) { ChromeDriver chromeDriver = new ChromeDriver(getChromeDriverOptions(capabilities)); return chromeDriver; } /** * Создает экземпляр FirefoxDriver с переданными capabilities и window dimensions * * @return WebDriver */ private WebDriver createFirefoxDriver(DesiredCapabilities capabilities) { FirefoxDriver firefoxDriver = new FirefoxDriver(getFirefoxDriverOptions(capabilities)); return firefoxDriver; } /** * Создает экземпляр OperaDriver с переданными capabilities и window dimensions * * @return WebDriver */ private WebDriver createOperaDriver(DesiredCapabilities capabilities) { OperaDriver operaDriver = new OperaDriver(getOperaDriverOptions(capabilities)); return operaDriver; } /** * Создает экземпляр InternetExplorerDriver с переданными capabilities и window dimensions * * @return WebDriver */ private WebDriver createIEDriver(DesiredCapabilities capabilities) { InternetExplorerDriver internetExplorerDriver = new InternetExplorerDriver(getIEDriverOptions(capabilities)); return internetExplorerDriver; } /** * Создает экземпляр EdgeDriver с переданными capabilities и window dimensions * * @return WebDriver */ private WebDriver createEdgeDriver(DesiredCapabilities capabilities) { EdgeDriver edgeDriver = new EdgeDriver(getEdgeDriverOptions(capabilities)); return edgeDriver; } /** * Создает экземпляр SafariDriver с переданными capabilities и window dimensions * * @return WebDriver */ private WebDriver createSafariDriver(DesiredCapabilities capabilities) { SafariDriver safariDriver = new SafariDriver(getSafariDriverOptions(capabilities)); return safariDriver; } /** * Читает значение параметра headless из application.properties * и selenide.headless из системных пропертей * * @return значение параметра headless или false, если он отсутствует */ private Boolean getHeadless() { Boolean isHeadlessApp = loadSystemPropertyOrDefault(HEADLESS, false); Boolean isHeadlessSys = Boolean.parseBoolean(System.getProperty("selenide." + HEADLESS, "false")); return isHeadlessApp || isHeadlessSys; } }