Nested Page Objects in Protractor - javascript

The Question:
What is the canonical way to define nested Page Objects in Protractor?
Use Case:
We have a complicated page that consists of multiple parts: a filter panel, a grid, a summary part, a control panel on a side. Putting all the element and method definitions into a single file and a single page object does not work and scale - it is becoming a mess which is difficult to maintain.

This is more of a general topic when it comes to Page Objects and how to maintain them. Sometime back I stumbled upon one of the Page Object Design Pattern techniques which I liked and made a lot of sense to me.
Rather than instantiating child page objects in the parent page objects, it would be ideal to follow javascript's Prototypal Inheritance concept. This has quite a number of benefits but first let me show how we can achieve it:
First we would create our parent page object ParentPage:
// parentPage.js
var ParentPage = function () {
// defining common elements
this.someElement = element(by.id("someid"));
// defining common methods
ParentPage.prototype.open = function (path) {
browser.get('/' + path)
}
}
module.exports = new ParentPage(); //export instance of this parent page object
We will always export an instance of a page object and never create that instance in the test. Since we are writing end to end tests we always see the page as a stateless construct the same way as each http request is a stateless construct.
Now let's create our child page objects ChildPage, we would use Object.create method to inherit the prototype of our parent page:
//childPage.js
var ParentPage = require('./parentPage')
var ChildPage = Object.create(ParentPage, {
/**
* define elements
*/
username: { get: function () { return element(by.css('#username')); } },
password: { get: function () { return element(by.css('#password')); } },
form: { get: function () { return element(by.css('#login')); } },
/**
* define or overwrite parent page methods
*/
open: { value: function() {
ParentPage.open.call(this, 'login'); // we are overriding parent page's open method
} },
submit: { value: function() {
this.form.click();
} }
});
module.exports = ChildPage
we are defining locators in getter functions, These functions get evaluated when you actually access the property and not when you generate the object. With that you always request the element before you do an action on it.
The Object.create method returns an instance of that page so we can start using it right away.
// childPage.spec.js
var ChildPage = require('../pageobjects/childPage');
describe('login form', function () {
it('test user login', function () {
ChildPage.open();
ChildPage.username.sendKeys('foo');
ChildPage.password.sendKeys('bar');
ChildPage.submit();
});
Notice above that we are only requiring the child page object and utilizing/overriding parent page objects in our specs. Following are the benefits of this design pattern:
removes tight coupling between parent and child page objects
promotes inheritance between page objects
lazy loading of elements
encapsulation of methods and action
cleaner & much easier to understand the elements relationship instead of parentPage.childPage.someElement.click();
I found this design pattern in webdriverIO's developer guide, most of the above methods I explained are taken from that guide. Feel free to explore it and let me know your thoughts!

The idea is define the Page Object as a package - directory with index.js as an entry point. The parent page object would act as a container for child page objects which in this case have a "part of a screen" meaning.
The parent page object would be defined inside the index.js and it would contain all the child page object definitions, for example:
var ChildPage1 = require("./page.child1.po"),
ChildPage2 = require("./page.child2.po"),
var ParentPage = function () {
// some elements and methods can be defined on this level as well
this.someElement = element(by.id("someid"));
// child page objects
this.childPage1 = new ChildPage1(this);
this.childPage2 = new ChildPage2(this);
}
module.exports = new ParentPage();
Note how this is passed into the child page object constructors.
This might be needed if a child page object would need access to the
parent page object's elements or methods.
The child Page Object would look like this:
var ChildPage1 = function (parent) {
// element and method definitions here
this.someOtherElement = element(by.id("someotherid"));
}
module.exports = ChildPage1;
Now, it would be quite convenient to use this kind of page object. You simply require the parent page object and use the dot notation to get access to the sub page objects:
var parentPage = requirePO("parent");
describe("Test Something", function () {
it("should test something", function () {
// accessing parent
parentPage.someElement.click();
// accessing nested page object
parentPage.childPage1.someOtherElement.sendKeys("test");
});
});
requirePO() is a helper function to ease imports.
Sample nested page object directory structure from one of our test automation projects:

I don't use Protractor, but maybe you can try the idea below - at least, it's been working well for me so far:
I use something you can call "Component Object" - I divide a page into components or parts, and suppose I am given the scope of each component, I search and add elements to the components based on their scopes. This way, I can easily reuse the same/similar components in different pages.
For example, with the page http://google.com, I divide it into 3 parts:
Let's say we will name those 3 parts as: Header, SearchForm, Footer
The code for each part will be something like this:
class Header {
public Header(SearchContext context){
_context = context;
}
WebElement GmailLink {
get {
return _context.FindElement(By.CssSelector("[data-pid='23']"));
}
}
WebElement ImagesLink {
get {
return _context.FindElement(By.CssSelector("[data-pid='2']"));
}
}
SearchContext _context;
}
class SearchForm{
public Header(SearchContext context){
_context = context;
}
WebElement SearchTextBox {
get {
return _context.FindElement(By.Name("q")):
}
}
WebElement SearchButton {
get {
return _context.FindElement(By.Name("btnK")):
}
}
SearchContext _context;
}
..
And the code for the page google.com will be like:
class GoogleComPage{
WebDriver _driver;
public GoogleCompage(driver){
_driver = driver;
}
public Header Header{
get {
return new Header(_driver.FindElement(By.Id("gb")));
}
}
public SearchForm SearchForm{
get {
return new SearchForm(_driver.FindElement(By.Id("tsf")));
}
}
}

Related

How do I allow objects to inherit members from their class?

This question is about Javascript, however the first programming language I learned was Java so I relate to that the most easily.
In Java, objects have access to static members, as demonstrated in the article Understanding Class Members by Oracle:
public class Bicycle {
// INSTANCE fields
private int cadence;
private int gear;
private int speed;
// CONSTRUCTOR
constructor Bicycle(c,g,s) { /*...*/ }
//...
// STATIC fields
private static int numberOfBicycles = 0;
}
You can also refer to static fields with an object reference like
myBike.numberOfBicycles
In addition to objects having access to static fields, subclasses do as well, as mentioned in Inheritance.
public class MountainBike extends Bicycle {
public int seatHeight;
constructor MountainBike(c,g,s,h) { /*...*/ }
private static int mountainBikePrice = 324;
}
MountainBike now has 4 instance fields: cadence, gear, speed, and seatHeight; and 2 static fields: numberOfBicycles, and mountainBikePrice.
I would like to emulate this behavior in my Javascript program, however, since JS is prototype-oriented and not object-oriented, objects and subclasses cannot have access to their "class" members.
// constructor
function Bicycle(c,g,s) {
this.cadence = c
this.gear = g
this.speed = s
}
// instance methods
Bicycle.prototype.go = function () { /*...*/ }
Bicycle.prototype.stop = function () { /*...*/ }
// STATIC field
Bicycle.numberOfBicycles = 0 // <-- I want THIS field inherited
When I create a new Bicycle object via var myBike = new Bicycle(), I have access to the instance methods myBike.go(), etc., however I cannot get the static field via myBike.numberOfBicycles as I could in Java. The only way to get this is by calling Bicycle.numberOfBicycles.
Additionally, when I create a new subclass, it won't have access to its parent’s fields.
// subclass constructor
function MountainBike(c,g,s,h) {
Bicycle.call(this,c,g,s)
this.seatHeight = h
}
// vvvvvvvv extension mechanism... ignore this vvvvvvvvvv
MountainBike.prototype = Object.create(Bicycle.prototype)
MountainBike.prototype.constructor = MountainBike
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
// STATIC field
MountainBike.mountainBikePrice = 324
If I call MountainBike.numberOfBicycles I get undefined (which would not happen in Java—it would return the parent’s value).
Other than assigning Bicycle.prototype.numberOfBicycles = 0, there must be an easier way for objects to inherit properties of their class, and subclasses to inherit properties of their parent.
Is there a way to access the object’s class, such as myBike.class? If so, then I could call myBike.class.numberOfBicycles.
Is there a way to access a subclass’s parent class, something like MountainBike.super?
Actually, the property you are looking for is constructor (as in myBike.constructor.numberOfBicycles works as intended).
The constructor of an object is (kind of) like the class of an instance in Java. It's probably best to take this with a grain of salt because the concepts are not exactly interchangeable.
Edit:
So I see you are basically trying to create some full implementation of "classes" in JavaScript using prototype and constructor hacks (which is fine really). Backbone has an interesting approach to this in their extend function. Relevant source is the following (see bottom of question for license of this code):
var extend = function(protoProps, staticProps) {
var parent = this;
var child;
// Inherits constructor if not specified in child props.
if (protoProps && _.has(protoProps, 'constructor')) {
child = protoProps.constructor;
} else {
child = function(){ return parent.apply(this, arguments); };
}
// copy all properties from parent and from static properties
// into child
_.extend(child, parent, staticProps);
// figure out child prototype (kind of like you had)
child.prototype = Object.create(parent.prototype);
_.extendOwn(child, protoProps);
child.prototype.constructor = child;
// Save super for later access (answers question 2)
child.__super__ = parent.prototype;
return child;
};
Annotations are mine. I edited the code to expose some similarities with what the OP had posted (i.e used Object.create instead of _.create). The way to use it is to set in the parent Parent.extend = extend; and then create the child class with Child = Parent.extend({...new properties and methods...},...).
I decided to go ahead and post the entire function because it might contain solutions to problems you haven't asked about yet (such as inheritance of constructors). Specifically for question 2, the Backbone devs made a decision to explicitly save the parent class in the child with __super__, so you could do that as well in your inheritance code (after you call the Object.create).
It also might just make more sense to use ES2015 Classes and inheritance with extends (here is some good docs on it). The support for it is not great, so you would need to use a preprocessor such as Babel. Here is an example of these classes:
class Parent {...}
class Child extends Parent {...}
License of Backbone code fragment posted:
(c) 2010-2016 Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors.
Backbone may be freely distributed under the MIT license.
For all details and documentation:
http://backbonejs.org

Data binding on JavaScript HTMLElement property in Polymer

Making an SPA using Polymer, and I need my custom components to all use a common custom component which represents my backend API and is responsible of GET-ting/POST-ing data from/to the API. It also serves as a "cache" and holds the data to display. This way, all the components that have access to this single element will share the same data.
So what I want to do is this... :
<my-api
users="{{users}}"
products="{{products}}">
</my-api>
...but programmatically, as <my-api> is not declared in all of my components but once in the top one and then passed down through the hierachy by JavaScript:
Polymer({
is: 'my-component',
properties: {
api: {
observer: '_onApiChanged',
type: HTMLElement
},
products: {
type: Array
},
users: {
type: Array
}
},
_onApiChanged: function(newVal, oldVal) {
if (oldVal)
oldVal.removeEventListener('users-changed', this._onDataChanged);
// Listen for data changes
newVal.addEventListener('users-changed', this._onDataChanged);
// Forward API object to children
this.$.child1.api = newVal;
this.$.child2.api = newVal;
...
},
_onDataChanged: function() {
this.users = this.api.users; // DOESN'T WORK as 'this' === <my-api>
this.products = this.api.products; // Plus I'd have to repeat for every array
}
});
Does Polymer offers a built-in way to do this ? Can I create a double curly braces binding programmatically ?
I would likely architect this slightly differently: passing down the products/users arrays declaratively taking advantage of Polymer's binding system. Or you could write your my-api element in such a way that they all share state and the first declared one is the primary while future declared ones are replicas. This would let you declare them wherever you need them and bind to the values via Polymer's normal ways.
But to answer your question, there's currently no way to easily programmatically setup the same kind of binding without using private Polymer APIs.
To avoid repeating as much and for the binding issue you were having you could use Polymer's built-in listen and unlisten methods:
Polymer({
is: 'my-component',
properties: {
api: {
observer: '_onApiChanged',
type: HTMLElement
},
products: {
type: Array
},
users: {
type: Array
}
},
_onApiChanged: function(newVal, oldVal) {
var apiProperties = ['users', 'products'];
if (oldVal) {
apiProperties.forEach(function(prop) {
this.unlisten(oldVal, prop + '-changed', '_onDataChanged');
});
}
// Listen for data changes
apiProperties.forEach(function(prop) {
this.listen(newVal, prop + '-changed', '_onDataChanged');
});
// Forward API object to children
this.$.child1.api = newVal;
this.$.child2.api = newVal;
...
},
_onDataChanged: function() {
this.users = this.api.users; // `this` should be the element now
this.products = this.api.products;
}
});
Given how this is a common pattern you're doing, you could probably get a lot of benefit out of extracting some of these things into a Behavior that abstracts away the binding/unbinding and API element forwarding.
Another optimization you may could make work would be to to look at the event passed to _onDataChanged to see if you can infer which value changed and update your corresponding property. This could prevent you needing to add a line for every property.
I ended up using an other solution. Instead of manually passing the top <my-api> element down the hierarchy any element that needs access to this shared data declares its own <my-api>.
Then in the <my-api> element's declaration I made that all instances use the same arrays references. So whenever I update one they all get updated, and I don't have to pass anything down the HTML hierarchy, which makes things a LOT simpler.

Ember lookup property from inside custom component

Say I have a custom component as my-panel
And in the backing JS (my-panel.js)
this.get('targetObject')
Also I reference/use this component from another custom component say my-sections.hbs as
{{my-panel}}
My question is when I do
this.get('targetObject') //Called inside my-panel.js
How/Where does it lookup the property targetObject in ? What does the chain look like?
It looks it up within the scope of the backing custom component js (probably).
The chain/scope really starts from the this object, so depending what this is in that scope, that's really where it is looking it up on. If you had a reference to a different object, or were within a callback function my statement may not be true.
App.MyCompComponent = Ember.Component.extend({
foo: 'bar',
actions: {
doit: function(){
// `this` is the scope of an instance of this component.
alert(this.get('foo'));
}
}
});
http://emberjs.jsbin.com/totibidohe/edit?html,js,output
You can think of it outside the scope of being in the template as well, and just create an instance and reference that instance and it would be the same.
var MyCompComponent = Ember.Component.extend({
foo: 'bar',
actions: {
doit: function(){
alert(this.get('foo'));
}
}
});
var j = MyCompComponent.create();
alert(j.get('foo'));
http://emberjs.jsbin.com/xekohoguto/1/edit?html,js,output

JavaScript variable scope - proper use

I read this style guide for angular from johnpapa. There is a snippet:
/*
* recommend
* Using function declarations
* and bindable members up top.
*/
function Avengers(dataservice, logger) {
var vm = this;
vm.avengers = [];
vm.getAvengers = getAvengers;
vm.title = 'Avengers';
activate();
function activate() {
return getAvengers().then(function() {
logger.info('Activated Avengers View');
});
}
function getAvengers() {
return dataservice.getAvengers().then(function(data) {
vm.avengers = data;
return vm.avengers;
});
}
}
So my question is in functions activate() and getAvengers(), they both reference variable (dataservice) and function (getAvengers()) outside of their scope. Is this proper use? Should I bind these 2 in the variable vm instead, e.g:
vm.getAvengers = getAvengers;
vm.dataservice = dataservice;
...
function activate() {
return vm.getAvengers().then(....);
}
function getAvengers() {
return vm.dataservice.getAvengers().then(.....);
}
Specifically for your case
Would say if you are meaning to use this within angular app would recommend not exposing the service, exposing it through this object does not add value and might down the road, when a less experienced developer modifies your code, might result in wonky access to shared dependencies.
If you want access to the dataservice objects functionality across multiple entities then register it as an angular service, and inject it to the different entities that need it.
In General
Both of the ways you are describing are perfectly correct use, but as is usually the case the answer which to use is "it depends."
Why you would use one for another would be if you wanted to expose the variable externally (i.e. if you wanted to let others access that object through the returned object, expecting others to dynamically change the service on your object)
So in this example you should ask yourself a few question
Do I want to expose this object through another object or do I want to let angular DI pass this along to the other controllers that need this functionality
Do I want to allow external entities to modify this object
Does exposing this service through my object make the use of the perceived use of this object more confusing?
But again for this particular case you should not expose it through your object ( through your variable vm, which is bound to the return object this, in this case )
The vm is a acronym for a view model (a object representation of your view) it is meant to be used within your view to bind elements, ui events to it. The dataservice and the logger seems to nothing to do with the view at all, they are just services used within a controller. If you assign them to the vm then you probably create a tightly coupling between your view and services thus it seems like a not a very good idea to me. You can think about the VM as a interface (glue) between your view and controller.
Here is a picture of the interactions between view model, controller, view and services.

Getting a list of routes in Ember.js

On my main page (index.hbs), I need to display a list of links to each route which matches a given criteria, (e.g. has a certain attribute). So what I need is something like this:
// Define a route with some attribute
App.FirstRoute = Ember.Route.extend({
showOnIndex: true
});
// Get a list of visible routes in the IndexController
visibleRoutes: function() {
var routes = /* How to do this */
return routes.filter(function(route) {
route.get('showOnIndex'));
});
}.property()
The problem is how to get routes. I have found that I can get a list of route names by:
var router = this.get('target');
var names = router.router.recognizer.names;
But I can't find out how to translate them into Route objects. Presumably the Router has the information to do so, but AFAICS it is not publically exposed. Any suggestions?
what about using the container and lookups?
Ember.keys(App.Router.router.recognizer.names).map(function(name) {
return App.__container__.lookup('route:' + name);
}).compact();
I know you access the __container__ which is private. But normally you shouldn't access all the routes anyway.
Here is one way to do it although I think it is a little bit horrible and maybe even dangerous long term since it is using features of Javascript rather than the Ember API. We start by getting the routes through your 'App' object. You can get a list of all the classes it contains using Object.keys(App). Then you can go through each of these, check if it is a route with .superclass which will be Ember.Route if it is a Route. You can then check the showOnIndex property on the prototype of each of these.
var allRoutes = [];
Object.keys(App).forEach(function(key, index, allKeys) {
if (App[key].superclass === Ember.Route && App[key].prototype.showOnIndex === true) {
allRoutes.push(App[key]);
}
});
We now have an array of all the class names of the routes which have showOnIndex of true.
However, you still have the problem of aligning the names from the recognizer with the class names of the Route but since the Ember way is that a route like my/url will map to MyUrlIndexRoute and 'my.url.index' etc then you can split the Route by upper case, remove the last part ('Route'), join them together and convert it all to lower case.
var routesByName = [];
allRoutes.forEach(function(name, index, all) {
var names = name.match(/[A-Z][a-z]+/g);
names.pop();
var routeName = names.join('.').toLowerCase();
routesByName.push(routeName);
});
Then you can just loop through the routesByName in the template and create a {{#link-to}} for each of them.
I think a better way to achieve what you want may be to keep a map of all the routes to their showOnIndex value separately inside your app and just update it for each new one you want to add. It may be better safer long term since who knows how the Ember internals will change in the future which could prevent doing some of the things above.
Note that I checked all this in debugger mode in an Ember app in chrome but haven't run all the code fragments directly so apologies for any mistakes.
In ember v4.9 you can do the following in any component:
export default class myComponent extends Component {
#service router;
#computed
get routes() {
// load RouterService
let router_service = this.router;
// extract routes and format.
let routes = router_service._router._routerMicrolib.recognizer.names;
let detailed_routes = Object.keys(routes)
.filter((key) => {
console.log(key === "error" )
return ! (key.match(/error/) || key.match(/loading/)
|| key.match(/index/) || key.match(/application/))
})
.map((route) => router_service.urlFor(route));
console.log(detailed_routes);
return detailed_routes
}
}

Categories

Resources