improve creation

master
io42630 2 years ago
parent 6859cb6493
commit 34c52c0023

@ -2,3 +2,15 @@
* Wrapper for `selenium` to make it easier to use. * Wrapper for `selenium` to make it easier to use.
* Must use `Chrome` (not `Chromium`). * Must use `Chrome` (not `Chromium`).
* Must supply `.properties` file with.
chrome.driver.path=
headless=false
download.dir=
* Usage:
var confPath = Path.of("/foo/tabdriver.properties");
var td = TabDriverBuilder.build(confPath);

@ -1,26 +1,28 @@
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion> <modelVersion>4.0.0</modelVersion>
<groupId>com.olexyn</groupId> <groupId>com.olexyn</groupId>
<artifactId>tabdriver</artifactId> <artifactId>tabdriver</artifactId>
<version>1.1</version> <version>1.2</version>
<packaging>jar</packaging> <packaging>jar</packaging>
<name>tabriver</name> <name>tabdriver</name>
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<xx.java.version>17</xx.java.version> <xx.java.version>17</xx.java.version>
<xx.selenium.version>3.141.59</xx.selenium.version>
<xx.webdriver.version>0.9.7376</xx.webdriver.version>
</properties> </properties>
<dependencyManagement> <dependencyManagement>
<dependencies> <dependencies>
<dependency> <dependency>
<groupId>com.olexyn</groupId> <groupId>com.olexyn</groupId>
<artifactId>zeebom</artifactId> <artifactId>zeebom</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.1</version>
<type>pom</type> <type>pom</type>
<scope>import</scope> <scope>import</scope>
</dependency> </dependency>
@ -37,43 +39,43 @@
<!-- must come before selenium --> <!-- must come before selenium -->
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-api</artifactId> <artifactId>selenium-api</artifactId>
<version>3.141.59</version> <version>${xx.selenium.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.webdriver</groupId> <groupId>org.seleniumhq.webdriver</groupId>
<artifactId>webdriver-selenium</artifactId> <artifactId>webdriver-selenium</artifactId>
<version>0.9.7376</version> <version>${xx.webdriver.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-chrome-driver</artifactId> <artifactId>selenium-chrome-driver</artifactId>
<version>3.141.59</version> <version>${xx.selenium.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.webdriver</groupId> <groupId>org.seleniumhq.webdriver</groupId>
<artifactId>webdriver-common</artifactId> <artifactId>webdriver-common</artifactId>
<version>0.9.7376</version> <version>${xx.webdriver.version}</version>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.webdriver</groupId> <groupId>org.seleniumhq.webdriver</groupId>
<artifactId>webdriver-support</artifactId> <artifactId>webdriver-support</artifactId>
<version>0.9.7376</version> <version>${xx.webdriver.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>org.seleniumhq.selenium</groupId> <groupId>org.seleniumhq.selenium</groupId>
<artifactId>selenium-java</artifactId> <artifactId>selenium-java</artifactId>
<version>3.141.59</version> <version>${xx.selenium.version}</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency> </dependency>
<dependency> <dependency>
<groupId>com.olexyn</groupId> <groupId>com.olexyn</groupId>
<artifactId>propconf</artifactId> <artifactId>propconf</artifactId>
<version>1.0-SNAPSHOT</version> <version>1.1</version>
<scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>
@ -101,5 +103,4 @@
</pluginManagement> </pluginManagement>
</build> </build>
</project> </project>

@ -2,6 +2,5 @@ package com.olexyn.tabdriver;
public interface Constants { public interface Constants {
String WORKING_DIR = "user.dir";
String EMPTY = ""; String EMPTY = "";
} }

@ -0,0 +1,283 @@
package com.olexyn.tabdriver;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import com.olexyn.min.log.LogU;
import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.NoSuchFrameException;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.chrome.ChromeDriverService;
public class TabDriver extends ChromeDriver implements JavascriptExecutor {
private final Map<String, Tab> TABS = new HashMap<>();
public TabDriver(ChromeDriverService service, Capabilities capabilities) {
super(service, capabilities);
manage().timeouts().implicitlyWait(10, TimeUnit.SECONDS);
}
public synchronized void registerCurrentTab(String purpose) {
Tab tab = new Tab(getWindowHandle());
tab.setName(getTitle());
tab.setUrl(getCurrentUrl());
tab.setPurpose(purpose);
TABS.put(tab.getHandle(), tab);
}
public synchronized Tab getCurrentTab() {
return TABS.get(getWindowHandle());
}
public synchronized List<Tab> getTabByPurpose(String purpose) {
return TABS.values().stream()
.filter(x -> x.getPurpose() == purpose)
.collect(Collectors.toList());
}
public synchronized String registerBlankTab(String purpose) {
Set<String> openTabHandles = getWindowHandles();
for (String openTabHandle : openTabHandles) {
if (!TABS.containsKey(openTabHandle)) {
Tab blankTab = new Tab(openTabHandle);
blankTab.setName("about:blank");
blankTab.setUrl("about:blank");
blankTab.setPurpose(purpose);
TABS.put(openTabHandle, blankTab);
return openTabHandle;
}
}
LogU.warnPlain("Not unregistered tab found.");
return null;
}
public synchronized String registerExistingTab(String purpose) {
String handle = getWindowHandle();
Tab tab = new Tab(handle);
tab.setName(getTitle());
tab.setUrl(getCurrentUrl());
tab.setPurpose(purpose);
TABS.put(handle, tab);
return handle;
}
public synchronized void switchToTab(String handle) {
for (Entry<String, Tab> entry : TABS.entrySet()) {
String tabHandle = entry.getKey();
if (tabHandle.equals(handle)) {
switchTo().window(tabHandle);
}
}
}
public synchronized void switchToTab(Tab tab) {
switchTo().window(tab.getHandle());
}
/**
* Opens a new tab, and "moves" the WebDriver to the new tab.
* If the current tab is empty, it is registered - this happens usually only with the initial tab of the session.
*/
public synchronized void newTab(String purpose) {
String currentUrl = getCurrentUrl();
if (currentUrl.isEmpty()
|| currentUrl.equals("data:,")
|| currentUrl.equals("about:blank")) {
registerExistingTab(purpose);
} else {
executeScript("window.open(arguments[0])");
switchToTab(registerBlankTab(purpose));
}
}
public synchronized void switchToTabByPurpose(String purpose) {
List<Tab> existingTabs = getTabByPurpose(purpose);
if (!existingTabs.isEmpty()) {
switchToTab(existingTabs.get(0));
} else {
Tab currentTab = getCurrentTab();
}
}
public synchronized void refresh() {
navigate().refresh();
}
@Override
public synchronized void get(String url) {
super.get(url);
}
@Override
public synchronized WebElement findElement(By by) {
return super.findElement(by);
}
public synchronized void executeScript(String script) {
((JavascriptExecutor) this).executeScript(script);
}
public synchronized void sendDeleteKeys(WebElement element, int n) {
for (int i = 0; i < n; i++) {
element.sendKeys(Keys.BACK_SPACE);
}
}
public synchronized WebElement filterElementListBy(List<WebElement> list, CRITERIA criteria, String text) {
for (WebElement element : list) {
String toEvaluate = switch (criteria) {
case CLASS -> element.getClass().getName();
case TEXT -> element.getText();
case TAG -> element.getTagName();
case HREF -> element.getAttribute("href");
case ID -> element.getAttribute("id");
case TITLE -> element.getAttribute("title");
case NONE -> text;
};
if (toEvaluate != null && toEvaluate.contains(text)) { return element; }
}
return null;
}
private static final String FRAME_ID_DEFAULT_CONTENT = "defaultContent";
private static final String FRAME_ID_NONE_FOUND = "noneFound";
/**
* Collects all frames accessible to WebDriver.
*/
public synchronized Map<String, String> collectAllFrames() throws NoSuchFrameException {
Map<String, String> mapOfCollectedSources = new HashMap<>();
switchTo().defaultContent();
mapOfCollectedSources.put(FRAME_ID_DEFAULT_CONTENT, getPageSource());
for (int i = 0; i < 10; i++) {
try {
switchTo().defaultContent();
switchTo().frame(i);
mapOfCollectedSources.put(String.valueOf(i), getPageSource());
} catch (NoSuchFrameException e) {
return mapOfCollectedSources;
}
}
return mapOfCollectedSources;
}
public synchronized String findFrameContainingCharSeq(Map<String, String> mapOfCollectedSources, String string) {
for (Entry<String, String> entry : mapOfCollectedSources.entrySet()) {
if (entry.getValue().contains(string)) {
return entry.getKey();
}
}
return FRAME_ID_NONE_FOUND;
}
// @Override
// public boolean isJavascriptEnabled() {
// return false;
// }
public enum CRITERIA {
CLASS,
TEXT,
TAG,
HREF,
NONE,
ID,
TITLE
}
public synchronized void switchToFrameContainingCharSeq(String charSeq) {
switchTo().defaultContent();
sleep(500);
final String frameId = findFrameContainingCharSeq(collectAllFrames(), charSeq);
sleep(400);
switch (frameId) {
case FRAME_ID_DEFAULT_CONTENT:
switchTo().defaultContent();
break;
case FRAME_ID_NONE_FOUND:
break;
default:
switchTo().frame(Integer.parseInt(frameId));
}
}
public static void sleep(long milli) {
try {
Thread.sleep(milli);
} catch (InterruptedException e) {
LogU.warnPlain("SLEEP was INTERRUPED.");
}
}
/**
* Return the first occurrence of specified class that has specified label.
*/
public synchronized WebElement getWhere(String className, CRITERIA criteria, String text) {
switchToFrameContainingCharSeq(text);
List<WebElement> elements = findElements(By.className(className));
return filterElementListBy(elements, criteria, text);
}
public synchronized WebElement getWhere(String className) {
switchToFrameContainingCharSeq(className);
List<WebElement> elements = findElements(By.className(className));
return filterElementListBy(elements, CRITERIA.NONE, Constants.EMPTY);
}
public synchronized void followContainedLink(WebElement element) {
String link = element.getAttribute("href");
if (link != null) { navigate().to(link); }
}
public synchronized void setRadio(By by, boolean checked) {
((JavascriptExecutor) this).executeScript("arguments[0].checked = " + checked + ";", findElement(by));
}
public synchronized void setComboByDataValue(By comboBy, String dataValue) {
WebElement combo = findElement(comboBy);
combo.click();
combo.findElement(By.cssSelector("li[data-value='" + dataValue + "']")).click();
}
/**
* Any-Match.
*/
public synchronized WebElement getByFieldValue(String type, String field, String value) {
return findElement(By.cssSelector(type + "[" + field + "*='" + value + "']"));
}
public synchronized void clickByFieldValue(String type, String field, String value) {
var we = getByFieldValue(type, field, value);
click(we);
}
/**
* @param field can contain wildcards like "class*" or "class^"
* @param value can contain partial matches like "last-"
*/
public synchronized WebElement getByFieldValue(SearchContext searchContext, String type, String field, String value) {
return searchContext.findElement(By.cssSelector(type + "[" + field + "='" + value + "']"));
}
public synchronized WebElement getByText(String text) {
return findElement(By.xpath("//*[contains(text(),'" + text + "')]"));
}
public synchronized void click(WebElement we) {
executeScript("arguments[0].click();", we);
}
}

@ -0,0 +1,59 @@
package com.olexyn.tabdriver;
import java.nio.file.Path;
import java.util.HashMap;
import com.olexyn.PropConf;
import lombok.experimental.UtilityClass;
import org.openqa.selenium.chrome.ChromeDriverService;
import org.openqa.selenium.chrome.ChromeOptions;
import org.openqa.selenium.remote.CapabilityType;
import org.openqa.selenium.remote.DesiredCapabilities;
@UtilityClass
public class TabDriverBuilder {
private static Path CONFIG_PATH;
public static TabDriver build(Path path) {
CONFIG_PATH = path;
if (CONFIG_PATH == null) {
throw new RuntimeException("CONFIG_PATH must be set to an absolute path.");
}
return new TabDriver(configureService(), configureCapabilities());
}
private static ChromeDriverService configureService() {
PropConf.loadProperties(CONFIG_PATH.toString());
var path = Path.of(PropConf.get("chrome.driver.path"));
return new ChromeDriverService.Builder()
.usingDriverExecutable(path.toFile())
.usingAnyFreePort()
.build();
}
private static DesiredCapabilities configureCapabilities() {
var cap = DesiredCapabilities.chrome();
cap.setCapability(CapabilityType.ACCEPT_SSL_CERTS, true);
ChromeOptions options = new ChromeOptions();
options.addArguments("--start-maximized");
if (PropConf.is("headless")) {
options.addArguments("--window-size=1920,1080");
options.addArguments("--headless");
}
// see also https://chromium.googlesource.com/chromium/src/+/master/chrome/common/pref_names.cc
HashMap<String, Object> chromePrefs = new HashMap<>();
chromePrefs.put("profile.default_content_settings.popups", 0);
chromePrefs.put("download.default_directory", PropConf.get("download.dir"));
chromePrefs.put("download.prompt_for_download", false);
options.setExperimentalOption("prefs", chromePrefs);
cap.setCapability(ChromeOptions.CAPABILITY, options);
return cap;
}
}

@ -1,3 +0,0 @@
chrome.driver.path=
headless=false
download.dir=
Loading…
Cancel
Save