[go: nahoru, domu]

blob: 9794f247d97f6e296ad1c19aeb546444f34a64f6 [file] [log] [blame]
Matvei Malkov2d37cbd2019-07-04 15:15:20 +01001/*
2 * Copyright 2019 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.ui.material
18
Jelle Fresen11fc7062020-01-13 16:53:55 +000019import android.os.SystemClock.sleep
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010020import androidx.compose.Model
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +000021import androidx.compose.emptyContent
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010022import androidx.test.filters.MediumTest
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010023import androidx.ui.core.TestTag
George Mountd02af602020-03-06 16:41:40 -080024import androidx.ui.core.onPositioned
Matvei Malkov201726d2020-03-13 14:03:54 +000025import androidx.ui.foundation.Box
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010026import androidx.ui.foundation.Clickable
Matvei Malkov201726d2020-03-13 14:03:54 +000027import androidx.ui.layout.LayoutSize
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010028import androidx.ui.semantics.Semantics
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010029import androidx.ui.test.createComposeRule
Jelle Fresen11fc7062020-01-13 16:53:55 +000030import androidx.ui.test.doGesture
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010031import androidx.ui.test.findByTag
Jelle Fresen11fc7062020-01-13 16:53:55 +000032import androidx.ui.test.globalBounds
33import androidx.ui.test.sendClick
George Mount8baef7a2020-01-21 13:40:57 -080034import androidx.ui.unit.IntPx
35import androidx.ui.unit.IntPxSize
George Mount842c8c12020-01-08 16:03:42 -080036import androidx.ui.unit.PxPosition
George Mount842c8c12020-01-08 16:03:42 -080037import androidx.ui.unit.dp
Ryan Mentley7865a632019-08-20 20:10:29 -070038import androidx.ui.unit.height
Jelle Fresen11fc7062020-01-13 16:53:55 +000039import androidx.ui.unit.px
George Mount842c8c12020-01-08 16:03:42 -080040import androidx.ui.unit.round
Ryan Mentley7865a632019-08-20 20:10:29 -070041import androidx.ui.unit.width
Filip Pavlise63b30c2020-01-09 16:21:07 +000042import com.google.common.truth.Truth.assertThat
Matvei Malkov886ec6a2020-02-03 01:45:29 +000043import org.junit.Ignore
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010044import org.junit.Rule
45import org.junit.Test
46import org.junit.runner.RunWith
47import org.junit.runners.JUnit4
Jelle Fresen4c566312020-01-16 10:32:40 +000048import java.util.concurrent.CountDownLatch
49import java.util.concurrent.TimeUnit
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010050import kotlin.math.roundToInt
51
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010052@Model
53data class DrawerStateHolder(var state: DrawerState)
54
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010055@MediumTest
56@RunWith(JUnit4::class)
57class DrawerTest {
58
59 @get:Rule
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010060 val composeTestRule = createComposeRule(disableTransitions = true)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010061
62 @Test
63 fun modalDrawer_testOffset_whenOpened() {
64 var position: PxPosition? = null
65 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010066 ModalDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +000067 Box(LayoutSize.Fill + onPositioned { coords ->
68 position = coords.localToGlobal(PxPosition.Origin)
69 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +000070 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010071 }
Filip Pavlise63b30c2020-01-09 16:21:07 +000072 composeTestRule.runOnIdleCompose {
73 assertThat(position!!.x.value).isEqualTo(0f)
74 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010075 }
76
77 @Test
78 fun modalDrawer_testOffset_whenClosed() {
79 var position: PxPosition? = null
80 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010081 ModalDrawerLayout(DrawerState.Closed, {}, drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +000082 Box(LayoutSize.Fill + onPositioned { coords ->
83 position = coords.localToGlobal(PxPosition.Origin)
84 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +000085 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010086 }
87 val width = composeTestRule.displayMetrics.widthPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +000088 composeTestRule.runOnIdleCompose {
89 assertThat(position!!.x.round().value).isEqualTo(-width)
90 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010091 }
92
93 @Test
94 fun modalDrawer_testEndPadding_whenOpened() {
George Mount8baef7a2020-01-21 13:40:57 -080095 var size: IntPxSize? = null
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010096 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010097 ModalDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +000098 Box(LayoutSize.Fill + onPositioned { coords -> size = coords.size })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +000099 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100100 }
101
102 val width = composeTestRule.displayMetrics.widthPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +0000103 composeTestRule.runOnIdleComposeWithDensity {
George Mount8baef7a2020-01-21 13:40:57 -0800104 assertThat(size!!.width.value)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100105 .isEqualTo(width - 56.dp.toPx().round().value)
106 }
107 }
108
109 @Test
110 fun bottomDrawer_testOffset_whenOpened() {
111 var position: PxPosition? = null
112 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100113 BottomDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +0000114 Box(LayoutSize.Fill + onPositioned { coords ->
115 position = coords.localToGlobal(PxPosition.Origin)
116 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000117 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100118 }
Filip Pavlise63b30c2020-01-09 16:21:07 +0000119
Matvei Malkov2270be12019-07-08 16:59:45 +0100120 val width = composeTestRule.displayMetrics.widthPixels
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100121 val height = composeTestRule.displayMetrics.heightPixels
Matvei Malkov2270be12019-07-08 16:59:45 +0100122 // temporary calculation of landscape screen
Matvei Malkovb5e3d8a2020-03-09 15:17:20 +0000123 val expectedHeight = if (width > height) 0 else (height / 2f).roundToInt()
Filip Pavlise63b30c2020-01-09 16:21:07 +0000124 composeTestRule.runOnIdleCompose {
125 assertThat(position!!.y.round().value).isEqualTo(expectedHeight)
126 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100127 }
128
129 @Test
130 fun bottomDrawer_testOffset_whenClosed() {
131 var position: PxPosition? = null
132 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100133 BottomDrawerLayout(DrawerState.Closed, {}, drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +0000134 Box(LayoutSize.Fill + onPositioned { coords ->
135 position = coords.localToGlobal(PxPosition.Origin)
136 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000137 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100138 }
139 val height = composeTestRule.displayMetrics.heightPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +0000140 composeTestRule.runOnIdleCompose {
141 assertThat(position!!.y.round().value).isEqualTo(height)
142 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100143 }
144
145 @Test
146 fun staticDrawer_testWidth_whenOpened() {
147 composeTestRule
Matvei Malkov804bee42019-07-16 16:24:06 +0100148 .setMaterialContentAndCollectSizes {
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100149 StaticDrawer {
Matvei Malkov201726d2020-03-13 14:03:54 +0000150 Box(LayoutSize.Fill)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100151 }
152 }
153 .assertWidthEqualsTo(256.dp)
154 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100155
156 @Test
Matvei Malkov886ec6a2020-02-03 01:45:29 +0000157 @Ignore("failing in postsubmit, fix in b/148751721")
Jelle Fresen4c566312020-01-16 10:32:40 +0000158 fun modalDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800159 var contentWidth: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000160 var openedLatch: CountDownLatch? = null
161 var closedLatch: CountDownLatch? = CountDownLatch(1)
162 val drawerState = DrawerStateHolder(DrawerState.Closed)
163 composeTestRule.setMaterialContent {
164 TestTag("Drawer") {
165 Semantics(container = true) {
166 ModalDrawerLayout(drawerState.state, { drawerState.state = it },
167 drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +0000168 Box(
169 LayoutSize.Fill + onPositioned { info ->
George Mountb47c3b02020-03-10 23:26:42 -0700170 val pos = info.localToGlobal(PxPosition.Origin)
171 if (pos.x == 0.px) {
172 // If fully opened, mark the openedLatch if present
173 openedLatch?.countDown()
174 } else if (-pos.x.round() == contentWidth) {
175 // If fully closed, mark the closedLatch if present
176 closedLatch?.countDown()
177 }
Jelle Fresen4c566312020-01-16 10:32:40 +0000178 }
George Mountb47c3b02020-03-10 23:26:42 -0700179 )
Jelle Fresen4c566312020-01-16 10:32:40 +0000180 },
181 bodyContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +0000182 Box(LayoutSize.Fill + onPositioned { contentWidth = it.size.width })
Jelle Fresen4c566312020-01-16 10:32:40 +0000183 })
184 }
185 }
186 }
187 // Drawer should start in closed state
188 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
189
190 // When the drawer state is set to Opened
191 openedLatch = CountDownLatch(1)
192 composeTestRule.runOnIdleCompose {
193 drawerState.state = DrawerState.Opened
194 }
195 // Then the drawer should be opened
196 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
197
198 // When the drawer state is set to Closed
199 closedLatch = CountDownLatch(1)
200 composeTestRule.runOnIdleCompose {
201 drawerState.state = DrawerState.Closed
202 }
203 // Then the drawer should be closed
204 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
205 }
206
207 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100208 fun modalDrawer_bodyContent_clickable() {
209 var drawerClicks = 0
210 var bodyClicks = 0
211 val drawerState = DrawerStateHolder(DrawerState.Closed)
212 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700213 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100214 TestTag("Drawer") {
215 Semantics(container = true) {
216 ModalDrawerLayout(drawerState.state, { drawerState.state = it },
217 drawerContent = {
218 Clickable(onClick = { drawerClicks += 1 }) {
Matvei Malkov201726d2020-03-13 14:03:54 +0000219 Box(LayoutSize.Fill, children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100220 }
221 },
222 bodyContent = {
223 Clickable(onClick = { bodyClicks += 1 }) {
Matvei Malkov201726d2020-03-13 14:03:54 +0000224 Box(LayoutSize.Fill, children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100225 }
226 })
227 }
228 }
229 }
230
Jelle Fresen11fc7062020-01-13 16:53:55 +0000231 // Click in the middle of the drawer (which is the middle of the body)
232 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100233
Filip Pavlis2b161e42019-11-22 17:40:08 +0000234 composeTestRule.runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000235 assertThat(drawerClicks).isEqualTo(0)
236 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100237
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100238 drawerState.state = DrawerState.Opened
239 }
Jelle Fresen11fc7062020-01-13 16:53:55 +0000240 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100241
Jelle Fresen11fc7062020-01-13 16:53:55 +0000242 // Click on the left-center pixel of the drawer
243 findByTag("Drawer").doGesture {
Ryan Mentley7865a632019-08-20 20:10:29 -0700244 val left = 1.px
Jelle Fresen11fc7062020-01-13 16:53:55 +0000245 val centerY = globalBounds.height / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700246 sendClick(PxPosition(left, centerY))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000247 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100248
Filip Pavlis2b161e42019-11-22 17:40:08 +0000249 composeTestRule.runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000250 assertThat(drawerClicks).isEqualTo(1)
251 assertThat(bodyClicks).isEqualTo(1)
Filip Pavlis2b161e42019-11-22 17:40:08 +0000252 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100253 }
254
255 @Test
Matvei Malkov886ec6a2020-02-03 01:45:29 +0000256 @Ignore("failing in postsubmit, fix in b/148751721")
Jelle Fresen4c566312020-01-16 10:32:40 +0000257 fun bottomDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800258 var contentHeight: IntPx? = null
259 var openedHeight: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000260 var openedLatch: CountDownLatch? = null
261 var closedLatch: CountDownLatch? = CountDownLatch(1)
262 val drawerState = DrawerStateHolder(DrawerState.Closed)
263 composeTestRule.setMaterialContent {
264 TestTag("Drawer") {
265 Semantics(container = true) {
266 BottomDrawerLayout(drawerState.state, { drawerState.state = it },
267 drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +0000268 Box(LayoutSize.Fill + onPositioned { info ->
269 val pos = info.localToGlobal(PxPosition.Origin)
270 if (pos.y.round() == openedHeight) {
271 // If fully opened, mark the openedLatch if present
272 openedLatch?.countDown()
273 } else if (pos.y.round() == contentHeight) {
274 // If fully closed, mark the closedLatch if present
275 closedLatch?.countDown()
Jelle Fresen4c566312020-01-16 10:32:40 +0000276 }
Matvei Malkov201726d2020-03-13 14:03:54 +0000277 })
Jelle Fresen4c566312020-01-16 10:32:40 +0000278 },
279 bodyContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +0000280 Box(LayoutSize.Fill + onPositioned {
281 contentHeight = it.size.height
282 openedHeight = it.size.height * BottomDrawerOpenFraction
283 })
George Mountb47c3b02020-03-10 23:26:42 -0700284 }
285 )
Jelle Fresen4c566312020-01-16 10:32:40 +0000286 }
287 }
288 }
289 // Drawer should start in closed state
290 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
291
292 // When the drawer state is set to Opened
293 openedLatch = CountDownLatch(1)
294 composeTestRule.runOnIdleCompose {
295 drawerState.state = DrawerState.Opened
296 }
297 // Then the drawer should be opened
298 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
299
300 // When the drawer state is set to Closed
301 closedLatch = CountDownLatch(1)
302 composeTestRule.runOnIdleCompose {
303 drawerState.state = DrawerState.Closed
304 }
305 // Then the drawer should be closed
306 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
307 }
308
309 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100310 fun bottomDrawer_bodyContent_clickable() {
311 var drawerClicks = 0
312 var bodyClicks = 0
313 val drawerState = DrawerStateHolder(DrawerState.Closed)
314 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700315 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100316 TestTag("Drawer") {
317 Semantics(container = true) {
318 BottomDrawerLayout(drawerState.state, { drawerState.state = it },
319 drawerContent = {
320 Clickable(onClick = { drawerClicks += 1 }) {
Matvei Malkov201726d2020-03-13 14:03:54 +0000321 Box(LayoutSize.Fill, children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100322 }
323 },
324 bodyContent = {
325 Clickable(onClick = { bodyClicks += 1 }) {
Matvei Malkov201726d2020-03-13 14:03:54 +0000326 Box(LayoutSize.Fill, children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100327 }
328 })
329 }
330 }
331 }
332
Jelle Fresen11fc7062020-01-13 16:53:55 +0000333 // Click in the middle of the drawer (which is the middle of the body)
334 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100335
Filip Pavlise63b30c2020-01-09 16:21:07 +0000336 composeTestRule.runOnIdleCompose {
337 assertThat(drawerClicks).isEqualTo(0)
338 assertThat(bodyClicks).isEqualTo(1)
339 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100340
Jelle Fresen11fc7062020-01-13 16:53:55 +0000341 composeTestRule.runOnUiThread {
342 drawerState.state = DrawerState.Opened
343 }
344 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
345
346 // Click on the bottom-center pixel of the drawer
347 findByTag("Drawer").doGesture {
348 val bounds = globalBounds
349 val centerX = bounds.width / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700350 val bottom = bounds.height - 1.px
351 sendClick(PxPosition(centerX, bottom))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000352 }
353
354 assertThat(drawerClicks).isEqualTo(1)
355 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100356 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100357}