Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to test angularjs component with DOM

I am trying to get familiar with testing an AngularJS application. While testing component logic is clear more or less, I have a trouble with html templates and model binding, because I'd like to test html binding together with controller's logic.

Test is run by karma in a real browser, so testing environment supports DOM.

It looks like it's not possible, doesn't it?

describe('sign-up', function () {
        angular.mock.module('myApp');
        angular.mock.inject(function($componentController, $rootScope, $document) {
            let scope = $rootScope.$new();
            let signUp = $componentController('signUp', {$scope: scope});
            console.log(`signup = ${signUp}`);
            for (let [k,v] of signUp) {
                // there is no field signUp.firstName
                // but inside the controller code referencing  
                // this.firstName is working
                console.log(`signup.${k} = ${v}`);
            }
            // jquery cannot find #firstName node
            // $('#firstName').val('dan') gets the same outcome
            $document.find('#firstName').val('dan');
            expect($document.find('#firstName').val()).toBe('dan');
            // without DOM form submission is not possible
        });
    });
});

Controller component:

angular.
    module('myApp').
    component('signUp', {
        templateUrl: template,
        controller: [
            function () {
                this.form = {};
                var self = this;
}]});

Template:

  <form novalidate name="$ctrl.form" >
    <div class="form-group">
      <input type="text" class="form-control"
             ng-model="$ctrl.firstName"
             required
             name="firstName"
             id="firstName"
             />
    </div>
  </form>
like image 495
Daniil Iaitskov Avatar asked Jan 15 '18 09:01

Daniil Iaitskov


People also ask

What is a DOM element in Angular?

DOM stands for Document Object Model. AngularJS's directives are used to bind application data to the attributes of HTML DOM elements.

Is AngularJS code unit testable?

AngularJS is written with testability in mind, but it still requires that you do the right thing. We tried to make the right thing easy, but if you ignore these guidelines you may end up with an untestable application.


1 Answers

You can use $compile service to test your component templates ($componentController creates instances of component controllers without creating any markup, even $compile will not attach it to $document, so you have to use angular.element to check your template).

Here is a working example for your component:

angular.module('myApp', [])
    .component('signUp', {
        template: '<form novalidate name="$ctrl.form" >\n' +
        '    <div class="form-group">\n' +
        '      <input type="text" class="form-control"\n' +
        '             ng-model="$ctrl.firstName"\n' +
        '             required\n' +
        '             name="firstName"\n' +
        '             id="firstName"\n' +
        '             />\n' +
        '    </div>\n' +
        '  </form>',
        controller: 'SignUpController'
    })
    .controller('SignUpController', [function myComponentController() {
        var ctrl = this;
        ctrl.form = {};
    }]);

/*
TESTS GO HERE
*/

describe('Testing a component controller', function() {

    beforeEach(module('myApp', function ($provide) {

    }));

    beforeEach(inject(function ($injector) {

    }));

    describe('with $compile', function () {
        var element;
        var scope;
        var controller;

        beforeEach(inject(function ($rootScope, $compile) {
            scope = $rootScope.$new();
            element = angular.element('<sign-up></sign-up>');
            element = $compile(element)(scope);
            controller = element.controller('signUp');
            console.log(element);
            scope.$apply();
        }));

        it('should render template', function () {
            expect(element.find('input').val()).toBe('');
            controller.firstName = 'Dan';
            scope.$apply();
            expect(element.find('input').val()).toBe('Dan');
        });
    })
    
});
.as-console-wrapper {
  height:0;
}
<!DOCTYPE html>
<html>

  <head>
    <!-- jasmine -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.js"></script>
    <!-- jasmine's html reporting code and css -->
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine-html.js"></script>
    <link href="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/jasmine.css" rel="stylesheet" />
    
    <script src="//cdnjs.cloudflare.com/ajax/libs/jasmine/2.8.0/boot.js"></script>
    <!-- angular itself -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular.js"></script>
    <!-- angular's testing helpers -->
    <script src="//ajax.googleapis.com/ajax/libs/angularjs/1.6.4/angular-mocks.js"></script>
  </head>

  <body>
    <!-- bootstrap jasmine! -->
  <script>
    var jasmineEnv = jasmine.getEnv();
    
    // Tell it to add an Html Reporter
    // this will add detailed HTML-formatted results
    // for each spec ran.
    jasmineEnv.addReporter(new jasmine.HtmlReporter());
    
    // Execute the tests!
    jasmineEnv.execute();
  </script>
  </body>

</html>
like image 52
Stanislav Kvitash Avatar answered Sep 30 '22 00:09

Stanislav Kvitash