Home | History | Annotate | Download | only in bots
      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 junit.framework.Assert.assertEquals;
     20 import static junit.framework.Assert.assertFalse;
     21 import static junit.framework.Assert.assertNotNull;
     22 import static junit.framework.Assert.assertTrue;
     23 import static junit.framework.Assert.fail;
     24 
     25 import android.app.UiAutomation;
     26 import android.content.Context;
     27 import android.graphics.Point;
     28 import android.graphics.Rect;
     29 import android.os.SystemClock;
     30 import android.support.test.uiautomator.By;
     31 import android.support.test.uiautomator.BySelector;
     32 import android.support.test.uiautomator.Configurator;
     33 import android.support.test.uiautomator.UiDevice;
     34 import android.support.test.uiautomator.UiObject;
     35 import android.support.test.uiautomator.UiObject2;
     36 import android.support.test.uiautomator.UiObjectNotFoundException;
     37 import android.support.test.uiautomator.UiSelector;
     38 import android.support.test.uiautomator.Until;
     39 import android.view.InputDevice;
     40 import android.view.KeyEvent;
     41 import android.view.MotionEvent;
     42 import android.view.View;
     43 import android.widget.ImageView;
     44 
     45 import java.util.ArrayList;
     46 import java.util.Arrays;
     47 import java.util.List;
     48 import java.util.regex.Pattern;
     49 
     50 /**
     51  * A test helper class that provides support for controlling directory list
     52  * and making assertions against the state of it.
     53  */
     54 public class DirectoryListBot extends Bots.BaseBot {
     55     private static final String DIR_CONTAINER_ID = "com.android.documentsui:id/container_directory";
     56     private static final String DIR_LIST_ID = "com.android.documentsui:id/dir_list";
     57 
     58     private static final BySelector SNACK_DELETE =
     59             By.desc(Pattern.compile("^Deleting [0-9]+ file.+"));
     60     private UiAutomation mAutomation;
     61 
     62     public DirectoryListBot(
     63             UiDevice device, UiAutomation automation, Context context, int timeout) {
     64         super(device, context, timeout);
     65         mAutomation = automation;
     66     }
     67 
     68     public void assertDocumentsCount(int count) throws UiObjectNotFoundException {
     69         UiObject docsList = findDocumentsList();
     70         assertEquals(count, docsList.getChildCount());
     71     }
     72 
     73     public void assertDocumentsPresent(String... labels) throws UiObjectNotFoundException {
     74         List<String> absent = new ArrayList<>();
     75         for (String label : labels) {
     76             if (!findDocument(label).exists()) {
     77                 absent.add(label);
     78             }
     79         }
     80         if (!absent.isEmpty()) {
     81             fail("Expected documents " + Arrays.asList(labels)
     82                     + ", but missing " + absent);
     83         }
     84     }
     85 
     86     public void assertDocumentsAbsent(String... labels) throws UiObjectNotFoundException {
     87         List<String> found = new ArrayList<>();
     88         for (String label : labels) {
     89             if (findDocument(label).exists()) {
     90                 found.add(label);
     91             }
     92         }
     93         if (!found.isEmpty()) {
     94             fail("Expected documents not present" + Arrays.asList(labels)
     95                     + ", but present " + found);
     96         }
     97     }
     98 
     99     public void assertDocumentsCountOnList(boolean exists, int count) throws UiObjectNotFoundException {
    100         UiObject docsList = findDocumentsList();
    101         assertEquals(exists, docsList.exists());
    102         if(docsList.exists()) {
    103             assertEquals(count, docsList.getChildCount());
    104         }
    105     }
    106 
    107     public void assertHasMessage(String expected) throws UiObjectNotFoundException {
    108         UiObject messageTextView = findHeaderMessageTextView();
    109         String msg = String.valueOf(expected);
    110         assertEquals(msg, messageTextView.getText());
    111     }
    112 
    113     public void assertHasMessage(boolean expected) throws UiObjectNotFoundException {
    114         UiObject messageTextView = findHeaderMessageTextView();
    115         if (expected) {
    116             assertTrue(messageTextView.exists());
    117         } else {
    118             assertFalse(messageTextView.exists());
    119         }
    120     }
    121 
    122     public void assertHasMessageButtonText(String expected) throws UiObjectNotFoundException {
    123         UiObject button = findHeaderMessageButton();
    124         String msg = String.valueOf(expected);
    125         assertEquals(msg, button.getText());
    126     }
    127 
    128     public void clickMessageButton() throws UiObjectNotFoundException {
    129         UiObject button = findHeaderMessageButton();
    130         button.click();
    131     }
    132 
    133     /**
    134      * Checks against placeholder text. Placeholder can be Empty page, No results page, or the
    135      * "Hourglass" page (ie. something-went-wrong page).
    136      */
    137     public void assertPlaceholderMessageText(String message) throws UiObjectNotFoundException {
    138         UiObject messageTextView = findPlaceholderMessageTextView();
    139         assertTrue(messageTextView.exists());
    140 
    141         String msg = String.valueOf(message);
    142         assertEquals(msg, messageTextView.getText());
    143 
    144     }
    145 
    146     private UiObject findHeaderMessageTextView() {
    147         return findObject(
    148                 DIR_CONTAINER_ID,
    149                 "com.android.documentsui:id/message_textview");
    150     }
    151 
    152     private UiObject findHeaderMessageButton() {
    153         return findObject(
    154                 DIR_CONTAINER_ID,
    155                 "com.android.documentsui:id/button_dismiss");
    156     }
    157 
    158     private UiObject findPlaceholderMessageTextView() {
    159         return findObject(
    160                 DIR_CONTAINER_ID,
    161                 "com.android.documentsui:id/message");
    162     }
    163 
    164     public void assertSnackbar(int id) {
    165         assertNotNull(getSnackbar(mContext.getString(id)));
    166     }
    167 
    168     public void openDocument(String label) throws UiObjectNotFoundException {
    169         int toolType = Configurator.getInstance().getToolType();
    170         Configurator.getInstance().setToolType(MotionEvent.TOOL_TYPE_FINGER);
    171         UiObject doc = findDocument(label);
    172         doc.click();
    173         Configurator.getInstance().setToolType(toolType);
    174     }
    175 
    176     public void selectDocument(String label) throws UiObjectNotFoundException {
    177         waitForDocument(label);
    178         UiObject2 selectionHotspot = findSelectionHotspot(label);
    179         selectionHotspot.click();
    180     }
    181 
    182     /**
    183      * @param label The filename of the document
    184      * @param number Which nth document it is. The number corresponding to "n selected"
    185      */
    186     public void selectDocument(String label, int number) throws UiObjectNotFoundException {
    187         selectDocument(label);
    188 
    189         // wait until selection is fully done to avoid future click being registered as double
    190         // clicking
    191         assertSelection(number);
    192     }
    193 
    194     public UiObject2 findSelectionHotspot(String label) {
    195         final BySelector list = By.res(DIR_LIST_ID);
    196 
    197         BySelector selector = By.hasChild(By.text(label));
    198         UiObject2 parent = mDevice.findObject(list).findObject(selector);
    199         if (parent.getClassName().equals("android.widget.LinearLayout")) {
    200             // For list mode, the parent of the textView does not contain the selector icon, but the
    201             // grandparent of the textView does
    202             // Gotta go one more level up
    203             selector = By.hasDescendant(By.text(label).depth(2));
    204             parent = mDevice.findObject(list).findObject(selector);
    205         }
    206         return parent.findObject(By.clazz(ImageView.class));
    207     }
    208 
    209     public void copyFilesToClipboard(String...labels) throws UiObjectNotFoundException {
    210         for (String label: labels) {
    211             selectDocument(label);
    212         }
    213         mDevice.pressKeyCode(KeyEvent.KEYCODE_C, KeyEvent.META_CTRL_ON);
    214     }
    215 
    216     public void pasteFilesFromClipboard() {
    217         mDevice.pressKeyCode(KeyEvent.KEYCODE_V, KeyEvent.META_CTRL_ON);
    218     }
    219 
    220     public UiObject2 getSnackbar(String message) {
    221         return mDevice.wait(Until.findObject(By.text(message)), mTimeout);
    222     }
    223 
    224     public void waitForDeleteSnackbar() {
    225         mDevice.wait(Until.findObject(SNACK_DELETE), mTimeout);
    226     }
    227 
    228     public void waitForDeleteSnackbarGone() {
    229         // wait a little longer for snackbar to go away, as it disappears after a timeout.
    230         mDevice.wait(Until.gone(SNACK_DELETE), mTimeout * 2);
    231     }
    232 
    233     public void waitForDocument(String label) throws UiObjectNotFoundException {
    234         findDocument(label).waitForExists(mTimeout);
    235     }
    236 
    237     public UiObject findDocument(String label) throws UiObjectNotFoundException {
    238         final UiSelector docList = new UiSelector().resourceId(
    239                 DIR_CONTAINER_ID).childSelector(
    240                         new UiSelector().resourceId(DIR_LIST_ID));
    241 
    242         // Wait for the first list item to appear
    243         new UiObject(docList.childSelector(new UiSelector())).waitForExists(mTimeout);
    244 
    245         // new UiScrollable(docList).scrollIntoView(new UiSelector().text(label));
    246         return mDevice.findObject(docList.childSelector(new UiSelector().text(label)));
    247     }
    248 
    249     public boolean hasDocuments(String... labels) throws UiObjectNotFoundException {
    250         for (String label : labels) {
    251             if (!findDocument(label).exists()) {
    252                 return false;
    253             }
    254         }
    255         return true;
    256     }
    257 
    258     public void assertFirstDocumentHasFocus() throws UiObjectNotFoundException {
    259         final UiSelector docList = new UiSelector().resourceId(
    260                 DIR_CONTAINER_ID).childSelector(
    261                         new UiSelector().resourceId(DIR_LIST_ID));
    262 
    263         // Wait for the first list item to appear
    264         UiObject doc = new UiObject(docList.childSelector(new UiSelector()));
    265         doc.waitForExists(mTimeout);
    266 
    267         assertTrue(doc.isFocused());
    268     }
    269 
    270     public UiObject findDocumentsList() {
    271         return findObject(
    272                 DIR_CONTAINER_ID,
    273                 DIR_LIST_ID);
    274     }
    275 
    276     public void assertHasFocus() {
    277         assertHasFocus(DIR_LIST_ID);
    278     }
    279 
    280     public void assertSelection(int numSelected) {
    281         String assertSelectionText = numSelected + " selected";
    282         UiObject2 selectionText = mDevice.wait(
    283                 Until.findObject(By.text(assertSelectionText)), mTimeout);
    284         assertTrue(selectionText != null);
    285     }
    286 
    287     public void assertOrder(String[] dirs, String[] files) throws UiObjectNotFoundException {
    288         for (int i = 0; i < dirs.length - 1; ++i) {
    289             assertOrder(dirs[i], dirs[i + 1]);
    290         }
    291 
    292         if (dirs.length > 0 && files.length > 0) {
    293             assertOrder(dirs[dirs.length - 1], files[0]);
    294         }
    295 
    296         for (int i = 0; i < files.length - 1; ++i) {
    297             assertOrder(files[i], files[i + 1]);
    298         }
    299     }
    300 
    301     public void rightClickDocument(String label) throws UiObjectNotFoundException {
    302         Rect startCoord = findDocument(label).getBounds();
    303         rightClickDocument(new Point(startCoord.centerX(), startCoord.centerY()));
    304     }
    305 
    306     public void rightClickDocument(Point point) throws UiObjectNotFoundException {
    307         //TODO: Use Espresso instead of doing the events mock ourselves
    308         MotionEvent motionDown = getTestMotionEvent(
    309                 MotionEvent.ACTION_DOWN,
    310                 MotionEvent.BUTTON_SECONDARY,
    311                 MotionEvent.TOOL_TYPE_MOUSE,
    312                 InputDevice.SOURCE_MOUSE,
    313                 point.x,
    314                 point.y);
    315         mAutomation.injectInputEvent(motionDown, true);
    316         SystemClock.sleep(100);
    317 
    318         MotionEvent motionUp = getTestMotionEvent(
    319                 MotionEvent.ACTION_UP,
    320                 MotionEvent.BUTTON_SECONDARY,
    321                 MotionEvent.TOOL_TYPE_MOUSE,
    322                 InputDevice.SOURCE_MOUSE,
    323                 point.x,
    324                 point.y);
    325 
    326         mAutomation.injectInputEvent(motionUp, true);
    327     }
    328 
    329     private MotionEvent getTestMotionEvent(
    330             int action, int buttonState, int toolType, int source, int x, int y) {
    331         long eventTime = SystemClock.uptimeMillis();
    332 
    333         MotionEvent.PointerProperties[] pp = {new MotionEvent.PointerProperties()};
    334         pp[0].clear();
    335         pp[0].id = 0;
    336         pp[0].toolType = toolType;
    337 
    338         MotionEvent.PointerCoords[] pointerCoords = {new MotionEvent.PointerCoords()};
    339         pointerCoords[0].clear();
    340         pointerCoords[0].x = x;
    341         pointerCoords[0].y = y;
    342         pointerCoords[0].pressure = 0;
    343         pointerCoords[0].size = 1;
    344 
    345         return MotionEvent.obtain(
    346                 eventTime,
    347                 eventTime,
    348                 action,
    349                 1,
    350                 pp,
    351                 pointerCoords,
    352                 0,
    353                 buttonState,
    354                 1f,
    355                 1f,
    356                 0,
    357                 0,
    358                 source,
    359                 0);
    360     }
    361 
    362     private void assertOrder(String first, String second) throws UiObjectNotFoundException {
    363 
    364         final UiObject firstObj = findDocument(first);
    365         final UiObject secondObj = findDocument(second);
    366 
    367         final int layoutDirection = mContext.getResources().getConfiguration().getLayoutDirection();
    368         final Rect firstBound = firstObj.getVisibleBounds();
    369         final Rect secondBound = secondObj.getVisibleBounds();
    370         if (layoutDirection == View.LAYOUT_DIRECTION_LTR) {
    371             assertTrue(
    372                     "\"" + first + "\" is not located above or to the left of \"" + second
    373                             + "\" in LTR",
    374                     firstBound.bottom < secondBound.top || firstBound.right < secondBound.left);
    375         } else {
    376             assertTrue(
    377                     "\"" + first + "\" is not located above or to the right of \"" + second +
    378                             "\" in RTL",
    379                     firstBound.bottom < secondBound.top || firstBound.left > secondBound.right);
    380         }
    381     }
    382 }
    383