1 /*
2  * Copyright (C) 2016 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 androidx.test.espresso.Espresso.onView;
20 import static androidx.test.espresso.action.ViewActions.typeText;
21 import static androidx.test.espresso.matcher.ViewMatchers.hasDescendant;
22 import static androidx.test.espresso.matcher.ViewMatchers.isClickable;
23 import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
24 import static androidx.test.espresso.matcher.ViewMatchers.withId;
25 
26 import static junit.framework.Assert.assertEquals;
27 import static junit.framework.Assert.assertFalse;
28 import static junit.framework.Assert.assertTrue;
29 
30 import static org.hamcrest.CoreMatchers.allOf;
31 import static org.hamcrest.CoreMatchers.anyOf;
32 
33 import android.content.Context;
34 import android.view.View;
35 
36 import androidx.test.uiautomator.UiDevice;
37 import androidx.test.uiautomator.UiObject;
38 import androidx.test.uiautomator.UiObjectNotFoundException;
39 import androidx.test.uiautomator.UiSelector;
40 
41 import com.android.documentsui.R;
42 
43 import org.hamcrest.Matcher;
44 
45 /**
46  * A test helper class that provides support for controlling the search UI
47  * programmatically, and making assertions against the state of the UI.
48  * <p>
49  * Support for working directly with Roots and Directory view can be found in the respective bots.
50  */
51 public class SearchBot extends Bots.BaseBot {
52 
53     // Base search layout changes substantially between Ryu and Angler.
54     @SuppressWarnings("unchecked")
55     private static final Matcher<View> SEARCH_WIDGET = allOf(
56             withId(R.id.option_menu_search),
57             anyOf(isClickable(), hasDescendant(isClickable())));
58 
59     // Note that input is visible when the clicky button is not
60     // present. So to clearly qualify the two...we explicitly
61     // require this input be not clickable.
62     @SuppressWarnings("unchecked")
63     private static final Matcher<View> SEARCH_INPUT = allOf(
64             withId(androidx.appcompat.R.id.search_src_text),
65             isDisplayed());
66 
SearchBot(UiDevice device, Context context, int timeout)67     public SearchBot(UiDevice device, Context context, int timeout) {
68         super(device, context, timeout);
69     }
70 
clickIcon()71     public void clickIcon() throws UiObjectNotFoundException {
72         UiObject searchView = findSearchView();
73         searchView.click();
74     }
75 
clickSearchViewClearButton()76     public void clickSearchViewClearButton() throws UiObjectNotFoundException {
77         UiObject clear = findSearchViewClearButton();
78         clear.click();
79     }
80 
81     // Click on the search history item with specified queryText, if exists.
clickSearchHistory(String queryText)82     public void clickSearchHistory(String queryText) throws UiObjectNotFoundException {
83         UiObject history = findSearchHistoryView();
84         UiSelector historyItemSelector = new UiSelector().text(queryText);
85         mDevice.findObject(history.getSelector().childSelector(historyItemSelector)).click();
86     }
87 
setInputText(String query)88     public void setInputText(String query) throws UiObjectNotFoundException {
89         onView(SEARCH_INPUT).perform(typeText(query));
90     }
91 
assertIconVisible(boolean visible)92     public void assertIconVisible(boolean visible) {
93         if (visible) {
94             assertTrue(
95                     "Search icon should be visible.",
96                     Matchers.present(SEARCH_WIDGET));
97         } else {
98             assertFalse(
99                     "Search icon should not be visible.",
100                     Matchers.present(SEARCH_WIDGET));
101         }
102     }
103 
assertSearchHistoryVisible(boolean visible)104     public void assertSearchHistoryVisible(boolean visible) {
105         if (visible) {
106             assertTrue(
107                     "Search fragment should be shown.",
108                     findSearchHistoryView().exists());
109         } else {
110             assertFalse(
111                     "Search fragment should be dismissed.",
112                     findSearchHistoryView().exists());
113         }
114     }
115 
assertInputEquals(String query)116     public void assertInputEquals(String query)
117             throws UiObjectNotFoundException {
118         UiObject textField = findSearchViewTextField();
119 
120         assertTrue(textField.exists());
121         assertEquals(query, textField.getText());
122     }
123 
assertInputFocused(boolean focused)124     public void assertInputFocused(boolean focused)
125             throws UiObjectNotFoundException {
126         UiObject textField = findSearchViewTextField();
127 
128         assertTrue(textField.exists());
129         assertEquals(focused, textField.isFocused());
130     }
131 
assertInputExists(boolean exists)132     public void assertInputExists(boolean exists)
133             throws UiObjectNotFoundException {
134         assertEquals(exists, findSearchViewTextField().exists());
135     }
136 
findSearchView()137     private UiObject findSearchView() {
138         return findObject(mTargetPackage + ":id/option_menu_search");
139     }
140 
findSearchHistoryView()141     private UiObject findSearchHistoryView() {
142         return findObject(mTargetPackage + ":id/history_list");
143     }
144 
findSearchViewTextField()145     private UiObject findSearchViewTextField() {
146         return findObject(mTargetPackage + ":id/option_menu_search",
147                 mTargetPackage + ":id/search_src_text");
148     }
149 
findSearchViewClearButton()150     private UiObject findSearchViewClearButton() {
151         return findObject(mTargetPackage + ":id/option_menu_search",
152                 mTargetPackage + ":id/search_close_btn");
153     }
154 
findSearchViewIcon()155     private UiObject findSearchViewIcon() {
156         return mContext.getResources().getBoolean(R.bool.full_bar_search_view)
157                 ? findObject(mTargetPackage + ":id/option_menu_search")
158                 : findObject(mTargetPackage + ":id/option_menu_search",
159                         "android:id/search_button");
160     }
161 }
162