Unit Testing the view of an Angular-UI modal - angularjs

I've recently learned a lot about testing controllers w/ modals, modal controllers and directives. Almost everything in my app that can be tested, is being tested and it's all rainbows and kittens.
My latest (seemingly impossible) problem is that the modal I'm testing has a sort of "wizard" UI, where the user progresses through each step to create something to submit when the modal is closed. I'd like to test that the UI is reacting accordingly.
For example, in the modal controller, when $scope.cloneType is false, I'd like to make sure that my div#cloneMenu is actually showing (or hidden when cloneType has been set).
In a directive, I can do something like this:
$el.isolateScope().cloneType = null;
$scope.$digest();
expect($el.find('#cloneMenu').hasClass('ng-hide')).toBe(false);
$el.isolateScope().cloneType = 'basic';
$scope.$digest();
expect($el.find('#cloneMenu').hasClass('ng-hide')).toBe(true);
but I don't have $el (or anything like it) available in the modal controller (that I know of). I tried using $document.find() but that didn't work. I'm suspecting that $compile comes in to play at some point, but don't know where.
So, aside from just making the body of my modal a directive and unit testing that, is there any way of testing the DOM in the view of an AngularUI modal dialog? Any code samples or links to info that can help would be appreciated.
I'm using Angular 1.2.9 and Jasmine 2.0 and I've included jQuery so that I have a better find() method. (Being limited to tagName is lame when trying to test/traverse real DOM).
Thanks!

I'll provide an answer just so this question has the conclusion that I was after.
I think Sunil D. has it right with testing the views via an E2E test.
I was forcing myself to see the modal as a 'component' like a directive and so wanted to write tests in that way. I know it's not a directive, but it just felt more natural to test it that way because of how it works/looks/is similar to a directive (controller fn + template).
The other issue was that the controller for this thing is so small (because angular is so awesome) so there was only a couple of tests. I felt there should be more to it and wasn't getting the sense of completeness/accomplishment that I did from writing directive tests.
In the end, I'm just going to finish the tests for the modal controller and look into getting some E2E in the mix. That was next on my list anyway.

Related

If you build an AngularJS custom directive, is it bad practice to use jQuery inside the code?

Sometimes when I build an Angular custom directive, I will hear a comment as to, if it is an Angular directive, it should not use jQuery code in it, because it should be built in an AngularJS way.
And I thought it might be true, but is it possible? For example, what if the directive template has 2 sections, one is the words and one is the tiny images (such as review stars), and so you need 2 sections in your template, labeled as .description and .star-images -- so then you should need to use $.find(".description") to find that section inside your template if you need to do something to it or inside it. jqLite won't work as jqLite's find() is limited to tags only.
Another example is, what if you have a directive that doesn't have a template, but just limit the keypress to digits only, say, for an input box. So you don't want your directive to have a template as <input type="text"> but just want the user of the directive to say <input type="text" digits-input-only> and your directive is called digitsInputOnly. So in that case, don't you need to use jQuery's elem.on() or elem.bind() to listen on keypress or keydown events, and when the key down code is not a digit, then do a event.preventDefault()? So in that case, it has to use jQuery?
Or other there other ways to do it so that you really shouldn't need to use jQuery?
As a long time user of jQuery it is easy while learning angular to lean on jQuery however none of the cases mentioned are difficult to work around not having it...and in most cases are actually easier
Try removing jQuery completely from your project to avoid temptation and you will quickly realize how little you really need it
The core directives provide the majority of the event handlers needed and angular.element (jQlite) also has bind() which will accept virtually any event name. $document.bind('contextmenu', function(event) for example.
The core dom event directives all let you pass in $event for things like event.preventDefault()
<input ng-keydown="somefunc($event)">
For traverses you can always use a native method to query DOM to find an element and wrap that element (or collection) in angular.element() the same way you would with $(). The more you focus on data models and core directives first however, the less you find need to actually do dom traverses
As for plugins ... it's not a sin to use jQuery plugins in directives. There are some very commonly used angular modules that are wrappers for well known jQuery plugins ... fullcalendar and Datatables are a couple that quickly come to mind along with numerous datepickers. However often you will find situations where you may have previously leaned on a plugin to do simple tasks that angular makes easy itself and you no longer would use such plugins
The sin with using jQuery plugins is using ones that are actually easier to achieve (and test) using angular itself
In conclusion, the biggest adjustment is learning how to focus on data models first, before thinking about the DOM. Also being intimately familiar with the left side menu of the API reference where all the core directives and services are listed is a huge help

Controller not loading when using jQuery.append

I'm currently using jQuery to display modal windows, the function basically builds some HTML and then does $('#myElement').append(modalHtml);
Inside that modal HTML I have <div ng-controller="MyController">...</div> however when the modal is displayed the controller doesn't seem to get initialized. Is this because it's being added to the DOM outside of angular's scope? If so is there anyway when I run that code I could notify angular to look out for changes?
The breakdown of how it is at the moment is Angular runs loadModal() through an ng-click on an element. loadModal() calls modal(), and modal() builds the html and adds it to the DOM. Modal is in a script of just standalone helper functions.
The modal controller won't be called because you're not parsing or compiling the appended DOM code with Angular. Hence, Angular will not 'notice' this change and as a result it will not run any controllers or directives in the added DOM. The best way to solve this kind of problems is by adding a custom directive.
As you're trying to make a modal work, you could also take a look at existing modal adaptations for Angular, such as the one in AngularUI.

mmenu is not initializated under angularjs

I am developing a mobile application with left and right drawer, using mmenu (http://mmenu.frebsite.nl/) it is working fine till I've decided to populate items of the right menu on the fly. My app is based on angularjs and all directives of angularjs in body are working fine except all directives inside <nav id="menu-right">somehow, angularjs directives inside the menu definitions are not executed. I am not sure if it is something related with the order of executing javascript. Any help will be really appreciated. Thank you !!
UPDATED (07-April)
An example in jsfiddle http://jsfiddle.net/jmhostalet/wcK8L/
"My controller says" is working in the body but not in the mmenu, in body prints "Hello" but in mmenu prints nothing
The problems you were running into were:
First off the was not included in the declaration
You are changing the DOM without angular knowing about it therefor it does not have a chance to compile the braces {{myCtrlVar}}
It seems like extra work to create a directive but in the long run will allow you to reuse your code more. Also if you are like me and have bad javascript habits; it is nicer to use a framework to keep things straight.
My solution the in the plunker below shows "one way" to do what you are asking. It would be interesting to port the entire MMenu code over to an angular module in order to have more parameters and control.
(Never heard of MMenu, but seems like a cool project -- I will look more into it).
Plunker
(sorry I am inept at using fiddler)
Your fiddle doesn't have controller initialization on #menu-right but on his sibling element and therefore {{myCtrlVar}} expression can't access controllers scope objects.

how to two way bind angular-ui-bootstrap-modal without watches and services

I am implementing the Angular ui bootstrap modal from
angular-ui-modal
The basic design exemplified by them is on the plunker next to it.
However my application requires it(the modal) to be implemented multiple times in the same single page web application (for saving loading etc).
In this plunker have modified their basic plunker [link : see my plunker here]
In the above plunker I am trying to apply dynamic binding between two controllers.
as in :
I want the value of "checkBind" from the inner controller modal template to be reflected in outer controller.
I know this is not possible like i am trying to do it because the "scopes are different".
Now i am not so convinced for using watches / broadcast /services for this
"petty modal thing".
I have few questions :
Do I have to create a separate controller for using the angular ui modal.
How can i use the "OuterCtrl"(in my plunker) to somehow write all the modal invoking and handler methods(like $scope.open.... $scope.ok .... $scope.cancel etc).
how can I directly bind the value of checkBind from the modal to the outerController in the least code possible(i mean by ignoring the watches services etc)
If "3" is possible then I can really ignore 1 and 2 (but i would still want to know the answer)
I know I am missing something here. Please tell me what is that.
Thanks in advance
I am new to angular and your english is a bit difficult to follow, but can't you instead set it to modal.checkBind and then put that property in a higher scope?

How do I test HTML that has to be rendered with testacular and jasmine

I want to test if a directive binds the correct jquery functions to the DOM and that they work.
For instance i want to test if an element is visible after it was slid up with $.slideUp() or i want to execute a click event on an html input.
It seems that i need to somehow attach the compiled directive to the DOM to make this happen. I've watched the testing directives video on youtube where he says that it is possible but he doesnt mention how you do it.
I'm kinda stuck.
Heres the link to the failing test: http://plnkr.co/edit/PojXf8?p=preview
Couple of things:
First, if you want to use jQuery with Angular, it needs to be loaded before Angular is, so move its script tag before Angular's.
Second, you're right about attaching the directive to the DOM. In a beforeEach call, you can use jQuery to create a host div and then remove that div in an afterEach call.
Here that is in a fork of your plunk.
That said, you only need your div in the DOM to check for visibility, which I'm not even sure you need to do in most cases. For example, you could use Sinon to spy on the slideUp call to make sure it happens and rely on jQuery's tests that it actually works.
And you can always trigger clicks directly just by calling elm.click() using jQuery.

Resources