[go: nahoru, domu]

blob: 31b72c83c6035d7f531868f80933f7d4ecce37bc [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
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +000020import androidx.compose.emptyContent
Leland Richardsonfcf76b32020-05-13 16:58:59 -070021import androidx.compose.mutableStateOf
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010022import androidx.test.filters.MediumTest
Adam Powell999a89b2020-03-11 09:08:07 -070023import androidx.ui.core.LayoutCoordinates
24import androidx.ui.core.Modifier
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010025import androidx.ui.core.TestTag
George Mountd02af602020-03-06 16:41:40 -080026import androidx.ui.core.onPositioned
Matvei Malkov201726d2020-03-13 14:03:54 +000027import androidx.ui.foundation.Box
Matvei Malkov323b7452020-05-01 16:57:52 +010028import androidx.ui.foundation.clickable
Adam Powell999a89b2020-03-11 09:08:07 -070029import androidx.ui.layout.fillMaxSize
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010030import androidx.ui.test.createComposeRule
Jelle Fresen11fc7062020-01-13 16:53:55 +000031import androidx.ui.test.doGesture
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010032import androidx.ui.test.findByTag
Jelle Fresen11fc7062020-01-13 16:53:55 +000033import androidx.ui.test.globalBounds
Filip Pavlis6fdb2502020-04-02 14:41:32 +010034import androidx.ui.test.runOnIdleCompose
35import androidx.ui.test.runOnUiThread
Jelle Fresen11fc7062020-01-13 16:53:55 +000036import androidx.ui.test.sendClick
George Mount8baef7a2020-01-21 13:40:57 -080037import androidx.ui.unit.IntPx
38import androidx.ui.unit.IntPxSize
George Mount842c8c12020-01-08 16:03:42 -080039import androidx.ui.unit.PxPosition
George Mount842c8c12020-01-08 16:03:42 -080040import androidx.ui.unit.dp
Ryan Mentley7865a632019-08-20 20:10:29 -070041import androidx.ui.unit.height
Jelle Fresen11fc7062020-01-13 16:53:55 +000042import androidx.ui.unit.px
George Mount842c8c12020-01-08 16:03:42 -080043import androidx.ui.unit.round
Ryan Mentley7865a632019-08-20 20:10:29 -070044import androidx.ui.unit.width
Filip Pavlise63b30c2020-01-09 16:21:07 +000045import com.google.common.truth.Truth.assertThat
Matvei Malkov886ec6a2020-02-03 01:45:29 +000046import org.junit.Ignore
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010047import org.junit.Rule
48import org.junit.Test
49import org.junit.runner.RunWith
50import org.junit.runners.JUnit4
Jelle Fresen4c566312020-01-16 10:32:40 +000051import java.util.concurrent.CountDownLatch
52import java.util.concurrent.TimeUnit
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010053import kotlin.math.roundToInt
54
55@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 = {
Adam Powell999a89b2020-03-11 09:08:07 -070067 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +000068 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 Pavlis6fdb2502020-04-02 14:41:32 +010072 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +000073 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 = {
Adam Powell999a89b2020-03-11 09:08:07 -070082 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +000083 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 Pavlis6fdb2502020-04-02 14:41:32 +010088 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +000089 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 = {
Adam Powell999a89b2020-03-11 09:08:07 -070098 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
99 size = coords.size
100 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000101 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100102 }
103
104 val width = composeTestRule.displayMetrics.widthPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +0000105 composeTestRule.runOnIdleComposeWithDensity {
George Mount8baef7a2020-01-21 13:40:57 -0800106 assertThat(size!!.width.value)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100107 .isEqualTo(width - 56.dp.toPx().round().value)
108 }
109 }
110
111 @Test
112 fun bottomDrawer_testOffset_whenOpened() {
113 var position: PxPosition? = null
114 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100115 BottomDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700116 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +0000117 position = coords.localToGlobal(PxPosition.Origin)
118 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000119 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100120 }
Filip Pavlise63b30c2020-01-09 16:21:07 +0000121
Matvei Malkov2270be12019-07-08 16:59:45 +0100122 val width = composeTestRule.displayMetrics.widthPixels
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100123 val height = composeTestRule.displayMetrics.heightPixels
Matvei Malkov2270be12019-07-08 16:59:45 +0100124 // temporary calculation of landscape screen
Matvei Malkovb5e3d8a2020-03-09 15:17:20 +0000125 val expectedHeight = if (width > height) 0 else (height / 2f).roundToInt()
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100126 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000127 assertThat(position!!.y.round().value).isEqualTo(expectedHeight)
128 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100129 }
130
131 @Test
132 fun bottomDrawer_testOffset_whenClosed() {
133 var position: PxPosition? = null
134 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100135 BottomDrawerLayout(DrawerState.Closed, {}, drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700136 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +0000137 position = coords.localToGlobal(PxPosition.Origin)
138 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000139 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100140 }
141 val height = composeTestRule.displayMetrics.heightPixels
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100142 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000143 assertThat(position!!.y.round().value).isEqualTo(height)
144 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100145 }
146
147 @Test
Matvei Malkov886ec6a2020-02-03 01:45:29 +0000148 @Ignore("failing in postsubmit, fix in b/148751721")
Jelle Fresen4c566312020-01-16 10:32:40 +0000149 fun modalDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800150 var contentWidth: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000151 var openedLatch: CountDownLatch? = null
152 var closedLatch: CountDownLatch? = CountDownLatch(1)
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700153 val drawerState = mutableStateOf(DrawerState.Closed)
Jelle Fresen4c566312020-01-16 10:32:40 +0000154 composeTestRule.setMaterialContent {
155 TestTag("Drawer") {
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100156 ModalDrawerLayout(drawerState.value, { drawerState.value = it },
157 drawerContent = {
158 Box(
159 Modifier.fillMaxSize().onPositioned { info: LayoutCoordinates ->
160 val pos = info.localToGlobal(PxPosition.Origin)
161 if (pos.x == 0.px) {
162 // If fully opened, mark the openedLatch if present
163 openedLatch?.countDown()
164 } else if (-pos.x.round() == contentWidth) {
165 // If fully closed, mark the closedLatch if present
166 closedLatch?.countDown()
Jelle Fresen4c566312020-01-16 10:32:40 +0000167 }
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100168 }
169 )
170 },
171 bodyContent = {
172 Box(Modifier.fillMaxSize()
173 .onPositioned { contentWidth = it.size.width })
174 })
Jelle Fresen4c566312020-01-16 10:32:40 +0000175 }
176 }
177 // Drawer should start in closed state
178 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
179
180 // When the drawer state is set to Opened
181 openedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100182 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700183 drawerState.value = DrawerState.Opened
Jelle Fresen4c566312020-01-16 10:32:40 +0000184 }
185 // Then the drawer should be opened
186 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
187
188 // When the drawer state is set to Closed
189 closedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100190 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700191 drawerState.value = DrawerState.Closed
Jelle Fresen4c566312020-01-16 10:32:40 +0000192 }
193 // Then the drawer should be closed
194 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
195 }
196
197 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100198 fun modalDrawer_bodyContent_clickable() {
199 var drawerClicks = 0
200 var bodyClicks = 0
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700201 val drawerState = mutableStateOf(DrawerState.Closed)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100202 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700203 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100204 TestTag("Drawer") {
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100205 ModalDrawerLayout(drawerState.value, { drawerState.value = it },
206 drawerContent = {
Matvei Malkov323b7452020-05-01 16:57:52 +0100207 Box(
208 Modifier.fillMaxSize().clickable { drawerClicks += 1 },
209 children = emptyContent()
210 )
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100211 },
212 bodyContent = {
Matvei Malkov323b7452020-05-01 16:57:52 +0100213 Box(
214 Modifier.fillMaxSize().clickable { bodyClicks += 1 },
215 children = emptyContent()
216 )
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100217 })
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100218 }
219 }
220
Jelle Fresen11fc7062020-01-13 16:53:55 +0000221 // Click in the middle of the drawer (which is the middle of the body)
222 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100223
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100224 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000225 assertThat(drawerClicks).isEqualTo(0)
226 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100227
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700228 drawerState.value = DrawerState.Opened
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100229 }
Jelle Fresen11fc7062020-01-13 16:53:55 +0000230 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100231
Jelle Fresen11fc7062020-01-13 16:53:55 +0000232 // Click on the left-center pixel of the drawer
233 findByTag("Drawer").doGesture {
Ryan Mentley7865a632019-08-20 20:10:29 -0700234 val left = 1.px
Jelle Fresen11fc7062020-01-13 16:53:55 +0000235 val centerY = globalBounds.height / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700236 sendClick(PxPosition(left, centerY))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000237 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100238
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100239 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000240 assertThat(drawerClicks).isEqualTo(1)
241 assertThat(bodyClicks).isEqualTo(1)
Filip Pavlis2b161e42019-11-22 17:40:08 +0000242 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100243 }
244
245 @Test
Matvei Malkov886ec6a2020-02-03 01:45:29 +0000246 @Ignore("failing in postsubmit, fix in b/148751721")
Jelle Fresen4c566312020-01-16 10:32:40 +0000247 fun bottomDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800248 var contentHeight: IntPx? = null
249 var openedHeight: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000250 var openedLatch: CountDownLatch? = null
251 var closedLatch: CountDownLatch? = CountDownLatch(1)
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700252 val drawerState = mutableStateOf(DrawerState.Closed)
Jelle Fresen4c566312020-01-16 10:32:40 +0000253 composeTestRule.setMaterialContent {
254 TestTag("Drawer") {
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100255 BottomDrawerLayout(drawerState.value, { drawerState.value = it },
256 drawerContent = {
257 Box(Modifier.fillMaxSize().onPositioned { info: LayoutCoordinates ->
258 val pos = info.localToGlobal(PxPosition.Origin)
259 if (pos.y.round() == openedHeight) {
260 // If fully opened, mark the openedLatch if present
261 openedLatch?.countDown()
262 } else if (pos.y.round() == contentHeight) {
263 // If fully closed, mark the closedLatch if present
264 closedLatch?.countDown()
265 }
266 })
267 },
268 bodyContent = {
269 Box(Modifier.fillMaxSize().onPositioned {
270 contentHeight = it.size.height
271 openedHeight = it.size.height * BottomDrawerOpenFraction
272 })
273 }
274 )
Jelle Fresen4c566312020-01-16 10:32:40 +0000275 }
276 }
277 // Drawer should start in closed state
278 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
279
280 // When the drawer state is set to Opened
281 openedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100282 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700283 drawerState.value = DrawerState.Opened
Jelle Fresen4c566312020-01-16 10:32:40 +0000284 }
285 // Then the drawer should be opened
286 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
287
288 // When the drawer state is set to Closed
289 closedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100290 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700291 drawerState.value = DrawerState.Closed
Jelle Fresen4c566312020-01-16 10:32:40 +0000292 }
293 // Then the drawer should be closed
294 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
295 }
296
297 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100298 fun bottomDrawer_bodyContent_clickable() {
299 var drawerClicks = 0
300 var bodyClicks = 0
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700301 val drawerState = mutableStateOf(DrawerState.Closed)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100302 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700303 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100304 TestTag("Drawer") {
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100305 BottomDrawerLayout(drawerState.value, { drawerState.value = it },
306 drawerContent = {
Matvei Malkov323b7452020-05-01 16:57:52 +0100307 Box(
308 Modifier.fillMaxSize().clickable { drawerClicks += 1 },
309 children = emptyContent()
310 )
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100311 },
312 bodyContent = {
Matvei Malkov323b7452020-05-01 16:57:52 +0100313 Box(
314 Modifier.fillMaxSize().clickable { bodyClicks += 1 },
315 children = emptyContent()
316 )
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100317 })
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100318 }
319 }
320
Jelle Fresen11fc7062020-01-13 16:53:55 +0000321 // Click in the middle of the drawer (which is the middle of the body)
322 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100323
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100324 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000325 assertThat(drawerClicks).isEqualTo(0)
326 assertThat(bodyClicks).isEqualTo(1)
327 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100328
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100329 runOnUiThread {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700330 drawerState.value = DrawerState.Opened
Jelle Fresen11fc7062020-01-13 16:53:55 +0000331 }
332 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
333
334 // Click on the bottom-center pixel of the drawer
335 findByTag("Drawer").doGesture {
336 val bounds = globalBounds
337 val centerX = bounds.width / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700338 val bottom = bounds.height - 1.px
339 sendClick(PxPosition(centerX, bottom))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000340 }
341
342 assertThat(drawerClicks).isEqualTo(1)
343 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100344 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100345}