/*! * @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 { SimpleChange } from '@angular/core'; import { async, ComponentFixture, TestBed } from '@angular/core/testing'; import { MatButtonModule, MatCardModule, MatInputModule, MatSelectModule } from '@angular/material'; import { ActivitiContentService, AppConfigService, FormModule, FormService } from '@alfresco/adf-core'; import { Observable } from 'rxjs/Observable'; import { ProcessInstanceVariable } from '../models/process-instance-variable.model'; import { ProcessService } from '../services/process.service'; import { newProcess, taskFormMock, testProcessDefRepr, testMultipleProcessDefs, testProcessDefWithForm, testProcessDefinitions } from '../../mock'; import { StartProcessInstanceComponent } from './start-process.component'; describe('StartFormComponent', () => { let appConfig: AppConfigService; let activitiContentService: ActivitiContentService; let component: StartProcessInstanceComponent; let fixture: ComponentFixture; let processService: ProcessService; let formService: FormService; let getDefinitionsSpy: jasmine.Spy; let getStartFormDefinitionSpy: jasmine.Spy; let startProcessSpy: jasmine.Spy; beforeEach(async(() => { TestBed.configureTestingModule({ imports: [ FormModule, MatButtonModule, MatCardModule, MatInputModule, MatSelectModule ], declarations: [ StartProcessInstanceComponent ], providers: [ ActivitiContentService, ProcessService, FormService ] }).compileComponents(); })); beforeEach(() => { appConfig = TestBed.get(AppConfigService); activitiContentService = TestBed.get(ActivitiContentService); fixture = TestBed.createComponent(StartProcessInstanceComponent); component = fixture.componentInstance; processService = fixture.debugElement.injector.get(ProcessService); formService = fixture.debugElement.injector.get(FormService); getDefinitionsSpy = spyOn(processService, 'getProcessDefinitions').and.returnValue(Observable.of(testMultipleProcessDefs)); startProcessSpy = spyOn(processService, 'startProcess').and.returnValue(Observable.of(newProcess)); getStartFormDefinitionSpy = spyOn(formService, 'getStartFormDefinition').and.returnValue(Observable.of(taskFormMock)); spyOn(activitiContentService, 'applyAlfrescoNode').and.returnValue(Observable.of({ id: 1234 })); }); it('should create instance of StartProcessInstanceComponent', () => { expect(fixture.componentInstance instanceof StartProcessInstanceComponent).toBe(true, 'should create StartProcessInstanceComponent'); }); describe('first step', () => { describe('without start form', () => { beforeEach(() => { component.name = 'My new process'; 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(() => { let startBtn = fixture.nativeElement.querySelector('#button-start'); expect(startBtn.disabled).toBe(true); }); })); it('should have start button disabled when no process is selected', async(() => { component.selectedProcessDef = null; fixture.detectChanges(); fixture.whenStable().then(() => { let startBtn = fixture.nativeElement.querySelector('#button-start'); expect(startBtn.disabled).toBe(true); }); })); }); describe('with start form', () => { beforeEach(() => { getDefinitionsSpy.and.returnValue(Observable.of(testProcessDefWithForm)); let change = new SimpleChange(null, 123, true); component.ngOnChanges({ 'appId': change }); }); it('should initialize start form', async(() => { fixture.detectChanges(); fixture.whenStable().then(() => { expect(component.startForm).toBeDefined(); expect(component.startForm).not.toBeNull(); }); })); 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(); fixture.whenStable().then(() => { expect(cancelSpy).toHaveBeenCalled(); }); })); }); describe('CS content connection', () => { it('alfrescoRepositoryName default configuration property', () => { expect(component.getAlfrescoRepositoryName()).toBe('alfresco-1Alfresco'); }); it('alfrescoRepositoryName configuration property should be fetched', () => { appConfig.config = Object.assign(appConfig.config, { 'alfrescoRepositoryName': 'alfresco-123' }; expect(component.getAlfrescoRepositoryName()).toBe('alfresco-123Alfresco'); }); it('if values in input is a node should be linked in the process service', async(() => { component.values = {}; component.values['file'] = { isFile: true, name= 'example-file' }; component.moveNodeFromCStoPS(); fixture.whenStable().then(() => { expect(component.values.file[0].id).toBe(1234); }); })); }); }); 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.processDefinitionName = '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); }); })); 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.processDefinitionName = '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.processDefinitionName = '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', async(() => { let startSpy: jasmine.Spy = spyOn(component.start, 'emit'); component.selectedProcessDef = testProcessDefRepr; component.name = 'my:Process'; component.startProcess(); fixture.detectChanges(); fixture.whenStable().then(() => { let startButton = fixture.nativeElement.querySelector('#button-start'); startButton.click(); 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); }); })); }); });