How to pass a parameter to a function in casperjs? - javascript

I have stored all the images url in an array, and am trying to test whether image has loaded properly. If you see the below code, i had to repeat few set of lines again and again. How can i write it to be generic?
casper.start()
var imagesArray = [];
imagesArray = ['https://www.google.co.in/images/srpr/logo11w.png',
'https://www.google.co.in/images/srpr/logo1w.png'];
casper.thenOpen(imagesArray[0], function () {
if (this.currentHTTPStatus === 404) {
this.warn(imagesArray[0] + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(imagesArray[0] + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
});
casper.thenOpen(imagesArray[1], function () {
if (this.currentHTTPStatus === 404) {
this.warn(imagesArray[0] + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(imagesArray[0] + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
});
casper.run(function() {
this.echo('Image loading test finished');
this.exit();
});
I tried the below method, calling a function but its throwing parser error, what am i doing wrong, or how can i proceed with it?
function checkImages(item){
if (this.currentHTTPStatus === 404) {
this.warn(item + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(item + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
}
casper.thenOpen(imagesArray[0], function () {
this.evaluate(checkImages(imagesArray[0]));
});
casper.thenOpen(imagesArray[1], function () {
this.evaluate(checkImages(imagesArray[1]));
});
Thanks in advance.

Since all then* function are asynchronous step function that insert a step into the queue, you can call them in a loop. Since imagesArray is a native array, it can be iterated over by using Array.prototype.forEach which PhantomJS supports:
var imagesArray = [
'https://www.google.co.in/images/srpr/logo11w.png',
'https://www.google.co.in/images/srpr/logo1w.png'
];
casper.start();
imagesArray.forEach(function(imageUrl){
casper.thenOpen(imageUrl, function () {
if (this.currentHTTPStatus === 404) {
this.warn(imageUrl + ' is missing (HTTP 404)');
} else if (this.currentHTTPStatus === 500) {
this.warn(imageUrl + ' is broken (HTTP 500)');
} else {
this.echo(' is okay (HTTP %s)');
}
});
});
casper.run();
A simple for loop would have sufficed, but then you would have a problem with the imagesArray[i] inside of thenOpen. The i variable would never change, because every step is executed after the loop finished executing. So every imagesArray[i] would show the last url. Because JavaScript has function level scope the url bound to each iteration and never changes afterwards.

As a reminder, think of the evaluate() method as a gate between the CasperJS environment and the one of the page you have opened; everytime you pass a closure to evaluate(), you’re entering the page and execute code as if you were using the browser console. So you can't use evaluate on checkImages.
Use echo like this :
casper.thenOpen(imagesArray[0], function () {
this.echo(checkImages(imagesArray[0]));
});
casper.thenOpen(imagesArray[1], function () {
this.echo(checkImages(imagesArray[1]));
});

You don't have any need for thenOpen in this test case, since you only want to verify response code. you CAN do it that way, but it's incredibly wasteful of time/resources. this is how I achieved the same goal:
casper.test.begin('link tester', 73, function(test) {
casper.start(url);
getLinks = function(){
links = this.evaluate(function(){
var links = document.getElementsByTagName('a');
links = Array.prototype.map.call(links,function(link){
return link.getAttribute('href');
});
return links;
});
}
casper.then(getLinks);
casper.then(function(response) {
for (q = 0; q < links.length; q++) {
if (response == undefined || response.status >= 400) {
this.test.fail("URL " + links[q] + " failed with response code " + (response.status));
} else {
this.test.pass((response.status + " ---- " + links[q]));
}
}
});
the only caveat with this would be that you can only have 1 failure per casper function. if you are testing 100 URLs on a page, and you fail on the 4th one, you will have to fix that one before you can see if the others failed. thus, you would need to nest a casper.then() within your if statement. In case you were wondering, it would look like this:
casper.then(function(response) {
links.forEach(function(link){
if (response == undefined || response.status >= 400) {
casper.then(function() {
this.test.fail("URL " + link + " failed with response code " + (response.status));
})
} else {
this.test.pass((response.status + " ---- " + link));
}
});
});

Related

Throw custom timeout exception

I have a Google Apps Script web app ("Web App") that executes as the user, then calls individual functions from another Apps Script project ("API Executable") via the Apps Script API using UrlFetchApp.fetch() and executes them as me (see Get user info when someone runs Google Apps Script web app as me).
A limitation of this method is that UrlFetchApp.fetch() has a 60s timeout, and one of my functions often takes longer than this. The API Executable function finishes running successfully, but the web app throws a timeout exception. I would like to handle this exception by running a second "follow-up" function that finds and returns the URL of the Google Sheet successfully created by the original function. However, I'll need to pass the follow-up function one of the parameters passed to the original function, and it appears I can't do this within a standard try...catch block.
My idea was to throw an exception that contains the needed parameter, but I can't figure out how to throw my own timeout exception; since Google Apps Script is synchronous, there's no way to track how long UrlFetchApp.fetch() has been running while it's running.
Is there a way to throw your own timeout exception? Or, is there another way I can pass the needed parameter to a function that executes if there's a timeout error?
I tagged Javascript in this post as well since there's a lot of overlap with Google Apps Script and I figured it would improve my chance of connecting with someone who has an answer--hope that's okay. Below is the function I'm using in my web app to call my API Executable functions, in case that's helpful.
EDIT: Based on #TheMaster's comment, I decided to write the script as though parameters passed to executeAsMe() WERE being passed to the catch() block, to see what happened. I expected an exception regarding the fact the opt_timeoutFunction was undefined, but strangely it looks like only the first line of the catch() block is even running, and I'm not sure why.
function executeAsMe(functionName, paramsArray, opt_timeoutFunction, opt_timeoutParams) {
try {
console.log('Using Apps Script API to call function ' + functionName.toString() + ' with parameter(s) ' + paramsArray.toString());
var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';
var payload = JSON.stringify({"function": functionName, "parameters": paramsArray, "devMode": true})
var params = {method:"POST",
headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
payload:payload,
contentType:"application/json",
muteHttpExceptions:true};
var results = UrlFetchApp.fetch(url, params);
var jsonResponse = JSON.parse(results).response;
if (jsonResponse == undefined) {
var jsonResults = undefined;
} else {
var jsonResults = jsonResponse.result;
}
} catch(error) {
console.log('error = ' + error); // I'm seeing this in the logs...
console.log('error.indexOf("Timeout") = ' + error.indexOf("Timeout").toString); // ...but not this. It skips straight to the finally block
if (error.indexOf('Timeout') > 0) { // If error is a timeout error, call follow-up function
console.log('Using Apps Script API to call follow-up function ' + opt_timeoutFunction.toString() + ' with parameter(s) ' + paramsArray.toString());
var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';
var payload = JSON.stringify({"function": opt_timeoutFunction, "parameters": opt_timeoutParams, "devMode": true})
var params = {method:"POST",
headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
payload:payload,
contentType:"application/json",
muteHttpExceptions:true};
var results = UrlFetchApp.fetch(url, params);
var jsonResponse = JSON.parse(results).response;
if (jsonResponse == undefined) {
var jsonResults = undefined;
} else {
var jsonResults = jsonResponse.result;
}
}
} finally {
console.log('jsonResults = ' + jsonResults);
return jsonResults;
}
}
I ended up using the '''catch()''' block to throw an exception back to the client side and handle it there.
Google Apps Script:
function executeAsMe(functionName, paramsArray) {
try {
console.log('Using Apps Script API to call function ' + functionName.toString() + ' with parameter(s) ' + paramsArray.toString());
var url = 'https://script.googleapis.com/v1/scripts/Mq71nLXJPX95eVDFPW2DJzcB61X_XfA8E:run';
var payload = JSON.stringify({"function": functionName, "parameters": paramsArray, "devMode": true})
var params = {method:"POST",
headers: {Authorization: 'Bearer ' + getAppsScriptService().getAccessToken()},
payload:payload,
contentType:"application/json",
muteHttpExceptions:true};
var results = UrlFetchApp.fetch(url, params);
var jsonResponse = JSON.parse(results).response;
if (jsonResponse == undefined) {
var jsonResults = undefined;
} else {
var jsonResults = jsonResponse.result;
}
return jsonResults;
} catch(error) {
console.log('error = ' + error);
if (error.toString().indexOf('Timeout') > 0) {
console.log('Throwing new error');
throw new Error('timeout');
} else {
throw new Error('unknown');
}
} finally {
}
}
Client-side Javascript (a simplified version):
function createMcs() {
var userFolder = getDataFromHtml().userFolder;
google.script.run
.withSuccessHandler(createMcsSuccess)
.withFailureHandler(createMcsFailure)
.withUserObject(userFolder)
.executeAsMe('createMasterCombinedSchedule', [userFolder]);
}
function createMcsSuccess(mcsParameter) {
if (mcsParameter == undefined) {
simpleErrorModal.style.display = "block"; // A generic error message
} else {
document.getElementById("simpleAlertHeaderDiv").innerHTML = 'Master Combined Schedule Created Successfully';
document.getElementById("simpleAlertBodyDiv").innerHTML = 'Your Master Combined Schedule was created successfully. Click here to view.';
simpleAlertModal.style.display = "block";
}
}
function createMcsFailure(mcsError, userFolder, counter) { // The exception I threw will activate this function
if (!counter) { // Added a counter to increment every time checkForCreatedMcs() runs so it doesn't run indefinitely
var counter = 0;
}
if (mcsError.message == 'Error: timeout' && counter < 5) { // If timeout error, wait 10s and look for MCS URL
window.setTimeout(checkForCreatedMcs(mcsError, userFolder, counter), 10000);
} else if (mcsError.message == 'Error: timeout' && counter == 5) { // If it hasn't worked after 5 tries, show generic error message
simpleErrorModal.style.display = "block";
} else { // For any error that's not a timeout exception, show generic error message
simpleErrorModal.style.display = "block";
}
}
function checkForCreatedMcs(mcsError, userFolder, counter) {
counter++;
google.script.run
.withSuccessHandler(checkForCreatedMcsSuccess)
.withUserObject([mcsError, userFolder, counter])
.executeAsMe('checkIfMcsExists', [userFolder]); // checkIfMcsExists() is a pre-existing function in my API Executable project I had already used elsewhere
}
function checkForCreatedMcsSuccess(mcsExistsParameter, params) {
var mcsError = params[0];
var userFolder = params[1];
var counter = params[2];
if (mcsExistsParameter == undefined) { // If function returns undefined, show generic error message
simpleErrorModal.style.display = "block";
} else if (mcsExistsParameter == false) { // If function returns false, wait 10s and try again
createMcsFailure(mcsError, userFolder, counter);
} else { // If function returns URL, show success modal with link
document.getElementById("simpleAlertHeaderDiv").innerHTML = 'Master Combined Schedule Created Successfully';
document.getElementById("simpleAlertBodyDiv").innerHTML = 'Your Master Combined Schedule was created successfully. Click here to view.';
simpleAlertModal.style.display = "block";
}
}
I am sure there has to be a tidier/less complex way to do this, but this worked!

NodeJS: Abort request function inside loop

I am creating a electron application that can download multiple pages on a website. I want to be able to stop downloading the pages whenever I hit the stop button by instantly terminating the function. I tried stopping the function by setting it to a new function but because downloading happens in a function loop it is not stopped (I am unsure if this approach would even stop the startDownload function).
Is there a way to easily stop the execution of a function, that does not stop the whole script?
Edit: If there isn't a way to stop the execution of the function, is there a way to send a message to and stop the NodeJS request?
Edit 2: NodeJS requests have an abort method, but I am unsure how to tell the function to abort the request?
startDownload('website.com');
startDownload(url) {
var startAt = 0;
var maxPages = 15;
download(url, startAt, maxPages);
}
download(url, page, maxPages) {
if (page == maxPages) { finishDownload(url); return; }
request(url + '?p=' + page, (error, response, html) => {
downloadPage(html);
download(url, page + 1, maxPages);
}).catch((error) => {
finishDownload(url, 'Failed to download');
});
}
finishDownload(url, error = undefined) {
if (!error) {
alert(url + ' finished downloading');
} else {
alert(url + error);
}
}
$(document).on('click', '#stopDownload', function() {
var downloadFunction = startDownload;
startDownload = function() {return false};
startDownload = downloadFunction;
alert('download stopped by killing function');
});
yes you can stop by
var r = request({uri: 'http://stackoverflow.com' }, function (error, response, body) {
console.log('url requested ') ;
if (!error){
console.log(body);
}
else
{
console.log(error);
}
});
r.abort();

Modify global variable from ajax request

I know this has been asked countless times, but i can't figure out the problem. As far as i know, you can modify global variables inside functions in javascript. It doesn't work for me.
var lastMessage = 0;
function loadChat() {
$.post("/lastmessage", { roomid: roomId })
.done(function(data) {
var response = JSON.parse(data);
if (response.state == "success") {
lastMessage = response.message;
console.log("Inside: " + lastMessage);
}
});
}
console.log("Outside: " + lastMessage);
This gives me
Outside: 0
Inside: 17
The inside value is correct, the outside is not. What could the problem possibly be?
It's asynchronous, therefore when you call it from outside, it has not finished executing yet. What this means is that this part of your code is only reached once the post has completed
.done(function(data) {
var response = JSON.parse(data);
if (response.state == "success") {
lastMessage = response.message;
console.log("Inside: " + lastMessage);
}
});
but console.log("Outside: " + lastMessage); will continue executing without waiting, since post is asynchronous.
If you want something to happen after you retrieve the value you, one option would be to use a callback function, such as:
function printMessage(message) {
console.log(message)
}
function loadChat(callback) {
$.post("/lastmessage", { roomid: roomId })
.done(function(data) {
var response = JSON.parse(data);
if (response.state == "success") {
lastMessage = response.message;
callback(lastMessage);
}
});
}
loadChat(printMessage);

How can I control functions to execute in order?

I am newbie in Javascript. I am trying to manage couple of function in order. But when it gets to API calls it dosen't wait for response and go backs to continue its execution and makes my code messy. This is a sample:
function readFacebook()
{
var myID = getMyID();
console.log("myID= " + myID);
}
function getMyID(){
FB.api('/me', function(response) {
console.log("response.id= "+response.id);
return(response.id);
});
}
The output is completely strange. First
console.log("myID= " + myID);
show output and then
console.log("response.id= "+response.id);
will be called. Anyone can explain how I can force it to implement in order. I meant program should wait until response from facebook instead of working asynchronously!
EDIT
I actually call 3 time API from main function to three sub functions. How can I organise this:
function getMyID(){
FB.api('/me', function(response) {
console.log("response.id= "+response.id);
return(response.id);
});
}
function readFacebookEvent(id)
{
var myID = getMyID();
console.log("myID= " + myID);
FB.api('/me/events', function(response) {
for(i=0; i<response.data.length;i++) {
var str;
var eventID = response.data[i].id;
getEvent(eventID,myID);
}
});
}
function getEvent(eventID,myID){
FB.api("/"+ eventID , function (response3) {
if (response3 && !response3.error) {
//console.log(response3);
var date = new Date((response3.start_time || "").replace(/-/g,"/").replace(/[TZ]/g," "));
var diff = (((new Date()).getTime() - date.getTime()) / 1000);
//console.log(diff);
if(myID == response3.owner.id && diff < 0 )
{
//console.log("found= " + myID);
var t = getImage(eventID);
if(t)
{
console.log("TRUE");
}
else
{
console.log("false");
}
}
}
});
}
function getImage(eventID){
//console.log("******eventID== "+eventID);
FB.api("/"+eventID+"/picture",
{
"redirect": false,
"type": "normal"
},function (response2) {
if (response2 && !response2.error) {
str="<br/><b>Pic</b> : <img src='"+response2.data.url+"'/>";
//console.log("response2.data.url= "+response2.data.url);
//str +="<b>name: </b>"+response3.name+"<br>";
document.getElementById("status2").innerHTML+=str;
return true;
}
else
{
return false;
}
});
}
As you mentioned the call to facebook's api is asynchronous meaning that you cannot be certain to know when the resulting data will come back from your request. You can work around and guarantee the order you're looking for by using a callback:
function readFacebook(id) {
console.log("myID= " + id);
}
function getMyID(cb) {
FB.api('/me', function(response) {
console.log("response.id= "+response.id);
cb(response.id);
});
}
getMyID(readFacebook);
What's happening here is that the call to FB.api accepts a callback that fires when the response comes back from the server. Since we're supplying our own callback to getMyID, we can use that to get access to response.id once the server responds.

Calling done on an array of http.get requests in Node.js

I have an array of URLs that I'm using a for loop to call http.get requests. Since this is an async process, I'd like to call done after ALL requests have returned.
Here is my current attempt:
grunt.registerTask('verify', function() {
var done = this.async();
var promises = [];
var urlPrefix = 'http://example.com/';
for(var i = 0; i < deployableFiles.length; i++) {
(function(i) {
var deferred = Q.defer();
promises.push(deferred);
var file = deployableFiles[i];
var path = file.filetype + '/' + getVersionedFileName(file.basename, file.filetype);
http.get(urlPrefix + path, function(res) {
deferred.resolve();
if(res.statusCode === 200) {
grunt.log.oklns(path + ' was found on production server.');
} else {
grunt.log.error('Error! ' + path + ' was not found on production server!');
}
}).on('error', function(e) {
grunt.log.error("Got error: " + e.message);
done();
});
})(i);
}
Q.all(promises)
.done(function() {
// Everything executed correctly
return done();
}, function(reason) {
// There was an error somewhere
return done(false);
});
});
I'm sure it's just me not wrapping my head around the whole async nature of node correctly, but is there anything glaringly obvious to anyone else?
I've searched about using http with the Q library, and it appears it might be required to use Q.nfcall to get this to work. I'm just having trouble seeing WHY I'd have to do that. (I'm not adverse to actually doing that, I'm more curious than anything else)
Thanks!
If this is not a typo, promises.push(deferred) should be pushed the promise promises.push(deferred.promise).
function foo() {
...
return defer.promise;
}
// => foo().then(function() ...);
Q.all([
foo(),
foo(),
...
]).done(function() ...);
Q.all expects an array of promises. https://github.com/kriskowal/q#combination
Q.nfcall is just sugar around that if
working with functions that make use of the Node.js callback pattern, where callbacks are in the form of function(err, result)
https://github.com/kriskowal/q#adapting-node
You should always perform promisification at the lowest level possible. That makes reasoning about concurrency a lot easier.
function getPing(url){
return new Q.Promise(function(resolve,reject){
http.get(url,function(res){
// note this will _not_ wait for the whole request
// but just the headers.
if(res.statusCode === 200) resolve();
else reject();
});
});
}
This would let you do:
grunt.registerTask('verify', function() {
var done = this.async();
var urlPrefix = 'http://example.com/';
var pings = deployableFiles.map(function(file){
var path = file.filetype + '/' +
getVersionedFileName(file.basename, file.filetype);
return getPing(urlPrefix + path);
});
Q.all(pings).then(done).catch(function(reason) {
// There was an error somewhere
// this will happen as soon as _one_ promise rejected
return done(false);
});
});
This can be further shortened by using a better promise library like Bluebird.
You can also do this with async:
var urlPrefix = 'http://example.com/';
async.each(deployableFiles, function(file, cb) {
var path = file.filetype
+ '/'
+ getVersionedFileName(file.basename, file.filetype);
http.get(urlPrefix + path, function(res) {
if (res.statusCode === 200)
grunt.log.oklns(path + ' was found on production server.');
else
grunt.log.error('Error! ' + path + ' was not found on production server!');
cb();
}).on('error', function(e) {
grunt.log.error("Got error: " + e.message);
cb(e);
});
}, function(err) {
// all done
if (err) throw err;
// all successful
});

Categories

Resources