Getting individual ElementFinders from an ElementArrayFinder in a Page Object - protractor

In a separate Page Object file (not in the actual file with tests) I'm trying to do something like:
this.item0 = element.all(by.repeater('menu items')).get(0);
This won't work because the code is executed before the tests are run. I haven't found another way of doing this except to call get() in the test file (which I don't want to do). Is there a way to do this in the Page Object file?

This behavior often confuses people who are trying to write page objects.
Your locator will not be executed until you call a function on the elementFinder or the elementArrayFinder (see https://github.com/angular/protractor/blob/master/docs/api.md).
I usually use this pattern:
// Page object
MyView = function() {
// This will not find elements until you call count(), get(), first(), etc.
this.itemList = element.all(by.repeater('menu items'));
};
module.exports = new MyView();
// Test
// Require the page object at the top of the test file.
var myView = require('./my-view.js');
// Use the page object in the test.
it('should get first element', function() {
myView.itemList.get(0).then(function(webElement) {
})
});

The following is from source code for ElementArrayFinder.get
/**
* Get an element within the ElementArrayFinder by index. The index starts at 0.
* This does not actually retrieve the underlying element.
*
ElementArrayFinder.prototype.get = function...
So Apparently you should be able to call it from within the page object even before the elements are loaded.

yay, i'm not the only one using page objects ;)
i did the following:
var MyPage = function () {
this.item0 = element.all(by.repeater('menu items')).get(0);
}
in my tests:
describe('MyPage:', function () {
var myPage = new MyPage();
// after this line navigate your browser to your page
// then you can call myPage.item0, myPage.whatEver
}

Related

Are getter functions necessary for Protractor element locators

When using PageObjects for Protractor e2e tests, should you use getter functions for the element locator variables instead of having variables?
example:
public loginButton: ElementFinder = $('#login-submit');
public loginUsername: ElementFinder = $('#login-username');
Or should you use a getter function in the Page Object like this:
public get loginButton(): ElementFinder {
return $('#login-submit');
}
public get loginUsername(): ElementFinder {
return $('#login-username');
}
Is one approach better than another?
No getters needed, since protractor ElementFinder and ElementArrayFinder objects are lazy - no any searching for this element will be done until you will try to call some methods on them. Actually thats also a reason why you don't need to use await for protractor element search methods:
const button = $('button') // no await needed, no getter needed
console.log('Element is not yet tried to be searched on the page')
await button.click() // now we are sending 2 commands one by one - find element and second - do a click on found element.
http://www.protractortest.org/#/api?view=ElementFinder
ElementFinder can be used to build a chain of locators that is used to find an element. An ElementFinder does not actually attempt to find the element until an action is called, which means they can be set up in helper files before the page is available.
It's fine to use either but if you're going to transform or do something else to your element, then the getter function would be better in my opinion since that's the reason why we utilize mutators in the first place.
I'm not really sure which method is more convenient, if there is one.
I've been using protractor for a bit longer than 1 year now, and I always stored locators in variables.
What I'd normally do is:
const button = element(by.buttonText('Button'));
Then I'd create a function for interacting with the element:
const EC = protractor.ExpectedConditions;
const clickElement = async (element) => {
await browser.wait(EC.elementToBeClickable(element));
await element.click();
};
Finally, use it:
await clickElement(button);
Of course I store the locators and functions in a page object, and invoking/calling them in the spec file. It's been working great so far.

Get newly created id of a record before redirecting page

I would like to retrieve the id of a newly created record using javascript when I click on save button and just before redirecting page.
Do you have any idea please ?
Thank you !
One way to do this in Sugar 7 would be by overriding the CreateView.
Here an example of a CustomCreateView that outputs the new id in an alert-message after a new Account was successfully created, but before Sugar gets to react to the created record.
custom/modules/Accounts/clients/base/views/create/create.js:
({
extendsFrom: 'CreateView',
// This initialize function override does nothing except log to console,
// so that you can see that your custom view has been loaded.
// You can remove this function entirely. Sugar will default to CreateView's initialize then.
initialize: function(options) {
this._super('initialize', [options]);
console.log('Custom create view initialized.');
},
// saveModel is the function used to save the new record, let's override it.
// Parameters 'success' and 'error' are functions/callbacks.
// (based on clients/base/views/create/create.js)
saveModel: function(success, error) {
// Let's inject our own code into the success callback.
var custom_success = function() {
// Execute our custom code and forward all callback arguments, in case you want to use them.
this.customCodeOnCreate(arguments)
// Execute the original callback (which will show the message and redirect etc.)
success(arguments);
};
// Make sure that the "this" variable will be set to _this_ view when our custom function is called via callback.
custom_success = _.bind(custom_success , this);
// Let's call the original saveModel with our custom callback.
this._super('saveModel', [custom_success, error]);
},
// our custom code
customCodeOnCreate: function() {
console.log('customCodeOnCreate() called with these arguments:', arguments);
// Retrieve the id of the model.
var new_id = this.model.get('id');
// do something with id
if (!_.isEmpty(new_id)) {
alert('new id: ' + new_id);
}
}
})
I tested this with the Accounts module of Sugar 7.7.2.1, but it should be possible to implement this for all other sidecar modules within Sugar.
However, this will not work for modules in backward-compatibility mode (those with #bwc in their URL).
Note: If the module in question already has its own Base<ModuleName>CreateView, you probably should extend from <ModuleName>CreateView (no Base) instead of from the default CreateView.
Be aware that this code has a small chance of breaking during Sugar upgrades, e.g. if the default CreateView code receives changes in the saveModel function definition.
Also, if you want to do some further reading on extending views, there is an SugarCRM dev blog post about this topic: https://developer.sugarcrm.com/2014/05/28/extending-view-javascript-in-sugarcrm-7/
I resolved this by using logic hook (after save), for your information, I am using Sugar 6.5 no matter the version of suitecrm.
Thank you !

Play Framework Search Bar

I'm trying to make a search bar with only one variable - the search input. I'm sure there's a fairly simple way to do this, but everything that I've found about getting input from the DOM (the views file) has been about using a Form and getting multiple variables. Is there a simpler way to do this if it's just a single variable?
I have a function in my Applications
def singleElement = Action { implicit request =>
val databaseSupport = new InteractWithDatabase(comm, db)
val put = Future {
while (true) {
val data = databaseSupport.getFromDatabase()
if (data.nonEmpty) {
comm.communicator ! data.head
}
}
}
Ok(views.html.singleElement)
}
I want to take some input from the user on the page singleElement and pass it into getFromDatabase which calls a MySQL query. How do I do this?
You can use restful and do something like this
routs file
GET /content/search/:search controllers.ContentController.search(search:String)
and in controller:
public Result search(String saerch) {}

Restangular extendModel on new object

Restangular offers a feature, extendModel, which lets you add functionality onto objects returned from the server. Is there any way to get these methods added to an empty / new model, that hasn't yet been saved to the server?
I wanted to do the same thing but didn't find an example. Here's how I ended up doing it:
models.factory('User', function(Restangular) {
var route = 'users';
var init = {a:1, b:2}; // custom User properties
Restangular.extendModel(route, function(model) {
// User functions
model.myfunc = function() {...}
return model;
});
var User = Restangular.all(route);
User.create = function(obj) {
// init provides default values which will be overridden by obj
return Restangular.restangularizeElement(null, _.merge({}, init, obj), route);
}
return User;
}
Some things to be aware of:
Use a function like _.merge() instead of angular.extend() because it clones the init variable rather than simply assigning its properties.
There is a known issue with Restangular 1.x that causes the Element's bound data to not be updated when you modify its properties (see #367 and related). The workaround is to call restangularizeElement() again before calling save(). However this call will always set fromServer to false which causes a POST to be sent so I wrote a wrapper function that checks if id is non-null and sets fromServer to true.

Can I divide my tests into separate specs and then call them from another or is it better to use helper functions?

Just got started with Protractor for E2E testing and I am having a bit of trouble with the test case structure.
Not sure if can I divide my tests into separate specs and then call them from another or how can I make nice helper functions to handle this.
I am finding elements by a repeater and then I would like to make tests for each of the operation for each of the element in the repeater. Sort of like this:
describe('tasty', function () {
'use strict';
var ptor;
beforeEach(function () {
ptor = protractor.getInstance();
ptor.get('http://localhost:8000/');
});
it('Should sample three tasty fruits of every kind on my shopping list.', function () {
ptor.findElement(protractor.By.className('fruitstore')).click();
var fruitshelves = ptor.findElements(protractor.By.repeater('fruit in fruits').column('header'));
fruitshelves.then(function(arr) {
for (var i=0;i<arr.length; i++) {
// Pick up three fruits of this kind from the shelf and put in shopping cart
// Should be listed on my shopping list
// Open the wallet
// Should have money
// Pay for the fruits and put it in your shopping bag
// Should be able to complete the transaction
// For each one of the fruits in your shopping bag
// Take a bite
// Should be tasty
}
});
});
});
Based on the #langliman answer, I've managed to achieve the desired behaviour.
Note login.spec.js and Login.page.js should be located in the same folder.
Login.page.js file:
var LoginPage = function (ptor) {
//following PageObject pattern define the functions here.
}
module.exports.getLoginPage = function (ptor) {
return new LoginPage(ptor);
};
login.spec.js file:
(function () {
'use strict';
describe('login page', function () {
var ptor = protractor.getInstance();
var loginPageBuilder = require('./Login.page.js');
var loginPage = loginPageBuilder.getLoginPage(ptor);
it('should login as admin', function () {
loginPage.visit();
loginPage.enterUsername('user');
loginPage.enterPassword('password');
loginPage.login();
});
});
}());
I came to this question looking for a way to have helper functions shared between spec files in Protractor. In case others are looking for the same, turns out since Protractor is just running in Node, all you need to do is var helpers = require('./your-helper-file').
In case you want shared setup and before/after functions as well as helper methods, one solution is to require the tests from your spec helper instead of requiring your spec helper from the tests.
conf.js
exports.config = {
seleniumAddress: 'http://localhost:4444/wd/hub',
specs: ['e2e/spec.js']
}
e2e/spec.js
var chai = require('chai'),
homepage = require('./homepage.js'),
signin = require('./signin.js');
chai.should()
browser.baseUrl = 'http://localhost:3000'
homepage.test()
signin.test()
e2e/homepage.js
exports.test = function() {
describe('homepage', function() {
it('should have the right title', function() {
browser.get('/')
browser.getTitle().then(function(title){
title.should.eq('Home')
})
});
});
}
e2e/signin.js
exports.test = function() {
describe('signin', function() {
it('should have the right title', function() {
browser.get('/signin')
browser.getTitle().then(function(title){
title.should.eq('Sign in')
})
});
});
}
I'm looking at the same thing myself, and to some extent I had hoped that you would have an answer for me on this question. :-)
Having said that, it appears that protractor is new enough that nobody really knows the answer, and I guess that makes my answer as good as the next persons.
Firstly, I'm using the page object notation that is described on the protractor getting started page, towards the bottom: https://github.com/angular/protractor/blob/master/docs/getting-started.md
This gives an element of modularity, my view here is that I end up with a set of classes, one per page, that abstract away some of the detail. So, for example, I might have a "foo" class, which includes in it abstractions like "foo.get" and "foo.validate(id, name, otherData)". This would be a way to pull out repeated code.
The bit that I haven't worked out is how to create a library of modules and then assemble those into a single set of scenarios. I have a few thoughts though:
The underlying problem is the ability to include javascript files in each other - which really doesn't exist as a capability. There are third party libraries, which I'd prefer not to use, and I haven't seen a way to use Angular's module capability to do this.
End 2 end testing can be very dependent on the order of the tests. So one test may create data, another test may then use that data. As an example, if you want a test that logs people on, you may need a test that registers people first. You probably don't want to put registration on the front of every test that you run. As such, you probably need a lot of control over the order of your test scenarios anyway
As such, one option is to just put everything in one really big file. Which goes against everything we all learned in school, but I haven't really come up with a reason that wouldn't work. Then you can write functions and abstractions to your hearts content.
If you follow that to the next stage, another option is to write a series of javascript files with strict naming conventions, then use grunt to concatenate them for you before executing them. So, for example:
A set of files named xxxx.page.scenario.js, which contain the "page object" definitions - basically helper methods for each page
A set of files named xxxx.functions.scenario.js, which contain common components of your scenarios - so maybe you have a register and logon set of actions, and you make that into a library function
A set of files named nnxx.scenarios.scenario.js, which contain the actual scripts themselves. These are numbered at the start (the nn), so we can concatenate them in a reliable sequence and thereby control which order our scripts run
I'm not yet saying this is a good idea, just that it at least superficially looks like it could work, and would give the desired result. My main concern is that it feels fragile - so as the test suite grows in size it would perhaps become very difficult to maintain. Perhaps another way to do this would be, instead of numbering the scenarios, to instead define them as dependencies, and have something that makes sure that any given script runs after any script it declares itself to be dependent on. That would maybe allow for subsetting of the scripts as well - so you could say "run the bar script" and the framework would know that the bar script needs the foo script run first, and maybe the login script. But it's OK to leave all the other scripts out.
EDIT: I see astrolabe as potentially a good answer here, it looks like it explicitly allows you to modularise your tests. https://github.com/stuplum/astrolabe. I've just completed a proof of concept with it, and it seems to do everything I might hope. The code for it ends up something like:
clubs.part.scenario.js:
/**
* Partial for the page objects associated with clubs
*/
var Page = require('astrolabe').Page;
module.exports = Page.create({
url: { value: 'UI/index.html#clubs' },
title: { get: function() { return this.findElement(this.by.id('title')); } },
description: { get: function() { return this.findElement(this.by.id('description')); } },
clubTableElement: { value: function(rowNum, columnBinding) {
return this.findElement(this.by.repeater('club in clubs').row(rowNum).column(columnBinding)); } }
}
);
clubs.scenario.js:
/**
* End to end tests for the club functionality
*/
var homePage = require('../home/home.part.scenario.js');
var clubsPage = require('./clubs.part.scenario.js');
describe( 'Navigate to club list page', function() {
it ( 'should allow navigation to the club list page', function() {
homePage.go();
expect(homePage.clubsLink.getText()).toEqual('Clubs');
homePage.clubsLink.click();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
it ( 'should allow us to go directly to the club list page', function() {
clubsPage.go();
expect(clubsPage.title.getText()).toEqual('Club functions');
expect(clubsPage.description.getText()).toEqual('Soon this will show a list of all the clubs, based on information from the server');
expect(clubsPage.clubTableElement(0, 'name').getText()).toEqual('First club');
expect(clubsPage.clubTableElement(0, 'contact_officer').getText()).toEqual('A Person');
expect(clubsPage.clubTableElement(1, 'name').getText()).toEqual('Second club');
expect(clubsPage.clubTableElement(1, 'contact_officer').getText()).toEqual('J Jones');
});
});
I'm pretty happy with this structure, it doesn't do everything but it does most things. The sample code I've provided is from the tutorial that I've been working on for a while with angularjs, which I'm updating for e2e testing and Rails 4 at the moment, if you want the context that goes with that: http://technpol.wordpress.com/2013/11/16/5-end-to-end-testing/