简体   繁体   中英

Angular test entry component from another component

I'm a beginner with Angular.

I created a simple application with Angular Bootstrap who open a simple modal.

Before to continue, I want to implements my tests.

My problems :

I would like test this workflow for MainComponent :

  • Click on the button openModal (or call openModal)
  • Verify all content of this modal
  • Simulate/click on dismiss and close, and verify that my functions getDismissReason and closeModal are called.

My questions :

Q1

When we execute :

expect(modalService.open).toHaveBeenCalled();

How to be sure that the modal is really open or function really called ?

Q2 :

When i try this code :

const h4DebEl : DebugElement = fixtureMainComponent.debugElement.query(By.css('h4'));

Why it's null ?

I think it's because the H4 tag is in element of ModalComponent.

Then, I tested :

fixtureModalComponent = TestBed.createComponent(ModalComponentComponent);

But I have an error provider for activeModal...

Please find bellow my actual code :

MainComponentComponent.component.ts

import { Component, OnInit } from '@angular/core';
import { NgbModal, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';

import { ModalComponentComponent } from '../modal-component/modal-component.component';

@Component({
  selector: 'app-main-component',
  templateUrl: './main-component.component.html',
  styleUrls: ['./main-component.component.scss']
})

export class MainComponentComponent implements OnInit {

  constructor(
    // Use modalService to open a modal
    private modalService: NgbModal
  ) { }

  ngOnInit() {

  }

  openModal() {
    const modalRef = this.modalService.open(ModalComponentComponent);

    // Define the value @Input person in ModalComponentComponent
    modalRef.componentInstance.person = {
      name: "Itachi",
      lastName: "Uchiwa"
    };

    modalRef.result.then((result) => {
      this.closeModal();
    }, (reason) => {
      this.dismissReason(reason);
    });
  }

  /**
   * Close modal
   */
  closeModal() {
    console.log('Closed modal');
  }

  /**
   * Dismiss modal
   */
  dismissReason(reason: any) {
    if (reason === ModalDismissReasons.ESC) {
      this.dismissReasonEsc();
    } else if (reason === ModalDismissReasons.BACKDROP_CLICK) {
      this.dismissReasonBackdropClick();
    } else {
      this.dismissReasonUnknownReason();
    }
  }

  dismissReasonEsc() {
    console.log('Dismiss called by pressing ESC');
  }

  dismissReasonBackdropClick() {
    console.log('Dismiss called by pressing BACKDROP_CLICK');
  }

  dismissReasonUnknownReason() {
    console.log("Dismiss called");
  }

}

MainComponentComponent.component.spec.ts

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { NgbModal, NgbModalRef, ModalDismissReasons } from '@ng-bootstrap/ng-bootstrap';
import { AppModule } from '../app.module';
import { MainComponentComponent } from './main-component.component';
import { ModalComponentComponent } from '../modal-component/modal-component.component';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';

describe('MainComponentComponent', () => {
  let component: MainComponentComponent;
  // Wrapper MainComponentComponent
  let fixtureMainComponent : ComponentFixture<MainComponentComponent>;
  let modalService: NgbModal;
  let modalRef: NgbModalRef; 

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      imports : [ AppModule ]
    }).compileComponents().then(() => {
      modalService = TestBed.get(NgbModal);
      fixtureMainComponent = TestBed.createComponent(MainComponentComponent);
      component = fixtureMainComponent.componentInstance;
      modalRef = modalService.open(ModalComponentComponent);
      spyOn(modalService, "open").and.returnValue(modalRef);

      spyOn(component, "openModal").and.callThrough();
      spyOn(component, "dismissReason").and.callThrough();
      spyOn(component, "dismissReasonEsc").and.callThrough();
      spyOn(component, "dismissReasonBackdropClick").and.callThrough();
      spyOn(component, "dismissReasonUnknownReason").and.callThrough();
      spyOn(component, "closeModal").and.callThrough();
    });    
  }));

  afterAll(() => {
    modalService.dismissAll();
    fixtureMainComponent.destroy();
    component = null;
    modalRef.close();
    modalRef = null;
  });

  it('should create', () => {
    expect(component).toBeTruthy();
  });

  it('Open modal', () => {
    component.openModal();
    expect(modalService.open).toHaveBeenCalled();
  });

  it('Open modal with click on the button', () => {
    fixtureMainComponent.debugElement.query(By.css('#openModalBtn')).triggerEventHandler("click", null);
    fixtureMainComponent.detectChanges();
    expect(component.openModal).toHaveBeenCalled();
    expect(modalService.open).toHaveBeenCalled();
  });

  it('Check element in modal', () => {
    fixtureMainComponent.debugElement.query(By.css('#openModalBtn')).triggerEventHandler("click", null);
    fixtureMainComponent.detectChanges();
    // Return null
    const h4DebEl : DebugElement = fixtureMainComponent.debugElement.query(By.css('h4'));
    // const h4HtmlEl : HTMLElement = h4DebEl.nativeElement;
    // expect(h4element.textContent).toEqual("Header :");
  });

  /**
   * Check the dismiss method ESC 
   * To do same for BACKDROP_CLICK, ...
   */
  it('Dimiss with ESC', () => {
    component.openModal();
    modalRef.dismiss(ModalDismissReasons.ESC);
    expect(component.dismissReason).toHaveBeenCalled(); 
    expect(component.dismissReasonEsc).toHaveBeenCalled();
  });

});

ModalComponentComponent.component.ts

import { Component, Input } from '@angular/core';
import { NgbActiveModal } from '@ng-bootstrap/ng-bootstrap';

@Component({
  selector: 'app-modal-component',
  template: `
  <div class="modal-header">
    <h4 class="modal-title" id="modal-basic-title">Header : {{person.lastName}} {{person.name}}</h4>
    <button type="button" class="close" aria-label="Close" (click)="activeModal.dismiss('Cross click')">
      <span aria-hidden="true">&times;</span>
    </button>
  </div>
  <div class="modal-body">
    Content modal
  </div>
  <div class="modal-footer">
    <button type="button" class="btn btn-outline-dark" (click)="activeModal.close('Save click')">Save</button>
  </div>
  `
})

export class ModalComponentComponent {
  @Input() person;
  constructor(public activeModal: NgbActiveModal) { }
}

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core';
import { NgbModule } from '@ng-bootstrap/ng-bootstrap';

import { AppComponent } from './app.component';
import { MainComponentComponent } from './main-component/main-component.component';
import { ModalComponentComponent } from './modal-component/modal-component.component';

@NgModule({
  declarations: [
    AppComponent,
    MainComponentComponent,
    ModalComponentComponent
  ],
  imports: [
    BrowserModule,
    NgbModule.forRoot()
  ],
  providers: [],
  bootstrap: [AppComponent],
  entryComponents: [ ModalComponentComponent ],
  schemas : [ CUSTOM_ELEMENTS_SCHEMA ]
})
export class AppModule { }

Thank you for your help and your time,

Best regards,

Q1 How to be sure that the modal is really open or function really called ?

  • really open: not the concern of your test, should be tested in ng-bootstrap
  • or function really called: is the test says its open, then its open :)

Q2 - debugelement: no idea

Mock activemodal

 const activeModal: any = jasmine.createSpyObj('activeModal', ['close', 'dismiss']);

Add to testbed providers

  { provide: NgbActiveModal, useValue: activeModal }

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM