• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.intentresolver.emptystate
18 
19 import android.view.LayoutInflater
20 import android.view.View
21 import android.view.ViewGroup
22 import android.widget.FrameLayout
23 import android.widget.TextView
24 import androidx.test.platform.app.InstrumentationRegistry
25 import com.google.common.truth.Truth.assertThat
26 import java.util.Optional
27 import java.util.function.Supplier
28 import org.junit.Before
29 import org.junit.Test
30 import org.mockito.kotlin.any
31 import org.mockito.kotlin.mock
32 import org.mockito.kotlin.never
33 import org.mockito.kotlin.verify
34 
35 class EmptyStateUiHelperTest {
36     private val context = InstrumentationRegistry.getInstrumentation().getContext()
37 
38     var shouldOverrideContainerPadding = false
39     val containerPaddingSupplier =
<lambda>null40         Supplier<Optional<Int>> {
41             Optional.ofNullable(if (shouldOverrideContainerPadding) 42 else null)
42         }
43 
44     lateinit var rootContainer: ViewGroup
45     lateinit var mainListView: View // Visible when no empty state is showing.
46     lateinit var emptyStateTitleView: TextView
47     lateinit var emptyStateSubtitleView: TextView
48     lateinit var emptyStateButtonView: View
49     lateinit var emptyStateProgressView: View
50     lateinit var emptyStateDefaultTextView: View
51     lateinit var emptyStateContainerView: View
52     lateinit var emptyStateRootView: View
53     lateinit var emptyStateUiHelper: EmptyStateUiHelper
54 
55     @Before
setupnull56     fun setup() {
57         rootContainer = FrameLayout(context)
58         LayoutInflater.from(context)
59             .inflate(
60                 com.android.intentresolver.R.layout.resolver_list_per_profile,
61                 rootContainer,
62                 true
63             )
64         mainListView = rootContainer.requireViewById(com.android.internal.R.id.resolver_list)
65         emptyStateRootView =
66             rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state)
67         emptyStateTitleView =
68             rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_title)
69         emptyStateSubtitleView =
70             rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_subtitle)
71         emptyStateButtonView =
72             rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_button)
73         emptyStateProgressView =
74             rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_progress)
75         emptyStateDefaultTextView = rootContainer.requireViewById(com.android.internal.R.id.empty)
76         emptyStateContainerView =
77             rootContainer.requireViewById(com.android.internal.R.id.resolver_empty_state_container)
78         emptyStateUiHelper =
79             EmptyStateUiHelper(
80                 rootContainer,
81                 com.android.internal.R.id.resolver_list,
82                 containerPaddingSupplier
83             )
84     }
85 
86     @Test
testResetViewVisibilitiesnull87     fun testResetViewVisibilities() {
88         // First set each view's visibility to differ from the expected "reset" state so we can then
89         // assert that they're all reset afterward.
90         // TODO: for historic reasons "reset" doesn't cover `emptyStateContainerView`; should it?
91         emptyStateRootView.visibility = View.GONE
92         emptyStateTitleView.visibility = View.GONE
93         emptyStateSubtitleView.visibility = View.GONE
94         emptyStateButtonView.visibility = View.VISIBLE
95         emptyStateProgressView.visibility = View.VISIBLE
96         emptyStateDefaultTextView.visibility = View.VISIBLE
97 
98         emptyStateUiHelper.resetViewVisibilities()
99 
100         assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
101         assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
102         assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
103         assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
104         assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
105         assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
106     }
107 
108     @Test
testShowSpinnernull109     fun testShowSpinner() {
110         emptyStateTitleView.visibility = View.VISIBLE
111         emptyStateButtonView.visibility = View.VISIBLE
112         emptyStateProgressView.visibility = View.GONE
113         emptyStateDefaultTextView.visibility = View.VISIBLE
114 
115         emptyStateUiHelper.showSpinner()
116 
117         // TODO: should this cover any other views? Subtitle?
118         assertThat(emptyStateTitleView.visibility).isEqualTo(View.INVISIBLE)
119         assertThat(emptyStateButtonView.visibility).isEqualTo(View.INVISIBLE)
120         assertThat(emptyStateProgressView.visibility).isEqualTo(View.VISIBLE)
121         assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
122     }
123 
124     @Test
testHidenull125     fun testHide() {
126         emptyStateRootView.visibility = View.VISIBLE
127         mainListView.visibility = View.GONE
128 
129         emptyStateUiHelper.hide()
130 
131         assertThat(emptyStateRootView.visibility).isEqualTo(View.GONE)
132         assertThat(mainListView.visibility).isEqualTo(View.VISIBLE)
133     }
134 
135     @Test
testBottomPaddingDelegate_defaultnull136     fun testBottomPaddingDelegate_default() {
137         shouldOverrideContainerPadding = false
138         emptyStateContainerView.setPadding(1, 2, 3, 4)
139 
140         emptyStateUiHelper.setupContainerPadding()
141 
142         assertThat(emptyStateContainerView.paddingLeft).isEqualTo(1)
143         assertThat(emptyStateContainerView.paddingTop).isEqualTo(2)
144         assertThat(emptyStateContainerView.paddingRight).isEqualTo(3)
145         assertThat(emptyStateContainerView.paddingBottom).isEqualTo(4)
146     }
147 
148     @Test
testBottomPaddingDelegate_overridenull149     fun testBottomPaddingDelegate_override() {
150         shouldOverrideContainerPadding = true // Set bottom padding to 42.
151         emptyStateContainerView.setPadding(1, 2, 3, 4)
152 
153         emptyStateUiHelper.setupContainerPadding()
154 
155         assertThat(emptyStateContainerView.paddingLeft).isEqualTo(1)
156         assertThat(emptyStateContainerView.paddingTop).isEqualTo(2)
157         assertThat(emptyStateContainerView.paddingRight).isEqualTo(3)
158         assertThat(emptyStateContainerView.paddingBottom).isEqualTo(42)
159     }
160 
161     @Test
testShowEmptyState_noOnClickHandlernull162     fun testShowEmptyState_noOnClickHandler() {
163         mainListView.visibility = View.VISIBLE
164 
165         // Note: an `EmptyState.ClickListener` isn't invoked directly by the UI helper; it has to be
166         // built into the "on-click handler" that's injected to implement the button-press. We won't
167         // display the button without a click "handler," even if it *does* have a `ClickListener`.
168         val clickListener = mock<EmptyState.ClickListener>()
169 
170         val emptyState =
171             object : EmptyState {
172                 override fun getTitle() = "Test title"
173                 override fun getSubtitle() = "Test subtitle"
174 
175                 override fun getButtonClickListener() = clickListener
176             }
177         emptyStateUiHelper.showEmptyState(emptyState, null)
178 
179         assertThat(mainListView.visibility).isEqualTo(View.GONE)
180         assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
181         assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
182         assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
183         assertThat(emptyStateButtonView.visibility).isEqualTo(View.GONE)
184         assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
185         assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
186 
187         assertThat(emptyStateTitleView.text).isEqualTo("Test title")
188         assertThat(emptyStateSubtitleView.text).isEqualTo("Test subtitle")
189 
190         verify(clickListener, never()).onClick(any())
191     }
192 
193     @Test
testShowEmptyState_withOnClickHandlerAndClickListenernull194     fun testShowEmptyState_withOnClickHandlerAndClickListener() {
195         mainListView.visibility = View.VISIBLE
196 
197         val clickListener = mock<EmptyState.ClickListener>()
198         val onClickHandler = mock<View.OnClickListener>()
199 
200         val emptyState =
201             object : EmptyState {
202                 override fun getTitle() = "Test title"
203                 override fun getSubtitle() = "Test subtitle"
204 
205                 override fun getButtonClickListener() = clickListener
206             }
207         emptyStateUiHelper.showEmptyState(emptyState, onClickHandler)
208 
209         assertThat(mainListView.visibility).isEqualTo(View.GONE)
210         assertThat(emptyStateRootView.visibility).isEqualTo(View.VISIBLE)
211         assertThat(emptyStateTitleView.visibility).isEqualTo(View.VISIBLE)
212         assertThat(emptyStateSubtitleView.visibility).isEqualTo(View.VISIBLE)
213         assertThat(emptyStateButtonView.visibility).isEqualTo(View.VISIBLE) // Now shown.
214         assertThat(emptyStateProgressView.visibility).isEqualTo(View.GONE)
215         assertThat(emptyStateDefaultTextView.visibility).isEqualTo(View.GONE)
216 
217         assertThat(emptyStateTitleView.text).isEqualTo("Test title")
218         assertThat(emptyStateSubtitleView.text).isEqualTo("Test subtitle")
219 
220         emptyStateButtonView.performClick()
221 
222         verify(onClickHandler).onClick(emptyStateButtonView)
223         // The test didn't explicitly configure its `OnClickListener` to relay the click event on
224         // to the `EmptyState.ClickListener`, so it still won't have fired here.
225         verify(clickListener, never()).onClick(any())
226     }
227 }
228