/* * Copyright 2016-2017 Hewlett Packard Enterprise Development Company, L.P. * Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. */ package com.autonomy.abc.selenium.find.bi; import com.autonomy.abc.selenium.find.Container; import com.google.common.base.Function; import com.hp.autonomy.frontend.selenium.element.RangeInput; import com.hp.autonomy.frontend.selenium.util.ElementUtil; import com.hp.autonomy.frontend.selenium.util.Waits; import org.apache.commons.lang3.tuple.ImmutablePair; import org.openqa.selenium.By; import org.openqa.selenium.Dimension; import org.openqa.selenium.Point; import org.openqa.selenium.SearchContext; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.interactions.Actions; import org.openqa.selenium.support.ui.ExpectedConditions; import org.openqa.selenium.support.ui.WebDriverWait; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.Set; import java.util.stream.Collectors; public class TopicMapView { private static final int CONCEPT_CLUSTER_TIMEOUT = 25; private static final By CONCEPT_LOCATOR = By.cssSelector(".entity-topic-map > svg > path[stroke-opacity='0.7']"); private static final By CONCEPT_CLUSTER_LOCATOR = By.cssSelector(".entity-topic-map > svg > path[stroke-opacity='0.2']"); private final WebDriver driver; private final WebElement container; public TopicMapView(final WebDriver driver) { this.driver = driver; container = ElementUtil.ancestor(Container.currentTabContents(driver).findElement(By.className("entity-topic-map")), 2); } public TopicMapView(final WebElement element, final WebDriver driver) { this.driver = driver; container = element; } //GENERAL PAGE public boolean topicMapVisible() { return !findElements(By.cssSelector(".entity-topic-map:not(.hide)")).isEmpty(); } public boolean topicMapPresent() { return !findElements(By.cssSelector(".entity-topic-map.clickable:not(.hide)")).isEmpty(); } public WebElement emptyMessage() { return findElement(By.cssSelector(".entity-topic-map-empty p")); } public RangeInput speedVsAccuracySlider() { return new RangeInput(findElement(By.className("range-input-slider")), driver, 10); } private void offCentreClick(final WebElement element) { final int xOffset = element.getSize().getWidth() / 8; final int yOffset = element.getSize().getHeight() / 8; final Actions build = new Actions(driver); build.moveToElement(element, xOffset, yOffset).click().build().perform(); } public void waitForMapLoaded() { waitForConceptClusters(); } //MAP public WebElement map() { return findElement(By.cssSelector(".entity-topic-map.clickable")); } public int numberOfMapEntities() { return mapEntities().size(); } public List<WebElement> mapEntities() { return findElements(By.cssSelector(".entity-topic-map > svg > path")); } private List<WebElement> mapEntityTextElements() { return findElements(By.cssSelector(".entity-topic-map > svg > text")); } public List<String> mapEntityText() { return mapEntityTextElements().stream() .map(this::getElementText) .collect(Collectors.toList()); } private String getElementText(final SearchContext webElement) { return webElement.findElements(By.tagName("tspan")).stream() .map(WebElement::getText) .collect(Collectors.joining(" ")); } //CONCEPT CLUSTERS/PARENT ENTITIES //Complicated because need to wait until any entity exists before then waiting until elements //have an opacity of either the top or bottom layer (i.e. the map is done loading). private void waitForConceptClusters() { new WebDriverWait(driver, CONCEPT_CLUSTER_TIMEOUT) .withMessage("entity to exist with opacity 2") .until(ExpectedConditions.presenceOfElementLocated(CONCEPT_CLUSTER_LOCATOR)); new WebDriverWait(driver, 10) .withMessage("all entities to have opacity of either 0.2 or 0.7") .until((Function<? super WebDriver, Boolean>)x -> container.findElements( By.cssSelector(".entity-topic-map > svg > path:not([stroke-opacity='0.2']):not([stroke-opacity='0.7'])") ).isEmpty()); } private List<WebElement> conceptClusters() { return findElements(CONCEPT_CLUSTER_LOCATOR); } //TODO: In IE this does not always reveal the lower layer private void clickConceptClusters() { //TODO: this won't work for any topic map with medium or small sized polygons (or long topic titles) conceptClusters().forEach(this::offCentreClick); } public List<String> conceptClusterNames() { final int numberOfClusters = conceptClusters().size(); final List<String> entityText = mapEntityText(); final int max = entityText.size() - 1; final List<String> clusterNames = new ArrayList<>(numberOfClusters); for(int i = 0; i < numberOfClusters; i++) { clusterNames.add(entityText.get(max - i)); } return clusterNames; } public String clickNthClusterHeading(final int index) { waitForMapLoaded(); final int workingIndex = conceptClusters().size() - 1 - index; final int actualIndex = workingIndex + concepts().size(); final WebElement textElement = mapEntityTextElements().get(actualIndex); final String text = getElementText(textElement); textElement.click(); return text; } private TopicMapConcept nthConceptCluster(final int n) { return new TopicMapConcept(conceptClusters().get(n)); } public void waitForConcepts() { new WebDriverWait(driver, 10) .withMessage("bottom layer entities to reach the right opacity") .until(ExpectedConditions.visibilityOfAllElementsLocatedBy(CONCEPT_LOCATOR)); } public List<WebElement> concepts() { return findElements(CONCEPT_LOCATOR); } public String clickConceptAndAddText(final int noOfClusters) { waitForMapLoaded(); final int maxIndex = mapEntities().size() - 1; waitForConceptClusters(); clickConceptClusters(); waitForConcepts(); final int i = maxIndex - 1; final WebElement textElement = mapEntityTextElements().get(i - noOfClusters); final String concept = getElementText(textElement); offCentreClick(textElement); Waits.loadOrFadeWait(); return concept; } private List<ImmutablePair<WebElement, Integer>> childConcepts(final int clusterIndex) { //((lowestX,highestX),(lowestY,highestY)) final Double[][] boundariesOfChosenCluster = nthConceptCluster(clusterIndex).getBoundaries(); final Point mapCoordinates = map().getLocation(); //L:Concept; Y:Index final List<ImmutablePair<WebElement, Integer>> childConceptsOfChosenCluster = new ArrayList<>(); int entityIndex = 0; for(final WebElement concepts : concepts()) { final Dimension entitySize = concepts.getSize(); final Point absolutePosition = concepts.getLocation(); final int centreX = absolutePosition.x - mapCoordinates.x + entitySize.getWidth() / 2; final int centreY = absolutePosition.y - mapCoordinates.y + entitySize.getHeight() / 2; final Point centre = new Point(centreX, centreY); if(boundariesOfChosenCluster[0][0] <= centre.x && centre.x <= boundariesOfChosenCluster[0][1] && boundariesOfChosenCluster[1][0] <= centre.y && centre.y <= boundariesOfChosenCluster[1][1]) { childConceptsOfChosenCluster.add(new ImmutablePair<>(concepts, entityIndex)); } entityIndex++; } return childConceptsOfChosenCluster; } private List<String> namesOfChildConcepts(final Collection<ImmutablePair<WebElement, Integer>> childConceptsOfChosenCluster) { final int numberOfConcepts = concepts().size(); final List<String> textElements = mapEntityText(); return childConceptsOfChosenCluster.stream() .map(path -> textElements.get(numberOfConcepts - 1 - path.getRight())) .collect(Collectors.toList()); } public List<String> getChildConceptsOfCluster(final int clusterIndex) { final List<ImmutablePair<WebElement, Integer>> childEntitiesOfChosenCluster = childConcepts(clusterIndex); return namesOfChildConcepts(childEntitiesOfChosenCluster); } public Set<String> getGradientIds() { return findElements(By.tagName("linearGradient")) .stream() .map(tag -> tag.getAttribute("id")) .collect(Collectors.toSet()); } public Set<String> getFills() { return mapEntities().stream() .map(path -> path.getAttribute("fill")) .collect(Collectors.toSet()); } private WebElement findElement(final By locator) { return container.findElement(locator); } private List<WebElement> findElements(final By locator) { return container.findElements(locator); } }