| // 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.page_insights; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotEquals; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.ArgumentMatchers.any; |
| import static org.mockito.ArgumentMatchers.anyBoolean; |
| import static org.mockito.ArgumentMatchers.anyInt; |
| import static org.mockito.ArgumentMatchers.anyLong; |
| import static org.mockito.ArgumentMatchers.eq; |
| import static org.mockito.Mockito.clearInvocations; |
| import static org.mockito.Mockito.doAnswer; |
| import static org.mockito.Mockito.doReturn; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.never; |
| import static org.mockito.Mockito.times; |
| import static org.mockito.Mockito.verify; |
| import static org.mockito.Mockito.verifyNoMoreInteractions; |
| import static org.mockito.Mockito.when; |
| |
| import static org.chromium.chrome.browser.page_insights.PageInsightsMediator.DEFAULT_TRIGGER_DELAY_MS; |
| import static org.chromium.chrome.browser.page_insights.PageInsightsMediator.PAGE_INSIGHTS_CAN_AUTOTRIGGER_AFTER_END; |
| import static org.chromium.chrome.browser.page_insights.PageInsightsMediator.PAGE_INSIGHTS_CAN_RETURN_TO_PEEK_AFTER_EXPANSION; |
| |
| import android.content.Context; |
| import android.graphics.drawable.GradientDrawable; |
| import android.text.format.DateUtils; |
| import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.widget.FrameLayout; |
| import android.widget.LinearLayout; |
| import android.widget.TextView; |
| |
| import androidx.test.filters.MediumTest; |
| |
| import com.google.protobuf.ByteString; |
| |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.ArgumentCaptor; |
| import org.mockito.Captor; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| import org.mockito.invocation.InvocationOnMock; |
| import org.mockito.stubbing.Answer; |
| import org.robolectric.annotation.LooperMode; |
| import org.robolectric.annotation.LooperMode.Mode; |
| import org.robolectric.shadows.ShadowLooper; |
| |
| import org.chromium.base.Callback; |
| import org.chromium.base.ContextUtils; |
| import org.chromium.base.FeatureList; |
| import org.chromium.base.FeatureList.TestValues; |
| import org.chromium.base.supplier.ObservableSupplier; |
| import org.chromium.base.supplier.ObservableSupplierImpl; |
| import org.chromium.base.supplier.Supplier; |
| import org.chromium.base.test.BaseRobolectricTestRunner; |
| import org.chromium.base.test.util.Features; |
| import org.chromium.base.test.util.Features.DisableFeatures; |
| import org.chromium.base.test.util.Features.EnableFeatures; |
| import org.chromium.base.test.util.HistogramWatcher; |
| import org.chromium.base.test.util.JniMocker; |
| import org.chromium.chrome.browser.back_press.BackPressManager; |
| import org.chromium.chrome.browser.browser_controls.BrowserControlsSizer; |
| import org.chromium.chrome.browser.browser_controls.BrowserControlsStateProvider; |
| import org.chromium.chrome.browser.flags.ChromeFeatureList; |
| import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridge; |
| import org.chromium.chrome.browser.optimization_guide.OptimizationGuideBridgeJni; |
| import org.chromium.chrome.browser.page_insights.PageInsightsMediator.PageInsightsEvent; |
| import org.chromium.chrome.browser.page_insights.proto.Config.PageInsightsConfig; |
| import org.chromium.chrome.browser.page_insights.proto.IntentParams.PageInsightsIntentParams; |
| import org.chromium.chrome.browser.page_insights.proto.PageInsights.AutoPeekConditions; |
| import org.chromium.chrome.browser.page_insights.proto.PageInsights.Page; |
| import org.chromium.chrome.browser.page_insights.proto.PageInsights.PageInsightsMetadata; |
| import org.chromium.chrome.browser.profiles.Profile; |
| import org.chromium.chrome.browser.profiles.ProfileManager; |
| import org.chromium.chrome.browser.share.ShareDelegate; |
| import org.chromium.chrome.browser.share.ShareDelegate.ShareOrigin; |
| import org.chromium.chrome.browser.signin.services.IdentityServicesProvider; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.util.ChromeAccessibilityUtil; |
| import org.chromium.chrome.browser.xsurface.ProcessScope; |
| import org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsActionsHandler; |
| import org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsLoggingParameters; |
| import org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsSurfaceRenderer; |
| import org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsSurfaceScope; |
| import org.chromium.chrome.browser.xsurface_provider.XSurfaceProcessScopeProvider; |
| import org.chromium.components.browser_ui.bottomsheet.BottomSheetController; |
| import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.SheetState; |
| import org.chromium.components.browser_ui.bottomsheet.BottomSheetController.StateChangeReason; |
| import org.chromium.components.browser_ui.bottomsheet.ExpandedSheetHelper; |
| import org.chromium.components.browser_ui.bottomsheet.ManagedBottomSheetController; |
| import org.chromium.components.browser_ui.share.ShareParams; |
| import org.chromium.components.browser_ui.widget.gesture.BackPressHandler; |
| import org.chromium.components.dom_distiller.core.DomDistillerUrlUtils; |
| import org.chromium.components.dom_distiller.core.DomDistillerUrlUtilsJni; |
| import org.chromium.components.optimization_guide.OptimizationGuideDecision; |
| import org.chromium.components.optimization_guide.proto.CommonTypesProto; |
| import org.chromium.components.optimization_guide.proto.HintsProto.PageInsightsHubRequestContextMetadata; |
| import org.chromium.components.optimization_guide.proto.HintsProto.RequestContextMetadata; |
| import org.chromium.components.signin.base.CoreAccountInfo; |
| import org.chromium.components.signin.identitymanager.ConsentLevel; |
| import org.chromium.components.signin.identitymanager.IdentityManager; |
| import org.chromium.content_public.browser.LoadUrlParams; |
| import org.chromium.content_public.browser.NavigationController; |
| import org.chromium.content_public.browser.NavigationEntry; |
| import org.chromium.content_public.browser.NavigationHandle; |
| import org.chromium.content_public.browser.WebContents; |
| import org.chromium.ui.base.ApplicationViewportInsetSupplier; |
| import org.chromium.url.GURL; |
| import org.chromium.url.JUnitTestGURLs; |
| |
| import java.util.Map; |
| import java.util.concurrent.TimeUnit; |
| |
| /** Unit tests for {@link PageInsightsMediator}. */ |
| @LooperMode(Mode.PAUSED) |
| @RunWith(BaseRobolectricTestRunner.class) |
| public class PageInsightsMediatorTest { |
| private static final String TEST_CHILD_PAGE_TITLE = "People also View"; |
| private static final byte[] TEST_FEED_ELEMENTS_OUTPUT = new byte[123]; |
| private static final byte[] TEST_CHILD_ELEMENTS_OUTPUT = new byte[456]; |
| private static final byte[] TEST_LOGGING_CGI = new byte[789]; |
| private static final int SHORT_TRIGGER_DELAY_MS = 2 * (int) DateUtils.SECOND_IN_MILLIS; |
| |
| @Rule public JniMocker jniMocker = new JniMocker(); |
| @Rule public Features.JUnitProcessor mFeaturesProcessor = new Features.JUnitProcessor(); |
| |
| @Mock protected OptimizationGuideBridge.Natives mOptimizationGuideBridgeJniMock; |
| @Mock private LayoutInflater mLayoutInflater; |
| @Mock private ObservableSupplier<Tab> mTabObservable; |
| @Mock private ManagedBottomSheetController mBottomSheetController; |
| @Mock private BottomSheetController mBottomUiController; |
| @Mock private ExpandedSheetHelper mExpandedSheetHelper; |
| @Mock private BrowserControlsStateProvider mControlsStateProvider; |
| @Mock private BrowserControlsSizer mBrowserControlsSizer; |
| @Mock private Tab mTab; |
| @Mock private Tab mSecondTab; |
| @Mock private ProcessScope mProcessScope; |
| @Mock private Supplier<Profile> mProfileSupplier; |
| @Mock private Profile mProfile; |
| @Mock private IdentityServicesProvider mIdentityServicesProvider; |
| @Mock private IdentityManager mIdentityManager; |
| @Mock private CoreAccountInfo mCoreAccountInfo; |
| @Mock private PageInsightsSurfaceScope mSurfaceScope; |
| @Mock private PageInsightsSurfaceRenderer mSurfaceRenderer; |
| @Mock private Supplier<ShareDelegate> mShareDelegateSupplier; |
| @Mock private ShareDelegate mShareDelegate; |
| @Mock private DomDistillerUrlUtils.Natives mDistillerUrlUtilsJniMock; |
| @Mock private BackPressManager mBackPressManager; |
| @Mock private BackPressHandler mBackPressHandler; |
| @Mock private PageInsightsCoordinator.ConfigProvider mPageInsightsConfigProvider; |
| @Mock private NavigationHandle mNavigationHandle; |
| @Mock private ObservableSupplier<Boolean> mInMotionSupplier; |
| @Mock private ApplicationViewportInsetSupplier mAppInsetSupplier; |
| @Mock private WebContents mWebContents; |
| @Mock private NavigationController mNavigationController; |
| @Mock private NavigationEntry mLastCommittedNavigationEntry; |
| |
| @Captor |
| private ArgumentCaptor<BrowserControlsStateProvider.Observer> |
| mBrowserControlsStateProviderObserver; |
| |
| @Captor private ArgumentCaptor<Map<String, Object>> mSurfaceRendererContextValues; |
| @Captor private ArgumentCaptor<LoadUrlParams> mLoadUrlParams; |
| @Captor private ArgumentCaptor<ShareParams> mShareParams; |
| @Captor private ArgumentCaptor<PageInsightsLoggingParameters> mLoggingParameters; |
| @Captor private ArgumentCaptor<Callback<Boolean>> mInMotionCallback; |
| @Captor private ArgumentCaptor<Callback<Tab>> mTabObservableCallback; |
| |
| private ShadowLooper mShadowLooper; |
| |
| private PageInsightsMediator mMediator; |
| |
| @Before |
| public void setUp() { |
| MockitoAnnotations.initMocks(this); |
| mShadowLooper = ShadowLooper.shadowMainLooper(); |
| jniMocker.mock(DomDistillerUrlUtilsJni.TEST_HOOKS, mDistillerUrlUtilsJniMock); |
| when(mDistillerUrlUtilsJniMock.getOriginalUrlFromDistillerUrl(any(String.class))) |
| .thenAnswer( |
| (invocation) -> { |
| return new GURL((String) invocation.getArguments()[0]); |
| }); |
| jniMocker.mock(OptimizationGuideBridgeJni.TEST_HOOKS, mOptimizationGuideBridgeJniMock); |
| doReturn(1L).when(mOptimizationGuideBridgeJniMock).init(); |
| ProfileManager.setLastUsedProfileForTesting(mProfile); |
| XSurfaceProcessScopeProvider.setProcessScopeForTesting(mProcessScope); |
| when(mProcessScope.obtainPageInsightsSurfaceScope( |
| any(PageInsightsSurfaceScopeDependencyProviderImpl.class))) |
| .thenReturn(mSurfaceScope); |
| when(mSurfaceScope.provideSurfaceRenderer()).thenReturn(mSurfaceRenderer); |
| when(mTabObservable.get()).thenReturn(mTab); |
| when(mTab.getUrl()).thenReturn(JUnitTestGURLs.EXAMPLE_URL); |
| when(mTab.getWebContents()).thenReturn(mWebContents); |
| when(mWebContents.getNavigationController()).thenReturn(mNavigationController); |
| when(mNavigationController.getLastCommittedEntryIndex()).thenReturn(0); |
| when(mNavigationController.getEntryAtIndex(0)).thenReturn(mLastCommittedNavigationEntry); |
| when(mShareDelegateSupplier.get()).thenReturn(mShareDelegate); |
| when(mProfileSupplier.get()).thenReturn(mProfile); |
| IdentityServicesProvider.setInstanceForTests(mIdentityServicesProvider); |
| when(mIdentityServicesProvider.getIdentityManager(mProfile)).thenReturn(mIdentityManager); |
| when(mBottomSheetController.getBottomSheetBackPressHandler()).thenReturn(mBackPressHandler); |
| when(mBottomSheetController.getCurrentSheetContent()).thenReturn(null); |
| when(mPageInsightsConfigProvider.get(any(), any())) |
| .thenReturn( |
| PageInsightsConfig.newBuilder() |
| .setShouldAutoTrigger(true) |
| .setShouldXsurfaceLog(true) |
| .build()); |
| when(mInMotionSupplier.get()).thenReturn(false); |
| ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(false); |
| } |
| |
| private void createMediator() { |
| createMediator(DEFAULT_TRIGGER_DELAY_MS); |
| } |
| |
| private void createMediator(int triggerDelayMs) { |
| TestValues testValues = new TestValues(); |
| testValues.addFieldTrialParamOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB, |
| PAGE_INSIGHTS_CAN_AUTOTRIGGER_AFTER_END, |
| String.valueOf(triggerDelayMs)); |
| createMediator(testValues, PageInsightsIntentParams.getDefaultInstance()); |
| } |
| |
| private void createMediator( |
| int triggerDelayMs, |
| TestValues furtherTestValues, |
| PageInsightsIntentParams intentParams) { |
| furtherTestValues.addFieldTrialParamOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB, |
| PAGE_INSIGHTS_CAN_AUTOTRIGGER_AFTER_END, |
| String.valueOf(triggerDelayMs)); |
| createMediator(furtherTestValues, intentParams); |
| } |
| |
| private void createMediator(TestValues testValues, PageInsightsIntentParams intentParams) { |
| FeatureList.mergeTestValues(testValues, /* replace= */ true); |
| Context context = ContextUtils.getApplicationContext(); |
| context.setTheme(org.chromium.chrome.R.style.Theme_BrowserUI); |
| mMediator = |
| new PageInsightsMediator( |
| context, |
| new View(ContextUtils.getApplicationContext()), |
| mTabObservable, |
| mShareDelegateSupplier, |
| mProfileSupplier, |
| mBottomSheetController, |
| mBottomUiController, |
| mExpandedSheetHelper, |
| mControlsStateProvider, |
| mBrowserControlsSizer, |
| mBackPressManager, |
| mInMotionSupplier, |
| mAppInsetSupplier, |
| intentParams, |
| () -> true, |
| mPageInsightsConfigProvider); |
| verify(mControlsStateProvider).addObserver(mBrowserControlsStateProviderObserver.capture()); |
| verify(mInMotionSupplier).addObserver(mInMotionCallback.capture()); |
| verify(mTabObservable).addObserver(mTabObservableCallback.capture()); |
| mockOptimizationGuideResponse(getPageInsightsMetadata()); |
| setBackgroundDrawable(); |
| } |
| |
| @Test |
| @MediumTest |
| @EnableFeatures(ChromeFeatureList.BACK_GESTURE_REFACTOR) |
| public void |
| constructor_backGestureRefactorEnabled_sameBackPressHandlerNotRegistered_registers() { |
| createMediator(); |
| |
| verify(mBackPressManager) |
| .addHandler(mBackPressHandler, BackPressHandler.Type.PAGE_INSIGHTS_BOTTOM_SHEET); |
| } |
| |
| @Test |
| @MediumTest |
| @EnableFeatures(ChromeFeatureList.BACK_GESTURE_REFACTOR) |
| public void |
| constructor_backGestureRefactorEnabled_sameBackPressHandlerRegistered_doesNotRegister() { |
| when(mBackPressManager.has(BackPressHandler.Type.PAGE_INSIGHTS_BOTTOM_SHEET)) |
| .thenReturn(true); |
| |
| createMediator(); |
| |
| verify(mBackPressManager, never()) |
| .addHandler(mBackPressHandler, BackPressHandler.Type.PAGE_INSIGHTS_BOTTOM_SHEET); |
| } |
| |
| @Test |
| @MediumTest |
| @DisableFeatures(ChromeFeatureList.BACK_GESTURE_REFACTOR) |
| public void constructor_backGestureRefactorDisabled_doesNotRegister() { |
| createMediator(); |
| |
| verify(mBackPressManager, never()) |
| .addHandler(mBackPressHandler, BackPressHandler.Type.PAGE_INSIGHTS_BOTTOM_SHEET); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_doesNotTriggerImmediately() { |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| mMediator.onPageLoadStarted(mTab, null); |
| |
| verify(mBottomSheetController, never()).requestShowContent(any(), anyBoolean()); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_notEnoughDuration_doesNotTrigger() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(250, TimeUnit.MILLISECONDS); |
| |
| verify(mBottomSheetController, never()).requestShowContent(any(), anyBoolean()); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_shouldNotAutoTrigger_doesNotTrigger() { |
| when(mPageInsightsConfigProvider.get(mNavigationHandle, mLastCommittedNavigationEntry)) |
| .thenReturn(PageInsightsConfig.newBuilder().setShouldAutoTrigger(false).build()); |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| verify(mBottomSheetController, never()).requestShowContent(any(), anyBoolean()); |
| verifyNoMoreInteractions(mOptimizationGuideBridgeJniMock); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_controlsOnScreen_a11yDisabled_doesNotTrigger() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| verify(mBottomSheetController, never()).requestShowContent(any(), anyBoolean()); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_inMotion_doesNotTrigger() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| when(mInMotionSupplier.get()).thenReturn(true); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| verify(mBottomSheetController, never()).requestShowContent(any(), anyBoolean()); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_controlsOnScreen_a11yEnabled_showsBottomSheet() { |
| ChromeAccessibilityUtil.get().setAccessibilityEnabledForTesting(true); |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| assertBottomSheetShownAfterAutoTrigger(feedView); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_navHandleProvidedAfterTimerFinishes_showsBottomSheet() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| |
| assertBottomSheetShownAfterAutoTrigger(feedView); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_controlsOnScreenWhenDataFetched_laterOffScreen_showsBottomSheet() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| verify(mBottomSheetController, never()).requestShowContent(any(), anyBoolean()); |
| |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| mBrowserControlsStateProviderObserver.getValue().onControlsOffsetChanged(0, 0, 0, 0, false); |
| |
| assertBottomSheetShownAfterAutoTrigger(feedView); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_inMotionWhenDataFetched_laterNotInMotion_showsBottomSheet() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| when(mInMotionSupplier.get()).thenReturn(true); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| verify(mBottomSheetController, never()).requestShowContent(any(), anyBoolean()); |
| |
| when(mInMotionSupplier.get()).thenReturn(false); |
| mInMotionCallback.getValue().onResult(false); |
| |
| assertBottomSheetShownAfterAutoTrigger(feedView); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_controlsOffScreen_showsBottomSheet() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| assertBottomSheetShownAfterAutoTrigger(feedView); |
| } |
| |
| @Test |
| @MediumTest |
| public void testNewPage_dismisses() { |
| createMediator(); |
| when(mBottomSheetController.getCurrentSheetContent()) |
| .thenReturn(mMediator.getSheetContent()); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| |
| verify(mBottomSheetController).hideContent(mMediator.getSheetContent(), true); |
| } |
| |
| @Test |
| @MediumTest |
| public void testNewTab_addsObserverAndDismisses() { |
| createMediator(); |
| when(mBottomSheetController.getCurrentSheetContent()) |
| .thenReturn(mMediator.getSheetContent()); |
| |
| mTabObservableCallback.getValue().onResult(mSecondTab); |
| |
| verify(mSecondTab).addObserver(mMediator); |
| verify(mBottomSheetController).hideContent(mMediator.getSheetContent(), true); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_sendsCorrectMetadata() { |
| when(mPageInsightsConfigProvider.get(mNavigationHandle, mLastCommittedNavigationEntry)) |
| .thenReturn( |
| PageInsightsConfig.newBuilder() |
| .setShouldAutoTrigger(true) |
| .setServerShouldNotLogOrPersonalize(true) |
| .setIsInitialPage(true) |
| .setNavigationTimestampMs(1234L) |
| .build()); |
| TestValues testValues = new TestValues(); |
| testValues.addFieldTrialParamOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB, |
| PageInsightsDataLoader.PAGE_INSIGHTS_SEND_CONTEXT_METADATA, |
| "true"); |
| testValues.addFieldTrialParamOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB, |
| PageInsightsDataLoader.PAGE_INSIGHTS_SEND_TIMESTAMP, |
| "true"); |
| createMediator( |
| SHORT_TRIGGER_DELAY_MS, testValues, PageInsightsIntentParams.getDefaultInstance()); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| RequestContextMetadata expectedMetadata = |
| RequestContextMetadata.newBuilder() |
| .setPageInsightsHubMetadata( |
| PageInsightsHubRequestContextMetadata.newBuilder() |
| .setIsUserInitiated(false) |
| .setIsInitialPage(true) |
| .setShouldNotLogOrPersonalize(true) |
| .setNavigationTimestampMs(1234L)) |
| .build(); |
| verify(mOptimizationGuideBridgeJniMock, times(1)) |
| .canApplyOptimizationOnDemand( |
| anyLong(), |
| any(), |
| any(), |
| eq(CommonTypesProto.RequestContext.CONTEXT_PAGE_INSIGHTS_HUB.getNumber()), |
| any(), |
| eq(expectedMetadata.toByteArray())); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_signedIn_providesBothXSurfaceLoggingParamsAndLogs() |
| throws Exception { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)) |
| .thenReturn(mCoreAccountInfo); |
| when(mCoreAccountInfo.getEmail()).thenReturn("email@address.com"); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| verify(mSurfaceRenderer).onSurfaceCreated(mLoggingParameters.capture()); |
| assertEquals( |
| ByteString.copyFrom(TEST_LOGGING_CGI), |
| ByteString.copyFrom(mLoggingParameters.getValue().parentData())); |
| assertEquals("email@address.com", mLoggingParameters.getValue().accountName()); |
| verify(mSurfaceRenderer) |
| .onEvent( |
| org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsEvent |
| .BOTTOM_SHEET_PEEKING); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_signedOut_providesPartialXSurfaceLoggingParamsAndLogs() |
| throws Exception { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)).thenReturn(null); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| verify(mSurfaceRenderer).onSurfaceCreated(mLoggingParameters.capture()); |
| assertEquals( |
| ByteString.copyFrom(TEST_LOGGING_CGI), |
| ByteString.copyFrom(mLoggingParameters.getValue().parentData())); |
| assertNull(mLoggingParameters.getValue().accountName()); |
| verify(mSurfaceRenderer) |
| .onEvent( |
| org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsEvent |
| .BOTTOM_SHEET_PEEKING); |
| } |
| |
| @Test |
| @MediumTest |
| public void |
| testExpandAfterAutoTrigger_cannotReturnToPeekAfterExpansion_peekDisabledSwipeEnabled() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.SWIPE); |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| assertEquals( |
| PageInsightsSheetContent.HeightMode.DISABLED, |
| mMediator.getSheetContent().getPeekHeight()); |
| assertTrue(mMediator.getSheetContent().swipeToDismissEnabled()); |
| } |
| |
| @Test |
| @MediumTest |
| public void |
| testExpandAfterAutoTrigger_canReturnToPeekAfterExpansionFromFlag_peekEnabledSwipeDisabled() { |
| TestValues testValues = new TestValues(); |
| testValues.addFieldTrialParamOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB, |
| PAGE_INSIGHTS_CAN_RETURN_TO_PEEK_AFTER_EXPANSION, |
| "true"); |
| createMediator( |
| SHORT_TRIGGER_DELAY_MS, testValues, PageInsightsIntentParams.getDefaultInstance()); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.SWIPE); |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| assertNotEquals( |
| PageInsightsSheetContent.HeightMode.DISABLED, |
| mMediator.getSheetContent().getPeekHeight()); |
| assertFalse(mMediator.getSheetContent().swipeToDismissEnabled()); |
| } |
| |
| @Test |
| @MediumTest |
| public void |
| testExpandAfterAutoTrigger_canReturnToPeekAfterExpansionFromIntentParam_peekEnabledSwipeDisabled() { |
| createMediator( |
| SHORT_TRIGGER_DELAY_MS, |
| new TestValues(), |
| PageInsightsIntentParams.newBuilder() |
| .setCanReturnToPeekAfterExpansion(true) |
| .build()); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.SWIPE); |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| assertNotEquals( |
| PageInsightsSheetContent.HeightMode.DISABLED, |
| mMediator.getSheetContent().getPeekHeight()); |
| assertFalse(mMediator.getSheetContent().swipeToDismissEnabled()); |
| } |
| |
| @Test |
| @MediumTest |
| public void testLaunch_sendsCorrectMetadata() throws Exception { |
| when(mPageInsightsConfigProvider.get(mNavigationHandle, mLastCommittedNavigationEntry)) |
| .thenReturn( |
| PageInsightsConfig.newBuilder() |
| .setServerShouldNotLogOrPersonalize(true) |
| .setIsInitialPage(true) |
| .setNavigationTimestampMs(1234L) |
| .build()); |
| TestValues testValues = new TestValues(); |
| testValues.addFieldTrialParamOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB, |
| PageInsightsDataLoader.PAGE_INSIGHTS_SEND_CONTEXT_METADATA, |
| "true"); |
| testValues.addFieldTrialParamOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB, |
| PageInsightsDataLoader.PAGE_INSIGHTS_SEND_TIMESTAMP, |
| "true"); |
| createMediator(testValues, PageInsightsIntentParams.getDefaultInstance()); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| |
| mMediator.launch(); |
| |
| RequestContextMetadata expectedMetadata = |
| RequestContextMetadata.newBuilder() |
| .setPageInsightsHubMetadata( |
| PageInsightsHubRequestContextMetadata.newBuilder() |
| .setIsUserInitiated(true) |
| .setIsInitialPage(true) |
| .setShouldNotLogOrPersonalize(true) |
| .setNavigationTimestampMs(1234L)) |
| .build(); |
| verify(mOptimizationGuideBridgeJniMock, times(1)) |
| .canApplyOptimizationOnDemand( |
| anyLong(), |
| any(), |
| any(), |
| eq(CommonTypesProto.RequestContext.CONTEXT_PAGE_INSIGHTS_HUB.getNumber()), |
| any(), |
| eq(expectedMetadata.toByteArray())); |
| } |
| |
| @Test |
| @MediumTest |
| public void testLaunch_showsBottomSheet() throws Exception { |
| createMediator(); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| |
| mMediator.launch(); |
| |
| verify(mBottomSheetController, times(1)).requestShowContent(any(), anyBoolean()); |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getToolbarView() |
| .findViewById(R.id.page_insights_feed_header) |
| .getVisibility()); |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getContentView() |
| .findViewById(R.id.page_insights_feed_content) |
| .getVisibility()); |
| assertEquals( |
| feedView, |
| ((FrameLayout) |
| mMediator |
| .getSheetContent() |
| .getContentView() |
| .findViewById(R.id.page_insights_feed_content)) |
| .getChildAt(0)); |
| assertEquals( |
| PageInsightsSheetContent.HeightMode.DISABLED, |
| mMediator.getSheetContent().getPeekHeight()); |
| verify(mBottomSheetController).expandSheet(); |
| } |
| |
| @Test |
| @MediumTest |
| public void testLaunch_signedIn_providesFullXSurfaceLoggingParamsAndLogs() throws Exception { |
| createMediator(); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)) |
| .thenReturn(mCoreAccountInfo); |
| when(mCoreAccountInfo.getEmail()).thenReturn("email@address.com"); |
| |
| mMediator.launch(); |
| |
| verify(mSurfaceRenderer).onSurfaceCreated(mLoggingParameters.capture()); |
| assertEquals( |
| ByteString.copyFrom(TEST_LOGGING_CGI), |
| ByteString.copyFrom(mLoggingParameters.getValue().parentData())); |
| assertEquals("email@address.com", mLoggingParameters.getValue().accountName()); |
| verify(mSurfaceRenderer) |
| .onEvent( |
| org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsEvent |
| .BOTTOM_SHEET_EXPANDED); |
| } |
| |
| @Test |
| @MediumTest |
| public void testLaunch_signedIn_shouldNotXSurfaceLog_doesNotCallOnSurfaceCreated() |
| throws Exception { |
| when(mPageInsightsConfigProvider.get(mNavigationHandle, mLastCommittedNavigationEntry)) |
| .thenReturn(PageInsightsConfig.newBuilder().setShouldXsurfaceLog(false).build()); |
| createMediator(); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)) |
| .thenReturn(mCoreAccountInfo); |
| when(mCoreAccountInfo.getEmail()).thenReturn("email@address.com"); |
| |
| mMediator.launch(); |
| |
| verify(mSurfaceRenderer, never()).onSurfaceCreated(any()); |
| } |
| |
| @Test |
| @MediumTest |
| public void testLaunch_signedOut_providesPartialhXSurfaceLoggingParamsAndLogs() |
| throws Exception { |
| createMediator(); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mIdentityManager.getPrimaryAccountInfo(ConsentLevel.SIGNIN)).thenReturn(null); |
| |
| mMediator.launch(); |
| |
| verify(mSurfaceRenderer).onSurfaceCreated(mLoggingParameters.capture()); |
| assertEquals( |
| ByteString.copyFrom(TEST_LOGGING_CGI), |
| ByteString.copyFrom(mLoggingParameters.getValue().parentData())); |
| assertNull(mLoggingParameters.getValue().accountName()); |
| verify(mSurfaceRenderer) |
| .onEvent( |
| org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsEvent |
| .BOTTOM_SHEET_EXPANDED); |
| } |
| |
| @Test |
| @MediumTest |
| public void actionHandler_navigateToPageInsightsPage_childPageOpened() { |
| createMediator(); |
| View childView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| when(mSurfaceRenderer.render(eq(TEST_CHILD_ELEMENTS_OUTPUT), any())).thenReturn(childView); |
| mMediator.launch(); |
| |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .navigateToPageInsightsPage(1); |
| |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getToolbarView() |
| .findViewById(R.id.page_insights_child_title) |
| .getVisibility()); |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getToolbarView() |
| .findViewById(R.id.page_insights_back_button) |
| .getVisibility()); |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getContentView() |
| .findViewById(R.id.page_insights_child_content) |
| .getVisibility()); |
| assertEquals( |
| childView, |
| ((FrameLayout) |
| mMediator |
| .getSheetContent() |
| .getContentView() |
| .findViewById(R.id.page_insights_child_content)) |
| .getChildAt(0)); |
| TextView childPageTitle = |
| mMediator |
| .getSheetContent() |
| .getToolbarView() |
| .findViewById(R.id.page_insights_child_title); |
| assertEquals(childPageTitle.getText(), TEST_CHILD_PAGE_TITLE); |
| } |
| |
| @Test |
| @MediumTest |
| public void actionHandler_openUrl_opensUrl() { |
| createMediator(); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| mMediator.launch(); |
| |
| String url = "https://www.realwebsite.com/"; |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .openUrl(url, /* doesRequestSpecifySameSession= */ false); |
| |
| verify(mTab).loadUrl(mLoadUrlParams.capture()); |
| assertEquals(url, mLoadUrlParams.getValue().getUrl()); |
| } |
| |
| @Test |
| @MediumTest |
| public void actionHandler_share_shares() { |
| createMediator(); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| mMediator.launch(); |
| |
| String url = "https://www.realwebsite.com/"; |
| String title = "Real Website TM"; |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .share(url, title); |
| |
| verify(mShareDelegate).share(mShareParams.capture(), any(), eq(ShareOrigin.PAGE_INSIGHTS)); |
| assertEquals(url, mShareParams.getValue().getUrl()); |
| assertEquals(title, mShareParams.getValue().getTitle()); |
| } |
| |
| @Test |
| @MediumTest |
| public void openInExpandedState_recordsHistogram_userInvokesPih() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newSingleRecordWatcher( |
| "CustomTabs.PageInsights.Event", |
| PageInsightsMediator.PageInsightsEvent.USER_INVOKES_PIH); |
| createMediator(); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| mMediator.launch(); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void testAutoTrigger_recordsHistogram_autoPeekTriggered() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newSingleRecordWatcher( |
| "CustomTabs.PageInsights.Event", PageInsightsEvent.AUTO_PEEK_TRIGGERED); |
| |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render(eq(TEST_FEED_ELEMENTS_OUTPUT), any())).thenReturn(feedView); |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(1.0f); |
| |
| mMediator.onPageLoadStarted(mTab, null); |
| mMediator.onDidFinishNavigationInPrimaryMainFrame(mTab, mNavigationHandle); |
| mShadowLooper.idleFor(2500, TimeUnit.MILLISECONDS); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void swipeToExpandedState_recordsHistogramInStateExpanded() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newSingleRecordWatcher( |
| "CustomTabs.PageInsights.Event", PageInsightsEvent.STATE_EXPANDED); |
| createMediator(); |
| |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void swipeToExpandedStateFromPeek_xSurfaceLogs() { |
| createMediator(); |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.NONE); |
| |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| verify(mSurfaceRenderer) |
| .onEvent( |
| org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsEvent |
| .BOTTOM_SHEET_EXPANDED); |
| } |
| |
| @Test |
| @MediumTest |
| public void openInExpandedState_updateToPeekState_recordsHistogramInStatePeek() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newSingleRecordWatcher( |
| "CustomTabs.PageInsights.Event", |
| PageInsightsMediator.PageInsightsEvent.STATE_PEEK); |
| |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| |
| // STATE_PEEK is recorded |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.SWIPE); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void dismiss_unbindsXSurfaceViewsAndCallsOnSurfaceClosed() { |
| createMediator(); |
| View childView = new View(ContextUtils.getApplicationContext()); |
| View feedView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(feedView); |
| when(mSurfaceRenderer.render(eq(TEST_CHILD_ELEMENTS_OUTPUT), any())).thenReturn(childView); |
| mMediator.launch(); |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .navigateToPageInsightsPage(1); |
| |
| mMediator.onSheetStateChanged(SheetState.HIDDEN, StateChangeReason.SWIPE); |
| |
| verify(mSurfaceRenderer).unbindView(feedView); |
| verify(mSurfaceRenderer).unbindView(childView); |
| verify(mSurfaceRenderer).onSurfaceClosed(); |
| } |
| |
| @Test |
| @MediumTest |
| public void dismissFromPeekState_XSurfaceLogs() { |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.NONE); |
| |
| mMediator.onSheetStateChanged(SheetState.HIDDEN, StateChangeReason.SWIPE); |
| |
| verify(mSurfaceRenderer) |
| .onEvent( |
| org.chromium.chrome.browser.xsurface.pageinsights.PageInsightsEvent |
| .DISMISSED_FROM_PEEKING_STATE); |
| } |
| |
| @Test |
| @MediumTest |
| public void dismissFromPeekState_recordsHistogramInDismissPeek() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newBuilder() |
| .expectIntRecords( |
| "CustomTabs.PageInsights.Event", |
| PageInsightsEvent.STATE_PEEK, |
| PageInsightsEvent.DISMISS_PEEK) |
| .build(); |
| |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| |
| // STATE_PEEK is recorded |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.SWIPE); |
| // DISMISS_PEEK is recorded |
| mMediator.onSheetStateChanged(SheetState.HIDDEN, StateChangeReason.SWIPE); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void dismissFromExpandedState_recordsHistogramInDismissExpanded() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newBuilder() |
| .expectIntRecords( |
| "CustomTabs.PageInsights.Event", |
| PageInsightsEvent.STATE_EXPANDED, |
| PageInsightsEvent.DISMISS_EXPANDED) |
| .build(); |
| |
| createMediator(SHORT_TRIGGER_DELAY_MS); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| |
| // STATE_PEEK is recorded |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| // DISMISS_PEEK is recorded |
| mMediator.onSheetStateChanged(SheetState.HIDDEN, StateChangeReason.SWIPE); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void |
| navigateToPageInsightsPage_childPageOpened_recordsHistogram_userInteractsWithChildPage() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newBuilder() |
| .expectIntRecords( |
| "CustomTabs.PageInsights.Event", |
| PageInsightsEvent.USER_INVOKES_PIH, |
| PageInsightsEvent.TAP_XSURFACE_VIEW_CHILD_PAGE) |
| .build(); |
| |
| createMediator(); |
| View childView = new View(ContextUtils.getApplicationContext()); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| when(mSurfaceRenderer.render(eq(TEST_CHILD_ELEMENTS_OUTPUT), any())).thenReturn(childView); |
| mMediator.launch(); |
| |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .navigateToPageInsightsPage(1); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void |
| navigateToPageInsightsPage_openUrl_recordsHistogram_userInteractsWithSurfaceElement() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newBuilder() |
| .expectIntRecords( |
| "CustomTabs.PageInsights.Event", |
| PageInsightsEvent.USER_INVOKES_PIH, |
| PageInsightsEvent.TAP_XSURFACE_VIEW_URL) |
| .build(); |
| |
| createMediator(); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| mMediator.launch(); |
| |
| String url = "https://www.realwebsite.com/"; |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .openUrl(url, /* doesRequestSpecifySameSession= */ false); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void navigateToPageInsightsPage_share_recordsHistogram_userInteractsWithShare() { |
| HistogramWatcher histogramWatcher = |
| HistogramWatcher.newBuilder() |
| .expectIntRecords( |
| "CustomTabs.PageInsights.Event", |
| PageInsightsEvent.USER_INVOKES_PIH, |
| PageInsightsEvent.TAP_XSURFACE_VIEW_SHARE) |
| .build(); |
| |
| createMediator(); |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| mMediator.launch(); |
| |
| String url = "https://www.realwebsite.com/"; |
| String title = "Real Website TM"; |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .share(url, title); |
| |
| histogramWatcher.assertExpected(); |
| } |
| |
| @Test |
| @MediumTest |
| public void tapOnBottomSheet_peekState_expands() { |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.PEEK); |
| createMediator(); |
| |
| mMediator.getSheetContent().getToolbarView().callOnClick(); |
| |
| verify(mBottomSheetController).expandSheet(); |
| } |
| |
| @Test |
| @MediumTest |
| public void tapOnBottomSheet_fullState_doesNotExpand() { |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.FULL); |
| createMediator(); |
| |
| mMediator.getSheetContent().getToolbarView().callOnClick(); |
| |
| verify(mBottomSheetController, never()).expandSheet(); |
| } |
| |
| @Test |
| @MediumTest |
| public void handleBackPress_peekState_doesNothing() { |
| createMediator(); |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.PEEK); |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.SWIPE); |
| |
| assertFalse(mMediator.getSheetContent().getBackPressStateChangedSupplier().get()); |
| |
| boolean handled = mMediator.getSheetContent().handleBackPress(); |
| |
| assertFalse(handled); |
| verify(mBottomSheetController, never()) |
| .hideContent( |
| eq(mMediator.getSheetContent()), |
| anyBoolean(), |
| eq(StateChangeReason.BACK_PRESS)); |
| } |
| |
| @Test |
| @MediumTest |
| public void handleBackPress_hiddenState_doesNothing() { |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.HIDDEN); |
| createMediator(); |
| mMediator.onSheetStateChanged(SheetState.HIDDEN, StateChangeReason.SWIPE); |
| |
| assertFalse(mMediator.getSheetContent().getBackPressStateChangedSupplier().get()); |
| |
| boolean handled = mMediator.getSheetContent().handleBackPress(); |
| |
| assertFalse(handled); |
| verify(mBottomSheetController, never()) |
| .hideContent( |
| eq(mMediator.getSheetContent()), |
| anyBoolean(), |
| eq(StateChangeReason.BACK_PRESS)); |
| } |
| |
| @Test |
| @MediumTest |
| public void handleBackPress_fullState_notChildPage_canCollapseToPeek_collapses() { |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.FULL); |
| when(mBottomSheetController.collapseSheet(true)).thenReturn(true); |
| createMediator(); |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| assertTrue(mMediator.getSheetContent().getBackPressStateChangedSupplier().get()); |
| |
| boolean handled = mMediator.getSheetContent().handleBackPress(); |
| |
| assertTrue(handled); |
| verify(mBottomSheetController).collapseSheet(true); |
| verify(mBottomSheetController, never()) |
| .hideContent(any(), anyBoolean(), eq(StateChangeReason.BACK_PRESS)); |
| } |
| |
| @Test |
| @MediumTest |
| public void handleBackPress_fullState_notChildPage_cannotCollapseToPeek_hidesContent() { |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.FULL); |
| when(mBottomSheetController.collapseSheet(true)).thenReturn(false); |
| createMediator(); |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| |
| assertTrue(mMediator.getSheetContent().getBackPressStateChangedSupplier().get()); |
| |
| boolean handled = mMediator.getSheetContent().handleBackPress(); |
| |
| assertTrue(handled); |
| verify(mBottomSheetController) |
| .hideContent(mMediator.getSheetContent(), true, StateChangeReason.BACK_PRESS); |
| } |
| |
| @Test |
| @MediumTest |
| public void handleBackPress_fullState_childPage_goesBackToFeedPage() { |
| when(mSurfaceRenderer.render( |
| eq(TEST_FEED_ELEMENTS_OUTPUT), mSurfaceRendererContextValues.capture())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| when(mSurfaceRenderer.render(eq(TEST_CHILD_ELEMENTS_OUTPUT), any())) |
| .thenReturn(new View(ContextUtils.getApplicationContext())); |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.FULL); |
| createMediator(); |
| mMediator.launch(); |
| mMediator.onSheetStateChanged(SheetState.FULL, StateChangeReason.SWIPE); |
| ((PageInsightsActionsHandler) |
| mSurfaceRendererContextValues |
| .getValue() |
| .get(PageInsightsActionsHandler.KEY)) |
| .navigateToPageInsightsPage(1); |
| |
| assertTrue(mMediator.getSheetContent().getBackPressStateChangedSupplier().get()); |
| |
| boolean handled = mMediator.getSheetContent().handleBackPress(); |
| |
| assertTrue(handled); |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getToolbarView() |
| .findViewById(R.id.page_insights_feed_header) |
| .getVisibility()); |
| verify(mBottomSheetController, never()) |
| .hideContent( |
| eq(mMediator.getSheetContent()), |
| anyBoolean(), |
| eq(StateChangeReason.BACK_PRESS)); |
| } |
| |
| @Test |
| @MediumTest |
| public void resizeInSync() { |
| TestValues testValues = new TestValues(); |
| testValues.addFeatureFlagOverride( |
| ChromeFeatureList.CCT_PAGE_INSIGHTS_HUB_BETTER_SCROLL, true); |
| FeatureList.mergeTestValues(testValues, /* replace= */ true); |
| createMediator(); |
| ObservableSupplierImpl<Integer> sheetInset = mMediator.getSheetInsetForTesting(); |
| verify(mAppInsetSupplier).setBottomSheetInsetSupplier(sheetInset); |
| |
| int fullHeight = 2000; |
| ViewGroup contentView = (ViewGroup) mock(ViewGroup.class); |
| when(contentView.getMeasuredHeight()).thenReturn(fullHeight); |
| var sheetContent = mMediator.getSheetContent(); |
| sheetContent.setShouldHavePeekStateForTesting(true); |
| sheetContent.setContentViewForTesting(contentView); |
| sheetContent.setFullScreenHeightForTesting(fullHeight); |
| int peekSheetHeight = sheetContent.getPeekHeight(); |
| |
| // First, let the sheet move to peeking state. |
| when(mBottomSheetController.getCurrentOffset()).thenReturn(peekSheetHeight); |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.PEEK); |
| |
| sheetInset.set(1); // set a non-zero value for testing. |
| mMediator.onSheetStateChanged(SheetState.PEEK, StateChangeReason.SWIPE); |
| |
| verify(mBrowserControlsSizer).setBottomControlsHeight(eq(peekSheetHeight), eq(0)); |
| assertEquals("BottomSheet inset should be reset", 0, (int) sheetInset.get()); |
| clearInvocations(mBrowserControlsSizer); |
| |
| // Make the browser controls fully visible. |
| when(mControlsStateProvider.getBrowserControlHiddenRatio()).thenReturn(0.f); |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.SCROLLING); |
| |
| // Simulate drag up and down the sheet across the peeking height. |
| float fraction = peekSheetHeight / (float) fullHeight + 0.1f; // above peek height |
| int offset = (int) (fullHeight * fraction); |
| mMediator.onSheetOffsetChanged(fraction, offset); |
| verify(mBrowserControlsSizer, never()).setBottomControlsHeight(anyInt(), anyInt()); |
| assertEquals("BottomSheet inset should remain zero", 0, (int) sheetInset.get()); |
| |
| fraction = peekSheetHeight / (float) fullHeight - 0.1f; // below peek height |
| offset = (int) (fullHeight * fraction); |
| mMediator.onSheetOffsetChanged(fraction, offset); |
| verify(mBrowserControlsSizer, never()).setBottomControlsHeight(eq(offset), eq(0)); |
| assertEquals("BottomSheet inset should be updated", offset, (int) sheetInset.get()); |
| |
| // Hide the sheet. |
| when(mBottomSheetController.getCurrentOffset()).thenReturn(0); |
| when(mBottomSheetController.getSheetState()).thenReturn(SheetState.HIDDEN); |
| mMediator.onSheetStateChanged(SheetState.HIDDEN, StateChangeReason.SWIPE); |
| |
| verify(mBrowserControlsSizer).setBottomControlsHeight(eq(0), eq(0)); |
| assertEquals("BottomSheet inset should be reset", 0, (int) sheetInset.get()); |
| } |
| |
| private PageInsightsMetadata getPageInsightsMetadata() { |
| Page childPage = |
| Page.newBuilder() |
| .setId(Page.PageID.PEOPLE_ALSO_VIEW) |
| .setTitle(TEST_CHILD_PAGE_TITLE) |
| .setElementsOutput(ByteString.copyFrom(TEST_CHILD_ELEMENTS_OUTPUT)) |
| .build(); |
| Page feedPage = |
| Page.newBuilder() |
| .setId(Page.PageID.SINGLE_FEED_ROOT) |
| .setTitle("Related Insights") |
| .setElementsOutput(ByteString.copyFrom(TEST_FEED_ELEMENTS_OUTPUT)) |
| .build(); |
| AutoPeekConditions mAutoPeekConditions = |
| AutoPeekConditions.newBuilder() |
| .setConfidence(0.51f) |
| .setPageScrollFraction(0.4f) |
| .setMinimumSecondsOnPage(30) |
| .build(); |
| return PageInsightsMetadata.newBuilder() |
| .setFeedPage(feedPage) |
| .addPages(childPage) |
| .setAutoPeekConditions(mAutoPeekConditions) |
| .setLoggingCgi(ByteString.copyFrom(TEST_LOGGING_CGI)) |
| .build(); |
| } |
| |
| private void mockOptimizationGuideResponse(PageInsightsMetadata metadata) { |
| doAnswer( |
| new Answer<Void>() { |
| @Override |
| public Void answer(InvocationOnMock invocation) { |
| OptimizationGuideBridge.OnDemandOptimizationGuideCallback callback = |
| (OptimizationGuideBridge.OnDemandOptimizationGuideCallback) |
| invocation.getArguments()[4]; |
| callback.onOnDemandOptimizationGuideDecision( |
| JUnitTestGURLs.EXAMPLE_URL, |
| org.chromium.components.optimization_guide.proto.HintsProto |
| .OptimizationType.PAGE_INSIGHTS, |
| OptimizationGuideDecision.TRUE, |
| CommonTypesProto.Any.newBuilder() |
| .setValue( |
| ByteString.copyFrom(metadata.toByteArray())) |
| .build()); |
| return null; |
| } |
| }) |
| .when(mOptimizationGuideBridgeJniMock) |
| .canApplyOptimizationOnDemand( |
| eq(1L), |
| eq(new GURL[] {JUnitTestGURLs.EXAMPLE_URL}), |
| eq( |
| new int[] { |
| org.chromium.components.optimization_guide.proto.HintsProto |
| .OptimizationType.PAGE_INSIGHTS |
| .getNumber() |
| }), |
| eq(CommonTypesProto.RequestContext.CONTEXT_PAGE_INSIGHTS_HUB.getNumber()), |
| any(OptimizationGuideBridge.OnDemandOptimizationGuideCallback.class), |
| any()); |
| } |
| |
| private void setBackgroundDrawable() { |
| // Making a ViewGroup only for testing purposes to provide with the value of Background |
| // Drawable (mBackgroundDrawable; which is a Gradient Drawable in PageInsightsMediator) |
| ViewGroup rootView = new LinearLayout(ContextUtils.getApplicationContext()); |
| View backgroundView = new View(ContextUtils.getApplicationContext()); |
| backgroundView.setId(R.id.background); |
| rootView.addView(backgroundView); |
| backgroundView.setBackground(new GradientDrawable()); |
| mMediator.initView(rootView); |
| } |
| |
| private void assertBottomSheetShownAfterAutoTrigger(View feedView) { |
| verify(mBottomSheetController, times(1)).requestShowContent(any(), anyBoolean()); |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getToolbarView() |
| .findViewById(R.id.page_insights_feed_header) |
| .getVisibility()); |
| assertEquals( |
| View.VISIBLE, |
| mMediator |
| .getSheetContent() |
| .getContentView() |
| .findViewById(R.id.page_insights_feed_content) |
| .getVisibility()); |
| assertEquals( |
| feedView, |
| ((FrameLayout) |
| mMediator |
| .getSheetContent() |
| .getContentView() |
| .findViewById(R.id.page_insights_feed_content)) |
| .getChildAt(0)); |
| assertNotEquals( |
| PageInsightsSheetContent.HeightMode.DISABLED, |
| mMediator.getSheetContent().getPeekHeight()); |
| verify(mBottomSheetController, never()).expandSheet(); |
| } |
| } |