Search code examples
unit-testingkarma-jasmineangular-directiveangular6mutation-observers

Failing Angular directive unit test on MutationObserver: parameter 1 is not of type 'Node'


I am setting up some unit tests for a directive that has a MutationObserver in it and emits an event on every change (as the active routerLink changes). It works well with Angular's ElementRef API.

I followed the official docs and this great explanation: https://stackoverflow.com/a/37680484/6454752 and can actually grab the elements with the directive on it in the test:

describe('ActiveLinkChangeDirective', () => {
    let component: TestMatExpansionPanelComponent
    let fixture: ComponentFixture<TestMatExpansionPanelComponent>;
    let link
    let observed

beforeEach(fakeAsync(() => {

TestBed.configureTestingModule({
    imports: [RouterTestingModule],
    declarations: [ActiveLinkChangeDirective, 
                   TestMatExpansionPanelComponent],
    providers: [
      { provide: ElementRef, useClass: TestMatExpansionPanelComponent }],
    schemas: [CUSTOM_ELEMENTS_SCHEMA],
}).compileComponents().then(() => {

  fixture = TestBed.createComponent(TestMatExpansionPanelComponent)
  component = fixture.debugElement

  link  = fixture.debugElement.query(By.directive(ActiveLinkChangeDirective))
  expect(link).not.toBeNull()

  observed = link.injector.get(ActiveLinkChangeDirective)
  expect(observed).not.toBeNull()

  let observer = new MutationObserver((mutations) => {
    mutations.forEach(mutation => console.log(mutation.target))
  })

  observer.observe(observed, { attributes: true })

  fixture.detectChanges();
})

}))

However, it fails with the error Failed to execute 'observe' on 'MutationObserver': parameter 1 is not of type 'Node'. here:

  it('should create an instance', () => {  
      let directive = new ActiveLinkChangeDirective(component)
      expect(directive).toBeTruthy()
  });

As far as I have understood it, this error means that the MutationObserver just has no element to observe on, and refers not specifically to the observed element type. I just cannot pass the instance of the observed element to the MutationObserver in the test. Any piece of advice is highly appreciated.


Solution

  • I was able to make the test pass by wrapping the observer.observe method like so:

    let addObserver = () => {
        if (!observed) {
          tick(500)
          addObserver
          return
        }
        observer.observe(observed, { attributes: true })
    }
    

    Apparently the method was being called too early in the asynchronous test.