blob: 6d985033f8fcc07c41e96d69933c197f9eba5a67 [file] [log] [blame]
// Copyright 2019 The Chromium Authors. All rights reserved.
// 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.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import androidx.browser.customtabs.CustomTabsSessionToken;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.chromium.base.UserDataHost;
import org.chromium.base.metrics.RecordHistogram;
import org.chromium.base.metrics.RecordUserAction;
import org.chromium.chrome.browser.ActivityTabProvider;
import org.chromium.chrome.browser.ActivityTabProvider.ActivityTabObserver;
import org.chromium.chrome.browser.ChromeActivity;
import org.chromium.chrome.browser.IntentHandler;
import org.chromium.chrome.browser.WarmupManager;
import org.chromium.chrome.browser.WebContentsFactory;
import org.chromium.chrome.browser.compositor.CompositorViewHolder;
import org.chromium.chrome.browser.customtabs.CloseButtonNavigator;
import org.chromium.chrome.browser.customtabs.CustomTabDelegateFactory;
import org.chromium.chrome.browser.customtabs.CustomTabIntentDataProvider;
import org.chromium.chrome.browser.customtabs.CustomTabNavigationEventObserver;
import org.chromium.chrome.browser.customtabs.CustomTabObserver;
import org.chromium.chrome.browser.customtabs.CustomTabTabPersistencePolicy;
import org.chromium.chrome.browser.customtabs.CustomTabsConnection;
import org.chromium.chrome.browser.customtabs.shadows.ShadowExternalNavigationDelegateImpl;
import org.chromium.chrome.browser.fullscreen.ChromeFullscreenManager;
import org.chromium.chrome.browser.init.ChromeBrowserInitializer;
import org.chromium.chrome.browser.init.StartupTabPreloader;
import org.chromium.chrome.browser.lifecycle.ActivityLifecycleDispatcher;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tab.TabObserverRegistrar;
import org.chromium.chrome.browser.tabmodel.AsyncTabParamsManager;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelectorImpl;
import org.chromium.chrome.browser.tabmodel.document.AsyncTabCreationParams;
import org.chromium.chrome.browser.toolbar.ToolbarManager;
import org.chromium.content_public.browser.LoadUrlParams;
import org.chromium.content_public.browser.NavigationController;
import org.chromium.content_public.browser.WebContents;
/**
* A TestRule that sets up the mocks and contains helper methods for JUnit/Robolectric tests scoped
* to the content layer of Custom Tabs code.
*/
public class CustomTabActivityContentTestEnvironment extends TestWatcher {
public static final String INITIAL_URL = "https://initial.com";
public static final String SPECULATED_URL = "https://speculated.com";
public static final String OTHER_URL = "https://other.com";
public final Intent intent = new Intent();
@Mock public CustomTabDelegateFactory customTabDelegateFactory;
@Mock public ChromeActivity activity;
@Mock public CustomTabsConnection connection;
@Mock public CustomTabIntentDataProvider intentDataProvider;
@Mock public TabObserverRegistrar tabObserverRegistrar;
@Mock public CompositorViewHolder compositorViewHolder;
@Mock public WarmupManager warmupManager;
@Mock public CustomTabTabPersistencePolicy tabPersistencePolicy;
@Mock public CustomTabActivityTabFactory tabFactory;
@Mock public CustomTabObserver customTabObserver;
@Mock public WebContentsFactory webContentsFactory;
@Mock public ActivityTabProvider activityTabProvider;
@Mock public ActivityLifecycleDispatcher lifecycleDispatcher;
@Mock public CustomTabsSessionToken session;
@Mock public TabModelSelectorImpl tabModelSelector;
@Mock public TabModel tabModel;
@Mock public CustomTabNavigationEventObserver navigationEventObserver;
@Mock public CloseButtonNavigator closeButtonNavigator;
@Mock public ToolbarManager toolbarManager;
@Mock public ChromeBrowserInitializer browserInitializer;
@Mock public ChromeFullscreenManager fullscreenManager;
@Mock
public StartupTabPreloader startupTabPreloader;
public final CustomTabActivityTabProvider tabProvider = new CustomTabActivityTabProvider();
@Captor public ArgumentCaptor<ActivityTabObserver> activityTabObserverCaptor;
// Captures the WebContents with which tabFromFactory is initialized
@Captor public ArgumentCaptor<WebContents> webContentsCaptor;
public Tab tabFromFactory;
@Override
protected void starting(Description description) {
RecordHistogram.setDisabledForTests(true);
RecordUserAction.setDisabledForTests(true);
MockitoAnnotations.initMocks(this);
tabFromFactory = prepareTab();
when(intentDataProvider.getIntent()).thenReturn(intent);
when(intentDataProvider.getSession()).thenReturn(session);
when(intentDataProvider.getUrlToLoad()).thenReturn(INITIAL_URL);
when(tabFactory.createTab()).thenReturn(tabFromFactory);
when(tabFactory.getTabModelSelector()).thenReturn(tabModelSelector);
when(tabModelSelector.getModel(anyBoolean())).thenReturn(tabModel);
when(connection.getSpeculatedUrl(any())).thenReturn(SPECULATED_URL);
when(browserInitializer.hasNativeInitializationCompleted()).thenReturn(true);
// Default setup is toolbarManager doesn't consume back press event.
when(toolbarManager.back()).thenReturn(null);
when(startupTabPreloader.takeTabIfMatchingOrDestroy(any(), anyInt())).thenReturn(null);
doNothing().when(activityTabProvider).addObserverAndTrigger(
activityTabObserverCaptor.capture());
doNothing()
.when(tabFromFactory)
.initialize(webContentsCaptor.capture(), any(), anyBoolean(), any(), anyBoolean());
}
@Override
protected void finished(Description description) {
RecordHistogram.setDisabledForTests(false);
RecordUserAction.setDisabledForTests(false);
AsyncTabParamsManager.getAsyncTabParams().clear();
ShadowExternalNavigationDelegateImpl.setWillChromeHandleIntent(false);
}
public CustomTabActivityTabController createTabController() {
return new CustomTabActivityTabController(activity,
()
-> customTabDelegateFactory,
connection, intentDataProvider, activityTabProvider, tabObserverRegistrar,
()
-> compositorViewHolder,
lifecycleDispatcher, warmupManager, tabPersistencePolicy, tabFactory,
()
-> customTabObserver,
webContentsFactory, navigationEventObserver, tabProvider, startupTabPreloader);
}
public CustomTabActivityNavigationController createNavigationController(
CustomTabActivityTabController tabController) {
return new CustomTabActivityNavigationController(tabController, tabProvider,
intentDataProvider, connection, () -> customTabObserver, closeButtonNavigator,
() -> toolbarManager, browserInitializer, activity, lifecycleDispatcher,
() -> fullscreenManager);
}
public CustomTabIntentHandler createIntentHandler(
CustomTabActivityNavigationController navigationController) {
CustomTabIntentHandlingStrategy strategy = new DefaultCustomTabIntentHandlingStrategy(
tabProvider, navigationController, navigationEventObserver,
() -> customTabObserver);
return new CustomTabIntentHandler(tabProvider,
intentDataProvider, strategy, (intent) -> false, activity);
}
public void warmUp() {
when(connection.hasWarmUpBeenFinished()).thenReturn(true);
}
public void changeTab(Tab newTab) {
when(activityTabProvider.get()).thenReturn(newTab);
for (ActivityTabObserver observer : activityTabObserverCaptor.getAllValues()) {
observer.onActivityTabChanged(newTab, false);
}
}
public void saveTab(Tab tab) {
when(activity.getSavedInstanceState()).thenReturn(new Bundle());
when(tabModelSelector.getCurrentTab()).thenReturn(tab);
}
// Dispatches lifecycle events up to native init.
public void reachNativeInit(CustomTabActivityTabController tabController) {
tabController.onPreInflationStartup();
tabController.onPostInflationStartup();
tabController.onFinishNativeInitialization();
}
public WebContents prepareTransferredWebcontents() {
int tabId = 1;
WebContents webContents = mock(WebContents.class);
AsyncTabParamsManager.add(tabId, new AsyncTabCreationParams(mock(LoadUrlParams.class),
webContents));
intent.putExtra(IntentHandler.EXTRA_TAB_ID, tabId);
return webContents;
}
public WebContents prepareSpareWebcontents() {
WebContents webContents = mock(WebContents.class);
when(warmupManager.takeSpareWebContents(
anyBoolean(), anyBoolean(), eq(WarmupManager.FOR_CCT)))
.thenReturn(webContents);
return webContents;
}
public Tab prepareHiddenTab() {
warmUp();
Tab hiddenTab = prepareTab();
when(connection.takeHiddenTab(any(), any(), any())).thenReturn(hiddenTab);
return hiddenTab;
}
public Tab prepareTab() {
Tab tab = mock(Tab.class);
when(tab.getView()).thenReturn(mock(View.class));
when(tab.getUserDataHost()).thenReturn(new UserDataHost());
WebContents webContents = mock(WebContents.class);
when(tab.getWebContents()).thenReturn(webContents);
NavigationController navigationController = mock(NavigationController.class);
when(webContents.getNavigationController()).thenReturn(navigationController);
return tab;
}
}