updated unit tests

This commit is contained in:
tomek.hanaj
2024-12-16 11:52:16 +01:00
parent b976ab2483
commit a8f13ef4b0
16 changed files with 713 additions and 332 deletions
@@ -18,21 +18,36 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { ScreenCloudComponent } from './screen-cloud.component';
import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ScreenRenderingService } from '../../../services/public-api';
import { By } from '@angular/platform-browser';
@Component({
selector: 'adf-cloud-test-component',
template: `<div class="adf-cloud-test-container">test component</div>`,
imports: [CommonModule],
standalone: true
})
class TestComponent {}
describe('ScreenCloudComponent', () => {
let component: ScreenCloudComponent;
let fixture: ComponentFixture<ScreenCloudComponent>;
let screenRenderingService: ScreenRenderingService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ScreenCloudComponent]
imports: [ScreenCloudComponent, TestComponent]
});
fixture = TestBed.createComponent(ScreenCloudComponent);
component = fixture.componentInstance;
screenRenderingService = TestBed.inject(ScreenRenderingService);
screenRenderingService.register({ ['test']: () => TestComponent });
fixture.componentRef.setInput('screenId', 'test');
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
it('should create custom component instance', () => {
const dynamicComponent = fixture.debugElement.query(By.css('.adf-cloud-test-container'));
expect(dynamicComponent).toBeTruthy();
});
});
@@ -15,8 +15,8 @@
* limitations under the License.
*/
// FormRenderingService,
import { DynamicComponentModel, ScreenRenderingService } from '@alfresco/adf-core';
import { DynamicComponentModel } from '@alfresco/adf-core';
import { ScreenRenderingService } from '../../../services/public-api';
import { CommonModule } from '@angular/common';
import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContainerRef } from '@angular/core';
@@ -24,8 +24,7 @@ import { Component, ComponentRef, inject, Input, OnInit, ViewChild, ViewContaine
selector: 'adf-cloud-screen-cloud',
standalone: true,
imports: [CommonModule],
templateUrl: './screen-cloud.component.html',
styleUrls: ['./screen-cloud.component.scss']
template: '<div #container></div>'
})
export class ScreenCloudComponent implements OnInit {
/** Task id to fetch corresponding form and values. */
@@ -33,19 +32,25 @@ export class ScreenCloudComponent implements OnInit {
/** App id to fetch corresponding form and values. */
@Input()
appName: string = '';
/** Screen id to fetch corresponding screen widget. */
@Input()
screenId: string = '';
/** Toggle readonly state of the task. */
@Input()
readOnly = false;
@ViewChild('container', { read: ViewContainerRef, static: true })
container: ViewContainerRef;
screenComponent: DynamicComponentModel = { type: 'screen-one' };
screenComponent: DynamicComponentModel;
componentRef: ComponentRef<any>;
private readonly screenRenderingService = inject(ScreenRenderingService);
ngOnInit() {
const componentType = this.screenRenderingService.resolveComponentType(this.screenComponent);
this.componentRef = this.container.createComponent(componentType);
if (this.screenId) {
this.screenComponent = { type: this.screenId };
const componentType = this.screenRenderingService.resolveComponentType(this.screenComponent);
this.componentRef = this.container.createComponent(componentType);
}
}
}
@@ -15,13 +15,14 @@
* limitations under the License.
*/
export * from './user-preference-cloud.service';
export * from './local-preference-cloud.service';
export * from './base-cloud.service';
export * from './cloud-token.service';
export * from './form-fields.interfaces';
export * from './local-preference-cloud.service';
export * from './notification-cloud.service';
export * from './preference-cloud.interface';
export * from './form-fields.interfaces';
export * from './base-cloud.service';
export * from './screen-rendering.service';
export * from './task-list-cloud.service.interface';
export * from './user-preference-cloud.service';
export * from './variable-mapper.sevice';
export * from './web-socket.service';
@@ -0,0 +1,32 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { TestBed } from '@angular/core/testing';
import { ScreenRenderingService } from './screen-rendering.service';
describe('ScreenRenderingService', () => {
let service: ScreenRenderingService;
beforeEach(() => {
TestBed.configureTestingModule({});
service = TestBed.inject(ScreenRenderingService);
});
it('should be created', () => {
expect(service).toBeTruthy();
});
});
@@ -0,0 +1,24 @@
/*!
* @license
* Copyright © 2005-2024 Hyland Software, Inc. and its affiliates. All rights reserved.
*
* 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 { Injectable } from '@angular/core';
import { DynamicComponentMapper } from '../../../../core/src/lib/common/services/dynamic-component-mapper.service';
@Injectable({
providedIn: 'root'
})
export class ScreenRenderingService extends DynamicComponentMapper {}
@@ -21,22 +21,17 @@
(displayModeOn)="onDisplayModeOn($event)"
(displayModeOff)="onDisplayModeOff($event)">
<adf-cloud-form-custom-outcomes>
<ng-template [ngTemplateOutlet]="taskFormCloudButtons">
</ng-template>
<adf-cloud-user-task-cloud-buttons
[appName]="appName"
[canClaimTask]="canClaimTask()"
[canUnclaimTask]="canUnclaimTask()"
[showCancelButton]="showCancelButton"
[taskId]="taskId"
(cancelClick)="onCancelClick()"
(claimTask)="onClaimTask()"
(unclaimTask)="onUnclaimTask()"
(error)="onError($event)">
</adf-cloud-user-task-cloud-buttons>
</adf-cloud-form-custom-outcomes>
</adf-cloud-form>
<ng-template #taskFormCloudButtons>
<adf-cloud-user-task-cloud-buttons
[appName]="appName"
[canClaimTask]="canClaimTask()"
[canUnclaimTask]="canUnclaimTask()"
[showCancelButton]="showCancelButton"
[taskId]="taskId"
(cancelClick)="onCancelClick()"
(claimTask)="onClaimTask()"
(unclaimTask)="onUnclaimTask()"
(error)="onError($event)">
</adf-cloud-user-task-cloud-buttons>
</ng-template>
</div>
@@ -15,32 +15,30 @@
* limitations under the License.
*/
import { DebugElement, SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import { of } from 'rxjs';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { FORM_FIELD_VALIDATORS, FormModel, FormOutcomeEvent, FormOutcomeModel } from '@alfresco/adf-core';
import { FormCustomOutcomesComponent } from '@alfresco/adf-process-services-cloud';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { of } from 'rxjs';
import { FormCloudComponent } from '../../../../form/components/form-cloud.component';
import { DisplayModeService } from '../../../../form/services/display-mode.service';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { ProcessServiceCloudTestingModule } from '../../../../testing/process-service-cloud.testing.module';
import { TaskFormCloudComponent } from './task-form-cloud.component';
import { TaskCloudService } from '../../../services/task-cloud.service';
import {
TaskDetailsCloudModel,
TASK_ASSIGNED_STATE,
TASK_CLAIM_PERMISSION,
TASK_CREATED_STATE,
TASK_RELEASE_PERMISSION,
TASK_VIEW_PERMISSION
TASK_VIEW_PERMISSION,
TaskDetailsCloudModel
} from '../../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { DisplayModeService } from '../../../../form/services/display-mode.service';
import { FormCloudComponent } from '../../../../form/components/form-cloud.component';
import { MockFormFieldValidator } from '../../mocks/task-form-cloud.mock';
import { UserTaskCloudButtonsComponent } from '../user-task-cloud-buttons/user-task-cloud-buttons.component';
import { TaskFormCloudComponent } from './task-form-cloud.component';
const taskDetails: TaskDetailsCloudModel = {
appName: 'simple-app',
appVersion: 1,
assignee: 'admin.adf',
completedDate: null,
createdDate: new Date(1555419255340),
@@ -55,21 +53,16 @@ const taskDetails: TaskDetailsCloudModel = {
};
describe('TaskFormCloudComponent', () => {
let loader: HarnessLoader;
let taskCloudService: TaskCloudService;
let identityUserService: IdentityUserService;
let getTaskSpy: jasmine.Spy;
let getCurrentUserSpy: jasmine.Spy;
let debugElement: DebugElement;
let component: TaskFormCloudComponent;
let fixture: ComponentFixture<TaskFormCloudComponent>;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [ProcessServiceCloudTestingModule],
declarations: [FormCloudComponent]
declarations: [FormCloudComponent, UserTaskCloudButtonsComponent, FormCustomOutcomesComponent]
});
taskDetails.status = TASK_ASSIGNED_STATE;
taskDetails.permissions = [TASK_VIEW_PERMISSION];
@@ -78,215 +71,127 @@ describe('TaskFormCloudComponent', () => {
identityUserService = TestBed.inject(IdentityUserService);
getCurrentUserSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({ username: 'admin.adf' });
taskCloudService = TestBed.inject(TaskCloudService);
getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(taskDetails));
spyOn(taskCloudService, 'getCandidateGroups').and.returnValue(of([]));
spyOn(taskCloudService, 'getCandidateUsers').and.returnValue(of([]));
fixture = TestBed.createComponent(TaskFormCloudComponent);
debugElement = fixture.debugElement;
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
});
afterEach(() => {
fixture.destroy();
});
describe('Complete button', () => {
beforeEach(() => {
component.taskId = 'task1';
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
});
it('should show complete button when status is ASSIGNED', () => {
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
expect(completeBtn.nativeElement).toBeDefined();
expect(completeBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE');
});
it('should not show complete button when status is ASSIGNED but assigned to a different person', () => {
getCurrentUserSpy.and.returnValue({});
fixture.detectChanges();
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
expect(completeBtn).toBeNull();
});
it('should not show complete button when showCompleteButton=false', () => {
component.showCompleteButton = false;
fixture.detectChanges();
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
expect(completeBtn).toBeNull();
});
});
describe('Claim/Unclaim buttons', () => {
beforeEach(() => {
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
getTaskSpy.and.returnValue(of(taskDetails));
fixture.componentRef.setInput('taskDetails', taskDetails);
component.taskId = 'task1';
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
component.showCancelButton = true;
fixture.detectChanges();
});
it('should not show release button for standalone task', () => {
taskDetails.permissions = [TASK_RELEASE_PERMISSION];
taskDetails.standalone = true;
getTaskSpy.and.returnValue(of(taskDetails));
fixture.detectChanges();
const canUnclaimTask = component.canUnclaimTask();
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
expect(unclaimBtn).toBeNull();
expect(canUnclaimTask).toBe(false);
});
it('should not show claim button for standalone task', () => {
taskDetails.status = TASK_CREATED_STATE;
taskDetails.permissions = [TASK_CLAIM_PERMISSION];
taskDetails.standalone = true;
getTaskSpy.and.returnValue(of(taskDetails));
fixture.detectChanges();
const canClaimTask = component.canClaimTask();
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
expect(claimBtn).toBeNull();
expect(canClaimTask).toBe(false);
});
it('should show release button when task is assigned to one of the candidate users', () => {
taskDetails.permissions = [TASK_RELEASE_PERMISSION];
fixture.detectChanges();
const canUnclaimTask = component.canUnclaimTask();
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
expect(unclaimBtn.nativeElement).toBeDefined();
expect(unclaimBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.UNCLAIM');
expect(canUnclaimTask).toBe(true);
});
it('should not show unclaim button when status is ASSIGNED but assigned to different person', () => {
getCurrentUserSpy.and.returnValue({});
fixture.detectChanges();
const canUnclaimTask = component.canUnclaimTask();
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
expect(unclaimBtn).toBeNull();
expect(canUnclaimTask).toBe(false);
});
it('should not show unclaim button when status is not ASSIGNED', () => {
taskDetails.status = undefined;
fixture.detectChanges();
const canUnclaimTask = component.canUnclaimTask();
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
expect(unclaimBtn).toBeNull();
expect(canUnclaimTask).toBe(false);
});
it('should not show unclaim button when status is ASSIGNED and permissions not include RELEASE', () => {
taskDetails.status = TASK_ASSIGNED_STATE;
taskDetails.permissions = [TASK_VIEW_PERMISSION];
fixture.detectChanges();
const canUnclaimTask = component.canUnclaimTask();
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
expect(unclaimBtn).toBeNull();
expect(canUnclaimTask).toBe(false);
});
it('should show claim button when status is CREATED and permission includes CLAIM', () => {
taskDetails.status = TASK_CREATED_STATE;
taskDetails.permissions = [TASK_CLAIM_PERMISSION];
fixture.detectChanges();
const canClaimTask = component.canClaimTask();
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
expect(claimBtn.nativeElement).toBeDefined();
expect(claimBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM');
expect(canClaimTask).toBe(true);
});
it('should not show claim button when status is not CREATED', () => {
taskDetails.status = undefined;
fixture.detectChanges();
const canClaimTask = component.canClaimTask();
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
expect(claimBtn).toBeNull();
expect(canClaimTask).toBe(false);
});
it('should not show claim button when status is CREATED and permission not includes CLAIM', () => {
taskDetails.status = TASK_CREATED_STATE;
taskDetails.permissions = [TASK_VIEW_PERMISSION];
fixture.detectChanges();
const canClaimTask = component.canClaimTask();
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
expect(claimBtn).toBeNull();
});
});
describe('Cancel button', () => {
it('should show cancel button by default', () => {
component.appName = 'app1';
component.taskId = 'task1';
fixture.detectChanges();
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
expect(cancelBtn.nativeElement).toBeDefined();
expect(cancelBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL');
});
it('should not show cancel button when showCancelButton=false', () => {
component.appName = 'app1';
component.taskId = 'task1';
component.showCancelButton = false;
fixture.detectChanges();
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
expect(cancelBtn).toBeNull();
expect(canClaimTask).toBe(false);
});
});
describe('Inputs', () => {
beforeEach(() => {
fixture.componentRef.setInput('taskDetails', taskDetails);
});
it('should not show complete/claim/unclaim buttons when readOnly=true', () => {
component.appName = 'app1';
component.taskId = 'task1';
component.readOnly = true;
fixture.detectChanges();
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
expect(completeBtn).toBeNull();
const canShowCompleteBtn = component.canCompleteTask();
expect(canShowCompleteBtn).toBe(false);
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
expect(claimBtn).toBeNull();
const canClaimTask = component.canClaimTask();
expect(canClaimTask).toBe(false);
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
expect(unclaimBtn).toBeNull();
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
expect(cancelBtn.nativeElement).toBeDefined();
expect(cancelBtn.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL');
});
it('should load data when appName changes', () => {
component.taskId = 'task1';
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
expect(getTaskSpy).toHaveBeenCalled();
});
it('should load data when taskId changes', () => {
component.appName = 'app1';
component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) });
expect(getTaskSpy).toHaveBeenCalled();
});
it('should not load data when appName changes and taskId is not defined', () => {
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
expect(getTaskSpy).not.toHaveBeenCalled();
});
it('should not load data when taskId changes and appName is not defined', () => {
component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) });
expect(getTaskSpy).not.toHaveBeenCalled();
const canUnclaimTask = component.canUnclaimTask();
expect(canUnclaimTask).toBe(false);
});
it('should append additional field validators to the default ones when provided', () => {
const mockFirstCustomFieldValidator = new MockFormFieldValidator();
const mockSecondCustomFieldValidator = new MockFormFieldValidator();
component.fieldValidators = [mockFirstCustomFieldValidator, mockSecondCustomFieldValidator];
fixture.componentRef.setInput('fieldValidators', [mockFirstCustomFieldValidator, mockSecondCustomFieldValidator]);
fixture.detectChanges();
expect(component.fieldValidators).toEqual([...FORM_FIELD_VALIDATORS, mockFirstCustomFieldValidator, mockSecondCustomFieldValidator]);
@@ -301,6 +206,7 @@ describe('TaskFormCloudComponent', () => {
describe('Events', () => {
beforeEach(() => {
fixture.componentRef.setInput('taskDetails', taskDetails);
component.appName = 'app1';
component.taskId = 'task1';
fixture.detectChanges();
@@ -308,53 +214,26 @@ describe('TaskFormCloudComponent', () => {
it('should emit cancelClick when cancel button is clicked', async () => {
spyOn(component.cancelClick, 'emit').and.stub();
component.onCancelClick();
fixture.detectChanges();
const cancelBtn = debugElement.query(By.css('#adf-cloud-cancel-task'));
cancelBtn.triggerEventHandler('click', {});
fixture.detectChanges();
await fixture.whenStable();
expect(component.cancelClick.emit).toHaveBeenCalledOnceWith('task1');
});
it('should emit taskCompleted when task is completed', async () => {
spyOn(taskCloudService, 'completeTask').and.returnValue(of({}));
spyOn(component.taskCompleted, 'emit').and.stub();
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
completeBtn.triggerEventHandler('click', {});
fixture.detectChanges();
await fixture.whenStable();
expect(component.taskCompleted.emit).toHaveBeenCalledOnceWith('task1');
});
it('should emit taskClaimed when task is claimed', async () => {
spyOn(taskCloudService, 'claimTask').and.returnValue(of({}));
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
spyOn(component.taskClaimed, 'emit').and.stub();
taskDetails.status = TASK_CREATED_STATE;
taskDetails.permissions = [TASK_CLAIM_PERMISSION];
getTaskSpy.and.returnValue(of(taskDetails));
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
component.onClaimTask();
fixture.detectChanges();
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
claimBtn.triggerEventHandler('click', {});
fixture.detectChanges();
await fixture.whenStable();
expect(component.taskClaimed.emit).toHaveBeenCalledOnceWith('task1');
});
it('should emit error when error occurs', async () => {
spyOn(component.error, 'emit').and.stub();
component.onError({});
fixture.detectChanges();
await fixture.whenStable();
@@ -362,88 +241,15 @@ describe('TaskFormCloudComponent', () => {
expect(component.error.emit).toHaveBeenCalled();
});
it('should reload when task is completed', async () => {
spyOn(taskCloudService, 'completeTask').and.returnValue(of({}));
const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough();
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const completeBtn = debugElement.query(By.css('[adf-cloud-complete-task]'));
completeBtn.nativeElement.click();
await fixture.whenStable();
expect(reloadSpy).toHaveBeenCalled();
});
it('should reload when task is claimed', async () => {
spyOn(taskCloudService, 'claimTask').and.returnValue(of({}));
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough();
taskDetails.permissions = [TASK_CLAIM_PERMISSION];
taskDetails.status = TASK_CREATED_STATE;
getTaskSpy.and.returnValue(of(taskDetails));
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const claimBtn = debugElement.query(By.css('[adf-cloud-claim-task]'));
claimBtn.nativeElement.click();
await fixture.whenStable();
expect(reloadSpy).toHaveBeenCalled();
});
it('should emit taskUnclaimed when task is unclaimed', async () => {
spyOn(taskCloudService, 'unclaimTask').and.returnValue(of({}));
const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough();
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
taskDetails.status = TASK_ASSIGNED_STATE;
taskDetails.permissions = [TASK_RELEASE_PERMISSION];
getTaskSpy.and.returnValue(of(taskDetails));
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const unclaimBtn = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
unclaimBtn.nativeElement.click();
await fixture.whenStable();
expect(reloadSpy).toHaveBeenCalled();
});
it('should show loading template while task data is being loaded', async () => {
component.loading = true;
fixture.detectChanges();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
});
it('should not show loading template while task data is not being loaded', async () => {
component.loading = false;
fixture.detectChanges();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
});
it('should emit an executeOutcome event when form outcome executed', () => {
const executeOutcomeSpy: jasmine.Spy = spyOn(component.executeOutcome, 'emit');
component.onFormExecuteOutcome(new FormOutcomeEvent(new FormOutcomeModel(new FormModel())));
expect(executeOutcomeSpy).toHaveBeenCalled();
});
it('should emit onTaskLoaded on initial load of component', () => {
component.appName = '';
spyOn(component.onTaskLoaded, 'emit');
component.ngOnInit();
fixture.detectChanges();
expect(component.onTaskLoaded.emit).toHaveBeenCalledWith(taskDetails);
});
it('should emit displayModeOn when display mode is turned on', async () => {
spyOn(component.displayModeOn, 'emit').and.stub();
component.onDisplayModeOn(DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0]);
fixture.detectChanges();
await fixture.whenStable();
@@ -453,7 +259,6 @@ describe('TaskFormCloudComponent', () => {
it('should emit displayModeOff when display mode is turned on', async () => {
spyOn(component.displayModeOff, 'emit').and.stub();
component.onDisplayModeOff(DisplayModeService.DEFAULT_DISPLAY_MODE_CONFIGURATIONS[0]);
fixture.detectChanges();
await fixture.whenStable();
@@ -462,45 +267,14 @@ describe('TaskFormCloudComponent', () => {
});
});
it('should display task name as title on no form template if showTitle is true', () => {
component.taskId = taskDetails.id;
fixture.detectChanges();
const noFormTemplateTitle = debugElement.query(By.css('.adf-form-title'));
expect(noFormTemplateTitle.nativeElement.innerText).toEqual('Task1');
});
it('should display default name as title on no form template if the task name empty/undefined', () => {
const mockTaskDetailsWithOutName = { id: 'mock-task-id', name: null, formKey: null };
getTaskSpy.and.returnValue(of(mockTaskDetailsWithOutName));
component.taskId = 'mock-task-id';
fixture.detectChanges();
const noFormTemplateTitle = debugElement.query(By.css('.adf-form-title'));
expect(noFormTemplateTitle.nativeElement.innerText).toEqual('FORM.FORM_RENDERER.NAMELESS_TASK');
});
it('should not display no form title if showTitle is set to false', () => {
component.taskId = taskDetails.id;
component.showTitle = false;
fixture.detectChanges();
const noFormTemplateTitle = debugElement.query(By.css('.adf-form-title'));
expect(noFormTemplateTitle).toBeNull();
});
it('should call children cloud task form change display mode when changing the display mode', () => {
const displayMode = 'displayMode';
component.taskDetails = { ...taskDetails, formKey: 'some-form' };
fixture.detectChanges();
expect(component.adfCloudForm).toBeDefined();
const switchToDisplayModeSpy = spyOn(component.adfCloudForm, 'switchToDisplayMode');
const switchToDisplayModeSpy = spyOn(component.adfCloudForm, 'switchToDisplayMode');
component.switchToDisplayMode(displayMode);
expect(switchToDisplayModeSpy).toHaveBeenCalledOnceWith(displayMode);
@@ -15,15 +15,15 @@
* limitations under the License.
*/
import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { ContentLinkModel, FORM_FIELD_VALIDATORS, FormFieldValidator, FormModel, FormOutcomeEvent, FormRenderingService } from '@alfresco/adf-core';
import { AttachFileCloudWidgetComponent } from '../../../../form/components/widgets/attach-file/attach-file-cloud-widget.component';
import { DropdownCloudWidgetComponent } from '../../../../form/components/widgets/dropdown/dropdown-cloud.widget';
import { DateCloudWidgetComponent } from '../../../../form/components/widgets/date/date-cloud.widget';
import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces';
import { Component, EventEmitter, Input, OnInit, Output, ViewChild, ViewEncapsulation } from '@angular/core';
import { FormCloudComponent } from '../../../../form/components/form-cloud.component';
import { AttachFileCloudWidgetComponent } from '../../../../form/components/widgets/attach-file/attach-file-cloud-widget.component';
import { DateCloudWidgetComponent } from '../../../../form/components/widgets/date/date-cloud.widget';
import { DropdownCloudWidgetComponent } from '../../../../form/components/widgets/dropdown/dropdown-cloud.widget';
import { FormCloudDisplayModeConfiguration } from '../../../../services/form-fields.interfaces';
import { TaskCloudService } from '../../../services/task-cloud.service';
import { TaskDetailsCloudModel } from '../../../start-task/models/task-details-cloud.model';
@Component({
selector: 'adf-cloud-task-form',
@@ -36,9 +36,11 @@ export class TaskFormCloudComponent implements OnInit {
@Input()
appName: string = '';
/**Candidates user and groups */
/**Candidates users*/
@Input()
candidateUsers: string[] = [];
/**Candidates groups */
@Input()
candidateGroups: string[] = [];
@@ -162,6 +164,10 @@ export class TaskFormCloudComponent implements OnInit {
return !this.readOnly && this.taskCloudService.canClaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups();
}
canUnclaimTask(): boolean {
return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups();
}
hasCandidateUsers(): boolean {
return this.candidateUsers.length !== 0;
}
@@ -174,10 +180,6 @@ export class TaskFormCloudComponent implements OnInit {
return this.hasCandidateUsers() || this.hasCandidateGroups();
}
canUnclaimTask(): boolean {
return !this.readOnly && this.taskCloudService.canUnclaimTask(this.taskDetails) && this.hasCandidateUsersOrGroups();
}
isReadOnly(): boolean {
return this.readOnly || !this.taskCloudService.canCompleteTask(this.taskDetails);
}
@@ -6,6 +6,7 @@
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate}}
</button>
<button
class="adf-user-task-cloud-claim-btn"
mat-button
*ngIf="canClaimTask"
adf-cloud-claim-task
@@ -16,6 +17,7 @@
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM' | translate}}
</button>
<button
class="adf-user-task-cloud-unclaim-btn"
mat-button
*ngIf="canUnclaimTask"
adf-cloud-unclaim-task
@@ -17,21 +17,114 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { UserTaskCloudButtonsComponent } from './user-task-cloud-buttons.component';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { HarnessLoader } from '@angular/cdk/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
import { NoopTranslateModule } from '@alfresco/adf-core';
import { By } from '@angular/platform-browser';
import { DebugElement } from '@angular/core';
import { ProcessServiceCloudTestingModule } from 'lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module';
import { TaskCloudService } from '@alfresco/adf-process-services-cloud';
import { of } from 'rxjs';
describe('UserTaskCloudButtonsComponent', () => {
let component: UserTaskCloudButtonsComponent;
let fixture: ComponentFixture<UserTaskCloudButtonsComponent>;
let loader: HarnessLoader;
let debugElement: DebugElement;
let taskCloudService: TaskCloudService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [UserTaskCloudButtonsComponent]
imports: [NoopTranslateModule, ProcessServiceCloudTestingModule],
declarations: [UserTaskCloudButtonsComponent]
});
fixture = TestBed.createComponent(UserTaskCloudButtonsComponent);
debugElement = fixture.debugElement;
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
taskCloudService = TestBed.inject(TaskCloudService);
fixture.componentRef.setInput('appName', 'app-test');
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
it('should show cancel button', async () => {
fixture.componentRef.setInput('showCancelButton', false);
let cancelButton: MatButtonHarness = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
expect(cancelButton).toBeNull();
fixture.componentRef.setInput('showCancelButton', true);
fixture.detectChanges();
cancelButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
expect(cancelButton).toBeTruthy();
});
it('should emit onCancelClick when cancel button clicked', async () => {
const cancelClickSpy = spyOn(component.cancelClick, 'emit');
fixture.componentRef.setInput('showCancelButton', true);
fixture.detectChanges();
const cancelButton: MatButtonHarness = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
await cancelButton.click();
expect(cancelClickSpy).toHaveBeenCalled();
});
it('should show claim button', async () => {
let claimButton: MatButtonHarness = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '.adf-user-task-cloud-claim-btn' }));
expect(claimButton).toBeNull();
fixture.componentRef.setInput('canClaimTask', true);
fixture.detectChanges();
claimButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '.adf-user-task-cloud-claim-btn' }));
expect(claimButton).toBeTruthy();
});
it('should emit claimTask when claim button clicked', async () => {
spyOn(taskCloudService, 'claimTask').and.returnValue(of({}));
fixture.componentRef.setInput('canClaimTask', true);
spyOn(component.claimTask, 'emit').and.stub();
fixture.detectChanges();
const claimButton = debugElement.query(By.css('[adf-cloud-claim-task]'));
expect(claimButton).toBeTruthy();
claimButton.triggerEventHandler('click', {});
fixture.detectChanges();
await fixture.whenStable();
expect(component.claimTask.emit).toHaveBeenCalled();
});
it('should show unclaim button', async () => {
let unclaimButton: MatButtonHarness = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '.adf-user-task-cloud-unclaim-btn' }));
expect(unclaimButton).toBeNull();
fixture.componentRef.setInput('canUnclaimTask', true);
fixture.detectChanges();
unclaimButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '.adf-user-task-cloud-unclaim-btn' }));
expect(unclaimButton).toBeTruthy();
});
it('should emit unclaim when button clicked', async () => {
spyOn(taskCloudService, 'unclaimTask').and.returnValue(of({}));
fixture.componentRef.setInput('canUnclaimTask', true);
spyOn(component.unclaimTask, 'emit').and.stub();
fixture.detectChanges();
const unclaimButton = debugElement.query(By.css('[adf-cloud-unclaim-task]'));
expect(unclaimButton).toBeTruthy();
unclaimButton.triggerEventHandler('click', {});
fixture.detectChanges();
await fixture.whenStable();
expect(component.unclaimTask.emit).toHaveBeenCalled();
});
});
@@ -19,8 +19,7 @@ import { Component, EventEmitter, Input, Output } from '@angular/core';
@Component({
selector: 'adf-cloud-user-task-cloud-buttons',
templateUrl: './user-task-cloud-buttons.component.html',
styleUrls: ['./user-task-cloud-buttons.component.scss']
templateUrl: './user-task-cloud-buttons.component.html'
})
export class UserTaskCloudButtonsComponent {
/** App id to fetch corresponding form and values. */
@@ -1,4 +1,4 @@
<div class="adf-task-form-cloud-container">
<div class="adf-user-task-cloud-container">
<div *ngIf="!loading; else loadingTemplate">
<ng-container [ngSwitch]="taskType">
<ng-container *ngSwitchCase="taskTypeEnum.Form">
@@ -24,7 +24,7 @@
</ng-container>
<ng-container *ngSwitchCase="taskTypeEnum.Screen">
<adf-cloud-screen-cloud [taskId]="taskId"></adf-cloud-screen-cloud>
<adf-cloud-screen-cloud [taskId]="taskId" [appName]="appName" [screenId]="screenId"></adf-cloud-screen-cloud>
</ng-container>
<ng-container *ngSwitchCase="taskTypeEnum.None">
@@ -57,7 +57,7 @@
(error)="onError($event)"
color="primary"
id="adf-form-complete"
>
>
{{'ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.COMPLETE' | translate}}
</button>
</mat-card-actions>
@@ -1,6 +1,9 @@
.adf-task-form-cloud-container {
width: 100%;
.adf-user-task-cloud-container {
height: 100%;
> div {
height: 100%;
}
}
.adf-user-task-cloud-spinner {
@@ -15,23 +15,457 @@
* limitations under the License.
*/
import { NoopTranslateModule } from '@alfresco/adf-core';
import {
TASK_ASSIGNED_STATE,
TASK_CLAIM_PERMISSION,
TASK_CREATED_STATE,
TASK_RELEASE_PERMISSION,
TASK_VIEW_PERMISSION,
TaskCloudService,
TaskDetailsCloudModel,
TaskFormCloudComponent
} from '@alfresco/adf-process-services-cloud';
import { HarnessLoader } from '@angular/cdk/testing';
import { TestbedHarnessEnvironment } from '@angular/cdk/testing/testbed';
import { SimpleChange } from '@angular/core';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { MatButtonHarness } from '@angular/material/button/testing';
import { MatCardHarness } from '@angular/material/card/testing';
import { MatProgressSpinnerHarness } from '@angular/material/progress-spinner/testing';
import { ProcessServiceCloudTestingModule } from 'lib/process-services-cloud/src/lib/testing/process-service-cloud.testing.module';
import { of } from 'rxjs';
import { IdentityUserService } from '../../../../people/services/identity-user.service';
import { UserTaskCloudComponent } from './user-task-cloud.component';
const taskDetails: TaskDetailsCloudModel = {
appName: 'simple-app',
assignee: 'admin.adf',
completedDate: null,
createdDate: new Date(1555419255340),
description: null,
formKey: null,
id: 'bd6b1741-6046-11e9-80f0-0a586460040d',
name: 'Task1',
owner: 'admin.adf',
standalone: false,
status: TASK_ASSIGNED_STATE,
permissions: [TASK_VIEW_PERMISSION]
};
describe('UserTaskCloudComponent', () => {
let component: UserTaskCloudComponent;
let fixture: ComponentFixture<UserTaskCloudComponent>;
let taskCloudService: TaskCloudService;
let getTaskSpy: jasmine.Spy;
let getCurrentUserSpy: jasmine.Spy;
let loader: HarnessLoader;
let identityUserService: IdentityUserService;
beforeEach(() => {
TestBed.configureTestingModule({
imports: [UserTaskCloudComponent]
imports: [NoopTranslateModule, ProcessServiceCloudTestingModule],
declarations: [UserTaskCloudComponent, TaskFormCloudComponent]
});
fixture = TestBed.createComponent(UserTaskCloudComponent);
component = fixture.componentInstance;
loader = TestbedHarnessEnvironment.loader(fixture);
taskCloudService = TestBed.inject(TaskCloudService);
identityUserService = TestBed.inject(IdentityUserService);
getTaskSpy = spyOn(taskCloudService, 'getTaskById').and.returnValue(of(taskDetails));
getCurrentUserSpy = spyOn(identityUserService, 'getCurrentUserInfo').and.returnValue({ username: 'admin.adf' });
spyOn(taskCloudService, 'getCandidateGroups').and.returnValue(of([]));
spyOn(taskCloudService, 'getCandidateUsers').and.returnValue(of([]));
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
describe('Complete button', () => {
beforeEach(() => {
fixture.componentRef.setInput('showCompleteButton', true);
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'task1');
getTaskSpy.and.returnValue(of({ ...taskDetails }));
fixture.detectChanges();
fixture.whenStable();
});
it('should show complete button when status is ASSIGNED', async () => {
const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' }));
expect(completeButton).not.toBeNull();
});
it('should not show complete button when status is ASSIGNED but assigned to a different person', async () => {
getCurrentUserSpy.and.returnValue({});
fixture.detectChanges();
const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' }));
expect(completeButton).toBeNull();
});
it('should not show complete button when showCompleteButton=false', async () => {
fixture.componentRef.setInput('showCompleteButton', false);
fixture.detectChanges();
const completeButton = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-form-complete' }));
expect(completeButton).toBeNull();
});
});
describe('Claim/Unclaim buttons', () => {
beforeEach(() => {
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
component.taskDetails = taskDetails;
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'task1');
getTaskSpy.and.returnValue(of(taskDetails));
fixture.detectChanges();
});
it('should not show release button for standalone task', async () => {
component.taskDetails.permissions = [TASK_RELEASE_PERMISSION];
component.taskDetails.standalone = true;
fixture.detectChanges();
const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' }));
expect(unclaimBtn).toBeNull();
});
it('should not show claim button for standalone task', async () => {
component.taskDetails.status = TASK_CREATED_STATE;
component.taskDetails.permissions = [TASK_CLAIM_PERMISSION];
component.taskDetails.standalone = true;
fixture.detectChanges();
const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' }));
expect(claimBtn).toBeNull();
});
it('should show release button when task is assigned to one of the candidate users', async () => {
component.taskDetails = { ...taskDetails, standalone: false, status: TASK_ASSIGNED_STATE, permissions: [TASK_RELEASE_PERMISSION] };
fixture.detectChanges();
const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' }));
expect(unclaimBtn).not.toBeNull();
const unclaimBtnLabel = await unclaimBtn.getText();
expect(unclaimBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.UNCLAIM');
});
it('should not show unclaim button when status is ASSIGNED but assigned to different person', async () => {
getCurrentUserSpy.and.returnValue({});
fixture.detectChanges();
const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' }));
expect(unclaimBtn).toBeNull();
});
it('should not show unclaim button when status is not ASSIGNED', async () => {
component.taskDetails.status = undefined;
fixture.detectChanges();
const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' }));
expect(unclaimBtn).toBeNull();
});
it('should not show unclaim button when status is ASSIGNED and permissions not include RELEASE', async () => {
component.taskDetails.status = TASK_ASSIGNED_STATE;
component.taskDetails.permissions = [TASK_VIEW_PERMISSION];
fixture.detectChanges();
const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' }));
expect(unclaimBtn).toBeNull();
});
it('should show claim button when status is CREATED and permission includes CLAIM', async () => {
component.taskDetails.standalone = false;
component.taskDetails.status = TASK_CREATED_STATE;
component.taskDetails.permissions = [TASK_CLAIM_PERMISSION];
fixture.detectChanges();
const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' }));
expect(claimBtn).not.toBeNull();
const claimBtnLabel = await claimBtn.getText();
expect(claimBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CLAIM');
});
it('should not show claim button when status is not CREATED', async () => {
component.taskDetails.status = undefined;
fixture.detectChanges();
const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' }));
expect(claimBtn).toBeNull();
});
it('should not show claim button when status is CREATED and permission not includes CLAIM', async () => {
component.taskDetails.status = TASK_CREATED_STATE;
component.taskDetails.permissions = [TASK_VIEW_PERMISSION];
fixture.detectChanges();
const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' }));
expect(claimBtn).toBeNull();
});
});
describe('Cancel button', () => {
beforeEach(() => {
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'task1');
fixture.detectChanges();
});
it('should show cancel button by default', async () => {
const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
expect(cancelBtn).toBeDefined();
const cancelBtnLabel = await cancelBtn.getText();
expect(cancelBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL');
});
it('should not show cancel button when showCancelButton=false', async () => {
fixture.componentRef.setInput('showCancelButton', false);
fixture.detectChanges();
const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
expect(cancelBtn).toBeNull();
});
});
describe('Inputs', () => {
it('should not show complete/claim/unclaim buttons when readOnly=true', async () => {
getTaskSpy.and.returnValue(of(taskDetails));
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'task1');
fixture.componentRef.setInput('readOnly', true);
fixture.componentRef.setInput('showCancelButton', true);
component.getTaskType();
fixture.detectChanges();
await fixture.whenStable();
const completeBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-complete-task]' }));
expect(completeBtn).toBeNull();
const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' }));
expect(claimBtn).toBeNull();
const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' }));
expect(unclaimBtn).toBeNull();
const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
expect(cancelBtn).toBeDefined();
const cancelBtnLabel = await cancelBtn.getText();
expect(cancelBtnLabel).toEqual('ADF_CLOUD_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL');
});
it('should load data when appName changes', () => {
component.taskId = 'task1';
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
expect(getTaskSpy).toHaveBeenCalled();
});
it('should load data when taskId changes', () => {
component.appName = 'app1';
component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) });
expect(getTaskSpy).toHaveBeenCalled();
});
it('should not load data when appName changes and taskId is not defined', async () => {
fixture.componentRef.setInput('taskId', null);
fixture.detectChanges();
expect(component.taskId).toBeNull();
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
await fixture.whenStable();
expect(getTaskSpy).not.toHaveBeenCalled();
});
it('should not load data when taskId changes and appName is not defined', async () => {
component.ngOnChanges({ taskId: new SimpleChange(null, 'task1', false) });
expect(getTaskSpy).not.toHaveBeenCalled();
});
});
describe('Events', () => {
beforeEach(() => {
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'task1');
fixture.componentRef.setInput('showCancelButton', true);
fixture.detectChanges();
});
it('should emit cancelClick when cancel button is clicked', async () => {
spyOn(component.cancelClick, 'emit').and.stub();
fixture.detectChanges();
const cancelBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '#adf-cloud-cancel-task' }));
await cancelBtn.click();
fixture.detectChanges();
await fixture.whenStable();
expect(component.cancelClick.emit).toHaveBeenCalledOnceWith('task1');
});
it('should emit taskCompleted when task is completed', async () => {
component.taskDetails.status = TASK_ASSIGNED_STATE;
spyOn(taskCloudService, 'completeTask').and.returnValue(of({}));
spyOn(component.taskCompleted, 'emit').and.stub();
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const completeBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-complete-task]' }));
await completeBtn.click();
fixture.detectChanges();
await fixture.whenStable();
expect(component.taskCompleted.emit).toHaveBeenCalledOnceWith('task1');
});
it('should emit taskClaimed when task is claimed', async () => {
spyOn(taskCloudService, 'claimTask').and.returnValue(of({}));
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
spyOn(component.taskClaimed, 'emit').and.stub();
taskDetails.status = TASK_CREATED_STATE;
taskDetails.permissions = [TASK_CLAIM_PERMISSION];
getTaskSpy.and.returnValue(of(taskDetails));
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' }));
await claimBtn.click();
fixture.detectChanges();
await fixture.whenStable();
expect(component.taskClaimed.emit).toHaveBeenCalledOnceWith('task1');
});
it('should emit error when error occurs', async () => {
spyOn(component.error, 'emit').and.stub();
component.onError({});
fixture.detectChanges();
await fixture.whenStable();
expect(component.error.emit).toHaveBeenCalled();
});
it('should reload when task is completed', async () => {
spyOn(taskCloudService, 'completeTask').and.returnValue(of({}));
const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough();
component.taskDetails.status = TASK_ASSIGNED_STATE;
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
await fixture.whenStable();
const completeBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-complete-task]' }));
await completeBtn.click();
await fixture.whenStable();
expect(reloadSpy).toHaveBeenCalled();
});
it('should reload when task is claimed', async () => {
spyOn(taskCloudService, 'claimTask').and.returnValue(of({}));
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough();
taskDetails.permissions = [TASK_CLAIM_PERMISSION];
taskDetails.status = TASK_CREATED_STATE;
getTaskSpy.and.returnValue(of(taskDetails));
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const claimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-claim-task]' }));
await claimBtn.click();
await fixture.whenStable();
expect(reloadSpy).toHaveBeenCalled();
});
it('should emit taskUnclaimed when task is unclaimed', async () => {
spyOn(taskCloudService, 'unclaimTask').and.returnValue(of({}));
const reloadSpy = spyOn(component, 'ngOnChanges').and.callThrough();
spyOn(component, 'hasCandidateUsers').and.returnValue(true);
taskDetails.status = TASK_ASSIGNED_STATE;
taskDetails.permissions = [TASK_RELEASE_PERMISSION];
getTaskSpy.and.returnValue(of(taskDetails));
component.ngOnChanges({ appName: new SimpleChange(null, 'app1', false) });
fixture.detectChanges();
const unclaimBtn = await loader.getHarnessOrNull(MatButtonHarness.with({ selector: '[adf-cloud-unclaim-task]' }));
await unclaimBtn.click();
await fixture.whenStable();
expect(reloadSpy).toHaveBeenCalled();
});
it('should show loading template while task data is being loaded', async () => {
component.loading = true;
fixture.detectChanges();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(true);
});
it('should not show loading template while task data is not being loaded', async () => {
component.loading = false;
fixture.detectChanges();
expect(await loader.hasHarness(MatProgressSpinnerHarness)).toBe(false);
});
it('should emit onTaskLoaded on initial load of component', () => {
component.appName = '';
spyOn(component.onTaskLoaded, 'emit');
component.ngOnInit();
fixture.detectChanges();
expect(component.onTaskLoaded.emit).toHaveBeenCalledWith(taskDetails);
});
});
it('should display task name as title on no form template if showTitle is true', async () => {
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'task1');
component.taskDetails = { ...taskDetails };
fixture.detectChanges();
const noFormTemplateTitle = await loader.getHarnessOrNull(MatCardHarness);
const noFormTemplateTitleText = await noFormTemplateTitle.getTitleText();
expect(noFormTemplateTitleText).toEqual('Task1');
});
it('should display default name as title on no form template if the task name empty/undefined', async () => {
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'mock-task-id');
const mockTaskDetailsWithOutName = { id: 'mock-task-id', name: null, formKey: null };
getTaskSpy.and.returnValue(of(mockTaskDetailsWithOutName));
fixture.detectChanges();
const matCard = await loader.getHarnessOrNull(MatCardHarness);
const noFormTemplateTitle = await matCard.getTitleText();
expect(noFormTemplateTitle).toEqual('FORM.FORM_RENDERER.NAMELESS_TASK');
});
it('should not display no form title if showTitle is set to false', async () => {
fixture.componentRef.setInput('appName', 'app1');
fixture.componentRef.setInput('taskId', 'task1');
fixture.componentRef.setInput('showTitle', false);
component.showTitle = false;
fixture.detectChanges();
const matCard = await loader.getHarnessOrNull(MatCardHarness);
expect(matCard).toBeDefined();
const noFormTemplateTitleText = await matCard.getTitleText();
expect(noFormTemplateTitleText).toBe('');
});
});
@@ -120,6 +120,7 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
candidateUsers: string[] = [];
candidateGroups: string[] = [];
loading: boolean = false;
screenId: string;
taskDetails: TaskDetailsCloudModel;
taskType: TaskTypesType;
taskTypeEnum = TaskTypes;
@@ -144,6 +145,8 @@ export class UserTaskCloudComponent implements OnInit, OnChanges {
this.taskType = this.taskTypeEnum.Form;
} else if (this.taskDetails && !!this.taskDetails.formKey && this.taskDetails.formKey.includes(this.taskTypeEnum.Screen)) {
this.taskType = this.taskTypeEnum.Screen;
const screenId = this.taskDetails.formKey.replace(this.taskTypeEnum.Screen + '-', '');
this.screenId = screenId;
} else {
this.taskType = this.taskTypeEnum.None;
}
@@ -20,7 +20,6 @@ import { CommonModule } from '@angular/common';
import { MaterialModule } from '../../material.module';
import { FormCloudModule } from '../../form/form-cloud.module';
import { TaskDirectiveModule } from '../directives/task-directive.module';
import { TaskFormCloudComponent } from './components/task-form-cloud/task-form-cloud.component';
import { CoreModule } from '@alfresco/adf-core';
import { ScreenCloudComponent } from '../../screen/components/screen-cloud/screen-cloud.component';