3

I have a directive that changes height of the text within element on window resize:

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


@Directive({selector: '[fittext]'})
export class FittextDirective {

  constructor(private element: ElementRef, private renderer: Renderer2) { }

  public changeFontSize(): void {
    const htmlElement: HTMLElement = this.element.nativeElement;
    const elemDims = htmlElement.getBoundingClientRect();
    const height = elemDims.height;
    // Set new font size on element
    this.renderer.setStyle(htmlElement, 'font-size', `${height / 2}px`);
  }

  @HostListener('window:resize')
  public onResize(): void {
    this.changeFontSize();
  }

}

And I'm trying to test it using custom wrapper component and by dispatching resize event on window.

import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { FittextDirective } from './fittext.directive';
import { Component, ViewChild, ElementRef } from '@angular/core';
import { By } from '@angular/platform-browser';


@Component({
  template: `
    <div #div fittext>{{ text }}</div>
    <style>
      div {
        height: 100vh;
        width: 100vw;
        font-size: 100%;
      }
    </style>
  `
})
class TestComponent {
  @ViewChild('div') div: ElementRef;
  public text: string = 'This is test string';
}


describe('FittextDirective', () => {
  let component: TestComponent;
  let fixture: ComponentFixture<TestComponent>;

  beforeEach(async(() => {
    TestBed.configureTestingModule({
      declarations: [
        TestComponent,
        FittextDirective
      ]
    }).compileComponents();
  }));

  beforeEach(() => {
    fixture = TestBed.createComponent(TestComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  fit('should trigger on resize', () => {
    const {height, width} = component.div.nativeElement.getBoundingClientRect();
    const fontSize = component.div.nativeElement.style.fontSize;
    window.dispatchEvent(new Event('resize'));
    fixture.detectChanges()

    const resizedElement = fixture.debugElement.query(By.directive(FittextDirective));
    const {newHeight, newWidth} = resizedElement.nativeElement.getBoundingClientRect();
    const newFontSize = resizedElement.nativeElement.style.fontSize;

    expect(newHeight).not.toEqual(height);
    expect(newWidth).not.toEqual(width);
    expect(newFontSize).not.toEqual(fontSize);
  });
});

However, tests report font sizes to be '', and height and width after resize (i.e. newHeight and newWidth) to be undefined. Why is the font size an empty string, even though the font should be sized to match the div? And why are the height and width after window resize undefined? I'm using headless chrome for testing so I can't use window.resizeTo(width, height) method.

Edit 1: After suggestion by Alex K I've changed newHeight and newWidth to:

const newHeight = resizedElement.nativeElement.getBoundingClientRect().height;
const newWidth = resizedElement.nativeElement.getBoundingClientRect().width;

However, it seems that after resize event, that windows is not resized (I get the error Expected 600 not to equal 600. and Expected 785 not to equal 785.). I've also changed the style between tags, to style in the component attribute i.e. styles attribute, but still, the fontSize is reported to be '' (or any style for that matter).

Edit 2: For some strange reason styles can only be fetched using window.getComputedStyle() (see this). Now, I get the size of the font, but it does not change after resize.

const styles = window.getComputedStyle(element.nativeElement);
const fontSize = styles.fontSize;
...
const newStyles = window.getComputedStyle(resizedElement.nativeElement);
const newFontSize = newStyles.fontSize;

Edit 3: As suggested, I've faked dimensions with spyOn, however, this does not resize fonts.

spyOn(component.div.nativeElement, 'getBoundingClientRect')
  .and.returnValue({height: height / 2, width: width / 2});

window.dispatchEvent(new Event('resize'));
fixture.detectChanges()
Gitnik
  • 564
  • 7
  • 26
  • `const {newHeight, newWidth} = resizedElement.nativeElement.getBoundingClientRect();` yields `undefined` because it's trying to read properties named `newHeight` and `newWidth` which don't exist on the object. You need to read the `height` and `width` properties. – Alex K Feb 20 '19 at 15:22
  • Not sure how headless Chrome handles things like `getBoundingClientRect()`. But you can use `jasmine.spyOn` to have the method return whatever dimensions you want. So you could have it return 1000 for height, then check to see that the font-size is 500px, for example. – Alex K Feb 20 '19 at 15:24
  • @AlexK thanks, I have update the code. The problem with the spying is that I always get the empty string for _any_ style I set, so I have nothing to spy. And I cannot directly spy directive methods, because it's wrapped with testing component. – Gitnik Feb 20 '19 at 15:41
  • You're triggering a fake window resize event, but the window size isn't actually changing. So the dimensions of your directive's div won't change either. But you can fake it before you trigger the resize event: `spyOn(component.div.nativeElement, 'getBoundingClientRect').and.returnValue({height: 100, width: 200})` – Alex K Feb 20 '19 at 18:12
  • @AlexK Thank you. This resolves issues with dimensions. But since this seems to be fake window resize event, the font size does not change. :( – Gitnik Feb 21 '19 at 14:04

0 Answers0