UNPKG

igniteui-angular-sovn

Version:

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

988 lines (819 loc) 155 kB
import { AfterViewInit, ChangeDetectorRef, Component, Injectable, OnInit, ViewChild, TemplateRef } from '@angular/core'; import { TestBed, fakeAsync, tick, flush, waitForAsync } from '@angular/core/testing'; import { BehaviorSubject, Observable } from 'rxjs'; import { By } from '@angular/platform-browser'; import { NoopAnimationsModule } from '@angular/platform-browser/animations'; import { IgxGridComponent } from './grid.component'; import { IgxColumnComponent } from '../columns/column.component'; import { IForOfState } from '../../directives/for-of/for_of.directive'; import { DisplayDensity } from '../../core/density'; import { GridColumnDataType } from '../../data-operations/data-util'; import { GridTemplateStrings } from '../../test-utils/template-strings.spec'; import { SampleTestData } from '../../test-utils/sample-test-data.spec'; import { BasicGridComponent } from '../../test-utils/grid-base-components.spec'; import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec'; import { IgxStringFilteringOperand, IgxNumberFilteringOperand } from '../../data-operations/filtering-condition'; import { configureTestSuite } from '../../test-utils/configure-suite'; import { GridSelectionMode } from '../common/enums'; import { FilteringExpressionsTree } from '../../data-operations/filtering-expressions-tree'; import { FilteringLogic } from '../../data-operations/filtering-expression.interface'; import { IgxTabContentComponent, IgxTabHeaderComponent, IgxTabItemComponent, IgxTabsComponent } from '../../tabs/tabs/public_api'; import { IgxGridRowComponent } from './grid-row.component'; import { ISortingExpression, SortingDirection } from '../../data-operations/sorting-strategy'; import { GRID_SCROLL_CLASS } from '../../test-utils/grid-functions.spec'; import { AsyncPipe, NgFor, NgIf } from '@angular/common'; import { IgxPaginatorComponent, IgxPaginatorContentDirective } from '../../paginator/paginator.component'; import { IgxGridFooterComponent, IgxGridRow, IgxGroupByRow, IgxSummaryRow } from '../public_api'; describe('IgxGrid Component Tests #grid', () => { const MIN_COL_WIDTH = '136px'; const COLUMN_HEADER_CLASS = '.igx-grid-th'; const TBODY_CLASS = '.igx-grid__tbody-content'; const THEAD_CLASS = '.igx-grid-thead'; configureTestSuite(); describe('IgxGrid - input properties', () => { beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ NoopAnimationsModule, IgxGridTestComponent, IgxGridMarkupDeclarationComponent, IgxGridRemoteVirtualizationComponent, IgxGridRemoteOnDemandComponent, IgxGridEmptyMessage100PercentComponent ] }) .compileComponents(); })); it('should initialize a grid with columns from markup', () => { const fix = TestBed.createComponent(IgxGridMarkupDeclarationComponent); fix.detectChanges(); const grid = fix.componentInstance.instance; const domGrid = fix.debugElement.query(By.css('igx-grid')).nativeElement; expect(grid).toBeDefined('Grid initializing through markup failed'); expect(grid.columnList.length).toEqual(2, 'Invalid number of columns initialized'); expect(grid.rowList.length).toEqual(3, 'Invalid number of rows initialized'); expect(grid.id).toContain('igx-grid-'); expect(domGrid.id).toContain('igx-grid-'); grid.id = 'customGridId'; fix.detectChanges(); expect(grid.id).toBe('customGridId'); expect(domGrid.id).toBe('customGridId'); expect(fix.componentInstance.columnEventCount).toEqual(2); }); it('should initialize a grid with autogenerated columns', () => { const fix = TestBed.createComponent(IgxGridTestComponent); fix.componentInstance.data = [ { Number: 1, String: '1', Boolean: true, Date: new Date(Date.now()) } ]; fix.componentInstance.columns = []; fix.componentInstance.autoGenerate = true; fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); const grid = fix.componentInstance.grid; expect(grid).toBeDefined('Grid initializing through autoGenerate failed'); expect(grid.columns.length).toEqual(4, 'Invalid number of columns initialized'); expect(grid.rowList.length).toEqual(1, 'Invalid number of rows initialized'); expect(grid.columns[0].dataType).toEqual(GridColumnDataType.Number, 'Invalid dataType set on column'); expect(grid.columns.find((col) => col.index === 1).dataType) .toEqual(GridColumnDataType.String, 'Invalid dataType set on column'); expect(grid.columns.find((col) => col.index === 2).dataType) .toEqual(GridColumnDataType.Boolean, 'Invalid dataType set on column'); expect(grid.columns[grid.columns.length - 1].dataType).toEqual(GridColumnDataType.Date, 'Invalid dataType set on column'); expect(fix.componentInstance.columnEventCount).toEqual(4); }); it('should initialize a grid and change column properties during initialization', () => { const fix = TestBed.createComponent(IgxGridTestComponent); fix.componentInstance.columns = []; fix.componentInstance.autoGenerate = true; fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); const grid = fix.componentInstance.grid; grid.columnList.forEach((column) => { expect(column.filterable).toEqual(true); expect(column.sortable).toEqual(true); }); }); it('should skip properties from autoGenerateExclude when auto-generating columns', () => { const fix = TestBed.createComponent(IgxGridTestComponent); fix.componentInstance.data = [ { Number: 1, String: '1', Boolean: true, Date: new Date(Date.now()) } ]; fix.componentInstance.autoGenerateExclude = ['Date', 'String']; fix.componentInstance.columns = []; fix.componentInstance.autoGenerate = true; fix.detectChanges(); const grid = fix.componentInstance.grid; expect(grid.columns.map(col => col.field)).toEqual(['Number', 'Boolean'], 'Invalid columns after exclusion initialized'); }); it('should initialize a grid and allow changing columns runtime with ngFor', () => { const fix = TestBed.createComponent(IgxGridTestComponent); fix.detectChanges(); // reverse order of ngFor bound collection fix.componentInstance.columns.reverse(); fix.detectChanges(); // check order const grid = fix.componentInstance.grid; expect(grid.columns[0].field).toBe('value'); expect(grid.columns[1].field).toBe('index'); }); it('should initialize grid with remote virtualization', async () => { const fix = TestBed.createComponent(IgxGridRemoteVirtualizationComponent); fix.detectChanges(); await wait(16); let rows = fix.componentInstance.instance.rowList.toArray(); expect(rows.length).toEqual(10); const verticalScroll = fix.componentInstance.instance.verticalScrollContainer; const elem = verticalScroll['scrollComponent'].elementRef.nativeElement; // scroll down expect(() => { elem.scrollTop = 1000; fix.detectChanges(); fix.componentRef.hostView.detectChanges(); }).not.toThrow(); fix.detectChanges(); fix.componentInstance.cdr.detectChanges(); await wait(16); rows = fix.componentInstance.instance.rowList.toArray(); const data = fix.componentInstance.data.source.getValue(); for (let i = fix.componentInstance.instance.virtualizationState.startIndex; i < rows.length; i++) { expect(rows[i].data['Col1']) .toBe(data[i]['Col1']); } }); it('should remove all rows if data becomes null/undefined.', () => { const fix = TestBed.createComponent(IgxGridRemoteVirtualizationComponent); fix.detectChanges(); const grid = fix.componentInstance.instance; expect(grid.rowList.length).toEqual(10); fix.componentInstance.nullData(); fix.detectChanges(); const noRecordsSpan = fix.debugElement.query(By.css('.igx-grid__tbody-message')); expect(grid.rowList.length).toEqual(0); expect(noRecordsSpan).toBeTruthy(); expect(noRecordsSpan.nativeElement.innerText).toBe('Grid has no data.'); }); it('height/width should be calculated depending on number of records', () => { const fix = TestBed.createComponent(IgxGridTestComponent); fix.detectChanges(); const grid = fix.componentInstance.grid; fix.componentInstance.grid.height = null; fix.detectChanges(); const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS)); const gridFooter = fix.debugElement.query(By.css('.igx-grid__tfoot')); const gridScroll = fix.debugElement.query(By.css(GRID_SCROLL_CLASS)); let gridBodyHeight; expect(grid.rowList.length).toEqual(1); expect(window.getComputedStyle(gridBody.nativeElement).height).toMatch('51px'); for (let i = 2; i <= 30; i++) { grid.addRow({ index: i, value: i }); } fix.detectChanges(); expect(grid.rowList.length).toEqual(30); expect(window.getComputedStyle(gridBody.nativeElement).height).toMatch('1530px'); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(false); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(false); grid.height = '200px'; fix.detectChanges(); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); // no horizontal scr, since columns have no width hence they should // distrubute the available width between them expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(false); const verticalScrollHeight = fix.componentInstance.getVerticalScrollHeight(); grid.width = '200px'; fix.detectChanges(); expect(fix.componentInstance.isVerticalScrollbarVisible()).toBe(true); expect(fix.componentInstance.isHorizontalScrollbarVisible()).toBe(true); expect(fix.componentInstance.getVerticalScrollHeight()).toBeLessThan(verticalScrollHeight); gridBodyHeight = parseInt(window.getComputedStyle(grid.nativeElement).height, 10) - parseInt(window.getComputedStyle(gridHeader.nativeElement).height, 10) - parseInt(window.getComputedStyle(gridFooter.nativeElement).height, 10) - parseInt(window.getComputedStyle(gridScroll.nativeElement).height, 10); expect(window.getComputedStyle(grid.nativeElement).width).toMatch('200px'); expect(window.getComputedStyle(grid.nativeElement).height).toMatch('200px'); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toEqual(gridBodyHeight); grid.height = '50%'; fix.detectChanges(); grid.width = '50%'; fix.detectChanges(); expect(window.getComputedStyle(grid.nativeElement).height).toMatch('300px'); expect(window.getComputedStyle(grid.nativeElement).width).toMatch('400px'); gridBodyHeight = parseInt(window.getComputedStyle(grid.nativeElement).height, 10) - parseInt(window.getComputedStyle(gridHeader.nativeElement).height, 10) - parseInt(window.getComputedStyle(gridFooter.nativeElement).height, 10); // The scrollbar is no longer visible expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toEqual(gridBodyHeight); }); it('should not have column misalignment when no vertical scrollbar is shown', () => { const fix = TestBed.createComponent(IgxGridTestComponent); fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); const grid = fix.componentInstance.grid; const gridBody = fix.debugElement.query(By.css(TBODY_CLASS)); const gridHeader = fix.debugElement.query(By.css(THEAD_CLASS)); expect(window.getComputedStyle(gridBody.children[0].nativeElement).width).toEqual( window.getComputedStyle(gridHeader.children[0].nativeElement).width ); expect(grid.rowList.length).toBeGreaterThan(0); }); it('should change displayDensity runtime correctly', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridTestComponent); const grid = fixture.componentInstance.grid; fixture.componentInstance.columns[1].hasSummary = true; grid.summaryRowHeight = 0; // density with custom class #6931: grid.nativeElement.classList.add('custom'); fixture.detectChanges(); const headerHight = fixture.debugElement.query(By.css(THEAD_CLASS)).query(By.css('.igx-grid__tr')).nativeElement; const rowHeight = fixture.debugElement.query(By.css(TBODY_CLASS)).query(By.css('.igx-grid__tr')).nativeElement; const summaryItemHeight = fixture.debugElement.query(By.css('.igx-grid__tfoot')) .query(By.css('.igx-grid-summary__item')).nativeElement; const summaryRowHeight = fixture.debugElement.query(By.css('.igx-grid__tfoot')).nativeElement; expect(grid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid', 'custom'])); expect(grid.defaultRowHeight).toBe(50); expect(headerHight.offsetHeight).toBe(grid.defaultRowHeight); expect(rowHeight.offsetHeight).toBe(51); expect(summaryItemHeight.offsetHeight).toBe(grid.defaultSummaryHeight - 1); expect(summaryRowHeight.offsetHeight).toBe(grid.defaultSummaryHeight); grid.displayDensity = 'cosy'; grid.summaryRowHeight = null; tick(16); fixture.detectChanges(); expect(grid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid--cosy', 'custom'])); expect(grid.defaultRowHeight).toBe(40); expect(headerHight.offsetHeight).toBe(grid.defaultRowHeight); expect(rowHeight.offsetHeight).toBe(41); expect(summaryItemHeight.offsetHeight).toBe(grid.defaultSummaryHeight - 1); expect(summaryRowHeight.offsetHeight).toBe(grid.defaultSummaryHeight); grid.displayDensity = 'compact'; grid.summaryRowHeight = undefined; tick(16); fixture.detectChanges(); expect(grid.nativeElement.classList).toEqual(jasmine.arrayWithExactContents(['igx-grid--compact', 'custom'])); expect(grid.defaultRowHeight).toBe(32); expect(headerHight.offsetHeight).toBe(grid.defaultRowHeight); expect(rowHeight.offsetHeight).toBe(33); expect(summaryItemHeight.offsetHeight).toBe(grid.defaultSummaryHeight - 1); expect(summaryRowHeight.offsetHeight).toBe(grid.defaultSummaryHeight); })); it ('checks if attributes are correctly assigned when grid has or does not have data', fakeAsync( () => { const fixture = TestBed.createComponent(IgxGridTestComponent); const grid = fixture.componentInstance.grid; fixture.componentInstance.generateData(30); fixture.detectChanges(); tick(100); // Checks if igx-grid__tbody-content attribute is null when there is data in the grid const container = fixture.nativeElement.querySelectorAll('.igx-grid__tbody-content')[0]; expect(container.getAttribute('role')).toBe(null); //Filter grid so no results are available and grid is empty grid.filter('index','111',IgxStringFilteringOperand.instance().condition('contains'),true); grid.markForCheck(); fixture.detectChanges(); expect(container.getAttribute('role')).toMatch('row'); // clear grid data and check if attribute is now 'row' grid.clearFilter(); fixture.componentInstance.clearData(); fixture.detectChanges(); tick(100); expect(container.getAttribute('role')).toMatch('row'); })); it('should render empty message', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridTestComponent); fixture.componentInstance.data = []; fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.grid; const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement; // make sure default width/height are applied when there is no data expect(domGrid.style.height).toBe('100%'); expect(domGrid.style.width).toBe('100%'); // Check for loaded rows in grid's container fixture.componentInstance.generateData(30); fixture.detectChanges(); tick(1000); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548); // Check for empty filter grid message and body less than 100px const columns = fixture.componentInstance.grid.columnList; grid.filter(columns.get(0).field, 546000, IgxNumberFilteringOperand.instance().condition('equals')); fixture.detectChanges(); tick(100); expect(gridBody.nativeElement.textContent).toEqual(grid.emptyFilteredGridMessage); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548); // Clear filter and check if grid's body height is restored based on all loaded rows grid.clearFilter(columns.get(0).field); fixture.detectChanges(); tick(100); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548); // Clearing grid's data and check for empty grid message fixture.componentInstance.clearData(); fixture.detectChanges(); tick(100); expect(gridBody.nativeElement.innerText).toMatch(grid.emptyGridMessage); })); it('should render loading indicator when loading is enabled', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridTestComponent); fixture.componentInstance.data = []; fixture.componentInstance.grid.isLoading = true; fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.grid; const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); let loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement; // make sure default width/height are applied when there is no data expect(domGrid.style.height).toBe('100%'); expect(domGrid.style.width).toBe('100%'); expect(loadingIndicator).not.toBeNull(); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); // Check for loaded rows in grid's container fixture.componentInstance.generateData(30); fixture.detectChanges(); tick(1000); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).toBeNull(); // Check for empty filter grid message and body less than 100px const columns = fixture.componentInstance.grid.columnList; grid.filter(columns.get(0).field, 546000, IgxNumberFilteringOperand.instance().condition('equals')); fixture.detectChanges(); tick(100); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548); // Clear filter and check if grid's body height is restored based on all loaded rows grid.clearFilter(columns.get(0).field); fixture.detectChanges(); tick(100); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548); // Clearing grid's data and check for empty grid message fixture.componentInstance.clearData(); fixture.detectChanges(); tick(100); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).not.toBeNull(); })); it('should render loading indicator when loading is enabled when there is height', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridTestComponent); fixture.componentInstance.data = []; fixture.componentInstance.grid.isLoading = true; fixture.componentInstance.grid.height = '400px'; fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.grid; const gridElement = fixture.debugElement.query(By.css('.igx-grid')); const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); let loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).not.toBeNull(); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); // Check for loaded rows in grid's container fixture.componentInstance.generateData(30); fixture.detectChanges(); tick(1000); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(300); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).toBeNull(); // the overlay should be shown loadingIndicator = gridElement.query(By.css('.igx-grid__loading-outlet')); expect(loadingIndicator.nativeElement.children.length).not.toBe(0); // Check for empty filter grid message and body less than 100px const columns = fixture.componentInstance.grid.columnList; grid.filter(columns.get(0).field, 546000, IgxNumberFilteringOperand.instance().condition('equals')); fixture.detectChanges(); tick(100); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); // Clear filter and check if grid's body height is restored based on all loaded rows grid.clearFilter(columns.get(0).field); fixture.detectChanges(); tick(100); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(300); // Clearing grid's data and check for empty grid message fixture.componentInstance.clearData(); fixture.detectChanges(); tick(100); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).not.toBeNull(); // the overlay should be hidden loadingIndicator = gridElement.query(By.css('.igx-grid__loading-outlet')); expect(loadingIndicator.nativeElement.children.length).toBe(0); })); it('should render loading indicator when loading is enabled and autoGenerate is enabled', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridTestComponent); fixture.componentInstance.data = []; fixture.componentInstance.grid.isLoading = true; fixture.componentInstance.columns = []; fixture.componentInstance.autoGenerate = true; fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.grid; const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS)); let loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); let colHeaders = gridHead.queryAll(By.css('igx-grid-header')); expect(loadingIndicator).not.toBeNull(); expect(colHeaders.length).toBe(0); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); // Check for loaded rows in grid's container fixture.componentInstance.grid.shouldGenerate = true; fixture.componentInstance.data = [ { Number: 1, String: '1', Boolean: true, Date: new Date(Date.now()) } ]; fixture.detectChanges(); tick(1000); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); colHeaders = gridHead.queryAll(By.css('igx-grid-header')); expect(colHeaders.length).toBeGreaterThan(0); expect(loadingIndicator).toBeNull(); // Clearing grid's data and check for empty grid message fixture.componentInstance.clearData(); fixture.detectChanges(); tick(100); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).not.toBeNull(); })); it('should render loading indicator when loading is enabled and autoGenerate is enabled and async data', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridRemoteOnDemandComponent); fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.instance; const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS)); let loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).not.toBeNull(); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); fixture.componentInstance.bind(); const colHeaders = gridHead.queryAll(By.css('igx-grid-header')); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(colHeaders.length).toBeGreaterThan(0); expect(loadingIndicator).toBeNull(); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(500); })); it('should render loading indicator when loading is enabled and the grid has empty filtering pre-applied', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridTestComponent); const grid = fixture.componentInstance.grid; grid.filteringExpressionsTree = new FilteringExpressionsTree(FilteringLogic.And); grid.filteringExpressionsTree.filteringOperands = [ { condition: IgxNumberFilteringOperand.instance().condition('equals'), fieldName: 'index', searchVal: 0 } ]; grid.isLoading = true; fixture.detectChanges(); tick(16); const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); const loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement; // make sure default width/height are applied when there is no data expect(domGrid.style.height).toBe('100%'); expect(domGrid.style.width).toBe('100%'); expect(loadingIndicator).not.toBeNull(); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); })); it('should allow applying custom loading indicator', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridRemoteOnDemandComponent); fixture.componentInstance.instance.loadingGridTemplate = fixture.componentInstance.customTemaplate; fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.instance; const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); const gridHead = fixture.debugElement.query(By.css(THEAD_CLASS)); expect(gridBody.nativeElement.textContent).toEqual('Loading...'); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); fixture.componentInstance.bind(); const colHeaders = gridHead.queryAll(By.css('igx-grid-header')); expect(colHeaders.length).toBeGreaterThan(0); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(500); })); it('should remove loading overlay when isLoading is set to false', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridTestComponent); fixture.componentInstance.data = []; fixture.componentInstance.grid.isLoading = true; fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.grid; const gridElement = fixture.debugElement.query(By.css('.igx-grid')); const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); let loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).not.toBeNull(); expect(gridBody.nativeElement.textContent).not.toEqual(grid.emptyFilteredGridMessage); // Check for loaded rows in grid's container fixture.componentInstance.generateData(30); fixture.detectChanges(); tick(1000); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBe(548); loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).toBeNull(); // the overlay should be shown loadingIndicator = gridElement.query(By.css('.igx-grid__loading-outlet')); expect(loadingIndicator.nativeElement.children.length).not.toBe(0); grid.isLoading = false; tick(16); expect(loadingIndicator.nativeElement.children.length).toBe(0); // Clearing grid's data and check for empty grid message fixture.componentInstance.clearData(); fixture.detectChanges(); tick(100); // isLoading is still false so the empty data message should show, not the loading indicator loadingIndicator = gridBody.query(By.css('.igx-grid__loading')); expect(loadingIndicator).toBeNull(); expect(gridBody.nativeElement.textContent).toEqual(grid.emptyGridMessage); })); it('should render empty message when grid height is 100%', fakeAsync(() => { const fixture = TestBed.createComponent(IgxGridEmptyMessage100PercentComponent); fixture.detectChanges(); tick(16); const grid = fixture.componentInstance.grid; const gridBody = fixture.debugElement.query(By.css(TBODY_CLASS)); const domGrid = fixture.debugElement.query(By.css('igx-grid')).nativeElement; // make sure default width/height are applied when there is no data expect(domGrid.style.height).toBe('100%'); expect(domGrid.style.width).toBe('100%'); expect(parseInt(window.getComputedStyle(gridBody.nativeElement).height, 10)).toBeGreaterThan(0); expect(gridBody.nativeElement.innerText).toMatch(grid.emptyGridMessage); })); }); describe('IgxGrid - virtualization tests', () => { beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ NoopAnimationsModule, IgxGridTestComponent ] }) .compileComponents(); })); it('should change chunk size for every record after enlarging the grid and the horizontal dirs are scrambled', async () => { const fix = TestBed.createComponent(IgxGridTestComponent); for (let i = 2; i < 100; i++) { fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i }); } fix.componentInstance.columns[0].width = '400px'; fix.componentInstance.columns[1].width = '400px'; fix.componentInstance.columns.push( { field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false }, { field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false } ); fix.detectChanges(); fix.componentInstance.grid.verticalScrollContainer.getScroll().scrollTop = 100; await wait(100); fix.detectChanges(); fix.componentInstance.grid.verticalScrollContainer.getScroll().scrollTop = 250; await wait(100); fix.detectChanges(); fix.componentInstance.grid.width = '1300px'; await wait(100); fix.detectChanges(); const rows = fix.componentInstance.grid.rowList.toArray(); for (const row of rows) { expect(row.cells.length).toEqual(4); } }); it('should not keep a cached-out template as master after column resizing', async () => { const fix = TestBed.createComponent(IgxGridTestComponent); for (let i = 2; i < 100; i++) { fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i }); } fix.componentInstance.columns[0].width = '400px'; fix.componentInstance.columns[1].width = '400px'; fix.componentInstance.columns.push( { field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false }, { field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false } ); fix.detectChanges(); fix.componentInstance.grid.groupBy({ fieldName: 'value', dir: SortingDirection.Asc }); fix.detectChanges(); fix.componentInstance.grid.getColumnByName('index').width = '100px'; fix.detectChanges(); await wait(16); const rows = fix.componentInstance.grid.dataRowList.toArray(); for (const row of rows) { expect(row.cells.length).toEqual(4); } }); it('Should scroll horizontally when press shift + mouse wheel over grid headers', (async () => { const fix = TestBed.createComponent(IgxGridTestComponent); for (let i = 2; i < 100; i++) { fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i }); } fix.componentInstance.columns[0].width = '400px'; fix.componentInstance.columns[1].width = '400px'; fix.componentInstance.columns.push( { field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false }, { field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false } ); fix.detectChanges(); const grid = fix.componentInstance.grid; grid.headerContainer.dc.instance._scrollInertia.smoothingDuration = 0; const initialScroll = grid.verticalScrollContainer.getScroll().scrollTop; const initialHorScroll = grid.headerContainer.getScroll().scrollLeft; const displayContainer = grid.headerContainer.dc.instance._viewContainer.element.nativeElement; await UIInteractions.simulateWheelEvent(displayContainer, 0, -240, true); fix.detectChanges(); await wait(16); expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll); expect(grid.headerContainer.getScroll().scrollLeft).toBeGreaterThan(initialHorScroll + 50); await UIInteractions.simulateWheelEvent(displayContainer, 0, 240, true); fix.detectChanges(); await wait(16); expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll); expect(grid.headerContainer.getScroll().scrollLeft).toEqual(initialHorScroll); })); it('Should scroll horizontally when press shift + mouse wheel over grid data row', (async () => { const fix = TestBed.createComponent(IgxGridTestComponent); for (let i = 2; i < 100; i++) { fix.componentInstance.data.push({ index: i, value: i, desc: i, detail: i }); } fix.componentInstance.columns[0].width = '400px'; fix.componentInstance.columns[1].width = '400px'; fix.componentInstance.columns.push( { field: 'desc', header: 'desc', dataType: 'number', width: '400px', hasSummary: false }, { field: 'detail', header: 'detail', dataType: 'number', width: '400px', hasSummary: false } ); fix.detectChanges(); const grid = fix.componentInstance.grid; grid.rowList.first.virtDirRow.dc.instance._scrollInertia.smoothingDuration = 0; const initialScroll = grid.verticalScrollContainer.getScroll().scrollTop; const initialHorScroll = grid.rowList.first.virtDirRow.getScroll().scrollLeft; const cell = grid.gridAPI.get_cell_by_index(3, 'value'); UIInteractions.simulateClickAndSelectEvent(cell); fix.detectChanges(); const displayContainer = grid.rowList.first.virtDirRow.dc.instance._viewContainer.element.nativeElement; await UIInteractions.simulateWheelEvent(displayContainer, 0, -240, true); fix.detectChanges(); await wait(16); expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll); expect(grid.headerContainer.getScroll().scrollLeft).toBeGreaterThan(initialHorScroll + 50); await UIInteractions.simulateWheelEvent(displayContainer, 0, -240, true); fix.detectChanges(); await wait(16); expect(grid.verticalScrollContainer.getScroll().scrollTop).toBe(initialScroll); expect(grid.headerContainer.getScroll().scrollLeft).toBeGreaterThanOrEqual(2 * (initialHorScroll + 50)); })); }); describe('IgxGrid - default rendering for rows and columns', () => { beforeAll(waitForAsync(() => { TestBed.configureTestingModule({ imports: [ NoopAnimationsModule, IgxGridDefaultRenderingComponent, IgxGridColumnPercentageWidthComponent, IgxGridWrappedInContComponent, IgxGridFormattingComponent, IgxGridFixedContainerHeightComponent ] }) .compileComponents(); })); it('should init columns with width >= 136px when 5 rows and 5 columns are rendered', () => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); fix.componentInstance.initColumnsRows(5, 5); fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); const grid = fix.componentInstance.grid; expect(grid.columnList.get(0).width).not.toBeLessThan(136); expect(grid.columnList.get(2).width).not.toBeLessThan(136); expect(grid.width).toMatch('100%'); expect(grid.rowList.length).toBeGreaterThan(0); }); it('should init columns with width >= 136px when 30 rows and 10 columns are rendered', () => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); fix.componentInstance.initColumnsRows(30, 10); fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); const grid = fix.componentInstance.grid; expect(grid.columnList.get(0).width).not.toBeLessThan(136); expect(grid.columnList.get(4).width).not.toBeLessThan(136); expect(grid.columnList.get(6).width).not.toBeLessThan(136); expect(grid.width).toMatch('100%'); expect(grid.rowList.length).toBeGreaterThan(0); }); it(`should init columns with width >= 136px and a horizontal scrollbar when 1000 rows and 30 columns are rendered`, () => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); fix.componentInstance.initColumnsRows(1000, 30); fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); const grid = fix.componentInstance.grid; expect(grid.columnList.get(0).width).not.toBeLessThan(136); expect(grid.columnList.get(4).width).not.toBeLessThan(136); expect(grid.columnList.get(14).width).not.toBeLessThan(136); expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); }); it(`should init columns with width >= 136px and a horizontal scrollbar when 200 rows and 150 columns are rendered`, () => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); fix.componentInstance.initColumnsRows(200, 150); fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); const grid = fix.componentInstance.grid; expect(grid.columnList.get(0).width).not.toBeLessThan(136); expect(grid.columnList.get(4).width).not.toBeLessThan(136); expect(grid.columnList.get(100).width).not.toBeLessThan(136); expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); expect(grid.rowList.length).toBeGreaterThan(0); }); it('should account for columns with set width when determining default column width when grid has 100% width', () => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); const grid = fix.componentInstance.grid; fix.componentInstance.initColumnsRows(5, 5); fix.componentInstance.changeInitColumns = true; fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); expect(grid.width).toEqual('100%'); expect(grid.columnList.get(0).width).toEqual('100px'); expect(grid.columnList.get(4).width).toEqual('100px'); const actualGridWidth = grid.nativeElement.clientWidth; const expectedDefWidth = Math.max(Math.floor((actualGridWidth - parseInt(grid.columnList.get(0).width, 10) - parseInt(grid.columnList.get(4).width, 10)) / 3), parseInt(MIN_COL_WIDTH, 10)); expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(1).width, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(2).width, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(3).width, 10)).toEqual(expectedDefWidth); grid.columnList.forEach((column) => { const width = parseInt(column.width, 10); const minWidth = parseInt(grid.columnWidth, 10); if (column.index !== 0 && column.index !== 4) { expect(width).toBeGreaterThanOrEqual(minWidth); } }); expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(false); expect(grid.rowList.length).toBeGreaterThan(0); }); it('should account for columns with set width when determining default column width when grid has px width', () => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); const grid = fix.componentInstance.grid; grid.width = '600px'; fix.componentInstance.initColumnsRows(5, 5); fix.componentInstance.changeInitColumns = true; fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); expect(grid.width).toEqual('600px'); expect(grid.columnList.get(0).width).toEqual('100px'); expect(grid.columnList.get(4).width).toEqual('100px'); const actualGridWidth = grid.nativeElement.clientWidth; const expectedDefWidth = Math.max(Math.floor((actualGridWidth - parseInt(grid.columnList.get(0).width, 10) - parseInt(grid.columnList.get(4).width, 10)) / 3), parseInt(MIN_COL_WIDTH, 10)); expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(1).width, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(2).width, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(3).width, 10)).toEqual(expectedDefWidth); grid.columnList.forEach((column) => { const width = parseInt(column.width, 10); const minWidth = parseInt(grid.columnWidth, 10); if (column.index !== 0 && column.index !== 4) { expect(width).toBeGreaterThanOrEqual(minWidth); } }); expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(true); expect(grid.rowList.length).toBeGreaterThan(0); }); it(`should account for columns with set width when determining default column width when grid has 100% width and there are enough rows to cover the grid's height`, () => { const fix = TestBed.createComponent(IgxGridDefaultRenderingComponent); const grid = fix.componentInstance.grid; fix.componentInstance.initColumnsRows(30, 5); fix.componentInstance.changeInitColumns = true; fix.detectChanges(); // fakeAsync is not needed. Need a second change detection cycle for height changes to be applied. fix.detectChanges(); expect(grid.width).toEqual('100%'); expect(grid.columnList.get(0).width).toEqual('100px'); expect(grid.columnList.get(4).width).toEqual('100px'); const actualGridWidth = grid.unpinnedWidth; const expectedDefWidth = Math.max(Math.floor((actualGridWidth - parseInt(grid.columnList.get(0).width, 10) - parseInt(grid.columnList.get(4).width, 10)) / 3), parseInt(MIN_COL_WIDTH, 10)); expect(parseInt(grid.columnWidth, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(1).width, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(2).width, 10)).toEqual(expectedDefWidth); expect(parseInt(grid.columnList.get(3).width, 10)).toEqual(expectedDefWidth); grid.columnList.forEach((column) => { const width = parseInt(column.width, 10); const minWidth = parseInt(grid.columnWidth, 10); if (column.index !== 0 && column.index !== 4) { expect(width).toBeGreaterThanOrEqual(minWidth); } }); expect(fix.componentInstance.isHorizonatScrollbarVisible()).toBe(false); expect(grid.rowList.length).toBeGreaterThan(0); }); it(`should account for columns with set width when determining default column width when grid has 100% width and there ar