jugglervr
jugglervr

Reputation: 175

Angular Jest can't mock HTMLElement? "not part of the custom element registry"

I'm trying to unit test a directive in Angular 11 with Jest 28. I need to simulate a click on an image, so I'm using the following code to mock the mouse event:

import { HTMLClickDirective } from "./html-click.directive";

describe('HTMLClickDirective', () => {
  it('should open a lightbox for images', () => {
    const directive = new HTMLClickDirective(null);
    
    const image = new HTMLImageElement;
    image.setAttribute('src','foo');
    let click = {target: image} as unknown as MouseEvent;
    
    directive.onClick(click);
    expect(directive.openLightbox).toHaveBeenCalled();
  });
});

Typescript doesn't flag anything as amiss, but Jest complains: "TypeError: Invalid constructor, the constructor is not part of the custom element registry".

Same complaint when I just try to make an HTMLElement and cast it to HTMLImageElement. If I just try to make a new Element for later type coercion, it complains, "TypeError: Illegal constructor".

Same thing happens when I move the variable assignment out of the describe block.

Maybe a JSDOM thing?

Edit: Directive being tested:

import { Directive, HostListener } from "@angular/core"
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal"
import { ModalLightboxComponent } from "../components/shared/modal-lightbox/modal-lightbox.component";

/**
 * Handle click events on HTML elements inside a safe HTML content projection.
 * 
 * @example
 * ```html
 * <div [innerHtml]="..." HTMLClick></div>
 * ```
 */
@Directive({
  selector: "[HTMLClick]",
})
export class HTMLClickDirective {
  modalRef: BsModalRef;
  constructor(
    private modalService: BsModalService,
  ) {}

  /**
   * Listener for click events in any HTMLElement.
   */
  @HostListener("click", ["$event"])
  onClick($event: MouseEvent) {
    const target = $event.target as any

    switch (target.constructor) {
      case HTMLImageElement:
        $event.preventDefault()
        this.handleImageElement(target)
        break;
      case HTMLElement:
        $event.preventDefault()
        this.handleElement(target)
        break;
      default:
        break;
    }
  }

  openLightbox(img: string) {
    this.modalRef = this.modalService.show(ModalLightboxComponent, {
      animated: false,
      class: 'lightbox',
      initialState: {
        modalPath: img,
        closeModal: this.closeModal.bind(this),
      }
    });
  }
  closeModal() {
    this.modalRef?.hide();
  }
  
  private async handleImageElement(target: HTMLImageElement): Promise<void> {
    this.openLightbox(target.getAttribute('src'));
  }
  private async handleElement(target: HTMLElement): Promise<void> {
    console.log(target)
    if(target.nodeName === 'STRONG') document.querySelector('[id="' + target.innerText.toLowerCase() + '"]').scrollIntoView();
  }
}

Upvotes: 1

Views: 2197

Answers (1)

jugglervr
jugglervr

Reputation: 175

let image = document.createElement('img');

Upvotes: 2

Related Questions