3

I have a Component which contains a very simple Reactive Form, with just one input field and one button to be used to submit the form.

Here the code of the html template

<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <input type="text" formControlName="idControl">
  <button type="submit" class="button" [disabled]="myForm.invalid">Submit Form</button>
</form>

and here the FormGroup within the Component code

@Component({
  selector: 'my-form',
  templateUrl: './my-form.component.html',
  styleUrls: ['./my-form.component.scss']
})
export class MyFormComponent implements OnInit {
  myForm = new FormGroup({
    idControl: new FormControl('' , [
      Validators.required
    ])
  });
.....
.....
}

Now I want to write a simple test for this Component, a test that basically says

  • first set the value of the input field to "Blah"
  • then click the button to submit the form
  • finally check that, after the click of the button, the value of the control named idControl is actually "Blah"

The following test example actually works

it('1.1 should update the value of the input field after an "input" event on the input field', () => {
    const inputVal = 'Blah';
    const input = fixture.nativeElement.querySelector('input');
    input.value = inputVal;

    input.dispatchEvent(new Event('input'));

    expect(fixture.componentInstance.myForm.value.idControl).toEqual(inputVal);
  });

This test though seems to me not to reflect exactly what I want to simulate, which is a click on the button. On the other hand, if I try to actually simulate the click event on the button, I am not able to create a successful test. For instance, the following test does not pass

it('1.2 should update the value of the input field after a "submit" event on the form', () => {
    const inputVal = 'Blah1';
    const input = fixture.nativeElement.querySelector('input');
    input.value = inputVal;
    const submitEl = fixture.debugElement.query(By.css('button'));
    submitEl.triggerEventHandler('click', null);
    fixture.detectChanges();

    expect(fixture.componentInstance.myForm.value.idControl).toEqual(inputVal);
  });

I have also tried different variants, e.g. using

fixture.debugElement.query(By.css('button')).nativeElement.click();

but nothing seems to work.

This is the test configuration code which I use

beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ InputFormComponent],
      imports: [ReactiveFormsModule]
    })
    .compileComponents();
  }));

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

My question is whether I am missing something about how to test a Form in Angular.

Picci
  • 16,775
  • 13
  • 70
  • 113
  • Quick thought - you may need a `fixture.whenStable().then(() => { fixture.detectChanges(); /* now test */ })` in there to allow the button to render on screen before clicking on it. – dmcgrandle Dec 30 '18 at 07:26

1 Answers1

0

Not a solution to the initial problem, but I found at least one way to change the value of an input. Instead of

const input = fixture.nativeElement.querySelector('input');
input.value = inputVal;

try

component.formControl.setValue(inputVal, { emitEvent: true });

The emitEvent option ensures that not only the value is changed but also it triggers the event as if a user just typed it.

Unfortunately this won't test the binding between template and component.

askuri
  • 345
  • 3
  • 10