diff --git a/ng2-components/ng2-activiti-processlist/src/assets/activiti-start-process.component.mock.ts b/ng2-components/ng2-activiti-processlist/src/assets/activiti-start-process.component.mock.ts
new file mode 100644
index 0000000000..da1ed613fe
--- /dev/null
+++ b/ng2-components/ng2-activiti-processlist/src/assets/activiti-start-process.component.mock.ts
@@ -0,0 +1,172 @@
+/*!
+ * @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 { ProcessInstance } from './../models/process-instance.model';
+import { ProcessDefinitionRepresentation } from './../models/process-definition.model';
+
+export var newProcess = new ProcessInstance({
+ id: '32323',
+ name: 'Process'
+});
+
+export var fakeProcessDefs = [new ProcessDefinitionRepresentation({
+ id: 'my:process1',
+ name: 'My Process 1',
+ hasStartForm: false
+}), new ProcessDefinitionRepresentation({
+ id: 'my:process2',
+ name: 'My Process 2',
+ hasStartForm: false
+})];
+
+export var fakeProcessDefWithForm = [new ProcessDefinitionRepresentation({
+ id: 'my:process1',
+ name: 'My Process 1',
+ hasStartForm: true
+})];
+
+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'
+};
diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html
index c506e6f7f9..fba8ef3380 100644
--- a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html
+++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.html
@@ -4,7 +4,7 @@
{{'START_PROCESS.DIALOG.TITLE'|translate}}
-
-
+
+
diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts
index 2e9ddef461..708cd042e0 100644
--- a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts
+++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.spec.ts
@@ -17,12 +17,13 @@
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
-import { DebugElement } from '@angular/core';
+import { DebugElement, SimpleChange } from '@angular/core';
import { Observable } from 'rxjs/Rx';
-import { ActivitiFormModule } from 'ng2-activiti-form';
import { AlfrescoTranslationService, CoreModule } from 'ng2-alfresco-core';
-import { FormService } from 'ng2-activiti-form';
+import { ActivitiFormModule, FormService } from 'ng2-activiti-form';
+
import { TranslationMock } from './../assets/translation.service.mock';
+import { newProcess, fakeProcessDefs, fakeProcessDefWithForm, taskFormMock } from './../assets/activiti-start-process.component.mock';
import { ActivitiStartProcessButton } from './activiti-start-process.component';
import { ActivitiProcessService } from '../services/activiti-process.service';
@@ -34,14 +35,10 @@ describe('ActivitiStartProcessButton', () => {
let processService: ActivitiProcessService;
let formService: FormService;
let getDefinitionsSpy: jasmine.Spy;
+ let getStartFormDefinitionSpy: jasmine.Spy;
let startProcessSpy: jasmine.Spy;
let debugElement: DebugElement;
- let newProcess = {
- id: '32323',
- name: 'Process'
- };
-
beforeEach(async(() => {
TestBed.configureTestingModule({
imports: [ CoreModule, ActivitiFormModule ],
@@ -64,14 +61,9 @@ describe('ActivitiStartProcessButton', () => {
processService = fixture.debugElement.injector.get(ActivitiProcessService);
formService = fixture.debugElement.injector.get(FormService);
- getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions').and.returnValue(Observable.of([{
- id: 'my:process1',
- name: 'My Process 1'
- }, {
- id: 'my:process2',
- name: 'My Process 2'
- }]));
+ getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions').and.returnValue(Observable.of(fakeProcessDefs));
startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(Observable.of(newProcess));
+ getStartFormDefinitionSpy = spyOn(formService, 'getStartFormDefinition').and.returnValue(Observable.of(taskFormMock));
componentHandler = jasmine.createSpyObj('componentHandler', [
'upgradeAllRegistered',
@@ -80,79 +72,217 @@ describe('ActivitiStartProcessButton', () => {
window['componentHandler'] = componentHandler;
});
- it('should display the correct number of processes in the select list', () => {
- fixture.detectChanges();
- let selectElement = debugElement.query(By.css('select'));
- expect(selectElement.children.length).toBe(3);
+ describe('process definitions list', () => {
+
+ it('should call service to fetch process definitions', () => {
+ fixture.detectChanges();
+ expect(getDefinitionsSpy).toHaveBeenCalled();
+ });
+
+ it('should call service to fetch process definitions with appId when provided', () => {
+ let appId = '123';
+ component.appId = appId;
+ fixture.detectChanges();
+ expect(getDefinitionsSpy).toHaveBeenCalledWith(appId);
+ });
+
+ it('should display the correct number of processes in the select list', () => {
+ fixture.detectChanges();
+ let selectElement = debugElement.query(By.css('select'));
+ expect(selectElement.children.length).toBe(3);
+ });
+
+ it('should display the correct process def details', (done) => {
+ fixture.detectChanges();
+ fixture.whenStable().then(() => {
+ let optionEl: HTMLOptionElement = debugElement.queryAll(By.css('select option'))[1].nativeElement;
+ expect(optionEl.value).toBe('my:process1');
+ expect(optionEl.textContent.trim()).toBe('My Process 1');
+ done();
+ });
+ });
+
});
- it('should display the correct process def details', (done) => {
- fixture.detectChanges();
- fixture.whenStable().then(() => {
- let optionEl: HTMLOptionElement = debugElement.queryAll(By.css('select option'))[1].nativeElement;
- expect(optionEl.value).toBe('my:process1');
- expect(optionEl.textContent.trim()).toBe('My Process 1');
- done();
+ describe('input changes', () => {
+
+ let change = new SimpleChange('123', '456');
+ let nullChange = new SimpleChange('123', null);
+
+ 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 reload processes when appId input changed to null', () => {
+ component.ngOnChanges({ appId: nullChange });
+ expect(getDefinitionsSpy).toHaveBeenCalledWith(null);
+ });
+
+ it('should not reload processes when changes do not include appId input', () => {
+ component.ngOnChanges({});
+ expect(getDefinitionsSpy).not.toHaveBeenCalled();
+ });
+
});
- it('should call service to start process if required fields provided', (done) => {
- component.name = 'My new process';
- component.showDialog();
- fixture.detectChanges();
- component.onChange('my:process1');
- component.startProcess();
- fixture.whenStable().then(() => {
- expect(startProcessSpy).toHaveBeenCalled();
- done();
+ describe('start process', () => {
+
+ beforeEach(() => {
+ component.name = 'My new process';
+ component.showDialog();
+ fixture.detectChanges();
});
+
+ 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);
+ });
+ }));
+
+ 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 avoid calling service to start process if required fields NOT provided', (done) => {
- component.showDialog();
- fixture.detectChanges();
- component.startProcess();
- fixture.whenStable().then(() => {
- expect(startProcessSpy).not.toHaveBeenCalled();
- done();
+ describe('start forms', () => {
+
+ describe('without start form', () => {
+
+ beforeEach(async(() => {
+ component.name = 'My new process';
+ component.showDialog();
+ fixture.detectChanges();
+ component.onProcessDefChange('my:process1');
+ fixture.whenStable();
+ }));
+
+ it('should indicate start form is missing', async(() => {
+ expect(component.isStartFormMissingOrValid()).toBe(true);
+ }));
+
+ it('should enable start button when name and process filled out', async(() => {
+ fixture.detectChanges();
+ let startBtn = debugElement.query(By.css('[data-automation-id="btn-start"]'));
+ expect(startBtn.properties['disabled']).toBe(false);
+ }));
+
});
+
+ describe('with start form', () => {
+
+ beforeEach(() => {
+ getDefinitionsSpy.and.returnValue(Observable.of(fakeProcessDefWithForm));
+ component.showDialog();
+ fixture.detectChanges();
+ component.onProcessDefChange('my:process1');
+ fixture.detectChanges();
+ fixture.whenStable();
+ });
+
+ 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 indicate start form is invalid', async(() => {
+ expect(component.isStartFormMissingOrValid()).toBe(false);
+ }));
+
+ it('should leave start button disabled when mandatory fields not filled out', async(() => {
+ component.name = 'My new process';
+ fixture.detectChanges();
+ let startBtn = debugElement.query(By.css('[data-automation-id="btn-start"]'));
+ expect(startBtn.properties['disabled']).toBe(true);
+ }));
+
+ });
+
});
- it('should call service to start process with the correct parameters', (done) => {
- component.name = 'My new process';
- component.showDialog();
- fixture.detectChanges();
- component.onChange('my:process1');
- component.startProcess();
- fixture.whenStable().then(() => {
- expect(startProcessSpy).toHaveBeenCalledWith('my:process1', 'My new process', undefined);
- done();
- });
- });
+ describe('cancel dialog', () => {
- it('should output start event when process started successfully', (done) => {
- let emitSpy = spyOn(component.start, 'emit');
- component.name = 'My new process';
- component.showDialog();
- fixture.detectChanges();
- component.onChange('my:process1');
- component.startProcess();
- fixture.whenStable().then(() => {
- expect(emitSpy).toHaveBeenCalledWith(newProcess);
- done();
- });
- });
+ let setupDialog = (definitions?: any) => {
+ if (definitions) {
+ getDefinitionsSpy.and.returnValue(Observable.of(definitions));
+ }
+ component.showDialog();
+ fixture.detectChanges();
+ component.onProcessDefChange('my:process1');
+ fixture.detectChanges();
+ };
+
+ let clickCancelButton = () => {
+ let closeButton: DebugElement = debugElement.query(By.css('[data-automation-id="btn-close"]'));
+ closeButton.triggerEventHandler('click', null);
+ };
+
+ it('should close dialog when cancel button clicked', async(() => {
+ let closeSpy = spyOn(component.dialog.nativeElement, 'close');
+ component.showDialog();
+ fixture.detectChanges();
+ clickCancelButton();
+ expect(closeSpy).toHaveBeenCalled();
+ }));
+
+ it('should clear processId and name when dialog cancelled', async(() => {
+ setupDialog();
+ clickCancelButton();
+ expect(component.currentProcessDef.id).toBeNull();
+ expect(component.name).toBe('');
+ }));
+
+ it('should clear form values when dialog cancelled', async(() => {
+ setupDialog(fakeProcessDefWithForm);
+ fixture.whenStable().then(() => {
+ component.startForm.data = {
+ language: 'fr',
+ upload: {}
+ };
+ fixture.detectChanges();
+ clickCancelButton();
+ expect(component.startForm.data['language']).toBeUndefined();
+ expect(component.startForm.data['upload']).toBeUndefined();
+ });
+ }));
- it('should indicate start form is missing when process does not have a start form', (done) => {
- component.name = 'My new process';
- component.showDialog();
- fixture.detectChanges();
- component.onChange('my:process1');
- component.startProcess();
- fixture.whenStable().then(() => {
- expect(component.isStartFormMissingOrValid()).toBe(true);
- done();
- });
});
});
diff --git a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts
index e1136e5418..8de557fdf1 100644
--- a/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts
+++ b/ng2-components/ng2-activiti-processlist/src/components/activiti-start-process.component.ts
@@ -18,6 +18,8 @@
import { Component, EventEmitter, Input, Output, OnInit, ViewChild, DebugElement, OnChanges, SimpleChanges } from '@angular/core';
import { AlfrescoTranslationService } from 'ng2-alfresco-core';
import { ActivitiStartForm } from 'ng2-activiti-form';
+import { ProcessInstance } from './../models/process-instance.model';
+import { ProcessDefinitionRepresentation } from './../models/process-definition.model';
import { ActivitiProcessService } from './../services/activiti-process.service';
declare let componentHandler: any;
@@ -35,7 +37,7 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges {
appId: string;
@Output()
- start: EventEmitter = new EventEmitter();
+ start: EventEmitter = new EventEmitter();
@ViewChild('dialog')
dialog: DebugElement;
@@ -43,11 +45,11 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges {
@ViewChild('startForm')
startForm: ActivitiStartForm;
- processDefinitions: any[] = [];
+ processDefinitions: ProcessDefinitionRepresentation[] = [];
name: string;
- currentProcessDef: any;
+ currentProcessDef: ProcessDefinitionRepresentation = new ProcessDefinitionRepresentation();
constructor(private translate: AlfrescoTranslationService,
private activitiProcess: ActivitiProcessService) {
@@ -70,9 +72,9 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges {
}
public load(appId: string) {
- this.reset();
- this.activitiProcess.getProcessDefinitions(this.appId).subscribe(
- (res: any[]) => {
+ this.resetSelectedProcessDefinition();
+ this.activitiProcess.getProcessDefinitions(appId).subscribe(
+ (res) => {
this.processDefinitions = res;
},
(err) => {
@@ -92,7 +94,7 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges {
if (this.currentProcessDef.id && this.name) {
let formValues = this.startForm ? this.startForm.form.values : undefined;
this.activitiProcess.startProcess(this.currentProcessDef.id, this.name, formValues).subscribe(
- (res: any) => {
+ (res) => {
this.name = '';
this.start.emit(res);
this.cancel();
@@ -105,10 +107,11 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges {
}
public cancel() {
+ this.reset();
this.dialog.nativeElement.close();
}
- onChange(processDefinitionId) {
+ onProcessDefChange(processDefinitionId) {
let processDef = this.processDefinitions.find((processDefinition) => {
return processDefinition.id === processDefinitionId;
});
@@ -128,7 +131,15 @@ export class ActivitiStartProcessButton implements OnInit, OnChanges {
return this.currentProcessDef.id && this.name && this.isStartFormMissingOrValid();
}
- reset() {
- this.currentProcessDef = {};
+ private resetSelectedProcessDefinition() {
+ this.currentProcessDef = new ProcessDefinitionRepresentation();
+ }
+
+ private reset() {
+ this.resetSelectedProcessDefinition();
+ this.name = '';
+ if (this.startForm) {
+ this.startForm.data = {};
+ }
}
}