From aad2f9a9181fbad0e103bc1ba57922599810e181 Mon Sep 17 00:00:00 2001 From: pooja Date: Thu, 23 Jul 2026 12:24:39 +0400 Subject: [PATCH 1/8] Add validation tests for invalid credentials and offline network status to the invite users Maestro flow. --- ...vite_users_using_qr_code_or_link_test.yaml | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml index 4f03f9da7..0c43d825e 100644 --- a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml +++ b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml @@ -197,6 +197,36 @@ onFlowComplete: file: "subflows/get_started_select_school_by_name.yaml" env: SCHOOL_NAME: ${SCHOOL_NAME} +# Test login error when login credential is not valid - Invalid Username +- tapOn: + id: "username" +- inputText: "teacheruser" +- tapOn: + id : "password" +- inputText: "test123" +- tapOn: "Login" +- assertVisible: "Invalid Username/Password" +- back +- tapOn: + id: "school_name_text" + text: ${SCHOOL_NAME} + +# Test login error when login credential is not valid - Invalid Password +- tapOn: + id: "username" +- inputText: "teacherauser" +- tapOn: + id : "password" +- inputText: "test1234" +- tapOn: "Login" +- assertVisible: "Invalid Username/Password" +- back +- tapOn: + id: "school_name_text" + text: ${SCHOOL_NAME} + +# Test error when device internet is not connected +- setAirplaneMode: enabled - tapOn: id: "username" - inputText: "teacherauser" @@ -204,6 +234,14 @@ onFlowComplete: id : "password" - inputText: "test123" - tapOn: "Login" +- assertVisible: "Network error: check your connection and try again" + +# Device back to Online mode - Login success +- setAirplaneMode: disabled +- assertVisible: + id: "app_title" + text: "Login" +- tapOn: "Login" - assertVisible: "Apps" - tapOn: "Classes" - assertVisible: From 117e510e84a6b74f88962fe9b798ba8fe9f15936 Mon Sep 17 00:00:00 2001 From: pooja Date: Thu, 23 Jul 2026 13:23:15 +0400 Subject: [PATCH 2/8] Simplify the login error test case and update assertion text for case sensitivity in Maestro flows. --- ...vite_users_using_qr_code_or_link_test.yaml | 31 +++++-------------- 1 file changed, 7 insertions(+), 24 deletions(-) diff --git a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml index 0c43d825e..c302f4d67 100644 --- a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml +++ b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml @@ -197,7 +197,7 @@ onFlowComplete: file: "subflows/get_started_select_school_by_name.yaml" env: SCHOOL_NAME: ${SCHOOL_NAME} -# Test login error when login credential is not valid - Invalid Username +# Test login error when login credential is not valid - Invalid username - tapOn: id: "username" - inputText: "teacheruser" @@ -205,34 +205,17 @@ onFlowComplete: id : "password" - inputText: "test123" - tapOn: "Login" -- assertVisible: "Invalid Username/Password" -- back -- tapOn: - id: "school_name_text" - text: ${SCHOOL_NAME} - -# Test login error when login credential is not valid - Invalid Password -- tapOn: - id: "username" -- inputText: "teacherauser" -- tapOn: - id : "password" -- inputText: "test1234" -- tapOn: "Login" -- assertVisible: "Invalid Username/Password" -- back -- tapOn: - id: "school_name_text" - text: ${SCHOOL_NAME} +- assertVisible: "Invalid username/password" +- runFlow: + file: "subflows/erase_text.yaml" + env: + TEXT: "teacheruser" # Test error when device internet is not connected - setAirplaneMode: enabled - tapOn: - id: "username" + id : "username" - inputText: "teacherauser" -- tapOn: - id : "password" -- inputText: "test123" - tapOn: "Login" - assertVisible: "Network error: check your connection and try again" From f569e12288c4e66fac43ffb4e1debd4724b9796e Mon Sep 17 00:00:00 2001 From: mandviverma Date: Tue, 4 Aug 2026 14:48:24 +0530 Subject: [PATCH 3/8] Add connectivity monitoring to login flow and update test for internet connection --- ...1_invite_users_using_qr_code_or_link_test.yaml | 4 ++++ .../app/view/manageuser/login/LoginScreen.kt | 9 +++++++++ .../viewmodel/manageuser/login/LoginViewModel.kt | 15 +++++++++++++-- 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml index c302f4d67..2424933d1 100644 --- a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml +++ b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml @@ -221,6 +221,10 @@ onFlowComplete: # Device back to Online mode - Login success - setAirplaneMode: disabled +- extendedWaitUntil: + visible: + id: "internet_connected_tag" + timeout: 10000 - assertVisible: id: "app_title" text: "Login" diff --git a/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt b/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt index 8b6a0f196..dd5903a25 100644 --- a/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt +++ b/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt @@ -128,4 +128,13 @@ fun LoginScreen( RespectShortVersionInfoText(Modifier.defaultItemPadding().fillMaxWidth()) } + + if (uiState.isConnected) { + Text( + text = "", + modifier = Modifier + .fillMaxWidth() + .testTag("internet_connected_tag") + ) + } } diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt index 74b43c79b..2bb889e4f 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt @@ -36,6 +36,7 @@ import world.respect.shared.navigation.WaitingForApproval import world.respect.shared.resources.StringResourceUiText import world.respect.shared.resources.StringUiText import world.respect.shared.resources.UiText +import com.ustadmobile.libcache.connectivitymonitor.ConnectivityMonitor import world.respect.shared.util.di.SchoolDirectoryEntryScopeId import world.respect.shared.util.exception.getUiText import world.respect.shared.util.exception.getUiTextOrGeneric @@ -48,6 +49,7 @@ data class LoginUiState( val errorText: UiText? = null, val usernameError: StringResourceUiText? = null, val passwordError: StringResourceUiText? = null, + val connectedTag: Boolean = true ) class LoginViewModel( @@ -56,8 +58,9 @@ class LoginViewModel( getCredentialUseCase: GetCredentialUseCase, respectAppDataSource: RespectAppDataSource, private val filterUsernameUseCase: FilterUsernameUseCase, - private val savePasswordUseCase: SavePasswordUseCase -) : RespectViewModel(savedStateHandle), KoinScopeComponent { + private val savePasswordUseCase: SavePasswordUseCase, + private val connectivityMonitor: ConnectivityMonitor + ) : RespectViewModel(savedStateHandle), KoinScopeComponent { private val _uiState = MutableStateFlow(LoginUiState()) @@ -85,6 +88,14 @@ class LoginViewModel( userAccountIconVisible = false ) } + + connectivityMonitor.statusFlow.collect { state -> + _uiState.update { + it.copy( + isConnected = state.isConnected + ) + } + } } viewModelScope.launch { try { From ba6e0e25cd6f2ad95b548bc792d396f274ff52bd Mon Sep 17 00:00:00 2001 From: mandviverma Date: Tue, 4 Aug 2026 15:10:06 +0530 Subject: [PATCH 4/8] Add connectivity monitoring to login flow and update test for internet connection --- .../respect/shared/viewmodel/manageuser/login/LoginViewModel.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt index 2bb889e4f..4f44499cf 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt @@ -49,7 +49,7 @@ data class LoginUiState( val errorText: UiText? = null, val usernameError: StringResourceUiText? = null, val passwordError: StringResourceUiText? = null, - val connectedTag: Boolean = true + val isConnected: Boolean = true ) class LoginViewModel( From f3d3345bcb829a19f0f8e1595b21599dcf6ada53 Mon Sep 17 00:00:00 2001 From: mandviverma Date: Wed, 5 Aug 2026 13:15:52 +0530 Subject: [PATCH 5/8] Add connectivity monitoring to login flow and update test for internet connection --- ...vite_users_using_qr_code_or_link_test.yaml | 6 +++++- .../kotlin/world/respect/app/app/App.kt | 4 ++++ .../kotlin/world/respect/app/app/AppBar.kt | 21 ++++++++++++++++++- .../app/view/manageuser/login/LoginScreen.kt | 9 -------- .../manageuser/login/LoginViewModel.kt | 17 +++------------ 5 files changed, 32 insertions(+), 25 deletions(-) diff --git a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml index 2424933d1..76fe05159 100644 --- a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml +++ b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml @@ -213,6 +213,10 @@ onFlowComplete: # Test error when device internet is not connected - setAirplaneMode: enabled +- extendedWaitUntil: + visible: + id: "internet_disconnected" + timeout: 10000 - tapOn: id : "username" - inputText: "teacherauser" @@ -223,7 +227,7 @@ onFlowComplete: - setAirplaneMode: disabled - extendedWaitUntil: visible: - id: "internet_connected_tag" + id: "internet_connected" timeout: 10000 - assertVisible: id: "app_title" diff --git a/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/App.kt b/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/App.kt index 4fdbe5a82..d58be0331 100644 --- a/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/App.kt +++ b/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/App.kt @@ -32,6 +32,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.runtime.collectAsState import androidx.compose.runtime.rememberCoroutineScope import androidx.navigation.compose.rememberNavController +import com.ustadmobile.libcache.connectivitymonitor.ConnectivityMonitor import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch @@ -154,8 +155,10 @@ fun App( val coroutineScope = rememberCoroutineScope() val accountManager: RespectAccountManager = koinInject() + val connectivityMonitor: ConnectivityMonitor = koinInject() val biometricAuthUseCase : BiometricAuthUseCase = koinInject() val activeAccount by accountManager.selectedAccountAndPersonFlow.collectAsState(null) + val connectivityState by connectivityMonitor.statusFlow.collectAsState() val topLevelNavItems = if (activeAccount?.isChild == true) { APP_TOP_LEVEL_NAV_ITEMS_FOR_CHILD } else { @@ -202,6 +205,7 @@ fun App( RespectAppBar( compactHeader = (widthClass != SizeClass.EXPANDED), appUiState = appUiStateVal, + isConnectedToInternet = connectivityState.isConnected, navController = navController, topLevelItems = topLevelNavItems, onProfileClick = { diff --git a/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt b/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt index 0940420e7..8f59608c4 100644 --- a/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt +++ b/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt @@ -1,6 +1,7 @@ package world.respect.app.app import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.width @@ -59,11 +60,16 @@ import world.respect.shared.viewmodel.app.appstate.AppUiState import world.respect.shared.viewmodel.app.appstate.LoadingUiState +const val INTERNET_CONNECTED_TAG = "internet_connected" + +const val INTERNET_DISCONNECTED_TAG = "internet_disconnected" + @OptIn(ExperimentalMaterial3Api::class) @Composable fun RespectAppBar( compactHeader: Boolean, appUiState: AppUiState, + isConnectedToInternet: Boolean, navController: NavController, onProfileClick: () -> Unit = {}, topLevelItems: List, @@ -109,6 +115,19 @@ fun RespectAppBar( Box( contentAlignment = Alignment.BottomCenter ) { + Spacer( + modifier = Modifier + .fillMaxWidth() + .height(1.dp) + .testTag( + if (isConnectedToInternet) { + INTERNET_CONNECTED_TAG + } else { + INTERNET_DISCONNECTED_TAG + } + ) + ) + TopAppBar( title = { Text( @@ -285,4 +304,4 @@ fun RespectAppBar( } -} \ No newline at end of file +} diff --git a/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt b/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt index dd5903a25..8b6a0f196 100644 --- a/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt +++ b/respect-app-compose/src/commonMain/kotlin/world/respect/app/view/manageuser/login/LoginScreen.kt @@ -128,13 +128,4 @@ fun LoginScreen( RespectShortVersionInfoText(Modifier.defaultItemPadding().fillMaxWidth()) } - - if (uiState.isConnected) { - Text( - text = "", - modifier = Modifier - .fillMaxWidth() - .testTag("internet_connected_tag") - ) - } } diff --git a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt index 4f44499cf..a9663c3a9 100644 --- a/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt +++ b/respect-lib-shared/src/commonMain/kotlin/world/respect/shared/viewmodel/manageuser/login/LoginViewModel.kt @@ -36,7 +36,6 @@ import world.respect.shared.navigation.WaitingForApproval import world.respect.shared.resources.StringResourceUiText import world.respect.shared.resources.StringUiText import world.respect.shared.resources.UiText -import com.ustadmobile.libcache.connectivitymonitor.ConnectivityMonitor import world.respect.shared.util.di.SchoolDirectoryEntryScopeId import world.respect.shared.util.exception.getUiText import world.respect.shared.util.exception.getUiTextOrGeneric @@ -49,7 +48,6 @@ data class LoginUiState( val errorText: UiText? = null, val usernameError: StringResourceUiText? = null, val passwordError: StringResourceUiText? = null, - val isConnected: Boolean = true ) class LoginViewModel( @@ -58,9 +56,8 @@ class LoginViewModel( getCredentialUseCase: GetCredentialUseCase, respectAppDataSource: RespectAppDataSource, private val filterUsernameUseCase: FilterUsernameUseCase, - private val savePasswordUseCase: SavePasswordUseCase, - private val connectivityMonitor: ConnectivityMonitor - ) : RespectViewModel(savedStateHandle), KoinScopeComponent { + private val savePasswordUseCase: SavePasswordUseCase +) : RespectViewModel(savedStateHandle), KoinScopeComponent { private val _uiState = MutableStateFlow(LoginUiState()) @@ -88,14 +85,6 @@ class LoginViewModel( userAccountIconVisible = false ) } - - connectivityMonitor.statusFlow.collect { state -> - _uiState.update { - it.copy( - isConnected = state.isConnected - ) - } - } } viewModelScope.launch { try { @@ -217,7 +206,7 @@ class LoginViewModel( try { val authResponse = accountManager.login( - credential = RespectPasswordCredential(username, password), + credential = RespectPasswordCredential(username.trim(), password.trim()), schoolUrl = route.schoolUrl ) From 0a9c4a573e920a9f44a8e7241b4b10820cc7c815 Mon Sep 17 00:00:00 2001 From: mandviverma Date: Wed, 5 Aug 2026 13:21:08 +0530 Subject: [PATCH 6/8] Update test case for login error: change username to 'teacherauser' --- .../001_001_invite_users_using_qr_code_or_link_test.yaml | 4 ++-- .../src/commonMain/kotlin/world/respect/app/app/AppBar.kt | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml index d8b5b7ec3..1e3ec6cab 100644 --- a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml +++ b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml @@ -205,7 +205,7 @@ onFlowComplete: # Test login error when login credential is not valid - Invalid username - tapOn: id: "username" -- inputText: "teacheruser" +- inputText: "teacherauser" - tapOn: id : "password" - inputText: "test123" @@ -214,7 +214,7 @@ onFlowComplete: - runFlow: file: "subflows/erase_text.yaml" env: - TEXT: "teacheruser" + TEXT: "teacherauser" # Test error when device internet is not connected - setAirplaneMode: enabled diff --git a/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt b/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt index 8f59608c4..77f7c4f5b 100644 --- a/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt +++ b/respect-app-compose/src/commonMain/kotlin/world/respect/app/app/AppBar.kt @@ -304,4 +304,4 @@ fun RespectAppBar( } -} +} \ No newline at end of file From dce68ceb393a03fd7b3d63e81fec871b2178eaac Mon Sep 17 00:00:00 2001 From: mandviverma Date: Wed, 5 Aug 2026 14:02:43 +0530 Subject: [PATCH 7/8] Update test case for login error: correct username from 'teacherauser' to 'teacheruser' --- .../flows/001_001_invite_users_using_qr_code_or_link_test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml index 1e3ec6cab..288bd83d8 100644 --- a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml +++ b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml @@ -205,7 +205,7 @@ onFlowComplete: # Test login error when login credential is not valid - Invalid username - tapOn: id: "username" -- inputText: "teacherauser" +- inputText: "teacheruser" - tapOn: id : "password" - inputText: "test123" From 8b5321e3cfc1716becfde142f5f58d200a9c9396 Mon Sep 17 00:00:00 2001 From: mandviverma Date: Wed, 5 Aug 2026 14:32:55 +0530 Subject: [PATCH 8/8] Update test case for login error: correct username from 'teacherauser' to 'teacheruser' --- .../flows/001_001_invite_users_using_qr_code_or_link_test.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml index 288bd83d8..d8b5b7ec3 100644 --- a/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml +++ b/.maestro/flows/001_001_invite_users_using_qr_code_or_link_test.yaml @@ -214,7 +214,7 @@ onFlowComplete: - runFlow: file: "subflows/erase_text.yaml" env: - TEXT: "teacherauser" + TEXT: "teacheruser" # Test error when device internet is not connected - setAirplaneMode: enabled