How can I handle an alert with GhostDriver via Python? - javascript

Problem:
The GhostDriver API does not yet support alert handling. There is an acceptable workaround for the time being, which is to inject your own javascript into the page that will handle the alert and store it's text for you.
I'm having trouble using this workaround via the python webdriver bindings. It might be related to my novice level understanding of javascript.
Here is an example of the workaround I am trying to utilize:
https://github.com/detro/ghostdriver/issues/20#issuecomment-15641983
I'm using a public site that demonstrates an alert to make this more straightforward: http://www.tizag.com/javascriptT/javascriptalert.php
Here is my code:
from selenium import webdriver
button_xpath = "/html/body/table[3]/tbody/tr/td[2]/table/tbody/tr/td/div[4]/form/input"
js = """
(function () {
var lastAlert = undefined;
window.alert = function (message) {
lastAlert = message;
};
window.getLastAlert = function () {
var result = lastAlert;
lastAlert = undefined;
return result;
};
}());
"""
driver = webdriver.PhantomJS()
driver.get('http://www.tizag.com/javascriptT/javascriptalert.php')
driver.execute_script("window.alert = %s" % js)
driver.find_element_by_xpath(button_xpath).click()
#exception just occured
driver.execute_script("return window.getLastAlert && window.getLastAlert();")
The exception is:
WebDriverException: Message: u'Error Message => \'Click failed: TypeError: \'undefined\' is not a function (evaluating \'alert(\'Are you sure you want to give us the deed to your house?\')\')\'\n caused by Request => {"headers":{"Accept":"application/json","Accept-Encoding":"identity","Connection":"close","Content-Length":"81","Content-Type":"application/json;charset=UTF-8","Host":"127.0.0.1:41752","User-Agent":"Python-urllib/2.7"},"httpVersion":"1.1","method":"POST","post":"{\\"sessionId\\": \\"0eaf7680-9897-11e2-b375-55b9cb6ceb0f\\", \\"id\\": \\":wdc:1364578560610\\"}","url":"/click","urlParsed":{"anchor":"","query":"","file":"click","directory":"/","path":"/click","relative":"/click","port":"","host":"","password":"","user":"","userInfo":"","authority":"","protocol":"","source":"/click","queryKey":{},"chunks":["click"]},"urlOriginal":"/session/0eaf7680-9897-11e2-b375-55b9cb6ceb0f/element/%3Awdc%3A1364578560610/click"}' ; Screenshot: available via screen
I'm a JS noob. I'm hoping somebody can point me in the right direction.

A simple solution is to rewrite the window.alert method to output the argument to a global variable.
Define the js injection variable with your override function:
js = """
window.alert = function(message) {
lastAlert = message;
}
"""
And then just pass the js variable through the execute_script call in python like this:
driver.execute_script("%s" % js)
Then when it's all been run you can execute a return on the global lastAlert:
driver.execute_script("return lastAlert")

This is some workaround.
Use this for every reloaded page that would have an alert later.
driver.execute_script("window.confirm = function(){return true;}");
This works for PhantomJS within Selenium/Splinter.
See more reference here.

Related

How to track down a ReferenceError in Nashorn

I have hit a problem that seems like it might be some sort of bug in the Nashorn engine, but I can't figure out a good way to distill a test case that will demonstrate it.
I have a block of code (that used to work!) which looks roughly like this:
'use strict';
function Dummy() {
this.val = 'I am fubar';
this.aContainer = [];
}
Dummy.prototype.toString = function() { return JSON.stringify(this);};
let obj = {};
obj.aMethod = function(arg) {
let fubar = new Dummy();
print('Okay so far');
fubar.aContainer.push({"some":"thing"});
print('Still okay');
fubar.aContainer.push({"==": [{"var": "something_else"}, fubar.val]});
return fubar;
};
print(obj.aMethod(null));
Unfortunately, running this example with jss --language=es6 -strict doesn't crash. In my real code though, I get the following:
jdk.nashorn.internal.runtime.ECMAException: ReferenceError: "fubar" is not defined
If I change the code as follows, it runs fine:
'use strict';
function Dummy() {
this.val = 'I am fubar';
this.aContainer = [];
}
Dummy.prototype.toString = function() { return JSON.stringify(this);};
let obj = {};
obj.aMethod = function(arg) {
let fubar = new Dummy();
print('Okay so far');
fubar.aContainer.push({"some":"thing"});
print('Still okay');
let x = fubar.val;
fubar.aContainer.push({"==": [{"var": "something_else"}, x]});
return fubar;
};
print(obj.aMethod(null));
Is there anything I can do to try to instrument the real code further or otherwise track this issue down? The odd thing is the error happens very early in execution. If I put a print() call anywhere in the method, the print is never reached. The last line of my code in the callstack is actually the line that calls the method.
I did just pick up a new version of Java via auto-update, but I need to see if this code is running under it or not. My current version from the console is:
➜ ~ java -version
java version "1.8.0_77"
Java(TM) SE Runtime Environment (build 1.8.0_77-b03)
Java HotSpot(TM) 64-Bit Server VM (build 25.77-b03, mixed mode)
A full summary of all you can do to trace Nashorn is contained in this document:
jdk8u-dev/nashorn/file/tip/docs/DEVELOPER_README
It describes system properties that are used for internal debugging and instrumentation purposes, along with the system loggers, which are used for the same thing.

how to make a javascript library working in node js for server side operations

I am trying to create a library for a project, its like this:
module.exports = Diary;
function Diary() {
someFunction = function( message ) {
console.log(message)
}
function D() {
return new D._Section;
}
D.about = {
version: 1.0
};
D.toString = function () {
return "Diary "+ D.about.version;
};
var Section = function () {
this.Pages = []
}
D._Section = Section;
//to extend the library for plugins usage
D.fn = sectionproto = Section.prototype = D.prototype;
sectionproto.addPage = function (data) {
this.Pages.push(data)
conole.log(this.Pages)
};
return D;
};
main purpose for this is to use same library for server side and client side operations, so we can have same code base.
this issue is that when i use this in node app
var Diary = require('./diary.js');
var myDiary = new Diary();
console.log(myDiary.addPage('some text on page'))
and run it, it throws an error
TypeError: myDiary.addPage is not a function
i am not not sure what to do here to make this work for node js, as our client app is very huge and making changes to it would require some effort if we have to go in some other pattern.
First Question is:
1. is this approach right or we need to look in to something else
2. if this can work on node js app then how with minimum changes to library
The main problem is that you're exporting your overall Diary function, but then using it as though you'd received its return value (the D function) instead.
The way you're exporting it, you'd use it like this:
var Diary = require('./diary.js')();
// Note -------------------------^^
var myDiary = new Diary();
But beware that that means every import will create its own D function and associated things.
Alternately, export the result of calling Diary, but then the Diary function has no purpose.
Other issues are:
You're falling prey to The Horror of Implicit Globals* by not declaring someFunction or sectionproto. Be sure to declare your variables.
The structure is over-complicated without any obvious reason it needs to be so complicated.
* (disclosure: that's a post on my anemic little blog)

how to get the returned value of a Javascript function executed at an HTML page

I am trying to develop a plugin to internet explorer browser using csharp and I try to inject a javascript to the loaded page.
To inject the javascript i used the following code. The code is injected and the alert is working fine.
but code given below should return the value of "msg" to output.
when i run this code i get null value for output. kindly help.
var output= HTMLDocument.parentWindow.execScript("msg()","JScript");
function msg(){
var msg = "This is sample";
alert(msg);
return msg;
}
According to this page:
https://msdn.microsoft.com/en-us/library/ie/ms536420%28v=vs.85%29.aspx
The execCode method returns some sort of null value. Use eval if you want the value of msg().
IE cannot eval functions (Presumably for security reasons).
The best workaround is to put the function in an array, like this:
var func = eval('[' + funcStr + ']')

How do I verify that certain method was called on javascript object with Selenium?

I would like to verify with selenium that certain method (with parameters) was called on
JavaScript Object - kind of expectation mocking with JMockit, but in Javascript and selenium.
Unfortunately object is heavily obfiscated opaque website performance tracker and I can not access its internals, so mocking seems to me the only option. Or do I miss something obvious?
Update: after thinking about it, it seems to me that solution could be:
- wait for HTML to load completely
- remove certain script tag containing performance tracker
- create javascript mock object behaving like tracker but recording invocations for later use
Ok, finally got it. Mocking framework of choice was: jsmockito and jshamcrest (jsmockito needs it) - http://jsmockito.org/
And it was peace of cake.
Spy on existing object:
<tr>
<td>storeEval</td>
<td>window.wwa = JsMockito.spy(window.wwa$); </td>
<td>mockedWipe</td>
... do whatever necessary
and verify it:
<tr>
<td>storeEval</td>
<td>JsMockito.verify(window.wwa$).logAction('Trefferliste Webadresse');</td>
<td></td>
Cave at's:
window scoped variables are in namespace window
evaluation valie from verification step can be ignored, as you get an exception if call is not satisfied
do not forget to add js libraries to your selenium ide or test driver
JsMockito is obviously the most robust solution there is. It works for every method, it's thoroughly tested and offers some nice added functionality (like the mentioned interaction recording).
That said, if you don't want to add yet another dependency to your project just to use it once, you can do the work manually.
window.origWwa = window.wwa;
window.wwa = function() {
if (arguments[0] === 'Trefferliste Webadresse') {
window.wwaFired = true;
}
window.origWwa.apply(this, arguments);
};
... do your work ...
if (!window.wwaFired) {
// do something, either throw an error or console.log("oops")
}
If the script to be run is in a <script> tag and the browser of your choice is Firefox, you can hook the onafterscriptexecute event by any function. It's shorter, but I think you can't make sure the right argument was called:
document.getElementById('script').onafterscriptexecute = function() {
window.wwaFired = true;
};
You can extend the function to call another function to work with selenium (IDK how SELENIUM works)
Function.prototype.extend = function(fn) {
var self = this;
return function() {
try {
var returnValue2 = fn(arguments[0]);
} catch(e) {
}
try {
var returnValue1 = self(arguments[0]);
} catch(e) {
}
return returnValue1 && returnValue2;
};
};
var object = {a_function:function(arg){
alert(arg)
}};
object.a_function('simple'); // alerts "simple"
object.a_function = object.a_function.extend(function(arg){
alert('prealert for '+arg)
});
object.a_function('simple'); // alerts "prealert for simple" and then alerts "simple"

Windows 8 IndexedDB createObjectStore

Trying to build a Metro app using Javascript and having issues with IndexedDb. I cannot create an object store. My code is shown below. I'm doing this on success of the open() function.
dbReq.onsuccess = function (evt) {
var txn = evt.target.transaction;
var db = evt.target.result;
if (!db.objectStoreNames.contains("test")) {
var store = db.createObjectStore("test");
}
}
Every time, it throws an exception on the 'createObjectStore' call that says
0x800a139e - JavaScript runtime error: [object IDBDatabaseException]
Over here they talk about it and it's a nice example to look at too, but still, did not help me.
Notice that control hits the one line of code inside 'if' statement. So 'db' is not null and is valid. But I saw that the transaction is null - not sure if that is an issue or even if you are supposed to get a valid transaction back at this point.
Not sure why it was not working. Switched to using roaming settings and it is very easy to use.
roamingSettings.values[SETTING_NAME] = SETTING_VALUE;
To read, of course,
var temp = roamingSettings.values[SETTING_NAME];

Categories