Just started working with CasperJS, writing a few sample tests. Everything works fine when using CSS Selectors, however I'm having trouble using Xpath. The only reason I want to use xpath is to create a more resilient locator, i.e.
'//a[text()="Office Building"]' versus the css version: #content > div > div > div > div:nth-child(1) > a
Here's what I've tried (Note: I have tested this xpath in the browser console):
Tried passing directly
casper.waitUntilVisible('//a[text()="Office Building"]');
Tried specifying the locator explicitly as Xpath:
casper.waitUntilVisible({
type: 'xpath',
path: '//a[text()="Office Building"]'
});
And tried requiring the helper selectXPath from the module as documented here:
var x = require('casper').selectXPath;
casper.waitUntilVisible(x('//a[text()="Office Building"]'))';
All of these result in a timeout error Wait timeout of 30000ms expired, exiting. because the element is never "found". Any ideas?
This could be a solution if you are only got text as identifier for waiting and clicking a link in casperjs without using xPath.
It's more simple to wait for the text and then use clickLabel (if the text is unique):
casper.start('http://yourTestUrl.com');
casper.then(function() {
casper.waitForText('Office Building');
});
casper.then(function() {
casper.clickLabel('Office Building');
});
casper.run();
Related
I want to use CasperJS to click each enabled date in a JQuery UI datepicker, do something while that is clicked, and then go back and click the next date. The problem here is that once I have the datepicker displayed, I don't know how to get the selectors for the anchors within its body and its body only.
I don't think that CasperJS does anything similar to jquery where you can pass it a selector and get an array of elements back.
I could drop into an evaluate call and get the array via jquery, but then I'd have to send it back as valid unique selectors for CasperJS to loop over.
I could use jquery to get the content of each anchor, like "1", and then use CasperJS's clickLabel, but clickLabel scopes only by tag, so all I could say is "click the anchor whose content is '1'", and then potentially click the wrong thing if there are multiple anchors with the content "1" onscreen.
You don't need jQuery to do this. XPath can be used to devise a good selector function for the datepicker days like so:
var x = require('casper').selectXPath;
function daySelector(day) {
return x("//*[contains(#class,'ui-datepicker')]//td[#data-handler='selectDay']/a[text()='"+day+"']");
}
Since you know the numbers that you want to click, you can do (untested):
var numbers = [2,3,4,5,6,7],
pickerSelector = ".ui-datepicker";
casper.thenEach(numbers, function(num){
num = num.data;
// TODO: click into the date field
this.waitUntilVisible(pickerSelector);
this.thenClick(daySelector(num));
this.waitWhileVisible(pickerSelector);
// TODO: Check or do something you need
});
I am using the following jQuery plugin :
http://pvdspek.github.com/jquery.autoellipsis/, and in general it works very well.
The problem comes when I need to update the text of the element. One would assume that changing the text of the element and calling the plugin again would perform the same action the initial call performed.
But, as can be seen in this fiddle - it doesn't.
The code is very simple
var container = $(".container");
container.text("This is a long text that should have text ellipsis");
//this works fine
container.ellipsis();
$("button").click(function()
{
container.text("This is the modified text that should also have ellipsis");
//this doesn't work
container.ellipsis();
});
The only way I could make it work is by deleting the data stored on the element, and by this
making the plugin run "from scratch".
any ideas?
Clear the data stored by autoellipsis: container.data('jqae', null);
var container = $(".container");
container.text("This is a long text that should have text ellipsis");
//this works fine
container.ellipsis();
$("button").click(function()
{
container.data('jqae', null);
container.text("This is the modified text that should also have ellipsis");
//this doesn't work
container.ellipsis();
});
I have a page with a lot of images, each of them being wrapped with an onclick event. For example:
<a onclick=javascript:xxx(y,z)><img id="myclass" src="yyy"></a>
Following command returns all the images correctly:
$x("//img")
This doesn't (returns null):
$("img")
I assume, because these img tags were created dynamically, so are not directly in the source.
My idea was to add .click() at the end, but somehow it doesn't work.
So the question is:
How do I simulate clicking all images at once to trigger all associated javascript functions?
You can't .click() on collection as it is just a container for some objects and it doesn't have any methods from objects inside. You need to iterate over each object and call .click() individually:
for (var idx = document.images.length - 1; idx >= 0; idx--){ document.images[idx].click() }
I've just ran it in console and opened your profile and ad on sidebar.
Since extensions can not access unsafeWindow, like Firefox can, to hook into DOM scripts am I looking for other ideas so I come to SO for help!
How about using some code to inject into DOM and sending the intercepted response to a background page, which then does some initial processing before calling a content script for final processing. When done, it answers to the background with a modified response, or the original (it depends), and the background page sends the response back to DOM which handles it to the DOM script response function.
There is just one problem with this, a background page cant communicate with the DOM.
I did a small test with injecting some code, where I output something to the console and an alert. The result wasnt good, as the alert fired but the console was empty - not even an error, which makes me wonder - what console received the output ?
function injectCode(fn){ // Executing an anonymous script
var script = document.createElement('script');
script.type = 'application/javascript';
script.textContent = '(' + fn + ')();';
document.documentElement.appendChild(script); // run the script
document.documentElement.removeChild(script); // clean up
}
var code = function(){
console.log('dom',window);
alert('code injected');
}
injectCode(code);
I also tried addEventListener, with DOMAttrModified DOMSubtreeModified DOMNodeInserted, on DOM elements that change when the DOM ajax response is fully parsed but all failed to fire.
Am I trying to do the impossible, by any means ?
Before continuing, make sure that you know the differences between the script contexts in an extension.
To inject a script from the background page, you have to execute a Content script, which on his turn injects the script as mentioned in your question / here.
Examples (using chrome.tabs.executeScript):
// null = current active tab
// Simple code, background:
chrome.tabs.executeScript(null, {
code: [
'var s = document.createElement("script");',
's.textContent = "console.log(window);";',
'(document.head||document.documentElement).appendChild(s);',
's.parentNode.removeChild(s);'
].join('\n')
});
I can imagine that this method is not doable for a big chuck of code. For a set of pre-defined scripts, you can then use two scripts: the code itself, and a helper script:
// config.js
var fn_code = function() {
console.log(window); ....
};
// helper.js
var s = document.createElement('script');
s.textContent = '(' + fn_code + ')();';
(document.head||document.documentElement).appendChild(s);
s.parentNode.removeChild(s);
// Background:
chrome.tabs.executeScript(null, {file: 'config.js'}, function() {
chrome.tabs.executeScript(null, {file: 'helper.js'});
});
Note: I did not directly link to "config.js", because that complicates the use when using manifest version 2, see "web_accessible_resources".
The previous method only shows how to execute code in one direction (background -> page). If there's a need to activate a background's function from the injected script, you have to define and listen to a custom event handler. See this answer + demo.
Because the code is injected, thus runs in the scope of the page, you have to check the console at the page.
When chrome.tabs.executeScript fails to execute the Content script (eg. because the extension does not have the permission to access a certain page), an error is logged at the console in the background page. This console can be accessed by following these steps.
I am trying to remove an element on AJAX success which was loaded and attached to the document during a previous AJAX call.
My code looks something like this:
$("#jobs-table-body").on("click", ".one-rc-button", function() {
var ctx = $.parseJSON($(this).siblings(".context").html());
$("#one-rc-candidate-id").val(ctx.candidateId);
$("#one-rc-job-id").val(ctx.jobId);
var loader = $("#wrapper").loader();
$.post($("#one-rc-form").attr("action"), $("#one-rc-form").serialize(), function(result) {
loader.remove();
if(result.success) {
// This works and returns 1
alert($("#candidate-row-" + result.rejectedCandidateId).length);
// This doesn't seem to be doing anything
$("#candidate-row-" + result.rejectedCandidateId).remove();
} else {
//$("#one-jc-messages").html(result.error);
}
});
});
The elements .one-rc-button and #candidate-row-<candidateId> were loaded by a previous AJAX call and they are attached to the document as I can very well see them on my page.
Now, on click of the previously generated .one-rc-button, I trigger a second AJAX call (which works fine) and on result.success, I want to delete the #candidate-row-<candidateId> (which is within the previously generated parent element).
The alert works and returns 1. So I know for sure that the selector is fine and it is matching one unique element.
What I don't understand is why it is unable to remove the element from the page.
Observations
I use Firefox 10.0.2 where this problem is reproducible.
On IE 8, it works (element gets removed)
On debugging the script on Firebug, I can verify that I have got a handle to the right eleemnt.
Try using FireBug to set a breakpoint on that line so you can see exactly what it's getting from that selector. Ideally break up the statement first, like this:
var unwantedDiv = $("#candidate-row-" + result.rejectedCandidateId);
unwantedDiv.remove(); // <-- Set a breakpoint on this line
You can then look at the unwantedDiv variable in the watch pane on the right of the firebug debugger and see what it is, what methods it has/has not got etc. I would assume that you are not getting back exactly what you think you are, possibly because of how you attached the div after the previous AJAX call. More information about JavaScript debugging with FireBug here.
Another option is to turn on strict warnings in the firebug console and see if you get any 'undefined method' errors, which don't stop the show on FireFox, but just bounce you out of that function. Do you get an error in IE?
Solved it by a really ugly workaround. I am still not sure what causes this behaviour.
if(result.success) {
var removeThis = $("#candidate-row-" + result.rejectedCandidateId);
removeThis.remove();
removeThis = $("#candidate-row-" + result.rejectedCandidateId);
if(removeThis.length != 0) {
removeThis.remove();
}
}
Now it works on both Firefox and IE.