With CasperJS or PhantomJS I want to:
1.) Visit 1 page and get Captcha image.
2.) Then decode Captcha on local server.
3.) Then submit the decoded Captcha results to 1. page.
4.) And get result (HTML).
A simple test assuming the Captcha code is 12345 to test that all values are entered and executed correctly like:
var casper = require('casper').create({ verbose: true, logLevel: "debug" });
var NUMBER_TO_CHECK = '356702087654321';
var DECODED_CAPTCHA = '12345';
casper.start('https://checkcoverage.apple.com/', function () {
this.sendKeys('input#serial-number', NUMBER_TO_CHECK);
this.sendKeys('input#captcha-input', DECODED_CAPTCHA);
this.mouseEvent('click', '.button-label', '50%', '50%');
this.wait(1000, function () {
this.echo('WAIT DONE');
});
});
casper.then(function (e) {
this.capture('logged-in.png');//print screen shot after click
});
casper.run();
This code snippet above gives Success result and says the given Captcha 12345 is incorrect which is true.
Now I need to modify this snippet so I can get the Captcha Image and process it on local server, and I have tried like:
var casper = require('casper').create({ verbose: true, logLevel: "debug" });
var NUMBER_TO_CHECK = '356702087654321';
casper.start('https://checkcoverage.apple.com/', function () {
this.sendKeys('input#serial-number', NUMBER_TO_CHECK);
// Get Encoded Captcha as Var
var captcha_encoded = casper.evaluate(function() {
return document.getElementsByClassName('captcha-image')[0].outerHTML;
});
// Post Encoded Captcha for decoding processing.
casper.then(function() { this.open('http://127.0.0.1/decode_captcha.php', {
method: 'post', data: { 'data': captcha_encoded } });
});
// Return Decoded Captch
casper.then(function() { var DECODED_CAPTCHA = this.getHTML('body');
this.echo(DECODED_CAPTCHA);
return(DECODED_CAPTCHA);
});
// How to Submit the Decoded Captcha result here ?
// Stuck here....
// ...
// this.sendKeys('input#captcha-input', DECODED_CAPTCHA);
// this.mouseEvent('click', '.button-label', '50%', '50%');
this.wait(1000, function () {
this.echo('WAIT DONE');
});
});
casper.then(function (e) {
this.capture('logged-in.png');//print screen shot after click
});
casper.run();
With this.echo(DECODED_CAPTCHA); I get the Decoded Captcha result in Console logs. But logged-in.png shows Screenshot from Local server, not from 1. page.
Question: How can I submit the var DECODED_CAPTCHA result to 1. page?
This is kind a delicate question. As per official documentation there is no support for parallel browsing
Is it possible to achieve parallel browsing using CasperJS?
And for your use case you need exactly that to keep your Captcha the same.
You can try examples posted in this group to see if it helps you.
Related
I am beginner in using protractor. I am just trying to do a page logon in a non angular page. I tried many solutions/workarounds, the last one was based on the following link:
http://agiletesters.com.br/topic/71/protractor-page-objects-typeerror-object-object-has-no-method-metodo/7
Basically, i have two files: conf.js and login.js (see codes further). I execute the following command (in my prompt, os windows):
protractor conf.js
// Code conf.js =>
exports.config = {
framework: 'jasmine',
specs: ['login.js'],
directConnect: true
}
// Code login.js =>
describe('Protractor Demo App', function() {
browser.driver.ignoreSynchronization = true;
var originalTimeout;
beforeEach(function() {
browser.driver.ignoreSynchronization = true;
originalTimeout = jasmine.DEFAULT_TIMEOUT_INTERVAL;
jasmine.DEFAULT_TIMEOUT_INTERVAL = 100000;
browser.driver.get('http://www.maisbolao.com.br');
});
afterEach(function() {
jasmine.DEFAULT_TIMEOUT_INTERVAL = originalTimeout;
});
it('should fill user and password and logins', function() {
var loginNameInputElm = browser.driver.findElement(by.xpath('//*[#id="Email"]'));
var passwordInputElm = browser.driver.findElement(by.xpath('//*[#id="Senha"]'));
var loginBtnElm = browser.driver.findElement(by.xpath('//*[#id="form-login"]'));
loginNameInputElm.sendKeys('login');//i omitted the login and password information just for security reasons
passwordInputElm.sendKeys('password');
loginBtnElm.click();
});
it('reach?', function() {
console.log("browser.driver.getCurrentUrl(): "+browser.driver.getCurrentUrl());
browser.driver.sleep(5000);
});
});
I also tried to use the addCookie function with the JSESSIONID cookie value, but the same message below was displayed and the same behaviour observed.
The message protractor gives to me is:
There are some issue in your code:
it('reach?', function() {
browser.sleep(5000);
// sleep 5s to wait page load after click login button for debug purpose
// should replace to browser.wait()
browser.getCurrentUrl().then(function(url){
console.log("browser.driver.getCurrentUrl(): " + url);
});
// browser.getCurrentUrl() return a promise
// you should consume the eventual value of promise inside then()
browser.sleep(5000);
});
According to your screenshot there is some warnings but your 2 tests passed (2 specs 0 failures).
If your test fails you will have red F instead of green dot.
My test passes without problems in safari while in chrome this particular bit doesn't seem to work:
it('should click the first source and get to the source preview page', function () {
var grid_icon = element(by.css('.fa-th'));
var sources = element.all(by.repeater('source in shownSources'));
sources.get(0).element(by.tagName('a')).click();
browser.pause();
// Check url
expect(browser.getCurrentUrl()).toContain('/source/');
});
After clicking on the hyperlink it should change to a url containing "/source/". This works perfectly fine in Safari but in Chrome it fails
My protractor config file:
exports.config = {
framework: 'jasmine2',
seleniumServerJar: '../node_modules/protractor/selenium/selenium-server-standalone-2.45.0.jar',
seleniumPort: 4444,
troubleshoot: false,
basePath: '../',
specs: ['protractor/***/**/*.js'],
baseUrl: 'http://localhost:9000',
capabilities: {
browserName: 'chrome'
},
onPrepare: function() {
browser.manage().window().maximize();
}
};
Edit: My initial problem seems to not occur anymore. But the test still behaves very strange. This bit here works perfectly fine in Safari:
it('should add all sources to the list and show the cart icon on the source in inventory', function () {
browser.get('/sources');
var ordersources = element.all(by.repeater('orderSource in orderSources'));
var sources = element.all(by.repeater('source in shownSources'));
sources.get(0).element(by.css('a')).click();
var add_to_cart_btn = element(by.binding('addBtnText'));
add_to_cart_btn.click();
browser.get('/sources');
sources.get(1).element(by.css('a')).click();
var add_to_cart_btn = element(by.binding('addBtnText'));
add_to_cart_btn.click();
browser.get('/sources');
browser.pause();
sources.get(2).element(by.css('a')).click();
var add_to_cart_btn = element(by.binding('addBtnText'));
add_to_cart_btn.click();
browser.get('/sources');
expect(ordersources.count()).toBe(3);
sources.each(function (field) {
var isInCart_symbol = field.element(by.css('.fa-cart-arrow-down'));
expect(isInCart_symbol.getAttribute('aria-hidden')).toBe('false');
});
});
In Chrome however the 'a' item isn't found the second time and the browser.sleep() is never executed and the next 'it' begins to run.
EDIT: I got it to work by using a another html element by the class attribute.
element.(by.css('.example'))
I'm assumming that when you say it fails, the expect is failing? Here are 3 possible things you could try.
// Wait Till Url Contains
function WaitTillUrlContains(text, time, errMessage){
if(typeof(time) ==='undefined') time = 5000;
browser.getCurrentUrl().then(function (currentUrl) {
browser.wait(function () {
return browser.getCurrentUrl().then(function (newUrl) {
var test = newUrl;
if( test.indexOf(text) >= 0){
// Found word
return true;
}
});
}, time , errMessage);
});
};
(1) add a wait before the expect.
it('should click the first source and get to the source preview page', function () {
var grid_icon = element(by.css('.fa-th'));
var sources = element.all(by.repeater('source in shownSources'));
sources.get(0).element(by.tagName('a')).click();
// Check url
WaitTillUrlContains("/source/", 5000, "✗ Failed to wait for page to load");
expect(browser.getCurrentUrl()).toContain('/source/');
});
(2) do a .then() function after the click
it('should click the first source and get to the source preview page', function () {
var grid_icon = element(by.css('.fa-th'));
var sources = element.all(by.repeater('source in shownSources'));
sources.get(0).element(by.tagName('a')).click().then(function(){
// Check url
WaitTillUrlContains("/source/", 5000, "✗ Failed to wait for page to load");
expect(browser.getCurrentUrl()).toContain('/source/');
});
});
(3) do a .then() function after getting the element then do the click
it('should click the first source and get to the source preview page', function () {
var grid_icon = element(by.css('.fa-th'));
var sources = element.all(by.repeater('source in shownSources'));
sources.get(0).element(by.tagName('a')).then(function(elem){
elem.click();
// Check url
WaitTillUrlContains("/source/", 5000, "✗ Failed to wait for page to load");
expect(browser.getCurrentUrl()).toContain('/source/');
});
});
The reason why your link isn't found the second time is because you reload the page with browser.get(). After the reload, the sources handle is lost and webdriver doesn't know what element to operate on.
You either need to declare sources variable again after the page reload or avoid reloading the page.
We have written several test cases with casperjs now. In comparison to other testing frameworks it works like charm. But there is one crucial part of our app, where we fail to write a suitable test case.
In our app we have integrated a type of autocomplete plugin which is called Geocomplete (http://ubilabs.github.io/geocomplete/) which makes it possible to fetch geodata from the Google Maps Api.
There is the following workflow. On the start page of our site there is a form with one single input field, which is used for the autocomplete functionality. There the user can enter the name of a specific city and Google returns the data. In the background a backbone model is populated with that data.
Here is the code of the testcase:
casper.test.begin('Test User Login Form', 4, function suite(test) {
casper.options.verbose = true;
casper.options.logLevel = 'debug';
var url = 'http://localhost:8889/';
var session;
casper.start(url);
casper.test.comment('Start Testing');
casper.waitFor(function check() {
return this.evaluate(function() {
return document.getElementById('page-wrap');
});
}, function then() {
casper.waitForSelector('#landingForm', function() {
this.echo('waiting');
});
});
// input is populated with a some letters
casper.then(function() {
casper.sendKeys('#landingForm input[name="location.name"]', 'Klag', {
keepFocus: true
});
});
// .pac-item container whill show the autocomplete suggestions
casper.then(function() {
casper.waitUntilVisible('.pac-item', function() {
// we have tried several methods here like mouse_over + click etc.
this.sendKeys('#landingForm input[name="location.name"]', casper.page.event.key.Down, {
keepFocus: true
});
this.sendKeys('#landingForm input[name="location.name"]', casper.page.event.key.Enter, {
keepFocus: true
});
// form is submitted
this.click('#landingForm > div > div > div > span > button');
});
});
casper.then(function() {
// wait until next page is visible
casper.waitUntilVisible('div.activity-pic', function() {
// get backbone session model
session = casper.evaluate(function() {
return require('model/session');
});
// test if model was populated correctly with the data from google
test.assertEquals(session.filterModel.attributes.location.name, 'Klagenfurt', 'Name equals expected values.');
});
});
casper.run(function() {
casper.test.comment('Ending Testing');
test.done();
});
});
The test
test.assertEquals(session.filterModel.attributes.location.name, 'Klagenfurt', 'Name equals expected values.');
always fails and tells me that the name-attribute is undefined. The input field is filled in correclty with the name of the city. We have used the evaluate-method in other testcases to check the values and attributes of our models too, there it worked.
Does anybody has the same problem?
There are two possible approaches to this. Based on this comment you can add an event listener through evaluate and waitFor its execution (here as a reusable casper function):
casper.waitForGeocodeResult = function(){
this.thenEvaluate(function(){
// TODO: initialize $input
$input.bind('geocode:result', function(event, result) {
window._myGeocodeResultArrived = true;
}
});
this.waitFor(function check(){
return this.evaluate(function(){
return "_myGeocodeResultArrived" in window && window._myGeocodeResultArrived;
});
});
this.thenEvaluate(function(){
window._myGeocodeResultArrived = false;
});
};
You may call it like this:
casper.waitForGeocodeResult();
casper.then(function() {
// get backbone session model
session = casper.evaluate(function() {
return require('model/session');
});
// test if model was populated correctly with the data from google
test.assertEquals(session.filterModel.attributes.location.name, 'Klagenfurt', 'Name equals expected values.');
});
If this doesn't work for you may directly check the session model repeatedly (again as a reusable casper function):
casper.getBackboneModel = function(name, keyFunc){
var oldRetry;
this.then(function(){
oldRetry = this.options.retryTimeout;
// set retry timeout a little higher in case the require is a time intensive function
this.options.retryTimeout = 500;
});
this.waitFor(function check(){
var model = casper.evaluate(function(modelName){
return require(modelName);
}, name);
return keyFunc(model);
}, null, function onTimeout(){
this.echo("warning: geocomplete was unsuccessful");
});
this.then(function(){
// reset timeout
this.options.retryTimeout = oldRetry;
});
};
Call it like this:
casper.getBackboneModel(function(session){
try {
var temp = session.filterModel.attributes.location.name;
return "name" in session.filterModel.attributes.location;
} catch(e){
return false;
}
});
casper.then(function() {
// get backbone session model
session = casper.evaluate(function() {
return require('model/session');
});
// test if model was populated correctly with the data from google
test.assertEquals(session.filterModel.attributes.location.name, 'Klagenfurt', 'Name equals expected values.');
});
I'm trying to test different parts of a "mostly" single page application. I'd like to split the tests up, but I really only want to load the page once and then have the tests go through and click the links etc.
Here's my code:
PRE.js
var port = require('system').env.PORT
var tester;
casper.options.viewportSize = {width: 1024, height: 768};
casper.test.begin('Test login', function suite(test) {
var done = false;
casper.on("page.error", function(msg, trace) {
this.echo("Error: " + msg, "ERROR");
this.echo("file: " + trace[0].file, "WARNING");
this.echo("line: " + trace[0].line, "WARNING");
this.echo("function: " + trace[0]["function"], "WARNING");
});
casper.on('remote.message', function(message) {
this.echo('remote message caught: ' + message);
if (message == "done") {
done = true;
}
});
casper.start('http://localhost:' + port, function() {
// Verify that the main menu links are present.
test.assertExists('input[name=username]');
// 10 articles should be listed.
test.assertElementCount('input', 3);
casper.fill("form", {
"username": "username",
"password": "my password goes right here you cant have it"
}, true);
casper.then(function() {
casper.waitFor(function(){
return done;
}, function(){
tester = casper.evaluate(function(){
return tester;
});
test.assert("undefined" != typeof tester);
test.assert(Object.keys(tester).length > 0);
});
});
});
casper.run(function() {
test.done();
});
});
and then I have a second file (and there will be lots more like this):
TEST.js
casper.test.assert(true);
casper.capture('.screenshot.png');
casper.test.done();
I'm hoping to get a screenshot of the browser session from pre.js.
I run it from a specialized program that starts up my program, but in essence it runs:
casperjs test casper_tests --pre=pre.js
casper_tests holds both files above
My Question:
What's the right way to do this? No screenshot is being taken, and perhaps more important (though I haven't tried it yet) I want to be able to click things inside and verify that other pieces are working. The screenshot just verifies that i'm in the right neighborhood.
This will not be easily possible and potentially dangerous. Every action that you do, would need to be reversed to not break the other tests. If you later decide that writing tests in a modular manner is a good thing, you will have a headache writing your tests.
PRE.js will be your start script which you modify to execute your tests in between. In the following fully working example you see how you can schedule multiple test cases for one execution of casper. This is bad, because the canvas test case depends on the proper back execution of the link test case.
casper.start('http://example.com');
casper.then(function() {
this.test.begin("link", function(test){
var url = casper.getCurrentUrl();
test.assertExists("a");
casper.click("a");
casper.then(function(){
test.assert(this.getCurrentUrl() !== url);
this.back(); // this is bad
test.done();
});
});
this.test.begin("canvas", function(test){
test.assertNotExists("canvas");
test.done();
});
});
casper.run();
Of course you can open the root again for the new test case, but then you have the same problem as with your initial code.
var url = 'http://example.com';
casper.start();
casper.thenOpen(url, function() {
this.test.begin("link", function(test){
var url = casper.getCurrentUrl();
test.assertExists("a");
casper.click("a");
casper.then(function(){
test.assert(this.getCurrentUrl() !== url);
test.done();
});
});
});
casper.thenOpen(url, function() {
this.test.begin("canvas", function(test){
test.assertNotExists("canvas");
test.done();
});
});
casper.run();
Now the test cases don't depend on each other, but you also load the page multiple times.
If you need some initial actions for every test case then the PRE.js is not the right place for that.
Create include.js and put the following code there:
function login(suite, username, password){
username = username || "defaultUsername";
password = password || "defaultPassword";
casper.test.begin('Test login', function suite(test) {
var done = false;
// event handlers
casper.start('http://localhost:' + port, function() {
// login if the session expired or it is the first run
if (!loggedIn) {
// login
}
// wait
});
casper.then(function(){
suite.call(casper, test);
});
casper.run(function() {
test.done();
});
});
}
Then you can run it as casperjs test casper_tests --includes=include.js with test files like
login(function(test){
this.click("#something");
this.waitForSelector(".somethingChanged");
this.then(function(){
test.assertExists(".somethingElseAlsoHappened");
});
});
Of course you can have different login functions (with different names) or more lightweight ones.
Building on the previous snippets, you can make a start script and load the test files yourself. Then you have all the flexibility you need to do this.
include.js:
function login(testScript, username, password, next){
// event handlers
casper.start('http://localhost:' + port, function() {
// login if the session expired or it is the first run
// waiting
});
testScript.forEach(function(case){
casper.thenOpen(case.name, function(){
this.test.begin(function suite(test){
case.func.call(casper, test);
casper.then(function(){
test.done();
});
});
});
});
casper.run(next);
}
start.js:
// pass the test file folder to the script and read it with sys.args
// fs.list(path) all files in that path and iterate over them
var filesContents = files.map(function(filename){
return require(filename).testcases;
});
var end = null;
// stack the test cases into the `run` callback of the previous execution
filesContents.forEach(function(case){
var newEnd = end;
var newFunc = function(){ login(case, u, p, newEnd) };
end = newFunc;
});
end(); // run the stack in reverse
each test file would look like this:
exports.testcases = [
{
name: "sometest",
func: function(test){
test.assert(true)
this.echo(this.getCurrenturl());
}
},
{
name: "sometest2",
func: function(test){
test.assert(true)
this.echo(this.getCurrenturl());
}
},
];
This is just a suggestion.
My code is working fine, but I do not like at all.
I would like to split one file into two files, one containing webServices and another one with a controller.
My file do something like this:
File: Validacion.js (controller)
// Load next view
var MainView = Alloy.createController('index').getView('tabGroup');
// this a function call when I click a button "validar" on Validación View.
function btnClick(){
var url = 'www.cocoloco.com/whatever';
var webService = Ti.Network.createHTTPClient({
onload: function(e){
// open new view
MainView.open();
// close actual view
$.tabValidacion.close();
$.tabValidacion = null;
},
onerror: function(e){
alert('onerror: ' + e.error);
},
timeout: 5000
});
webService.open('POST', url);
webService.send();
}
But I would like to do something like this below (divided in two files: webServices.js -library- and validation.js -controller-).
The problem is that I always have the message "error" because I pass throught "success = webServices.protocol();" but as far as it is "asynchronous" it doesn't stop and goes to following line of code without having server answer yet.
File: webServices.js (library)
exports.protocol = function(){
var url = 'www.cocoloco.com/whatever';
var webService = Ti.Network.createHTTPClient({
onload: function(e){
// on sucess exit with true
return(true);
},
onerror: function(e){
alert('onerror: ' + e.error);
// on sucess exit with false
return(false);
},
timeout: 5000
});
webService.open('POST', url);
webService.send();
}
File: Validacion.js (controller)
// Load next view
var MainView = Alloy.createController('index').getView('tabGroup');
function btnClick(){
var webServices = require('webServices');
var success = webServices.protocol();
if(success){
// open new view
MainView.open();
// close actual view
$.tabValidacion.close();
$.tabValidacion = null;
}else{
alert('error');
}
}
I have thought about two possible options:
Using promises.
Fire a new event on "success" and use that event run another callback function (in this function I open the new view and close the previous one).
I do not know how difficult is this as far as the event is one file (library) and the callback function in another one (controller)
I have never used any of these solutions, so I do not know how "good" they are.
Any suggestion?
The callback approach works fine in most cases. Just pass the function as a parameter, you can return an object containing anything from a success message to responseText and status.
webServices.js
exports.protocol = function(callback) {
var url = 'www.cocoloco.com/whatever';
var webService = Ti.Network.createHTTPClient({
onload: function(e){
// on success call callback
callback({ success: true });
},
onerror: function(e){
// on error call callback
callback({ success: false });
},
timeout: 5000
});
webService.open('POST', url);
webService.send();
}
Validacion.js
function btnClick(){
var webServices = require('webServices');
webServices.protocol(function(e) {
if(e.success){
// open new view
MainView.open();
// close actual view
$.tabValidacion.close();
$.tabValidacion = null;
} else {
alert('error');
}
});
}