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; 25e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.UiSelector; 26e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.support.test.uiautomator.Until; 27e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport android.util.Log; 28a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme 29a86a3012ef689af659261b209da86f2e24ac21ecFelipe Lemeimport static junit.framework.Assert.assertFalse; 30e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemeimport static junit.framework.Assert.assertTrue; 31e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 32e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme/** 33e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * A helper class for UI-related testing tasks. 34e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 35e53e85f6051d20cbd477bc25d446a41996411fabFelipe Lemefinal class UiBot { 36e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 37e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme private static final String TAG = "UiBot"; 383fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme private static final String SYSTEMUI_PACKAGE = "com.android.systemui"; 39e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 40a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme private final Instrumentation mInstrumentation; 41e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme private final UiDevice mDevice; 42e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme private final int mTimeout; 43e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 44a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme public UiBot(Instrumentation instrumentation, int timeout) { 45a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme mInstrumentation = instrumentation; 46a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme mDevice = UiDevice.getInstance(instrumentation); 47e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme mTimeout = timeout; 48e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 49e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 50e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 5169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Opens the system notification and gets a given notification. 52e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 53e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param text Notificaton's text as displayed by the UI. 5469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * @return notification object. 55e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 5669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public UiObject getNotification(String text) { 57e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme boolean opened = mDevice.openNotification(); 58e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme Log.v(TAG, "openNotification(): " + opened); 593fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.pkg(SYSTEMUI_PACKAGE)), mTimeout); 603fc44b9a6274254e8d3a53b6b1e245c5f9177229Felipe Leme assertTrue("could not get system ui (" + SYSTEMUI_PACKAGE + ")", gotIt); 61e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 6269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return getObject(text); 6369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 64e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 651ac5441f572c7866a8c5040ae16723fe604960e8Felipe Leme public void collapseStatusBar() throws Exception { 66a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme // TODO: mDevice should provide such method.. 67a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme StatusBarManager sbm = 68a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme (StatusBarManager) mInstrumentation.getContext().getSystemService("statusbar"); 69a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme sbm.collapsePanels(); 70a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme } 71a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme 7269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 7369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Opens the system notification and clicks a given notification. 7469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * 7569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * @param text Notificaton's text as displayed by the UI. 7669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 7769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public void clickOnNotification(String text) { 7869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme UiObject notification = getNotification(text); 79e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme click(notification, "bug report notification"); 80e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 81e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 82e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 8369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Gets an object that might not yet be available in current UI. 8469c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * 8569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * @param text Object's text as displayed by the UI. 8669c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme */ 8769c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme public UiObject getObject(String text) { 8869c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.text(text)), mTimeout); 8969c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme assertTrue("object with text '(" + text + "') not visible yet", gotIt); 9069c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme return getVisibleObject(text); 9169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme } 9269c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme 9369c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme /** 94bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Gets an object that might not yet be available in current UI. 95bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * 96bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * @param id Object's fully-qualified resource id (like {@code android:id/button1}) 97bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 98bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public UiObject getObjectById(String id) { 99bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme boolean gotIt = mDevice.wait(Until.hasObject(By.res(id)), mTimeout); 100bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme assertTrue("object with id '(" + id + "') not visible yet", gotIt); 101bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return getVisibleObjectById(id); 102bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 103bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 104bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 10569c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme * Gets an object which is guaranteed to be present in the current UI. 106e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 107e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param text Object's text as displayed by the UI. 108e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 109e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme public UiObject getVisibleObject(String text) { 110e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme UiObject uiObject = mDevice.findObject(new UiSelector().text(text)); 11169c0292affe8be51e10afb2dbf58f0133918a2c3Felipe Leme assertTrue("could not find object with text '" + text + "'", uiObject.exists()); 112e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme return uiObject; 113e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 114e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 115e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 116bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * Gets an object which is guaranteed to be present in the current UI. 117bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * 118bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme * @param text Object's text as displayed by the UI. 119bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme */ 120bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public UiObject getVisibleObjectById(String id) { 121bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme UiObject uiObject = mDevice.findObject(new UiSelector().resourceId(id)); 122bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme assertTrue("could not find object with id '" + id+ "'", uiObject.exists()); 123bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme return uiObject; 124bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 125bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 126a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme /** 127a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme * Asserts an object is not visible. 128a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme */ 129a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme public void assertNotVisibleById(String id) { 130a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme // TODO: not working when the bugreport dialog is shown, it hangs until the dialog is 131a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme // dismissed and hence always work. 132a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme boolean hasIt = mDevice.hasObject(By.res(id)); 133a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme assertFalse("should not have found object with id '" + id+ "'", hasIt); 134a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme } 135a86a3012ef689af659261b209da86f2e24ac21ecFelipe Leme 136bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme 137bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme /** 138e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * Clicks on a UI element. 139e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 140e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param uiObject UI element to be clicked. 141e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param description Elements's description used on logging statements. 142e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 143e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme public void click(UiObject uiObject, String description) { 144e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme try { 145e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme boolean clicked = uiObject.click(); 146e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme // TODO: assertion below fails sometimes, even though the click succeeded, 147e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme // (specially when clicking the "Just Once" button), so it's currently just logged. 148e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme // assertTrue("could not click on object '" + description + "'", clicked); 149e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 150e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme Log.v(TAG, "onClick for " + description + ": " + clicked); 151e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } catch (UiObjectNotFoundException e) { 152e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme throw new IllegalStateException("exception when clicking on object '" + description 153e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme + "'", e); 154e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 155e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme } 156e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme 157e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme /** 15891699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme * Chooses a given activity to handle an Intent. 159e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * 160e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * @param name name of the activity as displayed in the UI (typically the value set by 161e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme * {@code android:label} in the manifest). 162e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme */ 163e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme public void chooseActivity(String name) { 16491699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme // It uses an intent chooser now, so just getting the activity by text is enough... 165ca5002f6bde51c0dc80281a821650ab28f36b39cFelipe Leme UiObject activity = getObject(name); 16691699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme click(activity, name); 1673fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme } 1683fb3d88d2b81122038a47c831d10f6b5ecf2b83aFelipe Leme 169bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme public void pressBack() { 170bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme mDevice.pressBack(); 171bc73ffc06fd2b5b30802cc7e8874a986626b897dFelipe Leme } 17291699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme 17391699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme public void turnScreenOn() throws Exception { 17491699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme mDevice.executeShellCommand("input keyevent KEYCODE_WAKEUP"); 17591699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme mDevice.executeShellCommand("wm dismiss-keyguard"); 17691699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme } 17791699af8c6dae2a3ca703abc410fc00c0ab5de59Felipe Leme 178e53e85f6051d20cbd477bc25d446a41996411fabFelipe Leme} 179