#1426 - Checklist task delete action (#1812)

* #1426 - added remove checklist feature on task details
* #1426 - Improved test for tasklist service
* 426 - added component test for deleting a checklist
This commit is contained in:
Vito
2017-04-07 07:43:47 -07:00
committed by Mario Romano
parent d2f7a6858f
commit 4bee113e36
10 changed files with 840 additions and 589 deletions

View File

@@ -61,7 +61,8 @@
[taskId]="currentTaskId"
(formCompleted)="onFormCompleted($event)"
(formContentClicked)="onFormContentClick($event)"
(taskCreated)="onTaskCreated($event)">
(taskCreated)="onTaskCreated($event)"
(taskDeleted)="onTaskDeleted($event)">
</activiti-task-details>
</div>
</div>

View File

@@ -264,6 +264,10 @@ export class ActivitiDemoComponent implements AfterViewInit {
this.taskList.reload();
}
onTaskDeleted(data: any) {
this.taskList.reload();
}
ngAfterViewInit() {
// workaround for MDL issues with dynamic components
if (componentHandler) {

View File

@@ -0,0 +1,226 @@
/*!
* @license
* Copyright 2016 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 {
FilterRepresentationModel,
AppDefinitionRepresentationModel
} from '../models/filter.model';
export var fakeFilters = {
size: 2, total: 2, start: 0,
data: [
new AppDefinitionRepresentationModel(
{
id: '1', name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' }
}
),
{
id: '2', name: 'FakeMyTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-assignee' }
}
]
};
export var fakeAppFilter = {
size: 1, total: 1, start: 0,
data: [
{
id: 1, name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' }
}
]
};
export var fakeApps = {
size: 2, total: 2, start: 0,
data: [
{
id: 1, defaultAppId: null, name: 'Sales-Fakes-App', description: 'desc-fake1', modelId: 22,
theme: 'theme-1-fake', icon: 'glyphicon-asterisk', 'deploymentId': '111', 'tenantId': null
},
{
id: 2, defaultAppId: null, name: 'health-care-Fake', description: 'desc-fake2', modelId: 33,
theme: 'theme-2-fake', icon: 'glyphicon-asterisk', 'deploymentId': '444', 'tenantId': null
}
]
};
export var fakeFilter = {
sort: 'created-desc', text: '', state: 'open', assignment: 'fake-assignee'
};
export var fakeFilterWithProcessDefinitionKey = {
sort: 'created-desc', text: '', state: 'open', assignment: 'fake-assignee', processDefinitionKey: '1'
};
export var fakeUser = { id: 1, email: 'fake-email@dom.com', firstName: 'firstName', lastName: 'lastName' };
export var fakeTaskList = {
size: 1, total: 1, start: 0,
data: [
{
id: '1', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export var fakeTaskListDifferentProcessDefinitionKey = {
size: 1, total: 1, start: 0,
data: [
{
id: '1', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser,
processDefinitionKey: '1',
created: '2016-07-15T11:19:17.440+0000'
},
{
id: '2', name: 'FakeNameTask2', description: null, category: null,
assignee: fakeUser,
processDefinitionKey: '2',
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export var secondFakeTaskList = {
size: 1, total: 1, start: 0,
data: [
{
id: '200', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export var fakeErrorTaskList = {
error: 'wrong request'
};
export var fakeTaskDetails = { id: '999', name: 'fake-task-name', formKey: '99', assignee: fakeUser };
export var fakeTasksComment = {
size: 2, total: 2, start: 0,
data: [
{
id: 1, message: 'fake-message-1', created: '', createdBy: fakeUser
},
{
id: 2, message: 'fake-message-2', created: '', createdBy: fakeUser
}
]
};
export var fakeTasksChecklist = {
size: 1, total: 1, start: 0,
data: [
{
id: 1, name: 'FakeCheckTask1', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
},
{
id: 2, name: 'FakeCheckTask2', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
export var fakeRepresentationFilter1: FilterRepresentationModel = new FilterRepresentationModel({
appId: 1,
name: 'CONTAIN FILTER',
recent: true,
icon: 'glyphicon-align-left',
filter: {
processDefinitionId: null,
processDefinitionKey: null,
name: null,
state: 'open',
sort: 'created-desc',
assignment: 'involved',
dueAfter: null,
dueBefore: null
}
});
export var fakeRepresentationFilter2: FilterRepresentationModel = new FilterRepresentationModel({
appId: 2,
name: 'NO TASK FILTER',
recent: false,
icon: 'glyphicon-inbox',
filter: {
processDefinitionId: null,
processDefinitionKey: null,
name: null,
state: 'open',
sort: 'created-desc',
assignment: 'assignee',
dueAfter: null,
dueBefore: null
}
});
export var fakeAppPromise = new Promise(function (resolve, reject) {
resolve(fakeAppFilter);
});
export var fakeFormList = {
size: 2,
total: 2,
start: 0,
data: [{
id: 1,
name: 'form with all widgets',
description: '',
createdBy: 2,
createdByFullName: 'Admin Admin',
lastUpdatedBy: 2,
lastUpdatedByFullName: 'Admin Admin',
lastUpdated: 1491400951205,
latestVersion: true,
version: 4,
comment: null,
stencilSet: null,
referenceId: null,
modelType: 2,
favorite: null,
permission: 'write',
tenantId: null
}, {
id: 2,
name: 'uppy',
description: '',
createdBy: 2,
createdByFullName: 'Admin Admin',
lastUpdatedBy: 2,
lastUpdatedByFullName: 'Admin Admin',
lastUpdated: 1490951054477,
latestVersion: true,
version: 2,
comment: null,
stencilSet: null,
referenceId: null,
modelType: 2,
favorite: null,
permission: 'write',
tenantId: null
}]
};

View File

@@ -7,9 +7,9 @@
<div class="menu-container" *ngIf="checklist?.length > 0">
<ul class='mdl-list'>
<li class="mdl-list__item" *ngFor="let check of checklist">
<span class="mdl-list__item-primary-content" id="check-{{check.id}}">
<i class="material-icons mdl-list__item-icon">done</i>
{{check.name}}
<span class="mdl-chip mdl-chip--deletable" id="check-{{check.id}}">
<span class="mdl-chip__text">{{check.name}}</span>
<button type="button" class="mdl-chip__action"><i id="remove-{{check.id}}" (click)="delete(check.id)" class="material-icons">cancel</i></button>
</span>
</li>
</ul>

View File

@@ -140,6 +140,42 @@ describe('ActivitiChecklist', () => {
});
}));
it('should remove a checklist element', async(() => {
checklistComponent.taskId = 'new-fake-task-id';
checklistComponent.checklist.push(fakeTaskDetail);
fixture.detectChanges();
let checklistElementRemove = <HTMLElement> element.querySelector('#remove-fake-check-id');
expect(checklistElementRemove).toBeDefined();
expect(checklistElementRemove).not.toBeNull();
checklistElementRemove.click();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json'
});
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(element.querySelector('#fake-check-id')).toBeNull();
});
}));
it('should send an event when the checklist is deleted', (done) => {
checklistComponent.taskId = 'new-fake-task-id';
checklistComponent.checklist.push(fakeTaskDetail);
fixture.detectChanges();
let checklistElementRemove = <HTMLElement> element.querySelector('#remove-fake-check-id');
expect(checklistElementRemove).toBeDefined();
expect(checklistElementRemove).not.toBeNull();
checklistElementRemove.click();
jasmine.Ajax.requests.mostRecent().respondWith({
status: 200,
contentType: 'json'
});
checklistComponent.checklistTaskDeleted.subscribe(() => {
expect(element.querySelector('#fake-check-id')).toBeNull();
done();
});
});
it('should show load task checklist on change', async(() => {
checklistComponent.taskId = 'new-fake-task-id';
checklistComponent.checklist.push(fakeTaskDetail);

View File

@@ -44,6 +44,9 @@ export class ActivitiChecklist implements OnInit, OnChanges {
@Output()
checklistTaskCreated: EventEmitter<TaskDetailsModel> = new EventEmitter<TaskDetailsModel>();
@Output()
checklistTaskDeleted: EventEmitter<string> = new EventEmitter<string>();
@ViewChild('dialog')
dialog: any;
@@ -129,6 +132,17 @@ export class ActivitiChecklist implements OnInit, OnChanges {
this.cancel();
}
public delete(taskId: string) {
this.activitiTaskList.deleteTask(taskId).subscribe(
() => {
this.checklist = this.checklist.filter(check => check.id !== taskId);
this.checklistTaskDeleted.emit(taskId);
},
(err) => {
this.logService.error(err);
});
}
public cancel() {
if (this.dialog) {
this.dialog.nativeElement.close();

View File

@@ -38,7 +38,8 @@
[readOnly]="readOnlyForm"
[taskId]="taskDetails.id"
[assignee]="taskDetails?.assignee?.id"
(checklistTaskCreated)="onChecklistTaskCreated($event)">
(checklistTaskCreated)="onChecklistTaskCreated($event)"
(checklistTaskDeleted)="onChecklistTaskDeleted($event)">
</activiti-checklist>
</div>
</div>

View File

@@ -96,6 +96,9 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
@Output()
taskCreated: EventEmitter<TaskDetailsModel> = new EventEmitter<TaskDetailsModel>();
@Output()
taskDeleted: EventEmitter<string> = new EventEmitter<string>();
@Output()
onError: EventEmitter<any> = new EventEmitter<any>();
@@ -258,6 +261,10 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
this.taskCreated.emit(task);
}
onChecklistTaskDeleted(taskId: string) {
this.taskDeleted.emit(taskId);
}
onFormError(error: any) {
this.errorDialog.nativeElement.showModal();
this.onError.emit(error);

View File

@@ -15,173 +15,65 @@
* limitations under the License.
*/
import { TestBed, async } from '@angular/core/testing';
import { CoreModule } from 'ng2-alfresco-core';
import { ReflectiveInjector } from '@angular/core';
import { async } from '@angular/core/testing';
import {
AlfrescoAuthenticationService,
AlfrescoSettingsService,
AlfrescoApiService,
StorageService,
LogService
} from 'ng2-alfresco-core';
import { ActivitiTaskListService } from './activiti-tasklist.service';
import { TaskDetailsModel } from '../models/task-details.model';
import {
FilterRepresentationModel,
AppDefinitionRepresentationModel,
TaskQueryRequestRepresentationModel
} from '../models/filter.model';
import { Comment } from '../models/comment.model';
import {
fakeFilters,
fakeAppPromise,
fakeAppFilter,
fakeFilter,
fakeTaskList,
fakeErrorTaskList,
fakeTasksComment,
fakeTasksChecklist,
fakeTaskDetails,
fakeUser,
fakeApps,
fakeRepresentationFilter1,
secondFakeTaskList,
fakeRepresentationFilter2,
fakeFormList,
fakeTaskListDifferentProcessDefinitionKey,
fakeFilterWithProcessDefinitionKey
} from '../assets/tasklist-service.mock';
declare let jasmine: any;
describe('Activiti TaskList Service', () => {
let fakeFilters = {
size: 2, total: 2, start: 0,
data: [
new AppDefinitionRepresentationModel(
{
id: '1', name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' }
}
),
{
id: '2', name: 'FakeMyTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-assignee' }
}
]
};
let fakeAppFilter = {
size: 1, total: 1, start: 0,
data: [
{
id: 1, name: 'FakeInvolvedTasks', recent: false, icon: 'glyphicon-align-left',
filter: { sort: 'created-desc', name: '', state: 'open', assignment: 'fake-involved' }
}
]
};
let fakeApps = {
size: 2, total: 2, start: 0,
data: [
{
id: 1, defaultAppId: null, name: 'Sales-Fakes-App', description: 'desc-fake1', modelId: 22,
theme: 'theme-1-fake', icon: 'glyphicon-asterisk', 'deploymentId': '111', 'tenantId': null
},
{
id: 2, defaultAppId: null, name: 'health-care-Fake', description: 'desc-fake2', modelId: 33,
theme: 'theme-2-fake', icon: 'glyphicon-asterisk', 'deploymentId': '444', 'tenantId': null
}
]
};
let fakeFilter = {
sort: 'created-desc', text: '', state: 'open', assignment: 'fake-assignee'
};
let fakeUser = { id: 1, email: 'fake-email@dom.com', firstName: 'firstName', lastName: 'lastName' };
let fakeTaskList = {
size: 1, total: 1, start: 0,
data: [
{
id: '1', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
let secondFakeTaskList = {
size: 1, total: 1, start: 0,
data: [
{
id: '200', name: 'FakeNameTask', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
let fakeErrorTaskList = {
error: 'wrong request'
};
let fakeTaskDetails = { id: '999', name: 'fake-task-name', formKey: '99', assignee: fakeUser };
let fakeTasksComment = {
size: 2, total: 2, start: 0,
data: [
{
id: 1, message: 'fake-message-1', created: '', createdBy: fakeUser
},
{
id: 2, message: 'fake-message-2', created: '', createdBy: fakeUser
}
]
};
let fakeTasksChecklist = {
size: 1, total: 1, start: 0,
data: [
{
id: 1, name: 'FakeCheckTask1', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
},
{
id: 2, name: 'FakeCheckTask2', description: null, category: null,
assignee: fakeUser,
created: '2016-07-15T11:19:17.440+0000'
}
]
};
let fakeRepresentationFilter1: FilterRepresentationModel = new FilterRepresentationModel({
appId: 1,
name: 'CONTAIN FILTER',
recent: true,
icon: 'glyphicon-align-left',
filter: {
processDefinitionId: null,
processDefinitionKey: null,
name: null,
state: 'open',
sort: 'created-desc',
assignment: 'involved',
dueAfter: null,
dueBefore: null
}
});
let fakeRepresentationFilter2: FilterRepresentationModel = new FilterRepresentationModel({
appId: 2,
name: 'NO TASK FILTER',
recent: false,
icon: 'glyphicon-inbox',
filter: {
processDefinitionId: null,
processDefinitionKey: null,
name: null,
state: 'open',
sort: 'created-desc',
assignment: 'assignee',
dueAfter: null,
dueBefore: null
}
});
let fakeAppPromise = new Promise(function (resolve, reject) {
resolve(fakeAppFilter);
});
let service: ActivitiTaskListService;
let injector;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [
CoreModule.forRoot()
],
providers: [
ActivitiTaskListService
]
injector = ReflectiveInjector.resolveAndCreate([
AlfrescoSettingsService,
AlfrescoApiService,
AlfrescoAuthenticationService,
ActivitiTaskListService,
StorageService,
LogService
]);
});
service = TestBed.get(ActivitiTaskListService);
beforeEach(() => {
service = injector.get(ActivitiTaskListService);
});
beforeEach(() => {
jasmine.Ajax.install();
});
@@ -189,6 +81,8 @@ describe('ActivitiTaskListService', () => {
jasmine.Ajax.uninstall();
});
describe('Content tests', () => {
it('should return the task list filters', (done) => {
service.getTaskListFilters().subscribe(
(res) => {
@@ -257,6 +151,27 @@ describe('ActivitiTaskListService', () => {
});
});
it('should return the task list filtered by processDefinitionKey', (done) => {
service.getTasks(<TaskQueryRequestRepresentationModel>fakeFilterWithProcessDefinitionKey).subscribe(
res => {
expect(res).toBeDefined();
expect(res.length).toEqual(1);
expect(res[0].name).toEqual('FakeNameTask');
expect(res[0].assignee.email).toEqual('fake-email@dom.com');
expect(res[0].assignee.firstName).toEqual('firstName');
expect(res[0].assignee.lastName).toEqual('lastName');
expect(res[0].processDefinitionKey).toEqual('1');
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeTaskListDifferentProcessDefinitionKey)
});
});
it('should throw an exception when the response is wrong', () => {
service.getTasks(<TaskQueryRequestRepresentationModel>fakeFilter).subscribe(
(res) => {
@@ -369,6 +284,19 @@ describe('ActivitiTaskListService', () => {
});
});
it('should remove a checklist task ', (done) => {
service.deleteTask('999').subscribe(
() => {
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json'
});
});
it('should add a comment task ', (done) => {
service.addTaskComment('999', 'fake-comment-message').subscribe(
(res: Comment) => {
@@ -646,4 +574,25 @@ describe('ActivitiTaskListService', () => {
});
}));
it('should get possibile form list', (done) => {
service.getFormList().subscribe(
(res: any) => {
expect(res).toBeDefined();
expect(res.length).toBe(2);
expect(res[0].id).toBe(1);
expect(res[0].name).toBe('form with all widgets');
expect(res[1].id).toBe(2);
expect(res[1].name).toBe('uppy');
done();
}
);
jasmine.Ajax.requests.mostRecent().respondWith({
'status': 200,
contentType: 'application/json',
responseText: JSON.stringify(fakeFormList)
});
});
});
});

View File

@@ -256,6 +256,15 @@ export class ActivitiTaskListService {
}).catch(err => this.handleError(err));
}
/**
* Delete a task
* @param taskId - string
*/
deleteTask(taskId: string): Observable<TaskDetailsModel> {
return Observable.fromPromise(this.callApiDeleteTask(taskId))
.catch(err => this.handleError(err));
}
/**
* Add a filter
* @param filter - FilterRepresentationModel
@@ -357,6 +366,10 @@ export class ActivitiTaskListService {
return this.apiService.getInstance().activiti.taskApi.addSubtask(task.parentTaskId, task);
}
private callApiDeleteTask(taskId: string) {
return this.apiService.getInstance().activiti.taskApi.deleteTask(taskId);
}
private callApiAddFilter(filter: FilterRepresentationModel) {
return this.apiService.getInstance().activiti.userFiltersApi.createUserTaskFilter(filter);
}