Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
98 commits
Select commit Hold shift + click to select a range
0eed921
Renamed TextFindStrategy to InnerTextContainingFindStrategy and made …
mei-kyoseva May 27, 2024
10c5c3e
Update testFrameworkSettings.dev.json
mei-kyoseva May 27, 2024
bc86036
Created HtmlService
mei-kyoseva May 27, 2024
3d13d2d
Created ShadowXPathFindStrategy
mei-kyoseva May 27, 2024
3f8eade
Created ShadowRoot component and changed the component creation logic
mei-kyoseva May 27, 2024
43d26bc
Update IFramesAndShadowDOMTests.java
mei-kyoseva May 27, 2024
83fc36a
Updated WebComponent in bellatrix.web
mei-kyoseva May 27, 2024
368a5d8
Update WrappedBrowser.java
mei-kyoseva May 27, 2024
3868e86
Updated bellatrix.core pom.xml
mei-kyoseva May 27, 2024
1c37a7f
Renamed methods in HtmlService
mei-kyoseva Jun 7, 2024
6ccd496
Renamed methods in HtmlService
mei-kyoseva Jun 7, 2024
312ded7
Added module bellatrix.web.getting.started to the main pom.xml
mei-kyoseva Jun 7, 2024
a385fa4
Added common methods and classes in bellatrix.core
mei-kyoseva Jun 12, 2024
869ec53
Update ConverterService.java
mei-kyoseva Jun 12, 2024
f4fb4e8
Update InstanceFactory.java
mei-kyoseva Jun 12, 2024
43d9cf3
Update SingletonFactory.java
mei-kyoseva Jun 12, 2024
8382835
Changes to bellatrix.playwright
mei-kyoseva Jun 12, 2024
5eaf643
Corrected bellatrix.web.getting.started
mei-kyoseva Jun 12, 2024
eade3ad
Added Table component to bellatrix.web
mei-kyoseva Jun 12, 2024
4dea493
Update WebComponent.java
mei-kyoseva Jun 12, 2024
e1f6e63
Added local pages inside the resource folder in bellatrix.web.tests
mei-kyoseva Jun 12, 2024
96074fe
Updated config and added TestPagesSettings in bellatrix.web.tests
mei-kyoseva Jun 12, 2024
eaffe37
Added tests for Table component inside bellatrix.web.tests
mei-kyoseva Jun 12, 2024
d946a91
Update TypeParser.java
mei-kyoseva Jun 12, 2024
f49228a
Changes to Core utilities
mei-kyoseva Jun 14, 2024
20e55dc
Table fixes
mei-kyoseva Jun 14, 2024
1647acc
Create ControlDataHandler.java
mei-kyoseva Jun 14, 2024
55ce820
Update HeaderNamesService.java
mei-kyoseva Jun 14, 2024
8f81ea2
Update HeaderInfo.java
mei-kyoseva Jun 14, 2024
f1b0cf4
Added Grid component
mei-kyoseva Jun 14, 2024
ec3d60d
Added tests for Grid component
mei-kyoseva Jun 14, 2024
cfe909d
Added method <T>getAttribute(Element, String, Class<T>) to HtmlService
mei-kyoseva Jun 17, 2024
0242fc6
Changes to FooterService
mei-kyoseva Jun 17, 2024
3ac3280
Changes to FooterService
mei-kyoseva Jun 17, 2024
b3884d2
Create TableLocators.java
mei-kyoseva Jun 17, 2024
b97c4fc
Fixes
mei-kyoseva Jun 17, 2024
c251238
Moved HeaderNamesServices
mei-kyoseva Jun 17, 2024
ba65a10
Made HeaderNamesService parameterized
mei-kyoseva Jun 17, 2024
eb22c1f
Update Table.java
mei-kyoseva Jun 17, 2024
f900e02
Create GenericDateTimeParser.java
mei-kyoseva Jun 17, 2024
c19b78f
Update TypeParser.java
mei-kyoseva Jun 17, 2024
f314a37
Updated ControlDataHandler
mei-kyoseva Jun 17, 2024
a181118
Update GenericDateTimeParser.java
mei-kyoseva Jun 17, 2024
438265c
fixes
mei-kyoseva Jun 20, 2024
9a04436
fixes
mei-kyoseva Jun 20, 2024
c6b5d1a
Renamed getInnerText() to getText()
mei-kyoseva Jun 20, 2024
83fcb61
Update DialogService.java
mei-kyoseva Jun 20, 2024
8c1c841
Update pom.xml
mei-kyoseva Jun 20, 2024
b7ebba8
Added ControlDataHandlers to playwright module
mei-kyoseva Jun 20, 2024
4e0ffe5
Added Table and Grid components to playwright module
mei-kyoseva Jun 20, 2024
50c4e9d
fixes
mei-kyoseva Jun 24, 2024
2af11fe
fixes to grid component and table service
mei-kyoseva Jun 24, 2024
928806a
Merge pull request #39 from AutomateThePlanet/bellatrix-table-grid-co…
mei-kyoseva Jun 24, 2024
3e808c3
Changes and Added New Functionalities to Shadow DOM Logic
mei-kyoseva Jun 24, 2024
71c7e98
Merge branch 'bellatrix-shadow-dom-upgrade' of https://github.com/Aut…
mei-kyoseva Jun 24, 2024
999b703
added shadowDomPage to TestPagesSettings and redacted a comment in Ht…
mei-kyoseva Jun 24, 2024
d4ce3aa
Added PropertyReference and PropertyReferenceNameResolver
mei-kyoseva Jun 24, 2024
f406073
getCell by expression method now uses PropertyReference functional in…
mei-kyoseva Jun 24, 2024
85b88d2
Update Table.java
mei-kyoseva Jun 25, 2024
a186ed4
Update TableHeaderRow.java
mei-kyoseva Jun 25, 2024
bddd569
Locator fixes
mei-kyoseva Jun 25, 2024
940fa04
Renamed validate text method in playwright module
mei-kyoseva Jun 25, 2024
4aea7c4
Minor changes
mei-kyoseva Jun 25, 2024
e1f030b
Fixed getHeaderNameByExpression in HeaderNamesService, Playwright mod…
mei-kyoseva Jun 25, 2024
78baf5f
Renamed HtmlService getAbsoluteXPath method
mei-kyoseva Jun 25, 2024
146940a
Playwright: Made CssFindStrategy and XpathFindStrategy explicit
mei-kyoseva Jun 25, 2024
3dae418
Changes to SingletonFactory and added register(T instance) method
mei-kyoseva Jun 25, 2024
bb02ef4
Unnecessary object creation removed, now app() in classes WebTest get…
mei-kyoseva Jun 25, 2024
b1f99f3
When creating Playwright WrappedBrowser, it is being automatically re…
mei-kyoseva Jun 25, 2024
524030e
Fixes to Playwright Table and Grid components
mei-kyoseva Jun 25, 2024
f99aaa1
Changes to playwright shadow DOM logic
mei-kyoseva Jun 25, 2024
4aa16fd
Added bellatrix.playwright.tests module
mei-kyoseva Jun 25, 2024
99b94d1
Update to getElement method of ShadowDomService
mei-kyoseva Jun 25, 2024
6b39d2d
Added new method to HtmlService: addRootElementIfNeeded
mei-kyoseva Jun 25, 2024
df01ce1
Update to getTable method in TableService
mei-kyoseva Jun 25, 2024
1481c0c
Update Grid.java
mei-kyoseva Jun 25, 2024
0cae58f
Added new tests to ShadowDomTests for web
mei-kyoseva Jun 25, 2024
3a1a98c
Moved BELLATRIX framework tests in folder 'framework-tests' and getti…
mei-kyoseva Jun 26, 2024
6e9a8d4
Update playwright dependency from v1.40.0 to v1.44.0
mei-kyoseva Jun 26, 2024
33d36c4
Fixes to bellatrix.playwright.tests pom
mei-kyoseva Jun 27, 2024
77c46ca
Fixes to logic for creating shadow roots between element and outermos…
mei-kyoseva Jun 28, 2024
512d91e
Extracted common Strings into a constant in HtmlService
mei-kyoseva Jun 28, 2024
7020364
Updated removeDanglingChildChildCombinatorsFromCss in HtmlService
mei-kyoseva Jun 28, 2024
d86ebf4
Refactored getElement and getElements private methods in ShadowDomSer…
mei-kyoseva Jun 28, 2024
8c1475c
Fixes to ShadowDomService in playwright module
mei-kyoseva Jun 28, 2024
3118296
Squashed commit of the following:
mei-kyoseva Jul 3, 2024
ccbe975
minor fixes
mei-kyoseva Jul 3, 2024
e2fef43
Update HtmlService.java
mei-kyoseva Jul 4, 2024
f20282a
Added register method overload to SingletonFactory
mei-kyoseva Jul 4, 2024
cbbdd17
Changes to Grid component
mei-kyoseva Jul 4, 2024
c32307d
Fixes to ShadowDomService
mei-kyoseva Jul 4, 2024
fe40e6a
Update ShadowDomTests.java
mei-kyoseva Jul 4, 2024
155983d
Update Grid.html
mei-kyoseva Jul 4, 2024
43f2a28
Merge branch 'bellatrix-shadow-dom-upgrade' of https://github.com/Aut…
mei-kyoseva Jul 4, 2024
799732d
minor fixes
mei-kyoseva Jul 9, 2024
4f6a42e
Merge branch 'main' into bellatrix-shadow-dom-upgrade
mei-kyoseva Jul 9, 2024
840c809
Update GridControlTests.java
mei-kyoseva Jul 9, 2024
de28fb7
Added relative path to modules inside framework-tests and getting-sta…
mei-kyoseva Jul 9, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
15 changes: 15 additions & 0 deletions bellatrix.core/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -153,5 +153,20 @@
<version>${rest.assured.version}</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-security-keyvault-secrets</artifactId>
<version>4.2.3</version>
</dependency>
<dependency>
<groupId>com.azure</groupId>
<artifactId>azure-identity</artifactId>
<version>1.2.0</version>
</dependency>
<dependency>
<groupId>org.jsoup</groupId>
<artifactId>jsoup</artifactId>
<version>1.17.2</version>
</dependency>
</dependencies>
</project>
Original file line number Diff line number Diff line change
@@ -1,14 +1,33 @@
/*
* Copyright 2024 Automate The Planet Ltd.
* Author: Miriam Kyoseva
* 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 solutions.bellatrix.core.utilities;

import lombok.SneakyThrows;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;

public class ConverterService {
public static <SourceT, ResultT> ResultT convert(SourceT source, int index) {
ResultT object = InstanceFactory.createByTypeParameter(source.getClass(), index);

assert object != null;
return convert(source, object);
}

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

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

var sourceFields = source.getClass().getDeclaredFields();
var objectFields = object.getClass().getDeclaredFields();
List<Field> sourceFields = getAllFields(source.getClass());
List<Field> objectFields = getAllFields(object.getClass());

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

return object;
}

private static List<Field> getAllFields(Class<?> clazz) {
List<Field> fields = new ArrayList<>();
Class<?> currentClass = clazz;

while (currentClass != null) {
var declaredFields = currentClass.getDeclaredFields();
for (Field field : declaredFields) {
fields.add(field);
}
currentClass = currentClass.getSuperclass();
}

return fields;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
/*
* Copyright 2024 Automate The Planet Ltd.
* Author: Miriam Kyoseva
* 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 solutions.bellatrix.core.utilities;

import lombok.experimental.UtilityClass;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.nodes.Node;
import solutions.bellatrix.core.utilities.parsing.TypeParser;

import java.util.Arrays;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
* Web Utility Class
*/
@UtilityClass
public class HtmlService {
private static final String CHILD_COMBINATOR = " > ";
private static final String NODE = "/";
private static final String NODE_OR_SELF = "//";
private static final String ROOT_ELEMENT_TAG = "bellatrix-root";

public static Document addRootElementIfNeeded(Document doc) {
boolean hasMultipleTopLevelElements = doc.childNodes().stream()
.filter(node -> node instanceof Element).count() > 1;

if (hasMultipleTopLevelElements) {
var root = new Element(ROOT_ELEMENT_TAG);

for (Node node : doc.childNodes()) {
root.appendChild(node.clone());
}

doc.empty();
doc.appendChild(root);
}

return doc;
}

public static String getAbsoluteXpath(Element element) {
StringBuilder xpath = new StringBuilder();

Element currentElement = element;
while (currentElement != null) {
if (currentElement.tagName().equals("html") || currentElement.tagName().equals("body") || currentElement.tagName().startsWith("#") || currentElement.tagName().equals(ROOT_ELEMENT_TAG)) {
// ignore the <html> and <body>, because jsoup added them to the html fragment
// ignore added bellatrix root element
// ignore invalid element tags
break;
}

xpath.insert(0, indexElement(currentElement));

currentElement = currentElement.parent();
}

return xpath.toString();
}

private String indexElement(Element element) {
int index = 1;

Node previousSibling = element.previousSibling();
while (previousSibling != null) {
if (previousSibling.nodeName().equals(element.nodeName())) {
index++;
}
previousSibling = previousSibling.previousSibling();
}

return NODE + element.tagName() + "[" + index + "]";
}

public <T> T getAttribute(Element element, String attributeName, Class<T> clazz) {
if (element.attribute(attributeName) == null || element.attribute(attributeName).getValue() == null || element.attribute(attributeName).getValue().isBlank()) {
return null;
} else {
return TypeParser.parse(element.attribute(attributeName).getValue(), clazz);
}
}

public static String convertAbsoluteXpathToCss(String xpath) {
String cssSelector = xpath.replace(NODE, CHILD_COMBINATOR);

// Use regular expression to replace [number] with :nth-of-type(number)
Pattern pattern = Pattern.compile("\\[(\\d+)\\]");
Matcher matcher = pattern.matcher(cssSelector);
StringBuilder builder = new StringBuilder();

while (matcher.find()) {
matcher.appendReplacement(builder, ":nth-of-type(" + matcher.group(1) + ")");
}
matcher.appendTail(builder);

var semiFinalLocator = builder.toString();

if (semiFinalLocator.startsWith(CHILD_COMBINATOR)) {
semiFinalLocator = semiFinalLocator.substring(2);
}

return semiFinalLocator;
}

public static String removeDanglingChildCombinatorsFromCss(String css) {
// convert to array by splitting the css by the child combinator
// and remove from that array empty steps
var steps = Arrays.stream(css.split(CHILD_COMBINATOR))
.filter(x -> !x.isBlank())
.toArray(String[]::new);

// join the remaining steps with child combinator operators
return String.join(CHILD_COMBINATOR, steps);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.ParameterizedType;

@SuppressWarnings("unchecked")
public final class InstanceFactory {
public static <T> T create(Class<T> classOf) {
T obj = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package solutions.bellatrix.core.utilities;

import java.io.Serializable;

@FunctionalInterface
public interface PropertyReference<T> extends Serializable {
Object apply(T t);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package solutions.bellatrix.core.utilities;

import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;

import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

@UtilityClass
public class PropertyReferenceNameResolver {
public static <T> Field getMember(Class<T> clazz, PropertyReference<T> propertyReference) {
var methodName = getFunctionalMethodName(propertyReference);
var fields = clazz.getDeclaredFields();

for (var field : fields) {
if (methodName.toLowerCase().contains(field.getName().toLowerCase())) {
return field;
}
}

return null;
}

@SneakyThrows
public static String getFunctionalMethodName(PropertyReference<?> functionalInterface) {
Method writeReplaceMethod = functionalInterface.getClass().getDeclaredMethod("writeReplace");
writeReplaceMethod.setAccessible(true);
SerializedLambda serializedLambda = (SerializedLambda) writeReplaceMethod.invoke(functionalInterface);

return serializedLambda.getImplMethodName();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,15 @@
* limitations under the License.
*/

package solutions.bellatrix.playwright.utilities.functionalinterfaces;
package solutions.bellatrix.core.utilities;

@FunctionalInterface
public interface AssertionMethod {
void perform();
}
/**
* Wrapper for passing primitives by reference.
*/
public class Ref<T> {
public T value;

public Ref(T value) {
this.value = value;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,31 +13,42 @@

package solutions.bellatrix.core.utilities;

import lombok.SneakyThrows;
import lombok.experimental.UtilityClass;

import java.util.HashMap;
import java.util.Map;

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

private final Map<String, Object> mapHolder = new HashMap<>();
private final Map<Class<?>, Object> mapHolder = new HashMap<>();

private SingletonFactory() {
}

@SneakyThrows
public static <T> T getInstance(Class<T> classOf, Object... initargs) {
try {
if (!SINGLETON_FACTORY.mapHolder.containsKey(classOf.getName())) {

if (!SINGLETON_FACTORY.mapHolder.containsKey(classOf)) {
T obj = (T)classOf.getConstructors()[0].newInstance(initargs);
SINGLETON_FACTORY.mapHolder.put(classOf.getName(), obj);
SINGLETON_FACTORY.mapHolder.put(classOf, obj);
}

return (T)SINGLETON_FACTORY.mapHolder.get(classOf.getName());
return (T)SINGLETON_FACTORY.mapHolder.get(classOf);
} catch (Exception e) {
Log.error("Failed to create instance of the object. Exception was: " + e);
return null;
}
}

public static <T> void register(T instance) {
SINGLETON_FACTORY.mapHolder.put(instance.getClass(), instance);
}

public static <T> void register(Class<?> classKey, T instance) {
SINGLETON_FACTORY.mapHolder.put(classKey, instance);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,4 +77,28 @@ public static boolean retry(Runnable action, Duration timeout, Duration sleepInt
public static void retry(Runnable action, Duration timeout, Duration sleepInterval, Class<? extends Throwable> ... exceptionsToIgnore) {
Wait.retry(action, timeout, sleepInterval, true, exceptionsToIgnore);
}

public static boolean forConditionUntilTimeout(Comparator condition, long timeoutInMilliseconds, long pollingIntervalInMilliseconds) {
boolean isConditionMet = false;

long startTime = System.currentTimeMillis();

while (!isConditionMet && (System.currentTimeMillis() - startTime) <= (timeoutInMilliseconds)) {
try {
if (condition.evaluate()) {
isConditionMet = true;
} else {
Thread.sleep(pollingIntervalInMilliseconds);
}
} catch (Exception ignored) {
}
}

return isConditionMet;
}

@FunctionalInterface
public interface Comparator {
boolean evaluate();
}
}
Loading