Skip to content

Commit b5b5c29

Browse files
authored
Merge pull request #38 from AutomateThePlanet/bellatrix-shadow-dom-upgrade
Bellatrix Shadow DOM Upgrade
2 parents 7c1cc6d + de28fb7 commit b5b5c29

File tree

527 files changed

+13902
-424
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

527 files changed

+13902
-424
lines changed

bellatrix.core/pom.xml

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,5 +153,20 @@
153153
<version>${rest.assured.version}</version>
154154
<scope>compile</scope>
155155
</dependency>
156+
<dependency>
157+
<groupId>com.azure</groupId>
158+
<artifactId>azure-security-keyvault-secrets</artifactId>
159+
<version>4.2.3</version>
160+
</dependency>
161+
<dependency>
162+
<groupId>com.azure</groupId>
163+
<artifactId>azure-identity</artifactId>
164+
<version>1.2.0</version>
165+
</dependency>
166+
<dependency>
167+
<groupId>org.jsoup</groupId>
168+
<artifactId>jsoup</artifactId>
169+
<version>1.17.2</version>
170+
</dependency>
156171
</dependencies>
157172
</project>

bellatrix.core/src/main/java/solutions/bellatrix/core/utilities/ConverterService.java

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,33 @@
1+
/*
2+
* Copyright 2024 Automate The Planet Ltd.
3+
* Author: Miriam Kyoseva
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* You may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
114
package solutions.bellatrix.core.utilities;
215

316
import lombok.SneakyThrows;
417

18+
import java.lang.reflect.Field;
19+
import java.util.ArrayList;
20+
import java.util.List;
21+
522
public class ConverterService {
623
public static <SourceT, ResultT> ResultT convert(SourceT source, int index) {
724
ResultT object = InstanceFactory.createByTypeParameter(source.getClass(), index);
825

26+
assert object != null;
927
return convert(source, object);
1028
}
1129

30+
@SuppressWarnings("unchecked")
1231
public static <SourceT, ResultT> ResultT convert(SourceT source, ResultT result) {
1332
var object = (ResultT) InstanceFactory.create(result.getClass());
1433

@@ -38,8 +57,8 @@ public static <SourceT, ResultT> ResultT convert(SourceT source, ResultT result)
3857
public static <SourceT, ResultT> ResultT convertToClass(SourceT source, Class<ResultT> result) {
3958
var object = (ResultT) InstanceFactory.create(result);
4059

41-
var sourceFields = source.getClass().getDeclaredFields();
42-
var objectFields = object.getClass().getDeclaredFields();
60+
List<Field> sourceFields = getAllFields(source.getClass());
61+
List<Field> objectFields = getAllFields(object.getClass());
4362

4463
for (var sourceField : sourceFields) {
4564
for (var objectField : objectFields) {
@@ -55,4 +74,19 @@ public static <SourceT, ResultT> ResultT convertToClass(SourceT source, Class<Re
5574

5675
return object;
5776
}
77+
78+
private static List<Field> getAllFields(Class<?> clazz) {
79+
List<Field> fields = new ArrayList<>();
80+
Class<?> currentClass = clazz;
81+
82+
while (currentClass != null) {
83+
var declaredFields = currentClass.getDeclaredFields();
84+
for (Field field : declaredFields) {
85+
fields.add(field);
86+
}
87+
currentClass = currentClass.getSuperclass();
88+
}
89+
90+
return fields;
91+
}
5892
}
Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Copyright 2024 Automate The Planet Ltd.
3+
* Author: Miriam Kyoseva
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* You may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
14+
package solutions.bellatrix.core.utilities;
15+
16+
import lombok.experimental.UtilityClass;
17+
import org.jsoup.nodes.Document;
18+
import org.jsoup.nodes.Element;
19+
import org.jsoup.nodes.Node;
20+
import solutions.bellatrix.core.utilities.parsing.TypeParser;
21+
22+
import java.util.Arrays;
23+
import java.util.regex.Matcher;
24+
import java.util.regex.Pattern;
25+
26+
/**
27+
* Web Utility Class
28+
*/
29+
@UtilityClass
30+
public class HtmlService {
31+
private static final String CHILD_COMBINATOR = " > ";
32+
private static final String NODE = "/";
33+
private static final String NODE_OR_SELF = "//";
34+
private static final String ROOT_ELEMENT_TAG = "bellatrix-root";
35+
36+
public static Document addRootElementIfNeeded(Document doc) {
37+
boolean hasMultipleTopLevelElements = doc.childNodes().stream()
38+
.filter(node -> node instanceof Element).count() > 1;
39+
40+
if (hasMultipleTopLevelElements) {
41+
var root = new Element(ROOT_ELEMENT_TAG);
42+
43+
for (Node node : doc.childNodes()) {
44+
root.appendChild(node.clone());
45+
}
46+
47+
doc.empty();
48+
doc.appendChild(root);
49+
}
50+
51+
return doc;
52+
}
53+
54+
public static String getAbsoluteXpath(Element element) {
55+
StringBuilder xpath = new StringBuilder();
56+
57+
Element currentElement = element;
58+
while (currentElement != null) {
59+
if (currentElement.tagName().equals("html") || currentElement.tagName().equals("body") || currentElement.tagName().startsWith("#") || currentElement.tagName().equals(ROOT_ELEMENT_TAG)) {
60+
// ignore the <html> and <body>, because jsoup added them to the html fragment
61+
// ignore added bellatrix root element
62+
// ignore invalid element tags
63+
break;
64+
}
65+
66+
xpath.insert(0, indexElement(currentElement));
67+
68+
currentElement = currentElement.parent();
69+
}
70+
71+
return xpath.toString();
72+
}
73+
74+
private String indexElement(Element element) {
75+
int index = 1;
76+
77+
Node previousSibling = element.previousSibling();
78+
while (previousSibling != null) {
79+
if (previousSibling.nodeName().equals(element.nodeName())) {
80+
index++;
81+
}
82+
previousSibling = previousSibling.previousSibling();
83+
}
84+
85+
return NODE + element.tagName() + "[" + index + "]";
86+
}
87+
88+
public <T> T getAttribute(Element element, String attributeName, Class<T> clazz) {
89+
if (element.attribute(attributeName) == null || element.attribute(attributeName).getValue() == null || element.attribute(attributeName).getValue().isBlank()) {
90+
return null;
91+
} else {
92+
return TypeParser.parse(element.attribute(attributeName).getValue(), clazz);
93+
}
94+
}
95+
96+
public static String convertAbsoluteXpathToCss(String xpath) {
97+
String cssSelector = xpath.replace(NODE, CHILD_COMBINATOR);
98+
99+
// Use regular expression to replace [number] with :nth-of-type(number)
100+
Pattern pattern = Pattern.compile("\\[(\\d+)\\]");
101+
Matcher matcher = pattern.matcher(cssSelector);
102+
StringBuilder builder = new StringBuilder();
103+
104+
while (matcher.find()) {
105+
matcher.appendReplacement(builder, ":nth-of-type(" + matcher.group(1) + ")");
106+
}
107+
matcher.appendTail(builder);
108+
109+
var semiFinalLocator = builder.toString();
110+
111+
if (semiFinalLocator.startsWith(CHILD_COMBINATOR)) {
112+
semiFinalLocator = semiFinalLocator.substring(2);
113+
}
114+
115+
return semiFinalLocator;
116+
}
117+
118+
public static String removeDanglingChildCombinatorsFromCss(String css) {
119+
// convert to array by splitting the css by the child combinator
120+
// and remove from that array empty steps
121+
var steps = Arrays.stream(css.split(CHILD_COMBINATOR))
122+
.filter(x -> !x.isBlank())
123+
.toArray(String[]::new);
124+
125+
// join the remaining steps with child combinator operators
126+
return String.join(CHILD_COMBINATOR, steps);
127+
}
128+
}

bellatrix.core/src/main/java/solutions/bellatrix/core/utilities/InstanceFactory.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.lang.reflect.InvocationTargetException;
1717
import java.lang.reflect.ParameterizedType;
1818

19+
@SuppressWarnings("unchecked")
1920
public final class InstanceFactory {
2021
public static <T> T create(Class<T> classOf) {
2122
T obj = null;
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package solutions.bellatrix.core.utilities;
2+
3+
import java.io.Serializable;
4+
5+
@FunctionalInterface
6+
public interface PropertyReference<T> extends Serializable {
7+
Object apply(T t);
8+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package solutions.bellatrix.core.utilities;
2+
3+
import lombok.SneakyThrows;
4+
import lombok.experimental.UtilityClass;
5+
6+
import java.lang.invoke.SerializedLambda;
7+
import java.lang.reflect.Field;
8+
import java.lang.reflect.Method;
9+
10+
@UtilityClass
11+
public class PropertyReferenceNameResolver {
12+
public static <T> Field getMember(Class<T> clazz, PropertyReference<T> propertyReference) {
13+
var methodName = getFunctionalMethodName(propertyReference);
14+
var fields = clazz.getDeclaredFields();
15+
16+
for (var field : fields) {
17+
if (methodName.toLowerCase().contains(field.getName().toLowerCase())) {
18+
return field;
19+
}
20+
}
21+
22+
return null;
23+
}
24+
25+
@SneakyThrows
26+
public static String getFunctionalMethodName(PropertyReference<?> functionalInterface) {
27+
Method writeReplaceMethod = functionalInterface.getClass().getDeclaredMethod("writeReplace");
28+
writeReplaceMethod.setAccessible(true);
29+
SerializedLambda serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(functionalInterface);
30+
31+
return serializedLambda.getImplMethodName();
32+
}
33+
}
Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,9 +11,15 @@
1111
* limitations under the License.
1212
*/
1313

14-
package solutions.bellatrix.playwright.utilities.functionalinterfaces;
14+
package solutions.bellatrix.core.utilities;
1515

16-
@FunctionalInterface
17-
public interface AssertionMethod {
18-
void perform();
19-
}
16+
/**
17+
* Wrapper for passing primitives by reference.
18+
*/
19+
public class Ref<T> {
20+
public T value;
21+
22+
public Ref(T value) {
23+
this.value = value;
24+
}
25+
}

bellatrix.core/src/main/java/solutions/bellatrix/core/utilities/SingletonFactory.java

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,31 +13,42 @@
1313

1414
package solutions.bellatrix.core.utilities;
1515

16+
import lombok.SneakyThrows;
17+
import lombok.experimental.UtilityClass;
18+
1619
import java.util.HashMap;
1720
import java.util.Map;
1821

1922
// Based on http://neutrofoton.github.io/blog/2013/08/29/generic-singleton-pattern-in-java/
2023
// Can be used inside App design pattern.
24+
@SuppressWarnings("unchecked")
2125
public class SingletonFactory {
2226
private static final SingletonFactory SINGLETON_FACTORY = new SingletonFactory();
2327

24-
private final Map<String, Object> mapHolder = new HashMap<>();
28+
private final Map<Class<?>, Object> mapHolder = new HashMap<>();
2529

2630
private SingletonFactory() {
2731
}
2832

33+
@SneakyThrows
2934
public static <T> T getInstance(Class<T> classOf, Object... initargs) {
3035
try {
31-
if (!SINGLETON_FACTORY.mapHolder.containsKey(classOf.getName())) {
32-
36+
if (!SINGLETON_FACTORY.mapHolder.containsKey(classOf)) {
3337
T obj = (T)classOf.getConstructors()[0].newInstance(initargs);
34-
SINGLETON_FACTORY.mapHolder.put(classOf.getName(), obj);
38+
SINGLETON_FACTORY.mapHolder.put(classOf, obj);
3539
}
36-
37-
return (T)SINGLETON_FACTORY.mapHolder.get(classOf.getName());
40+
return (T)SINGLETON_FACTORY.mapHolder.get(classOf);
3841
} catch (Exception e) {
3942
Log.error("Failed to create instance of the object. Exception was: " + e);
4043
return null;
4144
}
4245
}
46+
47+
public static <T> void register(T instance) {
48+
SINGLETON_FACTORY.mapHolder.put(instance.getClass(), instance);
49+
}
50+
51+
public static <T> void register(Class<?> classKey, T instance) {
52+
SINGLETON_FACTORY.mapHolder.put(classKey, instance);
53+
}
4354
}

bellatrix.core/src/main/java/solutions/bellatrix/core/utilities/Wait.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,4 +77,28 @@ public static boolean retry(Runnable action, Duration timeout, Duration sleepInt
7777
public static void retry(Runnable action, Duration timeout, Duration sleepInterval, Class<? extends Throwable> ... exceptionsToIgnore) {
7878
Wait.retry(action, timeout, sleepInterval, true, exceptionsToIgnore);
7979
}
80+
81+
public static boolean forConditionUntilTimeout(Comparator condition, long timeoutInMilliseconds, long pollingIntervalInMilliseconds) {
82+
boolean isConditionMet = false;
83+
84+
long startTime = System.currentTimeMillis();
85+
86+
while (!isConditionMet && (System.currentTimeMillis() - startTime) <= (timeoutInMilliseconds)) {
87+
try {
88+
if (condition.evaluate()) {
89+
isConditionMet = true;
90+
} else {
91+
Thread.sleep(pollingIntervalInMilliseconds);
92+
}
93+
} catch (Exception ignored) {
94+
}
95+
}
96+
97+
return isConditionMet;
98+
}
99+
100+
@FunctionalInterface
101+
public interface Comparator {
102+
boolean evaluate();
103+
}
80104
}

0 commit comments

Comments
 (0)