Unable to locate buttons in protractor - protractor

There are 3 buttons in an angular application with the following details:
<button ng-class="btnClass1" class="btn btn-lg tab" ng-click="addCust()">
<b ng-show="btnClass1 === 'btn-primary'" id="notch" class="notch ng-hide"></b>
"Add Customer" </button>
<button ng-class="btnClass2" class="btn btn-lg tab" ng-click="addCust()">
<b ng-show="btnClass1 === 'btn-primary'" id="notch" class="notch ng-hide"></b>
"Open Account" </button>
<button ng-class="btnClass3" class="btn btn-lg tab" ng-click="addCust()">
<b ng-show="btnClass1 === 'btn-primary'" id="notch" class="notch ng-hide"></b>
"Customer" </button>
I tried following to locate:
var addCustomerButton = element(by.className('btn btn-lg tab'));
But this will only work for the first one as the class name is same for all. Any way to have unique locators for all the three buttons?

You can utilize element.all (https://www.protractortest.org/#/api?view=ElementArrayFinder.prototype.all) which will return an array of the elements so you can access the element by its index in the array (e.g addCustomerButton[0])
Or you can utilize nth-child() (https://developer.mozilla.org/en-US/docs/Web/CSS/:nth-child) to specify three different locators for three buttons and just interact with any of these.
However, I don't suggest any of this approaches since this can mess everything up hardly if more buttons gonna be added. Especially if the new button will be added between, let's say button 1 and button 2. Then your button 2 will become button 3, button 3 will become button 4, and so on...
Your to-go option should be to have unique locators for each button

Related

#circlon/angular-tree-component: How to customize checkbox template

I want to use custom templates for the tree nodes as shown here on their website
https://angular2-tree.readme.io/docs/templates
But I also need the checkbox tri-state functionality as demonstrated here
https://angular2-tree.readme.io/docs/tri-state-checkboxes
In this custom treeNodeFullTemplate example they have a checkbox as part of the template but it doesn't have the tri-state relationship between the parents and children. Is there a way to have a custom checkbox and keep all the correct event listeners etc.? I can't seem to find any documentation on the checkboxes API if there is one.
<tree-root id="tree" [focused]="true" [nodes]="nodes">
<ng-template #treeNodeFullTemplate let-node let-index="index" let-templates="templates">
<div class="tree-node">
<input type="checkbox" [checked]="node.isActive" (change)="node.toggleActivated(true)" />
<tree-node-expander [node]="node"></tree-node-expander>
<div
class="node-content-wrapper"
[class.node-content-wrapper-active]="node.isActive"
[class.node-content-wrapper-focused]="node.isFocused"
(click)="node.toggleActivated(true)">
<span [class]="node.data.className + 'Index'">{{ index }}</span>
<span [class]="node.data.className" [class.title]="true">{{ node.data.title }}</span>
</div>
<tree-node-children [node]="node" [templates]="templates"></tree-node-children>
</div>
</ng-template>
</tree-root>
I can see that the (change) method on the checkbox is not right but do I need to write my own one to get the parent and children and determine state on click? It seems strange that I can't just tap into an existing API.

how to find parent and grand parent of an element without using xpaths

<button click-stop-propagation="" mat-button="" class="mat-button" ng-reflect-disabled="true" disabled="">
<span class="mat-button-wrapper">
<i class="icon icon-md delete-icon"></i>
<span>Delete</span>
</span>
<div class="mat-button-ripple mat-ripple" matripple="" ng-reflect-centered="false" ng-reflect-disabled="true" ng-reflect-trigger="[object HTMLButtonElement]"></div>
<div class="mat-button-focus-overlay"></div>
</button>
I want to find grand parent of icon icon-md delete-icon which is mat-button is there any way in protractor to do that, I want to avoid using xPaths.
I know that using xpaths I can do that element(by.css('.icon.delete-icon')).element(by.xpath('../..')); is there any way by which I can identify it using CSS ??

Dynamically adding Semantic UI modals with SilverStripe

I'm having a hard time trying to connect SUI modal with SilverStripe to generate them dynamically.
I want to achieve something like this:
I have button (attach events) to trigger modal with some content. I wanted to loop that elements (GridField) to generate everything dynamically. But the only thing I can achieve is multiple modals with the same "trigger class" and it doesn't matter which button I have clicked (first, last or whatever). I only have one modal (the last in hierarchy). Without SUI the solution is easy - put "this" to fetch the closest element and I can have as many different modals as I want, but SUI seems to complicate things.
I think the best solution will be to generate a class/id with SilverStripe and pass it to the JavaScript to use with modal or use one class for every modal and to "somehow inform" that this button triggers this modal.
Basically I need one code snippet to handle many modals, not many snippets to handle many modals. I hope it's clear enough what the the problem is.
Here is my code:
HTML/SS
(without specific SilverStripe tags)
<% loop SomeName %>
<div class="job-offers">
<a class="ui right floated button job-application">Click here</a>
</div>
<div class="ui basic modal job-application">
<div class="job-application-sheet">
(...)
<div class="job-application-sheet-content">
<div class="ui grid">
(...)
<div class="ui center aligned container job-application-action">
<p>Lorem ipsum</p>
<button class="ui primary button">Click here</button>
</div>
</div>
</div>
</div>
</div>
<% end_loop %>
JavaScript
$('.ui.basic.modal.job-application')
.modal({
autofocus : false,
observeChanges: true
})
.modal('attach events', '.job-application', 'show');
As you can see "job-application" class is a trigger for modal, so is this possible to change it to "(this)" so I don't have to write "specific" class for each button/modal. Or maybe there is a different/easier solution?
first i generated a data-type attribute for each of my elements that are going to display a modal when they are clicked, and in send as a local the same index to the modal this way:
<% #relateds_array.each.with_index do |related,i| %>
<div class="card custom" data-id="<%=i%>">
<%= render partial: 'my_modal', locals: {index: i} %>
</div>
<% end %>
the i put the modal in the partial that i called my_modal for this example and used the index that i sent as a local to create an unique id for each my modals, like this:
<div class="ui modal" id="modal-<%=index%>">
<p>blabla things inside this modal.</p>
</div>
and then with javascript, simply get the data-id of the element clicked and select the element that contain the id for that data-id, like this:
$('.element_that_you_want_to_open_moda_on_click').click(function(event){
var card_clicked = $(this).attr('data-id');
$('#modal-' + card_clicked).modal('show');
});
Hope this was useful for you.
Note that i used Ruby on Rails for this example, but the behaviour that you should use should be something similar to this, no matter what framework you use.
Answer based on Sebastian's solution. I did some minor tweaks to meet my needs, like I've used ID variable to automatically get DataObject ID which is generated dynamically.
Basically to dynamically add Semantic UI (SUI) modal in SilverStripe, first you should add "data-id" in a modal trigger, for example:
Template.ss
<a class="ui button custom-trigger" data-id="my-item-$ID">Click here</a>
then inside modal container add an "id" tag, for example:
<div id="modal-my-item-$ID" class="ui basic modal">
(...)
</div>
Finally in JavaScript file:
$('.custom-trigger').click(function(event){
var triggerItem = $(this).attr('data-id');
$('#modal-' + triggerItem).modal('show');
});
I meet problem with SUI autofocus, so after a modal opens, screen went to the bottom and button placed there was highlighted.
I tweaked original snippet adding SUI settings:
$('.custom-trigger').click(function(event){
var triggerItem = $(this).attr('data-id');
$('.ui.modal')
.modal({
autofocus: false,
observeChanges: true
})
$('#modal-' + triggerItem).modal('show');
});
If someone wants to write "data-id trigger" manually using fields in CMS, then change $ID to $SomeField variable. Then normally add that field in .php file and in Page Controller add something like this:
public function init() {
parent::init();
Requirements::javascriptTemplate($this->ThemeDir().'/js/script.js', array(
'SomeField' => $this->SomeField
));
}
Hope this helps someone.

angularjs: trigger form validate programmatically (inside a controller)

I have this situation in which I show 1 form in two steps. So to proceed to the second part of the form you have to click on a button. But before moving on I would like to perform form validation (all required fields need to be filled in). But because this is a normal button, the whole submit magic is not triggered and the validation does not happen. So the question I'm interested in is how can I trigger form validation in my controller ? It would even be better to trigger validation for specific fields. Just to give an idea, the form looks like
<form name="form" submit="save()">
<section id="step1">
<label for="username" required>Username</label>
<input type="text" name="username" ng-model="user.username" required />
.....
<button ng-click="proceed()">Proceed</button>
</section>
<section id="step2">
<label ...></label>
<input...>
....
<button type="submit">Save</button>
</section>
</form>
Also, I don't want to opt for disabling the button until all required fields are valid.
Take a look at the ng-form directive. It allows nesting forms (actually not HTML <form>s, but Angular NgFormControllers). So you can split your one form, used for posting to the server, into two logical forms, used for independent validation:
<form submit="save()">
<div ng-form="form1">
...controls...
<button ng-click="proceed()"
ng-disabled="form1.$invalid">Proceed</button>
</div>
<div ng-form="form2">
...controls...
<button type="submit"
ng-disabled="form2.$invalid || form1.$invalid">Submit</button>
</div>
</form>
You can access the $valid property from your controller. Something like this could work.
$scope.proceed = function(){
if($scope.form.username.$valid){
//username is valid we may proceed to the next step
}
};
<button ng-click="proceed()">Proceed</button>
Replace To :
<button ng-click="proceed()" ng-disabled="form.$invalid">Proceed</button>
Button will not visible button until all required fields are valid.
DEMO

getting a collection back from a form in grails

I have an album view where a user can check which photos to delete. I am currently doing this:
<g:form action="delete">
<g:each in="${pictures}">
<div id="images2">
<img id="images2" src="${it.urlThumb}" alt="no Picture"/><br>
<g:checkBox name="myCheckbox"/>
</div>
</g:each>
<g:submitButton name="Submit"/>
</g:form>
The problem is this creates the form dynamically so each of the names are the same for the check boxes. I ideally want a collection returned to the controller that has each of the checked images.
Any ideas?
In the <g:checkBox> you need to specify a value. Each checkbox will be created with the same name but a different value. When the form is submitted with different checkboxes checked, you will get a list in the controller.
View:
<g:each in="${pictures}">
<div id="images2">
<img id="images2" src="${it.urlThumb}" alt="no Picture"/><br>
<g:checkBox name="myCheckbox" value="it.id"/>
</div>
</g:each>
Controller:
def checked = params.list('myCheckbox')