[go: nahoru, domu]

blob: 8bd538411ecf8411471ddbb18479e59bdcac6f6b [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
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010021import androidx.test.filters.MediumTest
Jelle Fresen4c566312020-01-16 10:32:40 +000022import androidx.ui.core.OnChildPositioned
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010023import androidx.ui.core.OnPositioned
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010024import androidx.ui.core.TestTag
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010025import androidx.ui.foundation.Clickable
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010026import androidx.ui.layout.Container
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010027import androidx.ui.semantics.Semantics
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010028import androidx.ui.test.createComposeRule
Jelle Fresen11fc7062020-01-13 16:53:55 +000029import androidx.ui.test.doGesture
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010030import androidx.ui.test.findByTag
Jelle Fresen11fc7062020-01-13 16:53:55 +000031import androidx.ui.test.globalBounds
32import androidx.ui.test.sendClick
George Mount8baef7a2020-01-21 13:40:57 -080033import androidx.ui.unit.IntPx
34import androidx.ui.unit.IntPxSize
George Mount842c8c12020-01-08 16:03:42 -080035import androidx.ui.unit.PxPosition
George Mount842c8c12020-01-08 16:03:42 -080036import androidx.ui.unit.dp
Ryan Mentley7865a632019-08-20 20:10:29 -070037import androidx.ui.unit.height
Jelle Fresen11fc7062020-01-13 16:53:55 +000038import androidx.ui.unit.px
George Mount842c8c12020-01-08 16:03:42 -080039import androidx.ui.unit.round
Ryan Mentley7865a632019-08-20 20:10:29 -070040import androidx.ui.unit.width
Filip Pavlise63b30c2020-01-09 16:21:07 +000041import com.google.common.truth.Truth.assertThat
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010042import org.junit.Rule
43import org.junit.Test
44import org.junit.runner.RunWith
45import org.junit.runners.JUnit4
Jelle Fresen4c566312020-01-16 10:32:40 +000046import java.util.concurrent.CountDownLatch
47import java.util.concurrent.TimeUnit
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010048import kotlin.math.roundToInt
49
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010050@Model
51data class DrawerStateHolder(var state: DrawerState)
52
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010053@MediumTest
54@RunWith(JUnit4::class)
55class DrawerTest {
56
57 @get:Rule
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010058 val composeTestRule = createComposeRule(disableTransitions = true)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010059
60 @Test
61 fun modalDrawer_testOffset_whenOpened() {
62 var position: PxPosition? = null
63 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010064 ModalDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Matvei Malkov2d37cbd2019-07-04 15:15:20 +010065 Container(expanded = true) {
66 OnPositioned { coords ->
67 position = coords.localToGlobal(PxPosition.Origin)
68 }
69 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010070 }) {}
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 Malkov2d37cbd2019-07-04 15:15:20 +010082 Container(expanded = true) {
83 OnPositioned { coords ->
84 position = coords.localToGlobal(PxPosition.Origin)
85 }
86 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +010087 }) {}
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 = {
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100100 Container(expanded = true) {
101 OnPositioned { coords ->
102 size = coords.size
103 }
104 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100105 }) {}
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100106 }
107
108 val width = composeTestRule.displayMetrics.widthPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +0000109 composeTestRule.runOnIdleComposeWithDensity {
George Mount8baef7a2020-01-21 13:40:57 -0800110 assertThat(size!!.width.value)
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100111 .isEqualTo(width - 56.dp.toPx().round().value)
112 }
113 }
114
115 @Test
116 fun bottomDrawer_testOffset_whenOpened() {
117 var position: PxPosition? = null
118 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100119 BottomDrawerLayout(DrawerState.Opened, {}, drawerContent = {
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100120 Container(expanded = true) {
121 OnPositioned { coords ->
122 position = coords.localToGlobal(PxPosition.Origin)
123 }
124 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100125 }) {}
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100126 }
Filip Pavlise63b30c2020-01-09 16:21:07 +0000127
Matvei Malkov2270be12019-07-08 16:59:45 +0100128 val width = composeTestRule.displayMetrics.widthPixels
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100129 val height = composeTestRule.displayMetrics.heightPixels
Matvei Malkov2270be12019-07-08 16:59:45 +0100130 // temporary calculation of landscape screen
131 val expectedHeight = if (width > height) height else (height / 2f).roundToInt()
Filip Pavlise63b30c2020-01-09 16:21:07 +0000132 composeTestRule.runOnIdleCompose {
133 assertThat(position!!.y.round().value).isEqualTo(expectedHeight)
134 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100135 }
136
137 @Test
138 fun bottomDrawer_testOffset_whenClosed() {
139 var position: PxPosition? = null
140 composeTestRule.setMaterialContent {
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100141 BottomDrawerLayout(DrawerState.Closed, {}, drawerContent = {
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100142 Container(expanded = true) {
143 OnPositioned { coords ->
144 position = coords.localToGlobal(PxPosition.Origin)
145 }
146 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100147 }) {}
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100148 }
149 val height = composeTestRule.displayMetrics.heightPixels
Filip Pavlise63b30c2020-01-09 16:21:07 +0000150 composeTestRule.runOnIdleCompose {
151 assertThat(position!!.y.round().value).isEqualTo(height)
152 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100153 }
154
155 @Test
156 fun staticDrawer_testWidth_whenOpened() {
157 composeTestRule
Matvei Malkov804bee42019-07-16 16:24:06 +0100158 .setMaterialContentAndCollectSizes {
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100159 StaticDrawer {
160 Container(expanded = true) {}
161 }
162 }
163 .assertWidthEqualsTo(256.dp)
164 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100165
166 @Test
Jelle Fresen4c566312020-01-16 10:32:40 +0000167 fun modalDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800168 var contentWidth: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000169 var openedLatch: CountDownLatch? = null
170 var closedLatch: CountDownLatch? = CountDownLatch(1)
171 val drawerState = DrawerStateHolder(DrawerState.Closed)
172 composeTestRule.setMaterialContent {
173 TestTag("Drawer") {
174 Semantics(container = true) {
175 ModalDrawerLayout(drawerState.state, { drawerState.state = it },
176 drawerContent = {
177 OnChildPositioned({ info ->
George Mount8baef7a2020-01-21 13:40:57 -0800178 val pos = info.localToGlobal(PxPosition.Origin)
Jelle Fresen4c566312020-01-16 10:32:40 +0000179 if (pos.x == 0.px) {
180 // If fully opened, mark the openedLatch if present
181 openedLatch?.countDown()
George Mount8baef7a2020-01-21 13:40:57 -0800182 } else if (-pos.x.round() == contentWidth) {
Jelle Fresen4c566312020-01-16 10:32:40 +0000183 // If fully closed, mark the closedLatch if present
184 closedLatch?.countDown()
185 }
186 }) {
187 Container(expanded = true) {}
188 }
189 },
190 bodyContent = {
191 OnChildPositioned({ contentWidth = it.size.width }) {
192 Container(expanded = true) {}
193 }
194 })
195 }
196 }
197 }
198 // Drawer should start in closed state
199 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
200
201 // When the drawer state is set to Opened
202 openedLatch = CountDownLatch(1)
203 composeTestRule.runOnIdleCompose {
204 drawerState.state = DrawerState.Opened
205 }
206 // Then the drawer should be opened
207 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
208
209 // When the drawer state is set to Closed
210 closedLatch = CountDownLatch(1)
211 composeTestRule.runOnIdleCompose {
212 drawerState.state = DrawerState.Closed
213 }
214 // Then the drawer should be closed
215 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
216 }
217
218 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100219 fun modalDrawer_bodyContent_clickable() {
220 var drawerClicks = 0
221 var bodyClicks = 0
222 val drawerState = DrawerStateHolder(DrawerState.Closed)
223 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700224 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100225 TestTag("Drawer") {
226 Semantics(container = true) {
227 ModalDrawerLayout(drawerState.state, { drawerState.state = it },
228 drawerContent = {
229 Clickable(onClick = { drawerClicks += 1 }) {
230 Container(expanded = true) {}
231 }
232 },
233 bodyContent = {
234 Clickable(onClick = { bodyClicks += 1 }) {
235 Container(expanded = true) {}
236 }
237 })
238 }
239 }
240 }
241
Jelle Fresen11fc7062020-01-13 16:53:55 +0000242 // Click in the middle of the drawer (which is the middle of the body)
243 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100244
Filip Pavlis2b161e42019-11-22 17:40:08 +0000245 composeTestRule.runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000246 assertThat(drawerClicks).isEqualTo(0)
247 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100248
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100249 drawerState.state = DrawerState.Opened
250 }
Jelle Fresen11fc7062020-01-13 16:53:55 +0000251 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100252
Jelle Fresen11fc7062020-01-13 16:53:55 +0000253 // Click on the left-center pixel of the drawer
254 findByTag("Drawer").doGesture {
Ryan Mentley7865a632019-08-20 20:10:29 -0700255 val left = 1.px
Jelle Fresen11fc7062020-01-13 16:53:55 +0000256 val centerY = globalBounds.height / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700257 sendClick(PxPosition(left, centerY))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000258 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100259
Filip Pavlis2b161e42019-11-22 17:40:08 +0000260 composeTestRule.runOnIdleCompose {
Filip Pavlise63b30c2020-01-09 16:21:07 +0000261 assertThat(drawerClicks).isEqualTo(1)
262 assertThat(bodyClicks).isEqualTo(1)
Filip Pavlis2b161e42019-11-22 17:40:08 +0000263 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100264 }
265
266 @Test
Jelle Fresen4c566312020-01-16 10:32:40 +0000267 fun bottomDrawer_openAndClose() {
George Mount8baef7a2020-01-21 13:40:57 -0800268 var contentHeight: IntPx? = null
269 var openedHeight: IntPx? = null
Jelle Fresen4c566312020-01-16 10:32:40 +0000270 var openedLatch: CountDownLatch? = null
271 var closedLatch: CountDownLatch? = CountDownLatch(1)
272 val drawerState = DrawerStateHolder(DrawerState.Closed)
273 composeTestRule.setMaterialContent {
274 TestTag("Drawer") {
275 Semantics(container = true) {
276 BottomDrawerLayout(drawerState.state, { drawerState.state = it },
277 drawerContent = {
278 OnChildPositioned({ info ->
George Mount8baef7a2020-01-21 13:40:57 -0800279 val pos = info.localToGlobal(PxPosition.Origin)
280 if (pos.y.round() == openedHeight) {
Jelle Fresen4c566312020-01-16 10:32:40 +0000281 // If fully opened, mark the openedLatch if present
282 openedLatch?.countDown()
George Mount8baef7a2020-01-21 13:40:57 -0800283 } else if (pos.y.round() == contentHeight) {
Jelle Fresen4c566312020-01-16 10:32:40 +0000284 // If fully closed, mark the closedLatch if present
285 closedLatch?.countDown()
286 }
287 }) {
288 Container(expanded = true) {}
289 }
290 },
291 bodyContent = {
292 OnChildPositioned({
293 contentHeight = it.size.height
294 openedHeight = it.size.height * BottomDrawerOpenFraction
295 }) {
296 Container(expanded = true) {}
297 }
298 })
299 }
300 }
301 }
302 // Drawer should start in closed state
303 assertThat(closedLatch!!.await(5, TimeUnit.SECONDS)).isTrue()
304
305 // When the drawer state is set to Opened
306 openedLatch = CountDownLatch(1)
307 composeTestRule.runOnIdleCompose {
308 drawerState.state = DrawerState.Opened
309 }
310 // Then the drawer should be opened
311 assertThat(openedLatch.await(5, TimeUnit.SECONDS)).isTrue()
312
313 // When the drawer state is set to Closed
314 closedLatch = CountDownLatch(1)
315 composeTestRule.runOnIdleCompose {
316 drawerState.state = DrawerState.Closed
317 }
318 // Then the drawer should be closed
319 assertThat(closedLatch.await(5, TimeUnit.SECONDS)).isTrue()
320 }
321
322 @Test
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100323 fun bottomDrawer_bodyContent_clickable() {
324 var drawerClicks = 0
325 var bodyClicks = 0
326 val drawerState = DrawerStateHolder(DrawerState.Closed)
327 composeTestRule.setMaterialContent {
Aurimas Liutikas7a828d32019-10-07 17:16:05 -0700328 // emulate click on the screen
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100329 TestTag("Drawer") {
330 Semantics(container = true) {
331 BottomDrawerLayout(drawerState.state, { drawerState.state = it },
332 drawerContent = {
333 Clickable(onClick = { drawerClicks += 1 }) {
334 Container(expanded = true) {}
335 }
336 },
337 bodyContent = {
338 Clickable(onClick = { bodyClicks += 1 }) {
339 Container(expanded = true) {}
340 }
341 })
342 }
343 }
344 }
345
Jelle Fresen11fc7062020-01-13 16:53:55 +0000346 // Click in the middle of the drawer (which is the middle of the body)
347 findByTag("Drawer").doGesture { sendClick() }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100348
Filip Pavlise63b30c2020-01-09 16:21:07 +0000349 composeTestRule.runOnIdleCompose {
350 assertThat(drawerClicks).isEqualTo(0)
351 assertThat(bodyClicks).isEqualTo(1)
352 }
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100353
Jelle Fresen11fc7062020-01-13 16:53:55 +0000354 composeTestRule.runOnUiThread {
355 drawerState.state = DrawerState.Opened
356 }
357 sleep(100) // TODO(147586311): remove this sleep when opening the drawer triggers a wait
358
359 // Click on the bottom-center pixel of the drawer
360 findByTag("Drawer").doGesture {
361 val bounds = globalBounds
362 val centerX = bounds.width / 2
Ryan Mentley7865a632019-08-20 20:10:29 -0700363 val bottom = bounds.height - 1.px
364 sendClick(PxPosition(centerX, bottom))
Jelle Fresen11fc7062020-01-13 16:53:55 +0000365 }
366
367 assertThat(drawerClicks).isEqualTo(1)
368 assertThat(bodyClicks).isEqualTo(1)
Matvei Malkov7aa7fd22019-08-16 16:52:59 +0100369 }
Matvei Malkov2d37cbd2019-07-04 15:15:20 +0100370}