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.click;
21 import static androidx.test.espresso.matcher.ViewMatchers.isDescendantOfA;
22 import static androidx.test.espresso.matcher.ViewMatchers.withChild;
23 import static androidx.test.espresso.matcher.ViewMatchers.withContentDescription;
24 import static androidx.test.espresso.matcher.ViewMatchers.withId;
25 import static androidx.test.espresso.matcher.ViewMatchers.withParent;
26 import static androidx.test.espresso.matcher.ViewMatchers.withText;
27 
28 import static com.android.documentsui.sorting.SortDimension.SORT_DIRECTION_ASCENDING;
29 
30 import static junit.framework.Assert.assertFalse;
31 import static junit.framework.Assert.assertTrue;
32 
33 import static org.hamcrest.Matchers.allOf;
34 
35 import android.content.Context;
36 import android.support.test.uiautomator.By;
37 import android.support.test.uiautomator.UiDevice;
38 import android.support.test.uiautomator.UiObject2;
39 import android.view.View;
40 
41 import androidx.annotation.StringRes;
42 
43 import com.android.documentsui.R;
44 import com.android.documentsui.sorting.SortDimension;
45 import com.android.documentsui.sorting.SortDimension.SortDirection;
46 import com.android.documentsui.sorting.SortListFragment;
47 import com.android.documentsui.sorting.SortModel;
48 import com.android.documentsui.sorting.SortModel.SortDimensionId;
49 
50 import org.hamcrest.Matcher;
51 
52 /**
53  * A test helper class that provides support for controlling the UI Breadcrumb
54  * programmatically, and making assertions against the state of the UI.
55  * <p>
56  * Support for working directly with Roots and Directory view can be found in the respective bots.
57  */
58 public class SortBot extends Bots.BaseBot {
59 
60     private final SortModel mSortModel = SortModel.createModel();
61     private final ColumnSortBot mColumnBot;
62     private final UiBot mUiBot;
63 
SortBot(UiDevice device, Context context, int timeout, UiBot uiBot)64     public SortBot(UiDevice device, Context context, int timeout, UiBot uiBot) {
65         super(device, context, timeout);
66         mColumnBot = new ColumnSortBot();
67         mUiBot = uiBot;
68     }
69 
sortBy(@ortDimensionId int id, @SortDirection int direction)70     public void sortBy(@SortDimensionId int id, @SortDirection int direction) {
71         assert(direction != SortDimension.SORT_DIRECTION_NONE);
72 
73         final @StringRes int labelId = mSortModel.getDimensionById(id).getLabelId();
74         final String label = mContext.getString(labelId);
75         final boolean result;
76         if (isHeaderShow()) {
77             result = mColumnBot.sortBy(label, direction);
78         } else {
79             result = sortByMenu(id, direction);
80         }
81 
82         assertTrue("Sorting by id: " + id + " in direction: " + direction + " failed.",
83                 result);
84     }
85 
isHeaderShow()86     public boolean isHeaderShow() {
87         return Matchers.present(mColumnBot.MATCHER);
88     }
89 
assertHeaderHide()90     public void assertHeaderHide() {
91         assertFalse(Matchers.present(mColumnBot.MATCHER));
92     }
93 
assertHeaderShow()94     public void assertHeaderShow() {
95         // BEWARE THOSE WHO TREAD IN THIS DARK CORNER.
96         // Note that for some reason this doesn't work:
97         // assertTrue(Matchers.present(mColumnBot.MATCHER));
98         // Dunno why, something to do with our implementation
99         // or with espresso. It's sad that I'm leaving you
100         // with this little gremlin, but we all have to
101         // move on and get stuff done :)
102         assertTrue(Matchers.present(mColumnBot.MATCHER));
103     }
104 
sortByMenu(@ortDimensionId int id, @SortDirection int direction)105     private boolean sortByMenu(@SortDimensionId int id, @SortDirection int direction) {
106         assert(direction != SortDimension.SORT_DIRECTION_NONE);
107 
108         clickMenuSort();
109         mDevice.waitForIdle();
110 
111         SortDimension dimension = mSortModel.getDimensionById(id);
112         @StringRes int labelRes = SortListFragment.getSheetLabelId(dimension, direction);
113         onView(withText(mContext.getString(labelRes))).perform(click());
114         mDevice.waitForIdle();
115 
116         clickMenuSort();
117         mDevice.waitForIdle();
118 
119         UiObject2 verifyLabel = mDevice.findObject(By.text(mContext.getString(labelRes)));
120         boolean isSelected = verifyLabel.isChecked();
121         onView(withText(mContext.getString(labelRes))).perform(click());
122 
123         return isSelected;
124     }
125 
clickMenuSort()126     private void clickMenuSort() {
127         mUiBot.clickToolbarOverflowItem(mContext.getString(R.string.menu_sort));
128     }
129 
130     private static class ColumnSortBot {
131 
132         private static final Matcher<View> MATCHER = withId(R.id.table_header);
133 
sortBy(String label, @SortDirection int direction)134         private boolean sortBy(String label, @SortDirection int direction) {
135             final Matcher<View> cellMatcher = allOf(
136                     withChild(withText(label)),
137                     isDescendantOfA(MATCHER));
138             onView(cellMatcher).perform(click());
139 
140             final @SortDirection int viewDirection = getDirection(cellMatcher);
141 
142             if (viewDirection != direction) {
143                 onView(cellMatcher).perform(click());
144             }
145 
146             return getDirection(cellMatcher) == direction;
147         }
148 
getDirection(Matcher<View> cellMatcher)149         private @SortDirection int getDirection(Matcher<View> cellMatcher) {
150             final boolean ascending =
151                     Matchers.present(
152                             allOf(
153                                     withContentDescription(R.string.sort_direction_ascending),
154                                     withParent(cellMatcher)));
155             if (ascending) {
156                 return SORT_DIRECTION_ASCENDING;
157             }
158 
159             final boolean descending =
160                     Matchers.present(
161                             allOf(
162                                     withContentDescription(R.string.sort_direction_descending),
163                                     withParent(cellMatcher)));
164 
165             return descending
166                     ? SortDimension.SORT_DIRECTION_DESCENDING
167                     : SortDimension.SORT_DIRECTION_NONE;
168         }
169     }
170 }
171