1c5793f3bd78feb5dd15c3e31b5a9cb54fbf6b3f
[sdc.git] / integration-tests / src / test / java / org / onap / sdc / frontend / ci / tests / pages / component / workspace / CompositionCanvasComponent.java
1 /*
2  * ============LICENSE_START=======================================================
3  *  Copyright (C) 2021 Nordix Foundation
4  *  ================================================================================
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at
8  *
9  *        http://www.apache.org/licenses/LICENSE-2.0
10  *  Unless required by applicable law or agreed to in writing, software
11  *  distributed under the License is distributed on an "AS IS" BASIS,
12  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  *  See the License for the specific language governing permissions and
14  *  limitations under the License.
15  *
16  *  SPDX-License-Identifier: Apache-2.0
17  *  ============LICENSE_END=========================================================
18  */
19
20 package org.onap.sdc.frontend.ci.tests.pages.component.workspace;
21
22 import static org.onap.sdc.backend.ci.tests.datatypes.enums.UserRoleEnum.DESIGNER;
23
24 import com.aventstack.extentreports.Status;
25 import com.google.gson.JsonObject;
26 import com.google.gson.JsonParser;
27 import java.time.Duration;
28 import java.util.ArrayList;
29 import java.util.HashSet;
30 import java.util.Optional;
31 import java.util.Random;
32 import java.util.Set;
33 import lombok.AllArgsConstructor;
34 import lombok.Getter;
35 import org.apache.commons.lang3.tuple.ImmutablePair;
36 import org.onap.sdc.backend.ci.tests.utils.general.AtomicOperationUtils;
37 import org.onap.sdc.frontend.ci.tests.datatypes.CanvasNodeElement;
38 import org.onap.sdc.frontend.ci.tests.exception.CompositionCanvasRuntimeException;
39 import org.onap.sdc.frontend.ci.tests.execute.setup.ExtentTestActions;
40 import org.onap.sdc.frontend.ci.tests.flow.exception.UiTestFlowRuntimeException;
41 import org.onap.sdc.frontend.ci.tests.pages.AbstractPageObject;
42 import org.openecomp.sdc.be.model.ComponentInstance;
43 import org.openecomp.sdc.be.model.Resource;
44 import org.openecomp.sdc.be.model.Service;
45 import org.openqa.selenium.Dimension;
46 import org.openqa.selenium.JavascriptExecutor;
47 import org.openqa.selenium.Point;
48 import org.openqa.selenium.WebDriver;
49 import org.openqa.selenium.WebElement;
50 import org.openqa.selenium.interactions.Actions;
51 import org.slf4j.Logger;
52 import org.slf4j.LoggerFactory;
53
54 public class CompositionCanvasComponent extends AbstractPageObject {
55
56     private static final Logger LOGGER = LoggerFactory.getLogger(CompositionCanvasComponent.class);
57     private static final String nodePositionJs = "var cy = window.jQuery('.sdc-composition-graph-wrapper').cytoscape('get');%n"
58         + "var n = cy.nodes('[name=\"%s\"]');%n"
59         + "var nPos = n.renderedPosition();%n"
60         + "return JSON.stringify({%n"
61         + "    x: nPos.x,%n"
62         + "    y: nPos.y%n"
63         + "})";
64
65     private static final String getNodesJs = "var cy = window.jQuery('.sdc-composition-graph-wrapper').cytoscape('get');\n"
66         + "var nodes = [];"
67         + "cy.nodes().forEach((node) => {nodes.push(JSON.stringify({name: node.data('name'), position: node.renderedPosition()}))});\n"
68         + "return nodes;";
69
70     private final CompositionElementsComponent compositionElementsComponent;
71     private final CompositionDetailSideBarComponent compositionDetailSideBarComponent;
72
73     private WebElement canvasWebElement;
74     private Set<CanvasNodeElement> canvasElementList;
75     private int canvasCenterX;
76     private int canvasCenterY;
77     private int canvasWidth;
78     private int canvasHeight;
79
80     public CompositionCanvasComponent(final WebDriver webDriver) {
81         super(webDriver);
82         compositionElementsComponent = new CompositionElementsComponent(webDriver);
83         compositionDetailSideBarComponent = new CompositionDetailSideBarComponent(webDriver);
84     }
85
86     @Override
87     public void isLoaded() {
88         //waiting the canvas data to be load and animation finishes.
89         new Actions(webDriver).pause(Duration.ofSeconds(2)).perform();
90         canvasWebElement = waitToBeClickable(XpathSelector.CANVAS_ELEMENT.getXpath());
91         compositionElementsComponent.isLoaded();
92         compositionDetailSideBarComponent.isLoaded();
93         loadCanvas();
94         loadElements();
95     }
96
97     private void loadCanvas() {
98         canvasWidth = canvasWebElement.getSize().getWidth();
99         canvasHeight = canvasWebElement.getSize().getHeight();
100         canvasCenterX = canvasWidth / 2;
101         canvasCenterY = canvasHeight / 2;
102         LOGGER.debug("Canvas with size [{}, {}] and center [{}, {}]", canvasWidth, canvasHeight, canvasCenterX, canvasCenterY);
103     }
104
105     private void loadElements() {
106         canvasElementList = new HashSet<>();
107         final Object nodeListObj = ((JavascriptExecutor) webDriver).executeScript(getNodesJs);
108         if (!(nodeListObj instanceof ArrayList)) {
109             return;
110         }
111         final ArrayList<String> nodeList = (ArrayList<String>) nodeListObj;
112         if (nodeList.isEmpty()) {
113             return;
114         }
115         nodeList.forEach(nodeString -> {
116             final JsonObject node = new JsonParser().parse(nodeString).getAsJsonObject();
117             final JsonObject position = node.get("position").getAsJsonObject();
118             final CanvasNodeElement canvasElement =
119                 new CanvasNodeElement(node.get("name").getAsString(), position.get("x").getAsInt(), position.get("y").getAsInt());
120             canvasElementList.add(canvasElement);
121         });
122     }
123
124     public void selectNode(final String elementName) {
125         final Optional<CanvasNodeElement> canvasElementOptional = canvasElementList.stream()
126             .filter(canvasNodeElement -> canvasNodeElement.getName().startsWith(elementName))
127             .findFirst();
128         if (canvasElementOptional.isEmpty()) {
129             throw new CompositionCanvasRuntimeException(String.format("Given element '%s' does not exist on the element list", elementName));
130         }
131         final CanvasNodeElement canvasNodeElement = canvasElementOptional.get();
132         final Point positionFromCenter = calculateOffsetFromCenter(canvasNodeElement.getPositionX(),
133             canvasNodeElement.getPositionY());
134         final Actions actions = new Actions(webDriver);
135         int offsetFromElementCenter = 10;
136         actions.moveToElement(canvasWebElement, positionFromCenter.getX() - offsetFromElementCenter,
137             positionFromCenter.getY() + offsetFromElementCenter)
138             .pause(Duration.ofSeconds(1))
139             .click()
140             .perform();
141         ExtentTestActions.takeScreenshot(Status.INFO, "canvas-node-selected", String.format("'%s' was selected", elementName));
142     }
143
144     public ComponentInstance createNodeOnServiceCanvas(final String serviceName, final String serviceVersion, final String resourceName,
145                                                        final String resourceVersion) {
146         final Point freePositionInCanvas = getFreePositionInCanvas(20);
147         final Point pointFromCanvasCenter = calculateOffsetFromCenter(freePositionInCanvas);
148         try {
149             final Service service =
150                 AtomicOperationUtils.getServiceObjectByNameAndVersion(DESIGNER, serviceName, serviceVersion);
151             final Resource resourceToAdd =
152                 AtomicOperationUtils.getResourceObjectByNameAndVersion(DESIGNER, resourceName, resourceVersion);
153             final ComponentInstance componentInstance = AtomicOperationUtils
154                 .addComponentInstanceToComponentContainer(resourceToAdd, service, DESIGNER, true,
155                     String.valueOf(pointFromCanvasCenter.getX()), String.valueOf(pointFromCanvasCenter.getY()))
156                 .left().value();
157
158             LOGGER.debug("Created instance {} in the Service {}", componentInstance.getName(), serviceName);
159             return componentInstance;
160         } catch (final Exception e) {
161             throw new CompositionCanvasRuntimeException("Could not create node through the API", e);
162         }
163     }
164
165     public ComponentInstance createNodeOnResourceCanvas(final String serviceName, final String serviceVersion, final String resourceName,
166                                                         final String resourceVersion) {
167         final Point freePositionInCanvas = getFreePositionInCanvas(20);
168         final Point pointFromCanvasCenter = calculateOffsetFromCenter(freePositionInCanvas);
169         try {
170             final Resource service = AtomicOperationUtils.getResourceObjectByNameAndVersion(DESIGNER, serviceName, serviceVersion);
171             final Resource resourceToAdd = AtomicOperationUtils.getResourceObjectByNameAndVersion(DESIGNER, resourceName, resourceVersion);
172             final ComponentInstance componentInstance =
173                 AtomicOperationUtils.addComponentInstanceToComponentContainer(resourceToAdd, service, DESIGNER, true,
174                     String.valueOf(pointFromCanvasCenter.getX()), String.valueOf(pointFromCanvasCenter.getY())).left().value();
175
176             LOGGER.debug("Created instance {} in the Service {}", componentInstance.getName(), serviceName);
177             return componentInstance;
178         } catch (final Exception e) {
179             throw new CompositionCanvasRuntimeException("Could not create node through the API", e);
180         }
181     }
182
183     private Point getFreePositionInCanvas(int maxAttempts) {
184         boolean isPositionFree;
185         final int minSpace = 150;
186         for (int attemptCount = 0; attemptCount < maxAttempts; attemptCount++) {
187             final Point randomPositionInCanvas = getRandomPositionInCanvas();
188             isPositionFree = canvasElementList.stream()
189                 .noneMatch(canvasNodeElement -> Math.abs(canvasNodeElement.getPositionX() - randomPositionInCanvas.getX()) < minSpace
190                     && Math.abs(canvasNodeElement.getPositionX() - randomPositionInCanvas.getY()) < minSpace);
191             if (isPositionFree) {
192                 return randomPositionInCanvas;
193             }
194         }
195         throw new CompositionCanvasRuntimeException("Could not find a free Canvas position");
196     }
197
198     private Point getRandomPositionInCanvas() {
199         final Random random = new Random();
200         int x = random.nextInt(canvasWidth);
201         final int maxAllowedWidth = canvasWidth - getRightMarginWidth();
202         final int minAllowedWidth = 30;
203         if (x > maxAllowedWidth) {
204             x = x - getRightMarginWidth();
205         } else if (x < minAllowedWidth) {
206             x = x + minAllowedWidth;
207         }
208         int bottomMargin = 0;
209         int heightTopMargin = 100;
210         int y = random.nextInt(canvasHeight);
211         int maxAllowedHeight = canvasHeight - bottomMargin;
212
213         if (y > maxAllowedHeight) {
214             y = y - bottomMargin;
215         } else if (y < heightTopMargin) {
216             y = y + heightTopMargin;
217         }
218         LOGGER.debug("Generated random position in canvas [{},{}]", x, y);
219
220         return new Point(x, y);
221     }
222
223     private int getRightMarginWidth() {
224         int canvasIconsOffset = 100;
225         final Dimension sideBarSize = compositionDetailSideBarComponent.getSize();
226         return sideBarSize.getWidth() + canvasIconsOffset;
227     }
228
229     private Point calculateOffsetFromCenter(final Point point) {
230         return calculateOffsetFromCenter(point.getX(), point.getY());
231     }
232
233     private Point calculateOffsetFromCenter(final int xPosition, final int yPosition) {
234         final int positionX = xPosition - canvasCenterX;
235         final int positionY = yPosition - canvasCenterY;
236         return new Point(positionX, positionY);
237     }
238
239     public ImmutablePair<Integer, Integer> getElementPositionByName(final String elementName) {
240         final String scriptJs = String.format(nodePositionJs, elementName);
241         final Object position = ((JavascriptExecutor) webDriver).executeScript(scriptJs);
242         final JsonObject positionAsJson = new JsonParser().parse(position.toString()).getAsJsonObject();
243         int xElement = positionAsJson.get("x").getAsInt();
244         int yElement = positionAsJson.get("y").getAsInt();
245         return new ImmutablePair<>(xElement, yElement);
246     }
247
248     public RelationshipWizardComponent createLink(final String fromNodeName, final String toNodeName) {
249         final CanvasNodeElement fromCanvasElement = canvasElementList.stream()
250             .filter(canvasNodeElement -> canvasNodeElement.getName().equals(fromNodeName)).findFirst()
251             .orElseThrow(() -> new UiTestFlowRuntimeException(String.format("Could not find node '%s'", fromNodeName)));
252         final CanvasNodeElement toCanvasElement = canvasElementList.stream()
253             .filter(canvasNodeElement -> canvasNodeElement.getName().equals(toNodeName)).findFirst()
254             .orElseThrow(() -> new UiTestFlowRuntimeException(String.format("Could not find node '%s'", toNodeName)));
255
256         final Point greenPlusPosition = getElementGreenPlusPosition(fromCanvasElement.getName());
257         final Point greenPlusPositionFromCenter = calculateOffsetFromCenter(greenPlusPosition);
258         final Point toElementPositionFromCenter = calculateOffsetFromCenter(toCanvasElement.getPositionX(), toCanvasElement.getPositionY());
259         new Actions(webDriver)
260             .moveToElement(canvasWebElement, greenPlusPositionFromCenter.getX(), greenPlusPositionFromCenter.getY())
261             .moveByOffset(3, 3).moveByOffset(-3, -3)
262             .pause(Duration.ofSeconds(2))
263             .clickAndHold()
264             .pause(Duration.ofSeconds(1))
265             .moveToElement(canvasWebElement, toElementPositionFromCenter.getX(), toElementPositionFromCenter.getY())
266             .pause(Duration.ofSeconds(1))
267             .release()
268             .perform();
269         return new RelationshipWizardComponent(webDriver);
270     }
271
272     public Point getElementGreenPlusPosition(final String elementName) {
273         String scriptJS = "var cy = window.jQuery('.sdc-composition-graph-wrapper').cytoscape('get');\n"
274             + "var cyZoom = cy.zoom();\n"
275             + "var n = cy.nodes('[name=\"" + elementName + "\"]');\n"
276             + "var nPos = n.renderedPosition();\n"
277             + "var nData = n.data();\n"
278             + "var nImgSize = nData.imgWidth;\n"
279             + "var shiftSize = (nImgSize-18)*cyZoom/2;\n"
280             + "return JSON.stringify({\n"
281             + "\tx: nPos.x + shiftSize,\n"
282             + "\ty: nPos.y - shiftSize\n"
283             + "});";
284         final String o = (String) ((JavascriptExecutor) webDriver).executeScript(scriptJS);
285         final JsonObject node = new JsonParser().parse(o).getAsJsonObject();
286         final int x = node.get("x").getAsInt();
287         final int y = node.get("y").getAsInt();
288         return new Point(x, y);
289     }
290
291     /**
292      * Enum that contains identifiers and xpath expressions to elements related to the enclosing page object.
293      */
294     @AllArgsConstructor
295     private enum XpathSelector {
296         CANVAS_ELEMENT("canvas", "//*[@data-tests-id='%s']//canvas[1]");
297
298         @Getter
299         private final String id;
300         private final String xpathFormat;
301
302         public String getXpath() {
303             return String.format(xpathFormat, id);
304         }
305     }
306 }