[go: nahoru, domu]

blob: ed3afdc6ff216eb27d0b17eb3105ada99cf518c2 [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
George Mountd02af602020-03-06 16:41:40 -080025import androidx.ui.core.onPositioned
Filip Pavlis1d4d1b832020-05-27 14:45:15 +010026import androidx.ui.core.testTag
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)
Nader Jawadfeb99f82020-05-21 13:07:36 -0700107 .isEqualTo(width - 56.dp.toPx().roundToInt())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100108 }
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 {
Filip Pavlis1d4d1b832020-05-27 14:45:15 +0100155 ModalDrawerLayout(drawerState.value, { drawerState.value = it },
156 drawerContent = {
157 Box(
158 Modifier.fillMaxSize().onPositioned { info: LayoutCoordinates ->
159 val pos = info.localToGlobal(PxPosition.Origin)
160 if (pos.x == 0.px) {
161 // If fully opened, mark the openedLatch if present
162 openedLatch?.countDown()
163 } else if (-pos.x.round() == contentWidth) {
164 // If fully closed, mark the closedLatch if present
165 closedLatch?.countDown()
Matvei Malkov9f27abf2020-05-19 15:15:56 +0100166 }
Filip Pavlis1d4d1b832020-05-27 14:45:15 +0100167 }
168 )
169 },
170 bodyContent = {
171 Box(Modifier.fillMaxSize()
172 .onPositioned { contentWidth = it.size.width })
173 })
Jelle Fresen4c566312020-01-16 10:32:40 +0000174 }
175 // Drawer should start in closed state
176 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
177
178 // When the drawer state is set to Opened
179 openedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100180 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700181 drawerState.value = DrawerState.Opened
Jelle Fresen4c566312020-01-16 10:32:40 +0000182 }
183 // Then the drawer should be opened
184 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
185
186 // When the drawer state is set to Closed
187 closedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100188 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700189 drawerState.value = DrawerState.Closed
Jelle Fresen4c566312020-01-16 10:32:40 +0000190 }
191 // Then the drawer should be closed
192 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
193 }
194
195 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100196 fun modalDrawer_bodyContent_clickable() {
197 var drawerClicks = 0
198 var bodyClicks = 0
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700199 val drawerState = mutableStateOf(DrawerState.Closed)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100200 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700201 // emulate click on the screen
Filip Pavlis1d4d1b832020-05-27 14:45:15 +0100202 ModalDrawerLayout(drawerState.value, { drawerState.value = it },
203 drawerContent = {
204 Box(
205 Modifier.fillMaxSize().clickable { drawerClicks += 1 },
206 children = emptyContent()
207 )
208 },
209 bodyContent = {
210 Box(
211 Modifier.testTag("Drawer").fillMaxSize().clickable { bodyClicks += 1 },
212 children = emptyContent()
213 )
214 })
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100215 }
216
Jelle Fresen11fc7062020-01-13 16:53:55 +0000217 // Click in the middle of the drawer (which is the middle of the body)
218 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100219
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100220 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000221 assertThat(drawerClicks).isEqualTo(0)
222 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100223
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700224 drawerState.value = DrawerState.Opened
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100225 }
Jelle Fresen11fc7062020-01-13 16:53:55 +0000226 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100227
Jelle Fresen11fc7062020-01-13 16:53:55 +0000228 // Click on the left-center pixel of the drawer
229 findByTag("Drawer").doGesture {
Ryan Mentley7865a632019-08-20 20:10:29 -0700230 val left = 1.px
Jelle Fresen11fc7062020-01-13 16:53:55 +0000231 val centerY = globalBounds.height / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700232 sendClick(PxPosition(left, centerY))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000233 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100234
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100235 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000236 assertThat(drawerClicks).isEqualTo(1)
237 assertThat(bodyClicks).isEqualTo(1)
Filip Pavlis2b161e42019-11-22 17:40:08 +0000238 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100239 }
240
241 @Test
Matvei Malkov886ec6a2020-02-03 01:45:29 +0000242 @Ignore("failing in postsubmit, fix in b/148751721")
Jelle Fresen4c566312020-01-16 10:32:40 +0000243 fun bottomDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800244 var contentHeight: IntPx? = null
245 var openedHeight: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000246 var openedLatch: CountDownLatch? = null
247 var closedLatch: CountDownLatch? = CountDownLatch(1)
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700248 val drawerState = mutableStateOf(DrawerState.Closed)
Jelle Fresen4c566312020-01-16 10:32:40 +0000249 composeTestRule.setMaterialContent {
Filip Pavlis1d4d1b832020-05-27 14:45:15 +0100250 BottomDrawerLayout(drawerState.value, { drawerState.value = it },
251 drawerContent = {
252 Box(Modifier.fillMaxSize().onPositioned { info: LayoutCoordinates ->
253 val pos = info.localToGlobal(PxPosition.Origin)
254 if (pos.y.round() == openedHeight) {
255 // If fully opened, mark the openedLatch if present
256 openedLatch?.countDown()
257 } else if (pos.y.round() == contentHeight) {
258 // If fully closed, mark the closedLatch if present
259 closedLatch?.countDown()
260 }
261 })
262 },
263 bodyContent = {
264 Box(Modifier.fillMaxSize().onPositioned {
265 contentHeight = it.size.height
266 openedHeight = it.size.height * BottomDrawerOpenFraction
267 })
268 }
269 )
Jelle Fresen4c566312020-01-16 10:32:40 +0000270 }
271 // Drawer should start in closed state
272 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
273
274 // When the drawer state is set to Opened
275 openedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100276 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700277 drawerState.value = DrawerState.Opened
Jelle Fresen4c566312020-01-16 10:32:40 +0000278 }
279 // Then the drawer should be opened
280 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
281
282 // When the drawer state is set to Closed
283 closedLatch = CountDownLatch(1)
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100284 runOnIdleCompose {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700285 drawerState.value = DrawerState.Closed
Jelle Fresen4c566312020-01-16 10:32:40 +0000286 }
287 // Then the drawer should be closed
288 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
289 }
290
291 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100292 fun bottomDrawer_bodyContent_clickable() {
293 var drawerClicks = 0
294 var bodyClicks = 0
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700295 val drawerState = mutableStateOf(DrawerState.Closed)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100296 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700297 // emulate click on the screen
Filip Pavlis1d4d1b832020-05-27 14:45:15 +0100298 BottomDrawerLayout(drawerState.value, { drawerState.value = it },
299 drawerContent = {
300 Box(
301 Modifier.fillMaxSize().clickable { drawerClicks += 1 },
302 children = emptyContent()
303 )
304 },
305 bodyContent = {
306 Box(
307 Modifier.testTag("Drawer").fillMaxSize().clickable { bodyClicks += 1 },
308 children = emptyContent()
309 )
310 })
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100311 }
312
Jelle Fresen11fc7062020-01-13 16:53:55 +0000313 // Click in the middle of the drawer (which is the middle of the body)
314 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100315
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100316 runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000317 assertThat(drawerClicks).isEqualTo(0)
318 assertThat(bodyClicks).isEqualTo(1)
319 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100320
Filip Pavlis6fdb2502020-04-02 14:41:32 +0100321 runOnUiThread {
Leland Richardsonfcf76b32020-05-13 16:58:59 -0700322 drawerState.value = DrawerState.Opened
Jelle Fresen11fc7062020-01-13 16:53:55 +0000323 }
324 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
325
326 // Click on the bottom-center pixel of the drawer
327 findByTag("Drawer").doGesture {
328 val bounds = globalBounds
329 val centerX = bounds.width / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700330 val bottom = bounds.height - 1.px
331 sendClick(PxPosition(centerX, bottom))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000332 }
333
334 assertThat(drawerClicks).isEqualTo(1)
335 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100336 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100337}