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

import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches;
import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText;

import static org.hamcrest.Matchers.allOf;
import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.matches;

import static org.chromium.base.ThreadUtils.runOnUiThreadBlocking;

import androidx.recyclerview.widget.RecyclerView;
import androidx.test.espresso.Espresso;
import androidx.test.filters.MediumTest;
import androidx.test.filters.SmallTest;

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

import org.chromium.base.test.util.CommandLineFlags;
import org.chromium.base.test.util.CriteriaHelper;
import org.chromium.base.test.util.DisabledTest;
import org.chromium.base.test.util.DoNotBatch;
import org.chromium.base.test.util.Features.DisableFeatures;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.Restriction;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.flags.ChromeSwitches;
import org.chromium.chrome.browser.profiles.ProfileManager;
import org.chromium.chrome.browser.sync.SyncTestRule;
import org.chromium.chrome.browser.ui.signin.SyncPromoController.SyncPromoState;
import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
import org.chromium.chrome.test.R;
import org.chromium.chrome.test.util.BookmarkTestRule;
import org.chromium.chrome.test.util.BookmarkTestUtil;
import org.chromium.components.browser_ui.widget.RecyclerViewTestUtils;
import org.chromium.components.sync.SyncFeatureMap;
import org.chromium.components.sync.UserSelectableType;
import org.chromium.ui.test.util.UiRestriction;
import org.chromium.url.GURL;

@RunWith(ChromeJUnit4ClassRunner.class)
@CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
@EnableFeatures({SyncFeatureMap.SYNC_ENABLE_BOOKMARKS_IN_TRANSPORT_MODE})
// TODO(crbug.com/40743432): Once SyncTestRule supports batching, investigate batching this suite.
@DoNotBatch(reason = "SyncTestRule doesn't support batching.")
public class AccountBookmarkTest {
    private static final String BOOKMARKS_TYPE_STRING = "Bookmarks";

    @Rule public SyncTestRule mSyncTestRule = new SyncTestRule();
    @Rule public BookmarkTestRule mBookmarkTestRule = new BookmarkTestRule();

    private BookmarkManagerCoordinator mBookmarkManagerCoordinator;
    private BookmarkModel mBookmarkModel;
    private RecyclerView mRecyclerView;

    @Before
    public void setUp() throws Exception {
        // Auto form factors are very small, disable the promo to leave room for bookmarks.
        BookmarkPromoHeader.forcePromoStateForTesting(SyncPromoState.NO_PROMO);
        mBookmarkModel =
                runOnUiThreadBlocking(
                        () ->
                                BookmarkModel.getForProfile(
                                        ProfileManager.getLastUsedRegularProfile()));
        mSyncTestRule.setUpAccountAndSignInForTesting();
        mBookmarkManagerCoordinator =
                mBookmarkTestRule.showBookmarkManager(mSyncTestRule.getActivity());
    }

    @Test
    @SmallTest
    @EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
    public void testReplaceSyncPromosWithSigninPromos() {
        CriteriaHelper.pollUiThread(() -> mBookmarkModel.getAccountMobileFolderId() != null);
        RecyclerViewTestUtils.waitForStableMvcRecyclerView(
                mBookmarkManagerCoordinator.getRecyclerViewForTesting());
        checkTopLevelAccountFoldersDisplayed();
    }

    @Test
    @SmallTest
    @DisableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
    public void testEnableDatatypesManually() {
        runOnUiThreadBlocking(
                () -> {
                    mSyncTestRule
                            .getSyncService()
                            .setSelectedType(UserSelectableType.BOOKMARKS, true);
                    mSyncTestRule
                            .getSyncService()
                            .setSelectedType(UserSelectableType.READING_LIST, true);
                });
        CriteriaHelper.pollUiThread(() -> mBookmarkModel.getAccountMobileFolderId() != null);
        RecyclerViewTestUtils.waitForStableMvcRecyclerView(
                mBookmarkManagerCoordinator.getRecyclerViewForTesting());
        checkTopLevelAccountFoldersDisplayed();
    }

    @Test
    @MediumTest
    @Restriction({UiRestriction.RESTRICTION_TYPE_PHONE})
    @EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
    @DisabledTest(
            message =
                    "Enable this test when reading list is available w/o restart crbug.com/1510547")
    public void testOpenFromReadingListAndNavigateBack() throws Exception {
        CriteriaHelper.pollUiThread(() -> mBookmarkModel.getAccountReadingListFolder() != null);
        RecyclerViewTestUtils.waitForStableMvcRecyclerView(
                mBookmarkManagerCoordinator.getRecyclerViewForTesting());

        runOnUiThreadBlocking(
                () ->
                        mBookmarkModel.addToReadingList(
                                mBookmarkModel.getAccountReadingListFolder(),
                                "test",
                                new GURL("https://test.com")));

        BookmarkTestUtil.getRecyclerRowViewInteraction(
                        "Reading list", /* isAccountBookmark= */ true)
                .perform(click());
        onView(withText("test")).perform(click());
        Espresso.pressBack();
        onView(withText("test")).check(matches(isDisplayed()));
    }

    @Test
    @SmallTest
    @EnableFeatures({ChromeFeatureList.REPLACE_SYNC_PROMOS_WITH_SIGN_IN_PROMOS})
    public void testDefaultFolders() {
        CriteriaHelper.pollUiThread(() -> mBookmarkModel.getAccountMobileFolderId() != null);
        runOnUiThreadBlocking(
                () -> {
                    assertEquals(
                            mBookmarkModel.getAccountMobileFolderId(),
                            mBookmarkModel.getDefaultBookmarkFolder());
                });
    }

    private void checkTopLevelAccountFoldersDisplayed() {
        // TODO(crbug.com/41483140): This is currently broken because the account reading list
        // folder doesn't show up without a restart. This should be updated once that folder is
        // available.
        checkToolbarTitleMatches("Bookmarks");
        if (!ChromeFeatureList.isEnabled(
                ChromeFeatureList.READING_LIST_ENABLE_SYNC_TRANSPORT_MODE_UPON_SIGNIN)) {
            onView(withText("In your Google Account")).check(matches(isDisplayed()));
        }
        BookmarkTestUtil.getRecyclerRowViewInteraction("Mobile bookmarks", true)
                .check(matches(isDisplayed()));
        BookmarkTestUtil.getRecyclerRowViewInteraction(
                        "Reading list",
                        ChromeFeatureList.isEnabled(
                                ChromeFeatureList
                                        .READING_LIST_ENABLE_SYNC_TRANSPORT_MODE_UPON_SIGNIN))
                .check(matches(isDisplayed()));
    }

    // Checks the toolbar title against the given string.
    private void checkToolbarTitleMatches(String text) {
        onView(allOf(isDescendantOfA(withId(R.id.action_bar)), withText(text)))
                .check(matches(isDisplayed()));
    }
}