[AAE-2557] - fixed variable mapping on start process (#5680)

* [AAE-2557] - fixed variable mapping on start process

* [AAE-2557] - fix for randomly failing e2e

* run

* uopdate process

* fix unit

* increase timeout

* Update edit-process-filter-cloud-component.page.ts

* [AAE-2557] - removed trailing whitespaces

* create only if change processes

* [create preview]

* trigger build

* [AAE-2557] - correct all filter on process filter selection

Co-authored-by: Eugenio Romano <eugenio.romano@alfresco.com>
Co-authored-by: Eugenio Romano <eromano@users.noreply.github.com>
This commit is contained in:
Vito
2020-05-12 20:35:45 +01:00
committed by GitHub
parent 6793c5c466
commit f5fe125c9e
9 changed files with 135 additions and 74 deletions

View File

@@ -66,3 +66,4 @@ All components are supported in the following browsers:
* Due to a [known issue](https://bugzilla.mozilla.org/show_bug.cgi?id=1188880) in Firefox, the Alfresco Upload Component does not currently support folder upload functionality on Firefox.
See the [Browser Support](BROWSER-SUPPORT.md) article for more details.

View File

@@ -71,7 +71,6 @@ describe('Task cloud visibility', async () => {
await navigationBarPage.navigateToProcessServicesCloudPage();
await appListCloudComponent.checkApsContainer();
await appListCloudComponent.goToApp(simpleApp);
await tasksCloudDemoPage.taskListCloudComponent().checkTaskListIsLoaded();
});
it('[C315170] Should be able to complete a task with a form with required number widgets', async () => {

View File

@@ -66,9 +66,17 @@ export class ProcessListCloudService extends BaseCloudService {
}
}
if (!queryParam['status']) {
queryParam['status'] = this.buildFilterForAllStatus();
}
return queryParam;
}
private buildFilterForAllStatus() {
return ['RUNNING', 'SUSPENDED', 'CANCELLED', 'COMPLETED'];
}
private isExcludedField(property: string): boolean {
return property === 'appName' || property === 'sorting';
}

View File

@@ -78,7 +78,7 @@
<button mat-button (click)="cancelStartProcess()" id="cancel_process">
{{ 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.CANCEL' | translate | uppercase}}
</button>
<button color="primary" mat-button [disabled]="!currentCreatedProcess || !isProcessFormValid()" (click)="startProcess()"
<button color="primary" mat-button [disabled]="disableStartButton || !isProcessFormValid()" (click)="startProcess()"
data-automation-id="btn-start" id="button-start" class="btn-start">
{{'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.FORM.ACTION.START' | translate | uppercase}}
</button>

View File

@@ -17,7 +17,14 @@
import { SimpleChange, DebugElement } from '@angular/core';
import { async, ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing';
import { setupTestBed, StorageService, LogService, TranslationService, TranslationMock, FormService } from '@alfresco/adf-core';
import {
setupTestBed,
StorageService,
LogService,
TranslationService,
TranslationMock,
FormService
} from '@alfresco/adf-core';
import { of, throwError } from 'rxjs';
import { StartProcessCloudService } from '../services/start-process-cloud.service';
import { FormCloudService } from '../../../form/services/form-cloud.service';
@@ -25,13 +32,25 @@ import { StartProcessCloudComponent } from './start-process-cloud.component';
import { HttpClientModule } from '@angular/common/http';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { TranslateModule, TranslateStore } from '@ngx-translate/core';
import { MatCardModule, MatOptionModule, MatAutocompleteModule, MatIconModule, MatButtonModule, MatFormFieldModule, MatInputModule, MatRippleModule, MatCommonModule } from '@angular/material';
import {
MatCardModule,
MatOptionModule,
MatAutocompleteModule,
MatIconModule,
MatButtonModule,
MatFormFieldModule,
MatInputModule,
MatRippleModule,
MatCommonModule
} from '@angular/material';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { FormCloudModule } from '../../../form/form-cloud.module';
import { fakeProcessDefinitions, fakeStartForm, fakeStartFormNotValid,
import {
fakeProcessDefinitions, fakeStartForm, fakeStartFormNotValid,
fakeProcessInstance, fakeNoNameProcessDefinitions,
fakeSingleProcessDefinition, fakeCreatedProcessInstance } from '../mock/start-process.component.mock';
fakeSingleProcessDefinition, fakeCreatedProcessInstance
} from '../mock/start-process.component.mock';
import { By } from '@angular/platform-browser';
import { ProcessPayloadCloud } from '../models/process-payload-cloud.model';
@@ -52,14 +71,14 @@ describe('StartProcessCloudComponent', () => {
selectElement.click();
fixture.detectChanges();
const options: any = fixture.debugElement.queryAll(By.css('.mat-option-text'));
const currentOption = options.find( (option: DebugElement) => option.nativeElement.innerHTML.trim() === name );
const currentOption = options.find((option: DebugElement) => option.nativeElement.innerHTML.trim() === name);
if (currentOption) {
currentOption.nativeElement.click();
}
};
function typeValueInto(selector: any, value: string ) {
function typeValueInto(selector: any, value: string) {
const inputElement = fixture.debugElement.query(By.css(`${selector}`));
inputElement.nativeElement.value = value;
inputElement.nativeElement.dispatchEvent(new Event('input'));
@@ -113,28 +132,26 @@ describe('StartProcessCloudComponent', () => {
describe('start a process without start form', () => {
it('should create a process instance if the selection is valid', fakeAsync(() => {
component.name = '';
component.processDefinitionName = '';
component.name = 'testFormWithProcess';
component.processDefinitionName = 'processwithoutform2';
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
fixture.detectChanges();
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
const change = new SimpleChange(null, 'MyApp', true);
component.ngOnChanges({ 'appName': change });
fixture.detectChanges();
tick();
typeValueInto('#processName', 'OLE');
typeValueInto('#processDefinitionName', 'processwithoutform2');
fixture.detectChanges();
tick(450);
fixture.whenStable().then(() => {
fixture.detectChanges();
const firstNameEl = fixture.nativeElement.querySelector('#firstName');
expect(firstNameEl).toBeDefined();
const lastNameEl = fixture.nativeElement.querySelector('#lastName');
expect(lastNameEl).toBeDefined();
const startBtn = fixture.nativeElement.querySelector('#button-start');
expect(component.formCloud.isValid).toBe(true);
expect(startBtn.disabled).toBe(false);
expect(component.isProcessFormValid()).toBe(true);
expect(createProcessSpy).toHaveBeenCalledWith('MyApp', new ProcessPayloadCloud({name: 'OLE',
processDefinitionKey: fakeProcessDefinitions[1].key}));
expect(component.currentCreatedProcess.status).toBe('CREATED');
expect(component.currentCreatedProcess.startDate).toBeNull();
});
}));
@@ -272,7 +289,10 @@ describe('StartProcessCloudComponent', () => {
it('should be able to start a process with a prefilled valid form', fakeAsync(() => {
component.processDefinitionName = 'processwithform';
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
component.values = [{'name': 'firstName', 'value': 'FakeName'}, {'name': 'lastName', 'value': 'FakeLastName'}];
component.values = [{ 'name': 'firstName', 'value': 'FakeName' }, {
'name': 'lastName',
'value': 'FakeLastName'
}];
fixture.detectChanges();
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartForm));
@@ -303,7 +323,10 @@ describe('StartProcessCloudComponent', () => {
it('should NOT be able to start a process with a prefilled NOT valid form', async(() => {
component.processDefinitionName = 'processwithform';
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
component.values = [{'name': 'firstName', 'value': 'FakeName'}, {'name': 'lastName', 'value': 'FakeLastName'}];
component.values = [{ 'name': 'firstName', 'value': 'FakeName' }, {
'name': 'lastName',
'value': 'FakeLastName'
}];
fixture.detectChanges();
formDefinitionSpy = spyOn(formCloudService, 'getForm').and.returnValue(of(fakeStartFormNotValid));
@@ -326,7 +349,10 @@ describe('StartProcessCloudComponent', () => {
}));
it('should create a process instance if the selection is valid', fakeAsync(() => {
component.values = [{'name': 'firstName', 'value': 'FakeName'}, {'name': 'lastName', 'value': 'FakeLastName'}];
component.values = [{ 'name': 'firstName', 'value': 'FakeName' }, {
'name': 'lastName',
'value': 'FakeLastName'
}];
component.name = 'testFormWithProcess';
component.processDefinitionName = 'processwithoutform2';
getDefinitionsSpy.and.returnValue(of(fakeSingleProcessDefinition(component.processDefinitionName)));
@@ -344,8 +370,10 @@ describe('StartProcessCloudComponent', () => {
expect(startBtn.disabled).toBe(false);
expect(component.formCloud.isValid).toBe(true);
expect(component.isProcessFormValid()).toBe(true);
expect(createProcessSpy).toHaveBeenCalledWith('MyApp', new ProcessPayloadCloud({name: 'testFormWithProcess',
processDefinitionKey: fakeProcessDefinitions[1].key}));
expect(createProcessSpy).toHaveBeenCalledWith('MyApp', new ProcessPayloadCloud({
name: 'testFormWithProcess',
processDefinitionKey: fakeProcessDefinitions[1].key
}));
expect(component.currentCreatedProcess.status).toBe('CREATED');
expect(component.currentCreatedProcess.startDate).toBeNull();
});
@@ -602,13 +630,13 @@ describe('StartProcessCloudComponent', () => {
component.currentCreatedProcess = fakeProcessInstance;
component.startProcess();
fixture.whenStable().then(() => {
expect(startProcessSpy).toHaveBeenCalledWith(component.appName, fakeProcessInstance.id);
expect(startProcessSpy).toHaveBeenCalledWith(component.appName, fakeProcessInstance.id, component.processPayloadCloud);
});
}));
it('should call service to start process with the variables setted', async(() => {
const inputProcessVariable: Map<string, object>[] = [];
inputProcessVariable['name'] = {value: 'Josh'};
inputProcessVariable['name'] = { value: 'Josh' };
component.variables = inputProcessVariable;
component.currentCreatedProcess = fakeProcessInstance;

View File

@@ -28,7 +28,7 @@ import { MatAutocompleteTrigger } from '@angular/material';
import { ProcessPayloadCloud } from '../models/process-payload-cloud.model';
import { debounceTime, takeUntil, switchMap, filter, distinctUntilChanged, tap } from 'rxjs/operators';
import { ProcessDefinitionCloud } from '../models/process-definition-cloud.model';
import { Subject, Observable, concat } from 'rxjs';
import { Subject, Observable } from 'rxjs';
import { TaskVariableCloud } from '../../../form/models/task-variable-cloud.model';
@Component({
@@ -100,7 +100,9 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
isLoading = false;
isFormCloudLoaded = false;
formCloud: FormModel;
currentCreatedProcess: any;
currentCreatedProcess: ProcessInstanceCloud;
disableStartButton: boolean = true;
protected onDestroy$ = new Subject<boolean>();
constructor(private startProcessCloudService: StartProcessCloudService,
@@ -118,19 +120,20 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
.pipe(takeUntil(this.onDestroy$))
.subscribe((processDefinitionName) => {
this.selectProcessDefinitionByProcesDefinitionName(processDefinitionName);
});
});
this.processForm.valueChanges
.pipe(
tap(() => this.currentCreatedProcess = undefined),
debounceTime(400),
distinctUntilChanged(),
filter(() => this.isProcessSelectionValid()),
switchMap(() => this.generateProcessInstance())
).pipe(takeUntil(this.onDestroy$))
.subscribe((res) => {
this.currentCreatedProcess = res;
});
.pipe(
debounceTime(200),
tap(() => this.disableStartButton = true),
distinctUntilChanged(),
filter(() => this.isProcessSelectionValid()),
switchMap(() => this.generateProcessInstance())
).pipe(takeUntil(this.onDestroy$))
.subscribe((res) => {
this.currentCreatedProcess = res;
this.disableStartButton = false;
});
if (this.processDefinitionName) {
this.processDefinition.setValue(this.processDefinitionName);
@@ -167,12 +170,17 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
StartProcessCloudComponent.MAX_NAME_LENGTH : this.maxNameLength;
}
private generateProcessInstance(): Observable <ProcessInstanceCloud> {
private generateProcessInstance(): Observable<ProcessInstanceCloud> {
const createPayload: ProcessPayloadCloud = new ProcessPayloadCloud({
name: this.processInstanceName.value,
processDefinitionKey: this.processPayloadCloud.processDefinitionKey
});
return this.startProcessCloudService.createProcess(this.appName, createPayload);
if (this.currentCreatedProcess && this.processPayloadCloud.processDefinitionKey === this.currentCreatedProcess.processDefinitionKey) {
return this.startProcessCloudService.updateProcess(this.appName, this.currentCreatedProcess.id, createPayload);
} else {
return this.startProcessCloudService.createProcess(this.appName, createPayload);
}
}
private selectProcessDefinitionByProcesDefinitionName(processDefinitionName: string): void {
@@ -184,8 +192,8 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
}
setProcessDefinitionOnForm(selectedProcessDefinitionName: string) {
this.processDefinitionCurrent = this.filteredProcesses.find( (process: ProcessDefinitionCloud) =>
process.name === selectedProcessDefinitionName || process.key === selectedProcessDefinitionName );
this.processDefinitionCurrent = this.filteredProcesses.find((process: ProcessDefinitionCloud) =>
process.name === selectedProcessDefinitionName || process.key === selectedProcessDefinitionName);
this.isFormCloudLoaded = false;
this.processPayloadCloud.processDefinitionKey = this.processDefinitionCurrent.key;
@@ -198,7 +206,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
}
private getProcessIfExists(processDefinition: string): ProcessDefinitionCloud {
let matchedProcess = this.processDefinitionList.find((option) => this.getProcessDefinition(option, processDefinition) );
let matchedProcess = this.processDefinitionList.find((option) => this.getProcessDefinition(option, processDefinition));
if (!matchedProcess) {
matchedProcess = new ProcessDefinitionCloud();
}
@@ -227,14 +235,14 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
this.startProcessCloudService.getProcessDefinitions(this.appName)
.pipe(takeUntil(this.onDestroy$))
.subscribe((processDefinitionRepresentations: ProcessDefinitionCloud[]) => {
this.processDefinitionList = processDefinitionRepresentations;
if (processDefinitionRepresentations.length === 1) {
this.selectDefaultProcessDefinition();
}
},
() => {
this.errorMessageId = 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.LOAD_PROCESS_DEFS';
});
this.processDefinitionList = processDefinitionRepresentations;
if (processDefinitionRepresentations.length === 1) {
this.selectDefaultProcessDefinition();
}
},
() => {
this.errorMessageId = 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.LOAD_PROCESS_DEFS';
});
}
private isValidName(name: string): boolean {
@@ -255,7 +263,7 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
private getProcessDefinition(option: ProcessDefinitionCloud, processDefinition: string): boolean {
return (this.isValidName(option.name) && option.name.toLowerCase().includes(processDefinition.toLowerCase())) ||
(option.key && option.key.toLowerCase().includes(processDefinition.toLowerCase()));
(option.key && option.key.toLowerCase().includes(processDefinition.toLowerCase()));
}
isProcessDefinitionsEmpty(): boolean {
@@ -276,23 +284,27 @@ export class StartProcessCloudComponent implements OnChanges, OnInit, OnDestroy
startProcess() {
this.isLoading = true;
this.buildProcessCloudPayload();
concat(
this.startProcessCloudService.updateProcess(this.appName, this.currentCreatedProcess.id, this.processPayloadCloud),
this.startProcessCloudService.startCreatedProcess(this.appName, this.currentCreatedProcess.id)
).subscribe(
(res) => {
this.success.emit(res);
this.isLoading = false;
},
(err) => {
this.errorMessageId = 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.START';
this.error.emit(err);
this.isLoading = false;
}
);
this.startProcessCloudService.startCreatedProcess(this.appName,
this.currentCreatedProcess.id,
this.processPayloadCloud)
.subscribe(
(res) => {
this.success.emit(res);
this.isLoading = false;
},
(err) => {
this.errorMessageId = 'ADF_CLOUD_PROCESS_LIST.ADF_CLOUD_START_PROCESS.ERROR.START';
this.error.emit(err);
this.isLoading = false;
}
);
}
cancelStartProcess() {
async cancelStartProcess() {
if (this.currentCreatedProcess) {
await this.startProcessCloudService.deleteProcess(this.appName, this.currentCreatedProcess.id);
}
this.currentCreatedProcess = null;
this.cancel.emit();
}

View File

@@ -124,7 +124,7 @@ describe('StartProcessCloudService', () => {
it('should be able to start a created new process instance', (done) => {
spyOn(service, 'startCreatedProcess').and.returnValue(of({ id: 'fake-id', name: 'fake-name', status: 'RUNNING' }));
service.startCreatedProcess('appName1', 'fake-id')
service.startCreatedProcess('appName1', 'fake-id', fakeProcessPayload)
.subscribe(
(res: ProcessInstanceCloud) => {
expect(res).toBeDefined();

View File

@@ -63,7 +63,7 @@ export class StartProcessCloudService extends BaseCloudService {
*/
createProcess(appName: string, payload: ProcessPayloadCloud): Observable<ProcessInstanceCloud> {
const url = `${this.getBasePath(appName)}/rb/v1/process-instances/create`;
payload.payloadType = 'StartProcessPayload';
payload.payloadType = 'CreateProcessInstancePayload';
return this.post(url, payload).pipe(
map((result: any) => result.entry),
@@ -76,10 +76,10 @@ export class StartProcessCloudService extends BaseCloudService {
* @param createdProcessInstanceId process instance id of the process previously created
* @returns Details of the process instance just started
*/
startCreatedProcess(appName: string, createdProcessInstanceId: string): Observable<ProcessInstanceCloud> {
startCreatedProcess(appName: string, createdProcessInstanceId: string, payload: ProcessPayloadCloud): Observable<ProcessInstanceCloud> {
const url = `${this.getBasePath(appName)}/rb/v1/process-instances/${createdProcessInstanceId}/start`;
return this.post(url).pipe(
return this.post(url, payload).pipe(
map((result: any) => result.entry),
map(processInstance => new ProcessInstanceCloud(processInstance.entry))
);
@@ -112,7 +112,20 @@ export class StartProcessCloudService extends BaseCloudService {
payload.payloadType = 'UpdateProcessPayload';
return this.put(url, payload).pipe(
map(processInstance => new ProcessInstanceCloud(processInstance))
map((processInstance: any) => {
return new ProcessInstanceCloud(processInstance.entry);
})
);
}
/**
* Delete an existing process instance
* @param appName name of the Application
* @param processInstanceId process instance to update
*/
deleteProcess(appName: string, processInstanceId: string): Observable<void> {
const url = `${this.getBasePath(appName)}/rb/v1/process-instances/${processInstanceId}`;
return this.delete(url);
}
}

View File

@@ -87,7 +87,7 @@ export class StartProcessCloudPage {
}
async checkStartProcessButtonIsEnabled(): Promise<boolean> {
await browser.sleep(1000); // waiting for API response
await browser.sleep(2000); // waiting for API response
await BrowserVisibility.waitUntilElementIsVisible(this.startProcessButton);
return this.startProcessButton.isEnabled();
}