UNPKG

igniteui-angular-sovn

Version:

Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps

1,110 lines (924 loc) 75.2 kB
import { AsyncPipe, NgClass, NgFor, NgForOfContext, NgIf } from '@angular/common'; import { AfterViewInit, ChangeDetectorRef, Component, Directive, Injectable, IterableDiffers, NgZone, OnInit, QueryList, TemplateRef, ViewChild, ViewChildren, ViewContainerRef, DebugElement } from '@angular/core'; import { TestBed, ComponentFixture, waitForAsync } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; import { BehaviorSubject, Observable } from 'rxjs'; import { IForOfState, IgxForOfDirective } from './for_of.directive'; import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { IgxForOfScrollSyncService } from './for_of.sync.service'; import { TestNgZone } from '../../test-utils/helper-utils.spec'; import { PlatformUtil } from '../../core/utils'; describe('IgxForOf directive -', () => { const INACTIVE_VIRT_CONTAINER = 'igx-display-container--inactive'; const DISPLAY_CONTAINER = 'igx-display-container'; const VERTICAL_SCROLLER = 'igx-virtual-helper'; let displayContainer: HTMLElement; let verticalScroller: HTMLElement; let horizontalScroller: HTMLElement; let dg: DataGenerator; beforeAll(() => { dg = new DataGenerator(); }); describe('empty virtual component', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [EmptyVirtualComponent] }).compileComponents(); })); it('should initialize empty directive', () => { const fix = TestBed.createComponent(EmptyVirtualComponent); fix.detectChanges(); displayContainer = fix.nativeElement.querySelector(DISPLAY_CONTAINER); expect(displayContainer).not.toBeNull(); }); }); describe('horizontal virtual component', () => { let fix: ComponentFixture<HorizontalVirtualComponent>; configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [HorizontalVirtualComponent], providers: [{ provide: NgZone, useFactory: () => new TestNgZone() }] }).compileComponents(); })); beforeEach(() => { fix = TestBed.createComponent(HorizontalVirtualComponent); dg.generateData(300, 5, fix.componentInstance); fix.componentRef.hostView.detectChanges(); fix.detectChanges(); displayContainer = fix.nativeElement.querySelector(DISPLAY_CONTAINER); verticalScroller = fix.nativeElement.querySelector(VERTICAL_SCROLLER); horizontalScroller = fix.nativeElement.querySelector('igx-horizontal-virtual-helper'); }); it('should initialize directive with horizontal virtualization', () => { expect(displayContainer).not.toBeNull(); expect(verticalScroller).toBeNull(); expect(horizontalScroller).not.toBeNull(); fix.componentInstance.scrollLeft(150); fix.detectChanges(); const firstRecChildren = displayContainer.children; for (let i = 0; i < firstRecChildren.length; i++) { expect(firstRecChildren[i].textContent) .toBe(fix.componentInstance.data[0][i + 1].toString()); } const secondRecChildren = fix.nativeElement.querySelectorAll(DISPLAY_CONTAINER)[1].children; for (let i = 0; i < secondRecChildren.length; i++) { expect(secondRecChildren[i].textContent) .toBe(fix.componentInstance.data[1][i + 1].toString()); } }); it('should always fill available space for last chunk size calculation - horizontal virtualization', () => { fix.componentInstance.width = '1900px'; fix.componentInstance.cols = [ { field: '1', width: 100 }, { field: '2', width: 1800 }, { field: '3', width: 200 }, { field: '4', width: 200 }, { field: '5', width: 300 }, { field: '6', width: 100 }, { field: '7', width: 100 }, { field: '8', width: 100 }, { field: '9', width: 150 }, { field: '10', width: 150 } ]; fix.componentRef.hostView.detectChanges(); fix.detectChanges(); const firstRecChildren = displayContainer.children; let chunkSize = firstRecChildren.length; expect(chunkSize).toEqual(9); fix.componentInstance.width = '1900px'; fix.componentInstance.cols = [ { field: '1', width: 1800 }, { field: '2', width: 100 }, { field: '3', width: 200 }, { field: '4', width: 200 }, { field: '5', width: 300 }, { field: '6', width: 100 }, { field: '7', width: 100 }, { field: '8', width: 100 }, { field: '9', width: 150 }, { field: '10', width: 150 } ]; fix.componentRef.hostView.detectChanges(); fix.detectChanges(); chunkSize = firstRecChildren.length; expect(chunkSize).toEqual(10); }); it('should update horizontal scroll offsets if igxForOf changes. ', () => { fix.componentInstance.width = '500px'; fix.componentInstance.cols = [ { field: '1', width: 100 }, { field: '2', width: 200 }, { field: '3', width: 200 }, { field: '4', width: 200 }, { field: '5', width: 300 } ]; fix.componentRef.hostView.detectChanges(); fix.detectChanges(); fix.componentInstance.scrollLeft(50); fix.detectChanges(); expect(parseInt(displayContainer.style.left, 10)).toEqual(-50); fix.componentInstance.cols = [{ field: '1', width: 100 }]; fix.detectChanges(); expect(parseInt(displayContainer.style.left, 10)).toEqual(0); }); it('should allow scroll in rtl direction.', () => { fix.debugElement.nativeElement.dir = 'rtl'; fix.detectChanges(); fix.componentInstance.width = '500px'; fix.componentInstance.cols = [ { field: '1', width: 100 }, { field: '2', width: 200 }, { field: '3', width: 200 }, { field: '4', width: 200 }, { field: '5', width: 300 } ]; fix.componentRef.hostView.detectChanges(); fix.detectChanges(); fix.componentInstance.scrollLeft(-50); fix.detectChanges(); expect(parseInt(displayContainer.style.left, 10)).toEqual(50); fix.componentInstance.scrollLeft(-250); fix.detectChanges(); const state = fix.componentInstance.childVirtDirs.toArray()[0].state; expect(state.startIndex).toBe(1); }); }); describe('vertical virtual component', () => { let fix: ComponentFixture<VerticalVirtualComponent>; configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [VerticalVirtualNoDataComponent, VerticalVirtualComponent], providers: [{ provide: NgZone, useFactory: () => new TestNgZone() }] }).compileComponents(); })); beforeEach(() => { fix = TestBed.createComponent(VerticalVirtualComponent); fix.componentInstance.data = dg.generateVerticalData(fix.componentInstance.cols); fix.componentRef.hostView.detectChanges(); fix.detectChanges(); displayContainer = fix.nativeElement.querySelector(DISPLAY_CONTAINER); verticalScroller = fix.nativeElement.querySelector(VERTICAL_SCROLLER); horizontalScroller = fix.nativeElement.querySelector('igx-horizontal-virtual-helper'); }); it('should initialize directive with vertical virtualization', async () => { expect(displayContainer).not.toBeNull(); expect(verticalScroller).not.toBeNull(); expect(horizontalScroller).toBeNull(); /* The height of the row is set to 50px so scrolling by 100px should render the third record */ fix.componentInstance.scrollTop(100); fix.detectChanges(); const firstRecChildren = displayContainer.children[0].children; let i = 0; const thirdRecord = fix.componentInstance.data[2]; for (const item in thirdRecord) { if (thirdRecord.hasOwnProperty(item)) { expect(thirdRecord[item].toString()) .toBe(firstRecChildren[i++].textContent); } } }); it('should update vertical scroll offsets if igxForOf changes. ', () => { fix.componentInstance.scrollTop(5); fix.detectChanges(); expect(parseInt(displayContainer.style.top, 10)).toEqual(-5); spyOn(fix.componentInstance.parentVirtDir.chunkLoad, 'emit'); fix.componentInstance.data = [{ 1: 1, 2: 2, 3: 3, 4: 4 }]; fix.detectChanges(); expect(parseInt(displayContainer.style.top, 10)).toEqual(0); expect(fix.componentInstance.parentVirtDir.chunkLoad.emit).toHaveBeenCalledTimes(1); }); it('should apply the changes when itemSize is changed.', () => { const firstRecChildren = displayContainer.children[0].children; // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < firstRecChildren.length; i++) { expect(firstRecChildren[i].clientHeight) .toBe(parseInt(fix.componentInstance.parentVirtDir.igxForItemSize, 10)); } fix.componentInstance.itemSize = '100px'; fix.detectChanges(); // eslint-disable-next-line @typescript-eslint/prefer-for-of for (let i = 0; i < firstRecChildren.length; i++) { expect(firstRecChildren[i].clientHeight) .toBe(parseInt(fix.componentInstance.parentVirtDir.igxForItemSize, 10)); } }); it('should not throw error when itemSize is changed while data is null/undefined.', () => { let errorMessage = ''; fix.componentInstance.data = null; fix.detectChanges(); try { fix.componentInstance.itemSize = '100px'; fix.detectChanges(); } catch (ex) { errorMessage = ex.message; } expect(errorMessage).toBe(''); }); it('should always fill available space for last chunk size calculation - vertical virtualization', async () => { fix.componentInstance.height = '1900px'; const virtualContainer = fix.componentInstance.parentVirtDir; virtualContainer.igxForSizePropName = 'height'; fix.componentInstance.data = [ { 1: '1', height: '100px' }, { 1: '2', height: '1800px' }, { 1: '3', height: '200px' }, { 1: '4', height: '200px' }, { 1: '5', height: '300px' }, { 1: '6', height: '100px' }, { 1: '7', height: '100px' }, { 1: '8', height: '100px' }, { 1: '9', height: '150px' }, { 1: '10', height: '150px' } ]; fix.detectChanges(); await wait(); let chunkSize = (virtualContainer as any)._calcMaxChunkSize(); expect(chunkSize).toEqual(9); fix.componentInstance.height = '1900px'; fix.componentInstance.data = [ { 1: '1', height: '1800px' }, { 1: '2', height: '100px' }, { 1: '3', height: '200px' }, { 1: '4', height: '200px' }, { 1: '5', height: '300px' }, { 1: '6', height: '100px' }, { 1: '7', height: '100px' }, { 1: '8', height: '100px' }, { 1: '9', height: '150px' }, { 1: '10', height: '150px' } ]; fix.detectChanges(); await wait(); chunkSize = (virtualContainer as any)._calcMaxChunkSize(); expect(chunkSize).toEqual(10); }); it('should take item margins into account when calculating the size cache', async () => { fix.componentInstance.height = '600px'; fix.componentInstance.itemSize = '100px'; const virtualContainer = fix.componentInstance.parentVirtDir; virtualContainer.igxForSizePropName = 'height'; fix.componentInstance.data = [ { 1: '1', height: '100px', margin: '30px' }, { 1: '2', height: '100px', margin: '0px' }, { 1: '3', height: '100px', margin: '0px' }, { 1: '4', height: '100px', margin: '0px' }, { 1: '5', height: '100px', margin: '0px' }, { 1: '6', height: '100px', margin: '0px' }, { 1: '7', height: '100px', margin: '0px' }, { 1: '8', height: '100px', margin: '30px' }, { 1: '9', height: '100px', margin: '30px' }, { 1: '10', height: '100px', margin: '30px' } ]; fix.detectChanges(); await wait(200); const cache = (fix.componentInstance.parentVirtDir as any).heightCache; expect(cache).toEqual([130, 100, 100, 100, 100, 100, 100, 100, 100, 100]); fix.componentInstance.scrollTop(400); fix.detectChanges(); await wait(200); expect(cache).toEqual([130, 100, 100, 100, 100, 100, 100, 130, 130, 130]); }); }); describe('vertical virtual component no data', () => { let fix: ComponentFixture<VerticalVirtualComponent>; configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [VerticalVirtualNoDataComponent, VerticalVirtualComponent], providers: [{ provide: NgZone, useFactory: () => new TestNgZone() }] }).compileComponents(); })); beforeEach(() => { fix = TestBed.createComponent(VerticalVirtualNoDataComponent); }); it('should allow initially undefined value for igxForOf and then detect changes correctly once the value is updated', () => { expect(() => { fix.detectChanges(); }).not.toThrow(); displayContainer = fix.nativeElement.querySelector(DISPLAY_CONTAINER); verticalScroller = fix.nativeElement.querySelector(VERTICAL_SCROLLER); expect(displayContainer).not.toBeNull(); expect(verticalScroller).not.toBeNull(); fix.componentInstance.height = '400px'; fix.detectChanges(); fix.componentInstance.height = '500px'; fix.detectChanges(); let rowsRendered = displayContainer.querySelectorAll('div'); expect(rowsRendered.length).toBe(0); fix.componentInstance.data = dg.generateVerticalData(fix.componentInstance.cols); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll('div'); expect(rowsRendered.length).not.toBe(0); }); }); describe('vertical and horizontal virtual component', () => { let fix: ComponentFixture<VirtualComponent>; configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [VirtualComponent], providers: [{ provide: NgZone, useFactory: () => new TestNgZone() }] }).compileComponents(); })); beforeEach(() => { fix = TestBed.createComponent(VirtualComponent); dg.generateData300x50000(fix.componentInstance); fix.detectChanges(); displayContainer = fix.nativeElement.querySelector(DISPLAY_CONTAINER); verticalScroller = fix.nativeElement.querySelector(VERTICAL_SCROLLER); horizontalScroller = fix.nativeElement.querySelector('igx-horizontal-virtual-helper'); expect(displayContainer).not.toBeNull(); expect(verticalScroller).not.toBeNull(); expect(horizontalScroller).not.toBeNull(); }); it('should initialize directive with vertical and horizontal virtualization', () => { /* The height of the row is set to 50px so scrolling by 100px should render the third record */ fix.componentInstance.scrollTop(100); const firstInnerDisplayContainer = displayContainer.children[0].querySelector(DISPLAY_CONTAINER); expect(firstInnerDisplayContainer).not.toBeNull(); fix.detectChanges(); const firstRecChildren = firstInnerDisplayContainer.children; for (let i = 0; i < firstRecChildren.length; i++) { expect(firstInnerDisplayContainer.children[i].textContent) .toBe(fix.componentInstance.data[2][i].toString()); } }); it('should allow scrolling at certain amount down and then to the top renders correct rows and cols', () => { fix.componentInstance.scrollTop(5000); fix.detectChanges(); fix.componentInstance.scrollTop(0); fix.detectChanges(); const firstInnerDisplayContainer = displayContainer.children[0].querySelector(DISPLAY_CONTAINER); expect(firstInnerDisplayContainer).not.toBeNull(); const firstRecChildren = firstInnerDisplayContainer.children; for (let i = 0; i < firstRecChildren.length; i++) { expect(firstInnerDisplayContainer.children[i].textContent) .toBe(fix.componentInstance.data[0][i].toString()); } }); it('should scroll to bottom and correct rows and columns should be rendered', () => { fix.componentInstance.scrollTop(2500000); const rows = displayContainer.children; const lastInnerDisplayContainer = rows[rows.length - 1].querySelector(DISPLAY_CONTAINER); expect(lastInnerDisplayContainer).not.toBeNull(); fix.detectChanges(); const lastRecChildren = lastInnerDisplayContainer.children; const data = fix.componentInstance.data; for (let i = 0; i < lastRecChildren.length; i++) { expect(lastInnerDisplayContainer.children[i].textContent) .toBe(data[data.length - 1][i].toString()); } }); it('should scroll to wheel event correctly', async () => { fix.componentInstance.parentVirtDir.dc.instance._scrollInertia.smoothingDuration = 0; /* 120 is default mousewheel on Chrome, scroll 2 records down */ await UIInteractions.simulateWheelEvent(displayContainer, 0, - 1 * 2 * 120); fix.detectChanges(); await wait(); const firstInnerDisplayContainer = displayContainer.children[0].querySelector(DISPLAY_CONTAINER); expect(firstInnerDisplayContainer).not.toBeNull(); const firstRecChildren = firstInnerDisplayContainer.children; for (let i = 0; i < firstRecChildren.length; i++) { expect(firstInnerDisplayContainer.children[i].textContent) .toBe(fix.componentInstance.data[2][i].toString()); } }); it('should scroll to the far right and last column should be visible', () => { // scroll to the last right pos fix.componentInstance.scrollLeft(90000); fix.detectChanges(); const rowChildren = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowChildren.length; i++) { expect(rowChildren[i].children.length).toBe(7); expect(rowChildren[i].children[5].textContent) .toBe(fix.componentInstance.data[i][298].toString()); expect(rowChildren[i].children[6].textContent) .toBe(fix.componentInstance.data[i][299].toString()); } }); it('should detect width change and update initially rendered columns', () => { let rows = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(rows.length).toBe(9); for (let i = 0; i < rows.length; i++) { expect(rows[i].children.length).toBe(7); expect(rows[i].children[3].textContent) .toBe(fix.componentInstance.data[i][3].toString()); } // scroll to the last right pos fix.componentInstance.width = '1200px'; fix.detectChanges(); rows = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(rows.length).toBe(9); for (let i = 0; i < rows.length; i++) { expect(rows[i].children.length).toBe(9); expect(rows[i].children[4].textContent) .toBe(fix.componentInstance.data[i][4].toString()); } }); it('should detect height change and update initially rendered rows', () => { let rows = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(rows.length).toBe(9); for (let i = 0; i < rows.length; i++) { expect(rows[i].children.length).toBe(7); expect(rows[i].children[2].textContent) .toBe(fix.componentInstance.data[i][2].toString()); } // scroll to the last right pos fix.componentInstance.height = '700px'; fix.detectChanges(); rows = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(rows.length).toBe(15); for (let i = 0; i < rows.length; i++) { expect(rows[i].children.length).toBe(7); expect(rows[i].children[2].textContent) .toBe(fix.componentInstance.data[i][2].toString()); } }); it('should not render vertical scrollbar when number of rows change to 5', () => { let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(displayContainer).not.toBeNull(); expect(verticalScroller).not.toBeNull(); expect(horizontalScroller).not.toBeNull(); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); /** Step 1. Lower the amount of rows to 5. The vertical scrollbar then should not be rendered */ dg.generateData(300, 5, fix.componentInstance); fix.detectChanges(); fix.componentInstance.scrollTop(verticalScroller.scrollTop); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(false); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(5); /** Step 2. Scroll to the left. There should be no errors then and everything should be still the same */ fix.componentInstance.scrollLeft(1000); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(false); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(5); /** Step 3. Increase the amount of rows back and vertical scrollbar should be rendered back */ dg.generateData300x50000(fix.componentInstance); fix.detectChanges(); // We trigger scrollTop with the current scroll position because otherwise the scroll events are not fired during a test. fix.componentInstance.scrollTop(verticalScroller.scrollTop); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(horizontalScroller.scrollLeft).toBe(1000); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); }); it('should not render vertical scrollbars when number of rows change to 0 after scrolling down', () => { let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(displayContainer).not.toBeNull(); expect(verticalScroller).not.toBeNull(); expect(horizontalScroller).not.toBeNull(); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); dg.generateData300x50000(fix.componentInstance); fix.detectChanges(); /** Step 1. Scroll to the bottom. */ fix.componentInstance.scrollTop(100000); fix.detectChanges(); /** Step 2. Lower the amount of rows to 5. The vertical scrollbar then should not be rendered */ fix.componentInstance.data = []; fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(false); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(0); /** * Step 3. Set the amount of rows back and vertical scrollbar should be rendered back then. * It should reset the scroll position. */ dg.generateData300x50000(fix.componentInstance); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(verticalScroller.scrollTop).toBe(0); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); }); it('should not render vertical scrollbar when number of rows change to 0 after scrolling right', async () => { let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); let colsRendered = rowsRendered[0].children; expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); expect(colsRendered.length).toBe(7); /** Step 1. Scroll to the right. */ fix.componentInstance.scrollLeft(1000); fix.detectChanges(); await wait(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[i][5].toString()); } /** Step 2. Lower the amount of cols to 0 so there would be no horizontal scrollbar */ fix.componentInstance.data = []; fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(false); expect(rowsRendered.length).toBe(0); /** Step 3. Set the data back to and it should render both scrollbars. It should reset the scroll position */ dg.generateData300x50000(fix.componentInstance); fix.detectChanges(); await wait(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); colsRendered = rowsRendered[0].children; expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[i][5].toString()); } }); it('should not render horizontal scrollbars when number of cols change to 3', () => { let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); let colsRendered = rowsRendered[0].children; expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); expect(colsRendered.length).toBe(7); /** Step 1. Lower the amount of cols to 3 so there would be no horizontal scrollbar */ dg.generateData(3, 50000, fix.componentInstance); fix.detectChanges(); // We trigger scrollTop with the current scroll position because otherwise the scroll events are not fired during a test. fix.componentInstance.scrollTop(verticalScroller.scrollTop); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); colsRendered = rowsRendered[0].children; expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(false); expect(rowsRendered.length).toBe(9); expect(colsRendered.length).toBe(3); /** Step 2. Scroll down. There should be no errors then and everything should be still the same */ fix.componentInstance.scrollTop(1000); fix.detectChanges(); // We trigger scrollTop with the current scroll position because otherwise the scroll events are not fired during a test. fix.componentInstance.scrollTop(verticalScroller.scrollTop); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); colsRendered = rowsRendered[0].children; expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(false); expect(rowsRendered.length).toBe(9); expect(colsRendered.length).toBe(3); /** Step 3. Set the data back to have 300 columns and the horizontal scrollbar should render now. */ dg.generateData300x50000(fix.componentInstance); fix.detectChanges(); // We trigger scrollTop with the current scroll position because otherwise the scroll events are not fired during a test. fix.componentInstance.scrollTop(verticalScroller.scrollTop); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); colsRendered = rowsRendered[0].children; expect(verticalScroller.scrollTop).toBe(1000); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(rowsRendered.length).toBe(9); expect(colsRendered.length).toBe(7); }); it('should scroll down when using touch events', async () => { let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[i][1].toString()); } const dcElem = fix.componentInstance.parentVirtDir.dc.instance._viewContainer.element.nativeElement; UIInteractions.simulateTouchStartEvent(dcElem, 200, 200); UIInteractions.simulateTouchMoveEvent(dcElem, 200, -300); fix.detectChanges(); await wait(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[10 + i][1].toString()); } }); xit('should apply inertia when swiping via touch interaction.', async () => { const dcElem = fix.componentInstance.parentVirtDir.dc.instance._viewContainer.element.nativeElement; // spyOn(fix.componentInstance.parentVirtDir, 'onScroll'); await UIInteractions.simulateTouchStartEvent( dcElem, 0, -150 ); await wait(1); await UIInteractions.simulateTouchMoveEvent(dcElem, 0, -180); await UIInteractions.simulateTouchEndEvent(dcElem, 0, -200); fix.detectChanges(); // wait for inertia to complete await wait(1500); fix.detectChanges(); const scrStepArray = fix.componentInstance.parentVirtDir.scrStepArray; expect(scrStepArray.length).toBeGreaterThan(55); // check if inertia first accelerates then decelerate const first = scrStepArray[0]; const mid = scrStepArray[10]; const end = scrStepArray[60]; expect(first).toBeLessThan(mid); expect(end).toBeLessThan(mid); }); it('should scroll left when using touch events', () => { let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[i][1].toString()); } const dcElem = fix.componentInstance.childVirtDirs.first.dc.instance._viewContainer.element.nativeElement; UIInteractions.simulateTouchStartEvent(dcElem, 200, 200); UIInteractions.simulateTouchMoveEvent(dcElem, -800, 0); // Trigger onScroll fix.componentInstance.scrollLeft(horizontalScroller.scrollLeft); fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[i][5].toString()); } }); it('should load next row and remove first row when using scrollNext method', () => { let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[i][1].toString()); } fix.componentInstance.parentVirtDir.scrollNext(); fix.componentInstance.scrollTop(verticalScroller.scrollTop); // Trigger onScroll manually. fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[1 + i][1].toString()); } }); it('should load previous row and remove last row when using scrollPrev method', () => { /** Step 1. Scroll down 500px first so we then have what to load previously */ fix.componentInstance.scrollTop(500); fix.detectChanges(); let rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[10 + i][1].toString()); } /** Step 2. Execute scrollPrev to load previous row */ fix.componentInstance.parentVirtDir.scrollPrev(); fix.componentInstance.scrollTop(verticalScroller.scrollTop); // Trigger onScroll manually. fix.detectChanges(); rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < rowsRendered.length; i++) { // Check only the second col, no need for the others expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[9 + i][1].toString()); } }); it('should not wrap around with scrollNext and scrollPrev', async () => { const forOf = fix.componentInstance.parentVirtDir; forOf.scrollPrev(); fix.detectChanges(); await wait(200); expect(forOf.state.startIndex).toEqual(0); forOf.scrollTo(forOf.igxForOf.length - 1); fix.detectChanges(); await wait(200); expect(forOf.state.startIndex).toEqual(forOf.igxForOf.length - forOf.state.chunkSize); forOf.scrollNext(); fix.detectChanges(); await wait(200); expect(forOf.state.startIndex).toEqual(forOf.igxForOf.length - forOf.state.chunkSize); }); it('should prevent scrollTo() when called with numbers outside the scope of the data records.', () => { fix.componentInstance.parentVirtDir.scrollTo(-1); expect(fix.componentInstance.parentVirtDir.state.startIndex).toBe(0); fix.componentInstance.parentVirtDir.scrollTo(fix.componentInstance.data.length + 1); expect(fix.componentInstance.parentVirtDir.state.startIndex).toBe(0); }); it('should set correct left offset when scrolling to right, clearing data and then setting new data', async () => { /** Scroll left 1500px */ fix.componentInstance.scrollLeft(1500); fix.detectChanges(); await wait(); /** Timeout for scroll event to trigger during test */ let firstRowDisplayContainer = fix.nativeElement.querySelectorAll(DISPLAY_CONTAINER)[1]; expect(firstRowDisplayContainer.style.left).toEqual('-82px'); dg.generateData(300, 0, fix.componentInstance); fix.detectChanges(); dg.generateData300x50000(fix.componentInstance); fix.detectChanges(); await wait(); /** Offset should be equal to the offset before so there is no misalignment */ firstRowDisplayContainer = fix.nativeElement.querySelectorAll(DISPLAY_CONTAINER)[1]; expect(firstRowDisplayContainer.style.left).toEqual('-82px'); }); it('should correctly scroll to the last element when using the scrollTo method', () => { spyOn(fix.componentInstance.parentVirtDir.chunkLoad, 'emit'); /** Scroll to the last 49999 row. */ fix.componentInstance.parentVirtDir.scrollTo(49999); fix.componentInstance.scrollTop(verticalScroller.scrollTop); fix.detectChanges(); expect(fix.componentInstance.parentVirtDir.chunkLoad.emit).toHaveBeenCalledTimes(1); const rowsRendered = displayContainer.querySelectorAll(DISPLAY_CONTAINER); for (let i = 0; i < 8; i++) { expect(rowsRendered[i].children[1].textContent) .toBe(fix.componentInstance.data[49991 + i][1].toString()); } }); it('should return correct value for getItemCountInView API. ', async () => { /** Scroll left 1500px and top 105px */ fix.componentInstance.scrollLeft(1500); fix.componentInstance.scrollTop(105); fix.detectChanges(); fix.componentInstance.parentVirtDir.cdr.detectChanges(); await wait(); expect(fix.componentInstance.parentVirtDir.getItemCountInView()).toBe(7); const hDirective = fix.componentInstance.childVirtDirs.toArray()[0]; expect(hDirective.getItemCountInView()).toBe(2); }); it('should emit the chunkPreload/chunkLoad only when startIndex or chunkSize have changed.', async () => { const verticalDir = fix.componentInstance.parentVirtDir; const chunkLoadSpy = spyOn<any>(verticalDir.chunkLoad, 'emit').and.callThrough(); const chunkPreLoadSpy = spyOn<any>(verticalDir.chunkPreload, 'emit').and.callThrough(); // scroll so that start index does not change. fix.componentInstance.scrollTop(1); fix.detectChanges(); await wait(); expect(chunkLoadSpy).toHaveBeenCalledTimes(0); expect(chunkPreLoadSpy).toHaveBeenCalledTimes(0); // scroll so that start index changes. fix.componentInstance.scrollTop(100); fix.detectChanges(); await wait(); expect(chunkLoadSpy).toHaveBeenCalledTimes(1); expect(chunkPreLoadSpy).toHaveBeenCalledTimes(1); // change size so that chunk size does not change fix.componentInstance.height = '399px'; fix.detectChanges(); await wait(); expect(chunkLoadSpy).toHaveBeenCalledTimes(1); // change size so that chunk size changes fix.componentInstance.height = '1500px'; fix.detectChanges(); await wait(); expect(chunkLoadSpy).toHaveBeenCalledTimes(2); }); }); describe('variable size component', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [VirtualVariableSizeComponent] }).compileComponents(); })); it('should update display container classes when content state changes from virtualized to non-virtualized.', () => { const fix = TestBed.createComponent(VirtualVariableSizeComponent); fix.detectChanges(); let displayContainerDebugEl: DebugElement = fix.debugElement.query(By.css(DISPLAY_CONTAINER)); // No size and no data - display container should be inactive expect(displayContainerDebugEl.classes[INACTIVE_VIRT_CONTAINER]).toBeTruthy(); // set size fix.componentInstance.height = '500px'; fix.detectChanges(); displayContainerDebugEl = fix.debugElement.query(By.css(DISPLAY_CONTAINER)); // Has size but no data - display container should be inactive expect(displayContainerDebugEl.classes[INACTIVE_VIRT_CONTAINER]).toBeTruthy(); // set data with 1 rec. fix.componentInstance.data = fix.componentInstance.generateData(1); fix.detectChanges(); displayContainerDebugEl = fix.debugElement.query(By.css(DISPLAY_CONTAINER)); // Has size but not enough data to be virtualized - display container should be inactive expect(displayContainerDebugEl.classes[INACTIVE_VIRT_CONTAINER]).toBeTruthy(); // set data with 1000 recs. fix.componentInstance.data = fix.componentInstance.generateData(1000); fix.detectChanges(); displayContainerDebugEl = fix.debugElement.query(By.css(DISPLAY_CONTAINER)); // Has size and enough data to be virtualized - display container should be active. expect(displayContainerDebugEl.classes[INACTIVE_VIRT_CONTAINER]).toBeFalsy(); }); }); describe('remote virtual component', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [RemoteVirtualizationComponent] }).compileComponents(); })); it('should allow remote virtualization', async () => { const fix = TestBed.createComponent(RemoteVirtualizationComponent); fix.componentRef.hostView.detectChanges(); fix.detectChanges(); displayContainer = fix.nativeElement.querySelector(DISPLAY_CONTAINER); verticalScroller = fix.nativeElement.querySelector(VERTICAL_SCROLLER); // verify data is loaded let rowsRendered = displayContainer.children; let data = fix.componentInstance.data.source.getValue(); for (let i = 0; i < rowsRendered.length; i++) { expect(rowsRendered[i].textContent.trim()) .toBe(data[i].toString()); } // scroll down verticalScroller.scrollTop = 10000; fix.detectChanges(); await wait(); // verify data is loaded rowsRendered = displayContainer.children; data = fix.componentInstance.data.source.getValue(); for (let i = fix.componentInstance.parentVirtDir.state.startIndex; i < rowsRendered.length; i++) { expect(rowsRendered[i].textContent.trim()) .toBe(data[i].toString()); } }); }); describe('remote virtual component with specified igxForTotalItemCount', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [RemoteVirtCountComponent] }).compileComponents(); })); it('should apply remote virtualization correctly', async () => { const fix = TestBed.createComponent(RemoteVirtCountComponent); fix.componentRef.hostView.detectChanges(); fix.detectChanges(); displayContainer = fix.nativeElement.querySelector(DISPLAY_CONTAINER); verticalScroller = fix.nativeElement.querySelector(VERTICAL_SCROLLER); // verify data is loaded let rowsRendered = displayContainer.children; let data = fix.componentInstance.data.source.getValue(); for (let i = 0; i < rowsRendered.length; i++) { expect(rowsRendered[i].textContent.trim()) .toBe(data[i].toString()); } // scroll down verticalScroller.scrollTop = 10000; fix.detectChanges(); await wait(); // verify data is loaded rowsRendered = displayContainer.children; data = fix.componentInstance.data.source.getValue(); for (let i = fix.componentInstance.parentVirtDir.state.startIndex; i < rowsRendered.length; i++) { expect(rowsRendered[i].textContent.trim()) .toBe(data[i].toString()); } }); }); describe('no width and height component', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [NoWidthAndHeightComponent] }).compileComponents(); })); it('should use itemSize when no width or height are provided', () => { const fix = TestBed.createComponent(NoWidthAndHeightComponent); fix.componentRef.hostView.detectChanges(); fix.detectChanges(); const children = fix.componentInstance.childVirtDirs; const instance = fix.componentInstance; const expectedElementsLength = (parseInt(instance.width, 10) / instance.itemSize) + 1; expect(children.length).toEqual(expectedElementsLength); }); }); describe('even odd first last functions', () => { configureTestSuite(); beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [LocalVariablesComponent] }).compileComponents(); })); it('should differentiate even odd items', () => { const fix = TestBed.createComponent(LocalVariablesComponent); fix.detectChanges(); const allItems: DebugElement[] = fix.debugElement.queryAll(By.css(DISPLAY_CON