actually i want to get an element with the #FindBy that is use into the Page Objects pattern.
I've 2 classes, the 1st one is my page objects named TestPage and the 2nd one is named PageSaveTest (where my tests happen and call the TestPage).
I've also tried to use the #FindBy with xpath and id.
>> This is my TestPage
import java.util.List;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.FindBy;
public class TestPage {
// get autocomplete input
#FindBy(css = "input[id*='supplierOps_input']")
private WebElement autocompleteSupplierOps;
// getter
public WebElement getAutocompleteSupplierOps() {
return autocompleteSupplierOps;
}
}
>> This is my PageSaveTest
// How i "inject" my TestPage
#Page
TestPage testpage;
[...]
// My test
WebElement autocomplete = testpage.getAutocompleteSupplierOps();
String keys = "OP";
autocomplete.sendKeys(keys); // >>>>>>> Error throwed here !
List<WebElement> listSugg = testpage.getSuggestionsSupplierOps();
Error message :
org.openqa.selenium.NoSuchElementException : Returned node was not an HTML element.
My thoughts :
I think the trouble comes from the #FindBy. But i use this example to build my TestPage and my test and this one too.
Question : Can someone explain to me how #FindBy works and be used in my example ? The documentation is really poor about Graphene.
EDIT :
I've modify my getter in TestPage (above), i've tried a simple print of the id attribute value like
public WebElement getAutocompleteSupplierOps() {
System.out.println(">>>> "+autocompleteSupplierOps.getAttribute("id"));
return autocompleteSupplierOps;
}
But still the same error, the #FindBy is f*cked up.
Another #FindBy spec to add in this issue.
Update :
I've fixed my selector but actually there is a probleme with the driver session like :
page2.getAutocompleteSupplierOps();
PAGE 1 ----------------------------------> PAGE 2
driver id:1 ----------------------------------> driver id:2
driver.showPageSource() is empty
return no element found <---------------------- driver.findElement() -> not found
I've used 3 different ways, the #FindBy, the #Drone WebDriver and finally what #Lukas Fryc suggested to me.
Instead of injection of WebElement using #FindBy, you can try using driver directly:
WebDriver driver = GrapheneContext.getProxy(); // this will be removed in Alpha5 version, use `#Drone WebDriver` instead
WebElement autocompleteSupplierOps =
driver.findElement(By.css("input[id*='supplierOps_input']"));
But it should give you the same result as #FindBy do - however it will check that the issue isn't caused by injection, but some other issue is appearing.
You might have wrong CSS selector - support of CSS selectors depends on the used browser and its version.
The node you are trying to find doesn't have to be yet in the page, you might need to wait before it will appear using Waiting API or request guards.
The best practice is usage of remote reusable session and real browser in a development - it can reveal the cause quickly.
I think that instead of using #FindBy(css ="...") you could try #FindBy(xpath="...") I find it a lot more reliable.
Related
I'm attempting to create an ArrayList of WebElements from this page:
If all the elements are displayed, they're able to be added to the ArrayList just fine.
ArrayList<WebElement> TodoArray = new ArrayList<WebElement>();
TodoArray.add(todo1);
If I remove one of the elements from the web page(not code) I get this error when running my Test Case:
org.openqa.selenium.NoSuchElementException: Unable to locate element: *[name='todo[9]']
Is there any way to bypass this error and force java to skip the missing element, but continue adding the elements that are displayed?
My code is pretty straight forward. I'm using JUnit to run my test cases. The elements are defined correctly using the #FindBy annotations.
#FindBy(name="todo[1]")
WebElement todo1;
I tried surrounding the variables with Try/Catch and using NoSuchElementException, but I wasn't sure how to format it. Is using the "continue" keyword possible in this situation?
Hopefully I provided enough information. Any help would be appreciated. Thank you!
The exact syntax for Java Try/Catch would be this for you:
try {
#FindBy(name="todo[1]")
WebElement todo1;
}
catch(NoSuchElementException e) {
System.out.println("could not find element todo1");
}
...
//rest of your code
Does this help?
Or you can use fluent wait, this way:
Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
.withTimeout(10, SECONDS)
.pollingEvery(1, SECONDS)
.ignoring(NoSuchElementException.class);
I think this is a more elegant way to do it.
Summary
I want to figure out a way to add a <script> tag into the head of DOM using Selenium's JavascriptExecutor, or any other way of doing this would be nice.
I have tried many ways and also found a few similar topics and none of them solved my problem which is why I felt the need to ask it on here.
For example :
Suggested solutions in this question did not solve my problem. Some people say it worked for them but nope, it didn't for me.
What I've been trying to execute?
Here is the small snippet of the code that I want to execute:
WebDriver driver = new FirefoxDriver();
JavascriptExecutor jse = (JavascriptExecutor) driver;
jse.executeScript("var s = document.createElement('script');");
jse.executeScript("s.type = 'text/javascript';");
jse.executeScript("s.text = 'function foo() {console.log('foo')}';");
jse.executeScript("window.document.head.appendChild(s);");
I just skipped the code above where you navigate to a webpage using driver.get() etc. and then try to execute the scripts.
Also, s.text would contain the actual script that I want to use so I just put there a foo() function just to give the idea.
The above code throws this error when you run it:
Exception in thread "main" org.openqa.selenium.JavascriptException: ReferenceError: s is not defined
So far I've tried every possible solution I could find on the Internet but none of them seems to work.
OP came up with the following solution:
jse.executeScript("var s=window.document.createElement('script');" +
"s.type = 'text/javascript';" + "s.text = function foo() {console.log('foo')};" +
"window.document.head.appendChild(s);");
For one, this line is invalid.
jse.executeScript("s.text = 'function foo() {console.log('foo')}';");
Note how you wrap single-quote text in single quotes. Use one set as "\""
I would personally do this by doing (edited to make it a global function):
using OpenQA.Selenium.Support.Extensions;
driver.ExecuteJavascript("window.foo = function foo() {console.log('foo')}");
It's as simple as that. You are registering foo as a method by doing this. After you execute this javascript, you can manually go in to the browser developer tools and call "foo()" to check. Additionally, you can check this by registering it directly in the console. Just enter "function foo() {console.log('foo')}" into your browser console, and then call "foo()".
No need to add this as a script tag.
EDIT #2: I fixed my above code suggestion so that the method is assigned to the window, and thus accessible globally, and outside of the anonymous script that javascript executor runs the code in. The original issues with this not working are resolved by this, at least in my testing of it.
I have written a simple code to enter email in facebook login page. But while entering the email value through sendKeys i am getting validation like "The method sendKeys(String) is undefined for the type By". I have already checked the compliance version which is 1.8. So what is going wrong here??. Below is code snippet:
package SeleniumTests;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.firefox.FirefoxDriver;
public class OpenFacebook {
public static void main(String[] args) {
System.setProperty("webdriver.gecko.driver","geckodriver path");
WebDriver driver=new FirefoxDriver();
driver.get("http:\\facebook.com");
driver.manage().window().maximize();
driver.findElement(By.xpath("//[#id='email']").sendKeys("swarup.wipro#gmail.com");
}
}
I am getting an option of Add cast to By.xpath as a quick fix. Can somebody explain the usage of this or is there any other solution available
As #KoustubhMadkaikar and #AndiCover mentioned, though adding the necessary closing bracket i.e. ) will solve the current issue but as per the best practices you should supply the tagName for your Locator Strategy to be robust and you can use either of the following solutions:
xpath:
driver.findElement(By.xpath("//input[#id='email']")).sendKeys("swarup.wipro#gmail.com");
cssSelector:
driver.findElement(By.cssSelector("input#email")).sendKeys("swarup.wipro#gmail.com");
A closing bracket is missing in your code before .sendKeys
Try following:
driver.findElement(By.xpath("//[#id='email']")).sendKeys("swarup.wipro#gmail.com");
You missed closing round bracket ), use:
driver.findElement(By.xpath("//[#id='email']")).sendKeys("swarup.wipro#gmail.com");
What can I do in case if I load the page in Selenium and then I have to do like 100 different parsing requests to this page?
At this moment I use different driver.findElement(By...) and the problem is that every time it is a http (get/post) request from java into selenium. From this case one simple page parsing costs me like 30+ seconds (too much).
I think that I must get source code (driver.getPageSource()) from first request and then parse this string locally (my page does not change while I parse it).
Can I build some kind of HTML object from this string to keep working with WebElement requests?
Do I have to use another lib to build HTML object? (for example - jsoup) In this case I will have to rebuild my parsing requests from webelement's and XPath.
Anything else?
When you call findElement, there is no need for Selenium to parse the page to find the element. The parsing of the HTML happens when the page is loaded. Some further parsing may happen due to JavaScript modifications to the page (like when doing element.innerHTML += ...). What Selenium does is query the DOM with methods like .getElementsByClassName, .querySelector, etc. This being said, if your browser is loaded on a remote machine, things can slow down. Even locally, if you are doing a huge amount of round-trip to between your Selenium script and the browser, it can impact the script's speed quite a bit. What can you do?
What I prefer to do when I have a lot of queries to do on a page is to use .executeScript to do the work on the browser side. This can reduce dozens of queries to a single one. For instance:
List<WebElement> elements = (List<WebElement>) ((JavascriptExecutor) driver)
.executeScript(
"var elements = document.getElementsByClassName('foo');" +
"return Array.prototype.filter.call(elements, function (el) {" +
" return el.attributes.whatever.value === 'something';" +
"});");
(I've not run the code above. Watch out for typos!)
In this example, you'd get a list of all elements of class foo that have an attribute named whatever which has a value equal to something. (The Array.prototype.filter.call rigmarole is because .getElementsByClassName returns something that behaves like an Array but which is not an Array so it does not have a .filter method.)
Parsing locally is an option if you know that the page won't change as you examine it. You should get the page's source by using something like:
String html = (String) ((JavascriptExecutor) driver).executeScript(
"return document.documentElement.outerHTML");
By doing this, you see the page exactly in the way the browser interpreted it. You will have to use something else than Selenium to parse the HTML.
Maybe try evaluating your elements only when you try to use them?
I dont know about the Java equivalent, but in C# you could do something similar to the following, which would only look for the element when it is used:
private static readonly By UsernameSelector = By.Name("username");
private IWebElement UsernameInputElement
{
get { return Driver.FindElement(UsernameSelector); }
}
I need to reach the following scenario:
1) Initializing JS var with JavascriptExecutor that will indicate if some operation is done.
2) Do some ordinary manipulation with the renderer page.
3) Verify the change to the var created in point (1).
For example:
jsc.executeScript("var test = false;");
Now, some manipulation is done.
And then:
String testVal = jsc.executeScript("return test;").toString
I get the error:
org.openqa.selenium.WebDriverException: {"errorMessage":"Can't find
variable: test","request":{"headers":{"Accept":"application/json,
image/png","Connection":"Keep-Alive","Content-Length":"35","Content-Type":"application/json;
charset=utf-8","Host":"localhost:14025"},"httpVersion":"1.1","method":"POST","post":"{\"args\":[],\"script\":\"return
test;\"}","url":"/execute","urlParsed":{"anchor":"","query":"","file":"execute","directory":"/","path":"/execute","relative":"/execute","port":"","host":"","password":"","user":"","userInfo":"","authority":"","protocol":"","source":"/execute","queryKey":{},"chunks":["execute"]},"urlOriginal":"/session/7e2c8ab0-b781-11e4-8a54-6115c321d700/execute"}}
When i'm running them in the same execution, it works correctly.:
String testVal = jsc.executeScript("var test = false; return test;").toString;
From JavascriptExecutor doc i found the explanation i needed:
Within the script, use document to refer to the current document. Note
that local variables will not be available once the script has
finished executing, though global variables will persist.
What is my alternative/workaround to this?
Not sure what is the motivation behind it, but you can use a globally available window object:
jsc.executeScript("window.test = false;");
String testVal = jsc.executeScript("return window.test;").toString
It might also be a use case for executeAsyncSript(), see:
Understanding execute async script in Selenium