[ADF-3539] created first version for process list cloud (#3925)

* [ADF-3539] created first version for process list cloud

* [ADF-3539] fixed process list and added demo page

* [ADF-3539] fixed sorting and start working on tests

* [ADF-3539] start adding tests for process list cloud

* [ADF-3539] fixed empty templates unit tests

* [ADF-3539] added documentation

* [ADF-3539] missed import

* [ADF-3539] fixed wrong export

* [ADF-3539] removed model

* [ADF-3539] fixed style problem and removed wrong comment
This commit is contained in:
Vito
2018-11-03 17:18:03 +00:00
committed by Eugenio Romano
parent a6cd910466
commit 63399f6830
28 changed files with 1581 additions and 33 deletions

View File

@@ -66,6 +66,7 @@ import { AuthBearerInterceptor } from './services';
import { ProcessServicesCloudModule } from '@alfresco/adf-process-services-cloud'; import { ProcessServicesCloudModule } from '@alfresco/adf-process-services-cloud';
import { CloudComponent } from './components/cloud/cloud.component'; import { CloudComponent } from './components/cloud/cloud.component';
import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component'; import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component';
import { ProcessListCloudExampleComponent } from './components/cloud/process-list-cloud-example.component';
@NgModule({ @NgModule({
imports: [ imports: [
@@ -115,7 +116,8 @@ import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/ta
DemoPermissionComponent, DemoPermissionComponent,
FormLoadingComponent, FormLoadingComponent,
ReportIssueComponent, ReportIssueComponent,
TaskListCloudDemoComponent TaskListCloudDemoComponent,
ProcessListCloudExampleComponent
], ],
providers: [ providers: [
{ {

View File

@@ -42,6 +42,7 @@ import { ReportIssueComponent } from './components/report-issue/report-issue.com
import { AppComponent } from './app.component'; import { AppComponent } from './app.component';
import { CloudComponent } from './components/cloud/cloud.component'; import { CloudComponent } from './components/cloud/cloud.component';
import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component'; import { TaskListCloudDemoComponent } from './components/task-list-cloud-demo/task-list-cloud-demo.component';
import { ProcessListCloudExampleComponent } from './components/cloud/process-list-cloud-example.component';
export const appRoutes: Routes = [ export const appRoutes: Routes = [
{ path: 'login', component: LoginComponent }, { path: 'login', component: LoginComponent },
@@ -147,6 +148,10 @@ export const appRoutes: Routes = [
} }
] ]
}, },
{
path: 'process-cloud',
component: ProcessListCloudExampleComponent
},
{ {
path: 'node-selector', path: 'node-selector',
loadChildren: 'app/components/content-node-selector/content-node-selector.module#AppContentNodeSelectorModule' loadChildren: 'app/components/content-node-selector/content-node-selector.module#AppContentNodeSelectorModule'

View File

@@ -0,0 +1,94 @@
<div>PROCESS LIST CLOUD</div>
<adf-cloud-app-list *ngIf="!currentAppName"
(appClick)="onAppClick($event)"></adf-cloud-app-list>
<div *ngIf="currentAppName">
<mat-accordion>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Process Example Filters
</mat-panel-title>
<mat-panel-description>
Apply one of the filters to the process list
</mat-panel-description>
</mat-expansion-panel-header>
<div class="app-process-cloud-spacing">
<mat-form-field>
<mat-select placeholder="Status" [(ngModel)]="status">
<mat-option value="">
ALL
</mat-option>
<mat-option value="RUNNING">
RUNNING
</mat-option>
<mat-option value="SUSPENDED">
SUSPENDED
</mat-option>
<mat-option value="CANCELLED">
CANCELLED
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="app-process-cloud-spacing">
<mat-form-field class="example-full-width">
<input matInput placeholder="Filter by id" [(ngModel)]="filterId">
</mat-form-field>
</div>
</mat-expansion-panel>
<mat-expansion-panel>
<mat-expansion-panel-header>
<mat-panel-title>
Sorting Panel
</mat-panel-title>
<mat-panel-description>
Choose how to sort your tasks
</mat-panel-description>
</mat-expansion-panel-header>
<div class="task-cloud-demo-select">
<mat-form-field>
<mat-select placeholder="Sort Field" [(ngModel)]="sortField">
<mat-option value="id">
ID
</mat-option>
<mat-option value="name">
NAME
</mat-option>
<mat-option value="status">
STATUS
</mat-option>
</mat-select>
</mat-form-field>
</div>
<div class="task-cloud-demo-select">
<mat-form-field>
<mat-select placeholder="Direction" [(ngModel)]="sortDirection">
<mat-option value="ASC">
ASC
</mat-option>
<mat-option value="DESC">
DESC
</mat-option>
</mat-select>
</mat-form-field>
</div>
<button mat-button (click)="onFilterButtonClick($event)">Apply Filter</button>
<button mat-button (click)="onClearFilters()">Clear Filter</button>
</mat-expansion-panel>
</mat-accordion>
<adf-cloud-process-list
[applicationName]="currentAppName"
[status]="status"
[sorting]="sortArray"
[id]="filterId"
#processCloud>
<data-columns>
<data-column key="entry.id" title="Id"></data-column>
<data-column key="entry.appName" title="Name"></data-column>
<data-column key="entry.status" title="Status"></data-column>
</data-columns>
</adf-cloud-process-list>
<adf-pagination [target]="processCloud" (changePageSize)="onChangePageSize($event)">
</adf-pagination>
<button mat-fab class="adf-process-list-cloud-button" color="primary" (click)="onClick()">Back</button>
</div>

View File

@@ -0,0 +1,7 @@
.adf-process-list-cloud-button {
margin: 15px;
}
.app-process-cloud-spacing {
margin: 10px;
}

View File

@@ -0,0 +1,66 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, ViewChild } from '@angular/core';
import { UserPreferencesService } from '@alfresco/adf-core';
import { ProcessListCloudComponent } from '@alfresco/adf-process-services-cloud';
@Component({
selector: 'app-process-list-example',
templateUrl: './process-list-cloud-example.component.html',
styleUrls: ['./process-list-cloud-example.component.scss']
})
export class ProcessListCloudExampleComponent {
@ViewChild('processCloud')
processCloud: ProcessListCloudComponent;
currentAppName: string = '';
status: string = '';
filterId: string = '';
sortArray: any = [];
sortField: string;
sortDirection: string;
constructor(private userPreference: UserPreferencesService) {
}
onAppClick(appClicked: any) {
this.currentAppName = appClicked.name;
}
onClick() {
this.currentAppName = '';
}
onChangePageSize(event) {
this.userPreference.paginationSize = event.maxItems;
}
onFilterButtonClick($event) {
let newSortParam: any = {
orderBy: this.sortField,
direction: this.sortDirection };
this.sortArray.push(newSortParam);
this.processCloud.reload();
}
onClearFilters() {
this.sortArray = [];
this.processCloud.reload();
}
}

View File

@@ -0,0 +1,193 @@
---
Added: v2.0.0
Status: Active
Last reviewed: 2018-05-24
---
# Process Instance List
Renders a list containing all the process instances matched by the parameters specified.
## Contents
- [Basic Usage](#basic-usage)
- [Transclusions](#transclusions)
- [Class members](#class-members)
- [Properties](#properties)
- [Events](#events)
- [Details](#details)
- [Setting Sorting Order for the list](#setting-sorting-order-for-the-list)
- [Pagination strategy](#pagination-strategy)
- [See also](#see-also)
## Basic Usage
**[app.component](../../demo-shell/src/app/app.component.ts).html**
```html
<adf-cloud-process-list
[applicationName]="'app-name'">
</adf-cloud-process-list>
```
### [Transclusions](../user-guide/transclusion.md)
Any content inside an `<adf-empty-custom-content>` sub-component will be shown
when the process list is empty:
```html
<adf-cloud-process-list>
<adf-empty-custom-content>
Your Content
</adf-empty-custom-content>
<adf-cloud-process-list>
```
## Class members
### Properties
| Name | Type | Default value | Description |
| ---- | ---- | ------------- | ----------- |
| applicationName | `string` | | The name of the application. |
| appVersion | `string` | | The application related version |
| initiator | `string` | | the name of the initiator of the process |
| id | `string` | | Filter the processes. Display only processes with id equal to the one insterted. |
| name | `string` | | Filter the processes. Display only processes with name equal to the one insterted. |
| processDefinitionId | `string` | | Filter the processes. Display only processes with processDefinitionId equal to the one insterted. |
| processDefinitionKey | `string` | | Filter the processes. Display only processes with processDefinitionKey equal to the one insterted. |
| serviceFullName | `string` | | Filter the processes. Display only processes with serviceFullName equal to the one insterted. |
| serviceName | `string` | | Filter the processes. Display only processes with serviceName equal to the one insterted. |
| serviceType | `string` | | Filter the processes. Display only processes with serviceType equal to the one insterted. |
| serviceVersion | `string` | | Filter the processes. Display only processes with serviceVersion equal to the one insterted. |
| status | `string` | | Filter the tasks. Display only processes with status equal to the one insterted. |
| businessKey | `string` | | Filter the tasks. Display only processes with businessKey equal to the one insterted. |
| selectFirstRow | `boolean` | true | Toggles default selection of the first row |
| landingTaskId | `string` | | Define which task id should be selected after reloading. If the task id doesn't exist or nothing is passed then the first task will be selected. |
| selectionMode | `string` | "single" | Row selection mode. Can be none, `single` or `multiple`. For `multiple` mode, you can use Cmd (macOS) or Ctrl (Win) modifier key to toggle selection for multiple rows. |
| multiselect | `boolean` | false | Toggles multiple row selection, renders checkboxes at the beginning of each row |
| sorting | `[ProcessListCloudSortingModel]` | | This array of `ProcessListCloudSortingModel` specify how the sorting on our table should be provided. This parameters are for BE sorting. |
### Events
| Name | Type | Description |
| ---- | ---- | ----------- |
| error | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when an error occurs while loading the list of process instances from the server. |
| rowClick | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<string>` | Emitted when a row in the process list is clicked. |
| rowsSelected | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any[]>` | Emitted when rows are selected/unselected. |
success | [`EventEmitter`](https://angular.io/api/core/EventEmitter)`<any>` | Emitted when the list of process instances has been loaded successfully from the server. |
## Details
You can define a custom schema for the list in the `app.config.json` file and access it with the
`presetColumn` property as shown below:
```json
"adf-cloud-process-list": {
"presets": {
"customSchema": [
{
"key": "name",
"type": "text",
"title": "name",
"sortable": true
}],
"default": [
{
"key": "name",
"type": "text",
"title": "name",
"sortable": true
}],
}
}
```
```html
<adf-cloud-process-list
[applicationName]="'appName'">
</adf-cloud-process-list>
```
You can also define the schema in the HTML using the
[Data column component](../core/data-column.component.md). You can combine this with schema
information defined in `app.config.json` as in the example below:
```json
"adf-cloud-process-list": {
"presets": {
"customSchema": [
{
"key": "id",
"type": "text",
"title": "Id",
"sortable": true
}],
"default": [
{
"key": "name",
"type": "text",
"title": "name",
"sortable": true
}],
}
}
```
<!-- {% raw %} -->
```html
<adf-cloud-process-list
[applicationName]="'appName'" >
<data-columns>
<data-column key="key" title="title" class="full-width name-column">
<ng-template let-entry="$implicit">
<div>{{getFullName(entry.row.obj.assignee)}}</div>
</ng-template>
</data-column>
</data-columns>
</adf-cloud-process-list>
```
### Setting Sorting Order for the list
you can pass sorting order as shown in the example below:
```ts
let sorting = [{ orderBy: 'status', direction: 'desc' }];
```
```html
<adf-cloud-process-list
[applicationName]="'appName'"
[sort]="sorting">
</adf-cloud-process-list>
```
<!-- {% endraw %} -->
### Pagination strategy
The Process Instance List also supports pagination:
```html
<adf-cloud-process-list
[appId]="'1'"
[page]="page"
[size]="size"
#processList>
</adf-cloud-process-list>
<adf-pagination
*ngIf="processList"
[target]="processList"
[supportedPageSizes]="supportedPages"
#processListPagination>
</adf-pagination>
```
## See also
- [Data column component](../core/data-column.component.md)
- [Data Table Adapter interface](../core/datatable-adapter.interface.md)
- [Pagination component](../core/pagination.component.md)

View File

@@ -1,3 +1,10 @@
{ {
"TEST_KEY": "MY TEST" "TEST_KEY": "MY TEST",
"ADF-PROCESS-LIST-CLOUD": {
"MESSAGES": {
"TITLE": "No Processes Found",
"SUBTITLE":"Create a new process that you want to easily find later",
"NONE": "No process instance filter selected."
}
}
} }

View File

@@ -0,0 +1,28 @@
<adf-datatable #dataTable
[rows]="rows"
[columns]="columns"
[loading]="isLoading"
[selectionMode]="selectionMode"
[multiselect]="multiselect"
(rowClick)="onRowClick($event)"
(row-keyup)="onRowKeyUp($event)">
<loading-content-template>
<ng-template>
<mat-progress-spinner
class="adf-cloud-process-list-loading-margin"
[color]="'primary'"
[mode]="'indeterminate'">
</mat-progress-spinner>
</ng-template>
</loading-content-template>
<no-content-template>
<ng-template>
<adf-empty-content *ngIf="!emptyCustomContent"
icon="assessment"
[title]="'ADF-PROCESS-LIST-CLOUD.MESSAGES.TITLE' | translate"
[subtitle]="'ADF-PROCESS-LIST-CLOUD.MESSAGES.SUBTITLE'| translate">
</adf-empty-content>
<ng-content select="adf-empty-custom-content"></ng-content>
</ng-template>
</no-content-template>
</adf-datatable>

View File

@@ -0,0 +1,19 @@
@mixin adf-process-filters-cloud-theme($theme) {
.adf {
&-cloud-process-list-loading-margin {
margin-left: calc((100% - 100px) / 2);
margin-right: calc((100% - 100px) / 2);
}
}
.no-content-message {
font-size: 16px;
font-weight: bold;
text-align: center;
opacity: 0.54;
}
}

View File

@@ -0,0 +1,279 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Component, SimpleChange, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, async } from '@angular/core/testing';
import { By } from '@angular/platform-browser';
import { AppConfigService, setupTestBed, CoreModule, DataTableModule } from '@alfresco/adf-core';
import { DataRowEvent, ObjectDataRow } from '@alfresco/adf-core';
import { ProcessListCloudService } from '../services/process-list-cloud.service';
import { ProcessListCloudComponent } from './process-list-cloud.component';
import { fakeProcessCloudList, fakeCustomSchema } from '../mock/process-list-service.mock';
import { of } from 'rxjs';
import { ProcessListCloudTestingModule } from '../testing/process-list.testing.module';
import { ProcessListCloudModule } from '../process-list-cloud.module';
@Component({
template: `
<adf-cloud-process-list #processListCloud>
<data-columns>
<data-column key="name" title="ADF_TASK_LIST.PROPERTIES.NAME" class="full-width name-column"></data-column>
<data-column key="created" title="ADF_TASK_LIST.PROPERTIES.CREATED" class="hidden"></data-column>
<data-column key="startedBy" title="ADF_TASK_LIST.PROPERTIES.CREATED" class="desktop-only dw-dt-col-3 ellipsis-cell">
<ng-template let-entry="$implicit">
<div>{{getFullName(entry.row.obj.startedBy)}}</div>
</ng-template>
</data-column>
</data-columns>
</adf-cloud-process-list>`
})
class CustomTaskListComponent {
@ViewChild(ProcessListCloudComponent)
processListCloud: ProcessListCloudComponent;
}
@Component({
template: `
<adf-cloud-process-list>
<adf-empty-custom-content>
<p id="custom-id">TEST</p>
</adf-empty-custom-content>
</adf-cloud-process-list>
`
})
class EmptyTemplateComponent {
}
/*tslint:disable*/
fdescribe('ProcessListCloudComponent', () => {
let component: ProcessListCloudComponent;
let fixture: ComponentFixture<ProcessListCloudComponent>;
let appConfig: AppConfigService;
let processListCloudService: ProcessListCloudService;
setupTestBed({
imports: [
ProcessListCloudTestingModule, ProcessListCloudModule
],
providers: [ProcessListCloudService]
});
beforeEach(() => {
appConfig = TestBed.get(AppConfigService);
processListCloudService = TestBed.get(ProcessListCloudService);
fixture = TestBed.createComponent(ProcessListCloudComponent);
component = fixture.componentInstance;
appConfig.config = Object.assign(appConfig.config, {
'adf-cloud-process-list': {
'presets': {
'fakeCustomSchema': [
{
'key': 'fakeName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.FAKE',
'sortable': true
},
{
'key': 'fakeTaskName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.TASK_FAKE',
'sortable': true
}
]
}
}
});
});
afterEach(() => {
fixture.destroy();
});
it('should use the default schemaColumn as default', () => {
component.ngAfterContentInit();
expect(component.columns).toBeDefined();
expect(component.columns.length).toEqual(2);
});
it('should use the custom schemaColumn from app.config.json', () => {
component.presetColumn = 'fakeCustomSchema';
component.ngAfterContentInit();
fixture.detectChanges();
expect(component.columns).toEqual(fakeCustomSchema);
});
it('should fetch custom schemaColumn when the input presetColumn is defined', () => {
component.presetColumn = 'fakeCustomSchema';
fixture.detectChanges();
expect(component.columns).toBeDefined();
expect(component.columns.length).toEqual(2);
});
it('should return the results if an application name is given', (done) => {
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
let appName = new SimpleChange(null, 'FAKE-APP-NAME', true);
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.rows.length).toEqual(3);
expect(component.rows[0].entry['serviceName']).toEqual('simple-app-rb');
expect(component.rows[0].entry['serviceFullName']).toEqual('simple-app-rb');
expect(component.rows[0].entry['serviceVersion']).toBe('');
expect(component.rows[0].entry['appName']).toBe('easy-peasy-japanesey');
expect(component.rows[0].entry['appVersion']).toBe('');
expect(component.rows[0].entry['serviceType']).toBeNull();
expect(component.rows[0].entry['id']).toBe('69eddfa7-d781-11e8-ae24-0a58646001fa');
expect(component.rows[0].entry['name']).toEqual('starring');
expect(component.rows[0].entry['description']).toBeNull();
expect(component.rows[0].entry['processDefinitionId']).toBe('BasicProcess:1:d05062f1-c6fb-11e8-ae24-0a58646001fa');
expect(component.rows[0].entry['processDefinitionKey']).toBe('BasicProcess');
expect(component.rows[0].entry['initiator']).toBe('devopsuser');
expect(component.rows[0].entry['startDate']).toBe(1540381146275);
expect(component.rows[0].entry['businessKey']).toBe('MyBusinessKey');
expect(component.rows[0].entry['status']).toBe('RUNNING');
expect(component.rows[0].entry['lastModified']).toBe(1540381146276);
expect(component.rows[0].entry['lastModifiedTo']).toBeNull();
expect(component.rows[0].entry['lastModifiedFrom']).toBeNull();
done();
});
component.applicationName = appName.currentValue;
component.ngOnChanges({ 'appName': appName });
fixture.detectChanges();
});
it('should reload tasks when reload() is called', (done) => {
component.applicationName = 'fake';
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
done();
});
fixture.detectChanges();
component.reload();
});
it('should emit row click event', (done) => {
let row = new ObjectDataRow({
entry: {
id: '999'
}
});
let rowEvent = new DataRowEvent(row, null);
component.rowClick.subscribe(taskId => {
expect(taskId).toEqual('999');
expect(component.getCurrentId()).toEqual('999');
done();
});
component.onRowClick(rowEvent);
});
describe('component changes', () => {
beforeEach(() => {
component.rows = fakeProcessCloudList.list.entries;
fixture.detectChanges();
});
it('should NOT reload the tasks if the landingTaskId is the same of the current task', () => {
spyOn(component, 'reload').and.stub();
component.currentInstanceId = '999';
component.rows = [{ entry: { id: '999', name: 'Fake-name' } }];
const landingTaskId = '999';
let change = new SimpleChange('999', landingTaskId, true);
component.ngOnChanges({ 'landingTaskId': change });
expect(component.reload).not.toHaveBeenCalled();
expect(component.rows.length).toEqual(1);
});
it('should reload the tasks if the loadingTaskId is different from the current task', (done) => {
component.currentInstanceId = '999';
component.rows = [{ id: '999', name: 'Fake-name' }];
const landingTaskId = '888';
let change = new SimpleChange(null, landingTaskId, true);
component.applicationName = 'fake';
spyOn(processListCloudService, 'getProcessByRequest').and.returnValue(of(fakeProcessCloudList));
component.success.subscribe((res) => {
expect(res).toBeDefined();
expect(component.rows).toBeDefined();
expect(component.rows.length).toEqual(3);
done();
});
component.ngOnChanges({ 'landingTaskId': change });
});
});
describe('Injecting custom colums for tasklist - CustomTaskListComponent', () => {
let fixtureCustom: ComponentFixture<CustomTaskListComponent>;
let componentCustom: CustomTaskListComponent;
setupTestBed({
imports: [CoreModule.forRoot()],
declarations: [ProcessListCloudComponent, CustomTaskListComponent],
providers: [ProcessListCloudService]
});
beforeEach(() => {
fixtureCustom = TestBed.createComponent(CustomTaskListComponent);
fixtureCustom.detectChanges();
componentCustom = fixtureCustom.componentInstance;
});
afterEach(() => {
fixtureCustom.destroy();
});
it('should create instance of CustomTaskListComponent', () => {
expect(componentCustom instanceof CustomTaskListComponent).toBe(true, 'should create CustomTaskListComponent');
});
it('should fetch custom schemaColumn from html', () => {
fixture.detectChanges();
expect(componentCustom.processListCloud.columnList).toBeDefined();
expect(componentCustom.processListCloud.columns[0]['title']).toEqual('ADF_TASK_LIST.PROPERTIES.NAME');
expect(componentCustom.processListCloud.columns[1]['title']).toEqual('ADF_TASK_LIST.PROPERTIES.CREATED');
expect(componentCustom.processListCloud.columns.length).toEqual(3);
});
});
describe('Creating an empty custom template - EmptyTemplateComponent', () => {
let fixtureEmpty: ComponentFixture<EmptyTemplateComponent>;
setupTestBed({
imports: [ProcessListCloudModule, ProcessListCloudTestingModule, DataTableModule],
declarations: [EmptyTemplateComponent]
});
beforeEach(() => {
fixtureEmpty = TestBed.createComponent(EmptyTemplateComponent);
fixtureEmpty.detectChanges();
});
afterEach(() => {
fixtureEmpty.destroy();
});
it('should render the custom template', async(() => {
fixtureEmpty.whenStable().then(() => {
fixtureEmpty.detectChanges();
expect(fixtureEmpty.debugElement.query(By.css('#custom-id'))).not.toBeNull();
expect(fixtureEmpty.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
});
}));
});
});

View File

@@ -0,0 +1,247 @@
import { Component, ViewEncapsulation, OnChanges, AfterContentInit, ContentChild, Output, EventEmitter, SimpleChanges, SimpleChange, Input } from '@angular/core';
import { DataTableSchema, PaginatedComponent,
EmptyCustomContentDirective, AppConfigService,
UserPreferencesService, PaginationModel,
UserPreferenceValues, DataRowEvent } from '@alfresco/adf-core';
import { ProcessListCloudService } from '../services/process-list-cloud.service';
import { BehaviorSubject } from 'rxjs';
import { processCloudPresetsDefaultModel } from '../models/process-cloud-preset.model';
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
import { MinimalNodeEntity } from 'alfresco-js-api';
@Component({
selector: 'adf-cloud-process-list',
templateUrl: './process-list-cloud.component.html',
styleUrls: ['./process-list-cloud.component.scss'],
encapsulation: ViewEncapsulation.None
})
export class ProcessListCloudComponent extends DataTableSchema implements OnChanges, AfterContentInit, PaginatedComponent {
static PRESET_KEY = 'adf-cloud-process-list.presets';
@ContentChild(EmptyCustomContentDirective)
emptyCustomContent: EmptyCustomContentDirective;
@Input()
applicationName: string = '';
@Input()
appVersion: string = '';
@Input()
initiator: string = '';
@Input()
id: string = '';
@Input()
name: string = '';
@Input()
processDefinitionId: string = '';
@Input()
processDefinitionKey: string = '';
@Input()
serviceFullName: string = '';
@Input()
serviceName: string = '';
@Input()
serviceType: string = '';
@Input()
serviceVersion: string = '';
@Input()
status: string = '';
@Input()
businessKey: string = '';
@Input()
selectFirstRow: boolean = true;
@Input()
landingTaskId: string;
@Input()
selectionMode: string = 'single'; // none|single|multiple
/** Toggles multiple row selection, renders checkboxes at the beginning of each row */
@Input()
multiselect: boolean = false;
@Input()
sorting: ProcessListCloudSortingModel[];
@Output()
rowClick: EventEmitter<string> = new EventEmitter<string>();
@Output()
rowsSelected: EventEmitter<any[]> = new EventEmitter<any[]>();
@Output()
error: EventEmitter<any> = new EventEmitter<any>();
@Output()
success: EventEmitter<any> = new EventEmitter<any>();
pagination: BehaviorSubject<PaginationModel>;
size: number;
skipCount: number = 0;
currentInstanceId: string;
selectedInstances: any[];
isLoading = false;
rows: any[] = [];
requestNode: ProcessQueryCloudRequestModel;
constructor(private processListCloudService: ProcessListCloudService,
appConfigService: AppConfigService,
private userPreferences: UserPreferencesService) {
super(appConfigService, ProcessListCloudComponent.PRESET_KEY, processCloudPresetsDefaultModel);
this.size = userPreferences.paginationSize;
this.userPreferences.select(UserPreferenceValues.PaginationSize).subscribe((pageSize) => {
this.size = pageSize;
});
this.pagination = new BehaviorSubject<PaginationModel>(<PaginationModel> {
maxItems: this.size,
skipCount: 0,
totalItems: 0
});
}
ngAfterContentInit() {
this.createDatatableSchema();
}
ngOnChanges(changes: SimpleChanges) {
if (this.isPropertyChanged(changes) &&
!this.isEqualToCurrentId(changes['landingTaskId'])) {
this.reload();
}
}
getCurrentId(): string {
return this.currentInstanceId;
}
reload() {
this.requestNode = this.createRequestNode();
if (this.requestNode.appName) {
this.load(this.requestNode);
} else {
this.rows = [];
}
}
private load(requestNode: ProcessQueryCloudRequestModel) {
this.isLoading = true;
this.processListCloudService.getProcessByRequest(requestNode).subscribe(
(processes) => {
this.rows = processes.list.entries;
this.selectTask(this.landingTaskId);
this.success.emit(processes);
this.isLoading = false;
this.pagination.next(processes.list.pagination);
}, (error) => {
this.error.emit(error);
this.isLoading = false;
});
}
private isEqualToCurrentId(landingTaskChanged: SimpleChange): boolean {
return landingTaskChanged && this.currentInstanceId === landingTaskChanged.currentValue;
}
private isPropertyChanged(changes: SimpleChanges): boolean {
for (let property in changes) {
if (changes.hasOwnProperty(property)) {
if (changes[property] &&
(changes[property].currentValue !== changes[property].previousValue)) {
return true;
}
}
}
return false;
}
selectTask(taskIdSelected: string) {
if (!this.isListEmpty()) {
let dataRow: any = null;
if (taskIdSelected) {
dataRow = this.rows.find((currentRow: MinimalNodeEntity) => {
return currentRow.entry.id === taskIdSelected;
});
}
if (!dataRow && this.selectFirstRow) {
dataRow = this.rows[0];
}
if (dataRow) {
dataRow.isSelected = true;
this.currentInstanceId = dataRow.entry.id;
}
} else {
this.currentInstanceId = null;
}
}
isListEmpty(): boolean {
return !this.rows || this.rows.length === 0;
}
updatePagination(pagination: PaginationModel) {
this.size = pagination.maxItems;
this.skipCount = pagination.skipCount;
this.pagination.next(pagination);
this.reload();
}
onRowClick(item: DataRowEvent) {
this.currentInstanceId = item.value.getValue('entry.id');
this.rowClick.emit(this.currentInstanceId);
}
onRowSelect(event: CustomEvent) {
this.selectedInstances = [...event.detail.selection];
this.rowsSelected.emit(this.selectedInstances);
}
onRowUnselect(event: CustomEvent) {
this.selectedInstances = [...event.detail.selection];
this.rowsSelected.emit(this.selectedInstances);
}
onRowKeyUp(event: CustomEvent) {
if (event.detail.keyboardEvent.key === 'Enter') {
event.preventDefault();
this.currentInstanceId = event.detail.row.getValue('entry.id');
this.rowClick.emit(this.currentInstanceId);
}
}
private createRequestNode(): ProcessQueryCloudRequestModel {
let requestNode = {
appName: this.applicationName,
appVersion: this.appVersion,
maxItems: this.size,
skipCount: this.skipCount,
initiator: this.initiator,
id: this.id,
name: this.name,
processDefinitionId: this.processDefinitionId,
processDefinitionKey: this.processDefinitionKey,
serviceFullName: this.serviceFullName,
serviceName: this.serviceName,
serviceType: this.serviceType,
serviceVersion: this.serviceVersion,
status: this.status,
businessKey: this.businessKey,
sorting: this.sorting
};
return new ProcessQueryCloudRequestModel(requestNode);
}
}

View File

@@ -0,0 +1,114 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the License);
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an AS IS BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ObjectDataColumn } from '@alfresco/adf-core';
export const fakeProcessCloudList = {
list: {
entries: [
{
entry: {
serviceName: 'simple-app-rb',
serviceFullName: 'simple-app-rb',
serviceVersion: '',
appName: 'easy-peasy-japanesey',
appVersion: '',
serviceType: null,
id: '69eddfa7-d781-11e8-ae24-0a58646001fa',
name: 'starring',
description: null,
processDefinitionId: 'BasicProcess:1:d05062f1-c6fb-11e8-ae24-0a58646001fa',
processDefinitionKey: 'BasicProcess',
initiator: 'devopsuser',
startDate: 1540381146275,
businessKey: 'MyBusinessKey',
status: 'RUNNING',
lastModified: 1540381146276,
lastModifiedTo: null,
lastModifiedFrom: null
}
},
{
entry: {
serviceName: 'simple-app-rb',
serviceFullName: 'simple-app-rb',
serviceVersion: '',
appName: 'easy-peasy-japanesey',
appVersion: '',
serviceType: null,
id: '8b3f625f-d781-11e8-ae24-0a58646001fa',
name: null,
description: null,
processDefinitionId: 'BasicProcess:1:d05062f1-c6fb-11e8-ae24-0a58646001fa',
processDefinitionKey: 'BasicProcess',
initiator: 'devopsuser',
startDate: 1540381202174,
businessKey: 'MyBusinessKey',
status: 'RUNNING',
lastModified: 1540381202174,
lastModifiedTo: null,
lastModifiedFrom: null
}
},
{
entry: {
serviceName: 'simple-app-rb',
serviceFullName: 'simple-app-rb',
serviceVersion: '',
appName: 'easy-peasy-japanesey',
appVersion: '',
serviceType: null,
id: '87c12637-d783-11e8-ae24-0a58646001fa',
name: null,
description: null,
processDefinitionId: 'BasicProcess:1:d05062f1-c6fb-11e8-ae24-0a58646001fa',
processDefinitionKey: 'BasicProcess',
initiator: 'superadminuser',
startDate: 1540382055307,
businessKey: 'MyBusinessKey',
status: 'RUNNING',
lastModified: 1540382055308,
lastModifiedTo: null,
lastModifiedFrom: null
}
}
],
pagination: {
skipCount: 0,
maxItems: 100,
count: 3,
hasMoreItems: false,
totalItems: 3
}
}
};
export let fakeCustomSchema =
[
new ObjectDataColumn({
'key': 'fakeName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.FAKE',
'sortable': true
}),
new ObjectDataColumn({
'key': 'fakeTaskName',
'type': 'text',
'title': 'ADF_TASK_LIST.PROPERTIES.TASK_FAKE',
'sortable': true
})
];

View File

@@ -0,0 +1,34 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export let processCloudPresetsDefaultModel = {
'default': [
{
'key': 'name',
'type': 'text',
'title': 'ADF_PROCESS_LIST.PROPERTIES.NAME',
'sortable': true
},
{
'key': 'created',
'type': 'text',
'title': 'ADF_PROCESS_LIST.PROPERTIES.CREATED',
'cssClass': 'hidden',
'sortable': true
}
]
};

View File

@@ -0,0 +1,67 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { ProcessListCloudSortingModel } from './process-list-sorting.model';
export class ProcessQueryCloudRequestModel {
appName: string;
appVersion?: string;
description?: string;
initiator?: null;
id?: string;
name?: string;
processDefinitionId?: string;
processDefinitionKey?: string;
serviceFullName?: string;
serviceName?: string;
serviceType?: string;
serviceVersion?: string;
status?: string;
startDate?: string;
businessKey?: string;
lastModified?: string;
lastModifiedTo?: string;
lastModifiedFrom?: string;
maxItems: number;
skipCount: number;
sorting?: ProcessListCloudSortingModel[];
constructor(obj?: any) {
if (obj) {
this.appName = obj.appName;
this.appVersion = obj.appVersion;
this.description = obj.description;
this.initiator = obj.initiator;
this.id = obj.id;
this.name = obj.name;
this.processDefinitionId = obj.processDefinitionId;
this.processDefinitionKey = obj.processDefinitionKey;
this.serviceFullName = obj.serviceFullName;
this.serviceName = obj.serviceName;
this.serviceType = obj.serviceType;
this.serviceVersion = obj.serviceVersion;
this.status = obj.status;
this.startDate = obj.startDate;
this.businessKey = obj.businessKey;
this.lastModified = obj.lastModified;
this.lastModifiedTo = obj.lastModifiedTo;
this.lastModifiedFrom = obj.lastModifiedFrom;
this.maxItems = obj.maxItems;
this.skipCount = obj.skipCount;
this.sorting = obj.sorting;
}
}
}

View File

@@ -0,0 +1,27 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export class ProcessListCloudSortingModel {
orderBy: string;
direction: string;
constructor(obj: any) {
if (obj) {
this.orderBy = obj.orderBy;
this.direction = obj.direction;
}
}
}

View File

@@ -0,0 +1,13 @@
import { ProcessListCloudModule } from './process-list-cloud.module';
describe('ProcessListCloudModule', () => {
let processListCloudModule: ProcessListCloudModule;
beforeEach(() => {
processListCloudModule = new ProcessListCloudModule();
});
it('should create an instance', () => {
expect(processListCloudModule).toBeTruthy();
});
});

View File

@@ -0,0 +1,26 @@
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { ProcessListCloudComponent } from './components/process-list-cloud.component';
import { MaterialModule } from '../material.module';
import { TranslateModule, TranslateLoader } from '@ngx-translate/core';
import { TranslateLoaderService, DataTableModule, TemplateModule } from '@alfresco/adf-core';
import { ProcessListCloudService } from './services/process-list-cloud.service';
@NgModule({
imports: [
CommonModule,
TranslateModule.forRoot({
loader: {
provide: TranslateLoader,
useClass: TranslateLoaderService
}
}),
MaterialModule,
DataTableModule,
TemplateModule
],
declarations: [ProcessListCloudComponent],
exports: [ProcessListCloudComponent],
providers: [ProcessListCloudService]
})
export class ProcessListCloudModule { }

View File

@@ -0,0 +1,22 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
export * from './components/process-list-cloud.component';
export * from './models/process-cloud-query-request.model';
export * from './models/process-cloud-preset.model';
export * from './models/process-list-sorting.model';
export * from './process-list-cloud.module';

View File

@@ -0,0 +1,134 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { async } from '@angular/core/testing';
import { setupTestBed } from '@alfresco/adf-core';
import { fakeProcessCloudList } from '../mock/process-list-service.mock';
import { AlfrescoApiServiceMock, LogService, AppConfigService, StorageService, CoreModule } from '@alfresco/adf-core';
import { ProcessListCloudService } from './process-list-cloud.service';
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
describe('Activiti ProcessList Cloud Service', () => {
let service: ProcessListCloudService;
let alfrescoApiMock: AlfrescoApiServiceMock;
function returFakeProcessListResults() {
return {
oauth2Auth: {
callCustomApi: () => {
return Promise.resolve(fakeProcessCloudList);
}
}
};
}
function returnCallQueryParameters() {
return {
oauth2Auth: {
callCustomApi: (queryUrl, operation, context, queryParams) => {
return Promise.resolve(queryParams);
}
}
};
}
function returnCallUrl() {
return {
oauth2Auth: {
callCustomApi: (queryUrl, operation, context, queryParams) => {
return Promise.resolve(queryUrl);
}
}
};
}
setupTestBed({
imports: [
CoreModule.forRoot()
]
});
beforeEach(async(() => {
alfrescoApiMock = new AlfrescoApiServiceMock(new AppConfigService(null), new StorageService());
service = new ProcessListCloudService(alfrescoApiMock,
new AppConfigService(null),
new LogService(new AppConfigService(null)));
}));
it('should return the processes', (done) => {
let processRequest: ProcessQueryCloudRequestModel = <ProcessQueryCloudRequestModel> { appName: 'fakeName' };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returFakeProcessListResults);
service.getProcessByRequest(processRequest).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.list.entries.length).toBe(3);
expect(res.list.entries[0].entry.appName).toBe('easy-peasy-japanesey');
expect(res.list.entries[1].entry.appName).toBe('easy-peasy-japanesey');
expect(res.list.entries[1].entry.appName).toBe('easy-peasy-japanesey');
done();
});
});
it('should append to the call all the parameters', (done) => {
let processRequest: ProcessQueryCloudRequestModel = <ProcessQueryCloudRequestModel> { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallQueryParameters);
service.getProcessByRequest(processRequest).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.skipCount).toBe(0);
expect(res.maxItems).toBe(20);
expect(res.service).toBe('fake-service');
done();
});
});
it('should concat the app name to the request url', (done) => {
let processRequest: ProcessQueryCloudRequestModel = <ProcessQueryCloudRequestModel> { appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service' };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallUrl);
service.getProcessByRequest(processRequest).subscribe((requestUrl) => {
expect(requestUrl).toBeDefined();
expect(requestUrl).not.toBeNull();
expect(requestUrl).toContain('/fakeName-query/v1/process-instances');
done();
});
});
it('should concat the sorting to append as parameters', (done) => {
let processRequest: ProcessQueryCloudRequestModel = <ProcessQueryCloudRequestModel> {
appName: 'fakeName', skipCount: 0, maxItems: 20, service: 'fake-service',
sorting: [{ orderBy: 'NAME', direction: 'DESC' }, { orderBy: 'TITLE', direction: 'ASC' }]
};
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallQueryParameters);
service.getProcessByRequest(processRequest).subscribe((res) => {
expect(res).toBeDefined();
expect(res).not.toBeNull();
expect(res.sort).toBe('NAME,DESC&TITLE,ASC');
done();
});
});
it('should return an error when app name is not specified', (done) => {
let processRequest: ProcessQueryCloudRequestModel = <ProcessQueryCloudRequestModel> { appName: null };
spyOn(alfrescoApiMock, 'getInstance').and.callFake(returnCallUrl);
service.getProcessByRequest(processRequest).subscribe(
() => { },
(error) => {
expect(error).toBe('Appname not configured');
done();
}
);
});
});

View File

@@ -0,0 +1,89 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Injectable } from '@angular/core';
import { AlfrescoApiService, AppConfigService, LogService } from '@alfresco/adf-core';
import { ProcessQueryCloudRequestModel } from '../models/process-cloud-query-request.model';
import { Observable, from, throwError } from 'rxjs';
import { ProcessListCloudSortingModel } from '../models/process-list-sorting.model';
@Injectable()
export class ProcessListCloudService {
contentTypes = ['application/json'];
accepts = ['application/json'];
constructor(private apiService: AlfrescoApiService,
private appConfigService: AppConfigService,
private logService: LogService) {
}
getProcessByRequest(requestNode: ProcessQueryCloudRequestModel): Observable<any> {
if (requestNode.appName) {
let queryUrl = this.buildQueryUrl(requestNode);
let queryParams = this.buildQueryParams(requestNode);
let sortingParams = this.buildSortingParam(requestNode.sorting);
if (sortingParams) {
queryParams['sort'] = sortingParams;
}
return from(this.apiService.getInstance()
.oauth2Auth.callCustomApi(queryUrl, 'GET',
null, queryParams, null,
null, null, null, this.contentTypes,
this.accepts, Object, null, null)
);
} else {
this.logService.error('Appname is mandatory for querying task');
return throwError('Appname not configured');
}
}
private buildQueryUrl(requestNode: ProcessQueryCloudRequestModel) {
return `${this.appConfigService.get('bpmHost', '')}/${requestNode.appName}-query/v1/process-instances`;
}
private isPropertyValueValid(requestNode, property) {
return requestNode[property] !== '' && requestNode[property] !== null && requestNode[property] !== undefined;
}
private buildQueryParams(requestNode: ProcessQueryCloudRequestModel) {
let queryParam = {};
for (let property in requestNode) {
if (requestNode.hasOwnProperty(property) &&
!this.isExcludedField(property) &&
this.isPropertyValueValid(requestNode, property)) {
queryParam[property] = requestNode[property];
}
}
return queryParam;
}
private isExcludedField(property) {
return property === 'appName' || property === 'sorting';
}
private buildSortingParam(sortings: ProcessListCloudSortingModel[]): string {
let finalSorting: string = '';
if (sortings) {
for (let sort of sortings) {
if (!finalSorting) {
finalSorting = `${sort.orderBy},${sort.direction}`;
} else {
finalSorting = `${finalSorting}&${sort.orderBy},${sort.direction}`;
}
}
}
return encodeURI(finalSorting);
}
}

View File

@@ -0,0 +1,49 @@
/*!
* @license
* Copyright 2016 Alfresco Software, Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { NgModule } from '@angular/core';
import { NoopAnimationsModule } from '@angular/platform-browser/animations';
import { HttpClientModule } from '@angular/common/http';
import { ProcessListCloudModule } from '../process-list-cloud.module';
import {
AlfrescoApiService,
AlfrescoApiServiceMock,
AppConfigService,
AppConfigServiceMock,
StorageService,
LogService,
TranslationService,
TranslationMock,
UserPreferencesService,
ContextMenuModule
} from '@alfresco/adf-core';
@NgModule({
imports: [
HttpClientModule,
NoopAnimationsModule,
ProcessListCloudModule,
ContextMenuModule
],
providers: [
{ provide: AlfrescoApiService, useClass: AlfrescoApiServiceMock },
{ provide: AppConfigService, useClass: AppConfigServiceMock },
{ provide: TranslationService, useClass: TranslationMock },
StorageService,
LogService,
UserPreferencesService
]
})
export class ProcessListCloudTestingModule { }

View File

@@ -3,12 +3,14 @@ import { TRANSLATION_PROVIDER } from '@alfresco/adf-core';
import { AppListCloudModule } from './app-list-cloud/app-list-cloud.module'; import { AppListCloudModule } from './app-list-cloud/app-list-cloud.module';
import { TaskListCloudModule } from './task-list-cloud/task-list-cloud.module'; import { TaskListCloudModule } from './task-list-cloud/task-list-cloud.module';
import { TaskCloudModule } from './task-cloud/task-cloud.module'; import { TaskCloudModule } from './task-cloud/task-cloud.module';
import { ProcessListCloudModule } from './process-list-cloud/process-list-cloud.module';
@NgModule({ @NgModule({
imports: [ imports: [
AppListCloudModule, AppListCloudModule,
TaskListCloudModule, TaskListCloudModule,
TaskCloudModule TaskCloudModule,
ProcessListCloudModule
], ],
providers: [ providers: [
{ {
@@ -21,6 +23,6 @@ import { TaskCloudModule } from './task-cloud/task-cloud.module';
} }
], ],
declarations: [], declarations: [],
exports: [AppListCloudModule, TaskListCloudModule, TaskCloudModule] exports: [AppListCloudModule, TaskListCloudModule, TaskCloudModule, ProcessListCloudModule]
}) })
export class ProcessServicesCloudModule { } export class ProcessServicesCloudModule { }

View File

@@ -1,9 +1,11 @@
@import './../app-list-cloud/components/app-details-cloud.component'; @import './../app-list-cloud/components/app-details-cloud.component';
@import './../app-list-cloud/components/app-list-cloud.component'; @import './../app-list-cloud/components/app-list-cloud.component';
@import './../task-cloud/task-filters-cloud/task-filters-cloud.component.scss'; @import './../task-cloud/task-filters-cloud/task-filters-cloud.component.scss';
@import './../process-list-cloud/components/process-list-cloud.component.scss';
@mixin adf-process-services-cloud-theme($theme) { @mixin adf-process-services-cloud-theme($theme) {
@include adf-cloud-app-list-theme($theme); @include adf-cloud-app-list-theme($theme);
@include adf-cloud-app-details-theme($theme); @include adf-cloud-app-details-theme($theme);
@include adf-cloud-task-filters-theme($theme); @include adf-cloud-task-filters-theme($theme);
@include adf-process-filters-cloud-theme($theme);
} }

View File

@@ -19,3 +19,4 @@ export * from './lib/process-services-cloud.module';
export * from './lib/app-list-cloud/public-api'; export * from './lib/app-list-cloud/public-api';
export * from './lib/task-list-cloud/public-api'; export * from './lib/task-list-cloud/public-api';
export * from './lib/task-cloud/public-api'; export * from './lib/task-cloud/public-api';
export * from './lib/process-list-cloud/public_api';

View File

@@ -115,6 +115,13 @@ export let fakeProcessInstancesWithNoName = {
] ]
}; };
export let fakeProcessInstancesEmpty = {
size: 0,
total: 0,
start: 0,
data: []
};
export let fakeProcessCustomSchema = [ export let fakeProcessCustomSchema = [
new ObjectDataColumn({ new ObjectDataColumn({
key: 'fakeName', key: 'fakeName',

View File

@@ -104,3 +104,10 @@ export let fakeColumnSchema = {
], ],
fakeCustomSchema fakeCustomSchema
}; };
export let fakeEmptyTask = {
size: 0,
start: 0,
total: 0,
data: []
};

View File

@@ -15,20 +15,21 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, SimpleChange, ViewChild, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { Component, SimpleChange, ViewChild } from '@angular/core';
import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing'; import { async, ComponentFixture, fakeAsync, TestBed, tick } from '@angular/core/testing';
import { of, throwError } from 'rxjs'; import { of, throwError } from 'rxjs';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { ProcessInstanceListComponent } from './process-list.component'; import { ProcessInstanceListComponent } from './process-list.component';
import { AppConfigService, setupTestBed, CoreModule } from '@alfresco/adf-core'; import { AppConfigService, setupTestBed, CoreModule, DataTableModule } from '@alfresco/adf-core';
import { DataRowEvent, ObjectDataRow, ObjectDataTableAdapter } from '@alfresco/adf-core'; import { DataRowEvent, ObjectDataRow, ObjectDataTableAdapter } from '@alfresco/adf-core';
import { fakeProcessInstance, fakeProcessInstancesWithNoName } from '../../mock'; import { fakeProcessInstance, fakeProcessInstancesWithNoName, fakeProcessInstancesEmpty } from '../../mock';
import { ProcessService } from '../services/process.service'; import { ProcessService } from '../services/process.service';
import { ProcessTestingModule } from '../../testing/process.testing.module'; import { ProcessTestingModule } from '../../testing/process.testing.module';
import { fakeProcessCustomSchema } from '../../mock'; import { fakeProcessCustomSchema } from '../../mock';
import { ProcessListModule } from 'process-list/process-list.module';
describe('ProcessInstanceListComponent', () => { describe('ProcessInstanceListComponent', () => {
@@ -506,27 +507,28 @@ describe('CustomProcessListComponent', () => {
@Component({ @Component({
template: ` template: `
<adf-process-instance-list> <adf-process-instance-list [appId]="1">
<adf-empty-content-holder> <adf-empty-custom-content>
<p id="custom-id"> No Process Instance</p> <p id="custom-id"> No Process Instance</p>
</adf-empty-content-holder> </adf-empty-custom-content>
</adf-process-instance-list> </adf-process-instance-list>
` `
}) })
class EmptyTemplateComponent { class EmptyTemplateComponent {
} }
describe('Process List: Custom EmptyTemplateComponent', () => { describe('Process List: Custom EmptyTemplateComponent', () => {
let fixture: ComponentFixture<EmptyTemplateComponent>; let fixture: ComponentFixture<EmptyTemplateComponent>;
let processService: ProcessService;
setupTestBed({ setupTestBed({
imports: [ProcessTestingModule], imports: [ProcessTestingModule, ProcessListModule, DataTableModule],
declarations: [EmptyTemplateComponent], declarations: [EmptyTemplateComponent]
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
}); });
beforeEach(() => { beforeEach(() => {
fixture = TestBed.createComponent(EmptyTemplateComponent); fixture = TestBed.createComponent(EmptyTemplateComponent);
processService = TestBed.get(ProcessService);
spyOn(processService, 'getProcessInstances').and.returnValue(of(fakeProcessInstancesEmpty));
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -534,13 +536,14 @@ describe('Process List: Custom EmptyTemplateComponent', () => {
fixture.destroy(); fixture.destroy();
}); });
it('should render the custom template', async(() => { it('should render the custom template', (done) => {
fixture.whenStable().then(() => { fixture.whenStable().then(() => {
fixture.detectChanges(); fixture.detectChanges();
let title = fixture.debugElement.query(By.css('#custom-id')); let title = fixture.debugElement.query(By.css('#custom-id'));
expect(title).not.toBeNull(); expect(title).not.toBeNull();
expect(title.nativeElement.innerText).toBe('No Process Instance'); expect(title.nativeElement.innerText).toBe('No Process Instance');
expect(fixture.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); expect(fixture.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
done();
}); });
})); });
}); });

View File

@@ -15,17 +15,18 @@
* limitations under the License. * limitations under the License.
*/ */
import { Component, SimpleChange, ViewChild, CUSTOM_ELEMENTS_SCHEMA } from '@angular/core'; import { Component, SimpleChange, ViewChild } from '@angular/core';
import { ComponentFixture, TestBed, fakeAsync, tick } from '@angular/core/testing'; import { ComponentFixture, TestBed } from '@angular/core/testing';
import { By } from '@angular/platform-browser'; import { By } from '@angular/platform-browser';
import { AppConfigService, setupTestBed, CoreModule } from '@alfresco/adf-core'; import { AppConfigService, setupTestBed, CoreModule, DataTableModule } from '@alfresco/adf-core';
import { DataRowEvent, ObjectDataRow } from '@alfresco/adf-core'; import { DataRowEvent, ObjectDataRow } from '@alfresco/adf-core';
import { TaskListService } from '../services/tasklist.service'; import { TaskListService } from '../services/tasklist.service';
import { TaskListComponent } from './task-list.component'; import { TaskListComponent } from './task-list.component';
import { ProcessTestingModule } from '../../testing/process.testing.module'; import { ProcessTestingModule } from '../../testing/process.testing.module';
import { fakeGlobalTask, fakeCustomSchema } from '../../mock'; import { fakeGlobalTask, fakeCustomSchema, fakeEmptyTask } from '../../mock';
import { TranslateService } from '@ngx-translate/core'; import { TranslateService } from '@ngx-translate/core';
import { of } from 'rxjs'; import { of } from 'rxjs';
import { TaskListModule } from 'task-list/task-list.module';
declare let jasmine: any; declare let jasmine: any;
@@ -567,10 +568,10 @@ describe('CustomTaskListComponent', () => {
@Component({ @Component({
template: ` template: `
<adf-tasklist> <adf-tasklist [appId]="1">
<adf-empty-content-holder> <adf-empty-custom-content>
<p id="custom-id"></p> <p id="custom-id">CUSTOM EMPTY</p>
</adf-empty-content-holder> </adf-empty-custom-content>
</adf-tasklist> </adf-tasklist>
` `
}) })
@@ -580,19 +581,20 @@ class EmptyTemplateComponent {
describe('Task List: Custom EmptyTemplateComponent', () => { describe('Task List: Custom EmptyTemplateComponent', () => {
let fixture: ComponentFixture<EmptyTemplateComponent>; let fixture: ComponentFixture<EmptyTemplateComponent>;
let translateService: TranslateService; let translateService: TranslateService;
let taskListService: TaskListService;
setupTestBed({ setupTestBed({
imports: [ProcessTestingModule], imports: [ProcessTestingModule, TaskListModule, DataTableModule],
declarations: [EmptyTemplateComponent], declarations: [EmptyTemplateComponent]
schemas: [ CUSTOM_ELEMENTS_SCHEMA ]
}); });
beforeEach(() => { beforeEach(() => {
translateService = TestBed.get(TranslateService); translateService = TestBed.get(TranslateService);
taskListService = TestBed.get(TaskListService);
spyOn(translateService, 'get').and.callFake((key) => { spyOn(translateService, 'get').and.callFake((key) => {
return of(key); return of(key);
}); });
spyOn(taskListService, 'findTasksByState').and.returnValue(of(fakeEmptyTask));
fixture = TestBed.createComponent(EmptyTemplateComponent); fixture = TestBed.createComponent(EmptyTemplateComponent);
fixture.detectChanges(); fixture.detectChanges();
}); });
@@ -601,10 +603,12 @@ describe('Task List: Custom EmptyTemplateComponent', () => {
fixture.destroy(); fixture.destroy();
}); });
it('should render the custom template', fakeAsync(() => { it('should render the custom template', (done) => {
fixture.detectChanges(); fixture.detectChanges();
tick(100); fixture.whenStable().then(() => {
expect(fixture.debugElement.query(By.css('#custom-id'))).not.toBeNull(); expect(fixture.debugElement.query(By.css('#custom-id'))).not.toBeNull();
expect(fixture.debugElement.query(By.css('.adf-empty-content'))).toBeNull(); expect(fixture.debugElement.query(By.css('.adf-empty-content'))).toBeNull();
})); done();
});
});
}); });