I'm crawling website secured with Cloudflare and sometimes getting an error due to redirection to page with ReCapcha, the page cannot be even loaded due to some javascript error. The code is failing on #getPage method and i have no idea why.
Here is the code works fine for normal pages, but fails on confirmation page:
final WebClient webClient = new WebClient(BrowserVersion.CHROME);
webClient.getOptions().setJavaScriptEnabled(true);
final HtmlPage page = webClient.getPage("https://mydummy.site");
webClient.waitForBackgroundJavaScript(10000);
int waitForBackgroundJavaScript = webClient.waitForBackgroundJavaScript(200);
int loopCount = 0;
while (waitForBackgroundJavaScript > 0 && loopCount < 2) {
++loopCount;
waitForBackgroundJavaScript = webClient.waitForBackgroundJavaScript(200);
if (waitForBackgroundJavaScript == 0) {
break;
}
}
Logs:
java.lang.RuntimeException: com.gargoylesoftware.htmlunit.ScriptException: Wrapped com.gargoylesoftware.htmlunit.ScriptException: Wrapped com.gargoylesoftware.htmlunit.ScriptException: TypeError: Cannot find function start in object [object MessagePort]. (https://www.gstatic.com/recaptcha/api2/v1536705955372/recaptcha__en.js#249) (https://www.gstatic.com/recaptcha/api2/v1536705955372/recaptcha__en.js#253)
at com.gargoylesoftware.htmlunit.html.HtmlPage.initialize(HtmlPage.java:305)
at com.gargoylesoftware.htmlunit.WebClient.loadWebResponseInto(WebClient.java:539)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:399)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:316)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:467)
at com.gargoylesoftware.htmlunit.WebClient.getPage(WebClient.java:449)
at Main.htmlUnit(Main.java:156)
at Main.main(Main.java:43)
Caused by: com.gargoylesoftware.htmlunit.ScriptException: Wrapped com.gargoylesoftware.htmlunit.ScriptException: Wrapped com.gargoylesoftware.htmlunit.ScriptException: TypeError: Cannot find function start in object [object MessagePort]. (https://www.gstatic.com/recaptcha/api2/v1536705955372/recaptcha__en.js#249) (https://www.gstatic.com/recaptcha/api2/v1536705955372/recaptcha__en.js#253)
at com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine$HtmlUnitContextAction.run(JavaScriptEngine.java:892)
at net.sourceforge.htmlunit.corejs.javascript.Context.call(Context.java:616)
at net.sourceforge.htmlunit.corejs.javascript.ContextFactory.call(ContextFactory.java:532)
at com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine.execute(JavaScriptEngine.java:772)
at com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine.execute(JavaScriptEngine.java:748)
at com.gargoylesoftware.htmlunit.javascript.JavaScriptEngine.execute(JavaScriptEngine.java:104)
at com.gargoylesoftware.htmlunit.html.HtmlPage.loadExternalJavaScriptFile(HtmlPage.java:992)
at com.gargoylesoftware.htmlunit.html.HtmlScript.executeScriptIfNeeded(HtmlScript.java:371)
at com.gargoylesoftware.htmlunit.html.HtmlScript$2.execute(HtmlScript.java:246)
at com.gargoylesoftware.htmlunit.html.HtmlPage.initialize(HtmlPage.java:298)
We have been struggling with this issue as well. Our test suite ran perfectly until late 2018 when this issue broke all of our logins. I believe Google has put this in deliberately to break automated attempts to break captchas, because solving one part of this seems to only lead to another problem. Both loading the page and submitting the page causes issues, even if you tell HtmlUnitDriver to ignore all JavaScript errors.
I have tried several options at this point. If you use the Google specified test site key, then the errors go away. So if you have full server-side control of how that site key is generated, you are OK. Remember to ensure that the test site key shows up again on validation errors and all similar use cases, otherwise you will get that error.
(Unfortunately for us, our login page is plain JSP and so implementing this is a headache unless we want to change the URL everywhere. Still debating what to do, for right now we do have a workable if ugly solution that involves some conditional logic on the page and catching JavaScript exceptions at other points in the test code.)
Related
Update:
Please see the answer noted below as, ultimately, the problem had nothing to do with jsquery.
=============
Issue:
I submit an object to jquery to convert into a serialized string that will become part of a "POST" request to a server, and the data returned from the serialization request is different than the data sent on many occasions.
An example:
The JavaScript code that implements the server POST request:
function send_data(gpg_data) {
var query_string;
query_string = '?' + $.param(gpg_data, traditional = true);
console.log('gpg_data =', gpg_data)
console.log('query_string =', query_string);
$.post(server_address + query_string);
return;
}
This is the structure sent to the jquery param() function.
(copied from the browser console in developer mode.)
gpg_data =
{controller_status: 'Connected', motion_state: 'Stopped', angle_dir: 'Stopped', time_stamp: 21442, x_axis: 0, …}
angle_dir: "Stopped"
controller_status: "Connected"
force: 0
head_enable: 0
head_x_axis: 0
head_y_axis: 0
motion_state: "Stopped"
time_stamp: 21490
trigger_1: 0
trigger_2: 0
x_axis: 0
y_axis: "0.00"
. . . and the returned "query string" was:
query_string = ?controller_status=Connected&motion_state=Stopped&angle_dir=Stopped&time_stamp=21282&x_axis=0&y_axis=0.00&head_x_axis=0&head_y_axis=0&force=0&trigger_1=1&trigger_2=1&head_enable=0
The data received by the server is:
ImmutableMultiDict([('controller_status', 'Connected'), ('motion_state', 'Stopped'), ('angle_dir', 'Stopped'), ('time_stamp', '21282'), ('x_axis', '0'), ('y_axis', '0.00'), ('head_x_axis', '0'), ('head_y_axis', '0'), ('force', '0'), ('trigger_1', '1'), ('trigger_2', '1'), ('head_enable', '0')])
For example, note that "trigger_1" returns 1 when the data sent to it is a zero.
I have tried setting the query to "traditional = true" to revert to an earlier style of query handling as some articles suggested - which did not work. I tried this with jquery 3.2 and 3.6.
I am not sure exactly how jquery manages to munge the data so I have no idea where to look.
I have looked at my script and at the unpacked jquery code, and I can make no sense out of why or how it does what it does.
Any help understanding this would be appreciated.
P.S.
web searches on "troubleshooting jquery" returned very complex replies that had more to do with editing e-commerce web pages with fancy buttons and logins than with simply serializing data.
P.P.S.
I am tempted to just chuck the jquery and write my own serialization routine. (grrrr!)
===================
Update:
As requested, a link to the browser-side context.
To run: unpack the zip file in a folder somewhere and attach an analog joystick/gamepad to any USB port, then launch index.html in a local browser. Note that a purely digital gamepad - with buttons only or with a joystick that acts like four buttons - won't work.
You will want to try moving joystick axes 1 and 2, (programmatically axes 0 and 1) and use the first (0th) trigger button.
You will get a zillion CORS errors and it will complain bitterly that it cannot reach the server, but the server side context requires a GoPiGo-3 robot running GoPiGo O/S 3.0.1, so I did not include it.
Note: This does not work in Firefox as Firefox absolutely requires a "secure context" to use the Gamepad API. It does work in the current version of Chrome, (Version 97.0.4692.99 (Official Build) (64-bit)), but throws warnings about requiring a secure context.
Please also note that I have made every attempt I know how to try to troubleshoot the offending JavaScript, but trying to debug code that depends on real-time event handling in a browser is something I have not figured out how to do - despite continuous searching and efforts. Any advice on how to do this would be appreciated!
======================
Update:
Researching debugging JavaScript in Chrome disclosed an interesting tidbit:
Including the line // #ts-check as the first line in the JavaScript code turns on additional "linting" (?) or other checks that, (mostly) were a question of adding "var" to the beginning of variable declarations.
However. . . .
There was one comment it made:
gopigo3_joystick.x_axis = Number.parseFloat((jsdata.axes[0]).toFixed(2));
gopigo3_joystick.y_axis = Number.parseFloat(jsdata.axes[1]).toFixed(2);
I could not assign gopigo3_joystick.y_axis to a string object, (or something like that), and I was scratching my head - that was one of the pesky problems I was trying to solve!
If you look closely at that second line, you will notice I forgot a pair of parenthesis, and that second line should look like this:
gopigo3_joystick.y_axis = Number.parseFloat((jsdata.axes[1]).toFixed(2));
Problem solved - at least with respect to that problem.
I figured it out and it had nothing to do with jquery.
Apparently two things are true:
The state of the gpg_data object's structure is "computed", (snapshot taken), the first time the JavaScript engine sees the structure and that is the state that is saved, (even though the value may change later on). In other words, that value is likely totally bogus.
Note: This may only be true for Chrome. Previous experiments with Firefox showed that these structures were updated each time they were encountered and the values seen in the console were valid. Since Firefox now absolutely requires a secure context to use the gamepad API, I could not use Firefox for debugging.
I was trying to be "too clever". Given the following code snippet:
function is_something_happening(old_time, gopigo3_joystick) {
if (gopigo3_joystick.trigger_1 == 1 || gopigo3_joystick.head_enable == 1) {
if (old_time != Number.parseFloat((gopigo3_joystick.time_stamp).toFixed(0))) {
send_data(gopigo3_joystick)
old_time = gopigo3_joystick.time_stamp
}
}
return;
}
The idea behind this particular construction was to determine if "something interesting" is happening, where "something interesting" is defined as:
A keypress, (handled separately)
A joystick movement if either the primary trigger or the pinky trigger is pressed.
Movement without any trigger pressed is ignored so that if the user accidentally bumps against the joystick, the robot doesn't go running around.
Therefore the joystick data only gets updated if the trigger is pressed. In other words, trigger "release" events - the trigger is now = 0 - are not recorded.
The combination of these two events - Chrome taking a "snapshot" of object variables once and once only, (or not keeping them current) - and the trigger value persisting, lead me to believe that jquery was the problem since the values appeared to be different on each side of the jquery call.
My objective: Test out my error handling functionality.
Temporary solution: Have a custom route: /error, which contains code which purposefully produces fatal error.
var a = undefined;
a.b.c // Breaks.
The above works, but I can't use it to test production site as the page is not required.
I was looking for a way to test it via the browser. I tried simply adding"
throw new Error("Custom error thrown here") to the console. That doesn't actually break it during runtime.
I tried adding a break point and adding the same code: throw new Error("Custom error thrown here"). That didn't work either.
Any other easier ways to do this rather than the above?
I was looking for a way where I can do it via browser only.
Thanks.
You did not clearly mention how and where the error should be thrown. I will assume that you can use a modified copy of your JavaScript file to throw errors. The modified file will reside on your computer and only be used when you're using Chrome developer tools. This feature is called Local Overrides. The steps are as follows:
Open the webpage
Open Chrome developer tools for that webpage
In Sources panel go to Overrides tab
Click Select folder for overrides and choose a folder on your computer
A warning appears on the webpage which reads "DevTools requests full access to ..." which you must allow
In Sources panel go to Page tab
Locate the file in which you need to inject the "throw error" code
Right click and choose Save for overrides
Now you can edit the copy of the file on your computer or from within developer tools. Insert the code that produces the error at the desired location. When you reload the page with developer tools open, Chrome will load the local copy of the JavaScript file and throw the error. The error thrown that way will contain the context from where it originated e.g. call stack. If the developer tools are closed then live copy will be used.
If I got your question right, this is How you can do it from the console:
var script_tag = document.createElement('script');
script_tag.type = 'text/javascript';
script_tag.text = 'throw new Error("Custom error thrown here")';
document.body.appendChild(script_tag);
Or if you want you can trigger it on click:
var script_tag = document.createElement('script');
script_tag.type = 'text/javascript';
script_tag.text = 'window.document.onclick = function() { throw new Error("Custom error thrown here")}';
document.body.appendChild(script_tag);
And then you click anywhere on the page, to throw the error;
I would use the exec function which actually takes string and runs the code within at compile time.
exec('a.b.c')
You won't be able to throw an error inside your application from the console, since you are out of scope of the app.
Having said that, one slightly awkward way you could do this is by adding a breakpoint at the start of the javascript file.
Reload the page and your app will pause at the breakpoint - you can then modify the code as you need - like adding a throw new Error("something...") - and save your edits.
Then allow the code to run and you will see your error.
A downside is if you reload the changes will be gone, but I believe it's as close as you can get to modifying code at runtime.
Add this code to your production code
window.addEventListener('err', () => {
throw new Error('break it');
})
and when you want to create an error simply
dispatchEvent(new Event('err'))
in the console
You can use a global variable, which is accessible from your app and from debug console.
if (window.shouldThrow) {
throw new Error("Custom error thrown here");
}
This way you can turn on/off the exception throwing using the window.shouldThrow variable.
Try this way to catch error detail on run time
try
{
var a = undefined;
a.b.c // Breaks.
}
catch ( e )
{
alert("Error: " + e.description );
}
https://github.com/gg2001/monero/blob/master/monero/NewWallet.js
I have a js file that is quite large 6000 lines and JavaScript core does not seem to be able to retrieve variable values whereas running the same file in any web browser works fine for me. When I try to retrieve the value of a variable it shows up as undefined, but when I use a js console in a browswer it shows up fine. I am speculating that this is due to the size of the file because when I put
var helloWorld = "Hello World";
in the front of the js file this swift code can retrieve it
func helloWorld() {
if let variableHelloWorld = self.jsContext.objectForKeyedSubscript("helloWorld") {
print(variableHelloWorld.toString())
}
}
but when I put it at the end it cannot.
Normally this indicates a parsing error. Try adding an error handler to self.jsContext before calling objectForKeyedSubscript() and see if it outputs anything insightful.
self.jsContext.exceptionHandler = { context, exception in
print("JS Error: \(exception?.description ?? "unknown error")")
}
Although your JS code may be valid in a browser console, iOS Safari doesn't support as many Javascript features as newer browsers.
I did see a line in your JS source code beginning with just a semicolon (followed immediately by (function). I wonder if the parser might complain about an empty line without a statement..? Maybe nothing, though.
I had been encountering this issue for a while now. Browsed through a lot of threads on SO & other forums but still clueless.
While automating a simple flow on a Web Application with Selenium 3.4.0, geckodriver v0.16.1 & Mozilla Firefox 53.0 within Eclipse Neon v2 IDE, I am facing an error intermittently on the console as:
JavaScript error: https://www.url.com/my, line 1715: TypeError: document.getElementById(...) is null
Though using chromedriver v2.29/Google Chrome 58.0 or using Python I don't face any such issue.
Once this error appears, the Test Execution halts and finally shows TimeoutException as follows:
Exception in thread "main" org.openqa.selenium.TimeoutException: Timeout loading page after 300000ms
URL of the website is: https://www.shareinvestor.com/my
The HTML DOM is:
<div id="sic_sitemap">
<div id="sic_container">
<div id="sic_header">
<h1 id="sic_siteTitle">
<div id="sic_headerMembershipLink">
<a id="sic_mobileEdition" href="/mobile">
<div id="sic_loginContainer" class="sic_logIn" style="">
<div class="sic_login-wrap">
<div class="sic_logIn-subscribe">
<div class="sic_logIn-bg">
<a href="/user/login.html">
</div>
</div>
</div>
</div>
<div id="sic_subHeader">
<div id="sic_mainNav" class="sic_withRightCorner">
<div id="sic_sideBar" class="sic_expanded { selected_market_suffix: 'MY'}">
<div class="sic_superBanner sic_superBannerTop">
<div id="sic_content" class="sic_collapsed">
<div id="sic_footer" class="si_fixed">
</div>
As of now, I have tried out the following options but of no avail:
Java Click
JavascriptExecutor Click
Actions Click
Here is my code:
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.firefox.FirefoxDriver;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.remote.DesiredCapabilities;
public class 78644072022 {
public static void main(String[] args) {
System.setProperty("webdriver.gecko.driver", "C:\\Utility\\BrowserDrivers\\geckodriver.exe");
DesiredCapabilities dc = DesiredCapabilities.firefox();
dc.setCapability("marionette", true);
WebDriver driver = new FirefoxDriver(dc);
driver.manage().window().maximize();
driver.get("https://www.shareinvestor.com/my");
WebElement login_button = driver.findElement(By.xpath("//div[#id='sic_loginContainer']/div/div[#class='sic_logIn-bg']/a"));
//Java Click
login_button.click();
//JavascriptExecutor Click
/*JavascriptExecutor jse = (JavascriptExecutor)driver;
jse.executeScript("arguments[0].click();", login_button);*/
//Actions Click
/*Actions act = new Actions(driver);
act.moveToElement(login_button).click().build().perform();*/
driver.findElement(By.xpath("//input[#id='sic_login_header_username']")).sendKeys("debanjan");
driver.findElement(By.xpath("//input[#id='sic_login_header_password']")).sendKeys("5786");
driver.findElement(By.xpath("//input[#id='sic_login_submit']")).click();
}
}
I am looking for a Java Solution with geckodriver to overcome the error JavaScript error:TypeError: document.getElementById(...) is null
In one of the SO threads I saw a solution like:
You need to do a null check in updateHTML like this:
function updateHTML(elmId, value) {
var elem = document.getElementById(elmId);
if(typeof elem !== 'undefined' && elem !== null) {
document.getElementById(elmId).innerHTML = value;
}
}
Can we implement this?
Any suggestions & pointers will be helpful.
I am looking for a Java Solution with geckodriver to overcome the error JavaScript error:TypeError: document.getElementById(...) is null
To answer your question, I don't believe there's anything you can do to "fix" this via Java/Selenium. This is a JavaScript error, which originates from the website that you are trying to access. You might want to consider contacting their support team, maybe one of their developers can look at this issue.
Instead of clicking on the login button, maybe consider navigating directly to the login page?
driver.get("https://www.shareinvestor.com/user/login.html");
Firstly, Those javascript errors are not triggered due to any of the selenium code. Ofcourse, the timeout has been triggered by the selenium(will discuss on this on a later point).
You will get that javascript error irrespective of any kind of browser you launch the URL with. But in case of the gecko, you are notified in the eclipse console with but not in case of Chrome. If you need to see the java script error in chrome, just launch the url in chrome and go to devtools/console(F12). You can also see the same in firefox console too.
Chrome Img:
Secondly, We are getting timeout exception because the site is really taking too much time to load. I have been waited for 7 minutes and the page is still loading even now. Selenium won't executes its script unless the page has been completely launched. As a result we are getting timeout exception(not sure about the default page launch time permitted). I thought of bypassing directly to the login page ("https://www.shareinvestor.com/user/login.html") and that's also not taking any finite time to load completely.
Intepolation: Those java script errors are not an issue for automation But those page loads are really. This site doesn't seems like a good candidate for automation with this issue.
Update1: otherwise we can also stop the page loading via another thread like sending "esc" key sequence using Action class.
Update2: I tried the same code today and it works fine. Below is the code snippet that i have tried(There is no change at all)
public static void main(String[] args) throws InterruptedException
{
System.setProperty("webdriver.gecko.driver", "F:\\Softwares\\Selenium\\Webdriver\\geckodriver.exe");
WebDriver driver = new FirefoxDriver();
driver.manage().window().maximize();
driver.manage().timeouts().implicitlyWait(5, TimeUnit.SECONDS);
driver.get("https://www.shareinvestor.com/my");
WebElement login_button = driver.findElement(By.xpath("//div[#id='sic_loginContainer']/div/div[#class='sic_logIn-bg']/a"));
//Java Click
login_button.click();
System.out.println("In Login PAge");
driver.findElement(By.xpath("//input[#id='sic_login_header_username']")).sendKeys("debanjan");
driver.findElement(By.xpath("//input[#id='sic_login_header_password']")).sendKeys("5786");
System.out.println("Entered password");
driver.findElement(By.xpath("//input[#id='sic_login_submit']")).click();
}
Selenium version - 3.4.0
Gecko driver - v0.16.1(mine is 32 bit)
Mozilla - 51.0.1 (Update=>It's working on 53.02 also)
Hope this helps you. Thanks.
I think I've managed to find what is causing this uproar in your script.
I inspected your HTML and it seems javascript method function showRemaining() is causing this problem; because showRemaining() contains this statement
document.getElementById('countdown').innerHTML = '';
where it tries to set innerHTML attribute for element having id as countdown to ''.
But countdown doesn't exist anywhere on your web page hence the error.
TypeError: document.getElementById(...) is null
and somehow selenium isn't able to look past this error. So I think getting it fixed from developers should help you.
UPDATE :
Basically you need to wait all elements to load using implicit wait, once all elements are loaded, then your Javascript error gets resolved and ultimately interaction with the webpage is not hindered by that javascript error:
driver.get("https://www.shareinvestor.com/my");
driver.manage().timeouts().implicitlyWait(20, TimeUnit.SECONDS);
driver.manage().window().maximize();
/*String script = "function updateHTML(countdown, value) {" +
"var elem = document.getElementById('countdown');"+
"if(typeof elem !== 'undefined' && elem !== null) {"+
" document.getElementById('countdown').innerHTML = value;"+
"}"+
"}";
((JavascriptExecutor) driver).executeScript(script);*/
WebElement login_button = driver.findElement(By.xpath("//div[#id='sic_loginContainer']/div/div[#class='sic_logIn-bg']/a"));
login_button.click();
driver.findElement(By.xpath("//input[#id='sic_login_header_username']")).sendKeys("debanjan");
driver.findElement(By.xpath("//input[#id='sic_login_header_password']")).sendKeys("5786");
driver.findElement(By.xpath("//input[#id='sic_login_submit']")).click();
It looks like the XPath for the login_button is incorrect:
By.xpath("//div[#id='sic_loginContainer']/div/div[#class='sic_logIn-bg']/a");
Should be:
By.xpath("//div[#id='sic_loginContainer']/div/div/div[#class='sic_logIn-bg']/a");
This may explain the TimeoutException, since Selenium is unable to locate the non-existent element. EDIT: My mistake, you should see a NoSuchElementException if the element cannot be located.
As for the JavaScript error, unless you were trying to use Selenium to access the web element being manipulated by the JavaScript code (id='countdown' element), Selenium should just ignore the error. Otherwise, there's the potential for other Selenium errors, such as StaleElementReferenceException, but that does not seem to be the case.
This is due to your application HTML page using the async javascript and it uses some variable reference that is not available at the time of execution. we had same issue and asked developer to follow some best practices for javascript in the HTML like putting the script tag at end of the page. I have checked the HTML page source of the site and it contains many script tags in between the code. This will block the DOM rendering. Better, ask the developer to follow some best practices to include script tag in HTML. you refer the link Where should I put <script> tags in HTML markup? for the best practices.
I'm currently creating a very small form on my homepage using HTML and JavaScript. I've run into a problem I'm pretty sure I could circumvent somehow (probably in a pretty ugly way though), but I got interested in why this error appears.
I have a form on my page which I specify in the following way:
<FORM name="form1" onsubmit="submitTheScript()">
The function "submitTheScript()" is placed in the header and reads:
setCookie("F_GivenSum", GivenSum);
window.location="LastScreen.html";
"setCookie()" is a function that basically just, well, creates a cookie :).
Now, the problem arises with the last line of code. I'm trying to send the user to another page, after the cookie has been set (I'm doing some controls that the input value is alright, but I've skipped that part here) but FireFox gives me the following error:
uncaught exception: [Exception... "Cannot modify properties of a WrappedNative" nsresult: "0x80570034 (NS_ERROR_XPC_CANT_MODIFY_PROP_ON_WN)" location: "JS frame :: chrome://global/content/bindings/autocomplete.xml :: onxblpopuphiding :: line 827" data: no]
I guess I can't call this function the way I do, from within a onsubmit command, however, I don't see why.
Okay, I did a quick test and found out that you need to assign the onsubmit event handler via javascript like so:
document.getElementById("myform").onsubmit = doSubmit;
function doSubmit() {
document.cookie = "F_GivenSum=200";
window.setTimeout(function() {
window.location = ('test2.html');
}, 20);
return false;
}
This did not give any errors in FF.