diff --git a/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView+DataSource.swift b/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView+DataSource.swift index fa279458..e05e128b 100644 --- a/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView+DataSource.swift +++ b/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView+DataSource.swift @@ -35,6 +35,8 @@ extension LoginView { @Published private(set) var viewState = LoginViewState() @Published var showingErrorAlert = false @Published var showingLoading = false + @Published var showingEmailError = false + @Published var showingPasswordError = false private var cancellables = Set() @@ -66,6 +68,8 @@ extension LoginView { private func updateStates(_ state: LoginViewState) { viewState = state + showingEmailError = state.isEmailError + showingPasswordError = state.isPasswordError showingLoading = state.isLoading showingErrorAlert = !state.error.string.isEmpty if state.isSuccess { diff --git a/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift b/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift index caca78eb..3f7aae26 100644 --- a/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift +++ b/iosApp/Survey/Sources/Presentation/Modules/Login/LoginView.swift @@ -80,7 +80,7 @@ struct LoginView: View { .autocapitalization(.none) .disableAutocorrection(true) .keyboardType(.emailAddress) - .primaryTextField(error: dataSource.viewState.isEmailError) + .primaryTextField(error: $dataSource.showingEmailError) .accessibility(.login(.emailField)) } @@ -99,7 +99,7 @@ struct LoginView: View { .accessibility(.login(.forgotButton)) } } - .primaryTextField(error: dataSource.viewState.isPasswordError) + .primaryTextField(error: $dataSource.showingPasswordError) .frame(maxHeight: 56.0) } diff --git a/iosApp/Survey/Sources/Presentation/ViewModifiers/PrimaryTextField.swift b/iosApp/Survey/Sources/Presentation/ViewModifiers/PrimaryTextField.swift index 7e353d6f..7e53fd7d 100644 --- a/iosApp/Survey/Sources/Presentation/ViewModifiers/PrimaryTextField.swift +++ b/iosApp/Survey/Sources/Presentation/ViewModifiers/PrimaryTextField.swift @@ -10,7 +10,7 @@ import SwiftUI struct PrimaryTextField: ViewModifier { - let error: Bool + @Binding var error: Bool func body(content: Content) -> some View { ZStack { @@ -31,7 +31,7 @@ struct PrimaryTextField: ViewModifier { extension View { - func primaryTextField(error: Bool = false) -> some View { + func primaryTextField(error: Binding = .constant(false)) -> some View { modifier(PrimaryTextField(error: error)) } } diff --git a/iosApp/Survey/Sources/Supports/Helpers/KMM/AutoMockable/Shared+AutoMockable.swift b/iosApp/Survey/Sources/Supports/Helpers/KMM/AutoMockable/Shared+AutoMockable.swift index b3c2c661..b3a11f75 100644 --- a/iosApp/Survey/Sources/Supports/Helpers/KMM/AutoMockable/Shared+AutoMockable.swift +++ b/iosApp/Survey/Sources/Supports/Helpers/KMM/AutoMockable/Shared+AutoMockable.swift @@ -13,20 +13,18 @@ import Shared protocol ResetPasswordUseCaseKMM: ResetPasswordUseCase { func invoke(email: String) -> Kotlinx_coroutines_coreFlow - func invokeNative(email: String) - -> ( - @escaping (String, KotlinUnit) -> KotlinUnit, - @escaping (Error?, KotlinUnit) -> KotlinUnit - ) -> () -> KotlinUnit + func invokeNative(email: String) -> ( + @escaping (String, KotlinUnit) -> KotlinUnit, + @escaping (Error?, KotlinUnit) -> KotlinUnit + ) -> () -> KotlinUnit } // sourcery: AutoMockable protocol LogInUseCaseKMM: LogInUseCase { func invoke(email: String, password: String) -> Kotlinx_coroutines_coreFlow - func invokeNative(email: String, password: String) - -> ( - @escaping (Token, KotlinUnit) -> KotlinUnit, - @escaping (Error?, KotlinUnit) -> KotlinUnit - ) -> () -> KotlinUnit + func invokeNative(email: String, password: String) -> ( + @escaping (Token, KotlinUnit) -> KotlinUnit, + @escaping (Error?, KotlinUnit) -> KotlinUnit + ) -> () -> KotlinUnit } diff --git a/iosApp/SurveyTests/Sources/Specs/Presentation/Modules/Login/LoginViewDataSourceSpec.swift b/iosApp/SurveyTests/Sources/Specs/Presentation/Modules/Login/LoginViewDataSourceSpec.swift index 524574a1..4a1eb455 100644 --- a/iosApp/SurveyTests/Sources/Specs/Presentation/Modules/Login/LoginViewDataSourceSpec.swift +++ b/iosApp/SurveyTests/Sources/Specs/Presentation/Modules/Login/LoginViewDataSourceSpec.swift @@ -40,24 +40,26 @@ final class LoginViewDataSourceSpec: QuickSpec { expect(viewState?.isSuccess) == false } - it("has no loading state") { - let viewState = try self.awaitPublisher(dataSource.$viewState.collectNext(1)).last - expect(viewState?.isLoading) == false + it("showingLoading is false") { + let showingLoading = try self.awaitPublisher(dataSource.$showingLoading.collectNext(1)).last + expect(showingLoading) == false } - it("has no error state") { - let viewState = try self.awaitPublisher(dataSource.$viewState.collectNext(1)).last - expect(viewState?.error) == nil + it("showingErrorAlert is false") { + let showingErrorAlert = try self.awaitPublisher(dataSource.$showingErrorAlert.collectNext(1)).last + expect(showingErrorAlert) == false } - it("has no email error state") { - let viewState = try self.awaitPublisher(dataSource.$viewState.collectNext(1)).last - expect(viewState?.isEmailError) == false + it("showingEmailError is false") { + let showingEmailError = try self.awaitPublisher(dataSource.$showingEmailError.collectNext(1)).last + expect(showingEmailError) == false } - it("has no password error state") { - let viewState = try self.awaitPublisher(dataSource.$viewState.collectNext(1)).last - expect(viewState?.isPasswordError) == false + it("showingPasswordError is false") { + let showingPasswordError = try self.awaitPublisher( + dataSource.$showingPasswordError.collectNext(1) + ).last + expect(showingPasswordError) == false } } @@ -68,9 +70,9 @@ final class LoginViewDataSourceSpec: QuickSpec { dataSource.login() } - it("sets loading state") { - let viewState = try self.awaitPublisher(dataSource.$viewState.collectNext(2)).last - expect(viewState?.isLoading) == true + it("showingPasswordError is true") { + let showingPasswordError = try self.awaitPublisher(dataSource.$showingLoading.collectNext(2)).last + expect(showingPasswordError) == true } context("when it return token") { @@ -106,9 +108,11 @@ final class LoginViewDataSourceSpec: QuickSpec { dataSource.login() } - it("sets showingErrorAlert to true") { - let showingError = try self.awaitPublisher(dataSource.$showingErrorAlert.collectNext(3)).last - expect(showingError) == true + it("showingErrorAlert is true") { + let showingErrorAlert = try self.awaitPublisher( + dataSource.$showingErrorAlert.collectNext(3) + ).last + expect(showingErrorAlert) == true } it("sets correct error state") { diff --git a/iosApp/SurveyUITests/Sources/Flows/LoginFlow.swift b/iosApp/SurveyUITests/Sources/Flows/LoginFlow.swift index 9f62c268..3a499723 100644 --- a/iosApp/SurveyUITests/Sources/Flows/LoginFlow.swift +++ b/iosApp/SurveyUITests/Sources/Flows/LoginFlow.swift @@ -18,11 +18,8 @@ final class LoginFlow { } func execute() { - let uiTestConfig = SharedBuildConfig.UITestConfig() let loginScreen = LoginScreen(in: app) loginScreen.waitForExistence() - loginScreen.replaceInInField(.emailField, with: uiTestConfig.email()) - loginScreen.replaceInSecuredField(.passwordField, with: uiTestConfig.password()) loginScreen.tapButton(.loginButton) } } diff --git a/iosApp/fastlane/Managers/TestManager.rb b/iosApp/fastlane/Managers/TestManager.rb index b8aeba29..0647cfbe 100644 --- a/iosApp/fastlane/Managers/TestManager.rb +++ b/iosApp/fastlane/Managers/TestManager.rb @@ -16,6 +16,7 @@ def build_and_test(scheme:, targets:) result_bundle: true, only_testing: targets, number_of_retries: 2, + output_remove_retry_attempts: true, fail_build: false ) end diff --git a/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/helpers/extensions/String+Regex.kt b/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/helpers/extensions/String+Regex.kt index 3295c2f9..7db6b25f 100644 --- a/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/helpers/extensions/String+Regex.kt +++ b/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/helpers/extensions/String+Regex.kt @@ -5,5 +5,5 @@ fun String.isValidEmail(): Boolean { val expression = "\\b[\\w\\.-]+@[\\w\\.-]+\\.\\w{2,4}\\b" val regex = Regex(expression) - return this.isNotEmpty() && this.matches(regex) + return isNotEmpty() && matches(regex) } diff --git a/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModel.kt b/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModel.kt index 530b9470..f79d5bb0 100644 --- a/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModel.kt +++ b/shared/src/commonMain/kotlin/co/nimblehq/blisskmmic/presentation/modules/login/LoginViewModel.kt @@ -26,9 +26,7 @@ class LoginViewModel(private val logInUseCase: LogInUseCase): BaseViewModel() { val viewState: StateFlow = mutableViewState fun login(email: String, password: String) { - if (!validInput(email, password)) { - return - } + if (!validInput(email, password)) return setStateLoading() viewModelScope.launch { logInUseCase(email, password)