Package org.loadui.testfx.service.finder.impl

Source Code of org.loadui.testfx.service.finder.impl.NodeFinderImpl

/*
* Copyright 2013-2014 SmartBear Software
*
* 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.loadui.testfx.service.finder.impl;

import java.util.List;
import java.util.Set;
import javafx.geometry.Bounds;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Labeled;
import javafx.scene.control.TextInputControl;
import javafx.stage.Window;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.hamcrest.Matcher;
import org.loadui.testfx.exceptions.NoNodesFoundException;
import org.loadui.testfx.exceptions.NoNodesVisibleException;
import org.loadui.testfx.service.finder.NodeFinder;
import org.loadui.testfx.service.finder.WindowFinder;

public class NodeFinderImpl implements NodeFinder {

    //---------------------------------------------------------------------------------------------
    // CONSTANTS.
    //---------------------------------------------------------------------------------------------

    static final String CSS_ID_SELECTOR_SYMBOL = "#";
    static final String CSS_CLASS_SELECTOR_SYMBOL = ".";

    static final String ERROR_NO_NODES_FOUND = "No matching nodes were found.";
    static final String ERROR_NO_VISIBLE_NODES_FOUND =
        "Matching nodes were found, but none of them are visible.";

    //---------------------------------------------------------------------------------------------
    // PRIVATE FIELDS.
    //---------------------------------------------------------------------------------------------

    private WindowFinder windowFinder;

    //---------------------------------------------------------------------------------------------
    // CONSTRUCTORS.
    //---------------------------------------------------------------------------------------------

    public NodeFinderImpl(WindowFinder windowFinder) {
        this.windowFinder = windowFinder;
    }

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

    public Node node(String query) {
        Set<Node> resultNodes = nodes(query);
        return Iterables.getFirst(resultNodes, null);
    }

    public Set<Node> nodes(String query) {
        Function<Node, Set<Node>> toResultNodesFunction;
        if (isCssSelector(query)) {
            toResultNodesFunction = fromNodesCssSelectorFunction(query);
        }
        else {
            Predicate<Node> nodeLabelPredicate = hasNodeLabelPredicate(query);
            toResultNodesFunction = fromNodesPredicateFunction(nodeLabelPredicate);
        }
        List<Window> orderedWindows = windowFinder.listOrderedWindows();
        return nodesImpl(orderedWindows, toResultNodesFunction);
    }

    public Node node(Predicate<Node> predicate) {
        Set<Node> resultNodes = nodes(predicate);
        return Iterables.getFirst(resultNodes, null);
    }

    public Set<Node> nodes(Predicate<Node> predicate) {
        List<Window> windows = windowFinder.listWindows();
        Function<Node, Set<Node>> toResultNodesFunction = fromNodesPredicateFunction(predicate);
        return nodesImpl(windows, toResultNodesFunction);
    }

    public Node node(Matcher<Object> matcher) {
        Set<Node> resultNodes = nodes(matcher);
        return Iterables.getFirst(resultNodes, null);
    }

    public Set<Node> nodes(Matcher<Object> matcher) {
        Predicate<Node> nodeMatcherPredicate = toNodeMatcherPredicate(matcher);
        return nodes(nodeMatcherPredicate);
    }

    public Node parent(Window window) {
        return window.getScene().getRoot();
    }

    public Node parent(int windowIndex) {
        return parent(windowFinder.window(windowIndex));
    }

    public Node parent(String stageTitleRegex) {
        return parent(windowFinder.window(stageTitleRegex));
    }

    public Node parent(Scene scene) {
        return scene.getRoot();
    }

    public Node node(String query, Node parentNode) {
        Set<Node> resultNodes = nodes(query, parentNode);
        return Iterables.getFirst(resultNodes, null);
    }

    public Set<Node> nodes(String query, Node parentNode) {
        // TODO: Filter visible nodes and allow label queries.
        // TODO: Filter instances of javafx.scene.control.Skin.
        windowFinder.target(parentNode.getScene().getWindow());
        return findNodesInParent(query, parentNode);
    }

    //---------------------------------------------------------------------------------------------
    // PRIVATE TRANSFORM METHODS.
    //---------------------------------------------------------------------------------------------

    private Set<Node> nodesImpl(List<Window> windows,
            Function<Node, Set<Node>> toResultNodesFunction) {
        Set<Node> resultNodes = transformToResultNodes(windows, toResultNodesFunction);
        assertNodesFound(resultNodes, ERROR_NO_NODES_FOUND);

        Set<Node> visibleNodes = Sets.filter(resultNodes, isNodeVisiblePredicate());
        assertNodesVisible(visibleNodes, ERROR_NO_VISIBLE_NODES_FOUND);
        return visibleNodes;
    }

    private Set<Node> transformToResultNodes(List<Window> windows,
            Function<Node, Set<Node>> toResultNodesFunction) {
        //return FluentIterable.from(windows)
        //    .transform(toRootNodeFunction())
        //    .transformAndConcat(toResultNodesFunction)
        //    .filter(Predicates.notNull())
        //    .toSet();
        Iterable<Node> rootNodes = Iterables.transform(windows, toRootNodeFunction());
        Iterable<Set<Node>> resultNodeSets = Iterables.transform(rootNodes, toResultNodesFunction);
        Iterable<Node> resultNodes = Iterables.concat(resultNodeSets);
        return ImmutableSet.copyOf(Iterables.filter(resultNodes, Predicates.notNull()));
    }

    private Function<Window, Node> toRootNodeFunction() {
        return window -> {
            if (window != null && window.getScene() != null) {
                return window.getScene().getRoot();
            }
            return null;
        };
    }

    private Function<Node, Set<Node>> fromNodesCssSelectorFunction(final String selector) {
        return rootNode -> findNodesInParent(selector, rootNode);
    }

    private Function<Node, Set<Node>> fromNodesPredicateFunction(final Predicate<Node> predicate) {
        return rootNode -> findNodesInParent(predicate, rootNode);
    }

    //---------------------------------------------------------------------------------------------
    // PRIVATE BACKEND METHODS.
    //---------------------------------------------------------------------------------------------

    private Set<Node> findNodesInParent(String selector, Node parentNode) {
        return parentNode.lookupAll(selector);
    }

    private Set<Node> findNodesInParent(Predicate<Node> predicate, Node parentNode) {
        Set<Node> resultNodes = Sets.newLinkedHashSet();
        if (applyPredicateOnNode(predicate, parentNode)) {
            resultNodes.add(parentNode);
        }
        if (parentNode instanceof Parent) {
            List<Node> childNodes = ((Parent) parentNode).getChildrenUnmodifiable();
            for (Node childNode : childNodes) {
                resultNodes.addAll(findNodesInParent(predicate, childNode));
            }
        }
        return ImmutableSet.copyOf(resultNodes);
    }

    //---------------------------------------------------------------------------------------------
    // PRIVATE HELPER METHODS.
    //---------------------------------------------------------------------------------------------

    private boolean applyPredicateOnNode(Predicate<Node> predicate, Node node) {
        // TODO: Test cases with ClassCastException.
        try {
            return predicate.apply(node);
        }
        catch (ClassCastException exception) {
            return false;
        }
    }

    private Predicate<Node> toNodeMatcherPredicate(final Matcher<Object> matcher) {
        return matcher::matches;
    }

    private Predicate<Node> isNodeVisiblePredicate() {
        return NodeFinderImpl.this::isNodeVisible;
    }

    private Predicate<Node> hasNodeLabelPredicate(final String label) {
        return node -> hasNodeLabel(node, label);
    }

    private boolean isCssSelector(String query) {
        return query.startsWith(CSS_ID_SELECTOR_SYMBOL) ||
            query.startsWith(CSS_CLASS_SELECTOR_SYMBOL);
    }

    private boolean hasNodeLabel(Node node, String label) {
        // TODO: Test cases with node.getText() == null.
        if (node instanceof Labeled) {
            return label.equals(((Labeled) node).getText());
        }
        else if (node instanceof TextInputControl) {
            return label.equals(((TextInputControl) node).getText());
        }
        return false;
    }

    @SuppressWarnings("deprecation")
    private boolean isNodeVisible(Node node) {
        if (!node.isVisible() || !node.impl_isTreeVisible()) {
            return false;
        }
        return isNodeWithinSceneBounds(node);
    }

    private boolean isNodeWithinSceneBounds(Node node) {
        Scene scene = node.getScene();
        Bounds nodeBounds = node.localToScene(node.getBoundsInLocal());
        return nodeBounds.intersects(0, 0, scene.getWidth(), scene.getHeight());
    }

    private void assertNodesFound(Set<Node> resultNodes, String errorMessage) {
        // TODO: Save screenshot on exception.
        if (resultNodes.isEmpty()) {
            throw new NoNodesFoundException(errorMessage);
        }
    }

    private void assertNodesVisible(Set<Node> resultNodes, String errorMessage) {
        // TODO: Save screenshot on exception.
        if (resultNodes.isEmpty()) {
            throw new NoNodesVisibleException(errorMessage);
        }
    }

}
TOP

Related Classes of org.loadui.testfx.service.finder.impl.NodeFinderImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.