I have been working on unit testing a directive in Angular 11 using Jest 28. In order to simulate a click event on an image, I have written the following code snippet to mock the mouse event:
import { HTMLClickDirective } from "./html-click.directive";
describe('HTMLClickDirective', () => {
it('should trigger a lightbox for images when clicked', () => {
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();
});
});
Even though TypeScript does not indicate any errors, Jest throws the following complaint: "TypeError: Invalid constructor, the constructor is not part of the custom element registry".
A similar error occurs when attempting to create an HTMLElement
and then casting it to an HTMLImageElement
. Moreover, if I try creating a new Element
for future type coercion, Jest complains about an "Illegal constructor".
The issue persists even when moving the variable assignment outside the describe block.
Could this be related to JSDOM?
Edit: here is the Directive under test:
import { Directive, HostListener } from "@angular/core"
import { BsModalRef, BsModalService } from "ngx-bootstrap/modal"
import { ModalLightboxComponent } from "../components/shared/modal-lightbox/modal-lightbox.component";
/**
* This directive handles click events on HTML elements within safe HTML content projections.
*
* @example
* html
* <div [innerHTML]="..." HTMLClick></div>
*
*/
@Directive({
selector: "[HTMLClick]",
})
export class HTMLClickDirective {
modalRef: BsModalRef;
constructor(
private modalService: BsModalService,
) {}
/**
* Listens for click events on 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();
}
}