com.facebook.litho.testing.viewtree.ViewPredicates.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.litho.testing.viewtree.ViewPredicates.java

Source

/*
 * Copyright (c) 2017-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

package com.facebook.litho.testing.viewtree;

import javax.annotation.Nullable;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.regex.Pattern;

import android.annotation.TargetApi;
import android.graphics.Canvas;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import com.facebook.litho.ComponentHost;

import com.google.common.base.Predicate;
import com.google.common.base.Predicates;
import org.robolectric.Shadows;
import org.robolectric.shadows.ShadowCanvas;

/**
 * A collection of useful predicates over Android views for tests
 */
final class ViewPredicates {

    private ViewPredicates() {
    }

    /**
     * Returns a predicate that returns true if the applied on view's text is equal to the given
     * text.
     * substring.
     * @param predicate the predicate with which to test the text
     * @return the predicate
     */
    public static Predicate<View> hasTextMatchingPredicate(final Predicate<String> predicate) {
        return new Predicate<View>() {
            @Override
            public boolean apply(final View input) {
                if (predicate.apply(extractString(input))) {
                    return true;
                }

                if (input instanceof ComponentHost) {
                    return ComponentQueries.hasTextMatchingPredicate((ComponentHost) input, predicate);
                }

                return false;
            }
        };
    }

    /**
     * Returns a predicate that returns true if the applied on view's text is equal to the given
     * text.
     * substring.
     * @param text the text to check
     * @return the predicate
     */
    public static Predicate<View> hasText(final String text) {
        return hasTextMatchingPredicate(Predicates.equalTo(text));
    }

    public static Predicate<View> hasTag(final int tagId, final Object tagValue) {
        return new Predicate<View>() {
            @Override
            public boolean apply(final View input) {
                final Object tag = input.getTag(tagId);
                return tag != null && tag.equals(tagValue);
            }
        };
    }

    public static Predicate<View> hasContentDescription(final String contentDescription) {
        return new Predicate<View>() {
            @Override
            public boolean apply(final View input) {
                if (input instanceof ComponentHost) {
                    final List<CharSequence> contentDescriptions = ((ComponentHost) input).getContentDescriptions();
                    return contentDescriptions.contains(contentDescription);
                }

                return contentDescription.equals(input.getContentDescription());
            }
        };
    }

    public static Predicate<View> hasVisibleText(final String text) {
        return Predicates.and(isVisible(), hasText(text));
    }

    public static Predicate<View> hasVisibleTextWithTag(final String text, final int tagId, final Object tagValue) {
        return Predicates.and(hasVisibleText(text), hasTag(tagId, tagValue));
    }

    public static Predicate<View> matchesText(final String text) {
        final Pattern pattern = Pattern.compile(text);

        return new Predicate<View>() {
            @Override
            public boolean apply(final View input) {
                if (pattern.matcher(extractString(input)).find()) {
                    return true;
                }

                if (input instanceof ComponentHost) {
                    return ComponentQueries.matchesPattern((ComponentHost) input, pattern);
                }

                return false;
            }
        };
    }

    public static Predicate<View> hasVisibleMatchingText(final String text) {
        return Predicates.and(isVisible(), matchesText(text));
    }

    public static Predicate<View> isVisible() {
        return new Predicate<View>() {
            @Override
            public boolean apply(final View input) {
                return input.getVisibility() == View.VISIBLE;
            }
        };
    }

    @SuppressWarnings("unchecked")
    public static Predicate<View> isClass(final Class<? extends View> clazz) {
        return (Predicate<View>) (Predicate<?>) Predicates.instanceOf(clazz);
    }

    /**
     * Tries to extract the description of a drawn drawable from a canvas
     */
    static String getDrawnDrawableDescription(final Drawable drawable) {
        final Canvas canvas = new Canvas();
        drawable.draw(canvas);
        final ShadowCanvas shadowCanvas = Shadows.shadowOf(canvas);
        return shadowCanvas.getDescription();
    }

    private static String extractString(final View view) {
        if (!(view instanceof TextView)) {
            return "";
        }

        final CharSequence text = ((TextView) view).getText();
        return text != null ? text.toString() : "";
    }

    public static Predicate<View> hasDrawable(final Drawable drawable) {
        return new Predicate<View>() {
            @Override
            public boolean apply(@Nullable final View input) {
                if (input instanceof ImageView) {
                    final Drawable imageDrawable = ((ImageView) input).getDrawable();

                    if (drawable instanceof BitmapDrawable && !(imageDrawable instanceof BitmapDrawable)) {
                        return false;
                    }
                }

                if (input instanceof ComponentHost) {
                    return ComponentQueries.hasDrawable((ComponentHost) input, drawable);
                }

                final String drawnDrawableDescription = getDrawnDrawableDescription(drawable);
                return !drawnDrawableDescription.isEmpty()
                        && getDrawnViewDescription(input).contains(drawnDrawableDescription);
            }
        };
    }

    public static Predicate<View> hasVisibleDrawable(final Drawable drawable) {
        return Predicates.and(isVisible(), hasDrawable(drawable));
    }

    /** @return A Predicate which is true if the view is visible and has the given id. */
    public static Predicate<View> hasVisibleId(final int viewId) {
        return Predicates.and(isVisible(), hasId(viewId));
    }

    /**
     * Tries to extract the description of a drawn view from a canvas
     *
     * Since Robolectric can screw up {@link View#draw}, this uses reflection to call
     * {@link View#onDraw} and give you a canvas that has all the information drawn into it.
     * This is useful for asserting some view draws something specific to a canvas.
     *
     * @param view the view to draw
     */
    @TargetApi(Build.VERSION_CODES.KITKAT)
    private static String getDrawnViewDescription(View view) {
        final Canvas canvas = new Canvas();
        view.draw(canvas);
        final ShadowCanvas shadowCanvas = Shadows.shadowOf(canvas);
        if (!shadowCanvas.getDescription().isEmpty()) {
            return shadowCanvas.getDescription();
        }

        try {
            final Method onDraw = view.getClass().getDeclaredMethod("onDraw", Canvas.class);
            onDraw.setAccessible(true);
            onDraw.invoke(view, canvas);
            final ShadowCanvas shadowCanvas2 = Shadows.shadowOf(canvas);
            return shadowCanvas2.getDescription();
        } catch (NoSuchMethodException | IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(e);
        }
    }

    public static Predicate<View> hasId(final int id) {
        return new Predicate<View>() {
            @Override
            public boolean apply(final View input) {
                return input.getId() == id;
            }
        };
    }
}