0

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">{{val}}</option>
    </select>
  </fieldset>
  <button mat-button [disabled]="myForm.invalid">Go!</button>
</form>
<div *ngIf="errorMsg">{{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.

1
  • Don't call fixture.detectChanges for starters : this is useful for HTML changes, not for variables. This could cause your issue. Next, don't need to test if you indeed have two options : you don't have to test what Angular (or Material) do, that's not your business. Finally, don't trigger the form by using the query selector : test your function directly. Commented Aug 24, 2018 at 8:56

1 Answer 1

1

This is because you are using Onpush strategy. When onPush is used change detection is propagating down from the parent component not the component itself.

My suggestion is to wrap your test component inside a host component. There is a pending issue regarding this on Angular's gitHub page you can look it up for further reading.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.