From 9be8112c9cc986c54f058305b6c711af9d87e2d3 Mon Sep 17 00:00:00 2001 From: Benoit Marty Date: Thu, 25 May 2023 17:32:55 +0200 Subject: [PATCH] Add test for TimelineItemGrouper and fix a bug: the last group if any was not added to the list. --- .../timeline/groups/TimelineItemGrouper.kt | 35 ++-- .../groups/TimelineItemGrouperTest.kt | 170 ++++++++++++++++++ 2 files changed, 194 insertions(+), 11 deletions(-) create mode 100644 features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt diff --git a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt index 622400ee93..3cd1b8fd4a 100644 --- a/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt +++ b/features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/timeline/groups/TimelineItemGrouper.kt @@ -46,22 +46,15 @@ class TimelineItemGrouper @Inject constructor() { // timelineItem cannot be grouped if (currentGroup.isNotEmpty()) { // There is a pending group, create a TimelineItem.GroupedEvents if there is more than 1 Event in the pending group. - if (currentGroup.size == 1) { - // Do not create a group with just 1 item, just add the item to the result - result.add(currentGroup.first()) - } else { - result.add( - TimelineItem.GroupedEvents( - expanded = expandedGroups[currentGroup.first().id + "_group"].orFalse(), - events = currentGroup.toImmutableList() - ) - ) - } + result.addGroup(currentGroup, expandedGroups) currentGroup.clear() } result.add(timelineItem) } } + if (currentGroup.isNotEmpty()) { + result.addGroup(currentGroup, expandedGroups) + } return result } @@ -80,3 +73,23 @@ class TimelineItemGrouper @Inject constructor() { } } } + +/** + * Will add a group if there is more than 1 item, else add the item to the list. + */ +private fun MutableList.addGroup( + group: MutableList, + expandedGroups: Map, +) { + if (group.size == 1) { + // Do not create a group with just 1 item, just add the item to the result + add(group.first()) + } else { + add( + TimelineItem.GroupedEvents( + expanded = expandedGroups[group.first().id + "_group"].orFalse(), + events = group.toImmutableList() + ) + ) + } +} diff --git a/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt new file mode 100644 index 0000000000..5829bbad9f --- /dev/null +++ b/features/messages/impl/src/test/kotlin/io/element/android/features/messages/timeline/groups/TimelineItemGrouperTest.kt @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2023 New Vector Ltd + * + * 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 io.element.android.features.messages.timeline.groups + +import com.google.common.truth.Truth.assertThat +import io.element.android.features.messages.fixtures.aMessageEvent +import io.element.android.features.messages.impl.timeline.groups.TimelineItemGrouper +import io.element.android.features.messages.impl.timeline.model.AggregatedReaction +import io.element.android.features.messages.impl.timeline.model.TimelineItem +import io.element.android.features.messages.impl.timeline.model.TimelineItemReactions +import io.element.android.features.messages.impl.timeline.model.event.TimelineItemStateEventContent +import io.element.android.features.messages.impl.timeline.model.virtual.aTimelineItemDaySeparatorModel +import io.element.android.libraries.designsystem.components.avatar.anAvatarData +import io.element.android.libraries.matrix.test.AN_EVENT_ID +import io.element.android.libraries.matrix.test.AN_EVENT_ID_2 +import io.element.android.libraries.matrix.test.A_USER_ID +import kotlinx.collections.immutable.toImmutableList +import org.junit.Test + +class TimelineItemGrouperTest { + private val sut = TimelineItemGrouper() + + private val aGroupableItem = TimelineItem.Event( + id = AN_EVENT_ID.value, + senderId = A_USER_ID, + senderAvatar = anAvatarData(), + senderDisplayName = "", + content = TimelineItemStateEventContent(body = "a state event"), + reactionsState = TimelineItemReactions(emptyList().toImmutableList()) + ) + private val aNonGroupableItem = aMessageEvent() + private val aNonGroupableItemNoEvent = TimelineItem.Virtual("virtual", aTimelineItemDaySeparatorModel("Today")) + + @Test + fun `test empty`() { + val result = sut.group(emptyList(), emptyMap()) + assertThat(result).isEmpty() + } + + @Test + fun `test non groupables`() { + val result = sut.group( + listOf( + aNonGroupableItem, + aNonGroupableItem, + ), + emptyMap() + ) + assertThat(result).isEqualTo( + listOf( + aNonGroupableItem, + aNonGroupableItem, + ) + ) + } + + @Test + fun `test groupables and ensure reordering`() { + val result = sut.group( + listOf( + aGroupableItem.copy(id = AN_EVENT_ID_2.value), + aGroupableItem, + ), + emptyMap() + ) + assertThat(result).isEqualTo( + listOf( + TimelineItem.GroupedEvents( + expanded = false, + events = listOf( + aGroupableItem, + aGroupableItem.copy(id = AN_EVENT_ID_2.value), + ).toImmutableList() + ), + ) + ) + } + + @Test + fun `test groupables expanded`() { + val result = sut.group( + listOf( + aGroupableItem, + aGroupableItem.copy(id = AN_EVENT_ID_2.value), + ), + mapOf("${AN_EVENT_ID_2.value}_group" to true) + ) + assertThat(result).isEqualTo( + listOf( + TimelineItem.GroupedEvents( + expanded = true, + events = listOf( + aGroupableItem.copy(id = AN_EVENT_ID_2.value), + aGroupableItem, + ).toImmutableList() + ), + ) + ) + } + + @Test + fun `test 1 groupable, not group must be created`() { + val listsToTest = listOf( + listOf(aGroupableItem), + listOf(aGroupableItem, aNonGroupableItem), + listOf(aGroupableItem, aNonGroupableItemNoEvent), + listOf(aNonGroupableItem, aGroupableItem), + listOf(aNonGroupableItemNoEvent, aGroupableItem), + listOf(aNonGroupableItem, aGroupableItem, aNonGroupableItem), + listOf(aNonGroupableItemNoEvent, aGroupableItem, aNonGroupableItemNoEvent), + listOf(aGroupableItem, aNonGroupableItem, aGroupableItem), + listOf(aGroupableItem, aNonGroupableItemNoEvent, aGroupableItem), + listOf(aNonGroupableItem), + listOf(aNonGroupableItemNoEvent), + ) + listsToTest.forEach { listToTest -> + val result = sut.group(listToTest, emptyMap()) + assertThat(result).isEqualTo(listToTest) + } + } + + @Test + fun `test 3 blocks`() { + val result = sut.group( + listOf( + aGroupableItem, + aGroupableItem, + aNonGroupableItem, + aGroupableItem, + aGroupableItem, + aGroupableItem, + ), + emptyMap() + ) + assertThat(result).isEqualTo( + listOf( + TimelineItem.GroupedEvents( + expanded = false, + events = listOf( + aGroupableItem, + aGroupableItem, + ).toImmutableList() + ), + aNonGroupableItem, + TimelineItem.GroupedEvents( + expanded = false, + events = listOf( + aGroupableItem, + aGroupableItem, + aGroupableItem, + ).toImmutableList() + ) + ) + ) + } +}