mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-10-31 16:24:11 +00:00
ff5dcb040d
Removes the ServiceLocator singleton and instead directly injects the dependencies it was holding for more control over instantiation of the various component subtrees and better visibility over how they're being used.
186 lines
6.8 KiB
Swift
186 lines
6.8 KiB
Swift
//
|
|
// Copyright 2025 Element Creations Ltd.
|
|
// Copyright 2022-2025 New Vector 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
|
|
|
|
@MainActor
|
|
struct PollFormScreenViewModelTests {
|
|
private let timelineProxy = TimelineProxyMock(.init())
|
|
|
|
private var viewModel: PollFormScreenViewModelProtocol!
|
|
private var context: PollFormScreenViewModelType.Context {
|
|
viewModel.context
|
|
}
|
|
|
|
@Test
|
|
mutating func newPollInitialState() async throws {
|
|
setupViewModel()
|
|
#expect(context.options.count == 2)
|
|
// This due to a bug in Swift testing that raises an error when allSatisfy is used in an #expect
|
|
let isEmpty = context.options.allSatisfy(\.text.isEmpty)
|
|
#expect(isEmpty)
|
|
#expect(context.question.isEmpty)
|
|
#expect(context.viewState.isSubmitButtonDisabled)
|
|
#expect(!context.viewState.bindings.isUndisclosed)
|
|
|
|
// Cancellation should work without confirmation
|
|
let deferred = deferFulfillment(viewModel.actions) { _ in true }
|
|
context.send(viewAction: .cancel)
|
|
let action = try await deferred.fulfill()
|
|
#expect(context.alertInfo == nil)
|
|
#expect(action == .close)
|
|
}
|
|
|
|
@Test
|
|
mutating func editPollInitialState() async throws {
|
|
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
|
|
|
#expect(context.options.count == 3)
|
|
#expect(context.options.allSatisfy { !$0.text.isEmpty })
|
|
#expect(!context.question.isEmpty)
|
|
#expect(context.viewState.isSubmitButtonDisabled)
|
|
#expect(!context.viewState.bindings.isUndisclosed)
|
|
|
|
// Cancellation should work without confirmation
|
|
let deferred = deferFulfillment(viewModel.actions) { _ in true }
|
|
context.send(viewAction: .cancel)
|
|
let action = try await deferred.fulfill()
|
|
#expect(context.alertInfo == nil)
|
|
#expect(action == .close)
|
|
}
|
|
|
|
@Test
|
|
mutating func newPollInvalidEmptyOption() {
|
|
setupViewModel()
|
|
context.question = "foo"
|
|
context.options[0].text = "bla"
|
|
context.options[1].text = "bla"
|
|
context.send(viewAction: .addOption)
|
|
#expect(context.viewState.isSubmitButtonDisabled)
|
|
}
|
|
|
|
@Test
|
|
mutating func editPollInvalidEmptyOption() {
|
|
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
|
|
|
context.send(viewAction: .addOption)
|
|
#expect(context.viewState.isSubmitButtonDisabled)
|
|
|
|
// Cancellation requires a confirmation
|
|
context.send(viewAction: .cancel)
|
|
#expect(context.alertInfo != nil)
|
|
}
|
|
|
|
@Test
|
|
mutating func editPollSubmitButtonState() {
|
|
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
|
|
|
#expect(context.viewState.isSubmitButtonDisabled)
|
|
context.options[0].text = "foo"
|
|
#expect(!context.viewState.isSubmitButtonDisabled)
|
|
|
|
// Cancellation requires a confirmation
|
|
context.send(viewAction: .cancel)
|
|
#expect(context.alertInfo != nil)
|
|
}
|
|
|
|
@Test
|
|
mutating func newPollSubmit() async throws {
|
|
setupViewModel()
|
|
context.question = "foo"
|
|
context.options[0].text = "bla1"
|
|
context.options[1].text = "bla2"
|
|
#expect(!context.viewState.isSubmitButtonDisabled)
|
|
|
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
|
|
|
try await confirmation { confirmation in
|
|
timelineProxy.createPollQuestionAnswersPollKindClosure = { question, options, kind in
|
|
#expect(question == "foo")
|
|
#expect(options.count == 2)
|
|
#expect(options[0] == "bla1")
|
|
#expect(options[1] == "bla2")
|
|
#expect(kind == .disclosed)
|
|
confirmation()
|
|
return .success(())
|
|
}
|
|
context.send(viewAction: .submit)
|
|
|
|
try await deferred.fulfill()
|
|
}
|
|
}
|
|
|
|
@Test
|
|
mutating func editPollSubmit() async throws {
|
|
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
|
|
|
context.question = "What is your favorite country?"
|
|
context.options.append(.init(text: "France 🇫🇷"))
|
|
#expect(!context.viewState.isSubmitButtonDisabled)
|
|
|
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
|
|
|
try await confirmation { confirmation in
|
|
timelineProxy.editPollOriginalQuestionAnswersPollKindClosure = { eventID, question, options, kind in
|
|
#expect(eventID == "foo")
|
|
#expect(question == "What is your favorite country?")
|
|
#expect(options.count == 4)
|
|
#expect(options[0] == "Italy 🇮🇹")
|
|
#expect(options[1] == "China 🇨🇳")
|
|
#expect(options[2] == "USA 🇺🇸")
|
|
#expect(options[3] == "France 🇫🇷")
|
|
#expect(kind == .disclosed)
|
|
confirmation()
|
|
return .success(())
|
|
}
|
|
context.send(viewAction: .submit)
|
|
|
|
try await deferred.fulfill()
|
|
}
|
|
}
|
|
|
|
@Test
|
|
mutating func deletePoll() async throws {
|
|
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
|
|
|
context.question = "What is your favorite country?"
|
|
context.options.append(.init(text: "France 🇫🇷"))
|
|
#expect(!context.viewState.isSubmitButtonDisabled)
|
|
|
|
let deferredFailure = deferFailure(viewModel.actions, timeout: .seconds(1)) { $0 == .close }
|
|
context.send(viewAction: .delete)
|
|
|
|
try await deferredFailure.fulfill()
|
|
#expect(context.alertInfo != nil, "An alert should be shown before deleting the poll.")
|
|
|
|
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
|
|
|
await waitForConfirmation(timeout: .seconds(1)) { confirmation in
|
|
timelineProxy.redactReasonClosure = { eventID, _ in
|
|
defer {
|
|
confirmation()
|
|
}
|
|
#expect(eventID == .eventID("foo"))
|
|
return .success(())
|
|
}
|
|
context.alertInfo?.secondaryButton?.action?()
|
|
}
|
|
try await deferred.fulfill()
|
|
}
|
|
|
|
// MARK: - Helpers
|
|
|
|
private mutating func setupViewModel(mode: PollFormMode = .new) {
|
|
viewModel = PollFormScreenViewModel(mode: mode,
|
|
timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
|
analytics: .mock(),
|
|
userIndicatorController: UserIndicatorControllerMock())
|
|
}
|
|
}
|