blob: d2f84034593ca76d9d88763e2be195e964ac2537 [file] [log] [blame]
Steve McKay0c3c6952015-10-26 17:03:55 -07001/*
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
17package com.android.documentsui;
18
Steve McKayc6a4cd82015-11-18 14:56:50 -080019import static junit.framework.Assert.assertEquals;
Aga Wronska0950df12016-01-26 14:06:29 -080020import static junit.framework.Assert.assertNotNull;
21import static junit.framework.Assert.assertTrue;
22import static junit.framework.Assert.assertFalse;
Steve McKayc6a4cd82015-11-18 14:56:50 -080023
Steve McKay9146ac62016-02-09 12:40:07 -080024import android.app.Activity;
Aga Wronska0950df12016-01-26 14:06:29 -080025import android.content.Context;
Steve McKay0c3c6952015-10-26 17:03:55 -070026import android.support.test.uiautomator.By;
27import android.support.test.uiautomator.BySelector;
Aga Wronskae436f942016-02-08 12:00:38 -080028import android.support.test.uiautomator.Configurator;
Steve McKay0c3c6952015-10-26 17:03:55 -070029import android.support.test.uiautomator.UiDevice;
30import android.support.test.uiautomator.UiObject;
31import android.support.test.uiautomator.UiObject2;
32import android.support.test.uiautomator.UiObjectNotFoundException;
33import android.support.test.uiautomator.UiScrollable;
34import android.support.test.uiautomator.UiSelector;
35import android.support.test.uiautomator.Until;
36import android.util.Log;
Aga Wronskae436f942016-02-08 12:00:38 -080037import android.view.MotionEvent;
Aga Wronska0950df12016-01-26 14:06:29 -080038import android.view.inputmethod.InputMethodManager;
Steve McKay0c3c6952015-10-26 17:03:55 -070039
40import junit.framework.Assert;
41
42import java.util.ArrayList;
43import java.util.Arrays;
Aga Wronska0950df12016-01-26 14:06:29 -080044import java.util.Iterator;
Steve McKay0c3c6952015-10-26 17:03:55 -070045import java.util.List;
46import java.util.regex.Pattern;
47
48/**
49 * A test helper class that provides support for controlling DocumentsUI activities
50 * programmatically, and making assertions against the state of the UI.
51 */
52class UiBot {
Tomasz Mikolajewskic7b83222016-02-04 17:46:35 +090053 public static final String TARGET_PKG = "com.android.documentsui";
Steve McKay0c3c6952015-10-26 17:03:55 -070054
55 private static final String TAG = "UiBot";
Tomasz Mikolajewskic7b83222016-02-04 17:46:35 +090056 private static final String LAUNCHER_PKG = "com.android.launcher";
Steve McKay0c3c6952015-10-26 17:03:55 -070057
58 private static final BySelector SNACK_DELETE =
59 By.desc(Pattern.compile("^Deleting [0-9]+ file.+"));
60
61 private UiDevice mDevice;
Aga Wronska0950df12016-01-26 14:06:29 -080062 private Context mContext;
Steve McKay0c3c6952015-10-26 17:03:55 -070063 private int mTimeout;
64
Aga Wronska0950df12016-01-26 14:06:29 -080065 public UiBot(UiDevice device, Context context, int timeout) {
Steve McKay0c3c6952015-10-26 17:03:55 -070066 mDevice = device;
Aga Wronska0950df12016-01-26 14:06:29 -080067 mContext = context;
Steve McKay0c3c6952015-10-26 17:03:55 -070068 mTimeout = timeout;
69 }
70
71 UiObject findRoot(String label) throws UiObjectNotFoundException {
72 final UiSelector rootsList = new UiSelector().resourceId(
73 "com.android.documentsui:id/container_roots").childSelector(
Ben Kwa2036dad2016-02-10 07:46:35 -080074 new UiSelector().resourceId("com.android.documentsui:id/roots_list"));
Steve McKay0c3c6952015-10-26 17:03:55 -070075
76 // We might need to expand drawer if not visible
77 if (!new UiObject(rootsList).waitForExists(mTimeout)) {
78 Log.d(TAG, "Failed to find roots list; trying to expand");
79 final UiSelector hamburger = new UiSelector().resourceId(
80 "com.android.documentsui:id/toolbar").childSelector(
81 new UiSelector().className("android.widget.ImageButton").clickable(true));
82 new UiObject(hamburger).click();
83 }
84
85 // Wait for the first list item to appear
86 new UiObject(rootsList.childSelector(new UiSelector())).waitForExists(mTimeout);
87
88 // Now scroll around to find our item
89 new UiScrollable(rootsList).scrollIntoView(new UiSelector().text(label));
90 return new UiObject(rootsList.childSelector(new UiSelector().text(label)));
91 }
92
93 void openRoot(String label) throws UiObjectNotFoundException {
94 findRoot(label).click();
95 mDevice.waitForIdle();
96 }
97
Steve McKayc6a4cd82015-11-18 14:56:50 -080098 void assertWindowTitle(String expected) {
99 // Turns out the title field on a window does not have
100 // an id associated with it at runtime (which confuses the hell out of me)
101 // In code we address this via "android.R.id.title".
102 UiObject2 o = find(By.text(expected));
103 // It's a bit of a conceit that we then *assert* that the title
104 // is the value that we used to identify the UiObject2.
105 // If the preceeding lookup fails, this'll choke with an NPE.
106 // But given the issue described in the comment above, we're
107 // going to do it anyway. Because we shouldn't be looking up
108 // the uiobject by it's expected content :|
109 assertEquals(expected, o.getText());
110 }
111
Steve McKay0c3c6952015-10-26 17:03:55 -0700112 void assertHasRoots(String... labels) throws UiObjectNotFoundException {
113 List<String> missing = new ArrayList<>();
114 for (String label : labels) {
115 if (!findRoot(label).exists()) {
116 missing.add(label);
117 }
118 }
119 if (!missing.isEmpty()) {
120 Assert.fail(
121 "Expected roots " + Arrays.asList(labels) + ", but missing " + missing);
122 }
123 }
124
Aga Wronska0950df12016-01-26 14:06:29 -0800125 void assertMenuEnabled(int id, boolean enabled) {
126 UiObject2 menu= findMenuWithName(mContext.getString(id));
127 assertNotNull(menu);
128 assertEquals(enabled, menu.isEnabled());
Steve McKay0c3c6952015-10-26 17:03:55 -0700129 }
130
Aga Wronska0950df12016-01-26 14:06:29 -0800131 void assertDocumentsCount(int count) throws UiObjectNotFoundException {
132 UiObject docsList = findDocumentsList();
133 assertEquals(count, docsList.getChildCount());
134 }
135
136 void assertDocumentsCount(String dir, int count) throws UiObjectNotFoundException {
137 openRoot(dir);
138 UiObject docsList = findDocumentsList();
139 assertEquals(count, docsList.getChildCount());
140 }
141
142 void assertSearchTextField(boolean isFocused, String query)
143 throws UiObjectNotFoundException {
144 UiObject textField = findSearchViewTextField();
145 UiObject searchIcon = findSearchViewIcon();
146
147 assertFalse(searchIcon.exists());
148 assertTrue(textField.exists());
149 assertEquals(isFocused, textField.isFocused());
150 if(query != null) {
151 assertEquals(query, textField.getText());
Steve McKay0c3c6952015-10-26 17:03:55 -0700152 }
Aga Wronska0950df12016-01-26 14:06:29 -0800153 }
154
155 void assertSearchTextFiledAndIcon(boolean searchTextFieldExists, boolean searchIconExists) {
156 assertEquals(searchTextFieldExists, findSearchViewTextField().exists());
157 assertEquals(searchIconExists, findSearchViewIcon().exists());
Steve McKay0c3c6952015-10-26 17:03:55 -0700158 }
159
160 void assertHasDocuments(String... labels) throws UiObjectNotFoundException {
161 List<String> missing = new ArrayList<>();
162 for (String label : labels) {
163 if (!findDocument(label).exists()) {
164 missing.add(label);
165 }
166 }
167 if (!missing.isEmpty()) {
168 Assert.fail(
169 "Expected documents " + Arrays.asList(labels) + ", but missing " + missing);
170 }
171 }
172
Aga Wronska0950df12016-01-26 14:06:29 -0800173 void assertDocument(String name, boolean exists) throws UiObjectNotFoundException {
174 UiObject doc = findDocument(name);
175 assertEquals(exists, doc.exists());
176 }
177
178 void assertDocumentsCountOnList(boolean exists, int count) throws UiObjectNotFoundException {
179 UiObject docsList = findDocumentsList();
180 assertEquals(exists, docsList.exists());
181 if(docsList.exists()) {
182 assertEquals(count, docsList.getChildCount());
183 }
184 }
185
186 void assertMessageTextView(String message) throws UiObjectNotFoundException {
187 UiObject messageTextView = findMessageTextView();
188 assertTrue(messageTextView.exists());
189
190 String msg = String.valueOf(message);
191 assertEquals(String.format(msg, "TEST_ROOT_0"), messageTextView.getText());
192
193 }
194 void assertSnackbar(int id) {
195 assertNotNull(getSnackbar(mContext.getString(id)));
196 }
197
Ben Kwa2036dad2016-02-10 07:46:35 -0800198 /**
199 * Asserts that the specified view or one of its descendents has focus.
200 */
201 void assertHasFocus(String resourceName) {
202 UiObject2 candidate = mDevice.findObject(By.res(resourceName));
203 assertNotNull("Expected " + resourceName + " to have focus, but it didn't.",
204 candidate.findObject(By.focused(true)));
205 }
206
Aga Wronskae436f942016-02-08 12:00:38 -0800207 void openDocument(String label) throws UiObjectNotFoundException {
208 int toolType = Configurator.getInstance().getToolType();
209 Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
210 UiObject doc = findDocument(label);
211 doc.click();
212 Configurator.getInstance().setToolType(toolType);
213 }
214
Steve McKay0c3c6952015-10-26 17:03:55 -0700215 void clickDocument(String label) throws UiObjectNotFoundException {
216 findDocument(label).click();
217 }
218
Aga Wronska0950df12016-01-26 14:06:29 -0800219 void openSearchView() throws UiObjectNotFoundException {
220 UiObject searchView = findSearchView();
221 searchView.click();
222 assertTrue(searchView.exists());
223 }
224
225 void setSearchQuery(String query) throws UiObjectNotFoundException {
226 UiObject searchView = findSearchView();
227 assertTrue(searchView.exists());
228 UiObject searchTextField = findSearchViewTextField();
229 searchTextField.setText(query);
230 assertSearchTextField(true, query);
231 }
232
233 UiObject openOverflowMenu() throws UiObjectNotFoundException {
234 UiObject obj = findMenuMoreOptions();
235 obj.click();
236 mDevice.waitForIdle(mTimeout);
237 return obj;
238 }
239
240 void openDialog(int id) {
241 UiObject2 menu= findMenuWithName(mContext.getString(id));
242 assertNotNull(menu);
243 assertEquals(true, menu.isEnabled());
244 menu.click();
245 }
246
247 void setDialogText(String text) throws UiObjectNotFoundException {
248 findDialogEditText().setText(text);
249 }
250
251 UiObject selectDocument(String label) throws UiObjectNotFoundException {
252 UiObject doc = findDocument(label);
253 doc.longClick();
254 return doc;
255 }
256
257 UiObject2 getSnackbar(String message) {
258 return mDevice.wait(Until.findObject(By.text(message)), mTimeout);
259 }
260
Steve McKay0c3c6952015-10-26 17:03:55 -0700261 void waitForDeleteSnackbar() {
262 mDevice.wait(Until.findObject(SNACK_DELETE), mTimeout);
263 }
264
265 void waitForDeleteSnackbarGone() {
266 // wait a little longer for snackbar to go away, as it disappears after a timeout.
267 mDevice.wait(Until.gone(SNACK_DELETE), mTimeout * 2);
268 }
269
Tomasz Mikolajewski65d9a512016-02-08 15:36:04 +0900270 void waitForDocument(String label) throws UiObjectNotFoundException {
271 findDocument(label).waitForExists(mTimeout);
272 }
273
Steve McKay0c3c6952015-10-26 17:03:55 -0700274 void switchViewMode() {
275 UiObject2 mode = menuGridMode();
276 if (mode != null) {
277 mode.click();
278 } else {
279 menuListMode().click();
280 }
281 }
282
283 UiObject2 menuGridMode() {
284 // Note that we're using By.desc rather than By.res, because of b/25285770
285 return find(By.desc("Grid view"));
286 }
287
288 UiObject2 menuListMode() {
289 // Note that we're using By.desc rather than By.res, because of b/25285770
290 return find(By.desc("List view"));
291 }
292
293 UiObject2 menuDelete() {
294 return find(By.res("com.android.documentsui:id/menu_delete"));
295 }
296
Steve McKaydbdaa492015-12-02 11:20:54 -0800297 UiObject2 menuShare() {
298 return find(By.res("com.android.documentsui:id/menu_share"));
299 }
300
Steve McKay0c3c6952015-10-26 17:03:55 -0700301 private UiObject2 find(BySelector selector) {
302 mDevice.wait(Until.findObject(selector), mTimeout);
303 return mDevice.findObject(selector);
304 }
Aga Wronska619f3be2016-01-14 11:15:20 -0800305
306 private UiObject findObject(String resourceId) {
307 final UiSelector object = new UiSelector().resourceId(resourceId);
308 return mDevice.findObject(object);
309 }
310
311 private UiObject findObject(String parentResourceId, String childResourceId) {
312 final UiSelector selector = new UiSelector()
313 .resourceId(parentResourceId)
314 .childSelector(new UiSelector().resourceId(childResourceId));
315 return mDevice.findObject(selector);
316 }
317
Aga Wronska0950df12016-01-26 14:06:29 -0800318 UiObject findDocument(String label) throws UiObjectNotFoundException {
319 final UiSelector docList = new UiSelector().resourceId(
320 "com.android.documentsui:id/container_directory").childSelector(
Ben Kwa2036dad2016-02-10 07:46:35 -0800321 new UiSelector().resourceId("com.android.documentsui:id/dir_list"));
Aga Wronska0950df12016-01-26 14:06:29 -0800322
323 // Wait for the first list item to appear
324 new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
325
326 // new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
327 return mDevice.findObject(docList.childSelector(new UiSelector().text(label)));
328 }
329
330 boolean hasDocuments(String... labels) throws UiObjectNotFoundException {
331 for (String label : labels) {
332 if (!findDocument(label).exists()) {
333 return false;
334 }
335 }
336 return true;
337 }
338
Aga Wronska619f3be2016-01-14 11:15:20 -0800339 UiObject findDocumentsList() {
340 return findObject(
341 "com.android.documentsui:id/container_directory",
Ben Kwa2036dad2016-02-10 07:46:35 -0800342 "com.android.documentsui:id/dir_list");
Aga Wronska619f3be2016-01-14 11:15:20 -0800343 }
344
345 UiObject findSearchView() {
346 return findObject("com.android.documentsui:id/menu_search");
347 }
348
349 UiObject findSearchViewTextField() {
350 return findObject("com.android.documentsui:id/menu_search", "android:id/search_src_text");
351 }
352
353 UiObject findSearchViewIcon() {
354 return findObject("com.android.documentsui:id/menu_search", "android:id/search_button");
355 }
356
357 UiObject findMessageTextView() {
358 return findObject(
359 "com.android.documentsui:id/container_directory",
360 "com.android.documentsui:id/message");
361 }
362
Aga Wronska0950df12016-01-26 14:06:29 -0800363 UiObject findActionModeBar() {
364 return findObject("android:id/action_mode_bar");
365 }
366
367 UiObject findDialogEditText() {
368 return findObject("android:id/content", "android:id/text1");
369 }
370
371 UiObject findRenameDialogOkButton() {
372 return findObject("android:id/content", "android:id/button1");
373 }
374
375 UiObject findRenameDialogCancelButton() {
376 return findObject("android:id/content", "android:id/button2");
377 }
378
379 UiObject findMenuLabelWithName(String label) {
380 UiSelector selector = new UiSelector().text(label);
381 return mDevice.findObject(selector);
382 }
383
384 UiObject2 findMenuWithName(String label) {
385 List<UiObject2> menuItems = mDevice.findObjects(By.clazz("android.widget.LinearLayout"));
386 Iterator<UiObject2> it = menuItems.iterator();
387
388 UiObject2 menuItem = null;
389 while(it.hasNext()) {
390 menuItem = it.next();
391 UiObject2 text = menuItem.findObject(By.text(label));
392 if(text != null) {
393 break;
394 }
395 }
396 return menuItem;
397 }
398
399 UiObject findMenuMoreOptions() {
400 UiSelector selector = new UiSelector().className("android.widget.ImageButton")
401 .descriptionContains("More options");
402 //TODO: use the system string ? android.R.string.action_menu_overflow_description
403 return mDevice.findObject(selector);
404 }
405
406 // Indirect way to detect the keyboard.
407 boolean isKeyboardPresent() {
408 InputMethodManager inputManager = (InputMethodManager) mContext
409 .getSystemService(Context.INPUT_METHOD_SERVICE);
410 return inputManager.isAcceptingText();
411 }
412
413 void dismissKeyboardIfPresent() {
414 if(isKeyboardPresent()) {
415 mDevice.pressBack();
416 }
417 }
418
Tomasz Mikolajewskic7b83222016-02-04 17:46:35 +0900419 void revealLauncher() {
420 mDevice.pressHome();
421 mDevice.wait(Until.hasObject(By.pkg(LAUNCHER_PKG).depth(0)), mTimeout);
422 }
423
424 void revealApp() {
425 mDevice.wait(Until.hasObject(By.pkg(TARGET_PKG).depth(0)), mTimeout);
426 mDevice.waitForIdle();
427 }
Ben Kwa2036dad2016-02-10 07:46:35 -0800428
429 void pressKey(int keyCode) {
430 mDevice.pressKeyCode(keyCode);
431 }
Steve McKay0c3c6952015-10-26 17:03:55 -0700432}