mirror of
https://github.com/Novaloop-AG/element-x-ios.git
synced 2026-11-03 06:55:59 +00:00
Use the Emoji/Map/Poll view models. (#4458)
* Toggle emojis in the EmojiPickerScreenViewModel. * Send locations in the StaticLocationScreen. * Send polls in the PollFormScreen.
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
//
|
||||
// Copyright 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.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
@testable import ElementX
|
||||
|
||||
@MainActor
|
||||
final class EmojiPickerScreenViewModelTests: XCTestCase {
|
||||
var timelineProxy: TimelineProxyMock!
|
||||
|
||||
var viewModel: EmojiPickerScreenViewModel!
|
||||
var context: EmojiPickerScreenViewModel.Context { viewModel.context }
|
||||
|
||||
func testToggleReaction() async throws {
|
||||
setupViewModel()
|
||||
let reaction = "👋"
|
||||
|
||||
let expectation = XCTestExpectation(description: "Toggle reaction")
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .dismiss }
|
||||
timelineProxy.toggleReactionToClosure = { toggledReaction, _ in
|
||||
XCTAssertEqual(toggledReaction, reaction)
|
||||
expectation.fulfill()
|
||||
return .success(())
|
||||
}
|
||||
context.send(viewAction: .emojiTapped(emoji: .init(id: "wave", value: reaction)))
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func setupViewModel(selectedEmojis: Set<String> = []) {
|
||||
timelineProxy = TimelineProxyMock(.init())
|
||||
|
||||
viewModel = EmojiPickerScreenViewModel(itemID: .randomEvent,
|
||||
selectedEmojis: selectedEmojis,
|
||||
emojiProvider: EmojiProvider(appSettings: ServiceLocator.shared.settings),
|
||||
timelineController: MockTimelineController(timelineProxy: timelineProxy))
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,14 @@ import XCTest
|
||||
|
||||
@MainActor
|
||||
class PollFormScreenViewModelTests: XCTestCase {
|
||||
let timelineProxy = TimelineProxyMock(.init())
|
||||
|
||||
var viewModel: PollFormScreenViewModelProtocol!
|
||||
|
||||
var context: PollFormScreenViewModelType.Context {
|
||||
viewModel.context
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
viewModel = PollFormScreenViewModel(mode: .new)
|
||||
}
|
||||
var context: PollFormScreenViewModelType.Context { viewModel.context }
|
||||
|
||||
func testNewPollInitialState() async throws {
|
||||
setupViewModel()
|
||||
|
||||
XCTAssertEqual(context.options.count, 2)
|
||||
XCTAssertTrue(context.options.allSatisfy(\.text.isEmpty))
|
||||
XCTAssertTrue(context.question.isEmpty)
|
||||
@@ -33,11 +30,12 @@ class PollFormScreenViewModelTests: XCTestCase {
|
||||
context.send(viewAction: .cancel)
|
||||
let action = try await deferred.fulfill()
|
||||
XCTAssertNil(context.alertInfo)
|
||||
XCTAssertEqual(action, .cancel)
|
||||
XCTAssertEqual(action, .close)
|
||||
}
|
||||
|
||||
func testEditPollInitialState() async throws {
|
||||
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
||||
|
||||
XCTAssertEqual(context.options.count, 3)
|
||||
XCTAssertTrue(context.options.allSatisfy { !$0.text.isEmpty })
|
||||
XCTAssertFalse(context.question.isEmpty)
|
||||
@@ -49,10 +47,12 @@ class PollFormScreenViewModelTests: XCTestCase {
|
||||
context.send(viewAction: .cancel)
|
||||
let action = try await deferred.fulfill()
|
||||
XCTAssertNil(context.alertInfo)
|
||||
XCTAssertEqual(action, .cancel)
|
||||
XCTAssertEqual(action, .close)
|
||||
}
|
||||
|
||||
func testNewPollInvalidEmptyOption() {
|
||||
setupViewModel()
|
||||
|
||||
context.question = "foo"
|
||||
context.options[0].text = "bla"
|
||||
context.options[1].text = "bla"
|
||||
@@ -62,6 +62,7 @@ class PollFormScreenViewModelTests: XCTestCase {
|
||||
|
||||
func testEditPollInvalidEmptyOption() {
|
||||
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
||||
|
||||
context.send(viewAction: .addOption)
|
||||
XCTAssertTrue(context.viewState.isSubmitButtonDisabled)
|
||||
|
||||
@@ -72,6 +73,7 @@ class PollFormScreenViewModelTests: XCTestCase {
|
||||
|
||||
func testEditPollSubmitButtonState() {
|
||||
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
||||
|
||||
XCTAssertTrue(context.viewState.isSubmitButtonDisabled)
|
||||
context.options[0].text = "foo"
|
||||
XCTAssertFalse(context.viewState.isSubmitButtonDisabled)
|
||||
@@ -82,68 +84,89 @@ class PollFormScreenViewModelTests: XCTestCase {
|
||||
}
|
||||
|
||||
func testNewPollSubmit() async throws {
|
||||
setupViewModel()
|
||||
|
||||
context.question = "foo"
|
||||
context.options[0].text = "bla1"
|
||||
context.options[1].text = "bla2"
|
||||
XCTAssertFalse(context.viewState.isSubmitButtonDisabled)
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||
switch action {
|
||||
case .submit:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
let expectation = XCTestExpectation(description: "Create poll")
|
||||
timelineProxy.createPollQuestionAnswersPollKindClosure = { question, options, kind in
|
||||
XCTAssertEqual(question, "foo")
|
||||
XCTAssertEqual(options.count, 2)
|
||||
XCTAssertEqual(options[0], "bla1")
|
||||
XCTAssertEqual(options[1], "bla2")
|
||||
XCTAssertEqual(kind, .disclosed)
|
||||
expectation.fulfill()
|
||||
return .success(())
|
||||
}
|
||||
|
||||
context.send(viewAction: .submit)
|
||||
|
||||
let action = try await deferred.fulfill()
|
||||
|
||||
guard case .submit(let question, let options, let kind) = action else {
|
||||
XCTFail("Unexpected action")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(question, "foo")
|
||||
XCTAssertEqual(options.count, 2)
|
||||
XCTAssertEqual(options[0], "bla1")
|
||||
XCTAssertEqual(options[1], "bla2")
|
||||
XCTAssertEqual(kind, .disclosed)
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
func testEditPollSubmit() async throws {
|
||||
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
||||
|
||||
context.question = "What is your favorite country?"
|
||||
context.options.append(.init(text: "France 🇫🇷"))
|
||||
XCTAssertFalse(context.viewState.isSubmitButtonDisabled)
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||
switch action {
|
||||
case .submit:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
let expectation = XCTestExpectation(description: "Edit poll")
|
||||
timelineProxy.editPollOriginalQuestionAnswersPollKindClosure = { eventID, question, options, kind in
|
||||
XCTAssertEqual(eventID, "foo")
|
||||
XCTAssertEqual(question, "What is your favorite country?")
|
||||
XCTAssertEqual(options.count, 4)
|
||||
XCTAssertEqual(options[0], "Italy 🇮🇹")
|
||||
XCTAssertEqual(options[1], "China 🇨🇳")
|
||||
XCTAssertEqual(options[2], "USA 🇺🇸")
|
||||
XCTAssertEqual(options[3], "France 🇫🇷")
|
||||
XCTAssertEqual(kind, .disclosed)
|
||||
expectation.fulfill()
|
||||
return .success(())
|
||||
}
|
||||
|
||||
context.send(viewAction: .submit)
|
||||
|
||||
let action = try await deferred.fulfill()
|
||||
|
||||
guard case .submit(let question, let options, let kind) = action else {
|
||||
XCTFail("Unexpected action")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(question, "What is your favorite country?")
|
||||
XCTAssertEqual(options.count, 4)
|
||||
XCTAssertEqual(options[0], "Italy 🇮🇹")
|
||||
XCTAssertEqual(options[1], "China 🇨🇳")
|
||||
XCTAssertEqual(options[2], "USA 🇺🇸")
|
||||
XCTAssertEqual(options[3], "France 🇫🇷")
|
||||
XCTAssertEqual(kind, .disclosed)
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
private func setupViewModel(mode: PollFormMode) {
|
||||
viewModel = PollFormScreenViewModel(mode: mode)
|
||||
func testDeletePoll() async throws {
|
||||
setupViewModel(mode: .edit(eventID: "foo", poll: .emptyDisclosed))
|
||||
|
||||
context.question = "What is your favorite country?"
|
||||
context.options.append(.init(text: "France 🇫🇷"))
|
||||
XCTAssertFalse(context.viewState.isSubmitButtonDisabled)
|
||||
|
||||
let deferredFailure = deferFailure(viewModel.actions, timeout: 1, message: "The alert should be shown.") { $0 == .close }
|
||||
context.send(viewAction: .delete)
|
||||
|
||||
try await deferredFailure.fulfill()
|
||||
XCTAssertNotNil(context.alertInfo, "An alert should be shown before deleting the poll.")
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
let expectation = XCTestExpectation(description: "Delete poll")
|
||||
timelineProxy.redactReasonClosure = { eventID, _ in
|
||||
XCTAssertEqual(eventID, .eventID("foo"))
|
||||
expectation.fulfill()
|
||||
return .success(())
|
||||
}
|
||||
context.alertInfo?.secondaryButton?.action?()
|
||||
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
// MARK: - Helpers
|
||||
|
||||
private func setupViewModel(mode: PollFormMode = .new) {
|
||||
viewModel = PollFormScreenViewModel(mode: mode,
|
||||
timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,18 +12,20 @@ import XCTest
|
||||
|
||||
@MainActor
|
||||
class StaticLocationScreenViewModelTests: XCTestCase {
|
||||
let timelineProxy = TimelineProxyMock(.init())
|
||||
|
||||
var viewModel: StaticLocationScreenViewModelProtocol!
|
||||
var context: StaticLocationScreenViewModel.Context { viewModel.context }
|
||||
|
||||
private var cancellables = Set<AnyCancellable>()
|
||||
|
||||
var context: StaticLocationScreenViewModel.Context {
|
||||
viewModel.context
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
cancellables.removeAll()
|
||||
let viewModel = StaticLocationScreenViewModel(interactionMode: .picker,
|
||||
mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration)
|
||||
mapURLBuilder: ServiceLocator.shared.settings.mapTilerConfiguration,
|
||||
timelineController: MockTimelineController(timelineProxy: timelineProxy),
|
||||
analytics: ServiceLocator.shared.analytics,
|
||||
userIndicatorController: UserIndicatorControllerMock())
|
||||
viewModel.state.bindings.isLocationAuthorized = true
|
||||
self.viewModel = viewModel
|
||||
}
|
||||
@@ -73,22 +75,18 @@ class StaticLocationScreenViewModelTests: XCTestCase {
|
||||
context.mapCenterLocation = .init(latitude: 0, longitude: 0)
|
||||
context.geolocationUncertainty = 10
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||
switch action {
|
||||
case .sendLocation:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
let expectation = XCTestExpectation(description: "sendLocation")
|
||||
timelineProxy.sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure = { _, geoURI, _, _, assetType in
|
||||
XCTAssertEqual(geoURI.uncertainty, 10)
|
||||
XCTAssertEqual(assetType, .sender)
|
||||
expectation.fulfill()
|
||||
return .success(())
|
||||
}
|
||||
|
||||
context.send(viewAction: .selectLocation)
|
||||
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
|
||||
XCTFail("Sent action should be 'sendLocation'")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(geoUri.uncertainty, 10)
|
||||
XCTAssertTrue(isUserLocation)
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
|
||||
func testSendPickedLocation() async throws {
|
||||
@@ -96,21 +94,17 @@ class StaticLocationScreenViewModelTests: XCTestCase {
|
||||
context.isLocationAuthorized = nil
|
||||
context.geolocationUncertainty = 10
|
||||
|
||||
let deferred = deferFulfillment(viewModel.actions) { action in
|
||||
switch action {
|
||||
case .sendLocation:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
let deferred = deferFulfillment(viewModel.actions) { $0 == .close }
|
||||
let expectation = XCTestExpectation(description: "sendLocation")
|
||||
timelineProxy.sendLocationBodyGeoURIDescriptionZoomLevelAssetTypeClosure = { _, geoURI, _, _, assetType in
|
||||
XCTAssertEqual(geoURI.uncertainty, nil)
|
||||
XCTAssertEqual(assetType, .pin)
|
||||
expectation.fulfill()
|
||||
return .success(())
|
||||
}
|
||||
|
||||
context.send(viewAction: .selectLocation)
|
||||
guard case .sendLocation(let geoUri, let isUserLocation) = try await deferred.fulfill() else {
|
||||
XCTFail("Sent action should be 'sendLocation'")
|
||||
return
|
||||
}
|
||||
XCTAssertEqual(geoUri.uncertainty, nil)
|
||||
XCTAssertFalse(isUserLocation)
|
||||
await fulfillment(of: [expectation], timeout: 1)
|
||||
try await deferred.fulfill()
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user