mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-24 17:32:15 +00:00
[ACA-30333]FE - [Process-services] Create ADF task form. (#5611)
* [ACA-3033] FE - [Process-services] Create ADF task form. * * Modifed task-from-template * * Refactored details component with the task-form * * Updated unit tests to the recent changes * * Modified task-details component * * Fixed failing tests * * Fixed failing tests* Added doc * * Fixed task-details-form e2e * Fixed failing test on tas-details e2e * * Fixed flaky process-services e2e * * Fixed flaky e2e tests
This commit is contained in:
@@ -336,5 +336,20 @@
|
||||
"CHOOSE_ITEM": "Choose {{ name }} to...",
|
||||
"CHOOSE_IN": "Choose file in {{ name }}..."
|
||||
}
|
||||
},
|
||||
"ADF_TASK_FORM": {
|
||||
"EMPTY_FORM": {
|
||||
"SUBTITLE": "Attach a form that can be viewed later",
|
||||
"COMPLETE-TASK-MESSAGE": "Task {{taskName}} completed",
|
||||
"COMPLETE-TASK-SUB-MESSAGE": "No forms to be added",
|
||||
"BUTTONS": {
|
||||
"COMPLETE": "COMPLETE",
|
||||
"CANCEL": "CANCEL"
|
||||
}
|
||||
},
|
||||
"COMPLETED_TASK": {
|
||||
"TITLE": "Task {{taskName}} completed",
|
||||
"SUBTITLE": "No forms to be added"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -97,6 +97,46 @@ export let standaloneTaskWithoutForm = new TaskDetailsModel({
|
||||
memberOfCandidateGroup: false
|
||||
});
|
||||
|
||||
export let completedStandaloneTaskWithoutForm = new TaskDetailsModel({
|
||||
id: '200',
|
||||
name: 'Standalone Task Without Form',
|
||||
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: new Date(),
|
||||
duration: null,
|
||||
priority: 50,
|
||||
parentTaskId: null,
|
||||
parentTaskName: null,
|
||||
processInstanceId: null,
|
||||
processInstanceName: null,
|
||||
processDefinitionId: null,
|
||||
processDefinitionName: null,
|
||||
processDefinitionDescription: null,
|
||||
processDefinitionKey: null,
|
||||
processDefinitionCategory: null,
|
||||
processDefinitionVersion: null,
|
||||
processDefinitionDeploymentId: null,
|
||||
formKey: null,
|
||||
processInstanceStartUserId: null,
|
||||
initiatorCanCompleteTask: false,
|
||||
adhocTaskCanBeReassigned: false,
|
||||
taskDefinitionKey: 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
|
||||
executionId: '86',
|
||||
involvedGroups: [],
|
||||
involvedPeople: [],
|
||||
memberOfCandidateUsers: false,
|
||||
managerOfCandidateGroup: false,
|
||||
memberOfCandidateGroup: false
|
||||
});
|
||||
|
||||
export let taskDetailsMock = new TaskDetailsModel({
|
||||
id: '91',
|
||||
name: 'Request translation',
|
||||
@@ -137,6 +177,54 @@ export let taskDetailsMock = new TaskDetailsModel({
|
||||
memberOfCandidateGroup: false
|
||||
});
|
||||
|
||||
export let initiatorCanCompleteTaskDetailsMock = new TaskDetailsModel({
|
||||
id: '91',
|
||||
name: 'Request translation',
|
||||
description: null,
|
||||
category: null,
|
||||
assignee: { email: 'mock-user-email' },
|
||||
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,
|
||||
firstName: 'Wilbur',
|
||||
lastName: 'Adams',
|
||||
email: 'wilbur@app.activiti.com'
|
||||
},
|
||||
{
|
||||
id: 111,
|
||||
firstName: 'fake-first-name',
|
||||
lastName: 'fake-last-name',
|
||||
email: 'fake@app.activiti.com'
|
||||
}
|
||||
],
|
||||
memberOfCandidateUsers: false,
|
||||
managerOfCandidateGroup: false,
|
||||
memberOfCandidateGroup: false
|
||||
});
|
||||
|
||||
export let taskDetailsWithOutAssigneeMock = new TaskDetailsModel({
|
||||
id: '91',
|
||||
name: 'Request translation',
|
||||
@@ -183,6 +271,7 @@ export let claimableTaskDetailsMock = new TaskDetailsModel({
|
||||
endDate: null,
|
||||
duration: null,
|
||||
priority: 50,
|
||||
formKey: '4',
|
||||
parentTaskId: null,
|
||||
parentTaskName: null,
|
||||
processInstanceId: '86',
|
||||
@@ -353,6 +442,36 @@ export let taskDetailsWithOutCandidateGroup = new TaskDetailsModel({
|
||||
]
|
||||
});
|
||||
|
||||
export let completedTaskWithFormMock = 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: new Date(),
|
||||
duration: null,
|
||||
priority: 50,
|
||||
formKey: '91',
|
||||
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 completedTaskDetailsMock = new TaskDetailsModel({
|
||||
id: '91',
|
||||
name: 'Request translation',
|
||||
@@ -366,9 +485,10 @@ export let completedTaskDetailsMock = new TaskDetailsModel({
|
||||
},
|
||||
created: '2016-11-03T15:25:42.749+0000',
|
||||
dueDate: null,
|
||||
endDate: '2016-11-03T15:25:42.749+0000',
|
||||
endDate: new Date(),
|
||||
duration: null,
|
||||
priority: 50,
|
||||
formKey: null,
|
||||
parentTaskId: null,
|
||||
parentTaskName: null,
|
||||
processInstanceId: '86',
|
||||
@@ -382,6 +502,40 @@ export let completedTaskDetailsMock = new TaskDetailsModel({
|
||||
memberOfCandidateUsers: false
|
||||
});
|
||||
|
||||
export let taskDetailsWithOutFormMock = new TaskDetailsModel({
|
||||
'id': '91',
|
||||
'name': 'Request translation',
|
||||
'description': 'fake description',
|
||||
'category': null,
|
||||
'assignee': {'id': 1001, 'firstName': 'Admin', 'lastName': 'Paul', 'email': 'my@mymail.com' },
|
||||
'created': '2016-11-03T15:25:42.749+0000',
|
||||
'dueDate': '2016-11-03T15:25:42.749+0000',
|
||||
'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': null,
|
||||
'processInstanceStartUserId': '1001',
|
||||
'initiatorCanCompleteTask': false,
|
||||
'adhocTaskCanBeReassigned': false,
|
||||
'taskDefinitionKey': 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
|
||||
'executionId': '86',
|
||||
'involvedPeople': [],
|
||||
'memberOfCandidateUsers': false,
|
||||
'managerOfCandidateGroup': false,
|
||||
'memberOfCandidateGroup': false
|
||||
});
|
||||
|
||||
export const taskFormMock = {
|
||||
id: 4,
|
||||
name: 'Translation request',
|
||||
|
@@ -11,6 +11,7 @@
|
||||
@import '../content-widget/attach-file-widget-dialog.component';
|
||||
@import '../form/start-form.component';
|
||||
@import '../process-list/components/start-process.component';
|
||||
@import '../task-list/components/task-form/task-form.component';
|
||||
|
||||
@mixin adf-process-services-theme($theme) {
|
||||
@include adf-process-filters-theme($theme);
|
||||
@@ -26,4 +27,5 @@
|
||||
@include adf-attach-file-widget-dialog-component-theme($theme);
|
||||
@include adf-start-form-component-theme($theme);
|
||||
@include adf-process-services-create-theme($theme);
|
||||
@include adf-task-form-theme($theme);
|
||||
}
|
||||
|
@@ -1,7 +1,7 @@
|
||||
<div class="adf-attach-form">
|
||||
<mat-card>
|
||||
<mat-card-content>
|
||||
<div class="adf-no-form-message-container">
|
||||
<div class="adf-attache-form-message-container">
|
||||
<mat-card-title class="mat-card-title">
|
||||
<h4 class="adf-form-title">{{ 'ADF_TASK_LIST.ATTACH_FORM.SELECT_FORM' | translate }}</h4>
|
||||
</mat-card-title>
|
||||
@@ -23,13 +23,13 @@
|
||||
</div>
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions class="adf-no-form-mat-card-actions">
|
||||
<mat-card-actions class="adf-attach-form-mat-card-actions">
|
||||
<div>
|
||||
<button mat-button id="adf-no-form-remove-button" color="warn" *ngIf="formKey" (click)="onRemoveButtonClick()">{{ 'ADF_TASK_LIST.ATTACH_FORM.REMOVE_FORM' | translate }}</button>
|
||||
<button mat-button id="adf-attach-form-remove-button" color="warn" *ngIf="formKey" (click)="onRemoveButtonClick()">{{ 'ADF_TASK_LIST.ATTACH_FORM.REMOVE_FORM' | translate }}</button>
|
||||
</div>
|
||||
<div>
|
||||
<button mat-button id="adf-no-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
|
||||
<button mat-button id="adf-no-form-attach-form-button" [disabled]="disableSubmit" color="primary" (click)="onAttachFormButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button>
|
||||
<button mat-button id="adf-attach-form-cancel-button" (click)="onCancelButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL' | translate }}</button>
|
||||
<button mat-button id="adf-attach-form-attach-button" [disabled]="disableSubmit" color="primary" (click)="onAttachFormButtonClick()">{{ 'ADF_TASK_LIST.START_TASK.FORM.LABEL.ATTACHFORM' | translate }}</button>
|
||||
</div>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
@@ -9,7 +9,7 @@
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.adf-no-form-mat-card-actions {
|
||||
.adf-attach-form-mat-card-actions {
|
||||
justify-content: space-between;
|
||||
margin-top: 30px;
|
||||
text-align: right;
|
||||
|
@@ -51,7 +51,7 @@ describe('AttachFormComponent', () => {
|
||||
it('should show the attach button disabled', async(() => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const attachButton = fixture.debugElement.query(By.css('#adf-no-form-attach-form-button'));
|
||||
const attachButton = fixture.debugElement.query(By.css('#adf-attach-form-attach-button'));
|
||||
expect(attachButton.nativeElement.disabled).toBeTruthy();
|
||||
});
|
||||
}));
|
||||
@@ -60,7 +60,7 @@ describe('AttachFormComponent', () => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const emitSpy = spyOn(component.cancelAttachForm, 'emit');
|
||||
const el = fixture.nativeElement.querySelector('#adf-no-form-cancel-button');
|
||||
const el = fixture.nativeElement.querySelector('#adf-attach-form-cancel-button');
|
||||
el.click();
|
||||
expect(emitSpy).toHaveBeenCalled();
|
||||
});
|
||||
@@ -72,8 +72,8 @@ describe('AttachFormComponent', () => {
|
||||
spyOn(taskService, 'attachFormToATask').and.returnValue(of(true));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
expect(element.querySelector('#adf-no-form-attach-form-button')).toBeDefined();
|
||||
const el = fixture.nativeElement.querySelector('#adf-no-form-attach-form-button');
|
||||
expect(element.querySelector('#adf-attach-form-attach-button')).toBeDefined();
|
||||
const el = fixture.nativeElement.querySelector('#adf-attach-form-attach-button');
|
||||
el.click();
|
||||
expect(taskService.attachFormToATask).toHaveBeenCalledWith(1, 2);
|
||||
});
|
||||
@@ -87,7 +87,7 @@ describe('AttachFormComponent', () => {
|
||||
spyOn(taskService, 'attachFormToATask').and.returnValue(of(true));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const attachButton = fixture.debugElement.query(By.css('#adf-no-form-attach-form-button'));
|
||||
const attachButton = fixture.debugElement.query(By.css('#adf-attach-form-attach-button'));
|
||||
expect(attachButton.nativeElement.disabled).toBeFalsy();
|
||||
});
|
||||
}));
|
||||
@@ -103,7 +103,7 @@ describe('AttachFormComponent', () => {
|
||||
fixture.detectChanges();
|
||||
component.attachFormControl.setValue(2);
|
||||
fixture.detectChanges();
|
||||
const attachButton = fixture.debugElement.query(By.css('#adf-no-form-attach-form-button'));
|
||||
const attachButton = fixture.debugElement.query(By.css('#adf-attach-form-attach-button'));
|
||||
expect(attachButton.nativeElement.disabled).toBeTruthy();
|
||||
});
|
||||
}));
|
||||
@@ -140,8 +140,8 @@ describe('AttachFormComponent', () => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(element.querySelector('#adf-no-form-remove-button')).toBeDefined();
|
||||
const el = fixture.nativeElement.querySelector('#adf-no-form-remove-button');
|
||||
expect(element.querySelector('#adf-attach-form-remove-button')).toBeDefined();
|
||||
const el = fixture.nativeElement.querySelector('#adf-attach-form-remove-button');
|
||||
el.click();
|
||||
expect(component.formId).toBeNull();
|
||||
});
|
||||
@@ -163,7 +163,7 @@ describe('AttachFormComponent', () => {
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
const emitSpy = spyOn(component.success, 'emit');
|
||||
const el = fixture.nativeElement.querySelector('#adf-no-form-attach-form-button');
|
||||
const el = fixture.nativeElement.querySelector('#adf-attach-form-attach-button');
|
||||
el.click();
|
||||
expect(emitSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
@@ -17,19 +17,14 @@
|
||||
|
||||
import { NoTaskDetailsTemplateDirective } from './no-task-detail-template.directive';
|
||||
import { TaskDetailsComponent } from './task-details.component';
|
||||
import { AuthenticationService } from '@alfresco/adf-core';
|
||||
import { of } from 'rxjs';
|
||||
|
||||
describe('NoTaskDetailsTemplateDirective', () => {
|
||||
|
||||
let component: NoTaskDetailsTemplateDirective;
|
||||
let detailsComponent: TaskDetailsComponent;
|
||||
let authService: AuthenticationService;
|
||||
|
||||
beforeEach(() => {
|
||||
authService = new AuthenticationService(null, null, null, null, null);
|
||||
spyOn(authService, 'getBpmLoggedUser').and.returnValue(of({ email: 'fake-email' }));
|
||||
detailsComponent = new TaskDetailsComponent(null, authService, null, null, null, null);
|
||||
detailsComponent = new TaskDetailsComponent(null, null, null, null, null);
|
||||
component = new NoTaskDetailsTemplateDirective(detailsComponent);
|
||||
});
|
||||
|
||||
|
@@ -22,51 +22,21 @@
|
||||
|
||||
<div class="adf-task-details-core-form">
|
||||
<div *ngIf="isAssigned()">
|
||||
<adf-form *ngIf="isFormComponentVisible()" #activitiForm
|
||||
<adf-task-form
|
||||
[taskId]="taskDetails.id"
|
||||
[showTitle]="showFormTitle"
|
||||
[showRefreshButton]="showFormRefreshButton"
|
||||
[showCompleteButton]="showFormCompleteButton"
|
||||
[disableCompleteButton]="!isCompleteButtonEnabled()"
|
||||
[showSaveButton]="isSaveButtonVisible()"
|
||||
[readOnly]="internalReadOnlyForm"
|
||||
[showFormTitle]="showFormTitle"
|
||||
[showFormRefreshButton]="showFormRefreshButton"
|
||||
[showCancelButton]="true"
|
||||
[fieldValidators]="fieldValidators"
|
||||
(formSaved)='onFormSaved($event)'
|
||||
(formCompleted)='onFormCompleted($event)'
|
||||
(formContentClicked)='onFormContentClick($event)'
|
||||
(formLoaded)='onFormLoaded($event)'
|
||||
(error)='onFormError($event)'
|
||||
(executeOutcome)='onFormExecuteOutcome($event)'>
|
||||
</adf-form>
|
||||
<adf-task-standalone *ngIf="isTaskStandaloneComponentVisible()"
|
||||
[taskName]="taskDetails.name"
|
||||
[taskId]="taskDetails.id"
|
||||
[isCompleted]="isCompletedTask()"
|
||||
[hasCompletePermission]="isCompleteButtonEnabled()"
|
||||
[hideCancelButton]="true"
|
||||
(complete)="onComplete()"
|
||||
(showAttachForm)="onShowAttachForm()">
|
||||
</adf-task-standalone>
|
||||
|
||||
<mat-card class="adf-message-card" *ngIf="!isTaskStandaloneComponentVisible() && !isCompletedTask() && !isFormComponentVisible()" >
|
||||
<mat-card-content>
|
||||
<div class="adf-no-form-message-container">
|
||||
<div class="adf-no-form-message-list">
|
||||
<div *ngIf="!isCompletedTask()" class="adf-no-form-message">
|
||||
<span id="adf-no-form-message">{{'ADF_TASK_LIST.STANDALONE_TASK.NO_FORM_MESSAGE' | translate}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions class="adf-no-form-mat-card-actions">
|
||||
<div>
|
||||
<button mat-button id="adf-no-form-complete-button" color="primary" (click)="onComplete()">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.COMPLETE' | translate }}</button>
|
||||
</div>
|
||||
</mat-card-actions>
|
||||
|
||||
</mat-card>
|
||||
|
||||
(completed)="onComplete()"
|
||||
(showAttachForm)="onShowAttachForm()"
|
||||
(executeOutcome)='onFormExecuteOutcome($event)'
|
||||
(error)="onFormError($event)" #activitiTaskForm>
|
||||
</adf-task-form>
|
||||
<adf-attach-form *ngIf="isShowAttachForm()"
|
||||
[taskId]="taskDetails.id"
|
||||
[formKey]="taskDetails.formKey"
|
||||
|
@@ -36,8 +36,6 @@ import { TaskDetailsModel } from '../models/task-details.model';
|
||||
import {
|
||||
noDataMock,
|
||||
taskDetailsMock,
|
||||
standaloneTaskWithForm,
|
||||
standaloneTaskWithoutForm,
|
||||
taskFormMock,
|
||||
tasksMock,
|
||||
taskDetailsWithOutAssigneeMock
|
||||
@@ -64,7 +62,6 @@ describe('TaskDetailsComponent', () => {
|
||||
let getTaskDetailsSpy: jasmine.Spy;
|
||||
let getTasksSpy: jasmine.Spy;
|
||||
let assignTaskSpy: jasmine.Spy;
|
||||
let completeTaskSpy: jasmine.Spy;
|
||||
let logService: LogService;
|
||||
let commentProcessService: CommentProcessService;
|
||||
let peopleProcessService: PeopleProcessService;
|
||||
@@ -99,7 +96,6 @@ describe('TaskDetailsComponent', () => {
|
||||
|
||||
getTasksSpy = spyOn(service, 'getTasks').and.returnValue(of(tasksMock));
|
||||
assignTaskSpy = spyOn(service, 'assignTask').and.returnValue(of(fakeUser));
|
||||
completeTaskSpy = spyOn(service, 'completeTask').and.returnValue(of({}));
|
||||
commentProcessService = TestBed.get(CommentProcessService);
|
||||
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
@@ -175,42 +171,6 @@ describe('TaskDetailsComponent', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display task standalone component when the task does not have an associated form', async(() => {
|
||||
component.taskId = '123';
|
||||
getTaskDetailsSpy.and.returnValue(of(standaloneTaskWithoutForm));
|
||||
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(component.isStandaloneTaskWithoutForm()).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not display task standalone component when the task has a form', async(() => {
|
||||
component.taskId = '123';
|
||||
getTaskDetailsSpy.and.returnValue(of(standaloneTaskWithForm));
|
||||
fixture.detectChanges();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(component.isStandaloneTaskWithForm()).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).toBeDefined();
|
||||
expect(fixture.debugElement.query(By.css('adf-task-standalone'))).not.toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display the AttachFormComponent when standaloneTaskWithForm and click on attach button', async(() => {
|
||||
component.taskId = '123';
|
||||
getTaskDetailsSpy.and.returnValue(of(standaloneTaskWithForm));
|
||||
fixture.detectChanges();
|
||||
component.onShowAttachForm();
|
||||
fixture.whenStable().then(() => {
|
||||
fixture.detectChanges();
|
||||
expect(component.isStandaloneTaskWithForm()).toBeTruthy();
|
||||
expect(fixture.debugElement.query(By.css('adf-attach-form'))).toBeDefined();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display the claim message when the task is not assigned', async(() => {
|
||||
component.taskDetails = taskDetailsWithOutAssigneeMock;
|
||||
fixture.detectChanges();
|
||||
@@ -229,86 +189,6 @@ describe('TaskDetailsComponent', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
describe('and form with visiblity', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
component.taskId = '123';
|
||||
spyOn(formService, 'completeTaskForm').and.returnValue(of({}));
|
||||
taskDetailsMock.formKey = '4';
|
||||
getTaskDetailsSpy.and.returnValue(of(taskDetailsMock));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('[C312410] - Should be possible to complete a task that has an invisible field on a form with a value', async (done) => {
|
||||
component.formCompleted.subscribe((form: FormModel) => {
|
||||
expect(form.id).toBe(taskFormMock.id);
|
||||
done();
|
||||
});
|
||||
component.taskDetails.initiatorCanCompleteTask = true;
|
||||
component.showNextTask = false;
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const inputTextOne: HTMLInputElement = fixture.nativeElement.querySelector('#text1');
|
||||
expect(inputTextOne).toBeDefined();
|
||||
expect(inputTextOne).not.toBeNull();
|
||||
const inputTextTwo: HTMLInputElement = fixture.nativeElement.querySelector('#text2');
|
||||
expect(inputTextTwo).toBeDefined();
|
||||
expect(inputTextTwo).not.toBeNull();
|
||||
let inputTextThree: HTMLInputElement = fixture.nativeElement.querySelector('#text3');
|
||||
expect(inputTextThree).toBeDefined();
|
||||
expect(inputTextThree).not.toBeNull();
|
||||
|
||||
inputTextOne.value = 'a';
|
||||
inputTextOne.dispatchEvent(new Event('input'));
|
||||
inputTextTwo.value = 'a';
|
||||
inputTextTwo.dispatchEvent(new Event('input'));
|
||||
inputTextThree.value = 'a';
|
||||
inputTextThree.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
inputTextThree = fixture.nativeElement.querySelector('#text3');
|
||||
expect(inputTextThree).toBeDefined();
|
||||
expect(inputTextThree).not.toBeNull();
|
||||
|
||||
inputTextOne.value = 'b';
|
||||
inputTextOne.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const inputThreeContainer = fixture.nativeElement.querySelector('#field-text3-container');
|
||||
expect(inputThreeContainer.hidden).toBe(true);
|
||||
const completeOutcomeButton: HTMLButtonElement = fixture.nativeElement.querySelector('#adf-form-complete');
|
||||
expect(completeOutcomeButton.hidden).toBe(false);
|
||||
completeOutcomeButton.click();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('[C277278] - Should show if the form is valid via the validation icon', async () => {
|
||||
const numberInput: HTMLInputElement = fixture.nativeElement.querySelector('#numberField');
|
||||
let validationForm = fixture.nativeElement.querySelector('#adf-valid-form-icon');
|
||||
|
||||
expect(numberInput).toBeDefined();
|
||||
expect(numberInput).not.toBeNull();
|
||||
expect(validationForm.textContent).toBe('check_circle');
|
||||
|
||||
numberInput.value = 'a';
|
||||
numberInput.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const invalidForm = fixture.nativeElement.querySelector('#adf-invalid-form-icon');
|
||||
expect(invalidForm).not.toBeNull();
|
||||
expect(invalidForm.textContent).toBe('error');
|
||||
|
||||
numberInput.value = '4';
|
||||
numberInput.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
validationForm = fixture.nativeElement.querySelector('#adf-valid-form-icon');
|
||||
expect(validationForm.textContent).toBe('check_circle');
|
||||
});
|
||||
});
|
||||
|
||||
describe('change detection', () => {
|
||||
|
||||
let change;
|
||||
@@ -414,11 +294,6 @@ describe('TaskDetailsComponent', () => {
|
||||
expect(getTasksSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should call service to complete task when complete button clicked', () => {
|
||||
component.onComplete();
|
||||
expect(completeTaskSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should emit a complete event when complete button clicked and task completed', () => {
|
||||
const emitSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
|
||||
component.onComplete();
|
||||
|
@@ -17,7 +17,6 @@
|
||||
|
||||
import {
|
||||
PeopleProcessService, UserProcessModel,
|
||||
AuthenticationService,
|
||||
CardViewUpdateService,
|
||||
ClickNotification,
|
||||
LogService,
|
||||
@@ -42,8 +41,8 @@ import { Observable, Observer, of, Subject } from 'rxjs';
|
||||
import { TaskQueryRequestRepresentationModel } from '../models/filter.model';
|
||||
import { TaskDetailsModel } from '../models/task-details.model';
|
||||
import { TaskListService } from './../services/tasklist.service';
|
||||
import { UserRepresentation } from '@alfresco/js-api';
|
||||
import { catchError, share, takeUntil } from 'rxjs/operators';
|
||||
import { TaskFormComponent } from './task-form/task-form.component';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-task-details',
|
||||
@@ -61,6 +60,9 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
@ViewChild('errorDialog')
|
||||
errorDialog: TemplateRef<any>;
|
||||
|
||||
@ViewChild('activitiTaskForm')
|
||||
taskFormComponent: TaskFormComponent;
|
||||
|
||||
/** Toggles debug mode. */
|
||||
@Input()
|
||||
debugMode: boolean = false;
|
||||
@@ -179,11 +181,9 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
peopleSearch: Observable<UserProcessModel[]>;
|
||||
|
||||
currentLoggedUser: UserRepresentation;
|
||||
data: any;
|
||||
|
||||
constructor(private taskListService: TaskListService,
|
||||
private authService: AuthenticationService,
|
||||
private peopleProcessService: PeopleProcessService,
|
||||
private logService: LogService,
|
||||
private cardViewUpdateService: CardViewUpdateService,
|
||||
@@ -192,9 +192,6 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
ngOnInit() {
|
||||
this.peopleSearch = new Observable<UserProcessModel[]>((observer) => this.peopleSearchObserver = observer).pipe(share());
|
||||
this.authService.getBpmLoggedUser().subscribe(user => {
|
||||
this.currentLoggedUser = user;
|
||||
});
|
||||
|
||||
if (this.taskId) {
|
||||
this.loadDetails(this.taskId);
|
||||
@@ -225,26 +222,6 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
}
|
||||
}
|
||||
|
||||
isStandaloneTask(): boolean {
|
||||
return !(this.taskDetails && (!!this.taskDetails.processDefinitionId));
|
||||
}
|
||||
|
||||
isStandaloneTaskWithForm(): boolean {
|
||||
return this.isStandaloneTask() && this.hasFormKey();
|
||||
}
|
||||
|
||||
isStandaloneTaskWithoutForm(): boolean {
|
||||
return this.isStandaloneTask() && !this.hasFormKey();
|
||||
}
|
||||
|
||||
isFormComponentVisible(): boolean {
|
||||
return this.hasFormKey() && !this.isShowAttachForm();
|
||||
}
|
||||
|
||||
isTaskStandaloneComponentVisible(): boolean {
|
||||
return this.isStandaloneTaskWithoutForm() && !this.isShowAttachForm();
|
||||
}
|
||||
|
||||
isShowAttachForm(): boolean {
|
||||
return this.showAttachForm;
|
||||
}
|
||||
@@ -256,13 +233,6 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.taskDetails = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the task has a form
|
||||
*/
|
||||
hasFormKey(): boolean {
|
||||
return (this.taskDetails && (!!this.taskDetails.formKey));
|
||||
}
|
||||
|
||||
isTaskActive() {
|
||||
return this.taskDetails && this.taskDetails.duration === null;
|
||||
}
|
||||
@@ -329,44 +299,6 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
return !!this.taskDetails.assignee;
|
||||
}
|
||||
|
||||
private hasEmailAddress(): boolean {
|
||||
return this.taskDetails.assignee.email ? true : false;
|
||||
}
|
||||
|
||||
isAssignedToMe(): boolean {
|
||||
return this.isAssigned() && this.hasEmailAddress() ?
|
||||
this.isEmailEqual(this.taskDetails.assignee.email, this.currentLoggedUser.email) :
|
||||
this.isExternalIdEqual(this.taskDetails.assignee.externalId, this.currentLoggedUser.externalId);
|
||||
}
|
||||
|
||||
private isEmailEqual(assigneeMail: string, currentLoggedEmail: string): boolean {
|
||||
return assigneeMail.toLocaleLowerCase() === currentLoggedEmail.toLocaleLowerCase();
|
||||
}
|
||||
|
||||
private isExternalIdEqual(assigneeExternalId: string, currentUserExternalId: string): boolean {
|
||||
return assigneeExternalId.toLocaleLowerCase() === currentUserExternalId.toLocaleLowerCase();
|
||||
}
|
||||
|
||||
isCompleteButtonEnabled(): boolean {
|
||||
return this.isAssignedToMe() || this.canInitiatorComplete();
|
||||
}
|
||||
|
||||
isCompleteButtonVisible(): boolean {
|
||||
return !this.hasFormKey() && this.isTaskActive() && this.isCompleteButtonEnabled();
|
||||
}
|
||||
|
||||
canInitiatorComplete(): boolean {
|
||||
return this.taskDetails.initiatorCanCompleteTask;
|
||||
}
|
||||
|
||||
isSaveButtonVisible(): boolean {
|
||||
return this.hasSaveButton() && (!this.canInitiatorComplete() || this.isAssignedToMe());
|
||||
}
|
||||
|
||||
hasSaveButton(): boolean {
|
||||
return this.showFormSaveButton;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the next open task
|
||||
* @param processInstanceId
|
||||
@@ -395,9 +327,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
* Complete button clicked
|
||||
*/
|
||||
onComplete(): void {
|
||||
this.taskListService
|
||||
.completeTask(this.taskId)
|
||||
.subscribe(() => this.onFormCompleted(null));
|
||||
this.onFormCompleted(null);
|
||||
}
|
||||
|
||||
onShowAttachForm() {
|
||||
@@ -410,6 +340,7 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
|
||||
onCompleteAttachForm() {
|
||||
this.showAttachForm = false;
|
||||
this.taskFormComponent.loadTask(this.taskId);
|
||||
this.loadDetails(this.taskId);
|
||||
}
|
||||
|
||||
@@ -464,10 +395,6 @@ export class TaskDetailsComponent implements OnInit, OnChanges, OnDestroy {
|
||||
this.loadDetails(taskId);
|
||||
}
|
||||
|
||||
isCompletedTask(): boolean {
|
||||
return this.taskDetails && this.taskDetails.endDate ? true : undefined;
|
||||
}
|
||||
|
||||
searchUser(searchedWord: string) {
|
||||
this.peopleProcessService.getWorkflowUsers(null, searchedWord)
|
||||
.subscribe(
|
||||
|
@@ -0,0 +1,74 @@
|
||||
<ng-container *ngIf="!loading; else loadingTemplate">
|
||||
<adf-form *ngIf="hasFormKey(); else withoutForm"
|
||||
[taskId]="taskDetails?.id"
|
||||
[showTitle]="showFormTitle"
|
||||
[showValidationIcon]="showFormValidationIcon"
|
||||
[showRefreshButton]="showFormRefreshButton"
|
||||
[showCompleteButton]="showFormCompleteButton"
|
||||
[disableCompleteButton]="!isCompleteButtonEnabled()"
|
||||
[showSaveButton]="isSaveButtonVisible()"
|
||||
[readOnly]="isReadOnlyForm()"
|
||||
[fieldValidators]="fieldValidators"
|
||||
(formSaved)='onFormSaved($event)'
|
||||
(formCompleted)='onFormCompleted($event)'
|
||||
(formContentClicked)='onFormContentClick($event)'
|
||||
(formLoaded)='onFormLoaded($event)'
|
||||
(formError)='onFormError($event)'
|
||||
(error)='onError($event)'
|
||||
(executeOutcome)='onFormExecuteOutcome($event)'>
|
||||
</adf-form>
|
||||
<ng-template #withoutForm>
|
||||
<adf-task-standalone *ngIf="isStandaloneTask(); else emptyFormMessage"
|
||||
[taskName]="taskDetails.name"
|
||||
[taskId]="taskDetails.id"
|
||||
[isCompleted]="isCompletedTask()"
|
||||
[hasCompletePermission]="isCompleteButtonVisible()"
|
||||
[hideCancelButton]="showCancelButton"
|
||||
(complete)="onCompleteTask()"
|
||||
(showAttachForm)="onShowAttachForm()">
|
||||
</adf-task-standalone>
|
||||
<ng-template #emptyFormMessage>
|
||||
<mat-card class="adf-task-form-container">
|
||||
<mat-card-header>
|
||||
<mat-card-title>
|
||||
<h4>
|
||||
<span class="adf-form-title">
|
||||
{{taskDetails.name}}
|
||||
<ng-container *ngIf="!taskDetails.name">
|
||||
{{'FORM.FORM_RENDERER.NAMELESS_TASK' | translate}}
|
||||
</ng-container>
|
||||
</span>
|
||||
</h4>
|
||||
</mat-card-title>
|
||||
</mat-card-header>
|
||||
<mat-card-content>
|
||||
<adf-empty-content *ngIf="isCompletedTask(); else emptyFormTemplate"
|
||||
[icon]="'description'"
|
||||
[title]="getCompletedTaskTranslatedMessage() | async"
|
||||
[subtitle]="'ADF_TASK_FORM.COMPLETED_TASK.SUBTITLE'">
|
||||
</adf-empty-content>
|
||||
<ng-template #emptyFormTemplate>
|
||||
<adf-empty-content
|
||||
[icon]="'description'"
|
||||
[title]="'ADF_TASK_LIST.STANDALONE_TASK.NO_FORM_MESSAGE'"
|
||||
[subtitle]="'ADF_TASK_FORM.EMPTY_FORM.SUBTITLE'">
|
||||
</adf-empty-content>
|
||||
</ng-template>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="adf-task-form-actions">
|
||||
<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">
|
||||
{{'ADF_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE' | translate}}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-template #loadingTemplate>
|
||||
<div fxLayout="row" fxLayoutAlign="center stretch">
|
||||
<mat-spinner></mat-spinner>
|
||||
</div>
|
||||
</ng-template>
|
@@ -0,0 +1,32 @@
|
||||
@mixin adf-task-form-theme($theme) {
|
||||
|
||||
$config: mat-typography-config();
|
||||
|
||||
.adf-task-form {
|
||||
&-container {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
&-actions {
|
||||
float: right;
|
||||
padding-bottom: 25px !important;
|
||||
padding-right: 25px !important;
|
||||
|
||||
& .mat-button {
|
||||
height: 36px;
|
||||
border-radius: 5px;
|
||||
|
||||
}
|
||||
|
||||
& .mat-button-wrapper {
|
||||
width: 58px;
|
||||
height: 20px;
|
||||
opacity: 0.54;
|
||||
font-size: mat-font-size($config, body-2);
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
@@ -0,0 +1,504 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
|
||||
import { TaskFormComponent } from './task-form.component';
|
||||
import {
|
||||
setupTestBed,
|
||||
TranslationService,
|
||||
TranslationMock,
|
||||
FormService,
|
||||
AuthenticationService,
|
||||
FormModel,
|
||||
FormOutcomeEvent,
|
||||
FormOutcomeModel
|
||||
} from '@alfresco/adf-core';
|
||||
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
|
||||
import { TaskListService } from '../../services/tasklist.service';
|
||||
import { TranslateStore } from '@ngx-translate/core';
|
||||
import { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
|
||||
import { of, throwError } from 'rxjs';
|
||||
import {
|
||||
taskFormMock,
|
||||
taskDetailsMock,
|
||||
completedTaskDetailsMock,
|
||||
taskDetailsWithOutFormMock,
|
||||
standaloneTaskWithoutForm,
|
||||
completedStandaloneTaskWithoutForm,
|
||||
claimableTaskDetailsMock,
|
||||
initiatorCanCompleteTaskDetailsMock
|
||||
} from '../../../mock/task/task-details.mock';
|
||||
import { TaskDetailsModel } from '../../models/task-details.model';
|
||||
import { TaskListModule } from '../../task-list.module';
|
||||
|
||||
describe('TaskFormComponent', () => {
|
||||
let component: TaskFormComponent;
|
||||
let fixture: ComponentFixture<TaskFormComponent>;
|
||||
let formService: FormService;
|
||||
let taskListService: TaskListService;
|
||||
let getTaskDetailsSpy: jasmine.Spy;
|
||||
let completeTaskSpy: jasmine.Spy;
|
||||
let element: HTMLElement;
|
||||
let authService: AuthenticationService;
|
||||
let getBpmLoggedUserSpy: jasmine.Spy;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
NoopAnimationsModule,
|
||||
TaskListModule
|
||||
],
|
||||
providers: [
|
||||
{ provide: TranslationService, useClass: TranslationMock },
|
||||
TranslateStore],
|
||||
schemas: [NO_ERRORS_SCHEMA]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(TaskFormComponent);
|
||||
component = fixture.componentInstance;
|
||||
element = fixture.nativeElement;
|
||||
|
||||
taskListService = TestBed.get(TaskListService);
|
||||
formService = TestBed.get(FormService);
|
||||
|
||||
getTaskDetailsSpy = spyOn(taskListService, 'getTaskDetails').and.returnValue(of(taskDetailsMock));
|
||||
completeTaskSpy = spyOn(taskListService, 'completeTask').and.returnValue(of({}));
|
||||
spyOn(formService, 'getTaskForm').and.returnValue(of(taskFormMock));
|
||||
taskDetailsMock.processDefinitionId = null;
|
||||
spyOn(formService, 'getTask').and.returnValue(of(taskDetailsMock));
|
||||
authService = TestBed.get(AuthenticationService);
|
||||
getBpmLoggedUserSpy = spyOn(authService, 'getBpmLoggedUser').and.returnValue(of({ email: 'fake-email' }));
|
||||
});
|
||||
|
||||
afterEach(async() => {
|
||||
await fixture.whenStable();
|
||||
getTaskDetailsSpy.calls.reset();
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
describe('Task with form', () => {
|
||||
|
||||
beforeEach(async() => {
|
||||
await fixture.whenStable();
|
||||
getTaskDetailsSpy.calls.reset();
|
||||
});
|
||||
|
||||
it('Should be able to display task form', async () => {
|
||||
component.taskId = '123';
|
||||
taskDetailsMock.formKey = '4';
|
||||
component.currentLoggedUser = taskDetailsMock.assignee;
|
||||
getTaskDetailsSpy.and.returnValue(of(taskDetailsMock));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const inputFieldOne = fixture.debugElement.nativeElement.querySelector('#text1');
|
||||
const inputFieldTwo = fixture.debugElement.nativeElement.querySelector('#text2');
|
||||
const inputFieldThree = fixture.debugElement.nativeElement.querySelector('#text3');
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(inputFieldOne['disabled']).toEqual(false);
|
||||
expect(inputFieldTwo['disabled']).toEqual(false);
|
||||
expect(inputFieldThree['disabled']).toEqual(false);
|
||||
});
|
||||
|
||||
it('Should be able to complete assigned task', async () => {
|
||||
getBpmLoggedUserSpy.and.returnValue(of({ id: 1001, 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(completeButton['disabled']).toEqual(false);
|
||||
completeButton.click();
|
||||
expect(completeTaskFormSpy).toHaveBeenCalled();
|
||||
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'}));
|
||||
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(errorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
describe('change detection', () => {
|
||||
|
||||
beforeEach(async() => {
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
getTaskDetailsSpy.calls.reset();
|
||||
});
|
||||
|
||||
it('should fetch new task details when taskId changed', () => {
|
||||
const change = new SimpleChange('123', '456', true);
|
||||
component.ngOnChanges({ 'taskId': change });
|
||||
fixture.detectChanges();
|
||||
expect(getTaskDetailsSpy).toHaveBeenCalledWith('123');
|
||||
});
|
||||
|
||||
it('should NOT fetch new task details when taskId changed to null', async () => {
|
||||
const nullChange = new SimpleChange('123', null, true);
|
||||
component.ngOnChanges({ 'taskId': nullChange });
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
expect(getTaskDetailsSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Task assigned to candidates', () => {
|
||||
|
||||
beforeEach(async() => {
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
getTaskDetailsSpy.calls.reset();
|
||||
});
|
||||
|
||||
it('Should be able to display form in readonly mode if the task assigned to candidates', async() => {
|
||||
getTaskDetailsSpy.and.returnValue(of(claimableTaskDetailsMock));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const inputFieldOne = fixture.debugElement.nativeElement.querySelector('#text1');
|
||||
const inputFieldTwo = fixture.debugElement.nativeElement.querySelector('#text2');
|
||||
const inputFieldThree = fixture.debugElement.nativeElement.querySelector('#text3');
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(inputFieldOne['disabled']).toEqual(true);
|
||||
expect(inputFieldTwo['disabled']).toEqual(true);
|
||||
expect(inputFieldThree['disabled']).toEqual(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Form events', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('Should emit a save event when form saved', () => {
|
||||
const formSavedSpy: jasmine.Spy = spyOn(component.formSaved, 'emit');
|
||||
component.onFormSaved(new FormModel());
|
||||
expect(formSavedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should emit a outcome execution event when form outcome executed', () => {
|
||||
const executeOutcomeSpy: jasmine.Spy = spyOn(component.executeOutcome, 'emit');
|
||||
component.onFormExecuteOutcome(new FormOutcomeEvent(new FormOutcomeModel(new FormModel())));
|
||||
expect(executeOutcomeSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should emit a complete event when form completed', () => {
|
||||
const formCompletedSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
|
||||
component.onFormCompleted(new FormModel());
|
||||
expect(formCompletedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should call service to complete task when complete button clicked', () => {
|
||||
component.onCompleteTask();
|
||||
expect(completeTaskSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should emit a complete event when complete button clicked and task completed', () => {
|
||||
const completeSpy: jasmine.Spy = spyOn(component.completed, 'emit');
|
||||
component.onCompleteTask();
|
||||
expect(completeSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should emit a load event when form loaded', () => {
|
||||
const formLoadedSpy: jasmine.Spy = spyOn(component.formLoaded, 'emit');
|
||||
component.onFormLoaded(new FormModel());
|
||||
expect(formLoadedSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should emit an error event when form error occurs', () => {
|
||||
const formErrorSpy: jasmine.Spy = spyOn(component.formError, 'emit');
|
||||
component.onFormError({});
|
||||
expect(formErrorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should emit an error event when form services fails', () => {
|
||||
const errorSpy: jasmine.Spy = spyOn(component.error, 'emit');
|
||||
component.onError({});
|
||||
expect(errorSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Completed Process Task', () => {
|
||||
|
||||
beforeEach(async() => {
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
getTaskDetailsSpy.calls.reset();
|
||||
});
|
||||
|
||||
it('Should be able to display form in readonly mode if the task completed', async() => {
|
||||
getTaskDetailsSpy.and.returnValue(of(completedTaskDetailsMock));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const inputFieldOne = fixture.debugElement.nativeElement.querySelector('#text1');
|
||||
const inputFieldTwo = fixture.debugElement.nativeElement.querySelector('#text2');
|
||||
const inputFieldThree = fixture.debugElement.nativeElement.querySelector('#text3');
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(inputFieldOne['disabled']).toEqual(true);
|
||||
expect(inputFieldTwo['disabled']).toEqual(true);
|
||||
expect(inputFieldThree['disabled']).toEqual(true);
|
||||
});
|
||||
|
||||
it('Should be able to show completed message and cancel button for the completed task without form', async () => {
|
||||
completedTaskDetailsMock.formKey = null;
|
||||
component.taskDetails = new TaskDetailsModel(completedTaskDetailsMock);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const completeButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-complete-button');
|
||||
const cancelButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-cancel-button');
|
||||
const completedFormMessage = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__title');
|
||||
const subMessage = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__subtitle');
|
||||
expect(completeButtonElement).toBeNull();
|
||||
expect(cancelButtonElement).not.toBeNull();
|
||||
expect(completedFormMessage.innerText).toContain('ADF_TASK_FORM.COMPLETED_TASK.TITLE');
|
||||
expect(subMessage.innerText).toContain('ADF_TASK_FORM.COMPLETED_TASK.SUBTITLE');
|
||||
});
|
||||
|
||||
it('Should not display complete button to the completed task without form', async () => {
|
||||
completedTaskDetailsMock.formKey = null;
|
||||
component.taskDetails = new TaskDetailsModel(completedTaskDetailsMock);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const completeButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-complete-button');
|
||||
expect(completeButtonElement).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Process Task with no form', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('Should be able to show no form message if the task does not attached a form', async () => {
|
||||
component.taskDetails = new TaskDetailsModel(taskDetailsWithOutFormMock);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const completeButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-complete-button');
|
||||
const cancelButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-cancel-button');
|
||||
const completedFormMessage = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__title');
|
||||
const subMessage = fixture.debugElement.nativeElement.querySelector('.adf-empty-content__subtitle');
|
||||
expect(completeButtonElement).not.toBeNull();
|
||||
expect(cancelButtonElement).not.toBeNull();
|
||||
expect(completedFormMessage.innerText).toContain('ADF_TASK_LIST.STANDALONE_TASK.NO_FORM_MESSAGE');
|
||||
expect(subMessage.innerText).toContain('ADF_TASK_FORM.EMPTY_FORM.SUBTITLE');
|
||||
});
|
||||
|
||||
it('Should be able display complete button to a task without form', async () => {
|
||||
component.taskDetails = new TaskDetailsModel(taskDetailsWithOutFormMock);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const completeButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-complete-button');
|
||||
expect(completeButtonElement).not.toBeNull();
|
||||
expect(completeButtonElement['disabled']).toEqual(false);
|
||||
});
|
||||
|
||||
it('Should be able to complete a task with no form when complete button is clicked', async () => {
|
||||
fixture.detectChanges();
|
||||
component.taskDetails = new TaskDetailsModel(taskDetailsWithOutFormMock);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const completeButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-complete-button');
|
||||
completeButtonElement.click();
|
||||
expect(completeTaskSpy).toHaveBeenCalledWith('91');
|
||||
});
|
||||
|
||||
it('Should emit error event in case complete task service fails', async () => {
|
||||
const errorSpy: jasmine.Spy = spyOn(component.error, 'emit');
|
||||
completeTaskSpy.and.returnValue(throwError({message: 'servce failed'}));
|
||||
component.taskDetails = new TaskDetailsModel(taskDetailsWithOutFormMock);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const activitFormSelector = element.querySelector('adf-form');
|
||||
const completeButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-complete-button');
|
||||
expect(activitFormSelector).toBeDefined();
|
||||
expect(completeButtonElement['disabled']).toEqual(false);
|
||||
completeButtonElement.click();
|
||||
expect(errorSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should be able to emit cancel event on task with no-form when cancel button is clicked', async () => {
|
||||
const cancelSpy = spyOn(component.cancel, 'emit');
|
||||
getTaskDetailsSpy.and.returnValue(of(taskDetailsWithOutFormMock));
|
||||
component.taskDetails = new TaskDetailsModel(taskDetailsWithOutFormMock);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const cancelButtonElement = fixture.debugElement.nativeElement.querySelector('#adf-no-form-cancel-button');
|
||||
cancelButtonElement.click();
|
||||
expect(cancelSpy).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Standalone Task with no form', () => {
|
||||
|
||||
beforeEach(() => {
|
||||
component.taskId = '123';
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('Should be able to display empty template in case standalone task does not attached a form', async () => {
|
||||
component.taskDetails = new TaskDetailsModel(standaloneTaskWithoutForm);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const taskStandAlone = element.querySelector('adf-task-standalone');
|
||||
const noFormMessage = fixture.debugElement.nativeElement.querySelector('#adf-no-form-message');
|
||||
expect(taskStandAlone).not.toBeNull();
|
||||
expect(noFormMessage.innerText).toContain('ADF_TASK_LIST.STANDALONE_TASK.NO_FORM_MESSAGE');
|
||||
});
|
||||
|
||||
it('Should be able display attach form button for a standalone task without form', async() => {
|
||||
const showAttachFormSpy = spyOn(component.showAttachForm, 'emit');
|
||||
component.taskDetails = new TaskDetailsModel(standaloneTaskWithoutForm);
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const attacheFormButton = fixture.debugElement.nativeElement.querySelector('#adf-no-form-attach-form-button');
|
||||
expect(attacheFormButton).not.toBeNull();
|
||||
attacheFormButton.click();
|
||||
expect(showAttachFormSpy).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('Should be able to display completed template if standalone task completed', async() => {
|
||||
component.taskDetails = completedStandaloneTaskWithoutForm;
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const taskStandAlone = element.querySelector('adf-task-standalone');
|
||||
const completedFormMessage = fixture.debugElement.nativeElement.querySelector('#adf-completed-form-message');
|
||||
const subMessage = fixture.debugElement.nativeElement.querySelector('.adf-no-form-submessage');
|
||||
expect(taskStandAlone).not.toBeNull();
|
||||
expect(completedFormMessage.innerText).toContain('ADF_TASK_LIST.STANDALONE_TASK.COMPLETE_TASK_MESSAGE');
|
||||
expect(subMessage.innerText).toContain('ADF_TASK_LIST.STANDALONE_TASK.COMPLETE_TASK_SUB_MESSAGE');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Form with visiblity', () => {
|
||||
|
||||
beforeEach(async () => {
|
||||
component.taskId = '123';
|
||||
spyOn(formService, 'completeTaskForm').and.returnValue(of({}));
|
||||
taskDetailsMock.formKey = '4';
|
||||
getTaskDetailsSpy.and.returnValue(of(taskDetailsMock));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
});
|
||||
|
||||
it('[C312410] - Should be possible to complete a task that has an invisible field on a form with a value', async () => {
|
||||
component.formCompleted.subscribe((form: FormModel) => {
|
||||
expect(form.id).toBe(taskFormMock.id);
|
||||
});
|
||||
component.taskDetails.initiatorCanCompleteTask = true;
|
||||
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const inputTextOne: HTMLInputElement = fixture.nativeElement.querySelector('#text1');
|
||||
expect(inputTextOne).toBeDefined();
|
||||
expect(inputTextOne).not.toBeNull();
|
||||
const inputTextTwo: HTMLInputElement = fixture.nativeElement.querySelector('#text2');
|
||||
expect(inputTextTwo).toBeDefined();
|
||||
expect(inputTextTwo).not.toBeNull();
|
||||
let inputTextThree: HTMLInputElement = fixture.nativeElement.querySelector('#text3');
|
||||
expect(inputTextThree).toBeDefined();
|
||||
expect(inputTextThree).not.toBeNull();
|
||||
|
||||
inputTextOne.value = 'a';
|
||||
inputTextOne.dispatchEvent(new Event('input'));
|
||||
inputTextTwo.value = 'a';
|
||||
inputTextTwo.dispatchEvent(new Event('input'));
|
||||
inputTextThree.value = 'a';
|
||||
inputTextThree.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
inputTextThree = fixture.nativeElement.querySelector('#text3');
|
||||
expect(inputTextThree).toBeDefined();
|
||||
expect(inputTextThree).not.toBeNull();
|
||||
|
||||
inputTextOne.value = 'b';
|
||||
inputTextOne.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const inputThreeContainer = fixture.nativeElement.querySelector('#field-text3-container');
|
||||
expect(inputThreeContainer.hidden).toBe(true);
|
||||
const completeOutcomeButton: HTMLButtonElement = fixture.nativeElement.querySelector('#adf-form-complete');
|
||||
expect(completeOutcomeButton.hidden).toBe(false);
|
||||
completeOutcomeButton.click();
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('[C277278] - Should show if the form is valid via the validation icon', async () => {
|
||||
const numberInput: HTMLInputElement = fixture.nativeElement.querySelector('#numberField');
|
||||
let validationForm = fixture.nativeElement.querySelector('#adf-valid-form-icon');
|
||||
|
||||
expect(numberInput).toBeDefined();
|
||||
expect(numberInput).not.toBeNull();
|
||||
expect(validationForm.textContent).toBe('check_circle');
|
||||
|
||||
numberInput.value = 'a';
|
||||
numberInput.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
const invalidForm = fixture.nativeElement.querySelector('#adf-invalid-form-icon');
|
||||
expect(invalidForm).not.toBeNull();
|
||||
expect(invalidForm.textContent).toBe('error');
|
||||
|
||||
numberInput.value = '4';
|
||||
numberInput.dispatchEvent(new Event('input'));
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
validationForm = fixture.nativeElement.querySelector('#adf-valid-form-icon');
|
||||
expect(validationForm.textContent).toBe('check_circle');
|
||||
});
|
||||
});
|
||||
});
|
@@ -0,0 +1,287 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, OnInit, Input, Output, EventEmitter, SimpleChanges } from '@angular/core';
|
||||
import {
|
||||
FormModel,
|
||||
ContentLinkModel,
|
||||
FormRenderingService,
|
||||
FormFieldValidator,
|
||||
FormOutcomeEvent,
|
||||
AuthenticationService,
|
||||
TranslationService,
|
||||
FormFieldModel
|
||||
} from '@alfresco/adf-core';
|
||||
import { TaskDetailsModel } from '../../models/task-details.model';
|
||||
import { TaskListService } from '../../services/tasklist.service';
|
||||
import { UserRepresentation } from '@alfresco/js-api';
|
||||
import { AttachFileWidgetComponent } from '../../../content-widget/attach-file-widget.component';
|
||||
import { AttachFolderWidgetComponent } from '../../../content-widget/attach-folder-widget.component';
|
||||
import { Observable } from 'rxjs';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-task-form',
|
||||
templateUrl: './task-form.component.html',
|
||||
styleUrls: ['./task-form.component.scss']
|
||||
})
|
||||
export class TaskFormComponent implements OnInit {
|
||||
|
||||
/** (**required**) The id of the task whose details we are asking for. */
|
||||
@Input()
|
||||
taskId: string;
|
||||
|
||||
/** Toggles rendering of the form title. */
|
||||
@Input()
|
||||
showFormTitle: boolean = false;
|
||||
|
||||
/** Toggles rendering of the `Complete` outcome button. */
|
||||
@Input()
|
||||
showFormCompleteButton: boolean = true;
|
||||
|
||||
/** Toggles rendering of the `Save` outcome button. */
|
||||
@Input()
|
||||
showFormSaveButton: boolean = true;
|
||||
|
||||
/** Toggle rendering of the `Cancel` button. */
|
||||
@Input()
|
||||
showCancelButton: boolean = true;
|
||||
|
||||
/** Toggles read-only state of the form. All form widgets render as read-only
|
||||
* if enabled.
|
||||
*/
|
||||
@Input()
|
||||
readOnlyForm: boolean = false;
|
||||
|
||||
/** Toggles rendering of the `Refresh` button. */
|
||||
@Input()
|
||||
showFormRefreshButton: boolean = true;
|
||||
|
||||
/** Toggle rendering of the validation icon next to the form title. */
|
||||
@Input()
|
||||
showFormValidationIcon: boolean = true;
|
||||
|
||||
/** Field validators for use with the form. */
|
||||
@Input()
|
||||
fieldValidators: FormFieldValidator[] = [];
|
||||
|
||||
/** Emitted when the form is submitted with the `Save` or custom outcomes. */
|
||||
@Output()
|
||||
formSaved = new EventEmitter<FormModel>();
|
||||
|
||||
/** Emitted when the form is submitted with the `Complete` outcome. */
|
||||
@Output()
|
||||
formCompleted = new EventEmitter<FormModel>();
|
||||
|
||||
/** Emitted when the form field content is clicked. */
|
||||
@Output()
|
||||
formContentClicked = new EventEmitter<ContentLinkModel>();
|
||||
|
||||
/** Emitted when the form is loaded or reloaded. */
|
||||
@Output()
|
||||
formLoaded = new EventEmitter<FormModel>();
|
||||
|
||||
/** Emitted when the form associated with the form task is attached. */
|
||||
@Output()
|
||||
showAttachForm: EventEmitter<void> = new EventEmitter<void>();
|
||||
|
||||
/** Emitted when any outcome is executed. Default behaviour can be prevented
|
||||
* via `event.preventDefault()`.
|
||||
*/
|
||||
@Output()
|
||||
executeOutcome = new EventEmitter<FormOutcomeEvent>();
|
||||
|
||||
/** Emitted when the form associated with the task is completed. */
|
||||
@Output()
|
||||
completed = new EventEmitter<void>();
|
||||
|
||||
/** Emitted when the supplied form values have a validation error. */
|
||||
@Output()
|
||||
formError: EventEmitter<FormFieldModel[]> = new EventEmitter<FormFieldModel[]>();
|
||||
|
||||
/** Emitted when an error occurs. */
|
||||
@Output()
|
||||
error = new EventEmitter<any>();
|
||||
|
||||
/** Emitted when the "Cancel" button is clicked. */
|
||||
@Output()
|
||||
cancel = new EventEmitter<void>();
|
||||
|
||||
taskDetails: TaskDetailsModel;
|
||||
currentLoggedUser: UserRepresentation;
|
||||
loading: boolean = false;
|
||||
completedTaskMessage: string;
|
||||
internalReadOnlyForm: boolean = false;
|
||||
|
||||
constructor(
|
||||
private taskListService: TaskListService,
|
||||
private authService: AuthenticationService,
|
||||
private formRenderingService: FormRenderingService,
|
||||
private translationService: TranslationService
|
||||
) {
|
||||
this.formRenderingService.setComponentTypeResolver('upload', () => AttachFileWidgetComponent, true);
|
||||
this.formRenderingService.setComponentTypeResolver('select-folder', () => AttachFolderWidgetComponent, true);
|
||||
}
|
||||
|
||||
ngOnInit() {
|
||||
this.authService.getBpmLoggedUser().subscribe(user => {
|
||||
this.currentLoggedUser = user;
|
||||
});
|
||||
this.loadTask(this.taskId);
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges) {
|
||||
const taskId = changes['taskId'];
|
||||
if (taskId && taskId.currentValue) {
|
||||
this.loadTask(this.taskId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
loadTask(taskId: string) {
|
||||
this.loading = true;
|
||||
if (taskId) {
|
||||
this.taskListService.getTaskDetails(taskId).subscribe(
|
||||
(res: TaskDetailsModel) => {
|
||||
this.taskDetails = res;
|
||||
|
||||
if (!this.taskDetails.name) {
|
||||
this.taskDetails.name = 'No name';
|
||||
}
|
||||
|
||||
const endDate: any = res.endDate;
|
||||
if (endDate && !isNaN(endDate.getTime())) {
|
||||
this.internalReadOnlyForm = true;
|
||||
} else {
|
||||
this.internalReadOnlyForm = this.readOnlyForm;
|
||||
}
|
||||
this.loading = false;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
onFormSaved(savedForm: FormModel) {
|
||||
this.formSaved.emit(savedForm);
|
||||
}
|
||||
|
||||
onFormCompleted(form: FormModel) {
|
||||
this.formCompleted.emit(form);
|
||||
}
|
||||
|
||||
onFormLoaded(form: FormModel): void {
|
||||
this.formLoaded.emit(form);
|
||||
}
|
||||
|
||||
onFormContentClick(content: ContentLinkModel): void {
|
||||
this.formContentClicked.emit(content);
|
||||
}
|
||||
|
||||
onFormExecuteOutcome(outcome: FormOutcomeEvent) {
|
||||
this.executeOutcome.emit(outcome);
|
||||
}
|
||||
|
||||
onFormError(error: any) {
|
||||
this.formError.emit(error);
|
||||
}
|
||||
|
||||
onError(error: any) {
|
||||
this.error.emit(error);
|
||||
}
|
||||
|
||||
onCompleteTask() {
|
||||
this.taskListService.completeTask(this.taskDetails.id).subscribe(
|
||||
() => this.completed.emit(),
|
||||
(error) => this.error.emit(error));
|
||||
}
|
||||
|
||||
onCancel() {
|
||||
this.cancel.emit();
|
||||
}
|
||||
|
||||
onShowAttachForm() {
|
||||
this.showAttachForm.emit();
|
||||
}
|
||||
|
||||
hasFormKey(): boolean {
|
||||
return (this.taskDetails && (!!this.taskDetails.formKey));
|
||||
}
|
||||
|
||||
isStandaloneTask(): boolean {
|
||||
return !(this.taskDetails && (!!this.taskDetails.processDefinitionId));
|
||||
}
|
||||
|
||||
isTaskLoaded(): boolean {
|
||||
return !!this.taskDetails;
|
||||
}
|
||||
|
||||
isCompletedTask(): boolean {
|
||||
return this.taskDetails && this.taskDetails.endDate !== undefined && this.taskDetails.endDate !== null;
|
||||
}
|
||||
|
||||
isCompleteButtonVisible(): boolean {
|
||||
return !this.hasFormKey() && this.isTaskActive() && this.isCompleteButtonEnabled();
|
||||
}
|
||||
|
||||
isTaskActive() {
|
||||
return this.taskDetails && this.taskDetails.duration === null;
|
||||
}
|
||||
|
||||
isAssigned(): boolean {
|
||||
return !!this.taskDetails.assignee;
|
||||
}
|
||||
|
||||
private hasEmailAddress(): boolean {
|
||||
return this.taskDetails.assignee.email ? true : false;
|
||||
}
|
||||
|
||||
isAssignedToMe(): boolean {
|
||||
return this.isAssigned() && this.hasEmailAddress() ?
|
||||
this.isEmailEqual() :
|
||||
this.isExternalIdEqual();
|
||||
}
|
||||
|
||||
private isEmailEqual(): boolean {
|
||||
return (this.taskDetails.assignee && this.currentLoggedUser) && ( this.taskDetails.assignee.email.toLocaleLowerCase() === this.currentLoggedUser.email.toLocaleLowerCase());
|
||||
}
|
||||
|
||||
private isExternalIdEqual(): boolean {
|
||||
return (this.taskDetails.assignee && this.currentLoggedUser) && (this.taskDetails.assignee.externalId === this.currentLoggedUser.externalId);
|
||||
}
|
||||
|
||||
isCompleteButtonEnabled(): boolean {
|
||||
return this.isAssignedToMe() || this.canInitiatorComplete();
|
||||
}
|
||||
|
||||
canInitiatorComplete(): boolean {
|
||||
return this.taskDetails.initiatorCanCompleteTask;
|
||||
}
|
||||
|
||||
isReadOnlyForm(): boolean {
|
||||
return this.internalReadOnlyForm || !(this.isAssignedToMe() || this.canInitiatorComplete());
|
||||
}
|
||||
|
||||
isSaveButtonVisible(): boolean {
|
||||
return this.showFormSaveButton && (!this.canInitiatorComplete() || this.isAssignedToMe());
|
||||
}
|
||||
|
||||
canCompleteTask(): boolean {
|
||||
return !this.isCompletedTask() && this.isAssignedToMe();
|
||||
}
|
||||
|
||||
getCompletedTaskTranslatedMessage(): Observable<string> {
|
||||
return this.translationService.get('ADF_TASK_FORM.COMPLETED_TASK.TITLE', { taskName: this.taskDetails.name });
|
||||
}
|
||||
}
|
@@ -20,6 +20,7 @@ export * from './components/checklist.component';
|
||||
export * from './components/task-header.component';
|
||||
export * from './components/no-task-detail-template.directive';
|
||||
export * from './components/task-filters.component';
|
||||
export * from './components/task-form/task-form.component';
|
||||
export * from './components/task-details.component';
|
||||
export * from './components/task-audit.directive';
|
||||
export * from './components/start-task.component';
|
||||
|
@@ -31,6 +31,7 @@ import { NoTaskDetailsTemplateDirective } from './components/no-task-detail-temp
|
||||
import { StartTaskComponent } from './components/start-task.component';
|
||||
import { TaskAuditDirective } from './components/task-audit.directive';
|
||||
import { TaskDetailsComponent } from './components/task-details.component';
|
||||
import { TaskFormComponent } from './components/task-form/task-form.component';
|
||||
import { TaskFiltersComponent } from './components/task-filters.component';
|
||||
import { TaskHeaderComponent } from './components/task-header.component';
|
||||
import { TaskListComponent } from './components/task-list.component';
|
||||
@@ -56,6 +57,7 @@ import { FormModule } from '../form/form.module';
|
||||
TaskFiltersComponent,
|
||||
TaskListComponent,
|
||||
TaskDetailsComponent,
|
||||
TaskFormComponent,
|
||||
TaskAuditDirective,
|
||||
ChecklistComponent,
|
||||
TaskHeaderComponent,
|
||||
@@ -68,6 +70,7 @@ import { FormModule } from '../form/form.module';
|
||||
TaskFiltersComponent,
|
||||
TaskListComponent,
|
||||
TaskDetailsComponent,
|
||||
TaskFormComponent,
|
||||
TaskAuditDirective,
|
||||
ChecklistComponent,
|
||||
TaskHeaderComponent,
|
||||
|
Reference in New Issue
Block a user