css testing in angularjs - angularjs

I am using controller to change the class of an object in angularjs
$scope.$watch('sideQuery',function(){
if($scope.sideQuery==""){
$(".indicator").removeClass('glyphicon-minus');
$(".indicator").addClass('glyphicon-plus');
}
else{
$(".indicator").removeClass('glyphicon-plus');
$(".indicator").addClass('glyphicon-minus');
}
});
How to test using karma? a function like
expect(scope.elem('.indicator').hasClass("glyphicon-plus")).toBe(true);

Please, do not use jQuery to toggle classes in Angular, it defeats the purpose of it. Use ng-class and apply your classes based on flags, like so:
<div class="indicator" ng-class="{'glyphicon-minus' : sideQuery != '', 'glyphicon-plus':sideQuery == ''} ></div>
Then in testing check the value of sideQuery and know that you'll have classes based off that.

If you are testing a directive, then you can compile a sample element and test the result object.
var $element;
beforeEach(inject(function ($compile) {
$element = $compile('<div data-my-directive></div>')($scope);
}));
it('should have the class "someClass"', function(){
expect($element.hasClass('someClass')).toBe(true);
});
However, be aware that tymeJV is right, you should use the ng-class directive and test your scope's values. If the scope value is right, then the class will be applied (you don't have to test the ng-class directive, that's something done in Angular's unit tests).

Related

angularjs-- declenching events

Please, how can I change this code into angularJs
$('a.product_add').on('click', function(event){
event.preventDefault();
var collectionHolder = $('#task_tags');
var prototype = collectionHolder.attr('data-prototype');
form = prototype.replace(/__name__/g, collectionHolder.children().length);
collectionHolder.append(form);
});
First of all you need to show us what you've tried, but I'll write something here to help you
You should make a directive because you're using jquery code. Read more about directives here
AngularJS directives are extended HTML attributes with the prefix ng-.
The ng-app directive initializes an AngularJS application.
The ng-init directive initializes application data.
The ng-model directive binds the value of HTML controls (input,
select, textarea) to application data.
Example of a directive
app.directive('myDirective', function(){
function link($scope,$elem,$attrs){
$elem.on('click', function(event){
// click event code here
});
}
return {
link:link,
scope:{},
restrict:'A'
}
})
Example of usage for myDirective:
<a class='product_add' my-directive>link</a>
We can use angular custom directives.
Now you can access the element in the directive and do the same operations in the directive.
<directive-element ng-click=appendFunction()></directive-element>

angular testing directive stub child directive

I want to test a directive whose markup contains a child directive.
e.g.
<div class="directiveUnderTest">
<child-directive></child-directive>
</div>
I would like to stub out the child directive in my unit test so it won't try to 'compile' it and instead just ignore this directive.
I could do
inject(function($templateCache) {
$templateCache.put('/path/to/child/directive', '');
});
But this now depends on the implementation of the child directive. I would like to stub out the entire child directive.
I don't want to override the template of the directive under test as I need to compile the real template in order for it to create the expected scope.
Any suggestions?
Thanks in advance.
Because a directive is just a factory, you can mock them out in tests with $provide. Then when you build the "fake" directive, you can provide it with a templateUrl that will still load what you need. An example below shows a way to make sure the fake directive stays in an isolated scope and provides what you need.
beforeEach(function () {
module('moduleName', function ($provide) {
$provide.factory('nameOfDirective', function () {
return { templateUrl: 'path/to/template' };
}
});
});
Something to be aware of is you actually need to suffix Directive onto the name of your directive, the normal compiler will do this automatically when declaring .directive('name', ...) to make angular aware of what type of factory it is.

Jasmine unit test for Angular directive

I have the following angular directive, which adds a tooltip when I hover over a span.
angular.module('mainMod')
.directive('toolTip', [function() {
return {
restrict: 'A',
scope: {
theTooltip: '#toolTip'
},
link: function(scope, element, attrs){
element.tooltip({
delay: 0,
showURL: false,
bodyHandler: function() {
return jQuery('<div class="hover">').text(scope.theTooltip);
}
});
}
}
}])
;
<span ng-show="data.tooltip" class="icon" tool-tip="{{data.tooltip}}"></span>
I'm looking to write a unit test for this directive, atm I can't use jasmine-jquery.
I'm fairly new to writing unit tests, could anyone possibly help me out?
Give me some pointers or point me towards some helpful resources?
Any advice or suggestions would be greatly appreciated.
What I have atm isn't much...
describe('Unit testing tooltip', function() {
var $compile;
var $rootScope;
// Load the myApp module, which contains the directive
beforeEach(module('mainMod'));
// Store references to $rootScope and $compile
// so they are available to all tests in this describe block
beforeEach(inject(function(_$compile_, _$rootScope_){
// The injector unwraps the underscores (_) from around the parameter names when matching
$compile = _$compile_;
$rootScope = _$rootScope_;
}));
it(' ', function() {
// Compile a piece of HTML containing the directive
FAILS HERE --> var element = $compile("<span class='icon' tool-tip='{{data.tooltip}}'></span>")($rootScope);
$rootScope.$digest();
});
});
It's failing with a message of
TypeError: undefined is not a function
I think it's being caused by the ($rootScope) at the end of the line I've specified above.
You have to wrap your DOM content with angular.element first before compiling it. I am not sure what the tooltip module you are using but I used the jQuery UI tooltip instead.
//create a new scope with $rootScope if you want
$scope = $rootScope.$new();
var element = angular.element("<span class='icon' tool-tip='This is the tooltip data'></span>");
//use the current scope has just been created above for the directive
$compile(element)($scope);
One more thing, because you are using isolate scope in your directive, to get the current scope from your directive, you need to call
element.isolateScope()
based on this reference : How to Unit Test Isolated Scope Directive in AngularJS
For a working fiddle, you can found it here : http://jsfiddle.net/themyth92/4w52wsms/1/
Any unit test is basically the same - mock the environment, construct the unit, check that the unit interacts with the environment in the expected manner. In this instance, you'd probably want to mock the tooltip creator
spyOn(jQuery.fn, 'tooltip');
then compile some template using the directive (which you're already doing), then simulate the hover event on the compiled element and then check that the tooltip creator was called in the expected manner
expect(jQuery.fn.tooltip).toHaveBeenCalledWith(jasmine.objectContaining({
// ... (expected properties)
}));
How you simulate the event depends on how the element.tooltip is supposed to work. If it really works the way you're using it in the question code, you don't need to simulate anything at all and just check the expected interaction right after template compilation.

What does attrs.$observe do in Angular Directive

Simple question, what does attrs.$observe do in Angularjs
Does it observer any changes in the html directive.
I have looked inline but the Angularjs documentation is woful
attrs.$observe('scroller', function() {
$scope.init();
});
If you are not using isolated scope and you want to observe a attribute which has interpolation then you use attrs.$observe. Like
<div my-directive my-attribute="{{i}}">
$attrs.$observe("myAttribute",function(newValue) {
//called when myAttribute value(i) changes
});

How to trigger ngAnimate class sequence when using element.addClass inside directive

Inside of a directive I'm adding various classes on an element based on user interaction.
How can I get the ngAnimate class sequence (e.g. my-class-add -> my-class-add-active) when using element.addClass in place of ngClass directive?
I want to use CSS transitions, not JS animations.
Thanks.
You need to add the class via the animate service (angularjs 1.2) like
module.directive('directive', function ($animate) {
return {
restrict:"A",
link: function($scope,$element) {
$element.on("click", function() {
$animate.addClass($element,"my-animation");
});
};
}
});
Doc
Ideally you'd delegate that to another directive which is tied in with ngAnimate (i.e. ng-show, ng-if, ng-repeat .etc.)
You can set a scope property and let another directive handle the animation classes.

Resources