Cristian
Cristian

Reputation: 7165

Angular 2 Update Select by Changing Model in Unit Test

I have a select element in my template as follows:

<select [(ngModel)]="snapshotCriteria.salesArea" required>
  <option *ngFor="let salesArea of salesAreas" [ngValue]="salesArea">
    {{salesArea.name}}
  </option>
</select>

You can see that the model is bound to snapshotCriteria.salesArea which is an object so we use ngValue instead of value. The salesArea property itself is also an object.


The component is as follows:

export class Component implements OnInit {

  salesAreas: SalesArea[];

  snapshotCriteria: SnapshotCriteria = {
    salesArea: null,
    date: null,
    startTime: 600,
    endTime: 3059
  };


  constructor(private salesAreaService: SalesAreaService) {}


  ngOnInit(): void {
    this.salesAreaService.fetchSalesAreas(salesAreas) => this.salesAreas = salesAreas);
  }

}

Note that salesArea which is bound to the select model is initialised to null.


The template functions correctly when changes are made via the template by manipulating the dropdown. However in my unit tests, I directly update the model which the select element references and contrary to what I'd expect, the view is not updated! As the field is required, my test is unable to submit the form as validation fails. This is the test:

it('...', fakeAsync(() => {

  spyOn(salesAreaService, 'fetchSalesAreas').and.returnValue(Observable.of([
    {areaNumber: 1, name: 'A'} as any as SalesArea,
    {areaNumber: 2, name: 'B'} as any as SalesArea,
    {areaNumber: 3, name: 'C'} as any as SalesArea
  ]));

  fixture.detectChanges();
  tick();

  component.snapshotCriteria = {
    salesArea: {areaNumber: 1, name: 'A'} as SalesArea,
    date: new Date(2015, 5, 25),
    startTime: 1000,
    endTime: 1001
  } as SnapshotCriteria;

  fixture.detectChanges();
  tick();

}));

Debugging shows that the select element is not updated. I have tried various things to no avail:

  1. Not re-instantiating the object in the test.
  2. Instantiating the salesArea object to an empty object instead of null.
  3. Dispatching input and change events in the test.
  4. Listening to ngModelChange in the view but this is not triggered in the test.

A note of interest is that by using value over ngValue in the template, I am able to update the view by changing the underlying model.

Upvotes: 2

Views: 990

Answers (1)

yurzui
yurzui

Reputation: 214175

I assume you need to do as follows:

it('...', fakeAsync(() => {
  var mockArr = [
    {areaNumber: 1, name: 'A'} as any as SalesArea,
    {areaNumber: 2, name: 'B'} as any as SalesArea,
    {areaNumber: 3, name: 'C'} as any as SalesArea
  ];
  spyOn(salesAreaService, 'fetchSalesAreas').and.returnValue(Observable.of(mockArr));

  fixture.detectChanges();
  tick();

  component.snapshotCriteria = {
    salesArea: mockArr.find(x => x.areaNumber === 1),
    date: new Date(2015, 5, 25),
    startTime: 1000,
    endTime: 1001
  } as SnapshotCriteria;

  fixture.detectChanges();

  tick();
  expect(...);
}));

Plunker Example

Upvotes: 2

Related Questions