Overwrite a javascript variable from webdriver - javascript

I'm not sure whether is there any solution for my issue but, unfortunately I haven't found any article or information about it.
The situation is the following. We have a site which uses jQuery heavily and there is a service which refreshes a part of the site in every 5th or 10th second. Due to this half of the time I got this error from WebDriver:
"Element not found in the cache - perhaps the page has changed since it was looked up"
According to the Internet I got this error when the DOM tree has changed between the moment when the WebElement has been initialized and when I want to use it to perform, for example, a click event.
According to our developers our jquery solution has a variable which controls whether the page will be refreshed or not. So, to solve my issue I have to overwrite this variable. I have to overwrite this variable in that way the jQuery will be able to understand it - I mean in the same instance if this definition is proper in this context.
So, I would like to ask whether is possible or not? If so, than I would like to ask a small example.
Thanks in advance!
AndrĂ¡s

I can only agree with Aleh.
Using JavaScriptExecutor is the only way to handle such issues.
I had a problem with jQuery jNice library and couldn't find any other solution.
Here is an example in Java for filling a text field:
JavascriptExecutor js = (JavascriptExecutor) webDriver;
js.executeScript("document.getElementsByName('<field_name_gets_here>')[0].value='" + your_value + "'");

If the JavaScript variable you mentioned is global, then yes - you can overwrite it by executing JavaScript from your Selenium. For example, if that variable is called doRefresh, you can overwrite it by executing JS like this: doRefresh = false; from Selenium.
If that variable is not global, the above won't work. However, it sounds like the elements in question might be dynamically loaded via ajax - in which case the xhr object is global and you can access it instead.
So, first you can make a local copy of the xhr object and then overwrite the original (effectively disabling it) by executing JavaScript from Selenium:
// create a copy of the xhr object for later use
var xhrHolder = window.XMLHttpRequest;
// overwrite the original object to disable it
window.XMLHttpRequest = {};
Then find your element via Selenium as you would normally. And proceed with your test.
When finished, you can put the xhr object back in place (so the page can continue refreshing and doing ajax) by executing JavaScript from Selenium:
// put the xhr object back
window.XMLHttpRequest = xhrHolder;

You can try my approach - I created my own wrapper for situations where page might be loading. The below part of code tries to search element in the loop, for three seconds (configurable). BTW the driver variable below is instance of WebDriver
private WebElement foundElement;
public WebElement find(By by){
for (int milis=0; milis<3000; milis = milis+200){
try{
foundElement = driver.findElement(by);
}catch (Exception e){
try {
Thread.sleep(200);
} catch (InterruptedException e1) {
e1.printStackTrace(); //To change body of catch statement use File | Settings | File Templates.
}
}
}
return foundElement;
}
And later in the code:
WebElement submitButton = find(By.id("submitNewBid"));
submitButton.click();

I believe it is possible. Example for c#:
((IJavaScriptExecutor)driver).ExecuteScript("window.$('.class').data('var') = 0;")

Related

Selenium click doesn't work with angularjs ng-click event

I'm looking to verify a particular external function is called on a ng-click event. The input declaration is as follows. This works fine in the browsers, so there are no problems functionally, only during testing with Selenium I'm not seeing the correct result. myFunction is calling an external myExternalFunction, which is the behaviour I'm trying to validate. We already have jasmine tests with spies validating this behaviour in a different environment, but Selenium is needed anyways. Also we don't use protractor at the moment. I'm looking for a solution with Selenium.
<input class="myClass" ng-click="myFunction()">
In the C# Selenium tests, I'm writing:
// Creating the mock external function since the context doesn't have it.
IJavaScriptExecutor executor = WebDriver as IJavaScriptExecutor;
executor.ExecuteScript("window.called='false'");
executor.ExecuteScript("window.external = {}");
// The behavior will be just setting the called variable to true.
// We will retrieve the variable and check the value to see if the function was called.
executor.ExecuteScript("window.external.myExternalFunction = function() {window.called = 'true';}");
// Select the element - there's only one element with this classname
IWebElement element = WebDriver.FindElement(By.ClassName("myClass"));
string value = "";
// Verified via debugging that the element is actually valid.
// Tried the following options.
// 1) Just click and wait for event propagation - didn't work.
element.Click(); // Didn't do anything so added a Thread Sleep to make sure.
Thread.Sleep(1000);
value = WebDriver.ExecuteJavaScript<string>("return window.called;"); // returns false - expected to be true.
// 2) Use the actions to focus and click.
IWebDriver driver = WebDriver;
Actions a = new Actions(driver);
a.MoveToElement(element).Perform();
a.MoveToElement(element).Click().Perform();
Thread.Sleep(1000);
value = WebDriver.ExecuteJavaScript<string>("return window.called;"); // returns false - expected to be true.
// 3) Select and click via javascript instead.
executor.ExecuteScript("angular.element('.myClass').click();");
Thread.Sleep(1000);
value = WebDriver.ExecuteJavaScript<string>("return window.called;"); // returns false - expected to be true.
I'm pretty much out of ideas at this stage. Is there no way to make Selenium play nice with angularjs? Similar question here: Selenium click event does not trigger angularjs ng-click without conclusive answer.
I would suggest, use xpath for locating the element and you should be good. Please share the xpath, if possible.
You can use css selector and It should work :
By.cssSelector("input[ng-click='myFunction()']")

OnContextCreated() in Cef not being called

I have a similar problem to the person in this post; I'm trying to extend the cefsimple.exe app included with the chromium embedded framework binaries to include a V8 handler. I implemented the OnContextCreated() method and made sure to extend RenderProcessHandler in the SimpleHandler class. I'm trying to implement a simple window bound variable called test_string; here's what my code looks like;
void SimpleHandler::OnContextCreated(
CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context)
{
CefRefPtr<CefV8Value> object = context->GetGlobal();
object->SetValue("test_string", CefV8Value::CreateString("this is a test"), V8_PROPERTY_ATTRIBUTE_NONE);
}
But the program never arrives at any breakpoints I add within the method, and the variable is undefined on any webpages I load within the app. I saw that one of the solutions in the other thread is to enable the settings.single_process flag, which i've done, but my code still doesn't reach the breakpoint.
To be clear, I'm accessing the variable on pages with window.test_string.
Make sure that you are sending that CefApp to CefExecuteProcess.
CefRefPtr<SimpleApp> app(new SimpleApp);
// CEF applications have multiple sub-processes (render, plugin, GPU, etc)
// that share the same executable. This function checks the command-line and,
// if this is a sub-process, executes the appropriate logic.
int exit_code = CefExecuteProcess(main_args, app, sandbox_info);
if (exit_code >= 0) {
// The sub-process has completed so return here.
return exit_code;
}
Found this solution here
Have you read through the General Usage guide? Some key points below
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-cefapp
https://bitbucket.org/chromiumembedded/cef/wiki/GeneralUsage#markdown-header-processes
The single_process mode is not supported so I've never used it. In general I'd avoid it. The multi process architecture means you need to attach the debugger to the process. The Chromium guide is relevant to CEF in this instance.
https://www.chromium.org/developers/how-tos/debugging-on-windows#TOC-Attaching-to-the-renderer
you need to ensure your App is derived from CefRenderProcessHandler
not SimpleHandler!!!
class SimpleApp : public CefApp
, public CefRenderProcessHandler
{
virtual void OnContextCreated(CefRefPtr<CefBrowser> browser,
CefRefPtr<CefFrame> frame,
CefRefPtr<CefV8Context> context) OVERRIDE;
valdemar-rudolfovich says you need to pass instance of SimpleApp in
CefExecuteProcess

Detect user key/mouse in Python Selenium

I'm using Selenium Browser for day to day browsing, and I'd like to fire some code when I press some keys on any page. At first I thought I can just load javascript on every page that registers keys/mouse input, but I'd actually really prefer to have some python list available with past keys/mouse clicks, e.g. my key example in javascript:
var myhistory = []
document.addEventListener("keydown", keyDownTextField, false);
function keyDownTextField(e) {
var keyCode = e.keyCode;
myhistory.push(keyCode)
}
Is there any way to do this in pure Python/Selenium?
What I would try:
Execute a javascript that registers at the document body
<body onkeyup="my_javasctipt_keyup()" and onkeydown="my_javasctipt_keydown()">
using browser.execute_script. (partially solved, see question)
Save the key up and keydown events in a variable in javascript. (solved, see question)
use browser.execute_script to return the variables.
What I am uncertain about:
The return value of browser.execute_script may return json serializable objects or strings only
keyup and keydown in body may not work if they are used in child elements that define their own event listeners
Hopefully this is of help. If any code results form this I would be interested in knowing.
This code is what I feel should work:
from selenium import webdriver
browser = webdriver.Firefox()
browser.execute_script("""var myhistory = []
document.addEventListener("keydown", keyDownTextField, false);
function keyDownTextField(e) {
var keyCode = e.keyCode;
myhistory.push(keyCode)
}""")
def get_history():
return browser.execute_script("myhistory")
# now wait for a while and type on the browser
import time; time.sleep(5000)
print("keys:", get_history())
The point is that the code of selenium can never run at the same time as the browser handles keyboard input. As such, events need to be handled in javascript, the result saved, e.g. in an array and then, when selenium is asked, the array is returned to Python.
boppreh/keyboard would let you do that.
You install it. pip install keyboard
You import it. import keyboard
You use it. keyboard.add_hotkey('left', print, args=['You pressed the left arrow key'])
Then you disable it. keyboard.remove_all_hotkeys()
Well, in that case you had to choose the right tool for the job, i advice puppeteer a web-automation family instrument a pure-made JS, which can easily interact with the browser ( from js to js ) and catch events directly from the other side without any mediation.
Yet with selenium you can still achieve this transitively without messing too much with the pages's code or overcharging it with unnecessary tasks, also reloading the page content resets all its variables, which means it's lossy approach. The best closest way is to set an eventhandler internally and directly catch it from outside using Runtime.evaluate instead because it doesn't affect the page content and specifically it sticks to the function until it yields something using promise calls, it's better away than probing around some global variable over and over which is seen a bad practise see here.
myhistory = []
evt_handler = """
new Promise((rs,rj) => window.onkeydown= e => rs(e.keyCode) )
"""
def waitforclick():
try:
myhistory.append(browser.execute_cdp_cmd('Runtime.evaluate', {'expression': evt_handler, 'awaitPromise': True,'returnByValue': True})['result']['value'])
except:
waitforclick()
To avoid locking out the cpu you need to fork a thread in parallel.
from threading import Timer
t = Timer(0.0, waitforclick)
then t.start() instead of waitforclick().
Also you can use timeout if you want to reject the promise with a zero value after some time.

How to code firefox extension which run javascript code in the page's context like firebug does

I know that for safety reasons that this is not easy to achieve, however there would be a way to do so as firebug does...
Please help, would like to invoke some script in the page's context to achieve some effect...
Basically, I would like to achieve two functionality:
1. add jQuery to any web page automatically if not already exist.
2. when open certain address, call a method of that page to auto notify the server. (an ajax functionality of the page)
I have tried to inject on the body, no luck.
tried to get the window object, which however do not have access to call the function.
Will try to change the location to something like: javascript:alert('test inject');
Many thx.
OK, after reading some official documentation and the GreaseMonkey's source, I get the following method which basically works for me.
Hope it will save sb's hour:
var appcontent = document.getElementById("appcontent"); // browser
if (appcontent) {
appcontent.addEventListener("DOMContentLoaded", function (evnt) {
var doc = evnt.originalTarget;
var win = doc.defaultView;
var unsafeWin = win.wrappedJSObject;
// vote.up is the function on the page's context
// which is take from this site as example
unsafeWin.vote.up(...);
}, true);
}
}
Greasemonkey does that. If you are developing your own extension with similar functionality, you can use Components.utils.evalInSandbox.

What causes the error "Can't execute code from a freed script"

I thought I'd found the solution a while ago (see my blog):
If you ever get the JavaScript (or should that be JScript) error "Can't execute code from a freed script" - try moving any meta tags in the head so that they're before your script tags.
...but based on one of the most recent blog comments, the fix I suggested may not work for everyone. I thought this would be a good one to open up to the StackOverflow community....
What causes the error "Can't execute code from a freed script" and what are the solutions/workarounds?
You get this error when you call a function that was created in a window or frame that no longer exists.
If you don't know in advance if the window still exists, you can do a try/catch to detect it:
try
{
f();
}
catch(e)
{
if (e.number == -2146823277)
// f is no longer available
...
}
The error is caused when the 'parent' window of script is disposed (ie: closed) but a reference to the script which is still held (such as in another window) is invoked. Even though the 'object' is still alive, the context in which it wants to execute is not.
It's somewhat dirty, but it works for my Windows Sidebar Gadget:
Here is the general idea:
The 'main' window sets up a function which will eval'uate some code, yup, it's that ugly.
Then a 'child' can call this "builder function" (which is /bound to the scope of the main window/) and get back a function which is also bound to the 'main' window. An obvious disadvantage is, of course, that the function being 'rebound' can't closure over the scope it is seemingly defined in... anyway, enough of the gibbering:
This is partially pseudo-code, but I use a variant of it on a Windows Sidebar Gadget (I keep saying this because Sidebar Gadgets run in "unrestricted zone 0", which may -- or may not -- change the scenario greatly.)
// This has to be setup from the main window, not a child/etc!
mainWindow.functionBuilder = function (func, args) {
// trim the name, if any
var funcStr = ("" + func).replace(/^function\s+[^\s(]+\s*\(/, "function (")
try {
var rebuilt
eval("rebuilt = (" + funcStr + ")")
return rebuilt(args)
} catch (e) {
alert("oops! " + e.message)
}
}
// then in the child, as an example
// as stated above, even though function (args) looks like it's
// a closure in the child scope, IT IS NOT. There you go :)
var x = {blerg: 2}
functionInMainWindowContenxt = mainWindow.functionBuilder(function (args) {
// in here args is in the bound scope -- have at the child objects! :-/
function fn (blah) {
return blah * args.blerg
}
return fn
}, x)
x.blerg = 7
functionInMainWindowContext(6) // -> 42 if I did my math right
As a variant, the main window should be able to pass the functionBuilder function to the child window -- as long as the functionBuilder function is defined in the main window context!
I feel like I used too many words. YMMV.
Here's a very specific case in which I've seen this behavior. It is reproducible for me in IE6 and IE7.
From within an iframe:
window.parent.mySpecialHandler = function() { ...work... }
Then, after reloading the iframe with new content, in the window containing the iframe:
window.mySpecialHandler();
This call fails with "Can't execute code from a freed script" because mySpecialHandler was defined in a context (the iframe's original DOM) that no longer exits. (Reloading the iframe destroyed this context.)
You can however safely set "serializeable" values (primitives, object graphs that don't reference functions directly) in the parent window. If you really need a separate window (in my case, an iframe) to specify some work to a remote window, you can pass the work as a String and "eval" it in the receiver. Be careful with this, it generally doesn't make for a clean or secure implementation.
If you are trying to access the JS object, the easiest way is to create a copy:
var objectCopy = JSON.parse(JSON.stringify(object));
Hope it'll help.
This error can occur in MSIE when a child window tries to communicate with a parent window which is no longer open.
(Not exactly the most helpful error message text in the world.)
Beginning in IE9 we began receiving this error when calling .getTime() on a Date object stored in an Array within another Object. The solution was to make sure it was a Date before calling Date methods:
Fail: rowTime = wl.rowData[a][12].getTime()
Pass: rowTime = new Date(wl.rowData[a][12]).getTime()
I ran into this problem when inside of a child frame I added a reference type to the top level window and attempted to access it after the child window reloaded
i.e.
// set the value on first load
window.top.timestamp = new Date();
// after frame reloads, try to access the value
if(window.top.timestamp) // <--- Raises exception
...
I was able to resolve the issue by using only primitive types
// set the value on first load
window.top.timestamp = Number(new Date());
This isn't really an answer, but more an example of where this precisely happens.
We have frame A and frame B (this wasn't my idea, but I have to live with it). Frame A never changes, Frame B changes constantly. We cannot apply code changes directly into frame A, so (per the vendor's instructions) we can only run JavaScript in frame B - the exact frame that keeps changing.
We have a piece of JavaScript that needs to run every 5 seconds, so the JavaScript in frame B create a new script tag and inserts into into the head section of frame B. The setInterval exists in this new scripts (the one injected), as well as the function to invoke. Even though the injected JavaScript is technically loaded by frame A (since it now contains the script tag), once frame B changes, the function is no longer accessible by the setInterval.
I got this error in IE9 within a page that eventually opens an iFrame. As long as the iFrame wasn't open, I could use localStorage. Once the iFrame was opened and closed, I wasn't able to use the localStorage anymore because of this error. To fix it, I had to add this code to in the Javascript that was inside the iFrame and also using the localStorage.
if (window.parent) {
localStorage = window.parent.localStorage;
}
got this error in DHTMLX while opening a dialogue & parent id or current window id not found
$(document).ready(function () {
if (parent.dxWindowMngr == undefined) return;
DhtmlxJS.GetCurrentWindow('wnManageConDlg').show();
});
Just make sure you are sending correct curr/parent window id while opening a dialogue
On update of iframe's src i am getting that error.
Got that error by accessing an event(click in my case) of an element in the main window like this (calling the main/outmost window directly):
top.$("#settings").on("click",function(){
$("#settings_modal").modal("show");
});
I just changed it like this and it works fine (calling the parent of the parent of the iframe window):
$('#settings', window.parent.parent.document).on("click",function(){
$("#settings_modal").modal("show");
});
My iframe containing the modal is also inside another iframe.
The explanations are very relevant in the previous answers. Just trying to provide my scenario. Hope this can help others.
we were using:
<script> window.document.writeln(table) </script>
, and calling other functions in the script on onchange events but writeln completely overrides the HTML in IE where as it is having different behavior in chrome.
we changed it to:
<script> window.document.body.innerHTML = table;</script>
Thus retained the script which fixed the issue.

Categories