Невозможно проверить директиву, которая возвращает различные числовые форматы - PullRequest
0 голосов
/ 09 октября 2018

Я создал файл спецификации Jasmine для проверки директивы.Я в основном пытаюсь убедиться, что значение, установленное в моем компоненте, отформатировано при применении директивы.Я не уверен, нужно ли мне явно вызывать registerOnChange директивы в моем тесте, чтобы формат работал.RegisterOnChange является функцией обратного вызова.Я не уверен, нужно ли мне вызывать registerOnChange или метод Onchange.На данный момент я не могу изменить формат ввода.Я ожидаю, что 112500 возвращает разделенный запятыми вывод 112500. Может ли кто-нибудь указать, что я делаю неправильно.

Spec

@Component({
  template: `<form> <input id="itemDefault"  type="number" name="title" numberFormat="number:.0-0"></form>`,
  host: { '(blur)': 'blurred($event.target.value)' }
})
class NumberFormatComponent {
  public decimalPlaces = 2;
  private _item = '112500';

  get item() {
    return this._item;
  }

  set item(value) {
    this._item = value;
  }
}

describe('Directive: ShortNumberFormat', () => {
  let component: NumberFormatComponent;
  let fixture: ComponentFixture<NumberFormatComponent>;
  let elDefault: DebugElement;
  //let elDefaultDecimalPlaces: HTMLElement;
  let numberFormat : DebugElement;
  let translate: TranslateService;
  let des:any;
  beforeEach(() => {
    TestBed.configureTestingModule({
        imports: [
            TranslateModule.forRoot({
              loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
            })
          ],
      declarations: [NumberFormatComponent, NumberFormatDirective],
      providers: [{ provide: TranslateService, useClass: TranslateService }]
    });
    translate = TestBed.get(TranslateService);
    translate.setDefaultLang('en-US');
    translate.use('en-US');


    fixture = TestBed.createComponent(NumberFormatComponent);
    component = fixture.componentInstance;
    component.item;
    component.decimalPlaces;
    fixture.detectChanges();


  });




     fit('should toggle menu', () => {

  TestBed.compileComponents().then(() => {
   // const fixture = TestBed.createComponent(NumberFormatComponent);
    const elDefault = fixture.debugElement.query(By.directive(NumberFormatDirective));
    fixture.detectChanges();  
    //expect(directiveEl.nativeElement.getAttribute('numberFormat')).toEqual('number:.0-0');
    console.log(elDefault.nativeElement.getAttribute('numberFormat'));
    const directiveInstance = elDefault.injector.get(NumberFormatDirective);
    directiveInstance.format = elDefault.nativeElement.getAttribute('numberFormat');
    directiveInstance.inputType = elDefault.nativeElement.getAttribute('type');
    directiveInstance.onChange(component.item);
    console.log(component.item);
  }); 


});

Директива числового формата

import {
  Input, Directive, forwardRef, ExistingProvider, Renderer, ElementRef, PipeTransform,
  Inject, LOCALE_ID
} from '@angular/core';
import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms';
import { DecimalPipe, PercentPipe } from '@angular/common';
import { IntPercentPipe } from '../pipes/int-percent.pipe';
import { NumberPercentPipe } from '../pipes/number-percent.pipe';


const CUSTOM_VALUE_ACCESSOR: ExistingProvider = { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => NumberFormatDirective), multi: true };
const _NUMBER_FORMAT_REGEXP = /^(\d+)?\.((\d+)(\-(\d+))?)?$/;
const _NUMBER_MATCH_REGEXP = /-?\d+(\.\d+)?/;

function precision(a: number) {
  if (!isFinite(a)) return 0;
  let e = 1, p = 0;
  while (Math.round(a * e) / e !== a) { e *= 10; p++; }
  return p;
}
function round_number(num: number, decimals: number) {
  return Math.round(num * Math.pow(10, decimals)) / Math.pow(10, decimals);
}

@Directive({
  selector: 'input[numberFormat]',
  host: { '(blur)': 'blurred($event.target.value)' },
  providers: [CUSTOM_VALUE_ACCESSOR]
})
export class NumberFormatDirective implements ControlValueAccessor {
  @Input('numberFormat') format: string;
  @Input('type') inputType: string;
  private _parsedFormat: {
    pipe: {
      instance: PipeTransform,
      map?: (x: number) => number
    },
    numbers: {
      minimumIntegerDigits: number,
      minimumFractionDigits: number,
      maximumFractionDigits: number
    },
    raw: string;
  };

  constructor(private _renderer: Renderer, private _elementRef: ElementRef, @Inject(LOCALE_ID) private locale: string) { }

  onChange = (_: any) => { };
  onTouched = () => { };
  blurred(value: any) {
    this.onChange(value);
    this.onTouched();
  }

  formatToString(value: number): string {
    if (!this._parsedFormat) this.parseFormat();
    const pipe = this._parsedFormat.pipe;
    return pipe.instance.transform(value, this._parsedFormat.raw);
  }

  parseToNumber(value: string): number {
    value = value.replace(/,| /g, ''); // strip out commas and whitespaces
    const match = _NUMBER_MATCH_REGEXP.exec(value);
    if (!match) {
      this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', '');
      return undefined;
    }
    let number = parseFloat(match[0]);
    if (isNaN(number)) {
      this.writeValue(null);
      return null;
    }
    if (this._parsedFormat.numbers.maximumFractionDigits !== undefined) {
      number = round_number(number, this._parsedFormat.numbers.maximumFractionDigits);
    }
    const pipe = this._parsedFormat.pipe;
    if (pipe.map) number = pipe.map(number);
    this.writeValue(number);
    return number;
  }

  writeValue(value: number): void {
    if (this.inputType === 'number') {
      this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', value);
    } else {
      const text = this.formatToString(value);
      this._renderer.setElementProperty(this._elementRef.nativeElement, 'value', text);
    }
  }

  registerOnChange(fn: (_: number) => void): void {
    this.onChange = (value) => { fn(value === '' ? null : this.parseToNumber(value)); };
  }

  registerOnTouched(fn: () => void): void { this.onTouched = fn; }

  private parseFormat() {
    if (!this.format) throw new Error(`format string not set`);
    const split = this.format.split(':');
    const getPipe = (input: string): {
      instance: PipeTransform,
      map?: (x: number) => number
    } => {
      if (input === 'number') return { instance: new DecimalPipe(this.locale) };
      if (input === 'numberPercent') return { instance: new NumberPercentPipe(this.locale), map: x => parseFloat((x / 100).toFixed(precision(x) + 2)) };
      if (input === 'percent') return { instance: new PercentPipe(this.locale), map: x => parseFloat((x / 100).toFixed(precision(x) + 2)) };
      if (input === 'intPercent') return { instance: new IntPercentPipe(this.locale) };
      throw new Error(`unknown formatter pipe: ${input}`);
    };

    const digits = split[1];
    let minInt = 1, minFraction = 0, maxFraction = 3;
    if (!!digits) {
      const parts = _NUMBER_FORMAT_REGEXP.exec(digits);
      if (!(parts)) {
        throw new Error(`${digits} is not a valid digit info for number pipes`);
      }
      if (!!parts[1]) {  // min integer digits
        minInt = parseInt(parts[1]);
      }
      if (!!parts[3]) {  // min fraction digits
        minFraction = parseInt(parts[3]);
      }
      if (!!parts[5]) {  // max fraction digits
        maxFraction = parseInt(parts[5]);
      }
    }
    this._parsedFormat = {
      pipe: getPipe(split[0]), numbers: {
        minimumIntegerDigits: minInt,
        minimumFractionDigits: minFraction,
        maximumFractionDigits: maxFraction,
      }, raw: digits
    };

  }
}
...