Super class that can override #FindBy - java

I am porting over an existing project and I have a super class called DetailPage that is the only thing I can change/edit. It's a selenium testing project and in previous versions I had hundreds of individual pages that extended DetailPage and had the following lines:
#FindBy(id="someID")
private WebElement someIDBox;
to search a page for a given id and assign that element to a variable. I now have a similar website that is nonW3C so instead of finding by id I now need to find by name ie:
#FindBy(name="someID")
private WebElement someIDBox;
is what I now want.
So my question is how can I (if possible) make a super class function in DetailPage that would notice I say id and override with name?
I DO NOT want a function that just replaces the text id with name in the individual pages as I need to preserve those.

The author of this article extended the 'FindBy` annotation to support his needs. You can use this to override the 'FindBy' and make your on implementation.
Edited code sample:
private static class CustomFindByAnnotations extends Annotations {
protected By buildByFromLongFindBy(FindBy findBy) {
How how = findBy.how();
String using = findBy.using();
switch (how) {
case CLASS_NAME:
return By.className(using);
case ID:
return By.id(using); // By.name(using); in your case
case ID_OR_NAME:
return new ByIdOrName(using);
case LINK_TEXT:
return By.linkText(using);
case NAME:
return By.name(using);
case PARTIAL_LINK_TEXT:
return By.partialLinkText(using);
case TAG_NAME:
return By.tagName(using);
case XPATH:
return By.xpath(using);
default:
throw new IllegalArgumentException("Cannot determine how to locate element " + field);
}
}
}
Please note I didn't try it myself. Hope it helps.

This does not meet the criteria that I asked but thanks to #guy I was introduced to ByIdOrName which helps with my problem. So I made a little script that went through all my files in the workspace and replaced
#FindBy(id="someID")
private WebElement someIDBox;
with
#FindBy(how = How.ID_OR_NAME, using="someID")
private WebElement someIDBox;
while this made it so I had to alter all test pages(which is what I wanted to avoid) it does not alter how other tests for other websites work which is key!

Related

Return the Specified Fields and the _id Field Only in java

UPDATE: Spuggiehawk advised in his answer to fix the include keyword issue, and also suggest an alternative way to get the _id other than projections. However, I still have trouble to call this method from my service class, which edit the user detail, that I must admit I have limited knowledge to make it right.
#Override
public User get(Object userId) {
FindIterable<User> userTbl = database.getCollection("User", User.class).find();
User user = new User();
for (User doc : userTbl) {
String oid = doc.getId().toHexString();
System.out.println("_id = " + oid);
return user;
}
return null;
}
In the Service class
public void editUser() {
String userId = request.getParameter("id");
User user = userDAO.get(userId);
System.out.println(user.getFullName());
}
You don't need to use projection if you just want the object ID. The syntax you want to get that (in a loop) is:
FindIterable<Document> userTbl = db.getCollection("User").find();
for (Document doc: userTbl2)
{
String id = doc.getObjectId("_id").toString();
System.out.println("_id = " + id);
}
Do what you need to with that id value.
As far as your use of include is concerned, if you do find a situation where you need that, then you need the static import. Eclipse should give you the option if you hover over the keyword:
If Eclipse doesn't show that, you might need to add the references in your Eclipse configuration under Window -> Preferences -> Java -> Editor -> Content Assist -> Favorites:
The important part is at the top of your code, it should include:
import static com.mongodb.client.model.Projections.include;
You'll find that useful for your filters too, eg.
Bson filter = eq("email", "email.com");
db.getCollection("User").find(filter);
Finally, if you only want to get the first matching record in your find(), use:
Document = db.getCollection("User").find(filter).first();

How to extend Selenium's FindBy annotation

I'm trying to use Selenium PageObjects to model a page that lacks a lot of convenient id or class attributes on the tags, so I'm finding that I need to develop more creative ways to identify elements on a page. Among them is a pattern like the following:
<div id="menuButtons">
<a><img src="logo.png" alt="New"></a>
<a><img src="logo2.png" alt="Upload"></a>
</div>
It would be convenient to be able to create a custom findBy search to be able to identify a link by the alt text of its contained image tag, so I could do something like the following:
#FindByCustom(alt = "New")
public WebElement newButton;
The exact format of the above isn't important, but what's important is that it continue to work with PageFactory.initElements.
The author of this article extended the 'FindBy` annotation to support his needs. You can use this to override the 'FindBy' and make your on implementation.
Edited code sample:
private static class CustomFindByAnnotations extends Annotations {
protected By buildByFromLongFindBy(FindBy findBy) {
How how = findBy.how();
String using = findBy.using();
switch (how) {
case CLASS_NAME:
return By.className(using);
case ID:
return By.id(using);
case ID_OR_NAME:
return new ByIdOrName(using);
case LINK_TEXT:
return By.linkText(using);
case NAME:
return By.name(using);
case PARTIAL_LINK_TEXT:
return By.partialLinkText(using);
case TAG_NAME:
return By.tagName(using);
case XPATH:
return By.xpath(using);
case ALT:
return By.cssSelector("[alt='" + using " + ']");
default:
throw new IllegalArgumentException("Cannot determine how to locate element " + field);
}
}
}
Please note I didn't try it myself. Hope it helps.
If you simply want the <a> tag you can use xpath to find the element and go one level up using /..
driver.findElement(By.xpath(".//img[alt='New']/.."));
Or you can put the buttons in list and access them by index
List<WebElement> buttons = driver.findElements(By.id("menuButtons")); //note the spelling of findElements
// butttons.get(0) is the first <a> tag

How do I get an HTML Element By CSS class name via a JavaFX WebEngine?

I am able to get an element by Id like this in JavaFX.
Element nameField = engine.getDocument().getElementById( "name" );
How do I do the same given element's classname?
Thanks.
I came across this and saw there wasn't much for answers. The way I found way to work with dom classes is not great but it gets the job done.
To add a class on a Node you obtained, use the setAttribute() method. Be careful to maintain any classes that might already exist on the Node.
Document doc = engine.getDocument();
if (doc != null){
Element elem = doc.getElementById('someID');
String classNames = elem.getAttribute("class");
classNames += " someClass"; //note the whitespace!
elem.setAttribute("class", classNames);
}
Then, if you wish to search the DOM by class you can execute javascript to do so using the executeScript on the WebEngine. The return type depends on what you're asking the script to do.
Small note: disabling javascript via engine.setJavaScriptEnabled(false); does not prohibit use of the engine.executeScript() method.
HTMLCollection result = (HTMLCollection)engine.executeScript("document.getElementsByClassName('someClass')");
However inefficient, I did this to determine what I would be getting back from executeScript() before writing any further code:
Object result = engine.executeScript("document.getElementsByClassName('someClass')");
System.out.println(result.getClass().getName());
Like I said it isn't great, but you can write some wrapper functions to make it easier to work with. Hope this helps!
You can access any component by using: the lookUp method.
For Example:
Button has class: specialClazz and your mainPane is a StackPane: rootPane
So you just do:
rootPane.lookUp(".specialClazz"); // .-selector!
For an ID:
rootPane.lookUp("#specialID"); // #-selector!
I'd use javax.xml.xpath. See XPath Tutorial - Example 6: Values of attributes can be used as selection criteria.
Note also that there is not necessarily a (single) element's class name. id is unique in a Document whereas class is not. So it should rather read elements' class name. (Subtle, but important. :-) In other words: there is not necessarily just one Element returned when searching for a given class.
The above answer:
"HTMLCollection result = (HTMLCollection)engine.executeScript("document.getElementsByClassName('someClass')");"
is bad. Because executeScript return's JSObject but not HTMLCollection; it cannot be casted.
The corrent is below java9 modular code sample (java14 has var):
and you should add vm options to project:
--add-exports javafx.web/com.sun.webkit.dom=atools
var doc = webEngine.getDocument();
if (doc instanceof HTMLDocument htmlDocument) { //actually htmlDoc is HTMLDocumentImpl but java9 have to reflect
try {
Method method = null;
var clazz = htmlDocument.getClass();
method = clazz.getSuperclass().getMethod("getElementsByClassName", String.class);
HTMLCollection collection = (HTMLCollection) method.invoke(htmlDocument,"button");
//if collection not only 1
//you should special the document more deeply
for (int i = 0; i < collection.getLength(); i++) {
var btn = collection.item(i);
//actual is HTMLButtonElementImpl
//HTMLButtonElement htmlButtonElement = (HTMLButtonElement) btn;
var method1 = btn.getClass().getMethod("click");
method1.invoke(btn);
Log.d("");
break;
}
} catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) {
e.printStackTrace();
}
}
If you are not using java9+ modular, these reflects can replace with HTMLButtonElementImpl,HTMLDocumentImpl.

I don't want to write driver.findelement(By.xpath("")) again and again

Hi this is the code below: What I want to do is this build a function in which i just pass the value of XPath, so i don't have to write driver.findElement(By.xpath("")) again and again.
driver.findElement(By.xpath("//*[#id='lead_source']")).sendKeys("Existing Customer");
driver.findElement(By.xpath("//*[#id='date_closed']")).sendKeys("08/07/2013");
driver.findElement(By.xpath("//*[#id='sales_stage']")).sendKeys("Opportuntiy Qualification");
driver.findElement(By.xpath("//*[#id='opportunity_monthly_volume']")).sendKeys("10895");
driver.findElement(By.xpath("//*[#id='probability']")).sendKeys("90");
driver.findElement(By.xpath("//*[#id='opportunity_sales_rep']")).sendKeys("Sales Rep");
driver.findElement(By.xpath("//*[#id='opportunity_sales_regions']")).sendKeys("Northeast");
driver.findElement(By.xpath("//*[#id='opportunity_current_lab']")).sendKeys("Current lab");
driver.findElement(By.cssSelector(Payermixcss +"opportunity_medicare")).sendKeys("5");
The best way would be to use the PageObject pattern. You could do something like this:
public class MyFormPageObject {
public MyFormPageObject enterLeadSource(String value) {
driver.findElement(By.id("lead_source")).sendKeys(value);
return this;
}
public MyFormPageObject enterDateClosed(String value) {
driver.findElement(By.id("date_closed")).sendKeys(value);
return this;
}
//...
}
// then in your test code
myFormPO.enterLeadSource("Existing Customer").enter("08/07/2013");
Note that as mentionned, you should use By.id if you have an identifier, because XPath is slower and not always well supported by all implementation of WebDriver.
Extract a method to upper level and just pass the values as parameters
eg:
yourMethod(Path path) {
driver.findElement(By.xpath(path))
}
As per your concern you can you use page object model and create the method and pass the variable to exact method.. I don't konw java but i know and concepts
private string variable= "Xpath value"
Pass this variable to method and it will interact with POM.. Before that u should know about POM. Then u can easily understand the concepts. Hope it will help full you...
To reduce the amount of code you have to write, you could use a function like this:
private WebElement findElementByXpath(String xpath) {
return driver.findElement(By.xpath(xpath));
}
The first line of your code would be:
findElementByXpath("//*[#id='lead_source']").sendKeys("Existing Customer");
It does not really reduce the length of the code but it only takes one CTRL + SPACE for autocompletion in Eclipse IDE.

Should Page Objects Return Page Objects?

We're currently working on building up a good test frame in our company. It's for a medium-to-large-sized webapp, perhaps with a couple dozen pages. We're currently writing mostly WebDriver Selenium UI-based tests.
We are trying to decide on some coding standards, and one thing we're discussing is whether to use Page Objects (PO) that always return PO (even if the page is the same), only return PO when you leave the current page for a new one, or even to not return PO. I've always thought returning PO is a key feature of the PO design pattern, but I may be incorrect about this.
Basically, we're trying to decide between the following patterns:
class SomePage {
// constructor
public SomePage(Driver) { //... }
// always return a page object
public SomePage fillInTextField(String val){
// details
return new SomePage(driver);
// only return a PO if it's a different page
public void fillInTextField(String val){
// details
return;
}
Is one preferable over the other?
It's a matter of style.
Using:
class SomePage {
...
// always return a page object
public SomePage fillInTextField(String val){
...
return this; // Note: NOT "new SomePage(driver)"
}
}
allows you to write things like:
SomePage somePage = HoweverYouGetASomePage();
NextPage nextPage = somePage.fillInTextField1("value1").fillInTextField2("value2").submit();
Using:
class SomePage {
...
// only return a PO if it's a different page
public void fillInTextField(String val){
...
return;
}
}
forces you to write things like:
SomePage somePage = HoweverYouGetASomePage();
somePage.fillInTextField1("value1");
somePage.fillInTextField2("value2");
NextPage nextPage = somePage.submit();
Which one you like depends on ... which one you like. But if you like being able to write the former, you need to always return the page object.
Short answer is that don't return same page objects if you are on the same page and state of the page is not changing. You would return new page objects if you are navigating from one page to another. It wouldn't make sense to return the same object when lets say you want to get some text or get a selected option from a page, since essentially nothing changed. If the state of the page is changing, then you would need to return the new page object otherwise you may likely face StaleElementException. In google docs, If you notice the LoginPage, getErrorMessage() does not return the same page object back
A little off from your original question, but I would recommend to use PageFactory, if you already aren't and are in the process of formalizing standards.
I've found it extremely maintainable to do this. Especially with Java 8. Using default methods in Java 8, components and widgets now become Mixins. By always returning a Page, you end up with test cases that look like this:
public class HomePageITest {
#Test
public we_should_be_able_to_search_from_the_homepage() {
pageFactory.getHomePage()
.get()
.doSomethingOnTheHomePage()
.clickSearchWidgetSubmitButton()
.doSomethingElseOnTheHomePage()
;
}
}
I have a blog post describing this in more detail here: http://blog.jsdevel.me/2015/04/pageobjects-done-right-in-java-8.html
As the PageObject Selenium help page said "Methods return other PageObjects"

Categories

Resources