mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
AAE-25271 Refresh button for processes (#10211)
* [AAE-25271] display process notifications * [AAE-25271] removed unnecessary conditional * [AAE-25271] replaced observable with promise * [AAE-25271] replaced observables with promises * [AAE-25271] updated unit tests * [AAE-25271] refactored tests to avoid circular dependency error * [AAE-25271] replaced automatic imports
This commit is contained in:
@@ -15,13 +15,18 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { NoopTranslateModule } from '@alfresco/adf-core';
|
||||
import { AppListCloudModule } from './app-list-cloud.module';
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
|
||||
describe('AppListCloudModule', () => {
|
||||
let appListCloudModule: AppListCloudModule;
|
||||
|
||||
beforeEach(() => {
|
||||
appListCloudModule = new AppListCloudModule();
|
||||
TestBed.configureTestingModule({
|
||||
imports: [AppListCloudModule, NoopTranslateModule]
|
||||
});
|
||||
appListCloudModule = TestBed.inject(AppListCloudModule);
|
||||
});
|
||||
|
||||
it('should create an instance', () => {
|
||||
|
@@ -15,37 +15,53 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { AlfrescoApiService } from '@alfresco/adf-content-services';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||
import { MatDialog } from '@angular/material/dialog';
|
||||
import { ADF_DATE_FORMATS, FullNamePipe, NoopTranslateModule, UserPreferencesService } from '@alfresco/adf-core';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { SimpleChange } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { ReactiveFormsModule } from '@angular/forms';
|
||||
import { DateFnsAdapter } from '@angular/material-date-fns-adapter';
|
||||
import { MatAutocompleteModule } from '@angular/material/autocomplete';
|
||||
import { MatChipsModule } from '@angular/material/chips';
|
||||
import { DateAdapter, MAT_DATE_FORMATS, MAT_DATE_LOCALE } from '@angular/material/core';
|
||||
import { MatDatepickerModule } from '@angular/material/datepicker';
|
||||
import { MatDialog, MatDialogModule } from '@angular/material/dialog';
|
||||
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
|
||||
import { MatFormFieldModule } from '@angular/material/form-field';
|
||||
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||
import { MatInputModule } from '@angular/material/input';
|
||||
import { MatProgressBarModule } from '@angular/material/progress-bar';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { MatSelectModule } from '@angular/material/select';
|
||||
import { MatSelectHarness } from '@angular/material/select/testing';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { endOfDay, format, isValid, startOfDay, subYears } from 'date-fns';
|
||||
import { enUS } from 'date-fns/locale';
|
||||
import { of } from 'rxjs';
|
||||
import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component';
|
||||
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
|
||||
import { DateRangeFilterComponent } from '../../../common/date-range-filter/date-range-filter.component';
|
||||
import { fakeEnvironmentList } from '../../../common/mock/environment.mock';
|
||||
import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
|
||||
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
|
||||
import { PeopleCloudComponent } from '../../../people/components/people-cloud.component';
|
||||
import { IdentityUserServiceMock } from '../../../people/mock/people-cloud.mock';
|
||||
import { IDENTITY_USER_SERVICE_TOKEN } from '../../../people/services/identity-user-service.token';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { ProcessCloudService } from '../../services/process-cloud.service';
|
||||
import { mockAppVersions } from '../mock/process-filters-cloud.mock';
|
||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
||||
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from './../../../app/mock/app-model.mock';
|
||||
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';
|
||||
import { AppsProcessCloudService } from '../../../app/services/apps-process-cloud.service';
|
||||
import { fakeApplicationInstance, fakeApplicationInstanceWithEnvironment } from './../../../app/mock/app-model.mock';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||
import { ProcessCloudService } from '../../services/process-cloud.service';
|
||||
import { DateCloudFilterType } from '../../../models/date-cloud-filter.model';
|
||||
import { MatIconTestingModule } from '@angular/material/icon/testing';
|
||||
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
|
||||
import { mockAppVersions } from '../mock/process-filters-cloud.mock';
|
||||
import { fakeEnvironmentList } from '../../../common/mock/environment.mock';
|
||||
import { endOfDay, format, startOfDay, subYears, isValid } from 'date-fns';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatSelectHarness } from '@angular/material/select/testing';
|
||||
import { MatExpansionPanelHarness } from '@angular/material/expansion/testing';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
import { ProcessFilterDialogCloudComponent } from './process-filter-dialog-cloud.component';
|
||||
|
||||
describe('EditProcessFilterCloudComponent', () => {
|
||||
let loader: HarnessLoader;
|
||||
@@ -59,6 +75,7 @@ describe('EditProcessFilterCloudComponent', () => {
|
||||
let getRunningApplicationsSpy: jasmine.Spy;
|
||||
let getProcessFilterByIdSpy: jasmine.Spy;
|
||||
let alfrescoApiService: AlfrescoApiService;
|
||||
let userPreferencesService: UserPreferencesService;
|
||||
|
||||
const fakeFilter = new ProcessFilterCloudModel({
|
||||
name: 'FakeRunningProcess',
|
||||
@@ -83,8 +100,30 @@ describe('EditProcessFilterCloudComponent', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessFiltersCloudModule, ProcessServiceCloudTestingModule, MatIconTestingModule],
|
||||
providers: [MatDialog, { provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
|
||||
imports: [
|
||||
MatIconTestingModule,
|
||||
MatDialogModule,
|
||||
NoopTranslateModule,
|
||||
NoopAnimationsModule,
|
||||
MatSelectModule,
|
||||
MatDatepickerModule,
|
||||
MatAutocompleteModule,
|
||||
FullNamePipe,
|
||||
MatFormFieldModule,
|
||||
MatInputModule,
|
||||
ReactiveFormsModule,
|
||||
MatChipsModule,
|
||||
MatProgressBarModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||
{ provide: MAT_DATE_LOCALE, useValue: enUS },
|
||||
{ provide: DateAdapter, useClass: DateFnsAdapter },
|
||||
{ provide: NotificationCloudService, useValue: { makeGQLQuery: () => of([]) } },
|
||||
{ provide: MAT_DATE_FORMATS, useValue: ADF_DATE_FORMATS },
|
||||
{ provide: IDENTITY_USER_SERVICE_TOKEN, useExisting: IdentityUserServiceMock }
|
||||
],
|
||||
declarations: [PeopleCloudComponent, DateRangeFilterComponent]
|
||||
});
|
||||
fixture = TestBed.createComponent(EditProcessFilterCloudComponent);
|
||||
component = fixture.componentInstance;
|
||||
@@ -93,7 +132,9 @@ describe('EditProcessFilterCloudComponent', () => {
|
||||
appsService = TestBed.inject(AppsProcessCloudService);
|
||||
processService = TestBed.inject(ProcessCloudService);
|
||||
alfrescoApiService = TestBed.inject(AlfrescoApiService);
|
||||
userPreferencesService = TestBed.inject(UserPreferencesService);
|
||||
dialog = TestBed.inject(MatDialog);
|
||||
|
||||
spyOn(dialog, 'open').and.returnValue({
|
||||
afterClosed: () =>
|
||||
of({
|
||||
@@ -105,6 +146,7 @@ describe('EditProcessFilterCloudComponent', () => {
|
||||
getProcessFilterByIdSpy = spyOn(service, 'getFilterById').and.returnValue(of(fakeFilter));
|
||||
getRunningApplicationsSpy = spyOn(appsService, 'getDeployedApplicationsByStatus').and.returnValue(of(fakeApplicationInstance));
|
||||
spyOn(alfrescoApiService, 'getInstance').and.returnValue(mock);
|
||||
spyOn(userPreferencesService, 'select').and.returnValue(of({ localize: 'en', formatLong: {} }));
|
||||
fixture.detectChanges();
|
||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||
});
|
||||
|
@@ -9,6 +9,7 @@
|
||||
[class.adf-active]="currentFilter === filter"
|
||||
>
|
||||
<div class="adf-process-filters__entry">
|
||||
<div>
|
||||
<adf-icon
|
||||
data-automation-id="adf-filter-icon"
|
||||
*ngIf="showIcons"
|
||||
@@ -20,6 +21,15 @@
|
||||
{{ filter.name | translate }}
|
||||
</span>
|
||||
</div>
|
||||
<span
|
||||
*ngIf="counters$[filter.key]"
|
||||
[attr.data-automation-id]="filter.key + '_filter-counter'"
|
||||
class="adf-process-filters__entry-counter"
|
||||
[class.adf-active]="isFilterUpdated(filter.key)"
|
||||
>
|
||||
{{ counters$[filter.key] | async }}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
</mat-action-list>
|
||||
<ng-template #loading>
|
||||
|
@@ -6,6 +6,7 @@
|
||||
color: var(--adf-theme-foreground-text-color-054);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
flex: 1;
|
||||
height: 100%;
|
||||
gap: var(--adf-theme-spacing);
|
||||
@@ -13,6 +14,17 @@
|
||||
&:hover {
|
||||
color: var(--theme-primary-color);
|
||||
}
|
||||
|
||||
&-counter {
|
||||
padding: 0 5px;
|
||||
border-radius: 15px;
|
||||
|
||||
&.adf-active {
|
||||
background-color: var(--theme-accent-color);
|
||||
color: var(--theme-accent-color-default-contrast);
|
||||
font-size: smaller;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.adf-active {
|
||||
|
@@ -21,28 +21,46 @@ import { of, throwError } from 'rxjs';
|
||||
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
||||
import { ProcessFiltersCloudComponent } from './process-filters-cloud.component';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||
import { ProcessFiltersCloudModule } from '../process-filters-cloud.module';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||
import { mockProcessFilters } from '../mock/process-filters-cloud.mock';
|
||||
import { AppConfigService, AppConfigServiceMock, NoopTranslateModule } from '@alfresco/adf-core';
|
||||
import { ProcessListCloudService } from '../../../process/process-list/services/process-list-cloud.service';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { ApolloModule } from 'apollo-angular';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { MatListModule } from '@angular/material/list';
|
||||
|
||||
const ProcessFilterCloudServiceMock = {
|
||||
getProcessFilters: () => of(mockProcessFilters),
|
||||
getProcessNotificationSubscription: () => of([])
|
||||
};
|
||||
|
||||
describe('ProcessFiltersCloudComponent', () => {
|
||||
let processFilterService: ProcessFilterCloudService;
|
||||
let component: ProcessFiltersCloudComponent;
|
||||
let fixture: ComponentFixture<ProcessFiltersCloudComponent>;
|
||||
let getProcessFiltersSpy: jasmine.Spy;
|
||||
let getProcessNotificationSubscriptionSpy: jasmine.Spy;
|
||||
|
||||
beforeEach(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [ProcessServiceCloudTestingModule, ProcessFiltersCloudModule],
|
||||
providers: [{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }]
|
||||
imports: [NoopTranslateModule, NoopAnimationsModule, MatListModule],
|
||||
providers: [
|
||||
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
||||
{ provide: ProcessListCloudService, useValue: { getProcessCounter: () => of(10) } },
|
||||
{ provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock },
|
||||
NotificationCloudService,
|
||||
ApolloModule
|
||||
]
|
||||
});
|
||||
fixture = TestBed.createComponent(ProcessFiltersCloudComponent);
|
||||
component = fixture.componentInstance;
|
||||
|
||||
processFilterService = TestBed.inject(ProcessFilterCloudService);
|
||||
getProcessFiltersSpy = spyOn(processFilterService, 'getProcessFilters').and.returnValue(of(mockProcessFilters));
|
||||
getProcessNotificationSubscriptionSpy = spyOn(processFilterService, 'getProcessNotificationSubscription').and.returnValue(of([]));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -288,12 +306,14 @@ describe('ProcessFiltersCloudComponent', () => {
|
||||
};
|
||||
|
||||
const clickOnFilter = async (filterKey: string) => {
|
||||
fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${filterKey}_filter"]`).click();
|
||||
const button = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${filterKey}_filter"]`);
|
||||
button.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
};
|
||||
|
||||
it('should apply active CSS class on filter click', async () => {
|
||||
component.enableNotifications = true;
|
||||
component.appName = 'mock-app-name';
|
||||
const appNameChange = new SimpleChange(null, 'mock-app-name', true);
|
||||
component.ngOnChanges({ appName: appNameChange });
|
||||
@@ -301,18 +321,24 @@ describe('ProcessFiltersCloudComponent', () => {
|
||||
await fixture.whenStable();
|
||||
|
||||
await clickOnFilter(allProcessesFilterKey);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(getActiveFilterElement(allProcessesFilterKey)).toBeDefined();
|
||||
expect(getActiveFilterElement(runningProcessesFilterKey)).toBeNull();
|
||||
expect(getActiveFilterElement(completedProcessesFilterKey)).toBeNull();
|
||||
|
||||
await clickOnFilter(runningProcessesFilterKey);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(getActiveFilterElement(allProcessesFilterKey)).toBeNull();
|
||||
expect(getActiveFilterElement(runningProcessesFilterKey)).toBeDefined();
|
||||
expect(getActiveFilterElement(completedProcessesFilterKey)).toBeNull();
|
||||
|
||||
await clickOnFilter(completedProcessesFilterKey);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(getActiveFilterElement(allProcessesFilterKey)).toBeNull();
|
||||
expect(getActiveFilterElement(runningProcessesFilterKey)).toBeNull();
|
||||
@@ -345,5 +371,77 @@ describe('ProcessFiltersCloudComponent', () => {
|
||||
expect(getActiveFilterElement(runningProcessesFilterKey)).toBeNull();
|
||||
expect(getActiveFilterElement(completedProcessesFilterKey)).toBeDefined();
|
||||
});
|
||||
|
||||
it('should made sbscription', () => {
|
||||
component.enableNotifications = true;
|
||||
component.appName = 'mock-app-name';
|
||||
const appNameChange = new SimpleChange(null, 'mock-app-name', true);
|
||||
component.ngOnChanges({ appName: appNameChange });
|
||||
fixture.detectChanges();
|
||||
expect(getProcessNotificationSubscriptionSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not emit filter key when filter counter is set for first time', () => {
|
||||
component.currentFiltersValues = {};
|
||||
const fakeFilterKey = 'testKey';
|
||||
const fakeFilterValue = 10;
|
||||
const updatedFilterSpy = spyOn(component.updatedFilter, 'emit');
|
||||
component.checkIfFilterValuesHasBeenUpdated(fakeFilterKey, fakeFilterValue);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.currentFiltersValues).not.toEqual({});
|
||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(fakeFilterValue);
|
||||
expect(updatedFilterSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should not emit filter key when filter counter has not changd', () => {
|
||||
component.currentFiltersValues = {};
|
||||
const fakeFilterKey = 'testKey';
|
||||
const fakeFilterValue = 10;
|
||||
const updatedFilterSpy = spyOn(component.updatedFilter, 'emit');
|
||||
component.checkIfFilterValuesHasBeenUpdated(fakeFilterKey, fakeFilterValue);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(component.currentFiltersValues).not.toEqual({});
|
||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(fakeFilterValue);
|
||||
|
||||
component.checkIfFilterValuesHasBeenUpdated(fakeFilterKey, fakeFilterValue);
|
||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(fakeFilterValue);
|
||||
expect(updatedFilterSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit filter key when filter counter is increased', () => {
|
||||
component.currentFiltersValues = {};
|
||||
const fakeFilterKey = 'testKey';
|
||||
const updatedFilterSpy = spyOn(component.updatedFilter, 'emit');
|
||||
component.checkIfFilterValuesHasBeenUpdated(fakeFilterKey, 10);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(updatedFilterSpy).not.toHaveBeenCalledWith(fakeFilterKey);
|
||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(10);
|
||||
|
||||
component.checkIfFilterValuesHasBeenUpdated(fakeFilterKey, 20);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(updatedFilterSpy).toHaveBeenCalledWith(fakeFilterKey);
|
||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(20);
|
||||
});
|
||||
|
||||
it('should emit filter key when filter counter is decreased', () => {
|
||||
component.currentFiltersValues = {};
|
||||
const fakeFilterKey = 'testKey';
|
||||
const updatedFilterSpy = spyOn(component.updatedFilter, 'emit');
|
||||
component.checkIfFilterValuesHasBeenUpdated(fakeFilterKey, 10);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(updatedFilterSpy).not.toHaveBeenCalledWith(fakeFilterKey);
|
||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(10);
|
||||
|
||||
component.checkIfFilterValuesHasBeenUpdated(fakeFilterKey, 5);
|
||||
fixture.detectChanges();
|
||||
|
||||
expect(updatedFilterSpy).toHaveBeenCalledWith(fakeFilterKey);
|
||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(5);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@@ -19,9 +19,10 @@ import { Component, EventEmitter, Input, OnChanges, Output, SimpleChanges, OnDes
|
||||
import { Observable, Subject } from 'rxjs';
|
||||
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||
import { TranslationService } from '@alfresco/adf-core';
|
||||
import { AppConfigService, TranslationService } from '@alfresco/adf-core';
|
||||
import { FilterParamsModel } from '../../../task/task-filters/models/filter-cloud.model';
|
||||
import { takeUntil } from 'rxjs/operators';
|
||||
import { debounceTime, takeUntil, tap } from 'rxjs/operators';
|
||||
import { ProcessListCloudService } from '../../../process/process-list/services/process-list-cloud.service';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-process-filters',
|
||||
@@ -58,19 +59,31 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
|
||||
/** Emitted when filter is updated. */
|
||||
@Output()
|
||||
updatedFilter: EventEmitter<string> = new EventEmitter<string>();
|
||||
|
||||
filters$: Observable<ProcessFilterCloudModel[]>;
|
||||
currentFilter?: ProcessFilterCloudModel;
|
||||
filters: ProcessFilterCloudModel[] = [];
|
||||
counters$: { [key: string]: Observable<number> } = {};
|
||||
enableNotifications: boolean;
|
||||
currentFiltersValues: { [key: string]: number } = {};
|
||||
updatedFiltersSet = new Set<string>();
|
||||
|
||||
private onDestroy$ = new Subject<boolean>();
|
||||
|
||||
private readonly processFilterCloudService = inject(ProcessFilterCloudService);
|
||||
private readonly translationService = inject(TranslationService);
|
||||
private readonly appConfigService = inject(AppConfigService);
|
||||
private readonly processListCloudService = inject(ProcessListCloudService);
|
||||
|
||||
ngOnInit() {
|
||||
this.enableNotifications = this.appConfigService.get('notifications', true);
|
||||
if (this.appName === '') {
|
||||
this.getFilters(this.appName);
|
||||
}
|
||||
this.initProcessNotification();
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
@@ -97,6 +110,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
|
||||
this.filters = res || [];
|
||||
this.selectFilterAndEmit(this.filterParam);
|
||||
this.success.emit(res);
|
||||
this.updateFilterCounters();
|
||||
},
|
||||
error: (err: any) => {
|
||||
this.error.emit(err);
|
||||
@@ -172,6 +186,8 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
|
||||
if (filter) {
|
||||
this.selectFilter(filter);
|
||||
this.filterClicked.emit(this.currentFilter);
|
||||
this.updateFilterCounter(this.currentFilter);
|
||||
this.updatedFiltersSet.delete(filter.key);
|
||||
} else {
|
||||
this.currentFilter = undefined;
|
||||
}
|
||||
@@ -220,4 +236,47 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
|
||||
isActiveFilter(filter: ProcessFilterCloudModel): boolean {
|
||||
return this.currentFilter.name === filter.name;
|
||||
}
|
||||
|
||||
initProcessNotification(): void {
|
||||
if (this.appName && this.enableNotifications) {
|
||||
this.processFilterCloudService
|
||||
.getProcessNotificationSubscription(this.appName)
|
||||
.pipe(debounceTime(1000), takeUntil(this.onDestroy$))
|
||||
.subscribe(() => {
|
||||
this.updateFilterCounters();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
updateFilterCounters(): void {
|
||||
this.filters.forEach((filter: ProcessFilterCloudModel) => {
|
||||
if (filter?.status) {
|
||||
this.updateFilterCounter(filter);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updateFilterCounter(filter: ProcessFilterCloudModel): void {
|
||||
this.counters$[filter.key] = this.processListCloudService.getProcessCounter(filter.appName, filter.status).pipe(
|
||||
tap((filterCounter) => {
|
||||
this.checkIfFilterValuesHasBeenUpdated(filter.key, filterCounter);
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
checkIfFilterValuesHasBeenUpdated(filterKey: string, filterValue: number): void {
|
||||
if (!this.currentFiltersValues[filterKey]) {
|
||||
this.currentFiltersValues[filterKey] = filterValue;
|
||||
return;
|
||||
}
|
||||
if (this.currentFiltersValues[filterKey] !== filterValue) {
|
||||
this.currentFiltersValues[filterKey] = filterValue;
|
||||
this.updatedFilter.emit(filterKey);
|
||||
this.updatedFiltersSet.add(filterKey);
|
||||
}
|
||||
}
|
||||
|
||||
isFilterUpdated(filterName: string): boolean {
|
||||
return this.updatedFiltersSet.has(filterName);
|
||||
}
|
||||
}
|
||||
|
@@ -186,3 +186,25 @@ const mockAppVersion2: ApplicationVersionModel = {
|
||||
};
|
||||
|
||||
export const mockAppVersions = [mockAppVersion1, mockAppVersion2];
|
||||
|
||||
export const processNotifications = [
|
||||
{
|
||||
eventType: 'PROCESS_CREATED',
|
||||
entity: {
|
||||
appVersion: '1',
|
||||
id: 'bccc1217-7036-11ef-86f2-bae4749e773e',
|
||||
processDefinitionId: 'Process_XmWTFMqf:1:1b30709b-6ff3-11ef-86f2-bae4749e773e',
|
||||
processDefinitionKey: 'Process_XmWTFMqf',
|
||||
initiator: 'hruser',
|
||||
status: 'CREATED',
|
||||
processDefinitionVersion: 1,
|
||||
processDefinitionName: 'processchild'
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
export const processCloudEngineEventsMock = {
|
||||
data: {
|
||||
engineEvents: processNotifications
|
||||
}
|
||||
};
|
||||
|
@@ -16,7 +16,7 @@
|
||||
*/
|
||||
|
||||
import { TestBed } from '@angular/core/testing';
|
||||
import { of } from 'rxjs';
|
||||
import { firstValueFrom, of } from 'rxjs';
|
||||
import { ProcessFilterCloudService } from './process-filter-cloud.service';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||
@@ -26,10 +26,12 @@ import {
|
||||
fakeProcessCloudFilterEntries,
|
||||
fakeProcessCloudFilters,
|
||||
fakeProcessCloudFilterWithDifferentEntries,
|
||||
fakeProcessFilter
|
||||
fakeProcessFilter,
|
||||
processCloudEngineEventsMock
|
||||
} from '../mock/process-filters-cloud.mock';
|
||||
import { ProcessFilterCloudModel } from '../models/process-filter-cloud.model';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
|
||||
describe('ProcessFilterCloudService', () => {
|
||||
let service: ProcessFilterCloudService;
|
||||
@@ -38,6 +40,7 @@ describe('ProcessFilterCloudService', () => {
|
||||
let updatePreferenceSpy: jasmine.Spy;
|
||||
let createPreferenceSpy: jasmine.Spy;
|
||||
let getCurrentUserInfoSpy: jasmine.Spy;
|
||||
let notificationCloudService: NotificationCloudService;
|
||||
|
||||
const identityUserMock = {
|
||||
username: 'mock-username',
|
||||
@@ -55,6 +58,7 @@ describe('ProcessFilterCloudService', () => {
|
||||
|
||||
const preferenceCloudService = TestBed.inject(PROCESS_FILTERS_SERVICE_TOKEN);
|
||||
const identityUserService = TestBed.inject(IdentityUserService);
|
||||
notificationCloudService = TestBed.inject(NotificationCloudService);
|
||||
|
||||
createPreferenceSpy = spyOn(preferenceCloudService, 'createPreference').and.returnValue(of(fakeProcessCloudFilters));
|
||||
updatePreferenceSpy = spyOn(preferenceCloudService, 'updatePreference').and.returnValue(of(fakeProcessCloudFilters));
|
||||
@@ -236,4 +240,13 @@ describe('ProcessFilterCloudService', () => {
|
||||
|
||||
expect(updatePreferenceSpy).toHaveBeenCalledWith('mock-appName', 'process-filters-mock-appName-mock-username', fakeProcessCloudFilters);
|
||||
});
|
||||
|
||||
it('should return engine event task subscription', async () => {
|
||||
spyOn(notificationCloudService, 'makeGQLQuery').and.returnValue(of(processCloudEngineEventsMock));
|
||||
|
||||
const result = await firstValueFrom(service.getProcessNotificationSubscription('testApp'));
|
||||
expect(result.length).toBe(1);
|
||||
expect(result[0].eventType).toBe('PROCESS_CREATED');
|
||||
expect(result[0].entity.status).toBe('CREATED');
|
||||
});
|
||||
});
|
||||
|
@@ -22,6 +22,25 @@ import { switchMap, map } from 'rxjs/operators';
|
||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||
import { IdentityUserService } from '../../../people/services/identity-user.service';
|
||||
import { NotificationCloudService } from '../../../services/notification-cloud.service';
|
||||
import { TaskCloudEngineEvent } from '../../../models/engine-event-cloud.model';
|
||||
|
||||
const PROCESS_EVENT_SUBSCRIPTION_QUERY = `
|
||||
subscription {
|
||||
engineEvents(eventType: [
|
||||
PROCESS_CANCELLED
|
||||
PROCESS_COMPLETED
|
||||
PROCESS_CREATED
|
||||
PROCESS_RESUMED
|
||||
PROCESS_SUSPENDED
|
||||
PROCESS_STARTED
|
||||
]) {
|
||||
eventType
|
||||
entity
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root'
|
||||
})
|
||||
@@ -31,6 +50,7 @@ export class ProcessFilterCloudService {
|
||||
|
||||
private readonly preferenceService = inject<PreferenceCloudServiceInterface>(PROCESS_FILTERS_SERVICE_TOKEN);
|
||||
private readonly identityUserService = inject(IdentityUserService);
|
||||
private readonly notificationCloudService = inject(NotificationCloudService);
|
||||
|
||||
constructor() {
|
||||
this.filtersSubject = new BehaviorSubject([]);
|
||||
@@ -377,4 +397,10 @@ export class ProcessFilterCloudService {
|
||||
})
|
||||
];
|
||||
}
|
||||
|
||||
getProcessNotificationSubscription(appName: string): Observable<TaskCloudEngineEvent[]> {
|
||||
return this.notificationCloudService
|
||||
.makeGQLQuery(appName, PROCESS_EVENT_SUBSCRIPTION_QUERY)
|
||||
.pipe(map((events: any) => events?.data?.engineEvents));
|
||||
}
|
||||
}
|
||||
|
@@ -39,7 +39,7 @@ import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud
|
||||
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
|
||||
import { PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data';
|
||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||
import { PreferenceCloudServiceInterface } from '@alfresco/adf-process-services-cloud';
|
||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
|
||||
|
@@ -20,6 +20,7 @@ import { ProcessListCloudService } from './process-list-cloud.service';
|
||||
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
|
||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||
import { AdfHttpClient } from '@alfresco/adf-core/api';
|
||||
import { firstValueFrom } from 'rxjs';
|
||||
|
||||
describe('ProcessListCloudService', () => {
|
||||
let service: ProcessListCloudService;
|
||||
@@ -36,9 +37,7 @@ describe('ProcessListCloudService', () => {
|
||||
|
||||
beforeEach(fakeAsync(() => {
|
||||
TestBed.configureTestingModule({
|
||||
imports: [
|
||||
ProcessServiceCloudTestingModule
|
||||
]
|
||||
imports: [ProcessServiceCloudTestingModule]
|
||||
});
|
||||
adfHttpClient = TestBed.inject(AdfHttpClient);
|
||||
service = TestBed.inject(ProcessListCloudService);
|
||||
@@ -71,8 +70,14 @@ describe('ProcessListCloudService', () => {
|
||||
|
||||
it('should concat the sorting to append as parameters', (done) => {
|
||||
const processRequest = {
|
||||
appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service',
|
||||
sorting: [{ orderBy: 'NAME', direction: 'DESC' }, { orderBy: 'TITLE', direction: 'ASC' }]
|
||||
appName: 'fakeName',
|
||||
skipCount: 0,
|
||||
maxItems: 20,
|
||||
service: 'fake-service',
|
||||
sorting: [
|
||||
{ orderBy: 'NAME', direction: 'DESC' },
|
||||
{ orderBy: 'TITLE', direction: 'ASC' }
|
||||
]
|
||||
} as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
service.getProcessByRequest(processRequest).subscribe((res) => {
|
||||
@@ -87,7 +92,7 @@ describe('ProcessListCloudService', () => {
|
||||
const processRequest = { appName: null } as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallUrl);
|
||||
service.getProcessByRequest(processRequest).subscribe(
|
||||
() => { },
|
||||
() => {},
|
||||
(error) => {
|
||||
expect(error).toBe('Appname not configured');
|
||||
done();
|
||||
@@ -95,8 +100,19 @@ describe('ProcessListCloudService', () => {
|
||||
);
|
||||
});
|
||||
|
||||
describe('getAdminProcessRequest', () => {
|
||||
it('should return number of total items of processes ', async () => {
|
||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 1, service: 'fake-service' } as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
const result = await firstValueFrom(service.getProcessByRequest(processRequest));
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result).not.toBeNull();
|
||||
expect(result.skipCount).toBe(0);
|
||||
expect(result.maxItems).toBe(1);
|
||||
expect(result.service).toBe('fake-service');
|
||||
});
|
||||
|
||||
describe('getAdminProcessRequest', () => {
|
||||
it('should append to the call all the parameters', async () => {
|
||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' } as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
@@ -121,8 +137,14 @@ describe('ProcessListCloudService', () => {
|
||||
|
||||
it('should concat the sorting to append as parameters', async () => {
|
||||
const processRequest = {
|
||||
appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service',
|
||||
sorting: [{ orderBy: 'NAME', direction: 'DESC' }, { orderBy: 'TITLE', direction: 'ASC' }]
|
||||
appName: 'fakeName',
|
||||
skipCount: 0,
|
||||
maxItems: 20,
|
||||
service: 'fake-service',
|
||||
sorting: [
|
||||
{ orderBy: 'NAME', direction: 'DESC' },
|
||||
{ orderBy: 'TITLE', direction: 'ASC' }
|
||||
]
|
||||
} as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
const request = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
@@ -140,7 +162,7 @@ describe('ProcessListCloudService', () => {
|
||||
await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
|
||||
fail('Should have thrown error');
|
||||
} catch(error) {
|
||||
} catch (error) {
|
||||
expect(error).toBe('Appname not configured');
|
||||
}
|
||||
});
|
||||
@@ -155,7 +177,13 @@ describe('ProcessListCloudService', () => {
|
||||
});
|
||||
|
||||
it('should not have variable keys as part of query parameters', async () => {
|
||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service', variableKeys: ['test-one', 'test-two'] } as ProcessQueryCloudRequestModel;
|
||||
const processRequest = {
|
||||
appName: 'fakeName',
|
||||
skipCount: 0,
|
||||
maxItems: 20,
|
||||
service: 'fake-service',
|
||||
variableKeys: ['test-one', 'test-two']
|
||||
} as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallQueryParameters);
|
||||
const requestParams = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
|
||||
@@ -165,7 +193,13 @@ describe('ProcessListCloudService', () => {
|
||||
});
|
||||
|
||||
it('should send right variable keys as post body', async () => {
|
||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service', variableKeys: ['test-one', 'test-two'] } as ProcessQueryCloudRequestModel;
|
||||
const processRequest = {
|
||||
appName: 'fakeName',
|
||||
skipCount: 0,
|
||||
maxItems: 20,
|
||||
service: 'fake-service',
|
||||
variableKeys: ['test-one', 'test-two']
|
||||
} as ProcessQueryCloudRequestModel;
|
||||
requestSpy.and.callFake(returnCallBody);
|
||||
const requestBodyParams = await service.getAdminProcessByRequest(processRequest).toPromise();
|
||||
|
||||
|
@@ -67,6 +67,48 @@ export class ProcessListCloudService extends BaseCloudService {
|
||||
return this.getProcess(callback, defaultQueryUrl, requestNode, queryUrl);
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a process using an object with optional query properties.
|
||||
*
|
||||
* @param appName app name
|
||||
* @param status filter status
|
||||
* @returns Total items
|
||||
*/
|
||||
getProcessCounter(appName: string, status: string): Observable<any> {
|
||||
const callback = (url: string, queryParams: any) => this.get(url, queryParams);
|
||||
let queryUrl: string;
|
||||
const defaultQueryUrl = 'query/v1/process-instances';
|
||||
const requestNode: ProcessQueryCloudRequestModel = {
|
||||
appName,
|
||||
appVersion: '',
|
||||
initiator: null,
|
||||
id: '',
|
||||
name: null,
|
||||
processDefinitionId: '',
|
||||
processDefinitionName: null,
|
||||
processDefinitionKey: '',
|
||||
status,
|
||||
businessKey: '',
|
||||
startFrom: null,
|
||||
startTo: null,
|
||||
completedFrom: null,
|
||||
completedTo: null,
|
||||
suspendedFrom: null,
|
||||
suspendedTo: null,
|
||||
completedDate: '',
|
||||
maxItems: 1,
|
||||
skipCount: 0,
|
||||
sorting: [
|
||||
{
|
||||
orderBy: 'startDate',
|
||||
direction: 'DESC'
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
return this.getProcess(callback, defaultQueryUrl, requestNode, queryUrl).pipe(map((tasks) => tasks?.list?.pagination?.totalItems));
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds a process using an object with optional query properties in admin app.
|
||||
*
|
||||
|
@@ -49,7 +49,8 @@ import { ProcessServiceCloudTestingModule } from '../../../testing/process-servi
|
||||
import { ProcessNameCloudPipe } from '../../../pipes/process-name-cloud.pipe';
|
||||
import { ProcessInstanceCloud } from '../models/process-instance-cloud.model';
|
||||
import { ESCAPE } from '@angular/cdk/keycodes';
|
||||
import { ProcessDefinitionCloud, TaskVariableCloud } from '@alfresco/adf-process-services-cloud';
|
||||
import { ProcessDefinitionCloud } from '../../../models/process-definition-cloud.model';
|
||||
import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model';
|
||||
import { first } from 'rxjs/operators';
|
||||
import { HarnessLoader } from '@angular/cdk/testing';
|
||||
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
|
||||
|
@@ -52,7 +52,7 @@ export class TaskFiltersCloudComponent extends BaseTaskFiltersCloudComponent imp
|
||||
filters: TaskFilterCloudModel[] = [];
|
||||
currentFilter: TaskFilterCloudModel;
|
||||
enableNotifications: boolean;
|
||||
currentFiltersValues = {};
|
||||
currentFiltersValues: { [key: string]: number } = {};
|
||||
|
||||
private readonly taskFilterCloudService = inject(TaskFilterCloudService);
|
||||
private readonly translationService = inject(TranslationService);
|
||||
|
@@ -15,7 +15,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { TaskDetailsCloudModel } from '@alfresco/adf-process-services-cloud';
|
||||
import { TaskDetailsCloudModel } from '../../../task/start-task/models/task-details-cloud.model';
|
||||
import { assignedTaskDetailsCloudMock } from '../../task-header/mocks/task-details-cloud.mock';
|
||||
import { TaskFilterCloudModel, ServiceTaskFilterCloudModel, AssignmentType, TaskStatusFilter } from '../models/filter-cloud.model';
|
||||
|
||||
|
Reference in New Issue
Block a user