diff --git a/changelog.d/6366.misc b/changelog.d/6366.misc new file mode 100644 index 0000000000..5752b3d700 --- /dev/null +++ b/changelog.d/6366.misc @@ -0,0 +1 @@ +Poll view state unit tests diff --git a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt index 81b034a809..ee31d5959e 100644 --- a/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt +++ b/matrix-sdk-android/src/main/java/org/matrix/android/sdk/api/session/room/model/message/PollCreationInfo.kt @@ -25,4 +25,7 @@ data class PollCreationInfo( @Json(name = "kind") val kind: PollType? = PollType.DISCLOSED_UNSTABLE, @Json(name = "max_selections") val maxSelections: Int = 1, @Json(name = "answers") val answers: List? = null -) +) { + + fun isUndisclosed() = kind in listOf(PollType.UNDISCLOSED_UNSTABLE, PollType.UNDISCLOSED) +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt index 54bfbdd8a0..853fef8bc8 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/MessageItemFactory.kt @@ -59,12 +59,6 @@ import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem import im.vector.app.features.home.room.detail.timeline.item.MessageVoiceItem_ import im.vector.app.features.home.room.detail.timeline.item.PollItem import im.vector.app.features.home.room.detail.timeline.item.PollItem_ -import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState.PollEnded -import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState.PollReady -import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState.PollSending -import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState.PollUndisclosed -import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState.PollVoted -import im.vector.app.features.home.room.detail.timeline.item.PollResponseData import im.vector.app.features.home.room.detail.timeline.item.RedactedMessageItem import im.vector.app.features.home.room.detail.timeline.item.RedactedMessageItem_ import im.vector.app.features.home.room.detail.timeline.item.VerificationRequestItem @@ -81,18 +75,11 @@ import im.vector.app.features.location.UrlMapProvider import im.vector.app.features.location.toLocationData import im.vector.app.features.media.ImageContentRenderer import im.vector.app.features.media.VideoContentRenderer -import im.vector.app.features.poll.PollState -import im.vector.app.features.poll.PollState.Ended -import im.vector.app.features.poll.PollState.Ready -import im.vector.app.features.poll.PollState.Sending -import im.vector.app.features.poll.PollState.Undisclosed -import im.vector.app.features.poll.PollState.Voted import im.vector.app.features.settings.VectorPreferences import im.vector.app.features.voice.AudioWaveformView import im.vector.lib.core.utils.epoxy.charsequence.toEpoxyCharSequence import me.gujun.android.span.span import org.matrix.android.sdk.api.MatrixUrls.isMxcUrl -import org.matrix.android.sdk.api.extensions.orFalse import org.matrix.android.sdk.api.session.Session import org.matrix.android.sdk.api.session.crypto.attachments.toElementToDecrypt import org.matrix.android.sdk.api.session.events.model.RelationType @@ -113,8 +100,6 @@ import org.matrix.android.sdk.api.session.room.model.message.MessageTextContent import org.matrix.android.sdk.api.session.room.model.message.MessageType import org.matrix.android.sdk.api.session.room.model.message.MessageVerificationRequestContent import org.matrix.android.sdk.api.session.room.model.message.MessageVideoContent -import org.matrix.android.sdk.api.session.room.model.message.PollAnswer -import org.matrix.android.sdk.api.session.room.model.message.PollType import org.matrix.android.sdk.api.session.room.model.message.getFileUrl import org.matrix.android.sdk.api.session.room.model.message.getThumbnailUrl import org.matrix.android.sdk.api.session.room.timeline.getLastMessageContent @@ -149,6 +134,7 @@ class MessageItemFactory @Inject constructor( private val vectorPreferences: VectorPreferences, private val urlMapProvider: UrlMapProvider, private val liveLocationShareMessageItemFactory: LiveLocationShareMessageItemFactory, + private val pollItemViewStateFactory: PollItemViewStateFactory, ) { // TODO inject this properly? @@ -251,62 +237,21 @@ class MessageItemFactory @Inject constructor( callback: TimelineEventController.Callback?, attributes: AbsMessageItem.Attributes, ): PollItem { - val pollResponseSummary = informationData.pollResponseAggregatedSummary - val pollState = createPollState(informationData, pollResponseSummary, pollContent) - val pollCreationInfo = pollContent.getBestPollCreationInfo() - val questionText = pollCreationInfo?.question?.getBestQuestion().orEmpty() - val question = createPollQuestion(informationData, questionText, callback) - val optionViewStates = pollCreationInfo?.answers?.mapToOptions(pollState, informationData) - val totalVotesText = createTotalVotesText(pollState, pollResponseSummary) + val pollViewState = pollItemViewStateFactory.create(pollContent, informationData) return PollItem_() .attributes(attributes) .eventId(informationData.eventId) - .pollQuestion(question) - .canVote(pollState.isVotable()) - .totalVotesText(totalVotesText) - .optionViewStates(optionViewStates) + .pollQuestion(createPollQuestion(informationData, pollViewState.question, callback)) + .canVote(pollViewState.canVote) + .totalVotesText(pollViewState.totalVotes) + .optionViewStates(pollViewState.optionViewStates) .edited(informationData.hasBeenEdited) .highlighted(highlight) .leftGuideline(avatarSizeProvider.leftGuideline) .callback(callback) } - private fun createPollState( - informationData: MessageInformationData, - pollResponseSummary: PollResponseData?, - pollContent: MessagePollContent, - ): PollState = when { - !informationData.sendState.isSent() -> Sending - pollResponseSummary?.isClosed.orFalse() -> Ended - pollContent.getBestPollCreationInfo()?.kind == PollType.UNDISCLOSED -> Undisclosed - pollResponseSummary?.myVote?.isNotEmpty().orFalse() -> Voted(pollResponseSummary?.totalVotes ?: 0) - else -> Ready - } - - private fun List.mapToOptions( - pollState: PollState, - informationData: MessageInformationData, - ) = map { answer -> - val pollResponseSummary = informationData.pollResponseAggregatedSummary - val winnerVoteCount = pollResponseSummary?.winnerVoteCount - val optionId = answer.id ?: "" - val optionAnswer = answer.getBestAnswer() ?: "" - val voteSummary = pollResponseSummary?.votes?.get(answer.id) - val voteCount = voteSummary?.total ?: 0 - val votePercentage = voteSummary?.percentage ?: 0.0 - val isMyVote = pollResponseSummary?.myVote == answer.id - val isWinner = winnerVoteCount != 0 && voteCount == winnerVoteCount - - when (pollState) { - Sending -> PollSending(optionId, optionAnswer) - Ready -> PollReady(optionId, optionAnswer) - is Voted -> PollVoted(optionId, optionAnswer, voteCount, votePercentage, isMyVote) - Undisclosed -> PollUndisclosed(optionId, optionAnswer, isMyVote) - Ended -> PollEnded(optionId, optionAnswer, voteCount, votePercentage, isWinner) - } - } - private fun createPollQuestion( informationData: MessageInformationData, question: String, @@ -317,20 +262,6 @@ class MessageItemFactory @Inject constructor( question }.toEpoxyCharSequence() - private fun createTotalVotesText( - pollState: PollState, - pollResponseSummary: PollResponseData?, - ): String { - val votes = pollResponseSummary?.totalVotes ?: 0 - return when { - pollState is Ended -> stringProvider.getQuantityString(R.plurals.poll_total_vote_count_after_ended, votes, votes) - pollState is Undisclosed -> "" - pollState is Voted -> stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, votes, votes) - votes == 0 -> stringProvider.getString(R.string.poll_no_votes_cast) - else -> stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_not_voted, votes, votes) - } - } - private fun buildAudioMessageItem( params: TimelineItemFactoryParams, messageContent: MessageAudioContent, diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactory.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactory.kt new file mode 100644 index 0000000000..8da0f2d279 --- /dev/null +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactory.kt @@ -0,0 +1,165 @@ +/* + * Copyright (c) 2022 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 im.vector.app.features.home.room.detail.timeline.factory + +import im.vector.app.R +import im.vector.app.core.resources.StringProvider +import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData +import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState +import im.vector.app.features.home.room.detail.timeline.item.PollResponseData +import im.vector.app.features.poll.PollViewState +import org.matrix.android.sdk.api.extensions.orFalse +import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent +import org.matrix.android.sdk.api.session.room.model.message.PollCreationInfo +import javax.inject.Inject + +class PollItemViewStateFactory @Inject constructor( + private val stringProvider: StringProvider, +) { + + fun create( + pollContent: MessagePollContent, + informationData: MessageInformationData, + ): PollViewState { + val pollCreationInfo = pollContent.getBestPollCreationInfo() + + val question = pollCreationInfo?.question?.getBestQuestion().orEmpty() + + val pollResponseSummary = informationData.pollResponseAggregatedSummary + val winnerVoteCount = pollResponseSummary?.winnerVoteCount + val totalVotes = pollResponseSummary?.totalVotes ?: 0 + + return when { + !informationData.sendState.isSent() -> { + createSendingPollViewState(question, pollCreationInfo) + } + informationData.pollResponseAggregatedSummary?.isClosed.orFalse() -> { + createEndedPollViewState(question, pollCreationInfo, pollResponseSummary, totalVotes, winnerVoteCount) + } + pollContent.getBestPollCreationInfo()?.isUndisclosed().orFalse() -> { + createUndisclosedPollViewState(question, pollCreationInfo, pollResponseSummary) + } + informationData.pollResponseAggregatedSummary?.myVote?.isNotEmpty().orFalse() -> { + createVotedPollViewState(question, pollCreationInfo, pollResponseSummary, totalVotes) + } + else -> { + createReadyPollViewState(question, pollCreationInfo, totalVotes) + } + } + } + + private fun createSendingPollViewState(question: String, pollCreationInfo: PollCreationInfo?): PollViewState { + return PollViewState( + question = question, + totalVotes = stringProvider.getString(R.string.poll_no_votes_cast), + canVote = false, + optionViewStates = pollCreationInfo?.answers?.map { answer -> + PollOptionViewState.PollSending( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "" + ) + }, + ) + } + + private fun createEndedPollViewState( + question: String, + pollCreationInfo: PollCreationInfo?, + pollResponseSummary: PollResponseData?, + totalVotes: Int, + winnerVoteCount: Int?, + ): PollViewState { + return PollViewState( + question = question, + totalVotes = stringProvider.getQuantityString(R.plurals.poll_total_vote_count_after_ended, totalVotes, totalVotes), + canVote = false, + optionViewStates = pollCreationInfo?.answers?.map { answer -> + val voteSummary = pollResponseSummary?.getVoteSummaryOfAnOption(answer.id ?: "") + PollOptionViewState.PollEnded( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "", + voteCount = voteSummary?.total ?: 0, + votePercentage = voteSummary?.percentage ?: 0.0, + isWinner = winnerVoteCount != 0 && voteSummary?.total == winnerVoteCount + ) + }, + ) + } + + private fun createUndisclosedPollViewState( + question: String, + pollCreationInfo: PollCreationInfo?, + pollResponseSummary: PollResponseData? + ): PollViewState { + return PollViewState( + question = question, + totalVotes = "", + canVote = true, + optionViewStates = pollCreationInfo?.answers?.map { answer -> + val isMyVote = pollResponseSummary?.myVote == answer.id + PollOptionViewState.PollUndisclosed( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "", + isSelected = isMyVote + ) + }, + ) + } + + private fun createVotedPollViewState( + question: String, + pollCreationInfo: PollCreationInfo?, + pollResponseSummary: PollResponseData?, + totalVotes: Int + ): PollViewState { + return PollViewState( + question = question, + totalVotes = stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, totalVotes, totalVotes), + canVote = true, + optionViewStates = pollCreationInfo?.answers?.map { answer -> + val isMyVote = pollResponseSummary?.myVote == answer.id + val voteSummary = pollResponseSummary?.getVoteSummaryOfAnOption(answer.id ?: "") + PollOptionViewState.PollVoted( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "", + voteCount = voteSummary?.total ?: 0, + votePercentage = voteSummary?.percentage ?: 0.0, + isSelected = isMyVote + ) + }, + ) + } + + private fun createReadyPollViewState(question: String, pollCreationInfo: PollCreationInfo?, totalVotes: Int): PollViewState { + val totalVotesText = if (totalVotes == 0) { + stringProvider.getString(R.string.poll_no_votes_cast) + } else { + stringProvider.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_not_voted, totalVotes, totalVotes) + } + return PollViewState( + question = question, + totalVotes = totalVotesText, + canVote = true, + optionViewStates = pollCreationInfo?.answers?.map { answer -> + PollOptionViewState.PollReady( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "" + ) + }, + ) + } +} diff --git a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt index 554dd0ada8..9b24720c88 100644 --- a/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt +++ b/vector/src/main/java/im/vector/app/features/home/room/detail/timeline/item/MessageInformationData.kt @@ -91,7 +91,10 @@ data class PollResponseData( val totalVotes: Int = 0, val winnerVoteCount: Int = 0, val isClosed: Boolean = false -) : Parcelable +) : Parcelable { + + fun getVoteSummaryOfAnOption(optionId: String) = votes?.get(optionId) +} @Parcelize data class PollVoteSummaryData( diff --git a/vector/src/main/java/im/vector/app/features/poll/PollState.kt b/vector/src/main/java/im/vector/app/features/poll/PollViewState.kt similarity index 71% rename from vector/src/main/java/im/vector/app/features/poll/PollState.kt rename to vector/src/main/java/im/vector/app/features/poll/PollViewState.kt index 93cdb0ecbe..0f01d58c96 100644 --- a/vector/src/main/java/im/vector/app/features/poll/PollState.kt +++ b/vector/src/main/java/im/vector/app/features/poll/PollViewState.kt @@ -16,12 +16,11 @@ package im.vector.app.features.poll -sealed interface PollState { - object Sending : PollState - object Ready : PollState - data class Voted(val votes: Int) : PollState - object Undisclosed : PollState - object Ended : PollState +import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState - fun isVotable() = this !is Sending && this !is Ended -} +data class PollViewState( + val question: String, + val totalVotes: String, + val canVote: Boolean, + val optionViewStates: List?, +) diff --git a/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactoryTest.kt b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactoryTest.kt new file mode 100644 index 0000000000..64ad03a019 --- /dev/null +++ b/vector/src/test/java/im/vector/app/features/home/room/detail/timeline/factory/PollItemViewStateFactoryTest.kt @@ -0,0 +1,223 @@ +/* + * Copyright (c) 2022 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 im.vector.app.features.home.room.detail.timeline.factory + +import im.vector.app.R +import im.vector.app.features.home.room.detail.timeline.item.MessageInformationData +import im.vector.app.features.home.room.detail.timeline.item.PollOptionViewState +import im.vector.app.features.home.room.detail.timeline.item.PollResponseData +import im.vector.app.features.home.room.detail.timeline.item.PollVoteSummaryData +import im.vector.app.features.home.room.detail.timeline.item.ReactionsSummaryData +import im.vector.app.features.home.room.detail.timeline.style.TimelineMessageLayout +import im.vector.app.features.poll.PollViewState +import im.vector.app.test.fakes.FakeStringProvider +import org.amshove.kluent.shouldBeEqualTo +import org.junit.Test +import org.matrix.android.sdk.api.session.room.model.message.MessagePollContent +import org.matrix.android.sdk.api.session.room.model.message.PollAnswer +import org.matrix.android.sdk.api.session.room.model.message.PollCreationInfo +import org.matrix.android.sdk.api.session.room.model.message.PollQuestion +import org.matrix.android.sdk.api.session.room.model.message.PollType +import org.matrix.android.sdk.api.session.room.send.SendState + +private val A_MESSAGE_INFORMATION_DATA = MessageInformationData( + eventId = "eventId", + senderId = "senderId", + ageLocalTS = 0, + avatarUrl = "", + sendState = SendState.SENT, + messageLayout = TimelineMessageLayout.Default(showAvatar = true, showDisplayName = true, showTimestamp = true), + reactionsSummary = ReactionsSummaryData(), + sentByMe = true, +) + +private val A_POLL_RESPONSE_DATA = PollResponseData( + myVote = null, + votes = emptyMap(), +) + +private val A_POLL_OPTION_IDS = listOf("5ef5f7b0-c9a1-49cf-a0b3-374729a43e76", "ec1a4db0-46d8-4d7a-9bb6-d80724715938", "3677ca8e-061b-40ab-bffe-b22e4e88fcad") + +private val A_POLL_CONTENT = MessagePollContent( + unstablePollCreationInfo = PollCreationInfo( + question = PollQuestion( + unstableQuestion = "What is your favourite coffee?" + ), + kind = PollType.UNDISCLOSED_UNSTABLE, + maxSelections = 1, + answers = listOf( + PollAnswer( + id = A_POLL_OPTION_IDS[0], + unstableAnswer = "Double Espresso" + ), + PollAnswer( + id = A_POLL_OPTION_IDS[1], + unstableAnswer = "Macchiato" + ), + PollAnswer( + id = A_POLL_OPTION_IDS[2], + unstableAnswer = "Iced Coffee" + ), + ) + ) +) + +class PollItemViewStateFactoryTest { + + @Test + fun `given a sending poll state then poll is not votable and option states are PollSending`() { + val stringProvider = FakeStringProvider() + val pollItemViewStateFactory = PollItemViewStateFactory(stringProvider.instance) + + val sendingPollInformationData = A_MESSAGE_INFORMATION_DATA.copy(sendState = SendState.SENDING) + val pollViewState = pollItemViewStateFactory.create( + pollContent = A_POLL_CONTENT, + informationData = sendingPollInformationData, + ) + + pollViewState shouldBeEqualTo PollViewState( + question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "", + totalVotes = stringProvider.instance.getString(R.string.poll_no_votes_cast), + canVote = false, + optionViewStates = A_POLL_CONTENT.getBestPollCreationInfo()?.answers?.map { answer -> + PollOptionViewState.PollSending( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "" + ) + }, + ) + } + + @Test + fun `given a sent poll state when poll is closed then poll is not votable and option states are Ended`() { + val stringProvider = FakeStringProvider() + val pollItemViewStateFactory = PollItemViewStateFactory(stringProvider.instance) + + val closedPollSummary = A_POLL_RESPONSE_DATA.copy(isClosed = true) + val closedPollInformationData = A_MESSAGE_INFORMATION_DATA.copy(pollResponseAggregatedSummary = closedPollSummary) + + val pollViewState = pollItemViewStateFactory.create( + pollContent = A_POLL_CONTENT, + informationData = closedPollInformationData, + ) + + pollViewState shouldBeEqualTo PollViewState( + question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "", + totalVotes = stringProvider.instance.getQuantityString(R.plurals.poll_total_vote_count_after_ended, 0, 0), + canVote = false, + optionViewStates = A_POLL_CONTENT.getBestPollCreationInfo()?.answers?.map { answer -> + PollOptionViewState.PollEnded( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "", + voteCount = 0, + votePercentage = 0.0, + isWinner = false + ) + }, + ) + } + + @Test + fun `given a sent poll when undisclosed poll type is selected then poll is votable and option states are PollUndisclosed`() { + val stringProvider = FakeStringProvider() + val pollItemViewStateFactory = PollItemViewStateFactory(stringProvider.instance) + + val pollViewState = pollItemViewStateFactory.create( + pollContent = A_POLL_CONTENT, + informationData = A_MESSAGE_INFORMATION_DATA, + ) + + pollViewState shouldBeEqualTo PollViewState( + question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "", + totalVotes = "", + canVote = true, + optionViewStates = A_POLL_CONTENT.getBestPollCreationInfo()?.answers?.map { answer -> + PollOptionViewState.PollUndisclosed( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "", + isSelected = false + ) + }, + ) + } + + @Test + fun `given a sent poll when my vote exists then poll is still votable and options states are PollVoted`() { + val stringProvider = FakeStringProvider() + val pollItemViewStateFactory = PollItemViewStateFactory(stringProvider.instance) + + val votedPollData = A_POLL_RESPONSE_DATA.copy( + totalVotes = 1, + myVote = A_POLL_OPTION_IDS[0], + votes = mapOf(A_POLL_OPTION_IDS[0] to PollVoteSummaryData(total = 1, percentage = 1.0)) + ) + val disclosedPollContent = A_POLL_CONTENT.copy( + unstablePollCreationInfo = A_POLL_CONTENT.getBestPollCreationInfo()?.copy( + kind = PollType.DISCLOSED_UNSTABLE + ), + ) + val votedInformationData = A_MESSAGE_INFORMATION_DATA.copy(pollResponseAggregatedSummary = votedPollData) + + val pollViewState = pollItemViewStateFactory.create( + pollContent = disclosedPollContent, + informationData = votedInformationData, + ) + + pollViewState shouldBeEqualTo PollViewState( + question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "", + totalVotes = stringProvider.instance.getQuantityString(R.plurals.poll_total_vote_count_before_ended_and_voted, 1, 1), + canVote = true, + optionViewStates = A_POLL_CONTENT.getBestPollCreationInfo()?.answers?.mapIndexed { index, answer -> + PollOptionViewState.PollVoted( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "", + voteCount = if (index == 0) 1 else 0, + votePercentage = if (index == 0) 1.0 else 0.0, + isSelected = index == 0 + ) + }, + ) + } + + @Test + fun `given a sent poll when poll type is disclosed then poll is votable and option view states are PollReady`() { + val stringProvider = FakeStringProvider() + val pollItemViewStateFactory = PollItemViewStateFactory(stringProvider.instance) + + val disclosedPollContent = A_POLL_CONTENT.copy( + unstablePollCreationInfo = A_POLL_CONTENT.getBestPollCreationInfo()?.copy( + kind = PollType.DISCLOSED_UNSTABLE + ) + ) + val pollViewState = pollItemViewStateFactory.create( + pollContent = disclosedPollContent, + informationData = A_MESSAGE_INFORMATION_DATA, + ) + + pollViewState shouldBeEqualTo PollViewState( + question = A_POLL_CONTENT.getBestPollCreationInfo()?.question?.getBestQuestion() ?: "", + totalVotes = stringProvider.instance.getString(R.string.poll_no_votes_cast), + canVote = true, + optionViewStates = A_POLL_CONTENT.getBestPollCreationInfo()?.answers?.map { answer -> + PollOptionViewState.PollReady( + optionId = answer.id ?: "", + optionAnswer = answer.getBestAnswer() ?: "" + ) + }, + ) + } +} diff --git a/vector/src/test/java/im/vector/app/test/fakes/FakeStringProvider.kt b/vector/src/test/java/im/vector/app/test/fakes/FakeStringProvider.kt index e63550abe0..28d9f7c732 100644 --- a/vector/src/test/java/im/vector/app/test/fakes/FakeStringProvider.kt +++ b/vector/src/test/java/im/vector/app/test/fakes/FakeStringProvider.kt @@ -27,6 +27,10 @@ class FakeStringProvider { every { instance.getString(any()) } answers { "test-${args[0]}" } + + every { instance.getQuantityString(any(), any(), any()) } answers { + "test-${args[0]}-${args[1]}" + } } fun given(id: Int, result: String) {