Home > Mobile >  How to mock 'CdkVirtualScrollViewport' for infinite scrolling in Angular?
How to mock 'CdkVirtualScrollViewport' for infinite scrolling in Angular?

Time:01-21

 @ViewChild('scroller')
  scroller!: CdkVirtualScrollViewport;

  constructor(private ngZone: NgZone) { }

  ngAfterViewInit(): void {
    this.unsub = this.scroller.elementScrolled().pipe(
      map(() => this.scroller.measureScrollOffset('bottom')),
      pairwise(),
      filter(([y1, y2]) => (y2 < y1 && y2 < 140)),
      throttleTime(200)
    ).subscribe(() => {
      this.ngZone.run(() => {
        (this.maxItems > this.listItems.length) && this.fetchMore();
      });
    })
  }
<cdk-virtual-scroll-viewport  #scroller itemSize="72">
  <table mat-table [dataSource]="dataSource" >

    <ng-container matColumnDef="name">
      <th mat-header-cell *matHeaderCellDef> Name </th>
      <td mat-cell *matCellDef="let element"> {{element.name}} </td>
    </ng-container>
  
    <ng-container matColumnDef="symbol">
      <th mat-header-cell *matHeaderCellDef> Symbol </th>
      <td mat-cell *matCellDef="let element"> {{element.symbol}} </td>
    </ng-container>
  
    <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true"></tr>
    <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>

  </table>
</cdk-virtual-scroll-viewport>

test case:

  it('should check \'ngAfterViewInit\'', () => {
    component.ngAfterViewInit();
    changeDetectorRef.detectChanges();
    spyOn(virtualScrollViewport, 'elementScrolled').and.callThrough();
    expect(virtualScrollViewport.elementScrolled).toHaveBeenCalled();
  });

The error trace what I got when mocking

        Error: Error: cdk-virtual-scroll-viewport requires the "itemSize" property to be set.
            at new CdkVirtualScrollViewport (http://localhost:9876/_karma_webpack_/node_modules/@angular/cdk/__ivy_ngcc__/fesm2015/scrolling.js:1264:1)
            at new MockCdkVirtualScrollViewport (http://localhost:9876/_karma_webpack_/main.js:27773:9)
            at Object.factory (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17371:1)
            at R3Injector.hydrate (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:17247:42)
            at R3Injector.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:16997:1)
            at NgModuleRef$1.get (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/core.js:36383:1)
            at TestBedRender3.inject (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/testing.js:3227:1)
            at Function.inject (http://localhost:9876/_karma_webpack_/node_modules/@angular/core/__ivy_ngcc__/fesm2015/testing.js:3110:1)
            at UserContext.<anonymous> (http://localhost:9876/_karma_webpack_/src/app/components/gridview/gridview.component.spec.ts:112:37)

CodePudding user response:

A possible implementation of the mock class could be like this:

import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { Subject } from 'rxjs';

export class MockCdkVirtualScrollViewport implements CdkVirtualScrollViewport {
  scrolledIndexChange: Subject<number> = new Subject<number>();
  elementScrolled: Subject<Event> = new Subject<Event>();
  setRenderedRange(): void { }
  getRenderedRange(): any { }
  // add any other properties and methods that are needed for your test
}

You can use it in your tests like so:

import { ComponentFixture, TestBed } from '@angular/core/testing';
import { Component } from '@angular/core';
import { CdkVirtualScrollViewport } from '@angular/cdk/scrolling';
import { MockCdkVirtualScrollViewport } from './mock-cdk-virtual-scroll-viewport';

describe('YourComponent', () => {
  let component: YourComponent;
  let fixture: ComponentFixture<YourComponent>;
  let virtualScrollViewport: CdkVirtualScrollViewport;

  beforeEach(async () => {
    // configure the test module with the mock class
    await TestBed.configureTestingModule({
      declarations: [ YourComponent ],
      providers: [
        { provide: CdkVirtualScrollViewport, useClass: MockCdkVirtualScrollViewport }
      ]
    }).compileComponents();

    // create an instance of the component and the mock class
    fixture = TestBed.createComponent(YourComponent);
    component = fixture.componentInstance;
    virtualScrollViewport = TestBed.inject(CdkVirtualScrollViewport);
  });

  it('should do something', () => {
    // use the methods and properties of the mock class in your test
    // for example you can call the `setRenderedRange()` method and check that it works as expected
  });
});
  • Related