• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.documentsui.bots;
18 
19 import static android.support.test.espresso.Espresso.onView;
20 import static android.support.test.espresso.action.ViewActions.click;
21 import static android.support.test.espresso.assertion.ViewAssertions.matches;
22 import static android.support.test.espresso.matcher.ViewMatchers.hasFocus;
23 import static android.support.test.espresso.matcher.ViewMatchers.isAssignableFrom;
24 import static android.support.test.espresso.matcher.ViewMatchers.withClassName;
25 import static android.support.test.espresso.matcher.ViewMatchers.withId;
26 import static android.support.test.espresso.matcher.ViewMatchers.withText;
27 import static junit.framework.Assert.assertEquals;
28 import static junit.framework.Assert.assertNotNull;
29 import static org.hamcrest.CoreMatchers.allOf;
30 import static org.hamcrest.CoreMatchers.is;
31 import static org.hamcrest.Matchers.endsWith;
32 
33 import android.content.Context;
34 import android.support.test.espresso.Espresso;
35 import android.support.test.espresso.action.ViewActions;
36 import android.support.test.espresso.matcher.BoundedMatcher;
37 import android.support.test.espresso.matcher.ViewMatchers;
38 import android.support.test.uiautomator.By;
39 import android.support.test.uiautomator.UiDevice;
40 import android.support.test.uiautomator.UiObject;
41 import android.support.test.uiautomator.UiObject2;
42 import android.support.test.uiautomator.UiObjectNotFoundException;
43 import android.support.test.uiautomator.UiSelector;
44 import android.support.test.uiautomator.Until;
45 import android.util.TypedValue;
46 import android.view.View;
47 import android.widget.Toolbar;
48 
49 import com.android.documentsui.R;
50 
51 import org.hamcrest.Description;
52 import org.hamcrest.Matcher;
53 
54 import java.util.Iterator;
55 import java.util.List;
56 
57 /**
58  * A test helper class that provides support for controlling DocumentsUI activities
59  * programmatically, and making assertions against the state of the UI.
60  * <p>
61  * Support for working directly with Roots and Directory view can be found in the respective bots.
62  */
63 public class UiBot extends Bots.BaseBot {
64 
65     public static final String TARGET_PKG = "com.android.documentsui";
66 
67     @SuppressWarnings("unchecked")
68     private static final Matcher<View> TOOLBAR = allOf(
69             isAssignableFrom(Toolbar.class),
70             withId(R.id.toolbar));
71 
72     @SuppressWarnings("unchecked")
73     private static final Matcher<View> ACTIONBAR = allOf(
74             withClassName(endsWith("ActionBarContextView")));
75 
76     @SuppressWarnings("unchecked")
77     private static final Matcher<View> TEXT_ENTRY = allOf(
78             withClassName(endsWith("EditText")));
79 
80     @SuppressWarnings("unchecked")
81     private static final Matcher<View> TOOLBAR_OVERFLOW = allOf(
82             withClassName(endsWith("OverflowMenuButton")),
83             ViewMatchers.isDescendantOfA(TOOLBAR));
84 
85     @SuppressWarnings("unchecked")
86     private static final Matcher<View> ACTIONBAR_OVERFLOW = allOf(
87             withClassName(endsWith("OverflowMenuButton")),
88             ViewMatchers.isDescendantOfA(ACTIONBAR));
89 
UiBot(UiDevice device, Context context, int timeout)90     public UiBot(UiDevice device, Context context, int timeout) {
91         super(device, context, timeout);
92     }
93 
assertWindowTitle(String expected)94     public void assertWindowTitle(String expected) {
95         onView(TOOLBAR)
96                 .check(matches(withToolbarTitle(is(expected))));
97     }
98 
assertMenuEnabled(int id, boolean enabled)99     public void assertMenuEnabled(int id, boolean enabled) {
100         UiObject2 menu = findMenuWithName(mContext.getString(id));
101         assertNotNull(menu);
102         assertEquals(enabled, menu.isEnabled());
103     }
104 
assertInActionMode(boolean inActionMode)105     public void assertInActionMode(boolean inActionMode) {
106         assertEquals(inActionMode, waitForActionModeBarToAppear());
107     }
108 
openOverflowMenu()109     public UiObject openOverflowMenu() throws UiObjectNotFoundException {
110         UiObject obj = findMenuMoreOptions();
111         obj.click();
112         mDevice.waitForIdle(mTimeout);
113         return obj;
114     }
115 
setDialogText(String text)116     public void setDialogText(String text) throws UiObjectNotFoundException {
117         onView(TEXT_ENTRY)
118                 .perform(ViewActions.replaceText(text));
119     }
120 
assertDialogText(String expected)121     public void assertDialogText(String expected) throws UiObjectNotFoundException {
122         onView(TEXT_ENTRY)
123                 .check(matches(withText(is(expected))));
124     }
125 
inFixedLayout()126     public boolean inFixedLayout() {
127         TypedValue val = new TypedValue();
128         // We alias files_activity to either fixed or drawer layouts based
129         // on screen dimensions. In order to determine which layout
130         // has been selected, we check the resolved value.
131         mContext.getResources().getValue(R.layout.files_activity, val, true);
132         return val.resourceId == R.layout.fixed_layout;
133     }
134 
inDrawerLayout()135     public boolean inDrawerLayout() {
136         return !inFixedLayout();
137     }
138 
switchToListMode()139     public void switchToListMode() {
140         final UiObject2 listMode = menuListMode();
141         if (listMode != null) {
142             listMode.click();
143         }
144     }
145 
clickActionItem(String label)146     public void clickActionItem(String label) throws UiObjectNotFoundException {
147         if (!waitForActionModeBarToAppear()) {
148             throw new UiObjectNotFoundException("ActionMode bar not found");
149         }
150         clickActionbarOverflowItem(label);
151         mDevice.waitForIdle();
152     }
153 
switchToGridMode()154     public void switchToGridMode() {
155         final UiObject2 gridMode = menuGridMode();
156         if (gridMode != null) {
157             gridMode.click();
158         }
159     }
160 
menuGridMode()161     UiObject2 menuGridMode() {
162         // Note that we're using By.desc rather than By.res, because of b/25285770
163         return find(By.desc("Grid view"));
164     }
165 
menuListMode()166     UiObject2 menuListMode() {
167         // Note that we're using By.desc rather than By.res, because of b/25285770
168         return find(By.desc("List view"));
169     }
170 
clickToolbarItem(int id)171     public void clickToolbarItem(int id) {
172         onView(withId(id)).perform(click());
173     }
174 
clickNewFolder()175     public void clickNewFolder() {
176         onView(ACTIONBAR_OVERFLOW).perform(click());
177 
178         // Click the item by label, since Espresso doesn't support lookup by id on overflow.
179         onView(withText("New folder")).perform(click());
180     }
181 
clickActionbarOverflowItem(String label)182     public void clickActionbarOverflowItem(String label) {
183         onView(ACTIONBAR_OVERFLOW).perform(click());
184         // Click the item by label, since Espresso doesn't support lookup by id on overflow.
185         onView(withText(label)).perform(click());
186     }
187 
clickToolbarOverflowItem(String label)188     public void clickToolbarOverflowItem(String label) {
189         onView(TOOLBAR_OVERFLOW).perform(click());
190         // Click the item by label, since Espresso doesn't support lookup by id on overflow.
191         onView(withText(label)).perform(click());
192     }
193 
waitForActionModeBarToAppear()194     public boolean waitForActionModeBarToAppear() {
195         UiObject2 bar =
196                 mDevice.wait(Until.findObject(By.res("android:id/action_mode_bar")), mTimeout);
197         return (bar != null);
198     }
199 
findDownloadRetryDialog()200     public UiObject findDownloadRetryDialog() {
201         UiSelector selector = new UiSelector().text("Couldn't download");
202         UiObject title = mDevice.findObject(selector);
203         title.waitForExists(mTimeout);
204         return title;
205     }
206 
findFileRenameDialog()207     public UiObject findFileRenameDialog() {
208         UiSelector selector = new UiSelector().text("Rename");
209         UiObject title = mDevice.findObject(selector);
210         title.waitForExists(mTimeout);
211         return title;
212     }
213 
findRenameErrorMessage()214     public UiObject findRenameErrorMessage() {
215         UiSelector selector = new UiSelector().text(mContext.getString(R.string.name_conflict));
216         UiObject title = mDevice.findObject(selector);
217         title.waitForExists(mTimeout);
218         return title;
219     }
220 
221     @SuppressWarnings("unchecked")
assertDialogOkButtonFocused()222     public void assertDialogOkButtonFocused() {
223         onView(withId(android.R.id.button1)).check(matches(hasFocus()));
224     }
225 
clickDialogOkButton()226     public void clickDialogOkButton() {
227         // Espresso has flaky results when keyboard shows up, so hiding it for now
228         // before trying to click on any dialog button
229         Espresso.closeSoftKeyboard();
230         onView(withId(android.R.id.button1)).perform(click());
231     }
232 
clickDialogCancelButton()233     public void clickDialogCancelButton() throws UiObjectNotFoundException {
234         // Espresso has flaky results when keyboard shows up, so hiding it for now
235         // before trying to click on any dialog button
236         Espresso.closeSoftKeyboard();
237         onView(withId(android.R.id.button2)).perform(click());
238     }
239 
findMenuLabelWithName(String label)240     UiObject findMenuLabelWithName(String label) {
241         UiSelector selector = new UiSelector().text(label);
242         return mDevice.findObject(selector);
243     }
244 
findMenuWithName(String label)245     UiObject2 findMenuWithName(String label) {
246         List<UiObject2> menuItems = mDevice.findObjects(By.clazz("android.widget.LinearLayout"));
247         Iterator<UiObject2> it = menuItems.iterator();
248 
249         UiObject2 menuItem = null;
250         while (it.hasNext()) {
251             menuItem = it.next();
252             UiObject2 text = menuItem.findObject(By.text(label));
253             if (text != null) {
254                 break;
255             }
256         }
257         return menuItem;
258     }
259 
hasMenuWithName(String label)260     boolean hasMenuWithName(String label) {
261         return findMenuWithName(label) != null;
262     }
263 
findMenuMoreOptions()264     UiObject findMenuMoreOptions() {
265         UiSelector selector = new UiSelector().className("android.widget.ImageButton")
266                 .descriptionContains("More options");
267         // TODO: use the system string ? android.R.string.action_menu_overflow_description
268         return mDevice.findObject(selector);
269     }
270 
withToolbarTitle( final Matcher<CharSequence> textMatcher)271     private static Matcher<Object> withToolbarTitle(
272             final Matcher<CharSequence> textMatcher) {
273         return new BoundedMatcher<Object, Toolbar>(Toolbar.class) {
274             @Override
275             public boolean matchesSafely(Toolbar toolbar) {
276                 return textMatcher.matches(toolbar.getTitle());
277             }
278 
279             @Override
280             public void describeTo(Description description) {
281                 description.appendText("with toolbar title: ");
282                 textMatcher.describeTo(description);
283             }
284         };
285     }
286 }
287