1

Based on the very first example in https://angular.io/guide/reactive-forms, I create the following dumb component:

@Component({
  selector: 'app-name-editor',
  templateUrl: './name-editor.component.html',
  styleUrls: ['./name-editor.component.css']
})
export class NameEditorComponent {

  name = new FormControl('');

  @Output('submitted') submitted = new EventEmitter<string>();

  onSubmit() { this.submitted.emit(this.name.value); }
}

... for which I would like to write a unit test that will validate that a value is submitted. This uses a TestHost as suggested in the https://angular.io/guide/testing#component-inside-a-test-host :

@Component({
  template: `
     <app-name-editor (submitted)=onSubmit($event)>
     </app-name-editor>
   `})
class TestHostComponent {
  submitted: string;
  onSubmit(data: string) { this.submitted = data; }
}

describe('NameEditorComponent', () => {
  let testHost: TestHostComponent;
  let fixture: ComponentFixture<TestHostComponent>;
  let editorDebugElt: DebugElement;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ NameEditorComponent, TestHostComponent ]
    });
    fixture = TestBed.createComponent(TestHostComponent);
    testHost = fixture.componentInstance;
    editorDebugElt = fixture.debugElement.query(By.directive(NameEditorComponent));
    fixture.detectChanges();
  });

  it('should capture data', () => {
    const compiled = fixture.debugElement.nativeElement;
    const nameInput = compiled.querySelector('input[type="text"]');
    expect(nameInput).toBeTruthy();
    nameInput.value = 'This is a test';
    fixture.detectChanges();

    // Find submit button
    const submitInput = compiled.querySelector('input[type="submit"]');
    expect(submitInput).toBeTruthy();

    // Trigger click action
    expect(testHost.submitted).toBeFalsy();
    submitInput.click();

    // Submitted
    expect(testHost.submitted).toBe('This is a test');    
  });
});

The test fails but I cannot see why. The input is populated with the value as shown below the test result. Any help would be very much appreciated.

test fails but input is populated

2 Answers 2

1

Instead of checking submitted with 'This is a test'. You can spy on emit method in testHost.submitted and check whether it is called with the value of form control.

it('should capture data', () => {
  const compiled = fixture.debugElement.nativeElement;
  spyOn(testHost.submitted, 'emit')
  testHost.name.value('This is a test')
  const submitInput = compiled.querySelector('input[type="submit"]');
  submitInput.click();
  expect(testHost.submitted.emit).toHaveBeenCalled();
  expect(testHost.submitted.emit).toHaveBeenCalledWith(testHost.name.value)
});
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks for the above but the problem I had was not about the submission detection. I now found what happened: 1) the value was set on the form, 2) the form was submitted immediately afterwards. As reactive forms are asynchronous, the form was submitted with a blank input.
0

The reason why the form submits a blank value is because reactive forms are asynchronous.

Editing the form and submitting immediately afterwards will submit a blank form as the edit is occurring asynchronously. The test now passes by adding a wait time of 500ms but it would be nice to know how to avoid the wait though:

    // Wait for asynchronous update on reactive form to happen
    setTimeout(() => {

      // Trigger click action
      expect(testHost.submitted).toBeFalsy();
      submitInput.click();

      // Submitted
      expect(testHost.submitted).toBe('This is a test');

    }, 500);

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.