chromium/chrome/android/javatests/src/org/chromium/chrome/browser/autofill/vcn/AutofillVcnEnrollBottomSheetViewBinderTest.java

// Copyright 2023 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.browser.autofill.vcn;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.isEmptyString;
import static org.junit.Assert.assertEquals;

import android.graphics.Bitmap;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import androidx.test.filters.SmallTest;

import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.test.util.Batch;
import org.chromium.base.test.util.CallbackHelper;
import org.chromium.chrome.browser.autofill.vcn.AutofillVcnEnrollBottomSheetProperties.Description;
import org.chromium.chrome.browser.autofill.vcn.AutofillVcnEnrollBottomSheetProperties.IssuerIcon;
import org.chromium.chrome.browser.autofill.vcn.AutofillVcnEnrollBottomSheetProperties.LegalMessages;
import org.chromium.chrome.browser.autofill.vcn.AutofillVcnEnrollBottomSheetProperties.LinkOpener;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.components.autofill.VirtualCardEnrollmentLinkType;
import org.chromium.components.autofill.payments.LegalMessageLine;
import org.chromium.components.autofill.payments.LegalMessageLine.Link;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModel.ReadableObjectPropertyKey;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.test.util.BlankUiTestActivityTestCase;
import org.chromium.ui.widget.LoadingView;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.concurrent.TimeoutException;

/** Tests for {@link AutofillVcnEnrollBottomSheetViewBinder}. */
@RunWith(ChromeJUnit4ClassRunner.class)
@Batch(Batch.PER_CLASS)
public final class AutofillVcnEnrollBottomSheetViewBinderTest extends BlankUiTestActivityTestCase
        implements LinkOpener {
    private PropertyModel.Builder mModelBuilder;
    private PropertyModel mModel;
    private AutofillVcnEnrollBottomSheetView mView;

    private static class LoadingViewObserver implements LoadingView.Observer {
        private final CallbackHelper mOnShowHelper = new CallbackHelper();

        private final CallbackHelper mOnHideHelper = new CallbackHelper();

        @Override
        public void onShowLoadingUIComplete() {
            mOnShowHelper.notifyCalled();
        }

        @Override
        public void onHideLoadingUIComplete() {
            mOnHideHelper.notifyCalled();
        }

        public CallbackHelper getOnShowLoadingUICompleteHelper() {
            return mOnShowHelper;
        }

        public CallbackHelper getOnHideLoadingUICompleteHelper() {
            return mOnHideHelper;
        }
    }

    @Override
    public void setUpTest() throws Exception {
        super.setUpTest();

        mModelBuilder = new PropertyModel.Builder(AutofillVcnEnrollBottomSheetProperties.ALL_KEYS);
        mView = new AutofillVcnEnrollBottomSheetView(getActivity());
        ThreadUtils.runOnUiThreadBlocking(() -> getActivity().setContentView(mView.mContentView));
        bind(mModelBuilder);
    }

    // Builds the model from the given builder and binds it to the view.
    private void bind(PropertyModel.Builder modelBuilder) {
        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mModel = modelBuilder.build();
                    PropertyModelChangeProcessor.create(
                            mModel, mView, AutofillVcnEnrollBottomSheetViewBinder::bind);
                });
    }

    // LinkOpener:
    @Override
    public void openLink(String url, @VirtualCardEnrollmentLinkType int linkType) {}

    @Test
    @SmallTest
    public void testMessageTextInDialogTitle() {
        assertThat(String.valueOf(mView.mDialogTitle.getText()), isEmptyString());

        bind(mModelBuilder.with(AutofillVcnEnrollBottomSheetProperties.MESSAGE_TEXT, null));
        assertThat(String.valueOf(mView.mDialogTitle.getText()), isEmptyString());

        bind(
                mModelBuilder.with(
                        AutofillVcnEnrollBottomSheetProperties.MESSAGE_TEXT, "Message text"));
        assertThat(String.valueOf(mView.mDialogTitle.getText()), equalTo("Message text"));
    }

    @Test
    @SmallTest
    public void testDescriptionText() {
        assertThat(String.valueOf(mView.mVirtualCardDescription.getText()), isEmptyString());

        bind(mModelBuilder.with(AutofillVcnEnrollBottomSheetProperties.DESCRIPTION, null));
        assertThat(String.valueOf(mView.mVirtualCardDescription.getText()), isEmptyString());

        bind(
                mModelBuilder.with(
                        AutofillVcnEnrollBottomSheetProperties.DESCRIPTION,
                        new Description(
                                null,
                                null,
                                null,
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK,
                                /* linkOpener= */ null)));
        assertThat(String.valueOf(mView.mVirtualCardDescription.getText()), isEmptyString());

        bind(
                mModelBuilder.with(
                        AutofillVcnEnrollBottomSheetProperties.DESCRIPTION,
                        new Description(
                                "",
                                "",
                                "",
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK,
                                /* linkOpener= */ this)));
        assertThat(String.valueOf(mView.mVirtualCardDescription.getText()), isEmptyString());

        bind(
                mModelBuilder.with(
                        AutofillVcnEnrollBottomSheetProperties.DESCRIPTION,
                        new Description(
                                "Description text",
                                "",
                                "",
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK,
                                /* linkOpener= */ this)));
        assertThat(String.valueOf(mView.mVirtualCardDescription.getText()), isEmptyString());

        bind(
                mModelBuilder.with(
                        AutofillVcnEnrollBottomSheetProperties.DESCRIPTION,
                        new Description(
                                "Description text",
                                "text",
                                "",
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK,
                                /* linkOpener= */ this)));
        assertThat(String.valueOf(mView.mVirtualCardDescription.getText()), isEmptyString());

        bind(
                mModelBuilder.with(
                        AutofillVcnEnrollBottomSheetProperties.DESCRIPTION,
                        new Description(
                                "Description text",
                                "text",
                                "https://example.test",
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_LEARN_MORE_LINK,
                                /* linkOpener= */ this)));
        assertThat(
                String.valueOf(mView.mVirtualCardDescription.getText()),
                equalTo("Description text"));
    }

    @Test
    @SmallTest
    public void testCardContainerAccessibilityDescription() {
        ReadableObjectPropertyKey<String> descriptionProperty =
                AutofillVcnEnrollBottomSheetProperties.CARD_CONTAINER_ACCESSIBILITY_DESCRIPTION;

        bind(mModelBuilder.with(descriptionProperty, ""));
        assertThat(String.valueOf(mView.mCardContainer.getContentDescription()), isEmptyString());

        bind(mModelBuilder.with(descriptionProperty, "Content description"));
        assertThat(
                String.valueOf(mView.mCardContainer.getContentDescription()),
                equalTo("Content description"));
    }

    @Test
    @SmallTest
    public void testIssuerIcon() {
        assertThat(mView.mIssuerIcon.getDrawable(), nullValue());

        bind(
                mModelBuilder.with(
                        AutofillVcnEnrollBottomSheetProperties.ISSUER_ICON,
                        new IssuerIcon(
                                createBitmap(/* dimensions= */ 10, /* color= */ 0xFFFF0000),
                                /* width= */ 5,
                                /* height= */ 5)));
        assertThat(mView.mIssuerIcon.getDrawable(), notNullValue());
    }

    private static Bitmap createBitmap(int dimensions, int color) {
        int[] colors = new int[dimensions * dimensions];
        Arrays.fill(colors, color);
        return Bitmap.createBitmap(colors, dimensions, dimensions, Bitmap.Config.ARGB_8888);
    }

    @Test
    @SmallTest
    public void testCardLabel() {
        runTextViewTest(mView.mCardLabel, AutofillVcnEnrollBottomSheetProperties.CARD_LABEL);
    }

    @Test
    @SmallTest
    public void testCardDescription() {
        runTextViewTest(
                mView.mCardDescription, AutofillVcnEnrollBottomSheetProperties.CARD_DESCRIPTION);
    }

    private void runTextViewTest(TextView view, ReadableObjectPropertyKey<String> property) {
        assertThat(String.valueOf(view.getText()), isEmptyString());

        bind(mModelBuilder.with(property, null));
        assertThat(String.valueOf(view.getText()), isEmptyString());

        bind(mModelBuilder.with(property, "Text view content"));
        assertThat(String.valueOf(view.getText()), equalTo("Text view content"));
    }

    @Test
    @SmallTest
    public void testGoogleLegalMessages() {
        runLegalMessageTest(
                mView.mGoogleLegalMessage,
                AutofillVcnEnrollBottomSheetProperties.GOOGLE_LEGAL_MESSAGES);
    }

    @Test
    @SmallTest
    public void testIssuerLegalMessages() {
        runLegalMessageTest(
                mView.mIssuerLegalMessage,
                AutofillVcnEnrollBottomSheetProperties.ISSUER_LEGAL_MESSAGES);
    }

    private void runLegalMessageTest(
            TextView view, ReadableObjectPropertyKey<LegalMessages> property) {
        assertThat(String.valueOf(view.getText()), isEmptyString());

        bind(mModelBuilder.with(property, null));
        assertThat(String.valueOf(view.getText()), isEmptyString());
        assertThat(view.getVisibility(), equalTo(View.GONE));

        bind(
                mModelBuilder.with(
                        property,
                        new LegalMessages(
                                null,
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_ISSUER_TOS_LINK,
                                /* linkOpener= */ null)));
        assertThat(String.valueOf(view.getText()), isEmptyString());
        assertThat(view.getVisibility(), equalTo(View.GONE));

        bind(
                mModelBuilder.with(
                        property,
                        new LegalMessages(
                                new LinkedList<LegalMessageLine>(),
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_ISSUER_TOS_LINK,
                                /* linkOpener= */ this)));
        assertThat(String.valueOf(view.getText()), isEmptyString());
        assertThat(view.getVisibility(), equalTo(View.GONE));

        LinkedList<LegalMessageLine> lines = new LinkedList<>();
        lines.add(new LegalMessageLine("Legal message line"));
        bind(
                mModelBuilder.with(
                        property,
                        new LegalMessages(
                                lines,
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_ISSUER_TOS_LINK,
                                /* linkOpener= */ this)));
        assertThat(String.valueOf(view.getText()), equalTo("Legal message line"));
        assertThat(view.getVisibility(), equalTo(View.VISIBLE));

        LegalMessageLine line = new LegalMessageLine("Legal message line");
        line.links.add(new Link(0, 5, "https://example.test"));
        lines = new LinkedList<>();
        lines.add(line);
        bind(
                mModelBuilder.with(
                        property,
                        new LegalMessages(
                                lines,
                                VirtualCardEnrollmentLinkType
                                        .VIRTUAL_CARD_ENROLLMENT_ISSUER_TOS_LINK,
                                /* linkOpener= */ this)));
        assertThat(String.valueOf(view.getText()), equalTo("Legal message line"));
        assertThat(view.getVisibility(), equalTo(View.VISIBLE));
    }

    @Test
    @SmallTest
    public void testAcceptButtonLabel() {
        runButtonLabelTest(
                mView.mAcceptButton, AutofillVcnEnrollBottomSheetProperties.ACCEPT_BUTTON_LABEL);
    }

    @Test
    @SmallTest
    public void testCancelButtonLabel() {
        runButtonLabelTest(
                mView.mCancelButton, AutofillVcnEnrollBottomSheetProperties.CANCEL_BUTTON_LABEL);
    }

    private void runButtonLabelTest(Button button, ReadableObjectPropertyKey<String> property) {
        assertThat(String.valueOf(button.getText()), isEmptyString());

        bind(mModelBuilder.with(property, null));
        assertThat(String.valueOf(button.getText()), isEmptyString());

        bind(mModelBuilder.with(property, "Button Action Text"));
        assertThat(String.valueOf(button.getText()), equalTo("Button Action Text"));
    }

    @Test
    @SmallTest
    public void testShowLoadingState() throws TimeoutException {
        LoadingViewObserver observer = new LoadingViewObserver();
        mView.mLoadingView.addObserver(observer);

        assertEquals(View.GONE, mView.mLoadingViewContainer.getVisibility());
        assertEquals(View.GONE, mView.mLoadingView.getVisibility());
        assertEquals(View.VISIBLE, mView.mAcceptButton.getVisibility());
        assertEquals(View.VISIBLE, mView.mCancelButton.getVisibility());

        int onShowLoadingUICompleteCount =
                observer.getOnShowLoadingUICompleteHelper().getCallCount();
        ThreadUtils.runOnUiThreadBlocking(
                () -> mModel.set(AutofillVcnEnrollBottomSheetProperties.SHOW_LOADING_STATE, true));
        observer.getOnShowLoadingUICompleteHelper().waitForCallback(onShowLoadingUICompleteCount);
        assertEquals(View.VISIBLE, mView.mLoadingViewContainer.getVisibility());
        assertEquals(View.VISIBLE, mView.mLoadingView.getVisibility());
        assertEquals(View.GONE, mView.mAcceptButton.getVisibility());
        assertEquals(View.GONE, mView.mCancelButton.getVisibility());

        int onHideLoadingUICompleteCount =
                observer.getOnHideLoadingUICompleteHelper().getCallCount();
        ThreadUtils.runOnUiThreadBlocking(
                () -> mModel.set(AutofillVcnEnrollBottomSheetProperties.SHOW_LOADING_STATE, false));
        observer.getOnHideLoadingUICompleteHelper().waitForCallback(onHideLoadingUICompleteCount);
        assertEquals(View.GONE, mView.mLoadingViewContainer.getVisibility());
        assertEquals(View.GONE, mView.mLoadingView.getVisibility());
        assertEquals(View.VISIBLE, mView.mAcceptButton.getVisibility());
        assertEquals(View.VISIBLE, mView.mCancelButton.getVisibility());
    }

    @Test
    @SmallTest
    public void testLoadingAccessibilityDescription() {
        ReadableObjectPropertyKey<String> loadingDescription =
                AutofillVcnEnrollBottomSheetProperties.LOADING_DESCRIPTION;

        bind(mModelBuilder.with(loadingDescription, ""));
        assertThat(
                String.valueOf(mView.mLoadingViewContainer.getContentDescription()),
                isEmptyString());

        bind(mModelBuilder.with(loadingDescription, "Loading description"));
        assertThat(
                String.valueOf(mView.mLoadingViewContainer.getContentDescription()),
                equalTo("Loading description"));
    }
}