Submit a form using Casperjs - casperjs

I have tried various methods in casperJS to fill in and submit a form. The code is shown below. Ultimately I am building a robot to automatically check the status of a cargo airwaybills on the IAG Cargo web portal.
Sendkeys will complete the form but I cannot manage to click the SEARCH button.
Using casperJS Form filling methods does not work at all.
Is this an unusual website or am I doing something wrong?
In the code below the program appears to fail on line
this.clickLabel('SEARCH', 'button');
and the subsequent code does not run.
(I have used an dummy airwaybill number in this example so the final page will show 'Airwaybill not found')
var casper = require('casper').create();
var x = require('casper').selectXPath;
phantom.cookiesEnabled = true;
casper.userAgent('Mozilla/4.0 (compatable; MSIE 6.0; Windows NT 5.1)');
casper.start('https://www.iagcargo.com/iagcargo/portlet/en/html/601/main/search');
casper.waitForSelector("#awb_cia", function() {
this.echo('Selector found');
casper.capture('iag_start.png');
this.sendKeys('#awb_cia','125');
this.sendKeys('#awb_cod','12345675');
});
casper.then(function step2() {
this.clickLabel('SEARCH', 'button');
this.echo('this is step 2');
casper.capture('iag_end.png');
});
require('utils').dump(casper.steps.map(function(step) {
return step.toString();
}));
casper.run();

Not being able to click is a common problem that could have by many reasons. clickLabel() won't work here, since it's not a button tag, but an input tag. Here is what I saw on the page:
You can try this,
casper.click('input[value="Search"]');
In general, here are things you could try when clicking an element.
Try changing the viewportSize, e.g.
viewportSize: { width: 1024, height: 768 }
This is because, casperjs won't click anything that is not visible on the page.
If using xpath selector, try CSS instead. XPath selectors are brittle, and do not work the same across all browsers.
Sometimes jquery click works, e.g.
casper.evaluate(function(){
var element = document.querySelector( 'span.control.critical.closer' );
$(element).click();
});
If there's a function inside the button tag, you can call that function directly. E.g.
SEARCH
You can call that function directly,
casper.someFunc();

var casper = require('casper').create({
verbose: true,
logLevel: "debug",
waitTimeout: 60000,
resourceTimeout: 10000,
viewportSize: {
width: 1024,
height: 768
},
pageSettings: {
javascriptEnabled: true,
loadImages: true,
loadPlugins: true
}
});
var x = require('casper').selectXPath;
phantom.cookiesEnabled = true;
casper.userAgent('Mozilla/4.0 (compatable; MSIE 6.0; Windows NT 5.1)');
casper.start('https://www.iagcargo.com/iagcargo/portlet/en/html/601/main/search');
casper.waitForSelector("#awb_cia", function() {
this.echo('Selector found');
casper.capture('iag_start.png');
this.sendKeys(x('//*[#id="awb_cia"]'),'125');
this.sendKeys('#awb_cod','12345675');
this.capture('iag_middle.png');
});
casper.then(function step2() {
if(this.exists('body > div.cuerpo > div.cuerpo > div.contenido > div > form > table > tbody > tr > td:nth-child(3) > input')){
this. click('body > div.cuerpo > div.cuerpo > div.contenido > div > form > table > tbody > tr > td:nth-child(3) > input');
}
});
casper.then(function step3() {
this.echo('this is step 2');
casper.capture('iag_end.png');
});
//require('utils').dump(casper.steps.map(function(step) {
// return step.toString();
//}));
casper.run();

Related

CasperJs Google login

I having been working on some code to access my google CSE
For that I need to sign in with my google account.
I have the following code:
var casper = require('casper').create({
verbose: true,
logLevel: 'debug',
waitTimeout: 5000,
clientScripts: ["libs/jquery.min.js"],
userAgent: 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_7_5)
AppleWebKit/537.4 (KHTML, like Gecko) Chrome/22.0.1229.94 Safari/537.4'
});
const google_email = "MY_EMAIL";
const google_passwd = "MY_PASSWORD";
const loginUrl = 'https://accounts.google.com';
casper.start(loginUrl, function() {
this.waitForSelector('#view_container > form');
});
casper.then(function() {
this.fillSelectors('#view_container > form', {
'input[name="identifier"]': google_email
}, false);
});
casper.then(function() {
this.click('#identifierNext');
});
casper.wait(500, function() { //Wait for next page to load
this.capture('images/step01.png');
});
casper.then(function() {
this.evaluate(function () {
var identifierNext = $('#identifierNext');
identifierNext.click();
});
});
casper.then(function() {
this.capture('images/step02.png');
});
casper.run(function() {
this.echo("Done");
});
The part of entering the email seems to work.
But the click part isn't working.
I found this but it seems outdated.
Thanks
I haven't fixed the issue related to the new form, but we found a way to access the old form, so the "old" scripts should still work, and that solution is disabling JS. For that, change the loginUrl to
https://accounts.google.com/ServiceLogin?passive=1209600&continue=https%3A%2F%2Faccounts.google.com%2FManageAccount&followup=https%3A%2F%2Faccounts.google.com%2FManageAccount&flowName=GlifWebSignIn&flowEntry=ServiceLogin&nojavascript=1#identifier
Important thing: nojavascript=1
We are using the script posted in Casperjs Google Login Not working
Just changing the login URL.
Try this - a lot more delays are needed to wait for the dynamically loaded pages.
casper.options.verbose = true; // verbose reporting
casper.options.logLevel = 'debug'; // full log reporting
casper.options.exitOnError = false; // Keep going on error
const google_email = "EMAIL";
const google_passwd = "PASSWORD";
const loginUrl = 'https://accounts.google.com';
// Load the login page
casper.start(loginUrl, function() {
this.waitForSelector('#view_container'); // '> form' doesn't seem to work
});
// Fill in the 'username' form
casper.then(function() {
this.fill('form', {
identifier: google_email,
});
this.sendKeys('#view_container', casper.page.event.key.Enter , {keepFocus: true});
});
// First 'Enter' is too quick for Google, send another one after a pause
casper.wait(2500, function() {
this.sendKeys('#identifierId', casper.page.event.key.Enter , {keepFocus: true});
});
// Wait for the 'password' form
casper.waitForSelector("#passwordNext", function() {
this.echo("password form is apparently available");
});
// Password form seems to load slowly, even if the selector is found/visible, this delay ensures next form fill works
casper.wait(2500, function() {
this.echo("password form is really available");
});
// Fill in the 'password' form
casper.then(function() {
this.fill('form', {
password: google_passwd,
});
this.sendKeys('#view_container', casper.page.event.key.Enter , {keepFocus: true});
});
// First 'Enter' is too quick for Google, send another one after a pause
casper.wait(500, function() {
this.sendKeys('input.whsOnd.zHQkBf', casper.page.event.key.Enter , {keepFocus: true});
});
// Extend timeout to allow for slow dynamic page rendering
casper.options.waitTimeout = 25000;
casper.waitForSelector("#gb", function() {
this.echo("login complete");
});
casper.thenOpen('https://(google app you want)', function() {
// Check it opened okay
});

why is casper.on('click' ...) not triggering?

Following is a minimal casper script that does a Google query. I've added casper.on('click' ...) prior to running the script, but it doesn't appear to get triggered.
What am I missing?
// File: google_click_test.js
"use strict";
var casper = require('casper').create();
casper.on('click', function(css) {
casper.echo('casper.on received click event ' + css);
});
// ================================================================
// agenda starts here
casper.start('https://google.com', function g01() {
casper.echo('seeking main page');
});
casper.then(function a02() {
casper.waitForSelector(
'form[action="/search"]',
function() {
casper.echo("found search form");
},
function() {
casper.echo("failed to find search form");
casper.exit();
});
});
casper.then(function a03() {
casper.fillSelectors('form[action="/search"]', {
'input[title="Google Search"]' : 'casperjs'
}, false);
});
casper.then(function a04() {
casper.click('form[action="/search"] input[name="btnG"]')
casper.echo('clicked search button');
});
casper.run();
Output:
Here's the output. I would expect to see casper.on received click event somewhere, but it seems that it didn't get triggered:
$ casperjs --ignore-ssl-errors=true --web-security=no google_click_test.js
seeking main page
found search form
clicked search button
$
Although your example runs fine for me using casperjs 1.1.0-beta3 and phantomjs 1.9.8, I've been having similar issues in the last few months with casperjs. Sadly it seems that the author has stopped maintaining the project. More information here:
https://github.com/n1k0/casperjs/issues/1299
I would suggest moving to a different testing framework. In my case I chose a combination of mocha + chai + nightmarejs. This gist is a good starting point:
https://gist.github.com/MikaelSoderstrom/4842a97ec399aae1e024

jQuery UI, AJAX and CKEditor - CKEditor only loads the first time

I'm having an issue similar to the issues reported both here and here, with a only a few changes in how my form data is loaded.
The solution provided in the second link seemingly resolves my issue, but removing the show/hide scaling effects should not be required in order for CKEditor to instantiate properly. There's bound to be a much better alternative to resolving this conflict.
My issue:
When I open my page, and click the edit button, a jQueryUI Dialog pops up, loads its data via ajax, and then I attempt to replace the textarea added to the dialog with a CKEditor instance. The first time I load the page, the dialog works without a hitch. I'm able to modify the data within the editor, save my form data, and get on with life. However, if I close the dialog, then open it again, the editor is no longer enabled. The buttons still have hover effects, and are clickable, but do nothing. The text area of the editor is disabled and set to "style: visibility: hidden; display: none;". Nearly all the information I can find regarding this issue is from many years ago, and the fixes involve using functions/techniques that no longer exist or are applicable.
Control Flow
I open the page containing a text link 'Edit Update', which calls my Javascript function openEditTicketUpdateDialog.
function openEditTicketUpdateDialog(tup_id, url)
{
simplePost(null, url, new Callback
(
function onSuccess(data)
{
$('#editticketupdatedialog').dialog('option', 'buttons',
[
{
text: 'Save Edits',
click: function()
{
// Save the Update info
var formData = {
tup_update: CKEDITOR.instances.tup_update_edit.getData(),
tup_internal: +$('#tup_internal_edit').is(":checked"),
tup_important: +$('#tup_important_edit').is(":checked")
};
simplePost(formData, data['submitRoute'], new Callback
(
function onSuccess(data)
{
$('#update-' + tup_id).html(data.input['tup_update']);
$('#updateflags-' + tup_id).html(data.flags);
$('#editticketupdatedialog').dialog('close');
},
function onFail(errors)
{
console.log(errors);
}
));
}
},
{
text: 'Cancel',
click: function()
{
$(this).dialog("close");
}
}
]);
$('#editticketupdatedialog').dialog('option', 'title', data['title']);
$('#editticketupdatedialog').html(data['view']);
$('#editticketupdatedialog').dialog('open');
destroyEditor('tup_update_edit');
console.log('CKEDITOR.status: ' + CKEDITOR.status);
createEditor('tup_update_edit');
},
function onFail(errors)
{
console.log(errors);
}
));
}
This function uses three helper functions, simplePost, destroyEditor and createEditor.
function simplePost(data, url, callback)
{
post(data, url, true, false, callback);
}
function createEditor(name)
{
console.log('Create editor: ' + name);
console.log('Current Instance: ');
console.log(CKEDITOR.instances.name);
if (CKEDITOR.status == 'loaded')
{
CKEDITOR.replace(name,
{
customConfig: '/js/ckeditor/custom/configurations/standard_config.js'
});
}
else
{
CKEDITOR.on('load', createEditor(name));
CKEDITOR.loadFullCore && CKEDITOR.loadFullCore();
}
console.log('After instance created: ');
var instance = CKEDITOR.instances.name;
console.log(instance);
}
function destroyEditor(name)
{
console.log('Destroy editor: ' + name);
console.log('Current Instance: ');
console.log(CKEDITOR.instances.name);
if (CKEDITOR.instances.name)
{
console.log('Instance exists - destroying...');
CKEDITOR.instances.name.destroy();
$('#' + name).off().remove();
}
console.log('After instance removed: ');
var instance = CKEDITOR.instances.name;
console.log(instance);
}
This method of creating a CKEditor instance was gathered from here. This method of destroying a CKEditor instance was gathered from here.
As you can see, openEditTicketUpdateDialog fires an AJAX call to my getEditUpdateForm function through Laravel routes.
public function getEditUpdateForm($tup_id, $update_number)
{
$update = Update::find($tup_id);
$data = [
'title' => 'Editing update #' . $update_number . ' of ticket #' . $update->tup_ticket,
'view' => View::make('tickets.ticketupdate-edit')
->with('update', $update)
->render(),
'submitRoute' => route('tickets/update/submit', $tup_id)
];
return Response::json(array('status' => 1, 'data' => $data));
}
From here, a status of 1 is returned, and the onSuccess function is called. I've attempted to add the create/delete calls before the $('#editticketupdatedialog').dialog('open'); call, but to no avail. I've also tried multiple other solutions that I've found surfacing, which involve hacked implementations of jQueryUI's Dialog functions and attributes: _allowInteraction and moveToTop. I was originally successful in resolving this issue the first time it arose by calling this function before doing a CKEDITOR.replace:
function enableCKEditorInDialog()
{
$.widget( "ui.dialog", $.ui.dialog, {
/**
* jQuery UI v1.11+ fix to accommodate CKEditor (and other iframed content) inside a dialog
* #see http://bugs.jqueryui.com/ticket/9087
* #see http://dev.ckeditor.com/ticket/10269
*/
_allowInteraction: function( event ) {
return this._super( event ) ||
// addresses general interaction issues with iframes inside a dialog
event.target.ownerDocument !== this.document[ 0 ] ||
// addresses interaction issues with CKEditor's dialog windows and iframe-based dropdowns in IE
!!$( event.target ).closest( ".cke_dialog, .cke_dialog_background_cover, .cke" ).length;
}
});
}
After updating to Laravel 5, and making a few other changes serverside, this fix no longer works. I have been successful in resolving my issue by removing the show/hide properties from my dialog. I would very much prefer not to have to remove these properties, as half the reasoning for having the dialog is the aesthetics of an animation. Here is my dialog initialization.
$('#editticketupdatedialog').dialog({
modal: true,
draggable: false,
minWidth: 722,
autoOpen: false,
show:
{
effect: "scale",
duration: 200
},
hide:
{
effect: "scale",
duration: 200
},
closeOnEscape: true
});
When I have these animations enabled, the first time I use the dialog, it works perfectly. The second time, I receive the error TypeError: this.getWindow(...).$ is undefined - ckeditor.js:83:18 in the JS console, which refers to this line:
function(a)
{
var d = this.getWindow().$.getComputedStyle(this.$,null);
return d ? d.getPropertyValue(a) : ""
}
Recap
My main goal here is to find a fix for this issue, without having to remove the jQueryUI Dialog animation. I am unsure whom to point fingers at, as I really can't determine if the issue lies in CKEditor, jQueryUI or my implementation.
I finally found a solution that works in my case. losnir updated the outdated solution to a post here, and adding the open function to my dialog initialization resolved my issue.
My initialization is as follows:
$('#editticketupdatedialog').dialog({
modal: true,
draggable: false,
minWidth: 722,
autoOpen: false,
show:
{
effect: "scale",
duration: 200
},
hide:
{
effect: "scale",
duration: 200
},
closeOnEscape: true,
open: function()
{
$(this).parent().promise().done(function ()
{
destroyEditor('tup_update_edit');
console.log('CKEDITOR.status: ' + CKEDITOR.status);
createEditor('tup_update_edit');
});
}
});

How to execute javascript from casperjs?

As a simple way to test if javascript is executing correctly from CasperJS, I'm trying to have an alert box in javascript pop up on the page. My end goal is running more complex javascript functions, but for right now I just wanted to start with something easy (i.e. if there is a native CasperJS alert box function, I don't want to use it because I want to get javascript working).
var casper = require("casper").create();
casper.start("http://www.google.com", function() {
});
casper.thenEvaluate(function() {
alert('hi');
});
casper.then(function(){
this.wait(500, function() {
this.echo("I've waited for 500 milliseconds");
});
this.capture('google.png', {
top: 0,
left: 0,
width: 1500,
height: 1000
});
});
casper.run(function() {
this.echo(this.getTitle()); // Google
this.exit();
});
When I run this script, I do visit http://www.google.com (as evidence from the screenshot) but no alert box shows up.
Does anyone know how to run javascript from CasperJS?
Thanks!
As to why alert('hi') won't be seen, this link gives a good explanation. Basically, casperjs/phantomjs is a headless browser and hence can't have a window or dialogs.
But inject your scripts into a remote page and execute them you can: here's how in Casperjs docs.
Here's your script slightly modified (note that jquery.js here is supposed to be in the local filesystem, in the same folder from which you issue your command to launch CasperJS in the console):
var casper = require("casper").create({
clientScripts: ["jquery.js"]
});
casper.start("http://www.google.com/ncr");
casper.then(function(){
this.evaluate(function() {
$("input[name='q']").val("CasperJS execute javascript on remote page");
});
});
casper.then(function(){
this.capture('google.png', {
top: 0,
left: 0,
width: 1500,
height: 1000
});
});
casper.run(function() {
this.echo(this.getTitle()); // Google
this.exit();
});

How to remove an event listener?

I use the Mozilla's Add-on Builder. I am looking for a way to remove an event listener in a contentScript. I use the port way to communicate between add-on script code and the content script code.
The problem is the callback on event "response" is called more than once. I want it to be called once and declared in the callback of the event show.
Can someone help me with that?
main.js code:
var Panel = require("panel").Panel;
var popup_panel = Panel({
width: 286,
height: 340,
contentURL: require("self").data.url("popup.html"),
allow: { script: true },
contentScriptWhen: "end",
contentScriptFile : [
require("self").data.url("test.js")
],
onShow: function(){
this.port.emit("show");
var pan = this;
this.port.on("hide", function(){pan.hide();});
}
});
var Widget = require("widget").Widget;
var widget = Widget({
id: "mozilla-icon",
label: "My Mozilla Widget",
contentURL: "http://www.mozilla.org/favicon.ico",
panel: popup_panel
});
popup_panel.port.on("get", function(){
popup_panel.port.emit("response");
});
Content script (test.js):
self.port.on("show", function(){
console.log("show");
function response(){
console.log("reponse called");
}
self.port.emit("get");
self.port.once("response", response);
self.port.removeListener("response", response);
});
full source code
Finally I found the problem. It is a bug in the add-on kit. In the file api-utils/lib/content/content-worker.js in the function removeListener the index is always -1.
The parameter given in the indexOf is the name of the event and it search a function. It is incorrect.
So to solve the problem I replace the line let index = listeners[name].indexOf(name); by let index = listeners[name].indexOf(callback);.
EDIT
The bug has been fixed. It will publish in the version 1.10 see here

Resources