Best practices in protractor for big pageObject files - protractor

In my previous protractor JS project (This new one I will do it with TS) I created one class for all my elements and another one for my functions, something like this:
specs
|_reportPage
|_lib
|_pageElements.js
|_pageFunctions.js
Then I was importing the files as necessary, in this way was easy to find the info since the element list was long.
So far all examples online for protractor TS projects are short pageObject files with a couple of elements and methods, but I would like to know how to correctly proceed when the page requires a lot of elements and functions/methods.
For example, lets say we have 5 specs under the same folder that test the same page and this page is full of fields and tables.
What would be the best practice here? create 1 pageobject for each spec, create one long class with all the elements and functions...?
Thanks for your time!

To Extend my answer you can add additional layer as a service which can execute several actions from the flow in different pages.
Code example:
export class E2EService {
mainPage: MainPage = new MainPage();
innerPage: InnerPage = new InnerPage();
doSomethingE2E() {
this.mainPage.headerPage.weDoSomething();
this.mainPage.contentPage.weDoSomething()
this.innerPage.somethingComplicated();
}
}
export class MainPage {
public readonly headerPage: HeaderPage;
public readonly contentPage: ContentPage;
}
export class InnerPage {
headerPage: InnerHeaderPage;
contentPage: InnerContentPage;
public somethingComplicated() {
this.headerPage.weDoSomething();
this.contentPage.weDoSomething();
}
}
export class ContentPage {
private readonly elements = {
// elements
};
public weDoSomething() {
// code
}
public getElements() {
return this.elements;
}
}
export class HeaderPage {
private readonly elements = {
btn1: element(by.id('')),
div: element(by.id('')),
h2: element(by.id(''))
};
public weDoSomething() {
// code
}
public getElements() {
return this.elements;
}
}

Based on Infern0's answer, I did dependency injection to the classes:
class HeaderElements {
foo = element(by.id("foo"));
//List goes on...
}
class HomePageElements {
foo = element(by.id("foo"));
//List goes on...
}
export class MainCommonElementsPage {
headerElements: HeaderElements;
homePageElements: HomePageElements;
constructor() {
this.headerElements = new HeaderElements();
this.homePageElements = new HomePageElements();
}
}

Best practices, even for large Page Objects is this:
Each page should only have 1 page object class. All of the tools needed to access that page should be located here. Think of your page object as an API.
Don't break the PO into different parts, especially for large pages. You'll eventually need to modify the PO to adjust for content changes. Would you rather change 1 file or 12? This also ensures that each of your e2e tests will remain functional after you update the PO.
I have one PO that handles a page with a lengthy form. The form has 12 controls and three buttons (cancel, reset, and submit). I have about 30 functions that deal with the form. I don't like having more than 1-2 methods in my test, so if it gets more complicated, I add to the PO.

Related

How to efficiently reuse Ionic page against different providers?

I'm working with Ionic 2 and I have entity management page which is suitable for different data providers.
Now I have page for character management.
export class CharacterListPage{
currentItems: Character[];
constructor(public provider: Characters) {
this.currentItems = this.provider.query();
}
// other stuff
}
I need to do creature management. Creatures structure is identical to character structure.
Obvious way is just copy-paste page and make some renaming:
export class CreatureListPage{
currentItems: Character[];
constructor(public provider: Creatures) {
this.currentItems = this.provider.query();
}
// other stuff
}
Is it possible to use more efficient way?
That looks like a typical use case for an abstract class, luckily typescript brings that feature to the .js world:
You have a base class, lets call it ListPage, which holds all the common functionality of a Character and a Creature which can then be extended by other pages and reuse this existing functionality:
class ListPage {
listItems: Character[] | Creature[];
constructor(provider: Characters | Creatures) {
this.listItems = provider.query();
}
// other common methods
}
class CharacterListPage extends ListPage {
constructor(provider: Characters) {
super(provider);
}
}
class CreatureListPage extends ListPage {
constructor(provider: Creatures) {
super(provider);
}
}
For this minimal example it may not seem very efficient but when your classes get more complex and have much common functionality it will be very useful.
Here you can find the documentation on typescript classes.

Finding a View with IViewFor<InterfaceClass> from a class VM that implements InterfaceClass?

I have the following class which is my view model (this is very simple right now, but it will contain a chunk more logic eventually):
public class IndicoTalk : ITalk
{
private Talk _talk;
public IndicoTalk(Talk t)
{
this._talk = t;
}
public string Title
{
get { return _talk.Title; }
}
}
Now, I have a reactive ui view for this guy:
public sealed partial class TalkView : UserControl, IViewFor<ITalk>
{
public TalkView()
{
this.InitializeComponent();
this.Bind(ViewModel, x => x.Title, y => y.TalkTitle.Text);
}
Note that the IViewFor is for ITalk, not IndicoTalk. This is because I can have other types of talk, and they will all fit into the same view.
And I register this ViewModel in my App start up:
Locator.CurrentMutable.Register(() => new TalkView(),
typeof(IViewFor<IWalker.DataModel.Inidco.IndicoMeetingRef.IndicoTalk>));
Finally, in another viewmodel I have a ReactiveList which contains a bunch of these IndicoTalks's. Of course, when I bind this to a ListBox, ReactiveUI fails to find the view. If I switch to IViewFor then everything works just fine.
What is the proper way to gently redirect the view resolution in this case?
A half-way solution: leave all code above the same, but put in the IViewFor ITalk instead of IndicoTalk. This works, but means I will have to register with Splat (the CurrentMutable call above) every ViewModel that inherrits from ITalk. I'd love to avoid that if possible!
Many thanks!
So, why not just do:
Locator.CurrentMutable.Register(() => new TalkView(), typeof(IViewFor<ITalk>));

Actionscript in a single class or in multiple scenes?

I am completely new to Actionscript and Adobe Flash CS6 and for a little bit of fun I have decided to try and make a little game. I had a few newbie (or noob-y) questions to ask about a general implementation approach.
The documentation I've been reading so far suggests creating a new flash project, and then create a document class so:
package {
import flash.display.MovieClip;
public class MyMainClass extends MovieClip {
public function MyMainClass() {
}
}
}
and I am wondering if I use this MainClass to code the whole game or include actionscript within a scene and have multiple scenes, or some combination of both.
Lets say I had a wanted 5 Levels in my game, would I do something like:
package {
import flash.display.MovieClip;
public class MyMainClass extends MovieClip {
public function MyMainClass() {
StartLevel1();
StartLevel2();
StartLevel3();
StartLevel4();
StartLevel5();
}
public function StartLevel1() {
// Do something
}
public function StartLevel2() {
// Do something
}
public function StartLevel3() {
// Do something
}
public function StartLevel4() {
// Do something
}
public function StartLevel5() {
// Do something
}
}
}
or create 5 scenes with actionscript in each scene?
Can anyone provide me with a bit of a starting point?
Thanks
I don't know of anyone who has anything good to say about scenes.
However, as you intuit, the timeline itself is a wonderful tool for managing the state of your Flash assets over time. If you use it, you also get the hidden advantage that you don't have to download 100% of your file to be able to use it (so you can reduce or even eliminate the need for a preloader by unchecking "Export in frame N" on your library symbols.
Lars has quite rightly pointed out that there are very few developers who understand this technique, and I know of exactly one who can and will help people who are interested in exploring this technique. That person is helping you right now. So if you choose to go that way, keep in mind you are mostly on your own except if I happen to notice your post and respond to it.
I am not in favor of timeline scripts, with a very few exceptions. What I suggest is a "both and" approach, where you use a Document Class to control timeline instances.
Your document Class might look something like this:
public class Game extends MovieClip {
protected var _level:ILevel;//Interface your Level MovieClips will implement
protected var levelController:LevelController = new LevelControler();
protected var currentLevel:int;
protected var maxLevels:int = 5;
public function Game() {
levelController.addEventListener(LevelEventKind.LEVEL_COMPLETE, nextLevel);
levelController.addEventListener(LevelEventKind.LEVEL_FAILED, gameOver);
startLevel(currentLevel);
}
public function startLevel(levelNumber:int):void {
goToLabel('Level' + String(levelNumber));
}
public function get level():ILevel {
return _level;
}
public function set level(value:ILevel):void {
_level = value;
//internally, this should release all listeners to the last
//level object (if any) so you don't get a memory leak
levelController.level = _level;
}
protected function nextLevel(e:Event):void {
if (currentLevel < maxLevels) {
startLevel(++currentLevel);
} else {
//do you won logic here
}
}
protected function gameOver(e:Event):void {
//do bombed out logic here
}
protected function goToLabel(label:String):void {
for each (var frameLabel:FrameLabel in currentLabels) {
if (frameLabel.name==label) {
//if your swf is media-heavy, may want to check that frame
//is loaded if you chose to reduce/eliminate preloader
goToAndStop(label);
return;
}
}
trace('no such label as', label);
}
}
What this gets you is a game where you can change how the different levels look without changing a single line of ActionScript, and you can change how they work by assigning different Base Classes that implement ILevel slightly differently. You can also change your functionality by swapping out different flavors of LevelController, but your Main Document Class (Game in this instance) would be aware of this change (wheras the other changes could be made without altering Game at all).

atk4 simple dropdown from database table

I want to populate a dropdown from a table 'accountgroups' which has 2 columns id and name. The first row of dropdown should be blank or '--Select Account Group--' when first loaded. Once the user selects the item (i.e. display field 'name' and value field 'id') I want to get id and name values when form is submited.
You'll need to create a model for your table first:
class Model_AccountGroups extends Model_Table {
public $table='accountgroups';
function init(){
parent::init();
$this->addField('name');
}
}
$form->addField('dropdown','account_id')->setModel('AccountGroups');
if($form->isSubmitted()){
$form->js()->univ()->alert('Selected ID='.$form->get('account_id'))->execute();
}
Probably (your post have an year and a half) you had resolved your issue, but I want anyway contribute with "my 2 cents" for this issue. I looked around (documentation, forums, etc) and neither found another solution to the same problem. My solution (tested and working) is this (please is is not efficient enough let me know):
>>>>>>>>>>>>>>>>>>>>> ON MODEL
class Model_Offices extends Model_Table {
public $entity_code='Offices';
function init(){
parent::init();
$this->addField('ID')->ReadOnly(True);
$this->addField('OfficeName');
}
function GetAll() {
$r=array();
$AllOffices = $this->SetModel('Offices');
foreach($AllOffices as $OneOffice) {
$r[$OneOffice['ID']]=$OneOffice['OfficeName'];
}
return $r;
}
}
>>>>>>>>>>>>>>>>>>>> ON PAGE
$m=$this->Add('Model_Offices');
$form->addField('dropdown','Office')->SetValueList($m->GetAll()) ;
Mack

Why does getting the nth child of a Node fail in an ExplorerManager listener?

I'm having problems with the NetBeans Nodes API.
I have this line of code:
Node n = (new MyNode(X)).getChildren().getNodeAt(Y);
The call to new MyNode(X) with the same X always initializes a MyNode the same way, independent of the context.
When I place it by itself (say, in an menu action), it successfully gets the Yth child, but if I put it in an event where other Node/Children stuff happens, it returns null.
MyNode's Children implementation is a trivial subclass of Children.Keys, which is approximately:
// Node
import org.openide.nodes.AbstractNode;
class MyNode extends AbstractNode {
MyNode(MyKey key) {
super(new MyNodeChildren(key));
}
}
// Children
import java.util.Collections;
import org.openide.nodes.Children;
import org.openide.nodes.Node;
public class MyNodeChildren extends Children.Keys<MyKey> {
MyKey parentKey;
MyNodeChildren(MyKey parentKey) {
super(true); // use lazy behavior
this.parentKey = parentKey;
}
#Override
protected Node[] createNodes(MyKey key) {
return new Node[] {new MyNode(key)};
}
#Override
protected void addNotify() {
setKeys(this.parentKey.getChildrenKeys());
}
#Override
protected void removeNotify() {
setKeys(Collections.EMPTY_SET);
}
}
// MyKey is trivial.
I assume this has something to do with the lazy behavior of Children.Keys. I have the sources for the API, and I've tried stepping through it, but they're so confusing that I haven't figured anything out yet.
NetBeans IDE 7.0.1 (Build 201107282000) with up-to-date plugins.
Edit: More details
The line with the weird behavior is inside a handler for an ExplorerManager selected-nodes property change. The weird thing is that it still doesn't work when the MyNode instance isn't in the heirarchy that the ExplorerManager is using (it's not even the same class as the nodes in the ExplorerManager), and isn't being used for anything else.
Accessing the nodes instead of the underlying model is actually necessary for my use case (I need to do stuff with the PropertySets), the MyNode example is just a simpler case that still has the problem.
It is recommended to use org.openide.nodes.ChildFactory to create child nodes unless you have a specific need to use one of the Children APIs. But for the common cases the ChildFactory is sufficient.
One thing to keep in mind when using the Nodes API is that it is only a presentation layer that wraps your model and used in conjunction with the Explorer API makes it available to the various view components in the NetBeans platform such as org.openide.explorer.view.BeanTreeView.
Using a model called MyModel which may look something like:
public class MyModel {
private String title;
private List<MyChild> children;
public MyModel(List<MyChild> children) {
this.children = children;
}
public String getTitle() {
return title;
}
public List<MyChild> getChildren() {
return Collections.unmodifiableList(children);
}
}
You can create a ChildFactory<MyModel> that will be responsible for creating your nodes:
public class MyChildFactory extends ChildFactory<MyModel> {
private List<MyModel> myModels;
public MyChildFactory(List<MyModel> myModels) {
this.myModels = myModels;
}
protected boolean createKeys(List<MyModel> toPopulate) {
return toPopulate.addAll(myModels);
}
protected Node createNodeForKey(MyModel myModel) {
return new MyNode(myModel);
}
protected void removeNotify() {
this.myModels= null;
}
}
Then, implementing MyNode which is the presentation layer and wraps MyModel:
public class MyNode extends AbstractNode {
public MyNode(MyModel myModel) {
this(myModel, new InstanceContent());
}
private MyNode(MyModel myModel, InstanceContent content) {
super(Children.create(
new MyChildrenChildFactory(myModel.getChildren()), true),
new AbstractLookup(content)); // add a Lookup
// add myModel to the lookup so you can retrieve it latter
content.add(myModel);
// set the name used in the presentation
setName(myModel.getTitle());
// set the icon used in the presentation
setIconBaseWithExtension("com/my/resouces/icon.png");
}
}
And now the MyChildrenChildFactory which is very similar to MyChildFactory except that it takes a List<MyChild> and in turn creates MyChildNode:
public class MyChildFactory extends ChildFactory<MyChild> {
private List<MyChild> myChildren;
public MyChildFactory(List<MyChild> myChildren) {
this.myChildren = myChildren;
}
protected boolean createKeys(List<MyChild> toPopulate) {
return toPopulate.addAll(myChildren);
}
protected Node createNodeForKey(MyChild myChild) {
return new MyChildNode(myChild);
}
protected void removeNotify() {
this.myChildren = null;
}
}
Then an implementation of MyChildNode which is very similar to MyNode:
public class MyChildNode extends AbstractNode {
public MyChildNode(MyChild myChild) {
// no children and another way to add a Lookup
super(Children.LEAF, Lookups.singleton(myChild));
// set the name used in the presentation
setName(myChild.getTitle());
// set the icon used in the presentation
setIconBaseWithExtension("com/my/resouces/child_icon.png");
}
}
And we will need the children's model, MyChild which is very similar to MyModel:
public class MyChild {
private String title;
public String getTitle() {
return title;
}
}
Finally to put it all to use, for instance with a BeanTreeView which would reside in a TopComponent that implements org.openide.explorer.ExplorerManager.Provider:
// somewhere in your TopComponent's initialization code:
List<MyModel> myModels = ...
// defined as a property in you TC
explorerManager = new ExplorerManager();
// this is the important bit and we're using true
// to tell it to create the children asynchronously
Children children = Children.create(new MyChildFactory(myModels), true);
explorerManager.setRootContext(new AbstractNode(children));
Notice that you don't need to touch the BeanTreeView and in fact it can be any view component that is included in the platform. This is the recommended way to create nodes and as I've stated, the use of nodes is as a presentation layer to be used in the various components that are included in the platform.
If you then need to get a child you can use the ExplorerManager which you can retrieve from the TopComponent using the method ExplorerManager.Provier.getExplorerManager() which was implemented due to the fact that your TopComponent implemented ExplorerManager.Provider and is in fact the way that a view component itself gets the nodes:
ExplorerManager explorerManager = ...
// the AbstractNode from above
Node rootContext = explorerManager.getRootContext();
// the MyNode(s) from above
Children children = rootContext.getChildren().getNodes(true);
// looking up the MyModel that we added to the lookup in the MyNode
MyModel myModel = nodes[0].getLookup().lookup(MyModel.class);
However, you must be aware that using the Children.getNodes(true) method to get your nodes will cause all of your nodes and their children to be created; which weren't created due to the fact that we told the factory that we wanted it to create the children asynchronously. This is not the recommended way to access the data but instead you should keep a reference to the List<MyModel> and use that if at all possible. From the documentation for Children.getNodes(boolean):
...in general if you are trying to get useful data by calling this method, you are probably doing something wrong. Usually you should be asking some underlying model for information, not the nodes for children.
Again, you must remember that the Nodes API is a presentation layer and is used as an adapter between your model and your views.
Where this becomes a powerful technique is when using the same ChildFactory in different and diverse views. You can reuse the above code in many TopComponents without any modifications. You can also use a FilterNode if you need to change only a part of the presentation of a node without having to touch the original node.
Learning the Nodes API is one of the more challenging aspects of learning the NetBeans platform API as you have undoubtedly discovered. Once you have some mastery of this API you will be able to take advantage of much more of the platforms built in capabilities.
Please see the following resources for more information on the Nodes API:
NetBeans Nodes API Tutorial
Great introduction to the Nodes API by Antonio Vieiro
Part 5: Nodes API and Explorer & Property Sheet API by Geertjan Wielenga
JavaDocs for the Nodes API
Timon Veenstra on the NetBeans Platform Developers mailing list solved this for me.
Actions on the explorerManager are guarded to ensure consistency. A
node selection listener on an explorer manager for example cannot
manipulate the same explorer manager while handling the selection
changed event because that would require a read to write upgrade. The
change will be vetoed and die a silent death.
Are you adding the MyNode root node to the explorer manager on
initialization, or somewhere else in a listener?
My problem line is in an ExplorerManager selection change listener. I guess the Children.MUTEX lock is getting set by ExplorerManager and preventing the Children.Keys instance from populating its Nodes...?
Anyways, I moved my Node access into a EventQueue.invokeLater(...), so it executes after the selection changed event finishes, and that fixed it.