[AAE-13800] - Avoid reloading task/process list when column visibility changes (#8499)

* Fixing rebase conflicts

* AAE-13310: Fixing lint issues

* AAE-13800: Avoid reloading tasks list component when column visibility changes, improved unit tests

* AAE-13800: Avoid reloading process list when column visibility changes, improving unit tests

* AAE-13800: Removing extra code from unit tests

* AAE-13800: updated obserable and unit tests in service tasks component

* AAE-13800: Updated service tasks reload method

* AAE-13800: Improved unit test

* AAE-13800: Removed unnecessary unit test

* AAE-13800: Removed extra subscription from a unit test

* AAE-13800: Replacing component reload with ngOnChanges as suggested in PR review

* AAE-13800: Adding done to the unit tests with a subscription

* AAE-13800: Removing extra spy

* AAE-13800: using ngHooks instead of calling directly component methods

* AAE-13800: code clean-up

* AAE-13800: making onDestroy subject private to each component

* AAE-13800: Code improvements

* AAE-13800: Code refactoring

* AAE-13800: Added more unit tests

* AAE-13800: Fixing lint issues
This commit is contained in:
Ehsan Rezaei
2023-05-05 10:45:29 +02:00
committed by GitHub
parent c26915ae3f
commit 23149cae58
7 changed files with 222 additions and 212 deletions

View File

@@ -364,10 +364,8 @@ describe('ProcessListCloudComponent', () => {
it('should re-create columns when a column width gets changed', () => { it('should re-create columns when a column width gets changed', () => {
component.isResizingEnabled = true; component.isResizingEnabled = true;
component.appName = 'FAKE-APP-NAME';
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList)); spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
fixture.detectChanges();
component.reload(); component.reload();
fixture.detectChanges(); fixture.detectChanges();
@@ -421,9 +419,6 @@ describe('ProcessListCloudComponent', () => {
}); });
it('should re-create columns when a column order gets changed', () => { it('should re-create columns when a column order gets changed', () => {
component.appName = 'FAKE-APP-NAME';
fixture.detectChanges();
component.reload(); component.reload();
fixture.detectChanges(); fixture.detectChanges();
@@ -437,6 +432,33 @@ describe('ProcessListCloudComponent', () => {
expect(component.columns[1].title).toBe('ADF_CLOUD_PROCESS_LIST.PROPERTIES.NAME'); expect(component.columns[1].title).toBe('ADF_CLOUD_PROCESS_LIST.PROPERTIES.NAME');
}); });
it('should create datatable schema when a column visibility gets changed', () => {
component.ngAfterContentInit();
spyOn(component, 'createDatatableSchema');
component.onColumnsVisibilityChange(component.columns);
fixture.detectChanges();
expect(component.createDatatableSchema).toHaveBeenCalled();
});
it('should call endpoint when a column visibility gets changed', () => {
spyOn(preferencesService, 'updatePreference').and.returnValue(of({}));
spyOn(processListCloudService, 'getProcessByRequest');
component.ngAfterContentInit();
spyOn(component, 'createDatatableSchema');
component.appName = 'fake-app-name';
component.reload();
fixture.detectChanges();
component.onColumnsVisibilityChange(component.columns);
fixture.detectChanges();
expect(processListCloudService.getProcessByRequest).toHaveBeenCalledTimes(1);
});
describe('component changes', () => { describe('component changes', () => {
beforeEach(() => { beforeEach(() => {
@@ -515,12 +537,12 @@ describe('ProcessListCloudComponent', () => {
const skipCount = component.skipCount; const skipCount = component.skipCount;
component.pagination.pipe(skip(3)) component.pagination.pipe(skip(3))
.subscribe((updatedPagination) => { .subscribe((updatedPagination) => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.size).toBe(size); expect(component.size).toBe(size);
expect(component.skipCount).toBe(skipCount); expect(component.skipCount).toBe(skipCount);
expect(updatedPagination.maxItems).toEqual(size); expect(updatedPagination.maxItems).toEqual(size);
expect(updatedPagination.skipCount).toEqual(skipCount); expect(updatedPagination.skipCount).toEqual(skipCount);
done(); done();
}); });
const pagination = { const pagination = {
@@ -546,12 +568,12 @@ describe('ProcessListCloudComponent', () => {
}; };
component.pagination.pipe(skip(1)) component.pagination.pipe(skip(1))
.subscribe((updatedPagination) => { .subscribe((updatedPagination) => {
fixture.detectChanges(); fixture.detectChanges();
expect(component.size).toBe(pagination.maxItems); expect(component.size).toBe(pagination.maxItems);
expect(component.skipCount).toBe(pagination.skipCount); expect(component.skipCount).toBe(pagination.skipCount);
expect(updatedPagination.maxItems).toEqual(pagination.maxItems); expect(updatedPagination.maxItems).toEqual(pagination.maxItems);
expect(updatedPagination.skipCount).toEqual(pagination.skipCount); expect(updatedPagination.skipCount).toEqual(pagination.skipCount);
done(); done();
}); });
component.updatePagination(pagination); component.updatePagination(pagination);
@@ -634,12 +656,10 @@ describe('ProcessListCloudComponent', () => {
fixtureEmpty.destroy(); fixtureEmpty.destroy();
}); });
it('should render the custom template', async () => { it('should render the custom template', () => {
const emptyList = {list: {entries: []}};
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(emptyList));
fixtureEmpty.componentInstance.processListCloud.isLoading = false; fixtureEmpty.componentInstance.processListCloud.isLoading = false;
fixtureEmpty.detectChanges(); fixtureEmpty.detectChanges();
await fixtureEmpty.whenStable();
expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull(); expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull();
expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull();

View File

@@ -21,11 +21,11 @@ import { DataTableSchema, PaginatedComponent,
UserPreferencesService, PaginationModel, UserPreferencesService, PaginationModel,
UserPreferenceValues, DataRowEvent, CustomLoadingContentTemplateDirective, DataCellEvent, DataRowActionEvent, DataTableComponent, DataColumn } from '@alfresco/adf-core'; UserPreferenceValues, DataRowEvent, CustomLoadingContentTemplateDirective, DataCellEvent, DataRowActionEvent, DataTableComponent, DataColumn } from '@alfresco/adf-core';
import { ProcessListCloudService } from '../services/process-list-cloud.service'; import { ProcessListCloudService } from '../services/process-list-cloud.service';
import { BehaviorSubject, of } from 'rxjs'; import { BehaviorSubject, Subject, of } from 'rxjs';
import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model'; import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model';
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model'; import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model'; import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
import { map, switchMap, take, tap } from 'rxjs/operators'; import { map, switchMap, take, takeUntil, tap } from 'rxjs/operators';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service'; import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service';
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences'; import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
@@ -202,6 +202,8 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
@Output() @Output()
success: EventEmitter<any> = new EventEmitter<any>(); success: EventEmitter<any> = new EventEmitter<any>();
private onDestroy$ = new Subject<boolean>();
pagination: BehaviorSubject<PaginationModel>; pagination: BehaviorSubject<PaginationModel>;
size: number; size: number;
skipCount: number = 0; skipCount: number = 0;
@@ -269,6 +271,11 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
}); });
} }
ngOnDestroy() {
this.onDestroy$.next(true);
this.onDestroy$.complete();
}
ngOnChanges(changes: SimpleChanges) { ngOnChanges(changes: SimpleChanges) {
if (this.isPropertyChanged(changes, 'sorting')) { if (this.isPropertyChanged(changes, 'sorting')) {
this.formatSorting(changes['sorting'].currentValue); this.formatSorting(changes['sorting'].currentValue);
@@ -294,10 +301,10 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
this.isLoading = true; this.isLoading = true;
this.isColumnSchemaCreated$.pipe( this.isColumnSchemaCreated$.pipe(
take(1),
switchMap(() => of(this.createRequestNode())), switchMap(() => of(this.createRequestNode())),
tap((requestNode) => this.requestNode = requestNode), tap((requestNode) => this.requestNode = requestNode),
switchMap((requestNode) => this.processListCloudService.getProcessByRequest(requestNode)) switchMap((requestNode) => this.processListCloudService.getProcessByRequest(requestNode)),
takeUntil(this.onDestroy$)
).subscribe((processes) => { ).subscribe((processes) => {
this.rows = this.variableMapperService.mapVariablesByColumnTitle( this.rows = this.variableMapperService.mapVariablesByColumnTitle(
processes.list.entries, processes.list.entries,
@@ -386,7 +393,7 @@ export class ProcessListCloudComponent extends DataTableSchema<ProcessListDataCo
}, {}); }, {});
this.createColumns(); this.createColumns();
this.reload(); this.createDatatableSchema();
if (this.appName) { if (this.appName) {
this.cloudPreferenceService.updatePreference( this.cloudPreferenceService.updatePreference(

View File

@@ -303,6 +303,7 @@ export abstract class BaseTaskListCloudComponent<T = unknown> extends DataTableS
}, {}); }, {});
this.createColumns(); this.createColumns();
this.createDatatableSchema();
if (this.appName) { if (this.appName) {
this.cloudPreferenceService.updatePreference( this.cloudPreferenceService.updatePreference(
@@ -311,8 +312,6 @@ export abstract class BaseTaskListCloudComponent<T = unknown> extends DataTableS
this.columnsVisibility this.columnsVisibility
); );
} }
this.reload();
} }
onColumnsWidthChanged(columns: DataColumn[]): void { onColumnsWidthChanged(columns: DataColumn[]): void {

View File

@@ -16,7 +16,7 @@
*/ */
import { Component, SimpleChange, ViewChild } from '@angular/core'; import { Component, SimpleChange, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync } 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, User } from '@alfresco/adf-core'; import { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow, User } from '@alfresco/adf-core';
import { ServiceTaskListCloudComponent } from './service-task-list-cloud.component'; import { ServiceTaskListCloudComponent } from './service-task-list-cloud.component';
@@ -179,9 +179,8 @@ describe('ServiceTaskListCloudComponent', () => {
expect(component.isListEmpty()).toBeTruthy(); expect(component.isListEmpty()).toBeTruthy();
}); });
it('should return the results if an application name is given', (done) => { it('should reload tasks when reload() is called', (done) => {
spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask)); spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe((res) => { component.success.subscribe((res) => {
expect(res).toBeDefined(); expect(res).toBeDefined();
expect(component.rows).toBeDefined(); expect(component.rows).toBeDefined();
@@ -202,22 +201,9 @@ describe('ServiceTaskListCloudComponent', () => {
expect(component.rows[0]['serviceName']).toBe('simpleapp-rb'); expect(component.rows[0]['serviceName']).toBe('simpleapp-rb');
done(); done();
}); });
component.appName = appName.currentValue;
component.ngOnChanges({ appName });
fixture.detectChanges();
});
it('should reload tasks when reload() is called', (done) => {
component.appName = 'fake';
spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).not.toBeTruthy();
done();
});
fixture.detectChanges();
component.reload(); component.reload();
fixture.detectChanges();
}); });
it('should emit row click event', (done) => { it('should emit row click event', (done) => {
@@ -231,6 +217,32 @@ describe('ServiceTaskListCloudComponent', () => {
component.onRowClick(rowEvent); component.onRowClick(rowEvent);
}); });
it('should create datatable schema when a column visibility gets changed', () => {
component.ngAfterContentInit();
spyOn(component, 'createDatatableSchema');
component.onColumnsVisibilityChange(component.columns);
fixture.detectChanges();
expect(component.createDatatableSchema).toHaveBeenCalled();
});
it('should call endpoint when a column visibility gets changed', () => {
spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest');
component.ngAfterContentInit();
spyOn(component, 'createDatatableSchema');
component.appName = 'fake-app-name';
component.reload();
fixture.detectChanges();
component.onColumnsVisibilityChange(component.columns);
fixture.detectChanges();
expect(serviceTaskListCloudService.getServiceTaskByRequest).toHaveBeenCalledTimes(1);
});
describe('component changes', () => { describe('component changes', () => {
beforeEach(() => { beforeEach(() => {
@@ -302,10 +314,6 @@ describe('ServiceTaskListCloudComponent', () => {
it('should reset pagination when resetPaginationValues is called', (done) => { it('should reset pagination when resetPaginationValues is called', (done) => {
spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask)); spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.ngOnChanges({ appName });
fixture.detectChanges();
const size = component.size; const size = component.size;
const skipCount = component.skipCount; const skipCount = component.skipCount;
component.pagination.pipe(skip(3)) component.pagination.pipe(skip(3))
@@ -323,17 +331,13 @@ describe('ServiceTaskListCloudComponent', () => {
skipCount: 200 skipCount: 200
}; };
component.updatePagination(pagination); component.updatePagination(pagination);
fixture.whenStable().then( () => {
component.resetPagination(); component.resetPagination();
});
}); });
it('should set pagination and reload when updatePagination is called', (done) => { it('should set pagination and reload when updatePagination is called', (done) => {
spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask)); spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
spyOn(component, 'reload').and.stub(); spyOn(component, 'reload').and.stub();
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.ngOnChanges({ appName });
fixture.detectChanges();
const pagination = { const pagination = {
maxItems: 250, maxItems: 250,
@@ -357,7 +361,6 @@ describe('ServiceTaskListCloudComponent', () => {
let fixtureCustom: ComponentFixture<CustomTaskListComponent>; let fixtureCustom: ComponentFixture<CustomTaskListComponent>;
let componentCustom: CustomTaskListComponent; let componentCustom: CustomTaskListComponent;
let customCopyComponent: CustomCopyContentTaskListComponent; let customCopyComponent: CustomCopyContentTaskListComponent;
let element: HTMLElement;
let copyFixture: ComponentFixture<CustomCopyContentTaskListComponent>; let copyFixture: ComponentFixture<CustomCopyContentTaskListComponent>;
setupTestBed({ setupTestBed({
@@ -378,8 +381,6 @@ describe('ServiceTaskListCloudComponent', () => {
fixtureCustom.detectChanges(); fixtureCustom.detectChanges();
componentCustom = fixtureCustom.componentInstance; componentCustom = fixtureCustom.componentInstance;
customCopyComponent = copyFixture.componentInstance; customCopyComponent = copyFixture.componentInstance;
element = copyFixture.debugElement.nativeElement;
customCopyComponent.taskList.isColumnSchemaCreated$ = of(true); customCopyComponent.taskList.isColumnSchemaCreated$ = of(true);
}); });
@@ -396,42 +397,32 @@ describe('ServiceTaskListCloudComponent', () => {
expect(componentCustom.taskList.columns.length).toEqual(2); expect(componentCustom.taskList.columns.length).toEqual(2);
}); });
it('it should show copy tooltip when key is present in data-colunn', fakeAsync(() => { it('it should show copy tooltip when key is present in data-column', () => {
customCopyComponent.taskList.reload();
copyFixture.detectChanges(); copyFixture.detectChanges();
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
copyFixture.whenStable().then(() => {
copyFixture.detectChanges();
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]');
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
copyFixture.detectChanges();
expect(copyFixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
});
customCopyComponent.taskList.appName = appName.currentValue;
customCopyComponent.taskList.ngOnChanges({ appName });
copyFixture.detectChanges();
}));
it('it should not show copy tooltip when key is not present in data-column', (done) => { copyFixture.debugElement
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); .query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]'))
customCopyComponent.taskList.success.subscribe(() => { .triggerEventHandler('mouseenter');
copyFixture.whenStable().then(() => {
copyFixture.detectChanges();
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="serviceTaskName"]');
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
copyFixture.detectChanges();
expect(copyFixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull();
done();
});
});
customCopyComponent.taskList.appName = appName.currentValue;
customCopyComponent.taskList.ngOnChanges({ appName });
copyFixture.detectChanges(); copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).not.toBeNull();
});
it('it should not show copy tooltip when key is not present in data-column', () => {
customCopyComponent.taskList.reload();
copyFixture.detectChanges();
copyFixture.debugElement
.query(By.css('span[title="serviceTaskName"]'))
.triggerEventHandler('mouseenter');
copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).toBeNull();
}); });
}); });
describe('Copy cell content directive from app.config specifications', () => { describe('Copy cell content directive from app.config specifications', () => {
let element: HTMLElement;
let taskSpy: jasmine.Spy; let taskSpy: jasmine.Spy;
setupTestBed({ setupTestBed({
@@ -467,7 +458,6 @@ describe('ServiceTaskListCloudComponent', () => {
}); });
fixture = TestBed.createComponent(ServiceTaskListCloudComponent); fixture = TestBed.createComponent(ServiceTaskListCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
element = fixture.debugElement.nativeElement;
taskSpy = spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask)); taskSpy = spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask));
}); });
@@ -475,42 +465,36 @@ describe('ServiceTaskListCloudComponent', () => {
fixture.destroy(); fixture.destroy();
}); });
it('shoud show tooltip if config copyContent flag is true', fakeAsync(() => { it('shoud show tooltip if config copyContent flag is true', () => {
taskSpy.and.returnValue(of(fakeServiceTask)); taskSpy.and.returnValue(of(fakeServiceTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe(() => {
fixture.whenStable().then(() => {
fixture.detectChanges();
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]');
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
});
});
component.presetColumn = 'fakeCustomSchema'; component.presetColumn = 'fakeCustomSchema';
component.appName = appName.currentValue;
component.ngOnChanges({ appName });
component.ngAfterContentInit();
}));
it('shoud not show tooltip if config copyContent flag is true', fakeAsync(() => { component.reload();
fixture.detectChanges();
const columnWithCopyContentFlagTrue = fixture.debugElement
.query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]'));
columnWithCopyContentFlagTrue.triggerEventHandler('mouseenter');
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
});
it('shoud not show tooltip if config copyContent flag is NOT true', () => {
taskSpy.and.returnValue(of(fakeServiceTask)); taskSpy.and.returnValue(of(fakeServiceTask));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe(() => {
fixture.whenStable().then(() => {
fixture.detectChanges();
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="serviceTaskName"]');
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull();
});
});
component.presetColumn = 'fakeCustomSchema'; component.presetColumn = 'fakeCustomSchema';
component.appName = appName.currentValue;
component.ngOnChanges({ appName }); component.reload();
component.ngAfterContentInit(); fixture.detectChanges();
}));
const columnWithCopyContentFlagNotTrue = fixture.debugElement
.query(By.css('span[title="serviceTaskName"]'));
columnWithCopyContentFlagNotTrue.triggerEventHandler('mouseenter');
fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull();
});
}); });
}); });

View File

@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ViewEncapsulation, Input, Inject } from '@angular/core'; import { Component, ViewEncapsulation, Input, Inject, OnDestroy } from '@angular/core';
import { import {
AppConfigService, UserPreferencesService AppConfigService, UserPreferencesService
} from '@alfresco/adf-core'; } from '@alfresco/adf-core';
@@ -23,9 +23,9 @@ import { ServiceTaskQueryCloudRequestModel } from '../models/service-task-cloud.
import { BaseTaskListCloudComponent } from './base-task-list-cloud.component'; import { BaseTaskListCloudComponent } from './base-task-list-cloud.component';
import { ServiceTaskListCloudService } from '../services/service-task-list-cloud.service'; import { ServiceTaskListCloudService } from '../services/service-task-list-cloud.service';
import { TaskCloudService } from '../../services/task-cloud.service'; import { TaskCloudService } from '../../services/task-cloud.service';
import { combineLatest } from 'rxjs'; import { Subject, combineLatest } from 'rxjs';
import { PreferenceCloudServiceInterface, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../services/public-api'; import { PreferenceCloudServiceInterface, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../services/public-api';
import { take } from 'rxjs/operators'; import { takeUntil } from 'rxjs/operators';
const PRESET_KEY = 'adf-cloud-service-task-list.presets'; const PRESET_KEY = 'adf-cloud-service-task-list.presets';
@@ -35,10 +35,12 @@ const PRESET_KEY = 'adf-cloud-service-task-list.presets';
styleUrls: ['./base-task-list-cloud.component.scss'], styleUrls: ['./base-task-list-cloud.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent { export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent implements OnDestroy {
@Input() @Input()
queryParams: { [key: string]: any } = {}; queryParams: { [key: string]: any } = {};
private onDestroyServiceTaskList$ = new Subject<boolean>();
constructor(private serviceTaskListCloudService: ServiceTaskListCloudService, constructor(private serviceTaskListCloudService: ServiceTaskListCloudService,
appConfigService: AppConfigService, appConfigService: AppConfigService,
taskCloudService: TaskCloudService, taskCloudService: TaskCloudService,
@@ -48,6 +50,11 @@ export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent {
super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService); super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService);
} }
ngOnDestroy() {
this.onDestroyServiceTaskList$.next(true);
this.onDestroyServiceTaskList$.complete();
}
reload() { reload() {
this.requestNode = this.createRequestNode(); this.requestNode = this.createRequestNode();
@@ -57,7 +64,7 @@ export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent {
this.serviceTaskListCloudService.getServiceTaskByRequest(this.requestNode), this.serviceTaskListCloudService.getServiceTaskByRequest(this.requestNode),
this.isColumnSchemaCreated$ this.isColumnSchemaCreated$
]).pipe( ]).pipe(
take(1) takeUntil(this.onDestroyServiceTaskList$)
).subscribe( ).subscribe(
([tasks]) => { ([tasks]) => {
this.rows = tasks.list.entries; this.rows = tasks.list.entries;

View File

@@ -16,7 +16,7 @@
*/ */
import { Component, SimpleChange, ViewChild } from '@angular/core'; import { Component, SimpleChange, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync, tick } 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, import { AppConfigService,
setupTestBed, setupTestBed,
@@ -167,7 +167,6 @@ describe('TaskListCloudComponent', () => {
it('should display empty content when process list is empty', () => { it('should display empty content when process list is empty', () => {
const emptyList = { list: { entries: [] } }; const emptyList = { list: { entries: [] } };
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList)); spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList));
fixture.detectChanges(); fixture.detectChanges();
expect(component.isLoading).toBe(false); expect(component.isLoading).toBe(false);
@@ -256,9 +255,7 @@ describe('TaskListCloudComponent', () => {
it('should return the results if an application name is given', (done) => { it('should return the results if an application name is given', (done) => {
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks)); spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
component.ngAfterContentInit();
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe((res) => { component.success.subscribe((res) => {
expect(res).toBeDefined(); expect(res).toBeDefined();
expect(component.rows).toBeDefined(); expect(component.rows).toBeDefined();
@@ -273,22 +270,9 @@ describe('TaskListCloudComponent', () => {
expect(component.rows[0]).toEqual(expectedTask); expect(component.rows[0]).toEqual(expectedTask);
done(); done();
}); });
component.appName = appName.currentValue;
component.ngOnChanges({ appName });
fixture.detectChanges();
});
it('should reload tasks when reload() is called', (done) => {
component.appName = 'fake';
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.isListEmpty()).not.toBeTruthy();
done();
});
fixture.detectChanges();
component.reload(); component.reload();
fixture.detectChanges();
}); });
it('should emit row click event', (done) => { it('should emit row click event', (done) => {
@@ -369,6 +353,32 @@ describe('TaskListCloudComponent', () => {
expect(component.columns[2].title).toBe('ADF_CLOUD_TASK_LIST.PROPERTIES.ASSIGNEE'); expect(component.columns[2].title).toBe('ADF_CLOUD_TASK_LIST.PROPERTIES.ASSIGNEE');
}); });
it('should create datatable schema when a column visibility gets changed', () => {
component.ngAfterContentInit();
spyOn(component, 'createDatatableSchema');
component.onColumnsVisibilityChange(component.columns);
fixture.detectChanges();
expect(component.createDatatableSchema).toHaveBeenCalled();
});
it('should call endpoint when a column visibility gets changed', () => {
spyOn(taskListCloudService, 'getTaskByRequest');
component.ngAfterContentInit();
spyOn(component, 'createDatatableSchema');
component.appName = 'fake-app-name';
component.reload();
fixture.detectChanges();
component.onColumnsVisibilityChange(component.columns);
fixture.detectChanges();
expect(taskListCloudService.getTaskByRequest).toHaveBeenCalledTimes(1);
});
describe('component changes', () => { describe('component changes', () => {
beforeEach(() => { beforeEach(() => {
@@ -449,10 +459,6 @@ describe('TaskListCloudComponent', () => {
it('should reset pagination when resetPaginationValues is called', (done) => { it('should reset pagination when resetPaginationValues is called', (done) => {
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks)); spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.ngOnChanges({ appName });
fixture.detectChanges();
const size = component.size; const size = component.size;
const skipCount = component.skipCount; const skipCount = component.skipCount;
component.pagination.pipe(skip(3)) component.pagination.pipe(skip(3))
@@ -470,17 +476,13 @@ describe('TaskListCloudComponent', () => {
skipCount: 200 skipCount: 200
}; };
component.updatePagination(pagination); component.updatePagination(pagination);
fixture.whenStable().then( () => {
component.resetPagination(); component.resetPagination();
});
}); });
it('should set pagination and reload when updatePagination is called', (done) => { it('should set pagination and reload when updatePagination is called', (done) => {
spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks)); spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
spyOn(component, 'reload').and.stub(); spyOn(component, 'reload').and.stub();
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.ngOnChanges({ appName });
fixture.detectChanges();
const pagination = { const pagination = {
maxItems: 250, maxItems: 250,
@@ -504,7 +506,6 @@ describe('TaskListCloudComponent', () => {
let fixtureCustom: ComponentFixture<CustomTaskListComponent>; let fixtureCustom: ComponentFixture<CustomTaskListComponent>;
let componentCustom: CustomTaskListComponent; let componentCustom: CustomTaskListComponent;
let customCopyComponent: CustomCopyContentTaskListComponent; let customCopyComponent: CustomCopyContentTaskListComponent;
let element: HTMLElement;
let copyFixture: ComponentFixture<CustomCopyContentTaskListComponent>; let copyFixture: ComponentFixture<CustomCopyContentTaskListComponent>;
setupTestBed({ setupTestBed({
@@ -525,8 +526,6 @@ describe('TaskListCloudComponent', () => {
fixtureCustom.detectChanges(); fixtureCustom.detectChanges();
componentCustom = fixtureCustom.componentInstance; componentCustom = fixtureCustom.componentInstance;
customCopyComponent = copyFixture.componentInstance; customCopyComponent = copyFixture.componentInstance;
element = copyFixture.debugElement.nativeElement;
customCopyComponent.taskList.isColumnSchemaCreated$ = of(true); customCopyComponent.taskList.isColumnSchemaCreated$ = of(true);
}); });
@@ -536,43 +535,35 @@ describe('TaskListCloudComponent', () => {
}); });
it('should fetch custom schemaColumn from html', () => { it('should fetch custom schemaColumn from html', () => {
fixture.detectChanges(); copyFixture.detectChanges();
expect(componentCustom.taskList.columnList).toBeDefined(); expect(componentCustom.taskList.columnList).toBeDefined();
expect(componentCustom.taskList.columns[0]['title']).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.NAME'); expect(componentCustom.taskList.columns[0]['title']).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.NAME');
expect(componentCustom.taskList.columns[1]['title']).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED'); expect(componentCustom.taskList.columns[1]['title']).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.CREATED');
expect(componentCustom.taskList.columns.length).toEqual(3); expect(componentCustom.taskList.columns.length).toEqual(3);
}); });
it('it should show copy tooltip when key is present in data-colunn', fakeAsync(() => { it('it should show copy tooltip when key is present in data-column', () => {
customCopyComponent.taskList.reload();
copyFixture.detectChanges(); copyFixture.detectChanges();
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
copyFixture.whenStable().then(() => {
copyFixture.detectChanges();
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]');
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
copyFixture.detectChanges();
expect(copyFixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
});
customCopyComponent.taskList.appName = appName.currentValue;
customCopyComponent.taskList.ngOnChanges({ appName });
copyFixture.detectChanges();
}));
it('it should not show copy tooltip when key is not present in data-column', (done) => { copyFixture.debugElement
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); .query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'))
customCopyComponent.taskList.success.subscribe(() => { .triggerEventHandler('mouseenter');
copyFixture.whenStable().then(() => {
copyFixture.detectChanges();
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="standalone-subtask"]');
spanHTMLElement.dispatchEvent(new Event('mouseenter'));
copyFixture.detectChanges();
expect(copyFixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull();
done();
});
});
customCopyComponent.taskList.appName = appName.currentValue;
customCopyComponent.taskList.ngOnChanges({ appName });
copyFixture.detectChanges(); copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).not.toBeNull();
});
it('it should not show copy tooltip when key is not present in data-column', () => {
customCopyComponent.taskList.reload();
copyFixture.detectChanges();
copyFixture.debugElement
.query(By.css('span[title="standalone-subtask"]'))
.triggerEventHandler('mouseenter');
copyFixture.detectChanges();
expect(copyFixture.debugElement.query(By.css('.adf-copy-tooltip'))).toBeNull();
}); });
}); });
@@ -615,8 +606,6 @@ describe('TaskListCloudComponent', () => {
}); });
describe('Copy cell content directive from app.config specifications', () => { describe('Copy cell content directive from app.config specifications', () => {
let element: HTMLElement;
let taskSpy: jasmine.Spy; let taskSpy: jasmine.Spy;
setupTestBed({ setupTestBed({
@@ -658,9 +647,7 @@ describe('TaskListCloudComponent', () => {
}); });
fixture = TestBed.createComponent(TaskListCloudComponent); fixture = TestBed.createComponent(TaskListCloudComponent);
component = fixture.componentInstance; component = fixture.componentInstance;
element = fixture.debugElement.nativeElement;
taskSpy = spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks)); taskSpy = spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks));
component.isColumnSchemaCreated$ = of(true); component.isColumnSchemaCreated$ = of(true);
}); });
@@ -668,34 +655,33 @@ describe('TaskListCloudComponent', () => {
fixture.destroy(); fixture.destroy();
}); });
it('should show tooltip if config copyContent flag is true', fakeAsync(() => { it('should show tooltip if config copyContent flag is true', () => {
taskSpy.and.returnValue(of(fakeGlobalTasks)); taskSpy.and.returnValue(of(fakeGlobalTasks));
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.presetColumn = 'fakeCustomSchema'; component.presetColumn = 'fakeCustomSchema';
component.appName = appName.currentValue;
component.ngOnChanges({ appName });
component.ngAfterContentInit();
tick(); component.reload();
fixture.detectChanges(); fixture.detectChanges();
const spanHTMLElement = element.querySelector<HTMLInputElement>('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]');
spanHTMLElement.dispatchEvent(new Event('mouseenter')); const columnWithCopyContentFlagTrue = fixture.debugElement
.query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]'));
columnWithCopyContentFlagTrue.triggerEventHandler('mouseenter');
fixture.detectChanges(); fixture.detectChanges();
expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull(); expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).not.toBeNull();
})); });
it('should replace priority values', fakeAsync(() => { it('should replace priority values', () => {
taskSpy.and.returnValue(of(fakeGlobalTasks)); taskSpy.and.returnValue(of(fakeGlobalTasks));
component.presetColumn = 'fakeCustomSchema'; component.presetColumn = 'fakeCustomSchema';
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.ngOnChanges({ appName });
fixture.detectChanges();
component.reload(); component.reload();
tick(); fixture.detectChanges();
const cell = fixture.nativeElement.querySelector('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]');
expect(cell.textContent).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE'); const cell = fixture.debugElement
})); .query(By.css('[data-automation-id="text_ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE"]'));
expect(cell.nativeElement.textContent).toEqual('ADF_CLOUD_TASK_LIST.PROPERTIES.PRIORITY_VALUES.NONE');
});
it('replacePriorityValues should return undefined when no rows defined', () => { it('replacePriorityValues should return undefined when no rows defined', () => {
const emptyList = { list: { entries: [] } }; const emptyList = { list: { entries: [] } };

View File

@@ -15,7 +15,7 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, ViewEncapsulation, Input, Inject } from '@angular/core'; import { Component, ViewEncapsulation, Input, Inject, OnDestroy } from '@angular/core';
import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core'; import { AppConfigService, UserPreferencesService } from '@alfresco/adf-core';
import { TaskQueryCloudRequestModel } from '../../../models/filter-cloud-model'; import { TaskQueryCloudRequestModel } from '../../../models/filter-cloud-model';
import { BaseTaskListCloudComponent } from './base-task-list-cloud.component'; import { BaseTaskListCloudComponent } from './base-task-list-cloud.component';
@@ -23,8 +23,8 @@ import { TaskCloudService } from '../../services/task-cloud.service';
import { TASK_LIST_CLOUD_TOKEN, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service'; import { TASK_LIST_CLOUD_TOKEN, TASK_LIST_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service';
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
import { TaskListCloudServiceInterface } from '../../../services/task-list-cloud.service.interface'; import { TaskListCloudServiceInterface } from '../../../services/task-list-cloud.service.interface';
import { of } from 'rxjs'; import { Subject, of } from 'rxjs';
import { switchMap, take, tap } from 'rxjs/operators'; import { switchMap, takeUntil, tap } from 'rxjs/operators';
import { VariableMapperService } from '../../../services/variable-mapper.sevice'; import { VariableMapperService } from '../../../services/variable-mapper.sevice';
import { ProcessListDataColumnCustomData } from '../../../models/data-column-custom-data'; import { ProcessListDataColumnCustomData } from '../../../models/data-column-custom-data';
import { TaskCloudModel } from '../../../models/task-cloud.model'; import { TaskCloudModel } from '../../../models/task-cloud.model';
@@ -40,7 +40,7 @@ const PRESET_KEY = 'adf-cloud-task-list.presets';
styleUrls: ['./base-task-list-cloud.component.scss'], styleUrls: ['./base-task-list-cloud.component.scss'],
encapsulation: ViewEncapsulation.None encapsulation: ViewEncapsulation.None
}) })
export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessListDataColumnCustomData> { export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessListDataColumnCustomData> implements OnDestroy {
/** /**
* The assignee of the process. Possible values are: "assignee" (the current user is the assignee), * The assignee of the process. Possible values are: "assignee" (the current user is the assignee),
* "candidate" (the current user is a task candidate", "group_x" (the task is assigned to a group * "candidate" (the current user is a task candidate", "group_x" (the task is assigned to a group
@@ -145,6 +145,8 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessLi
@Input() @Input()
candidateGroupId: string = ''; candidateGroupId: string = '';
private onDestroyTaskList$ = new Subject<boolean>();
rows: TaskInstanceCloudListViewModel[] = []; rows: TaskInstanceCloudListViewModel[] = [];
dataAdapter: TasksListDatatableAdapter | undefined; dataAdapter: TasksListDatatableAdapter | undefined;
@@ -158,14 +160,19 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent<ProcessLi
super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService); super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService);
} }
ngOnDestroy() {
this.onDestroyTaskList$.next(true);
this.onDestroyTaskList$.complete();
}
reload() { reload() {
this.isLoading = true; this.isLoading = true;
this.isColumnSchemaCreated$.pipe( this.isColumnSchemaCreated$.pipe(
take(1),
switchMap(() => of(this.createRequestNode())), switchMap(() => of(this.createRequestNode())),
tap((requestNode) => this.requestNode = requestNode), tap((requestNode) => this.requestNode = requestNode),
switchMap((requestNode) => this.taskListCloudService.getTaskByRequest(requestNode)) switchMap((requestNode) => this.taskListCloudService.getTaskByRequest(requestNode)),
takeUntil(this.onDestroyTaskList$)
).subscribe((tasks: { list: PaginatedEntries<TaskCloudModel> }) => { ).subscribe((tasks: { list: PaginatedEntries<TaskCloudModel> }) => {
const tasksWithVariables = tasks.list.entries.map((task) => ({ const tasksWithVariables = tasks.list.entries.map((task) => ({
...task, ...task,