1

我有一个PhoneMask 指令,我将其应用于输入,它将去除非数字字符并根据我设置的掩码格式化数字(例如,输入1234567890将被格式化为(123) 456-7890)。掩码本身有效,但我想为它编写一个单元测试,但我在弄清楚如何做到这一点时遇到了问题。

电话掩码指令:

import {Directive, ElementRef, EventEmitter, Output} from '@angular/core';
import {NgControl} from '@angular/forms';

@Directive({
  selector: '[PhoneMask]',
  host: {
    '(ngModelChange)': 'onInputChange($event)'
  }
})
export class PhoneMaskDirective {

  @Output() rawChange: EventEmitter<string> = new EventEmitter<string>();

  constructor(public model: NgControl, private el: ElementRef) {
  }

  onInputChange(inputValue) {
    const selectionStart = this.el.nativeElement.selectionStart;
    const [rawNumber, cursor] = this.getRawNumberAndCursor(inputValue, selectionStart);
    const [formattedNumber, formattedCursor] = this.getFormattedNumberAndCursor(rawNumber, cursor);

    // Set input to formatted value
    this.model.valueAccessor.writeValue(formattedNumber);
    // Set cursor in the right spot
    this.el.nativeElement.selectionStart = this.el.nativeElement.selectionEnd = formattedCursor;

    this.rawChange.emit(rawNumber);

  }

  getRawNumberAndCursor(text: string, cursorPos: number): [string, number] {
    let rawNumber = '';
    let rawPos = cursorPos;
    for (let i = 0, len = text.length; i < len; i++) {
      if (/\d/.test(text[i])) {
        rawNumber += text[i];
      } else if (i < cursorPos) {
        rawPos--;
      }
    }
    return [rawNumber, rawPos];
  }

  getFormattedNumberAndCursor(numberVal: string, cursorPos: number): [string, number] {
    let formattedNumber = '';
    if (numberVal.length == 0) {
      formattedNumber = '';
    } else if (numberVal.length <= 3) {
      formattedNumber = numberVal.replace(/^(\d{0,3})/, '($1)');
    } else if (numberVal.length <= 6) {
      formattedNumber = numberVal.replace(/^(\d{0,3})(\d{0,3})/, '($1) $2');
    } else if (numberVal.length <= 10) {
      formattedNumber = numberVal.replace(/^(\d{0,3})(\d{0,3})(.*)/, '($1) $2-$3');
    } else {
      formattedNumber = numberVal.replace(/^(\d{0,3})(\d{0,3})(\d{0,4})(.*)/, '($1) $2-$3 $4');
    }
    for (let i = 0; i < cursorPos; i++) {
      if (/\D/.test(formattedNumber[i])) {
        cursorPos++;
      }
    }
    return [formattedNumber, cursorPos];
  }
}

PhoneMask 测试/规格:

import {PhoneMaskDirective} from './phone-mask.directive';
import {Component} from '@angular/core';
import {TestBed} from '@angular/core/testing';
import {By} from '@angular/platform-browser';
import {FormsModule} from '@angular/forms';

@Component({
  template: `
    <input PhoneMask [(ngModel)]="phone"/>
  `
})
class TestPhoneMaskDirectiveComponent {
}

describe('PhoneMaskDirective', () => {
  let fixture;
  let theInput;
  beforeEach(async () => {
    fixture = TestBed.configureTestingModule({
      imports: [FormsModule],
      declarations: [PhoneMaskDirective, TestPhoneMaskDirectiveComponent],
    })
      .createComponent(TestPhoneMaskDirectiveComponent);
    fixture.detectChanges();

    theInput = fixture.debugElement.query(By.directive(PhoneMaskDirective));
  });
  it('should find one instance of the directive', () => {
    expect(theInput).toBeTruthy();
  });

  it('should handle input of non-numeric characters', async () => {
    fixture.detectChanges();

    let element = theInput.nativeElement;
    element.value = 'bob123';
    element.dispatchEvent(new Event('input', {bubbles: true, cancelable: true}));
    fixture.detectChanges();
    fixture.whenStable().then(() => {

      expect(fixture.componentInstance.phone).toEqual('(123)');
    });
  });
});

在测试中,我期望输入 ofbob123会产生(123). 但是,当我运行测试时,我得到一个失败的结果Expected 'bob123' to equal '(123)'.

我尝试了async, fakeAsync, 以及detectChanges()andtick()调用的各种组合,甚至用setTimeout(). 输入的 valueAccessor 更新后如何访问模型的值?

4

0 回答 0