How to access errors when using WebdriverJS? - selenium

We are using the superb WebdriverJS (with Selenium) to perform acceptance testing on our web app. Everything works fine, and our tests execute successfully when we use Firefox and Safari.
However, when we use PhantomJS, our tests fail with unhelpful errors. It's almost as if... Javascript isn't even running inside the client page! Something that would cause this would be if PhantomJS' javascript environment ran into errors. Unfortunately, I can't seem to find a way to access Javascript errors when using PhantomJS with WebdriverJS.
If we were using PhantomJS directly, we could simply do (from the PhantomJS site):
page.onError = function(msg, trace) {
console.log(msg);
trace.forEach(function(item) {
console.log(' ', item.file, ':', item.line);
});
}
Unfortunately, I don't know how to access this mysterious page object when using PhantomJS within WebdriverJS. Any thoughts?

You can actually access JS errors in your PhantomJS stdout log at INFO level.
$ phantomjs --webdriver 4444 --webdriver-loglevel=INFO
You can even push things forward by setting the log level to DEBUG and see what actually PhantomJS does to execute the commands you send through Webdriver / Ghostdriver.

I figured out a workable solution! Essentially, it involves using an onerror event handler to intercept (and store) the Javascript errors. Then, once the DOM is ready, we report the errors via hidden DOM elements. This allows Selenium to look for specific elements (e.g. ".javascript-errors"), which is something it's naturally quite good at. Thanks go to myriad other blog posts and SO questions for getting me to this point.
The code:
//For detecting and reporting Javascript errors via Selenium. Note that this should be in its own file to allow this code to reliably detect syntax errors in other files.
var errors = [];
//Handle all errors
window.onerror = function(message, url, line) {
errors.push({"message":message, "url":url, "line":line});
}
//Report errors visually via HTML once the DOM is ready
window.onload = function() {
if(errors.length==0)
return;
var div = document.createElement("div");
div.className = 'javascript-errors';
div.innerHTML = '';
var style = "position:absolute; left:-10000px; top:auto; width:1px; height:1px;"; //CSS to hide the errors; we can't use display:none, or Selenium won't be able to read the error messages. Adapted from http://webaim.org/techniques/css/invisiblecontent/
for(var i=0; i<errors.length; i++)
div.innerHTML += '<div class="javascript-error" style="' + style +'"><span class="message">' + errors[i].message.replace('<', '<').replace('>', '>') + '</span><br/><span class="url">' + errors[i].url + '</span><br/><span class="line">' + errors[i].line + '</span></div>';
document.body.appendChild(div);
}

Related

What is the Puppeteer equivalent of this Selenium code that finds an element by css and clicks on it?

I am trying to parse banking website and I have the whole workflow recorded in Selenium and everything works. Due to inability to persist cookies between sessions while using Selenium Webdriver (Cookies cannot be loaded - "missing name exception") I moved to puppeteer. Line by line it went good until I got the following lines:
In selenium:
await driver.findElement(By.css(".buttons:nth-child(4)")).click()
and in puppeteer
await page.frames()[1].click('.buttons:nth-child(4)');
does not work.
What is funny though:
await page.frames()[1].waitForSelector('.buttons:nth-child(4)');
does not throw exception so element is present on the page.
page.frames() will return a promise. You have to await almost everything:
let frames = await page.frames()
await frames[1].click('.buttons:nth-child(4)');

Cypress - run test in iframe

I'm trying to find elements in iframe but it doesn't work.
Is there anyone who have some system to run tests with Cypress in iframe? Some way to get in iframe and work in there.
It's a known issue mentioned here. You can create your own custom cypress command which mocks the iframe feature. Add following function to your cypress/support/commands.js
Cypress.Commands.add('iframe', { prevSubject: 'element' }, ($iframe, selector) => {
Cypress.log({
name: 'iframe',
consoleProps() {
return {
iframe: $iframe,
};
},
});
return new Cypress.Promise(resolve => {
resolve($iframe.contents().find(selector));
});
});
Then you can use it like this:
cy.get('#iframe-id')
.iframe('body #elementToFind')
.should('exist')
Also, because of CORS/same-origin policy reasons, you might have to set chromeWebSecurity to false in cypress.json (Setting chromeWebSecurity to false allows you to access cross-origin iframes that are embedded in your application and also navigate to any superdomain without cross-origin errors).
This is a workaround though, it worked for me locally but not during CI runs.
This works for me locally and via CI. Credit: Gleb Bahmutov iframes blog post
export const getIframeBody = (locator) => {
// get the iframe > document > body
// and retry until the body element is not empty
return cy
.get(locator)
.its('0.contentDocument.body').should('not.be.empty')
// wraps "body" DOM element to allow
// chaining more Cypress commands, like ".find(...)"
// https://on.cypress.io/wrap
.then(cy.wrap)
}
spec file:
let iframeStripe = 'iframe[name="stripe_checkout_app"]'
getIframeBody(iframeStripe).find('button[type="submit"] .Button-content > span').should('have.text', `Buy me`)
that is correct. Cypress doesn't support Iframes. It is simple not possible at the moment. You can follow (and upvote) this ticket: https://github.com/cypress-io/cypress/issues/136

How to debug a PhantomJS script?

My script has some syntax error but instead of showing the error, PhantomJS doesn't display anything. Why Phantom JS isn't showing parse error if he script has errors?
In the following PhantomJS script (running via windows CMD), phantomJs hangs instead of showing error if there is a parsing error in the script.
var system = require('system');
var webpage = require('webpage').create();
console.log('starting script');
if (system.args.length === 0) {
console.log('no args');
} else {
system.args.forEach(function(arg,index){
console.log('arg is '+arg+' at '+index);
});
}
webpage.open('http://localhost:3000/cookie-demo',function(status){
if (status === 'success'){
console.log('success in opening page');
phantom.cookies.forEach(function(cookie,index){
for ( var key in cookie){
/*if instead of index, I use i as variable (undefined), the script just hangs!*/
console.log('[cookie:'+index+']'+key+'='+'cookie[key]');
}
});
phantom.exit(0);
}
else{
console.log('could not open the page');
phantom.exit(1);
}
});
If there is no syntax error in the script, I get following output
C:\Users\Manu\Documents\manu\programs\random>phantomjs --cookies-file=cookie-jar.txt phantomTest.js
starting script
arg is phantomTest.js at 0
success in opening page
[cookie:0]domain=cookie[key]
[cookie:0]expires=cookie[key]
[cookie:0]expiry=cookie[key]
[cookie:0]httponly=cookie[key]
[cookie:0]name=cookie[key]
[cookie:0]path=cookie[key]
[cookie:0]secure=cookie[key]
[cookie:0]value=cookie[key]
[cookie:1]domain=cookie[key]
[cookie:1]expires=cookie[key]
[cookie:1]expiry=cookie[key]
[cookie:1]httponly=cookie[key]
[cookie:1]name=cookie[key]
[cookie:1]path=cookie[key]
[cookie:1]secure=cookie[key]
[cookie:1]value=cookie[key]
But if there is a syntax error, I see nothing on the console and it doesnt exit
C:\Users\Manu\Documents\manu\programs\random>phantomjs --cookies-file=cookie-jar.txt phantomTest.js
PhantomJS version 2.0 and 2.1 will silently fail and hang if there are syntax errors. Use versions 1.9.8 or 2.5 betas. All downloads are here: https://bitbucket.org/ariya/phantomjs/downloads/
Better still migrate to Puppeteer which is a project with very similar API that uses headless Google Chrome underneath.

Casperjs cannot access same global objects as in from browsers console

I am new to Casperjs, phantomjs .I have been trying hard to create some page automation to login and take some steps in a CMS but i am having issues with accessing the global window variables from casperjs evaluate() function. the below example is just checking jquery on Google. Jquery exists in the page and some other global functions but i can't access them from casperjs.
casper.start('https://www.google.ca/#hl=en', function() {
// search for 'casperjs' from google form
this.fill('form[action="/search"]', { q: 'casperjs' }, false);
});
casper.then(function() {
this.evaluate(function jquery() {
console.log('looking for jquery ---');
console.log($ + 'exists');
});
});
getting error - `ReferenceError: Can't find variable: $
How can i fix this ?
Any help is appreciated :)
For use jQuery in casperjs
inject script in page, something like:
var casper = require('casper').create({
some code here,
clientScripts: ['/path/to/jquery.js'],
});

Navigating site (including forms) with PhantomJS

I'm trying to automate an application that uses form security in order to upload a file and then scrape data from the returned HTML.
I started out using the solution from this question. I can define my steps and get through the entire workflow as long as the last step is rendering the page.
Here are the two steps that are the meat of my script:
function() {
page.open("https://remotesite.com/do/something", function(status) {
if ('success' === status) {
page.uploadFile('input[name=file]', 'x.csv');
page.evaluate(function() {
// assignButton is used to associate modules with an account
document.getElementById("assignButton").click();
});
}
});
},
function() {
page.render('upload-results.png');
page.evaluate(function() {
var results = document.getElementById("moduleProcessingReport");
console.log("results: " + results);
});
},
When I run the script, I see that the output render is correct. However, the evaluate part isn't working. I can confirm that my DOM selection is correct by running it in the Javascript console while on the remote site.
I have seen other questions, but they revolve around using setTimeout. Unfortunately, the step strategy from the original approach already has a timeout.
UPDATE
I tried a slightly different approach, using this post and got similar results. I believe that document uses an older PhantomJS API, so I used the 'onLoadFinished' event to drive between steps.
i recomend you use casperjs or if you use PJS's webPage.injectScript() you could load up jquery and then your own script to do form input/navigation.