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
text/typescript
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