[ADF-3282] Refactor Start Task Component (#4012)

* [ADF-3282] Refactor Start Task Component

* [ADF-3282] Fix e2e test

* [ADF-3282] Remove maxTaskNameLength from start task doc
This commit is contained in:
davidcanonieto
2018-11-29 14:57:36 +00:00
committed by Eugenio Romano
parent ca5543c48d
commit 49738ad555
10 changed files with 318 additions and 244 deletions

View File

@@ -497,6 +497,9 @@
] ]
} }
}, },
"adf-start-task": {
"name": "My Task Name"
},
"adf-task-list": { "adf-task-list": {
"presets": { "presets": {
"default": [ "default": [

View File

@@ -104,6 +104,7 @@
<div class="adf-grid-item adf-tasks-start" *ngIf="isStartTaskMode()" fxFlex.gt-md="1 1 auto"> <div class="adf-grid-item adf-tasks-start" *ngIf="isStartTaskMode()" fxFlex.gt-md="1 1 auto">
<adf-start-task <adf-start-task
[appId]="appId" [appId]="appId"
[name]="defaultTaskName"
(success)="onStartTaskSuccess($event)" (success)="onStartTaskSuccess($event)"
(cancel)="onCancelStartTask()"> (cancel)="onCancelStartTask()">
</adf-start-task> </adf-start-task>

View File

@@ -137,6 +137,7 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit
defaultProcessDefinitionName: string; defaultProcessDefinitionName: string;
defaultProcessName: string; defaultProcessName: string;
defaultTaskName: string;
activeTab: number = this.tabs.tasks; // tasks|processes|reports activeTab: number = this.tabs.tasks; // tasks|processes|reports
@@ -175,6 +176,7 @@ export class ProcessServiceComponent implements AfterViewInit, OnDestroy, OnInit
this.defaultProcessName = this.appConfig.get<string>('adf-start-process.name'); this.defaultProcessName = this.appConfig.get<string>('adf-start-process.name');
this.defaultProcessDefinitionName = this.appConfig.get<string>('adf-start-process.processDefinitionName'); this.defaultProcessDefinitionName = this.appConfig.get<string>('adf-start-process.processDefinitionName');
this.defaultTaskName = this.appConfig.get<string>('adf-start-task.name');
// Uncomment this line to replace all 'text' field editors with custom component // Uncomment this line to replace all 'text' field editors with custom component
// formRenderingService.setComponentTypeResolver('text', () => CustomEditorComponent, true); // formRenderingService.setComponentTypeResolver('text', () => CustomEditorComponent, true);

View File

@@ -15,7 +15,8 @@ Creates/Starts a new task for the specified app
```html ```html
<adf-start-task <adf-start-task
[appId]="YOUR_APP_ID"> [appId]="YOUR_APP_ID"
[name]="My Task Name">
</adf-start-task> </adf-start-task>
``` ```
@@ -26,6 +27,7 @@ Creates/Starts a new task for the specified app
| Name | Type | Default value | Description | | Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- | | ---- | ---- | ------------- | ----------- |
| appId | `number` | | (required) The id of the app. | | appId | `number` | | (required) The id of the app. |
| name | `string` | | Default Task Name |
### Events ### Events

View File

@@ -31,6 +31,7 @@ var StartTaskDialog = function () {
this.addName = function (userName) { this.addName = function (userName) {
Util.waitUntilElementIsVisible(name); Util.waitUntilElementIsVisible(name);
name.clear();
name.sendKeys(userName); name.sendKeys(userName);
return this; return this;
}; };

View File

@@ -114,6 +114,7 @@
} }
}, },
"START_TASK": { "START_TASK": {
"DEFAULT_NAME": "My Default Task",
"BUTTON": "CREATE TASK", "BUTTON": "CREATE TASK",
"FORM": { "FORM": {
"TITLE": "Start Task", "TITLE": "Start Task",
@@ -130,8 +131,10 @@
"START": "Start", "START": "Start",
"CANCEL": "Cancel" "CANCEL": "Cancel"
}, },
"DATE": { "ERROR": {
"ERROR": "Date format DD/MM/YYYY" "REQUIRED": "Field required",
"DATE": "Date format DD/MM/YYYY",
"MAXIMUM_LENGTH": "Length exceeded, {{characters}} characters max."
} }
} }
}, },

View File

@@ -1,93 +1,95 @@
<mat-card class="adf-new-task-layout-card"> <mat-card fxFlex="70%" class="adf-new-task-layout-card">
<mat-grid-list cols="1" rowHeight="60px"> <mat-card-header fxLayout="row" fxLayoutAlign="start center" fxLayoutGap="10px" class="adf-new-task-heading">
<mat-grid-tile> <mat-card-title>{{'ADF_TASK_LIST.START_TASK.FORM.TITLE' | translate}}</mat-card-title>
<div class="adf-new-task-heading">{{'ADF_TASK_LIST.START_TASK.FORM.TITLE'|translate}}</div> </mat-card-header>
</mat-grid-tile>
</mat-grid-list>
<mat-card-content> <mat-card-content>
<div class="adf-new-task-layout-card-content"> <form [formGroup]="taskForm" fxLayout="column" fxLayoutGap="10px">
<div class="adf-grid-full-width adf-grid-row"> <div class="adf-task-name">
<mat-form-field class="adf-grid-full-width adf-grid-column"> <mat-form-field fxFlex>
<input matInput <mat-label>{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.NAME' | translate}}</mat-label>
class="adf-grid-full-width" <input
placeholder="{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.NAME'|translate}}" matInput
[(ngModel)]="startTaskModel.name" id="name_id"
required formControlName="name">
id="name_id"> <mat-error *ngIf="nameController.hasError('required')">
{{ 'ADF_TASK_LIST.START_TASK.FORM.ERROR.REQUIRED' | translate }}
</mat-error>
<mat-error *ngIf="nameController.hasError('maxlength')">
{{ 'ADF_TASK_LIST.START_TASK.FORM.ERROR.MAXIMUM_LENGTH' | translate : { characters : maxTaskNameLength } }}
</mat-error>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="adf-task-description">
<div class="adf-grid-full-width adf-grid-row"> <mat-form-field fxFlex>
<mat-form-field class="adf-grid-full-width adf-grid-column"> <mat-label>{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.DESCRIPTION' | translate}}</mat-label>
<textarea <textarea
matInput matInput
class="adf-grid-full-width"
placeholder="{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.DESCRIPTION'|translate}}"
[(ngModel)]="startTaskModel.description"
rows="1" rows="1"
id="description_id"> id="description_id"
formControlName="description">
</textarea> </textarea>
</mat-form-field> </mat-form-field>
</div> </div>
<div class="input-row" fxLayout="row" fxLayout.lt-md="column" fxLayoutGap="20px" fxLayoutGap.lt-md="0px">
<div class="adf-grid-full-width adf-grid-row"> <mat-form-field fxFlex>
<div class="adf-grid-column adf-grid-half-width"> <input
<div class="adf-grid-full-width adf-grid-row"> matInput
<mat-form-field class="adf-grid-full-width"> (keyup)="onDateChanged($event.srcElement.value)"
<input matInput (dateInput)="onDateChanged($event.value)"
[matDatepicker]="taskDatePicker" [matDatepicker]="taskDatePicker"
(keydown)="true" placeholder="{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.DATE'|translate}}"
(keyup)="onDateChanged($event.srcElement.value)" id="date_id">
(dateInput)="onDateChanged($event.value)" <mat-datepicker-toggle
placeholder="{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.DATE'|translate}}" matSuffix
[(ngModel)]="startTaskModel.dueDate" [for]="taskDatePicker"></mat-datepicker-toggle>
id="date_id"> <mat-datepicker
<mat-datepicker-toggle matSuffix [for]="taskDatePicker"></mat-datepicker-toggle> #taskDatePicker
<mat-datepicker #taskDatePicker [touchUi]="true">
[touchUi]="true" </mat-datepicker>
(dateChanged)="onDateChanged($event)" <div class="adf-error-text-container">
(opened)="clearDateInput()"> <div *ngIf="dateError">
</mat-datepicker> <div class="adf-error-text">{{'ADF_TASK_LIST.START_TASK.FORM.ERROR.DATE'|translate}}</div>
<div class="adf-error-text-container"> <mat-icon class="adf-error-icon">warning</mat-icon>
<div *ngIf="dateError"> </div>
<div class="adf-error-text">{{'ADF_TASK_LIST.START_TASK.FORM.DATE.ERROR'|translate}}</div>
<mat-icon class="adf-error-icon">warning</mat-icon>
</div>
</div>
</mat-form-field>
</div> </div>
<div class="adf-grid-full-width adf-grid-row"> </mat-form-field>
<mat-form-field class="adf-grid-full-width"> <div fxFlex>
<mat-select placeholder="{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.FORM'|translate}}" id="form_id" [(ngModel)]="formKey">
<mat-option>{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.NONE'|translate}}</mat-option>
<mat-option *ngFor="let form of forms" [value]="form.id">{{ form.name }}</mat-option>
</mat-select>
</mat-form-field>
</div>
</div>
<div class="adf-grid-column adf-grid-half-width">
<people-widget <people-widget
(peopleSelected)="getAssigneeId($event)" (peopleSelected)="getAssigneeId($event)"
[field]="field" [field]="field"
class="adf-people-widget-content"></people-widget> class="adf-people-widget-content"></people-widget>
</div> </div>
</div> </div>
</div> <div class="adf-task-form">
<mat-form-field fxFlex="48%" fxFlex.xs="100%">
<mat-label id="form_label">{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.FORM'|translate}}</mat-label>
<mat-select
id="form_id"
class="form-control"
formControlName="formKey">
<mat-option>{{'ADF_TASK_LIST.START_TASK.FORM.LABEL.NONE'|translate}}</mat-option>
<mat-option *ngFor="let form of forms$ | async" [value]="form.id">{{ form.name }}</mat-option>
</mat-select>
</mat-form-field>
</div>
</form>
</mat-card-content> </mat-card-content>
<mat-card-actions> <mat-card-actions>
<mat-grid-list cols="1" rowHeight="60px"> <div class="adf-new-task-footer" fxLayout="row" fxLayoutAlign="end end">
<mat-grid-tile> <button
<div class="adf-new-task-footer"> mat-button
<button mat-button (click)="onCancel()" id="button-cancel"> (click)="onCancel()"
{{'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL'|translate}} id="button-cancel">
</button> {{'ADF_TASK_LIST.START_TASK.FORM.ACTION.CANCEL'|translate}}
<button color="primary" mat-button [disabled]="!startTaskModel.name || dateError" (click)="start()" id="button-start"> </button>
{{'ADF_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}} <button
</button> color="primary"
</div> mat-button
</mat-grid-tile> [disabled]="!isFormValid()"
</mat-grid-list> (click)="saveTask()"
id="button-start">
{{'ADF_TASK_LIST.START_TASK.FORM.ACTION.START'|translate}}
</button>
</div>
</mat-card-actions> </mat-card-actions>
</mat-card> </mat-card>

View File

@@ -6,56 +6,26 @@
$header-border: 1px solid mat-color($foreground, divider); $header-border: 1px solid mat-color($foreground, divider);
.adf-new-task-heading { .adf-new-task-heading {
padding: 12px 20px; padding-top: 12px;
font-weight: bold;
border-bottom: $header-border; border-bottom: $header-border;
font-size: 18px; .mat-card-title {
float: left; font-weight: bold;
text-align: left; font-size: 18px;
width: calc(100% - 40px); }
}
.adf-new-task-form {
width: 100%;
} }
.adf-new-task-layout-card { .adf-new-task-layout-card {
width: 66%;
margin: 10px auto; margin: 10px auto;
&-content {
display: flex;
flex-flow: row;
flex-wrap: wrap;
justify-content: space-between;
.adf-grid-row {
display: flex;
flex-flow: row;
flex-wrap: wrap;
justify-content: space-between;
margin-bottom: 8px;
}
.adf-grid-column {
display: flex;
flex-flow: column;
}
.adf-grid-full-width {
width: 100%;
}
.adf-grid-half-width {
width: 49%;
}
}
} }
.adf-new-task-footer { .adf-new-task-footer {
padding: 4px; padding: 4px;
font-size: 18px; font-size: 18px;
border-top: 1px solid #eee; border-top: 1px solid #eee;
float: left;
width: calc(100% - 40px);
text-align: right;
} }
.adf-mat-select { .adf-mat-select {
@@ -154,11 +124,3 @@
} }
} }
} }
@media (max-width: 600px) {
.adf-new-task-layout-card {
width: 90%;
margin-left: auto;
margin-right: auto;
}
}

View File

@@ -16,33 +16,36 @@
*/ */
import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core'; import { setupTestBed, LogService } from '@alfresco/adf-core';
import { Observable, of, throwError } from 'rxjs'; import { of, throwError, Observable } from 'rxjs';
import { startTaskMock } from '../../mock';
import { StartTaskModel } from '../models/start-task.model';
import { TaskListService } from '../services/tasklist.service'; import { TaskListService } from '../services/tasklist.service';
import { StartTaskComponent } from './start-task.component'; import { StartTaskComponent } from './start-task.component';
import { ProcessTestingModule } from '../../testing/process.testing.module'; import { ProcessTestingModule } from '../../testing/process.testing.module';
import { taskDetailsMock } from '../../mock/task/task-details.mock';
import { TaskDetailsModel } from '../models/task-details.model';
describe('StartTaskComponent', () => { describe('StartTaskComponent', () => {
let component: StartTaskComponent; let component: StartTaskComponent;
let fixture: ComponentFixture<StartTaskComponent>; let fixture: ComponentFixture<StartTaskComponent>;
let service: TaskListService; let service: TaskListService;
let logService: LogService;
let element: HTMLElement; let element: HTMLElement;
let getFormListSpy: jasmine.Spy; let getFormListSpy: jasmine.Spy;
let createNewTaskSpy: jasmine.Spy; let createNewTaskSpy: jasmine.Spy;
let fakeForms = [ let logSpy: jasmine.Spy;
let fakeForms$ = [
{ {
id: 123, id: 123,
name: 'Display Data' name: 'Display Data'
}, },
{ {
id: 1111, id: 1111,
name: 'Employee Info' name: 'Employee Info'
} }
]; ];
let testUser = {id: 1001, firstName: 'fakeName', email: 'fake@app.activiti.com'};
let testUser = { id: 1001, firstName: 'fakeName', email: 'fake@app.activiti.com' };
setupTestBed({ setupTestBed({
imports: [ProcessTestingModule] imports: [ProcessTestingModule]
@@ -54,21 +57,28 @@ describe('StartTaskComponent', () => {
element = fixture.nativeElement; element = fixture.nativeElement;
service = TestBed.get(TaskListService); service = TestBed.get(TaskListService);
getFormListSpy = spyOn(service, 'getFormList').and.returnValue(of(fakeForms)); logService = TestBed.get(LogService);
getFormListSpy = spyOn(service, 'getFormList').and.returnValue(new Observable((observer) => {
observer.next(fakeForms$);
observer.complete();
}));
fixture.detectChanges(); fixture.detectChanges();
})); }));
afterEach(() => {
fixture.destroy();
TestBed.resetTestingModule();
});
it('should create instance of StartTaskComponent', () => { it('should create instance of StartTaskComponent', () => {
expect(fixture.componentInstance instanceof StartTaskComponent).toBe(true, 'should create StartTaskComponent'); expect(component instanceof StartTaskComponent).toBe(true, 'should create StartTaskComponent');
}); });
it('should fetch fake form on init', () => { it('should fetch fake form on init', () => {
component.ngOnInit(); component.ngOnInit();
expect(component.forms).toEqual(fakeForms); fixture.detectChanges();
expect(component.forms[0].name).toEqual('Display Data'); expect(component.forms$).toBeDefined();
expect(component.forms[1].name).toEqual('Employee Info');
expect(component.forms[1].id).toEqual(1111);
expect(getFormListSpy).toHaveBeenCalled(); expect(getFormListSpy).toHaveBeenCalled();
}); });
@@ -87,8 +97,7 @@ describe('StartTaskComponent', () => {
it('should create new task when start is clicked', () => { it('should create new task when start is clicked', () => {
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.appId = 42; component.taskForm.controls['name'].setValue('task');
component.startTaskModel = new StartTaskModel(startTaskMock);
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click(); createTaskButton.click();
@@ -97,8 +106,8 @@ describe('StartTaskComponent', () => {
it('should send on success event when the task is started', () => { it('should send on success event when the task is started', () => {
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.appId = 42; component.taskDetailsModel = new TaskDetailsModel(taskDetailsMock);
component.startTaskModel = new StartTaskModel(startTaskMock); component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click(); createTaskButton.click();
@@ -113,7 +122,7 @@ describe('StartTaskComponent', () => {
it('should send on success event when only name is given', () => { it('should send on success event when only name is given', () => {
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.appId = 42; component.appId = 42;
component.startTaskModel.name = 'fakeName'; component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click(); createTaskButton.click();
@@ -122,7 +131,7 @@ describe('StartTaskComponent', () => {
it('should not emit success event when data not present', () => { it('should not emit success event when data not present', () => {
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.startTaskModel = new StartTaskModel(null); component.taskDetailsModel = new TaskDetailsModel(null);
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click(); createTaskButton.click();
@@ -141,6 +150,9 @@ describe('StartTaskComponent', () => {
assignee: null assignee: null
} }
)); ));
});
it('should attach form to the task when a form is selected', () => {
spyOn(service, 'attachFormToATask').and.returnValue(of( spyOn(service, 'attachFormToATask').and.returnValue(of(
{ {
id: 91, id: 91,
@@ -149,13 +161,11 @@ describe('StartTaskComponent', () => {
assignee: null assignee: null
} }
)); ));
});
it('should attach form to the task when a form is selected', () => {
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.taskForm.controls['formKey'].setValue(1204);
component.appId = 42; component.appId = 42;
component.startTaskModel = new StartTaskModel(startTaskMock); component.taskDetailsModel = new TaskDetailsModel(taskDetailsMock);
component.formKey = '1204';
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click(); createTaskButton.click();
@@ -168,10 +178,19 @@ describe('StartTaskComponent', () => {
}); });
it('should not attach form to the task when a no form is selected', () => { it('should not attach form to the task when a no form is selected', () => {
spyOn(service, 'attachFormToATask').and.returnValue(of(
{
id: 91,
name: 'fakeName',
formKey: null,
assignee: null
}
));
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.taskForm.controls['formKey'].setValue(null);
component.appId = 42; component.appId = 42;
component.startTaskModel = new StartTaskModel(startTaskMock); component.taskDetailsModel = new TaskDetailsModel(taskDetailsMock);
component.formKey = null;
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click(); createTaskButton.click();
@@ -214,9 +233,9 @@ describe('StartTaskComponent', () => {
it('should assign task when an assignee is selected', () => { it('should assign task when an assignee is selected', () => {
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.taskForm.controls['formKey'].setValue(1204);
component.appId = 42; component.appId = 42;
component.startTaskModel = new StartTaskModel(startTaskMock);
component.formKey = '1204';
component.assigneeId = testUser.id; component.assigneeId = testUser.id;
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
@@ -229,12 +248,31 @@ describe('StartTaskComponent', () => {
}); });
}); });
it('should assign task with id of selected user assigned', () => {
let successSpy = spyOn(component.success, 'emit');
component.taskDetailsModel = new TaskDetailsModel(taskDetailsMock);
component.taskForm.controls['name'].setValue('fakeName');
component.taskForm.controls['formKey'].setValue(1204);
component.appId = 42;
component.getAssigneeId(testUser.id);
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
expect(successSpy).toHaveBeenCalledWith({
id: 91,
name: 'fakeName',
formKey: 1204,
assignee: testUser
});
});
it('should not assign task when no assignee is selected', () => { it('should not assign task when no assignee is selected', () => {
let successSpy = spyOn(component.success, 'emit'); let successSpy = spyOn(component.success, 'emit');
component.taskForm.controls['name'].setValue('fakeName');
component.taskForm.controls['formKey'].setValue(1204);
component.appId = 42; component.appId = 42;
component.formKey = '1204';
component.assigneeId = null; component.assigneeId = null;
component.startTaskModel = new StartTaskModel(startTaskMock); component.taskDetailsModel = new TaskDetailsModel(taskDetailsMock);
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click(); createTaskButton.click();
@@ -245,27 +283,10 @@ describe('StartTaskComponent', () => {
assignee: null assignee: null
}); });
}); });
it('should assign task with id of selected user assigned', () => {
let successSpy = spyOn(component.success, 'emit');
component.appId = 42;
component.startTaskModel = new StartTaskModel(startTaskMock);
component.formKey = '1204';
component.getAssigneeId(testUser.id);
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start');
createTaskButton.click();
expect(successSpy).toHaveBeenCalledWith({
id: 91,
name: 'fakeName',
formKey: 1204,
assignee: testUser
});
});
}); });
it('should not attach a form when a form id is not selected', () => { it('should not attach a form when a form id is not selected', () => {
let attachFormToATask = spyOn(service, 'attachFormToATask').and.returnValue(of()); let attachFormToATask = spyOn(service, 'attachFormToATask').and.returnValue([]);
spyOn(service, 'createNewTask').and.callFake( spyOn(service, 'createNewTask').and.callFake(
function() { function() {
return new Observable((observer) => { return new Observable((observer) => {
@@ -273,14 +294,16 @@ describe('StartTaskComponent', () => {
observer.complete(); observer.complete();
}); });
}); });
component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges();
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
component.startTaskModel.name = 'fake-name';
fixture.detectChanges(); fixture.detectChanges();
createTaskButton.click(); createTaskButton.click();
expect(attachFormToATask).not.toHaveBeenCalled(); expect(attachFormToATask).not.toHaveBeenCalled();
}); });
it('should show start task button', () => { it('should show start task button', () => {
fixture.detectChanges();
expect(element.querySelector('#button-start')).toBeDefined(); expect(element.querySelector('#button-start')).toBeDefined();
expect(element.querySelector('#button-start')).not.toBeNull(); expect(element.querySelector('#button-start')).not.toBeNull();
expect(element.querySelector('#button-start').textContent).toContain('ADF_TASK_LIST.START_TASK.FORM.ACTION.START'); expect(element.querySelector('#button-start').textContent).toContain('ADF_TASK_LIST.START_TASK.FORM.ACTION.START');
@@ -294,16 +317,15 @@ describe('StartTaskComponent', () => {
}); });
it('should disable start button if name is empty', () => { it('should disable start button if name is empty', () => {
component.startTaskModel.name = ''; component.taskForm.controls['name'].setValue('');
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = fixture.nativeElement.querySelector('#button-start'); let createTaskButton = fixture.nativeElement.querySelector('#button-start');
expect(createTaskButton.disabled).toBeTruthy(); expect(createTaskButton.disabled).toBeTruthy();
}); });
it('should cancel start task on cancel button click', () => { it('should cancel start task on cancel button click', () => {
let emitSpy = spyOn(component.cancel, 'emit'); let emitSpy = spyOn(component.cancel, 'emit');
let cancelTaskButton = fixture.nativeElement.querySelector('#button-cancel'); let cancelTaskButton = <HTMLElement> element.querySelector('#button-cancel');
component.startTaskModel.name = '';
fixture.detectChanges(); fixture.detectChanges();
cancelTaskButton.click(); cancelTaskButton.click();
expect(emitSpy).not.toBeNull(); expect(emitSpy).not.toBeNull();
@@ -311,24 +333,24 @@ describe('StartTaskComponent', () => {
}); });
it('should enable start button if name is filled out', () => { it('should enable start button if name is filled out', () => {
component.startTaskModel.name = 'fakeName'; component.taskForm.controls['name'].setValue('fakeName');
fixture.detectChanges(); fixture.detectChanges();
let createTaskButton = fixture.nativeElement.querySelector('#button-start'); let createTaskButton = fixture.nativeElement.querySelector('#button-start');
expect(createTaskButton.disabled).toBeFalsy(); expect(createTaskButton.disabled).toBeFalsy();
}); });
it('should define the select option for Forms', () => { it('should define the select options for Forms', () => {
component.forms = fakeForms; component.forms$ = service.getFormList();
fixture.detectChanges(); fixture.detectChanges();
let selectElement = fixture.nativeElement.querySelector('#form_id'); let selectElement = fixture.nativeElement.querySelector('#form_label');
expect(selectElement.attributes['aria-label'].value).toContain('ADF_TASK_LIST.START_TASK.FORM.LABEL.FORM'); expect(selectElement.innerHTML).toContain('ADF_TASK_LIST.START_TASK.FORM.LABEL.FORM');
}); });
it('should get formatted full name', () => { it('should get formatted fullname', () => {
let testUser1 = {'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'}; let testUser1 = { 'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com' };
let testUser2 = {'id': 1002, 'firstName': '', 'lastName': 'Adams', 'email': 'adams@app.activiti.com'}; let testUser2 = { 'id': 1002, 'firstName': '', 'lastName': 'Adams', 'email': 'adams@app.activiti.com' };
let testUser3 = {'id': 1003, 'firstName': 'Wilbur', 'lastName': '', 'email': 'wilbur@app.activiti.com'}; let testUser3 = { 'id': 1003, 'firstName': 'Wilbur', 'lastName': '', 'email': 'wilbur@app.activiti.com' };
let testUser4 = {'id': 1004, 'firstName': '', 'lastName': '', 'email': 'test@app.activiti.com'}; let testUser4 = { 'id': 1004, 'firstName': '', 'lastName': '', 'email': 'test@app.activiti.com' };
let testFullName1 = component.getDisplayUser(testUser1.firstName, testUser1.lastName, ' '); let testFullName1 = component.getDisplayUser(testUser1.firstName, testUser1.lastName, ' ');
let testFullName2 = component.getDisplayUser(testUser2.firstName, testUser2.lastName, ' '); let testFullName2 = component.getDisplayUser(testUser2.firstName, testUser2.lastName, ' ');
@@ -342,12 +364,44 @@ describe('StartTaskComponent', () => {
}); });
it('should emit error when there is an error while creating task', () => { it('should emit error when there is an error while creating task', () => {
component.taskForm.controls['name'].setValue('fakeName');
let errorSpy = spyOn(component.error, 'emit'); let errorSpy = spyOn(component.error, 'emit');
spyOn(service, 'createNewTask').and.returnValue(throwError({})); spyOn(service, 'createNewTask').and.returnValue(throwError({}));
let createTaskButton = <HTMLElement> element.querySelector('#button-start'); let createTaskButton = <HTMLElement> element.querySelector('#button-start');
component.startTaskModel.name = 'fake-name';
fixture.detectChanges(); fixture.detectChanges();
createTaskButton.click(); createTaskButton.click();
expect(errorSpy).toHaveBeenCalled(); expect(errorSpy).toHaveBeenCalled();
}); });
it('should emit error when task name exceeds maximum length', () => {
component.maxTaskNameLength = 2;
component.ngOnInit();
fixture.detectChanges();
let name = component.taskForm.controls['name'];
name.setValue('task');
fixture.detectChanges();
expect(name.valid).toBeFalsy();
name.setValue('ta');
fixture.detectChanges();
expect(name.valid).toBeTruthy();
});
it('should emit error when task name field is empty', () => {
fixture.detectChanges();
let name = component.taskForm.controls['name'];
name.setValue('');
fixture.detectChanges();
expect(name.valid).toBeFalsy();
name.setValue('task');
fixture.detectChanges();
expect(name.valid).toBeTruthy();
});
it('should call logService when task name exceeds maximum length', () => {
logSpy = spyOn(logService, 'log').and.callThrough();
component.maxTaskNameLength = 300;
component.ngOnInit();
fixture.detectChanges();
expect(logSpy).toHaveBeenCalled();
});
}); });

View File

@@ -23,10 +23,10 @@ import moment from 'moment-es6';
import { Moment } from 'moment'; import { Moment } from 'moment';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { Form } from '../models/form.model'; import { Form } from '../models/form.model';
import { StartTaskModel } from '../models/start-task.model';
import { TaskDetailsModel } from '../models/task-details.model'; import { TaskDetailsModel } from '../models/task-details.model';
import { TaskListService } from './../services/tasklist.service'; import { TaskListService } from './../services/tasklist.service';
import { switchMap, defaultIfEmpty } from 'rxjs/operators'; import { switchMap, defaultIfEmpty } from 'rxjs/operators';
import { FormBuilder, AbstractControl, Validators, FormGroup, FormControl } from '@angular/forms';
@Component({ @Component({
selector: 'adf-start-task', selector: 'adf-start-task',
@@ -40,36 +40,31 @@ import { switchMap, defaultIfEmpty } from 'rxjs/operators';
export class StartTaskComponent implements OnInit { export class StartTaskComponent implements OnInit {
public FORMAT_DATE: string = 'DD/MM/YYYY'; public FORMAT_DATE: string = 'DD/MM/YYYY';
MAX_LENGTH: number = 255;
/** (required) The id of the app. */
@Input() @Input()
appId: number; appId: number;
/** Emitted when the task is successfully created. */ @Input()
name: string = '';
@Output() @Output()
success: EventEmitter<any> = new EventEmitter<any>(); success: EventEmitter<any> = new EventEmitter<any>();
/** Emitted when the cancel button is clicked by the user. */
@Output() @Output()
cancel: EventEmitter<void> = new EventEmitter<void>(); cancel: EventEmitter<void> = new EventEmitter<void>();
/** Emitted when an error occurs. */
@Output() @Output()
error: EventEmitter<any> = new EventEmitter<any>(); error: EventEmitter<any> = new EventEmitter<any>();
startTaskModel: StartTaskModel = new StartTaskModel(); taskDetailsModel: TaskDetailsModel = new TaskDetailsModel();
forms$: Observable<Form[]>;
forms: Form[];
assigneeId: number; assigneeId: number;
formKey: string;
taskId: string;
dateError: boolean;
field: FormFieldModel; field: FormFieldModel;
taskForm: FormGroup;
dateError: boolean = false;
maxTaskNameLength: number = this.MAX_LENGTH;
loading = false;
/** /**
* Constructor * Constructor
@@ -80,44 +75,74 @@ export class StartTaskComponent implements OnInit {
constructor(private taskService: TaskListService, constructor(private taskService: TaskListService,
private dateAdapter: DateAdapter<Moment>, private dateAdapter: DateAdapter<Moment>,
private preferences: UserPreferencesService, private preferences: UserPreferencesService,
private formBuilder: FormBuilder,
private logService: LogService) { private logService: LogService) {
} }
ngOnInit() { ngOnInit() {
this.field = new FormFieldModel(new FormModel(), {id: this.assigneeId, value: this.assigneeId, placeholder: 'Assignee'}); if (this.name) {
this.taskDetailsModel.name = this.name;
}
this.validateMaxTaskNameLength();
this.field = new FormFieldModel(new FormModel(), { id: this.assigneeId, value: this.assigneeId, placeholder: 'Assignee' });
this.preferences.locale$.subscribe((locale) => { this.preferences.locale$.subscribe((locale) => {
this.dateAdapter.setLocale(locale); this.dateAdapter.setLocale(locale);
}); });
this.loadFormsTask(); this.loadFormsTask();
this.buildForm();
} }
public start(): void { buildForm() {
if (this.startTaskModel.name) { this.taskForm = this.formBuilder.group({
if (this.appId) { name: new FormControl(this.taskDetailsModel.name, [Validators.required, Validators.maxLength(this.maxTaskNameLength)]),
this.startTaskModel.category = this.appId.toString(); description: new FormControl(''),
} formKey: new FormControl('')
this.taskService.createNewTask(new TaskDetailsModel(this.startTaskModel)) });
.pipe(
switchMap((createRes: any) => this.taskForm.valueChanges.subscribe((taskFormValues) => this.setTaskDetails(taskFormValues));
this.attachForm(createRes.id, this.formKey).pipe( }
defaultIfEmpty(createRes),
switchMap((attachRes: any) => setTaskDetails(form) {
this.assignTaskByUserId(createRes.id, this.assigneeId).pipe( this.taskDetailsModel.name = form.name;
defaultIfEmpty(attachRes ? attachRes : createRes) this.taskDetailsModel.description = form.description;
) this.taskDetailsModel.formKey = form.formKey ? form.formKey.toString() : null;
}
isFormValid() {
return this.taskForm.valid && !this.dateError && !this.loading;
}
public saveTask(): void {
this.loading = true;
if (this.appId) {
this.taskDetailsModel.category = this.appId.toString();
}
this.taskService.createNewTask(this.taskDetailsModel)
.pipe(
switchMap((createRes: any) =>
this.attachForm(createRes.id, this.taskDetailsModel.formKey).pipe(
defaultIfEmpty(createRes),
switchMap((attachRes: any) =>
this.assignTaskByUserId(createRes.id, this.assigneeId).pipe(
defaultIfEmpty(attachRes ? attachRes : createRes)
) )
) )
) )
) )
.subscribe( )
(res: any) => { .subscribe(
this.success.emit(res); (res: any) => {
}, this.loading = false;
(err) => { this.success.emit(res);
this.error.emit(err); },
this.logService.error('An error occurred while creating new task'); (err) => {
}); this.loading = false;
} this.error.emit(err);
this.logService.error('An error occurred while creating new task');
});
} }
getAssigneeId(userId) { getAssigneeId(userId) {
@@ -145,13 +170,7 @@ export class StartTaskComponent implements OnInit {
} }
private loadFormsTask(): void { private loadFormsTask(): void {
this.taskService.getFormList().subscribe((res: Form[]) => { this.forms$ = this.taskService.getFormList();
this.forms = res;
},
(err) => {
this.error.emit(err);
this.logService.error('An error occurred while trying to get the forms');
});
} }
public isUserNameEmpty(user: UserProcessModel): boolean { public isUserNameEmpty(user: UserProcessModel): boolean {
@@ -168,20 +187,45 @@ export class StartTaskComponent implements OnInit {
return firstName + delimiter + lastName; return firstName + delimiter + lastName;
} }
onDateChanged(newDateValue): void { onDateChanged(newDateValue: any) {
this.dateError = false; this.dateError = false;
if (newDateValue) { if (newDateValue) {
let momentDate = moment(newDateValue, this.FORMAT_DATE, true); let momentDate;
if (!momentDate.isValid()) {
this.dateError = true; if (typeof newDateValue === 'string') {
momentDate = moment(newDateValue, this.FORMAT_DATE, true);
} else {
momentDate = newDateValue;
} }
if (momentDate.isValid()) {
this.taskDetailsModel.dueDate = momentDate.toDate();
} else {
this.dateError = true;
this.taskDetailsModel.dueDate = null;
}
} else {
this.taskDetailsModel.dueDate = null;
} }
} }
clearDateInput() { private validateMaxTaskNameLength() {
const emptyValue = ''; if (this.maxTaskNameLength > this.MAX_LENGTH) {
this.startTaskModel.dueDate = emptyValue; this.maxTaskNameLength = this.MAX_LENGTH;
this.onDateChanged(emptyValue); this.logService.log(`the task name length cannot be greater than ${this.MAX_LENGTH}`);
}
}
get nameController(): AbstractControl {
return this.taskForm.get('name');
}
get descriptionController(): AbstractControl {
return this.taskForm.get('description');
}
get formKeyController(): AbstractControl {
return this.taskForm.get('formKey');
} }
} }