diff --git a/lib/process-services/mock/task/task-details.mock.ts b/lib/process-services/mock/task/task-details.mock.ts index dca45e1aba..6c7e0339fc 100644 --- a/lib/process-services/mock/task/task-details.mock.ts +++ b/lib/process-services/mock/task/task-details.mock.ts @@ -52,28 +52,7 @@ export let taskDetailsMock = new TaskDetailsModel({ 'memberOfCandidateGroup': false }); -export let completedTaskDetailsMock = new TaskDetailsModel({ - 'id': '91', - 'name': 'Request translation', - 'description': null, - 'category': null, - 'assignee': {'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}, - 'created': '2016-11-03T15:25:42.749+0000', - 'dueDate': null, - 'endDate': '2016-11-03T15:25:42.749+0000', - 'duration': null, - 'priority': 50, - 'parentTaskId': null, - 'parentTaskName': null, - 'processInstanceId': '86', - 'processInstanceName': null, - 'processDefinitionId': 'TranslationProcess:2:8', - 'processDefinitionName': 'Translation Process', - 'involvedGroups': [], - 'involvedPeople': [] -}); - -export let taskDetailsWithOutAssigneeMock = new TaskDetailsModel({ +export let claimableTaskDetailsMock = new TaskDetailsModel({ 'id': '91', 'name': 'Request translation', 'description': null, @@ -92,10 +71,13 @@ export let taskDetailsWithOutAssigneeMock = new TaskDetailsModel({ 'processDefinitionName': 'Translation Process', 'involvedGroups': [{'id': 7007, 'name': 'group1', 'externalId': null, 'status': 'active', 'groups': null}, {'id': 8008, 'name': 'group2', 'externalId': null, 'status': 'active', 'groups': null}], - 'involvedPeople': [] + 'involvedPeople': [], + 'managerOfCandidateGroup': true, + 'memberOfCandidateGroup': true, + 'memberOfCandidateUsers': false }); -export let taskDetailsWithInvolvedGroupMock = new TaskDetailsModel({ +export let claimedTaskDetailsMock = new TaskDetailsModel({ 'id': '91', 'name': 'Request translation', 'description': null, @@ -112,34 +94,15 @@ export let taskDetailsWithInvolvedGroupMock = new TaskDetailsModel({ 'processInstanceName': null, 'processDefinitionId': 'TranslationProcess:2:8', 'processDefinitionName': 'Translation Process', - 'involvedGroups': [{'id': 7007, 'name': 'group1', 'externalId': null, 'status': 'active', 'groups': null}, - {'id': 8008, 'name': 'group2', 'externalId': null, 'status': 'active', 'groups': null}], - 'involvedPeople': [] -}); - -export let taskDetailsWithInvolvedPeopleMock = new TaskDetailsModel({ - 'id': '91', - 'name': 'Request translation', - 'description': null, - 'category': null, - 'assignee': {'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}, - 'created': '2016-11-03T15:25:42.749+0000', - 'dueDate': null, - 'endDate': null, - 'duration': null, - 'priority': 50, - 'parentTaskId': null, - 'parentTaskName': null, - 'processInstanceId': '86', - 'processInstanceName': null, - 'processDefinitionId': 'TranslationProcess:2:8', - 'processDefinitionName': 'Translation Process', - 'involvedGroups': [], + 'involvedGroups': [{'id': 7007, 'name': 'group1', 'externalId': null, 'status': 'active', 'groups': null}], 'involvedPeople': [{'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}, - {'id': 111, 'firstName': 'fake-first-name', 'lastName': 'fake-last-name', 'email': 'fake@app.activiti.com'}] + {'id': 111, 'firstName': 'fake-first-name', 'lastName': 'fake-last-name', 'email': 'fake@app.activiti.com'}], + 'managerOfCandidateGroup': true, + 'memberOfCandidateGroup': true, + 'memberOfCandidateUsers': true }); -export let taskDetailsWithAssigneeMock = new TaskDetailsModel({ +export let claimedByGroupMemberMock = new TaskDetailsModel({ 'id': '91', 'name': 'Request translation', 'description': null, @@ -156,11 +119,63 @@ export let taskDetailsWithAssigneeMock = new TaskDetailsModel({ 'processInstanceName': null, 'processDefinitionId': 'TranslationProcess:2:8', 'processDefinitionName': 'Translation Process', + 'involvedGroups': [{'id': 7007, 'name': 'group1', 'externalId': null, 'status': 'active', 'groups': null}], + 'involvedPeople': [{'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}, + {'id': 111, 'firstName': 'fake-first-name', 'lastName': 'fake-last-name', 'email': 'fake@app.activiti.com'}], + 'managerOfCandidateGroup': true, + 'memberOfCandidateGroup': true, + 'memberOfCandidateUsers': true +}); + +export let taskDetailsWithOutCandidateGroup = new TaskDetailsModel({ + 'id': '91', + 'name': 'Request translation', + 'description': null, + 'category': null, + 'assignee': {'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}, + 'created': '2016-11-03T15:25:42.749+0000', + 'dueDate': null, + 'endDate': null, + 'duration': null, + 'priority': 50, + 'parentTaskId': null, + 'parentTaskName': null, + 'processInstanceId': null, + 'processInstanceName': null, + 'processDefinitionId': 'TranslationProcess:2:8', + 'processDefinitionName': 'Translation Process', + 'managerOfCandidateGroup': false, + 'memberOfCandidateGroup': false, + 'memberOfCandidateUsers': false, 'involvedGroups': [], 'involvedPeople': [{'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}, {'id': 111, 'firstName': 'fake-first-name', 'lastName': 'fake-last-name', 'email': 'fake@app.activiti.com'}] }); +export let completedTaskDetailsMock = new TaskDetailsModel({ + 'id': '91', + 'name': 'Request translation', + 'description': null, + 'category': null, + 'assignee': {'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}, + 'created': '2016-11-03T15:25:42.749+0000', + 'dueDate': null, + 'endDate': '2016-11-03T15:25:42.749+0000', + 'duration': null, + 'priority': 50, + 'parentTaskId': null, + 'parentTaskName': null, + 'processInstanceId': '86', + 'processInstanceName': null, + 'processDefinitionId': 'TranslationProcess:2:8', + 'processDefinitionName': 'Translation Process', + 'involvedGroups': [], + 'involvedPeople': [], + 'managerOfCandidateGroup': true, + 'memberOfCandidateGroup': true, + 'memberOfCandidateUsers': false +}); + export let taskFormMock = new TaskDetailsModel({ 'id': 4, 'name': 'Translation request', diff --git a/lib/process-services/task-list/components/task-header.component.html b/lib/process-services/task-list/components/task-header.component.html index f60a2e496a..9b7a297b2a 100644 --- a/lib/process-services/task-list/components/task-header.component.html +++ b/lib/process-services/task-list/components/task-header.component.html @@ -4,7 +4,7 @@ - diff --git a/lib/process-services/task-list/components/task-header.component.spec.ts b/lib/process-services/task-list/components/task-header.component.spec.ts index 8d30a73bfb..e78ecabf0d 100644 --- a/lib/process-services/task-list/components/task-header.component.spec.ts +++ b/lib/process-services/task-list/components/task-header.component.spec.ts @@ -17,17 +17,17 @@ import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { By } from '@angular/platform-browser'; -import { CardViewUpdateService, UserProcessModel } from '@alfresco/adf-core'; +import { CardViewUpdateService } from '@alfresco/adf-core'; import { BpmUserService } from '@alfresco/adf-core'; import { MaterialModule } from '../../material.module'; import { Observable } from 'rxjs/Observable'; import { completedTaskDetailsMock, taskDetailsMock, - taskDetailsWithAssigneeMock, - taskDetailsWithInvolvedGroupMock, - taskDetailsWithInvolvedPeopleMock, - taskDetailsWithOutAssigneeMock } from '../../mock'; + claimableTaskDetailsMock, + claimedTaskDetailsMock, + claimedByGroupMemberMock, + taskDetailsWithOutCandidateGroup } from '../../mock'; import { TaskDetailsModel } from '../models/task-details.model'; import { TaskListService } from './../services/tasklist.service'; @@ -128,7 +128,7 @@ describe('TaskHeaderComponent', () => { describe('Claiming', () => { it('should display the claim button if no assignee', () => { - component.taskDetails = new TaskDetailsModel(taskDetailsWithOutAssigneeMock); + component.taskDetails = new TaskDetailsModel(claimableTaskDetailsMock); component.ngOnChanges({}); fixture.detectChanges(); @@ -137,67 +137,69 @@ describe('TaskHeaderComponent', () => { expect(claimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.CLAIM'); }); - it('should display the claim button to the invovled group', () => { - component.taskDetails = new TaskDetailsModel(taskDetailsWithOutAssigneeMock); + it('should display the claim button if the task is claimable', () => { + component.taskDetails = new TaskDetailsModel(claimableTaskDetailsMock); component.ngOnChanges({}); fixture.detectChanges(); let claimButton = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]')); + expect(component.isTaskClaimable()).toBeTruthy(); expect(claimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.CLAIM'); }); + + it('should not display the claim/requeue button if the task is not claimable ', () => { + component.taskDetails = new TaskDetailsModel(taskDetailsWithOutCandidateGroup); + component.ngOnChanges({}); + fixture.detectChanges(); + let claimButton = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]')); + let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]')); + expect(component.isTaskClaimable()).toBeFalsy(); + expect(component.isTaskClaimedByCandidateMember()).toBeFalsy(); + expect(unclaimButton).toBeNull(); + expect(claimButton).toBeNull(); + }); }); - it('should display the requeue button if the current logged-in user is a part of the invovled group', () => { - component.taskDetails = new TaskDetailsModel(taskDetailsWithInvolvedGroupMock); + it('should display the requeue button if task is claimed by the current logged-in user', () => { + component.taskDetails = new TaskDetailsModel(claimedTaskDetailsMock); component.ngOnChanges({}); fixture.detectChanges(); let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]')); + expect(component.isTaskClaimedByCandidateMember()).toBeTruthy(); expect(unclaimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM'); }); - it('should display the requeue button if the current logged-in user is a part of the invovled people', () => { - component.taskDetails = new TaskDetailsModel(taskDetailsWithInvolvedPeopleMock); - component.ngOnChanges({}); - fixture.detectChanges(); - let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]')); - expect(unclaimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM'); - }); - - it('should not display the claim/requeue button if the current logged-in user is not part of the group', () => { - component.taskDetails = new TaskDetailsModel(taskDetailsWithInvolvedGroupMock); - component.ngOnChanges({}); - fixture.detectChanges(); - let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]')); - expect(unclaimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM'); - }); - - it('should not display the requeue button if the task is assigned to others', () => { - const batman = new UserProcessModel({ id : 1, email: 'bruce.wayne@gotham.com', firstName: 'Bruce', lastName: 'Wayne', userImage: 'batman.jpg' }); - component.taskDetails.assignee = batman; + it('should not display the requeue button to logged in user if task is claimed by other candidate member', () => { + component.taskDetails = new TaskDetailsModel(claimedByGroupMemberMock); component.ngOnChanges({}); fixture.detectChanges(); let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]')); + expect(component.isTaskClaimedByCandidateMember()).toBeFalsy(); expect(unclaimButton).toBeNull(); }); - it('should not display the requeue button if the task is assigned to others in a group', () => { - component.taskDetails = new TaskDetailsModel(taskDetailsWithAssigneeMock); + it('should display the claime button if the task is claimable by candidates members', () => { + component.taskDetails = new TaskDetailsModel(claimableTaskDetailsMock); component.ngOnChanges({}); fixture.detectChanges(); - let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]')); - expect(unclaimButton).toBeNull(); + let claimButton = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]')); + expect(component.isTaskClaimable()).toBeTruthy(); + expect(component.isTaskClaimedByCandidateMember()).toBeFalsy(); + expect(claimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.CLAIM'); }); it('should not display the requeue button if the task is completed', () => { component.taskDetails = new TaskDetailsModel(completedTaskDetailsMock); component.ngOnChanges({}); fixture.detectChanges(); + let claimButton = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]')); let unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]')); + expect(claimButton).toBeNull(); expect(unclaimButton).toBeNull(); }); it('should call the service\'s unclaim method on unclaiming', () => { spyOn(service, 'unclaimTask'); - component.taskDetails = new TaskDetailsModel(taskDetailsWithInvolvedGroupMock); + component.taskDetails = new TaskDetailsModel(claimedTaskDetailsMock); component.ngOnChanges({}); fixture.detectChanges(); @@ -210,7 +212,7 @@ describe('TaskHeaderComponent', () => { it('should trigger the unclaim event on successfull unclaiming', () => { let unclaimed: boolean = false; spyOn(service, 'unclaimTask').and.returnValue(Observable.of(true)); - component.taskDetails = new TaskDetailsModel(taskDetailsWithInvolvedGroupMock); + component.taskDetails = new TaskDetailsModel(claimedTaskDetailsMock); component.ngOnChanges({}); fixture.detectChanges(); component.unclaim.subscribe(() => { unclaimed = true; }); diff --git a/lib/process-services/task-list/components/task-header.component.ts b/lib/process-services/task-list/components/task-header.component.ts index aaa8c2fcb8..049f1655b6 100644 --- a/lib/process-services/task-list/components/task-header.component.ts +++ b/lib/process-services/task-list/components/task-header.component.ts @@ -197,31 +197,24 @@ export class TaskHeaderComponent implements OnChanges, OnInit { } /** - * Return true if the task has involvedGroup + * Return true if the user is a candidate member */ - public hasInvolvedGroup(): boolean { - return this.taskDetails.involvedGroups.length > 0 ? true : false; - } - - /** - * Return true if the task has involvedPeople - */ - public hasInvolvedPeople(): boolean { - return this.taskDetails.involvedPeople.length > 0 ? true : false; + isCandidateMember() { + return this.taskDetails.managerOfCandidateGroup || this.taskDetails.memberOfCandidateGroup || this.taskDetails.memberOfCandidateUsers; } /** * Return true if the task claimable */ public isTaskClaimable(): boolean { - return !this.isCompleted() && (this.hasInvolvedGroup() || this.hasInvolvedPeople()) && !this.hasAssignee() ; + return !this.hasAssignee() && this.isCandidateMember(); } /** - * Return true if the task claimed by currentUser + * Return true if the task claimed by candidate member. */ - public isTaskClaimedByCurrentUser(): boolean { - return !this.isCompleted() && (this.hasInvolvedGroup() || this.hasInvolvedPeople()) && this.isAssignedToCurrentUser(); + public isTaskClaimedByCandidateMember(): boolean { + return this.isCandidateMember() && this.isAssignedToCurrentUser() && !this.isCompleted(); } /**