mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
AAE-27327 New process search API (#10365)
* AAE-27327 New process search API * fix lint issue
This commit is contained in:
parent
f07636e297
commit
3bcaeef682
@ -80,6 +80,10 @@ when the process list is empty:
|
|||||||
| stickyHeader | `boolean` | false | Toggles the sticky header mode. |
|
| stickyHeader | `boolean` | false | Toggles the sticky header mode. |
|
||||||
| suspendedFrom | `string` | "" | Filter the processes. Display only process with suspendedFrom equal to the supplied date. |
|
| suspendedFrom | `string` | "" | Filter the processes. Display only process with suspendedFrom equal to the supplied date. |
|
||||||
| suspendedTo | `string` | "" | Filter the processes. Display only process with suspendedTo equal to the supplied date. |
|
| suspendedTo | `string` | "" | Filter the processes. Display only process with suspendedTo equal to the supplied date. |
|
||||||
|
| names | `string[]` | [] | Filter the processes. Display only processes with names matching any of the supplied strings. This input will be used only if `PROCESS_SEARCH_API_METHOD_TOKEN` is provided with `POST` value. |
|
||||||
|
initiators | `string[]` | [] | Filter the processes. Display only processes started by any of the users whose usernames are present in the array. This input will be used only if `PROCESS_SEARCH_API_METHOD_TOKEN` is provided with `POST` value. |
|
||||||
|
| appVersions | `string[]` | [] | Filter the processes. Display only processes present in any of the specified app versions. This input will be used only if `PROCESS_SEARCH_API_METHOD_TOKEN` is provided with `POST` value. |
|
||||||
|
| statuses | `string[]` | [] | Filter the processes. Display only processes with provided statuses. This input will be used only if `PROCESS_SEARCH_API_METHOD_TOKEN` is provided with `POST` value. |
|
||||||
|
|
||||||
### Events
|
### Events
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ import { of, throwError } from 'rxjs';
|
|||||||
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
import { ProcessFilterCloudService } from '../services/process-filter-cloud.service';
|
||||||
import { ProcessFiltersCloudComponent } from './process-filters-cloud.component';
|
import { ProcessFiltersCloudComponent } from './process-filters-cloud.component';
|
||||||
import { By } from '@angular/platform-browser';
|
import { By } from '@angular/platform-browser';
|
||||||
import { PROCESS_FILTERS_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
import { PROCESS_FILTERS_SERVICE_TOKEN, PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service';
|
||||||
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
import { LocalPreferenceCloudService } from '../../../services/local-preference-cloud.service';
|
||||||
import { mockProcessFilters } from '../mock/process-filters-cloud.mock';
|
import { mockProcessFilters } from '../mock/process-filters-cloud.mock';
|
||||||
import { AppConfigService, AppConfigServiceMock, NoopTranslateModule } from '@alfresco/adf-core';
|
import { AppConfigService, AppConfigServiceMock, NoopTranslateModule } from '@alfresco/adf-core';
|
||||||
@ -44,16 +44,20 @@ describe('ProcessFiltersCloudComponent', () => {
|
|||||||
let getProcessFiltersSpy: jasmine.Spy;
|
let getProcessFiltersSpy: jasmine.Spy;
|
||||||
let getProcessNotificationSubscriptionSpy: jasmine.Spy;
|
let getProcessNotificationSubscriptionSpy: jasmine.Spy;
|
||||||
|
|
||||||
beforeEach(() => {
|
const configureTestingModule = (providers: any[]) => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [NoopTranslateModule, NoopAnimationsModule, MatListModule],
|
imports: [NoopTranslateModule, NoopAnimationsModule, MatListModule],
|
||||||
providers: [
|
providers: [
|
||||||
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
{ provide: PROCESS_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService },
|
||||||
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
{ provide: AppConfigService, useClass: AppConfigServiceMock },
|
||||||
{ provide: ProcessListCloudService, useValue: { getProcessCounter: () => of(10) } },
|
{ provide: ProcessListCloudService, useValue: {
|
||||||
|
getProcessCounter: () => of(10),
|
||||||
|
getProcessListCounter: () => of(10)
|
||||||
|
}},
|
||||||
{ provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock },
|
{ provide: ProcessFilterCloudService, useValue: ProcessFilterCloudServiceMock },
|
||||||
NotificationCloudService,
|
NotificationCloudService,
|
||||||
ApolloModule
|
ApolloModule,
|
||||||
|
...providers
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
fixture = TestBed.createComponent(ProcessFiltersCloudComponent);
|
fixture = TestBed.createComponent(ProcessFiltersCloudComponent);
|
||||||
@ -62,12 +66,17 @@ describe('ProcessFiltersCloudComponent', () => {
|
|||||||
processFilterService = TestBed.inject(ProcessFilterCloudService);
|
processFilterService = TestBed.inject(ProcessFilterCloudService);
|
||||||
getProcessFiltersSpy = spyOn(processFilterService, 'getProcessFilters').and.returnValue(of(mockProcessFilters));
|
getProcessFiltersSpy = spyOn(processFilterService, 'getProcessFilters').and.returnValue(of(mockProcessFilters));
|
||||||
getProcessNotificationSubscriptionSpy = spyOn(processFilterService, 'getProcessNotificationSubscription').and.returnValue(of([]));
|
getProcessNotificationSubscriptionSpy = spyOn(processFilterService, 'getProcessNotificationSubscription').and.returnValue(of([]));
|
||||||
});
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
fixture.destroy();
|
fixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('PROCESS_SEARCH_API_METHOD_TOKEN injected with GET value', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
configureTestingModule([{ provide: PROCESS_SEARCH_API_METHOD_TOKEN, useValue: 'GET' }]);
|
||||||
|
});
|
||||||
|
|
||||||
it('should attach specific icon for each filter if hasIcon is true', async () => {
|
it('should attach specific icon for each filter if hasIcon is true', async () => {
|
||||||
const change = new SimpleChange(undefined, 'my-app-1', true);
|
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||||
component.ngOnChanges({ appName: change });
|
component.ngOnChanges({ appName: change });
|
||||||
@ -121,18 +130,161 @@ describe('ProcessFiltersCloudComponent', () => {
|
|||||||
expect(Object.keys(component.counters).length).toBe(3);
|
expect(Object.keys(component.counters).length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit an error with a bad response', () => {
|
it('should emit success with the filters when filters are loaded', async () => {
|
||||||
getProcessFiltersSpy.and.returnValue(throwError('wrong request'));
|
const successSpy = spyOn(component.success, 'emit');
|
||||||
|
|
||||||
const appName = 'my-app-1';
|
const appName = 'my-app-1';
|
||||||
const change = new SimpleChange(null, appName, true);
|
const change = new SimpleChange(null, appName, true);
|
||||||
|
|
||||||
let lastValue: any;
|
component.ngOnChanges({ appName: change });
|
||||||
component.error.subscribe((err) => (lastValue = err));
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(successSpy).toHaveBeenCalledWith(mockProcessFilters);
|
||||||
|
expect(component.filters).toBeDefined();
|
||||||
|
expect(component.filters[0].name).toEqual('FakeAllProcesses');
|
||||||
|
expect(component.filters[1].name).toEqual('FakeRunningProcesses');
|
||||||
|
expect(component.filters[2].name).toEqual('FakeCompletedProcesses');
|
||||||
|
expect(Object.keys(component.counters).length).toBe(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not select any filter as default', async () => {
|
||||||
|
const appName = 'my-app-1';
|
||||||
|
const change = new SimpleChange(null, appName, true);
|
||||||
|
|
||||||
component.ngOnChanges({ appName: change });
|
component.ngOnChanges({ appName: change });
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
expect(lastValue).toBeDefined();
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(component.currentFilter).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should filterClicked emit when a filter is clicked from the UI', async () => {
|
||||||
|
const filterClickedSpy = spyOn(component.filterClicked, 'emit');
|
||||||
|
const appName = 'my-app-1';
|
||||||
|
const change = new SimpleChange(null, appName, true);
|
||||||
|
component.ngOnChanges({ appName: change });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const filterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${mockProcessFilters[0].key}_filter"]`);
|
||||||
|
filterButton.click();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(component.currentFilter).toEqual(mockProcessFilters[0]);
|
||||||
|
expect(filterClickedSpy).toHaveBeenCalledWith(mockProcessFilters[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Highlight Selected Filter', () => {
|
||||||
|
const allProcessesFilterKey = mockProcessFilters[0].key;
|
||||||
|
const runningProcessesFilterKey = mockProcessFilters[1].key;
|
||||||
|
const completedProcessesFilterKey = mockProcessFilters[2].key;
|
||||||
|
|
||||||
|
const getActiveFilterElement = (filterKey: string): Element => {
|
||||||
|
const activeFilter = fixture.debugElement.query(By.css(`.adf-active`));
|
||||||
|
return activeFilter.nativeElement.querySelector(`[data-automation-id="${filterKey}_filter"]`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickOnFilter = async (filterKey: string) => {
|
||||||
|
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 });
|
||||||
|
fixture.detectChanges();
|
||||||
|
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();
|
||||||
|
expect(getActiveFilterElement(completedProcessesFilterKey)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PROCESS_SEARCH_API_METHOD_TOKEN injected with POST value', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
configureTestingModule([{ provide: PROCESS_SEARCH_API_METHOD_TOKEN, useValue: 'POST' }]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should attach specific icon for each filter if hasIcon is true', async () => {
|
||||||
|
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||||
|
component.ngOnChanges({ appName: change });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
component.showIcons = true;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(component.filters.length).toBe(3);
|
||||||
|
const filters = fixture.nativeElement.querySelectorAll('.adf-icon');
|
||||||
|
expect(filters.length).toBe(3);
|
||||||
|
expect(filters[0].innerText).toContain('adjust');
|
||||||
|
expect(filters[1].innerText).toContain('inbox');
|
||||||
|
expect(filters[2].innerText).toContain('done');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not attach icons for each filter if hasIcon is false', async () => {
|
||||||
|
component.showIcons = false;
|
||||||
|
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||||
|
component.ngOnChanges({ appName: change });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const filters: any = fixture.debugElement.queryAll(By.css('.adf-icon'));
|
||||||
|
expect(filters.length).toBe(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display the filters', async () => {
|
||||||
|
const change = new SimpleChange(undefined, 'my-app-1', true);
|
||||||
|
component.ngOnChanges({ appName: change });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
component.showIcons = true;
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const filters = fixture.debugElement.queryAll(By.css('.adf-process-filters__entry'));
|
||||||
|
expect(component.filters.length).toBe(3);
|
||||||
|
expect(filters.length).toBe(3);
|
||||||
|
expect(filters[0].nativeElement.innerText).toContain('FakeAllProcesses');
|
||||||
|
expect(filters[1].nativeElement.innerText).toContain('FakeRunningProcesses');
|
||||||
|
expect(filters[2].nativeElement.innerText).toContain('FakeCompletedProcesses');
|
||||||
|
expect(Object.keys(component.counters).length).toBe(3);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should emit success with the filters when filters are loaded', async () => {
|
it('should emit success with the filters when filters are loaded', async () => {
|
||||||
@ -163,6 +315,96 @@ describe('ProcessFiltersCloudComponent', () => {
|
|||||||
expect(component.currentFilter).toBeUndefined();
|
expect(component.currentFilter).toBeUndefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should filterClicked emit when a filter is clicked from the UI', async () => {
|
||||||
|
const filterClickedSpy = spyOn(component.filterClicked, 'emit');
|
||||||
|
const appName = 'my-app-1';
|
||||||
|
const change = new SimpleChange(null, appName, true);
|
||||||
|
component.ngOnChanges({ appName: change });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
const filterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${mockProcessFilters[0].key}_filter"]`);
|
||||||
|
filterButton.click();
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
await fixture.whenStable();
|
||||||
|
|
||||||
|
expect(component.currentFilter).toEqual(mockProcessFilters[0]);
|
||||||
|
expect(filterClickedSpy).toHaveBeenCalledWith(mockProcessFilters[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('Highlight Selected Filter', () => {
|
||||||
|
const allProcessesFilterKey = mockProcessFilters[0].key;
|
||||||
|
const runningProcessesFilterKey = mockProcessFilters[1].key;
|
||||||
|
const completedProcessesFilterKey = mockProcessFilters[2].key;
|
||||||
|
|
||||||
|
const getActiveFilterElement = (filterKey: string): Element => {
|
||||||
|
const activeFilter = fixture.debugElement.query(By.css(`.adf-active`));
|
||||||
|
return activeFilter.nativeElement.querySelector(`[data-automation-id="${filterKey}_filter"]`);
|
||||||
|
};
|
||||||
|
|
||||||
|
const clickOnFilter = async (filterKey: string) => {
|
||||||
|
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 });
|
||||||
|
fixture.detectChanges();
|
||||||
|
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();
|
||||||
|
expect(getActiveFilterElement(completedProcessesFilterKey)).toBeDefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('API agnostic', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
configureTestingModule([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit an error with a bad response', () => {
|
||||||
|
getProcessFiltersSpy.and.returnValue(throwError('wrong request'));
|
||||||
|
|
||||||
|
const appName = 'my-app-1';
|
||||||
|
const change = new SimpleChange(null, appName, true);
|
||||||
|
|
||||||
|
let lastValue: any;
|
||||||
|
component.error.subscribe((err) => (lastValue = err));
|
||||||
|
|
||||||
|
component.ngOnChanges({ appName: change });
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(lastValue).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
it('should not select any process filter if filter input does not exist', async () => {
|
it('should not select any process filter if filter input does not exist', async () => {
|
||||||
const change = new SimpleChange(null, { name: 'nonexistentFilter' }, true);
|
const change = new SimpleChange(null, { name: 'nonexistentFilter' }, true);
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
@ -220,25 +462,6 @@ describe('ProcessFiltersCloudComponent', () => {
|
|||||||
expect(filterSelectedSpy).toHaveBeenCalledWith(mockProcessFilters[2]);
|
expect(filterSelectedSpy).toHaveBeenCalledWith(mockProcessFilters[2]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should filterClicked emit when a filter is clicked from the UI', async () => {
|
|
||||||
const filterClickedSpy = spyOn(component.filterClicked, 'emit');
|
|
||||||
const appName = 'my-app-1';
|
|
||||||
const change = new SimpleChange(null, appName, true);
|
|
||||||
component.ngOnChanges({ appName: change });
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
await fixture.whenStable();
|
|
||||||
|
|
||||||
const filterButton = fixture.debugElement.nativeElement.querySelector(`[data-automation-id="${mockProcessFilters[0].key}_filter"]`);
|
|
||||||
filterButton.click();
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
await fixture.whenStable();
|
|
||||||
|
|
||||||
expect(component.currentFilter).toEqual(mockProcessFilters[0]);
|
|
||||||
expect(filterClickedSpy).toHaveBeenCalledWith(mockProcessFilters[0]);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reset the filter when the param is undefined', () => {
|
it('should reset the filter when the param is undefined', () => {
|
||||||
const change = new SimpleChange(mockProcessFilters[0], undefined, false);
|
const change = new SimpleChange(mockProcessFilters[0], undefined, false);
|
||||||
component.currentFilter = mockProcessFilters[0];
|
component.currentFilter = mockProcessFilters[0];
|
||||||
@ -319,46 +542,6 @@ describe('ProcessFiltersCloudComponent', () => {
|
|||||||
return activeFilter.nativeElement.querySelector(`[data-automation-id="${filterKey}_filter"]`);
|
return activeFilter.nativeElement.querySelector(`[data-automation-id="${filterKey}_filter"]`);
|
||||||
};
|
};
|
||||||
|
|
||||||
const clickOnFilter = async (filterKey: string) => {
|
|
||||||
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 });
|
|
||||||
fixture.detectChanges();
|
|
||||||
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();
|
|
||||||
expect(getActiveFilterElement(completedProcessesFilterKey)).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('Should apply active CSS class when filterParam input changed', async () => {
|
it('Should apply active CSS class when filterParam input changed', async () => {
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.ngOnChanges({ filterParam: new SimpleChange(null, { key: allProcessesFilterKey }, true) });
|
component.ngOnChanges({ filterParam: new SimpleChange(null, { key: allProcessesFilterKey }, true) });
|
||||||
@ -458,4 +641,5 @@ describe('ProcessFiltersCloudComponent', () => {
|
|||||||
expect(component.currentFiltersValues[fakeFilterKey]).toBe(5);
|
expect(component.currentFiltersValues[fakeFilterKey]).toBe(5);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@ -23,6 +23,8 @@ import { AppConfigService, TranslationService } from '@alfresco/adf-core';
|
|||||||
import { FilterParamsModel } from '../../../task/task-filters/models/filter-cloud.model';
|
import { FilterParamsModel } from '../../../task/task-filters/models/filter-cloud.model';
|
||||||
import { debounceTime, takeUntil, tap } from 'rxjs/operators';
|
import { debounceTime, takeUntil, tap } from 'rxjs/operators';
|
||||||
import { ProcessListCloudService } from '../../../process/process-list/services/process-list-cloud.service';
|
import { ProcessListCloudService } from '../../../process/process-list/services/process-list-cloud.service';
|
||||||
|
import { PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service';
|
||||||
|
import { ProcessFilterCloudAdapter } from '../../process-list/models/process-cloud-query-request.model';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'adf-cloud-process-filters',
|
selector: 'adf-cloud-process-filters',
|
||||||
@ -77,6 +79,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
|
|||||||
private readonly translationService = inject(TranslationService);
|
private readonly translationService = inject(TranslationService);
|
||||||
private readonly appConfigService = inject(AppConfigService);
|
private readonly appConfigService = inject(AppConfigService);
|
||||||
private readonly processListCloudService = inject(ProcessListCloudService);
|
private readonly processListCloudService = inject(ProcessListCloudService);
|
||||||
|
private readonly searchMethod = inject<'GET' | 'POST'>(PROCESS_SEARCH_API_METHOD_TOKEN, { optional: true });
|
||||||
|
|
||||||
ngOnInit() {
|
ngOnInit() {
|
||||||
this.enableNotifications = this.appConfigService.get('notifications', true);
|
this.enableNotifications = this.appConfigService.get('notifications', true);
|
||||||
@ -272,8 +275,7 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
|
|||||||
* @param filter filter
|
* @param filter filter
|
||||||
*/
|
*/
|
||||||
updateFilterCounter(filter: ProcessFilterCloudModel): void {
|
updateFilterCounter(filter: ProcessFilterCloudModel): void {
|
||||||
this.processListCloudService
|
this.fetchProcessFilterCounter(filter)
|
||||||
.getProcessCounter(filter.appName, filter.status)
|
|
||||||
.pipe(
|
.pipe(
|
||||||
tap((filterCounter) => {
|
tap((filterCounter) => {
|
||||||
this.checkIfFilterValuesHasBeenUpdated(filter.key, filterCounter);
|
this.checkIfFilterValuesHasBeenUpdated(filter.key, filterCounter);
|
||||||
@ -312,4 +314,10 @@ export class ProcessFiltersCloudComponent implements OnInit, OnChanges, OnDestro
|
|||||||
this.updatedFiltersSet.delete(filterKey);
|
this.updatedFiltersSet.delete(filterKey);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fetchProcessFilterCounter(filter: ProcessFilterCloudModel): Observable<number> {
|
||||||
|
return this.searchMethod === 'POST'
|
||||||
|
? this.processListCloudService.getProcessListCounter(new ProcessFilterCloudAdapter(filter))
|
||||||
|
: this.processListCloudService.getProcessCounter(filter.appName, filter.status)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -54,6 +54,7 @@ export const fakeProcessCloudFilters = [
|
|||||||
|
|
||||||
export const mockProcessFilters: any[] = [
|
export const mockProcessFilters: any[] = [
|
||||||
{
|
{
|
||||||
|
appName: 'mock-app-name',
|
||||||
name: 'FakeAllProcesses',
|
name: 'FakeAllProcesses',
|
||||||
key: 'FakeAllProcesses',
|
key: 'FakeAllProcesses',
|
||||||
icon: 'adjust',
|
icon: 'adjust',
|
||||||
@ -61,6 +62,7 @@ export const mockProcessFilters: any[] = [
|
|||||||
status: ''
|
status: ''
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
appName: 'mock-app-name',
|
||||||
name: 'FakeRunningProcesses',
|
name: 'FakeRunningProcesses',
|
||||||
key: 'FakeRunningProcesses',
|
key: 'FakeRunningProcesses',
|
||||||
icon: 'inbox',
|
icon: 'inbox',
|
||||||
@ -68,6 +70,7 @@ export const mockProcessFilters: any[] = [
|
|||||||
status: 'RUNNING'
|
status: 'RUNNING'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
appName: 'mock-app-name',
|
||||||
name: 'FakeCompletedProcesses',
|
name: 'FakeCompletedProcesses',
|
||||||
key: 'completed-processes',
|
key: 'completed-processes',
|
||||||
icon: 'done',
|
icon: 'done',
|
||||||
|
@ -48,6 +48,11 @@ export class ProcessFilterCloudModel {
|
|||||||
completedDate: Date;
|
completedDate: Date;
|
||||||
environmentId?: string;
|
environmentId?: string;
|
||||||
|
|
||||||
|
processDefinitionNames: string[] | null;
|
||||||
|
initiators: string[] | null;
|
||||||
|
appVersions: string[] | null;
|
||||||
|
statuses: string[] | null;
|
||||||
|
|
||||||
private dateRangeFilterService = new DateRangeFilterService();
|
private dateRangeFilterService = new DateRangeFilterService();
|
||||||
private _completedFrom: string;
|
private _completedFrom: string;
|
||||||
private _completedTo: string;
|
private _completedTo: string;
|
||||||
@ -94,6 +99,11 @@ export class ProcessFilterCloudModel {
|
|||||||
this.completedDate = obj.completedDate || null;
|
this.completedDate = obj.completedDate || null;
|
||||||
this._suspendedFrom = obj._suspendedFrom || null;
|
this._suspendedFrom = obj._suspendedFrom || null;
|
||||||
this._suspendedTo = obj._suspendedTo || null;
|
this._suspendedTo = obj._suspendedTo || null;
|
||||||
|
|
||||||
|
this.processDefinitionNames = obj.processDefinitionNames || null;
|
||||||
|
this.initiators = obj.initiators || null;
|
||||||
|
this.appVersions = obj.appVersions || null;
|
||||||
|
this.statuses = obj.statuses || null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,16 +86,19 @@ describe('ProcessFilterCloudService', () => {
|
|||||||
expect(res[0].id).toBe('1');
|
expect(res[0].id).toBe('1');
|
||||||
expect(res[0].name).toBe('MOCK_PROCESS_NAME_1');
|
expect(res[0].name).toBe('MOCK_PROCESS_NAME_1');
|
||||||
expect(res[0].status).toBe('MOCK_ALL');
|
expect(res[0].status).toBe('MOCK_ALL');
|
||||||
|
expect(res[0].statuses).toContain('MOCK_ALL');
|
||||||
|
|
||||||
expect(res[1].appName).toBe('mock-appName');
|
expect(res[1].appName).toBe('mock-appName');
|
||||||
expect(res[1].id).toBe('2');
|
expect(res[1].id).toBe('2');
|
||||||
expect(res[1].name).toBe('MOCK_PROCESS_NAME_2');
|
expect(res[1].name).toBe('MOCK_PROCESS_NAME_2');
|
||||||
expect(res[1].status).toBe('MOCK-RUNNING');
|
expect(res[1].status).toBe('MOCK-RUNNING');
|
||||||
|
expect(res[1].statuses).toContain('MOCK-RUNNING');
|
||||||
|
|
||||||
expect(res[2].appName).toBe('mock-appName');
|
expect(res[2].appName).toBe('mock-appName');
|
||||||
expect(res[2].id).toBe('3');
|
expect(res[2].id).toBe('3');
|
||||||
expect(res[2].name).toBe('MOCK_PROCESS_NAME_3');
|
expect(res[2].name).toBe('MOCK_PROCESS_NAME_3');
|
||||||
expect(res[2].status).toBe('MOCK-COMPLETED');
|
expect(res[2].status).toBe('MOCK-COMPLETED');
|
||||||
|
expect(res[2].statuses).toContain('MOCK-COMPLETED');
|
||||||
|
|
||||||
expect(createPreferenceSpy).toHaveBeenCalled();
|
expect(createPreferenceSpy).toHaveBeenCalled();
|
||||||
done();
|
done();
|
||||||
@ -112,16 +115,19 @@ describe('ProcessFilterCloudService', () => {
|
|||||||
expect(res[0].id).toBe('1');
|
expect(res[0].id).toBe('1');
|
||||||
expect(res[0].name).toBe('MOCK_PROCESS_NAME_1');
|
expect(res[0].name).toBe('MOCK_PROCESS_NAME_1');
|
||||||
expect(res[0].status).toBe('MOCK_ALL');
|
expect(res[0].status).toBe('MOCK_ALL');
|
||||||
|
expect(res[0].statuses).toContain('MOCK_ALL');
|
||||||
|
|
||||||
expect(res[1].appName).toBe('mock-appName');
|
expect(res[1].appName).toBe('mock-appName');
|
||||||
expect(res[1].id).toBe('2');
|
expect(res[1].id).toBe('2');
|
||||||
expect(res[1].name).toBe('MOCK_PROCESS_NAME_2');
|
expect(res[1].name).toBe('MOCK_PROCESS_NAME_2');
|
||||||
expect(res[1].status).toBe('MOCK-RUNNING');
|
expect(res[1].status).toBe('MOCK-RUNNING');
|
||||||
|
expect(res[1].statuses).toContain('MOCK-RUNNING');
|
||||||
|
|
||||||
expect(res[2].appName).toBe('mock-appName');
|
expect(res[2].appName).toBe('mock-appName');
|
||||||
expect(res[2].id).toBe('3');
|
expect(res[2].id).toBe('3');
|
||||||
expect(res[2].name).toBe('MOCK_PROCESS_NAME_3');
|
expect(res[2].name).toBe('MOCK_PROCESS_NAME_3');
|
||||||
expect(res[2].status).toBe('MOCK-COMPLETED');
|
expect(res[2].status).toBe('MOCK-COMPLETED');
|
||||||
|
expect(res[2].statuses).toContain('MOCK-COMPLETED');
|
||||||
|
|
||||||
expect(getPreferencesSpy).toHaveBeenCalled();
|
expect(getPreferencesSpy).toHaveBeenCalled();
|
||||||
done();
|
done();
|
||||||
@ -140,16 +146,19 @@ describe('ProcessFilterCloudService', () => {
|
|||||||
expect(res[0].id).toBe('1');
|
expect(res[0].id).toBe('1');
|
||||||
expect(res[0].name).toBe('MOCK_PROCESS_NAME_1');
|
expect(res[0].name).toBe('MOCK_PROCESS_NAME_1');
|
||||||
expect(res[0].status).toBe('MOCK_ALL');
|
expect(res[0].status).toBe('MOCK_ALL');
|
||||||
|
expect(res[0].statuses).toContain('MOCK_ALL');
|
||||||
|
|
||||||
expect(res[1].appName).toBe('mock-appName');
|
expect(res[1].appName).toBe('mock-appName');
|
||||||
expect(res[1].id).toBe('2');
|
expect(res[1].id).toBe('2');
|
||||||
expect(res[1].name).toBe('MOCK_PROCESS_NAME_2');
|
expect(res[1].name).toBe('MOCK_PROCESS_NAME_2');
|
||||||
expect(res[1].status).toBe('MOCK-RUNNING');
|
expect(res[1].status).toBe('MOCK-RUNNING');
|
||||||
|
expect(res[1].statuses).toContain('MOCK-RUNNING');
|
||||||
|
|
||||||
expect(res[2].appName).toBe('mock-appName');
|
expect(res[2].appName).toBe('mock-appName');
|
||||||
expect(res[2].id).toBe('3');
|
expect(res[2].id).toBe('3');
|
||||||
expect(res[2].name).toBe('MOCK_PROCESS_NAME_3');
|
expect(res[2].name).toBe('MOCK_PROCESS_NAME_3');
|
||||||
expect(res[2].status).toBe('MOCK-COMPLETED');
|
expect(res[2].status).toBe('MOCK-COMPLETED');
|
||||||
|
expect(res[2].statuses).toContain('MOCK-COMPLETED');
|
||||||
|
|
||||||
expect(getPreferencesSpy).toHaveBeenCalled();
|
expect(getPreferencesSpy).toHaveBeenCalled();
|
||||||
expect(createPreferenceSpy).toHaveBeenCalled();
|
expect(createPreferenceSpy).toHaveBeenCalled();
|
||||||
|
@ -131,7 +131,8 @@ export class ProcessFilterCloudService {
|
|||||||
} else {
|
} else {
|
||||||
return of(this.findFiltersByKeyInPreferences(preferences, key));
|
return of(this.findFiltersByKeyInPreferences(preferences, key));
|
||||||
}
|
}
|
||||||
})
|
}),
|
||||||
|
switchMap((filters) => this.handleCreateFilterBackwardsCompatibility(appName, key, filters))
|
||||||
)
|
)
|
||||||
.subscribe((filters) => {
|
.subscribe((filters) => {
|
||||||
this.addFiltersToStream(filters);
|
this.addFiltersToStream(filters);
|
||||||
@ -414,4 +415,39 @@ export class ProcessFilterCloudService {
|
|||||||
refreshFilter(filterKey: string): void {
|
refreshFilter(filterKey: string): void {
|
||||||
this.filterKeyToBeRefreshedSource.next(filterKey);
|
this.filterKeyToBeRefreshedSource.next(filterKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method is run after retrieving the filter array from preferences.
|
||||||
|
* It handles the backwards compatibility with the new API by looking for the new properties and their counterparts in each passed filter.
|
||||||
|
* If the new property is not found, it is created and assigned the value constructed from the old property.
|
||||||
|
* The filters are then updated in the preferences and returned.
|
||||||
|
* Old properties are left untouched for purposes like feature toggling.
|
||||||
|
*
|
||||||
|
* @param appName Name of the target app.
|
||||||
|
* @param key Key of the process filters.
|
||||||
|
* @param filters Array of process filters to be checked for backward compatibility.
|
||||||
|
* @returns Observable of process filters with updated properties.
|
||||||
|
*/
|
||||||
|
private handleCreateFilterBackwardsCompatibility(
|
||||||
|
appName: string,
|
||||||
|
key: string,
|
||||||
|
filters: ProcessFilterCloudModel[]
|
||||||
|
): Observable<ProcessFilterCloudModel[]> {
|
||||||
|
filters.forEach((filter) => {
|
||||||
|
if (filter.processDefinitionName && !filter.processDefinitionNames) {
|
||||||
|
filter.processDefinitionNames = [filter.processDefinitionName];
|
||||||
|
}
|
||||||
|
if (filter.initiator && !filter.initiators) {
|
||||||
|
filter.initiators = [filter.initiator];
|
||||||
|
}
|
||||||
|
if (filter.appVersion && !filter.appVersions) {
|
||||||
|
filter.appVersions = [filter.appVersion.toString()];
|
||||||
|
}
|
||||||
|
if (filter.status && !filter.statuses) {
|
||||||
|
filter.statuses = [filter.status];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this.updateProcessFilters(appName, key, filters);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,7 +35,7 @@ import { of } from 'rxjs';
|
|||||||
import { shareReplay, skip } from 'rxjs/operators';
|
import { shareReplay, skip } from 'rxjs/operators';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||||
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
|
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
|
||||||
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service';
|
||||||
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
|
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
|
||||||
import { PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data';
|
import { PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data';
|
||||||
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
import { MatProgressSpinnerModule } from '@angular/material/progress-spinner';
|
||||||
@ -76,9 +76,10 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
const fakeCustomSchemaName = 'fakeCustomSchema';
|
const fakeCustomSchemaName = 'fakeCustomSchema';
|
||||||
const schemaWithVariable = 'schemaWithVariableId';
|
const schemaWithVariable = 'schemaWithVariableId';
|
||||||
|
|
||||||
beforeEach(() => {
|
const configureTestingModule = (providers: any[]) => {
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [ProcessServiceCloudTestingModule]
|
imports: [ProcessServiceCloudTestingModule],
|
||||||
|
providers: providers
|
||||||
});
|
});
|
||||||
appConfig = TestBed.inject(AppConfigService);
|
appConfig = TestBed.inject(AppConfigService);
|
||||||
processListCloudService = TestBed.inject(ProcessListCloudService);
|
processListCloudService = TestBed.inject(ProcessListCloudService);
|
||||||
@ -119,37 +120,15 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
|
|
||||||
component.isColumnSchemaCreated$ = of(true).pipe(shareReplay(1));
|
component.isColumnSchemaCreated$ = of(true).pipe(shareReplay(1));
|
||||||
loader = TestbedHarnessEnvironment.loader(fixture);
|
loader = TestbedHarnessEnvironment.loader(fixture);
|
||||||
});
|
};
|
||||||
|
|
||||||
afterEach(() => {
|
afterEach(() => {
|
||||||
fixture.destroy();
|
fixture.destroy();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use the default schemaColumn', () => {
|
describe('PROCESS_SEARCH_API_METHOD_TOKEN injected with GET value', () => {
|
||||||
appConfig.config = Object.assign(appConfig.config, { 'adf-cloud-process-list': processListSchemaMock });
|
beforeEach(() => {
|
||||||
fixture.detectChanges();
|
configureTestingModule([{ provide: PROCESS_SEARCH_API_METHOD_TOKEN, useValue: 'GET' }]);
|
||||||
|
|
||||||
expect(component.columns).toBeDefined();
|
|
||||||
expect(component.columns.length).toEqual(10);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should display empty content when process list is empty', async () => {
|
|
||||||
const emptyList = { list: { entries: [] } };
|
|
||||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(emptyList));
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(component.isLoading).toBe(true);
|
|
||||||
|
|
||||||
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
|
|
||||||
|
|
||||||
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
|
||||||
component.ngOnChanges({ appName });
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
|
|
||||||
|
|
||||||
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
|
|
||||||
expect(emptyContent.nativeElement).toBeDefined();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should load spinner and show the content', async () => {
|
it('should load spinner and show the content', async () => {
|
||||||
@ -200,20 +179,6 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
expect(component.requestNode.appVersion).toEqual('');
|
expect(component.requestNode.appVersion).toEqual('');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use the custom schemaColumn from app.config.json', () => {
|
|
||||||
component.presetColumn = fakeCustomSchemaName;
|
|
||||||
component.ngAfterContentInit();
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(component.columns).toEqual(fakeCustomSchema);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should fetch custom schemaColumn when the input presetColumn is defined', () => {
|
|
||||||
component.presetColumn = fakeCustomSchemaName;
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(component.columns).toBeDefined();
|
|
||||||
expect(component.columns.length).toEqual(2);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return the results if an application name is given', (done) => {
|
it('should return the results if an application name is given', (done) => {
|
||||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||||
@ -243,18 +208,6 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should not shown columns selector by default', () => {
|
|
||||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
|
||||||
|
|
||||||
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
|
||||||
component.ngOnChanges({ appName });
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
const mainMenuButton = fixture.debugElement.query(By.css('[data-automation-id="adf-datatable-main-menu-button"]'));
|
|
||||||
expect(mainMenuButton).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should shown columns selector', () => {
|
it('should shown columns selector', () => {
|
||||||
component.showMainDatatableActions = true;
|
component.showMainDatatableActions = true;
|
||||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
@ -355,6 +308,435 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
component.reload();
|
component.reload();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
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(() => {
|
||||||
|
component.rows = fakeProcessCloudList.list.entries;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload the process list when input parameters changed', () => {
|
||||||
|
const getProcessByRequestSpy = spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.appName = 'mock-app-name';
|
||||||
|
component.status = 'mock-status';
|
||||||
|
component.initiator = 'mock-initiator';
|
||||||
|
const appNameChange = new SimpleChange(undefined, 'mock-app-name', true);
|
||||||
|
const statusChange = new SimpleChange(undefined, 'mock-status', true);
|
||||||
|
const initiatorChange = new SimpleChange(undefined, 'mock-initiator', true);
|
||||||
|
|
||||||
|
component.ngOnChanges({
|
||||||
|
appName: appNameChange,
|
||||||
|
assignee: initiatorChange,
|
||||||
|
status: statusChange
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isListEmpty()).toBeFalsy();
|
||||||
|
expect(getProcessByRequestSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload process list when sorting on a column changes', () => {
|
||||||
|
const getProcessByRequestSpy = spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.onSortingChanged(
|
||||||
|
new CustomEvent('sorting-changed', {
|
||||||
|
detail: {
|
||||||
|
key: 'fakeName',
|
||||||
|
direction: 'asc'
|
||||||
|
},
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.sorting).toEqual([
|
||||||
|
new ProcessListCloudSortingModel({
|
||||||
|
orderBy: 'fakeName',
|
||||||
|
direction: 'ASC'
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
expect(component.formattedSorting).toEqual(['fakeName', 'asc']);
|
||||||
|
expect(component.isListEmpty()).toBeFalsy();
|
||||||
|
expect(getProcessByRequestSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset pagination when resetPaginationValues is called', (done) => {
|
||||||
|
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
|
||||||
|
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)).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();
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagination = {
|
||||||
|
maxItems: 250,
|
||||||
|
skipCount: 200
|
||||||
|
};
|
||||||
|
component.updatePagination(pagination);
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
component.resetPagination();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('PROCESS_SEARCH_API_METHOD_TOKEN injected with POST value', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
configureTestingModule([{ provide: PROCESS_SEARCH_API_METHOD_TOKEN, useValue: 'POST' }]);
|
||||||
|
component.appName = 'fake-app-name';
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should load spinner and show the content', async () => {
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isLoading).toBe(true);
|
||||||
|
|
||||||
|
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
|
||||||
|
|
||||||
|
component.ngOnChanges({ appName });
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component.isLoading).toBe(false);
|
||||||
|
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
|
||||||
|
|
||||||
|
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
|
||||||
|
expect(emptyContent).toBeFalsy();
|
||||||
|
|
||||||
|
expect(component.rows.length).toEqual(3);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should the payload contain the appVersion if it is defined', () => {
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.appVersions = ['1'];
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
component.reload();
|
||||||
|
|
||||||
|
expect(component.processListRequestNode.appVersion).toEqual(['1']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should the payload contain all the app versions', () => {
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.appVersions = ['1', '2', '3'];
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
component.reload();
|
||||||
|
|
||||||
|
expect(component.processListRequestNode.appVersion).toEqual(['1', '2', '3']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should the payload NOT contain any app version when appVersion does not have a value', () => {
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.appVersion = undefined;
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
component.reload();
|
||||||
|
|
||||||
|
expect(component.processListRequestNode.appVersion.length).toEqual(0);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return the results if an application name is given', (done) => {
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||||
|
component.success.subscribe((res) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(component.rows).toBeDefined();
|
||||||
|
expect(component.rows.length).toEqual(3);
|
||||||
|
expect(component.rows[0].entry['appName']).toBe('easy-peasy-japanesey');
|
||||||
|
expect(component.rows[0].entry['appVersion']).toBe(1);
|
||||||
|
expect(component.rows[0].entry['id']).toBe('69eddfa7-d781-11e8-ae24-0a58646001fa');
|
||||||
|
expect(component.rows[0].entry['name']).toEqual('starring');
|
||||||
|
expect(component.rows[0].entry['processDefinitionId']).toBe('BasicProcess:1:d05062f1-c6fb-11e8-ae24-0a58646001fa');
|
||||||
|
expect(component.rows[0].entry['processDefinitionKey']).toBe('BasicProcess');
|
||||||
|
expect(component.rows[0].entry['initiator']).toBe('devopsuser');
|
||||||
|
expect(component.rows[0].entry['startDate']).toBe(1540381146275);
|
||||||
|
expect(component.rows[0].entry['businessKey']).toBe('MyBusinessKey');
|
||||||
|
expect(component.rows[0].entry['status']).toBe('RUNNING');
|
||||||
|
expect(component.rows[0].entry['lastModified']).toBe(1540381146276);
|
||||||
|
expect(component.rows[0].entry['lastModifiedTo']).toBeNull();
|
||||||
|
expect(component.rows[0].entry['lastModifiedFrom']).toBeNull();
|
||||||
|
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
component.appName = appName.currentValue;
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
component.ngOnChanges({ appName });
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should shown columns selector', () => {
|
||||||
|
component.showMainDatatableActions = true;
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
|
||||||
|
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
component.ngOnChanges({ appName });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const mainMenuButton = fixture.debugElement.query(By.css('[data-automation-id="adf-datatable-main-menu-button"]'));
|
||||||
|
expect(mainMenuButton).toBeTruthy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should hide columns on applying new columns visibility through columns selector', () => {
|
||||||
|
component.showMainDatatableActions = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
|
||||||
|
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||||
|
component.ngOnChanges({ appName });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const mainMenuButton = fixture.debugElement.query(By.css('[data-automation-id="adf-datatable-main-menu-button"]'));
|
||||||
|
mainMenuButton.triggerEventHandler('click', {});
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const columnSelectorMenu = fixture.debugElement.query(By.css('adf-datatable-column-selector'));
|
||||||
|
expect(columnSelectorMenu).toBeTruthy();
|
||||||
|
|
||||||
|
const newColumns = (component.columns as DataColumn[]).map((column, index) => ({
|
||||||
|
...column,
|
||||||
|
isHidden: index !== 0 // only first one is shown
|
||||||
|
}));
|
||||||
|
|
||||||
|
const columnsSelectorInstance = columnSelectorMenu.componentInstance as ColumnsSelectorComponent;
|
||||||
|
expect(columnsSelectorInstance.columns).toBe(component.columns, 'should use columns as input');
|
||||||
|
|
||||||
|
columnSelectorMenu.triggerEventHandler('submitColumnsVisibility', newColumns);
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const displayedColumns = fixture.debugElement.queryAll(By.css('.adf-datatable-cell-header'));
|
||||||
|
expect(displayedColumns.length).toBe(2, 'only column with isHidden set to false and action column should be shown');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should NOT request process variable if columns for process variables are not displayed', () => {
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
spyOn(preferencesService, 'getPreferences').and.returnValue(
|
||||||
|
of({
|
||||||
|
list: {
|
||||||
|
entries: []
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
component.reload();
|
||||||
|
|
||||||
|
expect(component.processListRequestNode.variableKeys).not.toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should request process variable if column for process variable is displayed', () => {
|
||||||
|
component.presetColumn = schemaWithVariable;
|
||||||
|
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
spyOn(preferencesService, 'getPreferences').and.returnValue(
|
||||||
|
of({
|
||||||
|
list: {
|
||||||
|
entries: [
|
||||||
|
{
|
||||||
|
entry: {
|
||||||
|
key: ProcessListCloudPreferences.columnsVisibility,
|
||||||
|
value: '{"variableColumnId":"id", "2":true}'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
component.reload();
|
||||||
|
|
||||||
|
expect(component.processListRequestNode.variableKeys).toEqual(['processKey/variableName']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload tasks when reload() is called', (done) => {
|
||||||
|
component.appName = 'fake';
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.success.subscribe((res) => {
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(component.rows).toBeDefined();
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
component.reload();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call endpoint when a column visibility gets changed', () => {
|
||||||
|
spyOn(preferencesService, 'updatePreference').and.returnValue(of({}));
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList');
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
spyOn(component, 'createDatatableSchema');
|
||||||
|
component.appName = 'fake-app-name';
|
||||||
|
component.reload();
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
component.onColumnsVisibilityChange(component.columns);
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(processListCloudService.fetchProcessList).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('component changes', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
component.rows = fakeProcessCloudList.list.entries;
|
||||||
|
fixture.detectChanges();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload the process list when input parameters changed', () => {
|
||||||
|
const fetchProcessListSpy = spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.appName = 'mock-app-name';
|
||||||
|
component.status = 'mock-status';
|
||||||
|
component.initiator = 'mock-initiator';
|
||||||
|
const appNameChange = new SimpleChange(undefined, 'mock-app-name', true);
|
||||||
|
const statusChange = new SimpleChange(undefined, 'mock-status', true);
|
||||||
|
const initiatorChange = new SimpleChange(undefined, 'mock-initiator', true);
|
||||||
|
|
||||||
|
component.ngOnChanges({
|
||||||
|
appName: appNameChange,
|
||||||
|
assignee: initiatorChange,
|
||||||
|
status: statusChange
|
||||||
|
});
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isListEmpty()).toBeFalsy();
|
||||||
|
expect(fetchProcessListSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reload process list when sorting on a column changes', () => {
|
||||||
|
const fetchProcessListSpy = spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
component.onSortingChanged(
|
||||||
|
new CustomEvent('sorting-changed', {
|
||||||
|
detail: {
|
||||||
|
key: 'fakeName',
|
||||||
|
direction: 'asc'
|
||||||
|
},
|
||||||
|
bubbles: true
|
||||||
|
})
|
||||||
|
);
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.sorting).toEqual([
|
||||||
|
new ProcessListCloudSortingModel({
|
||||||
|
orderBy: 'fakeName',
|
||||||
|
direction: 'ASC'
|
||||||
|
})
|
||||||
|
]);
|
||||||
|
expect(component.formattedSorting).toEqual(['fakeName', 'asc']);
|
||||||
|
expect(component.isListEmpty()).toBeFalsy();
|
||||||
|
expect(fetchProcessListSpy).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should reset pagination when resetPaginationValues is called', (done) => {
|
||||||
|
spyOn(processListCloudService, 'fetchProcessList').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
|
||||||
|
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)).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();
|
||||||
|
});
|
||||||
|
|
||||||
|
const pagination = {
|
||||||
|
maxItems: 250,
|
||||||
|
skipCount: 200
|
||||||
|
};
|
||||||
|
component.updatePagination(pagination);
|
||||||
|
fixture.whenStable().then(() => {
|
||||||
|
component.resetPagination();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('API agnostic', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
configureTestingModule([]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the default schemaColumn', () => {
|
||||||
|
appConfig.config = Object.assign(appConfig.config, { 'adf-cloud-process-list': processListSchemaMock });
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(component.columns).toBeDefined();
|
||||||
|
expect(component.columns.length).toEqual(10);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should display empty content when process list is empty', async () => {
|
||||||
|
const emptyList = { list: { entries: [] } };
|
||||||
|
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(emptyList));
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.isLoading).toBe(true);
|
||||||
|
|
||||||
|
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
|
||||||
|
|
||||||
|
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||||
|
component.ngOnChanges({ appName });
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
|
||||||
|
|
||||||
|
const emptyContent = fixture.debugElement.query(By.css('.adf-empty-content'));
|
||||||
|
expect(emptyContent.nativeElement).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should use the custom schemaColumn from app.config.json', () => {
|
||||||
|
component.presetColumn = fakeCustomSchemaName;
|
||||||
|
component.ngAfterContentInit();
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.columns).toEqual(fakeCustomSchema);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should fetch custom schemaColumn when the input presetColumn is defined', () => {
|
||||||
|
component.presetColumn = fakeCustomSchemaName;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.columns).toBeDefined();
|
||||||
|
expect(component.columns.length).toEqual(2);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not shown columns selector by default', () => {
|
||||||
|
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
|
|
||||||
|
const appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
|
||||||
|
component.ngOnChanges({ appName });
|
||||||
|
|
||||||
|
fixture.detectChanges();
|
||||||
|
|
||||||
|
const mainMenuButton = fixture.debugElement.query(By.css('[data-automation-id="adf-datatable-main-menu-button"]'));
|
||||||
|
expect(mainMenuButton).toBeFalsy();
|
||||||
|
});
|
||||||
|
|
||||||
it('should emit row click event', (done) => {
|
it('should emit row click event', (done) => {
|
||||||
const row = new ObjectDataRow({ id: '999' });
|
const row = new ObjectDataRow({ id: '999' });
|
||||||
const rowEvent = new DataRowEvent(row, null);
|
const rowEvent = new DataRowEvent(row, null);
|
||||||
@ -447,47 +829,12 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
expect(component.createDatatableSchema).toHaveBeenCalled();
|
expect(component.createDatatableSchema).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should call endpoint when a column visibility gets changed', () => {
|
|
||||||
spyOn(preferencesService, 'updatePreference').and.returnValue(of({}));
|
|
||||||
spyOn(processListCloudService, 'getProcessByRequest');
|
|
||||||
component.ngAfterContentInit();
|
|
||||||
spyOn(component, 'createDatatableSchema');
|
|
||||||
component.appName = 'fake-app-name';
|
|
||||||
component.reload();
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
component.onColumnsVisibilityChange(component.columns);
|
|
||||||
|
|
||||||
fixture.detectChanges();
|
|
||||||
|
|
||||||
expect(processListCloudService.getProcessByRequest).toHaveBeenCalledTimes(1);
|
|
||||||
});
|
|
||||||
|
|
||||||
describe('component changes', () => {
|
describe('component changes', () => {
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
component.rows = fakeProcessCloudList.list.entries;
|
component.rows = fakeProcessCloudList.list.entries;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reload the process list when input parameters changed', () => {
|
|
||||||
const getProcessByRequestSpy = spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
|
||||||
component.appName = 'mock-app-name';
|
|
||||||
component.status = 'mock-status';
|
|
||||||
component.initiator = 'mock-initiator';
|
|
||||||
const appNameChange = new SimpleChange(undefined, 'mock-app-name', true);
|
|
||||||
const statusChange = new SimpleChange(undefined, 'mock-status', true);
|
|
||||||
const initiatorChange = new SimpleChange(undefined, 'mock-initiator', true);
|
|
||||||
|
|
||||||
component.ngOnChanges({
|
|
||||||
appName: appNameChange,
|
|
||||||
assignee: initiatorChange,
|
|
||||||
status: statusChange
|
|
||||||
});
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(component.isListEmpty()).toBeFalsy();
|
|
||||||
expect(getProcessByRequestSpy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set formattedSorting if sorting input changes', () => {
|
it('should set formattedSorting if sorting input changes', () => {
|
||||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
spyOn(component, 'formatSorting').and.callThrough();
|
spyOn(component, 'formatSorting').and.callThrough();
|
||||||
@ -508,57 +855,6 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
expect(component.formattedSorting).toEqual(['startDate', 'desc']);
|
expect(component.formattedSorting).toEqual(['startDate', 'desc']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should reload process list when sorting on a column changes', () => {
|
|
||||||
const getProcessByRequestSpy = spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
|
||||||
component.onSortingChanged(
|
|
||||||
new CustomEvent('sorting-changed', {
|
|
||||||
detail: {
|
|
||||||
key: 'fakeName',
|
|
||||||
direction: 'asc'
|
|
||||||
},
|
|
||||||
bubbles: true
|
|
||||||
})
|
|
||||||
);
|
|
||||||
fixture.detectChanges();
|
|
||||||
expect(component.sorting).toEqual([
|
|
||||||
new ProcessListCloudSortingModel({
|
|
||||||
orderBy: 'fakeName',
|
|
||||||
direction: 'ASC'
|
|
||||||
})
|
|
||||||
]);
|
|
||||||
expect(component.formattedSorting).toEqual(['fakeName', 'asc']);
|
|
||||||
expect(component.isListEmpty()).toBeFalsy();
|
|
||||||
expect(getProcessByRequestSpy).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should reset pagination when resetPaginationValues is called', (done) => {
|
|
||||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
|
||||||
|
|
||||||
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)).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();
|
|
||||||
});
|
|
||||||
|
|
||||||
const pagination = {
|
|
||||||
maxItems: 250,
|
|
||||||
skipCount: 200
|
|
||||||
};
|
|
||||||
component.updatePagination(pagination);
|
|
||||||
fixture.whenStable().then(() => {
|
|
||||||
component.resetPagination();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should set pagination and reload when updatePagination is called', (done) => {
|
it('should set pagination and reload when updatePagination is called', (done) => {
|
||||||
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
|
||||||
spyOn(component, 'reload').and.stub();
|
spyOn(component, 'reload').and.stub();
|
||||||
@ -582,6 +878,7 @@ describe('ProcessListCloudComponent', () => {
|
|||||||
component.updatePagination(pagination);
|
component.updatePagination(pagination);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('ProcessListCloudComponent: Injecting custom columns for task list - CustomTaskListComponent', () => {
|
describe('ProcessListCloudComponent: Injecting custom columns for task list - CustomTaskListComponent', () => {
|
||||||
|
@ -27,7 +27,8 @@ import {
|
|||||||
Input,
|
Input,
|
||||||
ViewChild,
|
ViewChild,
|
||||||
Inject,
|
Inject,
|
||||||
OnDestroy
|
OnDestroy,
|
||||||
|
Optional
|
||||||
} from '@angular/core';
|
} from '@angular/core';
|
||||||
import {
|
import {
|
||||||
DataTableSchema,
|
DataTableSchema,
|
||||||
@ -45,13 +46,13 @@ import {
|
|||||||
DataColumn
|
DataColumn
|
||||||
} from '@alfresco/adf-core';
|
} from '@alfresco/adf-core';
|
||||||
import { ProcessListCloudService } from '../services/process-list-cloud.service';
|
import { ProcessListCloudService } from '../services/process-list-cloud.service';
|
||||||
import { BehaviorSubject, Subject, of } from 'rxjs';
|
import { BehaviorSubject, Subject } from 'rxjs';
|
||||||
import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model';
|
import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model';
|
||||||
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
|
import { ProcessListRequestModel, ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
|
||||||
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
|
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
|
||||||
import { map, switchMap, take, takeUntil, tap } from 'rxjs/operators';
|
import { filter, map, switchMap, take, takeUntil } from 'rxjs/operators';
|
||||||
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
import { PreferenceCloudServiceInterface } from '../../../services/preference-cloud.interface';
|
||||||
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN } from '../../../services/cloud-token.service';
|
import { PROCESS_LISTS_PREFERENCES_SERVICE_TOKEN, PROCESS_SEARCH_API_METHOD_TOKEN } from '../../../services/cloud-token.service';
|
||||||
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
|
import { ProcessListCloudPreferences } from '../models/process-cloud-preferences';
|
||||||
import { ProcessListDatatableAdapter } from '../datatable/process-list-datatable-adapter';
|
import { ProcessListDatatableAdapter } from '../datatable/process-list-datatable-adapter';
|
||||||
import { ProcessListDataColumnCustomData, PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data';
|
import { ProcessListDataColumnCustomData, PROCESS_LIST_CUSTOM_VARIABLE_COLUMN } from '../../../models/data-column-custom-data';
|
||||||
@ -66,10 +67,8 @@ const PRESET_KEY = 'adf-cloud-process-list.presets';
|
|||||||
encapsulation: ViewEncapsulation.None
|
encapsulation: ViewEncapsulation.None
|
||||||
})
|
})
|
||||||
export class ProcessListCloudComponent
|
export class ProcessListCloudComponent
|
||||||
extends DataTableSchema<ProcessListDataColumnCustomData>
|
extends DataTableSchema<ProcessListDataColumnCustomData>
|
||||||
implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy
|
implements OnChanges, AfterContentInit, PaginatedComponent, OnDestroy {
|
||||||
// eslint-disable-next-line @typescript-eslint/brace-style
|
|
||||||
{
|
|
||||||
@ViewChild(DataTableComponent)
|
@ViewChild(DataTableComponent)
|
||||||
dataTable: DataTableComponent;
|
dataTable: DataTableComponent;
|
||||||
|
|
||||||
@ -202,6 +201,34 @@ export class ProcessListCloudComponent
|
|||||||
@Input()
|
@Input()
|
||||||
isResizingEnabled: boolean = false;
|
isResizingEnabled: boolean = false;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the processes. Display only processes with names matching any of the supplied strings.
|
||||||
|
* This input will be used only if PROCESS_SEARCH_API_METHOD_TOKEN is provided with 'POST' value.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
names: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the processes. Display only processes started by any of the users whose usernames are present in the array.
|
||||||
|
* This input will be used only if PROCESS_SEARCH_API_METHOD_TOKEN is provided with 'POST' value.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
initiators: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the processes. Display only processes present in any of the specified app versions.
|
||||||
|
* This input will be used only if PROCESS_SEARCH_API_METHOD_TOKEN is provided with 'POST' value.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
appVersions: string[] = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Filter the processes. Display only processes with provided statuses.
|
||||||
|
* This input will be used only if PROCESS_SEARCH_API_METHOD_TOKEN is provided with 'POST' value.
|
||||||
|
*/
|
||||||
|
@Input()
|
||||||
|
statuses: string[] = [];
|
||||||
|
|
||||||
/** Emitted when a row in the process list is clicked. */
|
/** Emitted when a row in the process list is clicked. */
|
||||||
@Output()
|
@Output()
|
||||||
rowClick: EventEmitter<string> = new EventEmitter<string>();
|
rowClick: EventEmitter<string> = new EventEmitter<string>();
|
||||||
@ -242,11 +269,13 @@ export class ProcessListCloudComponent
|
|||||||
rows: any[] = [];
|
rows: any[] = [];
|
||||||
formattedSorting: any[];
|
formattedSorting: any[];
|
||||||
requestNode: ProcessQueryCloudRequestModel;
|
requestNode: ProcessQueryCloudRequestModel;
|
||||||
|
processListRequestNode: ProcessListRequestModel;
|
||||||
dataAdapter: ProcessListDatatableAdapter;
|
dataAdapter: ProcessListDatatableAdapter;
|
||||||
|
|
||||||
private defaultSorting = { key: 'startDate', direction: 'desc' };
|
private defaultSorting = { key: 'startDate', direction: 'desc' };
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(PROCESS_SEARCH_API_METHOD_TOKEN) @Optional() private searchMethod: 'GET' | 'POST',
|
||||||
private processListCloudService: ProcessListCloudService,
|
private processListCloudService: ProcessListCloudService,
|
||||||
appConfigService: AppConfigService,
|
appConfigService: AppConfigService,
|
||||||
private userPreferences: UserPreferencesService,
|
private userPreferences: UserPreferencesService,
|
||||||
@ -333,13 +362,23 @@ export class ProcessListCloudComponent
|
|||||||
|
|
||||||
this.isColumnSchemaCreated$
|
this.isColumnSchemaCreated$
|
||||||
.pipe(
|
.pipe(
|
||||||
switchMap(() => of(this.createRequestNode())),
|
filter((isColumnSchemaCreated) => !!isColumnSchemaCreated),
|
||||||
tap((requestNode) => (this.requestNode = requestNode)),
|
take(1),
|
||||||
switchMap((requestNode) => this.processListCloudService.getProcessByRequest(requestNode)),
|
switchMap(() => {
|
||||||
|
if (this.searchMethod === 'POST') {
|
||||||
|
const requestNode = this.createProcessListRequestNode();
|
||||||
|
this.processListRequestNode = requestNode;
|
||||||
|
return this.processListCloudService.fetchProcessList(requestNode).pipe(take(1));
|
||||||
|
} else {
|
||||||
|
const requestNode = this.createRequestNode();
|
||||||
|
this.requestNode = requestNode;
|
||||||
|
return this.processListCloudService.getProcessByRequest(requestNode).pipe(take(1));
|
||||||
|
}
|
||||||
|
}),
|
||||||
takeUntil(this.onDestroy$)
|
takeUntil(this.onDestroy$)
|
||||||
)
|
)
|
||||||
.subscribe(
|
.subscribe({
|
||||||
(processes) => {
|
next: (processes) => {
|
||||||
this.rows = this.variableMapperService.mapVariablesByColumnTitle(processes.list.entries, this.columns);
|
this.rows = this.variableMapperService.mapVariablesByColumnTitle(processes.list.entries, this.columns);
|
||||||
|
|
||||||
this.dataAdapter = new ProcessListDatatableAdapter(this.rows, this.columns);
|
this.dataAdapter = new ProcessListDatatableAdapter(this.rows, this.columns);
|
||||||
@ -348,11 +387,11 @@ export class ProcessListCloudComponent
|
|||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
this.pagination.next(processes.list.pagination);
|
this.pagination.next(processes.list.pagination);
|
||||||
},
|
},
|
||||||
(error) => {
|
error: (error) => {
|
||||||
this.error.emit(error);
|
this.error.emit(error);
|
||||||
this.isLoading = false;
|
this.isLoading = false;
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private isAnyPropertyChanged(changes: SimpleChanges): boolean {
|
private isAnyPropertyChanged(changes: SimpleChanges): boolean {
|
||||||
@ -481,6 +520,32 @@ export class ProcessListCloudComponent
|
|||||||
this.executeRowAction.emit(row);
|
this.executeRowAction.emit(row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createProcessListRequestNode(): ProcessListRequestModel {
|
||||||
|
const requestNode = {
|
||||||
|
appName: this.appName,
|
||||||
|
pagination: {
|
||||||
|
maxItems: this.size,
|
||||||
|
skipCount: this.skipCount
|
||||||
|
},
|
||||||
|
sorting: this.sorting,
|
||||||
|
name: this.names,
|
||||||
|
initiator: this.initiators,
|
||||||
|
appVersion: this.appVersions,
|
||||||
|
status: this.statuses,
|
||||||
|
lastModifiedFrom: this.lastModifiedFrom?.toISOString() || '',
|
||||||
|
lasModifiedTo: this.lastModifiedTo?.toISOString() || '',
|
||||||
|
startFrom: this.startFrom,
|
||||||
|
startTo: this.startTo,
|
||||||
|
completedFrom: this.completedFrom,
|
||||||
|
completedTo: this.completedTo,
|
||||||
|
suspendedFrom: this.suspendedFrom,
|
||||||
|
suspendedTo: this.suspendedTo,
|
||||||
|
variableKeys: this.getVariableDefinitionsRequestModel()
|
||||||
|
};
|
||||||
|
|
||||||
|
return new ProcessListRequestModel(requestNode);
|
||||||
|
}
|
||||||
|
|
||||||
private createRequestNode(): ProcessQueryCloudRequestModel {
|
private createRequestNode(): ProcessQueryCloudRequestModel {
|
||||||
const requestNode = {
|
const requestNode = {
|
||||||
appName: this.appName,
|
appName: this.appName,
|
||||||
|
@ -15,7 +15,9 @@
|
|||||||
* limitations under the License.
|
* limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { Pagination } from '@alfresco/js-api';
|
||||||
import { ProcessListCloudSortingModel } from './process-list-sorting.model';
|
import { ProcessListCloudSortingModel } from './process-list-sorting.model';
|
||||||
|
import { ProcessFilterCloudModel } from '../../process-filters/models/process-filter-cloud.model';
|
||||||
|
|
||||||
export class ProcessQueryCloudRequestModel {
|
export class ProcessQueryCloudRequestModel {
|
||||||
appName: string;
|
appName: string;
|
||||||
@ -76,3 +78,80 @@ export class ProcessQueryCloudRequestModel {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ProcessListRequestProcessVariableFilter {
|
||||||
|
processDefinitionKey?: string;
|
||||||
|
name?: string;
|
||||||
|
type?: string;
|
||||||
|
value?: string;
|
||||||
|
operator?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProcessListRequestModel {
|
||||||
|
appName: string;
|
||||||
|
pagination?: Pagination;
|
||||||
|
sorting?: ProcessListCloudSortingModel[];
|
||||||
|
|
||||||
|
name?: string[];
|
||||||
|
initiator?: string[];
|
||||||
|
appVersion?: string[];
|
||||||
|
status?: string[];
|
||||||
|
lastModifiedFrom?: string;
|
||||||
|
lasModifiedTo?: string;
|
||||||
|
startFrom?: string;
|
||||||
|
startTo?: string;
|
||||||
|
completedFrom?: string;
|
||||||
|
completedTo?: string;
|
||||||
|
suspendedFrom?: string;
|
||||||
|
suspendedTo?: string;
|
||||||
|
|
||||||
|
processVariableFilters?: ProcessListRequestProcessVariableFilter[];
|
||||||
|
variableKeys?: string[];
|
||||||
|
|
||||||
|
constructor(obj: Partial<ProcessListRequestModel>) {
|
||||||
|
if (!obj.appName) {
|
||||||
|
throw new Error('appName not configured');
|
||||||
|
}
|
||||||
|
|
||||||
|
this.appName = obj.appName;
|
||||||
|
this.pagination = obj.pagination;
|
||||||
|
this.sorting = obj.sorting;
|
||||||
|
|
||||||
|
this.name = obj.name;
|
||||||
|
this.initiator = obj.initiator;
|
||||||
|
this.appVersion = obj.appVersion;
|
||||||
|
this.status = obj.status;
|
||||||
|
this.lastModifiedFrom = obj.lastModifiedFrom;
|
||||||
|
this.lasModifiedTo = obj.lasModifiedTo;
|
||||||
|
this.startFrom = obj.startFrom;
|
||||||
|
this.startTo = obj.startTo;
|
||||||
|
this.completedFrom = obj.completedFrom;
|
||||||
|
this.completedTo = obj.completedTo;
|
||||||
|
this.suspendedFrom = obj.suspendedFrom;
|
||||||
|
this.suspendedTo = obj.suspendedTo;
|
||||||
|
this.variableKeys = obj.variableKeys;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class ProcessFilterCloudAdapter extends ProcessListRequestModel {
|
||||||
|
constructor(filter: ProcessFilterCloudModel) {
|
||||||
|
super({
|
||||||
|
appName: filter.appName,
|
||||||
|
pagination: { maxItems: 25, skipCount: 0 },
|
||||||
|
sorting: [{ orderBy: filter.sort, direction: filter.order }],
|
||||||
|
|
||||||
|
name: filter.processDefinitionNames,
|
||||||
|
initiator: filter.initiators,
|
||||||
|
appVersion: filter.appVersions,
|
||||||
|
status: filter.statuses,
|
||||||
|
lastModifiedFrom: filter.lastModifiedFrom?.toISOString(),
|
||||||
|
lasModifiedTo: filter.lastModifiedTo?.toISOString(),
|
||||||
|
startFrom: filter.startFrom,
|
||||||
|
startTo: filter.startTo,
|
||||||
|
completedFrom: filter.completedFrom,
|
||||||
|
completedTo: filter.completedTo,
|
||||||
|
suspendedFrom: filter.suspendedFrom,
|
||||||
|
suspendedTo: filter.suspendedTo
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -23,6 +23,5 @@ export * from './models/process-list-sorting.model';
|
|||||||
export * from './models/process-cloud-preferences';
|
export * from './models/process-cloud-preferences';
|
||||||
|
|
||||||
export * from './services/process-list-cloud.service';
|
export * from './services/process-list-cloud.service';
|
||||||
export * from './services/process-task-list-cloud.service';
|
|
||||||
|
|
||||||
export * from './process-list-cloud.module';
|
export * from './process-list-cloud.module';
|
||||||
|
@ -17,10 +17,10 @@
|
|||||||
|
|
||||||
import { fakeAsync, TestBed } from '@angular/core/testing';
|
import { fakeAsync, TestBed } from '@angular/core/testing';
|
||||||
import { ProcessListCloudService } from './process-list-cloud.service';
|
import { ProcessListCloudService } from './process-list-cloud.service';
|
||||||
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
|
import { ProcessListRequestModel, ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
|
||||||
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
|
||||||
import { AdfHttpClient } from '@alfresco/adf-core/api';
|
import { AdfHttpClient } from '@alfresco/adf-core/api';
|
||||||
import { firstValueFrom } from 'rxjs';
|
import { catchError, firstValueFrom, of } from 'rxjs';
|
||||||
|
|
||||||
describe('ProcessListCloudService', () => {
|
describe('ProcessListCloudService', () => {
|
||||||
let service: ProcessListCloudService;
|
let service: ProcessListCloudService;
|
||||||
@ -44,6 +44,7 @@ describe('ProcessListCloudService', () => {
|
|||||||
requestSpy = spyOn(adfHttpClient, 'request');
|
requestSpy = spyOn(adfHttpClient, 'request');
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('getProcessByRequest', () => {
|
||||||
it('should append to the call all the parameters', (done) => {
|
it('should append to the call all the parameters', (done) => {
|
||||||
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' } as ProcessQueryCloudRequestModel;
|
const processRequest = { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' } as ProcessQueryCloudRequestModel;
|
||||||
requestSpy.and.callFake(returnCallQueryParameters);
|
requestSpy.and.callFake(returnCallQueryParameters);
|
||||||
@ -111,6 +112,65 @@ describe('ProcessListCloudService', () => {
|
|||||||
expect(result.maxItems).toBe(1);
|
expect(result.maxItems).toBe(1);
|
||||||
expect(result.service).toBe('fake-service');
|
expect(result.service).toBe('fake-service');
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fetchProcessList', () => {
|
||||||
|
it('should append to the call all the parameters', async () => {
|
||||||
|
const processRequest = {
|
||||||
|
appName: 'fakeName',
|
||||||
|
pagination: { skipCount: 0, maxItems: 20 }
|
||||||
|
} as ProcessListRequestModel;
|
||||||
|
requestSpy.and.callFake(returnCallQueryParameters);
|
||||||
|
|
||||||
|
const res = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||||
|
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.skipCount).toBe(0);
|
||||||
|
expect(res.maxItems).toBe(20);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should concat the app name to the request url', async () => {
|
||||||
|
const processRequest = {
|
||||||
|
appName: 'fakeName',
|
||||||
|
pagination: { skipCount: 0, maxItems: 20 }
|
||||||
|
} as ProcessListRequestModel;
|
||||||
|
requestSpy.and.callFake(returnCallUrl);
|
||||||
|
|
||||||
|
const res = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||||
|
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res).toContain('/fakeName/query/v1/process-instances/search');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should concat the sorting to append as parameters', async () => {
|
||||||
|
const processRequest = {
|
||||||
|
appName: 'fakeName',
|
||||||
|
pagination: { skipCount: 0, maxItems: 20 },
|
||||||
|
sorting: [
|
||||||
|
{ orderBy: 'NAME', direction: 'DESC' },
|
||||||
|
{ orderBy: 'TITLE', direction: 'ASC' }
|
||||||
|
]
|
||||||
|
} as ProcessListRequestModel;
|
||||||
|
requestSpy.and.callFake(returnCallQueryParameters);
|
||||||
|
|
||||||
|
const res = await firstValueFrom(service.fetchProcessList(processRequest));
|
||||||
|
|
||||||
|
expect(res).toBeDefined();
|
||||||
|
expect(res).not.toBeNull();
|
||||||
|
expect(res.sort).toBe('NAME,DESC&TITLE,ASC');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should return an error when app name is not specified', async () => {
|
||||||
|
const taskRequest = { appName: null } as ProcessListRequestModel;
|
||||||
|
requestSpy.and.callFake(returnCallUrl);
|
||||||
|
|
||||||
|
const res = await firstValueFrom(service.fetchProcessList(taskRequest).pipe(catchError((error) => of(error.message))));
|
||||||
|
|
||||||
|
expect(res).toBe('Appname not configured');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
describe('getAdminProcessRequest', () => {
|
describe('getAdminProcessRequest', () => {
|
||||||
it('should append to the call all the parameters', async () => {
|
it('should append to the call all the parameters', async () => {
|
||||||
|
@ -16,7 +16,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
|
import { ProcessListRequestModel, ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
|
||||||
import { Observable, throwError } from 'rxjs';
|
import { Observable, throwError } from 'rxjs';
|
||||||
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
|
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
|
||||||
import { BaseCloudService } from '../../../services/base-cloud.service';
|
import { BaseCloudService } from '../../../services/base-cloud.service';
|
||||||
@ -56,6 +56,7 @@ export class ProcessListCloudService extends BaseCloudService {
|
|||||||
/**
|
/**
|
||||||
* Finds a process using an object with optional query properties.
|
* Finds a process using an object with optional query properties.
|
||||||
*
|
*
|
||||||
|
* @deprecated From Activiti 8.7.0 forward, use ProcessListCloudService.fetchProcessList instead.
|
||||||
* @param requestNode Query object
|
* @param requestNode Query object
|
||||||
* @param queryUrl Query url
|
* @param queryUrl Query url
|
||||||
* @returns Process information
|
* @returns Process information
|
||||||
@ -67,6 +68,76 @@ export class ProcessListCloudService extends BaseCloudService {
|
|||||||
return this.getProcess(callback, defaultQueryUrl, requestNode, queryUrl);
|
return this.getProcess(callback, defaultQueryUrl, requestNode, queryUrl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Available from Activiti version 8.7.0 onwards.
|
||||||
|
* Retrieves a list of processes using an object with optional query properties.
|
||||||
|
*
|
||||||
|
* @param requestNode Query object
|
||||||
|
* @param queryUrl Query url
|
||||||
|
* @returns List of processes
|
||||||
|
*/
|
||||||
|
fetchProcessList(requestNode: ProcessListRequestModel, queryUrl?: string): Observable<any> {
|
||||||
|
if (!requestNode?.appName) {
|
||||||
|
return throwError(() => new Error('Appname not configured'));
|
||||||
|
}
|
||||||
|
|
||||||
|
queryUrl = queryUrl || `${this.getBasePath(requestNode.appName)}/query/v1/process-instances/search`;
|
||||||
|
|
||||||
|
const queryParams = {
|
||||||
|
maxItems: requestNode.pagination?.maxItems || 25,
|
||||||
|
skipCount: requestNode.pagination?.skipCount || 0,
|
||||||
|
sort: this.buildSortingParam(requestNode.sorting || [])
|
||||||
|
};
|
||||||
|
|
||||||
|
const queryData = this.buildQueryData(requestNode);
|
||||||
|
return this.post<any, any>(queryUrl, queryData, queryParams).pipe(
|
||||||
|
map((response: any) => {
|
||||||
|
const entries = response.list?.entries;
|
||||||
|
if (entries) {
|
||||||
|
response.list.entries = entries.map((entryData) => entryData.entry);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
})
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected buildQueryData(requestNode: ProcessListRequestModel) {
|
||||||
|
const variableKeys = requestNode.variableKeys?.length > 0 ? requestNode.variableKeys.join(',') : undefined;
|
||||||
|
|
||||||
|
const queryData: any = {
|
||||||
|
name: requestNode.name,
|
||||||
|
initiator: requestNode.initiator,
|
||||||
|
appVersion: requestNode.appVersion,
|
||||||
|
status: requestNode.status,
|
||||||
|
lastModifiedFrom: requestNode.lastModifiedFrom,
|
||||||
|
lasModifiedTo: requestNode.lasModifiedTo,
|
||||||
|
startFrom: requestNode.startFrom,
|
||||||
|
startTo: requestNode.startTo,
|
||||||
|
completedFrom: requestNode.completedFrom,
|
||||||
|
completedTo: requestNode.completedTo,
|
||||||
|
suspendedFrom: requestNode.suspendedFrom,
|
||||||
|
suspendedTo: requestNode.suspendedTo,
|
||||||
|
variableKeys: variableKeys
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(queryData).forEach((key) => {
|
||||||
|
const value = queryData[key];
|
||||||
|
const isValueEmpty = !value;
|
||||||
|
const isValueArrayWithEmptyValue = Array.isArray(value) && (value.length === 0 || value[0] === null);
|
||||||
|
if (isValueEmpty || isValueArrayWithEmptyValue) {
|
||||||
|
delete queryData[key];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return queryData;
|
||||||
|
}
|
||||||
|
|
||||||
|
getProcessListCounter(requestNode: ProcessListRequestModel): Observable<number> {
|
||||||
|
if (!requestNode.appName) {
|
||||||
|
return throwError(() => new Error('Appname not configured'));
|
||||||
|
}
|
||||||
|
return this.fetchProcessList(requestNode).pipe(map((processes) => processes.list.pagination.totalItems));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finds a process using an object with optional query properties.
|
* Finds a process using an object with optional query properties.
|
||||||
*
|
*
|
||||||
|
@ -1,93 +0,0 @@
|
|||||||
/*!
|
|
||||||
* @license
|
|
||||||
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
|
||||||
import { Observable, throwError } from 'rxjs';
|
|
||||||
import { BaseCloudService } from '../../../services/base-cloud.service';
|
|
||||||
import { map } from 'rxjs/operators';
|
|
||||||
import { TaskQueryCloudRequestModel } from '../../../models/filter-cloud-model';
|
|
||||||
import { TaskCloudNodePaging } from '../../../models/task-cloud.model';
|
|
||||||
import { TaskListCloudSortingModel } from '../../../models/task-list-sorting.model';
|
|
||||||
|
|
||||||
@Injectable({ providedIn: 'root' })
|
|
||||||
export class ProcessTaskListCloudService extends BaseCloudService {
|
|
||||||
/**
|
|
||||||
* Finds a task using an object with optional query properties.
|
|
||||||
*
|
|
||||||
* @param requestNode Query object
|
|
||||||
* @param queryUrl Query url
|
|
||||||
* @returns Task information
|
|
||||||
*/
|
|
||||||
getTaskByRequest(requestNode: TaskQueryCloudRequestModel, queryUrl?: string): Observable<any> {
|
|
||||||
if (requestNode.appName || requestNode.appName === '') {
|
|
||||||
queryUrl = queryUrl || `${this.getBasePath(requestNode.appName)}/query/v1/process-instances/${requestNode.processInstanceId}/tasks`;
|
|
||||||
const queryParams = this.buildQueryParams(requestNode);
|
|
||||||
const sortingParams = this.buildSortingParam(requestNode.sorting);
|
|
||||||
if (sortingParams) {
|
|
||||||
queryParams['sort'] = sortingParams;
|
|
||||||
}
|
|
||||||
return this.get<TaskCloudNodePaging>(queryUrl, queryParams).pipe(
|
|
||||||
map((response) => {
|
|
||||||
const entries = response.list?.entries;
|
|
||||||
if (entries) {
|
|
||||||
// TODO: this is a hack of the model and should be revisited
|
|
||||||
response.list.entries = entries.map((entryData: any) => entryData.entry);
|
|
||||||
}
|
|
||||||
return response;
|
|
||||||
})
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return throwError('Appname not configured');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildQueryParams(requestNode: TaskQueryCloudRequestModel): any {
|
|
||||||
const queryParam: any = {};
|
|
||||||
for (const property in requestNode) {
|
|
||||||
if (
|
|
||||||
Object.prototype.hasOwnProperty.call(requestNode, property) &&
|
|
||||||
!this.isExcludedField(property) &&
|
|
||||||
this.isPropertyValueValid(requestNode, property)
|
|
||||||
) {
|
|
||||||
queryParam[property] = requestNode[property];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return queryParam;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected isExcludedField(property: string): boolean {
|
|
||||||
return property === 'appName' || property === 'sorting';
|
|
||||||
}
|
|
||||||
|
|
||||||
protected isPropertyValueValid(requestNode: TaskQueryCloudRequestModel, property: string): boolean {
|
|
||||||
return requestNode[property] !== '' && requestNode[property] !== null && requestNode[property] !== undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected buildSortingParam(models: TaskListCloudSortingModel[]): string {
|
|
||||||
let finalSorting: string = '';
|
|
||||||
if (models) {
|
|
||||||
for (const sort of models) {
|
|
||||||
if (!finalSorting) {
|
|
||||||
finalSorting = `${sort.orderBy},${sort.direction}`;
|
|
||||||
} else {
|
|
||||||
finalSorting = `${finalSorting}&${sort.orderBy},${sort.direction}`;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return encodeURI(finalSorting);
|
|
||||||
}
|
|
||||||
}
|
|
@ -34,3 +34,9 @@ export const TASK_LIST_CLOUD_TOKEN = new InjectionToken<TaskListCloudServiceInte
|
|||||||
* 'POST' value should be provided only if the used Activiti version is 8.7.0 or higher.
|
* 'POST' value should be provided only if the used Activiti version is 8.7.0 or higher.
|
||||||
*/
|
*/
|
||||||
export const TASK_SEARCH_API_METHOD_TOKEN = new InjectionToken<'GET' | 'POST'>('task-search-method');
|
export const TASK_SEARCH_API_METHOD_TOKEN = new InjectionToken<'GET' | 'POST'>('task-search-method');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Token used to indicate the API used to search for processes.
|
||||||
|
* 'POST' value should be provided only if the used Activiti version is 8.7.0 or higher.
|
||||||
|
*/
|
||||||
|
export const PROCESS_SEARCH_API_METHOD_TOKEN = new InjectionToken<'GET' | 'POST'>('process-search-method');
|
||||||
|
Loading…
x
Reference in New Issue
Block a user