From 2df4698fa51db4524f6728409d675104f0708b2d Mon Sep 17 00:00:00 2001 From: Carson Holzheimer Date: Sat, 22 Nov 2025 10:27:20 +1000 Subject: [PATCH] Update compose bom to the next major version * Add a new function to KLazyListNode that allows finding a descendant at a position, rather than a direct child. This helps with the Compose 1.9 update which inserts new nodes for certain composables. --- .../node/element/lazylist/KLazyListNode.kt | 33 +++++++++++++++++++ gradle/libs.versions.toml | 2 +- .../kakaocup/compose/test/LazyListTest.kt | 7 ++-- .../kakaocup/compose/sample/LazyListScreen.kt | 23 +++++++++++++ 4 files changed, 62 insertions(+), 3 deletions(-) diff --git a/compose/src/main/kotlin/io/github/kakaocup/compose/node/element/lazylist/KLazyListNode.kt b/compose/src/main/kotlin/io/github/kakaocup/compose/node/element/lazylist/KLazyListNode.kt index 90987a1b..c4fa694d 100644 --- a/compose/src/main/kotlin/io/github/kakaocup/compose/node/element/lazylist/KLazyListNode.kt +++ b/compose/src/main/kotlin/io/github/kakaocup/compose/node/element/lazylist/KLazyListNode.kt @@ -6,6 +6,7 @@ import androidx.compose.ui.test.SemanticsMatcher import androidx.compose.ui.test.SemanticsNodeInteractionsProvider import androidx.compose.ui.test.filter import androidx.compose.ui.test.filterToOne +import androidx.compose.ui.test.hasAnyAncestor import androidx.compose.ui.test.onChildren import io.github.kakaocup.compose.node.assertion.LazyListNodeAssertions import io.github.kakaocup.compose.node.builder.NodeMatcher @@ -84,6 +85,38 @@ class KLazyListNode( ) as T) } + /** + * Performs given actions/assertion on descendant at given position + * + * @param T Type of item at given position. Must be registered via constructor. + * @param position Position of item in lazy list + * @param function Tail lambda which receiver will be matched item with given type T + */ + @ExperimentalTestApi + inline fun > descendantAt( + position: Int, + function: T.() -> Unit + ) { + val provideItem = itemTypes.getOrElse(T::class) { + throw LazyListItemProvisionException(T::class) + }.provideItem + + performScrollToIndex(position) + + val semanticsNode = semanticsProvider + .orGlobal() + .checkNotNull() + .onNode(positionMatcher(position) and + hasAnyAncestor(semanticsMatcher) + ) + .fetchSemanticsNode() + + function(provideItem( + semanticsNode, + semanticsProvider.orGlobal().checkNotNull() + ) as T) + } + /** * Performs given actions/assertion on child that matches given matcher * diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index ddd99838..0d4d5bfc 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -22,7 +22,7 @@ activityComposeVersion = "1.11.0" composeMaterialVersion = "1.3.1" composeCompilerVersion = "1.5.15" -androidxComposeBom = "2025.10.00" +androidxComposeBom = "2025.11.01" [libraries] dokka-gradle-plugin = { module = "org.jetbrains.dokka:dokka-gradle-plugin", version.ref = "dokkaGradlePluginVersion" } diff --git a/sample/src/androidTest/java/io/github/kakaocup/compose/test/LazyListTest.kt b/sample/src/androidTest/java/io/github/kakaocup/compose/test/LazyListTest.kt index 323bf2c4..d008b762 100644 --- a/sample/src/androidTest/java/io/github/kakaocup/compose/test/LazyListTest.kt +++ b/sample/src/androidTest/java/io/github/kakaocup/compose/test/LazyListTest.kt @@ -32,7 +32,7 @@ class LazyListTest { swipeDown(startY = 200f) } list { - assertLengthEquals(33) + assertLengthEquals(34) firstChild { title.assertTextEquals("Items from 1 to 10") } @@ -55,12 +55,15 @@ class LazyListTest { childAt(32) { assertTextEquals("Item 30") } + descendantAt(33) { + assertTextEquals("Nested Item 1") + } } list.performScrollToIndex(0) pullToRefresh.performTouchInput { swipeDown(startY = 200f) } - list.assertLengthEquals(34) + list.assertLengthEquals(35) } } } diff --git a/sample/src/main/java/io/github/kakaocup/compose/sample/LazyListScreen.kt b/sample/src/main/java/io/github/kakaocup/compose/sample/LazyListScreen.kt index 2652550a..293d0019 100644 --- a/sample/src/main/java/io/github/kakaocup/compose/sample/LazyListScreen.kt +++ b/sample/src/main/java/io/github/kakaocup/compose/sample/LazyListScreen.kt @@ -1,7 +1,9 @@ package io.github.kakaocup.compose.sample import android.annotation.SuppressLint +import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.background +import androidx.compose.foundation.border import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxWidth @@ -68,6 +70,7 @@ fun LazyListScreen() { when (item) { is LazyListItem.Header -> ListItemHeader(item, positionModifier) is LazyListItem.Item -> ListItemCell(item, positionModifier) + is LazyListItem.NestedItem -> NestedListItemCell(item, positionModifier) } } } @@ -110,6 +113,23 @@ private fun ListItemCell(item: LazyListItem.Item, modifier: Modifier = Modifier) ) } +@Composable +private fun NestedListItemCell(item: LazyListItem.NestedItem, modifier: Modifier = Modifier) { + Box( + modifier = Modifier.border(BorderStroke(2.dp, Color.Blue)) + ) { + Text( + item.title, + Modifier + .fillMaxWidth() + .padding(16.dp) + .testTag("LazyListItemTitle") + .then(modifier) + ) + } +} + + private fun getItems(): List { val result = mutableListOf() @@ -120,12 +140,15 @@ private fun getItems(): List { result += LazyListItem.Item("Item ${index + 1}") } + result += LazyListItem.NestedItem("Nested Item 1") + return result } private sealed class LazyListItem { data class Header(val title: String) : LazyListItem() data class Item(val title: String) : LazyListItem() + data class NestedItem(val title: String) : LazyListItem() } val LazyListItemPositionSemantics = SemanticsPropertyKey("LazyListItemPosition")