How to test element scrolling with angular and jasmine (basic problem for testing specific directive)

62 Views Asked by At

I am having problems testing my directive that uses scrolling. Directive works as follows:

  • it adds left and right overlay before and after the content that is placed inside directive
  • it manages visibility of overlay based on scroll position

To simplify the case, I only test scrolling of container (without my directive)

/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-magic-numbers */
import { Component, DebugElement } from '@angular/core';
import {
  ComponentFixture,
  fakeAsync,
  flush,
  TestBed,
  tick,
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';

@Component({
  template: `
    <div class="ltr">
      <div style="width: 200px; height: 200px; position: relative">
        <div class="test-container" style="width: 200px; height: 200px">
          <div style="width: 400px; height: 200px;"></div>
        </div>
      </div>
    </div>
  `,
})
class TestScrollableComponent {}

describe('Scrolling test', () => {
  let component: TestScrollableComponent;
  let fixture: ComponentFixture<TestScrollableComponent>;
  let divEl: DebugElement;

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

    fixture = TestBed.createComponent(TestScrollableComponent);
    component = fixture.componentInstance;
    divEl = fixture.debugElement.query(By.css('.test-container'));
    fixture.detectChanges();
  });

  // TODO: Fix tests
  it('Should properly test scrolling', fakeAsync(() => {
    // Scenario 1: Scrolled to the very left
    divEl.nativeElement.scrollLeft = 0;
    divEl.nativeElement.dispatchEvent(new Event('scroll'));
    fixture.detectChanges();
    tick(100000);
    fixture.detectChanges();

    expect(divEl.nativeElement.scrollLeft).toBe(0);

    // Scenario 2: Scrolled to the middle
    // divEl.nativeElement.scrollLeft = 200;
    // divEl.nativeElement.dispatchEvent(new Event('scroll'));
    // fixture.detectChanges();
    // tick(100000);
    // fixture.detectChanges();

    // console.log(divEl.nativeElement.scrollLeft) // -> 0 !!!!
    // !!!! When debugger is inserted here, timing is right and test passes
    // expect(divEl.nativeElement.scrollLeft).toBe(200);
    flush();
  }));
});

What is interesting, when I add "debugger" before my spec, and proceed manually - tests are passing and value is correctly being updated to 200. So there is some timing issue. I don't have much experience with async testing, is there anyone that knows what could be the issue here?

I also added sample specs to stackblitz:

1

There are 1 best solutions below

2
Naren Murali On

I was able to get the test case running just by using fixture.whenStable() which run after the scroll left position is updated and my test cases passed!

/* eslint-disable @typescript-eslint/no-unused-vars */
/* eslint-disable @typescript-eslint/no-magic-numbers */
import { Component, DebugElement } from '@angular/core';
import {
  ComponentFixture,
  fakeAsync,
  flush,
  TestBed,
  tick,
} from '@angular/core/testing';
import { By } from '@angular/platform-browser';

@Component({
  template: `
    <div class="ltr">
      <div style="width: 200px; height: 200px; position: relative">
        <div class="test-container" style="width: 200px; height: 200px;overflow-x:scroll;">
          <div style="width: 400px; height: 200px;"></div>
        </div>
      </div>
    </div>
  `,
})
class TestScrollableComponent {}

describe('Scrolling test', () => {
  let component: TestScrollableComponent;
  let fixture: ComponentFixture<TestScrollableComponent>;
  let divEl: DebugElement;

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

    fixture = TestBed.createComponent(TestScrollableComponent);
    component = fixture.componentInstance;
    divEl = fixture.debugElement.query(By.css('.test-container'));
    fixture.detectChanges();
  });

  // TODO: Fix tests
  it('Should properly test scrolling', fakeAsync(() => {
    // Scenario 1: Scrolled to the very left
    divEl.nativeElement.scrollLeft = 0;
    console.log(divEl.nativeElement.scrollLeft);
    expect(divEl.nativeElement.scrollLeft).toBe(0);
    // Scenario 2: Scrolled to the middle
    divEl.nativeElement.scrollLeft = 200;
    fixture.whenStable().then(() => {
      console.log(divEl.nativeElement.scrollLeft);
      expect(divEl.nativeElement.scrollLeft).toBe(200);
    });
  }));
});

stackblitz demo