Check the presence of an element's attribute - protractor - angularjs

I am fairly new to protractor and angularJS so please help me with this. When the dayReport element is selected, the class='active' attribute is set but when the element is not selected, the attribute is completely removed.
I want to make sure that when another element is selected, dayReport doesn't have the class='active' attribute although the element itself is still present.
I tried this: expect(dayReport.getAttribute('class').isPresent()).toBe(false); and some other few ideas but it I don't seem to make it work. Any ideas?

For whoever is reading this and needs a clear answer, the response from #TomNijs was very helpful but the hasClass function should be created in order for that to work. So the whole piece of code after defining the dayReport element is now looking like this:
var hasClass = function (element, cls)
{
return element.getAttribute('class').then(function (classes)
{
return classes.split(' ').indexOf(cls) !== -1;
});
};
expect(hasClass(dayReport, 'active')).toBe(false);

I had the same issue, try the following code :
hasClass will return a promise which will immediatly be resolved by the expectstatement and return true or false wether your element has the class="active" attribute.
//hasClass will check if the first parameter (the element) has the following the second parameter(the class attribute)
expect(hasClass(dayReport, 'active')).toBe(true);

Related

Protractor retrieve single element from ElementArrayFinder

I want to retrieve the first element from a ElementArrayFinder matching a condition. The following code provides me with the element that I'm expecting but it goes through all elements which takes time.
Is there any way to this?
return content.all(by.css('.class')).filter(function(label){
return label.getText().then(function(text){
return text === searchName;
});
}).first();
CONCLUSION:
Answer provided by Sudharsan Selvaraj worked as a charm
var ele = content.all(by.xpath(".//*[contains(#class,'class')][normalize-space(text())='some-value']")).first()
Reduced drastically time searching the element
The answer provided by Florent B. also solves the problem and looks like a much simpler approach.
return content.element(by.cssContainingText('.class', searchName));
You can use xpath to find the element with specific text.Look at below sample xpath.
var ele = content.all(by.xpath(".//*[contains(#class,'class')][normalize-space(text())='some-value']")).first()
You should use the locator by.cssContainingText to find an element by CSS and text:
return content.element(by.cssContainingText('.class', searchName));
Why don't you directly access the first element of the label?
return content.all(by.css('.class label')).first().getText().then(function (text) {
return text === searchName;
});
P.S. I didn't test the code but it should probably work

Why selected object is not display in console when it is clicked

I am using this demo: https://github.com/ezraroi/ngJsTree
API reference: https://www.jstree.com/api/#/
I made two examples
First I have a checkbox. When I select any checkbox, it show the selected item object (codepen: http://codepen.io/naveennsit/pen/MKJdOZ?editors=101)
$scope.getSelectedCategories = function() {
alert('---')
var selected_nodes = $scope.treeInstance.jstree(true).get_checked(true);
console.log(selected_nodes);
};
But when I remove this checkbox the click and selected item is not displayed in the console (codepen: http://codepen.io/naveennsit/pen/RrKmxp)
I used same function in both examples, but the second one does not work.
According to the documentation of jsTree .get_checked(true) returns "an array of all checked nodes". If you don't have a check box associated to a node then I'm not sure it will work. Or it will not work without a significant workaround. However, the documentation says that "if tie_selection is on in the settings this function will return the same as get_selected". This may give you the solution you seek. Otherwise you could change .get_checked to .get_selected
The documentation I used can be found here.
It's pretty far down the page to find .get_checked fyi.
EDIT
I decided to just try it myself.
$scope.getSelectedCategories = function() {
var selected_nodes = $scope.treeInstance.jstree(true).get_selected(true);
alert(JSON.stringify(selected_nodes));
};
This alerted the object.

IE input element focus not working with angular directive

I have a div populated dynamically with input elements and setting the focus on the first input field. The focus works on Chrome but not on IE. Here is the plnkr http://plnkr.co/edit/b6urKKilDVRwuqVQfbWt
The focus is in fact done inside a timeout function but still the focus does not seem to do anything on the input field. I am using an angular directive to create my form elements.
directive('ngppParameters', ["$compile","$timeout",function($compile,$timeout) {
return {
restrict: 'E',
link:function($scope,$element,$attrs)
{
$scope.$watch("tree.length", function (value) {
if(value.length===0)
return;
$timeout(function() {
var fields = $("ngpp-parameters input").filter(":visible:first");
console.log("Setting the focus");
if(fields.length>0)
{
console.log("Setting focus");
fields[0].focus();
}
},10);
});
}
};
Update:
This is the directive.
Finally found the issue, IE is treating the disabled parent and not letting the focus to be set on the children.
I have removed the disabled="disabled" attr from the directive pp-field and it works as expected. All other browsers except IE are not considering the parents disabled state.
Make sure you use ng-disabled="" instead of disabled attribute to make browsers to ignore it and behave as expected.
fields[0] isn't what you think it is (i've been caught by this same mistake before).
You want fields.eq(0)
https://api.jquery.com/eq/
You might also be able to just called fields.focus(). If there's only one in there that should work.

Permission directive in AngularJS

Let's say I want to have a directive that checks permission.
So that I coud do this:
<a permissions="something.delete">Delete</a>
If "something.delete" in the list of allowed permissions then nothing is rendered.
Having this code:
link: function (scope, element, attrs) {
var permissionsPromise = PermissionService.checkForPermissions(attrs.permissions);
permissionsPromise.then(function(result) {
if (result=== false) {
element.remove();
}
})
}
But because PermissionService.checkForPermissions() returns promise, so it may take some time to figure out the permissions, meanwhile link function will render the a-element before knowing the result of permission-check.
What would be the proper solution to fix that issue?
Reverse your logic and hide the element by default and then show it based on the promise value.
something along the lines of elem[0].style.display = 'none' -> elem[0].style.display = 'block'
Ask yourself what is your real goal, as you aren't really implementing permissions checking on the client-side (or you shouldn't be). This should be more to provide the user with the correct UI experience. If you want to remove it altogether then just keep a reference to the parent element, remove the element, and when your promise returns append it back to the parent .. or not.

Linking MVC In AngularJS

I have a basic application in AngularJS. The model contains a number of items and associated tags of those items. What I'm trying to achieve is the ability to filter the items displayed so that only those with one or more active tags are displayed, however I'm not having a lot of luck with figuring out how to manipulate the model from the view.
The JS is available at http://jsfiddle.net/Qxbka/2 . This contains the state I have managed to reach so far, but I have two problems. First off, the directive attempts to call a method toggleTag() in the controller:
template: "<button class='btn' ng-repeat='datum in data' ng-click='toggleTag(datum.id)'>{{datum.name}}</button>"
but the method is not called. Second, I'm not sure how to alter the output section's ng-repeat so that it only shows items with one or more active tags.
Any pointers on what I'm doing wrong and how to get this working would be much appreciated.
Update
I updated the method in the directive to pass the data items directly, i.e.
template: "<button class='btn' ng-repeat='datum in data' ng-click='toggle(data, datum.id)'>{{datum.name}}</button>"
and also created a toggle() method in the directive. By doing this I can manipulate data and it is reflected in the state HTML, however I would appreciate any feedback as to if this is the correct way to do this (it doesn't feel quite right to me).
Still stuck on how to re-evaluate the output when a tag's value is updated.
You can use a filter (docs) on the ng-repeat:
<li ng-repeat="item in items | filter:tagfilter">...</li>
The argument to the filter expression can be many things, including a function on the scope that will get called once for each element in the array. If it returns true, the element will show up, if it returns false, it won't.
One way you could do this is to set up a selectedTags array on your scope, which you populate by watching the tags array:
$scope.$watch('tags', function() {
$scope.selectedTags = $scope.tags.reduce(function(selected, tag) {
if (tag._active) selected.push(tag.name);
return selected;
}, []);
}, true);
The extra true in there at the end makes angular compare the elements by equality vs reference (which we want, because we need it to watch the _active attribute on each tag.
Next you can set up a filter function:
$scope.tagfilter = function(item) {
// If no tags are selected, show all the items.
if ($scope.selectedTags.length === 0) return true;
return intersects($scope.selectedTags, item.tags);
}
With a quick and dirty helper function intersects that returns the intersection of two arrays:
function intersects(a, b) {
var i = 0, len = a.length, inboth = [];
for (i; i < len; i++) {
if (b.indexOf(a[i]) !== -1) inboth.push(a[i]);
}
return inboth.length > 0;
}
I forked your fiddle here to show this in action.
One small issue with the way you've gone about this is items have an array of tag "names" and not ids. So this example just works with arrays of tag names (I had to edit some of the initial data to make it consistent).

Resources