Angular Testing

Testing a Simple Component

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';

describe('AppComponent Tests', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
    }).compileComponents();
  }));

  it('should create the app', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    expect(app).toBeTruthy();
  }));

  it('should render greeting', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    fixture.detectChanges();
    const compiled = fixture.debugElement.nativeElement;
    expect(compiled.querySelector('h1').textContent).toContain('Welcome!');
  }));
});

Testing Template


// EX1
import { async, TestBed } from '@angular/core/testing';
import { SomeComponent } from './some.component';

beforeEach(() => {
  TestBed.configureTestingModule({
    declarations: [
      SomeComponent
    ],
    imports: [
      // HttpModule, etc.
    ],
    providers: [
      // { provide: ServiceA, useClass: TestServiceA }
    ]
  });
});

it('should do something', async(() => {
  TestBed.compileComponents().then(() => {
    const fixture = TestBed.createComponent(SomeComponent);

    // Access the dependency injected component instance
    const app = fixture.componentInstance;
    expect(app.something).toBe('something');

    // Access native element
    const element = fixture.nativeElement;

    // detect changes
    fixture.detectChanges();
    expect(element.textContent).toContain('something');
  });
}));


// EX2
it('should get quote', () => {

  fixture.debugElement.componentInstance.getQuote();
  fixture.detectChanges();
  var compiled = fixture.debugElement.nativeElement;

  expect(compiled.querySelector('div'))
    .toHaveText('Test Quote'); 
});
​

// EX3
import {TestBed, ComponentFixture, inject, async} from '@angular/core/testing';
import {LandingComponent, User} from './landing.component';
import {Component, DebugElement} from "@angular/core";
import {By} from "@angular/platform-browser";


describe('Component: Landing', () => {

  let component: LoginComponent;
  let fixture: ComponentFixture;
  let submitEl: DebugElement;
  let usernameEl: DebugElement;

  beforeEach(() => {

    // refine the test module by declaring the test component
    TestBed.configureTestingModule({
      declarations: [LandingComponent]
    });


    // create component and test fixture
    fixture = TestBed.createComponent(LandingComponent);

    // get test component from the fixture
    component = fixture.componentInstance;

    submitEl = fixture.debugElement.query(By.css('button'));
    usernameEl = fixture.debugElement.query(By.css('input[type=text]'));
  });

  it('Setting enabled to false disabled the submit button', () => {
    component.enabled = false;
    fixture.detectChanges();
    expect(submitEl.nativeElement.disabled).toBeTruthy();
  });

  it('Setting enabled to true enables the submit button', () => {
    component.enabled = true;
    fixture.detectChanges();
    expect(submitEl.nativeElement.disabled).toBeFalsy();
  });

  it('Entering email and password emits loggedIn event', () => {
    let user: User;
    usernameEl.nativeElement.value = "test";

    // Subscribe to the Observable and store the user in a local variable.
    component.loggedIn.subscribe((value) => user = value);

    // This sync emits the event and the subscribe callback gets executed above
    submitEl.triggerEventHandler('click', null);

    // Now we can check to make sure the emitted value is correct
    expect(user.name).toBe("test");
  });
});


Test a Simple Class


import { countries } from './countries';
import { Util } from './util';

let testClass: Util = null;
describe('Util Tests', () => {
  beforeEach(() => {
    testClass = new Util();
  });

  it('should ensure getCountries is based on countries file', () => {
    const actual = testClass.getCountries;
    expect(actual[0].id).toEqual(countries[0].id);
  });
});

import { countries } from './countries';
export class Util {

  public countries: any[] = countries;

  public getProducts() {
    return this.countries;
  }
}

Mock Event

  const mockEvent: Event = {
    srcElement: {
      classList: ''
    },
    charCode: 64,
    stopPropagation: ( ( e: any ) => { /**/ }),
    preventDefault: ( ( e: any ) => { /**/ }),
  };

Service Testing
https://semaphoreci.com/community/tutorials/testing-services-in-angular-2
https://blog.realworldfullstack.io/real-world-angular-part-9-unit-testing-c62ba20b1d93
https://stackoverflow.com/questions/42440234/unit-test-a-angular2-service-that-uses-rxjs-subjects

Template Testing
https://codecraft.tv/courses/angular/unit-testing/components/

GAnalytics
https://codeburst.io/using-google-analytics-with-angular-25c93bffaa18

JS Utils

Palindrome JS

const isPalindrome = (str) => 
  str.toLowerCase().split('').reverse().join('') === str.toLowerCase();
console.log(isPalindrome('lEveL'));

JWT Token

Intereptor to Refresh Token
Ex1: https://medium.com/@alexandrubereghici/angular-tutorial-implement-refresh-token-with-httpinterceptor-bfa27b966f57

Ex2: https://codeforgeek.com/2018/03/refresh-token-jwt-nodejs-authentication/
Ex3: https://gist.github.com/Toilal/8849bd63d53bd2df2dd4df92d3b12f26

Minimalist Example:
Ex1: http://jasonwatmore.com/post/2018/05/23/angular-6-jwt-authentication-example-tutorial

Prevent SQL Injection

RegEx Playground: https://regex101.com/

  • 2 int followed by 4 caps: ^[A-Z]{2}[0-9]{4}$
  • Example2: ^[A-Za-z ][A-Za-z0-9!@#$%^&*’ ]{1,20}$

str.replace(/]*>/g, ”)

var str = '
<h2>Hello</h2>
';
str.replace(/]*&gt;/g, '');

Sort array of objects by ID

var data = [1, 2, 3, 4, 5]
  .map(el => {
    return {
      id: el,
      name: 'alpha',
      title: 'Mr.',
      description: 'laziest objects',
      age: (el * 10)
    };
  });
data.sort(function(a, b){return b.id - a.year});

Date Diff

d2 = new Date(2019, 3, 18);
d1 = new Date(2018, 9, 18);

Date.diffInMonths = function(d1, d2) {
  return Math.round((d2 - d1) / (60 * 30 * 60 * 24 * 1000));
}

Date.DateDiff = {

    inDays: function(d1, d2) {
        var t2 = d2.getTime();
        var t1 = d1.getTime();

        return parseInt((t2-t1)/(24*3600*1000));
    },

    inWeeks: function(d1, d2) {
        var t2 = d2.getTime();
        var t1 = d1.getTime();

        return parseInt((t2-t1)/(24*3600*1000*7));
    },

    inMonths: function(d1, d2) {
        var d1Y = d1.getFullYear();
        var d2Y = d2.getFullYear();
        var d1M = d1.getMonth();
        var d2M = d2.getMonth();

        return (d2M+12*d2Y)-(d1M+12*d1Y);
    },

    inYears: function(d1, d2) {
        return d2.getFullYear()-d1.getFullYear();
    }
}

Date.DateDiff.inDays(d1,d2);

 

Response Mapping

REST API
http://jsonplaceholder.typicode.com/users/

export class MyApp {
  private users = [];

  constructor(http: Http) {
    http.get('http://jsonplaceholder.typicode.com/users/')
        .flatMap((response) => response.json())
        .filter((person) => person.id > 5)
        .map((person) => "Mr. " + person.name)
        .subscribe((data) => {
          this.users.push(data);
        });
  }
}


On Console:
var users = [];
[
  {
    "id": 2,
    "name": "Ervin Howell",
    "username": "Antonette",
    "email": "Shanna@melissa.tv",
    "address": {
      "street": "Victor Plains",
      "suite": "Suite 879",
      "city": "Wisokyburgh",
      "zipcode": "90566-7771"
    },
    "phone": "010-692-6593 x09125",
    "website": "anastasia.net"
  },
  {
    "id": 3,
    "name": "Clementine Bauch",
    "username": "Samantha",
    "email": "Nathan@yesenia.net",
    "address": {
      "street": "Douglas Extension",
      "suite": "Suite 847",
      "city": "McKenziehaven",
      "zipcode": "59590-4157"
    },
    "phone": "1-463-123-4447",
    "website": "ramiro.info"
  },
  {
    "id": 5,
    "name": "Chelsey Dietrich",
    "username": "Kamren",
    "email": "Lucio_Hettinger@annie.ca",
    "address": {
      "street": "Skiles Walks",
      "suite": "Suite 351",
      "city": "Roscoeview",
      "zipcode": "33263"
    },
    "phone": "(254)954-1289",
    "website": "demarco.info"
  },
  {
    "id": 6,
    "name": "Mrs. Dennis Schulist",
    "username": "Leopoldo_Corkery",
    "address": {
      "street": "Norberto Crossing",
      "suite": "Apt. 950",
      "city": "South Christy",
      "zipcode": "23505-1337"
    },
    "phone": "1-477-935-8478 x6430",
    "website": "ola.org"
  }
].map((response) => response)
        .filter((person) => person.id > 3)
        .map((person) => { 
          return {
            name: 'Mr.' + person.name,
            company: 'laziest approach to constructing objs',
            id: person.id
          };
        })
        .map((data) => {
          users.push(data);
        });