Skip to content

Commit

Permalink
Merge pull request #259 from brcolow/text-flow-matchers
Browse files Browse the repository at this point in the history
(feat) TextFlowMatchers: Add matchers for the text of a TextFlow.
  • Loading branch information
hastebrot committed Mar 25, 2016
2 parents bc22648 + c2282df commit c642ea6
Show file tree
Hide file tree
Showing 4 changed files with 586 additions and 10 deletions.
@@ -0,0 +1,166 @@
/*
* Copyright 2013-2014 SmartBear Software
* Copyright 2014-2015 The TestFX Contributors
*
* Licensed under the EUPL, Version 1.1 or - as soon they will be approved by the
* European Commission - subsequent versions of the EUPL (the "Licence"); You may
* not use this work except in compliance with the Licence.
*
* You may obtain a copy of the Licence at:
* http://ec.europa.eu/idabc/eupl
*
* Unless required by applicable law or agreed to in writing, software distributed
* under the Licence is distributed on an "AS IS" basis, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the Licence for the
* specific language governing permissions and limitations under the Licence.
*/
package org.testfx.matcher.control;

import static org.testfx.matcher.base.GeneralMatchers.typeSafeMatcher;

import java.util.Locale;
import java.util.Objects;
import java.util.Optional;

import javafx.scene.Node;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;

import org.hamcrest.Factory;
import org.hamcrest.Matcher;
import org.testfx.api.annotation.Unstable;
import org.testfx.util.ColorUtils;

@Unstable(reason = "needs more tests")
public class TextFlowMatchers {

//---------------------------------------------------------------------------------------------
// STATIC METHODS.
//---------------------------------------------------------------------------------------------

@Factory
@Unstable(reason = "is missing apidocs")
public static Matcher<Node> hasText(String string) {
String descriptionText = "has text \"" + string + "\"";
return typeSafeMatcher(TextFlow.class, descriptionText, node -> hasText(node, string));
}

/**
* Allows one to verify both the content and color of the text that makes
* up a TextFlow. The color is matched by using the closest named color,
* as described further on.
* <p>
* Colors are specified using the following markup:
*
* <pre><code><COLOR>text</COLOR></code></pre>
* <p>
* Where {@literal COLOR} is one of JavaFX's named colors.
* <p>
* Here is an example for verifying that a TextFlow contains the text
* "hello" and that the named color that has the closest value to the
* color of the text is {@literal Colors.RED}:
* <p>
* <pre><code>
* Text text = new Text("hello");
* text.setFill(Colors.RED);
* TextFlow textFlow = new TextFlow(text);
* assertThat(textFlow, TextFlowMatchers.hasColoredText("<RED>hello</RED>"));
* </code></pre>
*
* @param string the text contained in the TextFlow with color markup that
* specifies the expected color of the text
* @return a match if the text contained in the TextFlow has the same content
* and colors that match by the "closest named color" criteria
* @see <a href="https://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html#typecolor">Named Colors</a>
*/
@Factory
@Unstable(reason = "is missing apidocs")
public static Matcher<Node> hasColoredText(String string) {
String descriptionText = "has colored text \"" + string + "\"";
return typeSafeMatcher(TextFlow.class, descriptionText, node -> hasColoredText(node, string, false));
}

/**
* Allows one to verify both the content and color of the text that makes
* up a TextFlow. The color is matched in an exact way, as described fruther
* on.
* <p>
* Colors are specified using the following markup:
* <p>
* <pre><code><COLOR>text</COLOR></code></pre>
* <p>
* Where {@literal COLOR} is one of JavaFX's named colors.
* <p>
* Here is an example for verifying that a TextFlow contains the text
* "hello" and that the color of the text is *exactly* {@literal Colors.BLUE}
* (that is, it has an RGB value of (0, 0, 255)).
*
* <pre><code>
* Text text = new Text("hello");
* text.setFill(Colors.BLUE); // or: text.setFill(Colors.rgb(0, 0, 255));
* TextFlow textFlow = new TextFlow(text);
* assertThat(textFlow, TextFlowMatchers.hasExactlyColoredText("<BLUE>hello</BLUE>"));
* </code></pre>
*
* @param string the text contained in the TextFlow with color markup that
* specifies the expected color of the text
* @return a match if the text contained in the TextFlow has the same content
* and the exactly matching colors
* @see <a href="https://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html#typecolor">Named Colors</a>
*/
@Factory
@Unstable(reason = "is missing apidocs")
public static Matcher<Node> hasExactlyColoredText(String string) {
String descriptionText = "has exactly colored text \"" + string + "\"";
return typeSafeMatcher(TextFlow.class, descriptionText, node -> hasColoredText(node, string, true));
}

//---------------------------------------------------------------------------------------------
// PRIVATE STATIC METHODS.
//---------------------------------------------------------------------------------------------

private static boolean hasText(TextFlow textFlow, String string) {
StringBuilder textBuilder = new StringBuilder();

for (Node child : textFlow.getChildren()) {
if (Text.class.isAssignableFrom(child.getClass())) {
textBuilder.append(((Text) child).getText());
}
}
return Objects.equals(string, textBuilder.toString());
}

private static boolean hasColoredText(TextFlow textFlow, String string, boolean exact) {
StringBuilder textBuilder = new StringBuilder();

for (Node child : textFlow.getChildren()) {
if (Text.class.isAssignableFrom(child.getClass())) {
Text text = ((Text) child);
final String color;
if (exact) {
Optional<String> colorOptional = ColorUtils.getNamedColor(
text.getFill().toString().substring(2, 8));
if (colorOptional.isPresent()) {
color = colorOptional.get().toUpperCase(Locale.US);
} else {
return false;
}
} else {
color = ColorUtils.getClosestNamedColor(text.getFill().toString()
.substring(2, 8)).toUpperCase(Locale.US);
}

if (!color.equals("BLACK")) {
textBuilder.append("<").append(color).append(">");
}
textBuilder.append(text.getText());

if (!color.equals("BLACK")) {
textBuilder.append("</").append(color).append(">");
}
}
}
return Objects.equals(string, textBuilder.toString());
}

}
Expand Up @@ -21,6 +21,7 @@
import javafx.scene.paint.Color;

import org.testfx.service.support.PixelMatcher;
import org.testfx.util.ColorUtils;

public class PixelMatcherRgb extends PixelMatcherBase implements PixelMatcher {

Expand Down Expand Up @@ -56,11 +57,11 @@ public PixelMatcherRgb(double minColorDistFactor,
@Override
public boolean matchColors(Color color0, Color color1) {
if (minColorDistSq == Double.MIN_VALUE) {
double maxColorDistSq = calculateColorDistSq(Color.BLACK, Color.WHITE);
double maxColorDistSq = ColorUtils.calculateColorDistSq(Color.BLACK, Color.WHITE);
minColorDistSq = maxColorDistSq * (minColorDistFactor * minColorDistFactor);
}

double colorDistSq = calculateColorDistSq(color0, color1);
double colorDistSq = ColorUtils.calculateColorDistSq(color0, color1);
return colorDistSq < minColorDistSq;
}

Expand All @@ -87,14 +88,6 @@ public Color createNonMatchColor(Color color0, Color color1) {
// PRIVATE METHODS.
//---------------------------------------------------------------------------------------------

private double calculateColorDistSq(Color color0,
Color color1) {
double diffRed = color0.getRed() - color1.getRed();
double diffGreen = color0.getGreen() - color1.getGreen();
double diffBlue = color0.getBlue() - color1.getBlue();
return (diffRed * diffRed) + (diffGreen * diffGreen) + (diffBlue * diffBlue);
}

private double blendToWhite(double gray,
double factor) {
return ((1.0 - factor) * gray) + factor;
Expand Down

0 comments on commit c642ea6

Please sign in to comment.