Configure the AuthenticationService later now that we have 2 flows on the start screen. (#3316)

* Don't query the homeserver until confirming it (or selecting a different one).

* Setup the infrastructure to test AuthenticationService.

Implement basic tests for configuration & password login.

* Use the real AuthenticationService with a mock Client in all of the tests.

* Add tests for the ServerConfirmationScreenViewModel.

* Remove redundant view state and test for it.
This commit is contained in:
Doug
2024-09-25 14:40:18 +01:00
committed by GitHub
parent af8c16150b
commit a8dbda90d9
44 changed files with 1209 additions and 298 deletions
@@ -11,30 +11,22 @@ enum MockServerSelectionScreenState: CaseIterable {
case matrix
case emptyAddress
case invalidAddress
case nonModal
/// Generate the view struct for the screen state.
@MainActor var viewModel: ServerSelectionScreenViewModel {
switch self {
case .matrix:
return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
isModallyPresented: true)
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
case .emptyAddress:
return ServerSelectionScreenViewModel(homeserverAddress: "",
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
isModallyPresented: true)
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
case .invalidAddress:
let viewModel = ServerSelectionScreenViewModel(homeserverAddress: "thisisbad",
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
isModallyPresented: true)
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL)
viewModel.displayError(.footerMessage(L10n.errorUnknown))
return viewModel
case .nonModal:
return ServerSelectionScreenViewModel(homeserverAddress: "https://matrix.org",
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
isModallyPresented: false)
}
}
}
@@ -11,9 +11,9 @@ import SwiftUI
struct ServerSelectionScreenCoordinatorParameters {
/// The service used to authenticate the user.
let authenticationService: AuthenticationServiceProtocol
let authenticationFlow: AuthenticationFlow
let slidingSyncLearnMoreURL: URL
let userIndicatorController: UserIndicatorControllerProtocol
/// Whether the screen is presented modally or within a navigation stack.
let isModallyPresented: Bool
}
enum ServerSelectionScreenCoordinatorAction {
@@ -38,8 +38,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
init(parameters: ServerSelectionScreenCoordinatorParameters) {
self.parameters = parameters
viewModel = ServerSelectionScreenViewModel(homeserverAddress: parameters.authenticationService.homeserver.value.address,
slidingSyncLearnMoreURL: ServiceLocator.shared.settings.slidingSyncLearnMoreURL,
isModallyPresented: parameters.isModallyPresented)
slidingSyncLearnMoreURL: parameters.slidingSyncLearnMoreURL)
userIndicatorController = parameters.userIndicatorController
}
@@ -85,7 +84,7 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
startLoading()
Task {
switch await authenticationService.configure(for: homeserverAddress) {
switch await authenticationService.configure(for: homeserverAddress, flow: parameters.authenticationFlow) {
case .success:
MXLog.info("Selected homeserver: \(homeserverAddress)")
actionsSubject.send(.updated)
@@ -107,6 +106,8 @@ final class ServerSelectionScreenCoordinator: CoordinatorProtocol {
viewModel.displayError(.invalidWellKnownAlert(error))
case .slidingSyncNotAvailable:
viewModel.displayError(.slidingSyncAlert)
case .registrationNotSupported:
viewModel.displayError(.registrationAlert) // TODO: [DOUG] Test me!
default:
viewModel.displayError(.footerMessage(L10n.errorUnknown))
}
@@ -22,19 +22,12 @@ struct ServerSelectionScreenViewState: BindableState {
var bindings: ServerSelectionScreenBindings
/// An error message to be shown in the text field footer.
var footerErrorMessage: String?
/// Whether the screen is presented modally or within a navigation stack.
var isModallyPresented: Bool
/// The message to show in the text field footer.
var footerMessage: AttributedString {
footerErrorMessage.map(AttributedString.init) ?? regularFooterMessage
}
/// The title shown on the confirm button.
var buttonTitle: String {
isModallyPresented ? L10n.actionContinue : L10n.actionNext
}
/// The text field is showing an error.
var isShowingFooterError: Bool {
footerErrorMessage != nil
@@ -45,10 +38,9 @@ struct ServerSelectionScreenViewState: BindableState {
bindings.homeserverAddress.isEmpty || isShowingFooterError
}
init(slidingSyncLearnMoreURL: URL, bindings: ServerSelectionScreenBindings, footerErrorMessage: String? = nil, isModallyPresented: Bool) {
init(slidingSyncLearnMoreURL: URL, bindings: ServerSelectionScreenBindings, footerErrorMessage: String? = nil) {
self.bindings = bindings
self.footerErrorMessage = footerErrorMessage
self.isModallyPresented = isModallyPresented
let linkPlaceholder = "{link}"
var message = AttributedString(L10n.screenChangeServerFormNotice(linkPlaceholder))
@@ -82,4 +74,6 @@ enum ServerSelectionScreenErrorType: Hashable {
case invalidWellKnownAlert(String)
/// An alert that allows the user to learn about sliding sync.
case slidingSyncAlert
/// An alert that informs the user that registration isn't supported.
case registrationAlert
}
@@ -19,13 +19,12 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
actionsSubject.eraseToAnyPublisher()
}
init(homeserverAddress: String, slidingSyncLearnMoreURL: URL, isModallyPresented: Bool) {
init(homeserverAddress: String, slidingSyncLearnMoreURL: URL) {
self.slidingSyncLearnMoreURL = slidingSyncLearnMoreURL
let bindings = ServerSelectionScreenBindings(homeserverAddress: homeserverAddress)
super.init(initialViewState: ServerSelectionScreenViewState(slidingSyncLearnMoreURL: slidingSyncLearnMoreURL,
bindings: bindings,
isModallyPresented: isModallyPresented))
bindings: bindings))
}
override func process(viewAction: ServerSelectionScreenViewAction) {
@@ -46,7 +45,7 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
state.footerErrorMessage = message
}
case .invalidWellKnownAlert(let error):
state.bindings.alertInfo = AlertInfo(id: .slidingSyncAlert,
state.bindings.alertInfo = AlertInfo(id: .invalidWellKnownAlert(error),
title: L10n.commonServerNotSupported,
message: L10n.screenChangeServerErrorInvalidWellKnown(error))
case .slidingSyncAlert:
@@ -56,6 +55,10 @@ class ServerSelectionScreenViewModel: ServerSelectionScreenViewModelType, Server
message: L10n.screenChangeServerErrorNoSlidingSyncMessage,
primaryButton: .init(title: L10n.actionLearnMore, role: .cancel, action: openURL),
secondaryButton: .init(title: L10n.actionCancel, action: nil))
case .registrationAlert:
state.bindings.alertInfo = AlertInfo(id: .registrationAlert,
title: L10n.errorUnknown,
message: L10n.errorAccountCreationNotPossible)
}
}
@@ -64,7 +64,7 @@ struct ServerSelectionScreen: View {
.onSubmit(submit)
Button(action: submit) {
Text(context.viewState.buttonTitle)
Text(L10n.actionContinue)
}
.buttonStyle(.compound(.primary))
.disabled(context.viewState.hasValidationError)
@@ -72,15 +72,12 @@ struct ServerSelectionScreen: View {
}
}
@ToolbarContentBuilder
var toolbar: some ToolbarContent {
ToolbarItem(placement: .cancellationAction) {
if context.viewState.isModallyPresented {
Button { context.send(viewAction: .dismiss) } label: {
Text(L10n.actionCancel)
}
.accessibilityIdentifier(A11yIdentifiers.changeServerScreen.dismiss)
Button { context.send(viewAction: .dismiss) } label: {
Text(L10n.actionCancel)
}
.accessibilityIdentifier(A11yIdentifiers.changeServerScreen.dismiss)
}
}