1 /*
2  * Copyright (C) 2022 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 androidx.test.uiautomator.testapp;
18 
19 import static org.junit.Assert.assertEquals;
20 import static org.junit.Assert.assertFalse;
21 import static org.junit.Assert.assertThrows;
22 import static org.junit.Assert.assertTrue;
23 
24 import android.graphics.Point;
25 import android.graphics.Rect;
26 
27 import androidx.test.filters.SdkSuppress;
28 import androidx.test.uiautomator.UiObject;
29 import androidx.test.uiautomator.UiSelector;
30 
31 import org.junit.Test;
32 
33 public class UiObjectTest extends BaseTest {
34 
35     // Convenience method to create a selector from a resource ID.
resourceId(String id)36     private static UiSelector resourceId(String id) {
37         return new UiSelector().resourceId(TEST_APP + ":id/" + id);
38     }
39 
40     // Supplier that can throw exceptions.
41     private interface ThrowingSupplier<T> {
getOrThrow()42         T getOrThrow() throws Exception;
43     }
44 
45     // Asserts that a supplier returns true within the default timeout.
awaitTrue(String message, ThrowingSupplier<Boolean> supplier)46     private void awaitTrue(String message, ThrowingSupplier<Boolean> supplier) {
47         assertTrue(message, mDevice.wait(d -> {
48             try {
49                 return supplier.getOrThrow();
50             } catch (Exception e) {
51                 return false;
52             }
53         }, TIMEOUT_MS));
54     }
55 
56     @Test
testGetChild()57     public void testGetChild() throws Exception {
58         launchTestActivity(ParentChildTestActivity.class);
59 
60         UiObject treeN2 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/tree_N2"));
61         UiObject treeN3 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/tree_N3"));
62 
63         assertFalse(
64                 treeN2.getChild(new UiSelector().resourceId(TEST_APP + ":id/tree_N4")).exists());
65         assertTrue(treeN3.getChild(new UiSelector().resourceId(TEST_APP + ":id/tree_N4")).exists());
66     }
67 
68     @Test
testGetFromParent()69     public void testGetFromParent() throws Exception {
70         launchTestActivity(ParentChildTestActivity.class);
71 
72         UiObject treeN4 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/tree_N4"));
73 
74         assertFalse(treeN4.getFromParent(
75                 new UiSelector().resourceId(TEST_APP + ":id/tree_N2")).exists());
76         assertTrue(treeN4.getFromParent(
77                 new UiSelector().resourceId(TEST_APP + ":id/tree_N5")).exists());
78     }
79 
80     @Test
testGetChildCount()81     public void testGetChildCount() throws Exception {
82         launchTestActivity(ParentChildTestActivity.class);
83 
84         UiObject treeN2 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/tree_N2"));
85         UiObject treeN3 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/tree_N3"));
86 
87         assertEquals(0, treeN2.getChildCount());
88         assertEquals(2, treeN3.getChildCount());
89     }
90 
91     @Test
testGetChildCount_throwsUiObjectNotFoundException()92     public void testGetChildCount_throwsUiObjectNotFoundException() {
93         launchTestActivity(ParentChildTestActivity.class);
94 
95         UiObject noNode = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/no_node"));
96 
97         assertUiObjectNotFound(noNode::getChildCount);
98     }
99 
100     @Test
101     @SdkSuppress(minSdkVersion = 24)
testDragTo_destObjAndSteps()102     public void testDragTo_destObjAndSteps() throws Exception {
103         launchTestActivity(DragTestActivity.class);
104 
105         UiObject dragButton = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
106                 + "/drag_button"));
107         UiObject dragDestination = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
108                 + "/drag_destination"));
109 
110         UiObject expectedDragDest = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
111                 + "/drag_destination").text("drag_received"));
112 
113         assertEquals("no_drag_yet", dragDestination.getText());
114         // Returning true from `dragTo` means that the drag action is performed successfully, not
115         // necessarily the target is dragged to the desired destination.
116         // The same applies to all the following tests.
117         assertTrue(dragButton.dragTo(dragDestination, 40));
118         assertTrue(expectedDragDest.waitForExists(TIMEOUT_MS));
119     }
120 
121     @Test
122     @SdkSuppress(minSdkVersion = 24)
testDragTo_destXAndDestYAndSteps()123     public void testDragTo_destXAndDestYAndSteps() throws Exception {
124         launchTestActivity(DragTestActivity.class);
125 
126         UiObject dragButton = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
127                 + "/drag_button"));
128         UiObject dragDestination = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
129                 + "/drag_destination"));
130 
131         UiObject expectedDragDest = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
132                 + "/drag_destination").text("drag_received"));
133         Rect destBounds = dragDestination.getVisibleBounds();
134 
135         assertEquals("no_drag_yet", dragDestination.getText());
136         assertTrue(dragButton.dragTo(destBounds.centerX(), destBounds.centerY(), 40));
137         assertTrue(expectedDragDest.waitForExists(TIMEOUT_MS));
138     }
139 
140     @Test
testSwipeUp()141     public void testSwipeUp() throws Exception {
142         launchTestActivity(SwipeTestActivity.class);
143 
144         UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
145                 + "/swipe_region"));
146         UiObject verySmallRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
147                 + "/very_small_region"));
148 
149         UiObject expectedSwipeRegion = mDevice.findObject(
150                 new UiSelector().resourceId(TEST_APP + ":id"
151                         + "/swipe_region").text("swipe_up"));
152 
153         // Note that the `swipeRegion` will always show the swipe direction, even if the swipe
154         // action does not happen inside `swipeRegion`.
155         assertFalse(verySmallRegion.swipeUp(10));
156         assertEquals("no_swipe", swipeRegion.getText());
157         assertTrue(swipeRegion.swipeUp(10));
158         assertTrue(expectedSwipeRegion.waitForExists(TIMEOUT_MS));
159     }
160 
161     @Test
testSwipeDown()162     public void testSwipeDown() throws Exception {
163         launchTestActivity(SwipeTestActivity.class);
164 
165         UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
166                 + "/swipe_region"));
167         UiObject verySmallRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
168                 + "/very_small_region"));
169 
170         UiObject expectedSwipeRegion = mDevice.findObject(
171                 new UiSelector().resourceId(TEST_APP + ":id"
172                         + "/swipe_region").text("swipe_down"));
173 
174         assertFalse(verySmallRegion.swipeDown(10));
175         assertEquals("no_swipe", swipeRegion.getText());
176         assertTrue(swipeRegion.swipeDown(10));
177         assertTrue(expectedSwipeRegion.waitForExists(TIMEOUT_MS));
178     }
179 
180     @Test
testSwipeLeft()181     public void testSwipeLeft() throws Exception {
182         launchTestActivity(SwipeTestActivity.class);
183 
184         UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
185                 + "/swipe_region"));
186         UiObject verySmallRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
187                 + "/very_small_region"));
188 
189         UiObject expectedSwipeRegion = mDevice.findObject(
190                 new UiSelector().resourceId(TEST_APP + ":id"
191                         + "/swipe_region").text("swipe_left"));
192 
193         assertFalse(verySmallRegion.swipeLeft(10));
194         assertEquals("no_swipe", swipeRegion.getText());
195         assertTrue(swipeRegion.swipeLeft(10));
196         assertTrue(expectedSwipeRegion.waitForExists(TIMEOUT_MS));
197     }
198 
199     @Test
testSwipeRight()200     public void testSwipeRight() throws Exception {
201         launchTestActivity(SwipeTestActivity.class);
202 
203         UiObject swipeRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
204                 + "/swipe_region"));
205         UiObject verySmallRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
206                 + "/very_small_region"));
207 
208         UiObject expectedSwipeRegion = mDevice.findObject(
209                 new UiSelector().resourceId(TEST_APP + ":id"
210                         + "/swipe_region").text("swipe_right"));
211 
212         assertFalse(verySmallRegion.swipeRight(10));
213         assertEquals("no_swipe", swipeRegion.getText());
214         assertTrue(swipeRegion.swipeRight(10));
215         assertTrue(expectedSwipeRegion.waitForExists(TIMEOUT_MS));
216     }
217 
218     @Test
testClick()219     public void testClick() throws Exception {
220         launchTestActivity(ClickTestActivity.class);
221 
222         UiObject button1 = mDevice.findObject(
223                 new UiSelector().resourceId(TEST_APP + ":id/button1"));
224 
225         UiObject expectedButton1 = mDevice.findObject(
226                 new UiSelector().resourceId(TEST_APP + ":id/button1").text("text1_clicked"));
227 
228         assertEquals("text1", button1.getText());
229         assertTrue(button1.click());
230         assertTrue(expectedButton1.waitForExists(TIMEOUT_MS));
231     }
232 
233     @Test
testClickAndWaitForNewWindow()234     public void testClickAndWaitForNewWindow() throws Exception {
235         launchTestActivity(ClickAndWaitTestActivity.class);
236 
237         UiObject newWindowsButton = mDevice.findObject(
238                 new UiSelector().resourceId(TEST_APP + ":id/new_window_button"));
239 
240         assertTrue(newWindowsButton.clickAndWaitForNewWindow());
241     }
242 
243     @Test
testClickAndWaitForNewWindow_timeout()244     public void testClickAndWaitForNewWindow_timeout() throws Exception {
245         launchTestActivity(ClickAndWaitTestActivity.class);
246 
247         UiObject newWindowsButton = mDevice.findObject(
248                 new UiSelector().resourceId(TEST_APP + ":id/new_window_button"));
249 
250         assertTrue(newWindowsButton.clickAndWaitForNewWindow(4_000));
251     }
252 
253     @Test
testClickTopLeft()254     public void testClickTopLeft() throws Exception {
255         launchTestActivity(ClickOnPositionTestActivity.class);
256 
257         UiObject clickRegion = mDevice.findObject(resourceId("click_region"));
258 
259         assertEquals("click_region", clickRegion.getText());
260         assertTrue(clickRegion.clickTopLeft());
261         awaitTrue("Click not detected", () -> "top_left_clicked".equals(clickRegion.getText()));
262     }
263 
264     @Test
testLongClickBottomRight()265     public void testLongClickBottomRight() throws Exception {
266         launchTestActivity(ClickOnPositionTestActivity.class);
267 
268         UiObject clickRegion = mDevice.findObject(resourceId("click_region"));
269 
270         assertEquals("click_region", clickRegion.getText());
271         assertTrue(clickRegion.longClickBottomRight());
272         awaitTrue("Click not detected",
273                 () -> "bottom_right_long_clicked".equals(clickRegion.getText()));
274     }
275 
276     @Test
testClickBottomRight()277     public void testClickBottomRight() throws Exception {
278         launchTestActivity(ClickOnPositionTestActivity.class);
279 
280         UiObject clickRegion = mDevice.findObject(resourceId("click_region"));
281 
282         assertEquals("click_region", clickRegion.getText());
283         assertTrue(clickRegion.clickBottomRight());
284         awaitTrue("Click not detected", () -> "bottom_right_clicked".equals(clickRegion.getText()));
285     }
286 
287     @Test
testLongClick()288     public void testLongClick() throws Exception {
289         launchTestActivity(ClickOnPositionTestActivity.class);
290 
291         UiObject clickRegion = mDevice.findObject(resourceId("click_region"));
292 
293         assertEquals("click_region", clickRegion.getText());
294         assertTrue(clickRegion.longClick());
295         awaitTrue("Click not detected", () -> clickRegion.getText().contains("_long_clicked"));
296     }
297 
298     @Test
testLongClickTopLeft()299     public void testLongClickTopLeft() throws Exception {
300         launchTestActivity(ClickOnPositionTestActivity.class);
301 
302         UiObject clickRegion = mDevice.findObject(resourceId("click_region"));
303 
304         assertEquals("click_region", clickRegion.getText());
305         assertTrue(clickRegion.longClickTopLeft());
306         awaitTrue("Click not detected",
307                 () -> "top_left_long_clicked".equals(clickRegion.getText()));
308     }
309 
310     @Test
testClickFamily_throwsUiObjectNotFoundException()311     public void testClickFamily_throwsUiObjectNotFoundException() {
312         launchTestActivity(ClickTestActivity.class);
313 
314         UiObject noNode = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/no_node"));
315 
316         assertUiObjectNotFound(noNode::click);
317         assertUiObjectNotFound(noNode::clickAndWaitForNewWindow);
318         assertUiObjectNotFound(noNode::clickTopLeft);
319         assertUiObjectNotFound(noNode::longClickBottomRight);
320         assertUiObjectNotFound(noNode::clickBottomRight);
321         assertUiObjectNotFound(noNode::longClick);
322         assertUiObjectNotFound(noNode::longClickTopLeft);
323     }
324 
325     @Test
testGetText()326     public void testGetText() throws Exception {
327         launchTestActivity(MainActivity.class);
328 
329         UiObject sampleTextObject = mDevice.findObject(new UiSelector().text("Sample text"));
330         UiObject nullTextObject = mDevice.findObject(
331                 new UiSelector().resourceId(TEST_APP + ":id/nested_elements"));
332 
333         assertEquals("Sample text", sampleTextObject.getText());
334         assertEquals("", nullTextObject.getText());
335     }
336 
337     @Test
testGetClassName()338     public void testGetClassName() throws Exception {
339         launchTestActivity(MainActivity.class);
340 
341         UiObject button = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/button"));
342         UiObject textView = mDevice.findObject(
343                 new UiSelector().resourceId(TEST_APP + ":id/example_id"));
344 
345         assertEquals("android.widget.Button", button.getClassName());
346         assertEquals("android.widget.TextView", textView.getClassName());
347     }
348 
349     @Test
testGetContentDescription()350     public void testGetContentDescription() throws Exception {
351         launchTestActivity(MainActivity.class);
352 
353         UiObject button = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/button"));
354         UiObject textView = mDevice.findObject(new UiSelector().text("Text View 1"));
355 
356         assertEquals("I'm accessible!", button.getContentDescription());
357         assertEquals("", textView.getContentDescription());
358     }
359 
360     @Test
testSetText()361     public void testSetText() throws Exception {
362         launchTestActivity(ClearTextTestActivity.class);
363 
364         UiObject editText = mDevice.findObject(resourceId("edit_text"));
365 
366         assertEquals("sample_text", editText.getText());
367         editText.setText("new_text");
368         awaitTrue("Text not set", () -> "new_text".equals(editText.getText()));
369     }
370 
371     @Test
testClearTextField()372     public void testClearTextField() throws Exception {
373         launchTestActivity(ClearTextTestActivity.class);
374 
375         UiObject editText = mDevice.findObject(resourceId("edit_text"));
376 
377         assertEquals("sample_text", editText.getText());
378         editText.clearTextField();
379         awaitTrue("Text not cleared", () -> editText.getText().isEmpty());
380     }
381 
382     @Test
testTextFamily_throwsUiObjectNotFoundException()383     public void testTextFamily_throwsUiObjectNotFoundException() {
384         launchTestActivity(ClearTextTestActivity.class);
385 
386         UiObject noNode = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/no_node"));
387 
388         assertUiObjectNotFound(noNode::getText);
389         assertUiObjectNotFound(noNode::getClassName);
390         assertUiObjectNotFound(noNode::getContentDescription);
391         assertUiObjectNotFound(() -> noNode.setText("new_text"));
392         assertUiObjectNotFound(noNode::clearTextField);
393         assertUiObjectNotFound(noNode::getPackageName);
394     }
395 
396     @Test
testIsChecked()397     public void testIsChecked() throws Exception {
398         launchTestActivity(ClickTestActivity.class);
399 
400         UiObject checkBox = mDevice.findObject(resourceId("check_box"));
401 
402         assertFalse(checkBox.isChecked());
403         checkBox.click();
404         awaitTrue("Checkbox not checked", checkBox::isChecked);
405     }
406 
407     @Test
testIsSelected()408     public void testIsSelected() throws Exception {
409         launchTestActivity(IsSelectedTestActivity.class);
410 
411         UiObject selectedButton = mDevice.findObject(resourceId("selected_button"));
412         UiObject selectedTarget = mDevice.findObject(resourceId("selected_target"));
413 
414         assertFalse(selectedTarget.isSelected());
415         selectedButton.click();
416         awaitTrue("Target not selected", selectedTarget::isSelected);
417     }
418 
419     @Test
testIsCheckable()420     public void testIsCheckable() throws Exception {
421         launchTestActivity(ClickTestActivity.class);
422 
423         UiObject checkBox = mDevice.findObject(
424                 new UiSelector().resourceId(TEST_APP + ":id/check_box"));
425         UiObject button1 = mDevice.findObject(
426                 new UiSelector().resourceId(TEST_APP + ":id/button1"));
427 
428         assertTrue(checkBox.isCheckable());
429         assertFalse(button1.isCheckable());
430     }
431 
432     @Test
testIsEnabled()433     public void testIsEnabled() throws Exception {
434         launchTestActivity(IsEnabledTestActivity.class);
435 
436         UiObject disabledObject = mDevice.findObject(
437                 new UiSelector().resourceId(TEST_APP + ":id/disabled_text_view"));
438         UiObject enabledObject = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
439                 + "/enabled_text_view"));
440 
441         assertFalse(disabledObject.isEnabled());
442         assertTrue(enabledObject.isEnabled());
443     }
444 
445     @Test
testIsClickable()446     public void testIsClickable() throws Exception {
447         launchTestActivity(MainActivity.class);
448 
449         UiObject textView = mDevice.findObject(new UiSelector().text("Sample text"));
450         UiObject button = mDevice.findObject(new UiSelector().text("Accessible button"));
451 
452         assertFalse(textView.isClickable());
453         assertTrue(button.isClickable());
454     }
455 
456     @Test
testIsFocused()457     public void testIsFocused() throws Exception {
458         launchTestActivity(IsFocusedTestActivity.class);
459 
460         UiObject textView = mDevice.findObject(resourceId("focusable_text_view"));
461         UiObject button = mDevice.findObject(resourceId("button"));
462 
463         assertFalse(textView.isFocused());
464         button.click();
465         awaitTrue("TextView not focused", textView::isFocused);
466     }
467 
468     @Test
testIsFocusable()469     public void testIsFocusable() throws Exception {
470         launchTestActivity(IsFocusedTestActivity.class);
471 
472         UiObject nonFocusableTextView =
473                 mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
474                         + "/non_focusable_text_view"));
475         UiObject focusableTextView = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
476                 + "/focusable_text_view"));
477 
478         assertFalse(nonFocusableTextView.isFocusable());
479         assertTrue(focusableTextView.isFocusable());
480     }
481 
482     @Test
testIsScrollable()483     public void testIsScrollable() throws Exception {
484         launchTestActivity(VerticalScrollTestActivity.class);
485 
486         UiObject scrollView = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
487                 + "/scroll_view"));
488         UiObject textView = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
489                 + "/top_text"));
490 
491         assertTrue(scrollView.isScrollable());
492         assertFalse(textView.isScrollable());
493     }
494 
495     @Test
testIsLongClickable()496     public void testIsLongClickable() throws Exception {
497         launchTestActivity(IsLongClickableTestActivity.class);
498 
499         UiObject longClickableButton = mDevice.findObject(
500                 new UiSelector().resourceId(TEST_APP + ":id/long_clickable_button"));
501         UiObject nonLongClickableButton = mDevice.findObject(
502                 new UiSelector().resourceId(TEST_APP + ":id/non_long_clickable_button"));
503 
504         assertTrue(longClickableButton.isLongClickable());
505         assertFalse(nonLongClickableButton.isLongClickable());
506     }
507 
508     @Test
testAttributeCheckingMethods_throwsUiObjectNotFoundException()509     public void testAttributeCheckingMethods_throwsUiObjectNotFoundException() {
510         launchTestActivity(ClickTestActivity.class);
511 
512         UiObject noNode = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/no_node"));
513 
514         assertUiObjectNotFound(noNode::isChecked);
515         assertUiObjectNotFound(noNode::isSelected);
516         assertUiObjectNotFound(noNode::isCheckable);
517         assertUiObjectNotFound(noNode::isEnabled);
518         assertUiObjectNotFound(noNode::isClickable);
519         assertUiObjectNotFound(noNode::isFocused);
520         assertUiObjectNotFound(noNode::isFocusable);
521         assertUiObjectNotFound(noNode::isScrollable);
522         assertUiObjectNotFound(noNode::isLongClickable);
523     }
524 
525     @Test
testGetPackageName()526     public void testGetPackageName() throws Exception {
527         launchTestActivity(MainActivity.class);
528 
529         UiObject sampleTextObject = mDevice.findObject(new UiSelector().text("Sample text"));
530 
531         assertEquals(TEST_APP, sampleTextObject.getPackageName());
532     }
533 
534     @Test
testGetVisibleBounds()535     public void testGetVisibleBounds() throws Exception {
536         launchTestActivity(VisibleBoundsTestActivity.class);
537 
538         UiObject partlyInvisibleRegion =
539                 mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
540                         + "/partly_invisible_region"));
541         UiObject regionInsideScrollable =
542                 mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
543                         + "/region_inside_scrollable"));
544 
545         partlyInvisibleRegion.click();
546         regionInsideScrollable.click();
547         assertEquals(partlyInvisibleRegion.getText(),
548                 partlyInvisibleRegion.getVisibleBounds().toString());
549         assertEquals(regionInsideScrollable.getText(),
550                 regionInsideScrollable.getVisibleBounds().toString());
551     }
552 
553     @Test
554     @SdkSuppress(minSdkVersion = 23) // Bounds include invisible regions prior to API 23.
testGetBounds()555     public void testGetBounds() throws Exception {
556         launchTestActivity(VisibleBoundsTestActivity.class);
557 
558         UiObject partlyInvisibleRegion =
559                 mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
560                         + "/partly_invisible_region"));
561 
562         partlyInvisibleRegion.click();
563         assertEquals(partlyInvisibleRegion.getText(),
564                 partlyInvisibleRegion.getBounds().toString());
565     }
566 
567     @Test
testWaitForExists()568     public void testWaitForExists() throws Exception {
569         launchTestActivity(WaitTestActivity.class);
570 
571         UiObject text1 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/text_1"));
572         assertEquals("text_1_not_changed", text1.getText());
573 
574         UiObject expectedText1 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
575                 + "/text_1").text("text_1_changed"));
576 
577         text1.click();
578         assertTrue(expectedText1.waitForExists(TIMEOUT_MS));
579     }
580 
581     @Test
testWaitForExist_timeout()582     public void testWaitForExist_timeout() throws Exception {
583         launchTestActivity(WaitTestActivity.class);
584 
585         UiObject text1 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/text_1"));
586         assertEquals("text_1_not_changed", text1.getText());
587 
588         UiObject expectedText1 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
589                 + "/text_1").text("text_1_changed"));
590 
591         assertFalse(expectedText1.waitForExists(1_000));
592     }
593 
594     @Test
testWaitUntilGone()595     public void testWaitUntilGone() throws Exception {
596         launchTestActivity(WaitTestActivity.class);
597 
598         UiObject expectedText1 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
599                 + "/text_1").text("text_1_not_changed"));
600         assertTrue(expectedText1.exists());
601         expectedText1.click();
602         assertTrue(expectedText1.waitUntilGone(TIMEOUT_MS));
603     }
604 
605     @Test
testWaitUntilGone_timeout()606     public void testWaitUntilGone_timeout() {
607         launchTestActivity(WaitTestActivity.class);
608 
609         UiObject expectedText1 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
610                 + "/text_1").text("text_1_not_changed"));
611         assertTrue(expectedText1.exists());
612         assertFalse(expectedText1.waitUntilGone(1_000));
613     }
614 
615     @Test
testExists()616     public void testExists() {
617         launchTestActivity(WaitTestActivity.class);
618 
619         UiObject text1 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/text_1"));
620         UiObject text3 = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/text_3"));
621 
622         assertTrue(text1.exists());
623         assertFalse(text3.exists());
624     }
625 
626     @Test
testPinchOut()627     public void testPinchOut() throws Exception {
628         launchTestActivity(PinchTestActivity.class);
629 
630         UiObject pinchArea = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
631                 + "/pinch_area"));
632         UiObject scaleText = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
633                 + "/scale_factor"));
634 
635         UiObject expectedScaleText = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
636                 + "/scale_factor").text("1.0f"));
637 
638         assertTrue(pinchArea.pinchOut(0, 10));
639         assertFalse(expectedScaleText.waitUntilGone(TIMEOUT_MS));
640         assertTrue(pinchArea.pinchOut(100, 10));
641         assertTrue(expectedScaleText.waitUntilGone(TIMEOUT_MS));
642         float scaleValueAfterPinch = Float.parseFloat(scaleText.getText());
643         assertTrue(String.format(
644                 "Expected scale text to be greater than 1f after pinchOut(), but got [%f]",
645                 scaleValueAfterPinch), scaleValueAfterPinch > 1f);
646     }
647 
648     @Test
testPinchIn()649     public void testPinchIn() throws Exception {
650         launchTestActivity(PinchTestActivity.class);
651 
652         UiObject pinchArea = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
653                 + "/pinch_area"));
654         UiObject scaleText = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
655                 + "/scale_factor"));
656 
657         UiObject expectedScaleText = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
658                 + "/scale_factor").text("1.0f"));
659 
660         assertTrue(pinchArea.pinchIn(0, 10));
661         assertFalse(expectedScaleText.waitUntilGone(TIMEOUT_MS));
662         assertTrue(pinchArea.pinchIn(100, 10));
663         assertTrue(expectedScaleText.waitUntilGone(TIMEOUT_MS));
664         float scaleValueAfterPinch = Float.parseFloat(scaleText.getText());
665         assertTrue(String.format("Expected scale value to be less than 1f after pinchIn(), "
666                 + "but got [%f]", scaleValueAfterPinch), scaleValueAfterPinch < 1f);
667     }
668 
669     @Test
670     public void testPinchFamily_throwsExceptions() {
671         launchTestActivity(PinchTestActivity.class);
672 
673         UiObject noNode = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id/no_node"));
674         UiObject smallArea = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
675                 + "/small_area"));
676 
677         assertUiObjectNotFound(() -> noNode.pinchOut(100, 10));
678         assertUiObjectNotFound(() -> noNode.pinchIn(100, 10));
679         assertThrows(IllegalStateException.class, () -> smallArea.pinchOut(100, 10));
680         assertThrows(IllegalStateException.class, () -> smallArea.pinchIn(100, 10));
681         assertThrows(IllegalArgumentException.class, () -> smallArea.pinchOut(-1, 10));
682         assertThrows(IllegalArgumentException.class, () -> smallArea.pinchOut(101, 10));
683         assertThrows(IllegalArgumentException.class, () -> smallArea.pinchIn(-1, 10));
684         assertThrows(IllegalArgumentException.class, () -> smallArea.pinchIn(101, 10));
685     }
686 
687     @Test
testPerformTwoPointerGesture_withZeroSteps()688     public void testPerformTwoPointerGesture_withZeroSteps() throws Exception {
689         // Note that most part of `performTwoPointerGesture` (and `performMultiPointerGesture`)
690         // has already been indirectly tested in other tests. This test only test the case when
691         // the `step` parameter is set to zero.
692         launchTestActivity(PointerGestureTestActivity.class);
693 
694         UiObject touchRegion = mDevice.findObject(new UiSelector().resourceId(TEST_APP + ":id"
695                 + "/touch_region"));
696 
697         Rect visibleBounds = touchRegion.getVisibleBounds();
698         Point startPoint1 = new Point(visibleBounds.left + 50, visibleBounds.top + 50);
699         Point startPoint2 = new Point(visibleBounds.right - 50, visibleBounds.top + 50);
700         Point endPoint1 = new Point(visibleBounds.left + 50, visibleBounds.bottom - 50);
701         Point endPoint2 = new Point(visibleBounds.right - 50, visibleBounds.bottom - 50);
702 
703         assertTrue(touchRegion.performTwoPointerGesture(startPoint1, startPoint2, endPoint1,
704                 endPoint2, 0));
705         assertEquals("2 touch(es) received", touchRegion.getText());
706     }
707 }
708