mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-05-12 17:04:57 +00:00
[ACA-3416] Add Claim/Release actions on adf task form (#5753)
* [ACA-3255] FE - Claim a task * * Added unit tests * * Added unit tests * Changed cloud directive names * * Added/Updated documents * * Added showReleaseClaim button flag * Add unit test too * * Used claim/release directive in task-header component. * * Fixed unit test * * Fixed one comment * * After rebase * * Fixed comments
This commit is contained in:
parent
77bbecea8e
commit
ea62b1e3bd
BIN
docs/docassets/images/form-custom-outcomes.component.png
Normal file
BIN
docs/docassets/images/form-custom-outcomes.component.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -1,18 +1,18 @@
|
||||
---
|
||||
Title: Claim Task Directive
|
||||
Added: v3.1.0
|
||||
Title: Claim Task Cloud Directive
|
||||
Added: v3.9.0
|
||||
Status: Experimental
|
||||
Last reviewed: 2019-03-25
|
||||
Last reviewed: 2020-06-09
|
||||
---
|
||||
|
||||
# [Claim task directive](../../../lib/process-services-cloud/src/lib/task/directives/claim-task.directive.ts "Defined in claim-task.directive.ts")
|
||||
# [Claim task Cloud directive](../../../lib/process-services-cloud/src/lib/task/directives/claim-task-cloud.directive.ts "Defined in claim-task-cloud.directive.ts")
|
||||
|
||||
Claims a task
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<button adf-claim-task [appName]="appName" [taskId]="taskId" (success)="onTaskClaimed()">Complete</button>
|
||||
<button adf-cloud-claim-task [appName]="appName" [taskId]="taskId" (success)="onTaskClaimed()">Claim</button>
|
||||
```
|
||||
|
||||
## Class members
|
||||
@ -22,7 +22,7 @@ Claims a task
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| appName | `string` | "" | (Required) The name of the application. |
|
||||
| taskId | `string` | | (Required) The id of the task. |
|
||||
| taskId | `string` | "" | (Required) The id of the task. |
|
||||
|
||||
### Events
|
||||
|
@ -0,0 +1,32 @@
|
||||
---
|
||||
Title: Unclaim Task Cloud Directive
|
||||
Added: v3.9.0
|
||||
Status: Experimental
|
||||
Last reviewed: 2020-06-09
|
||||
---
|
||||
|
||||
# [Unclaim Task Cloud directive](../../../lib/process-services-cloud/src/lib/task/directives/unclaim-task-cloud.directive.ts "Defined in unclaim-task-cloud.directive.ts")
|
||||
|
||||
Unclaims a task
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<button adf-cloud-unclaim-task [appName]="appName" [taskId]="taskId" (success)="onTaskUnclaimed()">Unclaim</button>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| appName | `string` | "" | (Required) The name of the application. |
|
||||
| taskId | `string` | "" | (Required) The id of the task. |
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task cannot be completed. |
|
||||
| success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task is completed. |
|
@ -0,0 +1,34 @@
|
||||
---
|
||||
Title: Form custom outcomes component
|
||||
Added: v3.9.0
|
||||
Status: Active
|
||||
Last reviewed: 2020-06-09
|
||||
---
|
||||
|
||||
# [Form custom outcomes component](../../../lib/process-services/src/lib/form/form-custom-outcomes.component.ts "Defined in form-custom-outcomes.component.ts")
|
||||
|
||||
Supplies custom outcome buttons to be included in [Form component](form.component.md).
|
||||
|
||||

|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<adf-form>
|
||||
<adf-form-custom-outcomes>
|
||||
<button mat-button (click)="onCustomOutcome1()">
|
||||
Custom-outcome-1
|
||||
</button>
|
||||
<button mat-button (click)="onCustomOutcome2()">
|
||||
Custom-outcome-2
|
||||
</button>
|
||||
<button mat-button (click)="onCustomOutcome3()">
|
||||
Custom-outcome-3
|
||||
</button>
|
||||
</adf-form-custom-outcomes>
|
||||
</adf-form>
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [Form component](form.component.md)
|
@ -47,6 +47,8 @@ Shows a [`form`](../../../lib/process-services/src/lib/task-list/models/form.mod
|
||||
| formLoaded | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`FormModel`](../../../lib/core/form/components/widgets/core/form.model.ts)`>` | Emitted when the form is loaded or reloaded. |
|
||||
| formSaved | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<`[`FormModel`](../../../lib/core/form/components/widgets/core/form.model.ts)`>` | Emitted when the form is submitted with the `Save` or custom outcomes. |
|
||||
| showAttachForm | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<void>` | Emitted when the form associated with the form task is attached. |
|
||||
| taskClaimed | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when the task is claimed. |
|
||||
| taskUnclaimed | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when the task is unclaimed (ie, requeued). |
|
||||
|
||||
## See also
|
||||
|
||||
|
@ -27,6 +27,7 @@ Shows all the information related to a task.
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| formName | `string` | null | The name of the form. |
|
||||
| taskDetails | [`TaskDetailsModel`](../../../lib/process-services/src/lib/task-list/models/task-details.model.ts) | | (required) Details related to the task. |
|
||||
| showClaimRelease | `boolean` | true | Toggles display of the claim/release button. |
|
||||
|
||||
### Events
|
||||
|
||||
|
31
docs/process-services/directives/claim-task.directive.md
Normal file
31
docs/process-services/directives/claim-task.directive.md
Normal file
@ -0,0 +1,31 @@
|
||||
---
|
||||
Title: Claim Task Directive
|
||||
Added: v3.9.0
|
||||
Status: Experimental
|
||||
Last reviewed: 2020-06-09
|
||||
---
|
||||
|
||||
# [Claim task directive](../../../lib/process-services/src/lib/task-list/components/task-form/claim-task.directive.ts "Defined in claim-task.directive.ts")
|
||||
|
||||
Claims a task
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<button adf-claim-task [taskId]="taskId" (success)="onTaskClaimed()">Claim</button>
|
||||
```
|
||||
|
||||
## Class members
|
||||
|
||||
### Properties
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| taskId | `string` | "" | (Required) The id of the task. |
|
||||
|
||||
### Events
|
||||
|
||||
| Name | Type | Description |
|
||||
| ---- | ---- | ----------- |
|
||||
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task cannot be completed. |
|
||||
| success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the task is completed. |
|
@ -1,18 +1,18 @@
|
||||
---
|
||||
Title: Unclaim Task Directive
|
||||
Added: v3.1.0
|
||||
Added: v3.9.0
|
||||
Status: Experimental
|
||||
Last reviewed: 2019-03-25
|
||||
Last reviewed: 2020-06-09
|
||||
---
|
||||
|
||||
# [Unclaim task directive](../../../lib/process-services-cloud/src/lib/task/directives/unclaim-task.directive.ts "Defined in unclaim-task.directive.ts")
|
||||
# [Unclaim Task directive](../../../lib/process-services/src/lib/task-list/components/task-form/unclaim-task.directive.ts "Defined in unclaim-task.directive.ts")
|
||||
|
||||
Unclaims a task
|
||||
|
||||
## Basic Usage
|
||||
|
||||
```html
|
||||
<button adf-unclaim-task [appName]="appName" [taskId]="taskId" (success)="onTaskUnclaimed()">Complete</button>
|
||||
<button adf-unclaim-task [appName]="appName" [taskId]="taskId" (success)="onTaskUnclaimed()">Unclaim</button>
|
||||
```
|
||||
|
||||
## Class members
|
||||
@ -21,8 +21,7 @@ Unclaims a task
|
||||
|
||||
| Name | Type | Default value | Description |
|
||||
| ---- | ---- | ------------- | ----------- |
|
||||
| appName | `string` | "" | (Required) The name of the application. |
|
||||
| taskId | `string` | | (Required) The id of the task. |
|
||||
| taskId | `string` | "" | (Required) The id of the task. |
|
||||
|
||||
### Events
|
||||
|
@ -19,12 +19,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { TaskCloudService } from '../services/task-cloud.service';
|
||||
import { of } from 'rxjs';
|
||||
import { ClaimTaskDirective } from './claim-task.directive';
|
||||
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';
|
||||
|
||||
describe('ClaimTaskDirective', () => {
|
||||
describe('ClaimTaskCloudDirective', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-claim-test-component',
|
||||
@ -35,8 +35,8 @@ describe('ClaimTaskDirective', () => {
|
||||
taskMock = 'test1234';
|
||||
appNameMock = 'simple-app';
|
||||
|
||||
@ViewChild(ClaimTaskDirective)
|
||||
claimTaskDirective: ClaimTaskDirective;
|
||||
@ViewChild(ClaimTaskCloudDirective)
|
||||
claimTaskDirective: ClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
@ -78,8 +78,8 @@ describe('Claim Task Directive validation errors', () => {
|
||||
appNameUndefined = undefined;
|
||||
appNameNull = null;
|
||||
|
||||
@ContentChildren(ClaimTaskDirective)
|
||||
claimTaskValidationDirective: ClaimTaskDirective;
|
||||
@ContentChildren(ClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: ClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -90,8 +90,8 @@ describe('Claim Task Directive validation errors', () => {
|
||||
|
||||
appName = 'simple-app';
|
||||
|
||||
@ContentChildren(ClaimTaskDirective)
|
||||
claimTaskValidationDirective: ClaimTaskDirective;
|
||||
@ContentChildren(ClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: ClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -103,8 +103,8 @@ describe('Claim Task Directive validation errors', () => {
|
||||
appNameUndefined = undefined;
|
||||
taskMock = 'test1234';
|
||||
|
||||
@ContentChildren(ClaimTaskDirective)
|
||||
claimTaskValidationDirective: ClaimTaskDirective;
|
||||
@ContentChildren(ClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: ClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -116,8 +116,8 @@ describe('Claim Task Directive validation errors', () => {
|
||||
appNameNull = null;
|
||||
taskMock = 'test1234';
|
||||
|
||||
@ViewChild(ClaimTaskDirective)
|
||||
claimTaskValidationDirective: ClaimTaskDirective;
|
||||
@ViewChild(ClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: ClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<any>;
|
@ -22,7 +22,7 @@ import { TaskCloudService } from '../services/task-cloud.service';
|
||||
// tslint:disable-next-line: directive-selector
|
||||
selector: '[adf-cloud-claim-task]'
|
||||
})
|
||||
export class ClaimTaskDirective implements OnInit {
|
||||
export class ClaimTaskCloudDirective implements OnInit {
|
||||
|
||||
/** (Required) The id of the task. */
|
||||
@Input()
|
@ -15,8 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
export * from './claim-task.directive';
|
||||
export * from './unclaim-task.directive';
|
||||
export * from './claim-task-cloud.directive';
|
||||
export * from './unclaim-task-cloud.directive';
|
||||
export * from './complete-task.directive';
|
||||
|
||||
export * from './task-directive.module';
|
||||
|
@ -17,19 +17,19 @@
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { CompleteTaskDirective } from './complete-task.directive';
|
||||
import { ClaimTaskDirective } from './claim-task.directive';
|
||||
import { UnClaimTaskDirective } from './unclaim-task.directive';
|
||||
import { ClaimTaskCloudDirective } from './claim-task-cloud.directive';
|
||||
import { UnClaimTaskCloudDirective } from './unclaim-task-cloud.directive';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
CompleteTaskDirective,
|
||||
ClaimTaskDirective,
|
||||
UnClaimTaskDirective
|
||||
ClaimTaskCloudDirective,
|
||||
UnClaimTaskCloudDirective
|
||||
],
|
||||
exports: [
|
||||
CompleteTaskDirective,
|
||||
ClaimTaskDirective,
|
||||
UnClaimTaskDirective
|
||||
ClaimTaskCloudDirective,
|
||||
UnClaimTaskCloudDirective
|
||||
]
|
||||
})
|
||||
export class TaskDirectiveModule { }
|
||||
|
@ -19,12 +19,12 @@ import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { TaskCloudService } from '../services/task-cloud.service';
|
||||
import { of } from 'rxjs';
|
||||
import { UnClaimTaskDirective } from './unclaim-task.directive';
|
||||
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';
|
||||
|
||||
describe('UnClaimTaskDirective', () => {
|
||||
describe('UnClaimTaskCloudDirective', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-cloud-test-component',
|
||||
@ -35,8 +35,8 @@ describe('UnClaimTaskDirective', () => {
|
||||
appName = 'simple-app';
|
||||
taskIdMock = '1234';
|
||||
|
||||
@ContentChildren(UnClaimTaskDirective)
|
||||
unclaimTaskDirective: UnClaimTaskDirective;
|
||||
@ContentChildren(UnClaimTaskCloudDirective)
|
||||
unclaimTaskDirective: UnClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
@ -78,8 +78,8 @@ describe('UnClaim Task Directive validation errors', () => {
|
||||
appNameUndefined = undefined;
|
||||
appNameNull = null;
|
||||
|
||||
@ContentChildren(UnClaimTaskDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskDirective;
|
||||
@ContentChildren(UnClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -90,8 +90,8 @@ describe('UnClaim Task Directive validation errors', () => {
|
||||
|
||||
appName = 'simple-app';
|
||||
|
||||
@ContentChildren(UnClaimTaskDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskDirective;
|
||||
@ContentChildren(UnClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -103,8 +103,8 @@ describe('UnClaim Task Directive validation errors', () => {
|
||||
appNameUndefined = undefined;
|
||||
taskMock = 'test1234';
|
||||
|
||||
@ContentChildren(UnClaimTaskDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskDirective;
|
||||
@ContentChildren(UnClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
@Component({
|
||||
@ -116,8 +116,8 @@ describe('UnClaim Task Directive validation errors', () => {
|
||||
appNameNull = null;
|
||||
taskMock = 'test1234';
|
||||
|
||||
@ViewChild(UnClaimTaskDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskDirective;
|
||||
@ViewChild(UnClaimTaskCloudDirective)
|
||||
claimTaskValidationDirective: UnClaimTaskCloudDirective;
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<any>;
|
@ -21,7 +21,7 @@ import { TaskCloudService } from '../services/task-cloud.service';
|
||||
// tslint:disable-next-line: directive-selector
|
||||
selector: '[adf-cloud-unclaim-task]'
|
||||
})
|
||||
export class UnClaimTaskDirective implements OnInit {
|
||||
export class UnClaimTaskCloudDirective implements OnInit {
|
||||
|
||||
/** (Required) The id of the task. */
|
||||
@Input()
|
@ -0,0 +1,24 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'adf-form-custom-outcomes',
|
||||
template: '<ng-content></ng-content>'
|
||||
})
|
||||
export class FormCustomOutcomesComponent {}
|
@ -34,6 +34,7 @@
|
||||
</adf-form-renderer>
|
||||
</mat-card-content>
|
||||
<mat-card-actions *ngIf="form.hasOutcomes()" class="adf-form-mat-card-actions">
|
||||
<ng-content select="adf-form-custom-outcomes"></ng-content>
|
||||
<button [id]="'adf-form-'+ outcome.name | formatSpace" *ngFor="let outcome of form.outcomes"
|
||||
[color]="getColorForOutcome(outcome.name)" mat-button [disabled]="!isOutcomeButtonEnabled(outcome)"
|
||||
[class.adf-form-hide-button]="!isOutcomeButtonVisible(outcome, form.readOnly)"
|
||||
|
@ -15,7 +15,8 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { SimpleChange, ComponentFactoryResolver, Injector, NgModule, Component } from '@angular/core';
|
||||
import { SimpleChange, ComponentFactoryResolver, Injector, NgModule, Component, ViewChild, DebugElement } from '@angular/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
import { TestBed, ComponentFixture } from '@angular/core/testing';
|
||||
import { Observable, of, throwError } from 'rxjs';
|
||||
import { FormFieldModel, FormFieldTypes, FormModel, FormOutcomeEvent, FormOutcomeModel,
|
||||
@ -1013,3 +1014,79 @@ describe('FormComponent', () => {
|
||||
expect(radioFieldById.value).toBe('option_3');
|
||||
});
|
||||
});
|
||||
|
||||
@Component({
|
||||
selector: 'adf-form-with-custom-outcomes',
|
||||
template: `
|
||||
<adf-form #adfForm>
|
||||
<adf-form-custom-outcomes>
|
||||
<button mat-button id="adf-custom-outcome-1" (click)="onCustomButtonOneClick()">
|
||||
CUSTOM-BUTTON-1
|
||||
</button>
|
||||
<button mat-button id="adf-custom-outcome-2" (click)="onCustomButtonTwoClick()">
|
||||
CUSTOM-BUTTON-2
|
||||
</button>
|
||||
</adf-form-custom-outcomes>
|
||||
</adf-form>`
|
||||
})
|
||||
|
||||
class FormWithCustomOutComesComponent {
|
||||
|
||||
@ViewChild('adfForm')
|
||||
adfForm: FormComponent;
|
||||
|
||||
onCustomButtonOneClick() { }
|
||||
onCustomButtonTwoClick() { }
|
||||
}
|
||||
|
||||
describe('FormWithCustomOutComesComponent', () => {
|
||||
|
||||
let fixture: ComponentFixture<FormWithCustomOutComesComponent>;
|
||||
let customComponent: FormWithCustomOutComesComponent;
|
||||
let debugElement: DebugElement;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
TranslateModule.forRoot(),
|
||||
ProcessTestingModule
|
||||
],
|
||||
declarations: [FormWithCustomOutComesComponent]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(FormWithCustomOutComesComponent);
|
||||
customComponent = fixture.componentInstance;
|
||||
debugElement = fixture.debugElement;
|
||||
const formRepresentation = {
|
||||
fields: [
|
||||
{ id: 'container1' }
|
||||
],
|
||||
outcomes: [
|
||||
{ id: 'outcome-1', name: 'outcome 1' }
|
||||
]
|
||||
};
|
||||
|
||||
const form = new FormModel(formRepresentation);
|
||||
customComponent.adfForm.form = form;
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
fixture.destroy();
|
||||
});
|
||||
|
||||
it('should be able to inject custom outcomes and click on custom outcomes', () => {
|
||||
const onCustomButtonOneSpy = spyOn(customComponent, 'onCustomButtonOneClick').and.callThrough();
|
||||
const buttonOneBtn = debugElement.query(By.css('#adf-custom-outcome-1'));
|
||||
const buttonTwoBtn = debugElement.query(By.css('#adf-custom-outcome-2'));
|
||||
|
||||
expect(buttonOneBtn).not.toBeNull();
|
||||
expect(buttonTwoBtn).not.toBeNull();
|
||||
|
||||
buttonOneBtn.nativeElement.click();
|
||||
|
||||
expect(onCustomButtonOneSpy).toHaveBeenCalled();
|
||||
expect(buttonOneBtn.nativeElement.innerText).toBe('CUSTOM-BUTTON-1');
|
||||
expect(buttonTwoBtn.nativeElement.innerText).toBe('CUSTOM-BUTTON-2');
|
||||
});
|
||||
});
|
||||
|
@ -20,6 +20,7 @@ import { MaterialModule } from '../material.module';
|
||||
import { CoreModule } from '@alfresco/adf-core';
|
||||
import { FormComponent } from './form.component';
|
||||
import { StartFormComponent } from './start-form.component';
|
||||
import { FormCustomOutcomesComponent } from './form-custom-outcomes.component';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -28,11 +29,13 @@ import { StartFormComponent } from './start-form.component';
|
||||
],
|
||||
declarations: [
|
||||
FormComponent,
|
||||
StartFormComponent
|
||||
StartFormComponent,
|
||||
FormCustomOutcomesComponent
|
||||
],
|
||||
exports: [
|
||||
FormComponent,
|
||||
StartFormComponent
|
||||
StartFormComponent,
|
||||
FormCustomOutcomesComponent
|
||||
]
|
||||
})
|
||||
export class FormModule {}
|
||||
|
@ -18,4 +18,5 @@
|
||||
export * from './form.component';
|
||||
export * from './start-form.component';
|
||||
export * from './process-form-rendering.service';
|
||||
export * from './form-custom-outcomes.component';
|
||||
export * from './form.module';
|
||||
|
@ -35,6 +35,8 @@
|
||||
(completed)="onComplete()"
|
||||
(showAttachForm)="onShowAttachForm()"
|
||||
(executeOutcome)='onFormExecuteOutcome($event)'
|
||||
(taskClaimed)="onClaimAction($event)"
|
||||
(taskUnclaimed)="onUnclaimAction($event)"
|
||||
(error)="onFormError($event)" #activitiTaskForm>
|
||||
</adf-task-form>
|
||||
<adf-attach-form *ngIf="isShowAttachForm()"
|
||||
|
@ -0,0 +1,122 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Output, EventEmitter } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { of } from 'rxjs';
|
||||
import { TaskListService } from '../../services/tasklist.service';
|
||||
import { ProcessTestingModule } from '../../../testing/process.testing.module';
|
||||
|
||||
describe('ClaimTaskDirective', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-claim-test-component',
|
||||
template: '<button adf-claim-task [taskId]="taskId" (success)="onClaim($event)">Claim</button>'
|
||||
})
|
||||
class TestComponent {
|
||||
taskId = 'test1234';
|
||||
@Output()
|
||||
claim: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
onClaim(event) {
|
||||
this.claim.emit(event);
|
||||
}
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let taskListService: TaskListService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
ProcessTestingModule
|
||||
],
|
||||
declarations: [
|
||||
TestComponent
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
taskListService = TestBed.get(TaskListService);
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('Should be able to call claim task service', () => {
|
||||
const claimTaskSpy = spyOn(taskListService, 'claimTask').and.returnValue(of({}));
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
|
||||
expect(claimTaskSpy).toHaveBeenCalledWith(fixture.componentInstance.taskId);
|
||||
});
|
||||
|
||||
it('Should be able to catch success event on click of claim button', async() => {
|
||||
spyOn(taskListService, 'claimTask').and.returnValue(of({}));
|
||||
const unclaimSpy = spyOn(fixture.componentInstance.claim, 'emit');
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(unclaimSpy).toHaveBeenCalledWith(fixture.componentInstance.taskId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Claim Task Directive validation errors', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-claim-no-fields-validation-component',
|
||||
template: '<button adf-claim-task></button>'
|
||||
})
|
||||
class ClaimTestMissingInputDirectiveComponent { }
|
||||
|
||||
@Component({
|
||||
selector: 'adf-claim-no-taskid-validation-component',
|
||||
template: '<button adf-claim-task [taskId]=""></button>'
|
||||
})
|
||||
class ClaimTestMissingTaskIdDirectiveComponent { }
|
||||
|
||||
let fixture: ComponentFixture<any>;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
ProcessTestingModule
|
||||
],
|
||||
declarations: [
|
||||
ClaimTestMissingTaskIdDirectiveComponent,
|
||||
ClaimTestMissingInputDirectiveComponent
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ClaimTestMissingInputDirectiveComponent);
|
||||
});
|
||||
|
||||
it('should throw error when missing input', () => {
|
||||
fixture = TestBed.createComponent(ClaimTestMissingInputDirectiveComponent);
|
||||
|
||||
expect(() => fixture.detectChanges()).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw error when taskId is not set', () => {
|
||||
fixture = TestBed.createComponent(ClaimTestMissingTaskIdDirectiveComponent);
|
||||
|
||||
expect( () => fixture.detectChanges()).toThrowError('Attribute taskId is required');
|
||||
});
|
||||
});
|
@ -0,0 +1,89 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Directive,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
HostListener
|
||||
} from '@angular/core';
|
||||
import { TaskListService } from '../../services/tasklist.service';
|
||||
import { LogService } from '@alfresco/adf-core';
|
||||
|
||||
@Directive({
|
||||
// tslint:disable-next-line: directive-selector
|
||||
selector: '[adf-claim-task]'
|
||||
})
|
||||
export class ClaimTaskDirective {
|
||||
/** (Required) The id of the task. */
|
||||
@Input()
|
||||
taskId: string;
|
||||
|
||||
/** Emitted when the task is claimed. */
|
||||
@Output()
|
||||
success: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/** Emitted when the task cannot be claimed. */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
invalidParams: string[] = [];
|
||||
|
||||
constructor(
|
||||
private taskListService: TaskListService,
|
||||
private logService: LogService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.validateInputs();
|
||||
}
|
||||
|
||||
validateInputs() {
|
||||
if (!this.isTaskValid()) {
|
||||
this.invalidParams.push('taskId');
|
||||
}
|
||||
|
||||
if (this.invalidParams.length) {
|
||||
throw new Error(
|
||||
`Attribute ${this.invalidParams.join(', ')} is required`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
isTaskValid(): boolean {
|
||||
return this.taskId && this.taskId.length > 0;
|
||||
}
|
||||
|
||||
@HostListener('click')
|
||||
async onClick() {
|
||||
try {
|
||||
this.claimTask();
|
||||
} catch (error) {
|
||||
this.error.emit(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async claimTask() {
|
||||
await this.taskListService.claimTask(this.taskId).subscribe(
|
||||
() => {
|
||||
this.logService.info('Task claimed');
|
||||
this.success.emit(this.taskId);
|
||||
},
|
||||
error => this.error.emit(error)
|
||||
);
|
||||
}
|
||||
}
|
@ -16,6 +16,10 @@
|
||||
(formError)='onFormError($event)'
|
||||
(error)='onError($event)'
|
||||
(executeOutcome)='onFormExecuteOutcome($event)'>
|
||||
<adf-form-custom-outcomes>
|
||||
<ng-template [ngTemplateOutlet]="taskFormButtons">
|
||||
</ng-template>
|
||||
</adf-form-custom-outcomes>
|
||||
</adf-form>
|
||||
<ng-template #withoutForm>
|
||||
<adf-task-standalone *ngIf="isStandaloneTask(); else emptyFormMessage"
|
||||
@ -56,6 +60,7 @@
|
||||
</ng-template>
|
||||
</mat-card-content>
|
||||
<mat-card-actions class="adf-task-form-actions">
|
||||
<ng-template [ngTemplateOutlet]="taskFormButtons"></ng-template>
|
||||
<button id="adf-no-form-cancel-button" mat-button *ngIf="showCancelButton" (click)="onCancel()">
|
||||
{{'ADF_TASK_FORM.EMPTY_FORM.BUTTONS.CANCEL' | translate}}
|
||||
</button>
|
||||
@ -66,6 +71,23 @@
|
||||
</mat-card>
|
||||
</ng-template>
|
||||
</ng-template>
|
||||
|
||||
<ng-template #taskFormButtons>
|
||||
<button mat-button data-automation-id="adf-task-form-claim-button"
|
||||
*ngIf="isTaskClaimable()"
|
||||
adf-claim-task
|
||||
[taskId]="taskId"
|
||||
(success)="onClaimTask($event)">
|
||||
{{ 'ADF_TASK_LIST.DETAILS.BUTTON.CLAIM' | translate }}
|
||||
</button>
|
||||
<button mat-button data-automation-id="adf-task-form-unclaim-button"
|
||||
*ngIf="isTaskClaimedByCandidateMember()"
|
||||
adf-unclaim-task
|
||||
[taskId]="taskId"
|
||||
(success)="onUnclaimTask($event)">
|
||||
{{ 'ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM' | translate }}
|
||||
</button>
|
||||
</ng-template>
|
||||
</ng-container>
|
||||
<ng-template #loadingTemplate>
|
||||
<div fxLayout="row" fxLayoutAlign="center stretch">
|
||||
|
@ -37,11 +37,15 @@ import {
|
||||
standaloneTaskWithoutForm,
|
||||
completedStandaloneTaskWithoutForm,
|
||||
claimableTaskDetailsMock,
|
||||
initiatorCanCompleteTaskDetailsMock
|
||||
initiatorCanCompleteTaskDetailsMock,
|
||||
taskDetailsWithOutCandidateGroup,
|
||||
claimedTaskDetailsMock,
|
||||
claimedByGroupMemberMock
|
||||
} from '../../../mock/task/task-details.mock';
|
||||
import { TaskDetailsModel } from '../../models/task-details.model';
|
||||
import { ProcessTestingModule } from '../../../testing/process.testing.module';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { By } from '@angular/platform-browser';
|
||||
|
||||
describe('TaskFormComponent', () => {
|
||||
let component: TaskFormComponent;
|
||||
@ -495,4 +499,125 @@ describe('TaskFormComponent', () => {
|
||||
expect(validationForm.textContent).toBe('check_circle');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Claim/Unclaim buttons', () => {
|
||||
|
||||
it('should display the claim button if no assignee', async() => {
|
||||
getTaskDetailsSpy.and.returnValue(of(claimableTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const claimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-claim-button"]'));
|
||||
expect(claimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.CLAIM');
|
||||
});
|
||||
|
||||
it('should not display the claim/requeue button if the task is not claimable ', async() => {
|
||||
getTaskDetailsSpy.and.returnValue(of(taskDetailsWithOutCandidateGroup));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
fixture.detectChanges();
|
||||
|
||||
await fixture.whenStable();
|
||||
const claimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-claim-button"]'));
|
||||
const unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-unclaim-button"]'));
|
||||
|
||||
expect(component.isTaskClaimable()).toBe(false);
|
||||
expect(component.isTaskClaimedByCandidateMember()).toBe(false);
|
||||
expect(unclaimButton).toBeNull();
|
||||
expect(claimButton).toBeNull();
|
||||
});
|
||||
|
||||
it('should display the claim button if the task is claimable', async() => {
|
||||
getTaskDetailsSpy.and.returnValue(of(claimableTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const claimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-claim-button"]'));
|
||||
|
||||
expect(component.isTaskClaimable()).toBe(true);
|
||||
expect(claimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.CLAIM');
|
||||
});
|
||||
|
||||
it('should display the release button if task is claimed by the current logged-in user', async() => {
|
||||
getBpmLoggedUserSpy.and.returnValue(of(claimedTaskDetailsMock.assignee));
|
||||
getTaskDetailsSpy.and.returnValue(of(claimedTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-unclaim-button"]'));
|
||||
|
||||
expect(component.isTaskClaimedByCandidateMember()).toBe(true);
|
||||
expect(unclaimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM');
|
||||
});
|
||||
|
||||
it('should not display the release button to logged in user if task is claimed by other candidate member', async() => {
|
||||
getTaskDetailsSpy.and.returnValue(of(claimedByGroupMemberMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-unclaim-button"]'));
|
||||
|
||||
expect(component.isTaskClaimedByCandidateMember()).toBe(false);
|
||||
expect(unclaimButton).toBeNull();
|
||||
});
|
||||
|
||||
it('should not display the release button if the task is completed', async() => {
|
||||
getTaskDetailsSpy.and.returnValue(of(completedTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
const claimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-claim-button"]'));
|
||||
const unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="adf-task-form-unclaim-button"]'));
|
||||
|
||||
expect(claimButton).toBeNull();
|
||||
expect(unclaimButton).toBeNull();
|
||||
});
|
||||
|
||||
it('should emit taskClaimed when task is claimed', (done) => {
|
||||
spyOn(taskListService, 'claimTask').and.returnValue(of({}));
|
||||
getTaskDetailsSpy.and.returnValue(of(claimableTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
|
||||
component.taskClaimed.subscribe((taskId) => {
|
||||
expect(taskId).toEqual(component.taskId);
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
const claimBtn = fixture.debugElement.query(By.css('[adf-claim-task]'));
|
||||
claimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
it('should emit taskUnClaimed when task is unclaimed', (done) => {
|
||||
spyOn(taskListService, 'unclaimTask').and.returnValue(of({}));
|
||||
getBpmLoggedUserSpy.and.returnValue(of(claimedTaskDetailsMock.assignee));
|
||||
getTaskDetailsSpy.and.returnValue(of(claimedTaskDetailsMock));
|
||||
|
||||
component.taskId = 'mock-task-id';
|
||||
|
||||
component.taskUnclaimed.subscribe((taskId: string) => {
|
||||
expect(taskId).toEqual(component.taskId);
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
const unclaimBtn = fixture.debugElement.query(By.css('[adf-unclaim-task]'));
|
||||
unclaimBtn.nativeElement.click();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -117,6 +117,14 @@ export class TaskFormComponent implements OnInit {
|
||||
@Output()
|
||||
cancel = new EventEmitter<void>();
|
||||
|
||||
/** Emitted when the task is claimed. */
|
||||
@Output()
|
||||
taskClaimed = new EventEmitter<string>();
|
||||
|
||||
/** Emitted when the task is unclaimed (ie, requeued).. */
|
||||
@Output()
|
||||
taskUnclaimed = new EventEmitter<string>();
|
||||
|
||||
taskDetails: TaskDetailsModel;
|
||||
currentLoggedUser: UserRepresentation;
|
||||
loading: boolean = false;
|
||||
@ -278,4 +286,28 @@ export class TaskFormComponent implements OnInit {
|
||||
getCompletedTaskTranslatedMessage(): Observable<string> {
|
||||
return this.translationService.get('ADF_TASK_FORM.COMPLETED_TASK.TITLE', { taskName: this.taskDetails.name });
|
||||
}
|
||||
|
||||
isCandidateMember(): boolean {
|
||||
return this.taskDetails.managerOfCandidateGroup || this.taskDetails.memberOfCandidateGroup || this.taskDetails.memberOfCandidateUsers;
|
||||
}
|
||||
|
||||
isTaskClaimable(): boolean {
|
||||
return this.isCandidateMember() && !this.isAssigned();
|
||||
}
|
||||
|
||||
isTaskClaimedByCandidateMember(): boolean {
|
||||
return this.isCandidateMember() && this.isAssignedToMe() && !this.isCompletedTask();
|
||||
}
|
||||
|
||||
reloadTask() {
|
||||
this.loadTask(this.taskId);
|
||||
}
|
||||
|
||||
onClaimTask(taskId: string) {
|
||||
this.taskClaimed.emit(taskId);
|
||||
}
|
||||
|
||||
onUnclaimTask(taskId: string) {
|
||||
this.taskUnclaimed.emit(taskId);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,126 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { Component, Output, EventEmitter } from '@angular/core';
|
||||
import { ComponentFixture, TestBed } from '@angular/core/testing';
|
||||
import { setupTestBed } from '@alfresco/adf-core';
|
||||
import { of } from 'rxjs';
|
||||
import { TaskListService } from '../../services/tasklist.service';
|
||||
import { ProcessTestingModule } from '../../../testing/process.testing.module';
|
||||
|
||||
describe('UnclaimTaskDirective', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-unclaim-test-component',
|
||||
template: '<button adf-unclaim-task [taskId]="taskId" (success)="onUnclaim($event)">Unclaim</button>'
|
||||
})
|
||||
class TestComponent {
|
||||
taskId = 'test1234';
|
||||
@Output()
|
||||
unclaim: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
onUnclaim(event) {
|
||||
this.unclaim.emit(event);
|
||||
}
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<TestComponent>;
|
||||
let taskListService: TaskListService;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
ProcessTestingModule
|
||||
],
|
||||
declarations: [
|
||||
TestComponent
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
taskListService = TestBed.get(TaskListService);
|
||||
fixture = TestBed.createComponent(TestComponent);
|
||||
fixture.detectChanges();
|
||||
});
|
||||
|
||||
it('Should be able to call unclaim task service', () => {
|
||||
const claimTaskSpy = spyOn(taskListService, 'unclaimTask').and.returnValue(of({}));
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
|
||||
expect(claimTaskSpy).toHaveBeenCalledWith(fixture.componentInstance.taskId);
|
||||
});
|
||||
|
||||
it('Should be able to catch success event on click of unclaim button', async() => {
|
||||
spyOn(taskListService, 'unclaimTask').and.returnValue(of({}));
|
||||
const unclaimSpy = spyOn(fixture.componentInstance.unclaim, 'emit');
|
||||
|
||||
const button = fixture.nativeElement.querySelector('button');
|
||||
button.click();
|
||||
fixture.detectChanges();
|
||||
await fixture.whenStable();
|
||||
|
||||
expect(unclaimSpy).toHaveBeenCalledWith(fixture.componentInstance.taskId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Claim Task Directive validation errors', () => {
|
||||
|
||||
@Component({
|
||||
selector: 'adf-unclaim-no-fields-validation-component',
|
||||
template: '<button adf-unclaim-task></button>'
|
||||
})
|
||||
class ClaimTestMissingInputDirectiveComponent {
|
||||
|
||||
}
|
||||
|
||||
@Component({
|
||||
selector: 'adf-claim-no-taskid-validation-component',
|
||||
template: '<button adf-unclaim-task [taskId]=""></button>'
|
||||
})
|
||||
class ClaimTestMissingTaskIdDirectiveComponent {
|
||||
|
||||
}
|
||||
|
||||
let fixture: ComponentFixture<any>;
|
||||
|
||||
setupTestBed({
|
||||
imports: [
|
||||
ProcessTestingModule
|
||||
],
|
||||
declarations: [
|
||||
ClaimTestMissingTaskIdDirectiveComponent,
|
||||
ClaimTestMissingInputDirectiveComponent
|
||||
]
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
fixture = TestBed.createComponent(ClaimTestMissingInputDirectiveComponent);
|
||||
});
|
||||
|
||||
it('should throw error when missing input', () => {
|
||||
fixture = TestBed.createComponent(ClaimTestMissingInputDirectiveComponent);
|
||||
|
||||
expect(() => fixture.detectChanges()).toThrowError();
|
||||
});
|
||||
|
||||
it('should throw error when taskId is not set', () => {
|
||||
fixture = TestBed.createComponent(ClaimTestMissingTaskIdDirectiveComponent);
|
||||
|
||||
expect( () => fixture.detectChanges()).toThrowError('Attribute taskId is required');
|
||||
});
|
||||
});
|
@ -0,0 +1,88 @@
|
||||
/*!
|
||||
* @license
|
||||
* Copyright 2019 Alfresco Software, Ltd.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import {
|
||||
Directive,
|
||||
HostListener,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter
|
||||
} from '@angular/core';
|
||||
import { TaskListService } from '../../services/tasklist.service';
|
||||
import { LogService } from '@alfresco/adf-core';
|
||||
|
||||
@Directive({
|
||||
// tslint:disable-next-line: directive-selector
|
||||
selector: '[adf-unclaim-task]'
|
||||
})
|
||||
export class UnclaimTaskDirective {
|
||||
/** (Required) The id of the task. */
|
||||
@Input()
|
||||
taskId: string;
|
||||
|
||||
/** Emitted when the task is released. */
|
||||
@Output()
|
||||
success: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
/** Emitted when the task cannot be released. */
|
||||
@Output()
|
||||
error: EventEmitter<any> = new EventEmitter<any>();
|
||||
|
||||
invalidParams: string[] = [];
|
||||
|
||||
constructor(
|
||||
private taskListService: TaskListService,
|
||||
private logService: LogService) {}
|
||||
|
||||
ngOnInit() {
|
||||
this.validateInputs();
|
||||
}
|
||||
|
||||
validateInputs() {
|
||||
if (!this.isTaskValid()) {
|
||||
this.invalidParams.push('taskId');
|
||||
}
|
||||
if (this.invalidParams.length) {
|
||||
throw new Error(
|
||||
`Attribute ${this.invalidParams.join(', ')} is required`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
isTaskValid(): boolean {
|
||||
return this.taskId && this.taskId.length > 0;
|
||||
}
|
||||
|
||||
@HostListener('click')
|
||||
async onClick() {
|
||||
try {
|
||||
this.unclaimTask();
|
||||
} catch (error) {
|
||||
this.error.emit(error);
|
||||
}
|
||||
}
|
||||
|
||||
private async unclaimTask() {
|
||||
await this.taskListService.unclaimTask(this.taskId).subscribe(
|
||||
() => {
|
||||
this.logService.info('Task unclaimed');
|
||||
this.success.emit(this.taskId);
|
||||
},
|
||||
error => this.error.emit(error)
|
||||
);
|
||||
}
|
||||
}
|
@ -3,10 +3,26 @@
|
||||
<adf-card-view [properties]="properties" [editable]="!isCompleted()" [displayClearAction]="displayDateClearAction"></adf-card-view>
|
||||
</mat-card-content>
|
||||
|
||||
<mat-card-actions class="adf-controls">
|
||||
<button *ngIf="isTaskClaimedByCandidateMember()" mat-button data-automation-id="header-unclaim-button" id="unclaim-task" (click)="unclaimTask(taskDetails.id)" class="adf-claim-controls">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM' | translate }}
|
||||
<mat-card-actions class="adf-controls" *ngIf="showClaimRelease">
|
||||
<button *ngIf="isTaskClaimedByCandidateMember()"
|
||||
mat-button
|
||||
data-automation-id="header-unclaim-button"
|
||||
id="unclaim-task"
|
||||
class="adf-claim-controls"
|
||||
adf-unclaim-task
|
||||
[taskId]="taskDetails.id"
|
||||
(success)="onUnclaimTask($event)">
|
||||
{{ 'ADF_TASK_LIST.DETAILS.BUTTON.UNCLAIM' | translate }}
|
||||
</button>
|
||||
<button *ngIf="isTaskClaimable()" mat-button data-automation-id="header-claim-button" id="claim-task" (click)="claimTask(taskDetails.id)" class="adf-claim-controls">{{ 'ADF_TASK_LIST.DETAILS.BUTTON.CLAIM' | translate }}
|
||||
<button *ngIf="isTaskClaimable()"
|
||||
mat-button
|
||||
data-automation-id="header-claim-button"
|
||||
id="claim-task"
|
||||
class="adf-claim-controls"
|
||||
adf-claim-task
|
||||
[taskId]="taskDetails.id"
|
||||
(success)="onClaimTask($event)">
|
||||
{{ 'ADF_TASK_LIST.DETAILS.BUTTON.CLAIM' | translate }}
|
||||
</button>
|
||||
</mat-card-actions>
|
||||
</mat-card>
|
||||
|
@ -136,6 +136,30 @@ describe('TaskHeaderComponent', () => {
|
||||
|
||||
describe('Claiming', () => {
|
||||
|
||||
it('should be able display the claim/release button if showClaimRelease set to true', async(() => {
|
||||
component.taskDetails = new TaskDetailsModel(claimableTaskDetailsMock);
|
||||
component.showClaimRelease = true;
|
||||
component.refreshData();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
const claimButton = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]'));
|
||||
expect(claimButton.nativeElement.innerText).toBe('ADF_TASK_LIST.DETAILS.BUTTON.CLAIM');
|
||||
});
|
||||
}));
|
||||
|
||||
it('should not be able display the claim/release button if showClaimRelease set to false', async(() => {
|
||||
component.taskDetails = new TaskDetailsModel(claimableTaskDetailsMock);
|
||||
component.showClaimRelease = false;
|
||||
component.refreshData();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
const claimButton = fixture.debugElement.query(By.css('[data-automation-id="header-claim-button"]'));
|
||||
expect(claimButton).toBeNull();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display the claim button if no assignee', async(() => {
|
||||
component.taskDetails = new TaskDetailsModel(claimableTaskDetailsMock);
|
||||
|
||||
@ -227,39 +251,38 @@ describe('TaskHeaderComponent', () => {
|
||||
});
|
||||
}));
|
||||
|
||||
it('should call the service unclaim method on un-claiming', async(() => {
|
||||
spyOn(service, 'unclaimTask').and.returnValue(of(true));
|
||||
component.taskDetails = new TaskDetailsModel(claimedTaskDetailsMock);
|
||||
component.refreshData();
|
||||
it('should emit claim event when task is claimed', (done) => {
|
||||
spyOn(service, 'claimTask').and.returnValue(of({}));
|
||||
component.taskDetails = claimableTaskDetailsMock;
|
||||
|
||||
component.claim.subscribe((taskId) => {
|
||||
expect(taskId).toEqual(component.taskDetails.id);
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
const unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]'));
|
||||
unclaimButton.triggerEventHandler('click', {});
|
||||
|
||||
expect(service.unclaimTask).toHaveBeenCalledWith('91');
|
||||
const claimBtn = fixture.debugElement.query(By.css('[adf-claim-task]'));
|
||||
claimBtn.nativeElement.click();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should trigger the unclaim event on successful un-claiming', async(() => {
|
||||
let unclaimed: boolean = false;
|
||||
spyOn(service, 'unclaimTask').and.returnValue(of(true));
|
||||
component.taskDetails = new TaskDetailsModel(claimedTaskDetailsMock);
|
||||
component.refreshData();
|
||||
it('should emit unclaim event when task is unclaimed', (done) => {
|
||||
spyOn(service, 'unclaimTask').and.returnValue(of({}));
|
||||
component.taskDetails = claimedTaskDetailsMock;
|
||||
|
||||
component.unclaim.subscribe((taskId: string) => {
|
||||
expect(taskId).toEqual(component.taskDetails.id);
|
||||
done();
|
||||
});
|
||||
|
||||
component.ngOnInit();
|
||||
fixture.detectChanges();
|
||||
|
||||
fixture.whenStable().then(() => {
|
||||
component.unclaim.subscribe(() => {
|
||||
unclaimed = true;
|
||||
const unclaimBtn = fixture.debugElement.query(By.css('[adf-unclaim-task]'));
|
||||
unclaimBtn.nativeElement.click();
|
||||
});
|
||||
|
||||
const unclaimButton = fixture.debugElement.query(By.css('[data-automation-id="header-unclaim-button"]'));
|
||||
unclaimButton.triggerEventHandler('click', {});
|
||||
|
||||
expect(unclaimed).toBeTruthy();
|
||||
});
|
||||
}));
|
||||
|
||||
it('should display due date', async(() => {
|
||||
component.taskDetails.dueDate = new Date('2016-11-03');
|
||||
component.refreshData();
|
||||
|
@ -23,12 +23,10 @@ import {
|
||||
CardViewMapItemModel,
|
||||
CardViewTextItemModel,
|
||||
CardViewBaseItemModel,
|
||||
LogService,
|
||||
TranslationService,
|
||||
AppConfigService
|
||||
} from '@alfresco/adf-core';
|
||||
import { TaskDetailsModel } from '../models/task-details.model';
|
||||
import { TaskListService } from './../services/tasklist.service';
|
||||
import { TaskDescriptionValidator } from '../validators/task-description.validator';
|
||||
|
||||
@Component({
|
||||
@ -46,6 +44,10 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
|
||||
@Input()
|
||||
taskDetails: TaskDetailsModel;
|
||||
|
||||
/** Toggles display of the claim/release button. */
|
||||
@Input()
|
||||
showClaimRelease = true;
|
||||
|
||||
/** Emitted when the task is claimed. */
|
||||
@Output()
|
||||
claim: EventEmitter<any> = new EventEmitter<any>();
|
||||
@ -62,10 +64,8 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
|
||||
dateFormat: string;
|
||||
dateLocale: string;
|
||||
|
||||
constructor(private activitiTaskService: TaskListService,
|
||||
private bpmUserService: BpmUserService,
|
||||
constructor(private bpmUserService: BpmUserService,
|
||||
private translationService: TranslationService,
|
||||
private logService: LogService,
|
||||
private appConfig: AppConfigService) {
|
||||
this.dateFormat = this.appConfig.get('dateValues.defaultDateFormat');
|
||||
this.dateLocale = this.appConfig.get('dateValues.defaultDateLocale');
|
||||
@ -281,28 +281,12 @@ export class TaskHeaderComponent implements OnChanges, OnInit {
|
||||
return (this.taskDetails && this.taskDetails.isCompleted()) ? 'Completed' : 'Running';
|
||||
}
|
||||
|
||||
/**
|
||||
* Claim task
|
||||
*
|
||||
* @param taskId
|
||||
*/
|
||||
claimTask(taskId: string) {
|
||||
this.activitiTaskService.claimTask(taskId).subscribe(() => {
|
||||
this.logService.info('Task claimed');
|
||||
onClaimTask(taskId: string) {
|
||||
this.claim.emit(taskId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Unclaim task
|
||||
*
|
||||
* @param taskId
|
||||
*/
|
||||
unclaimTask(taskId: string) {
|
||||
this.activitiTaskService.unclaimTask(taskId).subscribe(() => {
|
||||
this.logService.info('Task unclaimed');
|
||||
onUnclaimTask(taskId: string) {
|
||||
this.unclaim.emit(taskId);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -21,6 +21,8 @@ export * from './components/task-header.component';
|
||||
export * from './components/no-task-detail-template.directive';
|
||||
export * from './components/task-filters.component';
|
||||
export * from './components/task-form/task-form.component';
|
||||
export * from './components/task-form/claim-task.directive';
|
||||
export * from './components/task-form/unclaim-task.directive';
|
||||
export * from './components/task-details.component';
|
||||
export * from './components/task-audit.directive';
|
||||
export * from './components/start-task.component';
|
||||
|
@ -38,6 +38,8 @@ import { TaskListComponent } from './components/task-list.component';
|
||||
import { TaskStandaloneComponent } from './components/task-standalone.component';
|
||||
import { AttachFormComponent } from './components/attach-form.component';
|
||||
import { FormModule } from '../form/form.module';
|
||||
import { ClaimTaskDirective } from './components/task-form/claim-task.directive';
|
||||
import { UnclaimTaskDirective } from './components/task-form/unclaim-task.directive';
|
||||
|
||||
@NgModule({
|
||||
imports: [
|
||||
@ -63,7 +65,9 @@ import { FormModule } from '../form/form.module';
|
||||
TaskHeaderComponent,
|
||||
StartTaskComponent,
|
||||
TaskStandaloneComponent,
|
||||
AttachFormComponent
|
||||
AttachFormComponent,
|
||||
ClaimTaskDirective,
|
||||
UnclaimTaskDirective
|
||||
],
|
||||
exports: [
|
||||
NoTaskDetailsTemplateDirective,
|
||||
@ -76,7 +80,9 @@ import { FormModule } from '../form/form.module';
|
||||
TaskHeaderComponent,
|
||||
StartTaskComponent,
|
||||
TaskStandaloneComponent,
|
||||
AttachFormComponent
|
||||
AttachFormComponent,
|
||||
ClaimTaskDirective,
|
||||
UnclaimTaskDirective
|
||||
]
|
||||
})
|
||||
export class TaskListModule {
|
||||
|
Loading…
x
Reference in New Issue
Block a user