Automatic clicking an button in JavaFX Webview - java

I am working an an JavaFX Webbrowser that can autologin to some sites, i know how to set the data to username and password fields but how to i make it execute the login button click?
This is what i got so far:
String email = "document.getElementsByName('email')[0].value='MY_EMAIL';";
String pass = "document.getElementsByName('pass')[0].value='MY_PASSWORD';";
String login = "";
webEngine.executeScript(email);
webEngine.executeScript(pass);
webEngine.executeScript(login);
and this is the javascript code of the button it should click:
<label class="uiButton uiButtonConfirm" id="loginbutton" for="u_0_c"><input value="Aanmelden" tabindex="4" type="submit" id="u_0_c"></label>

this is a concentrated, non-specialized example... uses the dom.w3c.Node.* package
HTMLInputElement element = (HTMLInputElement)myWebView.getEngine().getDocument().getElementsByTagName("input").item(0);
element.click();
Find a way to handle the object you're looking for, and it will work.

I haven't tried that, but it should work. The idea is add jQuery to the loaded page and then to use it to click the button. This post explains how to do this with JS: How Do I Add jQuery To Head With JavaScript? So with Java it should be (I've copied the first answer into a string and executing it with the web engine):
String script = "script = document.createElement('script');\n" +
"\n" +
"script.onload = function() {\n" +
" // jQuery is available now\n" +
"};\n" +
"var head = document.getElementsByTagName(\"head\")[0];\n" +
"\n" +
"script.type = 'text/javascript';\n" +
"script.src = 'https://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js';\n" +
"\n" +
"head.appendChild(script);";
webEngine.executeScript(script);
This should be done right when WebView is initialized (via webEngine.getLoadWorker().stateProperty().addListener(...)). Note: jQuery is loaded asynchronously.
Now you should be able to click a button with jQuery:
webEngine.executeScript("$('#buttonId')[0].click()");
This post should help you with debugging JS in a WebView: JAVAFX / WebView / WebEngine FireBugLite or Some other debugger?

Related

Highlight text in webview and save it

I have a webview that I display some html texts (I have them in assets). I'd like to allow users to highlight some parts of it.
I was thinking in some solutions:
try to put the texts user hightlight in a shared pref and use:
webview.findAllAsync(shared_pref_string);
webview.setFindListener(new FindListener() {
#Override
public void onFindResultReceived(int activeMatchOrdinal, int numberOfMatches, boolean isDoneCounting) {
// try to select the texts.
}
});
The problem I see is, user can select one word, like "what", and this code will select all "whats" the text has.
Use javascript:
public static String Highlightscript = " <script language="javascript">" +
"function highlightSelection(){" +
"var userSelection = window.getSelection();" +
"for(var i = 0; i < userSelection.rangeCount; i++)"
+ " highlightRange(userSelection.getRangeAt(i));" +
"}" +
"function highlightRange(range){"+
"span = document.createElement(\"span\");"+
"span.appendChild(range.extractContents());"+
"span.setAttribute(\"style\",\"display:block;background:#ffc570;\");"+
"range.insertNode(span);}"+
"</script> ";
webView.loadUrl("javascript:highlightSelection()");
But this 2 solutions not seems nice to me, any other best way to do this and more modern?
this android library is implemented what you need:
https://github.com/FolioReader/FolioReader-Android
they are using this javascript library https://github.com/timdown/rangy, maybe this will make sense.

Unable get options from a Select using Selenium

I am having difficulty selecting options from a dropdown list using Selenium Webdriver. Below is the HTML snippet:
<span id="id14">
<div class="stadium-input-row">
<span class="inputContainer">
<select id="id1f" class="departurePoint stadiumSelect" onchange="var wcall=..........">
<option value="">Please select</option>
<option value="BHX"> Birmingham - (BHX) </option>
<option value="GLA"> Glasgow - (GLA) </option>
<option value="LON"> London - (LON) </option>
<option value="MAN"> Manchester - (MAN) </option>............
The select tag id changes each time the DOM is loaded.
The select tag is greyed out until it is interacted with.
My code
Select oSelect = new Select(driver.findElement(By.xpath("(.//select)[1]"));
oSelect.selectByVisibleText("Birmingham");
Error
org.openqa.selenium.NoSuchElementException: Cannot locate element
with text: Birmingham
In debugging mode, the dropdown does not seem to be activated (clicked) by the driver.
There's a weird (at least it's weird to me) thing going on on that site. The SELECT that you are trying to access is permanently hidden (which means it can't be interacted with using Selenium). Users interact with DIVs, etc. via a fake dropdown (it's not a SELECT) and the result of those selections are stored in the hidden SELECT. There are two ways to accomplish your task.
Deal with what you can see.
This is really a pain on this site. I think it can be ultimately done but I don't want to spend any more time on it myself so I'll show you the door and you'll have to pick up where I left off. The code below will open the departure dropdown. From there, you find the departure airport and click on it. Done. Harder than it sounds...
driver.findElement(By.cssSelector("div.custom-select.departurePoint.airportSelect")).click();
Cheat and use JavascriptExecutor.
NOTE: By doing this you are no longer executing a real user scenario since users can't click on hidden elements or inject Javascript commands into the page. As long as you are OK with this, here's a sample.
This code executes Javascript on the page using the JavascriptExecutor. You pass the function a string you are looking for in the options, e.g. you can pass "EMA" or "East Midlands - (EMA)" or anywhere inbetween. The JS code will grab the hidden SELECT, search through the OPTIONS, and select the first that matches.
Also note: You will not see the UI update with the selection. Once you click SEARCH, it will work. I have tried it myself and it works.
Yet another note: I use Eclipse as my editor so the // #formatter:off you see in the code below keeps Eclipse from wrapping/reformatting the extra long string that contains the JS code. You can leave or remove it as you like. I like it there because I can still read the JS code with it formatted and indented like it is and I don't want Eclipse messing it up.
selectOption("EMA");
public void selectOption(String option)
{
// #formatter:off
String script =
"function selectOption(s) {\r\n" +
" var sel = document.querySelector('select.departurePoint.airportSelect');\r\n" +
" for (var i = 0; i < sel.options.length; i++)\r\n" +
" {\r\n" +
" if (sel.options[i].text.indexOf(s) > -1)\r\n" +
" {\r\n" +
" sel.options[i].selected = true;\r\n" +
" break;\r\n" +
" }\r\n" +
" }\r\n" +
"}\r\n" +
"return selectOption('" + option + "');";
// #formatter:on
((JavascriptExecutor) driver).executeScript(script);
}
what you need to do:
List<WebElement> options = driver.findElement(By.xpath("//div[#class='stadium-input-row']//select")).findElements(By.tagName("option"));
that will create you list of option tags as WebElement objects
or
Select oSelect = new Select(driver.findElement(By.xpath("//div[#class='stadium-input-row']//select"));
or just take the select as WebElement object
WebElement selectElement = driver.findElement(By.xpath("//div[#class='stadium-input-row']//select"));
Hovering element
Actions action = new Actions(driver);
WebElement hoverElement = driver.findElement(By.xpath("//div[#class='stadium-input-row']//select"));
action.moveToElement(hoverElement);
action.click().build().perform();
As your exception clearly says Cannot locate element with text: Birmingham means it compares with whole visible text option so you should try as below :-
WebDriverWait wait = new WebDriverWait(driver, 10);
WebElement select = wait.until(ExpectedConditions.visibilityOfElementLocated(By.xpath("(.//select)[1]")));
Select oSelect = new Select(select);
oSelect.selectByVisibleText("Birmingham - (BHX)");
or
oSelect.selectByIndex(1);
or
oSelect.selectByValue("BHX");
Edited :- If unfortunately none of the above solution works you should try using JavascriptExecutor as below :-
((JavascriptExecutor)driver).executeScript("var select = arguments[0]; for(var i = 0; i < select.options.length; i++){ if(select.options[i].text.indexOf(arguments[1]) != -1){ select.options[i].selected = true; } }",
driver.findElement(By.xpath("(.//select)[1]")),
"Birmingham");
Hope it will help you..:)
Select will not work in all the situations. Try this code
List <WebElement> options = driver.findElements(By.xpath("Target Element with Options"));
String element;
for(int i=0;i<options.size();i++)
{
element = options[i].get(i).getAttribute("value");
if(element.equals("BHX")){
options.get(i).click();
}
}

How can I consistently remove the default text from an input element with Selenium?

I'm trying to use Selenium WebDriver to input text to a GWT input element that has default text, "Enter User ID". Here are a few ways I've tried to get this to work:
searchField.click();
if(!searchField.getAttribute("value").isEmpty()) {
// clear field, if not already empty
searchField.clear();
}
if(!searchField.getAttribute("value").isEmpty()) {
// if it still didn't clear, click away and click back
externalLinksHeader.click();
searchField.click();
}
searchField.sendKeys(username);
The strange thing is the above this only works some of the time. Sometimes, it ends up searching for "Enter User IDus", basically beginning to type "username" after the default text -- and not even finishing that.
Any other better, more reliable ways to clear out default text from a GWT element?
Edited to add: The HTML of the input element. Unfortunately, there's not much to see, thanks to the JS/GWT hotness. Here's the field when it's unselected:
<input type="text" class="gwt-TextBox empty" maxlength="40">
After I've clicked it and given it focus manually, the default text and the "empty" class are removed.
The JS to setDefaultText() gets called both onBlur() and onChange() if the change results in an empty text field. Guess that's why the searchField.clear() isn't helping.
I've also stepped through this method in debug mode, and in that case, it never works. When run normally, it works the majority of the time. I can't say why, though.
Okay, the script obviously kicks in when the clear() method clears the input and leaves it empty. The solutions it came up with are given below.
The naïve one, presses Backspace 10 times:
String b = Keys.BACK_SPACE.toString();
searchField.sendKeys(b+b+b+b+b+b+b+b+b+b + username);
(StringUtils.repeat() from Apache Commons Lang or Google Guava's Strings.repeat() may come in handy)
The nicer one using Ctrl+A, Delete:
String del = Keys.chord(Keys.CONTROL, "a") + Keys.DELETE;
searchField.sendKeys(del + username);
Deleting the content of the input via JavaScript:
JavascriptExecutor js = (JavascriptExecutor)driver;
js.executeScript("arguments[0].value = '';", searchField);
searchField.sendKeys(username);
Setting the value of the input via JavaScript altogether:
JavascriptExecutor js = (JavascriptExecutor)driver;
js.executeScript("arguments[0].value = '" + username + "';", searchField);
Note that javascript might not always work, as shown here: Why can't I clear an input field with javascript?
For what it is worth I'm have a very similar issue. WebDriver 2.28.0 and FireFox 18.0.1
I'm also using GWT but can reproduce it with simple HTML/JS:
<html>
<body>
<div>
<h3>Box one</h3>
<input id="boxOne" type="text" onfocus="if (this.value == 'foo') this.value = '';" onblur="if (this.value == '') this.value = 'foo';"/>
</div>
<div>
<h3>Box two</h3>
<input id="boxTwo" type="text" />
</div>
</body>
</html>
This test fails most of the time:
#Test
public void testTextFocusBlurDirect() throws Exception {
FirefoxDriver driver = new FirefoxDriver();
driver.navigate().to(getClass().getResource("/TestTextFocusBlur.html"));
for (int i = 0; i < 200; i++) {
String magic = "test" + System.currentTimeMillis();
driver.findElementById("boxOne").clear();
Thread.sleep(100);
driver.findElementById("boxOne").sendKeys(magic);
Thread.sleep(100);
driver.findElementById("boxTwo").clear();
Thread.sleep(100);
driver.findElementById("boxTwo").sendKeys("" + i);
Thread.sleep(100);
assertEquals(magic, driver.findElementById("boxOne").getAttribute("value"));
}
driver.quit();
}
It could just be the OS taking focus away from the browser in a way WebDriver can't control. We don't seem to get this issue on the CI server to maybe that is the case.
I cannot add a comment yet, so I am putting it as an answer here. I want to inform you that if you want to use only javascript to clear and/or edit an input text field, then the javascript approach given by #slanec will not work. Here is an example: Why can't I clear an input field with javascript?
In case you use c# then solution would be :
// provide some text
webElement.SendKeys("aa");
// this is how you use this in C# , VS
String b = Keys.Backspace.ToString();
// then provide back space few times
webElement.SendKeys(b + b + b + b + b + b + b + b + b + b);

Fill fields in webview automatically

I have seen this question floating around the internet, but I haven't found a working solution yet. Basically, I want to load my app and press a button; the button action will then fill in a username and password in a website already loaded in the webview (or wait for onPageFinished). Finally, the submit button on the login page will be activated.
From what I understand this can be done by doing a java injection with the loadUrl(javascript), but I don't know what the java commands would be to fill in the fields. The same question was asked for iOS, but the commands are slightly different.
Is it possible to do what I am asking with javascript in a webivew, or do I have to do a http-post without a webview like this or this?
Thank you so much for any help you can give!
Thanks all for your answer, it helped me, but didn't work.
It was allways opening a white page until i found this :
https://stackoverflow.com/a/25606090/3204928
So here complete solution, mixing all infos found here and there :
1) first of all you have to enable DOM storage, if you don't do that, .GetElementByXXX will return nothing (you have to do it before loading the page)
myWebView.getSettings().setDomStorageEnabled(true);
2)Your last Javascript call on GetElementByXXX MUST store the result in a variable
Exemple 1 :
_webview.loadUrl("javascript:var uselessvar =document.getElementById('passwordfield').value='"+password+"';");
here only one call (only one semi-colon) so we immediatly store the result in 'uselessvar'
Example 2 : see user802467 answer
here there is 3 calls (one for login field, one for password field, one to submit button), only the last call need to be store, it's done in 'frms'
Javascript programmers should easily explain this behaviour...
hope this will help
You don't need to use "java commands"... but instead JavaScript... for instance:
String username = "cristian";
webview.loadUrl("javascript:document.getElementById('username').value = '"+username+"';");
So basically, what you have to do is a big string of JavaScript code that will get those fields and put values on them; also, you can enable/disable the submit button from JavaScript.
This worked for me to fill form values and submitting the form:
webView.loadUrl("javascript: {" +
"document.getElementById('username').value = '"+uname +"';" +
"document.getElementById('password').value = '"+password+"';" +
"var frms = document.getElementsByName('loginForm');" +
"frms[0].submit(); };");
Here is complete code which works for me (Bitbucket):
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setDomStorageEnabled(true);
webView.loadUrl("http://example.com/");
webView.setWebViewClient(new WebViewClient(){
#Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
final String password = "password";
final String username = "username";
final String answer = 5;
final String js = "javascript:" +
"document.getElementById('password').value = '" + password + "';" +
"document.getElementById('username').value = '" + username + "';" +
"var ans = document.getElementsByName('answer');" +
"ans[0].value = '" + answer + "';" +
"document.getElementById('fl').click()";
if (Build.VERSION.SDK_INT >= 19) {
view.evaluateJavascript(js, new ValueCallback<String>() {
#Override
public void onReceiveValue(String s) {
}
});
} else {
view.loadUrl(js);
}
}
});
I tried #user802467 solution.But there was a different behaviour in 2 things
If I stored ONLY the last javascript call in a variable, it was not filling in the fields. Instead if I stored all the three calls in variables, it did
For some reason my form was not being submitted using submit(). But instead of submitting the form, if I clicked on the submit button using button.click(), I didnot need to store all the three calls and everything worked perfectly!
Here is what I used (but didnt work)
view.loadUrl("javascript: var x = document.getElementById('username').value = '" + username + "';" +
"var y = document.getElementById('password').value = '" + password + "';" +
"var form1 = document.getElementById('loginform');" +
"form1[0].submit(); ");
Here is the code that worked for me
view.loadUrl("javascript: document.getElementById('username').value = '" + username + "';" +
" document.getElementById('password').value = '" + password + "';" +
"var z = document.getElementById('submitbutton').click();"
);
It works for me
webView.loadUrl("javascript:var uselessvar =document.getElementById('regno').value='"+mob+"';",null);
webView.loadUrl("javascript:var uselessvar =document.getElementById('passwd').value='"+pass+"';",null);

Trying to do searchable username from query result

I'm using google app engine to do a website that has some messages, i would like to display the message with a name in form of a link that appointed to the person profile. The only problem is that all my webpages are dynamic and printed from the code.
What I first thought of doing was a link that in click it would call a method in the program with the parameter of the clicked name. The method would then receive the paramet of the name, do a query and print the profile in a new webpage. I think this would work fine. The problem and question i have is how do I do the clickable name with method call.
My code to print the text message to the html page now is:
List<Texto> results = (List<Texto>) query.execute(tituloparam);
if (!results.isEmpty())
{
for (Texto e : results)
{
resp.getWriter().println("Titulo:"
+ results.get(0).titulo);
resp.getWriter().println("Nome:<a href='/author?name=" + results.get(0).autor + "'>" + results.get(0).autor + "</a>");
resp.getWriter().println("Data:"
+ results.get(0).data);
resp.getWriter().println("Texto:"
+ results.get(0).texto);
}
}
So the autor would be the clickable object. Can anyone help me?
Edit 1: Thanks to uwe (Thank you) now I have a clickable object. But how do I call a method with the parameter = autor from there?
You need to add a link to it like this:
resp.getWriter().println("Nome:<a href='/author?name=" + results.get(0).autor + "'>" + results.get(0).autor + "</a>");
Then it shows it as link and makes it clickable.

Categories

Resources