Increase Import VFC UI test coverage 02/121102/2
authorvasraz <vasyl.razinkov@est.tech>
Wed, 5 May 2021 09:52:16 +0000 (10:52 +0100)
committerChristophe Closset <christophe.closset@intl.att.com>
Thu, 6 May 2021 07:05:14 +0000 (07:05 +0000)
Change-Id: Ie51411304fefe6b8ee3aa437b980f1e12bbc4707
Signed-off-by: Vasyl Razinkov <vasyl.razinkov@est.tech>
Issue-ID: SDC-3566

catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html
catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.html
catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.ts
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributeModal.java [new file with mode: 0644]
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesPage.java [new file with mode: 0644]
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java
integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/utilities/LoaderHelper.java

index 094045e..590217d 100644 (file)
@@ -5,14 +5,14 @@
             <!-- ATTRIBUTE NAME - MANDATORY -->
             <div>
                 <sdc-input
-                        #attributeName
-                        label="Name"
-                        [required]="true"
-                        [(value)]="attributeToEdit.name"
-                        [disabled]="isEdit"
-                        name="attributeName"
-                        testId="attributeName"
-                        [maxLength]="255">
+                    #attributeName
+                    label="Name"
+                    [required]="true"
+                    [(value)]="attributeToEdit.name"
+                    [disabled]="isEdit"
+                    name="attributeName"
+                    testId="attributeName"
+                    [maxLength]="255">
                 </sdc-input>
                 <sdc-validation [validateElement]="attributeName" (validityChanged)="onValidityChange($event, 'name')">
                     <sdc-required-validator message="{{'VALIDATION_ERROR_REQUIRED' | translate : { 'field' : 'Name' } }}"></sdc-required-validator>
@@ -38,6 +38,7 @@
                 <!-- ATTRIBUTE TYPE - MANDATORY -->
                 <sdc-dropdown #attributeType [disabled]="false" label="Type" [required]="true"
                               [selectedOption]="toDropDownOption(this.attributeToEdit.type)" placeHolder="Choose Type"
+                              testId="attributeType"
                               [options]="types" (changed)="onTypeSelected($event)">
                     <sdc-validation [validateElement]="attributeType" (validityChanged)="onValidityChange($event, 'type')">
                         <sdc-required-validator message="'required field'"></sdc-required-validator>
             <!-- ATTRIBUTE DEFAULT VALUE TEXT - OPTIONAL -->
             <div *ngIf="attributeToEdit.type != 'boolean'">
                 <sdc-input
-                        #_default
-                        [required]="false"
-                        label="Default Value"
-                        [(value)]="attributeToEdit._default"
-                        [disabled]="false"
-                        name="_default"
-                        testId="defaultValue"
-                        [maxLength]="255"
-                        (valueChange)="defaultValueChanged()">
+                    #_default
+                    [required]="false"
+                    label="Default Value"
+                    [(value)]="attributeToEdit._default"
+                    [disabled]="false"
+                    name="_default"
+                    testId="defaultValue"
+                    [maxLength]="255"
+                    (valueChange)="defaultValueChanged()">
                 </sdc-input>
 
                 <sdc-validation [validateElement]="_default" (validityChanged)="onValidityChange($event, 'default')">
index 6d50bbe..a04849a 100644 (file)
 
     <div class="action-bar-wrapper">
         <svg-icon-label
-                *ngIf="!(this.isViewOnly$ | async)"
-                class="add-attr-icon"
-                [name]="'plus'"
-                [mode]="'primary'"
-                [size]="'medium'"
-                [label]="'Add'"
-                [labelPlacement]="'right'"
-                [labelClassName]="'externalActionLabel'"
-                (click)="onAddAttribute()">
+            *ngIf="!(this.isViewOnly$ | async)"
+            class="add-attr-icon"
+            [name]="'plus'"
+            [mode]="'primary'"
+            [size]="'medium'"
+            [label]="'Add'"
+            [labelPlacement]="'right'"
+            [labelClassName]="'externalActionLabel'"
+            (click)="onAddAttribute()">
         </svg-icon-label>
     </div>
 
     <ngx-datatable
-            columnMode="flex"
-            [footerHeight]="0"
-            [limit]="50"
-            [headerHeight]="40"
-            [rowHeight]="35"
-            [rows]="attributes"
-            #componentAttributesTable
-            (activate)="onExpandRow($event)">
+        columnMode="flex"
+        [footerHeight]="0"
+        [limit]="50"
+        [headerHeight]="40"
+        [rowHeight]="35"
+        [rows]="attributes"
+        #componentAttributesTable
+        (activate)="onExpandRow($event)">
 
         <ngx-datatable-row-detail [rowHeight]="80">
             <ng-template let-row="row" let-expanded="expanded" ngx-datatable-row-detail-template>
         <ngx-datatable-column [resizeable]="false" name="Name" [flexGrow]="2">
 
             <ng-template ngx-datatable-cell-template let-row="row" let-expanded="expanded">
-                <div class="expand-collapse-cell">
+                <div class="expand-collapse-cell" [attr.data-tests-id]="'attrib-name_'+row.name">
                     <svg-icon [clickable]="true" class="expand-collapse-icon"
-                              [name]="expanded ? 'caret1-up-o': 'caret1-down-o'" [mode]="'primary'"
+                              [name]="expanded ? 'caret1-up-o': 'caret1-down-o'"
+                              [mode]="'primary'"
                               [size]="'medium'"></svg-icon>
                     <span>{{ row.name }}</span>
                 </div>
                     <svg-icon [clickable]="true"
                               [mode]="'primary2'"
                               [name]="'edit-o'"
+                              [testId]="'edit_'+row.name"
                               [size]="'medium'"
                               (click)="onEditAttribute($event, row)">
                     </svg-icon>
                     <svg-icon [clickable]="true"
                               [mode]="'primary2'"
                               [name]="'trash-o'"
-                              (click)="onDeleteAttribute($event, row)"
-                              [size]="'medium'">
+                              [testId]="'delete_'+row.name"
+                              [size]="'medium'"
+                              (click)="onDeleteAttribute($event, row)">
                     </svg-icon>
                 </div>
             </ng-template>
index ca8d546..d85755b 100644 (file)
-import { Component, OnInit, ViewChild } from '@angular/core';
-import { Select } from '@ngxs/store';
-import { IAttributeModel } from 'app/models';
+import {Component, OnInit, ViewChild} from '@angular/core';
+import {Select} from '@ngxs/store';
+import {IAttributeModel} from 'app/models';
 import * as _ from 'lodash';
-import { SdcUiCommon, SdcUiComponents, SdcUiServices } from 'onap-ui-angular';
-import { ModalComponent } from 'onap-ui-angular/dist/modals/modal.component';
-import { AttributeModel } from '../../../../models';
-import { Resource } from '../../../../models';
-import { ModalsHandler } from '../../../../utils';
-import { TopologyTemplateService } from '../../../../ng2/services/component-services/topology-template.service';
-import { TranslateService } from '../../../../ng2/shared/translator/translate.service';
-import { WorkspaceState } from '../../../../ng2/store/states/workspace.state';
-import { WorkspaceService } from '../../../../ng2/pages/workspace/workspace.service';
-import { AttributeModalComponent } from '../../../../ng2/pages/workspace/attributes/attribute-modal.component';
+import {SdcUiCommon, SdcUiComponents, SdcUiServices} from 'onap-ui-angular';
+import {ModalComponent} from 'onap-ui-angular/dist/modals/modal.component';
+import {AttributeModel, Resource} from '../../../../models';
+import {ModalsHandler} from '../../../../utils';
+import {TopologyTemplateService} from '../../../../ng2/services/component-services/topology-template.service';
+import {TranslateService} from '../../../../ng2/shared/translator/translate.service';
+import {WorkspaceState} from '../../../../ng2/store/states/workspace.state';
+import {WorkspaceService} from '../../../../ng2/pages/workspace/workspace.service';
+import {AttributeModalComponent} from '../../../../ng2/pages/workspace/attributes/attribute-modal.component';
 
 @Component({
-    selector: 'attributes',
-    templateUrl: './attributes.component.html',
-    styleUrls: ['./attributes.component.less', '../../../../../assets/styles/table-style.less']
+  selector: 'attributes',
+  templateUrl: './attributes.component.html',
+  styleUrls: ['./attributes.component.less', '../../../../../assets/styles/table-style.less']
 })
 export class AttributesComponent implements OnInit {
 
-    @Select(WorkspaceState.isViewOnly)
-    isViewOnly$: boolean;
-
-    @ViewChild('componentAttributesTable')
-    private table: any;
-
-    private componentType: string;
-    private componentUid: string;
-
-    private attributes: IAttributeModel[] = [];
-    private temp: IAttributeModel[] = [];
-    private customModalInstance: ModalComponent;
-
-    constructor(private workspaceService: WorkspaceService,
-                private topologyTemplateService: TopologyTemplateService,
-                private modalsHandler: ModalsHandler,
-                private modalService: SdcUiServices.ModalService,
-                private loaderService: SdcUiServices.LoaderService,
-                private translateService: TranslateService) {
-
-        this.componentType = this.workspaceService.metadata.componentType;
-        this.componentUid = this.workspaceService.metadata.uniqueId;
-    }
-
-    ngOnInit(): void {
-        this.asyncInitComponent();
-    }
-
-    async asyncInitComponent() {
-        this.loaderService.activate();
-        const response = await this.topologyTemplateService.getComponentAttributes(this.componentType, this.componentUid);
-        this.attributes = response.attributes;
-        this.temp = [...response.attributes];
-        this.loaderService.deactivate();
-    }
+  @Select(WorkspaceState.isViewOnly)
+  isViewOnly$: boolean;
+
+  @ViewChild('componentAttributesTable')
+  private table: any;
+
+  private componentType: string;
+  private componentUid: string;
+
+  private attributes: IAttributeModel[] = [];
+  private temp: IAttributeModel[] = [];
+  private customModalInstance: ModalComponent;
+
+  constructor(private workspaceService: WorkspaceService,
+              private topologyTemplateService: TopologyTemplateService,
+              private modalsHandler: ModalsHandler,
+              private modalService: SdcUiServices.ModalService,
+              private loaderService: SdcUiServices.LoaderService,
+              private translateService: TranslateService) {
+
+    this.componentType = this.workspaceService.metadata.componentType;
+    this.componentUid = this.workspaceService.metadata.uniqueId;
+  }
+
+  ngOnInit(): void {
+    this.asyncInitComponent();
+  }
+
+  async asyncInitComponent() {
+    this.loaderService.activate();
+    const response = await this.topologyTemplateService.getComponentAttributes(this.componentType, this.componentUid);
+    this.attributes = response.attributes;
+    this.temp = [...response.attributes];
+    this.loaderService.deactivate();
+  }
+
+  getAttributes(): IAttributeModel[] {
+    return this.attributes;
+  }
+
+  addOrUpdateAttribute = async (attribute: AttributeModel, isEdit: boolean) => {
+    this.loaderService.activate();
+    let attributeFromServer: AttributeModel;
+    this.temp = [...this.attributes];
+
+    const deactivateLoader = () => {
+      this.loaderService.deactivate();
+      return undefined;
+    };
 
-    getAttributes(): IAttributeModel[] {
-        return this.attributes;
+    if (isEdit) {
+      attributeFromServer = await this.topologyTemplateService
+      .updateAttributeAsync(this.componentType, this.componentUid, attribute)
+      .catch(deactivateLoader);
+      if (attributeFromServer) {
+        const indexOfUpdatedAttribute = _.findIndex(this.temp, (e) => e.uniqueId === attributeFromServer.uniqueId);
+        this.temp[indexOfUpdatedAttribute] = attributeFromServer;
+      }
+    } else {
+      attributeFromServer = await this.topologyTemplateService
+      .addAttributeAsync(this.componentType, this.componentUid, attribute)
+      .catch(deactivateLoader);
+      if (attributeFromServer) {
+        this.temp.push(attributeFromServer);
+      }
     }
-
-    addOrUpdateAttribute = async (attribute: AttributeModel, isEdit: boolean) => {
-        this.loaderService.activate();
-        let attributeFromServer: AttributeModel;
-        this.temp = [...this.attributes];
-
-        const deactivateLoader = () => {
-            this.loaderService.deactivate();
-            return undefined;
-        };
-
-        if (isEdit) {
-            attributeFromServer = await this.topologyTemplateService
-                                    .updateAttributeAsync(this.componentType, this.componentUid, attribute)
-                                    .catch(deactivateLoader);
-            if (attributeFromServer) {
-                const indexOfUpdatedAttribute = _.findIndex(this.temp, (e) => e.uniqueId === attributeFromServer.uniqueId);
-                this.temp[indexOfUpdatedAttribute] = attributeFromServer;
-            }
-        } else {
-            attributeFromServer = await this.topologyTemplateService
-                                    .addAttributeAsync(this.componentType, this.componentUid, attribute)
-                                    .catch(deactivateLoader);
-            if (attributeFromServer) {
-                this.temp.push(attributeFromServer);
-            }
+    this.attributes = this.temp;
+    this.loaderService.deactivate();
+  }
+
+  deleteAttribute = async (attributeToDelete: AttributeModel) => {
+    this.loaderService.activate();
+    this.temp = [...this.attributes];
+    const res = await this.topologyTemplateService.deleteAttributeAsync(this.componentType, this.componentUid, attributeToDelete);
+    _.remove(this.temp, (attr) => attr.uniqueId === attributeToDelete.uniqueId);
+    this.attributes = this.temp;
+    this.loaderService.deactivate();
+  };
+
+  openAddEditModal(selectedRow: AttributeModel, isEdit: boolean) {
+    const component = new Resource(undefined, undefined, undefined);
+    component.componentType = this.componentType;
+    component.uniqueId = this.componentUid;
+
+    const title: string = this.translateService.translate('ATTRIBUTE_DETAILS_MODAL_TITLE');
+    const attributeModalConfig = {
+      title,
+      size: 'md',
+      type: SdcUiCommon.ModalType.custom,
+      buttons: [
+        {
+          id: 'save',
+          text: 'Save',
+          // spinner_position: Placement.left,
+          size: 'sm',
+          callback: () => this.modalCallBack(isEdit),
+          closeModal: true,
+          disabled: false,
         }
-        this.attributes = this.temp;
-        this.loaderService.deactivate();
-    }
-
-    deleteAttribute = async (attributeToDelete: AttributeModel) => {
-        this.loaderService.activate();
-        this.temp = [...this.attributes];
-        const res = await this.topologyTemplateService.deleteAttributeAsync(this.componentType, this.componentUid, attributeToDelete);
-        _.remove(this.temp, (attr) => attr.uniqueId === attributeToDelete.uniqueId);
-        this.attributes = this.temp;
-        this.loaderService.deactivate();
+      ] as SdcUiCommon.IModalButtonComponent[]
     };
 
-    openAddEditModal(selectedRow: AttributeModel, isEdit: boolean) {
-        const component = new Resource(undefined, undefined, undefined);
-        component.componentType = this.componentType;
-        component.uniqueId = this.componentUid;
-
-        const title: string = this.translateService.translate('ATTRIBUTE_DETAILS_MODAL_TITLE');
-        const attributeModalConfig = {
-            title,
-            size: 'md',
-            type: SdcUiCommon.ModalType.custom,
-            buttons: [
-                {
-                    id: 'save',
-                    text: 'Save',
-                    // spinner_position: Placement.left,
-                    size: 'sm',
-                    callback: () => this.modalCallBack(isEdit),
-                    closeModal: true,
-                    disabled: false,
-                }
-            ] as SdcUiCommon.IModalButtonComponent[]
-        };
-
-        this.customModalInstance = this.modalService.openCustomModal(attributeModalConfig, AttributeModalComponent, { attributeToEdit: selectedRow });
-        this.customModalInstance.innerModalContent.instance.
-            onValidationChange.subscribe((isValid) => this.customModalInstance.getButtonById('save').disabled = !isValid);
-    }
-
-    /***********************
-     * Call Backs from UI  *
-     ***********************/
-
-    /**
-     * Called when 'Add' is clicked
-     */
-    onAddAttribute() {
-        this.openAddEditModal(new AttributeModel(), false);
-    }
-
-    /**
-     * Called when 'Edit' button is clicked
-     */
-    onEditAttribute(event, row) {
-        event.stopPropagation();
-
-        const attributeToEdit: AttributeModel = new AttributeModel(row);
-        this.openAddEditModal(attributeToEdit, true);
-    }
-
-    /**
-     * Called when 'Delete' button is clicked
-     */
-    onDeleteAttribute(event, row: AttributeModel) {
-        event.stopPropagation();
-        const onOk = () => {
-            this.deleteAttribute(row);
-        };
-
-        const title: string = this.translateService.translate('ATTRIBUTE_VIEW_DELETE_MODAL_TITLE');
-        const message: string = this.translateService.translate('ATTRIBUTE_VIEW_DELETE_MODAL_TEXT');
-        const okButton = new SdcUiComponents.ModalButtonComponent();
-        okButton.testId = 'OK';
-        okButton.text = 'OK';
-        okButton.type = SdcUiCommon.ButtonType.info;
-        okButton.closeModal = true;
-        okButton.callback = onOk;
-
-        this.modalService.openInfoModal(title, message, 'delete-modal', [okButton]);
-    }
-
-    onExpandRow(event) {
-        if (event.type === 'click') {
-            this.table.rowDetail.toggleExpandRow(event.row);
-        }
-    }
+    this.customModalInstance = this.modalService.openCustomModal(attributeModalConfig, AttributeModalComponent, {attributeToEdit: selectedRow});
+    this.customModalInstance.innerModalContent.instance.onValidationChange.subscribe((isValid) => this.customModalInstance.getButtonById('save').disabled = !isValid);
+  }
+
+  /***********************
+   * Call Backs from UI  *
+   ***********************/
+
+  /**
+   * Called when 'Add' is clicked
+   */
+  onAddAttribute() {
+    this.openAddEditModal(new AttributeModel(), false);
+  }
+
+  /**
+   * Called when 'Edit' button is clicked
+   */
+  onEditAttribute(event, row) {
+    event.stopPropagation();
+
+    const attributeToEdit: AttributeModel = new AttributeModel(row);
+    this.openAddEditModal(attributeToEdit, true);
+  }
+
+  /**
+   * Called when 'Delete' button is clicked
+   */
+  onDeleteAttribute(event, row: AttributeModel) {
+    event.stopPropagation();
+    const onOk = () => {
+      this.deleteAttribute(row);
+    };
 
-    /**
-     * Callback from Modal after "Save" is clicked
-     *
-     * @param {boolean} isEdit - Whether modal is edit or add attribute
-     */
-    modalCallBack = (isEdit: boolean) => {
-        const attribute: AttributeModel = this.customModalInstance.innerModalContent.instance.attributeToEdit;
-        this.addOrUpdateAttribute(attribute, isEdit);
+    const title: string = this.translateService.translate('ATTRIBUTE_VIEW_DELETE_MODAL_TITLE');
+    const message: string = this.translateService.translate('ATTRIBUTE_VIEW_DELETE_MODAL_TEXT', {name: row.name});
+    const okButton = new SdcUiComponents.ModalButtonComponent();
+    okButton.testId = 'OK';
+    okButton.text = 'OK';
+    okButton.type = SdcUiCommon.ButtonType.info;
+    okButton.closeModal = true;
+    okButton.callback = onOk;
+
+    this.modalService.openInfoModal(title, message, 'delete-modal', [okButton]);
+  }
+
+  onExpandRow(event) {
+    if (event.type === 'click') {
+      this.table.rowDetail.toggleExpandRow(event.row);
     }
+  }
+
+  /**
+   * Callback from Modal after "Save" is clicked
+   *
+   * @param {boolean} isEdit - Whether modal is edit or add attribute
+   */
+  modalCallBack = (isEdit: boolean) => {
+    const attribute: AttributeModel = this.customModalInstance.innerModalContent.instance.attributeToEdit;
+    this.addOrUpdateAttribute(attribute, isEdit);
+  }
 
 }
index a571c1b..b3538e3 100644 (file)
 package org.onap.sdc.frontend.ci.tests.execute.sanity;
 
 import static org.hamcrest.MatcherAssert.assertThat;
-import static org.hamcrest.Matchers.anEmptyMap;
 import static org.hamcrest.Matchers.empty;
 import static org.hamcrest.Matchers.emptyString;
 import static org.hamcrest.Matchers.not;
 import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotNull;
 import static org.junit.jupiter.api.Assertions.assertTrue;
 import static org.junit.jupiter.api.Assertions.fail;
@@ -32,6 +32,7 @@ import com.aventstack.extentreports.Status;
 import java.util.Arrays;
 import java.util.Map;
 import java.util.Optional;
+import org.apache.commons.collections.MapUtils;
 import org.onap.sdc.backend.ci.tests.datatypes.enums.ComponentType;
 import org.onap.sdc.backend.ci.tests.datatypes.enums.ResourceCategoryEnum;
 import org.onap.sdc.backend.ci.tests.utils.general.ElementFactory;
@@ -46,7 +47,10 @@ import org.onap.sdc.frontend.ci.tests.flow.CreateVfFlow;
 import org.onap.sdc.frontend.ci.tests.flow.CreateVfcFlow;
 import org.onap.sdc.frontend.ci.tests.flow.DownloadCsarArtifactFlow;
 import org.onap.sdc.frontend.ci.tests.flow.exception.UiTestFlowRuntimeException;
+import org.onap.sdc.frontend.ci.tests.pages.AttributeModal;
+import org.onap.sdc.frontend.ci.tests.pages.AttributesPage;
 import org.onap.sdc.frontend.ci.tests.pages.ComponentPage;
+import org.onap.sdc.frontend.ci.tests.pages.ResourceCreatePage;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionDetailSideBarComponent;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionDetailSideBarComponent.CompositionDetailTabName;
 import org.onap.sdc.frontend.ci.tests.pages.component.workspace.CompositionInformationTab;
@@ -69,6 +73,7 @@ public class ImportVfcUiTest extends SetupCDTest {
     private HomePage homePage;
     private ResourceCreateData vfcCreateData;
     private ResourceCreateData vfCreateData;
+    private ComponentInstance createdComponentInstance;
 
     @BeforeClass
     public void beforeClass() {
@@ -97,13 +102,15 @@ public class ImportVfcUiTest extends SetupCDTest {
         // TC - Import hierarchy of VFCs
         fileName = "org.openecomp.resource.VFC-child.yml";
         createVfcFlow = createVFC(fileName);
-        componentPage = createVfcFlow.getLandedPage()
-            .orElseThrow(() -> new UiTestFlowRuntimeException("Missing expected return ResourceCreatePage"));
+        componentPage = createVfcFlow.getLandedPage().orElseThrow(() -> new UiTestFlowRuntimeException("Missing expected return ResourceCreatePage"));
+        componentPage.isLoaded();
+
+        componentPage = manageAttributes(componentPage);
         componentPage.isLoaded();
         componentPage.certifyComponent();
         componentPage.isLoaded();
 
-        yamlObject = downloadToscaArtifact(createVfcFlow.getLandedPage().get());
+        yamlObject = downloadToscaArtifact(componentPage);
         checkMetadata(yamlObject, vfcCreateData);
         checkNodeTypes(yamlObject);
         homePage.getTopNavComponent().clickOnHome();
@@ -126,6 +133,32 @@ public class ImportVfcUiTest extends SetupCDTest {
 
     }
 
+    private ComponentPage manageAttributes(final ComponentPage componentPage) {
+        final AttributesPage attributesPage = componentPage.goToAttributes();
+        attributesPage.isLoaded();
+
+        assertTrue(attributesPage.isAttributePresent("test_1"));
+        assertTrue(attributesPage.isAttributePresent("test_2"));
+        assertTrue(attributesPage.isAttributePresent("test_3"));
+        assertTrue(attributesPage.isAttributePresent("test_4"));
+
+        attributesPage.deleteAttribute("test_2");
+        assertFalse(attributesPage.isAttributePresent("test_2"));
+        ExtentTestActions.takeScreenshot(Status.INFO, "attributesPage.deleteAttribute", "Attribute 'test_2' successfully deleted");
+        attributesPage.addAttribute(new AttributeModal.AttributeData("test_9", "Additional attribute added from UI", "string", "one More Attribute"));
+        attributesPage.isLoaded();
+        assertTrue(attributesPage.isAttributePresent("test_9"));
+        ExtentTestActions.takeScreenshot(Status.INFO, "attributesPage.addAttribute", "Additional Attribute 'test_9' successfully added");
+
+        attributesPage.editAttribute(new AttributeModal.AttributeData("test_9", "Additional attribute added from UI".toUpperCase(), "string",
+            "one More Attribute".toUpperCase()));
+        attributesPage.isLoaded();
+        assertTrue(attributesPage.isAttributePresent("test_9"));
+        ExtentTestActions.takeScreenshot(Status.INFO, "attributesPage.editAttribute", "Additional Attribute 'test_9' successfully altered");
+
+        return attributesPage.clickOnGeneralMenuItem(ResourceCreatePage.class);
+    }
+
     private CompositionPage addInterfaceOperations(final ComponentPage componentPage) {
         final AddNodeToCompositionFlow addNodeToCompositionFlow = addNodeToCompositionFlow(componentPage);
         final CompositionPage compositionPage = addNodeToCompositionFlow.getLandedPage()
@@ -133,7 +166,7 @@ public class ImportVfcUiTest extends SetupCDTest {
         final CompositionDetailSideBarComponent detailSideBar = compositionPage.getDetailSideBar();
         detailSideBar.isLoaded();
 
-        final ComponentInstance createdComponentInstance = addNodeToCompositionFlow.getCreatedComponentInstance()
+        createdComponentInstance = addNodeToCompositionFlow.getCreatedComponentInstance()
             .orElseThrow(() -> new UiTestFlowRuntimeException("Expecting a ComponentInstance"));
 
         compositionPage.selectNode(createdComponentInstance.getName());
@@ -288,32 +321,38 @@ public class ImportVfcUiTest extends SetupCDTest {
         final Map<String, Object> mapEntry = getMapEntry(map, "node_types");
         final Map<String, Object> nodeTypes = getMapEntry(mapEntry, mapEntry.keySet().iterator().next());
 
-        assertNotNull(nodeTypes);
+        assertFalse(MapUtils.isEmpty(nodeTypes));
         assertEquals("aDescription", nodeTypes.get("description"));
 
         final Map<String, Object> properties = getMapEntry(nodeTypes, "properties");
-        assertThat(properties, not(anEmptyMap()));
+        assertFalse(MapUtils.isEmpty(properties));
 
         final Map<String, Object> attributes = getMapEntry(nodeTypes, "attributes");
-        assertThat(attributes, not(anEmptyMap()));
+        assertFalse(MapUtils.isEmpty(attributes));
 
         final Map<String, Object> interfaces = getMapEntry(nodeTypes, "interfaces");
-        assertThat(interfaces, not(anEmptyMap()));
+        assertFalse(MapUtils.isEmpty(interfaces));
 
     }
 
     private void checkTopologyTemplate(final Map<String, Object> map) {
-        final Map<String, Object> mapEntry = getMapEntry(map, "topology_template");
-        assertNotNull(mapEntry);
+        final Map<String, Object> topologyTemplate = getMapEntry(map, "topology_template");
+        assertNotNull(topologyTemplate);
+
+        final Map<String, Object> inputs = getMapEntry(topologyTemplate, "inputs");
+        assertFalse(MapUtils.isEmpty(inputs));
 
-        final Map<String, Object> properties = getMapEntry(mapEntry, "inputs");
-        assertThat(properties, not(anEmptyMap()));
+        final Map<String, Object> nodeTemplates = getMapEntry(topologyTemplate, "node_templates");
+        assertFalse(MapUtils.isEmpty(nodeTemplates));
 
-        final Map<String, Object> attributes = getMapEntry(mapEntry, "node_templates");
-        assertThat(attributes, not(anEmptyMap()));
+        final Map<String, Object> attributes = getMapEntry((Map<String, Object>) nodeTemplates.get(createdComponentInstance.getName()), "attributes");
+        assertFalse(MapUtils.isEmpty(attributes));
+        assertEquals(4, attributes.keySet().stream()
+            .filter(s -> (s.contains("test_1") || s.contains("test_3") || s.contains("test_4") || s.contains("test_9")) && !s.contains("test_2"))
+            .count());
 
-        final Map<String, Object> interfaces = getMapEntry(mapEntry, "substitution_mappings");
-        assertThat(interfaces, not(anEmptyMap()));
+        final Map<String, Object> substitutionMappings = getMapEntry(topologyTemplate, "substitution_mappings");
+        assertFalse(MapUtils.isEmpty(substitutionMappings));
 
     }
 
diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributeModal.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributeModal.java
new file mode 100644 (file)
index 0000000..bd1330d
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.sdc.frontend.ci.tests.pages;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+/**
+ * Handles the 'Attributes' Edit Modal UI actions
+ */
+public class AttributeModal extends AbstractPageObject {
+
+    private WebElement wrappingElement;
+
+    public AttributeModal(final WebDriver webDriver) {
+        super(webDriver);
+    }
+
+    @Override
+    public void isLoaded() {
+        waitForElementVisibility(By.xpath(XpathSelector.TITLE_DIV.getXPath()));
+        waitForElementVisibility(By.xpath(XpathSelector.SAVE_BTN.getXPath()));
+        wrappingElement = findElement(By.xpath(XpathSelector.ATTR_CONTAINER_DIV.getXPath()));
+    }
+
+    public void fillForm(final AttributeData attributeData, final boolean isUpdate) {
+        if (!isUpdate) {
+            editName(attributeData.getAttributeName());
+        }
+        editDescription(attributeData.getDescription());
+        editType(attributeData.getAttributeType());
+        editDefaultValue(attributeData.getDefaultValue());
+    }
+
+    private void editName(final String attributeName) {
+        final WebElement webElement = waitForElementVisibility(By.xpath(XpathSelector.ATTRIBUTE_NAME_INPUT.getXPath()));
+        webElement.clear();
+        webElement.sendKeys(attributeName);
+    }
+
+    private void editDescription(final String description) {
+        final WebElement webElement = waitForElementVisibility(By.xpath(XpathSelector.DESCRIPTION_INPUT.getXPath()));
+        webElement.clear();
+        webElement.sendKeys(description);
+    }
+
+    private void editType(final String attributeType) {
+        waitToBeClickable(By.xpath(XpathSelector.ATTRIBUT_TYPE_ICON.getXPath())).click();
+        final WebElement element = waitForElementVisibility(By.xpath(XpathSelector.DROPDOWN_RESULTS.getXPath()));
+        element.findElement(By.xpath(XpathSelector.ATTRIBUTE_TYPE_LI.getXPath(attributeType))).click();
+    }
+
+    private void editDefaultValue(final String defaultValue) {
+        final WebElement webElement = waitForElementVisibility(By.xpath(XpathSelector.DEFAULT_VALUE_INPUT.getXPath()));
+        webElement.clear();
+        webElement.sendKeys(defaultValue);
+    }
+
+    public void clickSave() {
+        waitToBeClickable(By.xpath(XpathSelector.SAVE_BTN.getXPath())).click();
+    }
+
+    @Getter
+    @AllArgsConstructor
+    public static class AttributeData {
+
+        private final String attributeName;
+        private final String description;
+        private final String attributeType;
+        private final String defaultValue;
+    }
+
+    /**
+     * Enum that contains identifiers and xpath expressions to elements related to the enclosing page object.
+     */
+    @AllArgsConstructor
+    private enum XpathSelector {
+        TITLE_DIV("//div[contains(@class,'title') and contains(text(), ' Attribute Details')]"),
+        ATTR_CONTAINER_DIV("//div[@class='attr-container']"),
+        ATTRIBUTE_NAME_INPUT("//input[@data-tests-id='attributeName']"),
+        DESCRIPTION_INPUT("//textarea[@data-tests-id='description']"),
+        ATTRIBUTE_TYPE_LI("//li[@data-tests-id='%s']"),
+        ATTRIBUT_TYPE_ICON("//div[@data-tests-id='attributeType-icon']"),
+        DROPDOWN_RESULTS("//ul[contains(@class,'dropdown-results')]"),
+        ATTRIBUTE_TYPE_DIV("//div[@data-tests-id='attributeType']"),
+        DEFAULT_VALUE_INPUT("//input[@data-tests-id='defaultValue']"),
+        SAVE_BTN("//button[@data-tests-id='button-save']");
+
+        @Getter
+        private final String xPath;
+
+        public String getXPath(final String... xpathParams) {
+            return String.format(xPath, xpathParams);
+        }
+
+    }
+
+}
diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesPage.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesPage.java
new file mode 100644 (file)
index 0000000..47b29b9
--- /dev/null
@@ -0,0 +1,126 @@
+/*
+ * ============LICENSE_START=======================================================
+ *  Copyright (C) 2021 Nordix Foundation
+ *  ================================================================================
+ *  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.
+ *
+ *  SPDX-License-Identifier: Apache-2.0
+ *  ============LICENSE_END=========================================================
+ */
+
+package org.onap.sdc.frontend.ci.tests.pages;
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import org.onap.sdc.frontend.ci.tests.pages.AttributeModal.AttributeData;
+import org.onap.sdc.frontend.ci.tests.utilities.LoaderHelper;
+import org.openqa.selenium.By;
+import org.openqa.selenium.WebDriver;
+import org.openqa.selenium.WebElement;
+
+/**
+ * Handles the 'Attributes' Page UI actions
+ */
+public class AttributesPage extends ComponentPage {
+
+    private WebElement wrappingElement;
+
+    public AttributesPage(final WebDriver webDriver) {
+        super(webDriver);
+    }
+
+    @Override
+    public void isLoaded() {
+        super.isLoaded();
+        waitForElementVisibility(By.xpath(XpathSelector.MAIN_DIV.getXpath()));
+        waitForElementVisibility(By.xpath(XpathSelector.TITLE_DIV.getXpath()));
+        waitForElementVisibility(By.xpath(XpathSelector.ATTRIBUTES_DIV.getXpath()));
+        wrappingElement = findElement(By.xpath(XpathSelector.ATTRIBUTES_DIV.getXpath()));
+    }
+
+    public boolean isAttributePresent(final String attributeName) {
+        try {
+            final WebElement element = wrappingElement.findElement(By.xpath(XpathSelector.ATTRIBUTES_NAME_SPAN.getXpath(attributeName)));
+            return element != null;
+        } catch (final Exception e) {
+            return false;
+        }
+    }
+
+    public void addAttribute(final AttributeData attributeData) {
+        final AttributeModal attributeModal = clickOnAdd();
+        attributeModal.isLoaded();
+        attributeModal.fillForm(attributeData, false);
+        attributeModal.clickSave();
+        loaderHelper.waitForLoader(LoaderHelper.XpathSelector.SDC_LOADER_LARGE, 5);
+    }
+
+    public AttributeModal clickOnAdd() {
+        waitToBeClickable(By.xpath(XpathSelector.ADD_BTN.getXpath())).click();
+        return new AttributeModal(webDriver);
+    }
+
+    public AttributeModal clickOnEdit(final String attributeName) {
+        waitToBeClickable(By.xpath(XpathSelector.EDIT_BTN.getXpath(attributeName))).click();
+        return new AttributeModal(webDriver);
+    }
+
+    public void deleteAttribute(final String attributeName) {
+        if (attributeName == null) {
+            return;
+        }
+        waitForElementVisibility(By.xpath(XpathSelector.DELETE_BTN.getXpath(attributeName))).click();
+        waitToBeClickable(By.xpath(XpathSelector.DELETE_ATTRIBUTE_CONFIRM_BTN.getXpath())).click();
+        waitForElementInvisibility(By.xpath(XpathSelector.DELETE_BTN.getXpath(attributeName)), 5);
+    }
+
+    public void editAttribute(final AttributeData attributeData) {
+        final AttributeModal attributeModal = clickOnEdit(attributeData.getAttributeName());
+        attributeModal.isLoaded();
+        attributeModal.fillForm(attributeData, true);
+        attributeModal.clickSave();
+        loaderHelper.waitForLoader(LoaderHelper.XpathSelector.SDC_LOADER_LARGE, 5);
+    }
+
+    /**
+     * Enum that contains identifiers and xpath expressions to elements related to the enclosing page object.
+     */
+    @AllArgsConstructor
+    private enum XpathSelector {
+        MAIN_DIV("w-sdc-main-right-container", "//div[@class='%s']"),
+        TITLE_DIV("workspace-tab-title", "//div[contains(@class,'%s') and contains(text(), 'Attributes')]"),
+        ATTRIBUTES_DIV("workspace-attributes", "//div[@class='%s']"),
+        ADD_BTN("svg-icon-label", "//span[contains(@class,'%s') and contains(text(), 'Add')]"),
+        ATTRIBUTES_NAME_SPAN("//div[@data-tests-id='attrib-name_%s']"),
+        EDIT_BTN("//div[contains(@class,'svg-icon') and @data-tests-id='edit_%s']"),
+        DELETE_BTN("//div[contains(@class,'svg-icon') and @data-tests-id='delete_%s']"),
+        DELETE_ATTRIBUTE_CONFIRM_BTN("delete-modal-button-ok", "//button[@data-tests-id='%s']");
+
+        @Getter
+        private String id;
+        private final String xpathFormat;
+
+        XpathSelector(final String xpathFormat) {
+            this.xpathFormat = xpathFormat;
+        }
+
+        public String getXpath(final String... xpathParams) {
+            return String.format(xpathFormat, xpathParams);
+        }
+
+        public String getXpath() {
+            return String.format(xpathFormat, id);
+        }
+
+    }
+
+}
index 68087f6..8bbbf5a 100644 (file)
@@ -91,6 +91,14 @@ public class ComponentPage extends AbstractPageObject {
         return resourceLeftSideMenu.clickOnAttributesOutputsMenuItem();
     }
 
+    public AttributesPage goToAttributes() {
+        return resourceLeftSideMenu.clickOnAttributesMenuItem();
+    }
+
+    public <T extends ComponentPage> T clickOnGeneralMenuItem(Class<? extends T> clazz) {
+        return resourceLeftSideMenu.clickOnGeneralMenuItem(clazz);
+    }
+
     public ResourcePropertiesAssignmentPage goToPropertiesAssignment() {
         return resourceLeftSideMenu.clickOnPropertiesAssignmentMenuItem();
     }
index 9b860d5..413d4b4 100644 (file)
@@ -74,6 +74,16 @@ public class ResourceLeftSideMenu extends AbstractPageObject {
         return new AttributesOutputsPage(webDriver);
     }
 
+    /**
+     * Clicks on the 'Attributes' menu item.
+     *
+     * @return the next page object
+     */
+    public AttributesPage clickOnAttributesMenuItem() {
+        wrappingElement.findElement(By.xpath(XpathSelector.ATTRIBUTES_MENU.getXpath())).click();
+        return new AttributesPage(webDriver);
+    }
+
     /**
      * Clicks on the TOSCA artifacts menu item.
      *
@@ -85,13 +95,13 @@ public class ResourceLeftSideMenu extends AbstractPageObject {
     }
 
     /**
-     * Clicks on the TOSCA artifacts menu item.
+     * Clicks on the 'General' menu item.
      *
      * @return the next page object
      */
-    public ServiceCreatePage clickOnGeneralMenuItem() {
+    public <T extends ComponentPage> T clickOnGeneralMenuItem(Class<? extends T> clazz) {
         wrappingElement.findElement(By.xpath(XpathSelector.GENERAL_MENU.getXpath())).click();
-        return new ServiceCreatePage(webDriver);
+        return (T) new ComponentPage(webDriver);
     }
 
     public CompositionPage clickOnCompositionMenuItem() {
@@ -106,9 +116,12 @@ public class ResourceLeftSideMenu extends AbstractPageObject {
     private enum XpathSelector {
         MAIN_DIV("w-sdc-left-sidebar", "//div[@class='%s']"),
         PROPERTIES_ASSIGNMENT_MENU("Properties AssignmentLeftSideMenu", "//*[@data-tests-id='%s']"),
+        PROPERTIES_MENU("PropertiesLeftSideMenu", "//*[@data-tests-id='%s']"),
         ATTRIBUTES_OUTPUTS_MENU("Attributes & OutputsLeftSideMenu", "//*[@data-tests-id='%s']"),
+        ATTRIBUTES_MENU("AttributesLeftSideMenu", "//*[@data-tests-id='%s']"),
         GENERAL_MENU("GeneralLeftSideMenu", "//*[@data-tests-id='%s']"),
         COMPOSITION_MENU("CompositionLeftSideMenu", "//*[@data-tests-id='%s']"),
+        REQUIREMENT_CAPABILITY_MENU("Req. & CapabilitiesLeftSideMenu", "//*[@data-tests-id='%s']"),
         TOSCA_ARTIFACTS_MENU("TOSCA ArtifactsLeftSideMenu", "//*[@data-tests-id='%s']");
 
         @Getter
index 6406c2a..4c0356f 100644 (file)
@@ -79,7 +79,8 @@ public class LoaderHelper extends AbstractPageObject {
     @AllArgsConstructor
     @Getter
     public enum XpathSelector {
-        LOADER_WITH_LOADER_BACKGROUND("//*[contains(@class, 'sdc-loader-background')]");
+        LOADER_WITH_LOADER_BACKGROUND("//*[contains(@class, 'sdc-loader-background')]"),
+        SDC_LOADER_LARGE("//div[@data-tests-id='loader']");
 
         private final String xpath;
     }