1/*
2 * Copyright (C) 2017 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
17package androidx.appcompat.widget;
18
19import static org.junit.Assert.assertEquals;
20import static org.junit.Assert.assertFalse;
21import static org.junit.Assert.assertTrue;
22import static org.mockito.Mockito.anyString;
23import static org.mockito.Mockito.mock;
24import static org.mockito.Mockito.never;
25import static org.mockito.Mockito.times;
26import static org.mockito.Mockito.verify;
27import static org.mockito.Mockito.verifyNoMoreInteractions;
28import static org.mockito.Mockito.when;
29
30import android.app.Activity;
31import android.app.Instrumentation;
32import android.content.res.Resources;
33import android.support.test.InstrumentationRegistry;
34import android.support.test.annotation.UiThreadTest;
35import android.support.test.filters.MediumTest;
36import android.support.test.rule.ActivityTestRule;
37import android.support.test.runner.AndroidJUnit4;
38import android.text.InputType;
39import android.text.TextUtils;
40import android.view.inputmethod.EditorInfo;
41
42import androidx.appcompat.test.R;
43
44import org.junit.Before;
45import org.junit.Rule;
46import org.junit.Test;
47import org.junit.runner.RunWith;
48
49/**
50 * Test {@link SearchView}.
51 */
52@MediumTest
53@RunWith(AndroidJUnit4.class)
54public class SearchViewTest {
55    private Instrumentation mInstrumentation;
56    private Activity mActivity;
57    private SearchView mSearchView;
58
59    @Rule
60    public ActivityTestRule<SearchViewTestActivity> mActivityRule =
61            new ActivityTestRule<>(SearchViewTestActivity.class);
62
63    @Before
64    public void setup() {
65        mInstrumentation = InstrumentationRegistry.getInstrumentation();
66        mActivity = mActivityRule.getActivity();
67        mSearchView = (SearchView) mActivity.findViewById(R.id.search_view);
68    }
69
70    @UiThreadTest
71    @Test
72    public void testConstructor() {
73        new SearchView(mActivity);
74
75        new SearchView(mActivity, null);
76
77        new SearchView(mActivity, null, android.R.attr.searchViewStyle);
78    }
79
80    @UiThreadTest
81    @Test
82    public void testAttributesFromXml() {
83        SearchView searchViewWithAttributes =
84                (SearchView) mActivity.findViewById(R.id.search_view_with_defaults);
85        assertEquals(mActivity.getString(R.string.search_query_hint),
86                searchViewWithAttributes.getQueryHint());
87        assertFalse(searchViewWithAttributes.isIconfiedByDefault());
88        assertEquals(EditorInfo.TYPE_TEXT_FLAG_CAP_CHARACTERS | EditorInfo.TYPE_CLASS_TEXT,
89                searchViewWithAttributes.getInputType());
90        assertEquals(EditorInfo.IME_ACTION_DONE, searchViewWithAttributes.getImeOptions());
91        assertEquals(mActivity.getResources().getDimensionPixelSize(R.dimen.search_view_max_width),
92                searchViewWithAttributes.getMaxWidth());
93    }
94
95    @UiThreadTest
96    @Test
97    public void testAccessIconified() {
98        mSearchView.setIconified(true);
99        assertTrue(mSearchView.isIconified());
100
101        mSearchView.setIconified(false);
102        assertFalse(mSearchView.isIconified());
103    }
104
105    @UiThreadTest
106    @Test
107    public void testAccessIconifiedByDefault() {
108        mSearchView.setIconifiedByDefault(true);
109        assertTrue(mSearchView.isIconfiedByDefault());
110
111        mSearchView.setIconifiedByDefault(false);
112        assertFalse(mSearchView.isIconfiedByDefault());
113    }
114
115    @Test
116    public void testDenyIconifyingNonInconifiableView() throws Throwable {
117        mActivityRule.runOnUiThread(new Runnable() {
118            @Override
119            public void run() {
120                mSearchView.setIconifiedByDefault(false);
121                mSearchView.setIconified(false);
122            }
123        });
124
125        mActivityRule.runOnUiThread(new Runnable() {
126            @Override
127            public void run() {
128                mSearchView.setIconified(true);
129            }
130        });
131        mInstrumentation.waitForIdleSync();
132
133        // Since our search view is marked with iconifiedByDefault=false, call to setIconified
134        // with true us going to be ignored, as detailed in the class-level documentation of
135        // SearchView.
136        assertFalse(mSearchView.isIconified());
137    }
138
139    @Test
140    public void testDenyIconifyingInconifiableView() throws Throwable {
141        mActivityRule.runOnUiThread(new Runnable() {
142            @Override
143            public void run() {
144                mSearchView.setIconifiedByDefault(true);
145                mSearchView.setIconified(false);
146            }
147        });
148
149        final SearchView.OnCloseListener mockDenyCloseListener =
150                mock(SearchView.OnCloseListener.class);
151        when(mockDenyCloseListener.onClose()).thenReturn(Boolean.TRUE);
152        mSearchView.setOnCloseListener(mockDenyCloseListener);
153
154        mActivityRule.runOnUiThread(new Runnable() {
155            @Override
156            public void run() {
157                mSearchView.setIconified(true);
158            }
159        });
160        mInstrumentation.waitForIdleSync();
161
162        // Our mock listener is configured to return true from its onClose, thereby preventing
163        // the iconify request to be completed. Check that the listener was called and that the
164        // search view is not iconified.
165        verify(mockDenyCloseListener, times(1)).onClose();
166        assertFalse(mSearchView.isIconified());
167    }
168
169    @Test
170    public void testAllowIconifyingInconifiableView() throws Throwable {
171        mActivityRule.runOnUiThread(new Runnable() {
172            @Override
173            public void run() {
174                mSearchView.setIconifiedByDefault(true);
175                mSearchView.setIconified(false);
176            }
177        });
178
179        final SearchView.OnCloseListener mockAllowCloseListener =
180                mock(SearchView.OnCloseListener.class);
181        when(mockAllowCloseListener.onClose()).thenReturn(Boolean.FALSE);
182        mSearchView.setOnCloseListener(mockAllowCloseListener);
183
184        mActivityRule.runOnUiThread(new Runnable() {
185            @Override
186            public void run() {
187                mSearchView.setIconified(true);
188            }
189        });
190        mInstrumentation.waitForIdleSync();
191
192        // Our mock listener is configured to return false from its onClose, thereby allowing
193        // the iconify request to be completed. Check that the listener was called and that the
194        // search view is not iconified.
195        verify(mockAllowCloseListener, times(1)).onClose();
196        assertTrue(mSearchView.isIconified());
197    }
198
199    @Test
200    public void testAccessMaxWidth() throws Throwable {
201        final Resources res = mActivity.getResources();
202        final int maxWidth1 = res.getDimensionPixelSize(R.dimen.search_view_max_width);
203        final int maxWidth2 = res.getDimensionPixelSize(R.dimen.search_view_max_width2);
204
205        // Set search view to not be iconified before running max-width tests
206        mActivityRule.runOnUiThread(new Runnable() {
207            @Override
208            public void run() {
209                mSearchView.setIconified(false);
210            }
211        });
212
213        mActivityRule.runOnUiThread(new Runnable() {
214            @Override
215            public void run() {
216                mSearchView.setMaxWidth(maxWidth1);
217            }
218        });
219        mInstrumentation.waitForIdleSync();
220        assertEquals(maxWidth1, mSearchView.getMaxWidth());
221        assertTrue(mSearchView.getWidth() <= maxWidth1);
222
223        mActivityRule.runOnUiThread(new Runnable() {
224            @Override
225            public void run() {
226                mSearchView.setMaxWidth(maxWidth2);
227            }
228        });
229        mInstrumentation.waitForIdleSync();
230        assertEquals(maxWidth2, mSearchView.getMaxWidth());
231        assertTrue(mSearchView.getWidth() <= maxWidth2);
232    }
233
234    @UiThreadTest
235    @Test
236    public void testAccessQuery() {
237        mSearchView.setIconified(false);
238
239        final SearchView.OnQueryTextListener mockQueryTextListener =
240                mock(SearchView.OnQueryTextListener.class);
241        when(mockQueryTextListener.onQueryTextSubmit(anyString())).thenReturn(Boolean.TRUE);
242        mSearchView.setOnQueryTextListener(mockQueryTextListener);
243
244        mSearchView.setQuery("alpha", false);
245        assertTrue(TextUtils.equals("alpha", mSearchView.getQuery()));
246        // Since we passed false as the second parameter to setQuery, our query text listener
247        // should have been invoked only with text change
248        verify(mockQueryTextListener, times(1)).onQueryTextChange("alpha");
249        verify(mockQueryTextListener, never()).onQueryTextSubmit(anyString());
250
251        mSearchView.setQuery("beta", true);
252        assertTrue(TextUtils.equals("beta", mSearchView.getQuery()));
253        // Since we passed true as the second parameter to setQuery, our query text listener
254        // should have been invoked on both callbacks
255        verify(mockQueryTextListener, times(1)).onQueryTextChange("beta");
256        verify(mockQueryTextListener, times(1)).onQueryTextSubmit("beta");
257
258        mSearchView.setQuery("gamma", true);
259        assertTrue(TextUtils.equals("gamma", mSearchView.getQuery()));
260        // Since we passed true as the second parameter to setQuery, our query text listener
261        // should have been invoked on both callbacks
262        verify(mockQueryTextListener, times(1)).onQueryTextChange("gamma");
263        verify(mockQueryTextListener, times(1)).onQueryTextSubmit("gamma");
264
265        verifyNoMoreInteractions(mockQueryTextListener);
266    }
267
268    @UiThreadTest
269    @Test
270    public void testAccessQueryHint() {
271        mSearchView.setQueryHint("hint 1");
272        assertTrue(TextUtils.equals("hint 1", mSearchView.getQueryHint()));
273
274        mSearchView.setQueryHint("hint 2");
275        assertTrue(TextUtils.equals("hint 2", mSearchView.getQueryHint()));
276    }
277
278    @UiThreadTest
279    @Test
280    public void testAccessInputType() {
281        mSearchView.setInputType(InputType.TYPE_CLASS_NUMBER
282                | InputType.TYPE_NUMBER_FLAG_DECIMAL
283                | InputType.TYPE_NUMBER_FLAG_SIGNED);
284        assertEquals(InputType.TYPE_CLASS_NUMBER
285                | InputType.TYPE_NUMBER_FLAG_DECIMAL
286                | InputType.TYPE_NUMBER_FLAG_SIGNED, mSearchView.getInputType());
287
288        mSearchView.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_WORDS);
289        assertEquals(InputType.TYPE_CLASS_TEXT
290                | InputType.TYPE_TEXT_FLAG_CAP_WORDS, mSearchView.getInputType());
291
292        mSearchView.setInputType(InputType.TYPE_CLASS_PHONE);
293        assertEquals(InputType.TYPE_CLASS_PHONE, mSearchView.getInputType());
294    }
295
296    @UiThreadTest
297    @Test
298    public void testAccessImeOptions() {
299        mSearchView.setImeOptions(EditorInfo.IME_ACTION_GO);
300        assertEquals(EditorInfo.IME_ACTION_GO, mSearchView.getImeOptions());
301
302        mSearchView.setImeOptions(EditorInfo.IME_ACTION_DONE);
303        assertEquals(EditorInfo.IME_ACTION_DONE, mSearchView.getImeOptions());
304
305        mSearchView.setImeOptions(EditorInfo.IME_NULL);
306        assertEquals(EditorInfo.IME_NULL, mSearchView.getImeOptions());
307    }
308}
309