[AAE-9293] Allow editing and restoring default process cloud filter (#7684)

* [AAE-9293] Allow editing and restoring default process cloud filter

* [AAE-9293] Fix e2e tests

* [AAE-9293] Fix review comments
This commit is contained in:
Pablo Martinez Garcia 2022-06-24 23:08:45 +02:00 committed by GitHub
parent 851e153080
commit db5392e72a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 162 additions and 40 deletions

View File

@ -252,8 +252,6 @@ describe('Process list cloud', () => {
await processList.checkContentIsDisplayedById(completedProcess.entry.id);
await expect(await processList.getDataTable().numberOfRows()).toBe(1);
await editProcessFilter.openFilter();
await expect(await editProcessFilter.getProcessInstanceId()).toEqual(completedProcess.entry.id);
});
@ -276,7 +274,6 @@ describe('Process list cloud', () => {
await editProcessFilter.saveAs('SavedFilter');
await expect(await processCloudDemoPage.processFilterCloudComponent.getActiveFilterName()).toBe('SavedFilter');
await editProcessFilter.openFilter();
await expect(await editProcessFilter.getProcessInstanceId()).toEqual(runningProcessInstance.entry.id);
await editProcessFilter.setStatusFilterDropDown(PROCESS_STATUS.RUNNING);
@ -288,7 +285,6 @@ describe('Process list cloud', () => {
await editProcessFilter.saveAs('SwitchFilter');
await expect(await processCloudDemoPage.processFilterCloudComponent.getActiveFilterName()).toBe('SwitchFilter');
await editProcessFilter.openFilter();
await expect(await editProcessFilter.getProcessInstanceId()).toEqual(switchProcessInstance.entry.id);
await expect(await editProcessFilter.getNumberOfAppNameOptions()).toBe(noOfApps);
await expect(await editProcessFilter.checkAppNamesAreUnique()).toBe(true);

View File

@ -85,7 +85,6 @@ describe('Edit process filters cloud', () => {
await createNewProcessCustomFilter('New');
await expect(await processFilter.getActiveFilterName()).toBe('New');
await processFilter.clickProcessFilter('custom-new');
await editProcessFilter.openFilter();
await editProcessFilter.setSortFilterDropDown('Start Date');
await expect(await editProcessFilter.getSortFilterDropDownValue()).toEqual('Start Date');
await expect(await editProcessFilter.getOrderFilterDropDownValue()).toEqual('Descending');
@ -105,7 +104,6 @@ describe('Edit process filters cloud', () => {
await createNewProcessCustomFilter('New');
await expect(await processFilter.getActiveFilterName()).toBe('New');
await editProcessFilter.openFilter();
await expect(await editProcessFilter.getSortFilterDropDownValue()).toEqual('Id');
await processFilter.clickAllProcessesFilter();
@ -119,11 +117,9 @@ describe('Edit process filters cloud', () => {
await createNewProcessCustomFilter('New');
await expect(await processFilter.getActiveFilterName()).toBe('New');
await editProcessFilter.openFilter();
await editProcessFilter.setSortFilterDropDown('Process Name');
await expect(await editProcessFilter.getSortFilterDropDownValue()).toEqual('Process Name');
await editProcessFilter.clickSaveButton();
await editProcessFilter.openFilter();
await expect(await processFilter.getActiveFilterName()).toBe('New');
await expect(await editProcessFilter.getSortFilterDropDownValue()).toEqual('Process Name');
@ -133,7 +129,6 @@ describe('Edit process filters cloud', () => {
it('[C291808] A process filter is deleted when clicking on delete button', async () => {
await createNewProcessCustomFilter('New');
await editProcessFilter.openFilter();
await expect(await processFilter.getActiveFilterName()).toBe('New');
await expect(await editProcessFilter.getSortFilterDropDownValue()).toEqual('Id');
await editProcessFilter.clickDeleteButton();
@ -154,7 +149,6 @@ describe('Edit process filters cloud', () => {
await expect(await processFilter.getActiveFilterName()).toEqual(PROCESSES.ALL);
await processFilter.clickRunningProcessesFilter();
await expect(await processFilter.getActiveFilterName()).toEqual(PROCESSES.RUNNING);
await editProcessFilter.openFilter();
await processFilter.clickAllProcessesFilter();
await expect(await processFilter.getActiveFilterName()).toEqual(PROCESSES.ALL);
await expect(await editProcessFilter.getSortFilterDropDownValue()).toEqual('Start Date');

View File

@ -277,7 +277,8 @@
"TOOL_TIP": {
"SAVE": "Save filter",
"SAVE_AS": "Save filter as",
"DELETE": "Delete filter"
"DELETE": "Delete filter",
"RESTORE": "Restore filter"
},
"DIALOG": {
"TITLE": "Save filter as",

View File

@ -7,7 +7,7 @@
<span *ngIf="showTitle"> {{ 'ADF_CLOUD_EDIT_PROCESS_FILTER.TITLE' | translate}}</span>
<div *ngIf="showFilterActions" class="adf-cloud-edit-process-filter-actions">
<ng-container *ngIf="toggleFilterActions">
<button *ngFor="let filterAction of processFilterActions" mat-icon-button matTooltip="{{ filterAction.tooltip | translate}}" [attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType" [disabled]="isDisabledAction(filterAction)" (click)="executeFilterActions(filterAction)">
<button *ngFor="let filterAction of processFilterActions" mat-icon-button matTooltip="{{ filterAction.tooltip | translate}}" [attr.data-automation-id]="'adf-filter-action-' + filterAction.actionType" [disabled]="isDisabledAction(filterAction)" (click)="executeFilterActions($event, filterAction)">
<adf-icon [value]="filterAction.icon"></adf-icon>
</button>
</ng-container>

View File

@ -25,7 +25,7 @@ import { MatDialog } from '@angular/material/dialog';
import { of } from 'rxjs';
import { debounceTime } from 'rxjs/operators';
import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component';
import { EditProcessFilterCloudComponent } from './edit-process-filter-cloud.component';
import { EditProcessFilterCloudComponent, PROCESS_FILTER_ACTION_RESTORE, PROCESS_FILTER_ACTION_SAVE_DEFAULT } from './edit-process-filter-cloud.component';
import { ProcessFiltersCloudModule } from '../process-filters-cloud.module';
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
@ -329,13 +329,13 @@ describe('EditProcessFilterCloudComponent', () => {
fixture.detectChanges();
component.editProcessFilterForm.valueChanges
.pipe(debounceTime(500))
.subscribe(() => {
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
fixture.detectChanges();
expect(saveButton.disabled).toEqual(false);
done();
});
.pipe(debounceTime(500))
.subscribe(() => {
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
fixture.detectChanges();
expect(saveButton.disabled).toEqual(false);
done();
});
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger');
stateElement.click();
@ -353,13 +353,13 @@ describe('EditProcessFilterCloudComponent', () => {
fixture.detectChanges();
component.editProcessFilterForm.valueChanges
.pipe(debounceTime(500))
.subscribe(() => {
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
fixture.detectChanges();
expect(saveButton.disabled).toEqual(false);
done();
});
.pipe(debounceTime(500))
.subscribe(() => {
const saveButton = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-filter-action-saveAs"]');
fixture.detectChanges();
expect(saveButton.disabled).toEqual(false);
done();
});
const stateElement = fixture.debugElement.nativeElement.querySelector('[data-automation-id="adf-cloud-edit-process-property-status"] .mat-select-trigger');
stateElement.click();
@ -501,7 +501,7 @@ describe('EditProcessFilterCloudComponent', () => {
suspendedDateType: DateCloudFilterType.RANGE
});
filter.suspendedFrom = new Date(2021, 1, 1).toString();
filter.suspendedTo = new Date(2021, 1, 2).toString();
filter.suspendedTo = new Date(2021, 1, 2).toString();
getProcessFilterByIdSpy.and.returnValue(of(filter));
fixture.detectChanges();
@ -888,6 +888,84 @@ describe('EditProcessFilterCloudComponent', () => {
expect(component.processFilterActions.length).toEqual(1);
});
it('should emit save default filter event and save the filter on click save default filter button', async () => {
const expectedAction = {
actionType: PROCESS_FILTER_ACTION_SAVE_DEFAULT,
icon: 'adf:save',
tooltip: 'ADF_CLOUD_EDIT_PROCESS_FILTER.TOOL_TIP.SAVE',
filter: jasmine.anything()
};
component.actions = [PROCESS_FILTER_ACTION_SAVE_DEFAULT];
component.toggleFilterActions = true;
fixture.detectChanges();
await fixture.whenStable();
const processFilterIdChange = new SimpleChange(null, 'mock-process-filter-id', true);
component.ngOnChanges({ id: processFilterIdChange });
fixture.detectChanges();
await fixture.whenStable();
const saveDefaultFilterSpy = spyOn(service, 'updateFilter').and.returnValue(of([fakeFilter]));
const saveDefaultFilterEmitSpy: jasmine.Spy = spyOn(component.action, 'emit');
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
expansionPanel.click();
fixture.detectChanges();
const saveDefaultFilterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="adf-filter-action-${PROCESS_FILTER_ACTION_SAVE_DEFAULT}"]`);
fixture.detectChanges();
expect(saveDefaultFilterButton.disabled).toBe(false);
saveDefaultFilterButton.click();
fixture.detectChanges();
await fixture.whenStable();
expect(saveDefaultFilterSpy).toHaveBeenCalled();
expect(saveDefaultFilterEmitSpy).toHaveBeenCalledWith(expectedAction);
});
it('should emit reset filter to defaults event and save the default filters on click reset button', async () => {
const expectedAction = {
actionType: PROCESS_FILTER_ACTION_RESTORE,
icon: 'settings_backup_restore',
tooltip: 'ADF_CLOUD_EDIT_PROCESS_FILTER.TOOL_TIP.RESTORE',
filter: jasmine.anything()
};
component.actions = [PROCESS_FILTER_ACTION_RESTORE];
component.toggleFilterActions = true;
fixture.detectChanges();
await fixture.whenStable();
const processFilterIdChange = new SimpleChange(null, 'mock-process-filter-id', true);
component.ngOnChanges({ id: processFilterIdChange });
fixture.detectChanges();
await fixture.whenStable();
const restoreDefaultProcessFiltersSpy = spyOn(service, 'getProcessFilters').and.returnValue(of([fakeFilter]));
const resetDefaultsFilterSpy = spyOn(service, 'resetProcessFilterToDefaults').and.returnValue(of([fakeFilter]));
const resetDefaultsEmitSpy: jasmine.Spy = spyOn(component.action, 'emit');
const expansionPanel = fixture.debugElement.nativeElement.querySelector('mat-expansion-panel-header');
expansionPanel.click();
fixture.detectChanges();
const resetButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="adf-filter-action-${PROCESS_FILTER_ACTION_RESTORE}"]`);
fixture.detectChanges();
expect(resetButton.disabled).toBe(false);
resetButton.click();
fixture.detectChanges();
await fixture.whenStable();
expect(resetDefaultsFilterSpy).toHaveBeenCalledWith(fakeFilter.appName, component.processFilter);
expect(restoreDefaultProcessFiltersSpy).toHaveBeenCalledWith(fakeFilter.appName);
expect(resetDefaultsEmitSpy).toHaveBeenCalledWith(expectedAction);
});
it('should display default filter actions when input is empty', async () => {
component.toggleFilterActions = true;
component.actions = [];
@ -1054,7 +1132,7 @@ describe('EditProcessFilterCloudComponent', () => {
it('should not call restore default filters service on deletion first filter', (done) => {
component.toggleFilterActions = true;
const deleteFilterSpy = spyOn(service, 'deleteFilter').and.returnValue(of([new ProcessFilterCloudModel({ name: 'mock-filter-name'})]));
const deleteFilterSpy = spyOn(service, 'deleteFilter').and.returnValue(of([new ProcessFilterCloudModel({ name: 'mock-filter-name' })]));
const restoreFiltersSpy = spyOn(component, 'restoreDefaultProcessFilters').and.returnValue(of([]));
const deleteSpy: jasmine.Spy = spyOn(component.action, 'emit');
fixture.detectChanges();
@ -1087,7 +1165,7 @@ describe('EditProcessFilterCloudComponent', () => {
component.ngOnChanges({ id: processFilterIdChange });
fixture.detectChanges();
expect(component.initiatorOptions).toEqual([ { username: 'user1' }, { username: 'user2'} ]);
expect(component.initiatorOptions).toEqual([{ username: 'user1' }, { username: 'user2' }]);
});
});
});

View File

@ -19,7 +19,7 @@ import { Component, Input, Output, EventEmitter, OnInit, OnChanges, SimpleChange
import { FormGroup, FormBuilder, AbstractControl } from '@angular/forms';
import { DateAdapter } from '@angular/material/core';
import { MatDialog } from '@angular/material/dialog';
import { debounceTime, filter, takeUntil, finalize, switchMap } from 'rxjs/operators';
import { debounceTime, filter, takeUntil, finalize, switchMap, tap } from 'rxjs/operators';
import { Subject, Observable, Subscription } from 'rxjs';
import moment from 'moment-es6';
import { Moment } from 'moment';
@ -34,6 +34,8 @@ import { DateCloudFilterType, DateRangeFilter } from '../../../models/date-cloud
export const PROCESS_FILTER_ACTION_SAVE = 'save';
export const PROCESS_FILTER_ACTION_SAVE_AS = 'saveAs';
export const PROCESS_FILTER_ACTION_DELETE = 'delete';
export const PROCESS_FILTER_ACTION_SAVE_DEFAULT = 'saveDefaultFilter';
export const PROCESS_FILTER_ACTION_RESTORE = 'restoreDefaultFilter';
const DEFAULT_PROCESS_FILTER_PROPERTIES = ['status', 'sort', 'order', 'lastModified'];
const DEFAULT_SORT_PROPERTIES = ['id', 'name', 'status', 'startDate'];
const DEFAULT_ACTIONS = ['save', 'saveAs', 'delete'];
@ -82,7 +84,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
@Input()
showTitle = true;
/** Toggles the appearance of the process filter name . */
/** Toggles the appearance of the process filter name . */
@Input()
showProcessFilterName = true;
@ -272,7 +274,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
if (this.filterProperties.includes('initiator')) {
this.initiatorOptions = !!this.processFilter.initiator
? this.processFilter.initiator.split(',').map( username => Object.assign({}, { username }))
? this.processFilter.initiator.split(',').map(username => Object.assign({}, { username }))
: [];
}
@ -377,7 +379,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
}
onChangedUser(users: IdentityUserModel[], processProperty: ProcessFilterProperties) {
this.getPropertyController(processProperty).setValue(users.map( user => user.username).join(','));
this.getPropertyController(processProperty).setValue(users.map(user => user.username).join(','));
}
hasError(property: ProcessFilterProperties): boolean {
@ -416,14 +418,19 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
});
}
executeFilterActions(action: ProcessFilterAction): void {
executeFilterActions(event: Event, action: ProcessFilterAction): void {
if (action.actionType === PROCESS_FILTER_ACTION_SAVE) {
this.save(action);
} else if (action.actionType === PROCESS_FILTER_ACTION_SAVE_AS) {
this.saveAs(action);
} else if (action.actionType === PROCESS_FILTER_ACTION_DELETE) {
this.delete(action);
} else if (action.actionType === PROCESS_FILTER_ACTION_SAVE_DEFAULT) {
this.save(action);
} else if (action.actionType === PROCESS_FILTER_ACTION_RESTORE) {
this.reset(action);
}
event.stopPropagation();
}
save(saveAction: ProcessFilterAction) {
@ -449,7 +456,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
return filters.length === 0;
}),
switchMap(() => this.restoreDefaultProcessFilters()))
.subscribe(() => {});
.subscribe(() => { });
}
/**
@ -485,6 +492,16 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
});
}
reset(resetAction: ProcessFilterAction) {
this.processFilterCloudService.resetProcessFilterToDefaults(this.appName, this.processFilter).pipe(
tap((filters: ProcessFilterCloudModel[]) => {
resetAction.filter = filters.find(defaultFilter => defaultFilter.name === this.processFilter.name) || this.processFilter;
this.action.emit(resetAction);
}),
switchMap(() => this.restoreDefaultProcessFilters()))
.subscribe(() => { });
}
/**
* Return filter name
*
@ -558,6 +575,16 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
actionType: PROCESS_FILTER_ACTION_DELETE,
icon: 'delete',
tooltip: 'ADF_CLOUD_EDIT_PROCESS_FILTER.TOOL_TIP.DELETE'
},
{
actionType: PROCESS_FILTER_ACTION_SAVE_DEFAULT,
icon: 'adf:save',
tooltip: 'ADF_CLOUD_EDIT_PROCESS_FILTER.TOOL_TIP.SAVE'
},
{
actionType: PROCESS_FILTER_ACTION_RESTORE,
icon: 'settings_backup_restore',
tooltip: 'ADF_CLOUD_EDIT_PROCESS_FILTER.TOOL_TIP.RESTORE'
}
];
}
@ -772,7 +799,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.COMPLETED_DATE',
type: 'date-range',
key: 'completedDateRange',
attributes: { dateType: 'completedDateType', from: '_completedFrom', to: '_completedTo'},
attributes: { dateType: 'completedDateType', from: '_completedFrom', to: '_completedTo' },
value: {
completedDateType: filterModel.completedDateType || null,
_completedFrom: filterModel.completedFrom || null,
@ -783,7 +810,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.STARTED_DATE',
type: 'date-range',
key: 'startedDateRange',
attributes: { dateType: 'startedDateType', from: '_startFrom', to: '_startTo'},
attributes: { dateType: 'startedDateType', from: '_startFrom', to: '_startTo' },
value: {
startedDateType: filterModel.startedDateType || null,
_startFrom: filterModel.startFrom || null,
@ -794,7 +821,7 @@ export class EditProcessFilterCloudComponent implements OnInit, OnChanges, OnDes
label: 'ADF_CLOUD_EDIT_PROCESS_FILTER.LABEL.SUSPENDED_DATE',
type: 'date-range',
key: 'suspendedDateRange',
attributes: { dateType: 'suspendedDateType', from: '_suspendedFrom', to: '_suspendedTo'},
attributes: { dateType: 'suspendedDateType', from: '_suspendedFrom', to: '_suspendedTo' },
value: {
suspendedDateType: filterModel.suspendedDateType || null,
_suspendedFrom: filterModel.suspendedFrom || null,

View File

@ -24,6 +24,7 @@ import { LocalPreferenceCloudService } from '../../../services/local-preference-
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { fakeEmptyProcessCloudFilterEntries, fakeProcessCloudFilterEntries, fakeProcessCloudFilters, fakeProcessCloudFilterWithDifferentEntries, fakeProcessFilter } from '../mock/process-filters-cloud.mock';
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
describe('ProcessFilterCloudService', () => {
let service: ProcessFilterCloudService;
@ -216,4 +217,14 @@ describe('ProcessFilterCloudService', () => {
expect(service.isDefaultFilter(defaultFilterName)).toBe(true);
expect(service.isDefaultFilter(fakeFilterName)).toBe(false);
});
it('should reset filters to default values', async () => {
const changedFilter = new ProcessFilterCloudModel(fakeProcessCloudFilters[0]);
changedFilter.processDefinitionKey = 'modifiedProcessDefinitionKey';
spyOn<any>(service, 'defaultProcessFilters').and.returnValue(fakeProcessCloudFilters);
await service.resetProcessFilterToDefaults('mock-appName', changedFilter).toPromise();
expect(updatePreferenceSpy).toHaveBeenCalledWith('mock-appName', 'process-filters-mock-appName-mock-username', fakeProcessCloudFilters);
});
});

View File

@ -143,7 +143,7 @@ export class ProcessFilterCloudService {
}),
map((filters: ProcessFilterCloudModel[]) => filters.filter((filter: ProcessFilterCloudModel) => filter.id === id)[0]),
catchError((err) => this.handleProcessError(err))
);
);
}
/**
@ -241,6 +241,21 @@ export class ProcessFilterCloudService {
return defaultFilters.findIndex((filter) => filterName === filter.name) !== -1;
}
/**
* Reset the process filters to the default configuration if it exists and stores it.
* If there is no default configuration for the process cloud filter with the provided filter name,
* then it changes nothing but stores the current values of the filter
*
* @param appName Name of the target app
* @param filter The process filter to be restored to defaults
* @returns Observable of process filters details
*/
resetProcessFilterToDefaults(appName: string, filter: ProcessFilterCloudModel): Observable<ProcessFilterCloudModel[]> {
const defaultFilter = this.defaultProcessFilters(appName).find(defaultFilterDefinition => defaultFilterDefinition.name === filter.name) || filter;
defaultFilter.id = filter.id;
return this.updateFilter(defaultFilter);
}
/**
* Checks user preference are empty or not
*