Reputation: 73
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.
Upvotes: 7
Views: 1306
Reputation: 3625
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.
Upvotes: 2
Reputation: 410
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));
}
}
Upvotes: 1