igniteui-angular-sovn
Version:
Ignite UI for Angular is a dependency-free Angular toolkit for building modern web apps
1,076 lines (880 loc) • 80.3 kB
text/typescript
import { TestBed, ComponentFixture, waitForAsync, fakeAsync, tick } from '@angular/core/testing';
import { DebugElement } from '@angular/core';
import { IgxTreeGridComponent } from './tree-grid.component';
import {
IgxTreeGridSimpleComponent, IgxTreeGridPrimaryForeignKeyComponent,
IgxTreeGridStringTreeColumnComponent, IgxTreeGridDateTreeColumnComponent, IgxTreeGridBooleanTreeColumnComponent,
IgxTreeGridRowEditingComponent, IgxTreeGridMultiColHeadersComponent,
IgxTreeGridRowEditingTransactionComponent,
IgxTreeGridRowEditingHierarchicalDSTransactionComponent,
IgxTreeGridRowPinningComponent
} from '../../test-utils/tree-grid-components.spec';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TreeGridFunctions } from '../../test-utils/tree-grid-functions.spec';
import { UIInteractions, wait } from '../../test-utils/ui-interactions.spec';
import { By } from '@angular/platform-browser';
import { configureTestSuite } from '../../test-utils/configure-suite';
import { IgxNumberFilteringOperand, IgxStringFilteringOperand } from '../../data-operations/filtering-condition';
import { IgxHierarchicalTransactionService } from '../../services/transaction/igx-hierarchical-transaction';
import { HierarchicalTransaction, TransactionType } from '../../services/public_api';
import { DropPosition } from '../moving/moving.service';
import { IgxTreeGridRowComponent } from './tree-grid-row.component';
import { IgxGridTransaction } from '../common/types';
import { SortingDirection } from '../../data-operations/sorting-strategy';
import { CellType, IgxTreeGridRow } from '../public_api';
const CSS_CLASS_BANNER = 'igx-banner';
const CSS_CLASS_ROW_EDITED = 'igx-grid__tr--edited';
const GRID_RESIZE_CLASS = '.igx-grid-th__resize-handle';
describe('IgxTreeGrid - Integration #tGrid', () => {
configureTestSuite();
let fix: ComponentFixture<any>;
let treeGrid: IgxTreeGridComponent;
beforeAll(waitForAsync(() => {
TestBed.configureTestingModule({
imports: [
NoopAnimationsModule,
IgxTreeGridSimpleComponent,
IgxTreeGridPrimaryForeignKeyComponent,
IgxTreeGridStringTreeColumnComponent,
IgxTreeGridDateTreeColumnComponent,
IgxTreeGridBooleanTreeColumnComponent,
IgxTreeGridRowEditingComponent,
IgxTreeGridRowPinningComponent,
IgxTreeGridMultiColHeadersComponent,
IgxTreeGridRowEditingTransactionComponent,
IgxTreeGridRowEditingHierarchicalDSTransactionComponent
],
providers: [
{ provide: IgxGridTransaction, useClass: IgxHierarchicalTransactionService }
]
}).compileComponents();
}));
it('should have tree-column with a \'string\' dataType', () => {
// Init test
fix = TestBed.createComponent(IgxTreeGridStringTreeColumnComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
TreeGridFunctions.verifyTreeColumn(fix, 'Name', 4);
});
it('should have tree-column with a \'date\' dataType', () => {
// Init test
fix = TestBed.createComponent(IgxTreeGridDateTreeColumnComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
TreeGridFunctions.verifyTreeColumn(fix, 'HireDate', 4);
});
it('should have tree-column with a \'boolean\' dataType', () => {
// Init test
fix = TestBed.createComponent(IgxTreeGridBooleanTreeColumnComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
TreeGridFunctions.verifyTreeColumn(fix, 'PTO', 5);
});
describe('Child Collection', () => {
// configureTestSuite();
beforeEach(() => {
fix = TestBed.createComponent(IgxTreeGridSimpleComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
});
it('should transform a non-tree column into a tree column when pinning it', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
treeGrid.pinColumn('Age');
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'Age', 4);
treeGrid.unpinColumn('Age');
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
});
it('should transform a non-tree column into a tree column when hiding the original tree-column', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
const column = treeGrid.columnList.filter(c => c.field === 'ID')[0];
column.hidden = true;
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'Name', 3);
column.hidden = false;
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
});
it('should transform the first visible column into tree column when pin and hide another column before that', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
treeGrid.pinColumn('Age');
fix.detectChanges();
const column = treeGrid.columnList.filter(c => c.field === 'Age')[0];
column.hidden = true;
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 3);
});
it('(API) should transform a non-tree column into a tree column when moving the original tree-column through', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
// Move tree-column
const sourceColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0];
const targetColumn = treeGrid.columnList.filter(c => c.field === 'HireDate')[0];
treeGrid.moveColumn(sourceColumn, targetColumn);
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'Name', 4);
});
it('(UI) should transform a non-tree column into a tree column when moving the original tree-column through', async () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
treeGrid.moving = true;
const header = TreeGridFunctions.getHeaderCell(fix, 'ID').nativeElement;
UIInteractions.simulatePointerEvent('pointerdown', header, 50, 50);
UIInteractions.simulatePointerEvent('pointermove', header, 56, 56);
await wait();
UIInteractions.simulatePointerEvent('pointermove', header, 490, 30);
UIInteractions.simulatePointerEvent('pointerup', header, 490, 30);
await wait();
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'Name', 4);
});
it('(API) should autosize tree-column', () => {
const headerCell = TreeGridFunctions.getHeaderCell(fix, 'ID');
const column = treeGrid.columnList.filter(c => c.field === 'ID')[0];
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(225, 'incorrect column width');
expect(parseInt(column.width, 10)).toBe(225);
// API autosizing
column.autosize();
fix.detectChanges();
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(148, 'incorrect headerCell width');
expect(parseInt(column.width, 10)).toBe(148);
});
it('(UI) should autosize tree-column', () => {
const headerCell = TreeGridFunctions.getHeaderCell(fix, 'ID').parent;
const column = treeGrid.columnList.filter(c => c.field === 'ID')[0];
column.resizable = true;
treeGrid.cdr.detectChanges();
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(225, 'incorrect column width');
expect(parseInt(column.width, 10)).toBe(225);
// UI autosizing
const resizer = headerCell.query(By.css(GRID_RESIZE_CLASS)).nativeElement;
UIInteractions.simulateMouseEvent('dblclick', resizer, 225, 5);
fix.detectChanges();
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(148, 'incorrect headerCell width');
expect(parseInt(column.width, 10)).toBe(148);
});
});
describe('Primary/Foreign key', () => {
beforeEach(() => {
fix = TestBed.createComponent(IgxTreeGridPrimaryForeignKeyComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
});
it('should transform a non-tree column into a tree column when pinning it', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
treeGrid.pinColumn('Name');
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'Name', 5);
treeGrid.unpinColumn('Name');
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
});
it('should transform a non-tree column into a tree column when hiding the original tree-column', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
const column = treeGrid.columnList.filter(c => c.field === 'ID')[0];
column.hidden = true;
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ParentID', 4);
column.hidden = false;
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
});
it('should transform the first visible column into tree column when pin and hide another column before that', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
treeGrid.pinColumn('Age');
fix.detectChanges();
const column = treeGrid.columnList.filter(c => c.field === 'Age')[0];
column.hidden = true;
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 4);
});
it('(API) should transform a non-tree column into a tree column when moving the original tree-column through', () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
// Move tree-column
const sourceColumn = treeGrid.columnList.filter(c => c.field === 'ID')[0];
const targetColumn = treeGrid.columnList.filter(c => c.field === 'JobTitle')[0];
treeGrid.moveColumn(sourceColumn, targetColumn);
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ParentID', 5);
});
it('(UI) should transform a non-tree column into a tree column when moving the original tree-column through', async () => {
TreeGridFunctions.verifyTreeColumn(fix, 'ID', 5);
treeGrid.moving = true;
const header = TreeGridFunctions.getHeaderCell(fix, 'ID').nativeElement;
UIInteractions.simulatePointerEvent('pointerdown', header, 50, 50);
UIInteractions.simulatePointerEvent('pointermove', header, 56, 56);
await wait();
UIInteractions.simulatePointerEvent('pointermove', header, 490, 30);
UIInteractions.simulatePointerEvent('pointerup', header, 490, 30);
await wait()
fix.detectChanges();
TreeGridFunctions.verifyTreeColumn(fix, 'ParentID', 5);
});
it('(API) should autosize tree-column', () => {
const headerCell = TreeGridFunctions.getHeaderCell(fix, 'ID');
const column = treeGrid.columnList.filter(c => c.field === 'ID')[0];
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(180, 'incorrect column width');
expect(parseInt(column.width, 10)).toBe(180);
// API autosizing
column.autosize();
fix.detectChanges();
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(135, 'incorrect headerCell width');
expect(parseInt(column.width, 10)).toBe(135);
});
it('(UI) should autosize tree-column', () => {
const headerCell = TreeGridFunctions.getHeaderCell(fix, 'ID').parent;
const column = treeGrid.columnList.filter(c => c.field === 'ID')[0];
column.resizable = true;
treeGrid.cdr.detectChanges();
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(180, 'incorrect column width');
expect(parseInt(column.width, 10)).toBe(180);
// UI autosizing
const resizer = headerCell.query(By.css(GRID_RESIZE_CLASS)).nativeElement;
UIInteractions.simulateMouseEvent('dblclick', resizer, 225, 5);
fix.detectChanges();
expect(headerCell.nativeElement.getBoundingClientRect().width).toBe(135, 'incorrect headerCell width');
expect(parseInt(column.width, 10)).toBe(135);
});
});
describe('Row editing', () => {
beforeEach(() => {
fix = TestBed.createComponent(IgxTreeGridRowEditingComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
});
it('should show the banner below the edited parent node', () => {
// Collapsed state
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
const verifyBannerPositioning = (columnIndex: number) => {
const cellElem = grid.gridAPI.get_cell_by_index(columnIndex, 'Name');
const cell = grid.getCellByColumn(columnIndex, 'Name');
cell.editMode = true;
fix.detectChanges();
const editRow = (cellElem as any).intRow.nativeElement;
const banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER)).nativeElement;
const bannerTop = banner.getBoundingClientRect().top;
const editRowBottom = editRow.getBoundingClientRect().bottom;
// The banner appears below the row
expect(bannerTop).toBeGreaterThanOrEqual(editRowBottom);
// No much space between the row and the banner
expect(bannerTop - editRowBottom).toBeLessThan(2);
};
grid.collapseAll();
fix.detectChanges();
verifyBannerPositioning(0);
// Expanded state
grid.expandAll();
fix.detectChanges();
verifyBannerPositioning(3);
});
it('should show the banner below the edited child node', () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.expandAll();
fix.detectChanges();
const cell = grid.gridAPI.get_cell_by_index(1, 'Name');
cell.setEditMode(true);
fix.detectChanges();
// const editRow = cell.row.nativeElement;
const editRow = grid.gridAPI.get_row_by_index(1).nativeElement;
const banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER)).nativeElement;
const bannerTop = banner.getBoundingClientRect().top;
const editRowBottom = editRow.getBoundingClientRect().bottom;
// The banner appears below the row
expect(bannerTop).toBeGreaterThanOrEqual(editRowBottom);
// No much space between the row and the banner
expect(bannerTop - editRowBottom).toBeLessThan(2);
});
it('should show the banner above the last parent node when in edit mode', () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.height = '200px';
fix.detectChanges();
grid.collapseAll();
fix.detectChanges();
const cell = grid.gridAPI.get_cell_by_index(2, 'Name');
cell.setEditMode(true);
fix.detectChanges();
// const editRow = cell.row.nativeElement;
const editRow = grid.gridAPI.get_row_by_index(2).nativeElement;
const banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER)).nativeElement;
const bannerBottom = banner.getBoundingClientRect().bottom;
const editRowTop = editRow.getBoundingClientRect().top;
// The banner appears below the row
expect(bannerBottom).toBeLessThanOrEqual(editRowTop);
// No much space between the row and the banner
expect(editRowTop - bannerBottom).toBeLessThan(2);
});
it('should show the banner above the last child node when in edit mode', () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.expandAll();
fix.detectChanges();
const cell = grid.gridAPI.get_cell_by_index(grid.rowList.length - 1, 'Name');
cell.setEditMode(true);
fix.detectChanges();
// const editRow = cell.row.nativeElement;
const editRow = grid.gridAPI.get_row_by_index(grid.rowList.length - 1).nativeElement;
const banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER)).nativeElement;
const bannerBottom = banner.getBoundingClientRect().bottom;
const editRowTop = editRow.getBoundingClientRect().top;
// The banner appears below the row
expect(bannerBottom).toBeLessThanOrEqual(editRowTop);
// No much space between the row and the banner
expect(editRowTop - bannerBottom).toBeLessThan(2);
});
it('should hide banner when edited parent row is being expanded/collapsed', () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.collapseAll();
fix.detectChanges();
// Edit parent row cell
const cell = grid.getCellByColumn(0, 'Name');
cell.editMode = true;
fix.detectChanges();
let banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
// Expand parent row
grid.expandRow(cell.row.key);
fix.detectChanges();
banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
// K.D. 01 Mar, 2022 #10634 Don't trigger endEdit/commit upon row expansion state change
expect(cell.editMode).toBe(true);
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
// Edit parent row cell
cell.editMode = true;
fix.detectChanges();
banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
// Collapse parent row
grid.collapseRow(cell.row.key);
fix.detectChanges();
banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
// K.D. 01 Mar, 2022 #10634 Don't trigger endEdit/commit upon row expansion state change
expect(cell.editMode).toBe(true);
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
});
it('should hide banner when edited child row is being expanded/collapsed', () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.expandAll();
fix.detectChanges();
// Edit child row child cell
const childCell = grid.getCellByColumn(4, 'Name');
childCell.editMode = true;
fix.detectChanges();
let banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
// Collapse parent child row
let parentRow = grid.getRowByIndex(3);
grid.collapseRow(parentRow.key);
fix.detectChanges();
banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
expect(childCell.editMode).toBe(false);
// K.D. 28 Feb, 2022 #10634 Don't trigger endEdit/commit upon row expansion state change
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
// Edit child row cell
const parentCell = grid.getCellByColumn(3, 'Name');
parentCell.editMode = true;
fix.detectChanges();
banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
// Collapse parent row
parentRow = grid.getRowByIndex(0);
grid.collapseRow(parentRow.key);
fix.detectChanges();
banner = fix.debugElement.query(By.css('.' + CSS_CLASS_BANNER));
expect(parentCell.editMode).toBe(false);
// K.D. 01 Mar, 2022 #10634 Don't trigger endEdit/commit upon row expansion state change
expect(banner.parent.attributes['aria-hidden']).toEqual('false');
});
it('TAB navigation should not leave the edited row and the banner.', async () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
const dateCell = grid.getCellByColumn(2, 'HireDate');
const nameCell = grid.getCellByColumn(2, 'Name');
const idCell = grid.getCellByColumn(2, 'ID');
const ageCell = grid.getCellByColumn(2, 'Age');
UIInteractions.simulateDoubleClickAndSelectEvent(grid.gridAPI.get_cell_by_index(2, 'HireDate'));
await wait(30);
fix.detectChanges();
await TreeGridFunctions.moveGridCellWithTab(fix, grid.gridAPI.get_cell_by_index(2, 'HireDate'));
expect(dateCell.editMode).toBeFalsy();
expect(nameCell.editMode).toBeTruthy();
await TreeGridFunctions.moveGridCellWithTab(fix, grid.gridAPI.get_cell_by_index(2, 'Name'));
expect(nameCell.editMode).toBeFalsy();
expect(idCell.editMode).toBeFalsy();
expect(ageCell.editMode).toBeTruthy();
const cancelBtn = fix.debugElement.queryAll(By.css('.igx-button--flat'))[0] as DebugElement;
const doneBtn = fix.debugElement.queryAll(By.css('.igx-button--flat'))[1];
spyOn(cancelBtn.nativeElement, 'focus').and.callThrough();
spyOn<any>(grid.rowEditTabs.first, 'move').and.callThrough();
spyOn<any>(grid.rowEditTabs.last, 'move').and.callThrough();
await TreeGridFunctions.moveGridCellWithTab(fix, grid.gridAPI.get_cell_by_index(2, 'Age'));
expect(cancelBtn.nativeElement.focus).toHaveBeenCalled();
const mockObj = jasmine.createSpyObj('mockObj', ['stopPropagation', 'preventDefault']);
cancelBtn.triggerEventHandler('keydown.Tab', mockObj);
await wait(30);
fix.detectChanges();
expect((grid.rowEditTabs.first as any).move).not.toHaveBeenCalled();
expect(mockObj.preventDefault).not.toHaveBeenCalled();
expect(mockObj.stopPropagation).toHaveBeenCalled();
doneBtn.triggerEventHandler('keydown.Tab', mockObj);
await wait(30);
fix.detectChanges();
expect(dateCell.editMode).toBeTruthy();
expect((grid.rowEditTabs.last as any).move).toHaveBeenCalled();
expect(mockObj.preventDefault).toHaveBeenCalled();
expect(mockObj.stopPropagation).toHaveBeenCalled();
});
it('should preserve updates after removing Filtering', () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.filter('Age', 40, IgxNumberFilteringOperand.instance().condition('greaterThan'));
fix.detectChanges();
const childCell = grid.getCellByColumn(2, 'Age');
const childRowID = childCell.row.key;
const parentCell = grid.getCellByColumn(0, 'Age');
const parentRowID = parentCell.row.key;
childCell.update(18);
parentCell.update(33);
fix.detectChanges();
grid.clearFilter();
fix.detectChanges();
const childRow = grid.rowList.filter(r => r.key === childRowID)[0] as IgxTreeGridRowComponent;
const editedChildCell = childRow.cells.filter(c => c.column.field === 'Age')[0];
expect(editedChildCell.value).toEqual(18);
const parentRow = grid.rowList.filter(r => r.key === parentRowID)[0] as IgxTreeGridRowComponent;
const editedParentCell = parentRow.cells.filter(c => c.column.field === 'Age')[0];
expect(editedParentCell.value).toEqual(33);
});
it('should preserve updates after removing Sorting', () => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.sort({ fieldName: 'Age', dir: SortingDirection.Desc, ignoreCase: false });
fix.detectChanges();
const childCell = grid.gridAPI.get_cell_by_index(0, 'Age');
const childRowID = childCell.row.key;
childCell.update(14);
const parentCell = grid.gridAPI.get_cell_by_index(1, 'Age');
const parentRowID = parentCell.row.key;
parentCell.update(80);
fix.detectChanges();
grid.clearSort();
fix.detectChanges();
const childRow = grid.rowList.filter(r => r.key === childRowID)[0] as IgxTreeGridRowComponent;
const editedChildCell = childRow.cells.filter(c => c.column.field === 'Age')[0];
expect(editedChildCell.value).toEqual(14);
const parentRow = grid.rowList.filter(r => r.key === parentRowID)[0] as IgxTreeGridRowComponent;
const editedParentCell = parentRow.cells.filter(c => c.column.field === 'Age')[0];
expect(editedParentCell.value).toEqual(80);
});
it('should select the text when the first cell (tree grid cell) enters edit mode', fakeAsync(() => {
const grid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
grid.expandAll();
fix.detectChanges();
// move the 'string' column 'Name' to first position, so its cells are the tree grid cells
const colName = grid.getColumnByName('Name');
const colHireDate = grid.getColumnByName('HireDate');
grid.moveColumn(colName, colHireDate, DropPosition.BeforeDropTarget);
fix.detectChanges();
tick(100);
const cell = grid.gridAPI.get_cell_by_index(0, 'Name');
cell.setEditMode(true);
fix.detectChanges();
tick(100);
expect(cell.editMode).toBe(true);
expect(document.activeElement.nodeName).toEqual('INPUT')
expect((document.activeElement as HTMLInputElement).value).toBe('John Winchester');
expect((document.activeElement as HTMLInputElement).selectionStart).toEqual(0);
expect((document.activeElement as HTMLInputElement).selectionEnd).toEqual(15);
}));
});
describe('Batch Editing', () => {
it('Children are transformed into parent nodes after their parent is deleted', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
const row: HTMLElement = treeGrid.gridAPI.get_row_by_index(0).nativeElement;
treeGrid.cascadeOnDelete = false;
const trans = treeGrid.transactions;
treeGrid.deleteRowById(1);
fix.detectChanges();
expect(row.classList).toContain('igx-grid__tr--deleted');
expect(treeGrid.getRowByKey(1).index).toBe(0);
expect(treeGrid.getRowByKey(2).index).toBe(1);
expect(treeGrid.getRowByKey(3).index).toBe(2);
trans.commit(treeGrid.data);
fix.detectChanges();
expect(row.classList).not.toContain('igx-grid__tr--deleted');
expect(treeGrid.getRowByKey(2).index).toBe(0);
expect(treeGrid.getRowByKey(3).index).toBe(1);
expect(trans.canUndo).toBe(false);
expect(treeGrid.getRowByIndex(-1)).toBeUndefined();
expect(treeGrid.getRowByKey(-1)).toBeUndefined();
});
it('Children are deleted along with their parent', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
treeGrid.cascadeOnDelete = true;
const trans = treeGrid.transactions;
treeGrid.deleteRowById(1);
fix.detectChanges();
for (let i = 0; i < 5; i++) {
const curRow: HTMLElement = treeGrid.gridAPI.get_row_by_index(i).nativeElement;
expect(curRow.classList).toContain('igx-grid__tr--deleted');
}
expect(treeGrid.getRowByKey(1).index).toBe(0);
expect(treeGrid.getRowByKey(2).index).toBe(1);
expect(treeGrid.getRowByKey(3).index).toBe(2);
expect(treeGrid.getRowByKey(7).index).toBe(3);
expect(treeGrid.getRowByKey(4).index).toBe(4);
trans.commit(treeGrid.data);
fix.detectChanges();
expect(treeGrid.getRowByKey(1)).toBeUndefined();
expect(treeGrid.getRowByKey(2)).toBeUndefined();
expect(treeGrid.getRowByKey(3)).toBeUndefined();
expect(treeGrid.getRowByKey(7)).toBeUndefined();
expect(treeGrid.getRowByKey(4)).toBeUndefined();
expect(treeGrid.getRowByKey(6).index).toBe(0);
expect(treeGrid.getRowByKey(10).index).toBe(1);
expect(trans.canUndo).toBe(false);
});
it('Editing a cell is possible with Hierarchical DS', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingHierarchicalDSTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
const trans = treeGrid.transactions;
const targetCell = treeGrid.getCellByColumn(3, 'Age');
targetCell.editMode = true;
targetCell.update('333');
fix.detectChanges();
// ged DONE button and click it
const rowEditingBannerElement = fix.debugElement.query(By.css('.igx-banner__row')).nativeElement;
const doneButtonElement = rowEditingBannerElement.lastElementChild;
doneButtonElement.dispatchEvent(new Event('click'));
fix.detectChanges();
// Verify the value is updated and the correct style is applied before committing
expect(targetCell.editMode).toBeFalsy();
expect(targetCell.value).toBe(333);
expect(treeGrid.gridAPI.get_cell_by_index(3, 'Age').nativeElement.classList).toContain('igx-grid__td--edited');
// Commit
trans.commit(treeGrid.data, treeGrid.primaryKey, treeGrid.childDataKey);
// Verify the correct value is set
expect(targetCell.value).toBe(333);
// Add new root lv row
treeGrid.addRow({ ID: 11, ParentID: -1, Name: 'Dan Kolov', JobTitle: 'wrestler', Age: 32, OnPTO: true });
fix.detectChanges();
// Edit a cell value and check it is correctly updated
const newTargetCell = treeGrid.getCellByColumn(10, 'Age');
newTargetCell.editMode = true;
newTargetCell.update('666');
fix.detectChanges();
expect(newTargetCell.value).toBe(666);
expect(treeGrid.gridAPI.get_cell_by_index(10, 'Age').nativeElement.classList).toContain('igx-grid__td--edited');
});
it('Undo/Redo keeps the correct number of steps with Hierarchical DS', () => {
// TODO:
// 1. Update a cell in three different rows
// 2. Execute "Undo" three times
// 3. Verify the initial state is shown
// 4. Execute "Redo" three times
// 5. Verify all the updates are shown with correct styles
// 6. Press "Commit"
// 7. Verify the changes are comitted
fix = TestBed.createComponent(IgxTreeGridRowEditingHierarchicalDSTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
const trans = treeGrid.transactions;
const treeGridData = treeGrid.data;
// Get initial data
const rowData = {
147: Object.assign({}, treeGrid.getRowByKey(147).data),
475: Object.assign({}, treeGrid.getRowByKey(475).data),
19: Object.assign({}, treeGrid.getRowByKey(19).data)
};
const initialData = treeGrid.data.map(e => Object.assign({}, e));
let targetCell: CellType;
// Get 147 row
targetCell = treeGrid.getCellByKey(147, 'Name');
expect(targetCell.value).toEqual('John Winchester');
// Edit 'Name'
targetCell.update('Testy Testington');
fix.detectChanges();
// Get 475 row (1st child of 147)
targetCell = treeGrid.getCellByKey(475, 'Age');
expect(targetCell.value).toEqual(30);
// Edit Age
targetCell.update(42);
fix.detectChanges();
// Get 19 row
targetCell = treeGrid.getCellByKey(19, 'Name');
// Edit Name
expect(targetCell.value).toEqual('Yang Wang');
targetCell.update('Old Richard');
fix.detectChanges();
expect(rowData[147].Name).not.toEqual(treeGrid.getRowByKey(147).data.Name);
expect(rowData[475].Age).not.toEqual(treeGrid.getRowByKey(475).data.Age);
expect(rowData[19].Name).not.toEqual(treeGrid.getRowByKey(19).data.Name);
expect(treeGridData[0].Employees[475]).toEqual(initialData[0].Employees[475]);
expect(trans.canUndo).toBeTruthy();
expect(trans.canRedo).toBeFalsy();
trans.undo();
fix.detectChanges();
trans.undo();
fix.detectChanges();
trans.undo();
fix.detectChanges();
expect(rowData[147].Name).toEqual(treeGrid.getRowByKey(147).data.Name);
expect(rowData[475].Age).toEqual(treeGrid.getRowByKey(475).data.Age);
expect(rowData[19].Name).toEqual(treeGrid.getRowByKey(19).data.Name);
expect(trans.canUndo).toBeFalsy();
expect(trans.canRedo).toBeTruthy();
trans.redo();
fix.detectChanges();
trans.redo();
fix.detectChanges();
trans.redo();
fix.detectChanges();
expect(rowData[147].Name).not.toEqual(treeGrid.getRowByKey(147).data.Name);
expect(rowData[475].Age).not.toEqual(treeGrid.getRowByKey(475).data.Age);
expect(rowData[19].Name).not.toEqual(treeGrid.getRowByKey(19).data.Name);
expect(treeGridData[0].Employees[475]).toEqual(initialData[0].Employees[475]);
trans.commit(treeGridData, treeGrid.primaryKey, treeGrid.childDataKey);
fix.detectChanges();
expect(treeGridData[0].Name).toEqual('Testy Testington');
expect(treeGridData[0].Employees[0].Age).toEqual(42);
expect(treeGridData[1].Name).toEqual('Old Richard');
});
it('Add parent node to a Flat DS tree grid', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
const trans = treeGrid.transactions;
treeGrid.addRow({ ID: 11, ParentID: -1, Name: 'Dan Kolov', JobTitle: 'wrestler', Age: 32 });
fix.detectChanges();
expect(trans.canUndo).toBe(true);
expect(treeGrid.gridAPI.get_row_by_key(11).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
trans.commit(treeGrid.data);
fix.detectChanges();
expect(treeGrid.gridAPI.get_row_by_key(11).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(trans.canUndo).toBe(false);
treeGrid.addRow({ ID: 12, ParentID: -1, Name: 'Kubrat Pulev', JobTitle: 'Boxer', Age: 33 });
fix.detectChanges();
expect(trans.canUndo).toBe(true);
expect(treeGrid.gridAPI.get_row_by_key(12).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
});
it('Add parent node to a Hierarchical DS tree grid', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingHierarchicalDSTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
const initialDataLength = treeGrid.data.length;
const trans = treeGrid.transactions;
spyOn(trans, 'add').and.callThrough();
const addedRowId_1 = treeGrid.rowList.length;
const newRow = {
ID: addedRowId_1,
Name: 'John Dow',
HireDate: new Date(2018, 10, 20),
Age: 22,
OnPTO: false,
Employees: []
};
treeGrid.addRow(newRow);
fix.detectChanges();
expect(trans.getTransactionLog().length).toEqual(1);
expect(trans.add).toHaveBeenCalled();
expect(trans.add).toHaveBeenCalledTimes(1);
const transParams: HierarchicalTransaction = {
id: addedRowId_1,
type: TransactionType.ADD,
newValue: newRow
};
expect(trans.add).toHaveBeenCalledWith(transParams);
expect(treeGrid.records.get(addedRowId_1).level).toBe(0);
expect(treeGrid.gridAPI.get_row_by_key(addedRowId_1).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
trans.commit(treeGrid.data);
fix.detectChanges();
expect(treeGrid.data.length).toEqual(initialDataLength + 1);
expect(treeGrid.data[initialDataLength]).toEqual(newRow);
expect(treeGrid.records.get(addedRowId_1).level).toBe(0);
expect(treeGrid.gridAPI.get_row_by_key(addedRowId_1).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(trans.getTransactionLog().length).toEqual(0);
expect(trans.canUndo).toBeFalsy();
const addedRowId_2 = treeGrid.rowList.length;
const newParentRow = {
ID: addedRowId_2,
Name: 'Brad Pitt',
HireDate: new Date(2016, 8, 14),
Age: 54,
OnPTO: false
};
treeGrid.addRow(newParentRow);
fix.detectChanges();
expect(treeGrid.records.get(addedRowId_2).level).toBe(0);
expect(treeGrid.gridAPI.get_row_by_key(addedRowId_2).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(addedRowId_1).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
});
it('Add a child node to a previously added parent node - Flat DS', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
const rootRow = { ID: 11, ParentID: -1, Name: 'Kubrat Pulev', JobTitle: 'wrestler', Age: 32 };
const childRow = { ID: 12, ParentID: 11, Name: 'Tervel Pulev', JobTitle: 'wrestler', Age: 30 };
const grandChildRow = { ID: 13, ParentID: 12, Name: 'Asparuh Pulev', JobTitle: 'wrestler', Age: 14 };
const trans = treeGrid.transactions;
treeGrid.addRow(rootRow);
fix.detectChanges();
treeGrid.addRow(childRow, 11);
fix.detectChanges();
expect(treeGrid.gridAPI.get_row_by_key(11).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(12).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
trans.commit(treeGrid.data);
fix.detectChanges();
expect(treeGrid.gridAPI.get_row_by_key(11).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(12).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
treeGrid.addRow(grandChildRow, 12);
fix.detectChanges();
expect(treeGrid.gridAPI.get_row_by_key(11).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(12).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(13).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
});
it('Add a child node to a previously added parent node - Hierarchical DS', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid as IgxTreeGridComponent;
const rowData = {
parent: { ID: 13, Name: 'Dr. Evil', JobTitle: 'Doctor of Evilness', Age: 52 },
child: { ID: 133, Name: 'Scott', JobTitle: `Annoying Teen, Dr. Evil's son`, Age: 17 },
grandChild: { ID: 1337, Name: 'Mr. Bigglesworth', JobTitle: 'Evil Cat', Age: 13 }
};
// 1. Add a row at level 0 to the grid
treeGrid.addRow(rowData.parent);
fix.detectChanges();
// 2. Add a child row to that parent
treeGrid.addRow(rowData.child, rowData.parent.ID);
fix.detectChanges();
// 3. Verify the new rows are pending with the correct styles
expect(treeGrid.gridAPI.get_row_by_key(13).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(133).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.data.findIndex(e => e.ID === rowData.parent.ID)).toEqual(-1);
expect(treeGrid.data.findIndex(e => e.ID === rowData.child.ID)).toEqual(-1);
expect(treeGrid.transactions.getAggregatedChanges(true).length).toEqual(2);
// 4. Commit
treeGrid.transactions.commit(treeGrid.data);
fix.detectChanges();
// 5. verify the rows are committed, the styles are OK
expect(treeGrid.data.findIndex(e => e.ID === rowData.parent.ID)).not.toEqual(-1);
expect(treeGrid.data.findIndex(e => e.ID === rowData.child.ID)).not.toEqual(-1);
expect(treeGrid.gridAPI.get_row_by_key(13).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(133).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.transactions.getAggregatedChanges(true).length).toEqual(0);
// 6. Add another child row at level 2 (grand-child of the first row)
treeGrid.addRow(rowData.grandChild, rowData.child.ID);
fix.detectChanges();
// 7. verify the pending styles is applied only to the newly added row
// and not to the previously added rows
expect(treeGrid.gridAPI.get_row_by_key(13).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(133).nativeElement.classList).not.toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.gridAPI.get_row_by_key(1337).nativeElement.classList).toContain(CSS_CLASS_ROW_EDITED);
expect(treeGrid.transactions.getAggregatedChanges(true).length).toEqual(1);
});
it('Delete a pending parent node - Flat DS', () => {
fix = TestBed.createComponent(IgxTreeGridPrimaryForeignKeyComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
treeGrid.batchEditing = true;
fix.detectChanges();
const trans = treeGrid.transactions;
spyOn(trans, 'add').and.callThrough();
treeGrid.foreignKey = 'ParentID';
const addedRowId = treeGrid.data.length;
const newRow = {
ID: addedRowId,
ParentID: 1,
Name: 'John Dow',
JobTitle: 'Copywriter',
Age: 22
};
treeGrid.addRow(newRow);
fix.detectChanges();
const addedRow = treeGrid.rowList.filter(r => r.key === addedRowId)[0] as IgxTreeGridRowComponent;
treeGrid.selectRows([treeGrid.getRowByIndex(addedRow.index).key], true);
fix.detectChanges();
expect(treeGrid.transactions.getTransactionLog().length).toEqual(1);
expect(trans.add).toHaveBeenCalled();
expect(trans.add).toHaveBeenCalledTimes(1);
const transParams: HierarchicalTransaction = {
id: addedRowId,
type: TransactionType.ADD,
newValue: newRow
};
expect(trans.add).toHaveBeenCalledWith(transParams);
treeGrid.deleteRowById(treeGrid.selectedRows[0]);
fix.detectChanges();
expect(treeGrid.rowList.filter(r => r.key === addedRowId).length).toEqual(0);
expect(treeGrid.transactions.getTransactionLog().length).toEqual(2);
expect(treeGrid.transactions.getTransactionLog()[1].id).toEqual(addedRowId);
expect(treeGrid.transactions.getTransactionLog()[1].type).toEqual('delete');
expect(treeGrid.transactions.getTransactionLog()[1].newValue).toBeNull();
treeGrid.transactions.undo();
fix.detectChanges();
expect(treeGrid.rowList.filter(r => r.key === addedRowId).length).toEqual(1);
expect(treeGrid.transactions.getTransactionLog().length).toEqual(1);
expect(trans.add).toHaveBeenCalled();
expect(trans.add).toHaveBeenCalledTimes(2);
expect(trans.add).toHaveBeenCalledWith(transParams);
});
it('Delete a pending parent node - Hierarchical DS', () => {
fix = TestBed.createComponent(IgxTreeGridRowEditingHierarchicalDSTransactionComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
const trans = treeGrid.transactions;
spyOn(trans, 'add').and.callThrough();
const parentRow = treeGrid.getRowByIndex(0);
const addedRowId = treeGrid.rowList.length;
const newRow = {
ID: addedRowId,
Name: 'John Dow',
HireDate: new Date(2018, 10, 20),
Age: 22,
OnPTO: false,
Employees: []
};
treeGrid.addRow(newRow, parentRow.key);
fix.detectChanges();
const addedRow = treeGrid.rowList.filter(r => r.key === addedRowId)[0] as IgxTreeGridRowComponent;
treeGrid.selectRows([treeGrid.getRowByIndex(addedRow.index).key], true);
fix.detectChanges();
expect(treeGrid.transactions.getTransactionLog().length).toEqual(1);
expect(trans.add).toHaveBeenCalled();
expect(trans.add).toHaveBeenCalledTimes(1);
const transParams: HierarchicalTransaction = {
id: addedRowId,
path: [parentRow.key],
newValue: newRow,
type: TransactionType.ADD
};
expect(trans.add).toHaveBeenCalledWith(transParams, null);
treeGrid.deleteRowById(treeGrid.selectedRows[0]);
fix.detectChanges();
expect(treeGrid.rowList.filter(r => r.key === addedRowId).length).toEqual(0);
expect(treeGrid.transactions.getTransactionLog().length).toEqual(2);
expect(treeGrid.transactions.getTransactionLog()[1].id).toEqual(addedRowId);
expect(treeGrid.transactions.getTransactionLog()[1].type).toEqual('delete');
expect(treeGrid.transactions.getTransactionLog()[1].newValue).toBeNull();
treeGrid.transactions.undo();
fix.detectChanges();
expect(treeGrid.rowList.filter(r => r.key === addedRowId).length).toEqual(1);
expect(treeGrid.transactions.getTransactionLog().length).toEqual(1);
expect(trans.add).toHaveBeenCalled();
expect(trans.add).toHaveBeenCalledTimes(2);
expect(trans.add).toHaveBeenCalledWith(transParams, null);
});
it('Delete a pending child node - Flat DS', () => {
fix = TestBed.createComponent(IgxTreeGridPrimaryForeignKeyComponent);
fix.detectChanges();
treeGrid = fix.componentInstance.treeGrid;
treeGrid.batchEditing = true;
fix.detectChanges();
const trans = treeGrid.transactions;
spyOn(trans, 'add').and.callThrough();
treeGrid.foreignKey = 'ParentID';
const addedRowId = treeGrid.data.length;
const newRow = {
ID: addedRowId,
ParentID: 1,
Name: 'John Dow',
JobTitle: 'Copywriter'