Я пытаюсь написать модульные тесты для компонента, имеющего 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);
});
});