chromium/chrome/android/junit/src/org/chromium/chrome/browser/customtabs/features/partialcustomtab/ContentGestureListenerTest.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.features.partialcustomtab;

import static org.junit.Assert.assertEquals;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import android.view.MotionEvent;

import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
import org.robolectric.annotation.Config;
import org.robolectric.annotation.LooperMode;
import org.robolectric.annotation.LooperMode.Mode;

import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.customtabs.features.partialcustomtab.ContentGestureListener.GestureState;
import org.chromium.chrome.browser.customtabs.features.partialcustomtab.PartialCustomTabHandleStrategy.DragEventCallback;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.components.embedder_support.view.ContentView;
import org.chromium.content.browser.RenderCoordinatesImpl;

import java.util.function.BooleanSupplier;

/** Tests for {@link ContentGestureListener}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
@LooperMode(Mode.PAUSED)
public class ContentGestureListenerTest {
    private static final float DISTX = 0.f;

    @Mock private Tab mTab;
    @Mock private BooleanSupplier mIsFullyExpanded;
    @Mock private DragEventCallback mCallback;
    @Mock private RenderCoordinatesImpl mRenderCoordinates;
    @Mock private ContentView mTabContentView;
    @Mock private MotionEvent mEventSrc;

    private ContentGestureListener mListener;
    private MotionEvent mEventTo;

    @Before
    public void setUp() {
        MockitoAnnotations.initMocks(this);
        mListener = new ContentGestureListener(() -> mTab, mCallback, mIsFullyExpanded);
        RenderCoordinatesImpl.setInstanceForTesting(mRenderCoordinates);
        when(mTab.getContentView()).thenReturn(mTabContentView);

        mListener.onDown(Mockito.mock(MotionEvent.class));
        RenderCoordinatesImpl.setInstanceForTesting(mRenderCoordinates);
        mEventTo = MotionEvent.obtain(0, 0, MotionEvent.ACTION_MOVE, 0, 0, 0);
    }

    @Test
    public void draggingTabSwitchesToScrollingContent() {
        // Verify dragging-up gesture expands tab.
        mListener.onScroll(mEventSrc, mEventTo, DISTX, 100.f);
        verify(mCallback).onDragStart(anyInt());
        assertEquals(GestureState.DRAG_TAB, mListener.getStateForTesting());

        // Make the tab fully expanded.
        when(mIsFullyExpanded.getAsBoolean()).thenReturn(true);
        when(mRenderCoordinates.getScrollYPixInt())
                .thenReturn(10); // Now not scrolled to the top any more.

        // Keep dragging up, and the mode switches to content scrolling.
        mListener.onScroll(mEventSrc, mEventTo, DISTX, 50.f);
        mListener.onScroll(mEventSrc, mEventTo, DISTX, 40.f);
        verify(mTabContentView).onTouchEvent(any(MotionEvent.class));
        assertEquals(GestureState.SCROLL_CONTENT, mListener.getStateForTesting());
    }

    @Test
    public void tabFling() {
        // Verify dragging-up gesture expands tab.
        mListener.onScroll(mEventSrc, mEventTo, DISTX, 100.f);
        verify(mCallback).onDragStart(anyInt());
        assertEquals(GestureState.DRAG_TAB, mListener.getStateForTesting());

        mListener.onFling(mEventSrc, mEventTo, DISTX, 100.f);
        verify(mCallback).onDragEnd(anyInt());
    }

    @Test
    public void dragDownFromExpandedState_movesTab() {
        when(mIsFullyExpanded.getAsBoolean()).thenReturn(true);

        // Verify dragging down gesture moves the tab down.
        mListener.onScroll(mEventSrc, mEventTo, DISTX, -50.f);
        verify(mCallback).onDragStart(anyInt());
        assertEquals(GestureState.DRAG_TAB, mListener.getStateForTesting());
    }

    @Test
    public void dragDownFromExpandedState_scrollsContents() {
        when(mIsFullyExpanded.getAsBoolean()).thenReturn(true);
        when(mRenderCoordinates.getScrollYPixInt())
                .thenReturn(10); // Not scrolled to the top any more.

        // Verify dragging down gesture moves the tab down.
        mListener.onScroll(mEventSrc, mEventTo, DISTX, -50.f);
        mListener.onScroll(mEventSrc, mEventTo, DISTX, -50.f);
        assertEquals(GestureState.SCROLL_CONTENT, mListener.getStateForTesting());
    }

    @Test
    public void dragUpFromInitialState_movesTab() {
        mListener.onScroll(mEventSrc, mEventTo, DISTX, 50.f);
        assertEquals(GestureState.DRAG_TAB, mListener.getStateForTesting());
    }

    @Test
    public void dragDownFromInitialState_scrollsContents() {
        mListener.onScroll(mEventSrc, mEventTo, DISTX, -50.f);
        assertEquals(GestureState.SCROLL_CONTENT, mListener.getStateForTesting());
    }
}