I am trying to take a screenshot of the second page on a website that uses ajax, using casperjs.
I used Chrome and Ressurectio to create a test script and changed it slightly in order to suit my needs.
However, when I run the script, the second screenshot only shows the "loading" page, which at first I thought it's due to ajax being slow...
The problem is that even with a timeout of 15 seconds, it still doesn't take the screenshot that I want.
Maybe I'm forgetting something?
Here's my script:
var x = require('casper').selectXPath;
casper.options.viewportSize = {width: 1366, height: 667};
casper.on('page.error', function(msg, trace) {
this.echo('Error: ' + msg, 'ERROR');
for(var i=0; i<trace.length; i++) {
var step = trace[i];
this.echo(' ' + step.file + ' (line ' + step.line + ')', 'ERROR');
}
});
casper.test.begin('Resurrectio test', function(test) {
casper.start('http://recrutamento.auchan.pt/listaofertas.aspx');
casper.waitForSelector(x("//a[normalize-space(text())='>>>']"),
function success() {
this.capture('click1.png')
test.assertExists(x("//a[normalize-space(text())='>>>']"));
this.click(x("//a[normalize-space(text())='>>>']"));
this.wait(7000);
this.capture('click2.png')
},
function fail() {
test.assertExists(x("//a[normalize-space(text())='>>>']"));
});
casper.run(function() {test.done();});
});
wait is an asynchronous step function such as then, so you have to put capture into the callback of wait:
this.wait(7000, function(){
this.capture('click2.png')
});
You're taking the screenshot to early.
Related
I'm developping an ASP MVC application that use Globalize.js. In the _Layout.cshtml I added this code
<script>
(function () {
$(function () {
$.when(
$.getJSON("#Url.Content("~/Scripts/cldr/supplemental/likelySubtags.json")"),
$.getJSON("#Url.Content("~/Scripts/cldr/main/fr/numbers.json")"),
$.getJSON("#Url.Content("~/Scripts/cldr/supplemental/numberingSystems.json")"),
$.getJSON("#Url.Content("~/Scripts/cldr/main/fr/ca-gregorian.json")"),
$.getJSON("#Url.Content("~/Scripts/cldr/main/fr/timeZoneNames.json")"),
$.getJSON("#Url.Content("~/Scripts/cldr/supplemental/timeData.json")"),
$.getJSON("#Url.Content("~/Scripts/cldr/supplemental/weekData.json")")
).then(function () {
// Normalize $.get results, we only need the JSON, not the request statuses.
return [].slice.apply(arguments, [0]).map(function (result) {
return result[0];
});
}).then(Globalize.load).then(function () {
Globalize.locale("fr");
});
});
})();
</script>
It's working. But when I tried to use it in other page in $(document).ready or $(window).load I Have the error JavaScript: E_DEFAULT_LOCALE_NOT_DEFINED: Default locale has not been defined.
It seems Like that The Globalize is not yet loaded.
I know that this is a very old story, but I stumbled upon it and the answer is pretty simple - instead of doing what you want to do on the $(document).ready event, you have to wait for the globalize to finish loading the resources and then do your stuff.
The simple way of doing this is to trigger your own event after you loaded globalize like this:
function loadcldr() {
var currentCultureCode = $("#hfTwoCharsCultureCode").val();
var publicCdnGlobalizeCompleteUrl = "/Resources/cldr/";
$.when(
$.get(publicCdnGlobalizeCompleteUrl + "main/" + currentCultureCode + "/ca-gregorian.json"),
$.get(publicCdnGlobalizeCompleteUrl + "main/" + currentCultureCode + "/numbers.json"),
$.get(publicCdnGlobalizeCompleteUrl + "main/" + currentCultureCode + "/currencies.json"),
$.get(publicCdnGlobalizeCompleteUrl + "supplemental/likelySubtags.json"),
$.get(publicCdnGlobalizeCompleteUrl + "supplemental/timeData.json"),
$.get(publicCdnGlobalizeCompleteUrl + "supplemental/weekData.json")
).then(function () {
// Normalize $.get results, we only need the JSON, not the request statuses.
return [].slice.apply(arguments, [0]).map(function (result) {
return result[0];
});
}).then(Globalize.load).then(function () {
Globalize.locale(currentCultureCode);
customNumberParser = Globalize.numberParser();
$(document).trigger("globalizeHasBeenLoadedEvent");
});
}
The line that is of interest for you is $(document).trigger("globalizeHasBeenLoadedEvent"); because this triggers the custom event.
And then you can wait for that event to happen and then do your stuff:
$(document).on("globalizeHasBeenLoadedEvent",
function () {
alert("I'm done loading globalize...");
});
Hope it helps someone in the future... (not once I had an issue and I've searched on SO and found my own answers) :-))
The below function works only after the page has been refreshed. When the page is refreshed again afterwards it stops working again and so on.
<button id="moreBtn" type="button" class="archive btn btn-default col-sm-12"></button>
function ShowHideBtn() {
var newss = 5;
var numItems = $(".news").length;
hidenews = "- Show Less Products";
shownews = "+ Show More Products";
$(".news:not(:lt(" + newss + "))").hide();
$("hr:not(:lt(" + newss + "))").hide();
if (numItems >= newss) {
$(".archive").show();
$(".archive").html(shownews);
$(".archive").on("click", function (e) {
e.preventDefault();
if ($(".news:eq(" + newss + ")").is(":hidden")) {
$("hr:hidden").show();
$(".news:hidden").show();
$(".archive").html( hidenews );
} else {
$("hr:not(:lt(" + newss + "))").hide();
$(".news:not(:lt(" + newss + "))").hide();
$(".archive").html(shownews);
}
return false;
});
} else {
$(".archive").hide();
}
}
Thanks in advance
This is a guess as there is insufficient information to confirm it. Please provide the full page HTML/code:
As browser page requests are stateless (so it can't know it is every other load), this sounds like a timing issue. The HTML would generally load slower the first time, so if the JS code is not positioned after the element it references (or is inside a DOM ready handler), then it may fail to find the .archive element. It is more likely random than "every other page load" though if it is a timing issue.
Try one of the following:
Place your JS code (or JS script include) after the element they reference. Just before the closing </body> tag is typical for this option.
Place your code inside a DOM ready handler, then its position does not matter. e.g. like:
$(document).ready(function(){
// Your code here
});
or the short-cut version of DOM ready:
$(function(){
// Your code here
});
I have this script, and there is a very noticeable 1-2 second delay to change the border color around a textbox (has-success) or (has-error). Basically I want to change the color (Green or Red) and show/hide a glyphicon based on an if/else statement.
$(document).ready(function () {
$("#lookupExtGuest").click(function () {
$.ajax({
url: "/NewUserRequest/LookupData",
data: { userInput: document.getElementById("ExtGuestID").value },
success: function (result) {
if (result.indexOf("was found") != -1) {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-success"
$('#extGuestGlyphiconOK').show();
$('#extGuestGlyphiconRemove').hide();
}
else {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-error"
$('#extGuestGlyphiconOK').hide();
$('#extGuestGlyphiconRemove').show();
}
}
});
});
});
Here is the reponse times from the chrome Network menu:
The javascript in the success function should only take a few milliseconds to run provided the result string is only a few kilobytes. A good way to test something like this is with console.time():
<script>
$(document).ready(function () {
$("#lookupExtGuest").click(function () {
console.time('lookupDataRequestTimer');
$.ajax({
url: "/NewUserRequest/LookupData",
data: { userInput: document.getElementById("ExtGuestID").value },
success: function (result) {
console.timeEnd('lookupDataRequestTimer');
console.time('lookupDataCallbackTimer');
if (result.indexOf("was found") != -1) {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-success"
$('#extGuestGlyphiconOK').show();
$('#extGuestGlyphiconRemove').hide();
}
else {
var d = document.getElementById("extGuestlookup")
d.className = d.className + " has-error"
$('#extGuestGlyphiconOK').hide();
$('#extGuestGlyphiconRemove').show();
}
console.timeEnd('lookupDataCallbackTimer');
}
});
});
});
</script>
I've added some console.time functions into the code you posted. If you run this you should see the timing in the web-inspector's console. This way you can see whether the ajax call (lookupDataRequestTimer) or the success callback (lookupDataCallbackTimer) is slower and by how much.
If the ajax call is much slower and the file being requested isn't too large (which I suspect) you'll probably find the server is quite slow. To speed things up you could run the GET request on page load and cache the data into a variable and access it immediately on click.
Edit: I see you've just added the network screenshot. The request's size is very small, 590b, but it's taking 2.47s. This is definitely the server which is taking a while to respond. Can you take another picture of the entire network tab, including the times for the html page itself.
Maybe you can use ('#parent children').remove() and then ('#parent').append('<element><\element>') for each node.
Or, to hide slow:
hide('slow') or show('slow')
I'm new to CasperJS and I'm trying to figure out the execution flow.
This is what I'm trying to achieve:
load a page
stores an image of the page
pass this image to a function and execute it (this process is quite long: ~15 seconds)
wait for the function to return the result
use the returned value to fill a field in the form in the loaded page
submit the form
this is a code snippet which tries to explain the solution I came up with:
var globProcessedImage;
var casper = require('casper').create({
viewportSize: {
width: 1024,
height: 768
}
});
casper.start('http://example.com/');
casper.then(function() {
this.captureSelector('./image.png', '#img-node');
});
casper.waitFor(function() {
return globProcessedImage !== undefined;
}, function then() {
this.sendKeys('#imagePassword', globProcessedImage);
});
casper.then(function() {
this.capture('./page.png');
});
casper.run();
casper.on('image.processed', function() {
setTimeout(function() {
globProcessedImage = 'my_result';
}, 15000);
});
This results in ReferenceError: Can't find variable: globProcessedImage.
It's still unclear to me how web automation and "external" functions mix together with CasperJS, as well as how parameters are passed between the page and casper/phantom environments.
Maybe something like that :
var globProcessedImage ;
var casper = require('casper').create({
viewportSize: {
width: 1024,
height: 768
}
});
casper.start('http://example.com/');
casper.options.waitTimeout = 16000;
casper.then(function() {
this.captureSelector('./image.png', '#img-node');
this.emit('image.processed');
});
/*
* If you need to wait for a fix time, it's okay, but a better way would be to wait in Casper
* 'the thing you do with your image' in a waitFor. -> asynchronous. With this solution you combine two timers, this is not the optimized solution.
*/
casper.waitFor(function() {
return globProcessedImage !== undefined;
}, function then() {
this.sendKeys('#imagePassword', globProcessedImage);
});
casper.then(function() {
this.capture('./page.png');
});
casper.run();
casper.on('image.processed', function() {
setTimeout(function() {
//when you will emit this event in your script, it will set the value of globProcessedImage to 'my_result' after 15sec
globProcessedImage = 'my_result';
}, 15000);
});
I tried for several hours to get the following code working.
The code should be paused until the page is loaded. The problem that I'm encountering is that the AJAX is executing asynchronously. How would I force the code to wait before executing?
var i = 1;
function On_Success(){
i = i+1;
if(i<=10){
$('<div id="p'+i+'">b</div>').replaceAll('#b');
$('#p'+i).load('tabelle.html');
//here shoul be waited, til the page is loaded
On_Success();
};
};
function knopf(){
$('body').append('<div id="p'+i+'">a</div>');
$('#p'+i).load('tabelle.html');
On_Success();
};
Both Ajax and load have an on successs function that can be run when the response is fully returned.
$.ajax({
async: true,
type: "GET",
url: url,
data: params,
success: function(html){
//do stuff here
},
error: handleAjaxErrors
});
If you want to use load, there is a success callback as well:
$("#myDiv").load('/ajax/data/get', function() {
//do stuff here
});
The load function has a success handler:
$('#p'+i).load('tabelle.html', function() {
On_Success();
});
The success handler is only called after the ajax call completes successfully, and after the provided content has been added to the DOM.
You can see full documentation here:
http://api.jquery.com/load/
If you also want to capture error conditions on the client you will need to use the full $.ajax call as per #Chris's answer.
.load accepts a callback that runs when loading is complete; you can use this instead of the more verbose jQuery.ajax function, which will require you to write additional code to populate your element.
jQuery("#p", i).load("tabelle.html", function() {
//code to run when tabelle.html has finished loading
});
Change your code to the following. It should work:
var i = 1;
function On_Success() {
i = i + 1;
if (i <= 10) {
$('<div id="p' + i + '">b</div>').replaceAll('#b');
$('#p' + i).load('tabelle.html', On_Success);
};
};
function knopf() {
$('body').append('<div id="p' + i + '">a</div>');
$('#p' + i).load('tabelle.html', On_Success);
};
On another note, is it absolutely necessary that you should wait for one load to complete before populating the other divs? Why can't you do something like this:
function On_Success() {
var i = 0;
while(i < 11) {
$('<div id="p' + i + '">b</div>').replaceAll('#b');
$('#p' + i).load('tabelle.html');
};
};
Also, I'm not sure what the knopf function is doing. What should the value of i be in that function? How are you calling your code?