我有一个下一个组件,它具有在Angular 9.1上编写的注册表单
import { Component, OnInit, EventEmitter, Input, Output } from '@angular/core';
import { FormBuilder, Validators, FormGroup, FormControl, FormGroupDirective, NgForm } from '@angular/forms';
@Component({
selector: 'app-sign-up-form',
templateUrl: './sign-up-form.component.html',
styleUrls: ['./sign-up-form.component.scss']
})
export class SignUpFormComponent implements OnInit {
@Output() submitedForm = new EventEmitter<any>();
@Input() accountExist = false;
public signUpForm: FormGroup;
public hidePassword = true;
constructor(private formBuilder: FormBuilder) {}
ngOnInit() {
this.signUpForm = this.formBuilder.group({
email: [
'',
[
Validators.required,
Validators.pattern('^([A-z0-9_-]+\.)*[A-z0-9_-]+@[a-z0-9_-]+(\.[a-z0-9_-]+)*\.[a-z]{2,6}$')
]
],
passwords: this.formBuilder.group({
password: [
'',
[
Validators.required,
Validators.pattern('^[a-zA-Z0-9]{8,25}$'),
]
],
confirmedPassword: [
'',
[
Validators.required,
Validators.pattern('^[a-zA-Z0-9]{8,25}$'),
]
]
},
{
validators: [
this.validatePasswords,
Validators.required
]
}
)
});
}
public onSubmit() {
if (this.signUpForm.valid) {
this.submitedForm.emit(this.signUpForm.value);
}
}
public customErrorStateMatcher() {
return {
isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
if (form.submitted && control.parent.invalid) {
return true;
} else if (control.invalid && control.touched) {
return true;
} else {
return false;
}
}
};
}
private validatePasswords(group: FormControl) {
const password = group.get('password').value;
const confirmPass = group.get('confirmedPassword').value;
return password === confirmPass ? null : { notSame: true };
}
}
并且有模板:
<div class="sign-up">
<h2 class="sign-up__header">Sign Up</h2>
<form class="sign-up__form"
[formGroup]="signUpForm">
<div class="sign-up__form-fields-container">
<mat-form-field class="sign-up__form-field"
hideRequiredMarker="true">
<mat-label>Email</mat-label>
<input placeholder="[email protected]"
matInput
formControlName="email" />
<mat-error *ngIf="signUpForm.get('email').hasError('required')">
You must enter a value
</mat-error>
<mat-error *ngIf="!signUpForm.get('email').hasError('required')">
Not a valid email
</mat-error>
</mat-form-field>
<div formGroupName="passwords">
<mat-form-field class="sign-up__form-field">
<mat-label style.color="white">Enter your password</mat-label>
<input matInput
formControlName="password"
[type]="hidePassword ? 'password' : 'text'" />
<mat-error *ngIf="signUpForm.get('passwords').get('password').hasError('required')">
You must enter a value
</mat-error>
<mat-error *ngIf="!signUpForm.get('passwords').get('password').hasError('required')">
Not a valid password
</mat-error>
<button type="button"
mat-icon-button
matSuffix
[attr.aria-label]="'Hide password'"
[attr.aria-pressed]="hidePassword"
(click)="hidePassword = !hidePassword">
<mat-icon *ngIf="!hidePassword">visibility</mat-icon>
<mat-icon *ngIf="hidePassword">visibility_off</mat-icon>
</button>
</mat-form-field>
<mat-form-field appearance="legacy"
class="sign-up__form-field">
<mat-label>Confirm password</mat-label>
<input matInput
type="password"
formControlName="confirmedPassword"
[errorStateMatcher]="customErrorStateMatcher()" />
<mat-error *ngIf="signUpForm.get('passwords').get('confirmedPassword').hasError('required')">
You must enter a value
</mat-error>
<mat-error *ngIf="this.signUpForm.get('passwords').get('confirmedPassword').value.length">
Password must match
</mat-error>
</mat-form-field>
</div>
</div>
<div class="sign-up__button-wrapper">
<button class="sign-up__submit"
(click)="onSubmit()"><span>SUBMIT</span></button>
<p *ngIf="accountExist"
class="sign-up__custom-error">Account already exist</p>
</div>
<div class="sign-up__account-exist">
<p class="sign-up__account-question">Already have an account ?</p>
<a class="sign-up__sign-in-link"
routerLink="/sign-in">Sign in</a>
<a mat-button
href="https://sportbuddies.herokuapp.com/oauth2/authorization/facebook"
class="sign-up__facebook-btn">
<span class="sign-up__facebook-icon"></span>
<span>Sign up with Facebook</span></a>
</div>
</form>
<div>
</div>
</div>
因此,我正在用Jasmine / Karma编写单元测试,以测试组件的创建,提交表单和验证。
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { BrowserModule, By } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { DebugElement } from '@angular/core';
import { SignUpFormComponent } from './sign-up-form.component';
describe('SignUpFormComponent', () => {
let component: SignUpFormComponent;
let fixture: ComponentFixture<SignUpFormComponent>;
let debug: DebugElement;
let el: HTMLElement;
beforeEach(async(() => {
TestBed.configureTestingModule({
declarations: [ SignUpFormComponent ],
imports: [
BrowserModule,
FormsModule,
ReactiveFormsModule
]
})
.compileComponents().then(() => {
fixture = TestBed.createComponent(SignUpFormComponent);
component = fixture.componentInstance;
debug = fixture.debugElement.query(By.css('form'));
el = debug.nativeElement;
});
}));
it('should create the form', () => {
fixture = TestBed.createComponent(SignUpFormComponent);
const app = fixture.debugElement.componentInstance;
expect(app).toBeTruthy();
});
it('should set submitted to true', async(() => {
component.onSubmit();
expect(component.signUpForm).toBeTruthy();
}));
it('should call the onSubmit method', async(() => {
fixture.detectChanges();
spyOn(component, 'onSubmit');
el = fixture.debugElement.query(By.css('button')).nativeElement;
el.click();
expect(component.onSubmit).toHaveBeenCalledTimes(0);
}));
it('form should be invalid', async(() => {
fixture.detectChanges();
component.signUpForm.controls['email'].setValue('');
component.signUpForm.controls['password'].setValue('');
component.signUpForm.controls['confirmedPassword'].setValue('');
expect(component.signUpForm.valid).toBeFalsy();
}));
it('form should be valid', async(() => {
component.signUpForm.controls['email'].setValue('[email protected]');
component.signUpForm.controls['password'].setValue('1234567F');
component.signUpForm.controls['confirmedPassword'].setValue('1234567F');
expect(component.signUpForm.valid).toBeTruthy();
}));
});
但是它显示了我的错误
Failed: Cannot read property 'setValue' of undefined
和
Failed: Cannot read property 'controls' of undefined
和
Failed: Cannot read property 'valid' of undefined
所以,为什么这个signUpForm没有被定义,我该怎么办?谢谢。
尝试将FormBuilder
添加到providers
中的TestBed.configureTestingModule
数组中。但是我敢打赌,真正的问题是创建组件时您没有调用fixture.detectChanges
,因此ngOnInit
无法运行。 fixture.detectChanges
之后的第一个createComponent
是ngOnInit
运行时。
因此将.then
更改为:
.compileComponents().then(() => {
fixture = TestBed.createComponent(SignUpFormComponent);
component = fixture.componentInstance;
debug = fixture.debugElement.query(By.css('form'));
el = debug.nativeElement;
fixture.detectChanges(); // add fixture.detectChanges so ngOnInit runs on all later tests
});
编辑:保留fixture.detectChanges
,但您看到passwords
为formBuilder.Group
,并且将其子项称为password
和confirmedPasword
。在HTML中,您可以使用formGroupName="passwords"
对此进行访问。
尝试:
component.signUpForm.controls['passwords'].controls['password'].setValue('1234567F');
component.signUpForm.controls['passwords'].controls['confirmedPassword'].setValue('1234567F');
无论哪种方式,您都以错误的方式访问您想要访问的内容。
我用2个答案的一部分重写了这个-最终-起作用了:
component.signUpForm.controls['email'].setValue('');
component.signUpForm.controls['passwords'].get('password').setValue('');
component.signUpForm.controls['passwords'].get('confirmedPassword').setValue('');