Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Directive binding not working in a spec file

I have a directive that highlights text:

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({ selector: '[appHighlight]' })
export class HighlightDirective {
  @Input('appHighlight') // tslint:disable-line no-input-rename
  highlightColor: string;

  constructor(private el: ElementRef) { }

  @HostListener('mouseenter')
  onMouseEnter() {
    this.highlight(this.highlightColor || 'yellow');
  }

  @HostListener('mouseleave')
  onMouseLeave() {
    this.highlight(null);
  }

  private highlight(color: string) {
    this.el.nativeElement.style.backgroundColor = color;
  }
}

On my app HTML I have:

This <span [appHighlight]="'pink'">is nice</span>!

And it works

hover text changes color

Then I started to build some tests, and in one test I try to bind a different color (just like the example above), but it doesn't bind the value, the field is undefined.

import { ComponentFixture, TestBed } from '@angular/core/testing';

import { Component } from '@angular/core';
import { HighlightDirective } from './highlight.directive';

@Component({
  selector: 'app-test-container',
  template: `
    <div>
      <span id="red" appHighlight>red text</span>
      <span id="green" [appHighlight]="'green'">green text</span>
      <span id="no">no color</span>
    </div>
  `
})
class ContainerComponent { }

const mouseEvents = {
  get enter() {
    const mouseenter = document.createEvent('MouseEvent');
    mouseenter.initEvent('mouseenter', true, true);
    return mouseenter;
  },
  get leave() {
    const mouseleave = document.createEvent('MouseEvent');
    mouseleave.initEvent('mouseleave', true, true);
    return mouseleave;
  },
};

fdescribe('HighlightDirective', () => {
  let fixture: ComponentFixture<ContainerComponent>;
  let container: ContainerComponent;
  let element: HTMLElement;

  beforeEach(() => {
    TestBed.configureTestingModule({
      declarations: [ContainerComponent, HighlightDirective],
    });

    fixture = TestBed.createComponent(ContainerComponent);
    container = fixture.componentInstance;
    element = fixture.nativeElement;
  });

  fit('should set background-color green when passing green parameter', () => {
    const targetElement = <HTMLSpanElement>element.querySelector('#green');

    targetElement.dispatchEvent(mouseEvents.enter);
    expect(targetElement.style.backgroundColor).toEqual('green');
  });
});

The test output shows

fallback color

Am I doing something wrong? Why it doesn't it bind the green color?

like image 956
BrunoLM Avatar asked Jul 22 '17 15:07

BrunoLM


People also ask

Can't bind to since it isn't a known property of directive?

This error often means that you haven't declared the directive "x" or haven't imported the NgModule to which "x" belongs. Perhaps you declared "x" in an application sub-module but forgot to export it. The "x" class isn't visible to other modules until you add it to the exports list.

How do you mock a directive?

A mock directive in Angular tests can be created by MockDirective function. The mock directive has the same interface as its original directive, but all its methods are dummies. In order to create a mock directive, pass the original directive into MockDirective function.

How to use directive in component?

To create a custom directive we have to replace @Component decorator with @Directive decorator. So, let's get started with creating our first Custom Attribute directive. In this directive, we are going to highlight the selected DOM element by setting an element's background color. Create an app-highlight.

Can't bind to accept since it isn't a known property of DIV?

The solution is actually pretty simple. You have to register CommonModule in the module that you are using. And that's it! The error should disappear.


1 Answers

I found out that by default Angular doesn't run bindings on templates during tests. Even a simple {{ myVar }} will not work unless you make it run bindings and lifecycle events, as described in Angular detectchanges documentation.

There are two options in this scenario, I can manually call

fixture.detectChanges();

Just after I get my fixture.

Or I can include a provider that sets it to run things automatically

providers: [
  { provide: ComponentFixtureAutoDetect, useValue: true },
],
like image 82
BrunoLM Avatar answered Oct 17 '22 02:10

BrunoLM