diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html
index 372b16b6b3..6907cd50ed 100644
--- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html
+++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.html
@@ -9,6 +9,14 @@
{{ property.displayValue }}
+
diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.scss b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.scss
index 82a4fa3ed1..30d7b2b878 100644
--- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.scss
+++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.scss
@@ -31,6 +31,7 @@
}
&-textitem-clickable {
+ cursor: pointer !important;
&:hover .adf-textitem-action {
color: mat-color($foreground, text);
}
diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts
index 7411fd07f2..b0792e3568 100644
--- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts
+++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.spec.ts
@@ -154,6 +154,54 @@ describe('CardViewTextItemComponent', () => {
expect(value).toBeNull();
});
+ it('should not render the clickable icon in case editable set to false', () => {
+ component.property = new CardViewTextItemModel({
+ label: 'Text label',
+ value: '',
+ key: 'textkey',
+ default: 'FAKE-DEFAULT-KEY',
+ clickable: true,
+ icon: 'create'
+ });
+ component.editable = false;
+ fixture.detectChanges();
+
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-textkey"]`));
+ expect(value).toBeNull('icon should NOT be shown');
+ });
+
+ it('should render the defined clickable icon in case of clickable true and editable input set to true', () => {
+ component.property = new CardViewTextItemModel({
+ label: 'Text label',
+ value: '',
+ key: 'textkey',
+ default: 'FAKE-DEFAULT-KEY',
+ clickable: true,
+ icon: 'FAKE_ICON'
+ });
+ component.editable = true;
+ fixture.detectChanges();
+
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-textkey"]`));
+ expect(value).not.toBeNull();
+ expect(value.nativeElement.innerText).toBe('FAKE_ICON');
+ });
+
+ it('should not render clickable icon in case of clickable true and icon undefined', () => {
+ component.property = new CardViewTextItemModel({
+ label: 'Text label',
+ value: '',
+ key: 'textkey',
+ default: 'FAKE-DEFAULT-KEY',
+ clickable: true
+ });
+ component.editable = true;
+ fixture.detectChanges();
+
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-textkey"]`));
+ expect(value).toBeNull('icon should NOT be shown');
+ });
+
it('should not render the edit icon in case of clickable true and icon undefined', () => {
component.property = new CardViewTextItemModel({
label: 'Text label',
diff --git a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts
index 68ab80dc0d..34e3ddc3dd 100644
--- a/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts
+++ b/lib/core/card-view/components/card-view-textitem/card-view-textitem.component.ts
@@ -59,6 +59,10 @@ export class CardViewTextItemComponent implements OnChanges {
return this.displayEmpty || !this.property.isEmpty();
}
+ showClickableIcon(): boolean {
+ return this.hasIcon() && this.editable;
+ }
+
isEditable(): boolean {
return this.editable && this.property.editable;
}
diff --git a/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts b/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts
index c640a25027..fb5a2746f6 100644
--- a/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts
+++ b/lib/process-services-cloud/src/lib/task/start-task/models/task-details-cloud.model.ts
@@ -32,7 +32,7 @@ export interface TaskDetailsCloudModel {
lastModifiedTo?: Date;
lastModifiedFrom?: Date;
owner?: any;
- parentTaskId?: number;
+ parentTaskId?: string;
priority?: number;
processDefinitionId?: string;
processInstanceId?: string;
diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts
index d2f84cc25d..f26d2676b9 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.spec.ts
@@ -15,33 +15,35 @@
* limitations under the License.
*/
-import { async, ComponentFixture, TestBed } from '@angular/core/testing';
-import { setupTestBed, AppConfigService, IdentityUserService } from '@alfresco/adf-core';
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, throwError } from 'rxjs';
-import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { RouterTestingModule } from '@angular/router/testing';
+import { By } from '@angular/platform-browser';
+import { ComponentFixture, TestBed, async } from '@angular/core/testing';
+import { setupTestBed, AppConfigService } from '@alfresco/adf-core';
+import { ProcessServiceCloudTestingModule } from '../../../testing/process-service-cloud.testing.module';
import { TaskCloudService } from '../../services/task-cloud.service';
+import { TaskHeaderCloudModule } from '../task-header-cloud.module';
+import {
+ assignedTaskDetailsCloudMock,
+ completedTaskDetailsCloudMock,
+ createdStateTaskDetailsCloudMock,
+ suspendedTaskDetailsCloudMock,
+ taskDetailsWithParentTaskIdMock
+} from '../mocks/task-details-cloud.mock';
import moment = require('moment');
describe('TaskHeaderCloudComponent', () => {
let component: TaskHeaderCloudComponent;
let fixture: ComponentFixture;
- let service: TaskCloudService;
let appConfigService: AppConfigService;
- let identityUserService: IdentityUserService;
+ let taskCloudService: TaskCloudService;
+ let getTaskByIdSpy: jasmine.Spy;
+ let updateTaskSpy: jasmine.Spy;
let getCandidateGroupsSpy: jasmine.Spy;
let getCandidateUsersSpy: jasmine.Spy;
+ let isTaskEditableSpy: jasmine.Spy;
- 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'];
@@ -51,67 +53,63 @@ describe('TaskHeaderCloudComponent', () => {
TaskHeaderCloudModule,
RouterTestingModule
],
- providers: [IdentityUserService]
+ providers: [TaskCloudService]
+ });
+
+ beforeEach(() => {
+ fixture = TestBed.createComponent(TaskHeaderCloudComponent);
+ component = fixture.componentInstance;
+ appConfigService = TestBed.get(AppConfigService);
+ taskCloudService = TestBed.get(TaskCloudService);
+ component.appName = 'mock-app-name';
+ component.taskId = 'mock-task-id';
+ getTaskByIdSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(assignedTaskDetailsCloudMock));
+ updateTaskSpy = spyOn(taskCloudService, 'updateTask').and.returnValue(of(assignedTaskDetailsCloudMock));
+ isTaskEditableSpy = spyOn(taskCloudService, 'isTaskEditable').and.returnValue(true);
+ getCandidateUsersSpy = spyOn(taskCloudService, 'getCandidateUsers').and.returnValue(of(mockCandidateUsers));
+ getCandidateGroupsSpy = spyOn(taskCloudService, 'getCandidateGroups').and.returnValue(of(mockCandidateGroups));
});
describe('Task Details', () => {
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);
+ component.ngOnChanges();
});
- it('should render empty component if no task details provided', async(() => {
- component.appName = undefined;
- component.taskId = undefined;
+ it('should fectch task details when appName and taskId defined', async(() => {
fixture.detectChanges();
- expect(fixture.debugElement.children.length).toBe(2);
+ fixture.whenStable().then(() => {
+ expect(getTaskByIdSpy).toHaveBeenCalled();
+ expect(component.taskDetails).toBe(assignedTaskDetailsCloudMock);
+ });
}));
it('should display assignee', async(() => {
- component.ngOnChanges();
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');
+ const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
+ expect(assigneeEl.nativeElement.innerText).toBe('AssignedTaskUser');
});
}));
- it('should display placeholder if no assignee', async(() => {
- component.taskDetails.assignee = null;
- component.refreshData();
+ it('should display status', async(() => {
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');
+ const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
+ expect(statusEl.nativeElement.innerText).toBe('ASSIGNED');
});
-
}));
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');
+ const priorityEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-priority"]'));
+ expect(priorityEl.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(() => {
@@ -134,7 +132,6 @@ describe('TaskHeaderCloudComponent', () => {
}));
it('should display due date', async(() => {
- component.ngOnChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -155,7 +152,6 @@ describe('TaskHeaderCloudComponent', () => {
}));
it('should display the default parent value if is undefined', async(() => {
- component.ngOnChanges();
component.taskDetails.processInstanceId = null;
fixture.detectChanges();
@@ -165,6 +161,241 @@ describe('TaskHeaderCloudComponent', () => {
});
}));
+ it('should be able to call update service on updating task description', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const descriptionEditIcon = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edit-icon-description"]'));
+ descriptionEditIcon.nativeElement.click();
+ fixture.detectChanges();
+
+ const inputEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edittextarea-description"]'));
+ inputEl.nativeElement.value = 'updated description';
+ inputEl.nativeElement.dispatchEvent(new Event('input'));
+ fixture.detectChanges();
+
+ const submitEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-update-description"]'));
+ submitEl.nativeElement.click();
+ fixture.detectChanges();
+ expect(updateTaskSpy).toHaveBeenCalled();
+ });
+ }));
+
+ });
+
+ describe('Task with parentTaskId', () => {
+
+ beforeEach(() => {
+ getTaskByIdSpy.and.returnValue(of(taskDetailsWithParentTaskIdMock));
+ component.ngOnChanges();
+ });
+
+ it('should fectch parent task details if the task has parent id', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ expect(getTaskByIdSpy).toHaveBeenCalledTimes(2);
+ });
+ }));
+
+ it('should display parent task id', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentTaskId"] span'));
+ expect(assigneeEl.nativeElement.innerText).toBe('mock-parent-task-id');
+ });
+ }));
+
+ it('should display parent task name', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentName"] span'));
+ expect(statusEl.nativeElement.innerText).toBe('This is a parent task name');
+ });
+ }));
+ });
+
+ describe('Assigned Task', () => {
+
+ beforeEach(() => {
+ getTaskByIdSpy.and.returnValue(of(assignedTaskDetailsCloudMock));
+ component.ngOnChanges();
+ });
+
+ it('should display assignee', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
+ expect(assigneeEl.nativeElement.innerText).toBe('AssignedTaskUser');
+ });
+ }));
+
+ it('should display status', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
+ expect(statusEl.nativeElement.innerText).toBe('ASSIGNED');
+ });
+ }));
+
+ it('should render defined edit icon for assignee property if the task in assigned state and assingee should be current user', () => {
+ fixture.detectChanges();
+
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-assignee"]`));
+ expect(value).not.toBeNull();
+ expect(value.nativeElement.innerText).toBe('create');
+ });
+
+ it('should render edit icon if the task in assigned state and assingee should be current user', () => {
+ fixture.detectChanges();
+ const priorityEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-priority"]`));
+ const descriptionEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-description"]`));
+ const dueDateEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-dueDate"]`));
+ expect(priorityEditIcon).not.toBeNull('Edit icon should be shown');
+ expect(descriptionEditIcon).not.toBeNull('Edit icon should be shown');
+ expect(dueDateEditIcon).not.toBeNull('Edit icon should be shown');
+ });
+
+ it('should not render defined clickable edit icon for assignee property if the task in assigned state and assingned user is different from current logged-in user', () => {
+ isTaskEditableSpy.and.returnValue(false);
+ fixture.detectChanges();
+
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-assignee"]`));
+ expect(value).toBeNull('Edit icon should NOT be shown');
+ });
+
+ it('should not render edit icon if the task in assigned state and assingned user is different from current logged-in user', () => {
+ isTaskEditableSpy.and.returnValue(false);
+ fixture.detectChanges();
+ const priorityEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-priority"]`));
+ const descriptionEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-description"]`));
+ const dueDateEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-dueDate"]`));
+ expect(priorityEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(descriptionEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(dueDateEditIcon).toBeNull('Edit icon should NOT be shown');
+ });
+ });
+
+ describe('Created Task', () => {
+
+ beforeEach(() => {
+ getTaskByIdSpy.and.returnValue(of(createdStateTaskDetailsCloudMock));
+ isTaskEditableSpy.and.returnValue(false);
+ component.ngOnChanges();
+ });
+
+ it('should display status', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
+ expect(statusEl.nativeElement.innerText).toBe('CREATED');
+ });
+ }));
+
+ it('should display placeholder if no assignee', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
+ expect(assigneeEl.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE_DEFAULT');
+ });
+ }));
+
+ it('should not render defined clickable edit icon for assignee property if the task in created state and not assigned', () => {
+ fixture.detectChanges();
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-assignee"]`));
+ expect(value).toBeNull('Edit icon should NOT be shown');
+ });
+
+ it('should not render edit icon if the task in created state and not assigned', () => {
+ fixture.detectChanges();
+ const priorityEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-priority"]`));
+ const descriptionEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-description"]`));
+ const dueDateEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-dueDate"]`));
+ expect(priorityEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(descriptionEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(dueDateEditIcon).toBeNull('Edit icon should NOT be shown');
+ });
+ });
+
+ describe('Completed Task', () => {
+
+ beforeEach(() => {
+ getTaskByIdSpy.and.returnValue(of(completedTaskDetailsCloudMock));
+ isTaskEditableSpy.and.returnValue(false);
+ component.ngOnChanges();
+ });
+
+ it('should display status', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
+ expect(statusEl.nativeElement.innerText).toBe('COMPLETED');
+ });
+ }));
+
+ it('should not render defined clickable edit icon for assignee property if the task in completed state', () => {
+ fixture.detectChanges();
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-assignee"]`));
+ expect(value).toBeNull('Edit icon should NOT be shown');
+ });
+
+ it('should not render edit icon if the task in completed state', () => {
+ fixture.detectChanges();
+ const priorityEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-priority"]`));
+ const descriptionEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-description"]`));
+ const dueDateEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-dueDate"]`));
+ expect(priorityEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(descriptionEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(dueDateEditIcon).toBeNull('Edit icon should NOT be shown');
+ });
+ });
+
+ describe('Suspended Task', () => {
+
+ beforeEach(() => {
+ getTaskByIdSpy.and.returnValue(of(suspendedTaskDetailsCloudMock));
+ isTaskEditableSpy.and.returnValue(false);
+ component.ngOnChanges();
+ });
+
+ it('should display status', async(() => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
+ expect(statusEl.nativeElement.innerText).toBe('SUSPENDED');
+ });
+ }));
+
+ it('should not render defined clickable edit icon for assignee property if the task in suspended state', () => {
+ fixture.detectChanges();
+ const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-assignee"]`));
+ expect(value).toBeNull('Edit icon should NOT be shown');
+ });
+
+ it('should not render edit icon if the task in suspended state', () => {
+ fixture.detectChanges();
+ const priorityEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-priority"]`));
+ const descriptionEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-description"]`));
+ const dueDateEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-dueDate"]`));
+ expect(priorityEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(descriptionEditIcon).toBeNull('Edit icon should NOT be shown');
+ expect(dueDateEditIcon).toBeNull('Edit icon should NOT be shown');
+ });
+ });
+
+ describe('Task with candidates', () => {
+
+ 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 candidate user', async(() => {
component.ngOnChanges();
fixture.detectChanges();
@@ -178,36 +409,10 @@ describe('TaskHeaderCloudComponent', () => {
});
}));
- 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();
+ component.ngOnChanges();
fixture.detectChanges();
fixture.whenStable().then(() => {
@@ -219,52 +424,58 @@ describe('TaskHeaderCloudComponent', () => {
}));
- 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');
- });
- }));
-
- });
+ it('should display placeholder if no candidate users', async(() => {
+ getCandidateUsersSpy.and.returnValue(of([]));
+ fixture.detectChanges();
+ component.ngOnChanges();
+ 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');
+ });
+ }));
});
- describe('Task Errors', () => {
+ describe('Config properties', () => {
- beforeEach(() => {
- fixture = TestBed.createComponent(TaskHeaderCloudComponent);
- component = fixture.componentInstance;
- service = TestBed.get(TaskCloudService);
- });
+ 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', () => {
it('should emit an error when task can not be found', async(() => {
- spyOn(service, 'getTaskById').and.returnValue(throwError('Task not found'));
+ getTaskByIdSpy.and.returnValue(throwError('Task not found'));
component.error.subscribe((error) => {
expect(error).toEqual('Task not found');
diff --git a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts
index a2b85fd096..7846da16d6 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/components/task-header-cloud.component.ts
@@ -16,6 +16,8 @@
*/
import { Component, Input, EventEmitter, Output, OnDestroy, OnChanges, OnInit } from '@angular/core';
+import { takeUntil, concatMap } from 'rxjs/operators';
+import { Subject, of, forkJoin } from 'rxjs';
import {
CardViewDateItemModel,
CardViewItem,
@@ -31,9 +33,7 @@ import {
import { TaskDetailsCloudModel, TaskStatus } from '../../start-task/models/task-details-cloud.model';
import { Router } from '@angular/router';
import { TaskCloudService } from '../../services/task-cloud.service';
-import { Subject } from 'rxjs';
import { NumericFieldValidator } from '../../../validators/numeric-field.validator';
-import { takeUntil } from 'rxjs/operators';
@Component({
selector: 'adf-cloud-task-header',
@@ -63,6 +63,8 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
error: EventEmitter = new EventEmitter();
taskDetails: TaskDetailsCloudModel = {};
+ candidateUsers: string[] = [];
+ candidateGroups: string[] = [];
properties: CardViewItem[];
inEdit: boolean = false;
parentTaskName: string;
@@ -107,10 +109,19 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
}
}
- loadTaskDetailsById(appName: string, taskId: string): any {
- this.taskCloudService.getTaskById(appName, taskId).subscribe(
- (taskDetails) => {
+ loadTaskDetailsById(appName: string, taskId: string) {
+ this.taskCloudService.getTaskById(appName, taskId).pipe(
+ concatMap((task) =>
+ forkJoin(
+ of(task),
+ this.taskCloudService.getCandidateUsers(this.appName, this.taskId),
+ this.taskCloudService.getCandidateGroups(this.appName, this.taskId)
+ )
+ )
+ ).subscribe(([taskDetails, candidateUsers, candidateGroups]) => {
this.taskDetails = taskDetails;
+ this.candidateGroups = candidateGroups;
+ this.candidateUsers = candidateUsers;
if (this.taskDetails.parentTaskId) {
this.loadParentName(`${this.taskDetails.parentTaskId}`);
} else {
@@ -222,7 +233,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
new CardViewArrayItemModel(
{
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS',
- value: this.taskCloudService.getCandidateUsers(this.appName, this.taskId),
+ value: of(this.candidateUsers),
key: 'candidateUsers',
icon: 'person',
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_USERS_DEFAULT'),
@@ -232,7 +243,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
new CardViewArrayItemModel(
{
label: 'ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS',
- value: this.taskCloudService.getCandidateGroups(this.appName, this.taskId),
+ value: of(this.candidateGroups),
key: 'candidateGroups',
icon: 'group',
default: this.translationService.instant('ADF_CLOUD_TASK_HEADER.PROPERTIES.CANDIDATE_GROUPS_DEFAULT'),
@@ -299,7 +310,7 @@ export class TaskHeaderCloudComponent implements OnInit, OnDestroy, OnChanges {
}
isClickable(): boolean {
- const states: TaskStatus[] = ['ASSIGNED', 'CREATED', 'SUSPENDED'];
+ const states: TaskStatus[] = ['ASSIGNED', 'CREATED'];
return states.includes(this.taskDetails.status);
}
diff --git a/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts b/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts
index 2428e8ab73..8c7f5cc699 100644
--- a/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts
+++ b/lib/process-services-cloud/src/lib/task/task-header/mocks/task-details-cloud.mock.ts
@@ -17,6 +17,30 @@
import { TaskDetailsCloudModel } from '../../start-task/models/task-details-cloud.model';
+export const taskDetailsWithParentTaskIdMock: TaskDetailsCloudModel = {
+ 'appName': 'task-app',
+ 'appVersion': 1,
+ 'id': '68d54a8f-01f3-11e9-8e36-0a58646002ad',
+ 'assignee': 'AssignedTaskUser',
+ 'name': 'This is a parent task name ',
+ 'description': 'This is the description ',
+ 'createdDate': new Date(1545048055900),
+ 'dueDate': new Date(),
+ 'claimedDate': null,
+ 'priority': 5,
+ 'category': null,
+ 'processDefinitionId': null,
+ 'processInstanceId': null,
+ 'status': 'ASSIGNED',
+ 'owner': 'ownerUser',
+ 'parentTaskId': 'mock-parent-task-id',
+ 'formKey': null,
+ 'lastModified': new Date(1545048055900),
+ 'lastModifiedTo': null,
+ 'lastModifiedFrom': null,
+ 'standalone': true
+};
+
export const assignedTaskDetailsCloudMock: TaskDetailsCloudModel = {
'appName': 'task-app',
'appVersion': 1,
@@ -88,3 +112,99 @@ export const emptyOwnerTaskDetailsCloudMock: TaskDetailsCloudModel = {
'lastModifiedFrom': null,
'standalone': true
};
+
+export const createdStateTaskDetailsCloudMock: TaskDetailsCloudModel = {
+ 'appName': 'mock-app-name',
+ 'appVersion': 1,
+ 'id': 'mock-task-id',
+ 'assignee': '',
+ 'name': 'This is a new task ',
+ 'description': 'This is the description ',
+ 'createdDate': new Date(1545048055900),
+ 'dueDate': new Date(1545091200000),
+ 'claimedDate': null,
+ 'priority': 5,
+ 'category': null,
+ 'processDefinitionId': null,
+ 'processInstanceId': null,
+ 'status': 'CREATED',
+ 'owner': 'ownerUser',
+ 'parentTaskId': null,
+ 'formKey': null,
+ 'lastModified': new Date(1545048055900),
+ 'lastModifiedTo': null,
+ 'lastModifiedFrom': null,
+ 'standalone': false
+};
+
+export const completedTaskDetailsCloudMock: TaskDetailsCloudModel = {
+ 'appName': 'mock-app-name',
+ 'appVersion': 1,
+ 'id': 'mock-task-id',
+ 'assignee': 'CompletedTaskAssignee',
+ 'name': 'This is a new task ',
+ 'description': 'This is the description ',
+ 'createdDate': new Date(1545048055900),
+ 'dueDate': new Date(1545091200000),
+ 'claimedDate': null,
+ 'priority': 5,
+ 'category': null,
+ 'processDefinitionId': null,
+ 'processInstanceId': null,
+ 'status': 'COMPLETED',
+ 'owner': 'ownerUser',
+ 'parentTaskId': null,
+ 'formKey': null,
+ 'lastModified': new Date(1545048055900),
+ 'lastModifiedTo': null,
+ 'lastModifiedFrom': null,
+ 'standalone': false
+};
+
+export const cancelledTaskDetailsCloudMock: TaskDetailsCloudModel = {
+ 'appName': 'mock-app-name',
+ 'appVersion': 1,
+ 'id': 'mock-task-id',
+ 'assignee': 'CancelledTaskAssignee',
+ 'name': 'This is a new task ',
+ 'description': 'This is the description ',
+ 'createdDate': new Date(1545048055900),
+ 'dueDate': new Date(1545091200000),
+ 'claimedDate': null,
+ 'priority': 5,
+ 'category': null,
+ 'processDefinitionId': null,
+ 'processInstanceId': null,
+ 'status': 'CANCELLED',
+ 'owner': 'ownerUser',
+ 'parentTaskId': null,
+ 'formKey': null,
+ 'lastModified': new Date(1545048055900),
+ 'lastModifiedTo': null,
+ 'lastModifiedFrom': null,
+ 'standalone': true
+};
+
+export const suspendedTaskDetailsCloudMock: TaskDetailsCloudModel = {
+ 'appName': 'mock-app-name',
+ 'appVersion': 1,
+ 'id': 'mock-task-id',
+ 'assignee': 'SuspendedTaskAssignee',
+ 'name': 'This is a new task ',
+ 'description': 'This is the description ',
+ 'createdDate': new Date(1545048055900),
+ 'dueDate': new Date(1545091200000),
+ 'claimedDate': null,
+ 'priority': 5,
+ 'category': null,
+ 'processDefinitionId': null,
+ 'processInstanceId': null,
+ 'status': 'SUSPENDED',
+ 'owner': 'ownerUser',
+ 'parentTaskId': null,
+ 'formKey': null,
+ 'lastModified': new Date(1545048055900),
+ 'lastModifiedTo': null,
+ 'lastModifiedFrom': null,
+ 'standalone': true
+};