I am not sure if this question is related to PhantomJS or simple Javascript.
I am using Ghostdriver to open a webpage and trying to capture the response headers. GhostDriver executes the javascript and adds the onResourceReceived event.
Like this:
String responsescript =
"var page = this,"+
"jsonResponse = \"\";"+
"page.onResourceReceived = function (res) {"+
"console.log(JSON.stringify(res));" /* This line works fine but i want to pass this data somehow to java program. To do that, i came up with the below alternative but it is failing*/
"jsonResponse = jsonResponse + JSON.stringify(res, undefined, 4);"+
"};"+
"function getJsonResponse(){"+
"return jsonResponse;"+
"}";
ghostDriver.executePhantomJS(responsescript);
ghostDriver.get("cnn.com");
ghostDriver.executePhantomJS("getJsonResponse();");
It always fails with the below message:
{message=Can't find variable: getJsonResponse, line=1, stack=ReferenceError: Can't find variable: getJsonResponse
I just want to get the response headers in some String variable in Java so that i can look for JSESSIONID in it..
Because of my poor javascript skills i am not able to solve this simple problem..
I don't know why i took long route but after going through the examples on the ghostdriver(link) ..its just 2 lines of code
So happy..now i can sleep peacefully :)
String responsescript =
"return JSON.stringify(this.cookies);";
Object object = phantom.executePhantomJS(responsescript);
System.out.println(((String)object));
Related
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.
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've found this guide on internet to publish on Wordpress using XML-RPC inside my Java Project, for example I want a message on my Blog, every time it's specified date.
http://wordpress.rintcius.nl/post/look-how-this-wordpress-post-got-created-from-java
Now, I've followed the guide and I'm trying to let it run but I don't understand yet how exactly parameters for my post works.
For example using the method blogger.NewPost I call:
public Integer post(String contents) throws XmlRpcException {
Object[] params = new Object[] {
blogInfo.getApiKey(),
blogInfo.getBlogId(),
blogInfo.getUserName(),
blogInfo.getPassword(),
contents,
postType.booleanValue()
};
return (Integer) client.execute(POST_METHOD_NAME, params);
}
and my "contents" value is:
[title]Look how this wordpress post got created from java![/title]"
+ "[category]6[/category]"
+ FileUtils.getContentsOfResource("rintcius/blog/post.txt");
(I'm using "[" instead of "<" and "]" instead of ">" that are processed by stackoverflow)
Now, how could I use all parameters in this XML way?
Parameters here: http://codex.wordpress.org/XML-RPC_MetaWeblog_API#metaWeblog.newPost
And, it's the content only a "string" without any tag?
Thanks a lot to all!
Still don't know why it gives me back errors but i think it's only a bit outdated.
Found this other libraries that works perfectly
http://code.google.com/p/wordpress-java/
I advice all to use this since the other one is outdated
Thanks all
After many years of successfully maintaining an applet that uses the good old:
<script src="foo.js"></script>
method of embedding a Java applet, we're unable to cover our ears and sing "La la la!" anymore.
It's time to be using:
deployJava.runApplet()
When I fire this method using a click handler (here using an event listener on a button via jQuery, but it doesn't matter):
$('#button').click(function() {
deployJava.runApplet(attributes, parameters, version);
});
...it wipes out the entire existing document and replaces it with the applet. All I need to know is how to target a specific DOM element to be the container for the applet, so that my page doesn't get wiped.
It seems like it would be an attribute I could pass in the form of target: someElement where "someElement" is either a DOM object or the element's ID as a string. But alas, I can't find documentation for such an attribute.
For the sake of being complete, here's what's being passed:
/*here is where I imagine there might be an applicable attribute */
var attributes = {
name: "SomeName",
code: "some.class",
archive: "some.jar",
width: 640,
height: 400
};
var parameters = {
someParameter: someValue
};
var version = "1.5";
I can document.write everything I need to rebuild a document, but I'm sure you can all well imagine how hideous that prospect seems to me.
Any pointers would be greatly appreciated.
As alternative to Christophe Roussy solution you may override document.write while running deployJava.runApplet.
Like so:
function docWriteWrapper(func) {
var writeTo = document.createElement('del'),
oldwrite = document.write,
content = '';
writeTo.id = "me";
document.write = function(text) {
content += text;
}
func();
writeTo.innerHTML += content;
document.write = oldwrite;
document.body.appendChild(writeTo);
}
An then:
docWriteWrapper(function () {
deployJava.runApplet(attributes, parameters, "1.6");
});
A little bit hackish but works like a charm;)
So the core of the problem is this: deployJava.js uses document.write. If you use this method AFTER page render (vs. as a part of the initial page render) it will first clear the document. Which has obvious negative repurcussions.
Although intended for JavaFX, people have reported success with dtjava.js, and I have every reason to believe it's a viable alternative.
However, other stakeholders on my team have already done work surrounding deployJava.js and are unwilling to throw away that work, which meant I needed to stick to deployJava.js. There's only one way to do this: make sure that deployJava is called during page render, not via Ajax, event, or other delayed trigger.
In the end, we are collecting our information, and passing it to a second page which will render the applet as expected. It works, and in most scenarios our clients will be doing this anyhow (collecting information, passing it server-side, and getting a redirect response), so it didn't make sense to force the issue. We are passing information via query string but you could probably use cookies and/or localstorage API instead, if you wanted the window.location to stay cleaner-looking.
Thanks for the replies, even though they were in the comment area. Other replies are still being taken on board if someone has a better way of doing it!
If you are using jQuery and want to target a specific dom element, rather than just appending:
function docWriteWrapper(jq, func) {
var oldwrite = document.write, content = '';
document.write = function(text) {
content += text;
}
func();
document.write = oldwrite;
jq.html(content);
}
docWriteWrapper($('#mydiv'), function () {
deployJava.runApplet(attributes, parameters, version);
});
To solve this annoying issue I downloaded and hacked deployJava.js at line 316, replaced the line by my own:
// document.write(n + "\n" + p + "\n" + r);
myDiv.append(n + "\n" + p + "\n" + r);
Where myDiv is a js global variable set to the desired div before calling runApplet:
myDiv = jQuery('#someDiv');
If you find a less intrusive solution let me know...
I want to be able to insert a Java applet into a web page dynamically using a Javascript function that is called when a button is pressed. (Loading the applet on page load slows things down too much, freezes the browser, etc...) I am using the following code, which works seamlessly in FF, but fails without error messages in IE8, Safari 4, and Chrome. Does anyone have any idea why this doesn't work as expected, and how to dynamically insert an applet in a way that works in all browsers? I've tried using document.write() as suggested elsewhere, but calling that after the page has loaded results in the page being erased, so that isn't an option for me.
function createPlayer(parentElem)
{
// The abc variable is declared and set here
player = document.createElement('object');
player.setAttribute("classid", "java:TunePlayer.class");
player.setAttribute("archive", "TunePlayer.class,PlayerListener.class,abc4j.jar");
player.setAttribute("codeType", "application/x-java-applet");
player.id = "tuneplayer";
player.setAttribute("width", 1);
player.setAttribute("height", 1);
param = document.createElement('param');
param.name = "abc";
param.value = abc;
player.appendChild(param);
param = document.createElement('param');
param.name = "mayscript";
param.value = true;
player.appendChild(param);
parentElem.appendChild(player);
}
document.write()
Will overwrite your entire document. If you want to keep the document, and just want an applet added, you'll need to append it.
var app = document.createElement('applet');
app.id= 'Java';
app.archive= 'Java.jar';
app.code= 'Java.class';
app.width = '400';
app.height = '10';
document.getElementsByTagName('body')[0].appendChild(app);
This code will add the applet as the last element of the body tag. Make sure this is run after the DOM has processed or you will get an error. Body OnLoad, or jQuery ready recommended.
I would have suggested doing something like what you're doing; so I'm baffled as to why it's not working.
Here's a document that looks pretty authoritative, coming from the horse's mouth as it were. It mentions the idiosyncrasies of different browsers. You may end up needing to do different tag soups for different implementations.
But maybe there's something magic about applet/object tags that keeps them from being processed if inserted dynamically. Having no more qualified advice, I have a crazy workaround to offer you: Howzabout you present the applet on a different page, and dynamically create an IFRAME to show that page in the space your applet should occupy? IFRAMEs are a bit more consistent in syntax across browsers, and I'd be surprised if they were to fail the same way.
Maybe you should use your browser's debugging tools to look at the DOM after you swap in your applet node. Maybe it's not appearing where you think it is, or not with the structure you think you're creating. Your code looks OK to me but I'm not very experienced with dynamic applets.
There is a JavaScript library for this purpose:
http://www.java.com/js/deployJava.js
// launch the Java 2D applet on JRE version 1.6.0
// or higher with one parameter (fontSize)
<script src=
"http://www.java.com/js/deployJava.js"></script>
<script>
var attributes = {code:'java2d.Java2DemoApplet.class',
archive:'http://java.sun.com/products/plugin/1.5.0/demos/plugin/jfc/Java2D/Java2Demo.jar',
width:710, height:540} ;
var parameters = {fontSize:16} ;
var version = '1.6' ;
deployJava.runApplet(attributes, parameters, version);
</script>
I did something similar to what Beachhouse suggested. I modified the deployJava.js like this:
writeAppletTag: function(attributes, parameters) {
...
// don't write directly to document anymore
//document.write(startApplet + '\n' + params + '\n' + endApplet);
var appletString = startApplet + '\n' + params + '\n' + endApplet;
var divApplet = document.createElement('div');
divApplet.id = "divApplet";
divApplet.innerHTML = appletString;
divApplet.style = "visibility: hidden; display: none;";
document.body.appendChild(divApplet);
}
It worked ok on Chrome, Firefox and IE. No problems so far.
I tried at first to have a div already created on my html file and just set its innerHTML to the appletString, but only IE were able to detect the new applet dynamically. Insert the whole div direclty to the body works on all browsers.
Create a new applet element and append it to an existing element using appendChild.
var applet = document.createElement('applet');
applet.id = 'player';
...
var param = document.createElement('param');
...
applet.appendChild(param);
document.getElementById('existingElement').appendChild(applet);
Also, make sure the existing element is visible, meaning you haven't set css to hide it, otherwise the browser will not load the applet after using appendChild. I spent too many hours trying to figure that out.
This worked for me:
// my js code
var app = document.createElement('applet');
app.code= 'MyApplet2.class';
app.width = '400';
app.height = '10';
var p1 = document.createElement('param');
p1.name = 'sm_UnwindType';
p1.value='200';
var p2 = document.createElement('param');
p2.name = 'sm_Intraday';
p2.value='300';
app.appendChild(p1);
app.appendChild(p2);
var appDiv = document.getElementById('applet_div');
appDiv.appendChild(app);
-----html code:
<div id="applet_div"></div>