Add task details component tests

Refs #851
This commit is contained in:
Will Abson 2016-11-07 17:56:05 +00:00
parent ea1953771b
commit 08ba048c89
4 changed files with 455 additions and 15 deletions

View File

@ -0,0 +1,192 @@
/*!
* @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.
*/
export var taskDetailsMock = {
'id': '91',
'name': 'Request translation',
'description': null,
'category': null,
'assignee': {'id': 1001, 'firstName': 'Wilbur', 'lastName': 'Adams', 'email': 'wilbur@app.activiti.com'},
'created': '2016-11-03T15:25:42.749+0000',
'dueDate': null,
'endDate': null,
'duration': null,
'priority': 50,
'parentTaskId': null,
'parentTaskName': null,
'processInstanceId': '86',
'processInstanceName': null,
'processDefinitionId': 'TranslationProcess:2:8',
'processDefinitionName': 'Translation Process',
'processDefinitionDescription': null,
'processDefinitionKey': 'TranslationProcess',
'processDefinitionCategory': 'http://www.activiti.org/processdef',
'processDefinitionVersion': 2,
'processDefinitionDeploymentId': '5',
'formKey': '4',
'processInstanceStartUserId': '1001',
'initiatorCanCompleteTask': false,
'adhocTaskCanBeReassigned': false,
'taskDefinitionKey': 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
'executionId': '86',
'involvedPeople': [],
'memberOfCandidateUsers': false,
'managerOfCandidateGroup': false,
'memberOfCandidateGroup': false
};
export var taskFormMock = {
'id': 4,
'name': 'Translation request',
'processDefinitionId': 'TranslationProcess:2:8',
'processDefinitionName': 'Translation Process',
'processDefinitionKey': 'TranslationProcess',
'taskId': '91',
'taskName': 'Request translation',
'taskDefinitionKey': 'sid-DDECD9E4-0299-433F-9193-C3D905C3EEBE',
'tabs': [],
'fields': [{
'fieldType': 'ContainerRepresentation',
'id': '1478093984155',
'name': 'Label',
'type': 'container',
'value': null,
'required': false,
'readOnly': false,
'overrideId': false,
'colspan': 1,
'placeholder': null,
'minLength': 0,
'maxLength': 0,
'minValue': null,
'maxValue': null,
'regexPattern': null,
'optionType': null,
'hasEmptyValue': null,
'options': null,
'restUrl': null,
'restResponsePath': null,
'restIdProperty': null,
'restLabelProperty': null,
'tab': null,
'className': null,
'dateDisplayFormat': null,
'layout': null,
'sizeX': 2,
'sizeY': 1,
'row': -1,
'col': -1,
'visibilityCondition': null,
'numberOfColumns': 2,
'fields': {
'1': [{
'fieldType': 'AttachFileFieldRepresentation',
'id': 'originalcontent',
'name': 'Original content',
'type': 'upload',
'value': [],
'required': true,
'readOnly': false,
'overrideId': false,
'colspan': 1,
'placeholder': null,
'minLength': 0,
'maxLength': 0,
'minValue': null,
'maxValue': null,
'regexPattern': null,
'optionType': null,
'hasEmptyValue': null,
'options': null,
'restUrl': null,
'restResponsePath': null,
'restIdProperty': null,
'restLabelProperty': null,
'tab': null,
'className': null,
'params': {
},
'dateDisplayFormat': null,
'layout': {'row': -1, 'column': -1, 'colspan': 1},
'sizeX': 1,
'sizeY': 1,
'row': -1,
'col': -1,
'visibilityCondition': null,
'metaDataColumnDefinitions': []
}],
'2': [{
'fieldType': 'RestFieldRepresentation',
'id': 'language',
'name': 'Language',
'type': 'dropdown',
'value': 'Choose one...',
'required': true,
'readOnly': false,
'overrideId': false,
'colspan': 1,
'placeholder': null,
'minLength': 0,
'maxLength': 0,
'minValue': null,
'maxValue': null,
'regexPattern': null,
'optionType': null,
'hasEmptyValue': true,
'options': [{'id': 'empty', 'name': 'Choose one...'}, {'id': 'fr', 'name': 'French'}, {
'id': 'de',
'name': 'German'
}, {'id': 'es', 'name': 'Spanish'}],
'restUrl': null,
'restResponsePath': null,
'restIdProperty': null,
'restLabelProperty': null,
'tab': null,
'className': null,
'params': {'existingColspan': 1, 'maxColspan': 1},
'dateDisplayFormat': null,
'layout': {'row': -1, 'column': -1, 'colspan': 1},
'sizeX': 1,
'sizeY': 1,
'row': -1,
'col': -1,
'visibilityCondition': null,
'endpoint': null,
'requestHeaders': null
}]
}
}],
'outcomes': [],
'javascriptEvents': [],
'className': '',
'style': '',
'customFieldTemplates': {},
'metadata': {},
'variables': [],
'gridsterForm': false,
'globalDateFormat': 'D-M-YYYY'
};
export var tasksMock = {
data: [
taskDetailsMock
]
};
export var noDataMock = {
data: []
};

View File

@ -9,7 +9,7 @@
</div> </div>
<div *ngIf="taskDetails"> <div *ngIf="taskDetails">
<h2 class="mdl-card__title-text">{{taskDetails.name}}</h2> <h2 class="mdl-card__title-text">{{taskDetails.name}}</h2>
<activiti-task-header [taskDetails]="taskDetails" [formName]="taskFormName" #activitiheader></activiti-task-header> <activiti-task-header [taskDetails]="taskDetails" [formName]="taskFormName"></activiti-task-header>
<div class="mdl-grid"> <div class="mdl-grid">
<div class="mdl-cell mdl-cell--4-col"> <div class="mdl-cell mdl-cell--4-col">
<activiti-people [people]="taskPeople" [readOnly]="readOnlyForm" <activiti-people [people]="taskPeople" [readOnly]="readOnlyForm"
@ -34,7 +34,7 @@
(formCompleted)='onFormCompleted($event)' (formCompleted)='onFormCompleted($event)'
(formLoaded)='onFormLoaded($event)' (formLoaded)='onFormLoaded($event)'
(onError)='onFormError($event)' (onError)='onFormError($event)'
(executeOutcome)='onExecuteFormOutcome($event)' (executeOutcome)='onFormExecuteOutcome($event)'
#activitiForm> #activitiForm>
</activiti-form> </activiti-form>
<button type="button" class="mdl-button" *ngIf="!hasFormKey() && isTaskActive()" (click)="onComplete()"> <button type="button" class="mdl-button" *ngIf="!hasFormKey() && isTaskActive()" (click)="onComplete()">

View File

@ -0,0 +1,249 @@
/*!
* @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 { NO_ERRORS_SCHEMA, SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { Observable } from 'rxjs/Rx';
import { AlfrescoTranslationService, CoreModule } from 'ng2-alfresco-core';
import { ActivitiFormModule, FormModel, FormOutcomeEvent, FormOutcomeModel, FormService } from 'ng2-activiti-form';
import { ActivitiTaskDetails } from './activiti-task-details.component';
import { ActivitiTaskListService } from './../services/activiti-tasklist.service';
import { ActivitiPeopleService } from './../services/activiti-people.service';
import { TranslationMock } from './../assets/translation.service.mock';
import { taskDetailsMock, taskFormMock, tasksMock, noDataMock } from './../assets/task-details.mock';
describe('ActivitiTaskDetails', () => {
let componentHandler: any;
let service: ActivitiTaskListService;
let formService: FormService;
let component: ActivitiTaskDetails;
let fixture: ComponentFixture<ActivitiTaskDetails>;
let getTaskDetailsSpy: jasmine.Spy;
let getFormSpy: jasmine.Spy;
let getTasksSpy: jasmine.Spy;
let completeTaskSpy: jasmine.Spy;
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [
CoreModule,
ActivitiFormModule
],
declarations: [
ActivitiTaskDetails
],
providers: [
{ provide: AlfrescoTranslationService, useClass: TranslationMock },
ActivitiTaskListService,
ActivitiPeopleService
],
schemas: [ NO_ERRORS_SCHEMA ]
}).compileComponents();
}));
beforeEach(() => {
fixture = TestBed.createComponent(ActivitiTaskDetails);
component = fixture.componentInstance;
service = fixture.debugElement.injector.get(ActivitiTaskListService);
formService = fixture.debugElement.injector.get(FormService);
getTaskDetailsSpy = spyOn(service, 'getTaskDetails').and.returnValue(Observable.of(taskDetailsMock));
getFormSpy = spyOn(formService, 'getTaskForm').and.returnValue(Observable.of(taskFormMock));
getTasksSpy = spyOn(service, 'getTasks').and.returnValue(Observable.of(tasksMock));
completeTaskSpy = spyOn(service, 'completeTask').and.returnValue(Observable.of({}));
spyOn(service, 'getTaskComments').and.returnValue(Observable.of(noDataMock));
spyOn(service, 'getTaskChecklist').and.returnValue(Observable.of(noDataMock));
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered',
'upgradeElement'
]);
window['componentHandler'] = componentHandler;
});
it('should load task details when taskId specified', () => {
component.taskId = '123';
fixture.detectChanges();
expect(getTaskDetailsSpy).toHaveBeenCalled();
});
it('should not load task details when no taskId is specified', () => {
fixture.detectChanges();
expect(getTaskDetailsSpy).not.toHaveBeenCalled();
});
it('should set a placeholder message when taskId not initialised', () => {
fixture.detectChanges();
expect(fixture.nativeElement.innerText).toBe('TASK_DETAILS.MESSAGES.NONE');
});
it('should display a form when the task has an associated form', async(() => {
component.taskId = '123';
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('activiti-form'))).not.toBeNull();
});
}));
it('should not display a form when the task does not have an associated form', async((done) => {
component.taskId = '123';
taskDetailsMock.formKey = undefined;
fixture.detectChanges();
fixture.whenStable().then(() => {
fixture.detectChanges();
expect(fixture.debugElement.query(By.css('activiti-form'))).toBeNull();
});
}));
describe('change detection', () => {
let change = new SimpleChange('123', '456');
let nullChange = new SimpleChange('123', null);
beforeEach(async(() => {
component.taskId = '123';
fixture.detectChanges();
fixture.whenStable().then(() => {
getTaskDetailsSpy.calls.reset();
});
}));
it('should fetch new task details when taskId changed', () => {
component.ngOnChanges({ 'taskId': change });
expect(getTaskDetailsSpy).toHaveBeenCalledWith('456');
});
it('should NOT fetch new task details when empty changeset made', () => {
component.ngOnChanges({});
expect(getTaskDetailsSpy).not.toHaveBeenCalled();
});
it('should NOT fetch new task details when taskId changed to null', () => {
component.ngOnChanges({ 'taskId': nullChange });
expect(getTaskDetailsSpy).not.toHaveBeenCalled();
});
it('should set a placeholder message when taskId changed to null', () => {
component.ngOnChanges({ 'taskId': nullChange });
fixture.detectChanges();
expect(fixture.nativeElement.innerText).toBe('TASK_DETAILS.MESSAGES.NONE');
});
});
describe('Form events', () => {
beforeEach(async(() => {
component.taskId = '123';
fixture.detectChanges();
fixture.whenStable();
}));
it('should emit a save event when form saved', () => {
let emitSpy: jasmine.Spy = spyOn(component.formSaved, 'emit');
component.onFormSaved(new FormModel());
expect(emitSpy).toHaveBeenCalled();
});
it('should emit a outcome execution event when form outcome executed', () => {
let emitSpy: jasmine.Spy = spyOn(component.executeOutcome, 'emit');
component.onFormExecuteOutcome(new FormOutcomeEvent(new FormOutcomeModel(new FormModel())));
expect(emitSpy).toHaveBeenCalled();
});
it('should emit a complete event when form completed', () => {
let emitSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
component.onFormCompleted(new FormModel());
expect(emitSpy).toHaveBeenCalled();
});
it('should load next task when form completed', () => {
component.onComplete();
expect(getTasksSpy).toHaveBeenCalled();
});
it('should show placeholder message if there is no next task', () => {
getTasksSpy.and.returnValue(Observable.of(noDataMock));
component.onComplete();
fixture.detectChanges();
expect(fixture.nativeElement.innerText).toBe('TASK_DETAILS.MESSAGES.NONE');
});
it('should emit an error event if an error occurs fetching the next task', () => {
let emitSpy: jasmine.Spy = spyOn(component.onError, 'emit');
getTasksSpy.and.returnValue(Observable.throw({}));
component.onComplete();
expect(emitSpy).toHaveBeenCalled();
});
it('should NOT load next task when form completed if showNextTask is false', () => {
component.showNextTask = false;
component.onComplete();
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', () => {
let emitSpy: jasmine.Spy = spyOn(component.formCompleted, 'emit');
component.onComplete();
expect(emitSpy).toHaveBeenCalled();
});
it('should call service to load next task when complete button clicked', () => {
component.onComplete();
expect(getTasksSpy).toHaveBeenCalled();
});
it('should emit a load event when form loaded', () => {
let emitSpy: jasmine.Spy = spyOn(component.formLoaded, 'emit');
component.onFormLoaded(new FormModel());
expect(emitSpy).toHaveBeenCalled();
});
it('should emit an error event when form error occurs', () => {
let emitSpy: jasmine.Spy = spyOn(component.onError, 'emit');
component.onFormError({});
expect(emitSpy).toHaveBeenCalled();
});
it('should display a dialog to the user when a form error occurs', () => {
let dialogEl = fixture.debugElement.query(By.css('.error-dialog')).nativeElement;
let showSpy: jasmine.Spy = spyOn(dialogEl, 'showModal');
component.onFormError({});
expect(showSpy).toHaveBeenCalled();
});
it('should close error dialog when close button clicked', () => {
let dialogEl = fixture.debugElement.query(By.css('.error-dialog')).nativeElement;
let closeSpy: jasmine.Spy = spyOn(dialogEl, 'close');
component.onFormError({});
component.closeErrorDialog();
expect(closeSpy).toHaveBeenCalled();
});
});
});

View File

@ -85,8 +85,10 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
/** /**
* Constructor * Constructor
* @param auth * @param auth Authentication service
* @param translate * @param translate Translation service
* @param activitiForm Form service
* @param activitiTaskList Task service
*/ */
constructor(private auth: AlfrescoAuthenticationService, constructor(private auth: AlfrescoAuthenticationService,
private translate: AlfrescoTranslationService, private translate: AlfrescoTranslationService,
@ -117,9 +119,9 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
} }
/** /**
* Reset the task detail to undefined * Reset the task details
*/ */
reset() { private reset() {
this.taskDetails = null; this.taskDetails = null;
} }
@ -141,7 +143,7 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
* Load the activiti task details * Load the activiti task details
* @param taskId * @param taskId
*/ */
loadDetails(taskId: string) { private loadDetails(taskId: string) {
this.taskPeople = []; this.taskPeople = [];
this.taskFormName = null; this.taskFormName = null;
if (taskId) { if (taskId) {
@ -157,10 +159,7 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
this.taskPeople.push(new User(user)); this.taskPeople.push(new User(user));
}); });
} }
} });
);
} else {
this.reset();
} }
} }
@ -169,7 +168,7 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
* @param processInstanceId * @param processInstanceId
* @param processDefinitionId * @param processDefinitionId
*/ */
loadNextTask(processInstanceId: string, processDefinitionId: string) { private loadNextTask(processInstanceId: string, processDefinitionId: string) {
let requestNode = new TaskQueryRequestRepresentationModel( let requestNode = new TaskQueryRequestRepresentationModel(
{ {
processInstanceId: processInstanceId, processInstanceId: processInstanceId,
@ -190,11 +189,11 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
} }
/** /**
* Complete the activiti task * Complete button clicked
*/ */
onComplete() { onComplete() {
this.activitiTaskList.completeTask(this.taskId).subscribe( this.activitiTaskList.completeTask(this.taskId).subscribe(
(res) => this.formCompleted.emit(null) (res) => this.onFormCompleted(null)
); );
} }
@ -222,7 +221,7 @@ export class ActivitiTaskDetails implements OnInit, OnChanges {
this.onError.emit(error); this.onError.emit(error);
} }
onExecuteFormOutcome(event: FormOutcomeEvent) { onFormExecuteOutcome(event: FormOutcomeEvent) {
this.executeOutcome.emit(event); this.executeOutcome.emit(event);
} }