mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 08:15:24 +00:00
Add max selections support for polls (#5726)
This commit is contained in:
committed by
GitHub
parent
173a927bf8
commit
30e4e6339d
@@ -26,6 +26,7 @@ struct PollFormScreenViewModelTests {
|
||||
let isEmpty = context.options.allSatisfy(\.text.isEmpty)
|
||||
#expect(isEmpty)
|
||||
#expect(context.question.isEmpty)
|
||||
#expect(context.maxSelections == 1)
|
||||
#expect(context.viewState.isSubmitButtonDisabled)
|
||||
#expect(!context.viewState.bindings.isUndisclosed)
|
||||
|
||||
@@ -44,6 +45,7 @@ struct PollFormScreenViewModelTests {
|
||||
#expect(context.options.count == 3)
|
||||
#expect(context.options.allSatisfy { !$0.text.isEmpty })
|
||||
#expect(!context.question.isEmpty)
|
||||
#expect(context.maxSelections == 1)
|
||||
#expect(context.viewState.isSubmitButtonDisabled)
|
||||
#expect(!context.viewState.bindings.isUndisclosed)
|
||||
|
||||
@@ -90,22 +92,58 @@ struct PollFormScreenViewModelTests {
|
||||
#expect(context.alertInfo != nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func maxSelectionsBounds() async throws {
|
||||
setupViewModel()
|
||||
|
||||
context.send(viewAction: .decrementMaxSelections)
|
||||
#expect(context.maxSelections == 1)
|
||||
|
||||
context.send(viewAction: .incrementMaxSelections)
|
||||
#expect(context.maxSelections == 2)
|
||||
|
||||
context.send(viewAction: .incrementMaxSelections)
|
||||
#expect(context.maxSelections == 2)
|
||||
|
||||
context.send(viewAction: .addOption)
|
||||
context.send(viewAction: .incrementMaxSelections)
|
||||
#expect(context.maxSelections == 3)
|
||||
|
||||
let deferredMaxSelections = deferFulfillment(context.observe(\.viewState.bindings.maxSelections)) { $0 == 2 }
|
||||
context.send(viewAction: .deleteOption(index: 2))
|
||||
try await deferredMaxSelections.fulfill()
|
||||
#expect(context.maxSelections == 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func editPollInitialMaxSelections() {
|
||||
setupViewModel(mode: .edit(eventID: "foo", poll: .mock(question: "Pick two",
|
||||
maxSelections: 2,
|
||||
options: [.mock(text: "One"),
|
||||
.mock(text: "Two"),
|
||||
.mock(text: "Three")])))
|
||||
|
||||
#expect(context.maxSelections == 2)
|
||||
}
|
||||
|
||||
@Test
|
||||
mutating func newPollSubmit() async throws {
|
||||
setupViewModel()
|
||||
context.question = "foo"
|
||||
context.options[0].text = "bla1"
|
||||
context.options[1].text = "bla2"
|
||||
context.send(viewAction: .incrementMaxSelections)
|
||||
#expect(!context.viewState.isSubmitButtonDisabled)
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
|
||||
try await confirmation { confirmation in
|
||||
timelineProxy.createPollQuestionAnswersPollKindClosure = { question, options, kind in
|
||||
timelineProxy.createPollQuestionAnswersMaxSelectionsPollKindClosure = { question, options, maxSelections, kind in
|
||||
#expect(question == "foo")
|
||||
#expect(options.count == 2)
|
||||
#expect(options[0] == "bla1")
|
||||
#expect(options[1] == "bla2")
|
||||
#expect(maxSelections == 2)
|
||||
#expect(kind == .disclosed)
|
||||
confirmation()
|
||||
return .success(())
|
||||
@@ -122,12 +160,13 @@ struct PollFormScreenViewModelTests {
|
||||
|
||||
context.question = "What is your favorite country?"
|
||||
context.options.append(.init(text: "France 🇫🇷"))
|
||||
context.send(viewAction: .incrementMaxSelections)
|
||||
#expect(!context.viewState.isSubmitButtonDisabled)
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
|
||||
try await confirmation { confirmation in
|
||||
timelineProxy.editPollOriginalQuestionAnswersPollKindClosure = { eventID, question, options, kind in
|
||||
timelineProxy.editPollOriginalQuestionAnswersMaxSelectionsPollKindClosure = { eventID, question, options, maxSelections, kind in
|
||||
#expect(eventID == "foo")
|
||||
#expect(question == "What is your favorite country?")
|
||||
#expect(options.count == 4)
|
||||
@@ -135,6 +174,7 @@ struct PollFormScreenViewModelTests {
|
||||
#expect(options[1] == "China 🇨🇳")
|
||||
#expect(options[2] == "USA 🇺🇸")
|
||||
#expect(options[3] == "France 🇫🇷")
|
||||
#expect(maxSelections == 2)
|
||||
#expect(kind == .disclosed)
|
||||
confirmation()
|
||||
return .success(())
|
||||
|
||||
@@ -0,0 +1,74 @@
|
||||
//
|
||||
// Copyright 2026 Element Creations Ltd.
|
||||
//
|
||||
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial.
|
||||
// Please see LICENSE files in the repository root for full details.
|
||||
//
|
||||
|
||||
@testable import ElementX
|
||||
import Testing
|
||||
|
||||
struct PollTests {
|
||||
@Test
|
||||
func singleSelectionReplacesSelectedAnswer() {
|
||||
let poll = Poll.mock(question: "Question?",
|
||||
maxSelections: 1,
|
||||
options: [
|
||||
.mock(id: "first", text: "First", isSelected: true),
|
||||
.mock(id: "second", text: "Second")
|
||||
])
|
||||
|
||||
#expect(poll.answerIDsAfterSelecting(optionID: "second") == ["second"])
|
||||
#expect(poll.answerIDsAfterSelecting(optionID: "first") == nil)
|
||||
}
|
||||
|
||||
@Test
|
||||
func multipleSelectionAddsAnswerUntilLimit() {
|
||||
let poll = Poll.mock(question: "Question?",
|
||||
maxSelections: 2,
|
||||
options: [
|
||||
.mock(id: "first", text: "First", isSelected: true),
|
||||
.mock(id: "second", text: "Second"),
|
||||
.mock(id: "third", text: "Third")
|
||||
])
|
||||
|
||||
#expect(poll.answerIDsAfterSelecting(optionID: "second") == ["first", "second"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func multipleSelectionCanRemoveAnswerWhenMoreThanOneIsSelected() {
|
||||
let poll = Poll.mock(question: "Question?",
|
||||
maxSelections: 2,
|
||||
options: [
|
||||
.mock(id: "first", text: "First", isSelected: true),
|
||||
.mock(id: "second", text: "Second", isSelected: true),
|
||||
.mock(id: "third", text: "Third")
|
||||
])
|
||||
|
||||
#expect(poll.answerIDsAfterSelecting(optionID: "first") == ["second"])
|
||||
}
|
||||
|
||||
@Test
|
||||
func multipleSelectionDoesNotAddAnswerOverLimit() {
|
||||
let poll = Poll.mock(question: "Question?",
|
||||
maxSelections: 2,
|
||||
options: [
|
||||
.mock(id: "first", text: "First", isSelected: true),
|
||||
.mock(id: "second", text: "Second", isSelected: true),
|
||||
.mock(id: "third", text: "Third")
|
||||
])
|
||||
|
||||
#expect(poll.answerIDsAfterSelecting(optionID: "third") == nil)
|
||||
}
|
||||
}
|
||||
|
||||
private extension Poll.Option {
|
||||
static func mock(id: String, text: String, isSelected: Bool = false) -> Self {
|
||||
.init(id: id,
|
||||
text: text,
|
||||
votes: 0,
|
||||
allVotes: 0,
|
||||
isSelected: isSelected,
|
||||
isWinning: false)
|
||||
}
|
||||
}
|
||||
@@ -139,14 +139,14 @@ struct RoomPollsHistoryScreenViewModelTests {
|
||||
func sendPollResponse() async throws {
|
||||
let deferred = deferFulfillment(interactionHandler.publisher.delay(for: 0.1, scheduler: DispatchQueue.main)) { _ in true }
|
||||
|
||||
interactionHandler.sendPollResponsePollStartIDOptionIDReturnValue = .success(())
|
||||
viewModel.context.send(viewAction: .sendPollResponse(pollStartID: "somePollID", optionID: "someOptionID"))
|
||||
interactionHandler.sendPollResponsePollStartIDAnswerIDsReturnValue = .success(())
|
||||
viewModel.context.send(viewAction: .sendPollResponse(pollStartID: "somePollID", answerIDs: ["firstOptionID", "secondOptionID"]))
|
||||
|
||||
try await deferred.fulfill()
|
||||
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDCalled)
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDReceivedInvocations[0].pollStartID == "somePollID")
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDReceivedInvocations[0].optionID == "someOptionID")
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDAnswerIDsCalled)
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDAnswerIDsReceivedInvocations[0].pollStartID == "somePollID")
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDAnswerIDsReceivedInvocations[0].answerIDs == ["firstOptionID", "secondOptionID"])
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -155,14 +155,14 @@ struct RoomPollsHistoryScreenViewModelTests {
|
||||
value.bindings.alertInfo != nil
|
||||
}
|
||||
|
||||
interactionHandler.sendPollResponsePollStartIDOptionIDReturnValue = .failure(SDKError.generic)
|
||||
viewModel.context.send(viewAction: .sendPollResponse(pollStartID: "somePollID", optionID: "someOptionID"))
|
||||
interactionHandler.sendPollResponsePollStartIDAnswerIDsReturnValue = .failure(SDKError.generic)
|
||||
viewModel.context.send(viewAction: .sendPollResponse(pollStartID: "somePollID", answerIDs: ["someOptionID"]))
|
||||
|
||||
try await deferred.fulfill()
|
||||
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDCalled)
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDReceivedInvocations[0].pollStartID == "somePollID")
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDOptionIDReceivedInvocations[0].optionID == "someOptionID")
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDAnswerIDsCalled)
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDAnswerIDsReceivedInvocations[0].pollStartID == "somePollID")
|
||||
#expect(interactionHandler.sendPollResponsePollStartIDAnswerIDsReceivedInvocations[0].answerIDs == ["someOptionID"])
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user