mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-26 17:24:56 +00:00
[ACA-3448] Candidate user is able to complete a task without a form attached before claiming it (#5780)
* [ACA-3448] Candidate user is able to complete a task without a form attached before claiming it * * Added unit test to the recent changes * * Updated unit test * Fixed comment * * Updated doc * * Revered claim changes * * Fixed comments * * Added unit tests to the recent changes * * Removed errorModel
This commit is contained in:
parent
29d953e2d1
commit
9a6fd0125b
@ -206,6 +206,41 @@ export let initiatorCanCompleteTaskDetailsMock = new TaskDetailsModel({
|
||||
taskDefinitionKey: 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
|
||||
executionId: '86',
|
||||
involvedGroups: [],
|
||||
involvedPeople: [],
|
||||
memberOfCandidateUsers: false,
|
||||
managerOfCandidateGroup: false,
|
||||
memberOfCandidateGroup: false
|
||||
});
|
||||
|
||||
export let initiatorWithCandidatesTaskDetailsMock = new TaskDetailsModel({
|
||||
id: '91',
|
||||
name: 'Request translation',
|
||||
description: null,
|
||||
category: null,
|
||||
assignee: null,
|
||||
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',
|
||||
processDefinitionDescription: null,
|
||||
processDefinitionKey: 'TranslationProcess',
|
||||
processDefinitionCategory: 'http://www.activiti.org/processdef',
|
||||
processDefinitionVersion: 2,
|
||||
processDefinitionDeploymentId: '5',
|
||||
formKey: '4',
|
||||
processInstanceStartUserId: '1001',
|
||||
initiatorCanCompleteTask: true,
|
||||
adhocTaskCanBeReassigned: false,
|
||||
taskDefinitionKey: 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
|
||||
executionId: '86',
|
||||
involvedGroups: [],
|
||||
involvedPeople: [
|
||||
{
|
||||
id: 1001,
|
||||
@ -220,9 +255,9 @@ export let initiatorCanCompleteTaskDetailsMock = new TaskDetailsModel({
|
||||
email: 'fake@app.activiti.com'
|
||||
}
|
||||
],
|
||||
memberOfCandidateUsers: false,
|
||||
managerOfCandidateGroup: false,
|
||||
memberOfCandidateGroup: false
|
||||
memberOfCandidateUsers: true,
|
||||
managerOfCandidateGroup: true,
|
||||
memberOfCandidateGroup: true
|
||||
});
|
||||
|
||||
export let taskDetailsWithOutAssigneeMock = new TaskDetailsModel({
|
||||
@ -316,10 +351,13 @@ export let claimedTaskDetailsMock = new TaskDetailsModel({
|
||||
endDate: null,
|
||||
duration: null,
|
||||
priority: 50,
|
||||
formKey: '4',
|
||||
parentTaskId: null,
|
||||
parentTaskName: null,
|
||||
processInstanceId: '86',
|
||||
processInstanceName: null,
|
||||
processInstanceStartUserId: '1002',
|
||||
initiatorCanCompleteTask: false,
|
||||
processDefinitionId: 'TranslationProcess:2:8',
|
||||
processDefinitionName: 'Translation Process',
|
||||
involvedGroups: [
|
||||
@ -507,7 +545,7 @@ export let taskDetailsWithOutFormMock = new TaskDetailsModel({
|
||||
'name': 'Request translation',
|
||||
'description': 'fake description',
|
||||
'category': null,
|
||||
'assignee': {'id': 1001, 'firstName': 'Admin', 'lastName': 'Paul', 'email': 'my@mymail.com' },
|
||||
'assignee': {'id': 1001, 'firstName': 'Admin', 'lastName': 'Paul', 'email': 'fake-email@gmail.com' },
|
||||
'created': '2016-11-03T15:25:42.749+0000',
|
||||
'dueDate': '2016-11-03T15:25:42.749+0000',
|
||||
'endDate': null,
|
||||
|
@ -5,7 +5,6 @@
|
||||
[showValidationIcon]="showFormValidationIcon"
|
||||
[showRefreshButton]="showFormRefreshButton"
|
||||
[showCompleteButton]="showFormCompleteButton"
|
||||
[disableCompleteButton]="!isCompleteButtonEnabled()"
|
||||
[showSaveButton]="isSaveButtonVisible()"
|
||||
[readOnly]="isReadOnlyForm()"
|
||||
[fieldValidators]="fieldValidators"
|
||||
@ -61,10 +60,11 @@
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="adf-task-form-actions">
|
||||
<ng-template [ngTemplateOutlet]="taskFormButtons"></ng-template>
|
||||
<button id="adf-no-form-cancel-button" mat-button *ngIf="showCancelButton" (click)="onCancel()">
|
||||
{{'ADF_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-button *ngIf="!isCompletedTask()" color="primary" (click)="onCompleteTask()" id="adf-no-form-complete-button">
|
||||
<button mat-button
|
||||
*ngIf="!isCompletedTask()" id="adf-no-form-complete-button"
|
||||
color="primary"
|
||||
[disabled]="canCompleteNoFormTask()"
|
||||
(click)="onCompleteTask()">
|
||||
{{'ADF_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE' | translate}}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
@ -73,18 +73,25 @@
|
||||
</ng-template>
|
||||
|
||||
<ng-template #taskFormButtons>
|
||||
<button mat-button id="adf-no-form-cancel-button"
|
||||
*ngIf="showCancelButton"
|
||||
(click)="onCancel()">
|
||||
{{'ADF_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate}}
|
||||
</button>
|
||||
<button mat-button data-automation-id="adf-task-form-claim-button"
|
||||
*ngIf="isTaskClaimable()"
|
||||
adf-claim-task
|
||||
[taskId]="taskId"
|
||||
(success)="onClaimTask($event)">
|
||||
(success)="onClaimTask($event)"
|
||||
(error)="onClaimTaskError($event)">
|
||||
{{ 'ADF_TASK_LIST.DETAILS.BUTTON.CLAIM' | translate }}
|
||||
</button>
|
||||
<button mat-button data-automation-id="adf-task-form-unclaim-button"
|
||||
*ngIf="isTaskClaimedByCandidateMember()"
|
||||
adf-unclaim-task
|
||||
[taskId]="taskId"
|
||||
(success)="onUnclaimTask($event)">
|
||||
(success)="onUnclaimTask($event)"
|
||||
(error)="onUnclaimTaskError($event)">
|
||||
{{ 'ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
|
@ -40,7 +40,8 @@ import {
|
||||
initiatorCanCompleteTaskDetailsMock,
|
||||
taskDetailsWithOutCandidateGroup,
|
||||
claimedTaskDetailsMock,
|
||||
claimedByGroupMemberMock
|
||||
claimedByGroupMemberMock,
|
||||
initiatorWithCandidatesTaskDetailsMock
|
||||
} from '../../../mock/task/task-details.mock';
|
||||
import { TaskDetailsModel } from '../../models/task-details.model';
|
||||
import { ProcessTestingModule } from '../../../testing/process.testing.module';
|
||||
@ -80,7 +81,7 @@ describe('TaskFormComponent', () => {
|
||||
taskDetailsMock.processDefinitionId = null;
|
||||
spyOn(formService, 'getTask').and.returnValue(of(taskDetailsMock));
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
getBpmLoggedUserSpy = spyOn(authService, 'getBpmLoggedUser').and.returnValue(of({ email: 'fake-email' }));
|
||||
getBpmLoggedUserSpy = spyOn(authService, 'getBpmLoggedUser').and.returnValue(of({ id: 1001, email: 'fake-email@gmail.com' }));
|
||||
});
|
||||
|
||||
afterEach(async() => {
|
||||
@ -129,22 +130,6 @@ describe('TaskFormComponent', () => {
|
||||
expect(formCompletedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should be able to complete the task as a process initiator', async () => {
|
||||
const formCompletedSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
|
||||
const completeTaskFormSpy = spyOn(formService, 'completeTaskForm').and.returnValue(of({}));
|
||||
getTaskDetailsSpy.and.returnValue(of(initiatorCanCompleteTaskDetailsMock));
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const completeButton = fixture.debugElement.nativeElement.querySelector('#adf-form-complete');
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(completeButton['disabled']).toEqual(false);
|
||||
completeButton.click();
|
||||
expect(completeTaskFormSpy).toHaveBeenCalled();
|
||||
expect(formCompletedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should emit error event in case form complete service fails', async () => {
|
||||
const errorSpy: jasmine.Spy = spyOn(component.error, 'emit');
|
||||
const completeTaskFormSpy = spyOn(formService, 'completeTaskForm').and.returnValue(throwError({message: 'servce failed'}));
|
||||
@ -160,7 +145,6 @@ describe('TaskFormComponent', () => {
|
||||
expect(completeTaskFormSpy).toHaveBeenCalled();
|
||||
expect(errorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('change detection', () => {
|
||||
@ -500,6 +484,97 @@ describe('TaskFormComponent', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('Complete task', () => {
|
||||
|
||||
it('Should be able to complete the assigned task in case process initiator not allowed to complete the task', async () => {
|
||||
getBpmLoggedUserSpy.and.returnValue(of({ id: 1002, firstName: 'Wilbur', lastName: 'Adams', email: 'wilbur@app.activiti.com' }));
|
||||
getTaskDetailsSpy.and.returnValue(of(taskDetailsMock));
|
||||
|
||||
const formCompletedSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
|
||||
const completeTaskFormSpy = spyOn(formService, 'completeTaskForm').and.returnValue(of({}));
|
||||
component.taskId = '123';
|
||||
component.ngOnInit();
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const completeButton = fixture.debugElement.nativeElement.querySelector('#adf-form-complete');
|
||||
|
||||
expect(component.isProcessInitiator()).toEqual(false);
|
||||
expect(completeButton['disabled']).toEqual(false);
|
||||
|
||||
completeButton.click();
|
||||
|
||||
expect(completeTaskFormSpy).toHaveBeenCalled();
|
||||
expect(formCompletedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should be able to complete the task if process initiator allowed to complete the task', async () => {
|
||||
getBpmLoggedUserSpy.and.returnValue(of({ id: 1001, firstName: 'Wilbur', lastName: 'Adams', email: 'wilbur@app.activiti.com' }));
|
||||
const formCompletedSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
|
||||
const completeTaskFormSpy = spyOn(formService, 'completeTaskForm').and.returnValue(of({}));
|
||||
getTaskDetailsSpy.and.returnValue(of(initiatorCanCompleteTaskDetailsMock));
|
||||
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const completeButton = fixture.debugElement.nativeElement.querySelector('#adf-form-complete');
|
||||
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(completeButton['disabled']).toEqual(false);
|
||||
expect(component.isProcessInitiator()).toEqual(true);
|
||||
|
||||
completeButton.click();
|
||||
|
||||
expect(completeTaskFormSpy).toHaveBeenCalled();
|
||||
expect(formCompletedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should not be able to complete a task with candidates users if process initiator allowed to complete the task', async () => {
|
||||
getTaskDetailsSpy.and.returnValue(of(initiatorWithCandidatesTaskDetailsMock));
|
||||
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(component.canInitiatorComplete()).toEqual(true);
|
||||
expect(component.isProcessInitiator()).toEqual(true);
|
||||
expect(component.isCandidateMember()).toEqual(true);
|
||||
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const completeButton = fixture.debugElement.nativeElement.querySelector('#adf-form-complete');
|
||||
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(completeButton['disabled']).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should be able to complete a task with candidates users if process initiator not allowed to complete the task', async () => {
|
||||
const formCompletedSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
|
||||
getBpmLoggedUserSpy.and.returnValue(of({ id: 1001, firstName: 'Wilbur', lastName: 'Adams', email: 'wilbur@app.activiti.com' }));
|
||||
const completeTaskFormSpy = spyOn(formService, 'completeTaskForm').and.returnValue(of({}));
|
||||
getTaskDetailsSpy.and.returnValue(of(claimedTaskDetailsMock));
|
||||
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const completeButton = fixture.debugElement.nativeElement.querySelector('#adf-form-complete');
|
||||
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(component.canInitiatorComplete()).toEqual(false);
|
||||
expect(component.isProcessInitiator()).toEqual(false);
|
||||
expect(completeButton['disabled']).toEqual(false);
|
||||
|
||||
completeButton.click();
|
||||
|
||||
expect(completeTaskFormSpy).toHaveBeenCalled();
|
||||
expect(formCompletedSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Claim/Unclaim buttons', () => {
|
||||
|
||||
it('should display the claim button if no assignee', async() => {
|
||||
@ -589,7 +664,7 @@ describe('TaskFormComponent', () => {
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
|
||||
component.taskClaimed.subscribe((taskId) => {
|
||||
component.taskClaimed.subscribe((taskId: string) => {
|
||||
expect(taskId).toEqual(component.taskId);
|
||||
done();
|
||||
});
|
||||
@ -601,6 +676,25 @@ describe('TaskFormComponent', () => {
|
||||
claimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit error event in case claim task api fails', (done) => {
|
||||
const mockError = { message: 'Api Failed' };
|
||||
spyOn(taskListService, 'claimTask').and.returnValue(throwError(mockError));
|
||||
getTaskDetailsSpy.and.returnValue(of(claimableTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
|
||||
component.error.subscribe((error: any) => {
|
||||
expect(error).toEqual(mockError);
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
const claimBtn = fixture.debugElement.query(By.css('[adf-claim-task]'));
|
||||
claimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit taskUnClaimed when task is unclaimed', (done) => {
|
||||
spyOn(taskListService, 'unclaimTask').and.returnValue(of({}));
|
||||
getBpmLoggedUserSpy.and.returnValue(of(claimedTaskDetailsMock.assignee));
|
||||
@ -619,5 +713,25 @@ describe('TaskFormComponent', () => {
|
||||
const unclaimBtn = fixture.debugElement.query(By.css('[adf-unclaim-task]'));
|
||||
unclaimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit error event in case unclaim task api fails', (done) => {
|
||||
const mockError = { message: 'Api Failed' };
|
||||
spyOn(taskListService, 'unclaimTask').and.returnValue(throwError(mockError));
|
||||
getBpmLoggedUserSpy.and.returnValue(of(claimedTaskDetailsMock.assignee));
|
||||
getTaskDetailsSpy.and.returnValue(of(claimedTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
|
||||
component.error.subscribe((error: any) => {
|
||||
expect(error).toEqual(mockError);
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
const unclaimBtn = fixture.debugElement.query(By.css('[adf-unclaim-task]'));
|
||||
unclaimBtn.nativeElement.click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -272,15 +272,26 @@ export class TaskFormComponent implements OnInit {
|
||||
}
|
||||
|
||||
isReadOnlyForm(): boolean {
|
||||
return this.internalReadOnlyForm || !(this.isAssignedToMe() || this.canInitiatorComplete());
|
||||
let readOnlyForm: boolean;
|
||||
if (this.isCandidateMember()) {
|
||||
readOnlyForm = this.internalReadOnlyForm || !this.isAssignedToMe();
|
||||
} else {
|
||||
readOnlyForm = this.internalReadOnlyForm || !(this.isAssignedToMe() || (this.canInitiatorComplete() && this.isProcessInitiator()));
|
||||
}
|
||||
|
||||
return readOnlyForm;
|
||||
}
|
||||
|
||||
isProcessInitiator(): boolean {
|
||||
return this.currentLoggedUser && ( this.currentLoggedUser.id === +this.taskDetails.processInstanceStartUserId);
|
||||
}
|
||||
|
||||
isSaveButtonVisible(): boolean {
|
||||
return this.showFormSaveButton && (!this.canInitiatorComplete() || this.isAssignedToMe());
|
||||
}
|
||||
|
||||
canCompleteTask(): boolean {
|
||||
return !this.isCompletedTask() && this.isAssignedToMe();
|
||||
canCompleteNoFormTask(): boolean {
|
||||
return this.isReadOnlyForm();
|
||||
}
|
||||
|
||||
getCompletedTaskTranslatedMessage(): Observable<string> {
|
||||
@ -307,7 +318,15 @@ export class TaskFormComponent implements OnInit {
|
||||
this.taskClaimed.emit(taskId);
|
||||
}
|
||||
|
||||
onClaimTaskError(error: any) {
|
||||
this.error.emit(error);
|
||||
}
|
||||
|
||||
onUnclaimTask(taskId: string) {
|
||||
this.taskUnclaimed.emit(taskId);
|
||||
}
|
||||
|
||||
onUnclaimTaskError(error: any) {
|
||||
this.error.emit(error);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user