George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 1 | /* |
| 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 | */ |
Louis Pullen-Freilich | ddda7be | 2020-07-17 18:28:12 +0100 | [diff] [blame] | 16 | package androidx.compose.foundation |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 17 | |
Matvei Malkov | 1a41de3 | 2019-08-15 16:08:40 +0100 | [diff] [blame] | 18 | import android.os.Handler |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 19 | import android.os.Looper |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 20 | import androidx.annotation.RequiresApi |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 21 | import androidx.compose.animation.core.FloatExponentialDecaySpec |
Louis Pullen-Freilich | 5bb0c471 | 2020-07-20 22:01:15 +0100 | [diff] [blame] | 22 | import androidx.compose.animation.core.ManualAnimationClock |
Louis Pullen-Freilich | ddda7be | 2020-07-17 18:28:12 +0100 | [diff] [blame] | 23 | import androidx.compose.foundation.animation.FlingConfig |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 24 | import androidx.compose.foundation.animation.scrollBy |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 25 | import androidx.compose.foundation.animation.smoothScrollBy |
| 26 | import androidx.compose.foundation.gestures.Scrollable |
Mihai Popa | 60a90cc | 2020-09-15 12:17:41 +0100 | [diff] [blame] | 27 | import androidx.compose.foundation.layout.Box |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 28 | import androidx.compose.foundation.layout.Column |
| 29 | import androidx.compose.foundation.layout.Row |
Louis Pullen-Freilich | 623e405 | 2020-07-19 20:24:03 +0100 | [diff] [blame] | 30 | import androidx.compose.foundation.layout.preferredHeight |
| 31 | import androidx.compose.foundation.layout.preferredSize |
Louis Pullen-Freilich | 051800b | 2020-10-30 19:26:32 +0000 | [diff] [blame] | 32 | import androidx.compose.foundation.text.BasicText |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 33 | import androidx.compose.runtime.Composable |
| 34 | import androidx.compose.runtime.Providers |
| 35 | import androidx.compose.runtime.mutableStateOf |
Jelle Fresen | 3ac4b80 | 2021-01-06 11:22:38 +0000 | [diff] [blame] | 36 | import androidx.compose.testutils.MockAnimationClock |
Filip Pavlis | 591e17a | 2020-11-02 18:47:42 +0000 | [diff] [blame] | 37 | import androidx.compose.testutils.assertPixels |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 38 | import androidx.compose.ui.Modifier |
| 39 | import androidx.compose.ui.graphics.Color |
Jens Ole Lauridsen | cb1ca65 | 2020-10-28 14:43:30 -0700 | [diff] [blame] | 40 | import androidx.compose.ui.platform.InspectableValue |
Louis Pullen-Freilich | d42e107 | 2021-01-27 18:20:02 +0000 | [diff] [blame^] | 41 | import androidx.compose.ui.platform.LocalLayoutDirection |
Jens Ole Lauridsen | cb1ca65 | 2020-10-28 14:43:30 -0700 | [diff] [blame] | 42 | import androidx.compose.ui.platform.isDebugInspectorInfoEnabled |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 43 | import androidx.compose.ui.platform.testTag |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 44 | import androidx.compose.ui.test.ExperimentalTestApi |
Filip Pavlis | ce148943 | 2020-10-29 12:18:06 +0000 | [diff] [blame] | 45 | import androidx.compose.ui.test.GestureScope |
| 46 | import androidx.compose.ui.test.SemanticsNodeInteraction |
| 47 | import androidx.compose.ui.test.assertIsDisplayed |
| 48 | import androidx.compose.ui.test.assertIsNotDisplayed |
Filip Pavlis | 591e17a | 2020-11-02 18:47:42 +0000 | [diff] [blame] | 49 | import androidx.compose.ui.test.captureToImage |
Jelle Fresen | 3ac4b80 | 2021-01-06 11:22:38 +0000 | [diff] [blame] | 50 | import androidx.compose.ui.test.center |
| 51 | import androidx.compose.ui.test.down |
Filip Pavlis | ce148943 | 2020-10-29 12:18:06 +0000 | [diff] [blame] | 52 | import androidx.compose.ui.test.junit4.StateRestorationTester |
| 53 | import androidx.compose.ui.test.junit4.createComposeRule |
| 54 | import androidx.compose.ui.test.onNodeWithTag |
| 55 | import androidx.compose.ui.test.onNodeWithText |
| 56 | import androidx.compose.ui.test.performGesture |
| 57 | import androidx.compose.ui.test.performScrollTo |
| 58 | import androidx.compose.ui.test.swipeDown |
| 59 | import androidx.compose.ui.test.swipeLeft |
| 60 | import androidx.compose.ui.test.swipeRight |
| 61 | import androidx.compose.ui.test.swipeUp |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 62 | import androidx.compose.ui.unit.Dp |
| 63 | import androidx.compose.ui.unit.IntSize |
| 64 | import androidx.compose.ui.unit.LayoutDirection |
| 65 | import androidx.compose.ui.unit.dp |
Jelle Fresen | 53dd7b7 | 2020-09-25 10:02:27 +0100 | [diff] [blame] | 66 | import androidx.test.ext.junit.runners.AndroidJUnit4 |
| 67 | import androidx.test.filters.LargeTest |
| 68 | import androidx.test.filters.MediumTest |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 69 | import androidx.test.filters.SdkSuppress |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 70 | import com.google.common.truth.Truth.assertThat |
Jelle Fresen | 7ce06d6 | 2020-02-25 11:38:59 +0000 | [diff] [blame] | 71 | import com.google.common.truth.Truth.assertWithMessage |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 72 | import kotlinx.coroutines.runBlocking |
Jens Ole Lauridsen | cb1ca65 | 2020-10-28 14:43:30 -0700 | [diff] [blame] | 73 | import org.junit.After |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 74 | import org.junit.Assert.assertEquals |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 75 | import org.junit.Assert.assertTrue |
Jens Ole Lauridsen | cb1ca65 | 2020-10-28 14:43:30 -0700 | [diff] [blame] | 76 | import org.junit.Before |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 77 | import org.junit.Ignore |
Matvei Malkov | 1a41de3 | 2019-08-15 16:08:40 +0100 | [diff] [blame] | 78 | import org.junit.Rule |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 79 | import org.junit.Test |
| 80 | import org.junit.runner.RunWith |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 81 | import java.util.concurrent.CountDownLatch |
Jelle Fresen | 7ce06d6 | 2020-02-25 11:38:59 +0000 | [diff] [blame] | 82 | import java.util.concurrent.TimeUnit |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 83 | |
Jelle Fresen | 53dd7b7 | 2020-09-25 10:02:27 +0100 | [diff] [blame] | 84 | @MediumTest |
Jelle Fresen | 17628d7 | 2020-09-24 16:23:51 +0100 | [diff] [blame] | 85 | @RunWith(AndroidJUnit4::class) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 86 | class ScrollTest { |
Matvei Malkov | 1a41de3 | 2019-08-15 16:08:40 +0100 | [diff] [blame] | 87 | |
| 88 | @get:Rule |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 89 | val rule = createComposeRule() |
Matvei Malkov | 1a41de3 | 2019-08-15 16:08:40 +0100 | [diff] [blame] | 90 | |
Filip Pavlis | 582a2be | 2020-01-07 17:50:32 +0000 | [diff] [blame] | 91 | private val scrollerTag = "ScrollerTest" |
Matvei Malkov | 1a41de3 | 2019-08-15 16:08:40 +0100 | [diff] [blame] | 92 | |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 93 | private val defaultCrossAxisSize = 45 |
| 94 | private val defaultMainAxisSize = 40 |
| 95 | private val defaultCellSize = 5 |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 96 | |
| 97 | private val colors = listOf( |
Jelle Fresen | 3ee9440 | 2019-11-14 15:00:41 +0000 | [diff] [blame] | 98 | Color(red = 0xFF, green = 0, blue = 0, alpha = 0xFF), |
| 99 | Color(red = 0xFF, green = 0xA5, blue = 0, alpha = 0xFF), |
| 100 | Color(red = 0xFF, green = 0xFF, blue = 0, alpha = 0xFF), |
| 101 | Color(red = 0xA5, green = 0xFF, blue = 0, alpha = 0xFF), |
| 102 | Color(red = 0, green = 0xFF, blue = 0, alpha = 0xFF), |
| 103 | Color(red = 0, green = 0xFF, blue = 0xA5, alpha = 0xFF), |
| 104 | Color(red = 0, green = 0, blue = 0xFF, alpha = 0xFF), |
| 105 | Color(red = 0xA5, green = 0, blue = 0xFF, alpha = 0xFF) |
Louis Pullen-Freilich | 541e3c0 | 2019-05-02 21:17:19 +0100 | [diff] [blame] | 106 | ) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 107 | |
Jens Ole Lauridsen | cb1ca65 | 2020-10-28 14:43:30 -0700 | [diff] [blame] | 108 | @Before |
| 109 | fun before() { |
| 110 | isDebugInspectorInfoEnabled = true |
| 111 | } |
| 112 | |
| 113 | @After |
| 114 | fun after() { |
| 115 | isDebugInspectorInfoEnabled = false |
| 116 | } |
| 117 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 118 | @SdkSuppress(minSdkVersion = 26) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 119 | @Test |
| 120 | fun verticalScroller_SmallContent() { |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 121 | val height = 40 |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 122 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 123 | composeVerticalScroller(height = height) |
| 124 | |
| 125 | validateVerticalScroller(height = height) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 126 | } |
| 127 | |
| 128 | @Test |
Matvei Malkov | cc0ea42 | 2019-09-25 14:24:02 +0100 | [diff] [blame] | 129 | fun verticalScroller_SmallContent_Unscrollable() { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 130 | val scrollState = ScrollState( |
| 131 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 132 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 133 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 134 | ) |
Matvei Malkov | cc0ea42 | 2019-09-25 14:24:02 +0100 | [diff] [blame] | 135 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 136 | composeVerticalScroller(scrollState) |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 137 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 138 | rule.runOnIdle { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 139 | assertTrue(scrollState.maxValue == 0f) |
Matvei Malkov | 985dcfa | 2019-09-26 11:37:27 +0100 | [diff] [blame] | 140 | } |
Matvei Malkov | cc0ea42 | 2019-09-25 14:24:02 +0100 | [diff] [blame] | 141 | } |
| 142 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 143 | @SdkSuppress(minSdkVersion = 26) |
Matvei Malkov | cc0ea42 | 2019-09-25 14:24:02 +0100 | [diff] [blame] | 144 | @Test |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 145 | fun verticalScroller_LargeContent_NoScroll() { |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 146 | val height = 30 |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 147 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 148 | composeVerticalScroller(height = height) |
| 149 | |
| 150 | validateVerticalScroller(height = height) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 151 | } |
| 152 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 153 | @OptIn(ExperimentalTestApi::class) |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 154 | @SdkSuppress(minSdkVersion = 26) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 155 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 156 | fun verticalScroller_LargeContent_ScrollToEnd() = runBlocking { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 157 | val scrollState = ScrollState( |
| 158 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 159 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 160 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 161 | ) |
| 162 | val height = 30 |
| 163 | val scrollDistance = 10 |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 164 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 165 | composeVerticalScroller(scrollState, height = height) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 166 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 167 | validateVerticalScroller(height = height) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 168 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 169 | rule.awaitIdle() |
| 170 | assertEquals(scrollDistance.toFloat(), scrollState.maxValue) |
| 171 | scrollState.scrollTo(scrollDistance.toFloat()) |
Filip Pavlis | 2b161e4 | 2019-11-22 17:40:08 +0000 | [diff] [blame] | 172 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 173 | rule.runOnIdle {} // Just so the block below is correct |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 174 | validateVerticalScroller(offset = scrollDistance, height = height) |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 175 | } |
| 176 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 177 | @SdkSuppress(minSdkVersion = 26) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 178 | @Test |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 179 | fun verticalScroller_Reversed() { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 180 | val scrollState = ScrollState( |
| 181 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 182 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 183 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 184 | ) |
| 185 | val height = 30 |
| 186 | val expectedOffset = defaultCellSize * colors.size - height |
| 187 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 188 | composeVerticalScroller(scrollState, height = height, isReversed = true) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 189 | |
| 190 | validateVerticalScroller(offset = expectedOffset, height = height) |
| 191 | } |
| 192 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 193 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 194 | @SdkSuppress(minSdkVersion = 26) |
| 195 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 196 | fun verticalScroller_LargeContent_Reversed_ScrollToEnd() = runBlocking { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 197 | val scrollState = ScrollState( |
| 198 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 199 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 200 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 201 | ) |
| 202 | val height = 20 |
| 203 | val scrollDistance = 10 |
| 204 | val expectedOffset = defaultCellSize * colors.size - height - scrollDistance |
| 205 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 206 | composeVerticalScroller(scrollState, height = height, isReversed = true) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 207 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 208 | rule.awaitIdle() |
| 209 | scrollState.scrollTo(scrollDistance.toFloat()) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 210 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 211 | rule.awaitIdle() |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 212 | validateVerticalScroller(offset = expectedOffset, height = height) |
| 213 | } |
| 214 | |
| 215 | @SdkSuppress(minSdkVersion = 26) |
| 216 | @Test |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 217 | fun horizontalScroller_SmallContent() { |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 218 | val width = 40 |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 219 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 220 | composeHorizontalScroller(width = width) |
| 221 | |
| 222 | validateHorizontalScroller(width = width) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 223 | } |
| 224 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 225 | @SdkSuppress(minSdkVersion = 26) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 226 | @Test |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 227 | fun horizontalScroller_rtl_SmallContent() { |
| 228 | val width = 40 |
| 229 | |
| 230 | composeHorizontalScroller(width = width, isRtl = true) |
| 231 | |
| 232 | validateHorizontalScroller(width = width, checkInRtl = true) |
| 233 | } |
| 234 | |
| 235 | @SdkSuppress(minSdkVersion = 26) |
| 236 | @Test |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 237 | fun horizontalScroller_LargeContent_NoScroll() { |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 238 | val width = 30 |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 239 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 240 | composeHorizontalScroller(width = width) |
| 241 | |
| 242 | validateHorizontalScroller(width = width) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 243 | } |
| 244 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 245 | @SdkSuppress(minSdkVersion = 26) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 246 | @Test |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 247 | fun horizontalScroller_rtl_LargeContent_NoScroll() { |
| 248 | val width = 30 |
| 249 | |
| 250 | composeHorizontalScroller(width = width, isRtl = true) |
| 251 | |
| 252 | validateHorizontalScroller(width = width, checkInRtl = true) |
| 253 | } |
| 254 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 255 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 256 | @SdkSuppress(minSdkVersion = 26) |
| 257 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 258 | fun horizontalScroller_LargeContent_ScrollToEnd() = runBlocking { |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 259 | val width = 30 |
| 260 | val scrollDistance = 10 |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 261 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 262 | val scrollState = ScrollState( |
| 263 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 264 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 265 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 266 | ) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 267 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 268 | composeHorizontalScroller(scrollState, width = width) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 269 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 270 | validateHorizontalScroller(width = width) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 271 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 272 | rule.awaitIdle() |
| 273 | assertEquals(scrollDistance.toFloat(), scrollState.maxValue) |
| 274 | scrollState.scrollTo(scrollDistance.toFloat()) |
Filip Pavlis | 2b161e4 | 2019-11-22 17:40:08 +0000 | [diff] [blame] | 275 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 276 | rule.runOnIdle {} // Just so the block below is correct |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 277 | validateHorizontalScroller(offset = scrollDistance, width = width) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 278 | } |
| 279 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 280 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 281 | @SdkSuppress(minSdkVersion = 26) |
| 282 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 283 | fun horizontalScroller_rtl_LargeContent_ScrollToEnd() = runBlocking { |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 284 | val width = 30 |
| 285 | val scrollDistance = 10 |
| 286 | |
| 287 | val scrollState = ScrollState( |
| 288 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 289 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 290 | animationClock = ManualAnimationClock(0) |
| 291 | ) |
| 292 | |
| 293 | composeHorizontalScroller(scrollState, width = width, isRtl = true) |
| 294 | |
| 295 | validateHorizontalScroller(width = width, checkInRtl = true) |
| 296 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 297 | rule.awaitIdle() |
| 298 | assertEquals(scrollDistance.toFloat(), scrollState.maxValue) |
| 299 | scrollState.scrollTo(scrollDistance.toFloat()) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 300 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 301 | rule.awaitIdle() |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 302 | validateHorizontalScroller(offset = scrollDistance, width = width, checkInRtl = true) |
| 303 | } |
| 304 | |
| 305 | @SdkSuppress(minSdkVersion = 26) |
| 306 | @Test |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 307 | fun horizontalScroller_reversed() { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 308 | val scrollState = ScrollState( |
| 309 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 310 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 311 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 312 | ) |
| 313 | val width = 30 |
| 314 | val expectedOffset = defaultCellSize * colors.size - width |
| 315 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 316 | composeHorizontalScroller(scrollState, width = width, isReversed = true) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 317 | |
| 318 | validateHorizontalScroller(offset = expectedOffset, width = width) |
| 319 | } |
| 320 | |
| 321 | @SdkSuppress(minSdkVersion = 26) |
| 322 | @Test |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 323 | fun horizontalScroller_rtl_reversed() { |
| 324 | val scrollState = ScrollState( |
| 325 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 326 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 327 | animationClock = ManualAnimationClock(0) |
| 328 | ) |
| 329 | val width = 30 |
| 330 | val expectedOffset = defaultCellSize * colors.size - width |
| 331 | |
| 332 | composeHorizontalScroller(scrollState, width = width, isReversed = true, isRtl = true) |
| 333 | |
| 334 | validateHorizontalScroller(offset = expectedOffset, width = width, checkInRtl = true) |
| 335 | } |
| 336 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 337 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 338 | @SdkSuppress(minSdkVersion = 26) |
| 339 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 340 | fun horizontalScroller_LargeContent_Reversed_ScrollToEnd() = runBlocking { |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 341 | val width = 30 |
| 342 | val scrollDistance = 10 |
| 343 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 344 | val scrollState = ScrollState( |
| 345 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 346 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 347 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 348 | ) |
| 349 | |
| 350 | val expectedOffset = defaultCellSize * colors.size - width - scrollDistance |
| 351 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 352 | composeHorizontalScroller(scrollState, width = width, isReversed = true) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 353 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 354 | rule.awaitIdle() |
| 355 | scrollState.scrollTo(scrollDistance.toFloat()) |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 356 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 357 | rule.runOnIdle {} // Just so the block below is correct |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 358 | validateHorizontalScroller(offset = expectedOffset, width = width) |
| 359 | } |
| 360 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 361 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 362 | @SdkSuppress(minSdkVersion = 26) |
| 363 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 364 | fun horizontalScroller_rtl_LargeContent_Reversed_ScrollToEnd() = runBlocking { |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 365 | val width = 30 |
| 366 | val scrollDistance = 10 |
| 367 | |
| 368 | val scrollState = ScrollState( |
| 369 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 370 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 371 | animationClock = ManualAnimationClock(0) |
| 372 | ) |
| 373 | |
| 374 | val expectedOffset = defaultCellSize * colors.size - width - scrollDistance |
| 375 | |
| 376 | composeHorizontalScroller(scrollState, width = width, isReversed = true, isRtl = true) |
| 377 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 378 | rule.awaitIdle() |
| 379 | |
| 380 | scrollState.scrollTo(scrollDistance.toFloat()) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 381 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 382 | rule.runOnIdle {} // Just so the block below is correct |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 383 | validateHorizontalScroller(offset = expectedOffset, width = width, checkInRtl = true) |
| 384 | } |
| 385 | |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 386 | @Test |
| 387 | fun verticalScroller_scrollTo_scrollForward() { |
| 388 | createScrollableContent(isVertical = true) |
| 389 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 390 | rule.onNodeWithText("50") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 391 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 392 | .performScrollTo() |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 393 | .assertIsDisplayed() |
| 394 | } |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 395 | |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 396 | @Test |
| 397 | fun horizontalScroller_scrollTo_scrollForward() { |
| 398 | createScrollableContent(isVertical = false) |
| 399 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 400 | rule.onNodeWithText("50") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 401 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 402 | .performScrollTo() |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 403 | .assertIsDisplayed() |
| 404 | } |
| 405 | |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 406 | @Ignore("Unignore when b/156389287 is fixed for proper reverse and rtl delegation") |
| 407 | @Test |
| 408 | fun horizontalScroller_rtl_scrollTo_scrollForward() { |
| 409 | createScrollableContent(isVertical = false, isRtl = true) |
| 410 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 411 | rule.onNodeWithText("50") |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 412 | .assertIsNotDisplayed() |
| 413 | .performScrollTo() |
| 414 | .assertIsDisplayed() |
| 415 | } |
| 416 | |
| 417 | @Ignore("Unignore when b/156389287 is fixed for proper reverse delegation") |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 418 | @Test |
| 419 | fun verticalScroller_reversed_scrollTo_scrollForward() { |
| 420 | createScrollableContent( |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 421 | isVertical = true, |
| 422 | scrollState = ScrollState( |
| 423 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 424 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 425 | animationClock = ManualAnimationClock(0) |
| 426 | ), |
| 427 | isReversed = true |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 428 | ) |
| 429 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 430 | rule.onNodeWithText("50") |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 431 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 432 | .performScrollTo() |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 433 | .assertIsDisplayed() |
| 434 | } |
| 435 | |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 436 | @Ignore("Unignore when b/156389287 is fixed for proper reverse and rtl delegation") |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 437 | @Test |
| 438 | fun horizontalScroller_reversed_scrollTo_scrollForward() { |
| 439 | createScrollableContent( |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 440 | isVertical = false, |
| 441 | scrollState = ScrollState( |
| 442 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 443 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 444 | animationClock = ManualAnimationClock(0) |
| 445 | ), |
| 446 | isReversed = true |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 447 | ) |
| 448 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 449 | rule.onNodeWithText("50") |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 450 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 451 | .performScrollTo() |
Matvei Malkov | 0ed3ed5 | 2020-05-12 20:28:47 +0100 | [diff] [blame] | 452 | .assertIsDisplayed() |
| 453 | } |
| 454 | |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 455 | @Test |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 456 | @Ignore("When b/157687898 is fixed, performScrollTo must be adjusted to use semantic bounds") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 457 | fun verticalScroller_scrollTo_scrollBack() { |
| 458 | createScrollableContent(isVertical = true) |
| 459 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 460 | rule.onNodeWithText("50") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 461 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 462 | .performScrollTo() |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 463 | .assertIsDisplayed() |
| 464 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 465 | rule.onNodeWithText("20") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 466 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 467 | .performScrollTo() |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 468 | .assertIsDisplayed() |
| 469 | } |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 470 | |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 471 | @Test |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 472 | @Ignore("When b/157687898 is fixed, performScrollTo must be adjusted to use semantic bounds") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 473 | fun horizontalScroller_scrollTo_scrollBack() { |
| 474 | createScrollableContent(isVertical = false) |
| 475 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 476 | rule.onNodeWithText("50") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 477 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 478 | .performScrollTo() |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 479 | .assertIsDisplayed() |
| 480 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 481 | rule.onNodeWithText("20") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 482 | .assertIsNotDisplayed() |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 483 | .performScrollTo() |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 484 | .assertIsDisplayed() |
| 485 | } |
| 486 | |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 487 | @Test |
Jelle Fresen | 53dd7b7 | 2020-09-25 10:02:27 +0100 | [diff] [blame] | 488 | @LargeTest |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 489 | fun verticalScroller_swipeUp_swipeDown() { |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 490 | swipeScrollerAndBack(true, GestureScope::swipeUp, GestureScope::swipeDown) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 491 | } |
| 492 | |
| 493 | @Test |
Jelle Fresen | 53dd7b7 | 2020-09-25 10:02:27 +0100 | [diff] [blame] | 494 | @LargeTest |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 495 | fun horizontalScroller_swipeLeft_swipeRight() { |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 496 | swipeScrollerAndBack(false, GestureScope::swipeLeft, GestureScope::swipeRight) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 497 | } |
| 498 | |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 499 | @Test |
Jelle Fresen | 53dd7b7 | 2020-09-25 10:02:27 +0100 | [diff] [blame] | 500 | @LargeTest |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 501 | fun horizontalScroller_rtl_swipeLeft_swipeRight() { |
| 502 | swipeScrollerAndBack( |
| 503 | false, |
| 504 | GestureScope::swipeRight, |
| 505 | GestureScope::swipeLeft, |
| 506 | isRtl = true |
| 507 | ) |
| 508 | } |
| 509 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 510 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 511 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 512 | fun scroller_coerce_whenScrollTo() = runBlocking { |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 513 | val clock = ManualAnimationClock(0) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 514 | val scrollState = ScrollState( |
| 515 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 516 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 517 | animationClock = clock |
| 518 | ) |
| 519 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 520 | createScrollableContent(isVertical = true, scrollState = scrollState) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 521 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 522 | rule.awaitIdle() |
| 523 | |
| 524 | assertThat(scrollState.value).isEqualTo(0f) |
| 525 | assertThat(scrollState.maxValue).isGreaterThan(0f) |
| 526 | |
| 527 | scrollState.scrollTo(-100f) |
| 528 | assertThat(scrollState.value).isEqualTo(0f) |
| 529 | |
| 530 | (scrollState as Scrollable).scrollBy(-100f) |
| 531 | assertThat(scrollState.value).isEqualTo(0f) |
| 532 | |
| 533 | scrollState.scrollTo(scrollState.maxValue) |
| 534 | assertThat(scrollState.value).isEqualTo(scrollState.maxValue) |
| 535 | |
| 536 | scrollState.scrollTo(scrollState.maxValue + 1000) |
| 537 | assertThat(scrollState.value).isEqualTo(scrollState.maxValue) |
| 538 | |
| 539 | (scrollState as Scrollable).scrollBy(100f) |
| 540 | assertThat(scrollState.value).isEqualTo(scrollState.maxValue) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 541 | } |
| 542 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 543 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 544 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 545 | fun verticalScroller_LargeContent_coerceWhenMaxChanges() = runBlocking { |
Matvei Malkov | dff8391 | 2020-04-08 18:25:15 +0100 | [diff] [blame] | 546 | val clock = ManualAnimationClock(0) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 547 | val scrollState = ScrollState( |
| 548 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 549 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | dff8391 | 2020-04-08 18:25:15 +0100 | [diff] [blame] | 550 | animationClock = clock |
| 551 | ) |
| 552 | val itemCount = mutableStateOf(100) |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 553 | rule.setContent { |
Mihai Popa | 60a90cc | 2020-09-15 12:17:41 +0100 | [diff] [blame] | 554 | Box { |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 555 | Column( |
| 556 | Modifier |
| 557 | .preferredSize(100.dp) |
| 558 | .testTag(scrollerTag) |
| 559 | .verticalScroll(scrollState) |
Matvei Malkov | 1da47fe | 2020-05-15 12:40:16 +0100 | [diff] [blame] | 560 | ) { |
Matvei Malkov | 9fb6924 | 2020-05-19 14:07:14 +0100 | [diff] [blame] | 561 | for (i in 0..itemCount.value) { |
Louis Pullen-Freilich | 051800b | 2020-10-30 19:26:32 +0000 | [diff] [blame] | 562 | BasicText(i.toString()) |
Matvei Malkov | dff8391 | 2020-04-08 18:25:15 +0100 | [diff] [blame] | 563 | } |
| 564 | } |
| 565 | } |
| 566 | } |
| 567 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 568 | rule.awaitIdle() |
Matvei Malkov | dff8391 | 2020-04-08 18:25:15 +0100 | [diff] [blame] | 569 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 570 | assertThat(scrollState.value).isEqualTo(0f) |
| 571 | assertThat(scrollState.maxValue).isGreaterThan(0f) |
| 572 | val max = scrollState.maxValue |
| 573 | |
| 574 | scrollState.scrollTo(max) |
| 575 | itemCount.value -= 2 |
| 576 | |
| 577 | rule.awaitIdle() |
| 578 | val newMax = scrollState.maxValue |
| 579 | assertThat(newMax).isLessThan(max) |
| 580 | assertThat(scrollState.value).isEqualTo(newMax) |
Matvei Malkov | dff8391 | 2020-04-08 18:25:15 +0100 | [diff] [blame] | 581 | } |
| 582 | |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 583 | @OptIn(ExperimentalTestApi::class) |
Matvei Malkov | dff8391 | 2020-04-08 18:25:15 +0100 | [diff] [blame] | 584 | @Test |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 585 | fun scroller_coerce_whenScrollSmoothTo() = runBlocking { |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 586 | val clock = ManualAnimationClock(0) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 587 | val scrollState = ScrollState( |
| 588 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 589 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 590 | animationClock = clock |
| 591 | ) |
| 592 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 593 | createScrollableContent(isVertical = true, scrollState = scrollState) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 594 | |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 595 | rule.awaitIdle() |
| 596 | assertThat(scrollState.value).isEqualTo(0f) |
| 597 | assertThat(scrollState.maxValue).isGreaterThan(0f) |
| 598 | val max = scrollState.maxValue |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 599 | |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 600 | scrollState.smoothScrollTo(-100f) |
| 601 | assertThat(scrollState.value).isEqualTo(0f) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 602 | |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 603 | (scrollState as Scrollable).smoothScrollBy(-100f) |
| 604 | assertThat(scrollState.value).isEqualTo(0f) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 605 | |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 606 | scrollState.smoothScrollTo(scrollState.maxValue) |
| 607 | assertThat(scrollState.value).isEqualTo(max) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 608 | |
Ryan Mentley | bc9c8bb | 2020-12-09 08:08:53 -0800 | [diff] [blame] | 609 | scrollState.smoothScrollTo(scrollState.maxValue + 1000) |
| 610 | assertThat(scrollState.value).isEqualTo(max) |
| 611 | |
| 612 | (scrollState as Scrollable).smoothScrollBy(100f) |
| 613 | assertThat(scrollState.value).isEqualTo(max) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 614 | } |
| 615 | |
| 616 | @Test |
| 617 | fun scroller_whenFling_stopsByTouchDown() { |
| 618 | val clock = ManualAnimationClock(0) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 619 | val scrollState = ScrollState( |
| 620 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 621 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 622 | animationClock = clock |
| 623 | ) |
| 624 | |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 625 | createScrollableContent(isVertical = true, scrollState = scrollState) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 626 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 627 | rule.runOnIdle { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 628 | assertThat(scrollState.value).isEqualTo(0f) |
| 629 | assertThat(scrollState.isAnimationRunning).isEqualTo(false) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 630 | } |
| 631 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 632 | rule.onNodeWithTag(scrollerTag) |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 633 | .performGesture { swipeUp() } |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 634 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 635 | rule.runOnIdle { |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 636 | clock.clockTimeMillis += 100 |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 637 | assertThat(scrollState.isAnimationRunning).isEqualTo(true) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 638 | } |
| 639 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 640 | rule.onNodeWithTag(scrollerTag) |
Jelle Fresen | 3ac4b80 | 2021-01-06 11:22:38 +0000 | [diff] [blame] | 641 | .performGesture { down(center) } |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 642 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 643 | rule.runOnIdle { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 644 | assertThat(scrollState.isAnimationRunning).isEqualTo(false) |
Matvei Malkov | cbdfa05 | 2020-03-11 11:39:20 +0000 | [diff] [blame] | 645 | } |
| 646 | } |
| 647 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 648 | @OptIn(ExperimentalTestApi::class) |
Andrey Kulikov | fe8be4d | 2020-05-04 20:05:40 +0100 | [diff] [blame] | 649 | @Test |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 650 | fun scroller_restoresScrollerPosition() = runBlocking { |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 651 | val restorationTester = StateRestorationTester(rule) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 652 | var scrollState: ScrollState? = null |
Andrey Kulikov | fe8be4d | 2020-05-04 20:05:40 +0100 | [diff] [blame] | 653 | |
| 654 | restorationTester.setContent { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 655 | scrollState = rememberScrollState() |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 656 | Column(Modifier.verticalScroll(scrollState!!)) { |
Matvei Malkov | 9fb6924 | 2020-05-19 14:07:14 +0100 | [diff] [blame] | 657 | repeat(50) { |
| 658 | Box(Modifier.preferredHeight(100.dp)) |
Andrey Kulikov | fe8be4d | 2020-05-04 20:05:40 +0100 | [diff] [blame] | 659 | } |
| 660 | } |
| 661 | } |
| 662 | |
Ryan Mentley | 4ac8498 | 2021-01-14 13:45:24 -0800 | [diff] [blame] | 663 | rule.awaitIdle() |
| 664 | scrollState!!.scrollTo(70f) |
| 665 | scrollState = null |
Andrey Kulikov | fe8be4d | 2020-05-04 20:05:40 +0100 | [diff] [blame] | 666 | |
| 667 | restorationTester.emulateSavedInstanceStateRestore() |
| 668 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 669 | rule.runOnIdle { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 670 | assertThat(scrollState!!.value).isEqualTo(70f) |
Andrey Kulikov | fe8be4d | 2020-05-04 20:05:40 +0100 | [diff] [blame] | 671 | } |
| 672 | } |
| 673 | |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 674 | private fun swipeScrollerAndBack( |
| 675 | isVertical: Boolean, |
| 676 | firstSwipe: GestureScope.() -> Unit, |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 677 | secondSwipe: GestureScope.() -> Unit, |
| 678 | isRtl: Boolean = false |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 679 | ) { |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 680 | val clock = ManualAnimationClock(0) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 681 | val scrollState = ScrollState( |
| 682 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 683 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 684 | animationClock = clock |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 685 | ) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 686 | |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 687 | createScrollableContent(isVertical, scrollState = scrollState, isRtl = isRtl) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 688 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 689 | rule.runOnIdle { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 690 | assertThat(scrollState.value).isEqualTo(0f) |
Filip Pavlis | 582a2be | 2020-01-07 17:50:32 +0000 | [diff] [blame] | 691 | } |
| 692 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 693 | rule.onNodeWithTag(scrollerTag) |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 694 | .performGesture { firstSwipe() } |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 695 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 696 | rule.runOnIdle { |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 697 | clock.clockTimeMillis += 5000 |
| 698 | } |
| 699 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 700 | rule.onNodeWithTag(scrollerTag) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 701 | .awaitScrollAnimation(scrollState) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 702 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 703 | val scrolledValue = rule.runOnIdle { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 704 | scrollState.value |
Filip Pavlis | 582a2be | 2020-01-07 17:50:32 +0000 | [diff] [blame] | 705 | } |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 706 | assertThat(scrolledValue).isGreaterThan(0f) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 707 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 708 | rule.onNodeWithTag(scrollerTag) |
Filip Pavlis | 659ea72 | 2020-07-13 14:14:32 +0100 | [diff] [blame] | 709 | .performGesture { secondSwipe() } |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 710 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 711 | rule.runOnIdle { |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 712 | clock.clockTimeMillis += 5000 |
| 713 | } |
| 714 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 715 | rule.onNodeWithTag(scrollerTag) |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 716 | .awaitScrollAnimation(scrollState) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 717 | |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 718 | rule.runOnIdle { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 719 | assertThat(scrollState.value).isLessThan(scrolledValue) |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 720 | } |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 721 | } |
| 722 | |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 723 | private fun composeVerticalScroller( |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 724 | scrollState: ScrollState = ScrollState( |
| 725 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 726 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 727 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 728 | ), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 729 | isReversed: Boolean = false, |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 730 | width: Int = defaultCrossAxisSize, |
| 731 | height: Int = defaultMainAxisSize, |
| 732 | rowHeight: Int = defaultCellSize |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 733 | ) { |
| 734 | // We assume that the height of the device is more than 45 px |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 735 | with(rule.density) { |
| 736 | rule.setContent { |
Mihai Popa | 60a90cc | 2020-09-15 12:17:41 +0100 | [diff] [blame] | 737 | Box { |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 738 | Column( |
Matvei Malkov | 1da47fe | 2020-05-15 12:40:16 +0100 | [diff] [blame] | 739 | modifier = Modifier |
| 740 | .preferredSize(width.toDp(), height.toDp()) |
| 741 | .testTag(scrollerTag) |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 742 | .verticalScroll(scrollState, reverseScrolling = isReversed) |
Matvei Malkov | 1da47fe | 2020-05-15 12:40:16 +0100 | [diff] [blame] | 743 | ) { |
Matvei Malkov | 9fb6924 | 2020-05-19 14:07:14 +0100 | [diff] [blame] | 744 | colors.forEach { color -> |
| 745 | Box( |
Matvei Malkov | 0de3d28 | 2020-09-03 18:01:03 +0100 | [diff] [blame] | 746 | Modifier |
| 747 | .preferredSize(width.toDp(), rowHeight.toDp()) |
| 748 | .background(color) |
Matvei Malkov | 9fb6924 | 2020-05-19 14:07:14 +0100 | [diff] [blame] | 749 | ) |
Anastasia Soboleva | d70e670 | 2019-12-16 13:53:41 +0000 | [diff] [blame] | 750 | } |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 751 | } |
| 752 | } |
| 753 | } |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 754 | } |
| 755 | } |
| 756 | |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 757 | private fun composeHorizontalScroller( |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 758 | scrollState: ScrollState = ScrollState( |
| 759 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 760 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 761 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 762 | ), |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 763 | isReversed: Boolean = false, |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 764 | width: Int = defaultMainAxisSize, |
| 765 | height: Int = defaultCrossAxisSize, |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 766 | isRtl: Boolean = false |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 767 | ) { |
| 768 | // We assume that the height of the device is more than 45 px |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 769 | with(rule.density) { |
| 770 | rule.setContent { |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 771 | val direction = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr |
Louis Pullen-Freilich | d42e107 | 2021-01-27 18:20:02 +0000 | [diff] [blame^] | 772 | Providers(LocalLayoutDirection provides direction) { |
Mihai Popa | 60a90cc | 2020-09-15 12:17:41 +0100 | [diff] [blame] | 773 | Box { |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 774 | Row( |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 775 | modifier = Modifier |
| 776 | .preferredSize(width.toDp(), height.toDp()) |
| 777 | .testTag(scrollerTag) |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 778 | .horizontalScroll(scrollState, reverseScrolling = isReversed) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 779 | ) { |
| 780 | colors.forEach { color -> |
| 781 | Box( |
Matvei Malkov | 0de3d28 | 2020-09-03 18:01:03 +0100 | [diff] [blame] | 782 | Modifier |
| 783 | .preferredSize(defaultCellSize.toDp(), height.toDp()) |
| 784 | .background(color) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 785 | ) |
| 786 | } |
Anastasia Soboleva | d70e670 | 2019-12-16 13:53:41 +0000 | [diff] [blame] | 787 | } |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 788 | } |
| 789 | } |
| 790 | } |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 791 | } |
| 792 | } |
| 793 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 794 | @RequiresApi(api = 26) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 795 | private fun validateVerticalScroller( |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 796 | offset: Int = 0, |
| 797 | width: Int = 45, |
| 798 | height: Int = 40, |
| 799 | rowHeight: Int = 5 |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 800 | ) { |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 801 | rule.onNodeWithTag(scrollerTag) |
Filip Pavlis | 591e17a | 2020-11-02 18:47:42 +0000 | [diff] [blame] | 802 | .captureToImage() |
George Mount | 8f23757 | 2020-04-30 12:08:30 -0700 | [diff] [blame] | 803 | .assertPixels(expectedSize = IntSize(width, height)) { pos -> |
| 804 | val colorIndex = (offset + pos.y) / rowHeight |
Filip Pavlis | 582a2be | 2020-01-07 17:50:32 +0000 | [diff] [blame] | 805 | colors[colorIndex] |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 806 | } |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 807 | } |
| 808 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 809 | @RequiresApi(api = 26) |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 810 | private fun validateHorizontalScroller( |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 811 | offset: Int = 0, |
| 812 | width: Int = 40, |
| 813 | height: Int = 45, |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 814 | checkInRtl: Boolean = false |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 815 | ) { |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 816 | val scrollerWidth = colors.size * defaultCellSize |
| 817 | val absoluteOffset = if (checkInRtl) scrollerWidth - width - offset else offset |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 818 | rule.onNodeWithTag(scrollerTag) |
Filip Pavlis | 591e17a | 2020-11-02 18:47:42 +0000 | [diff] [blame] | 819 | .captureToImage() |
George Mount | 8f23757 | 2020-04-30 12:08:30 -0700 | [diff] [blame] | 820 | .assertPixels(expectedSize = IntSize(width, height)) { pos -> |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 821 | val colorIndex = (absoluteOffset + pos.x) / defaultCellSize |
| 822 | if (checkInRtl) colors[colors.size - 1 - colorIndex] else colors[colorIndex] |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 823 | } |
George Mount | bc7a78f | 2019-08-02 14:20:03 -0700 | [diff] [blame] | 824 | } |
| 825 | |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 826 | private fun createScrollableContent( |
| 827 | isVertical: Boolean, |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 828 | itemCount: Int = 100, |
| 829 | width: Dp = 100.dp, |
| 830 | height: Dp = 100.dp, |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 831 | isReversed: Boolean = false, |
| 832 | scrollState: ScrollState = ScrollState( |
| 833 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 834 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 0186e46 | 2020-02-20 11:35:14 +0000 | [diff] [blame] | 835 | animationClock = ManualAnimationClock(0) |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 836 | ), |
| 837 | isRtl: Boolean = false |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 838 | ) { |
Filip Pavlis | 3d2d039 | 2020-09-03 14:43:36 +0100 | [diff] [blame] | 839 | rule.setContent { |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 840 | val content = @Composable { |
Jelle Fresen | 389cb60 | 2019-11-14 16:54:28 +0000 | [diff] [blame] | 841 | repeat(itemCount) { |
Louis Pullen-Freilich | 051800b | 2020-10-30 19:26:32 +0000 | [diff] [blame] | 842 | BasicText(text = "$it") |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 843 | } |
| 844 | } |
Mihai Popa | 60a90cc | 2020-09-15 12:17:41 +0100 | [diff] [blame] | 845 | Box { |
Matvei Malkov | 2b055a7 | 2020-06-12 17:26:17 +0100 | [diff] [blame] | 846 | Box( |
Matvei Malkov | 0de3d28 | 2020-09-03 18:01:03 +0100 | [diff] [blame] | 847 | Modifier.preferredSize(width, height).background(Color.White) |
Matvei Malkov | 2b055a7 | 2020-06-12 17:26:17 +0100 | [diff] [blame] | 848 | ) { |
| 849 | if (isVertical) { |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 850 | Column( |
| 851 | Modifier |
| 852 | .testTag(scrollerTag) |
| 853 | .verticalScroll(scrollState, reverseScrolling = isReversed) |
Jelle Fresen | 33c83a3 | 2020-08-27 15:39:59 +0100 | [diff] [blame] | 854 | ) { |
| 855 | content() |
Matvei Malkov | 2b055a7 | 2020-06-12 17:26:17 +0100 | [diff] [blame] | 856 | } |
| 857 | } else { |
Matvei Malkov | 2ff160f | 2020-08-06 16:42:22 +0100 | [diff] [blame] | 858 | val direction = if (isRtl) LayoutDirection.Rtl else LayoutDirection.Ltr |
Louis Pullen-Freilich | d42e107 | 2021-01-27 18:20:02 +0000 | [diff] [blame^] | 859 | Providers(LocalLayoutDirection provides direction) { |
Andrey Kulikov | 05c18b2 | 2020-12-14 14:26:44 +0000 | [diff] [blame] | 860 | Row( |
| 861 | Modifier.testTag(scrollerTag) |
| 862 | .horizontalScroll(scrollState, reverseScrolling = isReversed) |
Jelle Fresen | 33c83a3 | 2020-08-27 15:39:59 +0100 | [diff] [blame] | 863 | ) { |
| 864 | content() |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 865 | } |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 866 | } |
Matvei Malkov | c09bc56 | 2020-02-05 18:13:34 -0800 | [diff] [blame] | 867 | } |
Cătălin Tudor | 1ff2b58 | 2019-11-12 13:38:32 +0000 | [diff] [blame] | 868 | } |
| 869 | } |
| 870 | } |
| 871 | } |
| 872 | |
Filip Pavlis | 582a2be | 2020-01-07 17:50:32 +0000 | [diff] [blame] | 873 | // TODO(b/147291885): This should not be needed in the future. |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 874 | private fun SemanticsNodeInteraction.awaitScrollAnimation( |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 875 | scroller: ScrollState |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 876 | ): SemanticsNodeInteraction { |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 877 | val latch = CountDownLatch(1) |
| 878 | val handler = Handler(Looper.getMainLooper()) |
| 879 | handler.post(object : Runnable { |
| 880 | override fun run() { |
Matvei Malkov | 235b4fa | 2020-07-07 21:14:49 +0100 | [diff] [blame] | 881 | if (scroller.isAnimationRunning) { |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 882 | handler.post(this) |
| 883 | } else { |
| 884 | latch.countDown() |
| 885 | } |
| 886 | } |
| 887 | }) |
Jelle Fresen | 7ce06d6 | 2020-02-25 11:38:59 +0000 | [diff] [blame] | 888 | assertWithMessage("Scroll didn't finish after 20 seconds") |
| 889 | .that(latch.await(20, TimeUnit.SECONDS)).isTrue() |
Jelle Fresen | 2c776c6 | 2019-11-15 16:52:39 +0000 | [diff] [blame] | 890 | return this |
| 891 | } |
Jens Ole Lauridsen | cb1ca65 | 2020-10-28 14:43:30 -0700 | [diff] [blame] | 892 | |
| 893 | @Test |
| 894 | fun testInspectorValue() { |
| 895 | val state = ScrollState( |
| 896 | initial = 0f, |
Jelle Fresen | b40ab8e | 2020-09-08 14:18:41 +0100 | [diff] [blame] | 897 | flingConfig = FlingConfig(FloatExponentialDecaySpec()), |
Jelle Fresen | 3ac4b80 | 2021-01-06 11:22:38 +0000 | [diff] [blame] | 898 | animationClock = MockAnimationClock() |
Jens Ole Lauridsen | cb1ca65 | 2020-10-28 14:43:30 -0700 | [diff] [blame] | 899 | ) |
| 900 | rule.setContent { |
| 901 | val modifier = Modifier.verticalScroll(state) as InspectableValue |
| 902 | assertThat(modifier.nameFallback).isEqualTo("scroll") |
| 903 | assertThat(modifier.valueOverride).isNull() |
| 904 | assertThat(modifier.inspectableElements.map { it.name }.asIterable()).containsExactly( |
| 905 | "state", |
| 906 | "reverseScrolling", |
| 907 | "isScrollable", |
| 908 | "isVertical" |
| 909 | ) |
| 910 | } |
| 911 | } |
George Mount | e872c33 | 2019-04-12 15:03:59 -0700 | [diff] [blame] | 912 | } |