diff options
9 files changed, 539 insertions, 232 deletions
diff --git a/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html b/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html index 094045e8b4..590217d0a5 100644 --- a/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html +++ b/catalog-ui/src/app/ng2/pages/workspace/attributes/attribute-modal.component.html @@ -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> @@ -48,15 +49,15 @@ <!-- 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')"> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.html b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.html index 6d50bbe11b..a04849a7f6 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.html +++ b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.html @@ -17,27 +17,27 @@ <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> @@ -48,9 +48,10 @@ <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> @@ -76,14 +77,16 @@ <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> diff --git a/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.ts b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.ts index ca8d5460e8..d85755bce8 100644 --- a/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.ts +++ b/catalog-ui/src/app/view-models/workspace/tabs/attributes/attributes.component.ts @@ -1,188 +1,186 @@ -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); + } } diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java index a571c1b9f6..b3538e32d1 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/execute/sanity/ImportVfcUiTest.java @@ -19,11 +19,11 @@ 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 index 0000000000..bd1330d8cf --- /dev/null +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributeModal.java @@ -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 index 0000000000..47b29b9f23 --- /dev/null +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/AttributesPage.java @@ -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); + } + + } + +} diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java index 68087f6261..8bbbf5a655 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ComponentPage.java @@ -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(); } diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java index 9b860d5ab0..413d4b4a6b 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/pages/ResourceLeftSideMenu.java @@ -75,6 +75,16 @@ public class ResourceLeftSideMenu extends AbstractPageObject { } /** + * 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. * * @return the next page object @@ -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 diff --git a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/utilities/LoaderHelper.java b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/utilities/LoaderHelper.java index 6406c2a460..4c0356ff46 100644 --- a/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/utilities/LoaderHelper.java +++ b/integration-tests/src/test/java/org/onap/sdc/frontend/ci/tests/utilities/LoaderHelper.java @@ -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; } |