I am using selenium pagefactory, and have introduced an annotation @Name(Description = "Username"), which I use for all WebElements. I need to find the value for Description in my custom methods later on, for reporting, like:
public static void click(WebElement element) throws Throwable {
try {
element.click();
} catch(ElementNotInteractableException E1) {
throw new UnableToInteractWithElementException("Unable To Interact With " + WebElement Descriptionn);
}
}
My @Name annotation interface and pagefactory look like this:
Name Interface
@Target({ElementType.FIELD, ElementType.TYPE,ElementType.CONSTRUCTOR})
public @interface Name {
String Description() default "";
}
@Name(Description = "Username")
@FindBy(id="txtLoginID")
public WebElement username;
The problem I face while using reflections is the need to define classname of pagefactory , and also provide fieldname as string "username" , to retrieve the annotation value.
I wish to be able to retrieve the annotation value by only providing my WebElement and no other object to my click(WebElement element) method.
Steps To Create Test Cases:Initialize the driver and open the application. Create an object of the PageLayer Class(for each webpage) and pass the driver instance as a parameter. Using the object created, make a call to the methods in the PageLayer Class(for each webpage) in order to perform actions/verification.
Page Factory is a class provided by Selenium WebDriver to support Page Object Design patterns. In Page Factory, testers use @FindBy annotation. The initElements method is used to initialize web elements. Similarly, one can use @FindBy with different location strategies to find web elements and perform actions on them.
In POM, you define locators using 'By' while in Page Factory, you use FindBy annotation to define page objects. Page Object Model is a design approach while PageFactory is a class which provides implementation of Page Object Model design approach.
I can show you how to get the description partially using reflection.
First of all, we have to fix the Annotation @Name
because you didn't add RetentionPolicy
:
@Target({ElementType.FIELD, ElementType.TYPE, ElementType.CONSTRUCTOR})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Name {
String Description() default "";
}
Now, what I did and tested, we create storage for the names and we're gonna store them when initializing the Page Objects like this:
public class NameStore {
public static HashMap<WebElement, String> map = new HashMap<>();
public static void store(Object pageObject) {
Field[] fields = pageObject.getClass().getDeclaredFields();
for (Field field : fields) {
Name annotation = field.getAnnotation(Name.class);
if (annotation != null) {
try {
map.put((WebElement) field.get(pageObject), annotation.Description());
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
public static String getDescription(WebElement element) {
return map.get(element);
}
}
You pass a Page Object as an argument, @Name
annotation is read and stored into the HashMap<WebElement, String>
Initialize name storing in a constructor:
public PageObject(WebDriver driver) {
PageFactory.initElements(driver, this);
NameStore.store(this); //THIS IS CRUCIAL. Also make sure that's always AFTER PageFactory initialization
}
And now, to update your click
method:
public static void click(WebElement element) throws Throwable {
try {
element.click();
} catch(ElementNotInteractableException E1) {
throw new UnableToInteractWithElementException("Unable To Interact With " + NameStore.get(element));
}
}
Hope it helps!
Disclaimer: I didn't test it in the context of multi-threading. I did not test in the context of a constructor.
Hi I'm not the pro with reflection, I'm not sure if I did get the question right. You may have some another optimal way of doing it, but follow one solution, create a method that receives the WebElement and return the description:
private static String getDescription(WebElement element) throws IllegalAccessException {
//Get all the fields of the page object.
for (Field field : YOUR_PAGE_OBJECT.class.getDeclaredFields()) {
Name name = field.getAnnotation(Name.class);
//Consider only the ones that is annotated by your @Name
if (name != null) {
WebElement classElement = (WebElement) field.get(element);
//Get the element from the class and compare with your current.
if (classElement != null && (classElement).equals(element)) {
return name.Description();
}
}
}
return ":C"; // or throw something, whatever you want...
}
Your code will end like:
public static void click(WebElement element) throws Throwable {
try {
element.click();
} catch(ElementNotInteractableException E1) {
throw new UnableToInteractWithElementException("Unable To Interact With " + getDescription(element));
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With