// 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.tab_group_sync;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.annotation.Config;
import org.chromium.base.Token;
import org.chromium.base.test.BaseRobolectricTestRunner;
import org.chromium.chrome.browser.tab.Tab;
import org.chromium.chrome.browser.tabmodel.TabModel;
import org.chromium.chrome.browser.tabmodel.TabModelSelector;
import org.chromium.components.embedder_support.util.UrlConstants;
import org.chromium.components.tab_group_sync.LocalTabGroupId;
import org.chromium.components.tab_group_sync.TabGroupSyncService;
import org.chromium.content_public.browser.NavigationHandle;
import org.chromium.ui.base.PageTransition;
import org.chromium.url.GURL;
import java.util.ArrayList;
import java.util.List;
/** Unit tests for the {@link NavigationObserver}. */
@RunWith(BaseRobolectricTestRunner.class)
@Config(manifest = Config.NONE)
public class NavigationObserverUnitTest {
private static final int TAB_ID_1 = 5;
private static final int TAB_ID_2 = 6;
private static final Token TOKEN_1 = new Token(2, 3);
private static final Token TOKEN_2 = new Token(4, 5);
private static final LocalTabGroupId LOCAL_TAB_GROUP_ID_1 = new LocalTabGroupId(TOKEN_1);
private static final GURL CHROME_HISTORY_URL = new GURL("chrome://history");
@Rule public MockitoRule mMockitoRule = MockitoJUnit.rule();
@Mock private Tab mTab;
@Mock private TabGroupSyncService mTabGroupSyncService;
private NavigationObserver mNavigationObserver;
private NavigationTracker mNavigationTracker;
@Mock private TabModelSelector mTabModelSelector;
private List<TabModel> mTabModels = new ArrayList<>();
private final GURL mTestUrl = new GURL("https://url1.com");
private final GURL mTestUrl2 = new GURL("https://url2.com");
private final String mTestTitle = new String("Some title");
@Before
public void setUp() {
mTabModels = new ArrayList<>();
doReturn(mTabModels).when(mTabModelSelector).getModels();
mNavigationTracker = new NavigationTracker();
mNavigationObserver =
new NavigationObserver(mTabModelSelector, mTabGroupSyncService, mNavigationTracker);
}
private void mockTab(
int tabId,
Token tabGroupId,
String title,
GURL url,
boolean isIncognito,
boolean isGrouped) {
when(mTab.isIncognito()).thenReturn(isIncognito);
when(mTab.getId()).thenReturn(tabId);
when((mTab.getTabGroupId())).thenReturn(tabGroupId);
when(mTab.getTitle()).thenReturn(title);
when(mTab.getUrl()).thenReturn(url);
}
private void simulateNavigation(int transition) {
simulateNavigation(transition, /* isSaveableNavigation= */ true);
}
private void simulateNavigation(int transition, boolean isSaveableNavigation) {
NavigationHandle navigation =
NavigationHandle.createForTesting(
new GURL("unused"),
/* isInPrimaryMainFrame= */ true,
/*isSameDocument*/ false,
/*isRendererInitiated*/ false,
transition,
/* hasUserGesture= */ false,
/* isReload= */ false,
isSaveableNavigation);
mNavigationObserver.onDidFinishNavigationInPrimaryMainFrame(mTab, navigation);
}
@Test
public void testNavigationObserverBasic() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
mTestUrl,
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verify(mTabGroupSyncService)
.updateTab(
eq(LOCAL_TAB_GROUP_ID_1),
eq(TAB_ID_1),
eq(mTestTitle),
eq(mTestUrl),
eq(-1));
}
@Test
public void testMultipleNavigations() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
mTestUrl,
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verify(mTabGroupSyncService)
.updateTab(
eq(LOCAL_TAB_GROUP_ID_1),
eq(TAB_ID_1),
eq(mTestTitle),
eq(mTestUrl),
eq(-1));
mockTab(
TAB_ID_2,
TOKEN_2,
mTestTitle,
mTestUrl2,
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verify(mTabGroupSyncService)
.updateTab(
eq(new LocalTabGroupId(TOKEN_2)),
eq(TAB_ID_2),
eq(mTestTitle),
eq(mTestUrl2),
eq(-1));
}
@Test
public void testDisableObserver() {
mNavigationObserver.enableObservers(false);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
mTestUrl,
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verifyNoInteractions(mTabGroupSyncService);
}
@Test
public void testIncognito() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
mTestUrl,
/* isIncognito= */ true,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verifyNoInteractions(mTabGroupSyncService);
}
@Test
public void testChromeInternalUrl() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
CHROME_HISTORY_URL,
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verify(mTabGroupSyncService)
.updateTab(
eq(LOCAL_TAB_GROUP_ID_1),
eq(TAB_ID_1),
eq(TabGroupSyncUtils.UNSAVEABLE_TAB_TITLE),
eq(TabGroupSyncUtils.UNSAVEABLE_URL_OVERRIDE),
eq(-1));
}
@Test
public void testNotHttpOrHttpsOrChromeUrl() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
new GURL("ftp://someurl.com"),
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verify(mTabGroupSyncService)
.updateTab(
eq(LOCAL_TAB_GROUP_ID_1),
eq(TAB_ID_1),
eq(TabGroupSyncUtils.UNSAVEABLE_TAB_TITLE),
eq(TabGroupSyncUtils.UNSAVEABLE_URL_OVERRIDE),
eq(-1));
}
@Test
public void testNtpUrl() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
new GURL(UrlConstants.NTP_URL),
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK);
verify(mTabGroupSyncService)
.updateTab(
eq(LOCAL_TAB_GROUP_ID_1),
eq(TAB_ID_1),
eq(TabGroupSyncUtils.NEW_TAB_TITLE),
eq(TabGroupSyncUtils.NTP_URL),
eq(-1));
}
@Test
public void testSyncInitiatedNavigation() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
mTestUrl,
/* isIncognito= */ false,
/* isGrouped= */ true);
NavigationHandle navigation =
NavigationHandle.createForTesting(
mTestUrl,
/* isInPrimaryMainFrame= */ true,
/*isSameDocument*/ false,
/*isRendererInitiated*/ false,
PageTransition.LINK,
/* hasUserGesture= */ false,
/* isReload= */ false);
mNavigationTracker.setNavigationWasFromSync(navigation.getUserDataHost());
mNavigationObserver.onDidFinishNavigationInPrimaryMainFrame(mTab, navigation);
verifyNoInteractions(mTabGroupSyncService);
}
@Test
public void testNotSaveableNavigation() {
mNavigationObserver.enableObservers(true);
mockTab(
TAB_ID_1,
TOKEN_1,
mTestTitle,
mTestUrl,
/* isIncognito= */ false,
/* isGrouped= */ true);
simulateNavigation(PageTransition.LINK, false);
verifyNoInteractions(mTabGroupSyncService);
}
}