| /* |
| * Copyright 2021 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package androidx.tv.compose.foundation.lazy.grid |
| |
| import androidx.compose.animation.core.snap |
| import androidx.compose.foundation.gestures.animateScrollBy |
| import androidx.compose.foundation.gestures.scrollBy |
| import androidx.compose.foundation.layout.Box |
| import androidx.compose.foundation.layout.PaddingValues |
| import androidx.compose.foundation.layout.Spacer |
| import androidx.compose.foundation.layout.height |
| import androidx.compose.foundation.layout.requiredSize |
| import androidx.compose.foundation.layout.size |
| import androidx.compose.foundation.layout.width |
| import androidx.compose.ui.Modifier |
| import androidx.compose.ui.platform.testTag |
| import androidx.compose.ui.test.assertHeightIsEqualTo |
| import androidx.compose.ui.test.assertIsNotDisplayed |
| import androidx.compose.ui.test.assertLeftPositionInRootIsEqualTo |
| import androidx.compose.ui.test.assertTopPositionInRootIsEqualTo |
| import androidx.compose.ui.test.assertWidthIsEqualTo |
| import androidx.compose.ui.test.junit4.createComposeRule |
| import androidx.compose.ui.test.onNodeWithTag |
| import androidx.compose.ui.unit.Dp |
| import androidx.compose.ui.unit.dp |
| import androidx.test.ext.junit.runners.AndroidJUnit4 |
| import androidx.test.filters.LargeTest |
| import androidx.tv.foundation.lazy.AutoTestFrameClock |
| import androidx.tv.foundation.lazy.list.setContentWithTestViewConfiguration |
| import com.google.common.truth.Truth.assertThat |
| import kotlinx.coroutines.Dispatchers |
| import kotlinx.coroutines.runBlocking |
| import org.junit.Before |
| import org.junit.Rule |
| import org.junit.Test |
| import org.junit.runner.RunWith |
| |
| @LargeTest |
| @RunWith(AndroidJUnit4::class) |
| class LazyGridsContentPaddingTest { |
| private val LazyListTag = "LazyList" |
| private val ItemTag = "item" |
| private val ContainerTag = "container" |
| |
| @get:Rule |
| val rule = createComposeRule() |
| |
| private var itemSize: Dp = Dp.Infinity |
| private var smallPaddingSize: Dp = Dp.Infinity |
| private var itemSizePx = 50f |
| private var smallPaddingSizePx = 12f |
| |
| @Before |
| fun before() { |
| with(rule.density) { |
| itemSize = itemSizePx.toDp() |
| smallPaddingSize = smallPaddingSizePx.toDp() |
| } |
| } |
| |
| @Test |
| fun verticalGrid_contentPaddingIsApplied() { |
| lateinit var state: TvLazyGridState |
| val containerSize = itemSize * 2 |
| val largePaddingSize = itemSize |
| rule.setContent { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| modifier = Modifier.requiredSize(containerSize) |
| .testTag(LazyListTag), |
| state = rememberLazyGridState().also { state = it }, |
| contentPadding = PaddingValues( |
| start = smallPaddingSize, |
| top = largePaddingSize, |
| end = smallPaddingSize, |
| bottom = largePaddingSize |
| ) |
| ) { |
| items(listOf(1)) { |
| Spacer(Modifier.height(itemSize).testTag(ItemTag)) |
| } |
| } |
| } |
| |
| rule.onNodeWithTag(ItemTag) |
| .assertLeftPositionInRootIsEqualTo(smallPaddingSize) |
| .assertTopPositionInRootIsEqualTo(largePaddingSize) |
| .assertWidthIsEqualTo(containerSize - smallPaddingSize * 2) |
| .assertHeightIsEqualTo(itemSize) |
| |
| state.scrollBy(largePaddingSize) |
| |
| rule.onNodeWithTag(ItemTag) |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| .assertHeightIsEqualTo(itemSize) |
| } |
| |
| @Test |
| fun verticalGrid_contentPaddingIsNotAffectingScrollPosition() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| modifier = Modifier.requiredSize(itemSize * 2) |
| .testTag(LazyListTag), |
| state = rememberLazyGridState().also { state = it }, |
| contentPadding = PaddingValues( |
| top = itemSize, |
| bottom = itemSize |
| ) |
| ) { |
| items(listOf(1)) { |
| Spacer(Modifier.height(itemSize).testTag(ItemTag)) |
| } |
| } |
| } |
| |
| state.assertScrollPosition(0, 0.dp) |
| |
| state.scrollBy(itemSize) |
| |
| state.assertScrollPosition(0, itemSize) |
| } |
| |
| @Test |
| fun verticalGrid_scrollForwardItemWithinStartPaddingDisplayed() { |
| lateinit var state: TvLazyGridState |
| val padding = itemSize * 1.5f |
| rule.setContent { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| modifier = Modifier.requiredSize(padding * 2 + itemSize) |
| .testTag(LazyListTag), |
| state = rememberLazyGridState().also { state = it }, |
| contentPadding = PaddingValues( |
| top = padding, |
| bottom = padding |
| ) |
| ) { |
| items((0..3).toList()) { |
| Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| } |
| } |
| } |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(padding) |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize + padding) |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize * 2 + padding) |
| |
| state.scrollBy(padding) |
| |
| state.assertScrollPosition(1, padding - itemSize) |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize * 2) |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(itemSize * 3) |
| } |
| |
| @Test |
| fun verticalGrid_scrollBackwardItemWithinStartPaddingDisplayed() { |
| lateinit var state: TvLazyGridState |
| val padding = itemSize * 1.5f |
| rule.setContent { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| modifier = Modifier.requiredSize(itemSize + padding * 2) |
| .testTag(LazyListTag), |
| state = rememberLazyGridState().also { state = it }, |
| contentPadding = PaddingValues( |
| top = padding, |
| bottom = padding |
| ) |
| ) { |
| items((0..3).toList()) { |
| Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize * 3) |
| state.scrollBy(-itemSize * 1.5f) |
| |
| state.assertScrollPosition(1, itemSize * 0.5f) |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(itemSize * 1.5f - padding) |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize * 2.5f - padding) |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize * 3.5f - padding) |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(itemSize * 4.5f - padding) |
| } |
| |
| @Test |
| fun verticalGrid_scrollForwardTillTheEnd() { |
| lateinit var state: TvLazyGridState |
| val padding = itemSize * 1.5f |
| rule.setContent { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| modifier = Modifier.requiredSize(padding * 2 + itemSize) |
| .testTag(LazyListTag), |
| state = rememberLazyGridState().also { state = it }, |
| contentPadding = PaddingValues( |
| top = padding, |
| bottom = padding |
| ) |
| ) { |
| items((0..3).toList()) { |
| Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize * 3) |
| |
| state.assertScrollPosition(3, 0.dp) |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize - padding) |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize * 2 - padding) |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(itemSize * 3 - padding) |
| |
| // there are no space to scroll anymore, so it should change nothing |
| state.scrollBy(10.dp) |
| |
| state.assertScrollPosition(3, 0.dp) |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize - padding) |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize * 2 - padding) |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(itemSize * 3 - padding) |
| } |
| |
| @Test |
| fun verticalGrid_scrollForwardTillTheEndAndABitBack() { |
| lateinit var state: TvLazyGridState |
| val padding = itemSize * 1.5f |
| rule.setContent { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| modifier = Modifier.requiredSize(padding * 2 + itemSize) |
| .testTag(LazyListTag), |
| state = rememberLazyGridState().also { state = it }, |
| contentPadding = PaddingValues( |
| top = padding, |
| bottom = padding |
| ) |
| ) { |
| items((0..3).toList()) { |
| Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize * 3) |
| state.scrollBy(-itemSize / 2) |
| |
| state.assertScrollPosition(2, itemSize / 2) |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize * 1.5f - padding) |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize * 2.5f - padding) |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(itemSize * 3.5f - padding) |
| } |
| |
| @Test |
| fun verticalGrid_contentPaddingFixedWidthContainer() { |
| rule.setContent { |
| Box(modifier = Modifier.testTag(ContainerTag).width(itemSize + 8.dp)) { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| contentPadding = PaddingValues( |
| start = 2.dp, |
| top = 4.dp, |
| end = 6.dp, |
| bottom = 8.dp |
| ) |
| ) { |
| items(listOf(1)) { |
| Spacer(Modifier.size(itemSize).testTag(ItemTag)) |
| } |
| } |
| } |
| } |
| |
| rule.onNodeWithTag(ItemTag) |
| .assertLeftPositionInRootIsEqualTo(2.dp) |
| .assertTopPositionInRootIsEqualTo(4.dp) |
| .assertWidthIsEqualTo(itemSize) |
| .assertHeightIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag(ContainerTag) |
| .assertLeftPositionInRootIsEqualTo(0.dp) |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| .assertWidthIsEqualTo(itemSize + 2.dp + 6.dp) |
| .assertHeightIsEqualTo(itemSize + 4.dp + 8.dp) |
| } |
| |
| @Test |
| fun verticalGrid_contentPaddingAndNoContent() { |
| rule.setContent { |
| Box(modifier = Modifier.testTag(ContainerTag)) { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| contentPadding = PaddingValues( |
| start = 2.dp, |
| top = 4.dp, |
| end = 6.dp, |
| bottom = 8.dp |
| ) |
| ) { } |
| } |
| } |
| |
| rule.onNodeWithTag(ContainerTag) |
| .assertLeftPositionInRootIsEqualTo(0.dp) |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| .assertWidthIsEqualTo(8.dp) |
| .assertHeightIsEqualTo(12.dp) |
| } |
| |
| @Test |
| fun verticalGrid_contentPaddingAndZeroSizedItem() { |
| rule.setContent { |
| Box(modifier = Modifier.testTag(ContainerTag)) { |
| TvLazyVerticalGrid( |
| columns = TvGridCells.Fixed(1), |
| contentPadding = PaddingValues( |
| start = 2.dp, |
| top = 4.dp, |
| end = 6.dp, |
| bottom = 8.dp |
| ) |
| ) { |
| items(0) { } |
| } |
| } |
| } |
| |
| rule.onNodeWithTag(ContainerTag) |
| .assertLeftPositionInRootIsEqualTo(0.dp) |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| .assertWidthIsEqualTo(8.dp) |
| .assertHeightIsEqualTo(12.dp) |
| } |
| |
| @Test |
| fun verticalGrid_contentPaddingAndReverseLayout() { |
| val topPadding = itemSize * 2 |
| val bottomPadding = itemSize / 2 |
| val listSize = itemSize * 3 |
| lateinit var state: TvLazyGridState |
| rule.setContentWithTestViewConfiguration { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| reverseLayout = true, |
| state = rememberLazyGridState().also { state = it }, |
| modifier = Modifier.size(listSize), |
| contentPadding = PaddingValues(top = topPadding, bottom = bottomPadding), |
| ) { |
| items(3) { index -> |
| Box(Modifier.size(itemSize).testTag("$index")) |
| } |
| } |
| } |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(listSize - bottomPadding - itemSize) |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(listSize - bottomPadding - itemSize * 2) |
| // Partially visible. |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(-itemSize / 2) |
| |
| // Scroll to the top. |
| state.scrollBy(itemSize * 2.5f) |
| |
| rule.onNodeWithTag("2").assertTopPositionInRootIsEqualTo(topPadding) |
| // Shouldn't be visible |
| rule.onNodeWithTag("1").assertIsNotDisplayed() |
| rule.onNodeWithTag("0").assertIsNotDisplayed() |
| } |
| |
| @Test |
| fun column_overscrollWithContentPadding() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize + smallPaddingSize * 2)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues( |
| vertical = smallPaddingSize |
| ) |
| ) { |
| items(2) { |
| Box(Modifier.testTag("$it").height(itemSize)) |
| } |
| } |
| } |
| } |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(smallPaddingSize) |
| .assertHeightIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(smallPaddingSize + itemSize) |
| .assertHeightIsEqualTo(itemSize) |
| |
| rule.runOnIdle { |
| runBlocking { |
| // itemSizePx is the maximum offset, plus if we overscroll the content padding |
| // the layout mechanism will decide the item 0 is not needed until we start |
| // filling the over scrolled gap. |
| state.scrollBy(value = itemSizePx + smallPaddingSizePx * 1.5f) |
| } |
| } |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(smallPaddingSize) |
| .assertHeightIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(smallPaddingSize - itemSize) |
| .assertHeightIsEqualTo(itemSize) |
| } |
| |
| @Test |
| fun totalPaddingLargerParentSize_initialState() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag("1") |
| .assertDoesNotExist() |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(0, 0.dp) |
| state.assertVisibleItems(0 to 0.dp) |
| state.assertLayoutInfoOffsetRange(-itemSize, itemSize * 0.5f) |
| } |
| } |
| |
| @Test |
| fun totalPaddingLargerParentSize_scrollByPadding() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize) |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag("2") |
| .assertIsNotDisplayed() |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(1, 0.dp) |
| state.assertVisibleItems(0 to -itemSize, 1 to 0.dp) |
| } |
| } |
| |
| @Test |
| fun totalPaddingLargerParentSize_scrollToLastItem() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollTo(3) |
| |
| rule.onNodeWithTag("1") |
| .assertDoesNotExist() |
| |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(3, 0.dp) |
| state.assertVisibleItems(2 to -itemSize, 3 to 0.dp) |
| } |
| } |
| |
| @Test |
| fun totalPaddingLargerParentSize_scrollToLastItemByDelta() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize * 3) |
| |
| rule.onNodeWithTag("1") |
| .assertIsNotDisplayed() |
| |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(3, 0.dp) |
| state.assertVisibleItems(2 to -itemSize, 3 to 0.dp) |
| } |
| } |
| |
| @Test |
| fun totalPaddingLargerParentSize_scrollTillTheEnd() { |
| // the whole end content padding is displayed |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize * 4.5f) |
| |
| rule.onNodeWithTag("2") |
| .assertIsNotDisplayed() |
| |
| rule.onNodeWithTag("3") |
| .assertTopPositionInRootIsEqualTo(-itemSize * 0.5f) |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(3, itemSize * 1.5f) |
| state.assertVisibleItems(3 to -itemSize * 1.5f) |
| } |
| } |
| |
| @Test |
| fun eachPaddingLargerParentSize_initialState() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize * 2) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| rule.onNodeWithTag("0") |
| .assertIsNotDisplayed() |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(0, 0.dp) |
| state.assertVisibleItems(0 to 0.dp) |
| state.assertLayoutInfoOffsetRange(-itemSize * 2, -itemSize * 0.5f) |
| } |
| } |
| |
| @Test |
| fun eachPaddingLargerParentSize_scrollByPadding() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize * 2) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize * 2) |
| |
| rule.onNodeWithTag("0") |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag("2") |
| .assertIsNotDisplayed() |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(2, 0.dp) |
| state.assertVisibleItems(0 to -itemSize * 2, 1 to -itemSize, 2 to 0.dp) |
| } |
| } |
| |
| @Test |
| fun eachPaddingLargerParentSize_scrollToLastItem() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize * 2) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollTo(3) |
| |
| rule.onNodeWithTag("0") |
| .assertIsNotDisplayed() |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag("3") |
| .assertIsNotDisplayed() |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(3, 0.dp) |
| state.assertVisibleItems(1 to -itemSize * 2, 2 to -itemSize, 3 to 0.dp) |
| } |
| } |
| |
| @Test |
| fun eachPaddingLargerParentSize_scrollToLastItemByDelta() { |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize * 2) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollBy(itemSize * 3) |
| |
| rule.onNodeWithTag("0") |
| .assertIsNotDisplayed() |
| |
| rule.onNodeWithTag("1") |
| .assertTopPositionInRootIsEqualTo(0.dp) |
| |
| rule.onNodeWithTag("2") |
| .assertTopPositionInRootIsEqualTo(itemSize) |
| |
| rule.onNodeWithTag("3") |
| .assertIsNotDisplayed() |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(3, 0.dp) |
| state.assertVisibleItems(1 to -itemSize * 2, 2 to -itemSize, 3 to 0.dp) |
| } |
| } |
| |
| @Test |
| fun eachPaddingLargerParentSize_scrollTillTheEnd() { |
| // only the end content padding is displayed |
| lateinit var state: TvLazyGridState |
| rule.setContent { |
| state = rememberLazyGridState() |
| Box(modifier = Modifier.testTag(ContainerTag).size(itemSize * 1.5f)) { |
| TvLazyVerticalGrid( |
| TvGridCells.Fixed(1), |
| state = state, |
| contentPadding = PaddingValues(vertical = itemSize * 2) |
| ) { |
| items(4) { |
| Box(Modifier.testTag("$it").size(itemSize)) |
| } |
| } |
| } |
| } |
| |
| state.scrollBy( |
| itemSize * 1.5f + // container size |
| itemSize * 2 + // start padding |
| itemSize * 3 // all items |
| ) |
| |
| rule.onNodeWithTag("3") |
| .assertIsNotDisplayed() |
| |
| rule.runOnIdle { |
| state.assertScrollPosition(3, itemSize * 3.5f) |
| state.assertVisibleItems(3 to -itemSize * 3.5f) |
| } |
| } |
| |
| // @Test |
| // fun row_contentPaddingIsApplied() { |
| // lateinit var state: LazyGridState |
| // val containerSize = itemSize * 2 |
| // val largePaddingSize = itemSize |
| // rule.setContent { |
| // LazyRow( |
| // modifier = Modifier.requiredSize(containerSize) |
| // .testTag(LazyListTag), |
| // state = rememberLazyGridState().also { state = it }, |
| // contentPadding = PaddingValues( |
| // top = smallPaddingSize, |
| // start = largePaddingSize, |
| // bottom = smallPaddingSize, |
| // end = largePaddingSize |
| // ) |
| // ) { |
| // items(listOf(1)) { |
| // Spacer(Modifier.fillParentMaxHeight().width(itemSize).testTag(ItemTag)) |
| // } |
| // } |
| // } |
| |
| // rule.onNodeWithTag(ItemTag) |
| // .assertTopPositionInRootIsEqualTo(smallPaddingSize) |
| // .assertLeftPositionInRootIsEqualTo(largePaddingSize) |
| // .assertHeightIsEqualTo(containerSize - smallPaddingSize * 2) |
| // .assertWidthIsEqualTo(itemSize) |
| |
| // state.scrollBy(largePaddingSize) |
| |
| // rule.onNodeWithTag(ItemTag) |
| // .assertLeftPositionInRootIsEqualTo(0.dp) |
| // .assertWidthIsEqualTo(itemSize) |
| // } |
| |
| // @Test |
| // fun row_contentPaddingIsNotAffectingScrollPosition() { |
| // lateinit var state: LazyGridState |
| // val itemSize = with(rule.density) { |
| // 50.dp.roundToPx().toDp() |
| // } |
| // rule.setContent { |
| // LazyRow( |
| // modifier = Modifier.requiredSize(itemSize * 2) |
| // .testTag(LazyListTag), |
| // state = rememberLazyGridState().also { state = it }, |
| // contentPadding = PaddingValues( |
| // start = itemSize, |
| // end = itemSize |
| // ) |
| // ) { |
| // items(listOf(1)) { |
| // Spacer(Modifier.fillParentMaxHeight().width(itemSize).testTag(ItemTag)) |
| // } |
| // } |
| // } |
| |
| // state.assertScrollPosition(0, 0.dp) |
| |
| // state.scrollBy(itemSize) |
| |
| // state.assertScrollPosition(0, itemSize) |
| // } |
| |
| // @Test |
| // fun row_scrollForwardItemWithinStartPaddingDisplayed() { |
| // lateinit var state: LazyGridState |
| // val padding = itemSize * 1.5f |
| // rule.setContent { |
| // LazyRow( |
| // modifier = Modifier.requiredSize(padding * 2 + itemSize) |
| // .testTag(LazyListTag), |
| // state = rememberLazyGridState().also { state = it }, |
| // contentPadding = PaddingValues( |
| // start = padding, |
| // end = padding |
| // ) |
| // ) { |
| // items((0..3).toList()) { |
| // Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| // } |
| // } |
| // } |
| |
| // rule.onNodeWithTag("0") |
| // .assertLeftPositionInRootIsEqualTo(padding) |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(itemSize + padding) |
| // rule.onNodeWithTag("2") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 2 + padding) |
| |
| // state.scrollBy(padding) |
| |
| // state.assertScrollPosition(1, padding - itemSize) |
| |
| // rule.onNodeWithTag("0") |
| // .assertLeftPositionInRootIsEqualTo(0.dp) |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(itemSize) |
| // rule.onNodeWithTag("2") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 2) |
| // rule.onNodeWithTag("3") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 3) |
| // } |
| |
| // @Test |
| // fun row_scrollBackwardItemWithinStartPaddingDisplayed() { |
| // lateinit var state: LazyGridState |
| // val padding = itemSize * 1.5f |
| // rule.setContent { |
| // LazyRow( |
| // modifier = Modifier.requiredSize(itemSize + padding * 2) |
| // .testTag(LazyListTag), |
| // state = rememberLazyGridState().also { state = it }, |
| // contentPadding = PaddingValues( |
| // start = padding, |
| // end = padding |
| // ) |
| // ) { |
| // items((0..3).toList()) { |
| // Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| // } |
| // } |
| // } |
| |
| // state.scrollBy(itemSize * 3) |
| // state.scrollBy(-itemSize * 1.5f) |
| |
| // state.assertScrollPosition(1, itemSize * 0.5f) |
| |
| // rule.onNodeWithTag("0") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 1.5f - padding) |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 2.5f - padding) |
| // rule.onNodeWithTag("2") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 3.5f - padding) |
| // rule.onNodeWithTag("3") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 4.5f - padding) |
| // } |
| |
| // @Test |
| // fun row_scrollForwardTillTheEnd() { |
| // lateinit var state: LazyGridState |
| // val padding = itemSize * 1.5f |
| // rule.setContent { |
| // LazyRow( |
| // modifier = Modifier.requiredSize(padding * 2 + itemSize) |
| // .testTag(LazyListTag), |
| // state = rememberLazyGridState().also { state = it }, |
| // contentPadding = PaddingValues( |
| // start = padding, |
| // end = padding |
| // ) |
| // ) { |
| // items((0..3).toList()) { |
| // Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| // } |
| // } |
| // } |
| |
| // state.scrollBy(itemSize * 3) |
| |
| // state.assertScrollPosition(3, 0.dp) |
| |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(itemSize - padding) |
| // rule.onNodeWithTag("2") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 2 - padding) |
| // rule.onNodeWithTag("3") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 3 - padding) |
| |
| // // there are no space to scroll anymore, so it should change nothing |
| // state.scrollBy(10.dp) |
| |
| // state.assertScrollPosition(3, 0.dp) |
| |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(itemSize - padding) |
| // rule.onNodeWithTag("2") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 2 - padding) |
| // rule.onNodeWithTag("3") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 3 - padding) |
| // } |
| |
| // @Test |
| // fun row_scrollForwardTillTheEndAndABitBack() { |
| // lateinit var state: LazyGridState |
| // val padding = itemSize * 1.5f |
| // rule.setContent { |
| // LazyRow( |
| // modifier = Modifier.requiredSize(padding * 2 + itemSize) |
| // .testTag(LazyListTag), |
| // state = rememberLazyGridState().also { state = it }, |
| // contentPadding = PaddingValues( |
| // start = padding, |
| // end = padding |
| // ) |
| // ) { |
| // items((0..3).toList()) { |
| // Spacer(Modifier.requiredSize(itemSize).testTag(it.toString())) |
| // } |
| // } |
| // } |
| |
| // state.scrollBy(itemSize * 3) |
| // state.scrollBy(-itemSize / 2) |
| |
| // state.assertScrollPosition(2, itemSize / 2) |
| |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 1.5f - padding) |
| // rule.onNodeWithTag("2") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 2.5f - padding) |
| // rule.onNodeWithTag("3") |
| // .assertLeftPositionInRootIsEqualTo(itemSize * 3.5f - padding) |
| // } |
| |
| // @Test |
| // fun row_contentPaddingAndWrapContent() { |
| // rule.setContent { |
| // Box(modifier = Modifier.testTag(ContainerTag)) { |
| // LazyRow( |
| // contentPadding = PaddingValues( |
| // start = 2.dp, |
| // top = 4.dp, |
| // end = 6.dp, |
| // bottom = 8.dp |
| // ) |
| // ) { |
| // items(listOf(1)) { |
| // Spacer(Modifier.requiredSize(itemSize).testTag(ItemTag)) |
| // } |
| // } |
| // } |
| // } |
| |
| // rule.onNodeWithTag(ItemTag) |
| // .assertLeftPositionInRootIsEqualTo(2.dp) |
| // .assertTopPositionInRootIsEqualTo(4.dp) |
| // .assertWidthIsEqualTo(itemSize) |
| // .assertHeightIsEqualTo(itemSize) |
| |
| // rule.onNodeWithTag(ContainerTag) |
| // .assertLeftPositionInRootIsEqualTo(0.dp) |
| // .assertTopPositionInRootIsEqualTo(0.dp) |
| // .assertWidthIsEqualTo(itemSize + 2.dp + 6.dp) |
| // .assertHeightIsEqualTo(itemSize + 4.dp + 8.dp) |
| // } |
| |
| // @Test |
| // fun row_contentPaddingAndNoContent() { |
| // rule.setContent { |
| // Box(modifier = Modifier.testTag(ContainerTag)) { |
| // LazyRow( |
| // contentPadding = PaddingValues( |
| // start = 2.dp, |
| // top = 4.dp, |
| // end = 6.dp, |
| // bottom = 8.dp |
| // ) |
| // ) { } |
| // } |
| // } |
| |
| // rule.onNodeWithTag(ContainerTag) |
| // .assertLeftPositionInRootIsEqualTo(0.dp) |
| // .assertTopPositionInRootIsEqualTo(0.dp) |
| // .assertWidthIsEqualTo(8.dp) |
| // .assertHeightIsEqualTo(12.dp) |
| // } |
| |
| // @Test |
| // fun row_contentPaddingAndZeroSizedItem() { |
| // rule.setContent { |
| // Box(modifier = Modifier.testTag(ContainerTag)) { |
| // LazyRow( |
| // contentPadding = PaddingValues( |
| // start = 2.dp, |
| // top = 4.dp, |
| // end = 6.dp, |
| // bottom = 8.dp |
| // ) |
| // ) { |
| // items(0) {} |
| // } |
| // } |
| // } |
| |
| // rule.onNodeWithTag(ContainerTag) |
| // .assertLeftPositionInRootIsEqualTo(0.dp) |
| // .assertTopPositionInRootIsEqualTo(0.dp) |
| // .assertWidthIsEqualTo(8.dp) |
| // .assertHeightIsEqualTo(12.dp) |
| // } |
| |
| // @Test |
| // fun row_contentPaddingAndReverseLayout() { |
| // val startPadding = itemSize * 2 |
| // val endPadding = itemSize / 2 |
| // val listSize = itemSize * 3 |
| // lateinit var state: LazyGridState |
| // rule.setContentWithTestViewConfiguration { |
| // LazyRow( |
| // reverseLayout = true, |
| // state = rememberLazyGridState().also { state = it }, |
| // modifier = Modifier.requiredSize(listSize), |
| // contentPadding = PaddingValues(start = startPadding, end = endPadding), |
| // ) { |
| // items(3) { index -> |
| // Box(Modifier.requiredSize(itemSize).testTag("$index")) |
| // } |
| // } |
| // } |
| |
| // rule.onNodeWithTag("0") |
| // .assertLeftPositionInRootIsEqualTo(listSize - endPadding - itemSize) |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(listSize - endPadding - itemSize * 2) |
| // // Partially visible. |
| // rule.onNodeWithTag("2") |
| // .assertLeftPositionInRootIsEqualTo(-itemSize / 2) |
| |
| // // Scroll to the top. |
| // state.scrollBy(itemSize * 2.5f) |
| |
| // rule.onNodeWithTag("2").assertLeftPositionInRootIsEqualTo(startPadding) |
| // // Shouldn't be visible |
| // rule.onNodeWithTag("1").assertIsNotDisplayed() |
| // rule.onNodeWithTag("0").assertIsNotDisplayed() |
| // } |
| |
| // @Test |
| // fun row_overscrollWithContentPadding() { |
| // lateinit var state: LazyListState |
| // rule.setContent { |
| // state = rememberLazyListState() |
| // Box(modifier = Modifier.testTag(ContainerTag).size(itemSize + smallPaddingSize * 2)) { |
| // LazyRow( |
| // state = state, |
| // contentPadding = PaddingValues( |
| // horizontal = smallPaddingSize |
| // ) |
| // ) { |
| // items(2) { |
| // Box(Modifier.testTag("$it").fillParentMaxSize()) |
| // } |
| // } |
| // } |
| // } |
| |
| // rule.onNodeWithTag("0") |
| // .assertLeftPositionInRootIsEqualTo(smallPaddingSize) |
| // .assertWidthIsEqualTo(itemSize) |
| |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(smallPaddingSize + itemSize) |
| // .assertWidthIsEqualTo(itemSize) |
| |
| // rule.runOnIdle { |
| // runBlocking { |
| // // itemSizePx is the maximum offset, plus if we overscroll the content padding |
| // // the layout mechanism will decide the item 0 is not needed until we start |
| // // filling the over scrolled gap. |
| // state.scrollBy(value = itemSizePx + smallPaddingSizePx * 1.5f) |
| // } |
| // } |
| |
| // rule.onNodeWithTag("1") |
| // .assertLeftPositionInRootIsEqualTo(smallPaddingSize) |
| // .assertWidthIsEqualTo(itemSize) |
| |
| // rule.onNodeWithTag("0") |
| // .assertLeftPositionInRootIsEqualTo(smallPaddingSize - itemSize) |
| // .assertWidthIsEqualTo(itemSize) |
| // } |
| |
| private fun TvLazyGridState.scrollBy(offset: Dp) { |
| runBlocking(Dispatchers.Main + AutoTestFrameClock()) { |
| animateScrollBy(with(rule.density) { offset.roundToPx().toFloat() }, snap()) |
| } |
| } |
| |
| private fun TvLazyGridState.assertScrollPosition(index: Int, offset: Dp) = with(rule.density) { |
| assertThat(this@assertScrollPosition.firstVisibleItemIndex).isEqualTo(index) |
| assertThat(firstVisibleItemScrollOffset.toDp().value).isWithin(0.5f).of(offset.value) |
| } |
| |
| private fun TvLazyGridState.assertLayoutInfoOffsetRange(from: Dp, to: Dp) = with(rule.density) { |
| assertThat(layoutInfo.viewportStartOffset to layoutInfo.viewportEndOffset) |
| .isEqualTo(from.roundToPx() to to.roundToPx()) |
| } |
| |
| private fun TvLazyGridState.assertVisibleItems(vararg expected: Pair<Int, Dp>) = |
| with(rule.density) { |
| assertThat(layoutInfo.visibleItemsInfo.map { it.index to it.offset.y }) |
| .isEqualTo(expected.map { it.first to it.second.roundToPx() }) |
| } |
| |
| fun TvLazyGridState.scrollTo(index: Int) { |
| runBlocking(Dispatchers.Main + AutoTestFrameClock()) { |
| scrollToItem(index) |
| } |
| } |
| } |