Skip to content

Commit 1269ab0

Browse files
committed
Init
* Remove demo because the test is the demo
1 parent c7c6d37 commit 1269ab0

File tree

16 files changed

+997
-88
lines changed

16 files changed

+997
-88
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
[![Build](https://img.shields.io/github/actions/workflow/status/xdev-software/selenium-elements/check-build.yml?branch=develop)](https://github.com/xdev-software/selenium-elements/actions/workflows/check-build.yml?query=branch%3Adevelop)
33
[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=xdev-software_selenium-elements&metric=alert_status)](https://sonarcloud.io/dashboard?id=xdev-software_selenium-elements)
44

5-
# Elements for Selenium
5+
# <img src="https://raw.githubusercontent.com/SeleniumHQ/seleniumhq.github.io/690acbad7b4bf4656f116274809765db64e6ccf7/website_and_docs/static/images/logos/webdriver.svg" height=24 /> Elements for Selenium
66

77

88
## Installation

pom.xml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
<modules>
1818
<module>selenium-elements</module>
19-
<module>selenium-elements-demo</module>
2019
</modules>
2120

2221
<properties>

selenium-elements-demo/pom.xml

Lines changed: 0 additions & 85 deletions
This file was deleted.

selenium-elements/pom.xml

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
<connection>scm:git:https://github.com/xdev-software/selenium-elements.git</connection>
1919
</scm>
2020

21-
<inceptionYear>2023</inceptionYear>
21+
<inceptionYear>2025</inceptionYear>
2222

2323
<organization>
2424
<name>XDEV Software</name>
@@ -49,6 +49,81 @@
4949
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
5050
</properties>
5151

52+
<dependencies>
53+
<dependency>
54+
<groupId>org.seleniumhq.selenium</groupId>
55+
<artifactId>selenium-support</artifactId>
56+
<version>4.33.0</version>
57+
<exclusions>
58+
<!-- Tracing is not needed -->
59+
<exclusion>
60+
<groupId>io.opentelemetry</groupId>
61+
<artifactId>*</artifactId>
62+
</exclusion>
63+
<!-- No unused, beta-grade, RUST blobs -->
64+
<exclusion>
65+
<groupId>org.seleniumhq.selenium</groupId>
66+
<artifactId>selenium-manager</artifactId>
67+
</exclusion>
68+
</exclusions>
69+
</dependency>
70+
<dependency>
71+
<groupId>org.javassist</groupId>
72+
<artifactId>javassist</artifactId>
73+
<version>3.30.2-GA</version>
74+
</dependency>
75+
<dependency>
76+
<groupId>org.slf4j</groupId>
77+
<artifactId>slf4j-api</artifactId>
78+
<version>2.0.17</version>
79+
</dependency>
80+
81+
<!-- Tests -->
82+
<dependency>
83+
<groupId>org.junit.jupiter</groupId>
84+
<artifactId>junit-jupiter</artifactId>
85+
<version>5.13.1</version>
86+
<scope>test</scope>
87+
</dependency>
88+
<dependency>
89+
<groupId>org.slf4j</groupId>
90+
<artifactId>slf4j-simple</artifactId>
91+
<version>2.0.17</version>
92+
<scope>test</scope>
93+
</dependency>
94+
<dependency>
95+
<groupId>software.xdev</groupId>
96+
<artifactId>testcontainers-selenium</artifactId>
97+
<version>1.2.2</version>
98+
</dependency>
99+
<dependency>
100+
<groupId>org.seleniumhq.selenium</groupId>
101+
<artifactId>selenium-firefox-driver</artifactId>
102+
<version>4.33.0</version>
103+
<scope>test</scope>
104+
<exclusions>
105+
<!-- No unused, beta-grade, RUST blobs -->
106+
<exclusion>
107+
<groupId>org.seleniumhq.selenium</groupId>
108+
<artifactId>selenium-manager</artifactId>
109+
</exclusion>
110+
</exclusions>
111+
</dependency>
112+
<dependency>
113+
<groupId>org.seleniumhq.selenium</groupId>
114+
<artifactId>selenium-chrome-driver</artifactId>
115+
<version>4.33.0</version>
116+
<scope>test</scope>
117+
<exclusions>
118+
<!-- No unused, beta-grade, RUST blobs -->
119+
<exclusion>
120+
<groupId>org.seleniumhq.selenium</groupId>
121+
<artifactId>selenium-manager</artifactId>
122+
</exclusion>
123+
</exclusions>
124+
</dependency>
125+
</dependencies>
126+
52127
<build>
53128
<pluginManagement>
54129
<plugins>
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package software.xdev.selenium.elements;
2+
3+
import java.util.Arrays;
4+
import java.util.LinkedHashSet;
5+
import java.util.List;
6+
import java.util.stream.Collectors;
7+
8+
import org.openqa.selenium.By;
9+
import org.openqa.selenium.NoSuchElementException;
10+
import org.openqa.selenium.SearchContext;
11+
import org.openqa.selenium.WebElement;
12+
13+
14+
/**
15+
* Chains multiple {@link By Bys} together.
16+
*
17+
* @apiNote You should consider using {@link org.openqa.selenium.support.pagefactory.ByChained} as it's more performant
18+
*/
19+
@SuppressWarnings("java:S2160") // Not needed
20+
public class ByAnd extends By
21+
{
22+
private final By[] bys;
23+
24+
public ByAnd(final By... bys)
25+
{
26+
this.bys = bys;
27+
}
28+
29+
@Override
30+
public WebElement findElement(final SearchContext context)
31+
{
32+
final List<WebElement> elements = this.findElements(context);
33+
if(elements.isEmpty())
34+
{
35+
throw new NoSuchElementException("Cannot locate an element using " + this);
36+
}
37+
return elements.get(0);
38+
}
39+
40+
@Override
41+
public List<WebElement> findElements(final SearchContext context)
42+
{
43+
if(this.bys.length == 0)
44+
{
45+
return List.of();
46+
}
47+
48+
final LinkedHashSet<WebElement> elements = new LinkedHashSet<>(context.findElements(this.bys[0]));
49+
for(int i = 1; i < this.bys.length; i++)
50+
{
51+
if(elements.isEmpty()) // No match!
52+
{
53+
return List.of();
54+
}
55+
56+
elements.retainAll(context.findElements(this.bys[i]));
57+
}
58+
59+
return elements.stream().toList();
60+
}
61+
62+
@Override
63+
public String toString()
64+
{
65+
return "By.and({"
66+
+ Arrays.stream(this.bys)
67+
.map(By::toString)
68+
.collect(Collectors.joining(","))
69+
+ "}";
70+
}
71+
}
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
package software.xdev.selenium.elements;
2+
3+
import java.time.Duration;
4+
import java.util.function.Function;
5+
import java.util.stream.Collectors;
6+
import java.util.stream.IntStream;
7+
import java.util.stream.Stream;
8+
9+
import org.openqa.selenium.By;
10+
import org.openqa.selenium.JavascriptExecutor;
11+
import org.openqa.selenium.SearchContext;
12+
import org.openqa.selenium.WebDriver;
13+
import org.openqa.selenium.WebElement;
14+
import org.openqa.selenium.support.pagefactory.ByChained;
15+
import org.openqa.selenium.support.ui.WebDriverWait;
16+
17+
import software.xdev.selenium.elements.instantiator.ElementInstantiator;
18+
import software.xdev.selenium.elements.instantiator.ElementInstantiatorInstance;
19+
20+
21+
/**
22+
* Shorthand interface for common element lookup and interaction strategies/methods.
23+
*/
24+
public interface CanFindElements
25+
{
26+
Duration DEFAULT_WAIT_UNTIL_DURATION = Duration.ofSeconds(10);
27+
28+
default By byAttribute(final String attribute, final String value)
29+
{
30+
return By.cssSelector("[" + attribute + "='" + value + "']");
31+
}
32+
33+
default By byClassNamePart(final String className)
34+
{
35+
return By.cssSelector("." + className);
36+
}
37+
38+
default WebElement waitForFirstChained(final By... chainedBys)
39+
{
40+
return this.waitForFirst(new ByChained(chainedBys));
41+
}
42+
43+
default WebElement waitForFirstAnd(final By... andBys)
44+
{
45+
return this.waitForFirst(new ByAnd(andBys));
46+
}
47+
48+
default WebElement waitForFirst(final By by)
49+
{
50+
return this.waitUntil(wd -> this.determineSearchContext(wd).findElement(by));
51+
}
52+
53+
default <T extends WebElement> T waitForFirst(final Class<T> clazz)
54+
{
55+
return this.waitForFirst(clazz, null);
56+
}
57+
58+
default <T extends WebElement> T waitForFirst(
59+
final Class<T> clazz,
60+
final By additionalAndBy)
61+
{
62+
return this.waitForFirst(clazz, additionalAndBy, DEFAULT_WAIT_UNTIL_DURATION);
63+
}
64+
65+
default <T extends WebElement> T waitForFirst(
66+
final Class<T> clazz,
67+
final By additionalAndBy,
68+
final Duration duration)
69+
{
70+
return this.elementProxyCreator().find(
71+
by -> this.waitUntil(
72+
wd -> this.determineSearchContext(wd)
73+
.findElement(additionalAndBy != null ? new ByAnd(by, additionalAndBy) : by),
74+
duration),
75+
clazz);
76+
}
77+
78+
default ElementInstantiator elementProxyCreator()
79+
{
80+
return ElementInstantiatorInstance.instance();
81+
}
82+
83+
default <V> V waitUntil(final Function<WebDriver, V> isTrue)
84+
{
85+
return this.waitUntil(isTrue, DEFAULT_WAIT_UNTIL_DURATION);
86+
}
87+
88+
default <V> V waitUntil(final Function<WebDriver, V> isTrue, final Duration duration)
89+
{
90+
return new WebDriverWait(this.getWebDriver(), duration).until(isTrue);
91+
}
92+
93+
default <T extends WebElement> T waitForFirstByClassName(final Class<T> clazz, final String className)
94+
{
95+
return this.waitForFirst(clazz, By.cssSelector("." + className));
96+
}
97+
98+
default SearchContext determineSearchContext(final WebDriver webDriver)
99+
{
100+
return webDriver;
101+
}
102+
103+
WebDriver getWebDriver();
104+
105+
default Object executeScript(final String script, final Object... args)
106+
{
107+
if(this.getWebDriver() instanceof final JavascriptExecutor jsExecutor)
108+
{
109+
return jsExecutor.executeScript(script, args);
110+
}
111+
112+
throw new UnsupportedOperationException("WebDriver can't execute JS");
113+
}
114+
115+
default Object callFunction(final String methodName, final Object... args)
116+
{
117+
final String paramPlaceholders = IntStream.range(0, args.length)
118+
.mapToObj(i -> "arguments[" + (i + 1) + "]") // Offset by 1!
119+
.collect(Collectors.joining(","));
120+
121+
return this.executeScript(
122+
"return arguments[0]." + methodName + "(" + paramPlaceholders + ")",
123+
Stream.concat(Stream.of(this), Stream.of(args)).toArray(Object[]::new));
124+
}
125+
}

0 commit comments

Comments
 (0)