Is there anyone who have already implemented the famous "Page Object Pattern" with casperjs, it's very useful for test maintainability in the long term ?
It's very very cool to use that when you have to separate the mechanics and the purpose of your tests. It become more pleasurable to write your tests this way.
There are some examples with ruby and selenium:
http://blog.josephwilk.net/cucumber/page-object-pattern.html
https://code.google.com/p/selenium/wiki/PageObjects
Here is an exemple of Page object pattern with CasperJS to test a login feature. The page object is in a file called LoginPage.js :
function LoginPage() {
this.startOnLoginPage = function () {
casper.echo("base url is : " + casper.cli.options.baseUrl);
casper.start(casper.cli.options.baseUrl + '/login');
};
this.checkPage = function () {
casper.then(function () {
casper.test.assertUrlMatch('login', 'Is on login page');
casper.test.assertExists('form[name="f"]', 'Login page form has been found');
});
};
this.fillForm = function (username, password) {
casper.then(function () {
this.fill('form[name="f"]', {
'j_username': username,
'j_password': password
}, false);
});
};
this.submitForm = function () {
casper.then(function () {
this.click('form[name="f"] button[type="submit"]', 'Login submit button clicked');
});
};
}
Then you can use this Page Object on several tests. For example :
phantom.page.injectJs('LoginPage.js');
var loginPage = new LoginPage();
casper.test.begin('Should login', function (test) {
loginPage.startOnLoginPage();
loginPage.checkPage();
loginPage.fillForm('scott', 'rochester');
loginPage.submitForm();
});
For more details and a complete example : http://jsebfranck.blogspot.fr/2014/03/page-object-pattern-with-casperjs.html
Using the example in your second link, you can convert this to a Javascript class-like object and encapsulate in its own module:
var LoginPage = function(casper) {
this.casper = casper;
};
LoginPage.prototype.typeUsername = function(username) {
this.evaluate(function() {
__utils__.findOne('input.username').value = username;
});
};
exports = LoginPage;
and then use it in your test:
var LoginPage = require("./LoginPage.js");
var login = new LoginPage(this);
login.typeUsername("geoff");
Related
I am trying to integrate google-recaptcha but no success.
Getting error
feedback.js:39 Uncaught TypeError: grecaptcha.render is not a function
main.js
'googlerecaptcha':'https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit',
define(['ojs/ojcore', 'knockout', 'jquery', 'appController', 'ckeditor', 'googlerecaptcha', 'ojs/ojlabel',
'ojs/ojknockout', 'ojs/ojinputtext', 'ojs/ojformlayout'],
function (oj, ko, $, app, ckeditor, grecaptcha) {
/**
* The view model for the main content view template
*/
function feedbackViewModel() {
var self = this;
// For small screens: labels on top
// For medium screens and up: labels inline
this.labelEdge = ko.computed(function () {
return app.smScreen ? "top" : "start";
}, this);
onloadCallback = function (a) {
grecaptcha.render('submit', {
'sitekey': 'YOUR_API_KEY',
'callback': self.onSubmit
}, true);
};
this.handleActivated = function (info) {
};
self.onSubmit = function (token) {
console.info("google recatpcha onSubmit", token)
//do validation/application code using token
var data = {secret: grecaptcha, response: recaptchaToken};
$.post({
url: "https://www.google.com/recaptcha/api/siteverify",
form: data
}).then(function (e) {
//recaptcha service called...check result
var resp = JSON.parse(e);
if (resp.success == false) {
console.info("recaptcha token outcome is false")
} else {
console.info("recaptcha token validated")
}
});
};
}
return feedbackViewModel;
});
Do you have a mapping for 'googlerecaptcha' in src/js/path_mapping.json? If I go to https://www.google.com/recaptcha/api.js?onload=onloadCallback&render=explicit, I do not see that it is returning any valid object. So most likely 'grecaptcha' variable is undefined.
reCaptcha + RequireJS
Looks like reCaptcha is a function that has to be executed vs an object that can be interacted with directly. So you may need a different approach, something mentioned in this thread.
I've just introduced SSL to my MVC website. I made the whole default AccountContorller use it. It works fine unless I'm currently on http page and try to log on with ajax (the logon action is vredirected to httpS). This popup logon window doesn't even show up.
For the controller a used a custom attribute:
public class RequireSSL : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.ActionDescriptor.IsDefined(typeof(NoSSL), true) ||
filterContext.ActionDescriptor.IsDefined(typeof(OptionalSSL), true))
{
base.OnActionExecuting(filterContext);
return;
}
HttpRequestBase req = filterContext.HttpContext.Request;
HttpResponseBase res = filterContext.HttpContext.Response;
//Check if we're secure or not and if we're on the local box
if (!req.IsSecureConnection && (!req.IsLocal || Properties.Settings.Default.UseSSLForLocalRequests))
{
var builder = new UriBuilder(req.Url)
{
Scheme = Uri.UriSchemeHttps,
Port = Properties.Settings.Default.HttpsPort,
};
res.Redirect(builder.Uri.ToString());
}
base.OnActionExecuting(filterContext);
}
}
How can I make it work?
EDIT
The whole rest was generated by MVC. (Project with built in authentications)
That's the link.
#Html.ActionLink(#Labels.LogOn, "LogOn", "Account", routeValues: null, htmlAttributes: new { id = "logonLink", data_dialog_title = "Identification" })
JS somehow hooks into that link and performs ajax logon . Probably with this code: (JajaxLogin.js - also out of the box)
/// <reference path="jquery-1.6.2.js" />
/// <reference path="jquery.validate.js" />
$(function () {
// Cache for dialogs
var dialogs = {};
var getValidationSummaryErrors = function ($form) {
// We verify if we created it beforehand
var errorSummary = $form.data('validation-summary-errors');
if (!errorSummary) {
errorSummary = $('<div class="validation-summary-errors"><span>Please correct the errors and try again.</span><ul></ul></div>')
.insertBefore($form);
// Remember that we created it
$form.data('validation-summary-errors', errorSummary);
}
return errorSummary;
};
var formSubmitHandler = function (e) {
var $form = $(this);
// We check if jQuery.validator exists on the form
if (!$form.valid || $form.valid()) {
$.post($form.attr('action'), $form.serializeArray())
.done(function (json) {
json = json || {};
// In case of success, we redirect to the provided URL or the same page.
if (json.success) {
location = json.redirect || location.href;
} else if (json.errors) {
var errorSummary = getValidationSummaryErrors($form);
var items = $.map(json.errors, function (error) {
return '<li>' + error + '</li>';
}).join('');
var ul = errorSummary
.find('ul')
.empty()
.append(items);
}
});
}
// Prevent the normal behavior since we opened the dialog
e.preventDefault();
};
var loadAndShowDialog = function (id, link, url) {
var separator = url.indexOf('?') >= 0 ? '&' : '?';
// Save an empty jQuery in our cache for now.
dialogs[id] = $();
// Load the dialog with the content=1 QueryString in order to get a PartialView
$.get(url + separator + 'content=1')
.done(function (content) {
dialogs[id] = $('<div class="modal-popup">' + content + '</div>')
.hide() // Hide the dialog for now so we prevent flicker
.appendTo(document.body)
.filter('div') // Filter for the div tag only, script tags could surface
.dialog({ // Create the jQuery UI dialog
title: link.data('dialog-title'),
modal: true,
resizable: true,
draggable: true,
width: link.data('dialog-width') || 300
})
.find('form') // Attach logic on forms
.submit(formSubmitHandler)
.end();
});
};
// List of link ids to have an ajax dialog
var links = ['logonLink', 'registerLink'];
$.each(links, function (i, id) {
$('#' + id).click(function (e) {
var link = $(this),
url = link.attr('href');
if (!dialogs[id]) {
loadAndShowDialog(id, link, url);
} else {
dialogs[id].dialog('open');
}
// Prevent the normal behavior since we use a dialog
e.preventDefault();
});
});
});
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.
My application has been implemeted using MVC 3, .net.
I am trying to generate an excel file at the click of a button.
The call to the controller action is made using Ajax.
My main problem is: During the file generation i am trying to display an image on the screen to let the user know of the ingoing operation. I can very well display the image but i cannot hide it after the operation is completed. The codei am using is :
Javascript code:
$("input.DownloadExcelReport").click(function (e) {
e.preventDefault();
var parameter = -- code to fetch parameter value;
var outputViewUrl = (the url is created here);
showLoading(); -- This function displays the image
window.location.href = outputViewUrl;
});
Controller Action code:
public ActionResult DownExcelReportForAssortment(Guid parameter)
{
try
{
//the contents for the file generation are fetched here..
// Write contents to excel file
if (memoryStream != null)
{
var documentName = "Report.xls";
byte[] byteArrary = memoryStream.ToArray();
return File(byteArrary, "application/vnd.ms-excel", documentName);
}
}
catch (Exception ex)
{
LogManager.LogException(ex);
}
}
I do not return a Json result to the calling javascript method where i can write the code to hide the image.
I am returning a file which can be saved by the user and the action is completed.
Can somone please suggect/help me of how can i hide the image once the file generation operation is complete?
Appreciate the help...
You may checkout the following article and put this into action. So we start by defining a controller:
public class HomeController : Controller
{
public ActionResult Index()
{
return View();
}
public ActionResult DownExcelReportForAssortment(Guid parameter, string tokenId)
{
// Simulate some heavy work to fetch the report
Thread.Sleep(5000);
// we fake it
byte[] byteArray = System.IO.File.ReadAllBytes(#"c:\test.xls");
var cookie = new HttpCookie("fileDownloadToken", tokenId);
Response.AppendCookie(cookie);
return File(byteArray, "application/vnd.ms-excel", "report.xls");
}
}
and in the view:
#Html.ActionLink(
"download report",
"DownExcelReportForAssortment",
"Home",
new { parameter = Guid.NewGuid(), tokenId = "__token__" },
new { #class = "download" }
)
Now the last step is to include the jquery.cookie plugin:
<script type="text/javascript" src="#Url.Content("~/scripts/jquery.cookie.js")"></script>
and write a script to subscribe to the click event of the anchor and track the download progress:
$(function () {
var fileDownloadCheckTimer;
$('.download').click(function () {
var token = new Date().getTime();
$(this).attr('href', function () {
return this.href.replace('__token__', token);
});
// Show the download spinner
$('body').append('<span id="progress">Downloading ...</span>');
// Start polling for the cookie
fileDownloadCheckTimer = window.setInterval(function () {
var cookieValue = $.cookie('fileDownloadToken');
if (cookieValue == token) {
window.clearInterval(fileDownloadCheckTimer);
$.cookie('fileDownloadToken', null);
// Hide the download spinner
$('#progress').remove();
}
}, 1000);
});
});
ok so im having a hard time hiding some layout sections (divs in my layout page and im using mvc3).
I have this js fragment which is basically the main logic:
$('.contentExpand').bind('click', function () {
$.cookie('right_container_visible', "false");
});
//Cookies Functions========================================================
//Cookie for showing the right container
if ($.cookie('right_container_visible') === 'false') {
if ($('#RightContainer:visible')) {
$('#RightContainer').hide();
}
$.cookie('right_container_visible', null);
} else {
if ($('#RightContainer:hidden')) {
$('#RightContainer').show();
}
}
as you can see, im hidding the container whenever i click into some links that have a specific css. This seems to work fine for simple tests. But when i start testing it like
.contentExpand click --> detail button click --> .contentExpand click --> [here unexpected issue: the line $.cookie('right_container_visible', null); is read but it doesnt set the vaule to null as if its ignoring it]
Im trying to understand whats the right logic to implement this. Anyone knows how i can solve this?
The simpliest solution is to create variable outside delegate of bind.
For example:
var rightContVisibility = $.cookie('right_container_visible');
$('.contentExpand').bind('click', function () {
$.cookie('right_container_visible', "false");
rightContVisibility = "false";
});
if (rightContVisibility === 'false') {
...
}
The best thing that worked for me was to create an event that can catch the resize of an element. I got this from another post but I dont remember which one. Anyway here is the code for the event:
//Event to catch rezising============================================================================
(function () {
var interval;
jQuery.event.special.contentchange = {
setup: function () {
var self = this,
$this = $(this),
$originalContent = $this.text();
interval = setInterval(function () {
if ($originalContent != $this.text()) {
$originalContent = $this.text();
jQuery.event.handle.call(self, { type: 'contentchange' });
}
}, 100);
},
teardown: function () {
clearInterval(interval);
}
};
})();
//=========================================================================================
//Function to resize the right container============================================================
(function ($) {
$.fn.fixRightContainer = function () {
this.each(function () {
var width = $(this).width();
var parentWidth = $(this).offsetParent().width();
var percent = Math.round(100 * width / parentWidth);
if (percent > 62) {
$('#RightContainer').remove();
}
});
};
})(jQuery);
//===================================================================================================