igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
475 lines (400 loc) • 24.5 kB
text/typescript
import { By } from '@angular/platform-browser';
import { IgxTreeGridComponent } from '../grids/tree-grid/public_api';
import { CellType } from '../grids/common/grid.interface';
import { IgxCheckboxComponent } from '../checkbox/checkbox.component';
import { UIInteractions, wait } from './ui-interactions.spec';
import { GridFunctions } from './grid-functions.spec';
import { IgxRowDirective } from '../grids/row.directive';
import { IgxGridCellComponent } from '../grids/cell.component';
import { DebugElement } from '@angular/core';
// CSS class should end with a number that specified the row's level
const TREE_CELL_DIV_INDENTATION_CSS_CLASS = '.igx-grid__tree-cell--padding-level-';
const DEBOUNCETIME = 30;
export const TREE_ROW_DIV_SELECTION_CHECKBOX_CSS_CLASS = '.igx-grid__cbx-selection';
export const TREE_ROW_SELECTION_CSS_CLASS = 'igx-grid__tr--selected';
export const TREE_CELL_SELECTION_CSS_CLASS = 'igx-grid__td--selected';
export const TREE_HEADER_ROW_CSS_CLASS = '.igx-grid-thead';
export const CHECKBOX_INPUT_CSS_CLASS = '.igx-checkbox__input';
export const TREE_CELL_INDICATOR_CSS_CLASS = '.igx-grid__tree-grouping-indicator';
export const TREE_CELL_LOADING_CSS_CLASS = '.igx-grid__tree-loading-indicator';
export const NUMBER_CELL_CSS_CLASS = 'igx-grid__td--number';
export const CELL_VALUE_DIV_CSS_CLASS = '.igx-grid__td-text';
export const ROW_EDITING_BANNER_OVERLAY_CLASS = 'igx-overlay__content';
export class TreeGridFunctions {
public static getHeaderRow(fix) {
return fix.debugElement.query(By.css(TREE_HEADER_ROW_CSS_CLASS));
}
public static getAllRows(fix): DebugElement [] {
return fix.debugElement.queryAll(By.css('igx-tree-grid-row'));
}
public static getTreeCell(rowDOM) {
return rowDOM.query(By.css('igx-tree-grid-cell'));
}
public static getCell(fix, rowIndex, columnKey) {
const rowDOM = TreeGridFunctions.sortElementsVertically(TreeGridFunctions.getAllRows(fix))[rowIndex];
const rowCells = [TreeGridFunctions.getTreeCell(rowDOM)].concat(TreeGridFunctions.getNormalCells(rowDOM));
return rowCells.filter(domCell => domCell.componentInstance.column.field === columnKey)[0];
}
public static getTreeCells(fix) {
return fix.debugElement.queryAll(By.css('igx-tree-grid-cell'));
}
public static getNormalCells(rowDOM) {
return rowDOM.queryAll(By.css('igx-grid-cell'));
}
public static getColumnCells(fix, columnKey) {
const allTreeCells = fix.debugElement.queryAll(By.css('igx-tree-grid-cell'));
const allNormalCells = fix.debugElement.queryAll(By.css('igx-grid-cell'));
const allDOMCells = allTreeCells.concat(allNormalCells);
return allDOMCells.filter(domCell => domCell.componentInstance.column.field === columnKey);
}
public static getAllCells(fix) {
const allTreeCells = fix.debugElement.queryAll(By.css('igx-tree-grid-cell'));
const allNormalCells = fix.debugElement.queryAll(By.css('igx-grid-cell'));
return allTreeCells.concat(allNormalCells);
}
public static getCellValue(fix, rowIndex, columnKey) {
const rowDOM = TreeGridFunctions.sortElementsVertically(TreeGridFunctions.getAllRows(fix))[rowIndex];
const rowCells = [TreeGridFunctions.getTreeCell(rowDOM)].concat(TreeGridFunctions.getNormalCells(rowDOM));
const cell = rowCells.filter(domCell => domCell.componentInstance.column.field === columnKey)[0];
const valueDiv = cell.query(By.css(CELL_VALUE_DIV_CSS_CLASS));
return valueDiv.nativeElement.textContent;
}
public static getExpansionIndicatorDiv(rowDOM) {
const treeGridCell = TreeGridFunctions.getTreeCell(rowDOM);
return treeGridCell.query(By.css(TREE_CELL_INDICATOR_CSS_CLASS));
}
public static getLoadingIndicatorDiv(rowDOM) {
const treeGridCell = TreeGridFunctions.getTreeCell(rowDOM);
return treeGridCell.query(By.css(TREE_CELL_LOADING_CSS_CLASS));
}
public static getHeaderCell(fix, columnKey) {
const headerCells = fix.debugElement.queryAll(By.css('igx-grid-header'));
const headerCell = headerCells.filter((cell) => cell.nativeElement.textContent.indexOf(columnKey) !== -1)[0];
return headerCell;
}
public static getHeaderCellMultiColHeaders(fix, columnKey) {
const headerCells = fix.debugElement.queryAll(By.css('igx-grid-header'));
const headerCell = headerCells.filter((cell) => cell.nativeElement.textContent.indexOf(columnKey) !== -1).pop();
return headerCell;
}
public static getRowCheckbox(rowDOM) {
const checkboxDiv = TreeGridFunctions.getRowCheckboxDiv(rowDOM);
return checkboxDiv.query(By.css(CHECKBOX_INPUT_CSS_CLASS));
}
public static getRowCheckboxDiv(rowDOM) {
return rowDOM.query(By.css(TREE_ROW_DIV_SELECTION_CHECKBOX_CSS_CLASS));
}
public static clickHeaderCell(fix, columnKey) {
const cell = TreeGridFunctions.getHeaderCell(fix, columnKey);
cell.nativeElement.dispatchEvent(new Event('click'));
}
public static clickRowSelectionCheckbox(fix, rowIndex) {
const rowDOM = TreeGridFunctions.sortElementsVertically(TreeGridFunctions.getAllRows(fix))[rowIndex];
const checkbox = TreeGridFunctions.getRowCheckboxDiv(rowDOM);
checkbox.nativeElement.dispatchEvent(new Event('click'));
}
public static clickHeaderRowSelectionCheckbox(fix) {
const headerRow = TreeGridFunctions.getHeaderRow(fix);
const checkbox = TreeGridFunctions.getRowCheckboxDiv(headerRow);
checkbox.nativeElement.dispatchEvent(new Event('click'));
}
public static clickRowIndicator(fix, rowIndex) {
const rowDOM = TreeGridFunctions.sortElementsVertically(TreeGridFunctions.getAllRows(fix))[rowIndex];
const indicatorDiv = TreeGridFunctions.getExpansionIndicatorDiv(rowDOM);
indicatorDiv.triggerEventHandler('click', new Event('click'));
}
/**
* Verifies that the first cell of every row is its tree cell.
*/
public static verifyCellsPosition(rowsDOM, expectedColumnsCount) {
rowsDOM.forEach((row) => {
// Verify each row's cell count
const treeCell = TreeGridFunctions.getTreeCell(row);
const normalCells = TreeGridFunctions.getNormalCells(row);
expect(1 + normalCells.length).toBe(expectedColumnsCount, 'incorrect cell count for a row');
const treeCellRectRight = treeCell.nativeElement.getBoundingClientRect().right;
normalCells.forEach((normalCell) => {
// Verify that the treeCell is the first cell (on the left of all the other cells)
const normalCellRectLeft = normalCell.nativeElement.getBoundingClientRect().left;
expect(treeCellRectRight <= normalCellRectLeft).toBe(true, 'TreeCell is not on the left of a normal cell.');
});
});
}
/**
* Verifies both the RowComponent and the respective DOM Row are with the expected indentation level.
*/
public static verifyRowIndentationLevel(rowComponent, rowDOM, expectedIndentationLevel) {
const treeCell = TreeGridFunctions.getTreeCell(rowDOM);
const divChildren = treeCell.queryAll(By.css('div'));
// If 'expectedIndentationLevel' is 0, we expect the row to be a root level row
// and thus it has no indentation div.
const indentationDiv = treeCell.query(By.css(TREE_CELL_DIV_INDENTATION_CSS_CLASS + expectedIndentationLevel));
if (expectedIndentationLevel === 0) {
expect(divChildren.length).toBe(2, 'root treeCell has incorrect divs count');
expect(indentationDiv).toBeNull();
} else {
expect(divChildren.length).toBe(3, 'child treeCell has incorrect divs count');
expect(indentationDiv).toBeDefined();
expect(indentationDiv).not.toBeNull();
}
// Verify rowComponent's indentation API.
expect(rowComponent.treeRow.level).toBe(expectedIndentationLevel);
// Verify expand/collapse icon's position.
TreeGridFunctions.verifyTreeRowIconPosition(rowDOM, expectedIndentationLevel);
}
/**
* Verifies both the RowComponent and the respective DOM Row are with the expected indentation level.
* The rowIndex is the index of the row in ascending order (if rowIndex is 0, then the top-most row in view will be verified).
*/
public static verifyRowIndentationLevelByIndex(fix, rowIndex, expectedIndentationLevel) {
const treeGrid = fix.debugElement.query(By.css('igx-tree-grid')).componentInstance as IgxTreeGridComponent;
const rowComponent = treeGrid.getRowByIndex(rowIndex);
const rowDOM = TreeGridFunctions.sortElementsVertically(TreeGridFunctions.getAllRows(fix))[rowIndex];
TreeGridFunctions.verifyRowIndentationLevel(rowComponent, rowDOM, expectedIndentationLevel);
}
/**
* Verifies that the specified column is the tree column, that contains the tree cells.
*/
public static verifyTreeColumn(fix, expectedTreeColumnKey, expectedColumnsCount) {
const headerCell = TreeGridFunctions.getHeaderCell(fix, expectedTreeColumnKey).parent;
const treeCells = TreeGridFunctions.getTreeCells(fix);
const rows = TreeGridFunctions.getAllRows(fix);
// Verify the tree cells are first (on the left) in comparison to the rest of the cells.
TreeGridFunctions.verifyCellsPosition(rows, expectedColumnsCount);
// Verify the tree cells are exactly under the respective header cell.
const headerCellRect = headerCell.nativeElement.getBoundingClientRect();
treeCells.forEach(treeCell => {
const treeCellRect = treeCell.nativeElement.getBoundingClientRect();
expect(headerCellRect.bottom <= treeCellRect.top).toBe(true, 'headerCell is not on top of a treeCell');
expect(headerCellRect.left).toBe(treeCellRect.left, 'headerCell and treeCell are not left-aligned');
expect(headerCellRect.right).toBe(treeCellRect.right, 'headerCell and treeCell are not right-aligned');
});
}
/**
* Verifies that the specified column is the tree column, that contains the tree cells, when there are multi column headers.
*/
public static verifyTreeColumnInMultiColHeaders(fix, expectedTreeColumnKey, expectedColumnsCount) {
const headersDOM = TreeGridFunctions.sortElementsHorizontally(fix.debugElement.queryAll(By.css('igx-grid-header')));
const leftMostHeaders = headersDOM.filter(x =>
x.nativeElement.getBoundingClientRect().left === headersDOM[0].nativeElement.getBoundingClientRect().left);
const headerCell = TreeGridFunctions.getElementWithMinHeight(leftMostHeaders);
const treeCells = TreeGridFunctions.getTreeCells(fix);
const rows = TreeGridFunctions.getAllRows(fix);
// Verify the tree cells are first (on the left) in comparison to the rest of the cells.
TreeGridFunctions.verifyCellsPosition(rows, expectedColumnsCount);
// Verify the tree cells are exactly under the respective header cell.
const headerCellRect = headerCell.nativeElement.getBoundingClientRect();
treeCells.forEach(treeCell => {
const treeCellRect = treeCell.nativeElement.getBoundingClientRect();
expect(headerCellRect.bottom <= treeCellRect.top).toBe(true, 'headerCell is not above a treeCell');
expect(headerCellRect.left).toBe(treeCellRect.left, 'headerCell and treeCell are not left-aligned');
expect(headerCellRect.right).toBe(treeCellRect.right, 'headerCell and treeCell are not right-aligned');
});
}
public static getElementWithMinHeight(arr) {
return arr.reduce((a, b) =>
(a.nativeElement.getBoundingClientRect().height < b.nativeElement.getBoundingClientRect().height) ? a : b);
}
public static sortElementsVertically(arr) {
return arr.sort((a, b) =>
a.nativeElement.getBoundingClientRect().top - b.nativeElement.getBoundingClientRect().top);
}
public static sortElementsHorizontally(arr) {
return arr.sort((a, b) =>
a.nativeElement.getBoundingClientRect().left - b.nativeElement.getBoundingClientRect().left);
}
public static verifyTreeRowHasCollapsedIcon(treeRowDOM) {
const indicatorDiv = TreeGridFunctions.getExpansionIndicatorDiv(treeRowDOM);
const igxIcon = indicatorDiv.query(By.css('igx-icon'));
expect(igxIcon.nativeElement.textContent).toEqual('chevron_right');
}
public static verifyTreeRowHasExpandedIcon(treeRowDOM) {
const indicatorDiv = TreeGridFunctions.getExpansionIndicatorDiv(treeRowDOM);
const igxIcon = indicatorDiv.query(By.css('igx-icon'));
expect(igxIcon.nativeElement.textContent).toEqual('expand_more');
}
public static verifyTreeRowExpandIndicatorVisibility(treeRowDOM, visibility = 'visible') {
const indicatorDiv = TreeGridFunctions.getExpansionIndicatorDiv(treeRowDOM);
expect(indicatorDiv.nativeElement.style.visibility).toBe(visibility);
}
public static verifyTreeRowIconPosition(treeRowDOM, indentationLevel) {
const treeCell = TreeGridFunctions.getTreeCell(treeRowDOM);
const treeCellPaddingLeft = parseInt(window.getComputedStyle(treeCell.nativeElement).paddingLeft, 10);
const treeCellRect = treeCell.nativeElement.getBoundingClientRect();
let indentation = 0;
if (indentationLevel !== 0) {
const indentationDiv = treeCell.query(By.css(TREE_CELL_DIV_INDENTATION_CSS_CLASS + indentationLevel));
const indentationDivRect = indentationDiv.nativeElement.getBoundingClientRect();
indentation = indentationDivRect.width;
}
const iconDiv = TreeGridFunctions.getExpansionIndicatorDiv(treeRowDOM);
const iconDivRect = iconDiv.nativeElement.getBoundingClientRect();
expect((iconDivRect.left - (treeCellRect.left + treeCellPaddingLeft + indentation)) < 2)
.toBe(true, 'TreeRow icon has incorrect position');
}
/**
* Returns true if a tree-grid row is 'grayed out' because of filtering
*/
public static checkRowIsGrayedOut(row: IgxRowDirective): boolean {
return row.nativeElement.classList.contains('igx-grid__tr--filtered');
}
/**
* Returns true if a tree-grid row is NOT 'grayed out' because of filtering
*/
public static checkRowIsNotGrayedOut(row: IgxRowDirective): boolean {
return !row.nativeElement.classList.contains('igx-grid__tr--filtered');
}
/**
* Verifies the selection of both the RowComponent and the respective DOM Row.
*/
public static verifyTreeRowSelection(treeGridComponent, rowComponent, rowDOM, expectedSelection: boolean) {
// Verfiy selection of checkbox
const checkboxDiv = rowDOM.query(By.css(TREE_ROW_DIV_SELECTION_CHECKBOX_CSS_CLASS));
const checkboxComponent = checkboxDiv.query(By.css('igx-checkbox')).componentInstance as IgxCheckboxComponent;
expect(checkboxComponent.checked).toBe(expectedSelection, 'Incorrect checkbox selection state');
expect(checkboxComponent.nativeCheckbox.nativeElement.checked).toBe(expectedSelection, 'Incorrect native checkbox selection state');
// Verify selection of row
expect(rowComponent.selected).toBe(expectedSelection, 'Incorrect row selection state');
expect(rowDOM.nativeElement.classList.contains(TREE_ROW_SELECTION_CSS_CLASS)).toBe(expectedSelection);
// Verify selection of row through treeGrid
const selectedRows = (treeGridComponent as IgxTreeGridComponent).selectedRows;
expect(selectedRows.includes(rowComponent.key)).toBe(expectedSelection);
}
/**
* Verifies the selection of both the RowComponent and the respective DOM Row.
* The rowIndex is the index of the row in ascending order (if rowIndex is 0, then the top-most row in view will be verified).
*/
public static verifyTreeRowSelectionByIndex(fix, rowIndex, expectedSelection: boolean) {
const treeGrid = fix.debugElement.query(By.css('igx-tree-grid')).componentInstance as IgxTreeGridComponent;
const rowComponent = treeGrid.getRowByIndex(rowIndex);
const rowDOM = TreeGridFunctions.sortElementsVertically(TreeGridFunctions.getAllRows(fix))[rowIndex];
TreeGridFunctions.verifyTreeRowSelection(treeGrid, rowComponent, rowDOM, expectedSelection);
}
/**
* Verifies the selection of the treeGrid rows.
* Every index of the provided array is the index of the respective row in ascending order
* (if rowIndex is 0, then the top-most row in view will be verified).
*/
public static verifyDataRowsSelection(fix, expectedSelectedRowIndices: any[], expectedSelection: boolean) {
if (expectedSelection) {
const treeGrid = fix.debugElement.query(By.css('igx-tree-grid')).componentInstance as IgxTreeGridComponent;
expect(treeGrid.selectedRows.length).toBe(expectedSelectedRowIndices.length, 'Incorrect number of rows that are selected.');
}
expectedSelectedRowIndices.forEach(rowIndex => {
TreeGridFunctions.verifyTreeRowSelectionByIndex(fix, rowIndex, expectedSelection);
});
}
/**
* Verifies the selection and checkbox state of the treeGrid row.
*/
public static verifyRowByIndexSelectionAndCheckboxState(fix, rowIndex: any, expectedSelection: boolean,
expectedCheckboxState: boolean | null) {
const treeGrid = fix.debugElement.query(By.css('igx-tree-grid')).componentInstance as IgxTreeGridComponent;
const rowComponent = treeGrid.getRowByIndex(rowIndex);
const rowDOM = TreeGridFunctions.sortElementsVertically(TreeGridFunctions.getAllRows(fix))[rowIndex];
// Verfiy selection of checkbox
const checkboxDiv = rowDOM.query(By.css(TREE_ROW_DIV_SELECTION_CHECKBOX_CSS_CLASS));
const checkboxComponent = checkboxDiv.query(By.css('igx-checkbox')).componentInstance as IgxCheckboxComponent;
if (expectedCheckboxState === null) {
expect(checkboxComponent.indeterminate).toBe(true);
expect(checkboxComponent.checked).toBe(false, 'Incorrect checkbox selection state');
expect(checkboxComponent.nativeCheckbox.nativeElement.checked).toBe(false, 'Incorrect native checkbox selection state');
// Verify selection of row
expect(rowComponent.selected).toBe(false, 'Incorrect row selection state');
expect((rowDOM.nativeElement as HTMLElement).classList.contains(TREE_ROW_SELECTION_CSS_CLASS)).toBe(false);
// Verify selection of row through treeGrid
const selectedRows = (treeGrid as IgxTreeGridComponent).selectedRows;
expect(selectedRows.includes(rowComponent.key)).toBe(false);
} else {
expect(checkboxComponent.checked).toBe(expectedCheckboxState, 'Incorrect checkbox selection state');
expect(checkboxComponent.nativeCheckbox.nativeElement.checked).toBe(
expectedCheckboxState, 'Incorrect native checkbox selection state');
// Verify selection of row
expect(rowComponent.selected).toBe(expectedSelection, 'Incorrect row selection state');
expect((rowDOM.nativeElement as HTMLElement).classList.contains(TREE_ROW_SELECTION_CSS_CLASS)).toBe(expectedSelection);
// Verify selection of row through treeGrid
const selectedRows = (treeGrid as IgxTreeGridComponent).selectedRows;
expect(selectedRows.includes(rowComponent.key)).toBe(expectedSelection);
}
}
/**
* Verifies the selection of the header checkbox.
* The expected value can be true, false or null (indeterminate).
*/
public static verifyHeaderCheckboxSelection(fix, expectedSelection: boolean | null) {
const headerRow = TreeGridFunctions.getHeaderRow(fix);
const checkboxDiv = headerRow.query(By.css(TREE_ROW_DIV_SELECTION_CHECKBOX_CSS_CLASS));
const checkboxComponent = checkboxDiv.query(By.css('igx-checkbox')).componentInstance as IgxCheckboxComponent;
if (expectedSelection === null) {
expect(checkboxComponent.indeterminate).toBe(true);
expect(checkboxComponent.checked).toBe(false, 'Incorrect checkbox selection state');
expect(checkboxComponent.nativeCheckbox.nativeElement.checked).toBe(false, 'Incorrect native checkbox selection state');
} else {
expect(checkboxComponent.indeterminate).toBe(false);
expect(checkboxComponent.checked).toBe(expectedSelection, 'Incorrect checkbox selection state');
expect(checkboxComponent.nativeCheckbox.nativeElement.checked).toBe(expectedSelection,
'Incorrect native checkbox selection state');
}
}
public static verifyGridCellHasSelectedClass(cellDOM) {
return cellDOM.nativeElement.classList.contains(TREE_CELL_SELECTION_CSS_CLASS);
}
public static verifyTreeGridCellSelected(treeGrid: IgxTreeGridComponent,
cell: IgxGridCellComponent | CellType, selected = true) {
expect(cell).toBeDefined();
if (cell) {
expect(TreeGridFunctions.verifyGridCellHasSelectedClass(cell)).toBe(selected);
if (selected) {
const selectedCell = treeGrid.selectedCells[0];
expect(selectedCell).toBeDefined();
if (selectedCell) {
expect(selectedCell.value).toEqual(cell.value);
expect(selectedCell.column.field).toEqual(cell.column.field);
expect(selectedCell.row.index).toEqual(cell.row.index);
expect(selectedCell.value).toEqual(cell.value);
}
}
}
}
public static verifyTreeRowIndicator(row, isLoading: boolean, isExpandVisible = true) {
const indicatorDiv = TreeGridFunctions.getExpansionIndicatorDiv(row);
const loadingDiv = TreeGridFunctions.getLoadingIndicatorDiv(row);
if (isLoading) {
expect(loadingDiv).toBeDefined();
expect(indicatorDiv).toBeNull();
} else {
expect(loadingDiv).toBeNull();
expect(indicatorDiv).toBeDefined();
expect(indicatorDiv.nativeElement.style.visibility).toBe(isExpandVisible ? 'visible' : 'hidden');
}
}
public static moveCellUpDown(fix, treeGrid: IgxTreeGridComponent, rowIndex: number, columnName: string, moveDown = true) {
const cell = treeGrid.gridAPI.get_cell_by_index(rowIndex, columnName);
const newRowIndex = moveDown ? rowIndex + 1 : rowIndex - 1;
const keyboardEventKey = moveDown ? 'ArrowDown' : 'ArrowUp';
const gridContent = GridFunctions.getGridContent(fix);
UIInteractions.triggerEventHandlerKeyDown(keyboardEventKey, gridContent);
fix.detectChanges();
TreeGridFunctions.verifyTreeGridCellSelected(treeGrid, cell, false);
const newCell = treeGrid.gridAPI.get_cell_by_index(newRowIndex, columnName);
TreeGridFunctions.verifyTreeGridCellSelected(treeGrid, newCell);
}
public static moveCellLeftRight(fix, treeGrid: IgxTreeGridComponent, rowIndex: number,
firstColumnName: string, nextColumnName: string, moveRight = true) {
const cell = treeGrid.gridAPI.get_cell_by_index(rowIndex, firstColumnName);
const keyboardEventKey = moveRight ? 'ArrowRight' : 'ArrowLeft';
const gridContent = GridFunctions.getGridContent(fix);
UIInteractions.triggerEventHandlerKeyDown(keyboardEventKey, gridContent);
fix.detectChanges();
TreeGridFunctions.verifyTreeGridCellSelected(treeGrid, cell, false);
const newCell = treeGrid.gridAPI.get_cell_by_index(rowIndex, nextColumnName);
TreeGridFunctions.verifyTreeGridCellSelected(treeGrid, newCell);
}
public static moveGridCellWithTab =
(fix, cell: IgxGridCellComponent | CellType) => new Promise<void>(async resolve => {
UIInteractions.triggerKeyDownEvtUponElem('Tab', cell.nativeElement, true);
await wait(DEBOUNCETIME);
fix.detectChanges();
resolve();
});
}