2

I am trying to test the two way data binding of ngModel with the following code, but when I am running my test I always get: Expected '' to be '[email protected]', 'searchQuery property changes after text input'. Maybe it has something to do with the searchField.dispatchEvent part, but so far I couldn't figure out why the test is not changing the textContent of my displayField. The project was built with angular-cli": "1.0.0-beta.15. I tried to follow this guide but so far had no luck. Would be nice if you could help me make my test pass. I am not sure if I have to use fixture.whenStable() - as I've seen it used in the answer to another question - but I don't think that typing text into an input field is an asynchronous activity - I also implemented the sendInput() method mentioned in this question, but so far without any success.

This is my component:

import { Component, OnInit } from '@angular/core';

@Component({
  selector: 'app-search',
  templateUrl: './search.component.html',
  styleUrls: ['./search.component.css']
})
export class SearchComponent implements OnInit {
  searchQuery: string;
  active: boolean = false;

  onSubmit(): void {
    this.active = true;
  }
}

This is my component template:

<input id="name" [(ngModel)]="searchQuery" placeholder="customer">
<h2><span>{{searchQuery}}</span></h2>

And here are is my spec:

/* tslint:disable:no-unused-variable */

import {TestBed, async, ComponentFixture, tick} from '@angular/core/testing';
import { SearchComponent } from './search.component';
import {CommonModule} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {By} from "@angular/platform-browser";

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

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [SearchComponent],
      imports: [
        CommonModule,
        FormsModule
      ]
    });

    fixture = TestBed.createComponent(SearchComponent);
    component = fixture.componentInstance;
  });

  it('should bind the search input to the searchQuery variable', () => {   
    const searchInputText: string = '[email protected]';
    const searchField: HTMLInputElement = fixture.debugElement.query(By.css('input')).nativeElement;
    const displayField: HTMLElement = fixture.debugElement.query(By.css('span')).nativeElement;

    searchField.value = searchInputText;

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

    fixture.detectChanges();

    expect(displayField.textContent).toBe(searchInputText, 'searchQuery property changes after text input');
  });
});

Update: I changed my test to the following, which made it pass - as long as the input field is not inside a form tag:

/* tslint:disable:no-unused-variable */

import {TestBed, async, ComponentFixture, tick} from '@angular/core/testing';
import { SearchComponent } from './search.component';
import {CommonModule} from "@angular/common";
import {FormsModule} from "@angular/forms";
import {By} from "@angular/platform-browser";

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

  function sendInput(text: string, inputElement: HTMLInputElement) {
    inputElement.value = text;
    inputElement.dispatchEvent(new Event('input'));
    fixture.detectChanges();
    return fixture.whenStable();
  }

  beforeEach(done => {
      declarations: [SearchComponent],
      imports: [
        CommonModule,
        FormsModule
      ]
    });

    TestBed.compileComponents().then(() => {
      fixture = TestBed.createComponent(SearchComponent);
      component = fixture.componentInstance;
      fixture.detectChanges();
      done();
    })
  });

  it('should bind the search input to the searchQuery variable', done => {
    const searchInputText: string = '[email protected]';
    const searchField: HTMLInputElement = fixture.debugElement.query(By.css('#name')).nativeElement;

    sendInput(searchInputText, searchField).then(() => {
      expect(component.searchQuery).toBe(searchInputText);
      done();
    });
  });
});

Here is the updated template:

<form>
  <input id="name" [(ngModel)]="searchQuery" placeholder="customer" name="name" #name="ngModel">
</form>
<h2><span>{{searchQuery}}</span></h2>

The test result I get is: Expected undefined to be '[email protected]'.

4
  • 1
    You should TestBed.compileComponents().then(...) to make sure the component is correctly initialised. You may also need more waiting (resolving a .whenStable) for the component to have updated correctly. See e.g. github.com/textbook/known-for-web/blob/master/src/app/actor/… Commented Sep 27, 2016 at 9:00
  • Possible duplicate of Updating input html field from within an Angular 2 test Commented Sep 27, 2016 at 9:05
  • 1
    @jonrsharpe: Thanks this worked - but now when I place the input inside a form tag it doesn't seem to work again. <input class="form-control" id="customerSearch" [(ngModel)]="searchQuery" name="customerSearch"> Commented Sep 27, 2016 at 9:52
  • Please edit to give a minimal reproducible example then, what does "doesn't seem to work" mean? Maybe accept the duplicate and write another question? Commented Sep 27, 2016 at 9:59

0

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.