[ADF-5517] Add unit test to test clicking anywhere on a row in a datatable (#8287)

* [ADF-5517] Add unit test to test clicking anywhere on a row in a datatable

* Fix unit tests

* ADF-5517 Fixed failed jobs

* ADF-5517 Removed some duplications

* ADF-5517 Removed some duplications

* ADF-5517 Removed some duplications

* ADF-5517 Removed some duplications

* ADF-5517 Fixed failed test

* ADF-5517 Small correction

---------

Co-authored-by: Aleksander Sklorz <Aleksander.Sklorz@hyland.com>
This commit is contained in:
Thomas Hunter
2023-05-19 13:00:09 +01:00
committed by GitHub
parent 65dfd446fd
commit e4965ece6b
4 changed files with 306 additions and 421 deletions

View File

@@ -166,6 +166,7 @@
(select)="onEnterKeyPressed(row, $event)" (select)="onEnterKeyPressed(row, $event)"
(keyup)="onRowKeyUp(row, $event)" (keyup)="onRowKeyUp(row, $event)"
(keydown)="onRowEnterKeyDown(row, $event)" (keydown)="onRowEnterKeyDown(row, $event)"
(click)="onRowClick(row, $event)"
[adf-upload]="rowAllowsDrop(row)" [adf-upload]="rowAllowsDrop(row)"
[adf-upload-data]="row" [adf-upload-data]="row"
[ngStyle]="rowStyle" [ngStyle]="rowStyle"
@@ -209,7 +210,7 @@
[attr.data-automation-id]="getAutomationValue(row)" [attr.data-automation-id]="getAutomationValue(row)"
[attr.aria-selected]="row.isSelected ? true : false" [attr.aria-selected]="row.isSelected ? true : false"
[attr.aria-label]="col.title ? (col.title | translate) : null" [attr.aria-label]="col.title ? (col.title | translate) : null"
(click)="onRowClick(row, $event)" (click)="storeActiveRow(row)"
(keydown.enter)="onEnterKeyPressed(row, $any($event))" (keydown.enter)="onEnterKeyPressed(row, $any($event))"
[adf-context-menu]="getContextMenuActions(row, col)" [adf-context-menu]="getContextMenuActions(row, col)"
[adf-context-menu-enabled]="contextMenu" [adf-context-menu-enabled]="contextMenu"

View File

@@ -32,6 +32,8 @@ import { TranslateModule } from '@ngx-translate/core';
import { domSanitizerMock } from '../../../mock/dom-sanitizer-mock'; import { domSanitizerMock } from '../../../mock/dom-sanitizer-mock';
import { matIconRegistryMock } from '../../../mock/mat-icon-registry-mock'; import { matIconRegistryMock } from '../../../mock/mat-icon-registry-mock';
import { CdkDragDrop } from '@angular/cdk/drag-drop'; import { CdkDragDrop } from '@angular/cdk/drag-drop';
import { take } from 'rxjs/operators';
import { By } from '@angular/platform-browser';
@Component({selector: 'adf-custom-column-template-component', template: ` @Component({selector: 'adf-custom-column-template-component', template: `
<ng-template #tmplRef></ng-template> <ng-template #tmplRef></ng-template>
@@ -83,6 +85,67 @@ describe('DataTable', () => {
let dataTable: DataTableComponent; let dataTable: DataTableComponent;
let element: any; let element: any;
const testNotShownHeader = (data: ObjectDataTableAdapter) => {
dataTable.ngOnChanges({
data: new SimpleChange(null, data, false)
});
dataTable.showHeader = ShowHeaderMode.Data;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
dataTable.showHeader = ShowHeaderMode.Always;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
dataTable.showHeader = ShowHeaderMode.Never;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
};
const testIfRowIsSelected = (data: any[], done?: DoneFn) => {
dataTable.selectionMode = 'single';
dataTable.data = new ObjectDataTableAdapter(data, [new ObjectDataColumn({ key: 'name' })]);
const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
if (done) {
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeFalsy();
expect(rows[1].isSelected).toBeTruthy();
done();
});
}
return rows;
};
const testDoubleClickCount = (tickTime = 490, rowClickNumber = 1) => {
let doubleClickCount = 0;
const row = {} as DataRow;
dataTable.data = new ObjectDataTableAdapter([], []);
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowDblClick.subscribe(() => {
doubleClickCount += 1;
});
dataTable.onRowClick(row, new MouseEvent('click'));
setTimeout(() => {
for (let i = 0; i < rowClickNumber; i++) {
dataTable.onRowClick(row, new MouseEvent('click'));
}
}, 240);
tick(tickTime);
expect(doubleClickCount).toBe(1);
};
setupTestBed({ setupTestBed({
imports: [ imports: [
TranslateModule.forRoot(), TranslateModule.forRoot(),
@@ -255,41 +318,13 @@ describe('DataTable', () => {
dataTable.loading = false; dataTable.loading = false;
dataTable.noPermission = true; dataTable.noPermission = true;
dataTable.ngOnChanges({ testNotShownHeader(emptyData);
data: new SimpleChange(null, emptyData, false)
});
dataTable.showHeader = ShowHeaderMode.Data;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
dataTable.showHeader = ShowHeaderMode.Always;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
dataTable.showHeader = ShowHeaderMode.Never;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
}); });
it('should never show the header if loading is true', () => { it('should never show the header if loading is true', () => {
dataTable.loading = true; dataTable.loading = true;
dataTable.ngOnChanges({ testNotShownHeader(emptyData);
data: new SimpleChange(null, emptyData, false)
});
dataTable.showHeader = ShowHeaderMode.Data;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
dataTable.showHeader = ShowHeaderMode.Always;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
dataTable.showHeader = ShowHeaderMode.Never;
fixture.detectChanges();
expect(element.querySelector('.adf-datatable-header')).toBeNull();
}); });
}); });
@@ -476,65 +511,28 @@ describe('DataTable', () => {
}); });
it('should select only one row with [single] selection mode', (done) => { it('should select only one row with [single] selection mode', (done) => {
dataTable.selectionMode = 'single'; const rows = testIfRowIsSelected([
dataTable.data = new ObjectDataTableAdapter(
[
{ name: '1', isSelected: true }, { name: '1', isSelected: true },
{ name: '2' } { name: '2' }
], ], done);
[new ObjectDataColumn({ key: 'name' })]
);
const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeFalsy();
expect(rows[1].isSelected).toBeTruthy();
done();
});
dataTable.onRowClick(rows[1], new MouseEvent('click')); dataTable.onRowClick(rows[1], new MouseEvent('click'));
}); });
it('should select only one row with [single] selection mode and key modifier', (done) => { it('should select only one row with [single] selection mode and key modifier', (done) => {
dataTable.selectionMode = 'single'; const rows = testIfRowIsSelected([
dataTable.data = new ObjectDataTableAdapter(
[
{ name: '1', isSelected: true }, { name: '1', isSelected: true },
{ name: '2' } { name: '2' }
], ], done);
[new ObjectDataColumn({ key: 'name' })]
);
const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowClick.subscribe(() => {
expect(rows[0].isSelected).toBeFalsy();
expect(rows[1].isSelected).toBeTruthy();
done();
});
dataTable.onRowClick(rows[1], new MouseEvent('click', { dataTable.onRowClick(rows[1], new MouseEvent('click', {
metaKey: true metaKey: true
})); }));
}); });
it('should select only one row with [single] selection mode pressing enter key', () => { it('should select only one row with [single] selection mode pressing enter key', () => {
dataTable.selectionMode = 'single'; const rows = testIfRowIsSelected([
dataTable.data = new ObjectDataTableAdapter(
[
{ name: '1' }, { name: '1' },
{ name: '2' } { name: '2' }
], ]);
[new ObjectDataColumn({ key: 'name' })]
);
const rows = dataTable.data.getRows();
dataTable.ngOnChanges({});
dataTable.onEnterKeyPressed(rows[0], null); dataTable.onEnterKeyPressed(rows[0], null);
expect(rows[0].isSelected).toBeTruthy(); expect(rows[0].isSelected).toBeTruthy();
expect(rows[1].isSelected).toBeFalsy(); expect(rows[1].isSelected).toBeFalsy();
@@ -585,7 +583,7 @@ describe('DataTable', () => {
expect(rows[1].isSelected).toBeFalsy(); expect(rows[1].isSelected).toBeFalsy();
done(); done();
}); });
dataTable.onRowClick(rows[0], null); dataTable.onRowClick(rows[0], new MouseEvent('click'));
}); });
it('should unselect the row with [multiple] selection mode and modifier key', (done) => { it('should unselect the row with [multiple] selection mode and modifier key', (done) => {
@@ -606,7 +604,8 @@ describe('DataTable', () => {
dataTable.onRowClick(rows[0], { dataTable.onRowClick(rows[0], {
metaKey: true, metaKey: true,
preventDefault: () => {} preventDefault: () => {},
composedPath: () => []
} as any); } as any);
}); });
@@ -751,55 +750,15 @@ describe('DataTable', () => {
dataTable.ngOnChanges({}); dataTable.ngOnChanges({});
fixture.detectChanges(); fixture.detectChanges();
dataTable.onRowClick(row, null); dataTable.onRowClick(row, new MouseEvent('click'));
}); });
it('should emit double click if there are two single click in 250ms', fakeAsync(() => { it('should emit double click if there are two single click in 250ms', fakeAsync(() => {
let doubleClickCount = 0; testDoubleClickCount();
const row = {} as DataRow;
dataTable.data = new ObjectDataTableAdapter([], []);
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowDblClick.subscribe(() => {
doubleClickCount += 1;
});
dataTable.onRowClick(row, null);
setTimeout(() => {
dataTable.onRowClick(row, null);
}
, 240);
tick(490);
expect(doubleClickCount).toBe(1);
})); }));
it('should emit double click if there are more than two single click in 250ms', fakeAsync(() => { it('should emit double click if there are more than two single click in 250ms', fakeAsync(() => {
let doubleClickCount = 0; testDoubleClickCount(740, 2);
const row = {} as DataRow;
dataTable.data = new ObjectDataTableAdapter([], []);
dataTable.ngOnChanges({});
fixture.detectChanges();
dataTable.rowDblClick.subscribe(() => {
doubleClickCount += 1;
});
dataTable.onRowClick(row, null);
setTimeout(() => {
dataTable.onRowClick(row, null);
dataTable.onRowClick(row, null);
}
, 240);
tick(740);
expect(doubleClickCount).toBe(1);
})); }));
it('should emit single click if there are two single click in more than 250ms', fakeAsync(() => { it('should emit single click if there are two single click in more than 250ms', fakeAsync(() => {
@@ -814,10 +773,10 @@ describe('DataTable', () => {
clickCount += 1; clickCount += 1;
}); });
dataTable.onRowClick(row, null); dataTable.onRowClick(row, new MouseEvent('click'));
setTimeout(() => { setTimeout(() => {
dataTable.onRowClick(row, null); dataTable.onRowClick(row, new MouseEvent('click'));
} }
, 260); , 260);
@@ -837,7 +796,7 @@ describe('DataTable', () => {
dataTable.ngOnChanges({}); dataTable.ngOnChanges({});
fixture.detectChanges(); fixture.detectChanges();
dataTable.onRowClick(row, null); dataTable.onRowClick(row, new MouseEvent('click'));
}); });
it('should emit row-dblclick dom event', (done) => { it('should emit row-dblclick dom event', (done) => {
@@ -850,12 +809,13 @@ describe('DataTable', () => {
}); });
dataTable.ngOnChanges({}); dataTable.ngOnChanges({});
fixture.detectChanges(); fixture.detectChanges();
dataTable.onRowClick(row, null); dataTable.onRowClick(row, new MouseEvent('click'));
dataTable.onRowClick(row, null); dataTable.onRowClick(row, new MouseEvent('click'));
}); });
it('should prevent default behaviour on row click event', () => { it('should prevent default behaviour on row click event', () => {
const e = jasmine.createSpyObj('event', ['preventDefault']); const e = new MouseEvent('click');
spyOn(e, 'preventDefault');
dataTable.ngAfterContentInit(); dataTable.ngAfterContentInit();
dataTable.onRowClick(null, e); dataTable.onRowClick(null, e);
expect(e.preventDefault).toHaveBeenCalled(); expect(e.preventDefault).toHaveBeenCalled();
@@ -1373,6 +1333,45 @@ describe('DataTable', () => {
expect(dataTable.selectedRowId).toEqual('2345'); expect(dataTable.selectedRowId).toEqual('2345');
expect(row.isContextMenuSource).toBeTrue(); expect(row.isContextMenuSource).toBeTrue();
}); });
it('should select the row, regardless of where the user clicks in the row.', async () => {
dataTable.selectionMode = 'single';
const dataRows = [{ id: 0 }, { id: 1 }];
dataTable.data = new ObjectDataTableAdapter(
dataRows,
[new ObjectDataColumn({ key: 'id' })]
);
dataTable.ngOnChanges({
rows: new SimpleChange(null, dataRows, false)
});
fixture.detectChanges();
const rows = dataTable.data.getRows();
expect(rows[0].isSelected).toBeFalsy();
expect(rows[1].isSelected).toBeFalsy();
dataTable.resetSelection();
const rowClickPromise = dataTable.rowClick.pipe(take(1)).toPromise();
const rowElement = fixture.debugElement.query(By.css(`[data-automation-id="datatable-row-0"]`)).nativeElement as HTMLElement;
rowElement.dispatchEvent(new MouseEvent('click'));
fixture.detectChanges();
await rowClickPromise;
const rows2 = dataTable.data.getRows();
expect(rows2[0].isSelected).toBeTruthy();
expect(rows2[1].isSelected).toBeFalsy();
dataTable.resetSelection();
const cellClickPromise = dataTable.rowClick.pipe(take(1)).toPromise();
const cellElement = fixture.debugElement.query(By.css(`[data-automation-id="datatable-row-1"] > div`)).nativeElement as HTMLElement;
cellElement.dispatchEvent(new MouseEvent('click', { bubbles: true }));
fixture.detectChanges();
await cellClickPromise;
const rows3 = dataTable.data.getRows();
expect(rows3[0].isSelected).toBeFalsy();
expect(rows3[1].isSelected).toBeTruthy();
});
}); });
describe('Accesibility', () => { describe('Accesibility', () => {
@@ -1464,19 +1463,28 @@ describe('Accesibility', () => {
}); });
}); });
describe('Focus row', () => {
let event: KeyboardEvent;
let dataRows: {name: string}[];
beforeEach(() => {
event = new KeyboardEvent('keyup', {
code: 'ArrowUp',
key: 'ArrowUp',
keyCode: 38
} as KeyboardEventInit);
dataRows = [{name: 'test1'}, {name: 'test2'}];
dataTable.data = new ObjectDataTableAdapter([],
[new ObjectDataColumn({key: 'name'})]
);
});
it('should focus next row on ArrowDown event', () => { it('should focus next row on ArrowDown event', () => {
const event = new KeyboardEvent('keyup', { event = new KeyboardEvent('keyup', {
code: 'ArrowDown', code: 'ArrowDown',
key: 'ArrowDown', key: 'ArrowDown',
keyCode: 40 keyCode: 40
} as KeyboardEventInit ); } as KeyboardEventInit);
const dataRows =
[ { name: 'test1'}, { name: 'test2' } ];
dataTable.data = new ObjectDataTableAdapter([],
[new ObjectDataColumn({ key: 'name' })]
);
dataTable.ngOnChanges({ dataTable.ngOnChanges({
rows: new SimpleChange(null, dataRows, false) rows: new SimpleChange(null, dataRows, false)
@@ -1488,26 +1496,13 @@ describe('Accesibility', () => {
const rowElement = document.querySelectorAll('.adf-datatable-body .adf-datatable-row')[0]; const rowElement = document.querySelectorAll('.adf-datatable-body .adf-datatable-row')[0];
const rowCellElement = rowElement.querySelector('.adf-datatable-cell'); const rowCellElement = rowElement.querySelector('.adf-datatable-cell');
rowCellElement.dispatchEvent(new MouseEvent('click')); rowCellElement.dispatchEvent(new MouseEvent('click', {bubbles: true}));
fixture.debugElement.nativeElement.dispatchEvent(event); fixture.debugElement.nativeElement.dispatchEvent(event);
expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-1'); expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-1');
}); });
it('should focus previous row on ArrowUp event', () => { it('should focus previous row on ArrowUp event', () => {
const event = new KeyboardEvent('keyup', {
code: 'ArrowUp',
key: 'ArrowUp',
keyCode: 38
} as KeyboardEventInit );
const dataRows =
[ { name: 'test1'}, { name: 'test2' } ];
dataTable.data = new ObjectDataTableAdapter([],
[new ObjectDataColumn({ key: 'name' })]
);
dataTable.ngOnChanges({ dataTable.ngOnChanges({
rows: new SimpleChange(null, dataRows, false) rows: new SimpleChange(null, dataRows, false)
}); });
@@ -1518,26 +1513,13 @@ describe('Accesibility', () => {
const rowElement = document.querySelectorAll('.adf-datatable-body .adf-datatable-row')[1]; const rowElement = document.querySelectorAll('.adf-datatable-body .adf-datatable-row')[1];
const rowCellElement = rowElement.querySelector('.adf-datatable-cell'); const rowCellElement = rowElement.querySelector('.adf-datatable-cell');
rowCellElement.dispatchEvent(new MouseEvent('click')); rowCellElement.dispatchEvent(new MouseEvent('click', {bubbles: true}));
fixture.debugElement.nativeElement.dispatchEvent(event); fixture.debugElement.nativeElement.dispatchEvent(event);
expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-0'); expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-0');
}); });
it('should select header row when `showHeader` is `Always`', () => { it('should select header row when `showHeader` is `Always`', () => {
const event = new KeyboardEvent('keyup', {
code: 'ArrowUp',
key: 'ArrowUp',
keyCode: 38
} as KeyboardEventInit );
const dataRows =
[ { name: 'test1'}, { name: 'test2' } ];
dataTable.data = new ObjectDataTableAdapter([],
[new ObjectDataColumn({ key: 'name' })]
);
dataTable.showHeader = ShowHeaderMode.Always; dataTable.showHeader = ShowHeaderMode.Always;
dataTable.ngOnChanges({ dataTable.ngOnChanges({
@@ -1550,26 +1532,13 @@ describe('Accesibility', () => {
const rowElement = document.querySelector('.adf-datatable-row[data-automation-id="datatable-row-0"]'); const rowElement = document.querySelector('.adf-datatable-row[data-automation-id="datatable-row-0"]');
const rowCellElement = rowElement.querySelector('.adf-datatable-cell'); const rowCellElement = rowElement.querySelector('.adf-datatable-cell');
rowCellElement.dispatchEvent(new MouseEvent('click')); rowCellElement.dispatchEvent(new MouseEvent('click', {bubbles: true}));
fixture.debugElement.nativeElement.dispatchEvent(event); fixture.debugElement.nativeElement.dispatchEvent(event);
expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-header'); expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-header');
}); });
it('should not select header row when `showHeader` is `Never`', () => { it('should not select header row when `showHeader` is `Never`', () => {
const event = new KeyboardEvent('keyup', {
code: 'ArrowUp',
key: 'ArrowUp',
keyCode: 38
} as KeyboardEventInit );
const dataRows =
[ { name: 'test1'}, { name: 'test2' } ];
dataTable.data = new ObjectDataTableAdapter([],
[new ObjectDataColumn({ key: 'name' })]
);
dataTable.showHeader = ShowHeaderMode.Never; dataTable.showHeader = ShowHeaderMode.Never;
dataTable.ngOnChanges({ dataTable.ngOnChanges({
@@ -1587,6 +1556,7 @@ describe('Accesibility', () => {
expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-1'); expect(document.activeElement.getAttribute('data-automation-id')).toBe('datatable-row-1');
}); });
});
it('should remove cell focus when [focus] is set to false', () => { it('should remove cell focus when [focus] is set to false', () => {
dataTable.showHeader = ShowHeaderMode.Never; dataTable.showHeader = ShowHeaderMode.Never;
@@ -1833,6 +1803,21 @@ describe('Column Resizing', () => {
let data: { id: number; name: string }[] = []; let data: { id: number; name: string }[] = [];
let dataTableSchema: DataColumn[] = []; let dataTableSchema: DataColumn[] = [];
const testClassesAfterResizing = (headerColumnsSelector = '.adf-datatable-cell-header', excludedClass = 'adf-datatable__cursor--pointer') => {
dataTable.isResizingEnabled = true;
fixture.detectChanges();
const resizeHandle: HTMLElement = fixture.debugElement.nativeElement.querySelector('.adf-datatable__resize-handle');
resizeHandle.dispatchEvent(new MouseEvent('mousedown'));
fixture.detectChanges();
const headerColumns = fixture.debugElement.nativeElement.querySelectorAll(headerColumnsSelector);
expect(dataTable.isResizing).toBeTrue();
headerColumns.forEach((header: HTMLElement) => expect(header.classList).not.toContain(excludedClass));
};
setupTestBed({ setupTestBed({
imports: [ imports: [
TranslateModule.forRoot(), TranslateModule.forRoot(),
@@ -1890,35 +1875,11 @@ describe('Column Resizing', () => {
}); });
it('should NOT have the cursor pointer class in the header upon resizing starts', () => { it('should NOT have the cursor pointer class in the header upon resizing starts', () => {
dataTable.isResizingEnabled = true; testClassesAfterResizing();
fixture.detectChanges();
const resizeHandle: HTMLElement = fixture.debugElement.nativeElement.querySelector('.adf-datatable__resize-handle');
resizeHandle.dispatchEvent(new MouseEvent('mousedown'));
fixture.detectChanges();
const headerColumns = fixture.debugElement.nativeElement.querySelectorAll('.adf-datatable-cell-header');
expect(dataTable.isResizing).toBeTrue();
headerColumns.forEach((header: HTMLElement) => {
expect(header.classList).not.toContain('adf-datatable__cursor--pointer');
});
}); });
it('should NOT have the [adf-datatable-cell-header-content--hovered] class in the header upon resizing starts', () => { it('should NOT have the [adf-datatable-cell-header-content--hovered] class in the header upon resizing starts', () => {
dataTable.isResizingEnabled = true; testClassesAfterResizing('.adf-datatable-cell-header-content', 'adf-datatable-cell-header-content--hovered');
fixture.detectChanges();
const resizeHandle: HTMLElement = fixture.debugElement.nativeElement.querySelector('.adf-datatable__resize-handle');
resizeHandle.dispatchEvent(new MouseEvent('mousedown'));
fixture.detectChanges();
const headerColumns = fixture.debugElement.nativeElement.querySelectorAll('.adf-datatable-cell-header-content');
expect(dataTable.isResizing).toBeTrue();
headerColumns.forEach((header: HTMLElement) => {
expect(header.classList).not.toContain('adf-datatable-cell-header-content--hovered');
});
}); });
it('should NOT display drag icon upon resizing starts', () => { it('should NOT display drag icon upon resizing starts', () => {

View File

@@ -501,18 +501,26 @@ export class DataTableComponent implements OnInit, AfterContentInit, OnChanges,
} }
onRowClick(row: DataRow, mouseEvent: MouseEvent) { onRowClick(row: DataRow, mouseEvent: MouseEvent) {
if (!mouseEvent.composedPath().some(
(element: HTMLElement) => element.id?.startsWith('action_menu_right_') || element.classList?.contains('adf-checkbox-sr-only'))
) {
if (mouseEvent) { if (mouseEvent) {
mouseEvent.preventDefault(); mouseEvent.preventDefault();
} }
if (row) { if (row) {
const rowIndex = this.data.getRows().indexOf(row) + (this.isHeaderListVisible() ? 1 : 0);
this.keyManager.setActiveItem(rowIndex);
const dataRowEvent = new DataRowEvent(row, mouseEvent, this); const dataRowEvent = new DataRowEvent(row, mouseEvent, this);
this.clickObserver.next(dataRowEvent); this.clickObserver.next(dataRowEvent);
} }
} }
}
storeActiveRow(row: DataRow) {
if (row) {
const rowIndex = this.data.getRows().indexOf(row) + (this.isHeaderListVisible() ? 1 : 0);
this.keyManager.setActiveItem(rowIndex);
}
}
onEnterKeyPressed(row: DataRow, e: KeyboardEvent) { onEnterKeyPressed(row: DataRow, e: KeyboardEvent) {
if (row) { if (row) {

View File

@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, SimpleChange, ViewChild, OnInit, Output, EventEmitter } from '@angular/core'; import { Component, SimpleChange, ViewChild, OnInit, Output, EventEmitter, SimpleChanges } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow, DataCellEvent, ObjectDataColumn } from '@alfresco/adf-core'; import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow, DataCellEvent, ObjectDataColumn } from '@alfresco/adf-core';
@@ -39,6 +39,67 @@ describe('TaskListComponent', () => {
let appConfig: AppConfigService; let appConfig: AppConfigService;
let taskListService: TaskListService; let taskListService: TaskListService;
const testMostRecentCall = (changes: SimpleChanges) => {
component.ngAfterContentInit();
component.ngOnChanges(changes);
fixture.detectChanges();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeGlobalTask)
});
};
const testSubscribeForFilteredTaskList = (done: DoneFn) => {
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).not.toBeTruthy();
expect(component.rows.length).toEqual(2);
expect(component.rows[0]['name']).toEqual('nameFake1');
expect(component.rows[0]['processDefinitionId']).toEqual('myprocess:1:4');
done();
});
};
const testRowSelection = async (selectionMode?: string) => {
spyOn(taskListService, 'findTasksByState').and.returnValues(of(fakeGlobalTask));
const state = new SimpleChange(null, 'open', true);
component.multiselect = true;
if (selectionMode) {
component.selectionMode = selectionMode;
}
component.ngOnChanges({ sort: state });
fixture.detectChanges();
await fixture.whenStable();
const selectTask1 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-0"] .mat-checkbox-inner-container');
const selectTask2 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-1"] .mat-checkbox-inner-container');
selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask2.dispatchEvent(new MouseEvent('click', { bubbles: true }));
fixture.detectChanges();
await fixture.whenStable();
let selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
let selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined();
expect(selectRow2).toBeDefined();
expect(component.selectedInstances.length).toBe(2);
selectTask2.dispatchEvent(new MouseEvent('click', { bubbles: true }));
fixture.detectChanges();
await fixture.whenStable();
expect(component.selectedInstances.length).toBe(1);
selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined();
expect(selectRow2).toBeNull();
};
setupTestBed({ setupTestBed({
imports: [ imports: [
TranslateModule.forRoot(), TranslateModule.forRoot(),
@@ -157,15 +218,7 @@ describe('TaskListComponent', () => {
expect(component.rows[0]['processDefinitionCategory']).toEqual('http://www.activiti.org/processdef'); expect(component.rows[0]['processDefinitionCategory']).toEqual('http://www.activiti.org/processdef');
done(); done();
}); });
component.ngAfterContentInit(); testMostRecentCall({ state, processDefinitionKey, assignment });
component.ngOnChanges({ state, processDefinitionKey, assignment });
fixture.detectChanges();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeGlobalTask)
});
}); });
it('should return the filtered task list by processDefinitionKey', (done) => { it('should return the filtered task list by processDefinitionKey', (done) => {
@@ -182,16 +235,7 @@ describe('TaskListComponent', () => {
expect(component.rows[0]['name']).toEqual('nameFake1'); expect(component.rows[0]['name']).toEqual('nameFake1');
done(); done();
}); });
testMostRecentCall({ state, processDefinitionKey, assignment });
component.ngAfterContentInit();
component.ngOnChanges({ state, processDefinitionKey, assignment });
fixture.detectChanges();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeGlobalTask)
});
}); });
it('should return the filtered task list by processInstanceId', (done) => { it('should return the filtered task list by processInstanceId', (done) => {
@@ -208,16 +252,7 @@ describe('TaskListComponent', () => {
expect(component.rows[0]['processInstanceId']).toEqual(2511); expect(component.rows[0]['processInstanceId']).toEqual(2511);
done(); done();
}); });
testMostRecentCall({ state, processInstanceId, assignment });
component.ngAfterContentInit();
component.ngOnChanges({ state, processInstanceId, assignment });
fixture.detectChanges();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeGlobalTask)
});
}); });
it('should return the filtered task list by processDefinitionId', (done) => { it('should return the filtered task list by processDefinitionId', (done) => {
@@ -225,47 +260,15 @@ describe('TaskListComponent', () => {
const processDefinitionId = new SimpleChange(null, 'fakeprocessDefinitionId', true); const processDefinitionId = new SimpleChange(null, 'fakeprocessDefinitionId', true);
const assignment = new SimpleChange(null, 'fake-assignee', true); const assignment = new SimpleChange(null, 'fake-assignee', true);
component.success.subscribe((res) => { testSubscribeForFilteredTaskList(done);
expect(res).toBeDefined(); testMostRecentCall({ state, processDefinitionId, assignment });
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).not.toBeTruthy();
expect(component.rows.length).toEqual(2);
expect(component.rows[0]['name']).toEqual('nameFake1');
expect(component.rows[0]['processDefinitionId']).toEqual('myprocess:1:4');
done();
});
component.ngAfterContentInit();
component.ngOnChanges({ state, processDefinitionId, assignment });
fixture.detectChanges();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeGlobalTask)
});
}); });
it('should return the filtered task list by created date', (done) => { it('should return the filtered task list by created date', (done) => {
const state = new SimpleChange(null, 'open', true); const state = new SimpleChange(null, 'open', true);
const afterDate = new SimpleChange(null, '28-02-2017', true); const afterDate = new SimpleChange(null, '28-02-2017', true);
component.success.subscribe((res) => { testSubscribeForFilteredTaskList(done);
expect(res).toBeDefined(); testMostRecentCall({ state, afterDate });
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).not.toBeTruthy();
expect(component.rows.length).toEqual(2);
expect(component.rows[0]['name']).toEqual('nameFake1');
expect(component.rows[0]['processDefinitionId']).toEqual('myprocess:1:4');
done();
});
component.ngAfterContentInit();
component.ngOnChanges({ state, afterDate });
fixture.detectChanges();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeGlobalTask)
});
}); });
it('should return the filtered task list for all state', (done) => { it('should return the filtered task list for all state', (done) => {
@@ -285,16 +288,7 @@ describe('TaskListComponent', () => {
expect(component.rows[1]['endDate']).toBeUndefined(); expect(component.rows[1]['endDate']).toBeUndefined();
done(); done();
}); });
testMostRecentCall({ state, processInstanceId });
component.ngAfterContentInit();
component.ngOnChanges({ state, processInstanceId });
fixture.detectChanges();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeGlobalTask)
});
}); });
it('should return a currentId null when the taskList is empty', () => { it('should return a currentId null when the taskList is empty', () => {
@@ -608,83 +602,16 @@ describe('TaskListComponent', () => {
}); });
it('should be able to unselect a selected tasks using the checkbox', async () => { it('should be able to unselect a selected tasks using the checkbox', async () => {
spyOn(taskListService, 'findTasksByState').and.returnValues(of(fakeGlobalTask)); await testRowSelection();
const state = new SimpleChange(null, 'open', true);
component.multiselect = true;
component.ngOnChanges({ sort: state });
fixture.detectChanges();
await fixture.whenStable();
const selectTask1 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-0"] .mat-checkbox-inner-container');
const selectTask2 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-1"] .mat-checkbox-inner-container');
selectTask1.click();
selectTask1.click();
selectTask2.click();
fixture.detectChanges();
await fixture.whenStable();
let selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
let selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined();
expect(selectRow2).toBeDefined();
expect(component.selectedInstances.length).toBe(2);
selectTask2.click();
fixture.detectChanges();
await fixture.whenStable();
expect(component.selectedInstances.length).toBe(1);
selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined();
expect(selectRow2).toBeNull();
}); });
it('should not be able to select different row when selection mode is set to NONE and multiselection is enabled', async () => { it('should not be able to select different row when selection mode is set to NONE and multiselection is enabled', async () => {
spyOn(taskListService, 'findTasksByState').and.returnValues(of(fakeGlobalTask)); await testRowSelection('none');
const state = new SimpleChange(null, 'open', true);
component.multiselect = true;
component.selectionMode = 'none';
component.ngOnChanges({ sort: state });
fixture.detectChanges();
await fixture.whenStable();
const selectTask1 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-0"] .mat-checkbox-inner-container');
const selectTask2 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-1"] .mat-checkbox-inner-container');
selectTask1.click();
selectTask1.click();
selectTask2.click();
fixture.detectChanges();
await fixture.whenStable();
let selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
let selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined();
expect(selectRow2).toBeDefined();
expect(component.selectedInstances.length).toBe(2);
selectTask2.click();
fixture.detectChanges();
await fixture.whenStable();
expect(component.selectedInstances.length).toBe(1);
selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined();
expect(selectRow2).toBeNull();
const selectTask2Row = fixture.nativeElement.querySelector('[data-automation-id="text_No name"]'); const selectTask2Row = fixture.nativeElement.querySelector('[data-automation-id="text_No name"]');
selectTask2Row.click(); selectTask2Row.click();
selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]'); const selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]'); const selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
expect(selectRow1).toBeDefined(); expect(selectRow1).toBeDefined();
expect(selectRow2).toBeNull(); expect(selectRow2).toBeNull();
}); });
@@ -700,25 +627,13 @@ describe('TaskListComponent', () => {
const selectTask1 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-0"] .mat-checkbox-inner-container'); const selectTask1 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-0"] .mat-checkbox-inner-container');
const selectTask2 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-1"] .mat-checkbox-inner-container'); const selectTask2 = fixture.nativeElement.querySelector('[data-automation-id="datatable-row-1"] .mat-checkbox-inner-container');
selectTask1.click(); selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask1.click(); selectTask1.dispatchEvent(new MouseEvent('click', { bubbles: true }));
selectTask2.click(); selectTask2.dispatchEvent(new MouseEvent('click', { bubbles: true }));
fixture.detectChanges(); fixture.detectChanges();
await fixture.whenStable(); await fixture.whenStable();
expect(component.selectedInstances.length).toBe(2); expect(component.selectedInstances.length).toBe(2);
// const selectTask2Row = fixture.nativeElement.querySelector('[data-automation-id="text_No name"]');
// selectTask2Row.click();
// fixture.detectChanges();
// await fixture.whenStable();
// expect(component.selectedInstances.length).toBe(1);
// const selectRow1 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-0"]');
// const selectRow2 = fixture.nativeElement.querySelector('[class*="adf-is-selected"][data-automation-id="datatable-row-1"]');
// expect(selectRow1).toBeNull();
// expect(selectRow2).toBeDefined();
}); });
it('should change selected row after clicking on different row', async () => { it('should change selected row after clicking on different row', async () => {