diff --git a/docs/start-process.component.md b/docs/start-process.component.md index 5788f0b229..75c7a3cc3c 100644 --- a/docs/start-process.component.md +++ b/docs/start-process.component.md @@ -16,11 +16,12 @@ Starts a process. | Name | Type | Description | | ---- | -- | ----------- | -| appId | `number` | (required): Limit the list of processes which can be started to those contained in the specified app | +| appId | `number` | (optional): Limit the list of processes which can be started to those contained in the specified app | | name | `string` | (optional) name to assign to the current process | | processDefinitionId | `string` | (optional) definition ID of the process to start | | variables | `ProcessInstanceVariable[]` |Variables in input to the process [RestVariable](https://github.com/Alfresco/alfresco-js-api/tree/master/src/alfresco-activiti-rest-api/docs/RestVariable.md) | | values | `FormValues` | Parameter to pass form field values in the start form if is associated | +| showSelectProcessDropdown | `boolean` | hide or show the process selection drodown, true by default | ### Events @@ -30,31 +31,30 @@ Starts a process. | cancel | `EventEmitter` | Emitted when the process is canceled | | error | `EventEmitter` | Emitted when the start process operation fails | -## Details - -Displays the *Start Process* form, allowing the user to specify some details like process name and process definition, which are the most basic requirements to start a new process instance. The following -options are available for choosing which process to start: - -- If your app has only one `processDefinition` then the `adf-start-process` component will iind and - use it automatically. -- If your app has multiple `processDefinitions` and you don't define a `processDefinitionId` parameter - then a drop down will allow you to select which one to use. -- If your app has multiple `processDefinitions` and you do define a `processDefinitionId` parameter then the `adf-start-process` component will be automatically instantiated with the selected process. - -An error message will be shown if no process definition at all is available. - -### Start a process with processDefinitionId +### Start a process part of an app ```html + [processDefinition]="PROCESS_DEFINITION"> ``` Use this method to preselect which process to start if there is more than one process in your app. +### Start a process not included in an app + +```html + + +``` + +Use this method to preselect which process to start + + ### Custom data example Here is an example of how to pass in form field values to initialize the start form that has been diff --git a/lib/config/karma.conf-all.js b/lib/config/karma.conf-all.js index 8dcad6f9d9..540f03f4fe 100644 --- a/lib/config/karma.conf-all.js +++ b/lib/config/karma.conf-all.js @@ -1,4 +1,5 @@ const webpackCoverage = require('./webpack.coverage'); +const webpackTest= require('./webpack.test'); module.exports = function (config) { var _config = { @@ -46,7 +47,7 @@ module.exports = function (config) { {pattern: './config/app.config.json', included: false, served: true, watched: false} ], - webpack: webpackCoverage(config), + webpack: (config.mode === 'coverage') ? webpackCoverage(config) : webpackTest(config), webpackMiddleware: { noInfo: true, diff --git a/lib/config/webpack.coverage.js b/lib/config/webpack.coverage.js index fa4d1dbc42..4632ecdad5 100644 --- a/lib/config/webpack.coverage.js +++ b/lib/config/webpack.coverage.js @@ -1,15 +1,25 @@ const webpack = require('webpack'); const webpackMerge = require('webpack-merge'); -const testConfig = require('./webpack.test.js'); +const commonConfig = require('./webpack.common.js'); const helpers = require('./helpers'); module.exports = function (config) { - return webpackMerge(testConfig, { + return webpackMerge(commonConfig, { devtool: 'inline-source-map', module: { rules: [ + { + test: /\.(txt|pdf)$/, + loader: 'file-loader', + query: { + name: '[path][name].[ext]', + outputPath: (url)=> { + return url.replace('src', 'dist'); + } + } + }, { enforce: 'post', test: /^(?!(.*spec|index|.*mock|.*model|.*event)).*\.ts?$/, diff --git a/lib/config/webpack.test.js b/lib/config/webpack.test.js index 5a54422886..364d4b5bf9 100644 --- a/lib/config/webpack.test.js +++ b/lib/config/webpack.test.js @@ -1,22 +1,24 @@ const webpackMerge = require('webpack-merge'); const commonConfig = require('./webpack.common.js'); -module.exports = webpackMerge(commonConfig, { +module.exports = function (config) { + return webpackMerge(commonConfig, { - devtool: 'inline-source-map', + devtool: 'inline-source-map', - module: { - rules: [ - { - test: /\.(txt|pdf)$/, - loader: 'file-loader', - query: { - name: '[path][name].[ext]', - outputPath: (url)=> { + module: { + rules: [ + { + test: /\.(txt|pdf)$/, + loader: 'file-loader', + query: { + name: '[path][name].[ext]', + outputPath: (url)=> { return url.replace('src', 'dist'); + } } } - } - ] - } -}); + ] + } + }); +}; diff --git a/lib/package.json b/lib/package.json index 672c4a1dd5..a7dffa7697 100644 --- a/lib/package.json +++ b/lib/package.json @@ -104,7 +104,7 @@ "happypack": "4.0.0", "html-loader": "0.4.4", "html-webpack-plugin": "2.28.0", - "istanbul-instrumenter-loader": "0.2.0", + "istanbul-instrumenter-loader": "3.0.0", "jasmine-ajax": "3.2.0", "jasmine-core": "2.4.1", "karma": "0.13.22", @@ -131,22 +131,21 @@ "sass-loader": "6.0.5", "script-loader": "0.7.0", "scss-bundle": "2.1.0", - "source-map-loader": "0.1.6", + "source-map-loader": "0.2.3", "style-loader": "0.13.1", "systemjs-builder": "0.15.34", "to-string-loader": "1.1.5", "traceur": "0.0.91", - "ts-loader": "3.1.1", + "ts-loader": "3.3.0", "ts-node": "2.0.0", "tslint": "5.7.0", "tslint-loader": "3.5.3", "typescript": "2.6.1", - "uglifyjs-webpack-plugin": "^1.0.1", - "webpack": "3.8.1", + "uglifyjs-webpack-plugin": "1.1.6", + "webpack": "3.10.0", "webpack-bundle-analyzer": "2.9.0", - "webpack-dev-server": "2.9.4", - "webpack-merge": "2.6.1", - "wsrv": "0.1.7" + "webpack-dev-server": "2.11.1", + "webpack-merge": "2.6.1" }, "license": "Apache-2.0", "bundlesize": [ diff --git a/lib/process-services/process-list/components/start-process.component.html b/lib/process-services/process-list/components/start-process.component.html index 99c39f1aff..98ebbd7843 100644 --- a/lib/process-services/process-list/components/start-process.component.html +++ b/lib/process-services/process-list/components/start-process.component.html @@ -8,11 +8,12 @@ -
+ +
- + {{'ADF_PROCESS_LIST.START_PROCESS.FORM.TYPE_PLACEHOLDER' | translate}} - + {{ processDef.name }} @@ -22,7 +23,7 @@ diff --git a/lib/process-services/process-list/components/start-process.component.spec.ts b/lib/process-services/process-list/components/start-process.component.spec.ts index 61f758c319..2dd0c70f58 100644 --- a/lib/process-services/process-list/components/start-process.component.spec.ts +++ b/lib/process-services/process-list/components/start-process.component.spec.ts @@ -38,7 +38,7 @@ import { } from '../../mock'; import { StartProcessInstanceComponent } from './start-process.component'; -describe('StartProcessInstanceComponent', () => { +describe('StartFormComponent', () => { let appConfig: AppConfigService; let activitiContentService: ActivitiContentService; @@ -88,374 +88,87 @@ describe('StartProcessInstanceComponent', () => { expect(fixture.componentInstance instanceof StartProcessInstanceComponent).toBe(true, 'should create StartProcessInstanceComponent'); }); - describe('process definitions list', () => { - - it('should call service to fetch process definitions with appId', () => { - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - - expect(getDefinitionsSpy).toHaveBeenCalledWith('123'); - }); - - it('should call service to fetch process definitions without appId', () => { - let change = new SimpleChange(null, null, true); - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - - expect(getDefinitionsSpy).not.toHaveBeenCalledWith(null); - }); - - it('should call service to fetch process definitions with appId when provided', () => { - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - - expect(getDefinitionsSpy).toHaveBeenCalledWith('123'); - }); - - it('should display the correct number of processes in the select list', () => { - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - - let selectElement = fixture.nativeElement.querySelector('mat-select'); - expect(selectElement.children.length).toBe(1); - }); - - it('should display the option def details', () => { - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({ 'appId': change }); - component.processDefinitions = testMultipleProcessDefs; - fixture.detectChanges(); - fixture.whenStable().then(() => { - let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); - let optionElement = fixture.nativeElement.querySelectorAll('mat-option'); - selectElement.click(); - expect(selectElement).not.toBeNull(); - expect(selectElement).toBeDefined(); - expect(optionElement).not.toBeNull(); - expect(optionElement).toBeDefined(); - }); - }); - - it('should indicate an error to the user if process defs cannot be loaded', async(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.throw({})); - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - let errorEl = fixture.nativeElement.querySelector('#error-message'); - expect(errorEl).not.toBeNull('Expected error message to be present'); - expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS'); - }); - })); - - it('should show no process available message when no process definition is loaded', async(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of([])); - let change = new SimpleChange(null, '123', true); - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - - fixture.whenStable().then(() => { - let noprocessElement = fixture.nativeElement.querySelector('#no-process-message'); - expect(noprocessElement).not.toBeNull('Expected no available process message to be present'); - expect(noprocessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS'); - }); - })); - - it('should hide the process dropdown if the app contain only one processDefinition', async(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testProcessDefRepr)); - let change = new SimpleChange(null, '123', true); - component.appId = 123; - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - fixture.whenStable().then(() => { - let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); - expect(selectElement).toBeNull(); - }); - })); - - it('should hide the process dropdown if the processDefinition is already selected', async(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testMultipleProcessDefs)); - let change = new SimpleChange(null, '123', true); - component.appId = 123; - component.processDefinitionId = 'my:process2'; - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - fixture.whenStable().then(() => { - let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); - expect(selectElement).toBeNull(); - }); - })); - - it('should show the process dropdown if the processDefinition is not selected and the app contain multiple process', async(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testMultipleProcessDefs)); - let change = new SimpleChange(null, '123', true); - component.appId = 123; - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - fixture.whenStable().then(() => { - let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); - expect(selectElement).not.toBeNull(); - }); - })); - - it('should select processDefinition based on processDefinitionId input', async(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testMultipleProcessDefs)); - let change = new SimpleChange(null, '123', true); - component.appId = 123; - component.processDefinitionId = 'my:process2'; - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(component.currentProcessDef.name).toBe(JSON.parse(JSON.stringify(testMultipleProcessDefs[1])).name); - }); - })); - - it('should select automatically the processDefinition if the app contain oly one', async(() => { - getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testProcessDefinitions)); - let change = new SimpleChange(null, '123', true); - component.appId = 123; - component.ngOnChanges({ 'appId': change }); - fixture.detectChanges(); - fixture.whenStable().then(() => { - expect(component.currentProcessDef.name).toBe(JSON.parse(JSON.stringify(testProcessDefinitions[0])).name); - }); - })); - - }); - - describe('input changes', () => { - - let change = new SimpleChange(123, 456, true); - - beforeEach(async(() => { - component.appId = 123; - fixture.detectChanges(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - getDefinitionsSpy.calls.reset(); - }); - })); - - it('should reload processes when appId input changed', () => { - component.ngOnChanges({ appId: change }); - expect(getDefinitionsSpy).toHaveBeenCalledWith(456); - }); - - it('should get current processDeff', () => { - component.ngOnChanges({ appId: change }); - component.onProcessDefChange('my:Process'); - fixture.detectChanges(); - expect(getDefinitionsSpy).toHaveBeenCalled(); - expect(component.processDefinitions).toBe(testMultipleProcessDefs); - }); - }); - - describe('start process', () => { - - beforeEach(() => { - component.name = 'My new process'; - let change = new SimpleChange(null, 123, true); - component.ngOnChanges({ 'appId': change }); - }); - - it('should call service to start process if required fields provided', async(() => { - component.onProcessDefChange('my:process1'); - component.startProcess(); - fixture.whenStable().then(() => { - expect(startProcessSpy).toHaveBeenCalled(); - }); - })); - - it('should avoid calling service to start process if required fields NOT provided', async(() => { - component.name = ''; - component.startProcess(); - fixture.whenStable().then(() => { - expect(startProcessSpy).not.toHaveBeenCalled(); - }); - })); - - it('should call service to start process with the correct parameters', async(() => { - component.onProcessDefChange('my:process1'); - component.startProcess(); - fixture.whenStable().then(() => { - expect(startProcessSpy).toHaveBeenCalledWith('my:process1', 'My new process', undefined, undefined, undefined); - }); - })); - - it('should call service to start process with the variables setted', async(() => { - let inputProcessVariable: ProcessInstanceVariable[] = []; - - let variable: ProcessInstanceVariable = {}; - variable.name = 'nodeId'; - variable.value = 'id'; - - inputProcessVariable.push(variable); - - component.variables = inputProcessVariable; - component.onProcessDefChange('my:process1'); - component.startProcess(); - fixture.whenStable().then(() => { - expect(startProcessSpy).toHaveBeenCalledWith('my:process1', 'My new process', undefined, undefined, inputProcessVariable); - }); - })); - - it('should output start event when process started successfully', async(() => { - let emitSpy = spyOn(component.start, 'emit'); - component.onProcessDefChange('my:process1'); - component.startProcess(); - fixture.whenStable().then(() => { - expect(emitSpy).toHaveBeenCalledWith(newProcess); - }); - })); - - it('should throw error event when process cannot be started', async(() => { - let errorSpy = spyOn(component.error, 'error'); - let error = { message: 'My error' }; - startProcessSpy = startProcessSpy.and.returnValue(Observable.throw(error)); - component.onProcessDefChange('my:process1'); - component.startProcess(); - fixture.whenStable().then(() => { - expect(errorSpy).toHaveBeenCalledWith(error); - }); - })); - - it('should indicate an error to the user if process cannot be started', async(() => { - startProcessSpy = startProcessSpy.and.returnValue(Observable.throw({})); - component.onProcessDefChange('my:process1'); - component.startProcess(); - fixture.whenStable().then(() => { - fixture.detectChanges(); - let errorEl = fixture.nativeElement.querySelector('#error-message'); - expect(errorEl).not.toBeNull(); - expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.START'); - }); - })); - - it('should emit start event when start the process with currentProcessDef and name', () => { - let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); - component.currentProcessDef.id = '1001'; - component.name = 'my:Process'; - component.startProcess(); - fixture.detectChanges(); - expect(startSpy).toHaveBeenCalled(); - }); - - it('should not emit start event when start the process without currentProcessDef and name', () => { - let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); - component.startProcess(); - fixture.detectChanges(); - expect(startSpy).not.toHaveBeenCalled(); - }); - - it('should able to start the process when the required fields are filled up', async(() => { - let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); - component.name = 'my:process1'; - component.onProcessDefChange('my:process1'); - fixture.detectChanges(); - fixture.whenStable().then(() => { - let startButton = fixture.nativeElement.querySelector('#button-start'); - startButton.click(); - expect(startSpy).toHaveBeenCalled(); - }); - })); - - it('should return true if startFrom defined', async(() => { - component.currentProcessDef = testProcessDefRepr; - component.name = 'my:process1'; - component.currentProcessDef.hasStartForm = true; - component.hasStartForm(); - fixture.whenStable().then(() => { - expect(component.hasStartForm()).toBe(true); - }); - })); - - }); - - describe('start forms', () => { - - let startBtn; + describe('first step', () => { describe('without start form', () => { - beforeEach(async(() => { + beforeEach(() => { component.name = 'My new process'; - let change = new SimpleChange(null, '123', true); + let change = new SimpleChange(null, 123, true); component.ngOnChanges({ 'appId': change }); + }); + + it('should enable start button when name and process filled out', async(() => { + component.selectedProcessDef = testProcessDefRepr; fixture.detectChanges(); + fixture.whenStable().then(() => { + let startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(startBtn.disabled).toBe(false); + }); })); it('should have start button disabled when name not filled out', async(() => { component.name = ''; fixture.detectChanges(); fixture.whenStable().then(() => { - startBtn = fixture.nativeElement.querySelector('#button-start'); + let startBtn = fixture.nativeElement.querySelector('#button-start'); expect(startBtn.disabled).toBe(true); }); })); - it('should have start button disabled when no process is selected', () => { - component.onProcessDefChange(''); - fixture.detectChanges(); - startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(startBtn.disabled).toBe(true); - }); - - it('should enable start button when name and process filled out', async(() => { - component.onProcessDefChange('my:process1'); + it('should have start button disabled when no process is selected', async(() => { + component.selectedProcessDef = null; fixture.detectChanges(); fixture.whenStable().then(() => { - startBtn = fixture.nativeElement.querySelector('#button-start'); - expect(startBtn.disabled).toBe(false); + let startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(startBtn.disabled).toBe(true); }); })); - - it('should disable the start process button when process name is empty', () => { - component.name = ''; - fixture.detectChanges(); - let startButton = fixture.nativeElement.querySelector('#button-start'); - expect(startButton.disabled).toBe(true); - }); - }); describe('with start form', () => { beforeEach(() => { getDefinitionsSpy.and.returnValue(Observable.of(testProcessDefWithForm)); - let change = new SimpleChange(null, '123', true); + let change = new SimpleChange(null, 123, true); component.ngOnChanges({ 'appId': change }); - component.onProcessDefChange('my:process1'); + }); + + it('should initialize start form', async(() => { fixture.detectChanges(); - fixture.whenStable(); - startBtn = fixture.nativeElement.querySelector('#button-start'); - }); - it('should initialize start form', () => { - expect(component.startForm).toBeDefined(); - expect(component.startForm).not.toBeNull(); - }); - - it('should load start form from service', () => { - expect(getStartFormDefinitionSpy).toHaveBeenCalled(); - }); - - it('should not show the start process button', async(() => { - component.name = 'My new process'; - fixture.detectChanges(); - expect(startBtn).toBeNull(); + fixture.whenStable().then(() => { + expect(component.startForm).toBeDefined(); + expect(component.startForm).not.toBeNull(); + }); })); - it('should emit cancel event on cancel Button', () => { + it('should load start form from service', async(() => { + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(getStartFormDefinitionSpy).toHaveBeenCalled(); + }); + })); + + it('should have start button disabled if the process is not seleted', async(() => { + component.name = 'My new process'; + fixture.detectChanges(); + fixture.whenStable().then(() => { + let startBtn = fixture.nativeElement.querySelector('#button-start'); + expect(startBtn).toBeNull(); + }); + })); + + it('should emit cancel event on cancel Button', async(() => { + fixture.detectChanges(); let cancelButton = fixture.nativeElement.querySelector('#cancle_process'); let cancelSpy: jasmine.Spy = spyOn(component.cancel, 'emit'); cancelButton.click(); fixture.detectChanges(); - expect(cancelSpy).toHaveBeenCalled(); - }); + fixture.whenStable().then(() => { + expect(cancelSpy).toHaveBeenCalled(); + }); + })); }); describe('CS content connection', () => { @@ -488,4 +201,315 @@ describe('StartProcessInstanceComponent', () => { })); }); }); + + describe('process definitions list', () => { + + it('should call service to fetch process definitions with appId', () => { + component.appId = 123; + component.ngOnChanges({}); + fixture.detectChanges(); + + expect(getDefinitionsSpy).toHaveBeenCalledWith(123); + }); + + it('should call service to fetch process definitions with appId when provided', () => { + component.appId = 123; + component.ngOnChanges({}); + fixture.detectChanges(); + + expect(getDefinitionsSpy).toHaveBeenCalledWith(123); + }); + + it('should display the correct number of processes in the select list', () => { + component.appId = 123; + component.ngOnChanges({}); + fixture.detectChanges(); + + let selectElement = fixture.nativeElement.querySelector('mat-select'); + expect(selectElement.children.length).toBe(1); + }); + + it('should display the option def details', () => { + component.appId = 123; + component.ngOnChanges({}); + component.processDefinitions = testMultipleProcessDefs; + fixture.detectChanges(); + fixture.whenStable().then(() => { + let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); + let optionElement = fixture.nativeElement.querySelectorAll('mat-option'); + selectElement.click(); + expect(selectElement).not.toBeNull(); + expect(selectElement).toBeDefined(); + expect(optionElement).not.toBeNull(); + expect(optionElement).toBeDefined(); + }); + }); + + it('should indicate an error to the user if process defs cannot be loaded', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.throw({})); + component.appId = 123; + component.ngOnChanges({}); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + let errorEl = fixture.nativeElement.querySelector('#error-message'); + expect(errorEl).not.toBeNull('Expected error message to be present'); + expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS'); + }); + })); + + it('should show no process available message when no process definition is loaded', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of([])); + component.appId = 123; + component.ngOnChanges({}); + fixture.detectChanges(); + + fixture.whenStable().then(() => { + let noprocessElement = fixture.nativeElement.querySelector('#no-process-message'); + expect(noprocessElement).not.toBeNull('Expected no available process message to be present'); + expect(noprocessElement.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.NO_PROCESS_DEFINITIONS'); + }); + })); + + it('should select processDefinition based on processDefinition input', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testMultipleProcessDefs)); + component.appId = 123; + component.processDefinition = 'My Process 2'; + component.ngOnChanges({}); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(component.selectedProcessDef.name).toBe(JSON.parse(JSON.stringify(testMultipleProcessDefs[1])).name); + }); + })); + + it('should select automatically the processDefinition if the app contain oly one', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testProcessDefinitions)); + component.appId = 123; + component.ngOnChanges({}); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(component.selectedProcessDef.name).toBe(JSON.parse(JSON.stringify(testProcessDefinitions[0])).name); + }); + })); + + it('should select automatically the first processDefinition if the app contain multiple process and there is no processDefinition in input', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testMultipleProcessDefs)); + component.appId = 123; + component.ngOnChanges({}); + fixture.detectChanges(); + fixture.whenStable().then(() => { + expect(component.selectedProcessDef.name).toBe(JSON.parse(JSON.stringify(testMultipleProcessDefs[0])).name); + }); + })); + + describe('dropdown', () => { + + it('should hide the process dropdown if showSelectProcessDropdown is false', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of([testProcessDefRepr])); + component.appId = 123; + component.showSelectProcessDropdown = false; + component.ngOnChanges({}); + fixture.detectChanges(); + fixture.whenStable().then(() => { + let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); + expect(selectElement).toBeNull(); + }); + })); + + it('should show the process dropdown if showSelectProcessDropdown is false', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testMultipleProcessDefs)); + component.appId = 123; + component.processDefinition = 'My Process 2'; + component.showSelectProcessDropdown = true; + component.ngOnChanges({}); + fixture.detectChanges(); + fixture.whenStable().then(() => { + let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); + expect(selectElement).not.toBeNull(); + }); + })); + + it('should show the process dropdown by default', async(() => { + getDefinitionsSpy = getDefinitionsSpy.and.returnValue(Observable.of(testMultipleProcessDefs)); + component.appId = 123; + component.processDefinition = 'My Process 2'; + component.ngOnChanges({}); + fixture.detectChanges(); + fixture.whenStable().then(() => { + let selectElement = fixture.nativeElement.querySelector('mat-select > .mat-select-trigger'); + expect(selectElement).not.toBeNull(); + }); + })); + }); + }); + + describe('input changes', () => { + + let change = new SimpleChange(123, 456, true); + + beforeEach(async(() => { + component.appId = 123; + fixture.detectChanges(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + getDefinitionsSpy.calls.reset(); + }); + })); + + it('should reload processes when appId input changed', async(() => { + component.appId = 456; + component.ngOnChanges({ appId: change }); + fixture.whenStable().then(() => { + expect(getDefinitionsSpy).toHaveBeenCalledWith(456); + }); + })); + + it('should get current processDeff', () => { + component.appId = 456; + component.ngOnChanges({ appId: change }); + fixture.detectChanges(); + expect(getDefinitionsSpy).toHaveBeenCalled(); + expect(component.processDefinitions).toBe(testMultipleProcessDefs); + }); + }); + + describe('start process', () => { + + beforeEach(() => { + component.name = 'My new process'; + component.appId = 123; + component.ngOnChanges({}); + }); + + it('should call service to start process if required fields provided', async(() => { + component.selectedProcessDef = testProcessDefRepr; + component.startProcess(); + fixture.whenStable().then(() => { + expect(startProcessSpy).toHaveBeenCalled(); + }); + })); + + it('should avoid calling service to start process if required fields NOT provided', async(() => { + component.name = ''; + component.startProcess(); + fixture.whenStable().then(() => { + expect(startProcessSpy).not.toHaveBeenCalled(); + }); + })); + + it('should call service to start process with the correct parameters', async(() => { + component.selectedProcessDef = testProcessDefRepr; + component.startProcess(); + fixture.whenStable().then(() => { + expect(startProcessSpy).toHaveBeenCalledWith('my:process1', 'My new process', undefined, undefined, undefined); + }); + })); + + it('should call service to start process with the variables setted', async(() => { + let inputProcessVariable: ProcessInstanceVariable[] = []; + + let variable: ProcessInstanceVariable = {}; + variable.name = 'nodeId'; + variable.value = 'id'; + + inputProcessVariable.push(variable); + + component.variables = inputProcessVariable; + component.selectedProcessDef = testProcessDefRepr; + component.startProcess(); + fixture.whenStable().then(() => { + expect(startProcessSpy).toHaveBeenCalledWith('my:process1', 'My new process', undefined, undefined, inputProcessVariable); + }); + })); + + it('should output start event when process started successfully', async(() => { + let emitSpy = spyOn(component.start, 'emit'); + component.selectedProcessDef = testProcessDefRepr; + component.startProcess(); + fixture.whenStable().then(() => { + expect(emitSpy).toHaveBeenCalledWith(newProcess); + }); + })); + + it('should throw error event when process cannot be started', async(() => { + let errorSpy = spyOn(component.error, 'error'); + let error = { message: 'My error' }; + startProcessSpy = startProcessSpy.and.returnValue(Observable.throw(error)); + component.selectedProcessDef = testProcessDefRepr; + component.startProcess(); + fixture.whenStable().then(() => { + expect(errorSpy).toHaveBeenCalledWith(error); + }); + })); + + it('should indicate an error to the user if process cannot be started', async(() => { + startProcessSpy = startProcessSpy.and.returnValue(Observable.throw({})); + component.selectedProcessDef = testProcessDefRepr; + component.startProcess(); + fixture.whenStable().then(() => { + fixture.detectChanges(); + let errorEl = fixture.nativeElement.querySelector('#error-message'); + expect(errorEl).not.toBeNull(); + expect(errorEl.innerText.trim()).toBe('ADF_PROCESS_LIST.START_PROCESS.ERROR.START'); + }); + })); + + it('should emit start event when start select a process and add a name', () => { + let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); + component.selectedProcessDef.id = '1001'; + component.name = 'my:Process'; + component.startProcess(); + fixture.detectChanges(); + expect(startSpy).toHaveBeenCalled(); + }); + + it('should not emit start event when start the process without select a process and name', () => { + component.name = null; + component.selectedProcessDef = null; + let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); + component.startProcess(); + fixture.detectChanges(); + expect(startSpy).not.toHaveBeenCalled(); + }); + + it('should not emit start event when start the process without name', () => { + component.name = null; + let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); + component.startProcess(); + fixture.detectChanges(); + expect(startSpy).not.toHaveBeenCalled(); + }); + + it('should not emit start event when start the process without select a process', () => { + component.selectedProcessDef = null; + let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); + component.startProcess(); + fixture.detectChanges(); + expect(startSpy).not.toHaveBeenCalled(); + }); + + it('should able to start the process when the required fields are filled up', async(() => { + let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); + component.name = 'my:process1'; + component.selectedProcessDef = testProcessDefRepr; + fixture.detectChanges(); + fixture.whenStable().then(() => { + let startButton = fixture.nativeElement.querySelector('#button-start'); + startButton.click(); + expect(startSpy).toHaveBeenCalled(); + }); + })); + + it('should return true if startFrom defined', async(() => { + component.selectedProcessDef = testProcessDefRepr; + component.name = 'my:process1'; + component.selectedProcessDef.hasStartForm = true; + component.hasStartForm(); + fixture.whenStable().then(() => { + expect(component.hasStartForm()).toBe(true); + }); + })); + + }); + }); diff --git a/lib/process-services/process-list/components/start-process.component.ts b/lib/process-services/process-list/components/start-process.component.ts index 6762f3eb73..86312cae2f 100644 --- a/lib/process-services/process-list/components/start-process.component.ts +++ b/lib/process-services/process-list/components/start-process.component.ts @@ -50,7 +50,7 @@ export class StartProcessInstanceComponent implements OnChanges { appId: number; @Input() - processDefinitionId: string; + processDefinition: string; @Input() variables: ProcessInstanceVariable[]; @@ -61,6 +61,9 @@ export class StartProcessInstanceComponent implements OnChanges { @Input() name: string; + @Input() + showSelectProcessDropdown: boolean = true; + @Output() start: EventEmitter = new EventEmitter(); @@ -75,7 +78,7 @@ export class StartProcessInstanceComponent implements OnChanges { processDefinitions: ProcessDefinitionRepresentation[] = []; - currentProcessDef: ProcessDefinitionRepresentation = new ProcessDefinitionRepresentation(); + selectedProcessDef: ProcessDefinitionRepresentation = new ProcessDefinitionRepresentation(); errorMessageId: string = ''; @@ -92,10 +95,6 @@ export class StartProcessInstanceComponent implements OnChanges { this.moveNodeFromCStoPS(); } - if (changes['appId'] && changes['appId'].currentValue) { - this.appId = changes['appId'].currentValue; - } - this.loadStartProcess(); } @@ -103,30 +102,26 @@ export class StartProcessInstanceComponent implements OnChanges { this.resetSelectedProcessDefinition(); this.resetErrorMessage(); - if (this.appId) { - this.activitiProcess.getProcessDefinitions(this.appId).subscribe( - (processDefinitionRepresentations: ProcessDefinitionRepresentation[]) => { - this.processDefinitions = processDefinitionRepresentations; + this.activitiProcess.getProcessDefinitions(this.appId).subscribe( + (processDefinitionRepresentations: ProcessDefinitionRepresentation[]) => { + this.processDefinitions = processDefinitionRepresentations; - if (this.processDefinitions.length === 1) { - this.currentProcessDef = JSON.parse(JSON.stringify(this.processDefinitions[0])); - } else { - if (this.processDefinitionId) { - this.processDefinitions = this.processDefinitions.filter((currentProcessDefinition) => { - return currentProcessDefinition.id === this.processDefinitionId; - }); - this.currentProcessDef = JSON.parse(JSON.stringify(this.processDefinitions[0])); - } + if (this.processDefinitions.length === 1 || !this.processDefinition) { + this.selectedProcessDef = this.processDefinitions[0]; + } else { + this.selectedProcessDef = this.processDefinitions.find((currentProcessDefinition) => { + return currentProcessDefinition.name === this.processDefinition; + }); + + if (!this.selectedProcessDef) { + this.selectedProcessDef = this.processDefinitions[0]; } - }, - () => { - this.errorMessageId = 'ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS'; - }); - } - } + } + }, + () => { + this.errorMessageId = 'ADF_PROCESS_LIST.START_PROCESS.ERROR.LOAD_PROCESS_DEFS'; + }); - public hasMultipleProcessDefinitions(): boolean { - return this.processDefinitions.length > 1; } getAlfrescoRepositoryName(): string { @@ -154,10 +149,10 @@ export class StartProcessInstanceComponent implements OnChanges { } public startProcess(outcome?: string) { - if (this.currentProcessDef.id && this.name) { + if (this.selectedProcessDef && this.selectedProcessDef.id && this.name) { this.resetErrorMessage(); let formValues = this.startForm ? this.startForm.form.values : undefined; - this.activitiProcess.startProcess(this.currentProcessDef.id, this.name, outcome, formValues, this.variables).subscribe( + this.activitiProcess.startProcess(this.selectedProcessDef.id, this.name, outcome, formValues, this.variables).subscribe( (res) => { this.name = ''; this.start.emit(res); @@ -170,30 +165,12 @@ export class StartProcessInstanceComponent implements OnChanges { } } - compareProcessDef = (processDefId) => { - if (this.processDefinitions && this.processDefinitions.length === 1 && processDefId === this.processDefinitions[0].id) { - this.onProcessDefChange(processDefId); - return true; - } - } - - onProcessDefChange(processDefinitionId) { - let processDef = this.processDefinitions.find((processDefinition) => { - return processDefinition.id === processDefinitionId; - }); - if (processDef) { - this.currentProcessDef = JSON.parse(JSON.stringify(processDef)); - } else { - this.resetSelectedProcessDefinition(); - } - } - public cancelStartProcess() { this.cancel.emit(); } hasStartForm(): boolean { - return this.currentProcessDef && this.currentProcessDef.hasStartForm; + return this.selectedProcessDef && this.selectedProcessDef.hasStartForm; } isProcessDefinitionEmpty() { @@ -209,11 +186,11 @@ export class StartProcessInstanceComponent implements OnChanges { } validateForm(): boolean { - return this.currentProcessDef.id && this.name && this.isStartFormMissingOrValid(); + return this.selectedProcessDef && this.selectedProcessDef.id && this.name && this.isStartFormMissingOrValid(); } private resetSelectedProcessDefinition() { - this.currentProcessDef = new ProcessDefinitionRepresentation(); + this.selectedProcessDef = new ProcessDefinitionRepresentation(); } private resetErrorMessage(): void { diff --git a/scripts/npm-build-all.sh b/scripts/npm-build-all.sh index d7f29ecd89..c9d2ffdbad 100755 --- a/scripts/npm-build-all.sh +++ b/scripts/npm-build-all.sh @@ -54,7 +54,7 @@ enable_testbrowser(){ test_project() { echo "====== test project: $1 =====" - npm run test -- --component $1 || exit 1 + npm run test -- --component $1 --mode coverage || exit 1 } debug_project() {