Angular 8 - 单元测试问题 TypeError:无法读取未定义的属性“名称”
我一直在尝试找出这里发生的问题,但我无法找出问题所在,也无法确定 TS 或 Spec.TS 文件中哪个名称导致错误。我需要您的帮助。
edit-task-dialog.component.ts 文件
import {Component, Inject, OnInit} from '@angular/core';
import {FormControl, Validators} from '@angular/forms';
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {HttpClient} from '@angular/common/http';
import {SessionService} from '../../services/session.service';
import {DialogData} from '../tasks/tasks.component';
@Component({
selector: 'app-edit-task-dialog',
templateUrl: './edit-task-dialog.component.html',
styleUrls: ['./edit-task-dialog.component.css']
})
export class EditTaskDialogComponent implements OnInit {
id: number;
task: any;
showActuals: boolean;
scheduledStart;
scheduledEnd;
actualStart;
actualEnd;
selectedCategory = '';
categories;
minDate;
minEndDate;
timeRegex = /^(?:(?:1[0-2]|0?[1-9]):[0-5]\d\s[AP][M])?$/;
isWrongDate = false;
nameFormControl = new FormControl('', [
Validators.required
]);
sDateFormControl = new FormControl('', [
Validators.required
]);
eDateFormControl = new FormControl('', [
Validators.required
]);
sTimeFormControl = new FormControl('', [
Validators.required,
Validators.pattern(this.timeRegex)
]);
eTimeFormControl = new FormControl('', [
Validators.required,
Validators.pattern(this.timeRegex)
]);
constructor(
public dialogRef: MatDialogRef<EditTaskDialogComponent>,
private http: HttpClient,
private sessionService: SessionService,
@Inject(MAT_DIALOG_DATA) public data: DialogData) {
this.minDate = new Date();
this.minEndDate = new Date();
}
onNoClick(): void {
this.dialogRef.close();
}
onYesClick(): void {
this.scheduledStart = this.dateConversion(this.task.ssTime, this.task.ssDate);
this.scheduledEnd = this.dateConversion(this.task.seTime, this.task.seDate);
// this.actualStart = this.dateConversion(this.task.asTime, this.task.asDate);
// this.actualEnd = this.dateConversion(this.task.aeTime, this.task.aeDate);
if (this.scheduledEnd < this.scheduledStart) {
this.isWrongDate = true;
} else {
this.isWrongDate = false;
const headers = {Authorization: 'Bearer ' + this.sessionService.getToken()};
const body = {
id: this.task.id,
name: this.task.name,
description: this.task.description,
category: this.task.category,
scheduledStart: this.scheduledStart,
scheduledEnd: this.scheduledEnd
// actualstart: this.actualStart,
// actualend: this.actualEnd
};
console.log('Log body before put: ', body);
console.log('BEFORE put');
this.http.put('http://localhost:8001/tasks/task', body, {headers}).subscribe({
next: data => console.log(data),
error: error => console.error('There was an error!', error)
});
this.dialogRef.close();
}
}
onActualsClick(): void {
this.showActuals = true;
console.log('Actual click');
}
dateConversion(time: string, date: Date): Date {
const tempDate = date;
const tempTime = time;
const parts = tempTime.match(/(\d+):(\d+) (AM|PM)/);
if (parts) {
let hours = parseInt(parts[1], 10);
const minutes = parseInt(parts[2], 10);
const tt = parts[3];
if (tt === 'PM' && hours < 12) { hours += 12; }
if (tt === 'AM' && hours === 12) {hours = 0; }
// console.log("hours: ", hours);
// console.log("minutes: ", minutes);
// console.log("date: ", date);
date.setHours(hours, minutes, 0, 0);
}
return tempDate;
}
ngOnInit() {
this.showActuals = false;
this.getCategory();
this.id = this.data.id;
this.task = this.data.task;
setTimeout(() => {
let date = this.task.scheduledstart.substring(0, this.task.scheduledstart.length - 5);
const ssDate = new Date(date);
const ssTime = ssDate.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
this.task.ssTime = ssTime;
this.task.ssDate = ssDate;
date = this.task.scheduledEnd.substring(0, this.task.scheduledEnd.length - 5);
const seDate = new Date(date);
const seTime = seDate.toLocaleTimeString([], {hour: '2-digit', minute: '2-digit'});
this.task.seTime = seTime;
this.task.seDate = seDate;
});
console.log('The task: ', this.task);
}
getCategory(): void {
const headers = { Authorization: 'Bearer ' + this.sessionService.getToken()
};
this.http.get('http://localhost:8001/category/mine', { headers }).subscribe({
next: data => {
this.categories = data;
console.log(this.categories);
},
error: error => console.error('There was an error!', error)
});
}
changeMinEndDate() {
this.minEndDate = this.task.ssDate;
}
}
edit-task-dialog.component.spec.ts 文件
/** Linked Issue: TMGP4-32: Update Task
*
* Author: Chavarria Leo
*
* Unit Test - Frontend
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { MatIconModule} from '@angular/material/icon';
import {CustomMaterialModule} from '../../modules/material.module';
import {HttpClient, HttpClientModule} from '@angular/common/http';
import {AmplifyService} from 'aws-amplify-angular';
import { BrowserDynamicTestingModule, platformBrowserDynamicTesting } from '@angular/platform-browser-dynamic/testing';
import {RouterTestingModule} from '@angular/router/testing';
import {CUSTOM_ELEMENTS_SCHEMA, DebugElement} from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { EditTaskDialogComponent } from './edit-task-dialog.component';
import {Component, Inject, OnInit} from '@angular/core';
import {FormControl, Validators} from '@angular/forms';
import {SessionService} from '../../services/session.service';
import {DialogData} from '../tasks/tasks.component';
import {
MatFormFieldModule,
MatInputModule,
MatDialogModule,
MatDialogRef,
MAT_DIALOG_DATA,
MatButtonModule,
MatToolbar,
MatRadioModule,
MatSelectModule
} from '@angular/material';
describe('EditTaskDialogComponent', () => {
let component: EditTaskDialogComponent;
let fixture: ComponentFixture<EditTaskDialogComponent>;
let de: DebugElement;
beforeEach(async(() => {
TestBed.resetTestEnvironment();
TestBed.initTestEnvironment(BrowserDynamicTestingModule,
platformBrowserDynamicTesting());
TestBed.configureTestingModule({
imports: [ HttpClientModule, BrowserAnimationsModule,
RouterTestingModule.withRoutes([]),
MatIconModule,
MatSelectModule,
CustomMaterialModule,
MatFormFieldModule,
MatInputModule,
MatDialogModule,
],
providers: [AmplifyService, HttpClient, {
provide: MatDialogRef,
useValue: {}},
{ provide: MAT_DIALOG_DATA, useValue: {}}],
schemas: [CUSTOM_ELEMENTS_SCHEMA],
declarations: [ EditTaskDialogComponent ]
})
.compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(EditTaskDialogComponent);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
这是 HTML 文件
<mat-toolbar-row>
<h1 mat-dialog-title>Edit Task "{{task.name}}"</h1>
<span class="example-spacer"></span>
<button mat-button (click)="onNoClick()">
<mat-icon class="example-icon" aria-hidden="false" aria-label="Example user verified icon">close</mat-icon>
</button>
</mat-toolbar-row>
</mat-toolbar>
<div mat-dialog-content>
<form>
<div fxLayout="column">
<mat-form-field>
<mat-label>Name</mat-label>
<input matInput [(ngModel)]="task.name" name="taskName" [formControl]="nameFormControl">
<mat-error *ngIf="nameFormControl.hasError('required')">
Name is <strong>required</strong>
</mat-error>
</mat-form-field>
<mat-form-field>
<mat-label>Category</mat-label>
<mat-select [(ngModel)]="task.category" name="taskCat">
<mat-option *ngFor="let category of categories" [value]="category.name">
{{category.name}}
</mat-option>
</mat-select>
</mat-form-field>
<mat-form-field>
<mat-label>Description</mat-label>
<input matInput [(ngModel)]="task.description" name="taskDesc">
</mat-form-field>`enter code here`
</div>
<div fxLayout="row" fxLayoutGap="15px">
<div fxFlex="">
<mat-form-field>
<mat-label>Start date</mat-label>
<input matInput [matDatepicker]="picker1" [min]="minDate" [(ngModel)]="task.ssDate" name="taskSDate"
[formControl]="sDateFormControl" (dateChange)="changeMinEndDate()">
<mat-datepicker-toggle matSuffix [for]="picker1"></mat-datepicker-toggle>
<mat-datepicker #picker1></mat-datepicker>
<mat-error *ngIf="sDateFormControl.hasError('required')">
Start Date is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div fxFlex="" fxLayoutGap="15px">
<mat-form-field>
<mat-label>Start Time</mat-label>
<input matInput [(ngModel)]="task.ssTime" name="taskssTime" [formControl]="sTimeFormControl">
<mat-error *ngIf="sTimeFormControl.hasError('required')">
Start Time is <strong>required</strong>
</mat-error>
<mat-error *ngIf="sTimeFormControl.hasError('pattern')"> Start Time must follow <strong>HH:MM PM|AM</strong> format
</mat-error>
</mat-form-field>
</div>
</div>
<div fxLayout="row" fxLayoutGap="15px">
<div fxFlex="">
<mat-form-field>
<mat-label>End date</mat-label>
<input matInput [matDatepicker]="picker2" [(ngModel)]="task.seDate" name="taskseDate"
[min]="minEndDate" [formControl]="eDateFormControl">
<mat-datepicker-toggle matSuffix [for]="picker2"></mat-datepicker-toggle>
<mat-datepicker #picker2></mat-datepicker>
<mat-error *ngIf="eDateFormControl.hasError('required')">
End Date is <strong>required</strong>
</mat-error>
</mat-form-field>
</div>
<div fxFlex="" fxLayoutGap="15px">
<mat-form-field>
<mat-label>End Time</mat-label>
<input matInput [(ngModel)]="task.seTime" name="taskseTime" [formControl]="eTimeFormControl" [disabled]="true">
<mat-error *ngIf="eTimeFormControl.hasError('required')"> End Time is <strong>required</strong>
</mat-error>
<mat-error *ngIf="eTimeFormControl.hasError('pattern')"> End Time must follow <strong>HH:MM PM|AM</strong> format
</mat-error>
</mat-form-field>
</div>
</div>
<!-- <button mat-button (click)="onActualsClick()" *ngIf="!showActuals"> Edit actuals <mat-icon class="example-icon" aria-hidden="false" aria-label="Edit actual times icon">edit</mat-icon></button>-->
<!-- <div *ngIf="showActuals">-->
<!-- <mat-divider></mat-divider>-->
<!-- <p style="color:purple; !important">Actual Times</p>-->
<!-- <div fxLayout="row" fxLayoutGap="15px">-->
<!-- <div fxFlex="">-->
<!-- <mat-form-field>-->
<!-- <mat-label>Start date</mat-label>-->
<!-- <input matInput [matDatepicker]="picker3" [(ngModel)]="task.asDate" name="taskasDate"-->
<!-- [formControl]="sDateFormControl" >-->
<!-- <mat-datepicker-toggle matSuffix [for]="picker3"></mat-datepicker-toggle>-->
<!-- <mat-datepicker #picker3></mat-datepicker>-->
<!-- <mat-error *ngIf="sDateFormControl.hasError('required')">-->
<!-- Start Date is <strong>required</strong>-->
<!-- </mat-error>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div fxFlex="" >-->
<!-- <mat-form-field>-->
<!-- <mat-label>Start Time</mat-label>-->
<!-- <input matInput [(ngModel)]="task.asTime" name="taskasTime" [formControl]="sTimeFormControl">-->
<!-- <mat-error *ngIf="sTimeFormControl.hasError('required')">-->
<!-- Start Time is <strong>required</strong>-->
<!-- </mat-error>-->
<!-- <mat-error *ngIf="sTimeFormControl.hasError('pattern')"> Start Time must follow <strong>HH:MM PM|AM</strong> format-->
<!-- </mat-error>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div fxLayout="row" fxLayoutGap="15px">-->
<!-- <div fxFlex="" >-->
<!-- <mat-form-field>-->
<!-- <mat-label>End date</mat-label>-->
<!-- <input matInput [matDatepicker]="picker4" [(ngModel)]="task.aeDate" name="taskaeDate" [formControl]="eDateFormControl">-->
<!-- <mat-datepicker-toggle matSuffix [for]="picker4"></mat-datepicker-toggle>-->
<!-- <mat-datepicker #picker4></mat-datepicker>-->
<!-- <mat-error *ngIf="eDateFormControl.hasError('required')">-->
<!-- End Date is <strong>required</strong>-->
<!-- </mat-error>-->
<!-- </mat-form-field>-->
<!-- </div>-->
<!-- <div fxFlex="">-->
<!-- <mat-form-field>-->
<!-- <mat-label>End Time</mat-label>-->
<!-- <input matInput [(ngModel)]="task.aeTime" name="taskaeTime">-->
<!-- </div>-->
<!-- </div>-->
<!-- </div>-->
</form>
</div>
<div mat-dialog-actions align="end" class="header-text">
<mat-error *ngIf="isWrongDate"> End date and time should be later than start date and time.
</mat-error>
<button mat-raised-button color="primary" (click)="onYesClick()" cdkFocusInitial
[disabled]="!eTimeFormControl.valid || !sTimeFormControl.valid || !eDateFormControl.valid ||
!sDateFormControl.valid || !nameFormControl.valid">Save</button>
</div>
我得到的错误 - 我也在使用 WebStorm 和 Karma 配置
TypeError: Cannot read property 'name' of undefined
error properties: Object({ ngDebugContext: DebugContext_({ view: Object({ def: Object({ factory: Function, nodeFlags: 939514883, rootNodeFlags: 33554433, nodeMatchedQueries: -268894785, flags: 0, nodes: [ Object({ nodeIndex: 0, parent: null, renderParent: null, bindingIndex: 0, outputIndex: 0, checkIndex: 0, flags: 33554433, childFlags: 650887171, directChildFlags: 4243457, childMatchedQueries: 2, matchedQueries: Object({ }), matchedQueryIds: 0, references: Object({ }), ngContentIndex: null, childCount: 13, bindings: [ Object({ flags: 2, ns: '', name: 'mat-toolbar-multiple-rows', nonMinifiedName: 'mat-toolbar-multiple-rows', securityContext: undefined, suffix: undefined }), Object({ flags: 2, ns: '', name: 'mat-toolbar-single-row', nonMinifiedName: 'mat-toolbar-single-row', securityContext: undefined, suffix: undefined }) ], bindingFlags: 2, outputs: [ ], element: Object({ ns: '', name: 'mat-toolbar', attrs: [ Array, Array, Array ], template: null, componentProvider: Object({ nodeIndex: 1, parent: ... at
我已将 HTML 文件与原始帖子一起发布,并希望获得一些有关该问题的帮助。
测试中有些表单尚未初始化。您需要模拟一个提供数据的服务。或者您可以找到未初始化的表单,并在代码的以下部分为其设置一个值:
beforeEach(() => {
fixture = TestBed.createComponent(EditTaskDialogComponent);
component = fixture.componentInstance;
/*component.eDateFormControl ={new Date()} --form not initialized */
fixture.detectChanges();
});