chromium/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/content/EngagementSignalsInitialScrollObserverUnitTest.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.customtabs.content;

import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.verify;

import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.annotation.Config;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.customtabs.content.TabObserverRegistrar.CustomTabTabObserver;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabHidingType;
import org.chromium.content.browser.GestureListenerManagerImpl;
import org.chromium.content_public.browser.GestureStateListener;
import org.chromium.content_public.browser.LoadCommittedDetails;
import org.chromium.content_public.browser.WebContents;
import org.chromium.content_public.browser.WebContentsObserver;
import org.chromium.url.JUnitTestGURLs;

/** Unit tests for {@link EngagementSignalsInitialScrollObserver}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class EngagementSignalsInitialScrollObserverUnitTest {
    private static final int SCROLL_EXTENT = 100;

    @Rule
    public final CustomTabActivityContentTestEnvironment env =
            new CustomTabActivityContentTestEnvironment();

    @Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();

    @Mock private GestureListenerManagerImpl mGestureListenerManagerImpl;

    private EngagementSignalsInitialScrollObserver mInitialScrollObserver;

    @Before
    public void setUp() {
        GestureListenerManagerImpl.setInstanceForTesting(mGestureListenerManagerImpl);
    }

    @After
    public void tearDown() {
        GestureListenerManagerImpl.setInstanceForTesting(null);
    }

    @Test
    public void testHadScrollDown() {
        initializeTabForTest();
        var gestureStateListener = captureGestureStateListener();

        gestureStateListener.onScrollStarted(0, SCROLL_EXTENT, false);
        assertTrue(mInitialScrollObserver.hasCurrentPageHadScrollDown());
    }

    @Test
    public void testHasNotHadScrollDown() {
        initializeTabForTest();
        var gestureStateListener = captureGestureStateListener();

        assertFalse(mInitialScrollObserver.hasCurrentPageHadScrollDown());
        // A scroll up shouldn't change the result.
        gestureStateListener.onScrollStarted(0, SCROLL_EXTENT, true);
        assertFalse(mInitialScrollObserver.hasCurrentPageHadScrollDown());
    }

    @Test
    public void testResetsOnNavigation() {
        initializeTabForTest();
        var gestureStateListener = captureGestureStateListener();
        var webContentsObserver = captureWebContentsObserver();

        gestureStateListener.onScrollStarted(0, SCROLL_EXTENT, false);
        assertTrue(mInitialScrollObserver.hasCurrentPageHadScrollDown());

        var details =
                new LoadCommittedDetails(
                        0,
                        JUnitTestGURLs.URL_1,
                        false,
                        /* isSameDocument= */ false,
                        /* isMainFrame= */ true,
                        200);
        webContentsObserver.navigationEntryCommitted(details);
        assertFalse(mInitialScrollObserver.hasCurrentPageHadScrollDown());
    }

    @Test
    public void testResetOnTabChange() {
        initializeTabForTest();
        var gestureStateListener = captureGestureStateListener();

        gestureStateListener.onScrollStarted(0, SCROLL_EXTENT, false);
        assertTrue(mInitialScrollObserver.hasCurrentPageHadScrollDown());

        // Change tabs.
        mInitialScrollObserver.onHidden(env.tabProvider.getTab(), TabHidingType.CHANGED_TABS);
        assertFalse(mInitialScrollObserver.hasCurrentPageHadScrollDown());
    }

    private void initializeTabForTest() {
        Tab initialTab = env.prepareTab();
        doAnswer(
                        invocation -> {
                            CustomTabTabObserver observer = invocation.getArgument(0);
                            initialTab.addObserver(observer);
                            observer.onAttachedToInitialTab(initialTab);
                            return null;
                        })
                .when(env.tabObserverRegistrar)
                .registerActivityTabObserver(any());

        mInitialScrollObserver =
                new EngagementSignalsInitialScrollObserver(env.tabObserverRegistrar);
        verify(env.tabObserverRegistrar).registerActivityTabObserver(mInitialScrollObserver);

        env.tabProvider.setInitialTab(initialTab, TabCreationMode.DEFAULT);
    }

    private GestureStateListener captureGestureStateListener() {
        ArgumentCaptor<GestureStateListener> gestureStateListenerArgumentCaptor =
                ArgumentCaptor.forClass(GestureStateListener.class);
        verify(mGestureListenerManagerImpl, atLeastOnce())
                .addListener(
                        gestureStateListenerArgumentCaptor.capture(),
                        eq(org.chromium.cc.mojom.RootScrollOffsetUpdateFrequency.NONE));
        return gestureStateListenerArgumentCaptor.getValue();
    }

    private WebContentsObserver captureWebContentsObserver() {
        ArgumentCaptor<WebContentsObserver> webContentsObserverArgumentCaptor =
                ArgumentCaptor.forClass(WebContentsObserver.class);
        WebContents webContents = env.tabProvider.getTab().getWebContents();
        verify(webContents).addObserver(webContentsObserverArgumentCaptor.capture());
        return webContentsObserverArgumentCaptor.getValue();
    }
}