Search code examples
unit-testingjasminekarma-jasmineionic5angular11

ionic 5 unit test with popover controller


I am trying to spy popover present method in my ionic 5 / angular 11 project but getting an error

Unhandled Promise rejection: Cannot read property 'then' of undefined

Here is unit test code

describe('LoginPage', () => {

 let popoverSpy = jasmine.createSpyObj('Popover', ['create', 'present', 'onDidDismiss', 'dismiss']);
 let popoverCtrlSpy = jasmine.createSpyObj('PopoverController', ['create']);
 popoverCtrlSpy.create.and.callFake(function () {
  return popoverSpy;
 });

 beforeEach(waitForAsync(() => {
  TestBed.configureTestingModule({
   declarations: [LoginPage],
   imports: [
     IonicModule.forRoot(),
     TranslateModule.forChild(),
     ComponentsModule,
     TranslateModule.forRoot({
       loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
     })
   ],
   providers: [{ provide: PopoverController, useValue: popoverCtrlSpy }]
 }).compileComponents()

 it("check popover preset", () => {
  component.openEntitySelection();
  fixture.detectChanges();
  expect(popoverSpy.present).toHaveBeenCalled()
 })

}


private async openEntitySelection() {

 let popover = await this.popoverCtrl.create({
  component: PopoverPage
 });
 await popover.present();
 popover.onDidDismiss().then((response) => {
  //Handle response
 })

}

Thanks in advance!


Solution

  • It seems you're not mocking onDidDismiss to return a promise. You also need to use fixture.whenStable or waitForAsync utilities to wait for the promise to be resolved before doing your assertion.

    Try this:

    describe('LoginPage', () => {
    
     let popoverSpy = jasmine.createSpyObj('Popover', ['create', 'present', 'onDidDismiss', 'dismiss']);
     // !! -- Add this line -- !!
     // I am mock resolving it to a value of true, you can resolve it to any value
     popOverSpy.onDidDismiss.and.returnValue(Promise.resolve(true));
     // !! --- !!
     let popoverCtrlSpy = jasmine.createSpyObj('PopoverController', ['create']);
     popoverCtrlSpy.create.and.callFake(function () {
      return popoverSpy;
     });
    
     beforeEach(waitForAsync(() => {
      TestBed.configureTestingModule({
       declarations: [LoginPage],
       imports: [
         IonicModule.forRoot(),
         TranslateModule.forChild(),
         ComponentsModule,
         TranslateModule.forRoot({
           loader: { provide: TranslateLoader, useClass: TranslateFakeLoader }
         })
       ],
       providers: [{ provide: PopoverController, useValue: popoverCtrlSpy }]
     }).compileComponents()
    
     it("check popover preset", (done) => { // add done to let jasmine know you're done
      component.openEntitySelection();
      fixture.detectChanges();
      // you have to wait at least for one fixture.whenStable I am thinking
      // because we are returning a promise and we have to ensure that the promises
      // have completed before doing our assertion.
      fixture.whenStable().then(() => {
         expect(popoverSpy.present).toHaveBeenCalled();
         done();
      });
     });
    }
    
    
    private async openEntitySelection() {
    
     let popover = await this.popoverCtrl.create({
      component: PopoverPage
     });
     await popover.present();
     popover.onDidDismiss().then((response) => {
      //Handle response
     })
    
    }