ngx-virtual-scroller icon indicating copy to clipboard operation
ngx-virtual-scroller copied to clipboard

Unit testing the virtual scroll

Open sanjaybhaskar opened this issue 7 years ago • 13 comments

I want to unit test the virtual scroll please suggest the way to test this scenario I have the component.html

<virtual-scroll [items]="items" (update)="viewPortItems = $event">
    <list-item *ngFor="let item of viewPortItems" [item]="item">
        <div>{item.name}</div>
   </list-item>
</virtual-scroll>

in my spec file i'm trying to test the debug element of virtual-scroll to check the childnodes to the length of mock data. but the child nodes doesn't have the <div/> element inside the <list-item/>

let ListItemElement = riskfixture.debugElement.query(By.css('list-item'));
expect(mdlListItemElement.childNodes.length).toBe(mockData.length);

when i log the ListItemElement i see below

<virtual-scroll _ngcontent-c3="">
   <list-item _ngcontent-c3="">
     <!--bindings={}-->
   </list-item>
   <router-outlet _ngcontent-c3=""></router-outlet>
 </virtual-scroll>

Please help how can i achieve the unittesting

sanjaybhaskar avatar Aug 31 '17 13:08 sanjaybhaskar

@sanjaybhaskar You need to test it asynchronously as viewPortItems variable is calculated in requestAnimationFrame. Example:

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

...

it('should display logs', async(() => {
    fixture.whenStable().then(() => {
      fixture.detectChanges();
      const logs = fixture.debugElement.queryAll(By.css('.log'));
      expect(logs.length).toBe(component.logs.length);
    });
}));

kukiel avatar Sep 17 '17 20:09 kukiel

I am testing it asynchronously , however the virtual-scroll won't populate at all. I am using ngrx This is the test:

it('should create items', async(() => {
        
        store.dispatch(
            new ItemsSuccessAction([
                {
                    name: 'item1',
                }
            ])
        );
   
        fixture.whenStable().then(() => {
            fixture.detectChanges();
            expect(fixture.debugElement.query(By.directive(ItemComponent))).toBeTruthy();
        });
    }))

If I select the state, I can see that the items get saved into the store OK. However in the virtual scroll there are no items at all.

My HTML template:

<virtual-scroll  [items]="items$ | async" (update)="items= $event" (change)="fetchMore()">
        <div  #container>
            <item *ngFor="let items of items">
               <div>...</div>
            </item>
        </div>
    </virtual-scroll>

What do you think is the reason?

alexanto avatar Mar 13 '18 18:03 alexanto

hey @alexanto, did you have any luck with testing the scroll? We're having the same problem. I'd like to be able to test my component, but the virtual scroll isn't adding any rows to the DOM. At this point, even disabling the virtualisation during tests would be preferable

aaron-bond avatar Mar 22 '18 17:03 aaron-bond

For me the solution was to manually emit the update event in the test:

const virtualScrollComponent = fixture.debugElement.query(By.directive(VirtualScrollComponent)).componentInstance;
component.items$.subscribe(items => {
     virtualScrollComponent.update.emit(items);
});

alexanto avatar Mar 22 '18 17:03 alexanto

@alexanto thanks for the info! I'll give this a try

aaron-bond avatar Mar 23 '18 09:03 aaron-bond

I have the same problem, the solution provided by @alexanto works, but it's quite useless in my scenario...I can't test my component properly because my items are runtime filtered by some | pipes. @rintoj any way to test it?

andrea-spotsoftware avatar May 17 '18 10:05 andrea-spotsoftware

If anyone comes up with a solution to this problem, please submit a pull request and I'll be happy to merge it. Thanks.

speige avatar Jul 19 '18 23:07 speige

VirtualScrollComponent

hi, how u get the dependency for this ? as the library has been changed

satyavanu avatar Oct 26 '18 10:10 satyavanu

@sanjaybhaskar Was it working for you on a previous version of the code? If you can paste your previous code & the version number it was working with, I can help you upgrade it to the latest version.

speige avatar Oct 26 '18 17:10 speige

If this helps anyone, I ended up just creating a fake component.

Do not import the VirtualScrollerModule into the test, instead, just define a component like this:

@Component({
  selector: 'virtual-scroller',
  template: '<ng-content></ng-content>'
})
export class FakeVirtualScrollerComponent implements OnInit {
  @Input() items: any[];

  viewPortItems;

  ngOnInit(): void {
    this.viewPortItems = this.items;
  }
}

And add it as a declaration on

TestBed.configureTestingModule({
      declarations: [

ataraciuk avatar Jun 21 '19 20:06 ataraciuk

If this helps anyone, I ended up just creating a fake component.

Do not import the VirtualScrollerModule into the test, instead, just define a component like this:

@Component({
  selector: 'virtual-scroller',
  template: '<ng-content></ng-content>'
})
export class FakeVirtualScrollerComponent implements OnInit {
  @Input() items: any[];

  viewPortItems;

  ngOnInit(): void {
    this.viewPortItems = this.items;
  }
}

And add it as a declaration on

TestBed.configureTestingModule({
      declarations: [

Hi @ataraciuk, can you teach me how to use this fake component ? Thank you.

vinh1911 avatar Oct 21 '19 03:10 vinh1911

If this helps anyone, I ended up just creating a fake component. Do not import the VirtualScrollerModule into the test, instead, just define a component like this:

@Component({
  selector: 'virtual-scroller',
  template: '<ng-content></ng-content>'
})
export class FakeVirtualScrollerComponent implements OnInit {
  @Input() items: any[];

  viewPortItems;

  ngOnInit(): void {
    this.viewPortItems = this.items;
  }
}

And add it as a declaration on

TestBed.configureTestingModule({
      declarations: [

Hi @ataraciuk, can you teach me how to use this fake component ? Thank you.

Not much else to do. Add the FakeVirtualScrollerComponent in your TestBed Module configuration via declations: [FakeVirtualScrollerComponent,...]

This is only so the content you are putting inside the virtual scroller shows as if the scroller wasn't there. I'm not testing scroller behavior, it's just a workaround to test what is inside.

ataraciuk avatar Oct 25 '19 20:10 ataraciuk

Using the mocked virtual scroller (by @ataraciuk ) seems the easiest option. Include all those @Input or @Output used by your virtual-scroller in the FakeVirtualScrollerComponent. And if the VirtualScrollerModule declared in some shared module imported in the tests, then remove it using overrideModule:

TestBed.configureTestingModule({
    imports: [MySharedModule],
    declarations: [FakeVirtualScrollerComponent ]
});

TestBed.overrideModule(MySharedModule, {
    remove: {
        imports: [VirtualScrollerModule],
        exports: [VirtualScrollerModule]
    }
});

sergeykosik avatar Nov 13 '20 13:11 sergeykosik