Protractor - clicking a link in a list - javascript

Given a todo app that has a list of tasks: Walk the dog, Eat lunch, Go shopping. Each task has a 'complete' link.
Using Protractor, how do I click the complete link for the second task 'Eat lunch'? Preferably I'd like to do this without using indices in my test.
The html structure is like so...
<ul class="pending">
<li ng-repeat="task in tasks">
{{task.name}}
<a href='#'>Complete</a>
</li>
</ul>
This seems like a common situation so surely there has to be a simple solution that I'm overlooking. Thanks in advance

element.all(by.repeater('task in tasks')).
get(1).
element(by.linkText('Complete')).
click()
or
element.all(by.repeater('task in tasks')).
get(1).
$('a').
click()

Related

Protractor - locate element by both className and text

I have a situation in my web application where some listed items do share common className that is used by our automated test suites. Now, I have to click on specific element and need to locate it by it's class and text of that element.
Here is an example:
<ul>
<li class="autotest-list">Milk</li>
<li class="autotest-list">Sugar</li>
<li class="autotest-list">Candy</li>
</ul>
I need a way how to click on ie.Sugar.
Something like this:
element(by.className('autotest-list').text('Sugar');
or this
element(by.className('autotest-list')&&text('Sugar');
Thank you all in advance!
element(by.cssContainingText('.autotest-list','Sugar')
If you don't mind, you could use by.xpath:
element(by.xpath('li[#class="auto test-list" and text()="Sugar"]'))

AngularJS: Add Focus behavior to list items similar to `select-option`

I have a html list with some text items. I need to bring the list item into focus when user type the initial letters of it's text similar to select-option when user type initial letter of it's options; matching option is brought into focus. I need to have same behavior for list. I am rendering my list using ng-repeat directive of angular:
<ul class="list-group m-3 col">
<li ng-repeat="song in ctrl.songs | orderBy" class="list-group-item list-group-item-action" tabindex="{{$index}}">
<span>{{song}}</span>
</li>
</ul>
I googled for a plugin/library but unfortunately couldn't find any. I can write a directive for this but time does not permit it. So can anyone please tell me if there's any library/plugin available? Here's the plunkr.
After spending quality time with jQuery and tabindex of bootstrap I finally made it working.
function bringToFocus(text) {
var item = element.children().filter(function(child) {
return angular.element(this).text().trim().toLowerCase().startsWith(text.toLowerCase());
}).first();
item.focus();
}
Here's the working plunkr. Hope it might be helpful for someone.

qtip2 for HTML elements created in an angularjs function

I have this part of HTML
<ul class="list-group" ng-repeat="thing in things">
<li class="list-group-item" ng-bind-html="tmpfcn(thing.text, thing.objects)"></li>
</ul>
Which for every iteration returns some text with hyperlinks inside. e.g.
I like the winter
I want to use qtip for each of the hyperlinks with different content for each hyperlink.
I've defined a qtip directive which I've tested with an HTML element like this
<span qtip="This is the message printed"> hover over me</span>
and it works just fine.
I've looked at many solutions for similar problems like this but I couldn't get my code to work.
Could someone provide some guidelines? An example would bereally helpful as well.

Trying to use the same string properties in angular to do multiple things

I have a folder in my angular apps directory called demos that I want to load into an iframe and a navigation that I want populated based off of the folder name. Then I want it to list only 10 of the demos and it should be a random selection.
Here is my code but I am missing some steps obv.
app/demos/democara
app/demos/randomeDemo
app/demos/anotherDemo
//UL generated from app/demos/ structure
<ul class="randomDemoNav">
<li ngClick="{{cPDemo}}"><span class="pseudo">/democara</span></li>
<li ngClick="{{cPDemo}}"><span class="pseudo">/randomeDemo</span></li>
<li ngClick="{{cPDemo}}"><span class="pseudo">/anotherDemo</span></li>
</ul>
<iframe class="demo-loader" ng-src="{{cPDemo}}"></iframe>
<script>
and that's about as far as I get.
</script>
Any thoughts on what I should search in API to accomplish this? I would also be ok using just jquery for this.

AngularJS ng-repeat with no html element

I am currently using this piece of code to render a list:
<ul ng-cloak>
<div ng-repeat="n in list">
<li>{{ n[0] }}</li>
<li class="divider"></i>
</div>
<li>Additional item</li>
</ul>
However, the <div> element is causing some very minor rendering defects on some browsers.
I would like to know is there a way to do the ng-repeat without the div container, or some alternative method to achieve the same effect.
As Andy Joslin said they were working on comment based ng-repeats but apparently there were too many browser issues. Fortunately AngularJS 1.2 adds built-in support for repeating without adding child elements with the new directives ng-repeat-start and ng-repeat-end.
Here's a little example for adding Bootstrap pagination:
<ul class="pagination">
<li>
«
</li>
<li ng-repeat-start="page in [1,2,3,4,5,6]">{{page}}</li>
<li ng-repeat-end class="divider"></li>
<li>
»
</li>
</ul>
A full working example can be found here.
John Lindquist also has a video tutorial of this over at his excellent egghead.io page.
KnockoutJS containerless binding syntax
Please bear with me a second: KnockoutJS offers an ultra-convenient option of using a containerless binding syntax for its foreach binding as discussed in Note 4 of the foreach binding documentation.
http://knockoutjs.com/documentation/foreach-binding.html
As the Knockout documentation example illustrates, you can write your binding in KnockoutJS like this:
<ul>
<li class="header">Header item</li>
<!-- ko foreach: myItems -->
<li>Item <span data-bind="text: $data"></span></li>
<!-- /ko -->
</ul>
I think it is rather unfortunate AngularJS does not offer this type of syntax.
Angular's ng-repeat-start and ng-repeat-end
In the AngularJS way to solve ng-repeat problems, the samples I come across are of the type jmagnusson posted in his (helpful) answer.
<li ng-repeat-start="page in [1,2,3,4,5]">{{page}}</li>
<li ng-repeat-end></li>
My original thought upon seeing this syntax is: really? Why is Angular forcing all this extra markup that I want nothing to do with and that is so much easier in Knockout? But then hitautodestruct's comment in jmagnusson's answer started making me wonder: what is being generated with ng-repeat-start and ng-repeat-end on separate tags?
A cleaner way to use ng-repeat-start and ng-repeat-end
Upon investigation of hitautodestruct's assertion, adding ng-repeat-end on to a separate tag is exactly what I would not want to do in most cases, because it generates utterly usesless elements: in this case, <li> items with nothing in them. Bootstrap 3's paginated list styles the list items so that it looks like you did not generate any superfluous items, but when you inspect the generated html, they are there.
Fortunately, you do not need to do much to have a cleaner solution and a shorter amount of html: just put the ng-repeat-end declaration on the same html tag that has the ng-repeat-start.
<ul class="pagination">
<li>
«
</li>
<li ng-repeat-start="page in [1,2,3,4,5]" ng-repeat-end></li>
<li>
»
</li>
</ul>
This gives 3 advantages:
less html tags to write
useless, empty tags are not generated by Angular
when the array to repeat is empty, the tag with ng-repeat won't get generated,
giving you the same advantage Knockout's containerless binding gives you in this regard
But there is still a cleaner way
After further reviewing the comments in github on this issue for Angular, https://github.com/angular/angular.js/issues/1891,
you do not need to use ng-repeat-start and ng-repeat-end to achieve the same advantages.
Instead, forking again jmagnusson's example, we can just go:
<ul class="pagination">
<li>
«
</li>
<li ng-repeat="page in [1,2,3,4,5,6]">{{page}}</li>
<li>
»
</li>
</ul>
So when to use ng-repeat-start and ng-repeat-end? As per the angular documentation, to
...repeat a series of elements instead of just one parent element...
Enough talk, show some examples!
Fair enough; this jsbin walks through five examples of what happens when you do and when you don't use ng-repeat-end on the same tag.
http://jsbin.com/eXaPibI/1/
ngRepeat may not be enough, however you can combine that with a custom directive. You could delegate the the task of adding divider items to code if you don't mind a little bit of jQuery.
<li ng-repeat="item in coll" so-add-divide="your exp here"></li>
Such a simple directive doesn't really need an attribute value but might give you lots of possiblities like conditionally adding a divider according to index, length, etc or something completely different.
I recently had the same problem in that I had to repeat an arbitrary collection of spans and images - having an element around them was not an option - there's a simple solution however, create a "null" directive:
app.directive("diNull", function() {
return {
restrict: "E",
replace: true,
template: ""
};
});
You can then use a repeat on that Element, where element.url points to the template for that element:
<di-null ng-repeat="element in elements" ng-include="element.url" ></di-null>
This will repeat any number of different templates with no container around them
Note: hmm I could've sworn blind this removed the di-null element when rendering, but checking it again it doesn't...still solved my layout issues though...curioser and curioser...
for a solution that really works
html
<remove ng-repeat-start="itemGroup in Groups" ></remove>
html stuff in here including inner repeating loops if you want
<remove ng-repeat-end></remove>
add an angular.js directive
//remove directive
(function(){
var remove = function(){
return {
restrict: "E",
replace: true,
link: function(scope, element, attrs, controller){
element.replaceWith('<!--removed element-->');
}
};
};
var module = angular.module("app" );
module.directive('remove', [remove]);
}());
for a brief explanation,
ng-repeat binds itself to the <remove> element and loops as it should, and because we have used ng-repeat-start / ng-repeat-end it loops a block of html not just an element.
then the custom remove directive places the <remove> start and finish elements with <!--removed element-->
There is a comment directive restriction, but ngRepeat doesn't support it (since it needs an element to repeat).
I think I saw the angular team say they would work on comment ng-repeats, but I'm not sure. You should open an issue for it on the repo. http://github.com/angular/angular.js
There is no Angular magic way to do this, for your case you can do this, to get valid HTML, if you are using Bootstrap. Then you will get same effect as adding the li.divider
Create a class:
span.divider {
display: block;
}
Now change your code to this:
<ul ng-cloak>
<li div ng-repeat="n in list">
{{ n[0] }}
<span class="divider"></span>
</li>
<li>Additional item</li>
</ul>

Categories