chromium/chrome/browser/readaloud/android/java/src/org/chromium/chrome/browser/readaloud/TapToSeekSelectionManagerUnitTest.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.readaloud;

import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;

import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;

import org.chromium.base.supplier.ObservableSupplier;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.base.test.util.Features.EnableFeatures;
import org.chromium.base.test.util.JniMocker;
import org.chromium.chrome.browser.flags.ChromeFeatureList;
import org.chromium.chrome.browser.profiles.Profile;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
import org.chromium.content_public.browser.SelectAroundCaretResult;
import org.chromium.content_public.browser.SelectionClient;
import org.chromium.content_public.browser.SelectionPopupController;
import org.chromium.content_public.browser.WebContents;
import org.chromium.ui.touch_selection.SelectionEventType;
import org.chromium.url.GURL;
import org.chromium.url.JUnitTestGURLs;

/** Unit tests for {@link TapToSeekSelectionManager} */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@EnableFeatures({
    ChromeFeatureList.READALOUD,
    ChromeFeatureList.READALOUD_PLAYBACK,
    ChromeFeatureList.READALOUD_TAP_TO_SEEK
})
public class TapToSeekSelectionManagerUnitTest {
    @Rule
    public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();

    @Rule public JniMocker mJniMocker = new JniMocker();
    @Mock private ReadAloudController mReadAloudController;

    @Mock private Profile mProfile;
    private static final GURL sTestGURL = JUnitTestGURLs.EXAMPLE_URL;
    @Mock private ObservableSupplier<Tab> mMockTabProvider;
    @Mock private Tab mTab;
    @Mock private Tab mTab2;
    @Mock private WebContents mWebContents;
    @Mock private WebContents mWebContents2;
    @Mock private SelectionClient mSelectionClient;
    @Mock private SelectionPopupController mSelectionPopupController;
    @Mock private SelectionClient mSmartSelectionClient;
    @Mock private SelectionClient mSmartSelectionClient2;

    private TapToSeekSelectionManager mTapToSeekSelectionManager;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        doReturn(false).when(mProfile).isOffTheRecord();
        doReturn(mWebContents).when(mTab).getWebContents();
        doReturn(mWebContents2).when(mTab2).getWebContents();
        doReturn(mTab).when(mMockTabProvider).get();
        TapToSeekSelectionManager.setSmartSelectionClient(mSmartSelectionClient);
        TapToSeekSelectionManager.setSelectionPopupController(mSelectionPopupController);

        mTapToSeekSelectionManager =
                new TapToSeekSelectionManager(mReadAloudController, mMockTabProvider);
    }

    @Test
    public void testOnSurroundingTextReceived() {
        mTapToSeekSelectionManager.onSurroundingTextReceived("test", 12, 12);
        verify(mReadAloudController).tapToSeek("test", 12, 12);
    }

    @Test
    public void testOnSurroundingTextReceived_NotTap() {
        mTapToSeekSelectionManager.onSurroundingTextReceived("test", 12, 16);
        verify(mReadAloudController, never()).tapToSeek("test", 12, 16);
    }

    @Test
    public void testOnActivePlaybackTabUpdated() {
        mTapToSeekSelectionManager.onActivePlaybackTabUpdated(mTab);
        verify(mSelectionPopupController, times(1))
                .setSelectionClient(mTapToSeekSelectionManager.getSelectionClient());
        verify(mSmartSelectionClient, times(1))
                .addSurroundingTextReceivedListeners(mTapToSeekSelectionManager);
    }

    @Test
    public void testOnActivePlaybackTabUpdated_PreviousHooksRemoved() {
        mTapToSeekSelectionManager.onActivePlaybackTabUpdated(mTab);
        verify(mSelectionPopupController, times(1))
                .setSelectionClient(mTapToSeekSelectionManager.getSelectionClient());
        verify(mSmartSelectionClient, times(1))
                .addSurroundingTextReceivedListeners(mTapToSeekSelectionManager);

        // update playback tab to second Tab and check that hooks are released
        doReturn(mTapToSeekSelectionManager.getSelectionClient())
                .when(mSelectionPopupController)
                .getSelectionClient();
        TapToSeekSelectionManager.setSmartSelectionClient(mSmartSelectionClient2);
        mTapToSeekSelectionManager.onActivePlaybackTabUpdated(mTab2);
        verify(mSmartSelectionClient, times(1))
                .removeSurroundingTextReceivedListeners(mTapToSeekSelectionManager);
        verify(mSelectionPopupController, times(1)).setSelectionClient(null);
    }

    @Test
    public void testOnActivePlaybackTabUpdated_SameTab() {
        mTapToSeekSelectionManager.onActivePlaybackTabUpdated(mTab);
        verify(mSelectionPopupController, times(1))
                .setSelectionClient(mTapToSeekSelectionManager.getSelectionClient());
        verify(mSmartSelectionClient, times(1))
                .addSurroundingTextReceivedListeners(mTapToSeekSelectionManager);

        // update playback tab to same Tab and check that nothing happens
        mTapToSeekSelectionManager.onActivePlaybackTabUpdated(mTab);
        verify(mSmartSelectionClient, never())
                .removeSurroundingTextReceivedListeners(mTapToSeekSelectionManager);
        verify(mSelectionPopupController, never()).setSelectionClient(null);
    }

    @Test
    public void testTapToSeekSelectionClient() {
        // add hooks
        mTapToSeekSelectionManager.onActivePlaybackTabUpdated(mTab);

        TapToSeekSelectionManager.TapToSeekSelectionClient client =
                mTapToSeekSelectionManager.getSelectionClient();

        client.onSelectionChanged("selection");
        verify(mSmartSelectionClient, times(1)).onSelectionChanged("selection");
        verify(mSmartSelectionClient, times(1)).requestSelectionPopupUpdates(false);

        client.onSelectionEvent(SelectionEventType.SELECTION_HANDLES_SHOWN, 1, 2);
        verify(mSmartSelectionClient, times(1))
                .onSelectionEvent(SelectionEventType.SELECTION_HANDLES_SHOWN, 1, 2);

        SelectAroundCaretResult result = new SelectAroundCaretResult(0, 1, 2, 3);
        client.selectAroundCaretAck(result);
        verify(mSmartSelectionClient, times(1)).selectAroundCaretAck(result);

        client.requestSelectionPopupUpdates(true);
        verify(mSmartSelectionClient, times(1)).requestSelectionPopupUpdates(true);

        client.cancelAllRequests();
        verify(mSmartSelectionClient, times(1)).cancelAllRequests();

        client.setTextClassifier(null);
        verify(mSmartSelectionClient, times(1)).setTextClassifier(null);

        client.getTextClassifier();
        verify(mSmartSelectionClient, times(1)).getTextClassifier();

        client.getCustomTextClassifier();
        verify(mSmartSelectionClient, times(1)).getCustomTextClassifier();

        client.getSelectionEventProcessor();
        verify(mSmartSelectionClient, times(1)).getSelectionEventProcessor();
    }
}