在单元测试中设置选择,以便自定义选择在Angular 5中不起作用

时间:2018-01-02 12:12:23

标签: angular unit-testing

我实现了一个自定义选择控件,它在内部使用本机html选择。 所选选项通过[(ngModel)]绑定到自定义选择。

在我的单元测试中,我初始化一个带有有效id的变量,以便在select元素中进行预选,但它不起作用。

以下是代码:

MY-select.component.html:

<select
  #select
  (change)="onChange($event)"
  (blur)="onBlur()">

  <option *ngFor="let optionItem of options"
          [value]="optionItem.id !== undefined ? optionItem.id : optionItem.key"
          [selected]="'' + optionItem.id === selectedOption || optionItem.key === selectedOption">
    {{ optionItem.value }}
  </option>

</select>

MY-select.component.ts

import {
  Component, ElementRef, EventEmitter, forwardRef, Input, OnChanges, OnInit, Output, SimpleChanges,
  ViewChild
} from '@angular/core';
import { NG_VALUE_ACCESSOR } from '@angular/forms';

@Component({
  selector: 'app-my-select',
  templateUrl: './my-select.component.html',
  styleUrls: ['./my-select.component.css'],
  providers: [{
    provide: NG_VALUE_ACCESSOR,
    useExisting: forwardRef(() => MySelectComponent),
    multi: true
  }]
})
export class MySelectComponent implements OnInit, OnChanges {
  /**
  * Internal select element.
  */
  @ViewChild('select') selectElement: ElementRef;

  @Input() options: any[];

  @Output() select = new EventEmitter<string | number>();

  /**
  * Currently selected option.
  */
  selectedOption?: string;

  constructor() {
  }

  ngOnInit() {
  }

  /**
  * Handling user's select change.
  * @param {Event} event
  */
  onChange(event: Event) {
    const element = <HTMLSelectElement>this.selectElement.nativeElement;
    this.selectedOption = element.options[element.selectedIndex].value;

    this.propagateAndEmitValue();
  }

  onBlur() {
    this.onTouched();
  }

  ngOnChanges(changes: SimpleChanges) {
    if (changes.options && changes.options.currentValue && changes.options.currentValue.length > 0) {
      if (!this.isSelectedOptionAvailableInOptions()) {
        this.selectFirstOption();
      }
    }
  }

  /**
  * Write a new value to the element.
  */
  writeValue(obj: any) {
    if (obj !== undefined && obj !== null) {
      this.selectedOption = '' + obj;
    } else {
      this.selectFirstOption();
      this.propagateAndEmitValue();
    }
  }

  /**
  * Set the function to be called when the control receives a change event.
  */
  registerOnChange(fn: any) {
    this.propagateChange = fn;
  }
  /**
  * Set the function to be called when the control receives a touch event.
  */
  registerOnTouched(fn: any) {
    this.onTouched = fn;
  }

  /**
  * the method set in registerOnChange, it is just
  * a placeholder for a method that takes one parameter,
  * we use it to emit changes back to the form
  * @param _
  */
  private propagateChange = (_: any) => { };

  /**
  * Property saves angular callback method that has to be called on touch event.
  */
  private onTouched = () => {};

  /**
  * Propagates new value to angular and emits select event.
  */
  private propagateAndEmitValue() {
    if (this.options) {
      const selectValue = parseInt(this.selectedOption || '0', 10);
      this.propagateChange(selectValue);
      this.select.emit(selectValue);
    }
  }

  /**
  * Check if selectedOption is a valid option, i.e. it is available in the options list.
  * @returns {boolean} true, if selectedOption is available, otherwise false
  */
  private isSelectedOptionAvailableInOptions() {
    return this.options.some(option => '' + option.id === this.selectedOption);
  }

  /**
  * Selects first select option.
  */
  private selectFirstOption() {
    this.selectedOption = '' + this.options[0].id;
  }
}

MY-select.component.spec.ts:

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FormsModule } from '@angular/forms';
import { By } from '@angular/platform-browser';
import { Component, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import {MySelectComponent} from './my-select.component';

@Component({
  template: `<app-my-select
    [options]="options"
    [(ngModel)]="selectedOption"
    (select)="select($event)"></app-my-select>`
})
class TestComponent {
  options: any[];
  selectedOption: string;

  select() {}
}

describe('MySelectComponent', () => {
  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;

  const idSelectOptionsStub = [
    { id: 0, value: '' },
    { id: 1, value: 'First Text' },
    { id: 2, value: 'Second Text' },
    { id: 3, value: 'Third Text' }
  ];

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [ FormsModule ],
      declarations: [ TestComponent, MySelectComponent ],
      schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
    })
      .compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
    component.options = idSelectOptionsStub;
    fixture.detectChanges();
  });

  it('should init with given id', () => {
    component.selectedOption = '2';

    const select = fixture.debugElement.query(By.css('select'));
    fixture.whenStable().then(() => {
      expect(select.nativeElement.selectedIndex).toEqual(2);
      expect(select.nativeElement.value).toEqual('2');
      expect(component.selectedOption).toEqual('2');
    });
  });
});

当运行唯一的单元测试&#39;&#39;应该使用给定的id&#39;我得到以下输出:

Chrome 63.0.3239 (Mac OS X 10.13.2) MySelectComponent should init with given id FAILED
  Expected 0 to equal 2.
      at http://localhost:9876/_karma_webpack_/webpack:/Users/pascalchorus/Development/select-test/src/app/my-select/my-select.component.spec.ts:53:50
      at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke Users/pascalchorus/Development/select-test/~/zone.js/dist/zone.js:388:1)
      at ProxyZoneSpec.webpackJsonp.../../../../zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke Users/pascalchorus/Development/select-test/~/zone.js/dist/proxy.js:79:1)
      at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke Users/pascalchorus/Development/select-test/~/zone.js/dist/zone.js:387:1)
  Expected '0' to equal '2'.
      at http://localhost:9876/_karma_webpack_/webpack:/Users/pascalchorus/Development/select-test/src/app/my-select/my-select.component.spec.ts:54:42
      at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke Users/pascalchorus/Development/select-test/~/zone.js/dist/zone.js:388:1)
      at ProxyZoneSpec.webpackJsonp.../../../../zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke Users/pascalchorus/Development/select-test/~/zone.js/dist/proxy.js:79:1)
      at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke Users/pascalchorus/Development/select-test/~/zone.js/dist/zone.js:387:1)
  Expected 0 to equal '2'.
      at http://localhost:9876/_karma_webpack_/webpack:/Users/pascalchorus/Development/select-test/src/app/my-select/my-select.component.spec.ts:55:40
      at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke Users/pascalchorus/Development/select-test/~/zone.js/dist/zone.js:388:1)
      at ProxyZoneSpec.webpackJsonp.../../../../zone.js/dist/proxy.js.ProxyZoneSpec.onInvoke Users/pascalchorus/Development/select-test/~/zone.js/dist/proxy.js:79:1)
      at ZoneDelegate.webpackJsonp.../../../../zone.js/dist/zone.js.ZoneDelegate.invoke Users/pascalchorus/Development/select-test/~/zone.js/dist/zone.js:387:1)

我的期望是原生选择应该最初选择ID为2的项目,但事实并非如此。

你能解释一下这里有什么问题吗?

您可以在GitHub上找到示例项目: https://github.com/pchorus/select-test

0 个答案:

没有答案