diff --git a/CHANGELOG.md b/CHANGELOG.md index 227703bcc..f2e8c15b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,8 +18,8 @@ All notable changes to AET will be documented in this file. - [PR-430](https://github.com/Cognifide/aet/pull/430) Upgraded HTML codesniffer to latest release (3.2.0) - [PR-385](https://github.com/Cognifide/aet/pull/385) Fixed ChefDK and vagrant-berkshelf versions - [PR-404](https://github.com/Cognifide/aet/pull/404) Added missing tooltip for conditional tests - - [PR-408](https://github.com/Cognifide/aet/pull/408) Advanced Screen Comparision button layout fix +- [PR-410](https://github.com/Cognifide/aet/pull/410) Notification that displays when exclude-elements are not found on page now shows what specific elements were not found([#372](https://github.com/Cognifide/aet/issues/372)) ## Version 3.1.0 diff --git a/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/ExcludedElement.java b/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/ExcludedElement.java index 9e2a7ac82..03e69359e 100644 --- a/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/ExcludedElement.java +++ b/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/ExcludedElement.java @@ -20,6 +20,7 @@ import java.io.Serializable; public class ExcludedElement implements Serializable { + private static final long serialVersionUID = 692282363549228800L; private final Point point; diff --git a/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/LayoutExclude.java b/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/LayoutExclude.java index c31502890..f7742865c 100644 --- a/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/LayoutExclude.java +++ b/api/communication-api/src/main/java/com/cognifide/aet/communication/api/metadata/exclude/LayoutExclude.java @@ -17,18 +17,25 @@ import java.io.Serializable; import java.util.List; +import java.util.Set; public class LayoutExclude implements Serializable { + private static final long serialVersionUID = -6496109702966509444L; private final List excludedElements; + private final Set notFoundElements; - public LayoutExclude( - List excludedElements) { + public LayoutExclude(List excludedElements, Set notFoundElements) { this.excludedElements = excludedElements; + this.notFoundElements = notFoundElements; } public List getExcludedElements() { return excludedElements; } + + public Set getNotFoundElements() { + return notFoundElements; + } } diff --git a/core/jobs/pom.xml b/core/jobs/pom.xml index 688faf54a..8ef6359c0 100644 --- a/core/jobs/pom.xml +++ b/core/jobs/pom.xml @@ -141,11 +141,15 @@ org.slf4j slf4j-simple - com.google.code.findbugs jsr305 + + org.assertj + assertj-core + + diff --git a/core/jobs/src/main/java/com/cognifide/aet/job/common/collectors/screen/ScreenCollector.java b/core/jobs/src/main/java/com/cognifide/aet/job/common/collectors/screen/ScreenCollector.java index 0cb08cb3f..3aa9c9f01 100644 --- a/core/jobs/src/main/java/com/cognifide/aet/job/common/collectors/screen/ScreenCollector.java +++ b/core/jobs/src/main/java/com/cognifide/aet/job/common/collectors/screen/ScreenCollector.java @@ -32,10 +32,18 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.stream.Collectors; import javax.imageio.ImageIO; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; import org.apache.commons.lang3.StringUtils; import org.openqa.selenium.By; @@ -56,6 +64,8 @@ public class ScreenCollector extends WebElementsLocatorParams implements Collect private static final String PNG_FORMAT = "png"; + private static final String CSS_SELECTOR_SEPARATOR = ","; + private final WebDriver webDriver; private final ArtifactsDAO artifactsDAO; @@ -70,22 +80,6 @@ public class ScreenCollector extends WebElementsLocatorParams implements Collect this.artifactsDAO = artifactsDAO; } - private List getExcludeElementsFromWebElements(List webElements) { - List excludeExcludedElements = new ArrayList<>(webElements.size()); - - Point screenshotOffset = isSelectorPresent() ? - webDriver.findElement(getLocator()).getLocation() : new Point(0, 0); - for (WebElement webElement : webElements) { - Point point = webElement.getLocation() - .moveBy(-screenshotOffset.getX(), -screenshotOffset.getY()); - - excludeExcludedElements.add(new ExcludedElement( - new java.awt.Point(point.getX(), point.getY()), - new java.awt.Dimension(webElement.getSize().width, webElement.getSize().height))); - } - return excludeExcludedElements; - } - @Override public CollectorStepResult collect() throws ProcessingException { byte[] screenshot = takeScreenshot(); @@ -98,11 +92,7 @@ public CollectorStepResult collect() throws ProcessingException { String resultId = artifactsDAO.saveArtifact(properties, screenshotStream, CONTENT_TYPE); if (excludeCssSelector != null) { - List excludeExcludedElements = getExcludeElementsFromWebElements( - webDriver.findElements(By.cssSelector(excludeCssSelector))); - stepResult = CollectorStepResult - .newCollectedResult(resultId, - new Payload((new LayoutExclude(excludeExcludedElements)))); + stepResult = getResultWithExcludeSelectors(resultId); } else { stepResult = CollectorStepResult.newCollectedResult(resultId); } @@ -124,6 +114,65 @@ private boolean isPatternAndResultMD5Identical(byte[] screenshot) { } } + private CollectorStepResult getResultWithExcludeSelectors(String resultId) { + ArrayList cssSelectors = new ArrayList<>( + Arrays.asList(excludeCssSelector.split(CSS_SELECTOR_SEPARATOR))); + + Map> elements = searchElementsOnPage(cssSelectors); + + List foundExcludeElements = getFoundWebElements(elements); + Set notFoundSelectors = getNotFoundSelectors(elements); + + List excludedElements = getExcludeElementsFromWebElements( + foundExcludeElements); + return CollectorStepResult.newCollectedResult(resultId, + new Payload((new LayoutExclude(excludedElements, notFoundSelectors)))); + } + + private HashMap> searchElementsOnPage(ArrayList cssSelectors) { + HashMap> elements = new HashMap<>(); + + cssSelectors.forEach(selector -> { + List foundElements = webDriver.findElements(By.cssSelector(selector)); + if (!CollectionUtils.isEmpty(foundElements)) { + elements.put(selector, foundElements); + } else { + elements.put(selector, Collections.emptyList()); + } + }); + + return elements; + } + + private List getFoundWebElements(Map> elements) { + return elements.values().stream() + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + private Set getNotFoundSelectors(Map> elements) { + return elements.entrySet().stream() + .filter(entry -> entry.getValue().isEmpty()) + .map(Entry::getKey) + .collect(Collectors.toSet()); + } + + private List getExcludeElementsFromWebElements(List webElements) { + List excludeExcludedElements = new ArrayList<>(webElements.size()); + + Point screenshotOffset = isSelectorPresent() ? + webDriver.findElement(getLocator()).getLocation() : new Point(0, 0); + for (WebElement webElement : webElements) { + Point point = webElement.getLocation() + .moveBy(-screenshotOffset.getX(), -screenshotOffset.getY()); + + excludeExcludedElements.add(new ExcludedElement( + new java.awt.Point(point.getX(), point.getY()), + new java.awt.Dimension(webElement.getSize().width, webElement.getSize().height))); + } + return excludeExcludedElements; + } + @Override public void setParameters(Map params) throws ParametersException { if (StringUtils.isNotBlank(params.get(XPATH_PARAM)) || StringUtils diff --git a/core/jobs/src/main/java/com/cognifide/aet/job/common/comparators/layout/LayoutComparator.java b/core/jobs/src/main/java/com/cognifide/aet/job/common/comparators/layout/LayoutComparator.java index 028ad1cb4..4d8e24c74 100644 --- a/core/jobs/src/main/java/com/cognifide/aet/job/common/comparators/layout/LayoutComparator.java +++ b/core/jobs/src/main/java/com/cognifide/aet/job/common/comparators/layout/LayoutComparator.java @@ -34,9 +34,12 @@ import java.io.ByteArrayOutputStream; import java.io.IOException; import java.io.InputStream; +import java.security.SecureRandom; import java.util.List; import java.util.Map; import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; import javax.imageio.ImageIO; import org.apache.commons.collections.CollectionUtils; import org.apache.commons.io.IOUtils; @@ -64,7 +67,7 @@ public class LayoutComparator implements ComparatorJob { private boolean excludeFunctionIsOn; - private boolean excludeElementsNotFound; + private Set notFoundExcludeElements; LayoutComparator(ComparatorProperties comparatorProperties, ArtifactsDAO artifactsDAO) { this.properties = comparatorProperties; @@ -105,9 +108,8 @@ public ComparatorStepResult compare() throws ProcessingException { if (!CollectionUtils.isEmpty(excludedElements)) { hideElementsInImg(patternImg, excludedElements); hideElementsInImg(collectedImg, excludedElements); - } else { - excludeElementsNotFound = true; } + notFoundExcludeElements = exclude.getNotFoundElements(); }); imageComparisonResult = ImageComparison.compare(patternImg, collectedImg); @@ -144,8 +146,10 @@ private ComparatorStepResult saveArtifacts(ImageComparisonResult imageComparison result = new ComparatorStepResult(maskArtifactId, Status.FAILED, true); } - if (excludeElementsNotFound) { - addExcludeMessageToResult(result, "Elements to exclude are not found on page"); + if (!CollectionUtils.isEmpty(notFoundExcludeElements)) { + addExcludeMessageToResult(result, + "The following elements to be excluded have not been found on page: ", + String.join(", ", notFoundExcludeElements)); } addPixelDifferenceDataToResult(result, imageComparisonResult); @@ -203,8 +207,10 @@ private void addPixelDifferenceDataToResult(ComparatorStepResult result, Double.toString(imageComparisonResult.getPercentagePixelDifference())); } - private void addExcludeMessageToResult(ComparatorStepResult result, String message) { + private void addExcludeMessageToResult(ComparatorStepResult result, String message, + String notFoundCssElements) { result.addData("excludeMessage", message); + result.addData("notFoundCssElements", notFoundCssElements); } private void addTimestampToResult(ComparatorStepResult result) { diff --git a/core/jobs/src/test/java/com/cognifide/aet/job/common/collectors/screen/ScreenCollectorTest.java b/core/jobs/src/test/java/com/cognifide/aet/job/common/collectors/screen/ScreenCollectorTest.java new file mode 100644 index 000000000..b2f796d8c --- /dev/null +++ b/core/jobs/src/test/java/com/cognifide/aet/job/common/collectors/screen/ScreenCollectorTest.java @@ -0,0 +1,128 @@ +/** + * AET + * + * Copyright (C) 2013 Cognifide Limited + * + * 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. + */ +package com.cognifide.aet.job.common.collectors.screen; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Mockito.when; + +import com.cognifide.aet.communication.api.metadata.exclude.LayoutExclude; +import com.cognifide.aet.job.api.collector.CollectorProperties; +import com.cognifide.aet.job.api.exceptions.ParametersException; +import com.cognifide.aet.vs.ArtifactsDAO; +import java.lang.reflect.Array; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.Optional; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.mockito.Answers; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.runners.MockitoJUnitRunner; +import org.openqa.selenium.By; +import org.openqa.selenium.OutputType; +import org.openqa.selenium.Point; +import org.openqa.selenium.WebElement; +import org.openqa.selenium.remote.RemoteWebDriver; + +@RunWith(MockitoJUnitRunner.class) +public class ScreenCollectorTest { + + private static final String SELECTOR_1 = ".dynamic1"; + + private static final String SELECTOR_2 = ".dynamic2"; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private WebElement webElement1; + + @Mock(answer = Answers.RETURNS_DEEP_STUBS) + private WebElement webElement2; + + @Mock + private RemoteWebDriver webDriver; + + @Mock + private CollectorProperties collectorProperties; + + @Mock + private ArtifactsDAO artifactsDAO; + + @InjectMocks + private ScreenCollector screenCollector; + + private byte[] screenshot = {1, 2}; + + @Before + public void setup() throws Exception { + when(collectorProperties.getPatternId()).thenReturn(null); + when(webDriver.getScreenshotAs(OutputType.BYTES)).thenReturn(screenshot); + + when(webElement1.getLocation().moveBy(anyInt(), anyInt())).thenReturn(new Point(0, 0)); + when(webElement2.getLocation().moveBy(anyInt(), anyInt())).thenReturn(new Point(0, 0)); + + setExcludeElementsParam(String.format("%s,%s", SELECTOR_1, SELECTOR_2)); + } + + @Test + public void collectScreenExcludeElements_findAll() throws Exception { + when(webDriver.findElements(By.cssSelector(SELECTOR_1))) + .thenReturn(Collections.singletonList(webElement1)); + when(webDriver.findElements(By.cssSelector(SELECTOR_2))) + .thenReturn(Collections.singletonList(webElement2)); + + LayoutExclude exclude = screenCollector.collect().getPayload().getLayoutExclude(); + + assertThat(exclude.getExcludedElements()).hasSize(2); + assertThat(exclude.getNotFoundElements()).isNullOrEmpty(); + } + + @Test + public void collectScreenExcludeElements_findNone() throws Exception { + when(webDriver.findElements(By.cssSelector(SELECTOR_1))) + .thenReturn(Collections.emptyList()); + when(webDriver.findElements(By.cssSelector(SELECTOR_2))) + .thenReturn(Collections.emptyList()); + + LayoutExclude exclude = screenCollector.collect().getPayload().getLayoutExclude(); + + assertThat(exclude.getExcludedElements()).isEmpty(); + assertThat(exclude.getNotFoundElements()).hasSize(2); + } + + @Test + public void collectScreenExcludeElements_findSome() throws Exception { + when(webDriver.findElements(By.cssSelector(SELECTOR_1))) + .thenReturn(Collections.singletonList(webElement1)); + when(webDriver.findElements(By.cssSelector(SELECTOR_2))) + .thenReturn(Collections.emptyList()); + + LayoutExclude exclude = screenCollector.collect().getPayload().getLayoutExclude(); + + assertThat(exclude.getExcludedElements()).hasSize(1); + assertThat(exclude.getNotFoundElements()).hasSize(1); + } + + private void setExcludeElementsParam(String excludeCssElements) throws ParametersException { + Map params = new HashMap<>(); + params.put("exclude-elements", excludeCssElements); + screenCollector.setParameters(params); + } +} diff --git a/pom.xml b/pom.xml index cd825e1b9..ed9ac74c4 100644 --- a/pom.xml +++ b/pom.xml @@ -519,7 +519,12 @@ 1.3.0 test - + + org.assertj + assertj-core + 3.11.1 + test + com.github.stefanbirkner system-rules diff --git a/report/src/main/webapp/app/layout/main/url/reports/screen_layout.html b/report/src/main/webapp/app/layout/main/url/reports/screen_layout.html index 95e604c42..db89e340b 100644 --- a/report/src/main/webapp/app/layout/main/url/reports/screen_layout.html +++ b/report/src/main/webapp/app/layout/main/url/reports/screen_layout.html @@ -28,7 +28,6 @@ Show Mask - Exclude info: {{case.comparator.stepResult.data.excludeMessage}}
@@ -40,6 +39,14 @@
+
+
+ {{case.comparator.stepResult.data.excludeMessage}} +
+
+ {{case.comparator.stepResult.data.notFoundCssElements}} +
+