[ADF-4900] Card View and Metadata Components refactoring (#5592)

* [ADF-4900] Card View and Metadata Components refactoring

* CSS linting

* Unit test excluded

* Rebase branch

* Fix unit tests

* Fix linting

* Fix e2e tests

* Fix 2e2 tests

* Fix process-services e2e tests

* More fixes

* Fix more e2e tests

* Fix unit test

* Improve flaky unit test

* Fix process services e2e tests

* Update Process Header Cloud Page

* Fix linting

* Fix timing issue

* Lintintg

* Fix selectors

* Fix e2e tests

* Fix timing issue

* Fix C260328

* Fix spellcheck

* save screenshot

* performance issue

* Fix unit tests and e2e tests

* fix e2e

* refactoring

* fix lint

* fix e2e

* Fix C309698

* fix other e2e

* fix lint

* increase timeout

Co-authored-by: Eugenio Romano <eugenio.romano@alfresco.com>
This commit is contained in:
davidcanonieto
2020-04-27 00:09:52 +01:00
committed by GitHub
co-authored by Eugenio Romano
parent ebfeb053ce
commit 8f68899ce0
65 changed files with 1211 additions and 1214 deletions
@@ -1,9 +1,9 @@
<div class="adf-metadata-properties">
<mat-accordion displayMode="flat" [multi]="multi">
<mat-expansion-panel
*ngIf="displayDefaultProperties"
[expanded]="canExpandProperties()"
[attr.data-automation-id]="'adf-metadata-group-properties'" >
<mat-accordion displayMode="flat"
[multi]="multi">
<mat-expansion-panel *ngIf="displayDefaultProperties"
[expanded]="canExpandProperties()"
[attr.data-automation-id]="'adf-metadata-group-properties'">
<mat-expansion-panel-header>
<mat-panel-title role="heading">
{{ 'CORE.METADATA.BASIC.HEADER' | translate }}
@@ -23,10 +23,11 @@
<ng-container *ngIf="expanded">
<ng-container *ngIf="groupedProperties$ | async; else loading; let groupedProperties">
<div *ngFor="let group of groupedProperties; let first = first;" class="adf-metadata-grouped-properties-container">
<div *ngFor="let group of groupedProperties; let first = first;"
class="adf-metadata-grouped-properties-container">
<mat-expansion-panel *ngIf="showGroup(group) || editable"
[attr.data-automation-id]="'adf-metadata-group-' + group.title"
[expanded]="canExpandTheCard(group) || !displayDefaultProperties && first">
[attr.data-automation-id]="'adf-metadata-group-' + group.title"
[expanded]="canExpandTheCard(group) || !displayDefaultProperties && first">
<mat-expansion-panel-header>
<mat-panel-title>
{{ group.title | translate }}
@@ -51,4 +52,18 @@
</ng-template>
</ng-container>
</mat-accordion>
<div class="adf-metadata-action-buttons"
*ngIf="editable">
<button mat-button
(click)="cancelChanges()"
data-automation-id="reset-metadata"
[disabled]="!hasMetadataChanged">Cancel</button>
<button mat-raised-button
(click)="saveChanges()"
color="primary"
data-automation-id="save-metadata"
[disabled]="!hasMetadataChanged">Save</button>
</div>
</div>
@@ -13,9 +13,15 @@
height: 64px;
}
.mat-expansion-panel:not([class*=mat-elevation-z]) {
.mat-expansion-panel:not([class*='mat-elevation-z']) {
box-shadow: none;
}
}
&-metadata-action-buttons {
display: flex;
justify-content: space-evenly;
margin: 10px;
}
}
}
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { async, ComponentFixture, TestBed } from '@angular/core/testing';
import { async, ComponentFixture, TestBed, tick, fakeAsync } from '@angular/core/testing';
import { SimpleChange } from '@angular/core';
import { By } from '@angular/platform-browser';
import { Node } from '@alfresco/js-api';
@@ -108,49 +108,47 @@ describe('ContentMetadataComponent', () => {
});
describe('Saving', () => {
it('should save the node on itemUpdate', () => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
spyOn(nodesApiService, 'updateNode').and.callThrough();
it('itemUpdate', fakeAsync(() => {
spyOn(component, 'updateChanges').and.callThrough();
const property = <CardViewBaseItemModel> { key: 'properties.property-key', value: 'original-value' };
updateService.update(property, 'updated-value');
expect(nodesApiService.updateNode).toHaveBeenCalledWith('node-id', {
'property-key': 'updated-value'
});
});
tick(600);
expect(component.hasMetadataChanged).toEqual(true);
expect(component.updateChanges).toHaveBeenCalled();
expect(component.changedProperties).toEqual({ properties: { 'property-key': 'updated-value' } });
}));
it('should update the node on successful save', async(() => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
it('should save changedProperties on save click', fakeAsync(async () => {
component.editable = true;
const property = <CardViewBaseItemModel> { key: 'properties.property-key', value: 'original-value' };
const expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
return of(expectedNode);
});
updateService.update(property, 'updated-value');
tick(600);
fixture.whenStable().then(() => {
expect(component.node).toEqual(expectedNode);
});
fixture.detectChanges();
await fixture.whenStable();
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
saveButton.nativeElement.click();
await fixture.whenStable();
expect(component.node).toEqual(expectedNode);
expect(nodesApiService.updateNode).toHaveBeenCalled();
}));
it('should throw error on unsuccessful save', () => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
it('should throw error on unsuccessful save', fakeAsync(async (done) => {
const logService: LogService = TestBed.get(LogService);
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
return throwError(new Error('My bad'));
});
component.editable = true;
const property = <CardViewBaseItemModel> { key: 'properties.property-key', value: 'original-value' };
updateService.update(property, 'updated-value');
expect(logService.error).toHaveBeenCalledWith(new Error('My bad'));
});
it('should raise error message', (done) => {
const property = <CardViewBaseItemModel> { key: 'property-key', value: 'original-value' };
tick(600);
const sub = contentMetadataService.error.subscribe((err) => {
expect(logService.error).toHaveBeenCalledWith(new Error('My bad'));
expect(err.statusCode).toBe(0);
expect(err.message).toBe('METADATA.ERRORS.GENERIC');
sub.unsubscribe();
@@ -161,8 +159,33 @@ describe('ContentMetadataComponent', () => {
return throwError(new Error('My bad'));
});
updateService.update(property, 'updated-value');
});
fixture.detectChanges();
await fixture.whenStable();
const saveButton = fixture.debugElement.query(By.css('[data-automation-id="save-metadata"]'));
saveButton.nativeElement.click();
fixture.detectChanges();
}));
});
describe('Reseting', () => {
it('should reset changedProperties on reset click', async(async () => {
component.changedProperties = { properties: { 'property-key': 'updated-value' } };
component.hasMetadataChanged = true;
component.editable = true;
const expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
spyOn(nodesApiService, 'updateNode').and.callFake(() => {
return of(expectedNode);
});
fixture.detectChanges();
await fixture.whenStable();
const resetButton = fixture.debugElement.query(By.css('[data-automation-id="reset-metadata"]'));
resetButton.nativeElement.click();
fixture.detectChanges();
expect(component.changedProperties).toEqual({});
expect(nodesApiService.updateNode).not.toHaveBeenCalled();
}));
});
describe('Properties loading', () => {
@@ -271,14 +294,16 @@ describe('ContentMetadataComponent', () => {
it('should display card views group when there is at least one property that is not empty', async(() => {
component.expanded = true;
fixture.detectChanges();
const cardViewGroup = {title: 'Group 1', properties: [{
data: null,
default: null,
displayValue: 'DefaultName',
icon: '',
key: 'properties.cm:default',
label: 'To'
}]};
const cardViewGroup = {
title: 'Group 1', properties: [{
data: null,
default: null,
displayValue: 'DefaultName',
icon: '',
key: 'properties.cm:default',
label: 'To'
}]
};
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of([{ properties: [cardViewGroup] }]));
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
@@ -317,9 +342,9 @@ describe('ContentMetadataComponent', () => {
let expectedNode;
beforeEach(() => {
expectedNode = Object.assign({}, node, {name: 'some-modified-value'});
expectedNode = Object.assign({}, node, { name: 'some-modified-value' });
spyOn(contentMetadataService, 'getGroupedProperties').and.returnValue(of(mockGroupProperties));
component.ngOnChanges({node: new SimpleChange(node, expectedNode, false)});
component.ngOnChanges({ node: new SimpleChange(node, expectedNode, false) });
});
it('should open and update drawer with expand section dynamically', async(() => {
@@ -373,7 +398,7 @@ describe('ContentMetadataComponent', () => {
describe('events', () => {
it('should not propagate the event on left arrows press', () => {
fixture.detectChanges();
const event = { keyCode: 37, stopPropagation: () => {} };
const event = { keyCode: 37, stopPropagation: () => { } };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -382,7 +407,7 @@ describe('ContentMetadataComponent', () => {
it('should not propagate the event on right arrows press', () => {
fixture.detectChanges();
const event = { keyCode: 39, stopPropagation: () => {} };
const event = { keyCode: 39, stopPropagation: () => { } };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -391,7 +416,7 @@ describe('ContentMetadataComponent', () => {
it('should propagate the event on other keys press', () => {
fixture.detectChanges();
const event = { keyCode: 40, stopPropagation: () => {} };
const event = { keyCode: 40, stopPropagation: () => { } };
spyOn(event, 'stopPropagation').and.stub();
const element = fixture.debugElement.query(By.css('adf-card-view'));
element.triggerEventHandler('keydown', event);
@@ -401,5 +426,5 @@ describe('ContentMetadataComponent', () => {
});
function queryDom(fixture: ComponentFixture<ContentMetadataComponent>, properties: string = 'properties') {
return fixture.debugElement.query(By.css(`[data-automation-id="adf-metadata-group-${properties}"]`));
return fixture.debugElement.query(By.css(`[data-automation-id="adf-metadata-group-${properties}"]`));
}
@@ -25,11 +25,12 @@ import {
CardViewUpdateService,
AlfrescoApiService,
TranslationService,
AppConfigService
AppConfigService,
CardViewBaseItemModel
} from '@alfresco/adf-core';
import { ContentMetadataService } from '../../services/content-metadata.service';
import { CardViewGroup } from '../../interfaces/content-metadata.interfaces';
import { switchMap, takeUntil, catchError } from 'rxjs/operators';
import { takeUntil, debounceTime, catchError } from 'rxjs/operators';
@Component({
selector: 'adf-content-metadata',
@@ -90,6 +91,10 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
basicProperties$: Observable<CardViewItem[]>;
groupedProperties$: Observable<CardViewGroup[]>;
changedProperties = {};
hasMetadataChanged = false;
private targetProperty: CardViewBaseItemModel;
constructor(
private contentMetadataService: ContentMetadataService,
private cardViewUpdateService: CardViewUpdateService,
@@ -107,23 +112,13 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
ngOnInit() {
this.cardViewUpdateService.itemUpdated$
.pipe(
switchMap((changes) =>
this.saveNode(changes).pipe(
catchError((err) => {
this.cardViewUpdateService.updateElement(changes.target);
this.handleUpdateError(err);
return of(null);
})
)
),
takeUntil(this.onDestroy$)
)
debounceTime(500),
takeUntil(this.onDestroy$))
.subscribe(
(updatedNode) => {
if (updatedNode) {
Object.assign(this.node, updatedNode);
this.alfrescoApiService.nodeUpdated.next(this.node);
}
this.hasMetadataChanged = true;
this.targetProperty = updatedNode.target;
this.updateChanges(updatedNode.changed);
}
);
@@ -165,8 +160,43 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
}
}
private saveNode({ changed: nodeBody }): Observable<Node> {
return this.nodesApiService.updateNode(this.node.id, nodeBody);
updateChanges(updatedNodeChanges) {
Object.keys(updatedNodeChanges).map((propertyGroup: string) => {
if (typeof updatedNodeChanges[propertyGroup] === 'object') {
this.changedProperties[propertyGroup] = {
...this.changedProperties[propertyGroup],
...updatedNodeChanges[propertyGroup]
};
} else {
this.changedProperties[propertyGroup] = updatedNodeChanges[propertyGroup];
}
});
}
saveChanges() {
this.nodesApiService.updateNode(this.node.id, this.changedProperties).pipe(
catchError((err) => {
this.cardViewUpdateService.updateElement(this.targetProperty);
this.handleUpdateError(err);
return of(null);
}))
.subscribe((updatedNode) => {
if (updatedNode) {
this.revertChanges();
Object.assign(this.node, updatedNode);
this.alfrescoApiService.nodeUpdated.next(this.node);
}
});
}
revertChanges() {
this.changedProperties = {};
this.hasMetadataChanged = false;
}
cancelChanges() {
this.revertChanges();
this.loadProperties(this.node);
}
showGroup(group: CardViewGroup): boolean {
@@ -195,5 +225,4 @@ export class ContentMetadataComponent implements OnChanges, OnInit, OnDestroy {
event.stopPropagation();
}
}
}
+2
View File
@@ -1,12 +1,14 @@
@import './components/card-view-arrayitem/card-view-arrayitem.component';
@import './components/card-view-dateitem/card-view-dateitem.component';
@import './components/card-view-textitem/card-view-textitem.component';
@import './components/card-view-keyvaluepairsitem/card-view-keyvaluepairsitem.component';
@import './components/card-view/card-view.component';
@import '~@mat-datetimepicker/core/datetimepicker/datetimepicker-theme.scss';
@mixin adf-card-view-module-theme($theme) {
@include adf-card-view-dateitem-theme($theme);
@include adf-card-view-textitem-theme($theme);
@include adf-card-view-keyvaluepairsitem-theme($theme);
@include adf-card-view-theme($theme);
@include mat-datetimepicker-theme($theme);
@include adf-card-view-array-item-theme($theme);
+3 -1
View File
@@ -30,7 +30,8 @@ import {
MatChipsModule,
MatMenuModule,
MatCardModule,
MatTooltipModule
MatTooltipModule,
MatSlideToggleModule
} from '@angular/material';
import { MatDatetimepickerModule, MatNativeDatetimeModule } from '@mat-datetimepicker/core';
import { FlexLayoutModule } from '@angular/flex-layout';
@@ -66,6 +67,7 @@ import { CardViewArrayItemComponent } from './components/card-view-arrayitem/car
MatCardModule,
MatDatetimepickerModule,
MatNativeDatetimeModule,
MatSlideToggleModule,
MatTooltipModule
],
declarations: [
@@ -41,6 +41,8 @@
&-property-value {
.mat-chip-list {
width: 100%;
padding-top: 6px;
cursor: pointer;
}
.mat-chip {
@@ -1,12 +1,13 @@
<ng-container *ngIf="!property.isEmpty() || isEditable()">
<div [attr.data-automation-id]="'card-boolean-label-' + property.key" class="adf-property-label">{{ property.label | translate }}</div>
<div class="adf-property-value">
<mat-checkbox
[attr.data-automation-id]="'card-boolean-' + property.key"
[attr.title]="'CORE.METADATA.ACTIONS.TOGGLE' | translate"
[checked]="property.displayValue"
[disabled]="!isEditable()"
(change)="changed($event)">
<mat-checkbox [attr.data-automation-id]="'card-boolean-' + property.key"
[attr.title]="'CORE.METADATA.ACTIONS.TOGGLE' | translate"
[checked]="property.displayValue"
[disabled]="!isEditable()"
color="primary"
(change)="changed($event)">
<div [attr.data-automation-id]="'card-boolean-label-' + property.key"
class="adf-property-label">{{ property.label | translate }}</div>
</mat-checkbox>
</div>
</ng-container>
@@ -1,8 +1,10 @@
<div *ngIf="showProperty() || isEditable()"
<div class="adf-property-label"
[attr.data-automation-id]="'card-dateitem-label-' + property.key"
class="adf-property-label">{{ property.label | translate }}</div>
*ngIf="showProperty() || isEditable()">
{{ property.label | translate }}
</div>
<div class="adf-property-value">
<div class="adf-property-value adf-property-value-padding-top">
<span *ngIf="!isEditable()"
[attr.data-automation-id]="'card-' + property.type + '-value-' + property.key">
<span [attr.data-automation-id]="'card-dateitem-' + property.key">
@@ -1,4 +1,6 @@
@mixin adf-card-view-dateitem-theme($theme) {
$foreground: map-get($theme, foreground);
.adf {
&-invisible-date-input {
height: 2px;
@@ -13,6 +15,8 @@
&-dateitem-editable {
cursor: pointer;
border-bottom: 1px solid mat-color($foreground, text, 0.42);
padding-bottom: 6px;
&-controls {
display: flex;
@@ -1,21 +1,20 @@
<div [attr.data-automation-id]="'card-key-value-pairs-label-' + property.key" class="adf-property-label">{{ property.label | translate }}</div>
<div class="adf-property-value">
<div [attr.data-automation-id]="'card-key-value-pairs-label-' + property.key"
class="adf-property-label">{{ property.label | translate }}</div>
<div class="adf-property-field">
<div *ngIf="isEditable()">
{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.ADD' | translate }}
<button (click)="add()" mat-icon-button class="adf-card-view__key-value-pairs__add-btn" [attr.data-automation-id]="'card-key-value-pairs-button-' + property.key">
<mat-icon>add</mat-icon>
</button>
</div>
<div *ngIf="!isEditable()" class="adf-card-view__key-value-pairs__read-only">
<mat-table #table [dataSource]="matTableValues" class="mat-elevation-z8">
<div *ngIf="!isEditable()"
class="adf-card-view__key-value-pairs__read-only adf-property-value">
<mat-table #table
[dataSource]="matTableValues"
class="mat-elevation-z8">
<ng-container matColumnDef="name">
<mat-header-cell *matHeaderCellDef>{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.NAME' | translate }}</mat-header-cell>
<mat-header-cell *matHeaderCellDef>{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.NAME' | translate }}
</mat-header-cell>
<mat-cell *matCellDef="let item">{{item.name}}</mat-cell>
</ng-container>
<ng-container matColumnDef="value">
<mat-header-cell *matHeaderCellDef>{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.VALUE' | translate }}</mat-header-cell>
<mat-header-cell *matHeaderCellDef>{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.VALUE' | translate }}
</mat-header-cell>
<mat-cell *matCellDef="let item">{{item.value}}</mat-cell>
</ng-container>
@@ -25,15 +24,17 @@
</div>
<div class="adf-card-view__key-value-pairs" *ngIf="isEditable() && values && values.length">
<div class="adf-card-view__key-value-pairs adf-property-value"
*ngIf="isEditable() && values && values.length">
<div class="adf-card-view__key-value-pairs__row">
<div class="adf-card-view__key-value-pairs__col">{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.NAME' | translate }}</div>
<div class="adf-card-view__key-value-pairs__col">{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.VALUE' | translate }}</div>
</div>
<div class="adf-card-view__key-value-pairs__row" *ngFor="let item of values; let i = index">
<div class="adf-card-view__key-value-pairs__row"
*ngFor="let item of values; let i = index">
<div class="adf-card-view__key-value-pairs__col">
<mat-form-field class="adf-example-full-width">
<mat-form-field>
<input matInput
placeholder="{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.NAME' | translate }}"
(blur)="onBlur(item.value)"
@@ -42,17 +43,30 @@
</mat-form-field>
</div>
<div class="adf-card-view__key-value-pairs__col">
<mat-form-field class="adf-example-full-width">
<mat-form-field>
<input matInput
placeholder="{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.VALUE' | translate }}"
(blur)="onBlur(item.value)"
[attr.data-automation-id]="'card-'+ property.key +'-value-input-' + i"
[(ngModel)]="values[i].value">
<button matSuffix
mat-icon-button
(click)="remove(i)"
class="adf-card-view__key-value-pairs__remove-btn">
<mat-icon>close</mat-icon>
</button>
</mat-form-field>
</div>
<button mat-icon-button (click)="remove(i)" class="adf-card-view__key-value-pairs__remove-btn">
<mat-icon>close</mat-icon>
</button>
</div>
</div>
<div *ngIf="isEditable()"
class="adf-property-value adf-card-view__key-value-pairs__add-btn-container">
<button (click)="add()"
mat-button
class="adf-card-view__key-value-pairs__add-btn"
[attr.data-automation-id]="'card-key-value-pairs-button-' + property.key">
{{ 'CORE.CARDVIEW.KEYVALUEPAIRS.ADD' | translate }}
<mat-icon>add</mat-icon>
</button>
</div>
</div>
@@ -1,21 +1,45 @@
.adf-card-view {
&__key-value-pairs {
&__col {
display: inline-block;
width: 39%;
@mixin adf-card-view-keyvaluepairsitem-theme($theme) {
$foreground: map-get($theme, foreground);
.mat-form-field {
.adf-card-view {
&__key-value-pairs {
&__row {
display: flex;
justify-content: center;
}
&__col {
width: 100%;
}
}
&__read-only {
.mat-table {
box-shadow: none;
.mat-form-field {
width: 100%;
font-size: 14px;
}
.mat-form-field-appearance-legacy .mat-form-field-label {
color: mat-color($foreground, text, 0.4) !important;
}
}
.mat-header-row, .mat-row {
padding: 0;
&__add-btn-container {
display: flex;
justify-content: center;
}
&__add-btn.mat-button {
margin-bottom: 20px;
}
&__read-only {
padding-bottom: 20px;
.mat-table {
box-shadow: none;
}
.mat-header-row, .mat-row {
padding: 0;
}
}
}
}
@@ -1,16 +1,21 @@
<div [attr.data-automation-id]="'card-mapitem-label-' + property.key" class="adf-property-label" *ngIf="showProperty()">{{ property.label | translate }}</div>
<div class="adf-property-value">
<div [attr.data-automation-id]="'card-mapitem-label-' + property.key"
class="adf-property-label"
*ngIf="showProperty()">{{ property.label | translate }}</div>
<div class="adf-property-value adf-map-item-padding">
<div>
<span *ngIf="!isClickable(); else elseBlock" [attr.data-automation-id]="'card-mapitem-value-' + property.key">
<span *ngIf="!isClickable(); else clickableTemplate"
[attr.data-automation-id]="'card-mapitem-value-' + property.key">
<span *ngIf="showProperty();">{{ property.displayValue }}</span>
</span>
<ng-template #elseBlock>
<span class="adf-mapitem-clickable-value" (click)="clicked()" [attr.data-automation-id]="'card-mapitem-value-' + property.key">
<span *ngIf="showProperty(); else elseEmptyValueBlock">{{ property.displayValue }}</span>
</span>
</ng-template>
</div>
<ng-template #elseEmptyValueBlock>
<ng-template #clickableTemplate>
<span class="adf-mapitem-clickable-value"
(click)="clicked()"
[attr.data-automation-id]="'card-mapitem-value-' + property.key">
<span *ngIf="showProperty(); else emptyValueTemplate">{{ property.displayValue }}</span>
</span>
</ng-template>
<ng-template #emptyValueTemplate>
{{ property.default | translate }}
</ng-template>
</div>
@@ -2,4 +2,8 @@
&-mapitem-clickable-value {
cursor: pointer !important;
}
&-map-item-padding {
padding-top: 6px;
}
}
@@ -1,14 +1,20 @@
<div [attr.data-automation-id]="'card-select-label-' + property.key" class="adf-property-label">{{ property.label | translate }}</div>
<div class="adf-property-value">
<div *ngIf="!isEditable()" data-automation-class="read-only-value">{{ property.displayValue | async }}</div>
<div [attr.data-automation-id]="'card-select-label-' + property.key"
class="adf-property-label">{{ property.label | translate }}</div>
<div class="adf-property-field">
<div *ngIf="!isEditable()"
class="adf-select-item-padding adf-property-value"
data-automation-class="read-only-value">{{ property.displayValue | async }}</div>
<div *ngIf="isEditable()">
<mat-form-field>
<mat-select [(value)]="value" (selectionChange)="onChange($event)" data-automation-class="select-box">
<mat-option *ngIf="showNoneOption()">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
<mat-option *ngFor="let option of getOptions() | async" [value]="option.key">
{{ option.label | translate }}
</mat-option>
<mat-form-field class="adf-select-item-padding-editable adf-property-value">
<mat-select [(value)]="value"
(selectionChange)="onChange($event)"
data-automation-class="select-box">
<mat-option *ngIf="showNoneOption()">{{ 'CORE.CARDVIEW.NONE' | translate }}</mat-option>
<mat-option *ngFor="let option of getOptions() | async"
[value]="option.key">
{{ option.label | translate }}
</mat-option>
</mat-select>
</mat-form-field>
</mat-form-field>
</div>
</div>
@@ -1,3 +1,13 @@
.mat-form-field-type-mat-select {
width: 100%;
}
.adf-select-item-padding {
padding-top: 6px;
padding-bottom: 20px;
}
.adf-select-item-padding-editable {
padding-top: 6px;
padding-bottom: 6px;
}
@@ -1,151 +1,124 @@
<div [attr.data-automation-id]="'card-textitem-label-' + property.key"
class="adf-property-label"
*ngIf="showProperty() || isEditable()">{{ property.label | translate }}</div>
<div class="adf-property-value">
<span *ngIf="!isEditable()">
<span *ngIf="!isClickable(); else nonClickableTemplate"
[attr.data-automation-id]="'card-textitem-value-' + property.key">
<span *ngIf="!isChipViewEnabled; else chipListTemplate">
<span *ngIf="showProperty() && !copyToClipboardAction"
[ngClass]="property.multiline?'adf-textitem-multiline':'adf-textitem-scroll'"
class="adf-textitem-value">
{{ property.displayValue }}
</span>
<span *ngIf="showProperty() && copyToClipboardAction"
[ngClass]="property.multiline?'adf-textitem-multiline':'adf-textitem-scroll'"
(dblclick)="copyToClipboard(property.displayValue)"
class="adf-textitem-value"
matTooltipShowDelay="1000"
[matTooltip]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate">
{{ property.displayValue }}
</span>
</span>
</span>
<ng-template #nonClickableTemplate>
<div role="button"
class="adf-textitem-clickable"
[attr.data-automation-id]="'card-textitem-toggle-' + property.key"
(click)="clicked()"
fxLayout="row"
fxLayoutAlign="space-between center">
<span class="adf-textitem-clickable-value"
[attr.data-automation-id]="'card-textitem-value-' + property.key">
<span *ngIf="showProperty(); else emptyValueTemplate">{{ property.displayValue }}</span>
</span>
<button mat-icon-button
fxFlex="0 0 auto"
*ngIf="showClickableIcon()"
class="adf-textitem-action"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.data-automation-id]="'card-textitem-clickable-icon-' + property.key">
<mat-icon class="adf-textitem-icon">{{property?.icon}}</mat-icon>
</button>
</div>
</ng-template>
</span>
<span *ngIf="isEditable()">
<div *ngIf="!inEdit"
role="button"
tabindex="0"
[attr.aria-label]="'CORE.METADATA.ACTIONS.EDIT' | translate"
(click)="setEditMode(true)"
(keydown.enter)="setEditMode(true)"
class="adf-textitem-readonly"
[attr.data-automation-id]="'card-textitem-toggle-' + property.key"
fxLayout="row"
fxLayoutAlign="space-between center">
<span *ngIf="!isChipViewEnabled; else chipListTemplate"
[attr.data-automation-id]="'card-textitem-value-' + property.key">
<span *ngIf="showProperty(); else emptyValueTemplate">{{ property.displayValue }}</span>
</span>
<div [ngSwitch]="templateType">
<div class="adf-property-label"
[attr.data-automation-id]="'card-textitem-label-' + property.key"
*ngIf="showProperty || isEditable">
{{ property.label | translate }}
</div>
<button mat-icon-button
fxFlex="0 0 auto"
class="adf-textitem-action"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.data-automation-id]="'card-textitem-edit-icon-' + property.key">
<mat-icon class="adf-textitem-icon"> create</mat-icon>
<div *ngSwitchDefault>
<mat-form-field class="adf-property-field adf-card-textitem-field"
[ngClass]="{ 'adf-property-read-only': !isEditable }">
<input matInput
*ngIf="!property.multiline"
class="adf-property-value"
[placeholder]="property.default"
[(ngModel)]="editedValue"
(blur)="update()"
(keydown.enter)="update()"
[disabled]="!isEditable"
(dblclick)="copyToClipboard(property.displayValue)"
matTooltipShowDelay="1000"
[matTooltip]="'CORE.METADATA.ACTIONS.COPY_TO_CLIPBOARD' | translate"
[matTooltipDisabled]="isEditable"
[attr.data-automation-id]="'card-textitem-value-' + property.key">
<textarea matInput
*ngIf="property.multiline"
matTextareaAutosize
matAutosizeMaxRows="1"
matAutosizeMaxRows="5"
class="adf-property-value"
[placeholder]="property.default"
[(ngModel)]="editedValue"
(blur)="update()"
(keydown.enter)="update()"
[disabled]="!isEditable"
[attr.data-automation-id]="'card-textitem-value-' + property.key"></textarea>
<button mat-button
matSuffix
class="adf-property-clear-value"
*ngIf="isEditable"
mat-icon-button
aria-label="Clear"
(click)="editedValue=''">
<mat-icon>cancel</mat-icon>
</button>
</div>
<div *ngIf="inEdit"
class="adf-textitem-editable">
<div class="adf-textitem-editable-controls">
<mat-form-field floatPlaceholder="never"
class="adf-input-container">
<input *ngIf="!isChipViewEnabled && !property.multiline"
#editorInput
(keydown.escape)="reset($event)"
(keydown.enter)="update($event)"
matInput
class="adf-input"
[placeholder]="property.default | translate"
[(ngModel)]="editedValue"
[attr.data-automation-id]="'card-textitem-editinput-' + property.key">
<textarea *ngIf="!isChipViewEnabled && property.multiline"
#editorInput
matInput
matTextareaAutosize
matAutosizeMaxRows="1"
matAutosizeMaxRows="5"
class="adf-textarea"
[placeholder]="property.default | translate"
[(ngModel)]="editedValue"
(input)="onTextAreaInputChange()"
[attr.data-automation-id]="'card-textitem-edittextarea-' + property.key"></textarea>
<div *ngIf="isChipViewEnabled">
<mat-chip-list class="adf-input"
#chipList>
<mat-chip *ngFor="let propertyValue of editedValue; let idx = index"
[removable]="true"
(removed)="removeValueFromList(idx)">
{{ propertyValue }}
<mat-icon matChipRemove>cancel</mat-icon>
</mat-chip>
<input #editorInput
[placeholder]="property.default | translate"
[matChipInputFor]="chipList"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addValueToList($event)"
[attr.data-automation-id]="'card-textitem-editchipinput-' + property.key">
</mat-chip-list>
</div>
</mat-form-field>
<button mat-icon-button
class="adf-textitem-action"
(click)="update($event)"
[attr.title]="'CORE.METADATA.ACTIONS.SAVE' | translate"
[attr.data-automation-id]="'card-textitem-update-' + property.key">
<mat-icon class="adf-textitem-icon">done</mat-icon>
</button>
<mat-icon matSuffix
*ngIf="isEditable"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
class="adf-textitem-edit-icon">mode_edit</mat-icon>
<button mat-icon-button
(click)="reset($event)"
class="adf-textitem-action"
[attr.title]="'CORE.METADATA.ACTIONS.CANCEL' | translate"
[attr.data-automation-id]="'card-textitem-reset-' + property.key">
</mat-form-field>
</div>
<mat-icon>clear</mat-icon>
</button>
</div>
<mat-error [attr.data-automation-id]="'card-textitem-error-' + property.key"
class="adf-textitem-editable-error"
*ngIf="hasErrors()">
<ul>
<li *ngFor="let errorMessage of errorMessages">{{ errorMessage | translate }}</li>
</ul>
</mat-error>
</div>
</span>
<ng-template #emptyValueTemplate>
<span class="adf-textitem-default-value">{{ property.default | translate }}</span>
</ng-template>
<mat-error [attr.data-automation-id]="'card-textitem-error-' + property.key"
class="adf-textitem-editable-error"
*ngIf="hasErrors">
<ul>
<li *ngFor="let errorMessage of errorMessages">{{ errorMessage | translate }}</li>
</ul>
</mat-error>
<ng-template #chipListTemplate>
<mat-chip-list>
<mat-chip *ngFor="let propertyValue of editedValue">
<div *ngSwitchCase="'chipsTemplate'"
class="adf-property-field adf-textitem-chip-list-container">
<mat-chip-list #chipList
class="adf-textitem-chip-list">
<mat-chip *ngFor="let propertyValue of editedValue; let idx = index"
[removable]="isEditable"
(removed)="removeValueFromList(idx)">
{{ propertyValue }}
<mat-icon *ngIf="isEditable"
matChipRemove>cancel</mat-icon>
</mat-chip>
</mat-chip-list>
</ng-template>
<mat-form-field *ngIf="isEditable"
class="adf-property-field adf-textitem-chip-list-input"
[ngClass]="{ 'adf-property-read-only': !isEditable }">
<input matInput
class="adf-property-value"
[placeholder]="property.default | translate"
[matChipInputFor]="chipList"
[matChipInputAddOnBlur]="true"
(matChipInputTokenEnd)="addValueToList($event)"
[attr.data-automation-id]="'card-textitem-editchipinput-' + property.key">
<mat-icon matSuffix
class="adf-textitem-edit-icon">mode_edit</mat-icon>
</mat-form-field>
</div>
<div *ngSwitchCase="'clickableTemplate'"
role="button"
class="adf-textitem-clickable"
[ngClass]="{ 'adf-property-read-only': !isEditable }"
[attr.data-automation-id]="'card-textitem-toggle-' + property.key"
(click)="clicked()"
fxLayout="row"
fxLayoutAlign="space-between center">
<mat-form-field class="adf-property-field adf-card-textitem-field">
<input matInput
[type]=property.inputType
class="adf-property-value"
[ngClass]="{ 'adf-textitem-clickable-value': !isEditable }"
[placeholder]="property.default"
[(ngModel)]="editedValue"
(blur)="update()"
(keydown.enter)="update()"
[disabled]="!isEditable"
[attr.data-automation-id]="'card-textitem-value-' + property.key">
<button mat-icon-button
matSuffix
fxFlex="0 0 auto"
*ngIf="showClickableIcon"
class="adf-textitem-action"
[attr.title]="'CORE.METADATA.ACTIONS.EDIT' | translate"
[attr.data-automation-id]="'card-textitem-clickable-icon-' + property.key">
<mat-icon class="adf-textitem-icon">{{ property?.icon }}</mat-icon>
</button>
</mat-form-field>
</div>
<div *ngSwitchCase="'emptyTemplate'">
<span class="adf-textitem-default-value">{{ property.default | translate }}</span>
</div>
</div>
@@ -5,17 +5,22 @@
$outline: 1px solid mat-color($alfresco-ecm-blue, A200) !default;
.adf {
&-textitem-icon {
&-textitem-edit-icon.mat-icon {
font-size: 16px;
width: 16px;
height: 16px;
}
&-textitem-action {
color: mat-color($foreground, text, 0.25);
}
&-textitem-action:hover, &-textitem-action:focus {
&-textitem-action.mat-icon-button {
width: 20px;
height: 20px;
line-height: 20px;
color: mat-color($foreground, text, 0.25);
}
&-textitem-action:hover,
&-textitem-action:focus {
color: mat-color($foreground, text);
}
@@ -32,8 +37,23 @@
}
}
&-textitem-chip-list-container {
margin-bottom: 25px !important;
margin-top: 6px;
.mat-form-field-label {
margin-top: 6px;
}
}
&-textitem-clickable {
cursor: pointer !important;
padding-top: 3px;
.adf-textitem-edit-icon.mat-icon {
line-height: 16px;
}
&:hover .adf-textitem-action {
color: mat-color($foreground, text);
}
@@ -41,11 +61,10 @@
&-textitem-clickable-value {
cursor: pointer !important;
color: mat-color($primary);
color: mat-color($primary) !important;
}
&-textitem-editable {
&-controls {
display: flex;
align-items: center;
@@ -148,5 +167,22 @@
&-textitem-multiline {
display: block;
}
&-property-field .adf-property-clear-value {
display: none;
}
&-property-field.adf-card-textitem-field:hover {
.adf-textitem-edit-icon {
display: none;
}
.adf-property-clear-value {
color: mat-color($foreground, text, 0.25);
display: inline;
}
.adf-property-clear-value:hover {
color: mat-color($foreground, text, 1);
}
}
}
}
File diff suppressed because it is too large Load Diff
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { Component, Input, OnChanges, ViewChild } from '@angular/core';
import { Component, Input, OnChanges } from '@angular/core';
import { CardViewTextItemModel } from '../../models/card-view-textitem.model';
import { CardViewUpdateService } from '../../services/card-view-update.service';
import { BaseCardView } from '../base-card-view';
@@ -24,6 +24,13 @@ import { ClipboardService } from '../../../clipboard/clipboard.service';
import { TranslationService } from '../../../services/translation.service';
export const DEFAULT_SEPARATOR = ', ';
const templateTypes = {
clickableTemplate: 'clickableTemplate',
multilineTemplate: 'multilineTemplate',
chipsTemplate: 'chipsTemplate',
emptyTemplate: 'emptyTemplate',
defaultTemplate: 'defaultTemplate'
};
@Component({
selector: 'adf-card-view-textitem',
@@ -47,12 +54,9 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
@Input()
multiValueSeparator: string = DEFAULT_SEPARATOR;
@ViewChild('editorInput')
private editorInput: any;
inEdit: boolean = false;
editedValue: string | string[];
errorMessages: string[];
templateType: string;
constructor(cardViewUpdateService: CardViewUpdateService,
private clipboardService: ClipboardService,
@@ -62,54 +66,28 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
ngOnChanges(): void {
this.resetValue();
this.setTemplateType();
}
showProperty(): boolean {
return this.displayEmpty || !this.property.isEmpty();
}
showClickableIcon(): boolean {
return this.hasIcon() && this.editable;
}
isEditable(): boolean {
return this.editable && this.property.editable;
}
isClickable(): boolean {
return !!this.property.clickable;
}
hasIcon(): boolean {
return !!this.property.icon;
}
hasErrors(): boolean {
return this.errorMessages && this.errorMessages.length > 0;
}
setEditMode(editStatus: boolean): void {
this.inEdit = editStatus;
setTimeout(() => {
if (this.editorInput) {
this.editorInput.nativeElement.click();
private setTemplateType() {
if (this.showProperty || this.isEditable) {
if (this.isClickable) {
this.templateType = templateTypes.clickableTemplate;
} else if (this.isChipViewEnabled) {
this.templateType = templateTypes.chipsTemplate;
} else {
this.templateType = templateTypes.defaultTemplate;
}
}, 0);
}
reset(event: Event): void {
event.stopPropagation();
this.resetValue();
this.setEditMode(false);
this.resetErrorMessages();
} else {
this.templateType = templateTypes.emptyTemplate;
}
}
resetValue() {
if (this.isChipViewEnabled) {
this.editedValue = this.property.value ? Array.from(this.property.value) : [];
} else {
this.editedValue = this.property.multiline ? this.property.displayValue : this.property.value;
this.editedValue = this.property.displayValue;
}
}
@@ -117,14 +95,11 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
this.errorMessages = [];
}
update(event: Event): void {
event.stopPropagation();
update(): void {
if (this.property.isValid(this.editedValue)) {
const updatedValue = this.prepareValueForUpload(this.property, this.editedValue);
this.cardViewUpdateService.update(<CardViewTextItemModel> { ...this.property }, updatedValue);
this.property.value = updatedValue;
this.setEditMode(false);
this.resetErrorMessages();
} else {
this.errorMessages = this.property.getValidationErrors(this.editedValue);
@@ -142,6 +117,7 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
removeValueFromList(itemIndex: number) {
if (typeof this.editedValue !== 'string') {
this.editedValue.splice(itemIndex, 1);
this.update();
}
}
@@ -152,16 +128,12 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
if (typeof this.editedValue !== 'string') {
if (chipValue) {
this.editedValue.push(chipValue);
this.update();
}
if (chipInput) {
chipInput.value = '';
}
}
}
onTextAreaInputChange() {
this.errorMessages = this.property.getValidationErrors(this.editedValue);
} }
}
clicked(): void {
@@ -172,9 +144,39 @@ export class CardViewTextItemComponent extends BaseCardView<CardViewTextItemMode
}
}
clearValue() {
this.editedValue = '';
}
copyToClipboard(valueToCopy: string) {
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
if (this.copyToClipboard) {
const clipboardMessage = this.translateService.instant('CORE.METADATA.ACCESSIBILITY.COPY_TO_CLIPBOARD_MESSAGE');
this.clipboardService.copyContentToClipboard(valueToCopy, clipboardMessage);
}
}
get showProperty(): boolean {
return this.displayEmpty || !this.property.isEmpty();
}
get showClickableIcon(): boolean {
return this.hasIcon && this.editable;
}
get isEditable(): boolean {
return this.editable && this.property.editable;
}
get isClickable(): boolean {
return this.property.clickable;
}
get hasIcon(): boolean {
return !!this.property.icon;
}
get hasErrors(): boolean {
return this.errorMessages && this.errorMessages.length > 0;
}
get isChipViewEnabled(): boolean {
@@ -8,13 +8,43 @@
.adf-property {
margin-bottom: 20px;
.adf-property-value-padding-top {
margin-top: 6px;
}
.adf-property-field {
width: 100%;
margin-bottom: -25px;
.mat-form-field-infix {
border-top-width: 0;
}
.mat-form-field-label {
margin-top: 6px;
}
.adf-property-read-only {
.mat-form-field-underline {
display: none;
}
}
}
.adf-property-read-only {
.mat-form-field-underline {
display: none;
}
}
.adf-property-label {
font-size: 12px;
color: mat-color($foreground, text, 0.54);
word-wrap: break-word;
}
.adf-property-value {
.adf-property-value,
.mat-form-field-label {
font-size: 14px;
color: mat-color($foreground, text, 0.87);
}
@@ -53,7 +53,7 @@ describe('CardViewComponent', () => {
const value = fixture.debugElement.query(By.css('.adf-property-value'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText).toBe('My value');
expect(value.nativeElement.value).toBe('My value');
});
}));
@@ -112,9 +112,9 @@ describe('CardViewComponent', () => {
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('My default label');
const value = fixture.debugElement.query(By.css('.adf-property-value [data-automation-id="card-textitem-value-some-key"]'));
const value = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-some-key"]'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText.trim()).toBe('default value');
expect(value.nativeElement.value).toBe('default value');
});
}));
@@ -137,9 +137,9 @@ describe('CardViewComponent', () => {
expect(labelValue).not.toBeNull();
expect(labelValue.nativeElement.innerText).toBe('My default label');
const value = fixture.debugElement.query(By.css('.adf-property-value [data-automation-id="card-textitem-value-some-key"]'));
const value = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-some-key"]'));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText.trim()).toBe('default value');
expect(value.nativeElement.value).toBe('default value');
});
}));
});
@@ -23,6 +23,7 @@ import { CardViewItemFloatValidator } from '../validators/card-view.validators';
export class CardViewFloatItemModel extends CardViewTextItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'float';
inputType: string = 'number';
constructor(cardViewTextItemProperties: CardViewTextItemProperties) {
super(cardViewTextItemProperties);
@@ -23,6 +23,7 @@ import { CardViewItemIntValidator } from '../validators/card-view.validators';
export class CardViewIntItemModel extends CardViewTextItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'int';
inputType: string = 'number';
constructor(cardViewTextItemProperties: CardViewTextItemProperties) {
super(cardViewTextItemProperties);
@@ -22,6 +22,7 @@ import { CardViewTextItemPipeProperty, CardViewTextItemProperties } from '../int
export class CardViewTextItemModel extends CardViewBaseItemModel implements CardViewItem, DynamicComponentModel {
type: string = 'text';
inputType: string = 'text';
multiline?: boolean;
multivalued?: boolean;
pipes?: CardViewTextItemPipeProperty[];
@@ -75,8 +75,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-id"] span'));
expect(formNameEl.nativeElement.innerText).toBe('00fcc4ab-4290-11e9-b133-0a586460016a');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-id"]'));
expect(formNameEl.nativeElement.value).toBe('00fcc4ab-4290-11e9-b133-0a586460016a');
});
}));
@@ -85,8 +85,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-name"] span'));
expect(formNameEl.nativeElement.innerText).toBe('new name');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-name"]'));
expect(formNameEl.nativeElement.value).toBe('new name');
});
}));
@@ -96,8 +96,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-name"] span'));
expect(valueEl.nativeElement.innerText).toBe('ADF_CLOUD_PROCESS_HEADER.PROPERTIES.NAME_DEFAULT');
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-name"]'));
expect(valueEl.nativeElement.value).toBe('ADF_CLOUD_PROCESS_HEADER.PROPERTIES.NAME_DEFAULT');
});
}));
@@ -107,8 +107,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
expect(formNameEl.nativeElement.innerText).toBe('RUNNING');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"]'));
expect(formNameEl.nativeElement.value).toBe('RUNNING');
});
}));
@@ -117,8 +117,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-initiator"] span'));
expect(formNameEl.nativeElement.innerText).toBe('devopsuser');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-initiator"]'));
expect(formNameEl.nativeElement.value).toBe('devopsuser');
});
}));
@@ -147,8 +147,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentId"] span'));
expect(formNameEl.nativeElement.innerText).toBe('00fcc4ab-4290-11e9-b133-0a586460016b');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentId"]'));
expect(formNameEl.nativeElement.value).toBe('00fcc4ab-4290-11e9-b133-0a586460016b');
});
}));
@@ -158,8 +158,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentId"] span'));
expect(formNameEl.nativeElement.innerText).toBe('ADF_CLOUD_PROCESS_HEADER.PROPERTIES.NONE');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentId"]'));
expect(formNameEl.nativeElement.value).toBe('ADF_CLOUD_PROCESS_HEADER.PROPERTIES.NONE');
});
}));
@@ -168,8 +168,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-businessKey"] span'));
expect(formNameEl.nativeElement.innerText).toBe('MyBusinessKey');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-businessKey"]'));
expect(formNameEl.nativeElement.value).toBe('MyBusinessKey');
});
}));
@@ -179,8 +179,8 @@ describe('ProcessHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-businessKey"] span'));
expect(formNameEl.nativeElement.innerText).toBe('ADF_CLOUD_PROCESS_HEADER.PROPERTIES.NONE');
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-businessKey"]'));
expect(formNameEl.nativeElement.value).toBe('ADF_CLOUD_PROCESS_HEADER.PROPERTIES.NONE');
});
}));
@@ -85,16 +85,16 @@ describe('TaskHeaderCloudComponent', () => {
it('should display assignee', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
expect(assigneeEl.nativeElement.innerText).toBe('AssignedTaskUser');
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"]'));
expect(assigneeEl.nativeElement.value).toBe('AssignedTaskUser');
});
}));
it('should display status', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
expect(statusEl.nativeElement.innerText).toBe('ASSIGNED');
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"]'));
expect(statusEl.nativeElement.value).toBe('ASSIGNED');
});
}));
@@ -103,7 +103,7 @@ describe('TaskHeaderCloudComponent', () => {
fixture.whenStable().then(() => {
const priorityEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-priority"]'));
expect(priorityEl.nativeElement.innerText).toBe('5');
expect(priorityEl.nativeElement.value).toBe('5');
});
}));
@@ -111,17 +111,11 @@ describe('TaskHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const edit = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edit-icon-priority"]'));
edit.nativeElement.click();
fixture.detectChanges();
const formPriorityEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-editinput-priority"]'));
const formPriorityEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-priority"]'));
formPriorityEl.nativeElement.value = 'stringValue';
formPriorityEl.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
const submitEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-update-priority"]'));
submitEl.nativeElement.click();
formPriorityEl.nativeElement.dispatchEvent(new Event('blur'));
fixture.detectChanges();
const errorMessageEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-error-priority"]'));
@@ -154,8 +148,8 @@ describe('TaskHeaderCloudComponent', () => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-parentName"] .adf-property-value'));
expect(valueEl.nativeElement.innerText.trim()).toEqual('ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_NAME_DEFAULT');
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-parentName"] input'));
expect(valueEl.nativeElement.value).toEqual('ADF_CLOUD_TASK_HEADER.PROPERTIES.PARENT_NAME_DEFAULT');
});
}));
@@ -163,17 +157,12 @@ describe('TaskHeaderCloudComponent', () => {
spyOn(taskCloudService, 'updateTask').and.returnValue(of(assignedTaskDetailsCloudMock));
fixture.detectChanges();
fixture.whenStable().then(() => {
const descriptionEditIcon = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edit-icon-description"]'));
descriptionEditIcon.nativeElement.click();
fixture.detectChanges();
const inputEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edittextarea-description"]'));
const inputEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-description"]'));
inputEl.nativeElement.value = 'updated description';
inputEl.nativeElement.dispatchEvent(new Event('input'));
fixture.detectChanges();
inputEl.nativeElement.dispatchEvent(new Event('blur'));
const submitEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-update-description"]'));
submitEl.nativeElement.click();
fixture.detectChanges();
expect(taskCloudService.updateTask).toHaveBeenCalled();
});
@@ -185,24 +174,20 @@ describe('TaskHeaderCloudComponent', () => {
await fixture.whenStable();
let description = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-description"]'));
expect(description.nativeElement.innerText.trim()).toEqual('This is the description');
expect(description.nativeElement.value.trim()).toEqual('This is the description');
const descriptionEditIcon = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edit-icon-description"]'));
descriptionEditIcon.nativeElement.click();
fixture.detectChanges();
const inputEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-edittextarea-description"]'));
const inputEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-description"]'));
inputEl.nativeElement.value = 'updated description';
inputEl.nativeElement.dispatchEvent(new Event('input'));
inputEl.nativeElement.dispatchEvent(new Event('blur'));
const submitEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-update-description"]'));
submitEl.nativeElement.click();
fixture.detectChanges();
expect(taskCloudService.updateTask).toHaveBeenCalled();
await fixture.whenStable();
fixture.detectChanges();
description = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-description"]'));
expect(description.nativeElement.innerText.trim()).toEqual('This is the description');
expect(description.nativeElement.value.trim()).toEqual('This is the description');
});
});
@@ -223,16 +208,16 @@ describe('TaskHeaderCloudComponent', () => {
it('should display parent task id', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentTaskId"] span'));
expect(assigneeEl.nativeElement.innerText).toBe('mock-parent-task-id');
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentTaskId"'));
expect(assigneeEl.nativeElement.value).toBe('mock-parent-task-id');
});
}));
it('should display parent task name', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentName"] span'));
expect(statusEl.nativeElement.innerText).toBe('This is a parent task name');
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-parentName"]'));
expect(statusEl.nativeElement.value.trim()).toBe('This is a parent task name');
});
}));
});
@@ -247,35 +232,35 @@ describe('TaskHeaderCloudComponent', () => {
it('should display assignee', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
expect(assigneeEl.nativeElement.innerText).toBe('AssignedTaskUser');
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"]'));
expect(assigneeEl.nativeElement.value).toBe('AssignedTaskUser');
});
}));
it('should display status', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
expect(statusEl.nativeElement.innerText).toBe('ASSIGNED');
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"]'));
expect(statusEl.nativeElement.value).toBe('ASSIGNED');
});
}));
it('should render defined edit icon for assignee property if the task in assigned state and assingee should be current user', () => {
fixture.detectChanges();
const value = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-clickable-icon-assignee"]`));
const value = fixture.debugElement.query(By.css(`[data-automation-id="header-assignee"] [data-automation-id="card-textitem-clickable-icon-assignee"]`));
expect(value).not.toBeNull();
expect(value.nativeElement.innerText).toBe('create');
});
it('should render edit icon if the task in assigned state and assingee should be current user', () => {
fixture.detectChanges();
const priorityEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-priority"]`));
const descriptionEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="card-textitem-edit-icon-description"]`));
const priorityEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="header-priority"] [class*="adf-textitem-edit-icon"]`));
const descriptionEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="header-description"] [class*="adf-textitem-edit-icon"]`));
const dueDateEditIcon = fixture.debugElement.query(By.css(`[data-automation-id="datepickertoggle-dueDate"]`));
expect(priorityEditIcon).not.toBeNull('Edit icon should be shown');
expect(descriptionEditIcon).not.toBeNull('Edit icon should be shown');
expect(dueDateEditIcon).not.toBeNull('Edit icon should be shown');
expect(priorityEditIcon).not.toBeNull('Priority edit icon should be shown');
expect(descriptionEditIcon).not.toBeNull('Description edit icon should be shown');
expect(dueDateEditIcon).not.toBeNull('Due date edit icon should be shown');
});
it('should not render defined clickable edit icon for assignee property if the task in assigned state and assingned user is different from current logged-in user', () => {
@@ -309,16 +294,16 @@ describe('TaskHeaderCloudComponent', () => {
it('should display status', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
expect(statusEl.nativeElement.innerText).toBe('CREATED');
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"]'));
expect(statusEl.nativeElement.value).toBe('CREATED');
});
}));
it('should display placeholder if no assignee', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"] span'));
expect(assigneeEl.nativeElement.innerText).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE_DEFAULT');
const assigneeEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-assignee"]'));
expect(assigneeEl.nativeElement.value).toBe('ADF_CLOUD_TASK_HEADER.PROPERTIES.ASSIGNEE_DEFAULT');
});
}));
@@ -350,8 +335,8 @@ describe('TaskHeaderCloudComponent', () => {
it('should display status', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
expect(statusEl.nativeElement.innerText).toBe('COMPLETED');
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"]'));
expect(statusEl.nativeElement.value).toBe('COMPLETED');
});
}));
@@ -383,8 +368,8 @@ describe('TaskHeaderCloudComponent', () => {
it('should display status', async(() => {
fixture.detectChanges();
fixture.whenStable().then(() => {
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"] span'));
expect(statusEl.nativeElement.innerText).toBe('SUSPENDED');
const statusEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-status"]'));
expect(statusEl.nativeElement.value).toBe('SUSPENDED');
});
}));
@@ -50,20 +50,22 @@ describe('ProcessInstanceHeaderComponent', () => {
expect(fixture.debugElement.children.length).toBe(0);
});
it('should display status as running when process is not complete', () => {
it('should display status as running when process is not complete', async () => {
component.processInstance.ended = null;
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-status"]');
expect(valueEl.innerText).toBe('Running');
expect(valueEl.value).toBe('Running');
});
it('should display status as completed when process is complete', () => {
it('should display status as completed when process is complete', async () => {
component.processInstance.ended = new Date('2016-11-03');
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-status"]');
expect(valueEl.innerText).toBe('Completed');
expect(valueEl.value).toBe('Completed');
});
it('should display due date', () => {
@@ -82,20 +84,22 @@ describe('ProcessInstanceHeaderComponent', () => {
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.END_DATE_DEFAULT');
});
it('should display process category', () => {
it('should display process category', async () => {
component.processInstance.processDefinitionCategory = 'Accounts';
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-category"]');
expect(valueEl.innerText).toBe('Accounts');
expect(valueEl.value).toBe('Accounts');
});
it('should display placeholder if no process category', () => {
it('should display placeholder if no process category', async () => {
component.processInstance.processDefinitionCategory = null;
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-category"]');
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.CATEGORY_DEFAULT');
expect(valueEl.value).toBe('ADF_PROCESS_LIST.PROPERTIES.CATEGORY_DEFAULT');
});
it('should display created date', () => {
@@ -106,52 +110,58 @@ describe('ProcessInstanceHeaderComponent', () => {
expect(valueEl.innerText).toBe('Nov 3, 2016');
});
it('should display started by', () => {
it('should display started by', async () => {
component.processInstance.startedBy = {firstName: 'Admin', lastName: 'User'};
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-assignee"]');
expect(valueEl.innerText).toBe('Admin User');
expect(valueEl.value).toBe('Admin User');
});
it('should display process instance id', () => {
it('should display process instance id', async () => {
component.processInstance.id = '123';
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-id"]');
expect(valueEl.innerText).toBe('123');
expect(valueEl.value).toBe('123');
});
it('should display description', () => {
it('should display description', async () => {
component.processInstance.processDefinitionDescription = 'Test process';
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-description"]');
expect(valueEl.innerText).toBe('Test process');
expect(valueEl.value).toBe('Test process');
});
it('should display placeholder if no description', () => {
it('should display placeholder if no description', async () => {
component.processInstance.processDefinitionDescription = null;
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-description"]');
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.DESCRIPTION_DEFAULT');
expect(valueEl.value).toBe('ADF_PROCESS_LIST.PROPERTIES.DESCRIPTION_DEFAULT');
});
it('should display businessKey value', () => {
it('should display businessKey value', async () => {
component.processInstance.businessKey = 'fakeBusinessKey';
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-businessKey"]');
expect(valueEl.innerText).toBe('fakeBusinessKey');
expect(valueEl.value).toBe('fakeBusinessKey');
});
it('should display default key if no businessKey', () => {
it('should display default key if no businessKey', async () => {
component.processInstance.businessKey = null;
component.ngOnChanges();
fixture.detectChanges();
await fixture.whenStable();
const valueEl = fixture.nativeElement.querySelector('[data-automation-id="card-textitem-value-businessKey"]');
expect(valueEl.innerText).toBe('ADF_PROCESS_LIST.PROPERTIES.BUSINESS_KEY_DEFAULT');
expect(valueEl.value).toBe('ADF_PROCESS_LIST.PROPERTIES.BUSINESS_KEY_DEFAULT');
});
describe('Config Filtering', () => {
@@ -83,7 +83,7 @@ describe('TaskHeaderComponent', () => {
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="header-assignee"] .adf-textitem-clickable-value'));
expect(formNameEl.nativeElement.innerText).toBe('Wilbur Adams');
expect(formNameEl.nativeElement.value).toBe('Wilbur Adams');
});
}));
@@ -94,7 +94,7 @@ describe('TaskHeaderComponent', () => {
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-assignee"] .adf-textitem-clickable-value'));
expect(valueEl.nativeElement.innerText).toBe('ADF_TASK_LIST.PROPERTIES.ASSIGNEE_DEFAULT');
expect(valueEl.nativeElement.value).toBe('ADF_TASK_LIST.PROPERTIES.ASSIGNEE_DEFAULT');
});
}));
@@ -106,7 +106,7 @@ describe('TaskHeaderComponent', () => {
fixture.whenStable().then(() => {
const formNameEl = fixture.debugElement.query(By.css('[data-automation-id="card-textitem-value-priority"]'));
expect(formNameEl.nativeElement.innerText).toBe('27');
expect(formNameEl.nativeElement.value).toBe('27');
});
}));
@@ -287,7 +287,7 @@ describe('TaskHeaderComponent', () => {
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-formName"] .adf-textitem-clickable-value'));
expect(valueEl.nativeElement.innerText).toBe('test form');
expect(valueEl.nativeElement.value).toBe('test form');
});
}));
@@ -301,8 +301,8 @@ describe('TaskHeaderComponent', () => {
const clickableForm = fixture.debugElement.query(By.css('[data-automation-id="header-formName"] .adf-textitem-clickable-value'));
expect(clickableForm).toBeNull();
const readOnlyForm = fixture.debugElement.query(By.css('[data-automation-id="header-formName"] .adf-textitem-scroll'));
expect(readOnlyForm.nativeElement.innerText).toBe('test form');
const readOnlyForm = fixture.debugElement.query(By.css('[data-automation-id="header-formName"] input'));
expect(readOnlyForm.nativeElement.value).toBe('test form');
});
}));
@@ -335,7 +335,7 @@ describe('TaskHeaderComponent', () => {
fixture.whenStable().then(() => {
const valueEl = fixture.debugElement.query(By.css('[data-automation-id="header-formName"] .adf-property-value'));
expect(valueEl.nativeElement.innerText).toBe('ADF_TASK_LIST.PROPERTIES.FORM_NAME_DEFAULT');
expect(valueEl.nativeElement.value).toBe('ADF_TASK_LIST.PROPERTIES.FORM_NAME_DEFAULT');
});
}));
@@ -15,20 +15,19 @@
* limitations under the License.
*/
import { element, by, ElementFinder, Locator } from 'protractor';
import { element, by, ElementFinder, Locator, Key } from 'protractor';
import { BrowserActions, BrowserVisibility } from '../../utils/public-api';
export class CardTextItemPage {
rootElement: ElementFinder;
textField: Locator = by.css('input[data-automation-id*="card-textitem-editinput"]');
textField: Locator = by.css('[data-automation-id*="card-textitem-value"]');
saveButton: Locator = by.css('button[data-automation-id*="card-textitem-update"]');
clearButton: Locator = by.css('button[data-automation-id*="card-textitem-reset"]');
field: Locator = by.css('span[data-automation-id*="card-textitem-value"] span');
field: Locator = by.css('[data-automation-id*="card-textitem-value"]');
labelLocator: Locator = by.css('div[data-automation-id*="card-textitem-label"]');
toggle: Locator = by.css('div[data-automation-id*="card-textitem-toggle"]');
editButton: Locator = by.css('button.adf-textitem-action[title*=Edit]');
errorMessage: Locator = by.css('.adf-textitem-editable-error');
clickableElement: Locator = by.css('.adf-textitem-clickable');
readOnlyField: Locator = by.css('.adf-property-read-only');
constructor(label: string = 'assignee') {
this.rootElement = element(by.xpath(`//div[contains(@data-automation-id, "label-${label}")]/ancestor::adf-card-view-textitem`));
@@ -36,7 +35,7 @@ export class CardTextItemPage {
async getFieldValue(): Promise<string> {
const fieldElement = this.rootElement.all(this.field).first();
return BrowserActions.getText(fieldElement);
return BrowserActions.getInputValue(fieldElement);
}
async checkLabelIsPresent(): Promise<void> {
@@ -44,14 +43,10 @@ export class CardTextItemPage {
await BrowserVisibility.waitUntilElementIsPresent(labelElement);
}
async clickOnToggleTextField(): Promise<void> {
const toggleText: ElementFinder = this.rootElement.element(this.toggle);
await BrowserActions.click(toggleText);
}
async enterTextField(text: string): Promise<void> {
await BrowserVisibility.waitUntilElementIsVisible(this.rootElement.element(this.textField));
await BrowserActions.clearSendKeys(this.rootElement.element(this.textField), text);
await this.rootElement.element(this.textField).sendKeys(Key.TAB);
}
async clickOnSaveButton(): Promise<void> {
@@ -62,17 +57,12 @@ export class CardTextItemPage {
await BrowserActions.click(this.rootElement.element(this.clearButton));
}
async clickOnEditButton(): Promise<void> {
await BrowserActions.click(this.rootElement.element(this.editButton));
}
async getErrorMessage(): Promise<string> {
const errorField = this.rootElement.element(this.errorMessage);
return BrowserActions.getText(errorField);
}
async checkElementIsReadonly(): Promise <void> {
await BrowserVisibility.waitUntilElementIsNotVisible(this.rootElement.element(this.clickableElement));
await BrowserVisibility.waitUntilElementIsNotVisible(this.rootElement.element(this.editButton));
async checkElementIsReadonly(): Promise<void> {
await BrowserVisibility.waitUntilElementIsVisible(this.rootElement.element(this.readOnlyField));
}
}
@@ -65,6 +65,15 @@ export class BrowserActions {
}
}
static async getInputValue(elementFinder: ElementFinder): Promise<string> {
const present = await BrowserVisibility.waitUntilElementIsPresent(elementFinder);
if (present) {
return elementFinder.getAttribute('value');
} else {
return '';
}
}
static async getArrayText(elementFinders: ElementArrayFinder): Promise<string> {
return elementFinders.getText();
}
@@ -75,12 +84,13 @@ export class BrowserActions {
return webElem.getCssValue('color');
}
static async clearWithBackSpace(elementFinder: ElementFinder) {
static async clearWithBackSpace(elementFinder: ElementFinder, sleepTime: number = 0) {
await BrowserVisibility.waitUntilElementIsVisible(elementFinder);
await elementFinder.click();
const value = await elementFinder.getAttribute('value');
for (let i = value.length; i >= 0; i--) {
await elementFinder.sendKeys(protractor.Key.BACK_SPACE);
await browser.sleep(sleepTime);
}
}
@@ -183,7 +183,7 @@ export class EditTaskFilterCloudComponentPage {
}
async clearAssignee(): Promise<void> {
await BrowserActions.clearWithBackSpace(this.assignee);
await BrowserActions.clearWithBackSpace(this.assignee, 200);
await browser.driver.sleep(1000);
}
@@ -20,29 +20,29 @@ import { BrowserActions } from '../../core/utils/browser-actions';
export class ProcessHeaderCloudPage {
idField: ElementFinder = element.all(by.css('span[data-automation-id*="id"] span span')).first();
nameField: ElementFinder = element.all(by.css('span[data-automation-id*="name"] span span')).first();
statusField: ElementFinder = element(by.css('span[data-automation-id*="status"] span span'));
initiatorField: ElementFinder = element(by.css('span[data-automation-id*="initiator"] span span'));
startDateField: ElementFinder = element.all(by.css('span[data-automation-id*="startDate"] span span')).first();
lastModifiedField: ElementFinder = element.all(by.css('span[data-automation-id*="lastModified"] span span')).first();
parentIdField: ElementFinder = element(by.css('span[data-automation-id*="parentId"] span span'));
businessKeyField: ElementFinder = element.all(by.css('span[data-automation-id*="businessKey"] span span')).first();
idField: ElementFinder = element.all(by.css('[data-automation-id="card-textitem-value-id"]')).first();
nameField: ElementFinder = element.all(by.css('[data-automation-id="card-textitem-value-name"]')).first();
statusField: ElementFinder = element(by.css('[data-automation-id="card-textitem-value-status"]'));
initiatorField: ElementFinder = element(by.css('[data-automation-id="card-textitem-value-initiator"]'));
startDateField: ElementFinder = element.all(by.css('span[data-automation-id*="startDate"] span')).first();
lastModifiedField: ElementFinder = element.all(by.css('span[data-automation-id*="lastModified"] span')).first();
parentIdField: ElementFinder = element(by.css('[data-automation-id="card-textitem-value-parentId"]'));
businessKeyField: ElementFinder = element(by.css('[data-automation-id="card-textitem-value-businessKey"]'));
async getId(): Promise<string> {
return BrowserActions.getText(this.idField);
return BrowserActions.getInputValue(this.idField);
}
async getName(): Promise<string> {
return BrowserActions.getText(this.nameField);
return BrowserActions.getInputValue(this.nameField);
}
async getStatus(): Promise<string> {
return BrowserActions.getText(this.statusField);
return BrowserActions.getInputValue(this.statusField);
}
async getInitiator(): Promise<string> {
return BrowserActions.getText(this.initiatorField);
return BrowserActions.getInputValue(this.initiatorField);
}
async getStartDate(): Promise<string> {
@@ -54,11 +54,11 @@ export class ProcessHeaderCloudPage {
}
async getParentId(): Promise<string> {
return BrowserActions.getText(this.parentIdField);
return BrowserActions.getInputValue(this.parentIdField);
}
async getBusinessKey(): Promise<string> {
return BrowserActions.getText(this.businessKeyField);
return BrowserActions.getInputValue(this.businessKeyField);
}
}
@@ -79,10 +79,6 @@ export class StartTasksCloudPage {
await BrowserVisibility.waitUntilElementIsVisible(errorElement);
}
async validateAssignee(error: string): Promise<void> {
await this.checkValidationErrorIsDisplayed(error, '.adf-start-task-cloud-error');
}
async validateDate(error: string): Promise<void> {
await this.checkValidationErrorIsDisplayed(error, '.adf-error-text');
}
@@ -15,63 +15,26 @@
* limitations under the License.
*/
import { by, element, ElementFinder, Locator } from 'protractor';
import { browser, by, element, ElementFinder } from 'protractor';
import { BrowserVisibility } from '../../core/utils/browser-visibility';
import { BrowserActions } from '../../core/utils/browser-actions';
export class TaskFiltersCloudComponentPage {
filter: ElementFinder;
taskIcon: Locator = by.xpath("ancestor::div[@class='mat-list-item-content']/mat-icon");
taskFilters: ElementFinder = element(by.css(`mat-expansion-panel[data-automation-id='Task Filters']`));
activeFilter: ElementFinder = element(by.css("mat-list-item[class*='active'] span"));
defaultActiveFilter: ElementFinder = element.all(by.css('.adf-filters__entry')).first();
async checkTaskFilterIsDisplayed(filterName: string): Promise<void> {
this.filter = this.getTaskFilterLocatorByFilterName(filterName);
await BrowserVisibility.waitUntilElementIsVisible(this.filter);
}
async getTaskFilterIcon(filterName: string): Promise<string> {
this.filter = this.getTaskFilterLocatorByFilterName(filterName);
await BrowserVisibility.waitUntilElementIsVisible(this.filter);
const icon = this.filter.element(this.taskIcon);
return BrowserActions.getText(icon);
}
async checkTaskFilterHasNoIcon(filterName: string): Promise<void> {
this.filter = this.getTaskFilterLocatorByFilterName(filterName);
await BrowserVisibility.waitUntilElementIsVisible(this.filter);
await BrowserVisibility.waitUntilElementIsNotVisible(this.filter.element(this.taskIcon));
}
async clickTaskFilter(filterName): Promise<void> {
this.filter = this.getTaskFilterLocatorByFilterName(filterName);
await BrowserVisibility.waitUntilElementIsClickable(this.filter);
await BrowserActions.click(this.filter);
}
async clickMyTasksFilter(): Promise<void> {
this.filter = this.getTaskFilterLocatorByFilterName('my-tasks');
await BrowserVisibility.waitUntilElementIsClickable(this.filter);
await BrowserActions.click(this.filter);
}
async clickCompletedTasksFilter(): Promise<void> {
this.filter = this.getTaskFilterLocatorByFilterName('completed-tasks');
await BrowserVisibility.waitUntilElementIsClickable(this.filter);
await BrowserActions.click(this.filter);
}
async checkMyTasksFilterIsDisplayed(): Promise<void> {
this.filter = this.getTaskFilterLocatorByFilterName('my-tasks');
await BrowserVisibility.waitUntilElementIsVisible(this.filter);
}
async checkCompletedTasksFilterIsDisplayed(): Promise<void> {
this.filter = this.getTaskFilterLocatorByFilterName('completed-tasks');
await BrowserVisibility.waitUntilElementIsVisible(this.filter);
await browser.sleep(1000);
}
async checkTaskFilterNotDisplayed(filterName: string): Promise<void> {
@@ -87,11 +50,6 @@ export class TaskFiltersCloudComponentPage {
return BrowserActions.getText(this.activeFilter);
}
async firstFilterIsActive(): Promise<boolean> {
const value = await this.defaultActiveFilter.getAttribute('class');
return value.includes('adf-active');
}
getTaskFilterLocatorByFilterName(filterName: string): ElementFinder {
return element(by.css(`span[data-automation-id="${filterName}-filter"]`));
}
@@ -15,7 +15,7 @@
* limitations under the License.
*/
import { element, by, ElementFinder } from 'protractor';
import { element, by, ElementFinder, browser } from 'protractor';
import { BrowserVisibility } from '../../core/utils/browser-visibility';
import { BrowserActions } from '../../core/utils/browser-actions';
import { FormFields } from '../../core/pages/form/form-fields';
@@ -77,6 +77,7 @@ export class TaskFormCloudComponent {
async clickCompleteButton(): Promise<void> {
await BrowserActions.click(this.completeButton);
await browser.sleep(500);
}
async checkFormOutcomeButtonIsDisplayedByName(name: string): Promise<void> {
@@ -26,10 +26,10 @@ export class TaskHeaderCloudPage {
statusCardTextItem: CardTextItemPage = new CardTextItemPage('status');
priorityCardTextItem: CardTextItemPage = new CardTextItemPage('priority');
dueDateField: ElementFinder = element.all(by.css('span[data-automation-id*="dueDate"] span')).first();
categoryField: ElementFinder = element.all(by.css('span[data-automation-id*="category"] span')).first();
categoryCardTextItem: CardTextItemPage = new CardTextItemPage('category');
createdField: ElementFinder = element(by.css('span[data-automation-id="card-dateitem-created"] span'));
parentNameField: ElementFinder = element(by.css('span[data-automation-id*="parentName"] span'));
parentTaskIdField: ElementFinder = element(by.css('span[data-automation-id*="parentTaskId"] span'));
parentNameCardTextItem: CardTextItemPage = new CardTextItemPage('parentName');
parentTaskIdCardTextItem: CardTextItemPage = new CardTextItemPage('parentTaskId');
endDateField: ElementFinder = element.all(by.css('span[data-automation-id*="endDate"] span')).first();
idCardTextItem: CardTextItemPage = new CardTextItemPage('id');
descriptionCardTextItem: CardTextItemPage = new CardTextItemPage('description');
@@ -39,10 +39,6 @@ export class TaskHeaderCloudPage {
return this.assigneeCardTextItem.getFieldValue();
}
async clickOnAssignee(): Promise<void> {
await this.assigneeCardTextItem.clickOnToggleTextField();
}
async getStatus(): Promise<string> {
return this.statusCardTextItem.getFieldValue();
}
@@ -52,15 +48,15 @@ export class TaskHeaderCloudPage {
}
async getCategory(): Promise<string> {
return BrowserActions.getText(this.categoryField);
return this.categoryCardTextItem.getFieldValue();
}
async getParentName(): Promise<string> {
return BrowserActions.getText(this.parentNameField);
return this.parentNameCardTextItem.getFieldValue();
}
async getParentTaskId(): Promise<string> {
return BrowserActions.getText(this.parentTaskIdField);
return this.parentTaskIdCardTextItem.getFieldValue();
}
async getEndDate(): Promise<string> {