1/5/17

Unit Tests In Angular2 - part 1 (Get Started)

Get Started

Here is link to amazing tutorial for someone who wanted to learn from real angular developers...
Angular 2 has it's way for writing unit tests, it is pretty resembles the angular1. The same jasmine, only in typescript

Example of Testing Person Model

Lets start with models since they are usual javascript objects:
(person-model.ts):


export class PersonModel {

    constructor(
        public firstname: string = '',
        public lastname: string = ''
    ) {};

}

So here how the tests should look:
(person-model.spec.ts):

import {PersonModel} from './person-model';

describe('PersonModel', () => {

  it('should return the correct properties', () => {

      var person = new PersonModel();
      person.firstname = 'Shlomo';
      person.lastname = 'The King';

      expect(person.firstname).toBe('Shlomo');
      expect(person.lastname).toBe('The King');

  });

});

Nothing complicate here...

Testing Angular2 Service

Next step I want to show how to test some service.
Services are usually built for send http requests and bring data:


import {Injectable} from '@angular/core';
import {Http} from '@angular/http';
import 'rxjs/add/operator/map';

@Injectable()
export class UsersService {
  constructor(private http:Http) { }
  
  get(){
    return this.http.get('api/users.json')
      .map(response => response.json());
  }
}

The Users service has only one method - get
So, this is how the tests should look:
users.service.spec.ts

import {inject, TestBed, async} from '@angular/core/testing';
import {HttpModule} from '@angular/http';

import {UsersService} from './users.service'
describe('Service: UsersService', () => {
  let service;
  
  //setup
  beforeEach(() => TestBed.configureTestingModule({
    imports: [ HttpModule ],
    providers: [ UsersService ]
  }));
  
  beforeEach(inject([UsersService], s => {
    service = s;
  }));
  
  //specs
  it('should return available users (async)', async(() => {
    service.get().subscribe(x => { 
      expect(x).toContain('sasha');
      expect(x).toContain('vasia');
      expect(x).toContain('pasha');
      expect(x.length).toEqual(3);
    });
  }));
  

}) 

Note that I'm using TestBed utility to initiate ngModel and the service with injected Http.
here is working plunker

12/29/16

Unit Tests With Typescript

Hi lovely people my-gs500-blog-readers! I'm so glad to meet you again!
Today i want to speak about unit tests. Yes, a already wrote some series about unit testing angular1 directives, but now it is slightly different.
Why? Because i'm talking about angular typescript project.
Remember this angular-seed repo, we learned how to translate it to typescript(in this post)?
So, since the project is now contains only typescript (.ts) files - current tests would not work anymore (Because all the code that tests should be testing is not exists yet, it need to be transpilled to javascript)

Translate The Tests To Typescript

First thing to start with - lets translate all the tests to typescript: This is very simple, instead of:


'use strict';

describe('myApp.version module', function() {
  beforeEach(module('myApp.version'));

  describe('version service', function() {
    it('should return current version', inject(function(version) {
      expect(version).toEqual('0.1');
    }));
  });
});


now it is:

import * as angular from "angular";
import "angular-mocks";
import "phantomjs-polyfill";
import {version} from './version'
describe('myApp.version module', () => {
  beforeEach(angular.mock.module('myApp.version'));

  describe('version service', () => {
    it('should return current version', () => {
      expect(version).toEqual('0.1');
    });
  });
});

Not a big difference, right?

Karma Is A Bitch

Next things we need to change karma.conf file, to explain to karma: "you should load ts files now, baby"


...
files: [
    './node_modules/phantomjs-polyfill/bind-polyfill.js',
    './app/test.ts'
]
...


Processing With Webpack

For convert ts files to ES5 you need to use some transpiling tool, for example webpack:


    ...
    plugins: [
      require('karma-webpack'),
      require('karma-sourcemap-loader'),
      require('karma-jasmine'),
      require('karma-phantomjs-launcher'),
      require('karma-chrome-launcher')
    ],

    webpack: webpackConfig,
    webpackMiddleware: {
      stats: { chunks: false },
    },


    ...

For full code look in this repo

12/25/16

Add Ajax Request Indicator

Some times it is good practice to notify user that due to his actions (like sending the contact us form) some ajax request been sent and it is been processed. If you not reckon with nprogress angular lib yet, this good opportunity to introduce it... This library is angular implementation of nprogress jquery plugin, and its showing very nice loading bar on top of you application:

How to make it shown only if some ajax request made?

If you don't want to track any other requests like js and css files, only data requests to backend, it may be done by watching pendingRequests property of $http angular service:


myApp.run(function ($rootScope, $http, ngProgressFactory) {
        $rootScope.progrs = ngProgressFactory.createInstance();
        var progresstarted, progressbar = $rootScope.progrs;
        $rootScope.$watch(function() {
            var onlyJsonRequests = $http.pendingRequests.filter(function(r){return r.url.match(/json/g);});
            return onlyJsonRequests.length;
        }, function(n) {
             
            if(n>0 && !progresstarted) {
             
                progresstarted = true; 
                progressbar.setColor('blue');
                progressbar.setParent(document.getElementById('mainContainer')); //set a custom container, otherwise will be attached to "body"
               
                progressbar.start();
            } else if(n===0 && progresstarted) { 
                progressbar.complete(); 
                progresstarted = false;  
            }
        }) 
}) 

Notice that progress bar will be shown only if requests with url property that contains /json/ are sent

Provider is Better

Currently we have a lot of logic inside our run section.
One thing that can make the code more accurate is to move the progress logic to provider recipy:


myApp.provider('progress', function($provide, $injector) {
  this.setPattern = function(pattern) {
    this.pattern = new RegExp(pattern,'g');
  };
  this.setContainer = function(container) {
    this.container = container;
  };
  this.setColor = function(color) {
    this.color = color;
  };
  //will fire when used in one of recipies like "controller" or "directive" or "run"
  this.$get = function ($rootScope, $http, ngProgressFactory) {
        var that = this;
        $rootScope.progrs = ngProgressFactory.createInstance();
        var progresstarted, progressbar = $rootScope.progrs;
        $rootScope.$watch(function() {
            var onlyJsonRequests = $http.pendingRequests.filter(function(r){
              return r.url.match(that.pattern);
            });
            return onlyJsonRequests.length;
        }, function(n) {
            if(n>0 && !progresstarted) {
                progresstarted = true; 
                progressbar.setColor(that.color);
               progressbar.setParent(document.getElementById(that.container)); 
                progressbar.start();
            } else if(n===0 && progresstarted) { 
                progressbar.complete(); 
                progresstarted = false;  
            }
        }) 
        return 'watching'
   };
});

Now we can set all the custom settings inside config section:

myApp.config(function(progressProvider) {
  progressProvider.setContainer('mainContainer');
  progressProvider.setPattern('json');
  progressProvider.setColor('blue');

Now inside our run section we can leave only progress injection:

myApp.run(function (progress) {
}) 

If you want to see the full code - look on this plnkr

Getting started with docker

It is very simple to get started usig docker. All you need to do-is download the docker desktop for your system Once you get docker syste...