chromium/chrome/android/javatests/src/org/chromium/chrome/browser/contextmenu/ContextMenuHeaderViewTest.java

// Copyright 2019 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.contextmenu;

import static org.hamcrest.CoreMatchers.equalTo;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;

import android.graphics.Bitmap;
import android.graphics.drawable.BitmapDrawable;
import android.text.TextUtils;
import android.view.View;
import android.widget.ImageView;
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.chrome.R;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.ui.modelutil.PropertyModel;
import org.chromium.ui.modelutil.PropertyModelChangeProcessor;
import org.chromium.ui.test.util.BlankUiTestActivityTestCase;

/** Tests for ContextMenuHeader view and {@link ContextMenuHeaderViewBinder} */
@RunWith(ChromeJUnit4ClassRunner.class)
@Batch(Batch.UNIT_TESTS)
public class ContextMenuHeaderViewTest extends BlankUiTestActivityTestCase {
    private static final String TITLE_STRING = "Some Very Cool Title";
    private static final String URL_STRING = "www.website.com";

    private View mHeaderView;
    private TextView mTitle;
    private TextView mUrl;
    private View mTitleAndUrl;
    private ImageView mImage;
    private View mCircleBg;
    private View mImageContainer;
    private PropertyModel mModel;
    private PropertyModelChangeProcessor mMCP;

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

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    getActivity().setContentView(R.layout.context_menu_header);
                    mHeaderView = getActivity().findViewById(android.R.id.content);
                    mTitle = mHeaderView.findViewById(R.id.menu_header_title);
                    mUrl = mHeaderView.findViewById(R.id.menu_header_url);
                    mTitleAndUrl = mHeaderView.findViewById(R.id.title_and_url);
                    mImage = mHeaderView.findViewById(R.id.menu_header_image);
                    mCircleBg = mHeaderView.findViewById(R.id.circle_background);
                    mImageContainer = mHeaderView.findViewById(R.id.menu_header_image_container);
                    mModel =
                            new PropertyModel.Builder(ContextMenuHeaderProperties.ALL_KEYS)
                                    .with(ContextMenuHeaderProperties.TITLE, "")
                                    .with(ContextMenuHeaderProperties.URL, "")
                                    .with(
                                            ContextMenuHeaderProperties
                                                    .TITLE_AND_URL_CLICK_LISTENER,
                                            null)
                                    .with(ContextMenuHeaderProperties.IMAGE, null)
                                    .with(ContextMenuHeaderProperties.CIRCLE_BG_VISIBLE, false)
                                    .build();

                    mMCP =
                            PropertyModelChangeProcessor.create(
                                    mModel, mHeaderView, ContextMenuHeaderViewBinder::bind);
                });
    }

    @Override
    public void tearDownTest() throws Exception {
        ThreadUtils.runOnUiThreadBlocking(mMCP::destroy);
        super.tearDownTest();
    }

    @Test
    @SmallTest
    public void testTitle() {
        assertThat(
                "Incorrect initial title visibility.", mTitle.getVisibility(), equalTo(View.GONE));

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mModel.set(ContextMenuHeaderProperties.TITLE, TITLE_STRING);
                    mModel.set(ContextMenuHeaderProperties.TITLE_MAX_LINES, 2);
                });

        assertThat("Incorrect title visibility.", mTitle.getVisibility(), equalTo(View.VISIBLE));
        assertThat("Incorrect title string.", mTitle.getText(), equalTo(TITLE_STRING));
        assertThat("Incorrect max line count for title.", mTitle.getMaxLines(), equalTo(2));
        assertThat(
                "Incorrect title ellipsize mode.",
                mTitle.getEllipsize(),
                equalTo(TextUtils.TruncateAt.END));
    }

    @Test
    @SmallTest
    public void testUrl() {
        assertThat("Incorrect initial URL visibility.", mUrl.getVisibility(), equalTo(View.GONE));

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mModel.set(ContextMenuHeaderProperties.URL, URL_STRING);
                    mModel.set(ContextMenuHeaderProperties.URL_MAX_LINES, 1);
                });

        assertThat("Incorrect URL visibility.", mUrl.getVisibility(), equalTo(View.VISIBLE));
        assertThat("Incorrect URL string.", mUrl.getText(), equalTo(URL_STRING));
        assertThat("Incorrect max line count for URL.", mUrl.getMaxLines(), equalTo(1));
        assertThat(
                "Incorrect URL ellipsize mode.",
                mUrl.getEllipsize(),
                equalTo(TextUtils.TruncateAt.END));

        ThreadUtils.runOnUiThreadBlocking(
                () -> mModel.set(ContextMenuHeaderProperties.URL_MAX_LINES, Integer.MAX_VALUE));

        assertThat(
                "Incorrect max line count for URL.",
                mUrl.getMaxLines(),
                equalTo(Integer.MAX_VALUE));
        assertNull("URL is ellipsized when it shouldn't be.", mUrl.getEllipsize());
    }

    @Test
    @SmallTest
    public void testTitleAndUrlClick() {
        // Clicking on the title or the URL expands/shrinks both of them.
        assertFalse(
                "Title and URL have onClickListeners when it shouldn't, yet, have.",
                mTitleAndUrl.hasOnClickListeners());

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mModel.set(ContextMenuHeaderProperties.TITLE, TITLE_STRING);
                    mModel.set(ContextMenuHeaderProperties.TITLE_MAX_LINES, 1);
                    mModel.set(ContextMenuHeaderProperties.URL, URL_STRING);
                    mModel.set(ContextMenuHeaderProperties.URL_MAX_LINES, 1);
                    mModel.set(
                            ContextMenuHeaderProperties.TITLE_AND_URL_CLICK_LISTENER,
                            (v) -> {
                                if (mModel.get(ContextMenuHeaderProperties.URL_MAX_LINES)
                                        == Integer.MAX_VALUE) {
                                    mModel.set(ContextMenuHeaderProperties.TITLE_MAX_LINES, 1);
                                    mModel.set(ContextMenuHeaderProperties.URL_MAX_LINES, 1);
                                } else {
                                    mModel.set(
                                            ContextMenuHeaderProperties.TITLE_MAX_LINES,
                                            Integer.MAX_VALUE);
                                    mModel.set(
                                            ContextMenuHeaderProperties.URL_MAX_LINES,
                                            Integer.MAX_VALUE);
                                }
                            });
                    mTitleAndUrl.callOnClick();
                });

        assertThat(
                "Incorrect max line count for title.",
                mTitle.getMaxLines(),
                equalTo(Integer.MAX_VALUE));
        assertNull("Title is ellipsized when it shouldn't be.", mTitle.getEllipsize());
        assertThat(
                "Incorrect max line count for URL.",
                mUrl.getMaxLines(),
                equalTo(Integer.MAX_VALUE));
        assertNull("URL is ellipsized when it shouldn't be.", mUrl.getEllipsize());

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mTitleAndUrl.callOnClick();
                });

        assertThat("Incorrect max line count for title.", mTitle.getMaxLines(), equalTo(1));
        assertThat(
                "Incorrect title ellipsize mode.",
                mTitle.getEllipsize(),
                equalTo(TextUtils.TruncateAt.END));
        assertThat("Incorrect max line count for URL.", mUrl.getMaxLines(), equalTo(1));
        assertThat(
                "Incorrect URL ellipsize mode.",
                mUrl.getEllipsize(),
                equalTo(TextUtils.TruncateAt.END));
    }

    @Test
    @SmallTest
    public void testImage() {
        assertThat(
                "Incorrect initial circle background visibility.",
                mCircleBg.getVisibility(),
                equalTo(View.INVISIBLE));
        ThreadUtils.runOnUiThreadBlocking(
                () -> mModel.set(ContextMenuHeaderProperties.CIRCLE_BG_VISIBLE, true));
        assertThat(
                "Incorrect circle background visibility.",
                mCircleBg.getVisibility(),
                equalTo(View.VISIBLE));

        assertFalse(
                "Thumbnail drawable should use fallback color initially.",
                mImage.getDrawable() instanceof BitmapDrawable);
        final Bitmap bitmap = Bitmap.createBitmap(4, 4, Bitmap.Config.ARGB_8888);
        ThreadUtils.runOnUiThreadBlocking(
                () -> mModel.set(ContextMenuHeaderProperties.IMAGE, bitmap));
        assertThat(
                "Incorrect thumbnail bitmap.",
                ((BitmapDrawable) mImage.getDrawable()).getBitmap(),
                equalTo(bitmap));
    }
}