When I use CasperJS to handle web, I find a strange case. The program exits on exception even if I use try-catch block!
My code is as follows. I hope the program can continue running next loop. However, when the iframe cannot be found, an exception throws like CasperError: Frame number "1" is out of bounds. and the whole program exits. The myStore function in catch block is also not run.
Is there anybody can help us?
try {
// find the iframe and then fill in the message
casper.withFrame(1, function() {
casper.evaluate(function(message) {
// some function code
});
casper.wait(1000, function() {
myStore(stores, index+1);
// when the iframe not found, function myStroe will not be run
});
});
} catch (err) {
output(false, "error=" + err.message);
myStore(stores, index+1); // myStroe will not be run on Exception
}
I try the example given by Artjom B but it does not work.
var frameExists = false;
casper.withFrame(1, function() {
frameExists = true;
casper.evaluate(function(message) {
// some function code
});
});
casper.wait(3000, function() {
if (frameExists) {
// the program run this branch and got stuck in the nonexistent selector since the frame is not found.
casper.click("input#send");
// some function code
output(true, "index=" + index + ";storeId=" + store.id + ";succeeded");
myStore(stores, index+1);
} else {
output(false, "index=" + index + ";storeId=" + store.id + ";error=" + err.message);
myStore(stores, index+1);
}
});
It is very strange. I don't know why the program run into the wrong branch with frameExists === true.
There is the handy little option called exitOnError:
At the beginning:
var casper = require('casper').create({
exitOnError: false
});
or later:
casper.options.exitOnError = false;
If you want myStore() to be run regardless of the existence of the frame, you can do something like this:
casper.then(function(){
var frameExists = false;
// find the iframe and then fill in the message
casper.withFrame(1, function() {
frameExists = true;
casper.evaluate(function(message) {
// some function code
});
casper.wait(1000, function() {
myStore(stores, index+1);
// when the iframe not found, function myStroe will not be run
});
});
casper.then(function(){
if (!frameExists) {
output(false, "error=" + err.message);
myStore(stores, index+1); // myStroe will not be run on Exception
}
});
});
Related
I need help with loop beaking.
For my check I did the simple test:
while(i < 10) {
element(by.xpath("//a[contains(#id, 'ma-apply')]")).isPresent().then(function(result) {
if(!result) {
helper.one_page_ahead();
} else {
console.log('there is on the page');
break;
}
});
i++;
};
This code leads to the error.
I tried to follow advice via StackOverflow and changed break to return.
But this leads to full loop execution (up to 10).
Here is the output:
[14:17:46] I/launcher - Running 1 instances of WebDriver Started user
skills: AJAX there is on the page there is on the page there is on the
page there is on the page there is on the page there is on the page
there is on the page there is on the page .
1 spec, 0 failures Finished in 37.93 seconds
I tried the same with for loop like
for(i = 0; i < 10; i++) {
//code
break;
}
Would be glad to find the answer.
This is some commentary about why the while statement does not work: When you call isPresent you are returned a webdriver.promise.Promise<boolean>. Since you are in the webdriver control flow, you'll need to throw an error,
browser.get('http://angularjs.org');
var i = 0;
var running = true;
while(i < 3 && running) {
console.log('while: ' + running + ' ' + i);
element(by.model('username')).isPresent().then((result) => {
console.log('element: ' + running + ' ' + i);
if (result) {
// huzzah we found it, so lets break the element after the first test
browser.get('https://docs.angularjs.org/tutorial');
} else {
running = false;
throw new Error('no username')
}
}).catch((err) => {
console.log(err);
});
i++;
}
This basically prints out:
[19:07:18] I/hosted - Using the selenium server at http://localhost:4444/wd/hub
[19:07:18] I/launcher - Running 1 instances of WebDriver
Started
while: true 0
while: true 1
while: true 2
element: true 3
[Error: no username]
element: false 3
[Error: no username]
element: false 3
[Error: no username]
So basically your while loop queues up items in the control flow to execute. These then will get executed asynchronously in order.
I like the suggestion by Sudharsan Selvaraj to do this recursively.
You need to implement a recursive method to achieve what you want, try the below piece of code,
function runTillElementFound(totalCount,currentCount){
var self = this;
var _element = element(by.xpath("//a[contains(#id, 'ma-apply')]"));
if(currentCount < totalCount){
return _element.isPresent().then(function(isElementPresent){
if(isElementPresent){
return;
}else{
helper.one_page_ahead();
self.runTillElementFound(totalCount,currentCount++);
}
})
}else{
return false; //if element not present after Max count reached.
}
}
this.runTillElementFound(10,0); //this will execute the method untill the required element found on the page.
If you want to avoid recursion you could modify the index variable inside the returned promised
while(i < 10) {
element(by.xpath("//a[contains(#id, 'ma-apply')]")).isPresent().then(function(result) {
if(!result) {
helper.one_page_ahead();
} else {
console.log('there is on the page');
i = 10;
}
});
i++;
};
And I would add a browser.sleep(x) in between each repetion to avoid the code to be run before the result from the promise is evaluated.
i = 10; is not effecting, Still loop iterating
I work at the moment on an automated tests scripts which needs a headless browser (PhantomJS) and all the DOM navigation and actions possible (except downloads).
So PhantomJS should fit.
However I can't use PhantomJS with javascript jQuery as the following errors :
var page = require('webpage').create();
page.open('http://mywebsite.com/', function() {
function dothat() {
console.log('0 started');
$('#id').children().eq(0).click();
console.log('0 finished');
return main(1, 0);
}
}
dothat()
Here the scripts stop a '0 started'. The page I load uses jQuery so I though I wouldn't need to download it back. Even so, let's try with loading it.
var page = require('webpage').create();
page.open('http://mywebsite.com/', function() {
page.injectJs('jquery-2.2.1.min.js');
function dothat() {
console.log('0 started');
$('#id').children().eq(0).click();
console.log('0 finished');
return main(1, 0);
}
}
dothat()
Yes I have the file locally in the same directory...
And it shows that error (very fastly) :
TypeError: undefined is not a constructor (evaluating 'jQuery.easing[jQuery.easing.def](t,e,n,i,a)')
What do you suggest doing to fix that?
Is this an internal bug in PhantomJS and so I should try a Firefox and Xvfb on Ubuntu or is it my error somewhere?
I though first about changing code to a total non-jQuery version, but I need great selectors with classes and to click on some objects...
Thank you very much for pointing me the solution!
EDIT 1: I changed this according to what I have understand from a first answer. Now the console does not print anything
var page = require('webpage').create();
page.onConsoleMessage = function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
page.open('http://mywebsite.com/', function() {
page.injectJs('jquery-2.2.1.min.js');
var result = page.evaluate(function() {
function dothat() {
console.log('0 started');
$('#id').children().eq(0).click();
console.log('0 finished');
return main(1, 0);
}
function main(x, y) {
if (x === 0) {
return setTimeout(dothat(), y);
} else if (x === 1) {
return setTimeout(dothis(), y);
}
}
return main(0, 5000); //first launch
}
});
I'm not sure that was what you was pointed out though.
EDIT 2: This is a real script that wants to go on google.com and login as me. However the script does not work as intended, see below.
var page = require('webpage').create();
page.onConsoleMessage = function(msg) {
console.log('CONSOLE: ' + msg);
};
page.open('https://google.com/', function() {
page.injectJs('jquery-2.2.1.min.js');
page.evaluate(function() {
function dofirst() {
$('#gb_70').click();
return main(1, 0);
}
function dosecond() {
document.getElementById('Email').value = 'myemail#gmail.com';
$('#next').click();
return main(2, 0);
}
function dothird() {
document.getElementById('Passwd').value = 'mypwd';
$('#signIn').click();
}
function main(i, j) {
if (i === 0) {
console.log('launching 0');
return setTimeout(dofirst(), j); // connections
}
else if (i === 1) {
console.log('launching 1');
return setTimeout(dosecond(), 5000);
}
else if (i === 2) {
console.log('launching 2');
return setTimeout(dothird(), 5000);
}
}
return main(0, 5000);
});
});
As you can see, main is a function I'm using for delaying steps.
However the code does not work and returns me this error (or those) :
TypeError: null is not an object (evaluating 'document.getElementById('Email').value = 'myemail#gmail.com'')
undefined:7 in dosecond
:22 in main
:4 in dofirst
:18 in main
:29
:30
Thank you for keeping trying to help me!
In PhantomJS there's always two javascirpt contexts where functions and variables exist:
PhantomJS context
Opened page context
They are not aware of each other and do not intersect!
PhantomJS cannot access any of the target page objects or DOM, that is why $('#id').children().eq(0).click(); won't work - there is no jQuery in PhantomJS script, there are no DOM elements. They are in the second context, or, in other words, sandbox.
To do anything with the page you have to sort of teleport code there. It is done with the help of page.evaluate() method.
var page = require('webpage').create();
page.open('http://mywebsite.com/', function() {
page.injectJs('jquery-2.2.1.min.js');
var result = page.evaluate(function(){
// You can certainly use functions
// Be sure to declare them also
// inside of page.evaluate
function dothat() {
console.log('0 started');
$('#id').children().eq(0).click();
console.log('0 finished');
return main(1, 0);
}
// Important: don't forget to return result
// back to PhantomJS main context
return dothat();
});
});
Note that to receieve console messages from sandboxed page context you must subscribe to them via page.onConsoleMessage callback:
page.onConsoleMessage = function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
After EDIT 1:
My bad, I've lost a bracket editing your example (fixed above) which led to a syntax error, a fact that PhantomJS v2.x won't complain about, it'll just hang there silently. That's what you mistook for no console messages. Could be useful to test script for syntax errors in v1.9.8 first or in some IDE with syntax highlighting.
Your current code should look this (the missing bracket also fixed):
var page = require('webpage').create();
page.onConsoleMessage = function(msg, lineNum, sourceId) {
console.log('CONSOLE: ' + msg + ' (from line #' + lineNum + ' in "' + sourceId + '")');
};
page.open('http://mywebsite.com/', function() {
page.injectJs('jquery-2.2.1.min.js');
var result = page.evaluate(function() {
function dothat() {
console.log('0 started');
$('#id').children().eq(0).click();
console.log('0 finished');
return main(1, 0);
}
function main(x, y) {
if (x === 0) {
return setTimeout(dothat(), y);
} else if (x === 1) {
return setTimeout(dothis(), y);
}
}
return main(0, 5000); //first launch
}); //
});
Note: will complain about inextistent dothis() function.
Don't you want to do it the other way around?
function dothat() {
console.log('0 started');
$('#id').children().eq(0).click();
console.log('0 finished');
return main(1, 0);
}
var page = require('webpage').create();
page.open('http://mywebsite.com/', dothat)
Please i am having an issue with my java script.
start_clock_ws : function(){
var that = this;
..decl
function init(){
if(socket.isReady() === true){
socket.send({ "time": 1});
console.log("clock started");
}else{
console.log("The other guy");
console.log("The ready state is",socket.isReady());
return that.start_clock(); //Here is my issue
}
};
function responseMsg(response){
..decl
}
this.run = function(){
console.log("the parent fired");
setInterval(init, 60000);
};
console.log("Its here");
init();
this.run();
this.clock_started = true;
socket.init({
onmessage : function(msg){
var response = JSON.parse(msg.data);
console.log("It fires here first");
if (response && response.msg_type === 'time') {
responseMsg(response);
}
}
});
},
start_clock : function(){
}
My issue is with the else above. I checked the value of my isReady() and it was false . I then returned my start_clock. However, when i execute , it returns the start_clock and proceeded and run my this.run. Shouldn't the return above force it to proceed with the start_clock leaving the rest part of the code ? Before it was not return , it was just a function call. I then added the return hoping it would work. Also while debugging. I noticed my socket.onmessage function is being called multiple times and due to ws request. I then took it to the extreme below the run .
Please help or explanation would be appreciated. Its quite new to me and is having difficulty understanding the problem.
Try this when you call the init function:
if(init())
{
return;
}
If the function returns int value, it is because you are returning a function and the code has to stop. But if no return value (undefined), the code will continue.
I hope ist help!
Try this:
// Check if clock_started and if not then run following code.
if(!init()){
this.run();
this.clock_started = true;
socket.init({
onmessage : function(msg){
var response = JSON.parse(msg.data);
console.log("It fires here first");
if (response && response.msg_type === 'time') {
responseMsg(response);
}
}
});
}
Also
function init(){
if(socket.isReady() === true){
socket.send({ "time": 1});
console.log("clock started");
return false;
}else{
console.log("The other guy");
console.log("The ready state is",socket.isReady());
return that.start_clock(); //Here is my issue
}
};
I have created a simple Fiddle for explanation purpose.
Explanation of issue
Return is used to return a value to the calling function. You have to receive this value and process it accordingly. In your code, you are calling a function but there is no check for return value, hence it proceeds.
Code
function welcome(name){
console.log("Hello " + name);
}
function permission(returnValue){
var str = returnValue?"You may proceed.":"You may not.";
console.log(str);
return returnValue
}
function proceed(){
console.log("Proceeding to next task.")
}
function run1(){
welcome('foo');
permission(false);
proceed();
}
function run2(){
welcome('foo');
if(permission(false))
proceed()
else
console.log("Permission denied.");
}
<button onclick="run1()">Not Checking return value</button>
<button onclick="run2()">Checking return value</button>
I have a Node.js application that, upon initialisation, reads two tables from an SQL database and reconstructs their relationship in memory. They're used for synchronously looking up data that changes (very) infrequently.
Problem: Sometimes I can't access the data, even though the application reports successfully loading it.
Code:
constants.js
module.exports = {
ready: function () { return false; }
};
var log = sysLog('core', 'constants')
, Geo = require('../models/geo.js');
var _ready = false
, _countries = []
, _carriers = [];
function reload() {
_ready = false;
var index = Object.create(null);
return Geo.Country.find().map(function (country) {
var obj = country.toPlainObject()
, id = obj.id;
delete obj.id;
index[id] = obj;
return Object.freeze(obj);
}).then(function (countries) {
log.debug('Loaded ' + countries.length + ' countries');
_countries = countries;
return Geo.Carrier.Descriptor.find().map(function (carrier) {
var obj = carrier.toPlainObject();
if (obj.country) {
obj.country = index[obj.country];
}
return Object.freeze(obj);
}).then(function (carriers) {
log.debug('Loaded ' + carriers.length + ' carriers');
_carriers = carriers;
});
}).finally(function () {
_ready = true;
});
}
reload().catch(function (err) {
log.crit({ message: 'Could not load constants', reason: err });
process.exit(-42);
}).done();
module.exports = {
reload : reload,
ready : function () { return _ready; },
countries : function () { return _countries; },
carriers : function () { return _carriers; }
};
utils.js
var log = sysLog('core', 'utils')
, constants = require('./constants');
module.exports = {
getCountryByISO: function(iso) {
if (!iso) {
return;
}
if ('string' != typeof iso) {
throw new Error('getCountryByISO requires a string');
}
if (!constants.ready()) {
throw new UnavailableError('Try again in a few seconds');
}
switch (iso.length) {
case 2:
return _.findWhere(constants.countries(), { 'iso2' : iso.toUpperCase() });
case 3:
return _.findWhere(constants.countries(), { 'iso3' : iso.toUpperCase() });
default:
throw new Error('getCountryByISO requires a 2 or 3 letter ISO code');
}
},
getCarrierByCode: function(code) {
if (!code) {
return;
}
if ('string' != typeof code) {
throw new Error('getCarrierByCode requires a string');
}
if (!constants.ready()) {
throw new UnavailableError('Try again in a few seconds');
}
return _.findWhere(constants.carriers(), { 'code' : code });
},
getCarrierByHandle: function(handle) {
if (!handle) {
return;
}
if ('string' != typeof handle) {
throw new Error('getCarrierByHandle requires a string');
}
if (!constants.ready()) {
throw new UnavailableError('Try again in a few seconds');
}
return _.findWhere(constants.carriers(), { 'handle' : handle });
}
};
Use case
if (data.handle) {
carrier = utils.getCarrierByHandle(data.handle);
if (_.isEmpty(carrier)) {
throw new InternalError('Unknown carrier', { handle: data.handle });
}
}
What's going on: All errors are logged; as soon as I see an error (i.e. "Unknown carrier") in the logs, I check the SQL database to see if it should've been recognised. That has always been the case so far, so I check the debug log to see if data was loaded. I always see "Loaded X countries" and "Loaded Y carriers" with correct values and no sign of "Could not load constants" or any other kind of trouble.
This happens around 10% of the time I start the application and the problem persists (i.e. didn't seem to go away after 12+ hours) and seems to occur regardless of input, leading me to think that the data isn't referenced correctly.
Questions:
Is there something wrong in constants.js or am I doing something very obviously wrong? I've tried setting it up for cyclical loading (even though I am not aware of that happening in this case).
Why can't I (sometimes) access my data?
What can I do to figure out what's wrong?
Is there any way I can work around this? Is there anything else I could to achieve the desired behaviour? Hard-coding the data in constants.js is excluded.
Additional information:
constants.reload() is never actually called from outside of constants.js.
constants.js is required only in utils.js.
utils.js is required in app.js (application entry); all files required before it do not require it.
SQL access is done through an in-house library built on top of knex.js and bluebird; so far it's been very stable.
Versions:
Node.js v0.10.33
underscore 1.7.0
bluebird 2.3.11
knex 0.6.22
}).finally(function () {
_ready = true;
});
Code in a finally will always get called, regardless of if an error was thrown up the promise chain. Additionally, your reload().catch(/* ... */) clause will never be reached, because finally swallows the error.
Geo.Country.find() or Geo.Carrier.Descriptor.find() could throw an error, and _ready would still be set to true, and the problem of your countries and carriers not being set would persist.
This problem would not have occurred if you had designed your system without a ready call, as I described in my previous post. Hopefully this informs you that the issue here is really beyond finally swallowing a catch. The real issue is relying on side-effects; the modification of free variables results in brittle systems, especially when asynchrony is involved. I highly recommend against it.
Try this
var log = sysLog('core', 'constants');
var Geo = require('../models/geo.js');
var index;
var _countries;
var _carriers;
function reload() {
index = Object.create(null);
_countries = Geo.Country.find().map(function (country) {
var obj = country.toPlainObject();
var id = obj.id;
delete obj.id;
index[id] = obj;
return Object.freeze(obj);
});
_carriers = _countries.then(function(countries) {
return Geo.Carrier.Descriptor.find().map(function (carrier) {
var obj = carrier.toPlainObject();
if (obj.country) {
obj.country = index[obj.country];
}
return Object.freeze(obj);
});
});
return _carriers;
}
reload().done();
module.exports = {
reload : reload,
countries : function () { return _countries; },
carriers : function () { return _carriers; }
};
constants.reload() is never actually called from outside of
constants.js.
That's your issue. constants.reload() reads from a database, which is an aysnchronous process. Node's require() is a synchronous process. At the time constants.js is required in utils.js and the module.exports value is returned, your database query is still running. And at whatever point in time that app.js reaches the point where it calls a method from the utils module, that query could still be running, resulting in the error.
You could say that requiring utils.js has the side-effect of requiring constants.js, which has the side-effect of executing a database query, which has the side-effect of concurrently modifying the free variables _countries and _carriers.
Initialize _countries and _carriers as unresolved promises. Have reload() resolve them. Make the utils.js api async.
promises.js:
// ...
var Promise = require('bluebird');
var countriesResolve
, carriersResolve;
var _ready = false
, _countries = new Promise(function (resolve) {
countriesResolve = resolve;
})
, _carriers = new Promise(function (resolve) {
carriersResolve = resolve;
});
function reload() {
_ready = false;
var index = Object.create(null);
return Geo.Country.find().map(function (country) {
// ...
}).then(function (countries) {
log.debug('Loaded ' + countries.length + ' countries');
countriesResolve(countries);
return Geo.Carrier.Descriptor.find().map(function (carrier) {
// ...
}).then(function (carriers) {
log.debug('Loaded ' + carriers.length + ' carriers');
carriersResolve(carriers);
});
}).finally(function () {
_ready = true;
});
}
reload().catch(function (err) {
log.crit({ message: 'Could not load constants', reason: err });
process.exit(-42);
}).done();
module.exports = {
reload : reload,
ready : function () { return _ready; },
countries : function () { return _countries; },
carriers : function () { return _carriers; }
};
utils.js
getCarrierByHandle: function(handle) {
// ...
return constants.carriers().then(function (carriers) {
return _.findWhere(carriers, { 'handle' : handle });
});
}
Use case:
utils.getCarrierByHandle(data.handle).then(function (carrier) {
if (_.isEmpty(carrier)) {
throw new InternalError('Unknown carrier', { handle: data.handle });
}
}).then(function () {
// ... next step in application logic
});
This design will also eliminate the need for a ready method.
Alternatively, you could call constants.reload() on initialization and hang all possibly-dependent operations until it completes. This approach would also obsolete the ready method.
What can I do to figure out what's wrong?
You could have analyzed your logs and observed that "Loaded X countries" and "Loaded Y carriers" were sometimes written after "Unknown carrier", helping you realize that the success of utils.getCarrierByHandle() was a race condition.
describe('some tests', function () {
/*
* Run some tests...
*/
})
after(function () {
failures = ? // <--- what goes here?
console.log(failures + " tests failed!")
})
I'd use this to keep chromedriver's browser open if a test failed, and to report success or failure to sauce labs.
Mocha's Runner and Reporters have the info I'm looking for as stats but I'm not sure how to get to them from within a test file.
I found answer to this question here
afterEach(function() {
if (this.currentTest.state == 'failed') {
// ...
}
});
After a quick check of the code, I believe there is no way for code in after to have access to the runner or the reporters. However, there's a way to get the state of the tests at run time:
describe("blah", function () {
it("blah", function () {
throw new Error("blah");
});
after(function (){
var failed = false;
var tests = this.test.parent.tests;
for(var i = 0, limit = tests.length; !failed && i < limit; ++i)
failed = tests[i].state === "failed";
if (failed)
console.log("FAILED");
});
});
Look at the line var tests = this.test.parent.tests. I believe that this.test seems to be a test object associated with the after call. The value of this.test.parent is the suite object associated with the top level describe. And the value of this.test.parent.tests is the list of tests in that suite. So the code there goes through each test, and detects whether the test is in the "failed" state.
None of the variables used in the code above are marked as private (by use of a leading underscore in the name). At the same time, there are no guarantees that future versions of Mocha will use the exact same structure.
All test failures are exceptions so to catch hook failures, I'd wrap the hook with code that detects exceptions. This is a proof-of-concept that shows how it could be done (and I've moved some of the code in the previous example into a has_failed function):
var hook_failure = false;
function make_wrapper(f) {
return function wrapper() {
try {
f.apply(this, arguments);
}
catch (e) {
hook_failure = true;
throw e;
}
};
}
function has_failed(it) {
var failed = false;
var tests = it.test.parent.tests;
for(var i = 0, limit = tests.length; !failed && i < limit; ++i)
failed = tests[i].state === "failed";
return failed;
}
describe("blah", function () {
before(make_wrapper(function () {
throw new Error("yikes!");
}));
it("blah", function () {
throw new Error("q");
});
after(function (){
var failed = has_failed(this);
if (failed)
console.log(this.test.parent.title + " FAILED");
});
});
after(function () {
if (hook_failure)
console.log("HOOK FAILURE");
});
In the example above I use the wrapper method and the method of scanning tests in after. However, it would be possible to only use wrappers throughout for hooks and tests.