Привязка NgModel не работает внутри <form>для теста Жасмин - PullRequest
0 голосов
/ 31 августа 2018

В моем проекте Angular 5.2.0 у меня есть следующая структура:

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { FormsModule } from '@angular/forms';

import { AppComponent } from './app.component';


@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

app.component.ts

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

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  private _title = 'initial value';
  public get title(): string {
    return this._title;
  }
  public set title(v: string) {
    this._title = v;
  }
}

app.component.spec.ts

import { TestBed, async } from '@angular/core/testing';
import { AppComponent } from './app.component';
import { By } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms';
describe('AppComponent', () => {
  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        AppComponent
      ],
      imports: [FormsModule]
    }).compileComponents();
  }));
  it('should bind an input to a property', async(() => {
    const fixture = TestBed.createComponent(AppComponent);
    const app = fixture.debugElement.componentInstance;
    fixture.detectChanges();

    // Update the title input
    const inputElement = fixture.debugElement.query(By.css('input[name="title"]')).nativeElement;
    inputElement.value = 'new value';
    inputElement.dispatchEvent(new Event('input'));

    fixture.whenStable().then(() => {
      fixture.detectChanges();
      expect(app.title).toEqual('new value');
    });
  }));
});

И для следующих испытаний:

app.component.html

<input name="title" type="text" [(ngModel)]="title">

Но если я введу ввод в тег формы, тест не пройден:

app.component.html

<form>
  <input name="title" type="text" [(ngModel)]="title">
</form>

Chrome 67.0.3396 (Windows 7 0.0.0) AppComponent должен связать входные данные со свойством FAILED Ожидаемое «начальное значение» будет равно «новому значению».

Есть идеи, почему это происходит и как это исправить?

1 Ответ

0 голосов
/ 02 сентября 2018

Первое решение (с использованием fakeAsync + галочка):

it('should bind an input to a property', fakeAsync(() => {
  const fixture = TestBed.createComponent(AppComponent);
  const app = fixture.debugElement.componentInstance;
  fixture.detectChanges();
  tick();

  const inputElement = fixture.debugElement.query(By.css('input[name="title"]')).nativeElement;
  inputElement.value = 'new value';
  inputElement.dispatchEvent(new Event('input'));

  fixture.detectChanges();
  tick();

  expect(app.title).toEqual('new value');
}));

Второе решение (с использованием синхронизации и небольшого рефакторинга кода):

describe('AppComponent', () => {
  let fixture: ComponentFixture<AppComponent>;
  let app: AppComponent;

  beforeEach(async(() => {
    TestBed.configureTestingModule({...}).compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    app = fixture.debugElement.componentInstance;

    fixture.detectChanges(); // this call is required
  }));

  it('should bind an input to a property', async(() => {
    const inputElement = fixture.debugElement.query(By.css('input[name="title"]')).nativeElement;
    inputElement.value = 'new value';
    inputElement.dispatchEvent(new Event('input'));

    fixture.whenStable().then(() => {
      expect(app.title).toEqual('new value');
    });
  }));
  ...

Есть идеи, почему это происходит?

Согласно официальным документам Angular :

Формируемые на основе шаблонов формы делегируют создание своих элементов управления формам директивам. Чтобы избежать изменения после проверенных ошибок, эти директивы занимают более одного цикла для построения всего дерева управления. Это означает, что вы должны подождать, пока не произойдет следующий цикл обнаружения изменений, прежде чем манипулировать каким-либо из элементов управления из класса компонента.

Например, если вы внедрите элемент управления формы с помощью запроса @ViewChild (NgForm) и изучите его в хуке жизненного цикла ngAfterViewInit, вы обнаружите, что у него нет дочерних элементов. Вы должны запустить цикл обнаружения изменений с помощью setTimeout (), прежде чем сможете извлечь значение из элемента управления, проверить его действительность или установить для него новое значение.

p.s. Также была похожая проблема (dispatchEvent не запускает изменения ngModel # 13550) в Angular GitHub repo , вы также можете проверить это.

...