[go: nahoru, domu]

blob: acc6cfd78fa2349d73f826b8a165ddad7fe5ad8d [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
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 Malkov7aa7fd22019-08-16 16:52:59 +010028import androidx.ui.foundation.Clickable
Adam Powell999a89b2020-03-11 09:08:07 -070029import androidx.ui.layout.fillMaxSize
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010030import androidx.ui.semantics.Semantics
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010031import androidx.ui.test.createComposeRule
Jelle Fresen11fc7062020-01-13 16:53:55 +000032import androidx.ui.test.doGesture
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010033import androidx.ui.test.findByTag
Jelle Fresen11fc7062020-01-13 16:53:55 +000034import androidx.ui.test.globalBounds
35import androidx.ui.test.sendClick
George Mount8baef7a2020-01-21 13:40:57 -080036import androidx.ui.unit.IntPx
37import androidx.ui.unit.IntPxSize
George Mount842c8c12020-01-08 16:03:42 -080038import androidx.ui.unit.PxPosition
George Mount842c8c12020-01-08 16:03:42 -080039import androidx.ui.unit.dp
Ryan Mentley7865a632019-08-20 20:10:29 -070040import androidx.ui.unit.height
Jelle Fresen11fc7062020-01-13 16:53:55 +000041import androidx.ui.unit.px
George Mount842c8c12020-01-08 16:03:42 -080042import androidx.ui.unit.round
Ryan Mentley7865a632019-08-20 20:10:29 -070043import androidx.ui.unit.width
Filip Pavlise63b30c2020-01-09 16:21:07 +000044import com.google.common.truth.Truth.assertThat
Matvei Malkov886ec6a2020-02-03 01:45:29 +000045import org.junit.Ignore
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010046import org.junit.Rule
47import org.junit.Test
48import org.junit.runner.RunWith
49import org.junit.runners.JUnit4
Jelle Fresen4c566312020-01-16 10:32:40 +000050import java.util.concurrent.CountDownLatch
51import java.util.concurrent.TimeUnit
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010052import kotlin.math.roundToInt
53
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010054@Model
55data class DrawerStateHolder(var state: DrawerState)
56
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010057@MediumTest
58@RunWith(JUnit4::class)
59class DrawerTest {
60
61 @get:Rule
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010062 val composeTestRule = createComposeRule(disableTransitions = true)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010063
64 @Test
65 fun modalDrawer_testOffset_whenOpened() {
66 var position: PxPosition? = null
67 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010068 ModalDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -070069 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +000070 position = coords.localToGlobal(PxPosition.Origin)
71 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +000072 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010073 }
Filip Pavlise63b30c2020-01-09 16:21:07 +000074 composeTestRule.runOnIdleCompose {
75 assertThat(position!!.x.value).isEqualTo(0f)
76 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010077 }
78
79 @Test
80 fun modalDrawer_testOffset_whenClosed() {
81 var position: PxPosition? = null
82 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010083 ModalDrawerLayout(DrawerState.Closed, {}, drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -070084 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +000085 position = coords.localToGlobal(PxPosition.Origin)
86 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +000087 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010088 }
89 val width = composeTestRule.displayMetrics.widthPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +000090 composeTestRule.runOnIdleCompose {
91 assertThat(position!!.x.round().value).isEqualTo(-width)
92 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010093 }
94
95 @Test
96 fun modalDrawer_testEndPadding_whenOpened() {
George Mount8baef7a2020-01-21 13:40:57 -080097 var size: IntPxSize? = null
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010098 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010099 ModalDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700100 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
101 size = coords.size
102 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000103 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100104 }
105
106 val width = composeTestRule.displayMetrics.widthPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +0000107 composeTestRule.runOnIdleComposeWithDensity {
George Mount8baef7a2020-01-21 13:40:57 -0800108 assertThat(size!!.width.value)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100109 .isEqualTo(width - 56.dp.toPx().round().value)
110 }
111 }
112
113 @Test
114 fun bottomDrawer_testOffset_whenOpened() {
115 var position: PxPosition? = null
116 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100117 BottomDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700118 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +0000119 position = coords.localToGlobal(PxPosition.Origin)
120 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000121 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100122 }
Filip Pavlise63b30c2020-01-09 16:21:07 +0000123
Matvei Malkov2270be12019-07-08 16:59:45 +0100124 val width = composeTestRule.displayMetrics.widthPixels
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100125 val height = composeTestRule.displayMetrics.heightPixels
Matvei Malkov2270be12019-07-08 16:59:45 +0100126 // temporary calculation of landscape screen
Matvei Malkovb5e3d8a2020-03-09 15:17:20 +0000127 val expectedHeight = if (width > height) 0 else (height / 2f).roundToInt()
Filip Pavlise63b30c2020-01-09 16:21:07 +0000128 composeTestRule.runOnIdleCompose {
129 assertThat(position!!.y.round().value).isEqualTo(expectedHeight)
130 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100131 }
132
133 @Test
134 fun bottomDrawer_testOffset_whenClosed() {
135 var position: PxPosition? = null
136 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100137 BottomDrawerLayout(DrawerState.Closed, {}, drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700138 Box(Modifier.fillMaxSize().onPositioned { coords: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +0000139 position = coords.localToGlobal(PxPosition.Origin)
140 })
Louis Pullen-Freiliche46bcf02020-02-12 16:06:21 +0000141 }, bodyContent = emptyContent())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100142 }
143 val height = composeTestRule.displayMetrics.heightPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +0000144 composeTestRule.runOnIdleCompose {
145 assertThat(position!!.y.round().value).isEqualTo(height)
146 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100147 }
148
149 @Test
150 fun staticDrawer_testWidth_whenOpened() {
151 composeTestRule
Matvei Malkov804bee42019-07-16 16:24:06 +0100152 .setMaterialContentAndCollectSizes {
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100153 StaticDrawer {
Adam Powell999a89b2020-03-11 09:08:07 -0700154 Box(Modifier.fillMaxSize())
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100155 }
156 }
157 .assertWidthEqualsTo(256.dp)
158 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100159
160 @Test
Matvei Malkov886ec6a2020-02-03 01:45:29 +0000161 @Ignore("failing in postsubmit, fix in b/148751721")
Jelle Fresen4c566312020-01-16 10:32:40 +0000162 fun modalDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800163 var contentWidth: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000164 var openedLatch: CountDownLatch? = null
165 var closedLatch: CountDownLatch? = CountDownLatch(1)
166 val drawerState = DrawerStateHolder(DrawerState.Closed)
167 composeTestRule.setMaterialContent {
168 TestTag("Drawer") {
169 Semantics(container = true) {
170 ModalDrawerLayout(drawerState.state, { drawerState.state = it },
171 drawerContent = {
Matvei Malkov201726d2020-03-13 14:03:54 +0000172 Box(
Adam Powell999a89b2020-03-11 09:08:07 -0700173 Modifier.fillMaxSize().onPositioned { info: LayoutCoordinates ->
George Mountb47c3b02020-03-10 23:26:42 -0700174 val pos = info.localToGlobal(PxPosition.Origin)
175 if (pos.x == 0.px) {
176 // If fully opened, mark the openedLatch if present
177 openedLatch?.countDown()
178 } else if (-pos.x.round() == contentWidth) {
179 // If fully closed, mark the closedLatch if present
180 closedLatch?.countDown()
181 }
Jelle Fresen4c566312020-01-16 10:32:40 +0000182 }
George Mountb47c3b02020-03-10 23:26:42 -0700183 )
Jelle Fresen4c566312020-01-16 10:32:40 +0000184 },
185 bodyContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700186 Box(Modifier.fillMaxSize()
187 .onPositioned { contentWidth = it.size.width })
Jelle Fresen4c566312020-01-16 10:32:40 +0000188 })
189 }
190 }
191 }
192 // Drawer should start in closed state
193 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
194
195 // When the drawer state is set to Opened
196 openedLatch = CountDownLatch(1)
197 composeTestRule.runOnIdleCompose {
198 drawerState.state = DrawerState.Opened
199 }
200 // Then the drawer should be opened
201 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
202
203 // When the drawer state is set to Closed
204 closedLatch = CountDownLatch(1)
205 composeTestRule.runOnIdleCompose {
206 drawerState.state = DrawerState.Closed
207 }
208 // Then the drawer should be closed
209 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
210 }
211
212 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100213 fun modalDrawer_bodyContent_clickable() {
214 var drawerClicks = 0
215 var bodyClicks = 0
216 val drawerState = DrawerStateHolder(DrawerState.Closed)
217 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700218 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100219 TestTag("Drawer") {
220 Semantics(container = true) {
221 ModalDrawerLayout(drawerState.state, { drawerState.state = it },
222 drawerContent = {
223 Clickable(onClick = { drawerClicks += 1 }) {
Adam Powell999a89b2020-03-11 09:08:07 -0700224 Box(Modifier.fillMaxSize(), children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100225 }
226 },
227 bodyContent = {
228 Clickable(onClick = { bodyClicks += 1 }) {
Adam Powell999a89b2020-03-11 09:08:07 -0700229 Box(Modifier.fillMaxSize(), children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100230 }
231 })
232 }
233 }
234 }
235
Jelle Fresen11fc7062020-01-13 16:53:55 +0000236 // Click in the middle of the drawer (which is the middle of the body)
237 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100238
Filip Pavlis2b161e42019-11-22 17:40:08 +0000239 composeTestRule.runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000240 assertThat(drawerClicks).isEqualTo(0)
241 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100242
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100243 drawerState.state = DrawerState.Opened
244 }
Jelle Fresen11fc7062020-01-13 16:53:55 +0000245 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100246
Jelle Fresen11fc7062020-01-13 16:53:55 +0000247 // Click on the left-center pixel of the drawer
248 findByTag("Drawer").doGesture {
Ryan Mentley7865a632019-08-20 20:10:29 -0700249 val left = 1.px
Jelle Fresen11fc7062020-01-13 16:53:55 +0000250 val centerY = globalBounds.height / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700251 sendClick(PxPosition(left, centerY))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000252 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100253
Filip Pavlis2b161e42019-11-22 17:40:08 +0000254 composeTestRule.runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000255 assertThat(drawerClicks).isEqualTo(1)
256 assertThat(bodyClicks).isEqualTo(1)
Filip Pavlis2b161e42019-11-22 17:40:08 +0000257 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100258 }
259
260 @Test
Matvei Malkov886ec6a2020-02-03 01:45:29 +0000261 @Ignore("failing in postsubmit, fix in b/148751721")
Jelle Fresen4c566312020-01-16 10:32:40 +0000262 fun bottomDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800263 var contentHeight: IntPx? = null
264 var openedHeight: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000265 var openedLatch: CountDownLatch? = null
266 var closedLatch: CountDownLatch? = CountDownLatch(1)
267 val drawerState = DrawerStateHolder(DrawerState.Closed)
268 composeTestRule.setMaterialContent {
269 TestTag("Drawer") {
270 Semantics(container = true) {
271 BottomDrawerLayout(drawerState.state, { drawerState.state = it },
272 drawerContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700273 Box(Modifier.fillMaxSize().onPositioned { info: LayoutCoordinates ->
Matvei Malkov201726d2020-03-13 14:03:54 +0000274 val pos = info.localToGlobal(PxPosition.Origin)
275 if (pos.y.round() == openedHeight) {
276 // If fully opened, mark the openedLatch if present
277 openedLatch?.countDown()
278 } else if (pos.y.round() == contentHeight) {
279 // If fully closed, mark the closedLatch if present
280 closedLatch?.countDown()
Jelle Fresen4c566312020-01-16 10:32:40 +0000281 }
Matvei Malkov201726d2020-03-13 14:03:54 +0000282 })
Jelle Fresen4c566312020-01-16 10:32:40 +0000283 },
284 bodyContent = {
Adam Powell999a89b2020-03-11 09:08:07 -0700285 Box(Modifier.fillMaxSize().onPositioned {
Matvei Malkov201726d2020-03-13 14:03:54 +0000286 contentHeight = it.size.height
287 openedHeight = it.size.height * BottomDrawerOpenFraction
288 })
George Mountb47c3b02020-03-10 23:26:42 -0700289 }
290 )
Jelle Fresen4c566312020-01-16 10:32:40 +0000291 }
292 }
293 }
294 // Drawer should start in closed state
295 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
296
297 // When the drawer state is set to Opened
298 openedLatch = CountDownLatch(1)
299 composeTestRule.runOnIdleCompose {
300 drawerState.state = DrawerState.Opened
301 }
302 // Then the drawer should be opened
303 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
304
305 // When the drawer state is set to Closed
306 closedLatch = CountDownLatch(1)
307 composeTestRule.runOnIdleCompose {
308 drawerState.state = DrawerState.Closed
309 }
310 // Then the drawer should be closed
311 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
312 }
313
314 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100315 fun bottomDrawer_bodyContent_clickable() {
316 var drawerClicks = 0
317 var bodyClicks = 0
318 val drawerState = DrawerStateHolder(DrawerState.Closed)
319 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700320 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100321 TestTag("Drawer") {
322 Semantics(container = true) {
323 BottomDrawerLayout(drawerState.state, { drawerState.state = it },
324 drawerContent = {
325 Clickable(onClick = { drawerClicks += 1 }) {
Adam Powell999a89b2020-03-11 09:08:07 -0700326 Box(Modifier.fillMaxSize(), children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100327 }
328 },
329 bodyContent = {
330 Clickable(onClick = { bodyClicks += 1 }) {
Adam Powell999a89b2020-03-11 09:08:07 -0700331 Box(Modifier.fillMaxSize(), children = emptyContent())
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100332 }
333 })
334 }
335 }
336 }
337
Jelle Fresen11fc7062020-01-13 16:53:55 +0000338 // Click in the middle of the drawer (which is the middle of the body)
339 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100340
Filip Pavlise63b30c2020-01-09 16:21:07 +0000341 composeTestRule.runOnIdleCompose {
342 assertThat(drawerClicks).isEqualTo(0)
343 assertThat(bodyClicks).isEqualTo(1)
344 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100345
Jelle Fresen11fc7062020-01-13 16:53:55 +0000346 composeTestRule.runOnUiThread {
347 drawerState.state = DrawerState.Opened
348 }
349 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
350
351 // Click on the bottom-center pixel of the drawer
352 findByTag("Drawer").doGesture {
353 val bounds = globalBounds
354 val centerX = bounds.width / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700355 val bottom = bounds.height - 1.px
356 sendClick(PxPosition(centerX, bottom))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000357 }
358
359 assertThat(drawerClicks).isEqualTo(1)
360 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100361 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100362}