1

In my directive I'm injecting DOCUMENT and adding an event listener:

constructor(@Inject(DOCUMENT) private document: Document) {}

ngOnInit() {
  this.document.addEventListener('click', this.clicked, true);
}

@Bound // custom decorator, you can ignore it
private clicked() {
  // do stuff
}

Then I have a test which needs to grab the injected document and spy on it to see if addEventListener was called:

it('should add a click event listener to document on ngOnInit', async(() => {

  // Overrides the template and returns the fixture
  overrideAndCompileComponent(LibTestComponent, `
    <div libClickOutsideDocumentListener></div>
  `).then((fixture) => {

    const spy = spyOn<any>(fixture.componentRef.injector.get(Document), 'addEventListener');

    expect(spy).toHaveBeenCalled();
  });
}));

This gives me the following error:

StaticInjectorError(Platform: core)[Document]

The issue is that I cannot figure out how to provide DOCUMENT properly. If I add the following to TestBed's providers array:

TestBed.configureTestingModule({
  ... excluded code ...
  providers: [
   { provide: DOCUMENT, useValue: Document }
  ]
});

I get the following (internal angular) error:

el.querySelectorAll is not a function

So it seems to override document with an incorrect value. I've been digging through the Angular docs but cannot find a solution..

What am I doing wrong?

Chrillewoodz
  • 27,055
  • 21
  • 92
  • 175
  • Possible duplicate of [How to inject Document in Angular 2 service](https://stackoverflow.com/questions/37521298/how-to-inject-document-in-angular-2-service) – Borys Kupar Jun 25 '19 at 14:06
  • 1
    @BorysKupar I know how to inject it, not provide it. The solutions there aren't working, already tried them. – Chrillewoodz Jun 25 '19 at 14:08
  • Why do you need to provide Document in your test when it's automatically provided by Angular? – Borys Kupar Jun 25 '19 at 14:14
  • @BorysKupar Because `StaticInjectorError(Platform: core)[Document]`. – Chrillewoodz Jun 25 '19 at 14:15
  • did you try providing `window.document` ? – Borys Kupar Jun 25 '19 at 14:21
  • offtopic: you could also use `@HostListener('document:click', ['$event'])` for your `clicked` method, no need to manually add the listener. – Borys Kupar Jun 25 '19 at 14:23
  • You could try a different testing strategy. Try in your test to trigger a click on document, and verify that component reacted correctly. - `document.dispatchEvent(new MouseEvent('click'))` – Borys Kupar Jun 25 '19 at 14:24
  • @BorysKupar Yes but doesn't work. I would but this directive is kinda special since everything is `private` and it doesn't really do much other than register an event listener and push something to a service. Not much to react to unfortunately. – Chrillewoodz Jun 25 '19 at 14:27

1 Answers1

0

Seems a bit overkill to me ...

Use the host listener decorator to simplify your workflow :

@HostListener('document:click', ['$event'])
onDocumentClick(event: MouseEvent) { ... }

This cancels the need to override the providers and allows you to only test the function itself, rather than testing if the event listener has been added (since now, you delegate the event listener logic to Angular, and you don't test what angular do, only what you do)

  • I'll revisit this solution I guess, I opted not to use it before for some reason. I'll accept as it doesn't seem to be any other decent solution. – Chrillewoodz Jun 26 '19 at 07:03