chromium/chrome/test/android/javatests/src/org/chromium/chrome/test/transit/MessageFacility.java

// Copyright 2024 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

package org.chromium.chrome.test.transit;

import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import static org.chromium.base.test.transit.ViewSpec.viewSpec;

import android.os.Build;
import android.view.View;

import androidx.annotation.CallSuper;
import androidx.test.espresso.action.GeneralLocation;
import androidx.test.espresso.action.GeneralSwipeAction;
import androidx.test.espresso.action.Press;
import androidx.test.espresso.action.Swipe;
import androidx.test.espresso.action.ViewActions;

import org.hamcrest.CoreMatchers;
import org.hamcrest.Matcher;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.transit.Elements;
import org.chromium.base.test.transit.Facility;
import org.chromium.base.test.transit.Station;
import org.chromium.base.test.transit.Transition.Trigger;
import org.chromium.base.test.transit.ViewElement;
import org.chromium.base.test.transit.ViewSpec;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.transit.page.PageStation;
import org.chromium.components.messages.DismissReason;
import org.chromium.components.messages.MessageDispatcher;
import org.chromium.components.messages.MessageDispatcherProvider;
import org.chromium.components.messages.MessageIdentifier;
import org.chromium.components.messages.MessageStateHandler;
import org.chromium.components.messages.MessagesTestHelper;
import org.chromium.ui.modelutil.PropertyModel;

import java.util.List;

/**
 * Represents a message banner shown over a PageStation.
 *
 * <p>Subclass for specific messages types to specify expected title, button text and behavior.
 *
 * @param <HostStationT> the type of host {@link Station} this is scoped to.
 */
public class MessageFacility<HostStationT extends PageStation> extends Facility<HostStationT> {
    public static final Matcher<View> MESSAGE_TITLE_MATCHER = withId(R.id.message_title);
    public static final Matcher<View> MESSAGE_PRIMARY_BUTTON_MATCHER =
            withId(R.id.message_primary_button);

    public static final ViewSpec MESSAGE_BANNER = viewSpec(withId(R.id.message_banner));
    public static final ViewSpec MESSAGE_ICON = viewSpec(withId(R.id.message_icon));

    @CallSuper
    @Override
    public void declareElements(Elements.Builder elements) {
        // Unscoped because other messages can appear and fail the exit condition.
        elements.declareView(MESSAGE_BANNER, ViewElement.unscopedOption());
        elements.declareView(MESSAGE_ICON, ViewElement.unscopedOption());
    }

    /** Dismiss the message banner. */
    public void dismiss() {
        Trigger dismissTrigger;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            // b/329707221: For S+, GeneralSwipeAction is not working in the current version of
            // Espresso (3.2). Workaround by using the test helpers to dismiss the popup
            // programmatically.
            dismissTrigger =
                    () ->
                            ThreadUtils.runOnUiThreadBlocking(
                                    () -> {
                                        MessageDispatcher messageDispatcher =
                                                MessageDispatcherProvider.from(
                                                        mHostStation
                                                                .getActivity()
                                                                .getWindowAndroid());
                                        assert messageDispatcher != null;
                                        List<MessageStateHandler> messages =
                                                MessagesTestHelper.getEnqueuedMessages(
                                                        messageDispatcher,
                                                        MessageIdentifier.POPUP_BLOCKED);
                                        assert !messages.isEmpty();
                                        PropertyModel m =
                                                MessagesTestHelper.getCurrentMessage(
                                                        messages.get(0));
                                        messageDispatcher.dismissMessage(m, DismissReason.GESTURE);
                                    });
        } else {
            dismissTrigger =
                    () ->
                            MESSAGE_BANNER.perform(
                                    ViewActions.actionWithAssertions(
                                            new GeneralSwipeAction(
                                                    Swipe.FAST,
                                                    GeneralLocation.CENTER,
                                                    GeneralLocation.TOP_CENTER,
                                                    Press.FINGER)));
        }
        mHostStation.exitFacilitySync(this, dismissTrigger);
    }

    /** Create a {@link ViewSpec} expecting the message's |title|. */
    protected static ViewSpec titleViewSpec(String title) {
        Matcher<View> viewMatcher = CoreMatchers.allOf(MESSAGE_TITLE_MATCHER, withText(title));
        return viewSpec(viewMatcher);
    }

    /** Create a {@link ViewSpec} expecting the message's primary button with |text|. */
    protected static ViewSpec primaryButtonViewSpec(String text) {
        Matcher<View> viewMatcher =
                CoreMatchers.allOf(MESSAGE_PRIMARY_BUTTON_MATCHER, withText(text));
        return viewSpec(viewMatcher);
    }
}