From 8f97c766680e1991793dc43241e5099c3d7abe01 Mon Sep 17 00:00:00 2001 From: Will Abson Date: Tue, 15 Nov 2016 17:41:09 +0000 Subject: [PATCH] Complete test coverage for ActivitiProcessService Refs #849 --- .../ng2-activiti-processlist/index.ts | 3 + .../ng2-activiti-processlist/karma.conf.js | 3 + .../src/assets/activiti-process.model.mock.ts | 2 +- .../assets/activiti-process.service.mock.ts | 67 +- .../activiti-filters.component.spec.ts | 2 +- .../components/activiti-filters.component.ts | 6 +- ...viti-process-instance-details.component.ts | 2 +- ...-process-instance-header.component.spec.ts | 2 +- ...iviti-process-instance-header.component.ts | 2 +- ...i-process-instance-tasks.component.spec.ts | 2 +- ...tiviti-process-instance-tasks.component.ts | 2 +- .../src/models/index.ts | 20 + .../src/models/process-definition.model.ts | 42 ++ .../models/process-instance-filter.model.ts | 34 + ...-instance.ts => process-instance.model.ts} | 0 .../services/activiti-process.service.spec.ts | 683 ++++++++++++++++-- .../src/services/activiti-process.service.ts | 60 +- 17 files changed, 825 insertions(+), 107 deletions(-) create mode 100644 ng2-components/ng2-activiti-processlist/src/models/index.ts create mode 100644 ng2-components/ng2-activiti-processlist/src/models/process-definition.model.ts create mode 100644 ng2-components/ng2-activiti-processlist/src/models/process-instance-filter.model.ts rename ng2-components/ng2-activiti-processlist/src/models/{process-instance.ts => process-instance.model.ts} (100%) diff --git a/ng2-components/ng2-activiti-processlist/index.ts b/ng2-components/ng2-activiti-processlist/index.ts index 97e19efdaa..08a0671b82 100644 --- a/ng2-components/ng2-activiti-processlist/index.ts +++ b/ng2-components/ng2-activiti-processlist/index.ts @@ -34,6 +34,9 @@ import { ActivitiProcessService } from './src/services/activiti-process.service' export * from './src/components/activiti-processlist.component'; export * from './src/components/activiti-process-instance-details.component'; +// models +export * from './src/models/index'; + // services export * from './src/services/activiti-process.service'; diff --git a/ng2-components/ng2-activiti-processlist/karma.conf.js b/ng2-components/ng2-activiti-processlist/karma.conf.js index c97ec7d13f..279184d459 100644 --- a/ng2-components/ng2-activiti-processlist/karma.conf.js +++ b/ng2-components/ng2-activiti-processlist/karma.conf.js @@ -48,6 +48,9 @@ module.exports = function (config) { { pattern: 'node_modules/ng2-activiti-tasklist/dist/**/*.*', included: false, served: true, watched: false }, { pattern: 'node_modules/ng2-activiti-form/dist/**/*.*', included: false, served: true, watched: false }, + // library dependencies + { pattern: 'node_modules/moment/moment.js', included: true, watched: false }, + // paths to support debugging with source maps in dev tools {pattern: 'src/**/*.ts', included: false, watched: false}, {pattern: 'dist/**/*.js.map', included: false, watched: false} diff --git a/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.model.mock.ts b/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.model.mock.ts index 909d2ea6ef..202b62cf69 100644 --- a/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.model.mock.ts +++ b/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.model.mock.ts @@ -15,7 +15,7 @@ * limitations under the License. */ -import { ProcessInstance } from '../models/process-instance'; +import { ProcessInstance } from '../models/process-instance.model'; export class ProcessList { diff --git a/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.service.mock.ts b/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.service.mock.ts index 9519380532..007020bf7a 100644 --- a/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.service.mock.ts +++ b/ng2-components/ng2-activiti-processlist/src/assets/activiti-process.service.mock.ts @@ -15,10 +15,17 @@ * limitations under the License. */ -import { FilterRepresentationModel } from 'ng2-activiti-tasklist'; +import { + AppDefinitionRepresentationModel, + Comment, + FilterRepresentationModel, + TaskDetailsModel, + User +} from 'ng2-activiti-tasklist'; +import { ProcessDefinitionRepresentation } from '../models/index'; export var fakeFilters = { - size: 0, total: 0, start: 0, + size: 1, total: 1, start: 0, data: [new FilterRepresentationModel({ 'name': 'Running', 'appId': '22', @@ -33,16 +40,54 @@ export var fakeEmptyFilters = { data: [] }; -export var fakeApi = { - activiti: { - userFiltersApi: { - getUserProcessInstanceFilters: (filterOpts) => Promise.resolve({}), - createUserProcessInstanceFilter: (filter: FilterRepresentationModel) => Promise.resolve(filter) - } - } -}; - export var fakeError = { message: null, messageKey: 'GENERAL.ERROR.FORBIDDEN' }; + +export var fakeApp1 = new AppDefinitionRepresentationModel({ + deploymentId: 26, + name: 'HR processes', + icon: 'glyphicon-cloud', + description: null, + theme: 'theme-6', + modelId: 4, + id: 1 +}); + +export var fakeApp2 = new AppDefinitionRepresentationModel({ + deploymentId: 2501, + name: 'Sales onboarding', + icon: 'glyphicon-asterisk', + description: null, + theme: 'theme-1', + modelId: 1002, + id: 1000 +}); + +export var fakeTaskList = { + data: [ new TaskDetailsModel({ + id: 1, + name: 'Task 1', + processInstanceId: 1000, + created: '2016-11-10T03:37:30.010+0000' + }), new TaskDetailsModel({ + id: 2, + name: 'Task 2', + processInstanceId: 1000, + created: '2016-11-10T03:37:30.010+0000' + })] +}; + +export var fakeComment = new Comment(1, 'Test', '2016-11-10T03:37:30.010+0000', new User({ + id: 13, + firstName: 'Wilbur', + lastName: 'Adams', + email: 'wilbur@app.com' +})); + +export var fakeProcessDef = new ProcessDefinitionRepresentation({ + id: '32323', + key: 'blah', + name: 'Process 1' +}); diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.spec.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.spec.ts index cbbb5adefe..fcc540f7e1 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.spec.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.spec.ts @@ -84,7 +84,7 @@ describe('ActivitiFilters', () => { }); it('should emit an error with a bad response', (done) => { - filterList.appId = '1'; + filterList.appId = 1; spyOn(activitiService, 'getProcessFilters').and.returnValue(Observable.fromPromise(fakeErrorFilterPromise)); filterList.onError.subscribe((err) => { diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.ts index 05a0a77504..69305fea58 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-filters.component.ts @@ -41,7 +41,7 @@ export class ActivitiProcessFilters implements OnInit, OnChanges { onError: EventEmitter = new EventEmitter(); @Input() - appId: string; + appId: number; @Input() appName: string; @@ -88,7 +88,7 @@ export class ActivitiProcessFilters implements OnInit, OnChanges { * @param appId * @param appName */ - getFilters(appId?: string, appName?: string) { + getFilters(appId?: number, appName?: string) { if (appName) { this.getFiltersByAppName(appName); } else { @@ -100,7 +100,7 @@ export class ActivitiProcessFilters implements OnInit, OnChanges { * Return the filter list filtered by appId * @param appId - optional */ - getFiltersByAppId(appId?: string) { + getFiltersByAppId(appId?: number) { this.activiti.getProcessFilters(appId).subscribe( (res: FilterRepresentationModel[]) => { this.resetFilter(); diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-details.component.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-details.component.ts index e27dc7634b..c74d5a5f85 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-details.component.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-details.component.ts @@ -21,7 +21,7 @@ import { ActivitiProcessService } from './../services/activiti-process.service'; import { ActivitiProcessInstanceHeader } from './activiti-process-instance-header.component'; import { ActivitiProcessInstanceTasks } from './activiti-process-instance-tasks.component'; import { ActivitiComments } from './activiti-comments.component'; -import { ProcessInstance } from '../models/process-instance'; +import { ProcessInstance } from '../models/process-instance.model'; declare let componentHandler: any; diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.spec.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.spec.ts index 92d0d53a8b..9b9717e42c 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.spec.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.spec.ts @@ -23,7 +23,7 @@ import { AlfrescoTranslationService, CoreModule } from 'ng2-alfresco-core'; import { ActivitiProcessInstanceHeader } from './activiti-process-instance-header.component'; import { TranslationMock } from './../assets/translation.service.mock'; import { exampleProcess } from './../assets/activiti-process.model.mock'; -import { ProcessInstance } from './../models/process-instance'; +import { ProcessInstance } from './../models/process-instance.model'; import { ActivitiProcessService } from './../services/activiti-process.service'; describe('ActivitiProcessInstanceHeader', () => { diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.ts index f6b5793f16..9cc4b8ec49 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-header.component.ts @@ -17,7 +17,7 @@ import { Component, Input, Output, EventEmitter } from '@angular/core'; import { AlfrescoTranslationService } from 'ng2-alfresco-core'; -import { ProcessInstance } from '../models/process-instance'; +import { ProcessInstance } from '../models/process-instance.model'; import { ActivitiProcessService } from './../services/activiti-process.service'; import { DatePipe } from '@angular/common'; diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.spec.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.spec.ts index 5a18fa9f39..8670f9bcba 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.spec.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.spec.ts @@ -26,7 +26,7 @@ import { TaskDetailsModel } from 'ng2-activiti-tasklist'; import { ActivitiProcessInstanceTasks } from './activiti-process-instance-tasks.component'; import { TranslationMock } from './../assets/translation.service.mock'; import { taskDetailsMock } from './../assets/task-details.mock'; -import { ProcessInstance } from './../models/process-instance'; +import { ProcessInstance } from './../models/process-instance.model'; import { ActivitiProcessService } from './../services/activiti-process.service'; describe('ActivitiProcessInstanceTasks', () => { diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.ts index 233631d3db..09ef0e1191 100644 --- a/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.ts +++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-process-instance-tasks.component.ts @@ -21,7 +21,7 @@ import { ActivitiProcessService } from './../services/activiti-process.service'; import { TaskDetailsModel } from 'ng2-activiti-tasklist'; import { Observable, Observer } from 'rxjs/Rx'; import { DatePipe } from '@angular/common'; -import { ProcessInstance } from '../models/process-instance'; +import { ProcessInstance } from '../models/process-instance.model'; declare let componentHandler: any; declare let dialogPolyfill: any; diff --git a/ng2-components/ng2-activiti-processlist/src/models/index.ts b/ng2-components/ng2-activiti-processlist/src/models/index.ts new file mode 100644 index 0000000000..e1c4292453 --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/models/index.ts @@ -0,0 +1,20 @@ +/*! + * @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 * from './process-definition.model'; +export * from './process-instance.model'; +export * from './process-instance-filter.model'; diff --git a/ng2-components/ng2-activiti-processlist/src/models/process-definition.model.ts b/ng2-components/ng2-activiti-processlist/src/models/process-definition.model.ts new file mode 100644 index 0000000000..eb3bd59f17 --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/models/process-definition.model.ts @@ -0,0 +1,42 @@ +/*! + * @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 class ProcessDefinitionRepresentation { + id: string; + name: string; + description: string; + key: string; + category: string; + version: number; + deploymentId: string; + tenantId: string; + metaDataValues: any[]; + hasStartForm: boolean; + + constructor(obj?: any) { + this.id = obj && obj.id || null; + this.name = obj && obj.name || null; + this.description = obj && obj.description || null; + this.key = obj && obj.key || null; + this.category = obj && obj.category || null; + this.version = obj && obj.version || 0; + this.deploymentId = obj && obj.deploymentId || null; + this.tenantId = obj && obj.tenantId || null; + this.metaDataValues = obj && obj.metaDataValues || []; + this.hasStartForm = obj && obj.hasStartForm === true ? true : false; + } +} diff --git a/ng2-components/ng2-activiti-processlist/src/models/process-instance-filter.model.ts b/ng2-components/ng2-activiti-processlist/src/models/process-instance-filter.model.ts new file mode 100644 index 0000000000..b5ac36df06 --- /dev/null +++ b/ng2-components/ng2-activiti-processlist/src/models/process-instance-filter.model.ts @@ -0,0 +1,34 @@ +/*! + * @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 class ProcessFilterRequestRepresentation { + processDefinitionId: string; + appDefinitionId: string; + state: string; + sort: string; + page: number; + size: number; + + constructor(obj?: any) { + this.processDefinitionId = obj && obj.processDefinitionId || null; + this.appDefinitionId = obj && obj.appDefinitionId || null; + this.state = obj && obj.state || null; + this.sort = obj && obj.sort || null; + this.page = obj && obj.page || 0; + this.size = obj && obj.size || 25; + } +} diff --git a/ng2-components/ng2-activiti-processlist/src/models/process-instance.ts b/ng2-components/ng2-activiti-processlist/src/models/process-instance.model.ts similarity index 100% rename from ng2-components/ng2-activiti-processlist/src/models/process-instance.ts rename to ng2-components/ng2-activiti-processlist/src/models/process-instance.model.ts diff --git a/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.spec.ts b/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.spec.ts index ed93bdbbe2..eb60c100f0 100644 --- a/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.spec.ts +++ b/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.spec.ts @@ -16,18 +16,34 @@ */ import { ReflectiveInjector } from '@angular/core'; +import { async } from '@angular/core/testing'; import { AlfrescoAuthenticationService, AlfrescoSettingsService, AlfrescoApiService } from 'ng2-alfresco-core'; -import { fakeApi, fakeEmptyFilters, fakeFilters, fakeError } from '../assets/activiti-process.service.mock'; +import { FilterRepresentationModel } from 'ng2-activiti-tasklist'; +import { AlfrescoApi } from 'alfresco-js-api'; +import { + fakeEmptyFilters, + fakeFilters, + fakeError, + fakeApp1, + fakeApp2, + fakeTaskList, + fakeComment, + fakeProcessDef +} from '../assets/activiti-process.service.mock'; +import { exampleProcess } from '../assets/activiti-process.model.mock'; +import { ProcessFilterRequestRepresentation } from '../models/process-instance-filter.model'; import { ActivitiProcessService } from './activiti-process.service'; describe('ActivitiProcessService', () => { let service: ActivitiProcessService; + let authenticationService: AlfrescoAuthenticationService; let injector: ReflectiveInjector; + let alfrescoApi: AlfrescoApi; beforeEach(() => { injector = ReflectiveInjector.resolveAndCreate([ @@ -37,81 +53,636 @@ describe('ActivitiProcessService', () => { AlfrescoSettingsService ]); service = injector.get(ActivitiProcessService); - let authenticationService: AlfrescoAuthenticationService = injector.get(AlfrescoAuthenticationService); - spyOn(authenticationService, 'getAlfrescoApi').and.returnValue(fakeApi); + authenticationService = injector.get(AlfrescoAuthenticationService); + alfrescoApi = authenticationService.getAlfrescoApi(); }); - xit('should get process instances', (done) => { + describe('process instances', () => { + + let getProcessInstances: jasmine.Spy; + + beforeEach(() => { + getProcessInstances = spyOn(alfrescoApi.activiti.processApi, 'getProcessInstances') + .and + .returnValue(Promise.resolve({ data: [ exampleProcess ] })); + }); + + it('should return the correct number of instances', async(() => { + service.getProcessInstances(null).subscribe((instances) => { + expect(instances.length).toBe(1); + }); + })); + + it('should return the correct instance data', async(() => { + service.getProcessInstances(null).subscribe((instances) => { + let instance = instances[0]; + expect(instance.id).toBe(exampleProcess.id); + expect(instance.name).toBe(exampleProcess.name); + expect(instance.started).toBe(exampleProcess.started); + }); + })); + + it('should call service to fetch process instances', () => { + service.getProcessInstances(null); + expect(getProcessInstances).toHaveBeenCalled(); + }); + + it('should call service with default parameters if no filter specified', () => { + service.getProcessInstances(null); + expect(getProcessInstances).toHaveBeenCalledWith(new ProcessFilterRequestRepresentation({ + page: 0, + sort: 'created-desc', + state: 'all' + })); + }); + + it('should call service with supplied parameters', () => { + let filter: ProcessFilterRequestRepresentation = new ProcessFilterRequestRepresentation({ + processDefinitionId: '1', + appDefinitionId: '1', + page: 1, + sort: 'created-asc', + state: 'completed' + }); + service.getProcessInstances(filter); + expect(getProcessInstances).toHaveBeenCalledWith(filter); + }); + + it('should pass on any error that is returned by the API', async(() => { + getProcessInstances = getProcessInstances.and.returnValue(Promise.reject(fakeError)); + service.getProcessInstances(null).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + getProcessInstances = getProcessInstances.and.returnValue(Promise.reject(null)); + service.getProcessInstances(null).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + describe('process instance', () => { + + const processId = 'test'; + let getProcessInstance: jasmine.Spy; + + beforeEach(() => { + getProcessInstance = spyOn(alfrescoApi.activiti.processApi, 'getProcessInstance') + .and + .returnValue(Promise.resolve(exampleProcess)); + }); + + it('should return the correct instance data', async(() => { + service.getProcess(processId).subscribe((instance) => { + expect(instance.id).toBe(exampleProcess.id); + expect(instance.name).toBe(exampleProcess.name); + expect(instance.started).toBe(exampleProcess.started); + }); + })); + + it('should call service to fetch process instances', () => { + service.getProcess(processId); + expect(getProcessInstance).toHaveBeenCalled(); + }); + + it('should call service with supplied process ID', () => { + service.getProcess(processId); + expect(getProcessInstance).toHaveBeenCalledWith(processId); + }); + + it('should pass on any error that is returned by the API', async(() => { + getProcessInstance = getProcessInstance.and.returnValue(Promise.reject(fakeError)); + service.getProcess(null).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + getProcessInstance = getProcessInstance.and.returnValue(Promise.reject(null)); + service.getProcess(null).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + describe('start process instance', () => { + + const processDefId = '1234', processName = 'My process instance'; + let startNewProcessInstance: jasmine.Spy; + + beforeEach(() => { + startNewProcessInstance = spyOn(alfrescoApi.activiti.processApi, 'startNewProcessInstance') + .and + .returnValue(Promise.resolve(exampleProcess)); + }); + + it('should call the API to create the process instance', () => { + service.startProcess(processDefId, processName); + expect(startNewProcessInstance).toHaveBeenCalledWith({ + name: processName, + processDefinitionId: processDefId + }); + }); + + it('should call the API to create the process instance with form parameters', () => { + let formParams = { + type: 'ford', + color: 'red' + }; + service.startProcess(processDefId, processName, formParams); + expect(startNewProcessInstance).toHaveBeenCalledWith({ + name: processName, + processDefinitionId: processDefId, + values: formParams + }); + }); + + it('should return the created process instance', async(() => { + service.startProcess(processDefId, processName).subscribe((createdProcess) => { + expect(createdProcess.id).toBe(exampleProcess.id); + expect(createdProcess.name).toBe(exampleProcess.name); + expect(createdProcess.started).toBe(exampleProcess.started); + expect(createdProcess.startedBy.id).toBe(exampleProcess.startedBy.id); + }); + })); + + it('should pass on any error that is returned by the API', async(() => { + startNewProcessInstance = startNewProcessInstance.and.returnValue(Promise.reject(fakeError)); + + service.startProcess(processDefId, processName).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + startNewProcessInstance = startNewProcessInstance.and.returnValue(Promise.reject(null)); + service.startProcess(processDefId, processName).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + describe('cancel process instance', () => { + + const processInstanceId = '1234'; + let deleteProcessInstance: jasmine.Spy; + + beforeEach(() => { + deleteProcessInstance = spyOn(alfrescoApi.activiti.processApi, 'deleteProcessInstance') + .and + .returnValue(Promise.resolve()); + }); + + it('should call service to delete process instances', () => { + service.cancelProcess(processInstanceId); + expect(deleteProcessInstance).toHaveBeenCalled(); + }); + + it('should call service with supplied process ID', () => { + service.cancelProcess(processInstanceId); + expect(deleteProcessInstance).toHaveBeenCalledWith(processInstanceId); + }); + + it('should run the success callback', (done) => { + service.cancelProcess(processInstanceId).subscribe(() => { + done(); + }); + }); + + it('should pass on any error that is returned by the API', async(() => { + deleteProcessInstance = deleteProcessInstance.and.returnValue(Promise.reject(fakeError)); + service.cancelProcess(null).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + deleteProcessInstance = deleteProcessInstance.and.returnValue(Promise.reject(null)); + service.cancelProcess(null).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + describe('process definitions', () => { + + let getProcessDefinitions: jasmine.Spy; + + beforeEach(() => { + getProcessDefinitions = spyOn(alfrescoApi.activiti.processApi, 'getProcessDefinitions') + .and + .returnValue(Promise.resolve({ data: [ fakeProcessDef, fakeProcessDef ] })); + }); + + it('should return the correct number of process defs', async(() => { + service.getProcessDefinitions().subscribe((defs) => { + expect(defs.length).toBe(2); + }); + })); + + it('should return the correct process def data', async(() => { + service.getProcessDefinitions().subscribe((defs) => { + expect(defs[0].id).toBe(fakeProcessDef.id); + expect(defs[0].key).toBe(fakeProcessDef.key); + expect(defs[0].name).toBe(fakeProcessDef.name); + }); + })); + + it('should call API with correct parameters when no appId provided', () => { + service.getProcessDefinitions(); + expect(getProcessDefinitions).toHaveBeenCalledWith({ + latest: true + }); + }); + + it('should call API with correct parameters when appId provided', () => { + const appId = '1'; + service.getProcessDefinitions(appId); + expect(getProcessDefinitions).toHaveBeenCalledWith({ + latest: true, + appDefinitionId: appId + }); + }); + + it('should pass on any error that is returned by the API', async(() => { + getProcessDefinitions = getProcessDefinitions.and.returnValue(Promise.reject(fakeError)); + service.getProcessDefinitions().subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + getProcessDefinitions = getProcessDefinitions.and.returnValue(Promise.reject(null)); + service.getProcessDefinitions().subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + describe('process instance tasks', () => { + + const processId = '1001'; + let listTasks: jasmine.Spy; + + beforeEach(() => { + listTasks = spyOn(alfrescoApi.activiti.taskApi, 'listTasks') + .and + .returnValue(Promise.resolve(fakeTaskList)); + }); + + it('should return the correct number of tasks', async(() => { + service.getProcessTasks(processId).subscribe((tasks) => { + expect(tasks.length).toBe(2); + }); + })); + + it('should return the correct task data', async(() => { + let fakeTasks = fakeTaskList.data; + service.getProcessTasks(processId).subscribe((tasks) => { + let task = tasks[0]; + expect(task.id).toBe(fakeTasks[0].id); + expect(task.name).toBe(fakeTasks[0].name); + expect(task.created).toBe('2016-11-10T00:00:00+00:00'); + }); + })); + + it('should call service to fetch process instance tasks', () => { + service.getProcessTasks(processId); + expect(listTasks).toHaveBeenCalled(); + }); + + it('should call service with processInstanceId parameter', () => { + service.getProcessTasks(processId); + expect(listTasks).toHaveBeenCalledWith({ + processInstanceId: processId + }); + }); + + it('should call service with processInstanceId and state parameters', () => { + service.getProcessTasks(processId, 'completed'); + expect(listTasks).toHaveBeenCalledWith({ + processInstanceId: processId, + state: 'completed' + }); + }); + + it('should pass on any error that is returned by the API', async(() => { + listTasks = listTasks.and.returnValue(Promise.reject(fakeError)); + service.getProcessTasks(processId).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + listTasks = listTasks.and.returnValue(Promise.reject(null)); + service.getProcessTasks(processId).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + describe('comments', () => { + + const processId = '1001'; + + describe('get comments', () => { + + let getProcessInstanceComments: jasmine.Spy; + + beforeEach(() => { + getProcessInstanceComments = spyOn(alfrescoApi.activiti.commentsApi, 'getProcessInstanceComments') + .and + .returnValue(Promise.resolve({ data: [ fakeComment, fakeComment ] })); + }); + + it('should return the correct number of comments', async(() => { + service.getProcessInstanceComments(processId).subscribe((tasks) => { + expect(tasks.length).toBe(2); + }); + })); + + it('should return the correct comment data', async(() => { + service.getProcessInstanceComments(processId).subscribe((comments) => { + let comment = comments[0]; + expect(comment.id).toBe(fakeComment.id); + expect(comment.created).toBe(fakeComment.created); + expect(comment.message).toBe(fakeComment.message); + expect(comment.createdBy.id).toBe(fakeComment.createdBy.id); + }); + })); + + it('should call service to fetch process instance comments', () => { + service.getProcessInstanceComments(processId); + expect(getProcessInstanceComments).toHaveBeenCalledWith(processId); + }); + + it('should pass on any error that is returned by the API', async(() => { + getProcessInstanceComments = getProcessInstanceComments.and.returnValue(Promise.reject(fakeError)); + service.getProcessInstanceComments(processId).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + getProcessInstanceComments = getProcessInstanceComments.and.returnValue(Promise.reject(null)); + service.getProcessInstanceComments(processId).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + describe('add comment', () => { + + const message = 'Test message'; + let addProcessInstanceComment: jasmine.Spy; + + beforeEach(() => { + addProcessInstanceComment = spyOn(alfrescoApi.activiti.commentsApi, 'addProcessInstanceComment') + .and + .returnValue(Promise.resolve(fakeComment)); + }); + + it('should call service to add comment', () => { + service.addProcessInstanceComment(processId, message); + expect(addProcessInstanceComment).toHaveBeenCalledWith({ + message: message + }, processId); + }); + + it('should return the created comment', async(() => { + service.addProcessInstanceComment(processId, message).subscribe((comment) => { + expect(comment.id).toBe(fakeComment.id); + expect(comment.created).toBe(fakeComment.created); + expect(comment.message).toBe(fakeComment.message); + expect(comment.createdBy).toBe(fakeComment.createdBy); + }); + })); + + it('should pass on any error that is returned by the API', async(() => { + addProcessInstanceComment = addProcessInstanceComment.and.returnValue(Promise.reject(fakeError)); + service.addProcessInstanceComment(processId, message).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + addProcessInstanceComment = addProcessInstanceComment.and.returnValue(Promise.reject(null)); + service.addProcessInstanceComment(processId, message).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); + + }); + + }); + + describe('deployed apps', () => { + + let getAppDefinitions: jasmine.Spy; + + beforeEach(() => { + getAppDefinitions = spyOn(alfrescoApi.activiti.appsApi, 'getAppDefinitions') + .and + .returnValue(Promise.resolve({ data: [ fakeApp1, fakeApp2 ] })); + }); + + it('should return the correct app', async(() => { + service.getDeployedApplications(fakeApp1.name).subscribe((app) => { + expect(app.id).toBe(fakeApp1.id); + expect(app.name).toBe(fakeApp1.name); + expect(app.deploymentId).toBe(fakeApp1.deploymentId); + }); + })); + + it('should call service to fetch apps', () => { + service.getDeployedApplications(null); + expect(getAppDefinitions).toHaveBeenCalled(); + }); + + it('should pass on any error that is returned by the API', async(() => { + getAppDefinitions = getAppDefinitions.and.returnValue(Promise.reject(fakeError)); + service.getDeployedApplications(null).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + + it('should return a default error if no data is returned by the API', async(() => { + getAppDefinitions = getAppDefinitions.and.returnValue(Promise.reject(null)); + service.getDeployedApplications(null).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); - expect(true).toBe(true); - done(); }); describe('filters', () => { - let userFiltersApi = fakeApi.activiti.userFiltersApi; - let getFilters: any, createFilter: any; + let getFilters: jasmine.Spy; + let createFilter: jasmine.Spy; beforeEach(() => { - getFilters = spyOn(userFiltersApi, 'getUserProcessInstanceFilters'); - createFilter = spyOn(userFiltersApi, 'createUserProcessInstanceFilter'); + getFilters = spyOn(alfrescoApi.activiti.userFiltersApi, 'getUserProcessInstanceFilters') + .and + .returnValue(Promise.resolve(fakeFilters)); + + createFilter = spyOn(alfrescoApi.activiti.userFiltersApi, 'createUserProcessInstanceFilter') + .and + .callFake((filter: FilterRepresentationModel) => Promise.resolve(filter)); }); - it('should call the API without an appId defined by default', () => { - getFilters = getFilters.and.returnValue(Promise.resolve(fakeFilters)); - service.getProcessFilters(null); - expect(getFilters).toHaveBeenCalledWith({}); + describe('get filters', () => { + + it('should call the API without an appId defined by default', () => { + service.getProcessFilters(null); + expect(getFilters).toHaveBeenCalledWith({}); + }); + + it('should call the API with the correct appId when specified', () => { + service.getProcessFilters(226); + expect(getFilters).toHaveBeenCalledWith({appId: 226}); + }); + + it('should return the non-empty filter list that is returned by the API', async(() => { + service.getProcessFilters(null).subscribe( + (res) => { + expect(res.length).toBe(1); + } + ); + })); + + it('should return the default filters when none are returned by the API', async(() => { + getFilters = getFilters.and.returnValue(Promise.resolve(fakeEmptyFilters)); + + service.getProcessFilters(null).subscribe( + (res) => { + expect(res.length).toBe(3); + } + ); + })); + + it('should create the default filters when none are returned by the API', async(() => { + getFilters = getFilters.and.returnValue(Promise.resolve(fakeEmptyFilters)); + createFilter = createFilter.and.returnValue(Promise.resolve({})); + + service.getProcessFilters(null).subscribe( + () => { + expect(createFilter).toHaveBeenCalledTimes(3); + } + ); + })); + + it('should pass on any error that is returned by the API', async(() => { + getFilters = getFilters.and.returnValue(Promise.reject(fakeError)); + + service.getProcessFilters(null).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); + }); - it('should call the API with the correct appId when specified', () => { - getFilters = getFilters.and.returnValue(Promise.resolve(fakeFilters)); - service.getProcessFilters('226'); - expect(getFilters).toHaveBeenCalledWith({appId: '226'}); - }); + describe('add filter', () => { - it('should return the non-empty filter list that is returned by the API', (done) => { - getFilters = getFilters.and.returnValue(Promise.resolve(fakeFilters)); - service.getProcessFilters(null).subscribe( - (res) => { - expect(res.length).toBe(1); - done(); - } - ); - }); + let filter = fakeFilters.data[0]; - it('should return the default filters when none are returned by the API', (done) => { - getFilters = getFilters.and.returnValue(Promise.resolve(fakeEmptyFilters)); + it('should call the API to create the filter', () => { + service.addFilter(filter); + expect(createFilter).toHaveBeenCalledWith(filter); + }); - service.getProcessFilters(null).subscribe( - (res) => { - expect(res.length).toBe(3); - done(); - } - ); - }); + it('should return the created filter', async(() => { + service.addFilter(filter).subscribe((createdFilter: FilterRepresentationModel) => { + expect(createdFilter).toBe(filter); + }); + })); - it('should create the default filters when none are returned by the API', (done) => { - getFilters = getFilters.and.returnValue(Promise.resolve(fakeEmptyFilters)); - createFilter = createFilter.and.returnValue(Promise.resolve({})); + it('should pass on any error that is returned by the API', async(() => { + createFilter = createFilter.and.returnValue(Promise.reject(fakeError)); - service.getProcessFilters(null).subscribe( - (res) => { - expect(createFilter).toHaveBeenCalledTimes(3); - done(); - } - ); - }); + service.addFilter(filter).subscribe( + () => {}, + (res) => { + expect(res).toBe(fakeError); + } + ); + })); - it('should pass on any error that is returned by the API', (done) => { - getFilters = getFilters.and.returnValue(Promise.reject(fakeError)); + it('should return a default error if no data is returned by the API', async(() => { + createFilter = createFilter.and.returnValue(Promise.reject(null)); + service.addFilter(filter).subscribe( + () => {}, + (res) => { + expect(res).toBe('Server error'); + } + ); + })); - service.getProcessFilters(null).subscribe( - () => {}, - (res) => { - expect(res).toBe(fakeError); - done(); - } - ); }); }); }); diff --git a/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.ts b/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.ts index 3ad0aed595..9bcf9c0c4a 100644 --- a/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.ts +++ b/ng2-components/ng2-activiti-processlist/src/services/activiti-process.service.ts @@ -16,12 +16,17 @@ */ import { AlfrescoAuthenticationService } from 'ng2-alfresco-core'; -import { ProcessInstance } from '../models/process-instance'; -import { User, Comment, FilterRepresentationModel, TaskQueryRequestRepresentationModel } from 'ng2-activiti-tasklist'; +import { ProcessInstance, ProcessDefinitionRepresentation } from '../models/index'; +import { ProcessFilterRequestRepresentation } from '../models/process-instance-filter.model'; +import { + AppDefinitionRepresentationModel, + Comment, + FilterRepresentationModel, + TaskDetailsModel, + User +} from 'ng2-activiti-tasklist'; import { Injectable } from '@angular/core'; import { Observable } from 'rxjs/Observable'; -import 'rxjs/add/operator/map'; -import 'rxjs/add/operator/catch'; declare var moment: any; @@ -32,30 +37,27 @@ export class ActivitiProcessService { } /** - * Retrive all the Deployed app + * Retrieve all deployed apps * @returns {Observable} */ - getDeployedApplications(name: string): Observable { + getDeployedApplications(name: string): Observable { return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.appsApi.getAppDefinitions()) - .map((response: any) => response.data.find(p => p.name === name)) - .do(data => console.log('Application: ' + JSON.stringify(data))); - } - - getProcesses(): Observable { - let request = {'page': 0, 'sort': 'created-desc', 'state': 'all'}; - - return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.processApi.getProcessInstances(request)) - .map(this.extractData) + .map((response: any) => response.data.find((p: AppDefinitionRepresentationModel) => p.name === name)) .catch(this.handleError); } - getProcessInstances(requestNode: TaskQueryRequestRepresentationModel): Observable { + getProcessInstances(requestNode?: ProcessFilterRequestRepresentation): Observable { + requestNode = requestNode || new ProcessFilterRequestRepresentation({ + page: 0, + sort: 'created-desc', + state: 'all' + }); return Observable.fromPromise(this.authService.getAlfrescoApi().activiti.processApi.getProcessInstances(requestNode)) .map(this.extractData) .catch(this.handleError); } - getProcessFilters(appId: string): Observable { + getProcessFilters(appId: number): Observable { let filterOpts = appId ? { appId: appId } : {}; @@ -79,7 +81,7 @@ export class ActivitiProcessService { * @param appId * @returns {FilterRepresentationModel[]} */ - private createDefaultFilters(appId: string): FilterRepresentationModel[] { + private createDefaultFilters(appId: number): FilterRepresentationModel[] { let filters: FilterRepresentationModel[] = []; let involvedTasksFilter = this.getRunningFilterInstance(appId); @@ -102,7 +104,7 @@ export class ActivitiProcessService { * @param appId * @returns {FilterRepresentationModel} */ - private getRunningFilterInstance(appId: string): FilterRepresentationModel { + private getRunningFilterInstance(appId: number): FilterRepresentationModel { return new FilterRepresentationModel({ 'name': 'Running', 'appId': appId, @@ -117,7 +119,7 @@ export class ActivitiProcessService { * @param appId * @returns {FilterRepresentationModel} */ - private getCompletedFilterInstance(appId: string): FilterRepresentationModel { + private getCompletedFilterInstance(appId: number): FilterRepresentationModel { return new FilterRepresentationModel({ 'name': 'Completed', 'appId': appId, @@ -132,7 +134,7 @@ export class ActivitiProcessService { * @param appId * @returns {FilterRepresentationModel} */ - private getAllFilterInstance(appId: string): FilterRepresentationModel { + private getAllFilterInstance(appId: number): FilterRepresentationModel { return new FilterRepresentationModel({ 'name': 'All', 'appId': appId, @@ -149,10 +151,7 @@ export class ActivitiProcessService { */ addFilter(filter: FilterRepresentationModel): Observable { return Observable.fromPromise(this.callApiAddFilter(filter)) - .map(res => res) - .map((response: FilterRepresentationModel) => { - return response; - }).catch(this.handleError); + .catch(this.handleError); } getProcess(id: string): Observable { @@ -160,7 +159,7 @@ export class ActivitiProcessService { .catch(this.handleError); } - getProcessTasks(id: string, state: string): Observable { + getProcessTasks(id: string, state?: string): Observable { let taskOpts = state ? { processInstanceId: id, state: state @@ -209,14 +208,13 @@ export class ActivitiProcessService { return Observable.fromPromise( this.authService.getAlfrescoApi().activiti.commentsApi.addProcessInstanceComment({message: message}, id) ) - .map(res => res) .map((response: Comment) => { return new Comment(response.id, response.message, response.created, response.createdBy); }).catch(this.handleError); } - getProcessDefinitions(appId: string) { + getProcessDefinitions(appId?: string): Observable { let opts = appId ? { latest: true, appDefinitionId: appId @@ -227,10 +225,11 @@ export class ActivitiProcessService { this.authService.getAlfrescoApi().activiti.processApi.getProcessDefinitions(opts) ) .map(this.extractData) + .map(processDefs => processDefs.map((pd) => new ProcessDefinitionRepresentation(pd))) .catch(this.handleError); } - startProcess(processDefinitionId: string, name: string, startFormValues?: any) { + startProcess(processDefinitionId: string, name: string, startFormValues?: any): Observable { let startRequest: any = { name: name, processDefinitionId: processDefinitionId @@ -241,10 +240,11 @@ export class ActivitiProcessService { return Observable.fromPromise( this.authService.getAlfrescoApi().activiti.processApi.startNewProcessInstance(startRequest) ) + .map((pd) => new ProcessInstance(pd)) .catch(this.handleError); } - cancelProcess(processInstanceId: string) { + cancelProcess(processInstanceId: string): Observable { return Observable.fromPromise( this.authService.getAlfrescoApi().activiti.processApi.deleteProcessInstance(processInstanceId) )