[ADF-4979] Add onChanges detection for Task Header Cloud component (#5208)

* [ADF-4979] Add onChanges detection for Task Header Cloud component

* [ADF-4979] Revert licenses.txt changes

* [ADF-4979] Documentation added for the taskError Event
This commit is contained in:
arditdomi
2019-11-06 11:07:48 +00:00
committed by Maurizio Vitale
parent 7d36400dbd
commit 3c1097fb84
25 changed files with 380 additions and 238 deletions

View File

@@ -50,7 +50,7 @@ export class ProcessHeaderCloudComponent implements OnChanges {
}
ngOnChanges() {
if ((this.appName || this.appName === '') && this.processInstanceId) {
if (this.appName && this.processInstanceId) {
this.loadProcessInstanceDetails(this.appName, this.processInstanceId);
}
}

View File

@@ -45,7 +45,7 @@ export class ProcessHeaderCloudService extends BaseCloudService {
* @returns Process instance details
*/
getProcessInstanceById(appName: string, processInstanceId: string): Observable<ProcessInstanceCloud> {
if ((appName || appName === '') && processInstanceId) {
if (appName && processInstanceId) {
const queryUrl = `${this.getBasePath(appName)}/query/v1/process-instances/${processInstanceId}`;
return from(this.alfrescoApiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'GET',

View File

@@ -185,7 +185,7 @@ export class ProcessListCloudComponent extends DataTableSchema implements OnChan
reload() {
this.requestNode = this.createRequestNode();
if (this.requestNode.appName || this.requestNode.appName === '') {
if (this.requestNode.appName) {
this.load(this.requestNode);
} else {
this.rows = [];

View File

@@ -39,7 +39,7 @@ export class ProcessListCloudService extends BaseCloudService {
* @returns Process information
*/
getProcessByRequest(requestNode: ProcessQueryCloudRequestModel): Observable<any> {
if (requestNode.appName || requestNode.appName === '') {
if (requestNode.appName) {
const queryUrl = this.buildQueryUrl(requestNode);
const queryParams = this.buildQueryParams(requestNode);
const sortingParams = this.buildSortingParam(requestNode.sorting);

View File

@@ -48,7 +48,7 @@ export class StartProcessCloudService extends BaseCloudService {
*/
getProcessDefinitions(appName: string): Observable<ProcessDefinitionCloud[]> {
if (appName || appName === '') {
if (appName) {
const queryUrl = `${this.getBasePath(appName)}/rb/v1/process-definitions`;
return from(this.alfrescoApiService.getInstance()

View File

@@ -41,7 +41,7 @@ export class UserPreferenceCloudService extends BaseCloudService implements Pref
* @returns List of user preferences
*/
getPreferences(appName: string): Observable<any> {
if (appName || appName === '') {
if (appName) {
const uri = this.buildPreferenceServiceUri(appName);
return from(this.alfrescoApiService.getInstance()
.oauth2Auth.callCustomApi(uri, 'GET',
@@ -62,7 +62,7 @@ export class UserPreferenceCloudService extends BaseCloudService implements Pref
* @returns Observable of user preference
*/
getPreferenceByKey(appName: string, key: string): Observable<any> {
if (appName || appName === '') {
if (appName) {
const uri = this.buildPreferenceServiceUri(appName) + '/' + `${key}`;
return from(
this.alfrescoApiService.getInstance()
@@ -85,7 +85,7 @@ export class UserPreferenceCloudService extends BaseCloudService implements Pref
* @returns Observable of created user preferences
*/
createPreference(appName: string, key: string, newPreference: any): Observable<any> {
if (appName || appName === '') {
if (appName) {
const uri = this.buildPreferenceServiceUri(appName) + '/' + `${key}`;
const requestPayload = JSON.stringify(newPreference);
return from(this.alfrescoApiService.getInstance()
@@ -121,7 +121,7 @@ export class UserPreferenceCloudService extends BaseCloudService implements Pref
* @returns Observable of delete operation status
*/
deletePreference(appName: string, key: string): Observable<any> {
if (appName || appName === '') {
if (appName) {
const uri = this.buildPreferenceServiceUri(appName) + '/' + `${key}`;
return from(this.alfrescoApiService.getInstance()
.oauth2Auth.callCustomApi(uri, 'DELETE',

View File

@@ -68,7 +68,7 @@ export class ClaimTaskDirective implements OnInit {
}
isAppValid(): boolean {
return (this.appName && this.appName.length > 0) || (this.appName === '');
return (this.appName && this.appName.length > 0);
}
@HostListener('click')

View File

@@ -65,7 +65,7 @@ export class CompleteTaskDirective implements OnInit {
}
isAppValid(): boolean {
return (this.appName && this.appName.length > 0) || (this.appName === '');
return (this.appName && this.appName.length > 0);
}
@HostListener('click')

View File

@@ -66,7 +66,7 @@ export class UnClaimTaskDirective implements OnInit {
}
isAppValid(): boolean {
return (this.appName && this.appName.length > 0) || (this.appName === '');
return (this.appName && this.appName.length > 0);
}
@HostListener('click')

View File

@@ -17,7 +17,7 @@
import { Injectable } from '@angular/core';
import { AlfrescoApiService, LogService, AppConfigService, IdentityUserService } from '@alfresco/adf-core';
import { from, throwError, Observable, of } from 'rxjs';
import { from, throwError, Observable, of, Subject } from 'rxjs';
import { catchError, map } from 'rxjs/operators';
import { TaskDetailsCloudModel, StartTaskCloudResponseModel } from '../start-task/models/task-details-cloud.model';
import { BaseCloudService } from '../../services/base-cloud.service';
@@ -32,6 +32,9 @@ export class TaskCloudService extends BaseCloudService {
accepts = ['application/json'];
returnType = Object;
private dataChangesDetected = new Subject();
dataChangesDetected$: Observable<object>;
constructor(
private apiService: AlfrescoApiService,
private appConfigService: AppConfigService,
@@ -40,6 +43,7 @@ export class TaskCloudService extends BaseCloudService {
) {
super();
this.contextRoot = this.appConfigService.get('bpmHost', '');
this.dataChangesDetected$ = this.dataChangesDetected.asObservable();
}
/**
@@ -49,7 +53,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Details of the task that was completed
*/
completeTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
if ((appName || appName === '') && taskId) {
if (appName && taskId) {
const queryUrl = this.buildCompleteTaskUrl(appName, taskId);
const bodyParam = { 'payloadType': 'CompleteTaskPayload' };
const pathParams = {}, queryParams = {}, headerParams = {},
@@ -116,7 +120,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Details of the claimed task
*/
claimTask(appName: string, taskId: string, assignee: string): Observable<TaskDetailsCloudModel> {
if ((appName || appName === '') && taskId) {
if (appName && taskId) {
const queryUrl = `${this.getBasePath(appName)}/rb/v1/tasks/${taskId}/claim?assignee=${assignee}`;
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'POST',
@@ -143,7 +147,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Details of the task that was unclaimed
*/
unclaimTask(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
if ((appName || appName === '') && taskId) {
if (appName && taskId) {
const queryUrl = `${this.getBasePath(appName)}/rb/v1/tasks/${taskId}/release`;
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'POST',
@@ -170,7 +174,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Task details
*/
getTaskById(appName: string, taskId: string): Observable<TaskDetailsCloudModel> {
if ((appName || appName === '') && taskId) {
if (appName && taskId) {
const queryUrl = `${this.getBasePath(appName)}/query/v1/tasks/${taskId}`;
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'GET',
@@ -180,6 +184,7 @@ export class TaskCloudService extends BaseCloudService {
this.returnType, null, null)
).pipe(
map((res: any) => {
this.dataChangesDetected.next();
return new TaskDetailsCloudModel(res.entry);
}),
catchError((err) => this.handleError(err))
@@ -224,7 +229,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Updated task details
*/
updateTask(appName: string, taskId: string, updatePayload: any): Observable<TaskDetailsCloudModel> {
if ((appName || appName === '') && taskId) {
if (appName && taskId) {
updatePayload.payloadType = 'UpdateTaskPayload';
const queryUrl = `${this.getBasePath(appName)}/rb/v1/tasks/${taskId}`;
@@ -253,7 +258,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Candidate users
*/
getCandidateUsers(appName: string, taskId: string): Observable<string[]> {
if ((appName || appName === '') && taskId) {
if (appName && taskId) {
const queryUrl = `${this.getBasePath(appName)}/query/v1/tasks/${taskId}/candidate-users`;
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'GET',
@@ -280,7 +285,7 @@ export class TaskCloudService extends BaseCloudService {
* @returns Candidate groups
*/
getCandidateGroups(appName: string, taskId: string): Observable<string[]> {
if ((appName || appName === '') && taskId) {
if (appName && taskId) {
const queryUrl = `${this.getBasePath(appName)}/query/v1/tasks/${taskId}/candidate-groups`;
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'GET',

View File

@@ -1,4 +1,4 @@
<h3 class="adf-task-title">{{ taskDetails.name }}</h3>
<h3 class="adf-task-title">{{ taskDetails?.name }}</h3>
<div class="adf-task-header-container">
<mat-card *ngIf="isTaskValid()" class="adf-card-container">

View File

@@ -21,7 +21,7 @@ import { TaskHeaderCloudComponent } from './task-header-cloud.component';
import { assignedTaskDetailsCloudMock } from '../mocks/task-details-cloud.mock';
import { TaskHeaderCloudModule } from '../task-header-cloud.module';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { of, throwError } from 'rxjs';
import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { RouterTestingModule } from '@angular/router/testing';
import { TaskCloudService } from '../../services/task-cloud.service';
@@ -35,7 +35,12 @@ describe('TaskHeaderCloudComponent', () => {
let getCandidateGroupsSpy: jasmine.Spy;
let getCandidateUsersSpy: jasmine.Spy;
const identityUserMock = { username: 'testuser', firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
const identityUserMock = {
username: 'testuser',
firstName: 'fake-identity-first-name',
lastName: 'fake-identity-last-name',
email: 'fakeIdentity@email.com'
};
const mockCandidateUsers = ['mockuser1', 'mockuser2', 'mockuser3'];
const mockCandidateGroups = ['mockgroup1', 'mockgroup2', 'mockgroup3'];
@@ -48,200 +53,222 @@ describe('TaskHeaderCloudComponent', () => {
providers: [IdentityUserService]
});
beforeEach(() => {
fixture = TestBed.createComponent(TaskHeaderCloudComponent);
component = fixture.componentInstance;
component.appName = 'myApp';
component.taskId = assignedTaskDetailsCloudMock.id;
service = TestBed.get(TaskCloudService);
identityUserService = TestBed.get(IdentityUserService);
appConfigService = TestBed.get(AppConfigService);
spyOn(service, 'getTaskById').and.returnValue(of(assignedTaskDetailsCloudMock));
getCandidateUsersSpy = spyOn(service, 'getCandidateUsers').and.returnValue(of(mockCandidateUsers));
getCandidateGroupsSpy = spyOn(service, 'getCandidateGroups').and.returnValue(of(mockCandidateGroups));
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
});
describe('Task Details', () => {
it('should render empty component if no task details provided', async(() => {
component.appName = undefined;
component.taskId = undefined;
fixture.detectChanges();
expect(fixture.debugElement.children.length).toBe(2);
}));
it('should display assignee', async(() => {
component.ngOnInit();
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
expect(formNameEl.nativeElement.innerText).toBe('AssignedTaskUser');
});
}));
it('should display placeholder if no assignee', async(() => {
component.ngOnInit();
component.taskDetails.assignee = null;
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
expect(valueEl.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE_DEFAULT');
beforeEach(() => {
fixture = TestBed.createComponent(TaskHeaderCloudComponent);
component = fixture.componentInstance;
component.appName = 'myApp';
component.taskId = assignedTaskDetailsCloudMock.id;
service = TestBed.get(TaskCloudService);
identityUserService = TestBed.get(IdentityUserService);
appConfigService = TestBed.get(AppConfigService);
spyOn(service, 'getTaskById').and.returnValue(of(assignedTaskDetailsCloudMock));
getCandidateUsersSpy = spyOn(service, 'getCandidateUsers').and.returnValue(of(mockCandidateUsers));
getCandidateGroupsSpy = spyOn(service, 'getCandidateGroups').and.returnValue(of(mockCandidateGroups));
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
});
}));
it('should display priority', async(() => {
component.ngOnInit();
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-priority"]'));
expect(formNameEl.nativeElement.innerText).toBe('5');
});
}));
it('should display error if priority is not a number', async(() => {
component.ngOnInit();
component.taskDetails.assignee = 'testuser';
fixture.detectChanges();
fixture.whenStable().then(() => {
const edit = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edit-icon-priority"]'));
edit.nativeElement.click();
it('should render empty component if no task details provided', async(() => {
component.appName = undefined;
component.taskId = undefined;
fixture.detectChanges();
expect(fixture.debugElement.children.length).toBe(2);
}));
const formPriorityEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-editinput-priority"]'));
formPriorityEl.nativeElement.value = 'stringValue';
formPriorityEl.nativeElement.dispatchEvent(new Event('input'));
it('should display assignee', async(() => {
component.ngOnChanges();
fixture.detectChanges();
const submitEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-update-priority"]'));
submitEl.nativeElement.click();
fixture.detectChanges();
const errorMessageEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-error-priority"]'));
expect(errorMessageEl).not.toBeNull();
});
}));
it('should display due date', async(() => {
component.ngOnInit();
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('Dec 18, 2018');
});
}));
it('should display placeholder if no due date', async(() => {
component.ngOnInit();
component.taskDetails.dueDate = null;
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.DUE_DATE_DEFAULT');
});
}));
it('should display the default parent value if is undefined', async(() => {
component.ngOnInit();
component.taskDetails.processInstanceId = null;
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-parentName"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_NAME_DEFAULT');
});
}));
it('should display candidate user', async(() => {
component.ngOnInit();
fixture.detectChanges();
fixture.whenStable().then(() => {
const candidateUser1 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockuser1"] span');
const candidateUser2 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockuser2"] span');
expect(getCandidateUsersSpy).toHaveBeenCalled();
expect(candidateUser1.innerText).toBe('mockuser1');
expect(candidateUser2.innerText).toBe('mockuser2');
});
}));
it('should display placeholder if no candidate users', async(() => {
component.ngOnInit();
getCandidateUsersSpy.and.returnValue(of([]));
fixture.detectChanges();
fixture.whenStable().then(() => {
const labelValue = fixture.debugElement.query(By.css('[data-automation-id="card-array-label-candidateUsers"]'));
const defaultElement = fixture.debugElement.query(By.css('[data-automation-id="card-arrayitem-default"]'));
expect(labelValue.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS');
expect(defaultElement.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS_DEFAULT');
});
}));
it('should display candidate groups', async(() => {
component.ngOnInit();
fixture.detectChanges();
fixture.whenStable().then(() => {
const candidateGroup1 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockgroup1"] span');
const candidateGroup2 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockgroup2"] span');
expect(getCandidateGroupsSpy).toHaveBeenCalled();
expect(candidateGroup1.innerText).toBe('mockgroup1');
expect(candidateGroup2.innerText).toBe('mockgroup2');
});
}));
it('should display placeholder if no candidate groups', async(() => {
component.ngOnInit();
getCandidateGroupsSpy.and.returnValue(of([]));
fixture.detectChanges();
fixture.whenStable().then(() => {
const labelValue = fixture.debugElement.query(By.css('[data-automation-id="card-array-label-candidateGroups"]'));
const defaultElement = fixture.debugElement.query(By.css('[data-automation-id="card-arrayitem-default"]'));
expect(labelValue.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS');
expect(defaultElement.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS_DEFAULT');
});
}));
describe('Config Filtering', () => {
it('should show only the properties from the configuration file', async(() => {
spyOn(appConfigService, 'get').and.returnValue(['assignee', 'status']);
component.ngOnInit();
fixture.detectChanges();
const propertyList = fixture.debugElement.queryAll(By.css('.adf-property-list .adf-property'));
fixture.whenStable().then(() => {
expect(propertyList).toBeDefined();
expect(propertyList).not.toBeNull();
expect(propertyList.length).toBe(2);
expect(propertyList[0].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE');
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
expect(formNameEl.nativeElement.innerText).toBe('AssignedTaskUser');
});
}));
it('should show all the default properties if there is no configuration', async(() => {
spyOn(appConfigService, 'get').and.returnValue(null);
component.ngOnInit();
fixture.detectChanges();
it('should display placeholder if no assignee', async(() => {
component.taskDetails.assignee = null;
component.refreshData();
fixture.detectChanges();
fixture.whenStable().then(() => {
const propertyList = fixture.debugElement.queryAll(By.css('.adf-property-list .adf-property'));
expect(propertyList).toBeDefined();
expect(propertyList).not.toBeNull();
expect(propertyList.length).toBe(component.properties.length);
expect(propertyList[0].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE');
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS');
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
expect(valueEl.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE_DEFAULT');
});
}));
it('should display priority', async(() => {
component.ngOnChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-priority"]'));
expect(formNameEl.nativeElement.innerText).toBe('5');
});
}));
it('should display error if priority is not a number', async(() => {
component.ngOnChanges();
component.taskDetails.assignee = 'testuser';
fixture.detectChanges();
fixture.whenStable().then(() => {
const edit = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edit-icon-priority"]'));
edit.nativeElement.click();
fixture.detectChanges();
const formPriorityEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-editinput-priority"]'));
formPriorityEl.nativeElement.value = 'stringValue';
formPriorityEl.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
const submitEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-update-priority"]'));
submitEl.nativeElement.click();
fixture.detectChanges();
const errorMessageEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-error-priority"]'));
expect(errorMessageEl).not.toBeNull();
});
}));
it('should display due date', async(() => {
component.ngOnChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('Dec 18, 2018');
});
}));
it('should display placeholder if no due date', async(() => {
component.taskDetails.dueDate = null;
component.refreshData();
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-dueDate"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.DUE_DATE_DEFAULT');
});
}));
it('should display the default parent value if is undefined', async(() => {
component.ngOnChanges();
component.taskDetails.processInstanceId = null;
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-parentName"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_NAME_DEFAULT');
});
}));
it('should display candidate user', async(() => {
component.ngOnChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
const candidateUser1 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockuser1"] span');
const candidateUser2 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockuser2"] span');
expect(getCandidateUsersSpy).toHaveBeenCalled();
expect(candidateUser1.innerText).toBe('mockuser1');
expect(candidateUser2.innerText).toBe('mockuser2');
});
}));
it('should display placeholder if no candidate users', async(() => {
getCandidateUsersSpy.and.returnValue(of([]));
component.refreshData();
fixture.detectChanges();
fixture.whenStable().then(() => {
const labelValue = fixture.debugElement.query(By.css('[data-automation-id="card-array-label-candidateUsers"]'));
const defaultElement = fixture.debugElement.query(By.css('[data-automation-id="card-arrayitem-default"]'));
expect(labelValue.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS');
expect(defaultElement.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS_DEFAULT');
});
}));
it('should display candidate groups', async(() => {
component.ngOnChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
const candidateGroup1 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockgroup1"] span');
const candidateGroup2 = fixture.nativeElement.querySelector('[data-automation-id="card-arrayitem-chip-mockgroup2"] span');
expect(getCandidateGroupsSpy).toHaveBeenCalled();
expect(candidateGroup1.innerText).toBe('mockgroup1');
expect(candidateGroup2.innerText).toBe('mockgroup2');
});
}));
it('should display placeholder if no candidate groups', async(() => {
getCandidateGroupsSpy.and.returnValue(of([]));
component.refreshData();
fixture.detectChanges();
fixture.whenStable().then(() => {
const labelValue = fixture.debugElement.query(By.css('[data-automation-id="card-array-label-candidateGroups"]'));
const defaultElement = fixture.debugElement.query(By.css('[data-automation-id="card-arrayitem-default"]'));
expect(labelValue.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS');
expect(defaultElement.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS_DEFAULT');
});
}));
describe('Config Filtering', () => {
it('should show only the properties from the configuration file', async(() => {
spyOn(appConfigService, 'get').and.returnValue(['assignee', 'status']);
component.ngOnChanges();
fixture.detectChanges();
const propertyList = fixture.debugElement.queryAll(By.css('.adf-property-list .adf-property'));
fixture.whenStable().then(() => {
expect(propertyList).toBeDefined();
expect(propertyList).not.toBeNull();
expect(propertyList.length).toBe(2);
expect(propertyList[0].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE');
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS');
});
}));
it('should show all the default properties if there is no configuration', async(() => {
spyOn(appConfigService, 'get').and.returnValue(null);
component.ngOnChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
const propertyList = fixture.debugElement.queryAll(By.css('.adf-property-list .adf-property'));
expect(propertyList).toBeDefined();
expect(propertyList).not.toBeNull();
expect(propertyList.length).toBe(component.properties.length);
expect(propertyList[0].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE');
expect(propertyList[1].nativeElement.textContent).toContain('ADF_CLOUD_TASK_HEADER.PROPERTIES.STATUS');
});
}));
});
});
describe('Task Errors', () => {
beforeEach(() => {
fixture = TestBed.createComponent(TaskHeaderCloudComponent);
component = fixture.componentInstance;
service = TestBed.get(TaskCloudService);
spyOn(service, 'getTaskById').and.returnValue(throwError('Task not found error'));
});
it('should emit an error when getTaskById returns an error', async(() => {
const taskErrorSpy = spyOn(component.taskError, 'emit');
component.loadTaskDetailsById(component.appName, component.taskId);
fixture.detectChanges();
expect(taskErrorSpy).toHaveBeenCalledWith('Task not found error');
}));
});
});

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, Input, OnInit, EventEmitter, Output, OnDestroy } from '@angular/core';
import { Component, Input, EventEmitter, Output, OnDestroy, OnChanges, OnInit } from '@angular/core';
import {
CardViewDateItemModel,
CardViewItem,
@@ -30,7 +30,7 @@ import {
import { TaskDetailsCloudModel, TaskStatusEnum } from '../../start-task/models/task-details-cloud.model';
import { Router } from '@angular/router';
import { TaskCloudService } from '../../services/task-cloud.service';
import { Subject, Observable } from 'rxjs';
import { Subject } from 'rxjs';
import { NumericFieldValidator } from '../../../validators/numeric-field.validator';
import { takeUntil } from 'rxjs/operators';
@@ -39,7 +39,7 @@ import { takeUntil } from 'rxjs/operators';
templateUrl: './task-header-cloud.component.html',
styleUrls: ['./task-header-cloud.component.scss']
})
export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
/** (Required) The name of the application. */
@Input()
@@ -57,6 +57,10 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
@Output()
unclaim: EventEmitter<any> = new EventEmitter<any>();
/** Emitted when the task has not been found. */
@Output()
taskError: EventEmitter<any> = new EventEmitter<any>();
taskDetails: TaskDetailsCloudModel = new TaskDetailsCloudModel();
properties: CardViewItem[];
inEdit: boolean = false;
@@ -79,13 +83,23 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
}
ngOnInit() {
if ((this.appName || this.appName === '') && this.taskId) {
this.taskCloudService.dataChangesDetected$
.pipe(takeUntil(this.onDestroy$))
.subscribe(() => {
this.loadTaskDetailsById(this.appName, this.taskId);
}
});
this.cardViewUpdateService.itemUpdated$
.pipe(takeUntil(this.onDestroy$))
.subscribe(this.updateTaskDetails.bind(this));
.subscribe(this.updateTaskDetails.bind(this)
);
}
ngOnChanges() {
this.taskDetails = new TaskDetailsCloudModel();
if (this.appName && this.taskId) {
this.loadTaskDetailsById(this.appName, this.taskId);
}
}
loadTaskDetailsById(appName: string, taskId: string): any {
@@ -97,7 +111,8 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
} else {
this.refreshData();
}
});
},
(err) => this.taskError.emit(err), () => {});
}
private initDefaultProperties() {
@@ -202,7 +217,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
new CardViewArrayItemModel(
{
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS',
value: this.getCandidateUsers(),
value: this.taskCloudService.getCandidateUsers(this.appName, this.taskId),
key: 'candidateUsers',
icon: 'person',
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS_DEFAULT'),
@@ -212,7 +227,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
new CardViewArrayItemModel(
{
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS',
value: this.getCandidateGroups(),
value: this.taskCloudService.getCandidateGroups(this.appName, this.taskId),
key: 'candidateGroups',
icon: 'group',
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS_DEFAULT'),
@@ -222,14 +237,6 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
];
}
private getCandidateUsers(): Observable<string[]> {
return this.taskCloudService.getCandidateUsers(this.appName, this.taskId);
}
private getCandidateGroups(): Observable<string[]> {
return this.taskCloudService.getCandidateGroups(this.appName, this.taskId);
}
/**
* Refresh the card data
*/
@@ -275,7 +282,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy {
}
isTaskValid(): boolean {
return (this.appName || this.appName === '') && !!this.taskId;
return (this.appName) && !!this.taskId;
}
isTaskAssigned(): boolean {

View File

@@ -233,7 +233,7 @@ export class TaskListCloudComponent extends DataTableSchema implements OnChanges
reload() {
this.requestNode = this.createRequestNode();
if (this.requestNode.appName || this.requestNode.appName === '') {
if (this.requestNode.appName) {
this.load(this.requestNode);
} else {
this.rows = [];

View File

@@ -41,7 +41,7 @@ export class TaskListCloudService extends BaseCloudService {
*/
getTaskByRequest(requestNode: TaskQueryCloudRequestModel): Observable<any> {
if (requestNode.appName || requestNode.appName === '') {
if (requestNode.appName) {
const queryUrl = this.buildQueryUrl(requestNode);
const queryParams = this.buildQueryParams(requestNode);
const sortingParams = this.buildSortingParam(requestNode.sorting);