[AAE-8306] - Managed to disable complete/claim/release buttons on first click (#8166)

* AAE-8306: Managed to disable complete/save/claim buttons on user click

* AAE-8306: reverted back save button disable

* AAE-8306: Managed to disable unclaim or release button on user click

* AAE-8306: Fixed formatting issues

* AAE-8306: Fixed formatting issues
This commit is contained in:
Ehsan Rezaei
2023-01-26 10:29:19 +01:00
committed by GitHub
parent 588ffe0fa0
commit 2c248a611f
11 changed files with 146 additions and 31 deletions

View File

@@ -174,6 +174,8 @@ export abstract class FormBaseComponent {
}
if (outcome.id === FormBaseComponent.COMPLETE_OUTCOME_ID) {
this.disableSaveButton = true;
this.disableCompleteButton = true;
this.completeTaskForm();
return true;
}
@@ -201,6 +203,8 @@ export abstract class FormBaseComponent {
}
handleError(err: any): any {
this.disableSaveButton = false;
this.disableCompleteButton = false;
this.error.emit(err);
}

View File

@@ -75,7 +75,7 @@ describe('FormCloudComponent', () => {
selector: 'adf-cloud-custom-widget',
template: '<div></div>'
})
// eslint-disable-next-line @angular-eslint/component-class-suffix
// eslint-disable-next-line @angular-eslint/component-class-suffix
class CustomWidget {
typeId = 'CustomWidget';
}
@@ -1019,6 +1019,43 @@ describe('FormCloudComponent', () => {
expect(radioFieldById.value).toBe('option_2');
});
it('should disable complete & save buttons on [complete] outcome click', () => {
const formModel = new FormModel();
const outcome = new FormOutcomeModel(formModel, {
id: FormCloudComponent.COMPLETE_OUTCOME_ID,
name: 'COMPLETE',
isSystem: true
});
formComponent.form = formModel;
formComponent.onOutcomeClicked(outcome);
expect(formComponent.disableSaveButton).toBeTrue();
expect(formComponent.disableCompleteButton).toBeTrue();
});
it('should ENABLE complete & save buttons when something goes wrong during completion process', (done) => {
const errorMessage = 'Something went wrong.';
spyOn(formCloudService, 'completeTaskForm').and.callFake(() => throwError(errorMessage));
formCloudService.completeTaskForm('test-app', '123', '333-444', '123', {
pfx_property_one: 'testValue',
pfx_property_two: true,
pfx_property_three: 'opt_1',
pfx_property_four: 'option_2',
pfx_property_five: 'orange',
pfx_property_none: 'no_form_field'
}, 'Complete', 123).subscribe({
next: _ => done.fail('expected an error, not data'),
error: error => {
expect(error).toBe(errorMessage);
expect(formComponent.disableSaveButton).toBeFalse();
expect(formComponent.disableCompleteButton).toBeFalse();
done();
}
});
});
describe('form validations', () => {
it('should be able to set visibility conditions for Attach File widget', async () => {
spyOn(formCloudService, 'getForm').and.returnValue(of(conditionalUploadWidgetsMock));

View File

@@ -65,14 +65,14 @@ describe('ProcessFilterCloudService', () => {
getCurrentUserInfoSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
});
it('should create processfilter key by using appName and the username', async() => {
it('should create processfilter key by using appName and the username', async () => {
await service.getProcessFilters('mock-appName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(getCurrentUserInfoSpy).toHaveBeenCalled();
});
});
it('should create default process filters', async() => {
it('should create default process filters', async () => {
getPreferencesSpy.and.returnValue(of(fakeEmptyProcessCloudFilterEntries));
await service.getProcessFilters('mock-appName').subscribe((res: any) => {
expect(res).toBeDefined();
@@ -97,7 +97,7 @@ describe('ProcessFilterCloudService', () => {
expect(createPreferenceSpy).toHaveBeenCalled();
});
it('should fetch the process filters if filters are available', async() => {
it('should fetch the process filters if filters are available', async () => {
await service.getProcessFilters('mock-appName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -121,7 +121,7 @@ describe('ProcessFilterCloudService', () => {
expect(getPreferencesSpy).toHaveBeenCalled();
});
it('should create the process filters in case the filters are not exist in the user preferences', async() => {
it('should create the process filters in case the filters are not exist in the user preferences', async () => {
getPreferencesSpy.and.returnValue(of(fakeProcessCloudFilterWithDifferentEntries));
await service.getProcessFilters('mock-appName').subscribe((res: any) => {
expect(res).toBeDefined();
@@ -147,7 +147,7 @@ describe('ProcessFilterCloudService', () => {
expect(createPreferenceSpy).toHaveBeenCalled();
});
it('should return filter by process filter id', async() => {
it('should return filter by process filter id', async () => {
await service.getFilterById('mock-appName', '2').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -159,7 +159,7 @@ describe('ProcessFilterCloudService', () => {
expect(getPreferenceByKeySpy).toHaveBeenCalled();
});
it('should add process filter if filter is not exist in the filters', async() => {
it('should add process filter if filter is not exist in the filters', async () => {
getPreferenceByKeySpy.and.returnValue(of([]));
await service.getFilterById('mock-appName', '2').subscribe((res: any) => {
expect(res).toBeDefined();
@@ -171,7 +171,7 @@ describe('ProcessFilterCloudService', () => {
});
});
it('should update filter', async() => {
it('should update filter', async () => {
await service.updateFilter(fakeProcessFilter).subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -182,7 +182,7 @@ describe('ProcessFilterCloudService', () => {
});
});
it('should create process filter when trying to update in case filter is not exist in the filters', async() => {
it('should create process filter when trying to update in case filter is not exist in the filters', async () => {
getPreferenceByKeySpy.and.returnValue(of([]));
await service.updateFilter(fakeProcessFilter).subscribe((res: any) => {
expect(res).toBeDefined();
@@ -195,7 +195,7 @@ describe('ProcessFilterCloudService', () => {
expect(createPreferenceSpy).toHaveBeenCalled();
});
it('should delete filter', async() => {
it('should delete filter', async () => {
await service.deleteFilter(fakeProcessFilter).subscribe((res: any) => {
expect(res).toBeDefined();
});

View File

@@ -24,6 +24,7 @@ import { ClaimTaskCloudDirective } from './claim-task-cloud.directive';
import { taskClaimCloudMock } from '../task-header/mocks/fake-claim-task.mock';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
describe('ClaimTaskCloudDirective', () => {
@@ -80,6 +81,25 @@ describe('ClaimTaskCloudDirective', () => {
expect(taskCloudService.claimTask).toHaveBeenCalled();
expect(fixture.componentInstance.onError).toHaveBeenCalledWith(error);
});
it('should DISABLE the button on task completion', () => {
spyOn(taskCloudService, 'claimTask').and.returnValue(of(taskClaimCloudMock));
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.click();
expect(taskCloudService.claimTask).toHaveBeenCalled();
expect(button.disabled).toBe(true);
});
it('should ENABLE the button on api failure', () => {
spyOn(taskCloudService, 'claimTask').and.throwError('process key not found');
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.click();
expect(button.disabled).toBeFalsy();
});
});
describe('Claim Task Directive validation errors', () => {

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Directive, Input, HostListener, Output, EventEmitter, OnInit } from '@angular/core';
import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core';
import { IdentityUserService } from '../../people/services/identity-user.service';
import { TaskCloudService } from '../services/task-cloud.service';
@@ -44,6 +44,8 @@ export class ClaimTaskCloudDirective implements OnInit {
invalidParams: string[] = [];
constructor(
private readonly el: ElementRef,
private readonly renderer: Renderer2,
private taskListService: TaskCloudService,
private identityUserService: IdentityUserService) { }
@@ -85,11 +87,13 @@ export class ClaimTaskCloudDirective implements OnInit {
private async claimTask() {
const currentUser: string = this.identityUserService.getCurrentUserInfo().username;
try {
this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'true');
const result = await this.taskListService.claimTask(this.appName, this.taskId, currentUser).toPromise();
if (result) {
this.success.emit(result);
}
} catch (error) {
this.renderer.removeAttribute(this.el.nativeElement, 'disabled');
this.error.emit(error);
}
}

View File

@@ -24,6 +24,7 @@ import { taskCompleteCloudMock } from '../task-header/mocks/fake-complete-task.m
import { TaskCloudService } from '../services/task-cloud.service';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
describe('CompleteTaskDirective', () => {
@@ -84,6 +85,25 @@ describe('CompleteTaskDirective', () => {
expect(taskCloudService.completeTask).toHaveBeenCalled();
expect(fixture.componentInstance.onError).toHaveBeenCalledWith(error);
});
it('should DISABLE the button on task completion', () => {
spyOn(taskCloudService, 'completeTask').and.returnValue(of(taskCompleteCloudMock));
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.click();
expect(taskCloudService.completeTask).toHaveBeenCalled();
expect(button.disabled).toBe(true);
});
it('should ENABLE the button on api failure', () => {
spyOn(taskCloudService, 'completeTask').and.throwError('process key not found');
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.click();
expect(button.disabled).toBeFalsy();
});
});
describe('Complete Task Directive validation errors', () => {

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Directive, Input, HostListener, Output, EventEmitter, OnInit } from '@angular/core';
import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core';
import { TaskCloudService } from '../services/task-cloud.service';
@Directive({
@@ -42,7 +42,11 @@ export class CompleteTaskDirective implements OnInit {
invalidParams: string[] = [];
constructor(private taskListService: TaskCloudService) {}
constructor(
private readonly el: ElementRef,
private readonly renderer: Renderer2,
private readonly taskListService: TaskCloudService
) { }
ngOnInit() {
this.validateInputs();
@@ -72,11 +76,13 @@ export class CompleteTaskDirective implements OnInit {
@HostListener('click')
async onClick() {
try {
this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'true');
const result = await this.taskListService.completeTask(this.appName, this.taskId).toPromise();
if (result) {
this.success.emit(result);
}
} catch (error) {
this.renderer.removeAttribute(this.el.nativeElement, 'disabled');
this.error.emit(error);
}

View File

@@ -24,6 +24,7 @@ import { UnClaimTaskCloudDirective } from './unclaim-task-cloud.directive';
import { taskClaimCloudMock } from '../task-header/mocks/fake-claim-task.mock';
import { ProcessServiceCloudTestingModule } from '../../testing/process-service-cloud.testing.module';
import { TranslateModule } from '@ngx-translate/core';
import { By } from '@angular/platform-browser';
describe('UnClaimTaskCloudDirective', () => {
@@ -80,6 +81,25 @@ describe('UnClaimTaskCloudDirective', () => {
expect(taskCloudService.unclaimTask).toHaveBeenCalled();
expect(fixture.componentInstance.onError).toHaveBeenCalledWith(error);
});
it('should DISABLE the button on task completion', () => {
spyOn(taskCloudService, 'unclaimTask').and.returnValue(of(taskClaimCloudMock));
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.click();
expect(taskCloudService.unclaimTask).toHaveBeenCalled();
expect(button.disabled).toBe(true);
});
it('should ENABLE the button on api failure', () => {
spyOn(taskCloudService, 'unclaimTask').and.throwError('process key not found');
const button = fixture.debugElement.query(By.css('button')).nativeElement;
button.click();
expect(button.disabled).toBeFalsy();
});
});
describe('UnClaim Task Directive validation errors', () => {

View File

@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Directive, Input, HostListener, Output, EventEmitter, OnInit } from '@angular/core';
import { Directive, Input, HostListener, Output, EventEmitter, OnInit, ElementRef, Renderer2 } from '@angular/core';
import { TaskCloudService } from '../services/task-cloud.service';
@Directive({
@@ -43,6 +43,8 @@ export class UnClaimTaskCloudDirective implements OnInit {
invalidParams: string[] = [];
constructor(
private readonly el: ElementRef,
private readonly renderer: Renderer2,
private taskListService: TaskCloudService) { }
ngOnInit() {
@@ -73,9 +75,11 @@ export class UnClaimTaskCloudDirective implements OnInit {
@HostListener('click')
async onClick() {
try {
this.renderer.setAttribute(this.el.nativeElement, 'disabled', 'true');
await this.taskListService.unclaimTask(this.appName, this.taskId).toPromise();
this.success.emit(this.taskId);
} catch (error) {
this.renderer.removeAttribute(this.el.nativeElement, 'disabled');
this.error.emit(error);
}
}

View File

@@ -62,7 +62,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
it('should attach specific icon for each filter if hasIcon is true', async () => {
const change = new SimpleChange(undefined, 'my-app-1', true);
component.ngOnChanges({appName: change});
component.ngOnChanges({ appName: change });
fixture.detectChanges();
await fixture.whenStable();
@@ -84,7 +84,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
it('should not attach icons for each filter if hasIcon is false', async () => {
component.showIcons = false;
const change = new SimpleChange(undefined, 'my-app-1', true);
component.ngOnChanges({appName: change});
component.ngOnChanges({ appName: change });
fixture.detectChanges();
await fixture.whenStable();
@@ -96,7 +96,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
it('should display the filters', async () => {
const change = new SimpleChange(undefined, 'my-app-1', true);
component.ngOnChanges({appName: change});
component.ngOnChanges({ appName: change });
fixture.detectChanges();
await fixture.whenStable();
@@ -114,7 +114,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
expect(filters[2].nativeElement.innerText).toContain('FakeMyServiceTasks2');
});
it('should emit an error with a bad response', async() => {
it('should emit an error with a bad response', async () => {
const mockErrorFilterList = {
error: 'wrong request'
};
@@ -127,7 +127,7 @@ describe('ServiceTaskFiltersCloudComponent', () => {
expect(err).toBeDefined();
});
component.ngOnChanges({appName: change});
component.ngOnChanges({ appName: change });
fixture.detectChanges();
});

View File

@@ -74,14 +74,14 @@ describe('TaskFilterCloudService', () => {
getCurrentUserInfoSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
});
it('should create task filter key by using appName and the username', async() => {
it('should create task filter key by using appName and the username', async () => {
await service.getTaskListFilters('fakeAppName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(getCurrentUserInfoSpy).toHaveBeenCalled();
});
});
it('should create default task filters if there are no task filter preferences', async() => {
it('should create default task filters if there are no task filter preferences', async () => {
getPreferencesSpy.and.returnValue(of(fakeEmptyTaskCloudPreferenceList));
await service.getTaskListFilters('fakeAppName').subscribe((res: any) => {
expect(res).toBeDefined();
@@ -106,7 +106,7 @@ describe('TaskFilterCloudService', () => {
expect(createPreferenceSpy).toHaveBeenCalled();
});
it('should return the task filters if filters available', async() => {
it('should return the task filters if filters available', async () => {
await service.getTaskListFilters('fakeAppName').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -130,10 +130,10 @@ describe('TaskFilterCloudService', () => {
expect(getPreferencesSpy).toHaveBeenCalled();
});
it('should create the task filters if the user preference does not have task filters', async() => {
it('should create the task filters if the user preference does not have task filters', async () => {
getPreferencesSpy.and.returnValue(of(fakePreferenceWithNoTaskFilterPreference));
await service.getTaskListFilters('fakeAppName').subscribe( (res) => {
await service.getTaskListFilters('fakeAppName').subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.length).toBe(3);
@@ -156,7 +156,7 @@ describe('TaskFilterCloudService', () => {
});
it('should return filter by task filter id', async() => {
it('should return filter by task filter id', async () => {
await service.getTaskFilterById('fakeAppName', '2').subscribe((res: any) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
@@ -168,7 +168,7 @@ describe('TaskFilterCloudService', () => {
expect(getPreferenceByKeySpy).toHaveBeenCalled();
});
it('should add task filter if filter is not exist in the filters', async() => {
it('should add task filter if filter is not exist in the filters', async () => {
getPreferenceByKeySpy.and.returnValue(of([]));
await service.getTaskFilterById('fakeAppName', '2').subscribe((res: any) => {
expect(res).toBeDefined();
@@ -180,7 +180,7 @@ describe('TaskFilterCloudService', () => {
});
});
it('should update filter', async() => {
it('should update filter', async () => {
createPreferenceSpy.and.returnValue(of(fakeTaskCloudFilters));
await service.updateFilter(fakeTaskFilter).subscribe((res: any) => {
expect(res).toBeDefined();
@@ -192,7 +192,7 @@ describe('TaskFilterCloudService', () => {
});
});
it('should create task filter when trying to update in case filter is not exist in the filters', async() => {
it('should create task filter when trying to update in case filter is not exist in the filters', async () => {
getPreferenceByKeySpy.and.returnValue(of([]));
await service.updateFilter(fakeTaskFilter).subscribe((res: any) => {
expect(res).toBeDefined();
@@ -213,7 +213,7 @@ describe('TaskFilterCloudService', () => {
expect(service.isDefaultFilter(fakeFilterName)).toBe(false);
});
it('should return engine event task subscription', async() => {
it('should return engine event task subscription', async () => {
spyOn(notificationCloudService, 'makeGQLQuery').and.returnValue(of(taskCloudEngineEventsMock));
await service.getTaskNotificationSubscription('myAppName').subscribe((res: TaskCloudEngineEvent[]) => {
@@ -233,7 +233,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
const identityUserMock = { username: 'fakeusername', firstName: 'fake-identity-first-name', lastName: 'fake-identity-last-name', email: 'fakeIdentity@email.com' };
setupTestBed({
imports: [ HttpClientTestingModule, ApolloModule ],
imports: [HttpClientTestingModule, ApolloModule],
providers: [
{ provide: TASK_FILTERS_SERVICE_TOKEN, useClass: LocalPreferenceCloudService }
]
@@ -247,7 +247,7 @@ describe('Inject [LocalPreferenceCloudService] into the TaskFilterCloudService',
spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue(identityUserMock);
});
it('should create default task filters if there are no task filter preferences', async() => {
it('should create default task filters if there are no task filter preferences', async () => {
const appName = 'fakeAppName';
await service.getTaskListFilters(appName).subscribe((res: TaskFilterCloudModel[]) => {
expect(res.length).toEqual(3);