2/3/19

Testing Angular directive using Jasmine Marbles

Directive in contemporary angular - means piece of code which doing something on the html block is attached to as attribute.

For example lets take a directive which changes value 'text-align' css rule, depending on "currentLang" property of translate service.

import { Directive, ElementRef, OnInit, OnDestroy } from '@angular/core';
import { TranslateService, LangChangeEvent } from '@ngx-translate/core';
import { Subscription } from 'rxjs';

@Directive({
  selector: '[rtl]'
})
export class RtlSupportDirective implements OnInit, OnDestroy {
  private subscription: Subscription;
  constructor(private el: ElementRef, public translate: TranslateService) {
    el.nativeElement.style.textAlign =
      translate.currentLang === 'he' ? 'right' : 'left';
 
  }
  ngOnInit() {
    this.subscription = this.translate.onLangChange.subscribe(
      (event: LangChangeEvent) => {
        this.el.nativeElement.style.textAlign =
          event.lang === 'he' ? 'right' : 'left';
      }
    );
  }

  ngOnDestroy() {
    if (this.subscription) {
      this.subscription.unsubscribe();
    }
  }
}
The idea is that Hebrew is "rtl" (right to left) language, and thus - the align must be changed to "right" in case of Hebrew.

How To Test

As you can see - the directive doing only two things:
1. setting text align according to current language
2. changing text align of element when language changed

Before Testing

Prior to testing directive - we need to create some component is will be placed on:


@Component({
  template: `
  <h2 rtl>Something Yellow</h2>
  `
})
class TestComponent {}

First Test

Testing the first behaviour is very easy: you only need to access the HTML element and check if "test-align" style property is set correctly:



  let fixture: ComponentFixture;
  let des: DebugElement[];

  beforeEach(() => {
    fixture = TestBed.configureTestingModule({
      imports: [TranslateModule.forRoot()],
      declarations: [RtlSupportDirective, TestComponent],
      providers: [
        {
          provide: TranslateService,
          useValue: {
            currentLang: 'he',
            onLangChange: of({lang: 'he'})
          }
        }
      ]
    }).createComponent(TestComponent);
    fixture.detectChanges(); // initial binding

    // all elements with an attached RtlDirective
    des = fixture.debugElement.queryAll(By.directive(RtlSupportDirective));

  });



  it('should set "text-align" rule value to "right" if current language is hebrew', () => {
    const textAlign = des[0].nativeElement.style.textAlign;
    expect(textAlign).toBe('right');
  });

Marbles

How to test the handling of "onLangChange" event (which is observable)?
One strategy in such scenario - is to use jasmine marbles


import { cold, getTestScheduler } from 'jasmine-marbles';
...
...
...

  beforeEach(() => {
    fixture = TestBed.configureTestingModule({
      imports: [TranslateModule.forRoot()],
      declarations: [RtlSupportDirective, TestComponent],
      providers: [
        {
          provide: TranslateService,
          useValue: {
            currentLang: 'he',
            onLangChange: cold('--x--y|', { //<-- here is the magic!
              x: { lang: 'he' },
              y: { lang: 'de' }
            })
          }
        }
      ]
    }).createComponent(TestComponent);

    getTestScheduler().flush(); // flush the observables
    fixture.detectChanges(); // initial binding
Note that first time the "flush" command is firing at "beforeEach" block and makes the language to be set with "he" value.

Testing The Change

To test handling of language change - we must simulate the "onLangChange" fired one more time
Since we use jasmine-marbles - to make this happen we only need to call "getTestScheduler().flush()"


  it('should set "text-align" rule value to "left" after current language changed to NOT hebrew', () => {
    getTestScheduler().flush(); // flush the observables
    fixture.detectChanges();

    const textAlign = des[0].nativeElement.style.textAlign;
    expect(textAlign).toBe('left');

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...