vendredi 24 août 2018

Unit testing component with @Input / @Output and reactive form

This is my component:

import { Component, OnInit, Input, Output, EventEmitter, ChangeDetectionStrategy, OnDestroy } from '@angular/core';
import { FormBuilder, FormGroup , Validators } from '@angular/forms';
import { Subscription } from 'rxjs';
import { Values } from '../_models/values';

@Component({
  selector: 'some',
  templateUrl: './my.component.html',
  styleUrls: ['./my.component.scss'],
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class Mycomponent implements OnInit, OnDestroy {

  @Input()
  values: Array<string>;

  @Output()
  selectedValues = new EventEmitter<Values>();

  private myForm: FormGroup;

  @Input()
  errorMsg: string;

  private selectSubscription: Subscription;

  constructor(private fb: FormBuilder) {}

  ngOnInit() {
    this.myForm = this.fb.group({
      'selectAll': [false],
      'values': [this.values, Validators.required]
    });

    this.selectSubscription = this.myForm.get('selectAll').valueChanges.subscribe(value => {
      this.changeSelection(value);
    });
  }

  submit(): void {
    console.log('called');

    console.log(this.myForm.value.values);

    const theSelectedValues = {
      vals: this.myForm.value.values
    };
    this.selectedValues.emit(theSelectedValues);
  }

  private changeSelection(selectAll: boolean): void {
    if (selectAll) {
      const valuesSelect = this.myForm.controls['values'];
      valuesSelect.disable();
    } else {
      this.myForm.controls['values'].enable();

    }
  }

  ngOnDestroy() {
    this.selectSubscription.unsubscribe();
  }

}

The template:

<form [formGroup]="myForm" (ngSubmit)="submit()">
  <fieldset>
    <mat-checkbox formControlName="all">Select all</mat-checkbox>
  </fieldset>
  <fieldset>
    <select id="chooser" formControlName="values" multiple>
      <option *ngFor="let val of values?.urls" [value]="val"></option>
    </select>
  </fieldset>
  <button mat-button [disabled]="myForm.invalid">Go!</button>
</form>
<div *ngIf="errorMsg"></div>

The test

import { async, ComponentFixture, TestBed } from '@angular/core/testing';

import { Mycomponent } from './my.component';
import { By } from '@angular/platform-browser';
import { FormBuilder } from '@angular/forms';
import { NO_ERRORS_SCHEMA } from '@angular/core';

describe('Mycomponent', () => {
  let component: Mycomponent;
  let fixture: ComponentFixture<Mycomponent>;


  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [],
      schemas: [NO_ERRORS_SCHEMA],
      declarations: [Mycomponent],
      providers: [
        FormBuilder      ]
    })
      .compileComponents();

  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(Mycomponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('should emit selected values', () => {

    spyOn(component.selectedValues, 'emit');
    component.values = ['abc', 'de'];

    fixture.detectChanges();

    expect(fixture.debugElement.queryAll(By.css('option')).length).toBe(2); // is 0

    const formDE = fixture.debugElement.query(By.css('form'));
    formDE.triggerEventHandler('ngSubmit', {});

    // urls: null
    expect(component.selectedValues.emit).toHaveBeenCalledWith({ vals: ['abc', 'de'] });
  });
});

The test fails because

a)

component.values = ['abc', 'de'];

Does not lead to the form having two option elements

and b)

expect(component.selectedValues.emit).toHaveBeenCalledWith({ vals: ['abc', 'de'] });

Is called, but with { vals: null }

The code works, the app itself works fine, just the test is failing.

How do I set up the form properly, the @Input element?

I have looked at some blog posts but have not been able to adapt them to my code.

Aucun commentaire:

Enregistrer un commentaire