Angular “角度材质2”对话框,表单测试失败

Angular “角度材质2”对话框,表单测试失败,angular,unit-testing,angular-material,dialog,Angular,Unit Testing,Angular Material,Dialog,我有一个使用Angular Material 2生成的简单登录对话框 和角反应形式。 在程序中使用该对话框时,它的工作方式应该是正常的,但是它的单元测试并没有反映这一点。表单登录按钮应被禁用,直到名称和密码字段通过组件中设置的验证标准,此时登录按钮被启用并可供单击。 但是,当我运行测试并将名称和输入字段设置为有效内容时,登录按钮保持禁用状态,测试失败。 测试代码的相关部分如下所示 it('should enable the login button when a valid username

我有一个使用Angular Material 2生成的简单登录对话框 和角反应形式。
在程序中使用该对话框时,它的工作方式应该是正常的,但是它的单元测试并没有反映这一点。表单登录按钮应被禁用,直到名称和密码字段通过组件中设置的验证标准,此时登录按钮被启用并可供单击。
但是,当我运行测试并将名称和输入字段设置为有效内容时,登录按钮保持禁用状态,测试失败。
测试代码的相关部分如下所示

  it('should enable the login button when a valid username and password are entered', fakeAsync(() => {
    (overlayContainerElement.querySelector('input[formcontrolname="name"]') as HTMLInputElement).value = 'ABC';
    (overlayContainerElement.querySelector('input[formcontrolname="password"]') as HTMLInputElement).value = '12345678';
    viewContainerFixture.detectChanges();
    tick();

    viewContainerFixture.detectChanges();
    const loginBtn = overlayContainerElement.querySelector('button[md-raised-button]');
    const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]');
    const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]');
    console.log('Login Button is:', loginBtn.textContent);
    console.log('Login Button is:', loginBtn.getAttribute('ng-reflect-disabled'));

    expect((nameInput as HTMLInputElement).value).toEqual('ABC');
    expect((passwordInput as HTMLInputElement).value).toEqual('12345678');
    expect((overlayContainerElement.querySelector('button[md-raised-button]')).getAttribute('ng-reflect-disabled')).toBe('false');
  }));
我显然没有刷新登录按钮的状态,但不明白为什么会这样

任何帮助都将不胜感激

是指向plunker的链接,其中显示了组件和测试套件,代码如下所示

组件

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { MdDialog, MdDialogRef } from '@angular/material';


@Component({
  selector: 'lpa-login-dialog',
  templateUrl: './login-dialog.component.html',
})
export class LoginDialogComponent implements OnInit {
  loginForm: FormGroup;

  constructor(
    private fb: FormBuilder,
    private dlgRef: MdDialogRef<LoginDialogComponent>
  ) {
    this.createForm()
  }

  ngOnInit() {
  }

  private createForm() {
    this.loginForm = this.fb.group({
      name: ['', [Validators.required, Validators.minLength(3)]],
      password: ['', [Validators.required, Validators.minLength(8)]]
    })
  }

  public login() {
    this.dlgRef.close(this.loginForm.value.name);
  }

}
从'@angular/core'导入{Component,OnInit};
从“@angular/forms”导入{FormBuilder、FormGroup、Validators};
从“@angular/material”导入{MdDialog,MdDialogRef};
@组成部分({
选择器:“lpa登录对话框”,
templateUrl:'./login dialog.component.html',
})
导出类LoginDialogComponent实现OnInit{
loginForm:FormGroup;
建造师(
私人fb:FormBuilder,
私有dlgRef:MdDialogRef
) {
这个
}
恩戈尼尼特(){
}
私有createForm(){
this.loginForm=this.fb.group({
名称:['',[Validators.required,Validators.minLength(3)],
密码:[''[Validators.required,Validators.minLength(8)]]
})
}
公共登录(){
this.dlgRef.close(this.loginForm.value.name);
}
}
HTML

<h1 class="mdl-dialog-title" style="text-align: center">App Login</h1>

<form [formGroup]="loginForm" (ngSubmit)="login()" ngnovalidate>
  <div class="mdl-dialog-content">
    <div class="form-group">
      <md-input-container style="width: 100%">
        <input mdInput class="form-control" formControlName="name" placeholder="Name">
      </md-input-container>
    </div>
    <div class="form-group">
      <md-input-container style="width: 100%">
        <input mdInput type="password" class="form-control" formControlName="password" placeholder="Password">
      </md-input-container>
    </div>
  </div>

  <div class="mdl-dialog-actions" style="text-align: center">
    <button md-raised-button color="primary" type="submit" [disabled]="!loginForm.valid" >Login</button>
    <button md-button md-dialog-close=false color="warn">Cancel</button>
  </div>
</form>
应用程序登录
登录
取消
单元测试(.spec)

import{inject,async,fakeAsync,flushMicrotasks,ComponentFixture,TestBed,tick,}从'@angular/core/testing'导入;
从“@angular/core”导入{NgModule,Component,Directive,ViewChild,ViewContainerRef,Injector,injection,DebugElement};
从“@angular/platform browser”导入{By}”;
从“@angular/forms”导入{ReactiveFormsModule、FormBuilder、FormGroup、Validators};
从“@angular/platform browser/animations”导入{NoopAnimationsModule};
从“@angular/material”导入{MaterialModule、MdDialogModule、MdDialog、MdDialogRef、MdButton、OverlyContainer};
从“rxjs/Observable”导入{Observable};
从'rxjs/Subscriber'导入{Subscriber};
从“./login dialog.component”导入{LoginDialogComponent};
//助手类
//tslint:禁用下一行:指令选择器
@指令({selector:'dir with view container'})
类DlgTestViewContainerDirective{
构造函数(公共viewContainerRef:viewContainerRef){}
}
@组成部分({
选择器:“lpa任意组件”,
模板:``,
})
类DlgTestChildViewContainerComponent{
@ViewChild(DlgTestViewContainerDirective)具有ViewContainer的子级:DlgTestViewContainerDirective;
获取childViewContainer(){
返回this.childWithViewContainer.viewContainerRef;
}
}
//创建一个真实(非测试)NgModule作为
// https://github.com/angular/angular/issues/10760
常量测试_指令=[
DlgTestViewContainerDirective,
DlgTestChildViewContainerComponent,
物流组件
];
@NGD模块({
进口:[
MdDialogModule,
反应形式模块,
材料模块,
NoopAnimationsModule
],
导出:测试单元指令,
声明:TEST_指令,
入口组件:[
物流组件
]
})
类对话框TestModule{}
描述('登录对话框组件',()=>{
let dialog:MdDialog;
让dialogRef:MdDialogRef;
let overlycontainerement:HTMLElement;
让viewContainerFixture:ComponentFixture;
beforeach(异步(()=>{
TestBed.configureTestingModule({
进口:[
DialogTestModule,
],
声明:[
],
供应商:[
{
提供:OverlyContainer,useFactory:()=>{
OverlyContaineRelement=document.createElement('div');
返回{getContainerElement:()=>OverlyContainerElement};
}
}
]
})
.compileComponents();
}));
beforeach(注入([MdDialog],(d:MdDialog)=>{
dialog=d;
}));
在每个之前(()=>{
viewContainerFixture=TestBed.createComponent(DlgTestChildViewContainerComponent);
viewContainerFixture.detectChanges();
dialogRef=dialog.open(LoginDialogComponent);
viewContainerFixture.detectChanges();
});
它('should created',fakeAsync(()=>{
expect(LoginDialogComponent的dialogRef.componentInstance instanceof).toBe(true,‘打开失败’);
expect(overlycontainerement.querySelector('h1').innerText).toEqual('applogin');
dialogRef.close();
勾选(500);
viewContainerFixture.detectChanges();
}));
它('按下取消按钮时应关闭并返回false',异步(()=>{
const afterCloseCallback=jasmine.createSpy('afterCloseCallback');
dialogRef.afterClosed().subscribe(afterCloseCallback);
(OverlyContaineRelement.querySelector('button[md dialog close=“false”]”)作为HTMLElement)。单击();
viewContainerFixture.detectChanges();
viewContainerFixture.whenStable()。然后(()=>{
expect(overlycontainerement.querySelector('md-dialog-container')).toBeNull('对话框仍然打开');
期望(在CloseCallback.toHaveBeenCalledWith('false');
});
}));
描述('应禁用登录按钮',()=>{
它('没有用户和密码输入',fakeAsync(()=>{
const btn=OverlyContainerElement.querySelector('按钮[md升起的按钮]);
expect(btn.getAttribute('ng-reflect-disabled')).toBe('true');
dialogRef.close()
勾选(500);
viewContainerFixture.detectChanges();
}));
它('有用户输入但没有密码输入'),异步(()=>{
import { inject, async, fakeAsync, flushMicrotasks, ComponentFixture, TestBed, tick, } from '@angular/core/testing';
import { NgModule, Component, Directive, ViewChild, ViewContainerRef, Injector, Inject, DebugElement } from '@angular/core';
import { By } from '@angular/platform-browser';
import { ReactiveFormsModule, FormBuilder, FormGroup, Validators } from '@angular/forms';

import { NoopAnimationsModule } from '@angular/platform-browser/animations';

import { MaterialModule, MdDialogModule, MdDialog, MdDialogRef, MdButton, OverlayContainer } from '@angular/material';

import { Observable } from 'rxjs/Observable';
import { Subscriber } from 'rxjs/Subscriber';

import { LoginDialogComponent } from './login-dialog.component';

// helper classes
// tslint:disable-next-line:directive-selector
@Directive({ selector: 'dir-with-view-container' })
class DlgTestViewContainerDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

@Component({
  selector: 'lpa-arbitrary-component',
  template: `<dir-with-view-container></dir-with-view-container>`,
})
class DlgTestChildViewContainerComponent {
  @ViewChild(DlgTestViewContainerDirective) childWithViewContainer: DlgTestViewContainerDirective;

  get childViewContainer() {
    return this.childWithViewContainer.viewContainerRef;
  }
}

// Create a real (non-test) NgModule as a workaround for
// https://github.com/angular/angular/issues/10760
const TEST_DIRECTIVES = [
  DlgTestViewContainerDirective,
  DlgTestChildViewContainerComponent,
  LoginDialogComponent
];

@NgModule({
  imports: [
    MdDialogModule,
    ReactiveFormsModule,
    MaterialModule,
    NoopAnimationsModule
  ],
  exports: TEST_DIRECTIVES,
  declarations: TEST_DIRECTIVES,
  entryComponents: [
    LoginDialogComponent
  ]
})
class DialogTestModule { }

describe('Login Dialog Component', () => {

  let dialog: MdDialog;
  let dialogRef: MdDialogRef<LoginDialogComponent>;

  let overlayContainerElement: HTMLElement;

  let viewContainerFixture: ComponentFixture<DlgTestChildViewContainerComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports: [
        DialogTestModule,
     ],
      declarations: [
      ],
      providers: [
        {
          provide: OverlayContainer, useFactory: () => {
            overlayContainerElement = document.createElement('div');
            return { getContainerElement: () => overlayContainerElement };
          }
        }
      ]
    })
      .compileComponents();
  }));

  beforeEach(inject([MdDialog], (d: MdDialog) => {
    dialog = d;
  }));

  beforeEach(() => {
    viewContainerFixture = TestBed.createComponent(DlgTestChildViewContainerComponent);
    viewContainerFixture.detectChanges();

    dialogRef = dialog.open(LoginDialogComponent);
    viewContainerFixture.detectChanges();

  });

  it('should be created', fakeAsync(() => {
    expect(dialogRef.componentInstance instanceof LoginDialogComponent).toBe(true, 'Failed to open');
    expect(overlayContainerElement.querySelector('h1').innerText).toEqual('App Login');

    dialogRef.close();
    tick(500);
    viewContainerFixture.detectChanges();
  }));

  it('should close and return false when cancel button pressed', async(() => {
    const afterCloseCallback = jasmine.createSpy('afterClose callback');

    dialogRef.afterClosed().subscribe(afterCloseCallback);
    (overlayContainerElement.querySelector('button[md-dialog-close="false"]') as HTMLElement).click();
    viewContainerFixture.detectChanges();

    viewContainerFixture.whenStable().then(() => {
      expect(overlayContainerElement.querySelector('md-dialog-container')).toBeNull('Dialog box still open');
      expect(afterCloseCallback).toHaveBeenCalledWith('false');
    });
  }));

  describe('should disable login button', () => {
    it('without a user and password entry', fakeAsync(() => {

      const btn = overlayContainerElement.querySelector('button[md-raised-button]');
      expect(btn.getAttribute('ng-reflect-disabled')).toBe('true');

      dialogRef.close()
      tick(500);
      viewContainerFixture.detectChanges();
    }));

    it('with a user entry but without a password entry', async(() => {

      (overlayContainerElement.querySelector('input[formcontrolname="name"]') as HTMLInputElement).value = 'DD';
      viewContainerFixture.detectChanges();

      viewContainerFixture.whenStable().then(() => {
        viewContainerFixture.detectChanges();
        const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]');
        const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]');

        expect((nameInput as HTMLInputElement).value).toEqual('DD');
        expect((passwordInput as HTMLInputElement).value).toEqual('');
        expect((overlayContainerElement.querySelector('button[md-raised-button]')).getAttribute('ng-reflect-disabled')).toBe('true');
      });
    }));

    it('with a password but without a user entry', async(() => {

      (overlayContainerElement.querySelector('input[formcontrolname="password"]') as HTMLInputElement).value = 'Password';
      viewContainerFixture.detectChanges();

      viewContainerFixture.whenStable().then(() => {
        viewContainerFixture.detectChanges();
        const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]');
        const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]');

        expect((nameInput as HTMLInputElement).value).toEqual('');
        expect((passwordInput as HTMLInputElement).value).toEqual('Password');
        expect((overlayContainerElement.querySelector('button[md-raised-button]')).getAttribute('ng-reflect-disabled')).toBe('true');
      });
    }));

    it('with a valid user name but invalid password', async(() => {

      (overlayContainerElement.querySelector('input[formcontrolname="name"]') as HTMLInputElement).value = 'ABC';
      (overlayContainerElement.querySelector('input[formcontrolname="password"]') as HTMLInputElement).value = '1234567';
      viewContainerFixture.detectChanges();

      viewContainerFixture.whenStable().then(() => {
        viewContainerFixture.detectChanges();
        const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]');
        const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]');

        expect((nameInput as HTMLInputElement).value).toEqual('ABC');
        expect((passwordInput as HTMLInputElement).value).toEqual('1234567');
        expect((overlayContainerElement.querySelector('button[md-raised-button]')).getAttribute('ng-reflect-disabled')).toBe('true');
      });
    }));

    it('with an invalid user name but with a valid password', async(() => {

      (overlayContainerElement.querySelector('input[formcontrolname="name"]') as HTMLInputElement).value = 'AB';
      (overlayContainerElement.querySelector('input[formcontrolname="password"]') as HTMLInputElement).value = '12345678';
      viewContainerFixture.detectChanges();

      viewContainerFixture.whenStable().then(() => {
        viewContainerFixture.detectChanges();
        const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]');
        const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]');

        expect((nameInput as HTMLInputElement).value).toEqual('AB');
        expect((passwordInput as HTMLInputElement).value).toEqual('12345678');
        expect((overlayContainerElement.querySelector('button[md-raised-button]')).getAttribute('ng-reflect-disabled')).toBe('true');
      });
    }));
  });

  it('should enable the login button when a valid username and password are entered', fakeAsync(() => {
    (overlayContainerElement.querySelector('input[formcontrolname="name"]') as HTMLInputElement).value = 'ABC';
    (overlayContainerElement.querySelector('input[formcontrolname="password"]') as HTMLInputElement).value = '12345678';
    viewContainerFixture.detectChanges();
    tick();

    viewContainerFixture.detectChanges();
    const loginBtn = overlayContainerElement.querySelector('button[md-raised-button]');
    const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]');
    const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]');
    console.log('Login Button is:', loginBtn.textContent);
    console.log('Login Button is:', loginBtn.getAttribute('ng-reflect-disabled'));

    expect((nameInput as HTMLInputElement).value).toEqual('ABC');
    expect((passwordInput as HTMLInputElement).value).toEqual('12345678');
    expect((overlayContainerElement.querySelector('button[md-raised-button]')).getAttribute('ng-reflect-disabled')).toBe('false');
  }));

  it('should enable the login button when a valid username and password are entered', async(() => {
    (overlayContainerElement.querySelector('input[formcontrolname="name"]') as HTMLInputElement).value = 'ABC';
    (overlayContainerElement.querySelector('input[formcontrolname="password"]') as HTMLInputElement).value = '12345678';
    viewContainerFixture.detectChanges();

    viewContainerFixture.whenStable().then(() => {
      viewContainerFixture.detectChanges();
      const loginBtn = overlayContainerElement.querySelector('button[md-raised-button]');
      const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]');
      const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]');
      console.log('Login Button is:', loginBtn.textContent);
      console.log('Login Button is:', loginBtn.getAttribute('ng-reflect-disabled'));

      expect((nameInput as HTMLInputElement).value).toEqual('ABC');
      expect((passwordInput as HTMLInputElement).value).toEqual('12345678');
      expect((overlayContainerElement.querySelector('button[md-raised-button]')).getAttribute('ng-reflect-disabled')).toBe('false');
    });
  }));
});
  it('should enable the login button when a valid username and password are entered', async(() => {
    const loginBtn = overlayContainerElement.querySelector('button[md-raised-button]') as HTMLButtonElement;
    const nameInput = overlayContainerElement.querySelector('input[formcontrolname="name"]') as HTMLInputElement;
    const passwordInput = overlayContainerElement.querySelector('input[formcontrolname="password"]') as HTMLInputElement;
    nameInput.value = 'ABC';
    nameInput.dispatchEvent(new Event('input'));
    passwordInput.value = '12345678';
    passwordInput.dispatchEvent(new Event('input'));
    viewContainerFixture.detectChanges();

    viewContainerFixture.whenStable().then(() => {
      viewContainerFixture.detectChanges();
      expect(nameInput.value).toEqual('ABC');
      expect(passwordInput.value).toEqual('12345678');
      expect(loginBtn.getAttribute('ng-reflect-disabled')).toBe('false', 'Login button disabled should now be false');
    });
  }));