Skip to content

Commit e1506d9

Browse files
committed
Fix tests and lint issues
1 parent 8f129eb commit e1506d9

File tree

4 files changed

+75
-10
lines changed

4 files changed

+75
-10
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/TimelineView.kt

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ import io.element.android.libraries.designsystem.utils.animateScrollToItemCenter
7272
import io.element.android.libraries.matrix.api.core.EventId
7373
import io.element.android.libraries.matrix.api.core.UserId
7474
import io.element.android.libraries.matrix.api.timeline.Timeline
75+
import io.element.android.libraries.testtags.TestTags
76+
import io.element.android.libraries.testtags.testTag
7577
import io.element.android.libraries.ui.strings.CommonStrings
7678
import kotlinx.coroutines.ExperimentalCoroutinesApi
7779
import kotlinx.coroutines.flow.collectLatest
@@ -143,7 +145,8 @@ fun TimelineView(
143145
LazyColumn(
144146
modifier = Modifier
145147
.fillMaxSize()
146-
.nestedScroll(nestedScrollConnection),
148+
.nestedScroll(nestedScrollConnection)
149+
.testTag(TestTags.timeline),
147150
state = lazyListState,
148151
reverseLayout = useReverseLayout,
149152
contentPadding = PaddingValues(vertical = 8.dp),
@@ -220,6 +223,8 @@ private fun TimelinePrefetchingHelper(
220223
lazyListState: LazyListState,
221224
prefetch: () -> Unit,
222225
) {
226+
val latestPrefetch by rememberUpdatedState(prefetch)
227+
223228
// We're using snapshot flows for these because using `LaunchedEffect` with `derivedState` doesn't seem to be responsive enough
224229
val firstVisibleItemIndexFlow = snapshotFlow {
225230
lazyListState.firstVisibleItemIndex
@@ -231,19 +236,22 @@ private fun TimelinePrefetchingHelper(
231236
lazyListState.isScrollInProgress
232237
}
233238

234-
LaunchedEffect(Unit) {
239+
LaunchedEffect(latestPrefetch) {
235240
val isCloseToStartOfLoadedTimelineFlow = combine(layoutInfoFlow, firstVisibleItemIndexFlow) { layoutInfo, firstVisibleItemIndex ->
236241
firstVisibleItemIndex + layoutInfo.visibleItemsInfo.size >= layoutInfo.totalItemsCount - 40
237242
}
238243

239-
combine(isCloseToStartOfLoadedTimelineFlow, isScrollingFlow) { needsPrefetch, isScrolling ->
244+
combine(
245+
isCloseToStartOfLoadedTimelineFlow,
246+
isScrollingFlow,
247+
) { needsPrefetch, isScrolling ->
240248
needsPrefetch && isScrolling
241249
}
242250
.distinctUntilChanged()
243251
.collectLatest { needsPrefetch ->
244252
if (needsPrefetch) {
245253
Timber.d("Prefetching pagination with ${lazyListState.layoutInfo.totalItemsCount} items")
246-
prefetch()
254+
latestPrefetch()
247255
}
248256
}
249257
}
@@ -274,7 +282,7 @@ private fun BoxScope.TimelineScrollHelper(
274282
coroutineScope.launch {
275283
if (lazyListState.firstVisibleItemIndex > 10) {
276284
lazyListState.scrollToItem(0)
277-
} else {
285+
} else if (lazyListState.firstVisibleItemIndex != 0) {
278286
lazyListState.animateScrollToItem(0)
279287
}
280288
}

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/MessagesViewTest.kt

Lines changed: 24 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ import io.element.android.features.messages.impl.pinned.banner.aLoadedPinnedMess
4141
import io.element.android.features.messages.impl.timeline.FOCUS_ON_PINNED_EVENT_DEBOUNCE_DURATION_IN_MILLIS
4242
import io.element.android.features.messages.impl.timeline.TimelineEvents
4343
import io.element.android.features.messages.impl.timeline.aTimelineItemEvent
44+
import io.element.android.features.messages.impl.timeline.aTimelineItemList
4445
import io.element.android.features.messages.impl.timeline.aTimelineItemReadReceipts
4546
import io.element.android.features.messages.impl.timeline.aTimelineRoomInfo
4647
import io.element.android.features.messages.impl.timeline.aTimelineState
@@ -50,6 +51,7 @@ import io.element.android.features.messages.impl.timeline.components.reactionsum
5051
import io.element.android.features.messages.impl.timeline.components.receipt.aReadReceiptData
5152
import io.element.android.features.messages.impl.timeline.components.receipt.bottomsheet.ReadReceiptBottomSheetEvents
5253
import io.element.android.features.messages.impl.timeline.model.TimelineItem
54+
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemTextContent
5355
import io.element.android.libraries.matrix.api.core.UserId
5456
import io.element.android.libraries.matrix.test.AN_EVENT_ID
5557
import io.element.android.libraries.testtags.TestTags
@@ -126,6 +128,9 @@ class MessagesViewTest {
126128
fun `clicking on an Event invoke expected callback`() {
127129
val eventsRecorder = EventsRecorder<MessagesEvents>(expectEvents = false)
128130
val state = aMessagesState(
131+
timelineState = aTimelineState(
132+
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
133+
),
129134
eventSink = eventsRecorder
130135
)
131136
val timelineItem = state.timelineState.timelineItems.first()
@@ -182,6 +187,9 @@ class MessagesViewTest {
182187
canSendReaction = userHasPermissionToSendReaction,
183188
canPinUnpin = userCanPinEvent,
184189
),
190+
timelineState = aTimelineState(
191+
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
192+
),
185193
)
186194
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
187195
rule.setMessagesView(
@@ -349,7 +357,10 @@ class MessagesViewTest {
349357
fun `clicking on a reaction emits the expected Event`() {
350358
val eventsRecorder = EventsRecorder<MessagesEvents>()
351359
val state = aMessagesState(
352-
eventSink = eventsRecorder
360+
timelineState = aTimelineState(
361+
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
362+
),
363+
eventSink = eventsRecorder,
353364
)
354365
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
355366
rule.setMessagesView(
@@ -363,6 +374,9 @@ class MessagesViewTest {
363374
fun `long clicking on a reaction emits the expected Event`() {
364375
val eventsRecorder = EventsRecorder<ReactionSummaryEvents>()
365376
val state = aMessagesState(
377+
timelineState = aTimelineState(
378+
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
379+
),
366380
reactionSummaryState = aReactionSummaryState(
367381
target = null,
368382
eventSink = eventsRecorder,
@@ -380,6 +394,9 @@ class MessagesViewTest {
380394
fun `clicking on more reaction emits the expected Event`() {
381395
val eventsRecorder = EventsRecorder<CustomReactionEvents>()
382396
val state = aMessagesState(
397+
timelineState = aTimelineState(
398+
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
399+
),
383400
customReactionState = aCustomReactionState(
384401
eventSink = eventsRecorder,
385402
),
@@ -396,7 +413,11 @@ class MessagesViewTest {
396413
@Test
397414
fun `clicking on more reaction from action list emits the expected Event`() {
398415
val eventsRecorder = EventsRecorder<CustomReactionEvents>()
399-
val state = aMessagesState()
416+
val state = aMessagesState(
417+
timelineState = aTimelineState(
418+
timelineItems = aTimelineItemList(aTimelineItemTextContent()),
419+
),
420+
)
400421
val timelineItem = state.timelineState.timelineItems.first() as TimelineItem.Event
401422
val stateWithActionListState = state.copy(
402423
actionListState = anActionListState(
@@ -538,7 +559,7 @@ private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setMessa
538559
onCreatePollClick = onCreatePollClick,
539560
onJoinCallClick = onJoinCallClick,
540561
onViewAllPinnedMessagesClick = onViewAllPinnedMessagesClick,
541-
knockRequestsBannerView = {}
562+
knockRequestsBannerView = {},
542563
)
543564
}
544565
}

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/timeline/TimelineViewTest.kt

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,18 @@ import androidx.activity.ComponentActivity
1111
import androidx.compose.ui.test.junit4.AndroidComposeTestRule
1212
import androidx.compose.ui.test.junit4.createAndroidComposeRule
1313
import androidx.compose.ui.test.onNodeWithContentDescription
14+
import androidx.compose.ui.test.onNodeWithTag
1415
import androidx.compose.ui.test.performClick
16+
import androidx.compose.ui.test.performScrollToIndex
1517
import androidx.test.ext.junit.runners.AndroidJUnit4
1618
import io.element.android.features.messages.impl.timeline.components.aCriticalShield
1719
import io.element.android.features.messages.impl.timeline.model.TimelineItem
1820
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemImageContent
21+
import io.element.android.features.messages.impl.timeline.model.event.aTimelineItemUnknownContent
1922
import io.element.android.features.messages.impl.timeline.model.virtual.TimelineItemLoadingIndicatorModel
2023
import io.element.android.features.messages.impl.timeline.protection.TimelineProtectionState
2124
import io.element.android.features.messages.impl.timeline.protection.aTimelineProtectionState
25+
import io.element.android.libraries.matrix.api.core.EventId
2226
import io.element.android.libraries.matrix.api.core.UniqueId
2327
import io.element.android.libraries.matrix.api.core.UserId
2428
import io.element.android.libraries.matrix.api.timeline.Timeline
@@ -31,6 +35,7 @@ import io.element.android.tests.testutils.EventsRecorder
3135
import io.element.android.tests.testutils.clickOn
3236
import io.element.android.tests.testutils.setSafeContent
3337
import kotlinx.collections.immutable.persistentListOf
38+
import kotlinx.collections.immutable.toPersistentList
3439
import org.junit.Rule
3540
import org.junit.Test
3641
import org.junit.rules.TestRule
@@ -114,8 +119,6 @@ class TimelineViewTest {
114119
rule.onNodeWithContentDescription(contentDescription).performClick()
115120
eventsRecorder.assertList(
116121
listOf(
117-
TimelineEvents.OnScrollFinished(0),
118-
TimelineEvents.OnScrollFinished(0),
119122
TimelineEvents.OnScrollFinished(0),
120123
TimelineEvents.ShowShieldDialog(MessageShield.UnverifiedIdentity(true)),
121124
)
@@ -135,6 +138,34 @@ class TimelineViewTest {
135138
rule.clickOn(CommonStrings.action_ok)
136139
eventsRecorder.assertSingle(TimelineEvents.HideShieldDialog)
137140
}
141+
142+
@Test
143+
fun `scrolling near to the start of the loaded items triggers a pre-fetch`() {
144+
val eventsRecorder = EventsRecorder<TimelineEvents>()
145+
val items = List<TimelineItem>(20) {
146+
aTimelineItemEvent(
147+
eventId = EventId("\$event_$it"),
148+
content = aTimelineItemUnknownContent(),
149+
)
150+
}.toPersistentList()
151+
152+
rule.setTimelineView(
153+
state = aTimelineState(
154+
timelineItems = items,
155+
eventSink = eventsRecorder,
156+
focusedEventIndex = -1,
157+
isLive = false,
158+
),
159+
)
160+
161+
rule.onNodeWithTag("timeline").performScrollToIndex(10)
162+
eventsRecorder.assertList(
163+
listOf(
164+
TimelineEvents.OnScrollFinished(firstIndex = 0),
165+
TimelineEvents.LoadMore(Timeline.PaginationDirection.BACKWARDS),
166+
)
167+
)
168+
}
138169
}
139170

140171
private fun <R : TestRule> AndroidComposeTestRule<R, ComponentActivity>.setTimelineView(

libraries/testtags/src/main/kotlin/io/element/android/libraries/testtags/TestTags.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ object TestTags {
9797
*/
9898
val floatingActionButton = TestTag("floating-action-button")
9999

100+
/**
101+
* Timeline.
102+
*/
103+
val timeline = TestTag("timeline")
104+
100105
/**
101106
* Timeline item.
102107
*/

0 commit comments

Comments
 (0)