角度自定义材质对话框单元测试

时间:2019-11-24 20:22:31

标签: angular angular-material

我是Angular的菜鸟,但找不到用于测试对话框组件的示例。我尝试了各种可能性,但都失败了。

import { Component, Inject, OnInit } from '@angular/core';
import { MAT_DIALOG_DATA, MatDialogRef } from '@angular/material';

@Component({
  selector: 'app-custom-confirm-dialog',
  templateUrl: './custom-confirm-dialog.component.html',
  styleUrls: ['./custom-confirm-dialog.component.css'],
})
export class CustomConfirmDialogComponent implements OnInit {
  title: string;
  paragraph: string;
  confirm: string;
  refuse: string;

  constructor(private dialogRef: MatDialogRef<CustomConfirmDialogComponent>,
              @Inject(MAT_DIALOG_DATA) data) {
      this.title = data.title;
      this.paragraph = data.paragraph;
      this.confirm = data.confirm;
      this.refuse = data.refuse;
      dialogRef.keydownEvents().subscribe((e) => {
        if (e.keyCode === 27) {
          this.cancel();
        }
      });
    }

  ngOnInit() {
  }

  cancel() {
    this.dialogRef.close(false);
  }

  accept() {
    this.dialogRef.close(true);
  }
}

这很简单,但我只是在尝试..:)

而且我有这个规格:

/* tslint:disable:max-classes-per-file */
/* tslint:disable:no-big-function */
import { OverlayContainer } from '@angular/cdk/overlay';
import { Component, DebugElement, Directive, ViewChild, ViewContainerRef } from '@angular/core';
import { async, ComponentFixture, inject, TestBed } from '@angular/core/testing';
import { MatDialog, MatDialogModule, MatDialogRef } from '@angular/material/';
import { By } from '@angular/platform-browser';
import { BrowserDynamicTestingModule } from '@angular/platform-browser-dynamic/testing';
import { CustomConfirmDialogComponent } from './custom-confirm-dialog.component';

@Directive({
  // tslint:disable-next-line:directive-selector
  selector: 'view-container-directive',
})
class ViewContainerDirective {
  constructor(public viewContainerRef: ViewContainerRef) { }
}

@Component({
  selector: 'app-view-container-component',
  template: `<view-container-directive></view-container-directive>`,
})
class ViewContainerComponent {
  @ViewChild(ViewContainerDirective) childWithViewContainer: ViewContainerDirective;

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

describe('CustomConfirmDialogComponent', () => {
  let dialog: MatDialog;
  let overlayContainerElement: HTMLElement;

  let testViewContainerRef: ViewContainerRef;
  let viewContainerFixture: ComponentFixture<ViewContainerComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        ViewContainerComponent,
        ViewContainerDirective,
        CustomConfirmDialogComponent,
      ],
      imports: [
        MatDialogModule, // module of material modules I use
      ],
      providers: [
        { provide: OverlayContainer, useFactory: () => {
          overlayContainerElement = document.createElement('div');
          return { getContainerElement: () => overlayContainerElement };
        }},
      ],
    });

    TestBed.overrideModule(BrowserDynamicTestingModule, {
     // not sure why I needed this, but error message told me to include it
     set: {
        entryComponents: [ CustomConfirmDialogComponent ],
      },
    });

    TestBed.compileComponents();
  }));

  beforeEach(() => {
    viewContainerFixture = TestBed.createComponent(ViewContainerComponent);
    viewContainerFixture.detectChanges();
    testViewContainerRef = viewContainerFixture.componentInstance.childViewContainer;
  });

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

  describe('Save and Cancel', () => {
    let dialogRef: MatDialogRef<CustomConfirmDialogComponent, any>;
    let afterCloseCallback: jasmine.Spy;

    beforeEach(() => {
      dialogRef = dialog.open(CustomConfirmDialogComponent, {
        viewContainerRef: testViewContainerRef,
        data: {
            // DialogData goes here
      }});

      afterCloseCallback = jasmine.createSpy('afterClose callback');
      dialogRef.afterClosed().subscribe(afterCloseCallback);
    });

    it('should return input on save if no edits', async(() => {
      // no edits
      // click save
      const saveButton: DebugElement = viewContainerFixture.debugElement.query(By.css('#acceptBtn'));
      saveButton.triggerEventHandler('click', null);

      viewContainerFixture.detectChanges();
      viewContainerFixture.whenStable().then(() => {
        expect(afterCloseCallback).toHaveBeenCalled();
        expect(dialogRef.componentInstance).toBeFalsy(); // is closed
      });

    }));

    it('should return undefined if cancelled', async(() => {
      // no edits
      // click cancel
      const cancelButton: DebugElement = viewContainerFixture.debugElement.query(By.css('#cancelBtn'));
      cancelButton.triggerEventHandler('click', null);

      viewContainerFixture.detectChanges();

      viewContainerFixture.whenStable().then(() => {
        expect(afterCloseCallback).toHaveBeenCalledWith(undefined);
        expect(dialogRef.componentInstance).toBeFalsy(); // is closed
      });
    }));

  });
});

这是我的html:

<div class="Modal-BG">
    <div class="row">
        <div class="col-10"></div>
        <div class="col-2">
            <button hotkey="{27: 'click'}" class="float-right button-esc" (click)="cancel()"><img class="remove"/></button>
            <caption class="float-right">ESC</caption>
        </div>
    </div>
    <div clas="row">
        <p class="title">{{title}}</p>
    </div>
    <div clas="row">
        <p class="paragraph">{{paragraph}}</p>
    </div>
    <div class="Separator"></div>
    <mat-dialog-actions>
        <div class="float-right">
            <button type="button" id="#cancelBtn" class="Button-bg-cancel" (click)="cancel()">{{ refuse }}</button>
            <button type="button" id="#acceptBtn" class="Button-bg-confirm" (click)="accept()">{{ confirm }}</button>
        </div>
    </mat-dialog-actions>
</div>

但是当我运行测试时,出现以下错误:

  

HeadlessChrome 0.0.0(Linux 0.0.0)CustomConfirmDialogComponent保存   如果没有编辑失败,则“取消”和“取消”应返回保存时的输入           失败:无法读取null的属性'triggerEventHandler'           TypeError:无法读取null的属性'triggerEventHandler'

我认为它可以找到模拟点击事件的按钮,但我不知道如何解决

有什么想法吗?

谢谢!

1 个答案:

答案 0 :(得分:1)

bounds测试中,'should return input on save if no edits'saveButton,因为null部分无法找到按钮。原因是模板代码中不需要viewContainerFixture.debugElement.query(By.css('#acceptBtn'));。 使用以下模板代码,应在测试中找到按钮:

#