| // 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.contextmenu; |
| |
| import static org.mockito.Mockito.when; |
| |
| import static org.chromium.chrome.browser.contextmenu.ContextMenuCoordinator.ListItemType.CONTEXT_MENU_ITEM; |
| |
| import android.content.ClipData; |
| import android.content.ClipboardManager; |
| import android.content.Context; |
| import android.os.Looper; |
| import android.support.test.InstrumentationRegistry; |
| import android.view.KeyEvent; |
| |
| import androidx.test.filters.LargeTest; |
| import androidx.test.filters.MediumTest; |
| import androidx.test.filters.SmallTest; |
| |
| import org.hamcrest.Matchers; |
| import org.junit.After; |
| import org.junit.Assert; |
| import org.junit.Before; |
| import org.junit.Rule; |
| import org.junit.Test; |
| import org.junit.runner.RunWith; |
| import org.mockito.Mock; |
| import org.mockito.MockitoAnnotations; |
| |
| import org.chromium.base.Callback; |
| import org.chromium.base.metrics.RecordHistogram; |
| import org.chromium.base.test.util.CallbackHelper; |
| import org.chromium.base.test.util.CloseableOnMainThread; |
| import org.chromium.base.test.util.CommandLineFlags; |
| import org.chromium.base.test.util.Criteria; |
| import org.chromium.base.test.util.CriteriaHelper; |
| import org.chromium.base.test.util.Feature; |
| import org.chromium.base.test.util.FlakyTest; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.compositor.bottombar.ephemeraltab.EphemeralTabCoordinator; |
| import org.chromium.chrome.browser.compositor.layouts.LayoutManagerImpl; |
| import org.chromium.chrome.browser.download.DownloadTestRule; |
| import org.chromium.chrome.browser.firstrun.FirstRunStatus; |
| import org.chromium.chrome.browser.flags.ChromeFeatureList; |
| import org.chromium.chrome.browser.flags.ChromeSwitches; |
| import org.chromium.chrome.browser.share.LensUtils; |
| import org.chromium.chrome.browser.share.ShareDelegate; |
| import org.chromium.chrome.browser.share.ShareHelper; |
| import org.chromium.chrome.browser.tab.Tab; |
| import org.chromium.chrome.browser.tab.TabCreationState; |
| import org.chromium.chrome.browser.tab.state.CriticalPersistedTabData; |
| import org.chromium.chrome.browser.tabmodel.TabModel; |
| import org.chromium.chrome.browser.tabmodel.TabModelSelectorObserver; |
| import org.chromium.chrome.test.ChromeJUnit4ClassRunner; |
| import org.chromium.chrome.test.util.ChromeTabUtils; |
| import org.chromium.chrome.test.util.browser.Features; |
| import org.chromium.chrome.test.util.browser.contextmenu.ContextMenuUtils; |
| import org.chromium.components.embedder_support.contextmenu.ContextMenuParams; |
| import org.chromium.components.externalauth.ExternalAuthUtils; |
| import org.chromium.components.policy.test.annotations.Policies; |
| import org.chromium.content_public.browser.test.util.DOMUtils; |
| import org.chromium.content_public.browser.test.util.TestThreadUtils; |
| import org.chromium.content_public.browser.test.util.TestTouchUtils; |
| import org.chromium.net.test.EmbeddedTestServer; |
| import org.chromium.ui.base.Clipboard; |
| import org.chromium.ui.base.MenuSourceType; |
| import org.chromium.url.GURL; |
| |
| import java.io.IOException; |
| import java.util.ArrayList; |
| import java.util.Arrays; |
| import java.util.List; |
| import java.util.concurrent.TimeoutException; |
| import java.util.concurrent.atomic.AtomicReference; |
| |
| /** |
| * Instrumentation tests for the context menu. |
| */ |
| @RunWith(ChromeJUnit4ClassRunner.class) |
| // clang-format off |
| @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE, |
| ChromeSwitches.GOOGLE_BASE_URL + "=http://example.com/"}) |
| public class ContextMenuTest implements DownloadTestRule.CustomMainActivityStart { |
| |
| @Mock |
| private ContextMenuItemDelegate mItemDelegate; |
| @Mock |
| private ShareDelegate mShareDelegate; |
| |
| // clang-format on |
| @Rule |
| public DownloadTestRule mDownloadTestRule = new DownloadTestRule(this); |
| |
| private static final String TEST_PATH = |
| "/chrome/test/data/android/contextmenu/context_menu_test.html"; |
| |
| private EmbeddedTestServer mTestServer; |
| private String mTestUrl; |
| |
| private static final String FILENAME_GIF = "download.gif"; |
| private static final String FILENAME_PNG = "test_image.png"; |
| private static final String FILENAME_WEBM = "test.webm"; |
| private static final String TEST_GIF_IMAGE_FILE_EXTENSION = ".gif"; |
| private static final String TEST_JPG_IMAGE_FILE_EXTENSION = ".jpg"; |
| |
| // Test chip delegate that always returns valid chip render params. |
| private static final ChipDelegate FAKE_CHIP_DELEGATE = new ChipDelegate() { |
| @Override |
| public boolean isChipSupported() { |
| return true; |
| } |
| |
| @Override |
| public void getChipRenderParams(Callback<ChipRenderParams> callback) { |
| // Do nothing. |
| } |
| |
| @Override |
| public void onMenuClosed() { |
| // Do nothing. |
| } |
| |
| @Override |
| public boolean isValidChipRenderParams(ChipRenderParams chipRenderParams) { |
| return true; |
| } |
| }; |
| |
| private static final String[] TEST_FILES = |
| new String[] {FILENAME_GIF, FILENAME_PNG, FILENAME_WEBM}; |
| |
| @Before |
| public void setUp() { |
| TestThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(true)); |
| } |
| |
| @Override |
| public void customMainActivityStart() { |
| mTestServer = EmbeddedTestServer.createAndStartServer(InstrumentationRegistry.getContext()); |
| mTestUrl = mTestServer.getURL(TEST_PATH); |
| deleteTestFiles(); |
| mDownloadTestRule.startMainActivityWithURL(mTestUrl); |
| mDownloadTestRule.assertWaitForPageScaleFactorMatch(0.5f); |
| } |
| |
| @After |
| public void tearDown() { |
| mTestServer.stopAndDestroyServer(); |
| TestThreadUtils.runOnUiThreadBlocking(() -> FirstRunStatus.setFirstRunFlowComplete(false)); |
| deleteTestFiles(); |
| } |
| |
| @Test |
| @MediumTest |
| public void testCopyLinkURL() throws Throwable { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| // Allow DiskWrites temporarily in main thread to avoid |
| // violation during copying under emulator environment. |
| try (CloseableOnMainThread ignored = CloseableOnMainThread.StrictMode.allowDiskWrites()) { |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "testLink", |
| R.id.contextmenu_copy_link_address); |
| } |
| |
| assertStringContains("test_link.html", getClipboardText()); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| public void testCopyImageLinkCopiesLinkURL() throws Throwable { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| // Allow DiskWrites temporarily in main thread to avoid |
| // violation during copying under emulator environment. |
| try (CloseableOnMainThread ignored = CloseableOnMainThread.StrictMode.allowDiskWrites()) { |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "testImageLink", |
| R.id.contextmenu_copy_link_address); |
| } |
| |
| assertStringContains("test_link.html", getClipboardText()); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST}) |
| @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS}) |
| public void testLensShoppingAllowlistWithLensFeaturesDisabled() throws Throwable { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| LensUtils.setFakeImageUrlInShoppingAllowlistForTesting(true); |
| ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "testImage", |
| R.id.contextmenu_search_by_image); |
| |
| Assert.assertEquals(1, |
| RecordHistogram.getHistogramTotalCountForTesting( |
| "ContextMenu.SelectedOptionAndroid.Image")); |
| Assert.assertEquals(1, |
| RecordHistogram.getHistogramTotalCountForTesting( |
| "ContextMenu.SelectedOptionAndroid.Image.ShoppingDomain")); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| public void testLongPressOnImage() throws TimeoutException { |
| checkOpenImageInNewTab("testImage", "/chrome/test/data/android/contextmenu/test_image.png"); |
| } |
| |
| /** |
| * @MediumTest |
| * @Feature({"Browser"}) |
| * @CommandLineFlags.Add(ChromeSwitches.DISABLE_DOCUMENT_MODE) |
| */ |
| @Test |
| @FlakyTest(message = "http://crbug.com/606939") |
| public void testLongPressOnImageLink() throws TimeoutException { |
| checkOpenImageInNewTab( |
| "testImageLink", "/chrome/test/data/android/contextmenu/test_image.png"); |
| } |
| |
| private void checkOpenImageInNewTab(String domId, final String expectedPath) |
| throws TimeoutException { |
| final Tab activityTab = mDownloadTestRule.getActivity().getActivityTab(); |
| |
| final CallbackHelper newTabCallback = new CallbackHelper(); |
| final AtomicReference<Tab> newTab = new AtomicReference<>(); |
| mDownloadTestRule.getActivity().getTabModelSelector().addObserver( |
| new TabModelSelectorObserver() { |
| @Override |
| public void onNewTabCreated(Tab tab, @TabCreationState int creationState) { |
| if (CriticalPersistedTabData.from(tab).getParentId() |
| != activityTab.getId()) { |
| return; |
| } |
| newTab.set(tab); |
| newTabCallback.notifyCalled(); |
| |
| mDownloadTestRule.getActivity().getTabModelSelector().removeObserver(this); |
| } |
| }); |
| |
| int callbackCount = newTabCallback.getCallCount(); |
| |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), activityTab, domId, |
| R.id.contextmenu_open_image_in_new_tab); |
| |
| try { |
| newTabCallback.waitForCallback(callbackCount); |
| } catch (TimeoutException ex) { |
| Assert.fail("New tab never created from context menu press"); |
| } |
| |
| // Only check for the URL matching as the tab will not be fully created in svelte mode. |
| final String expectedUrl = mTestServer.getURL(expectedPath); |
| CriteriaHelper.pollUiThread( |
| () |
| -> Criteria.checkThat(ChromeTabUtils.getUrlStringOnUiThread(newTab.get()), |
| Matchers.is(expectedUrl))); |
| } |
| |
| @Test |
| @MediumTest |
| public void testDismissContextMenuOnBack() throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| Assert.assertNotNull("Context menu was not properly created", menuCoordinator); |
| CriteriaHelper.pollUiThread(() -> { |
| return !mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Context menu did not have window focus"); |
| |
| InstrumentationRegistry.getInstrumentation().sendKeyDownUpSync(KeyEvent.KEYCODE_BACK); |
| CriteriaHelper.pollUiThread(() -> { |
| return mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Activity did not regain focus."); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS}) |
| public void testLensTranslateChipNotShowingIfNotEnabled() throws Throwable { |
| // Required to avoid runtime error. |
| Looper.prepare(); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| // Needs to run on UI thread so creation happens on same thread as dismissal. |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| Assert.assertNull("Chip popoup was initialized.", |
| menuCoordinator.getCurrentPopupWindowForTesting()); |
| }); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS}) |
| public void testSelectLensTranslateChip() throws Throwable { |
| // Required to avoid runtime error. |
| Looper.prepare(); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| // Needs to run on UI thread so creation happens on same thread as dismissal. |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| menuCoordinator.simulateTranslateImageClassificationForTesting(); |
| Assert.assertTrue("Chip popoup not showing.", |
| menuCoordinator.getCurrentPopupWindowForTesting().isShowing()); |
| menuCoordinator.clickChipForTesting(); |
| }); |
| |
| Assert.assertEquals("Selection histogram pings not equal to one", 1, |
| RecordHistogram.getHistogramValueCountForTesting( |
| "ContextMenu.LensChip.Event", ContextMenuChipController.ChipEvent.CLICKED)); |
| Assert.assertFalse("Chip popoup still showing.", |
| menuCoordinator.getCurrentPopupWindowForTesting().isShowing()); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS}) |
| public void testLensChipNotShowingAfterMenuDismissed() throws Throwable { |
| // Required to avoid runtime error. |
| Looper.prepare(); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| // Dismiss context menu. |
| TestTouchUtils.singleClickView(InstrumentationRegistry.getInstrumentation(), tab.getView(), |
| tab.getView().getWidth() - 5, tab.getView().getHeight() - 5); |
| // Needs to run on UI thread so creation happens on same thread as dismissal. |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| ChipRenderParams chipRenderParams = |
| menuCoordinator.simulateImageClassificationForTesting(); |
| menuCoordinator.getChipRenderParamsCallbackForTesting(FAKE_CHIP_DELEGATE) |
| .bind(chipRenderParams) |
| .run(); |
| Assert.assertNull("Chip popoup was initialized.", |
| menuCoordinator.getCurrentPopupWindowForTesting()); |
| }); |
| } |
| |
| // Assert that focus is unchanged and that the chip popup does not block the dismissal of the |
| // context menu. |
| @Test |
| @MediumTest |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_TRANSLATE_WITH_GOOGLE_LENS}) |
| public void testDismissContextMenuOnClickLensTranslateChipEnabled() throws TimeoutException { |
| // Required to avoid runtime error. |
| Looper.prepare(); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| // Needs to run on UI thread so creation happens on same thread as dismissal. |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> menuCoordinator.simulateTranslateImageClassificationForTesting()); |
| Assert.assertNotNull("Context menu was not properly created", menuCoordinator); |
| CriteriaHelper.pollUiThread(() -> { |
| return !mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Context menu did not have window focus"); |
| |
| TestTouchUtils.singleClickView(InstrumentationRegistry.getInstrumentation(), tab.getView(), |
| tab.getView().getWidth() - 5, tab.getView().getHeight() - 5); |
| |
| CriteriaHelper.pollUiThread(() -> { |
| return mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Activity did not regain focus."); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP}) |
| public void testLensShoppingChipNotShowingIfNotEnabled() throws Throwable { |
| // Required to avoid runtime error. |
| Looper.prepare(); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| // Needs to run on UI thread so creation happens on same thread as dismissal. |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| Assert.assertNull("Chip popoup was initialized.", |
| menuCoordinator.getCurrentPopupWindowForTesting()); |
| }); |
| } |
| |
| @Test |
| @MediumTest |
| @Feature({"Browser"}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP}) |
| public void testSelectLensShoppingChip() throws Throwable { |
| // Required to avoid runtime error. |
| Looper.prepare(); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| // Needs to run on UI thread so creation happens on same thread as dismissal. |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| menuCoordinator.simulateShoppyImageClassificationForTesting(); |
| Assert.assertTrue("Chip popoup not showing.", |
| menuCoordinator.getCurrentPopupWindowForTesting().isShowing()); |
| menuCoordinator.clickChipForTesting(); |
| }); |
| |
| Assert.assertEquals("Selection histogram pings not equal to one", 1, |
| RecordHistogram.getHistogramValueCountForTesting( |
| "ContextMenu.LensChip.Event", ContextMenuChipController.ChipEvent.CLICKED)); |
| Assert.assertFalse("Chip popoup still showing.", |
| menuCoordinator.getCurrentPopupWindowForTesting().isShowing()); |
| } |
| |
| // Assert that focus is unchanged and that the chip popup does not block the dismissal of the |
| // context menu. |
| @Test |
| @MediumTest |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP}) |
| public void testDismissContextMenuOnClickShoppingLensChipEnabled() throws TimeoutException { |
| // Required to avoid runtime error. |
| Looper.prepare(); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| // Needs to run on UI thread so creation happens on same thread as dismissal. |
| TestThreadUtils.runOnUiThreadBlocking( |
| () -> menuCoordinator.simulateShoppyImageClassificationForTesting()); |
| Assert.assertNotNull("Context menu was not properly created", menuCoordinator); |
| CriteriaHelper.pollUiThread(() -> { |
| return !mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Context menu did not have window focus"); |
| |
| TestTouchUtils.singleClickView(InstrumentationRegistry.getInstrumentation(), tab.getView(), |
| tab.getView().getWidth() - 5, tab.getView().getHeight() - 5); |
| |
| CriteriaHelper.pollUiThread(() -> { |
| return mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Activity did not regain focus."); |
| } |
| |
| @Test |
| @MediumTest |
| public void testDismissContextMenuOnClick() throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menuCoordinator = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| Assert.assertNotNull("Context menu was not properly created", menuCoordinator); |
| CriteriaHelper.pollUiThread(() -> { |
| return !mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Context menu did not have window focus"); |
| |
| TestTouchUtils.singleClickView(InstrumentationRegistry.getInstrumentation(), tab.getView(), |
| tab.getView().getWidth() - 5, tab.getView().getHeight() - 5); |
| |
| CriteriaHelper.pollUiThread(() -> { |
| return mDownloadTestRule.getActivity().hasWindowFocus(); |
| }, "Activity did not regain focus."); |
| } |
| |
| @Test |
| @MediumTest |
| public void testCopyEmailAddress() throws Throwable { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| // Allow all thread policies temporarily in main thread to avoid |
| // DiskWrite and UnBufferedIo violations during copying under |
| // emulator environment. |
| try (CloseableOnMainThread ignored = |
| CloseableOnMainThread.StrictMode.allowAllThreadPolicies()) { |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "testEmail", R.id.contextmenu_copy); |
| } |
| |
| Assert.assertEquals("Copied email address is not correct", |
| "someone1@example.com,someone2@example.com", getClipboardText()); |
| } |
| |
| @Test |
| @MediumTest |
| public void testCopyTelNumber() throws Throwable { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| // Allow DiskWrites temporarily in main thread to avoid |
| // violation during copying under emulator environment. |
| try (CloseableOnMainThread ignored = CloseableOnMainThread.StrictMode.allowDiskWrites()) { |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "testTel", R.id.contextmenu_copy); |
| } |
| |
| Assert.assertEquals("Copied tel number is not correct", "10000000000", getClipboardText()); |
| } |
| |
| @Test |
| @LargeTest |
| public void testSaveDataUrl() throws TimeoutException, SecurityException, IOException { |
| saveMediaFromContextMenu("dataUrlIcon", R.id.contextmenu_save_image, FILENAME_GIF); |
| } |
| |
| @Test |
| @LargeTest |
| public void testSaveImage() throws TimeoutException, SecurityException, IOException { |
| saveMediaFromContextMenu("testImage", R.id.contextmenu_save_image, FILENAME_PNG); |
| } |
| |
| @Test |
| @LargeTest |
| public void testSaveVideo() throws TimeoutException, SecurityException, IOException { |
| saveMediaFromContextMenu("videoDOMElement", R.id.contextmenu_save_video, FILENAME_WEBM); |
| } |
| |
| /** |
| * Opens a link and image in new tabs and verifies the order of the tabs. Also verifies that |
| * the parent page remains in front after opening links in new tabs. |
| * |
| * This test only applies in tabbed mode. In document mode, Android handles the ordering of the |
| * tabs. |
| */ |
| @Test |
| @LargeTest |
| public void testOpenLinksInNewTabsAndVerifyTabIndexOrdering() throws TimeoutException { |
| TabModel tabModel = mDownloadTestRule.getActivity().getCurrentTabModel(); |
| int numOpenedTabs = tabModel.getCount(); |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "testLink", R.id.contextmenu_open_in_new_tab); |
| InstrumentationRegistry.getInstrumentation().waitForIdleSync(); |
| int indexOfLinkPage = numOpenedTabs; |
| numOpenedTabs += 1; |
| Assert.assertEquals( |
| "Number of open tabs does not match", numOpenedTabs, tabModel.getCount()); |
| |
| // Wait for any new tab animation to finish if we're being driven by the compositor. |
| final LayoutManagerImpl layoutDriver = |
| mDownloadTestRule.getActivity().getCompositorViewHolder().getLayoutManager(); |
| CriteriaHelper.pollUiThread(() -> { |
| return layoutDriver.getActiveLayout().shouldDisplayContentOverlay(); |
| }, "Background tab animation not finished."); |
| |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "testLink2", |
| R.id.contextmenu_open_in_new_tab); |
| InstrumentationRegistry.getInstrumentation().waitForIdleSync(); |
| int indexOfLinkPage2 = numOpenedTabs; |
| numOpenedTabs += 1; |
| Assert.assertEquals( |
| "Number of open tabs does not match", numOpenedTabs, tabModel.getCount()); |
| |
| // Verify the Url is still the same of Parent page. |
| Assert.assertEquals(mTestUrl, |
| ChromeTabUtils.getUrlStringOnUiThread( |
| mDownloadTestRule.getActivity().getActivityTab())); |
| |
| // Verify that the background tabs were opened in the expected order. |
| String newTabUrl = |
| mTestServer.getURL("/chrome/test/data/android/contextmenu/test_link.html"); |
| Assert.assertEquals(newTabUrl, |
| ChromeTabUtils.getUrlStringOnUiThread(tabModel.getTabAt(indexOfLinkPage))); |
| |
| String imageUrl = |
| mTestServer.getURL("/chrome/test/data/android/contextmenu/test_link2.html"); |
| Assert.assertEquals(imageUrl, |
| ChromeTabUtils.getUrlStringOnUiThread(tabModel.getTabAt(indexOfLinkPage2))); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| public void testContextMenuRetrievesLinkOptions() throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testLink"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_open_in_new_tab, |
| R.id.contextmenu_open_in_incognito_tab, R.id.contextmenu_save_link_as, |
| R.id.contextmenu_copy_link_text, R.id.contextmenu_copy_link_address, |
| R.id.contextmenu_share_link}; |
| expectedItems = addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, |
| new Integer[] {R.id.contextmenu_open_in_ephemeral_tab}); |
| expectedItems = addItemsIf(ChromeFeatureList.isEnabled(ChromeFeatureList.READ_LATER), |
| expectedItems, new Integer[] {R.id.contextmenu_read_later}); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS}) |
| public void testContextMenuRetrievesImageOptions() throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_by_image, |
| R.id.contextmenu_share_image, R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST}) |
| public void testContextMenuRetrievesImageOptionsWithLensShoppingAllowlist() |
| throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_by_image, |
| R.id.contextmenu_share_image, R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| public void testContextMenuRetrievesImageOptionsLensEnabled() throws TimeoutException { |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_with_google_lens, |
| R.id.contextmenu_share_image, R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP}) |
| @Features.EnableFeatures({ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST, |
| ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS}) |
| public void |
| testContextMenuLensEnabledShopImageWithGoogleLens() throws TimeoutException { |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| LensUtils.setFakeImageUrlInShoppingAllowlistForTesting(true); |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image, |
| R.id.contextmenu_shop_image_with_google_lens, R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_SHOP_WITH_GOOGLE_LENS}) |
| @CommandLineFlags. |
| Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_ENABLE_LENS_SHOPPING_ALLOWLIST |
| + "<FakeStudyName", |
| "force-fieldtrials=FakeStudyName/Enabled", |
| "force-fieldtrial-params=FakeStudyName.Enabled:shoppingUrlPatterns/^shopping-site.*"}) |
| public void |
| testContextMenuLensDisableShopWithGoogleLensForShoppingUrl() throws TimeoutException { |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_with_google_lens, |
| R.id.contextmenu_share_image, R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @Policies.Add({ @Policies.Item(key = "DefaultSearchProviderEnabled", string = "false") }) |
| public void testContextMenuRetrievesImageOptions_NoDefaultSearchEngine() |
| throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image, |
| R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @Policies.Add({ @Policies.Item(key = "DefaultSearchProviderEnabled", string = "false") }) |
| public void testContextMenuRetrievesImageOptions_NoDefaultSearchEngineLensEnabled() |
| throws TimeoutException { |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| |
| // Search with Google Lens is only supported when Google is the default search provider. |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image, |
| R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @Features.DisableFeatures({ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS}) |
| public void testContextMenuRetrievesImageLinkOptions() throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImageLink"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_open_in_new_tab, |
| R.id.contextmenu_open_in_incognito_tab, R.id.contextmenu_copy_link_address, |
| R.id.contextmenu_save_link_as, R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_by_image, |
| R.id.contextmenu_share_image, R.id.contextmenu_share_link, |
| R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_in_ephemeral_tab, |
| R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| public void testContextMenuRetrievesImageLinkOptionsSearchLensEnabled() |
| throws TimeoutException { |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImageLink"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_open_in_new_tab, |
| R.id.contextmenu_open_in_incognito_tab, R.id.contextmenu_copy_link_address, |
| R.id.contextmenu_save_link_as, R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_search_with_google_lens, |
| R.id.contextmenu_share_image, R.id.contextmenu_share_link, |
| R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_in_ephemeral_tab, |
| R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @CommandLineFlags. |
| Add({"enable-features=" + ChromeFeatureList.CONTEXT_MENU_GOOGLE_LENS_CHIP + "<FakeStudyName", |
| "force-fieldtrials=FakeStudyName/Enabled", |
| "force-fieldtrial-params=FakeStudyName.Enabled:orderShareImageBeforeLens/true"}) |
| public void |
| testContextMenuShareImageStillAddedWhenReordered() throws TimeoutException { |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImageLink"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_open_in_new_tab, |
| R.id.contextmenu_open_in_incognito_tab, R.id.contextmenu_copy_link_address, |
| R.id.contextmenu_save_link_as, R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image, |
| R.id.contextmenu_search_with_google_lens, R.id.contextmenu_share_link, |
| R.id.contextmenu_copy_image}; |
| Integer[] featureItems = {R.id.contextmenu_open_in_ephemeral_tab, |
| R.id.contextmenu_open_image_in_ephemeral_tab}; |
| expectedItems = |
| addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, featureItems); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| public void testContextMenuRetrievesVideoOptions() throws TimeoutException { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| DOMUtils.clickNode( |
| mDownloadTestRule.getActivity().getCurrentWebContents(), "videoDOMElement"); |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "videoDOMElement"); |
| |
| Integer[] expectedItems = {R.id.contextmenu_save_video}; |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @CommandLineFlags.Add({"enable-features=" |
| + ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName", |
| "force-fieldtrials=FakeStudyName/Enabled"}) |
| public void |
| testSearchWithGoogleLensMenuItemName() throws Throwable { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image, |
| R.id.contextmenu_copy_image, R.id.contextmenu_search_with_google_lens}; |
| expectedItems = addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, |
| new Integer[] {R.id.contextmenu_open_image_in_ephemeral_tab}); |
| String title = getMenuTitleFromItem(menu, R.id.contextmenu_search_with_google_lens); |
| Assert.assertTrue("Context menu item name should be \'Search with Google Lens\'.", |
| title.startsWith("Search with Google Lens")); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| @CommandLineFlags.Add({"enable-features=" |
| + ChromeFeatureList.CONTEXT_MENU_SEARCH_WITH_GOOGLE_LENS + "<FakeStudyName", |
| "force-fieldtrials=FakeStudyName/Enabled", |
| "force-fieldtrial-params=FakeStudyName.Enabled:useSearchImageWithGoogleLensItemName/true"}) |
| public void |
| testSearchImageWithGoogleLensMenuItemName() throws Throwable { |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| |
| LensUtils.setFakePassableLensEnvironmentForTesting(true); |
| ShareHelper.setIgnoreActivityNotFoundExceptionForTesting(true); |
| hardcodeTestImageForSharing(TEST_JPG_IMAGE_FILE_EXTENSION); |
| |
| ContextMenuCoordinator menu = ContextMenuUtils.openContextMenu(tab, "testImage"); |
| Integer[] expectedItems = {R.id.contextmenu_save_image, |
| R.id.contextmenu_open_image_in_new_tab, R.id.contextmenu_share_image, |
| R.id.contextmenu_copy_image, R.id.contextmenu_search_with_google_lens}; |
| expectedItems = addItemsIf(EphemeralTabCoordinator.isSupported(), expectedItems, |
| new Integer[] {R.id.contextmenu_open_image_in_ephemeral_tab}); |
| String title = getMenuTitleFromItem(menu, R.id.contextmenu_search_with_google_lens); |
| Assert.assertTrue("Context menu item name should be \'Search image with Google Lens\'.", |
| title.startsWith("Search image with Google Lens")); |
| assertMenuItemsAreEqual(menu, expectedItems); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| public void testCopyImage() throws Throwable { |
| // Clear the clipboard. |
| Clipboard.getInstance().setText(""); |
| |
| hardcodeTestImageForSharing(TEST_GIF_IMAGE_FILE_EXTENSION); |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| // Allow all thread policies temporarily in main thread to avoid |
| // DiskWrite and UnBufferedIo violations during copying under |
| // emulator environment. |
| try (CloseableOnMainThread ignored = |
| CloseableOnMainThread.StrictMode.allowAllThreadPolicies()) { |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, "dataUrlIcon", |
| R.id.contextmenu_copy_image); |
| } |
| |
| CriteriaHelper.pollUiThread(() -> { |
| Criteria.checkThat(Clipboard.getInstance().getImageUri(), Matchers.notNullValue()); |
| }); |
| |
| String imageUriString = Clipboard.getInstance().getImageUri().toString(); |
| |
| Assert.assertTrue("Image content prefix is not correct", |
| imageUriString.startsWith( |
| "content://org.chromium.chrome.tests.FileProvider/images/screenshot/")); |
| Assert.assertTrue("Image extension is not correct", |
| imageUriString.endsWith(TEST_GIF_IMAGE_FILE_EXTENSION)); |
| |
| // Clean up the clipboard. |
| Clipboard.getInstance().setText(""); |
| } |
| |
| @Test |
| @SmallTest |
| @Feature({"Browser", "ContextMenu"}) |
| public void testContextMenuOpenedFromHighlight() { |
| MockitoAnnotations.initMocks(this); |
| when(mItemDelegate.isIncognito()).thenReturn(false); |
| when(mItemDelegate.getPageTitle()).thenReturn(""); |
| |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| ContextMenuHelper contextMenuHelper = |
| ContextMenuHelper.createForTesting(0, tab.getWebContents()); |
| ContextMenuParams params = new ContextMenuParams(0, 0, new GURL("http://example.com/"), |
| GURL.emptyGURL(), "", GURL.emptyGURL(), GURL.emptyGURL(), "", null, false, 0, 0, |
| MenuSourceType.MENU_SOURCE_TOUCH, /*getOpenedFromHighlight*/ true); |
| ContextMenuPopulatorFactory populatorFactory = new ChromeContextMenuPopulatorFactory( |
| mItemDelegate, |
| () |
| -> mShareDelegate, |
| ChromeContextMenuPopulator.ContextMenuMode.NORMAL, ExternalAuthUtils.getInstance()); |
| Integer[] expectedItems = {R.id.contextmenu_share_highlight, |
| R.id.contextmenu_remove_highlight, R.id.contextmenu_learn_more}; |
| TestThreadUtils.runOnUiThreadBlocking(() -> { |
| ContextMenuHelper.setMenuShownCallbackForTests((coordinator) -> { |
| assertMenuItemsAreEqual(coordinator, expectedItems); |
| Assert.assertEquals( |
| 1, RecordHistogram.getHistogramTotalCountForTesting("ContextMenu.Shown")); |
| Assert.assertEquals(1, |
| RecordHistogram.getHistogramTotalCountForTesting( |
| "ContextMenu.Shown.SharedHighlightingInteraction")); |
| ContextMenuHelper.setMenuShownCallbackForTests(null); |
| }); |
| contextMenuHelper.showContextMenuForTesting( |
| populatorFactory, params, null, tab.getView(), 0); |
| }); |
| } |
| |
| // TODO(benwgold): Add more test coverage for histogram recording of other context menu types. |
| |
| /** |
| * Takes all the visible items on the menu and compares them to a the list of expected items. |
| * @param menu A context menu that is displaying visible items. |
| * @param expectedItems A list of items that is expected to appear within a context menu. The |
| * list does not need to be ordered. |
| */ |
| private void assertMenuItemsAreEqual(ContextMenuCoordinator menu, Integer... expectedItems) { |
| List<Integer> actualItems = new ArrayList<>(); |
| for (int i = 0; i < menu.getCount(); i++) { |
| if (menu.getItem(i).type >= CONTEXT_MENU_ITEM) { |
| actualItems.add(menu.getItem(i).model.get(ContextMenuItemProperties.MENU_ID)); |
| } |
| } |
| |
| Assert.assertThat("Populated menu items were:" + getMenuTitles(menu), actualItems, |
| Matchers.containsInAnyOrder(expectedItems)); |
| } |
| |
| private String getMenuTitles(ContextMenuCoordinator menu) { |
| StringBuilder items = new StringBuilder(); |
| for (int i = 0; i < menu.getCount(); i++) { |
| if (menu.getItem(i).type >= CONTEXT_MENU_ITEM) { |
| items.append("\n").append( |
| menu.getItem(i).model.get(ContextMenuItemProperties.TEXT)); |
| } |
| } |
| return items.toString(); |
| } |
| |
| private String getMenuTitleFromItem(ContextMenuCoordinator menu, int itemId) { |
| StringBuilder itemName = new StringBuilder(); |
| for (int i = 0; i < menu.getCount(); i++) { |
| if (menu.getItem(i).type >= CONTEXT_MENU_ITEM) { |
| if (menu.getItem(i).model.get(ContextMenuItemProperties.MENU_ID) == itemId) { |
| itemName.append(menu.getItem(i).model.get(ContextMenuItemProperties.TEXT)); |
| return itemName.toString(); |
| } |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Adds items to the baseItems if the given condition is true. |
| * @param condition The condition to check for whether to add items or not. |
| * @param baseItems The base list of items to add to. |
| * @param additionalItems The additional items to add. |
| * @return An array of items that has the additional items added if the condition is true. |
| */ |
| private Integer[] addItemsIf( |
| boolean condition, Integer[] baseItems, Integer[] additionalItems) { |
| List<Integer> variableItems = new ArrayList<>(); |
| variableItems.addAll(Arrays.asList(baseItems)); |
| if (condition) { |
| for (int i = 0; i < additionalItems.length; i++) variableItems.add(additionalItems[i]); |
| } |
| return variableItems.toArray(baseItems); |
| } |
| |
| private void saveMediaFromContextMenu(String mediaDOMElement, int saveMenuID, |
| String expectedFilename) throws TimeoutException, SecurityException, IOException { |
| // Select "save [image/video]" in that menu. |
| Tab tab = mDownloadTestRule.getActivity().getActivityTab(); |
| int callCount = mDownloadTestRule.getChromeDownloadCallCount(); |
| ContextMenuUtils.selectContextMenuItem(InstrumentationRegistry.getInstrumentation(), |
| mDownloadTestRule.getActivity(), tab, mediaDOMElement, saveMenuID); |
| |
| // Wait for the download to complete and see if we got the right file |
| Assert.assertTrue(mDownloadTestRule.waitForChromeDownloadToFinish(callCount)); |
| mDownloadTestRule.checkLastDownload(expectedFilename); |
| } |
| |
| private String getClipboardText() throws Throwable { |
| final AtomicReference<String> clipboardTextRef = new AtomicReference<>(); |
| mDownloadTestRule.runOnUiThread(() -> { |
| ClipboardManager clipMgr = |
| (ClipboardManager) mDownloadTestRule.getActivity().getSystemService( |
| Context.CLIPBOARD_SERVICE); |
| ClipData clipData = clipMgr.getPrimaryClip(); |
| Assert.assertNotNull("Primary clip is null", clipData); |
| Assert.assertTrue("Primary clip contains no items.", clipData.getItemCount() > 0); |
| clipboardTextRef.set(clipData.getItemAt(0).getText().toString()); |
| }); |
| return clipboardTextRef.get(); |
| } |
| |
| /** |
| * Hardcode image bytes to non-null arbitrary data. |
| * @param extension Image file extension. |
| */ |
| private void hardcodeTestImageForSharing(String extension) { |
| // This string just needs to be not empty in order for the code to accept it as valid |
| // image data and generate the temp file for sharing. In the future we could explore |
| // transcoding the actual test image from png to jpeg to make the test more realistic. |
| String mockImageData = "randomdata"; |
| byte[] mockImageByteArray = mockImageData.getBytes(); |
| // See function javadoc for more context. |
| ContextMenuNativeDelegateImpl.setHardcodedImageBytesForTesting( |
| mockImageByteArray, extension); |
| } |
| |
| private void assertStringContains(String subString, String superString) { |
| Assert.assertTrue("String '" + superString + "' does not contain '" + subString + "'", |
| superString.contains(subString)); |
| } |
| |
| /** |
| * Makes sure there are no files with names identical to the ones this test uses in the |
| * downloads directory |
| */ |
| private void deleteTestFiles() { |
| mDownloadTestRule.deleteFilesInDownloadDirectory(TEST_FILES); |
| } |
| } |