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