From 23149cae58e970aa6c1931c6a12818322bbac4b5 Mon Sep 17 00:00:00 2001 From: Ehsan Rezaei Date: Fri, 5 May 2023 10:45:29 +0200 Subject: [PATCH] [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 --- .../process-list-cloud.component.spec.ts | 62 ++++--- .../process-list-cloud.component.ts | 17 +- .../base-task-list-cloud.component.ts | 3 +- .../service-task-list-cloud.component.spec.ts | 168 ++++++++---------- .../service-task-list-cloud.component.ts | 17 +- .../task-list-cloud.component.spec.ts | 148 +++++++-------- .../components/task-list-cloud.component.ts | 19 +- 7 files changed, 222 insertions(+), 212 deletions(-) diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts index a0b88990b5..25f4977205 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.spec.ts @@ -364,10 +364,8 @@ describe('ProcessListCloudComponent', () => { it('should re-create columns when a column width gets changed', () => { component.isResizingEnabled = true; - component.appName = 'FAKE-APP-NAME'; spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList)); - fixture.detectChanges(); component.reload(); fixture.detectChanges(); @@ -421,9 +419,6 @@ describe('ProcessListCloudComponent', () => { }); it('should re-create columns when a column order gets changed', () => { - component.appName = 'FAKE-APP-NAME'; - - fixture.detectChanges(); component.reload(); fixture.detectChanges(); @@ -437,6 +432,33 @@ describe('ProcessListCloudComponent', () => { 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', () => { beforeEach(() => { @@ -515,12 +537,12 @@ describe('ProcessListCloudComponent', () => { const skipCount = component.skipCount; component.pagination.pipe(skip(3)) .subscribe((updatedPagination) => { - fixture.detectChanges(); - expect(component.size).toBe(size); - expect(component.skipCount).toBe(skipCount); - expect(updatedPagination.maxItems).toEqual(size); - expect(updatedPagination.skipCount).toEqual(skipCount); - done(); + fixture.detectChanges(); + expect(component.size).toBe(size); + expect(component.skipCount).toBe(skipCount); + expect(updatedPagination.maxItems).toEqual(size); + expect(updatedPagination.skipCount).toEqual(skipCount); + done(); }); const pagination = { @@ -546,12 +568,12 @@ describe('ProcessListCloudComponent', () => { }; component.pagination.pipe(skip(1)) .subscribe((updatedPagination) => { - fixture.detectChanges(); - expect(component.size).toBe(pagination.maxItems); - expect(component.skipCount).toBe(pagination.skipCount); - expect(updatedPagination.maxItems).toEqual(pagination.maxItems); - expect(updatedPagination.skipCount).toEqual(pagination.skipCount); - done(); + fixture.detectChanges(); + expect(component.size).toBe(pagination.maxItems); + expect(component.skipCount).toBe(pagination.skipCount); + expect(updatedPagination.maxItems).toEqual(pagination.maxItems); + expect(updatedPagination.skipCount).toEqual(pagination.skipCount); + done(); }); component.updatePagination(pagination); @@ -634,12 +656,10 @@ describe('ProcessListCloudComponent', () => { fixtureEmpty.destroy(); }); - it('should render the custom template', async () => { - const emptyList = {list: {entries: []}}; - spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(emptyList)); + it('should render the custom template', () => { fixtureEmpty.componentInstance.processListCloud.isLoading = false; + fixtureEmpty.detectChanges(); - await fixtureEmpty.whenStable(); expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull(); expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); diff --git a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts index ab177fbfbe..27ea15b514 100644 --- a/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/process/process-list/components/process-list-cloud.component.ts @@ -21,11 +21,11 @@ import { DataTableSchema, PaginatedComponent, UserPreferencesService, PaginationModel, UserPreferenceValues, DataRowEvent, CustomLoadingContentTemplateDirective, DataCellEvent, DataRowActionEvent, DataTableComponent, DataColumn } from '@alfresco/adf-core'; 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 { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.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 { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service'; import { ProcessListCloudPreferences } from '../models/process-cloud-preferences'; @@ -202,6 +202,8 @@ export class ProcessListCloudComponent extends DataTableSchema = new EventEmitter(); + private onDestroy$ = new Subject(); + pagination: BehaviorSubject; size: number; skipCount: number = 0; @@ -269,6 +271,11 @@ export class ProcessListCloudComponent extends DataTableSchema of(this.createRequestNode())), tap((requestNode) => this.requestNode = requestNode), - switchMap((requestNode) => this.processListCloudService.getProcessByRequest(requestNode)) + switchMap((requestNode) => this.processListCloudService.getProcessByRequest(requestNode)), + takeUntil(this.onDestroy$) ).subscribe((processes) => { this.rows = this.variableMapperService.mapVariablesByColumnTitle( processes.list.entries, @@ -386,7 +393,7 @@ export class ProcessListCloudComponent extends DataTableSchema extends DataTableS }, {}); this.createColumns(); + this.createDatatableSchema(); if (this.appName) { this.cloudPreferenceService.updatePreference( @@ -311,8 +312,6 @@ export abstract class BaseTaskListCloudComponent extends DataTableS this.columnsVisibility ); } - - this.reload(); } onColumnsWidthChanged(columns: DataColumn[]): void { diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.spec.ts index ad0ff466d9..bc55e7180c 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.spec.ts @@ -16,7 +16,7 @@ */ 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 { AppConfigService, setupTestBed, DataRowEvent, ObjectDataRow, User } from '@alfresco/adf-core'; import { ServiceTaskListCloudComponent } from './service-task-list-cloud.component'; @@ -179,9 +179,8 @@ describe('ServiceTaskListCloudComponent', () => { 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)); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); component.success.subscribe((res) => { expect(res).toBeDefined(); expect(component.rows).toBeDefined(); @@ -202,22 +201,9 @@ describe('ServiceTaskListCloudComponent', () => { expect(component.rows[0]['serviceName']).toBe('simpleapp-rb'); 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(); + fixture.detectChanges(); }); it('should emit row click event', (done) => { @@ -231,6 +217,32 @@ describe('ServiceTaskListCloudComponent', () => { 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', () => { beforeEach(() => { @@ -302,10 +314,6 @@ describe('ServiceTaskListCloudComponent', () => { it('should reset pagination when resetPaginationValues is called', (done) => { 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 skipCount = component.skipCount; component.pagination.pipe(skip(3)) @@ -323,17 +331,13 @@ describe('ServiceTaskListCloudComponent', () => { skipCount: 200 }; component.updatePagination(pagination); - fixture.whenStable().then( () => { - component.resetPagination(); - }); + + component.resetPagination(); }); it('should set pagination and reload when updatePagination is called', (done) => { spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask)); spyOn(component, 'reload').and.stub(); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - component.ngOnChanges({ appName }); - fixture.detectChanges(); const pagination = { maxItems: 250, @@ -357,7 +361,6 @@ describe('ServiceTaskListCloudComponent', () => { let fixtureCustom: ComponentFixture; let componentCustom: CustomTaskListComponent; let customCopyComponent: CustomCopyContentTaskListComponent; - let element: HTMLElement; let copyFixture: ComponentFixture; setupTestBed({ @@ -378,8 +381,6 @@ describe('ServiceTaskListCloudComponent', () => { fixtureCustom.detectChanges(); componentCustom = fixtureCustom.componentInstance; customCopyComponent = copyFixture.componentInstance; - element = copyFixture.debugElement.nativeElement; - customCopyComponent.taskList.isColumnSchemaCreated$ = of(true); }); @@ -396,42 +397,32 @@ describe('ServiceTaskListCloudComponent', () => { 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(); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - copyFixture.whenStable().then(() => { - copyFixture.detectChanges(); - const spanHTMLElement = element.querySelector('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) => { - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - customCopyComponent.taskList.success.subscribe(() => { - copyFixture.whenStable().then(() => { - copyFixture.detectChanges(); - const spanHTMLElement = element.querySelector('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.debugElement + .query(By.css('span[title="04fdf69f-4ddd-48ab-9563-da776c9b163c"]')) + .triggerEventHandler('mouseenter'); + 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', () => { - - let element: HTMLElement; let taskSpy: jasmine.Spy; setupTestBed({ @@ -467,7 +458,6 @@ describe('ServiceTaskListCloudComponent', () => { }); fixture = TestBed.createComponent(ServiceTaskListCloudComponent); component = fixture.componentInstance; - element = fixture.debugElement.nativeElement; taskSpy = spyOn(serviceTaskListCloudService, 'getServiceTaskByRequest').and.returnValue(of(fakeServiceTask)); }); @@ -475,42 +465,36 @@ describe('ServiceTaskListCloudComponent', () => { 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)); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - - component.success.subscribe(() => { - fixture.whenStable().then(() => { - fixture.detectChanges(); - const spanHTMLElement = element.querySelector('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.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)); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - component.success.subscribe(() => { - fixture.whenStable().then(() => { - fixture.detectChanges(); - const spanHTMLElement = element.querySelector('span[title="serviceTaskName"]'); - spanHTMLElement.dispatchEvent(new Event('mouseenter')); - fixture.detectChanges(); - expect(fixture.debugElement.nativeElement.querySelector('.adf-copy-tooltip')).toBeNull(); - }); - }); component.presetColumn = 'fakeCustomSchema'; - component.appName = appName.currentValue; - component.ngOnChanges({ appName }); - component.ngAfterContentInit(); - })); + + component.reload(); + 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(); + }); }); }); diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts index 88654c5e32..f22efad224 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/service-task-list-cloud.component.ts @@ -15,7 +15,7 @@ * 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'; @@ -23,9 +23,9 @@ import { ServiceTaskQueryCloudRequestModel } from '../models/service-task-cloud. import { BaseTaskListCloudComponent } from './base-task-list-cloud.component'; import { ServiceTaskListCloudService } from '../services/service-task-list-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 { take } from 'rxjs/operators'; +import { takeUntil } from 'rxjs/operators'; 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'], encapsulation: ViewEncapsulation.None }) -export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent { +export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent implements OnDestroy { @Input() queryParams: { [key: string]: any } = {}; + private onDestroyServiceTaskList$ = new Subject(); + constructor(private serviceTaskListCloudService: ServiceTaskListCloudService, appConfigService: AppConfigService, taskCloudService: TaskCloudService, @@ -48,6 +50,11 @@ export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent { super(appConfigService, taskCloudService, userPreferences, PRESET_KEY, cloudPreferenceService); } + ngOnDestroy() { + this.onDestroyServiceTaskList$.next(true); + this.onDestroyServiceTaskList$.complete(); + } + reload() { this.requestNode = this.createRequestNode(); @@ -57,7 +64,7 @@ export class ServiceTaskListCloudComponent extends BaseTaskListCloudComponent { this.serviceTaskListCloudService.getServiceTaskByRequest(this.requestNode), this.isColumnSchemaCreated$ ]).pipe( - take(1) + takeUntil(this.onDestroyServiceTaskList$) ).subscribe( ([tasks]) => { this.rows = tasks.list.entries; diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts index e0efb529d7..5592be4086 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.spec.ts @@ -16,7 +16,7 @@ */ 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 { AppConfigService, setupTestBed, @@ -167,7 +167,6 @@ describe('TaskListCloudComponent', () => { it('should display empty content when process list is empty', () => { const emptyList = { list: { entries: [] } }; spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(emptyList)); - fixture.detectChanges(); expect(component.isLoading).toBe(false); @@ -256,9 +255,7 @@ describe('TaskListCloudComponent', () => { it('should return the results if an application name is given', (done) => { spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks)); - component.ngAfterContentInit(); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); component.success.subscribe((res) => { expect(res).toBeDefined(); expect(component.rows).toBeDefined(); @@ -273,22 +270,9 @@ describe('TaskListCloudComponent', () => { expect(component.rows[0]).toEqual(expectedTask); 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(); + fixture.detectChanges(); }); 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'); }); + 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', () => { beforeEach(() => { @@ -449,10 +459,6 @@ describe('TaskListCloudComponent', () => { it('should reset pagination when resetPaginationValues is called', (done) => { 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 skipCount = component.skipCount; component.pagination.pipe(skip(3)) @@ -470,17 +476,13 @@ describe('TaskListCloudComponent', () => { skipCount: 200 }; component.updatePagination(pagination); - fixture.whenStable().then( () => { - component.resetPagination(); - }); + + component.resetPagination(); }); it('should set pagination and reload when updatePagination is called', (done) => { spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks)); spyOn(component, 'reload').and.stub(); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - component.ngOnChanges({ appName }); - fixture.detectChanges(); const pagination = { maxItems: 250, @@ -504,7 +506,6 @@ describe('TaskListCloudComponent', () => { let fixtureCustom: ComponentFixture; let componentCustom: CustomTaskListComponent; let customCopyComponent: CustomCopyContentTaskListComponent; - let element: HTMLElement; let copyFixture: ComponentFixture; setupTestBed({ @@ -525,8 +526,6 @@ describe('TaskListCloudComponent', () => { fixtureCustom.detectChanges(); componentCustom = fixtureCustom.componentInstance; customCopyComponent = copyFixture.componentInstance; - element = copyFixture.debugElement.nativeElement; - customCopyComponent.taskList.isColumnSchemaCreated$ = of(true); }); @@ -536,43 +535,35 @@ describe('TaskListCloudComponent', () => { }); it('should fetch custom schemaColumn from html', () => { - fixture.detectChanges(); + copyFixture.detectChanges(); expect(componentCustom.taskList.columnList).toBeDefined(); 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.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(); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - copyFixture.whenStable().then(() => { - copyFixture.detectChanges(); - const spanHTMLElement = element.querySelector('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) => { - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - customCopyComponent.taskList.success.subscribe(() => { - copyFixture.whenStable().then(() => { - copyFixture.detectChanges(); - const spanHTMLElement = element.querySelector('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.debugElement + .query(By.css('span[title="11fe013d-c263-11e8-b75b-0a5864600540"]')) + .triggerEventHandler('mouseenter'); + 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', () => { - - let element: HTMLElement; let taskSpy: jasmine.Spy; setupTestBed({ @@ -658,9 +647,7 @@ describe('TaskListCloudComponent', () => { }); fixture = TestBed.createComponent(TaskListCloudComponent); component = fixture.componentInstance; - element = fixture.debugElement.nativeElement; taskSpy = spyOn(taskListCloudService, 'getTaskByRequest').and.returnValue(of(fakeGlobalTasks)); - component.isColumnSchemaCreated$ = of(true); }); @@ -668,34 +655,33 @@ describe('TaskListCloudComponent', () => { 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)); - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - component.presetColumn = 'fakeCustomSchema'; - component.appName = appName.currentValue; - component.ngOnChanges({ appName }); - component.ngAfterContentInit(); - tick(); + component.reload(); fixture.detectChanges(); - const spanHTMLElement = element.querySelector('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(); 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)); component.presetColumn = 'fakeCustomSchema'; - const appName = new SimpleChange(null, 'FAKE-APP-NAME', true); - component.ngOnChanges({ appName }); - fixture.detectChanges(); + component.reload(); - tick(); - 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'); - })); + fixture.detectChanges(); + + 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', () => { const emptyList = { list: { entries: [] } }; diff --git a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts index 1a05d6faa9..f37dfe608e 100644 --- a/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts +++ b/lib/process-services-cloud/src/lib/task/task-list/components/task-list-cloud.component.ts @@ -15,7 +15,7 @@ * 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 { TaskQueryCloudRequestModel } from '../../../models/filter-cloud-model'; 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 { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface'; import { TaskListCloudServiceInterface } from '../../../services/task-list-cloud.service.interface'; -import { of } from 'rxjs'; -import { switchMap, take, tap } from 'rxjs/operators'; +import { Subject, of } from 'rxjs'; +import { switchMap, takeUntil, tap } from 'rxjs/operators'; import { VariableMapperService } from '../../../services/variable-mapper.sevice'; import { ProcessListDataColumnCustomData } from '../../../models/data-column-custom-data'; 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'], encapsulation: ViewEncapsulation.None }) -export class TaskListCloudComponent extends BaseTaskListCloudComponent { +export class TaskListCloudComponent extends BaseTaskListCloudComponent implements OnDestroy { /** * 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 @@ -145,6 +145,8 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent(); + rows: TaskInstanceCloudListViewModel[] = []; dataAdapter: TasksListDatatableAdapter | undefined; @@ -158,14 +160,19 @@ export class TaskListCloudComponent extends BaseTaskListCloudComponent of(this.createRequestNode())), tap((requestNode) => this.requestNode = requestNode), - switchMap((requestNode) => this.taskListCloudService.getTaskByRequest(requestNode)) + switchMap((requestNode) => this.taskListCloudService.getTaskByRequest(requestNode)), + takeUntil(this.onDestroyTaskList$) ).subscribe((tasks: { list: PaginatedEntries }) => { const tasksWithVariables = tasks.list.entries.map((task) => ({ ...task,