package com.github.andreptb.fitnesse.selenium; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.function.BiConsumer; import java.util.function.BiFunction; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.lang3.EnumUtils; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.math.NumberUtils; import org.openqa.selenium.Capabilities; import org.openqa.selenium.chrome.ChromeOptions; import org.openqa.selenium.firefox.FirefoxDriver; import org.openqa.selenium.firefox.FirefoxProfile; import org.openqa.selenium.ie.InternetExplorerDriver; import org.openqa.selenium.remote.DesiredCapabilities; /** * Utility class to produce browser driver capabilities. Parses and inject default capabilities commonly used in test environments */ public class WebDriverCapabilitiesHelper { private static final String FIREFOX_ALLOWED_DOWNLOAD_CONTENT_TYPES = "application/msword, application/csv, application/ris, text/csv, image/png, application/pdf, text/html, text/plain, application/zip, application/x-zip, application/x-zip-compressed, application/download, application/octet-stream"; /** * Pattern to parse capability string. Expected format: key='value' or key="value" */ private static final Pattern ENCODED_CONFIG_PATTERN = Pattern.compile("\\s*([^=]+)=['\"]([^'\"]+)['\"]"); /** * Enum to inject preferences and capabilities according to the browser. Will inject default preferences or capabilities if applicable */ private enum CapabilitiesAndPreferencesInjector { chrome((capabilities, preferences) -> { ChromeOptions chromeOptions = new ChromeOptions(); CapabilitiesAndPreferencesInjector.applyIfUndefined("disable-popup-blocking", Boolean.TRUE.toString(), preferences::getOrDefault, preferences::put); chromeOptions.setExperimentalOption("prefs", preferences); capabilities.setCapability(ChromeOptions.CAPABILITY, chromeOptions); }), firefox((capabilities, preferences) -> { FirefoxProfile firefoxProfile = new FirefoxProfile(); firefoxProfile.setAcceptUntrustedCertificates(true); preferences.forEach((key, value) -> firefoxProfile.setPreference(key, value)); CapabilitiesAndPreferencesInjector.applyIfUndefined("browser.download.folderList", 2, firefoxProfile::getIntegerPreference, firefoxProfile::setPreference); CapabilitiesAndPreferencesInjector.applyIfUndefined("plugin.state.java", 2, firefoxProfile::getIntegerPreference, firefoxProfile::setPreference); CapabilitiesAndPreferencesInjector.applyIfUndefined("security.enable_java", true, firefoxProfile::getBooleanPreference, firefoxProfile::setPreference); CapabilitiesAndPreferencesInjector.applyIfUndefined("browser.helperApps.neverAsk.saveToDisk", WebDriverCapabilitiesHelper.FIREFOX_ALLOWED_DOWNLOAD_CONTENT_TYPES, firefoxProfile::getStringPreference, firefoxProfile::setPreference); capabilities.setCapability(FirefoxDriver.PROFILE, firefoxProfile); }), internetexplorer((capabilities, preferences) -> { capabilities.setCapability(InternetExplorerDriver.INTRODUCE_FLAKINESS_BY_IGNORING_SECURITY_DOMAINS, true); }); private BiConsumer<DesiredCapabilities, Map<String, String>> injector; private CapabilitiesAndPreferencesInjector(BiConsumer<DesiredCapabilities, Map<String, String>> injector) { this.injector = injector; } private static <K, V> void applyIfUndefined(K key, V defaultValue, BiFunction<K, V, V> configProvider, BiConsumer<K, V> configApplier) { if (StringUtils.isBlank(Objects.toString(defaultValue, null))) { return; } configApplier.accept(key, configProvider.apply(key, defaultValue)); } } /** * Creates {@link Capabilities} from string. Injects default preferences to supported browsers * Supported formats: * <p> * key1='value1' key2='value with space 2' key3='value3' * </p> * <p> * key1="value1" key2="value with space 2" key3="value3" * </p> * * @see CapabilitiesAndPreferencesInjector * @param browser Used to determine default configurations to inject * @param capabilities {@link String} * @param preferences Directory to save browser downloaded files * @return capabilitiesInstance which is an instanceof {@link Capabilities} */ public DesiredCapabilities parse(String browser, String capabilities, String preferences) { DesiredCapabilities desiredCapabilities = new DesiredCapabilities(); parseFromString(capabilities, desiredCapabilities::setCapability); Map<String, String> parsedPreferences = new HashMap<>(); parseFromString(capabilities, desiredCapabilities::setCapability); parseFromString(preferences, parsedPreferences::put); CapabilitiesAndPreferencesInjector entry = Optional.ofNullable(EnumUtils.getEnum(CapabilitiesAndPreferencesInjector.class, browser)) .orElse(EnumUtils.getEnum(CapabilitiesAndPreferencesInjector.class, StringUtils.deleteWhitespace(desiredCapabilities.getBrowserName()))); if (entry != null) { entry.injector.accept(desiredCapabilities, parsedPreferences); } return desiredCapabilities; } private <K, V> void parseFromString(String encodedConfig, BiConsumer<String, String> applier) { if (StringUtils.isBlank(encodedConfig)) { return; } Matcher matcher = WebDriverCapabilitiesHelper.ENCODED_CONFIG_PATTERN.matcher(encodedConfig); while (matcher.find()) { String value = matcher.group(2); if (StringUtils.isNotBlank(value)) { applier.accept(matcher.group(NumberUtils.INTEGER_ONE), value); } } } }