0

I'm trying to implement a dialog component where user can click escape button to close the popup (I'm using DaisyUI). It works fine when I try it manually, but for some reason I'm not able to create the unit test for it. It fails.

Unit Test:

  it('should allow using escape key when allowClosing is set to true', () => {

    fixture.componentInstance.allowClosing = true
    
    let dialog: HTMLDialogElement | undefined = 
    fixture.componentInstance["dialog"]?.nativeElement
    expect(dialog?.open).toBeFalse()

    dialog?.showModal()
    expect(dialog?.open).toBeTrue()

    let event = new KeyboardEvent('keydown', {key: 'Escape'});
    spyOn(component, 'cancel');
    dialog?.dispatchEvent(event);
    fixture.detectChanges();

    expect(component["cancel"]).toHaveBeenCalled();
    expect(dialog?.open).toBeFalse()

  });

HTML:

<dialog #dialog id="{{popupId}}" class="tw-modal"
        (close)="close()"
        (cancel)="cancel($event)">
  <form method="dialog" class="tw-modal-box tw-rounded-md tw-p-3">
    <div class="tw-flex tw-flex-col tw-justify-center">
      <button #closeButton class="tw-self-end" *ngIf="allowClosing">✕</button>
      <ng-content class="tw-self-auto"></ng-content>
    </div>
  </form>
  <form method="dialog" class="tw-modal-backdrop">
    <button></button>
  </form>
</dialog>

ts:


@Component({
  selector: 'popup',
  templateUrl: './popup.component.html'
})
export class PopupComponent implements AfterViewInit {
  
  @Input() allowClosing: boolean = false;
  @Input() popupId: string | undefined;
  @Output() onClose: EventEmitter<void> = new EventEmitter()

  @ViewChild("dialog") protected dialog: ElementRef<HTMLDialogElement> | undefined
  @ViewChild("closeButton") protected closeButton: ElementRef<HTMLButtonElement> | undefined

  ngAfterViewInit(): void {
    this.dialog?.nativeElement.addEventListener("open", ()=>{
      console.log("event listener on ipen")
    })
  }


  public show(){
    this.dialog?.nativeElement.showModal()
  }

  public close(){
    this.onClose.emit()
    this.dialog?.nativeElement.close()
  }

  public cancel(event: Event) {
    console.log("cancel")
    event.preventDefault()
    if (!this.allowClosing){
      return
    }
    else{
      this.close()
    }
  }

  get isOpen(){
    return this.dialog?.nativeElement.open
  }
}

What could be wrong? is it not working for security reasons?

1 Answers1

0

The issue could be that when we do:

spyOn(component, 'cancel');

We are spying on the cancel method to see how it was called, if it was called and how many times it was called.

I am thinking the cancel method is what's responsible for setting the open property to false.

To potentially fix it, you can do:

spyOn(component, 'cancel').and.callThrough();

Now we have a spy on cancel method and every time it is being called, we are calling the actual implementation (.and.callThrough()) and hopefully this will allow to close the dialog.

AliF50
  • 16,947
  • 1
  • 21
  • 37