chromium/chrome/android/javatests/src/org/chromium/chrome/browser/bookmarks/BookmarkOpenerTest.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.bookmarks;

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import androidx.recyclerview.widget.RecyclerView;
import androidx.test.filters.MediumTest;
import androidx.test.platform.app.InstrumentationRegistry;

import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;

import org.chromium.base.ThreadUtils;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.test.util.ApplicationTestUtils;
import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.DoNotBatch;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.UserActionTester;
import org.chromium.chrome.browser.app.bookmarks.BookmarkActivity;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.chrome.browser.ui.signin.SyncPromoController.SyncPromoState;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.util.ActivityTestUtils;
import org.chromium.chrome.test.util.BookmarkTestUtil;
import org.chromium.chrome.test.util.MenuUtils;
import org.chromium.components.bookmarks.BookmarkId;
import org.chromium.components.browser_ui.widget.RecyclerViewTestUtils;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.sync.SyncFeatureMap;
import org.chromium.ui.accessibility.AccessibilityState;
import org.chromium.url.GURL;

import java.util.ArrayList;
import java.util.List;

/** Tests for the bookmark opener. */
@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@DoNotBatch(reason = "Tabs can't be closed reliably between tests.")
@DisableFeatures({
    SyncFeatureMap.SYNC_ENABLE_BOOKMARKS_IN_TRANSPORT_MODE,
    // TODO(crbug.com/344981899): ReplaceSyncPromosWithSigninPromos is disabled because bookmarks
    // account storage is disabled above, otherwise tests run into assertion failures. Long term,
    // these tests probably need to be fixed for the bookmarks account storage case rather than
    // force-disable the feature.
    ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS
})
public class BookmarkOpenerTest {
    @Rule
    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();

    private BookmarkOpener mBookmarkOpener;

    private BookmarkModel mBookmarkModel;
    private BookmarkActivity mBookmarkActivity;
    private BookmarkManagerCoordinator mBookmarkManagerCoordinator;
    private RecyclerView mItemsContainer;

    private TabModelSelector mTabModelSelector;
    private UserActionTester mActionTester;

    @Before
    public void setUp() {
        mActivityTestRule.startMainActivityOnBlankPage();
        mActionTester = new UserActionTester();
        mTabModelSelector = mActivityTestRule.getActivity().getTabModelSelectorSupplier().get();

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    mBookmarkModel = mActivityTestRule.getActivity().getBookmarkModelForTesting();
                });
    }

    @After
    public void tearDown() throws Exception {
        if (mBookmarkActivity != null) ApplicationTestUtils.finishActivity(mBookmarkActivity);
        if (mActionTester != null) mActionTester.tearDown();
    }

    private void openBookmarkManager() {
        BookmarkPromoHeader.forcePromoStateForTesting(SyncPromoState.NO_PROMO);

        if (mActivityTestRule.getActivity().isTablet()) {
            mActivityTestRule.loadUrl(UrlConstants.BOOKMARKS_URL);
            mItemsContainer =
                    mActivityTestRule
                            .getActivity()
                            .findViewById(R.id.selectable_list_recycler_view);
            mItemsContainer.setItemAnimator(null); // Disable animation to reduce flakiness.
            mBookmarkManagerCoordinator =
                    ((BookmarkPage)
                                    mActivityTestRule
                                            .getActivity()
                                            .getActivityTab()
                                            .getNativePage())
                            .getManagerForTesting();
        } else {
            // Phone
            mBookmarkActivity =
                    ActivityTestUtils.waitForActivity(
                            InstrumentationRegistry.getInstrumentation(),
                            BookmarkActivity.class,
                            new MenuUtils.MenuActivityTrigger(
                                    InstrumentationRegistry.getInstrumentation(),
                                    mActivityTestRule.getActivity(),
                                    R.id.all_bookmarks_menu_id));
            mItemsContainer = mBookmarkActivity.findViewById(R.id.selectable_list_recycler_view);
            mItemsContainer.setItemAnimator(null); // Disable animation to reduce flakiness.
            mBookmarkManagerCoordinator = mBookmarkActivity.getManagerForTesting();
        }

        ThreadUtils.runOnUiThreadBlocking(
                () -> {
                    AccessibilityState.setIsAnyAccessibilityServiceEnabledForTesting(false);
                    mBookmarkOpener = mBookmarkManagerCoordinator.getBookmarkOpenerForTesting();
                });
    }

    void openRootFolder() {
        ThreadUtils.runOnUiThreadBlocking(
                () -> getBookmarkDelegate().openFolder(mBookmarkModel.getRootFolderId()));
        RecyclerViewTestUtils.waitForStableRecyclerView(mItemsContainer);
    }

    void openMobileBookmarks() {
        openRootFolder();

        // Mobile bookmarks is merged into all bookmarks when improved bookmark is enabled.
        onView(withText("Mobile bookmarks")).perform(click());
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }

    void openReadingList() {
        openRootFolder();

        onView(withText("Reading list")).perform(click());
        InstrumentationRegistry.getInstrumentation().waitForIdleSync();
    }

    private BookmarkDelegate getBookmarkDelegate() {
        return mBookmarkManagerCoordinator.getBookmarkDelegateForTesting();
    }

    @Test
    @MediumTest
    public void testOpenBookmarkInCurrentTab() {
        GURL url = new GURL(UrlConstants.ABOUT_URL);
        BookmarkId id = addMobileBookmark("test", url);
        openBookmarkManager();
        openMobileBookmarks();

        ThreadUtils.runOnUiThreadBlocking(
                () -> mBookmarkOpener.openBookmarkInCurrentTab(id, /* incognito= */ false));
        CriteriaHelper.pollUiThread(
                () -> mActivityTestRule.getActivity().getActivityTab().getUrl().equals(url));
        Assert.assertEquals(1, mActivityTestRule.tabsCount(/* incognito= */ false));

        Assert.assertTrue(mActionTester.getActions().contains("MobileBookmarkManagerEntryOpened"));
        Assert.assertEquals(
                1, RecordHistogram.getHistogramTotalCountForTesting("Bookmarks.OpenBookmarkType"));
        Assert.assertEquals(
                1,
                RecordHistogram.getHistogramTotalCountForTesting(
                        "Bookmarks.OpenBookmarkTimeInterval2.Normal"));
    }

    @Test
    @MediumTest
    public void testOpenBookmarkInCurrentTab_ReadingList() {
        GURL url = new GURL("https://google.com"); // Chrome URLs not allowed for reading list
        BookmarkId id = addReadingListBookmark("test", url);
        openBookmarkManager();
        openReadingList();

        ThreadUtils.runOnUiThreadBlocking(
                () -> mBookmarkOpener.openBookmarkInCurrentTab(id, /* incognito= */ false));
        CriteriaHelper.pollUiThread(
                () -> mActivityTestRule.getActivity().getActivityTab().getUrl().equals(url));
        Assert.assertEquals(
                "Reading List will always open in a new tab",
                2,
                mActivityTestRule.tabsCount(/* incognito= */ false));

        Assert.assertTrue(mActionTester.getActions().contains("MobileBookmarkManagerEntryOpened"));
        Assert.assertEquals(
                1, RecordHistogram.getHistogramTotalCountForTesting("Bookmarks.OpenBookmarkType"));
        Assert.assertEquals(
                1,
                RecordHistogram.getHistogramTotalCountForTesting(
                        "Bookmarks.OpenBookmarkTimeInterval2.ReadingList"));
    }

    @Test
    @MediumTest
    public void testOpenBookmarkInCurrentTab_Incognito() {
        GURL url = new GURL(UrlConstants.ABOUT_URL);
        BookmarkId id = addMobileBookmark("test", url);

        mActivityTestRule.loadUrlInNewTab(UrlConstants.NTP_NON_NATIVE_URL, /* incognito= */ true);

        openBookmarkManager();
        openMobileBookmarks();

        ThreadUtils.runOnUiThreadBlocking(
                () -> mBookmarkOpener.openBookmarkInCurrentTab(id, /* incognito= */ true));
        CriteriaHelper.pollUiThread(
                () -> mActivityTestRule.getActivity().getActivityTab().getUrl().equals(url));
        Assert.assertEquals(1, mActivityTestRule.tabsCount(/* incognito= */ true));
    }

    @Test
    @MediumTest
    public void testOpenBookmarksInNewTabs() {
        GURL url = new GURL(UrlConstants.ABOUT_URL);

        List<BookmarkId> ids = new ArrayList<>();
        ids.add(addMobileBookmark("test", url));
        ids.add(addMobileBookmark("test1", new GURL(UrlConstants.NTP_NON_NATIVE_URL)));
        ids.add(addMobileBookmark("test2", new GURL(UrlConstants.NTP_NON_NATIVE_URL)));
        openBookmarkManager();
        openMobileBookmarks();

        ThreadUtils.runOnUiThreadBlocking(
                () -> mBookmarkOpener.openBookmarksInNewTabs(ids, /* incognito= */ false));
        CriteriaHelper.pollUiThread(
                () -> mActivityTestRule.getActivity().getActivityTab().getUrl().equals(url));
        Assert.assertEquals(4, mActivityTestRule.tabsCount(/* incognito= */ false));

        Assert.assertTrue(
                mActionTester.getActions().contains("MobileBookmarkManagerMultipleEntriesOpened"));
        Assert.assertEquals(
                3,
                RecordHistogram.getHistogramTotalCountForTesting(
                        "Bookmarks.MultipleOpened.OpenBookmarkType"));
        Assert.assertEquals(
                3,
                RecordHistogram.getHistogramTotalCountForTesting(
                        "Bookmarks.MultipleOpened.OpenBookmarkTimeInterval2.Normal"));
    }

    @Test
    @MediumTest
    public void testOpenBookmarksInNewTabs_Incognito() {
        GURL url = new GURL(UrlConstants.ABOUT_URL);

        List<BookmarkId> ids = new ArrayList<>();
        ids.add(addMobileBookmark("test", url));
        ids.add(addMobileBookmark("test1", new GURL(UrlConstants.NTP_NON_NATIVE_URL)));
        ids.add(addMobileBookmark("test2", new GURL(UrlConstants.NTP_NON_NATIVE_URL)));

        mActivityTestRule.loadUrlInNewTab(UrlConstants.NTP_NON_NATIVE_URL, /* incognito= */ true);

        openBookmarkManager();
        openMobileBookmarks();

        ThreadUtils.runOnUiThreadBlocking(
                () -> mBookmarkOpener.openBookmarksInNewTabs(ids, /* incognito= */ true));
        CriteriaHelper.pollUiThread(
                () -> mActivityTestRule.getActivity().getActivityTab().getUrl().equals(url));
        Assert.assertEquals(4, mActivityTestRule.tabsCount(/* incognito= */ true));
    }

    private BookmarkId addMobileBookmark(final String title, GURL url) {
        BookmarkTestUtil.readPartnerBookmarks(mActivityTestRule);
        BookmarkTestUtil.waitForBookmarkModelLoaded();
        return ThreadUtils.runOnUiThreadBlocking(
                () ->
                        mBookmarkModel.addBookmark(
                                mBookmarkModel.getMobileFolderId(), 0, title, url));
    }

    private BookmarkId addReadingListBookmark(final String title, final GURL url) {
        BookmarkTestUtil.readPartnerBookmarks(mActivityTestRule);
        BookmarkTestUtil.waitForBookmarkModelLoaded();
        BookmarkId bookmarkId =
                ThreadUtils.runOnUiThreadBlocking(
                        () -> mBookmarkModel.addToDefaultReadingList(title, url));
        return bookmarkId;
    }
}