Угловой материал Datepicker Карма Жасмин модульное тестирование дает ошибку «Не удается прочитать свойство« подписаться »из неопределенного» - PullRequest
0 голосов
/ 23 сентября 2019

Я пытаюсь написать модульные тесты для компонента, имеющего Angular Material Datepicker, но я получаю эту ошибку:

TypeError: Cannot read property 'subscribe' of undefined error properties: Object({ ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 939514899, rootNodeFlags: 1, nodeMatchedQueries: 4444720, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 1, childFlags: 939514899, directChildFlags: 1, childMatchedQueries: 4444720, matchedQueries: Object({ }), matchedQueryIds: 0, references: Object({ }), ngContentIndex: null, childCount: 62, bindings: [ ], bindingFlags: 0, outputs: [ ], element: Object({ ns: '', name: 'div', attrs: [ Array ], template: null, componentProvider: null, componentView: null, componentRendererType: null, publicProviders: null({ }), allProviders: null({ }), handleEvent: Function }), provider: null, text: null, query: null, ngContent: null }), Object({ nodeIndex: 1, parent: Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 1, childFlags: 939514899, directChi ... at <Jasmine> at new MatDatepickerInput (http://localhost:9876/_karma_webpack_/C:/Projects/v3/WebUi/softraPackages/node_modules/@angular/material/esm2015/datepicker.js:2515:1) at createClass (http://localhost:9876/_karma_webpack_/C:/Projects/v3/WebUi/softraPackages/node_modules/@angular/core/fesm2015/core.js:31997:1) at createDirectiveInstance (http://localhost:9876/_karma_webpack_/C:/Projects/v3/WebUi/softraPackages/node_modules/@angular/core/fesm2015/core.js:31806:1) at createViewNodes (http://localhost:9876/_karma_webpack_/C:/Projects/v3/WebUi/softraPackages/node_modules/@angular/core/fesm2015/core.js:44209:1) at createEmbeddedView (http://localhost:9876/_karma_webpack_/C:/Projects/v3/WebUi/softraPackages/node_modules/@angular/core/fesm2015/core.js:44068:1) at callWithDebugContext (http://localhost:9876/_karma_webpack_/C:/Projects/v3/WebUi/softraPackages/node_modules/@angular/core/fesm2015/core.js:45631:1) at Object.debugCreateEmbeddedView [as createEmbeddedView]

Если я закомментирую часть Datepicker изhtml тогда не сработает эта ошибка.В чем реальная проблема здесь?:?Нужно ли каким-либо образом смоделировать Datepicker или предоставить начальное значение / сначала при событии изменения значения формы?

table-filter.component.html

<div class="lib-table-filter">
  <mat-accordion>
    <mat-expansion-panel>
      ...

      <form *ngIf="filterForm"
            [formGroup]="filterForm">
        <div class="form-array-wrapper" formArrayName="filters">
          <div
            *ngFor="let filter of filterOptions; let i = index"
            [formGroupName]="i"
            class="form-array-item"
            [ngClass]="{ 'date-time-item': ValueTypesEnum.daterange === filter.type }">
            <div
              *ngIf="ValueTypesEnum.daterange === filter.type"
              class="date-time">
              <div class="date-time-from">
                <mat-form-field>
                  <input
                    matInput
                    class="clickable"
                    [matDatepicker]="df"
                    placeholder="{{ 'common.date.date-from' | appTranslate }}"
                    (dateChange)="onDateSet($event.value, i, 'dateFrom')"
                    (click)="df.open()"
                    formControlName="dateFrom"
                    readonly />
                  <mat-datepicker-toggle
                    matSuffix
                    [for]="df">
                  </mat-datepicker-toggle>
                  <mat-datepicker #df></mat-datepicker>
                </mat-form-field>
                <mat-form-field>
                  <mat-label>
                    {{ 'common.date.time-from' | appTranslate }}
                  </mat-label>
                  <input
                    matInput
                    #timeFromInput
                    class="clickable"
                    [format]="24"
                    [ngxTimepicker]="timeFrom"
                    [value]="getTimeValue(i, 'timeFrom')"
                    readonly >
                  <button
                    mat-button
                    mat-icon-button
                    matSuffix
                    (click)="timeFromInput.click()">
                    <mat-icon>access_time</mat-icon>
                  </button>
                </mat-form-field>

                <ngx-material-timepicker
                  #timeFrom
                  [cancelBtnTmpl]="cancelBtn"
                  [confirmBtnTmpl]="confirmBtn"
                  (timeSet)="onTimeSet($event, i, true)">
                </ngx-material-timepicker>
              </div>
              <div class="date-time-to">
                <mat-form-field>
                  <input
                    matInput
                    class="clickable"
                    [matDatepicker]="dt"
                    placeholder="{{ 'common.date.date-to' | appTranslate }}"
                    (dateChange)="onDateSet($event.value, i, 'dateTo')"
                    (click)="dt.open()"
                    formControlName="dateTo"
                    readonly />
                  <mat-datepicker-toggle
                    matSuffix
                    [for]="dt">
                  </mat-datepicker-toggle>
                  <mat-datepicker #dt></mat-datepicker>
                </mat-form-field>
                <mat-form-field>
                  <mat-label>
                    {{ 'common.date.time-to' | appTranslate }}
                  </mat-label>
                  <input
                    matInput
                    class="clickable"
                    [format]="24"
                    [ngxTimepicker]="timeTo"
                    #timeToInput
                    [value]="getTimeValue(i, 'timeTo')"
                    readonly >
                  <button
                    mat-button
                    mat-icon-button
                    matSuffix
                    (click)="timeToInput.click()">
                    <mat-icon>access_time</mat-icon>
                  </button>
                </mat-form-field>

                <ngx-material-timepicker
                  #timeTo
                  [cancelBtnTmpl]="cancelBtn"
                  [confirmBtnTmpl]="confirmBtn"
                  (timeSet)="onTimeSet($event, i, false)">
                </ngx-material-timepicker>
              </div>
            </div>
          </div>
        </div>

        ...
      </form>
    </mat-expansion-panel>
  </mat-accordion>
</div>

<ng-template #cancelBtn>
  <button
    mat-raised-button
    class="cancel-ticker">
    {{ 'common.cancel' | appTranslate }}
  </button>
</ng-template>
<ng-template #confirmBtn>
  <button
    mat-raised-button
    class="submit-ticker">
    {{ 'common.confirm' | appTranslate }}
  </button>
</ng-template>

table-filter.component.ts

@Component({
  selector: 'lib-table-filter',
  templateUrl: './table-filter.component.html',
  styleUrls: ['./table-filter.component.sass']
})
export class TableFilterComponent implements OnInit {
  @Input() filterOptions: ITableFilter[] = [];
  @Output() filter: EventEmitter<string> = new EventEmitter<string>();
  @Output() reset: EventEmitter<boolean> = new EventEmitter<boolean>();

  filterForm: FormGroup;
  ValueTypesEnum = ValueTypesEnum;
  private filterQuery: IFilterBEQuery = null;

  constructor(
    private fb: FormBuilder,
  ) { }

  ngOnInit(): void {
    if (this.filterOptions) {
      this.initForm();
    }
  }

onReset(): void {
  (<FormArray>this.filterForm.get('filters')).controls.map(control => {
    if (control.get('value')) {
      control.get('value').setValue('');
    }

    if (control.get('dateFrom')) {
      control.get('dateFrom').setValue('');
    }

    if (control.get('timeFrom')) {
      control.get('timeFrom').setValue('');
    }

    if (control.get('dateTo')) {
      control.get('dateTo').setValue('');
    }

    if (control.get('timeTo')) {
      control.get('timeTo').setValue('');
    }

    return control;
  });

  this.reset.emit(true);
}

  onDateSet(
    date: string,
    index: number,
    field: string
  ): void {
    (<FormArray>this.filterForm.get('filters'))
      .controls[index]
      .get(field)
      .setValue(new Date(date));
  }

  ...

  private initForm(): void {
    this.filterForm = this.fb.group({
      'filters': new FormArray([])
    });

    for (const filter of this.filterOptions) {
      if (filter.type === ValueTypesEnum.daterange) {
        (<FormArray>this.filterForm.get('filters')).push(
          new FormGroup({
            'dateFrom': new FormControl(''),
            'timeFrom': new FormControl(''),
            'dateTo': new FormControl(''),
            'timeTo': new FormControl('')
          })
        );
      } else {
        (<FormArray>this.filterForm.get('filters')).push(
          new FormGroup({
            'value': new FormControl(filter.value),
          })
        );
      }
    }
  }
}

table-filter.component.spec.ts

fdescribe('TableFilterComponent', () => {
  let component: TableFilterComponent;
  let fixture: ComponentFixture<TableFilterComponent>;

  let fb: jasmine.SpyObj<FormBuilder>;

  const MOCK_TABLE_DATA: ITableFilter[] = [
    {
      title: 'title-6',
      key: 'key-6',
      type: ValueTypesEnum.daterange
    },
  ];

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [ TableFilterComponent ],
      imports: [
        HttpClientModule,
        CommonModule,
        MatFormFieldModule,
        MatInputModule,
        MatButtonModule,
        MatSelectModule,
        FormsModule,
        ReactiveFormsModule,
        MatCheckboxModule,
        MatExpansionModule,
        AppTranslateModule,
        BrowserAnimationsModule,
        NgxMaterialTimepickerModule,
        MatDatepickerModule,
        MatNativeDateModule,
        MatIconModule,
      ],
      providers: [
        FormBuilder
      ]
    })
    .compileComponents();

    fb = TestBed.get(FormBuilder);

    fixture = TestBed.createComponent(TableFilterComponent);
    component = fixture.componentInstance;
  }));

  it('should create', () => {
    fixture.detectChanges();
    expect(component)
      .toBeTruthy();
  });

  it('should reset form', () => {
    component.filterOptions = MOCK_TABLE_DATA;
    fixture.detectChanges();

    let result: string = (<FormArray>component.filterForm.get('filters'))
      .value[1].value;
    expect(result)
      .toEqual(MOCK_TABLE_DATA[1].value);

    spyOn(component.reset, 'emit').and.callThrough();

    component.onReset();

    result = (<FormArray>component.filterForm.get('filters'))
      .value[1].value;

    expect(result)
      .toEqual('');
    expect(component.reset.emit)
      .toHaveBeenCalled();
    expect(component.reset.emit)
      .toHaveBeenCalledWith(true);
  });
});
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...