UiBot.java revision a86a3012ef689af659261b209da86f2e24ac21ec
1e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme/* 2e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Copyright (C) 2015 The Android Open Source Project 3e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 4e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Licensed under the Apache License, Version 2.0 (the "License"); 5e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * you may not use this file except in compliance with the License. 6e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * You may obtain a copy of the License at 7e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 8e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * http://www.apache.org/licenses/LICENSE-2.0 9e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 10e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Unless required by applicable law or agreed to in writing, software 11e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * distributed under the License is distributed on an "AS IS" BASIS, 12e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * See the License for the specific language governing permissions and 14e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * limitations under the License. 15e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 16e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 17e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemepackage com.android.shell; 18e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 19a86a3012ef689af659261b209da86f2e24ac21ecFelipe Lemeimport android.app.Instrumentation; 20a86a3012ef689af659261b209da86f2e24ac21ecFelipe Lemeimport android.app.StatusBarManager; 21e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.By; 22e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiDevice; 23e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiObject; 24e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiObjectNotFoundException; 25a413143afc73792e31d7dca90a7690e7f4352469Felipe Lemeimport android.support.test.uiautomator.UiScrollable; 26e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiSelector; 27e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.Until; 28e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.util.Log; 29a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme 30a86a3012ef689af659261b209da86f2e24ac21ecFelipe Lemeimport static junit.framework.Assert.assertFalse; 31e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport static junit.framework.Assert.assertTrue; 32e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 33e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme/** 34e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * A helper class for UI-related testing tasks. 35e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 36e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemefinal class UiBot { 37e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 38e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme private static final String TAG = "UiBot"; 393fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; 40e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 41a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme private final Instrumentation mInstrumentation; 42e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme private final UiDevice mDevice; 43e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme private final int mTimeout; 44e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 45a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme public UiBot(Instrumentation instrumentation, int timeout) { 46a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme mInstrumentation = instrumentation; 47a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme mDevice = UiDevice.getInstance(instrumentation); 48e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme mTimeout = timeout; 49e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 50e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 51e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 5269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Opens the system notification and gets a given notification. 53e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 54e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param text Notificaton's text as displayed by the UI. 5569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * @return notification object. 56e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 5769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public UiObject getNotification(String text) { 58e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme boolean opened = mDevice.openNotification(); 59e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme Log.v(TAG, "openNotification(): " + opened); 603fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout); 613fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme assertTrue("could not get system ui (" + SYSTEMUI_PACKAGE + ")", gotIt); 62e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 6369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return getObject(text); 6469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 65e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 66a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme public void closeNotifications() throws Exception { 67a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme // TODO: mDevice should provide such method.. 68a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme StatusBarManager sbm = 69a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme (StatusBarManager) mInstrumentation.getContext().getSystemService("statusbar"); 70a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme sbm.collapsePanels(); 71a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme } 72a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme 7369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 7469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Opens the system notification and clicks a given notification. 7569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * 7669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * @param text Notificaton's text as displayed by the UI. 7769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 7869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void clickOnNotification(String text) { 7969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme UiObject notification = getNotification(text); 80e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme click(notification, "bug report notification"); 81e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 82e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 83e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 8469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Gets an object that might not yet be available in current UI. 8569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * 8669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * @param text Object's text as displayed by the UI. 8769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 8869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public UiObject getObject(String text) { 8969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout); 9069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme assertTrue("object with text '(" + text + "') not visible yet", gotIt); 9169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return getVisibleObject(text); 9269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 9369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 9469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 95bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Gets an object that might not yet be available in current UI. 96bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * 97bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * @param id Object's fully-qualified resource id (like {@code android:id/button1}) 98bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 99bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public UiObject getObjectById(String id) { 100bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.res(id)), mTimeout); 101bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme assertTrue("object with id '(" + id + "') not visible yet", gotIt); 102bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return getVisibleObjectById(id); 103bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 104bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 105bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 10669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Gets an object which is guaranteed to be present in the current UI. 107e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 108e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param text Object's text as displayed by the UI. 109e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 110e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme public UiObject getVisibleObject(String text) { 111e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme UiObject uiObject = mDevice.findObject(new UiSelector().text(text)); 11269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme assertTrue("could not find object with text '" + text + "'", uiObject.exists()); 113e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme return uiObject; 114e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 115e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 116e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 117bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Gets an object which is guaranteed to be present in the current UI. 118bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * 119bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * @param text Object's text as displayed by the UI. 120bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 121bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public UiObject getVisibleObjectById(String id) { 122bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme UiObject uiObject = mDevice.findObject(new UiSelector().resourceId(id)); 123bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme assertTrue("could not find object with id '" + id+ "'", uiObject.exists()); 124bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return uiObject; 125bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 126bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 127a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme /** 128a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme * Asserts an object is not visible. 129a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme */ 130a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme public void assertNotVisibleById(String id) { 131a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme // TODO: not working when the bugreport dialog is shown, it hangs until the dialog is 132a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme // dismissed and hence always work. 133a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme boolean hasIt = mDevice.hasObject(By.res(id)); 134a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme assertFalse("should not have found object with id '" + id+ "'", hasIt); 135a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme } 136a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme 137bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 138bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 139e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Clicks on a UI element. 140e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 141e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param uiObject UI element to be clicked. 142e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param description Elements's description used on logging statements. 143e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 144e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme public void click(UiObject uiObject, String description) { 145e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme try { 146e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme boolean clicked = uiObject.click(); 147e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme // TODO: assertion below fails sometimes, even though the click succeeded, 148e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme // (specially when clicking the "Just Once" button), so it's currently just logged. 149e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme // assertTrue("could not click on object '" + description + "'", clicked); 150e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 151e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme Log.v(TAG, "onClick for " + description + ": " + clicked); 152e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } catch (UiObjectNotFoundException e) { 153e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme throw new IllegalStateException("exception when clicking on object '" + description 154e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme + "'", e); 155e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 156e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 157e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 158e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 159e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Chooses a given activity to handle an Intent, using the "Just Once" button. 160e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 161e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param name name of the activity as displayed in the UI (typically the value set by 162e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * {@code android:label} in the manifest). 163e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 164e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme // TODO: UI Automator should provide such logic. 165e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme public void chooseActivity(String name) { 166a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme // First check if the activity is the default option. 167ba477939f0ae38926b4b0a6501a2371acc612433Felipe Leme String shareText = "Share with " + name; 168ba477939f0ae38926b4b0a6501a2371acc612433Felipe Leme Log.v(TAG, "Waiting for ActivityChooser text: '" + shareText + "'"); 169a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.text(shareText)), mTimeout); 1703fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme boolean justOnceHack = false; 171a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme 172e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme if (gotIt) { 173a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme Log.v(TAG, "Found activity " + name + ", it's the default action"); 1743fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme clickJustOnce(); 175e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } else { 176a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme // Since it's not, need to find it in the scrollable list... 177a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme Log.v(TAG, "Activity " + name + " is not default action"); 178a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme UiScrollable activitiesList = new UiScrollable(new UiSelector().scrollable(true)); 179a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme try { 180a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme activitiesList.scrollForward(); 181a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme } catch (UiObjectNotFoundException e) { 1823fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme // TODO: for some paranormal issue, the first time a test is run the scrollable 1833fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme // activity list is displayed but calling scrollForwad() (or even isScrollable()) 1843fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme // throws a "UiObjectNotFoundException: UiSelector[SCROLLABLE=true]" exception 1853fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme justOnceHack = true; 1863fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme Log.d(TAG, "could not scroll forward", e); 187a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme } 1883fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme UiObject activity = getVisibleObject(name); 189a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme // ... then select it. 190a413143afc73792e31d7dca90a7690e7f4352469Felipe Leme click(activity, name); 1913fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme if (justOnceHack) { 1923fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme clickJustOnce(); 1933fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme } 194e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 195e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 196bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 1973fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme private void clickJustOnce() { 1983fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.res("android", "button_once")), mTimeout); 1993fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme assertTrue("'Just Once' button not visible yet", gotIt); 2003fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme 2013fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme UiObject justOnce = mDevice 2023fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme .findObject(new UiSelector().resourceId("android:id/button_once")); 2033fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme assertTrue("'Just Once' button not found", justOnce.exists()); 2043fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme 2053fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme click(justOnce, "Just Once"); 2063fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme } 2073fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme 208bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void pressBack() { 209bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDevice.pressBack(); 210bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 211e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme} 212