I am trying to automate a process in CasperJS. However, I am facing some discrepancy between the behavior on a real browser vs the same in CasperJS.
Update: Process works fine with slimerJS but gives issue with headless
Here is my script:
var casper = require('casper').create({
"waitTimeout": 10000
});
var inputElements = {
"url": "http://www.jabong.com/incult-Tapered-Jeans-In-Indigo-1032119.html?pos=4",
"size": "34",
"address": {
"email": "someemail#xyz.com"
}
}
casper.options.viewportSize = {width: 1679, height: 902};
casper.start(inputElements.url, function (){
this.wait(10000, function (){
console.log("loaded");
})
});
var x = require('casper').selectXPath;
var sizeToSelect = inputElements.size;
// select size
casper.thenClick(x("//*[contains(#class,'size-desktop')]//"+
"li[contains(#class,'first') and contains(#class,'popover-options')]//"+
"span[contains(text(),'"+sizeToSelect+"')]/"+
".."));
// add to bag
casper.thenClick(x("//*[#id='add-to-cart']"));
// view cart
casper.thenClick(x("//*[#id='header-bag-sec']/a"))
// take a screenshot
casper.then(function() {
console.log("saving screenshot");
this.capture('../../1_view_cart.png');
});
casper.then(function() {
console.log('clicked ok, new location is ' + this.getCurrentUrl());
});
// place order
casper.then(function (){
this.click("[href='https://www.jabong.com/checkout/']");
})
// take a screenshot
casper.then(function() {
console.log("saving screenshot");
this.capture('../../2_address_details.png');
});
casper.then(function (){
this.sendKeys("#login-email", inputElements.address.email);
})
casper.then(function (){
this.click("#do-guest-checkout");
});
// take a screenshot
casper.then(function() {
console.log("saving screenshot");
this.capture('../../3_guest_checkout.png');
});
casper.waitFor(function check() {
return this.evaluate(function() {
return document.querySelector('#btn-login-checkout').textContent == "Continue as Guest";
});
}, function then() {
this.click('#btn-login-checkout');
});
casper.run();
On the last page, clicking on checkbox "Checkout as Guest" on a browser removes the password input field and changes the text of the Login button to "Continue as Guest". Attached screenshot:
However, in CasperJS, the password input field and the Login button are not changing. Any idea what am I doing wrong ?
Related
I am trying to download a file with CasperJS. If using browser, the download begins when user clicks a button and the response headers look like this:
I have tried these two methods with no luck:
1) https://stackoverflow.com/a/26334034
With this approach, the code block inside the if-statement is never executed. If I remove the condition, a bunch of resources are being saved, such as css files and so on. So the event listener is working, but for some reason not triggering when I use CasperJs click function to click the button that should start the download.
2) https://stackoverflow.com/a/30122021/692695
File.csv is saved but it's the web sites sourcecode, not the csv-file I get when I click the button on the website.
All of my code:
'use strict';
var utils = require('utils');
var casper = require('casper').create({
//verbose: true,
//logLevel: "debug",
clientScripts: ["node_modules/jquery/dist/jquery.min.js"]
});
function writeHtml(filename) {
var fs = require('fs');
var content = casper.getHTML();
fs.write(filename, content, 'w');
}
function getUrl() {
var url;
url = $('.tableofcontent_link:contains("Väestö työmarkkina-aseman, sukupuolen ja iän mukaan")').parent().attr('href');
return url;
}
casper.selectOptionByValue = function(selector, valueToMatch){
this.evaluate(function(selector, valueToMatch){
var select = document.querySelector(selector),
found = false;
Array.prototype.forEach.call(select.children, function(opt, i){
if (!found && opt.value.indexOf(valueToMatch) !== -1) {
select.selectedIndex = i;
found = true;
}
});
// dispatch change event in case there is some kind of validation
var evt = document.createEvent("UIEvents"); // or "HTMLEvents"
evt.initUIEvent("change", true, true);
select.dispatchEvent(evt);
}, selector, valueToMatch);
};
var link;
var url = 'http://pxnet2.stat.fi/PXWeb/pxweb/fi/StatFin/StatFin__tym__tyti/?table';
casper.start(url);
casper.userAgent('Mozilla/5.0 (Macintosh; Intel Mac OS X)');
casper.then(function () {
this.echo("Now at: " + this.getCurrentUrl());
link = 'http://pxnet2.stat.fi' + casper.evaluate(getUrl);
});
casper.then(function () {
this.open(link);
});
casper.then(function() {
this.echo("Now at: " + this.getCurrentUrl());
// Select all data for each item
casper.click('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_VariableSelectorValueSelectRepeater_ctl01_VariableValueSelect_VariableValueSelect_SelectAllButton');
casper.click('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_VariableSelectorValueSelectRepeater_ctl02_VariableValueSelect_VariableValueSelect_SelectAllButton');
casper.click('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_VariableSelectorValueSelectRepeater_ctl03_VariableValueSelect_VariableValueSelect_SelectAllButton');
casper.click('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_VariableSelectorValueSelectRepeater_ctl04_VariableValueSelect_VariableValueSelect_SelectAllButton');
casper.click('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_VariableSelectorValueSelectRepeater_ctl05_VariableValueSelect_VariableValueSelect_SelectAllButton');
});
casper.then(function() {
// casper.selectOptionByValue('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_OutputFormats_OutputFormats_OutputFormatDropDownList',
// 'FileTypeExcelX');
// Select the format of the file from the select option list at the bottom
casper.selectOptionByValue('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_OutputFormats_OutputFormats_OutputFormatDropDownList',
'FileTypeCsvWithHeadingAndSemiColon');
});
casper.then(function () {
// just for debugging
writeHtml('page1.html');
});
casper.then(function() {
//casper.click('#ctl00_ContentPlaceHolderMain_VariableSelector1_VariableSelector1_ButtonViewTable');
});
casper.then(function() {
// FIRST ATTEMPT TO LOAD THE DATA TO a file called file.csv
var formData = casper.evaluate(function(){
return $('form#aspnetForm').serialize();
});
//this.echo("Params: " + formData);
var targetFile = 'file.csv';
casper.download(link, targetFile, 'POST', formData);
});
casper.then(function () {
// just for debugging
writeHtml('page2.html');
});
// SECCOND ATTEMPT TO LOAD THE DATA TO a file called stats.csv
casper.on('resource.received', function (resource) {
if ((resource.url.indexOf('tyti_001') !== -1) ) {
this.echo(resource.url);
var file;
file = "stats.csv";
try {
this.echo("Attempting to download file " + file);
var fs = require('fs');
casper.download(resource.url, fs.workingDirectory + '/' + file);
} catch (e) {
this.echo(e);
}
}
});
casper.run(function () {
this.echo('End').exit();
});
And my package.json:
{
"scripts": {
"test": "dotest"
},
"pre-commit": ["test"],
"dependencies": {
"jquery": "^3.3.1"
},
"devDependencies": {
"pre-commit": "^1.2.2"
}
}
Explanation of the code:
First visit this page: http://pxnet2.stat.fi/PXWeb/pxweb/fi/StatFin/StatFin__tym__tyti/statfin_tyti_pxt_001.px/?rxid=bd4d5dc1-358d-407e-ae47-13266b79bfd0
There, dynamically pick a specified link and move there.
Select all data by clicking the V-shapen icon (look at the attached screenshot) and then select the format of the file.
I've faced this issue earlier with all versions except phantomjs 2.0.0. I also tried the solutions you shared from SO a year ago and they didn't work as well.
I'm going to assume you're using a phantomjs version other than 2.0.0.
Here's the link to download it
https://bitbucket.org/ariya/phantomjs/downloads/
With it, you will have access to onFileDownload method which you can override and use like below
casper.page.onFileDownload = function(status){
console.log('onFileDownload(' + status + ')');
return "newfile.csv";
};
onFileDownload will be called whenever a file is downloaded as a result of clicking a button (ajax) or as a result of sequential get/post requests.
All you have to do is, trigger the click on the button/link that will initiate download.
Note : My solution is assuming that everything else (site is not blocking phantomjs and your request headers/cookies are as expected)
This is my code I would like to accept confirm dialog but I couldn't catch dialog however I click on correct selector. I expect that where dialog window appeared the popup.loaded event will be triggered.
which :
casper.test.begin('Sign up with Yahoo' , 2 , function(test) {
casper.on('popup.loaded', function () {
this.echo("HELLOOPOPUP")
});
x=require('casper').selectXPath;
casper.start("file:///home/sb/mytest/casperlogin-master/caspertest/ak.html" , function() {
this.wait(3000, function() {
this.on('popup.loaded', function () {
this.echo("HELLOOPOPUP")
});
this.click(x('/html/body/div[6]/div/div[11]/div/button'));
});
this.wait(2000, function() {
this.setFilter('page.confirm', function(message) {
return true;
});
})
});
});
casper.run(function() {
});
Mark up link:
https://drive.google.com/file/d/0B5Nj-vLTRyujTm1zeGdZbmVmcnc/view?usp=sharing
I have made this little test:
casper.test.begin('Test', function() {
casper.start();
casper.then(function() {
casper = this;
setTimeout(function(casper) {
casper.echo('wait 5s');
}, 5000);
});
casper.then(function() {
this.echo('should appear after 5s');
});
casper.run(function() {
this.test.done();
});
});
When I execute this test, my console shows only this line "should appear after 5s" but not the first sentence, in fact the second 'then' does not wait for 5 seconds.
It's a huge problem because it's the possible cause of many random failure in my casperjs test suite.
Maybe I have to use async (with series) to execute each step after the other.
Do you have this problem ? What's the best practice to execute some javascript functions one after the other in casperjs tests ?
Try to use wait() here:
casper.then(function() {
casper = this;
casper.echo('wait 5s');
});
casper.then(function() {
casper.wait(5000, function() {
this.echo('should appear after 5s');
});
});
"What's the best practice to execute some javascript functions one after the other in casperjs tests ?"
Here how i do :
login.js
casper.login = function (id,password) {
casper.then(function(){ //or this.then(...)
this.test.comment('----------------Connexion Only ! : --------------------');
if (!this.visible('#div_boxhover')) {
this.echo('Connexion box not visible before mouse on it!');
}
this.mouse.move(x('//*[#id="identification"]'));
if (this.visible('#div_boxhover')) {
this.echo('Connexion box visible after mouse on it!');
} else { this.echo("FAIL : Connexion box doesn't appear");}
//this.echo("I can see the logo");
this.fill(x('//*[#id="div_boxhover"][1]'), {
login: id,
pass: password
}, false);
this.click(x('//*[#name="log_in"]'));
this.waitForSelector(('.boxValid'), function() {
this.test.assertSelectorHasText('.boxValid', 'Vous êtes identifié en tant que Membre !');
this.test.assertTextExists('Succès', 'Page contains "succès" : so identification done');
});
});
};
scenario1.js
var x = require('casper').selectXPath;
phantom.injectJs( 'C:/bin/Aptana_casperjs/ccm/_functions/login.js');
casper.test.begin('\n********* Stack title : ***********\n', 3 , function suite(test) {
casper.start(yourUrl,function(){
//check one main element in the page
this.test.assertExists('.search','Search toolbar present');
})
//set a viewport (for slimerJS)
.viewport(1200,800)
//call an external function
.then(function() {
casper.login("pseudo","password");//this.login("pseudo","password");
})
//then click on a link + fill the form which appears
.thenClick('div.colMiddle > a.button', function(){
this.fillSelectors('form#frmqa', {
'select[name="cat"]' : "2",
'input[name="titre"]' : title,
'textarea[name="message"]' : message
}, false);
this.click('input#submitqa');
})
//wait for the redirection after a click at the end of a step
.waitForText(topic, function() {
this.test.assertSelectorHasText('.boxTitle', 'Mes discussions suivies', "New topic in 'Mes dicussions suivies'");
this.test.assertExists('a[actid="shqafrm"].button','Button answer topic present');
})
//click on a new link
.thenClick('a[actid="shqafrm"].button', function(){
//wait for the textarea to appear
this.waitForSelector('textarea[name="message"]',function(){
//send message and submit
this.sendKeys('textarea[name="message"]', newPost);
this.click('input#submitqa');
});
})
//check redirection or validation after a click on button/form/link
.waitForSelector('.article',function(){
test.assertTextExists(newPost, 'Answer in topic ' + title + ' done');
})
.run(function() {
this.test.comment('----------------------- Stack title over ------------------------\n');
test.done();
});
});
So i just call my functions in a new step.
I'm using ajax to pull in content to create a light box type content page. It also has a next and prev button once loaded so hence the use of ajax.
I wanted to use Ajax for the page navigation too. But if someone clicks a page link and then tries to use the light box feature both the jquery and ajax requests no longer work within the loaded area.
I've read a lot about bind and delegate but not sure how to use them in this context
Here's my main pieces of code:
// This gets called on document ready
function clicky() {
$link.click(function(e) {
e.preventDefault();
var linkPage = $(this).attr('href');
if ($(this).hasClass('pages')){
// PAGE specific code
if ($('body').scrollTop() != 0) {
$('body').animate({ scrollTop: 0 }, 500, function(){
pageLoad(linkPage);
});
} else { pageLoad(linkPage); }
console.log('page');
} else {
// PRODUCT specific code
if ($('body').scrollTop() != 0) {
$('body').animate({ scrollTop: 0 }, 500, function(){
productLoad(linkPage);
});
} else {
productLoad(linkPage);
}
}
});
}
Here's my ajax for the two different areas:
// Ajax stuff going on for pages
function pageLoad(linkPage) {
// Page stuff fades out
history.pushState(null, null, linkPage);
$("#page-content").load(linkPage + " #guts", function(){
// Loads in page content
});
}
// Ajax stuff going on for Products
function productLoad(linkPage) {
// Page stuff fades out
history.pushState(null, null, linkPage);
$("#product-content").load(linkPage + " #guts", function(){
// Shows an overlay/lightbox and loads in content
});
}
Edit: This worked for me
$(document).on('click', '.link' , function(){
console.log('this worked');
return false;
});
This worked:
$(document).on('click', '.link' , function(){
console.log('this worked');
return false;
});
I would like to click a submit button, wait for the next page to load, then obtain html on that second page.. I do the start, then and run, but the then step is still run on the first page. Any ideas?
var casper = require('casper').create();
var site = 'http://www.example.com';
var data = {};
casper.start(site, function() {
this.evaluate(function() {
$('input[type="submit"]:first').click();
});
});
casper.then(function() {
data.body = this.evaluate(function() {
var rows = $('#content table:first tbody tr');
var listings = rows.eq(3).text();
var count = rows.eq(4).text();
return {
listings: listings,
count: count
};
});
});
casper.run(function() {
this.echo(data.body.listings);
this.exit();
});
This only partially solves your problem, but you can confirm that you've made it to the second page using waitFor. For example:
this.waitFor(function check() {
return (this.getCurrentUrl() === PAGE_2_URL);
},
function then() { // step to execute when check() is ok
this.echo('Navigated to page 2', 'INFO');
},
function timeout() { // step to execute if check has failed
this.echo('Failed to navigate to page 2', 'ERROR');
});
Its a good idea use waitForResource to wait the page load finished, see documentation here documentation
example:
casper.waitForResource(function checkAuth(validcredentials) {
return validcredentials;
}, function onReceived() {
if (authTitle !== this.getTitle()) {
this.log('Autenticação realizada com sucesso, aguarde...');
} else {
// this.capture('pic3.png');
this.log('Usuario ou senha invalidos!', 'ERROR');
this.die('User or password invalids!', 1); }
});
I have a similar problem with doubleclick. Make sure the click event is actually fired. I suspect this is the cause of running the next step within the same content.