1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 package org.chromium.chrome.browser.app.appmenu;
6 
7 import android.content.pm.ActivityInfo;
8 import android.support.test.InstrumentationRegistry;
9 import android.view.KeyEvent;
10 import android.view.Menu;
11 import android.view.MenuItem;
12 import android.view.View;
13 import android.widget.LinearLayout;
14 import android.widget.ListView;
15 import android.widget.TextView;
16 
17 import androidx.test.filters.SmallTest;
18 
19 import org.hamcrest.Matchers;
20 import org.junit.Assert;
21 import org.junit.Before;
22 import org.junit.Rule;
23 import org.junit.Test;
24 import org.junit.runner.RunWith;
25 
26 import org.chromium.base.Callback;
27 import org.chromium.base.task.PostTask;
28 import org.chromium.base.test.util.CallbackHelper;
29 import org.chromium.base.test.util.CommandLineFlags;
30 import org.chromium.base.test.util.Criteria;
31 import org.chromium.base.test.util.CriteriaHelper;
32 import org.chromium.base.test.util.DisableIf;
33 import org.chromium.base.test.util.DisabledTest;
34 import org.chromium.base.test.util.Feature;
35 import org.chromium.base.test.util.Restriction;
36 import org.chromium.base.test.util.UrlUtils;
37 import org.chromium.chrome.R;
38 import org.chromium.chrome.browser.ChromeTabbedActivity;
39 import org.chromium.chrome.browser.compositor.layouts.EmptyOverviewModeObserver;
40 import org.chromium.chrome.browser.compositor.layouts.OverviewModeBehavior;
41 import org.chromium.chrome.browser.flags.ChromeFeatureList;
42 import org.chromium.chrome.browser.flags.ChromeSwitches;
43 import org.chromium.chrome.browser.share.ShareUtils;
44 import org.chromium.chrome.browser.tab.Tab;
45 import org.chromium.chrome.browser.ui.appmenu.AppMenuHandler;
46 import org.chromium.chrome.browser.ui.appmenu.AppMenuTestSupport;
47 import org.chromium.chrome.test.ChromeJUnit4ClassRunner;
48 import org.chromium.chrome.test.ChromeTabbedActivityTestRule;
49 import org.chromium.chrome.test.util.ChromeRenderTestRule;
50 import org.chromium.chrome.test.util.ChromeTabUtils;
51 import org.chromium.chrome.test.util.browser.Features.DisableFeatures;
52 import org.chromium.chrome.test.util.browser.Features.EnableFeatures;
53 import org.chromium.content_public.browser.UiThreadTaskTraits;
54 import org.chromium.content_public.browser.test.util.TestThreadUtils;
55 import org.chromium.content_public.browser.test.util.TestTouchUtils;
56 import org.chromium.ui.modaldialog.ModalDialogProperties;
57 import org.chromium.ui.modelutil.PropertyModel;
58 import org.chromium.ui.test.util.UiDisableIf;
59 import org.chromium.ui.test.util.UiRestriction;
60 
61 import java.io.IOException;
62 import java.util.concurrent.TimeoutException;
63 
64 /**
65  * Tests tabbed mode app menu popup.
66  */
67 @RunWith(ChromeJUnit4ClassRunner.class)
68 @CommandLineFlags.Add({ChromeSwitches.DISABLE_FIRST_RUN_EXPERIENCE})
69 public class TabbedAppMenuTest {
70     @Rule
71     public ChromeTabbedActivityTestRule mActivityTestRule = new ChromeTabbedActivityTestRule();
72     @Rule
73     public ChromeRenderTestRule mRenderTestRule =
74             ChromeRenderTestRule.Builder.withPublicCorpus().build();
75 
76     private static final String TEST_URL = UrlUtils.encodeHtmlDataUri("<html>foo</html>");
77     private static final String TEST_URL2 = UrlUtils.encodeHtmlDataUri("<html>bar</html>");
78 
79     private AppMenuHandler mAppMenuHandler;
80 
81     int mLastSelectedItemId = -1;
82     private Callback<MenuItem> mItemSelectedCallback =
83             (item) -> mLastSelectedItemId = item.getItemId();
84 
85     @Before
setUp()86     public void setUp() {
87         // We need list selection; ensure we are not in touch mode.
88         InstrumentationRegistry.getInstrumentation().setInTouchMode(false);
89 
90         mActivityTestRule.startMainActivityWithURL(TEST_URL);
91 
92         AppMenuTestSupport.overrideOnOptionItemSelectedListener(
93                 mActivityTestRule.getAppMenuCoordinator(), mItemSelectedCallback);
94         mAppMenuHandler = mActivityTestRule.getAppMenuCoordinator().getAppMenuHandler();
95 
96         showAppMenuAndAssertMenuShown();
97 
98         PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> getListView().setSelection(0));
99         CriteriaHelper.pollInstrumentationThread(
100                 () -> Criteria.checkThat(getCurrentFocusedRow(), Matchers.is(0)));
101         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
102     }
103 
104     /**
105      * Verify opening a new tab from the menu.
106      */
107     @Test
108     @SmallTest
109     @Feature({"Browser", "Main"})
testMenuNewTab()110     public void testMenuNewTab() {
111         final int tabCountBefore = mActivityTestRule.getActivity().getCurrentTabModel().getCount();
112         ChromeTabUtils.newTabFromMenu(InstrumentationRegistry.getInstrumentation(),
113                 (ChromeTabbedActivity) mActivityTestRule.getActivity());
114         final int tabCountAfter = mActivityTestRule.getActivity().getCurrentTabModel().getCount();
115         Assert.assertTrue("Expected: " + (tabCountBefore + 1) + " Got: " + tabCountAfter,
116                 tabCountBefore + 1 == tabCountAfter);
117     }
118 
119     /**
120      * Test bounds when accessing the menu through the keyboard.
121      * Make sure that the menu stays open when trying to move past the first and last items.
122      */
123     @Test
124     @SmallTest
125     @Feature({"Browser", "Main"})
testKeyboardMenuBoundaries()126     public void testKeyboardMenuBoundaries() {
127         moveToBoundary(false, true);
128         Assert.assertEquals(getCount() - 1, getCurrentFocusedRow());
129         moveToBoundary(true, true);
130         Assert.assertEquals(0, getCurrentFocusedRow());
131         moveToBoundary(false, true);
132         Assert.assertEquals(getCount() - 1, getCurrentFocusedRow());
133     }
134 
135     /**
136      * Test that typing ENTER immediately opening the menu works.
137      */
138     @Test
139     @SmallTest
140     @Feature({"Browser", "Main"})
testKeyboardMenuEnterOnOpen()141     public void testKeyboardMenuEnterOnOpen() {
142         hitEnterAndAssertAppMenuDismissed();
143     }
144 
145     /**
146      * Test that hitting ENTER past the top item doesn't crash Chrome.
147      */
148     @Test
149     @SmallTest
150     @Feature({"Browser", "Main"})
testKeyboardEnterAfterMovePastTopItem()151     public void testKeyboardEnterAfterMovePastTopItem() {
152         moveToBoundary(true, true);
153         Assert.assertEquals(0, getCurrentFocusedRow());
154         hitEnterAndAssertAppMenuDismissed();
155     }
156 
157     /**
158      * Test that hitting ENTER past the bottom item doesn't crash Chrome.
159      * Catches regressions for http://crbug.com/181067
160      */
161     @Test
162     @SmallTest
163     @Feature({"Browser", "Main"})
testKeyboardEnterAfterMovePastBottomItem()164     public void testKeyboardEnterAfterMovePastBottomItem() {
165         moveToBoundary(false, true);
166         Assert.assertEquals(getCount() - 1, getCurrentFocusedRow());
167         hitEnterAndAssertAppMenuDismissed();
168     }
169 
170     /**
171      * Test that hitting ENTER on the top item actually triggers the top item.
172      * Catches regressions for https://crbug.com/191239 for shrunken menus.
173      */
174     /*
175     @SmallTest
176     @Feature({"Browser", "Main"})
177     */
178     @Test
179     @DisabledTest(message = "crbug.com/945861")
testKeyboardMenuEnterOnTopItemLandscape()180     public void testKeyboardMenuEnterOnTopItemLandscape() {
181         mActivityTestRule.getActivity().setRequestedOrientation(
182                 ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
183         showAppMenuAndAssertMenuShown();
184         moveToBoundary(true, false);
185         Assert.assertEquals(0, getCurrentFocusedRow());
186         hitEnterAndAssertAppMenuDismissed();
187     }
188 
189     /**
190      * Test that hitting ENTER on the top item doesn't crash Chrome.
191      */
192     @Test
193     @SmallTest
194     @Feature({"Browser", "Main"})
testKeyboardMenuEnterOnTopItemPortrait()195     public void testKeyboardMenuEnterOnTopItemPortrait() {
196         mActivityTestRule.getActivity().setRequestedOrientation(
197                 ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
198         showAppMenuAndAssertMenuShown();
199         moveToBoundary(true, false);
200         Assert.assertEquals(0, getCurrentFocusedRow());
201         hitEnterAndAssertAppMenuDismissed();
202     }
203 
204     @Test
205     @SmallTest
206     @Feature({"Browser", "Main"})
207     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
testHideMenuOnToggleOverview()208     public void testHideMenuOnToggleOverview() throws TimeoutException {
209         CallbackHelper overviewModeFinishedShowingCallback = new CallbackHelper();
210         OverviewModeBehavior.OverviewModeObserver overviewModeObserver =
211                 new EmptyOverviewModeObserver() {
212                     @Override
213                     public void onOverviewModeFinishedShowing() {
214                         overviewModeFinishedShowingCallback.notifyCalled();
215                     }
216                 };
217 
218         // App menu is shown during setup.
219         Assert.assertTrue("App menu should be showing.", mAppMenuHandler.isAppMenuShowing());
220         Assert.assertFalse("Overview shouldn't be showing.",
221                 mActivityTestRule.getActivity().getOverviewModeBehavior().overviewVisible());
222 
223         TestThreadUtils.runOnUiThreadBlocking(() -> {
224             mActivityTestRule.getActivity().getLayoutManager().addOverviewModeObserver(
225                     overviewModeObserver);
226             mActivityTestRule.getActivity().getLayoutManager().showOverview(false);
227         });
228         overviewModeFinishedShowingCallback.waitForCallback(0);
229 
230         Assert.assertTrue("Overview should be showing.",
231                 mActivityTestRule.getActivity().getOverviewModeBehavior().overviewVisible());
232         Assert.assertFalse("App menu shouldn't be showing.", mAppMenuHandler.isAppMenuShowing());
233         TestThreadUtils.runOnUiThreadBlocking(() -> {
234             Assert.assertTrue("App menu should be allowed to show.",
235                     AppMenuTestSupport.shouldShowAppMenu(
236                             mActivityTestRule.getAppMenuCoordinator()));
237         });
238         showAppMenuAndAssertMenuShown();
239 
240         TestThreadUtils.runOnUiThreadBlocking(
241                 () -> mActivityTestRule.getActivity().getLayoutManager().hideOverview(false));
242         Assert.assertFalse("Overview shouldn't be showing.",
243                 mActivityTestRule.getActivity().getOverviewModeBehavior().overviewVisible());
244         CriteriaHelper.pollUiThread(
245                 () -> !mAppMenuHandler.isAppMenuShowing(), "App menu shouldn't be showing.");
246     }
247 
248     @Test
249     @SmallTest
250     @Feature({"Browser", "Main", "Bookmark", "RenderTest"})
251     @DisableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_REGROUP,
252             ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_ICONS})
253     @DisableIf.Device(type = {UiDisableIf.TABLET}) // See https://crbug.com/1065043.
254     public void
testBookmarkMenuItem()255     testBookmarkMenuItem() throws IOException {
256         MenuItem bookmarkStar =
257                 AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
258                         .findItem(R.id.bookmark_this_page_id);
259         Assert.assertFalse("Bookmark item should not be checked.", bookmarkStar.isChecked());
260         Assert.assertEquals("Incorrect content description.",
261                 mActivityTestRule.getActivity().getString(R.string.menu_bookmark),
262                 bookmarkStar.getTitleCondensed());
263         mRenderTestRule.render(getListView().getChildAt(0), "icon_row");
264 
265         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
266         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(true);
267         showAppMenuAndAssertMenuShown();
268         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
269 
270         bookmarkStar = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
271                                .findItem(R.id.bookmark_this_page_id);
272         Assert.assertTrue("Bookmark item should be checked.", bookmarkStar.isChecked());
273         Assert.assertEquals("Incorrect content description for bookmarked page.",
274                 mActivityTestRule.getActivity().getString(R.string.edit_bookmark),
275                 bookmarkStar.getTitleCondensed());
276         mRenderTestRule.render(getListView().getChildAt(0), "icon_row_page_bookmarked");
277 
278         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(null);
279     }
280 
281     @Test
282     @SmallTest
283     @Feature({"Browser", "Main", "RenderTest"})
284     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
285     @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_REGROUP + "<Study"})
286     @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
287             "force-fieldtrial-params=Study.Group:action_bar/backward_button"})
288     public void
testBackButtonMenuItem()289     testBackButtonMenuItem() throws IOException {
290         MenuItem backArrow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
291                                      .findItem(R.id.backward_menu_id);
292         Assert.assertFalse("Backward button item should be disabled.", backArrow.isEnabled());
293         Assert.assertEquals("Incorrect content description.",
294                 mActivityTestRule.getActivity().getString(R.string.back),
295                 backArrow.getTitleCondensed());
296         mRenderTestRule.render(getListView().getChildAt(0), "icon_row_backward_diabled");
297 
298         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
299         mActivityTestRule.loadUrl(TEST_URL2);
300         showAppMenuAndAssertMenuShown();
301         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
302 
303         backArrow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
304                             .findItem(R.id.backward_menu_id);
305         Assert.assertTrue("Backward button item should be enabled.", backArrow.isEnabled());
306 
307         LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0);
308         Assert.assertEquals(5, actionBar.getChildCount());
309 
310         mRenderTestRule.render(getListView().getChildAt(0), "icon_row_backward_enabled");
311 
312         selectMenuItem(R.id.backward_menu_id);
313         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
314         ShareUtils shareUtils = new ShareUtils();
315         CriteriaHelper.pollUiThread(() -> {
316             Tab tab = mActivityTestRule.getActivity().getActivityTab();
317             Criteria.checkThat(tab, Matchers.notNullValue());
318             Criteria.checkThat(tab.getUrlString(), Matchers.is(TEST_URL));
319             Criteria.checkThat(shareUtils.shouldEnableShare(tab), Matchers.is(false));
320         });
321         showAppMenuAndAssertMenuShown();
322         backArrow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
323                             .findItem(R.id.backward_menu_id);
324         Assert.assertFalse("Backward button item should be disabled.", backArrow.isEnabled());
325     }
326 
327     @Test
328     @SmallTest
329     @Feature({"Browser", "Main", "RenderTest"})
330     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
331     @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_REGROUP + "<Study"})
332     @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
333             "force-fieldtrial-params=Study.Group:action_bar/share_button"})
334     public void
testShareButtonMenuItem()335     testShareButtonMenuItem() throws IOException {
336         MenuItem shareButton = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
337                                        .findItem(R.id.share_menu_button_id);
338         Assert.assertFalse("Share button item should be disabled.", shareButton.isEnabled());
339         Assert.assertEquals("Incorrect content description.",
340                 mActivityTestRule.getActivity().getString(R.string.share),
341                 shareButton.getTitleCondensed());
342         mRenderTestRule.render(getListView().getChildAt(0), "icon_row_share_diabled");
343 
344         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
345         mActivityTestRule.loadUrl(mActivityTestRule.getTestServer().getURL(
346                 "/chrome/test/data/android/contextualsearch/tap_test.html"));
347         showAppMenuAndAssertMenuShown();
348         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
349 
350         shareButton = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
351                               .findItem(R.id.share_menu_button_id);
352         Assert.assertTrue("Share button item should be enabled.", shareButton.isEnabled());
353 
354         LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0);
355         Assert.assertEquals(5, actionBar.getChildCount());
356         mRenderTestRule.render(getListView().getChildAt(0), "icon_row_share_enabled");
357     }
358 
359     @Test
360     @SmallTest
361     @Feature({"Browser", "Main", "RenderTest"})
362     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
363     @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR + "<Study"})
364     @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
365             "force-fieldtrial-params=Study.Group:three_button_action_bar/action_chip_view"})
366     public void
testActionChipViewMenuItem()367     testActionChipViewMenuItem() throws IOException {
368         LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0);
369         Assert.assertEquals(3, actionBar.getChildCount());
370         mRenderTestRule.render(getListView().getChildAt(0), "tinted_icon_row_three_buttons");
371 
372         int downloadRowIndex = findIndexOfMenuItemById(R.id.downloads_row_menu_id);
373         Assert.assertNotEquals("No download row found.", -1, downloadRowIndex);
374         mRenderTestRule.render(getListView().getChildAt(downloadRowIndex),
375                 "download_row_rounded_action_chip_view");
376 
377         MenuItem bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
378                                        .findItem(R.id.all_bookmarks_row_menu_id);
379         MenuItem bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1);
380         Assert.assertFalse("Bookmark item should not be checked.", bookmarkMenuItem.isChecked());
381         int bookmarkRowIndex = findIndexOfMenuItemById(R.id.all_bookmarks_row_menu_id);
382         Assert.assertTrue("No bookmark row found.", bookmarkRowIndex != -1);
383         mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex),
384                 "bookmark_row_rounded_action_chip_view");
385 
386         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
387         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(true);
388         showAppMenuAndAssertMenuShown();
389         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
390 
391         bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
392                               .findItem(R.id.all_bookmarks_row_menu_id);
393         bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1);
394         Assert.assertTrue("Bookmark item should be checked.", bookmarkMenuItem.isChecked());
395         mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex),
396                 "bookmark_row_rounded_action_chip_view_bookmarked");
397 
398         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(null);
399     }
400 
401     @Test
402     @SmallTest
403     @Feature({"Browser", "Main", "RenderTest"})
404     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
405     @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR + "<Study"})
406     @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
407             "force-fieldtrial-params=Study.Group:three_button_action_bar/destination_chip_view"})
408     public void
testDestinationChipViewMenuItem()409     testDestinationChipViewMenuItem() throws IOException {
410         LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0);
411         Assert.assertEquals(3, actionBar.getChildCount());
412         mRenderTestRule.render(getListView().getChildAt(0), "tinted_icon_row_three_buttons");
413 
414         int downloadRowIndex = findIndexOfMenuItemById(R.id.downloads_row_menu_id);
415         Assert.assertNotEquals("No download row found.", -1, downloadRowIndex);
416         mRenderTestRule.render(getListView().getChildAt(downloadRowIndex),
417                 "download_row_rounded_destination_chip_view");
418 
419         MenuItem bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
420                                        .findItem(R.id.all_bookmarks_row_menu_id);
421         MenuItem bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1);
422         Assert.assertFalse("Bookmark item should not be checked.", bookmarkMenuItem.isChecked());
423         int bookmarkRowIndex = findIndexOfMenuItemById(R.id.all_bookmarks_row_menu_id);
424         Assert.assertTrue("No bookmark row found.", bookmarkRowIndex != -1);
425         mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex),
426                 "bookmark_row_rounded_destination_chip_view");
427 
428         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
429         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(true);
430         showAppMenuAndAssertMenuShown();
431         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
432 
433         bookmarkRow = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator())
434                               .findItem(R.id.all_bookmarks_row_menu_id);
435         bookmarkMenuItem = bookmarkRow.getSubMenu().getItem(1);
436         Assert.assertTrue("Bookmark item should be checked.", bookmarkMenuItem.isChecked());
437         mRenderTestRule.render(getListView().getChildAt(bookmarkRowIndex),
438                 "bookmark_row_rounded_destination_chip_view_bookmarked");
439 
440         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(null);
441     }
442 
443     @Test
444     @SmallTest
445     @Feature({"Browser", "Main", "RenderTest"})
446     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
447     @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR + "<Study"})
448     @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
449             "force-fieldtrial-params=Study.Group:three_button_action_bar/add_to_option"})
450     public void
testAddToMenuItem_not_bookmarked()451     testAddToMenuItem_not_bookmarked() throws IOException {
452         LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0);
453         Assert.assertEquals(3, actionBar.getChildCount());
454         mRenderTestRule.render(getListView().getChildAt(0), "tinted_icon_row_three_buttons");
455 
456         int addToIndex = findIndexOfMenuItemById(R.id.add_to_menu_id);
457         Assert.assertNotEquals("No add to row found.", -1, addToIndex);
458         mRenderTestRule.render(getListView().getChildAt(addToIndex), "add_to_menu_item");
459 
460         View addToItem = getListView().getChildAt(addToIndex);
461         PropertyModel dialogModel = clickAndGetCurrentDialog(addToItem);
462         Assert.assertNotNull("No add to dialog found.", dialogModel);
463         LinearLayout addToCustomView =
464                 (LinearLayout) dialogModel.get(ModalDialogProperties.CUSTOM_VIEW);
465         Assert.assertEquals("The dialog should have 2 children, one is title, another is ListView.",
466                 2, addToCustomView.getChildCount());
467         TextView addToTitle = (TextView) addToCustomView.getChildAt(0);
468         mRenderTestRule.render(addToTitle, "menu_add_to_dialog_title");
469         ListView addToList = (ListView) addToCustomView.getChildAt(1);
470         Assert.assertEquals(3, addToList.getChildCount());
471         mRenderTestRule.render(addToList, "items_in_add_to_dialog_not_bookmarked");
472     }
473 
474     @Test
475     @SmallTest
476     @Feature({"Browser", "Main", "RenderTest"})
477     @Restriction(UiRestriction.RESTRICTION_TYPE_PHONE)
478     @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_THREE_BUTTON_ACTIONBAR + "<Study"})
479     @CommandLineFlags.Add({"force-fieldtrials=Study/Group",
480             "force-fieldtrial-params=Study.Group:three_button_action_bar/add_to_option"})
481     public void
testAddToMenuItem_bookmarked()482     testAddToMenuItem_bookmarked() throws IOException {
483         TestThreadUtils.runOnUiThreadBlocking(() -> mAppMenuHandler.hideAppMenu());
484         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(true);
485         showAppMenuAndAssertMenuShown();
486         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
487         LinearLayout actionBar = (LinearLayout) getListView().getChildAt(0);
488         Assert.assertEquals("Add to Bookmarks/Downloads/Home screen should be shown", 3,
489                 actionBar.getChildCount());
490         mRenderTestRule.render(getListView().getChildAt(0), "tinted_icon_row_three_buttons");
491 
492         int addToIndex = findIndexOfMenuItemById(R.id.add_to_menu_id);
493         Assert.assertNotEquals("No add to row found.", -1, addToIndex);
494         mRenderTestRule.render(getListView().getChildAt(addToIndex), "add_to_menu_item");
495 
496         View addToItem = getListView().getChildAt(addToIndex);
497         PropertyModel dialogModel = clickAndGetCurrentDialog(addToItem);
498         Assert.assertNotNull("No add to dialog found.", dialogModel);
499         LinearLayout addToCustomView =
500                 (LinearLayout) dialogModel.get(ModalDialogProperties.CUSTOM_VIEW);
501         Assert.assertEquals("The dialog should have 2 children, one is title, another is ListView.",
502                 2, addToCustomView.getChildCount());
503         TextView addToTitle = (TextView) addToCustomView.getChildAt(0);
504         mRenderTestRule.render(addToTitle, "menu_add_to_dialog_title");
505         ListView addToList = (ListView) addToCustomView.getChildAt(1);
506         Assert.assertEquals(3, addToList.getChildCount());
507         mRenderTestRule.render(addToList, "items_in_add_to_dialog_bookmarked");
508 
509         AppMenuPropertiesDelegateImpl.setPageBookmarkedForTesting(null);
510     }
511 
512     @Test
513     @SmallTest
514     @Feature({"Browser", "Main", "RenderTest"})
515     @EnableFeatures({ChromeFeatureList.TABBED_APP_OVERFLOW_MENU_REGROUP})
testDividerLineMenuItem()516     public void testDividerLineMenuItem() throws IOException {
517         int firstDividerLineIndex = findIndexOfMenuItemById(R.id.divider_line_id);
518         Assert.assertTrue("No divider line found.", firstDividerLineIndex != -1);
519         mRenderTestRule.render(getListView().getChildAt(firstDividerLineIndex), "divider_line");
520     }
521 
showAppMenuAndAssertMenuShown()522     private void showAppMenuAndAssertMenuShown() {
523         TestThreadUtils.runOnUiThreadBlocking(() -> {
524             AppMenuTestSupport.showAppMenu(mActivityTestRule.getAppMenuCoordinator(), null, false);
525             Assert.assertTrue(mAppMenuHandler.isAppMenuShowing());
526         });
527     }
528 
hitEnterAndAssertAppMenuDismissed()529     private void hitEnterAndAssertAppMenuDismissed() {
530         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
531         pressKey(KeyEvent.KEYCODE_ENTER);
532         CriteriaHelper.pollInstrumentationThread(
533                 () -> !mAppMenuHandler.isAppMenuShowing(), "AppMenu did not dismiss");
534     }
535 
moveToBoundary(boolean towardsTop, boolean movePast)536     private void moveToBoundary(boolean towardsTop, boolean movePast) {
537         // Move to the boundary.
538         final int end = towardsTop ? 0 : getCount() - 1;
539         int increment = towardsTop ? -1 : 1;
540         for (int index = getCurrentFocusedRow(); index != end; index += increment) {
541             pressKey(towardsTop ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN);
542             final int expectedPosition = index + increment;
543             CriteriaHelper.pollInstrumentationThread(() -> {
544                 Criteria.checkThat(getCurrentFocusedRow(), Matchers.is(expectedPosition));
545             });
546         }
547 
548         // Try moving past it by one.
549         if (movePast) {
550             pressKey(towardsTop ? KeyEvent.KEYCODE_DPAD_UP : KeyEvent.KEYCODE_DPAD_DOWN);
551             CriteriaHelper.pollInstrumentationThread(
552                     () -> Criteria.checkThat(getCurrentFocusedRow(), Matchers.is(end)));
553         }
554 
555         // The menu should stay open.
556         Assert.assertTrue(mAppMenuHandler.isAppMenuShowing());
557     }
558 
pressKey(final int keycode)559     private void pressKey(final int keycode) {
560         final View view = getListView();
561         PostTask.runOrPostTask(UiThreadTaskTraits.DEFAULT, () -> {
562             view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keycode));
563             view.dispatchKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keycode));
564         });
565         InstrumentationRegistry.getInstrumentation().waitForIdleSync();
566     }
567 
getCurrentFocusedRow()568     private int getCurrentFocusedRow() {
569         ListView listView = getListView();
570         if (listView == null) return ListView.INVALID_POSITION;
571         return listView.getSelectedItemPosition();
572     }
573 
getCount()574     private int getCount() {
575         ListView listView = getListView();
576         if (listView == null) return 0;
577         return listView.getCount();
578     }
579 
getListView()580     private ListView getListView() {
581         return AppMenuTestSupport.getListView(mActivityTestRule.getAppMenuCoordinator());
582     }
583 
selectMenuItem(int id)584     private void selectMenuItem(int id) {
585         CriteriaHelper.pollUiThread(
586                 () -> { mActivityTestRule.getActivity().onMenuOrKeyboardAction(id, true); });
587     }
588 
findIndexOfMenuItemById(int id)589     private int findIndexOfMenuItemById(int id) {
590         Menu menu = AppMenuTestSupport.getMenu(mActivityTestRule.getAppMenuCoordinator());
591         int firstMenuItemIndex = -1;
592         boolean foundMenuItem = false;
593         for (int i = 0; i < menu.size(); i++) {
594             MenuItem item = menu.getItem(i);
595             if (item.isVisible()) {
596                 firstMenuItemIndex++;
597             }
598             if (item.getItemId() == id) {
599                 foundMenuItem = true;
600                 break;
601             }
602         }
603 
604         return foundMenuItem ? firstMenuItemIndex : -1;
605     }
606 
clickAndGetCurrentDialog(View view)607     private PropertyModel clickAndGetCurrentDialog(View view) {
608         TestTouchUtils.performClickOnMainSync(InstrumentationRegistry.getInstrumentation(), view);
609         CriteriaHelper.pollUiThread(() -> {
610             PropertyModel propertyModel = mActivityTestRule.getActivity()
611                                                   .getModalDialogManager()
612                                                   .getCurrentDialogForTest();
613             Criteria.checkThat(propertyModel, Matchers.notNullValue());
614         });
615         return TestThreadUtils.runOnUiThreadBlockingNoException(
616                 ()
617                         -> mActivityTestRule.getActivity()
618                                    .getModalDialogManager()
619                                    .getCurrentDialogForTest());
620     }
621 }
622