From 5bb30085ba90c1f49aec69d604ba3d056a3460ef Mon Sep 17 00:00:00 2001 From: charlie mangano Date: Mon, 28 Oct 2024 18:59:25 +0100 Subject: [PATCH] test: add UI tests for the SignIn screen Uncommented the existing (partly wrong) tests, corrected them and created new ones. Used mocking for `NavigationActions` and `AuthViewModel` to be able to check for calls to them. Used a `companion object` to have a reusable valid email and password. The tests now check for: * display of all components * correct errors showing up when invalid attempt (empty email or password) * not trying to log in the user if invalid attempt * valid attempt navigates to next screen * valid attempt logs in the user * click on sign up navigates to SignUp screen --- .../ui/authentication/SignInTest.kt | 269 ++++++++---------- 1 file changed, 126 insertions(+), 143 deletions(-) diff --git a/app/src/androidTest/java/com/android/periodpals/ui/authentication/SignInTest.kt b/app/src/androidTest/java/com/android/periodpals/ui/authentication/SignInTest.kt index d68f97149..fc46ede04 100644 --- a/app/src/androidTest/java/com/android/periodpals/ui/authentication/SignInTest.kt +++ b/app/src/androidTest/java/com/android/periodpals/ui/authentication/SignInTest.kt @@ -1,144 +1,127 @@ package com.android.periodpals.ui.authentication -// -// import androidx.compose.ui.test.assertIsDisplayed -// import androidx.compose.ui.test.assertTextEquals -// import androidx.compose.ui.test.junit4.createComposeRule -// import androidx.compose.ui.test.onNodeWithTag -// import androidx.compose.ui.test.performClick -// import androidx.compose.ui.test.performTextInput -// import androidx.navigation.compose.rememberNavController -// import com.android.periodpals.model.auth.AuthViewModel -// import com.android.periodpals.ui.navigation.NavigationActions -// import org.junit.Before -// import org.junit.Rule -// import org.junit.Test -// import org.mockito.Mockito.mock -// -// class SignInScreenTest { -// -// @get:Rule val composeTestRule = createComposeRule() -// lateinit var authViewModel: AuthViewModel -// -// // lateinit var supabaseClient: SupabaseClient -// -// @Before -// fun setUp() { -// authViewModel = mock(AuthViewModel::class.java) -// // supabaseClient = mock(SupabaseClient::class.java) -// } -// -// @Test -// fun signInScreen_displaysCorrectUI() { -// // Set the content to the SignInScreen -// composeTestRule.setContent { -// SignInScreen(authViewModel, NavigationActions(rememberNavController())) -// } -// -// // Check if the welcome text is displayed -// composeTestRule.onNodeWithTag("signInScreen").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInBackground").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInTitle").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInInstruction").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInEmail").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInPassword").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInPasswordVisibility").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInButton").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInOrText").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInGoogleButton").assertIsDisplayed() -// composeTestRule.onNodeWithTag("signInNotRegistered").assertIsDisplayed() -// } -// -// @Test -// fun signInScreen_emailValidation_emptyEmail_showsError() { -// composeTestRule.setContent { -// SignInScreen(authViewModel, NavigationActions(rememberNavController())) -// } -// -// // Click on the sign in button with empty fields -// composeTestRule.onNodeWithTag("signInButton").performClick() -// -// // Verify that the error message for email is displayed -// composeTestRule.onNodeWithTag("signInEmailError").assertTextEquals("Email cannot be empty") -// } -// -// @Test -// fun signInScreen_emailValidation_invalidEmail_showsError() { -// composeTestRule.setContent { -// SignInScreen(authViewModel, NavigationActions(rememberNavController())) -// } -// -// // Enter an invalid email -// composeTestRule.onNodeWithTag("signInEmail").performTextInput("invalidEmail") -// -// // Click on the sign in button -// composeTestRule.onNodeWithTag("signInButton").performClick() -// -// // Verify that the error message for email is displayed -// composeTestRule.onNodeWithTag("signInEmailError").assertTextEquals("Email must contain @") -// } -// -// @Test -// fun signInScreen_passwordValidation_emptyPassword_showsError() { -// composeTestRule.setContent { -// SignInScreen(authViewModel, NavigationActions(rememberNavController())) -// } -// -// // Enter a valid email -// composeTestRule.onNodeWithTag("signInEmail").performTextInput("test@example.com") -// -// // Click on the sign in button with empty password -// composeTestRule.onNodeWithTag("signInButton").performClick() -// -// // Verify that the error message for password is displayed -// composeTestRule -// .onNodeWithTag("signInPasswordError") -// .assertTextEquals("Password cannot be empty") -// } -// -// @Test -// fun signInScreen_signIn_successfulLogin() { -// composeTestRule.setContent { -// SignInScreen(authViewModel, NavigationActions(rememberNavController())) -// } -// -// // Enter valid email and password -// composeTestRule.onNodeWithTag("signInEmail").performTextInput("test@example.com") -// composeTestRule.onNodeWithTag("signInPassword").performTextInput("ValidPassword123") -// -// // Click on the sign in button -// composeTestRule.onNodeWithTag("signInButton").performClick() -// -// // Check for a successful login Toast (mocking would be required here) -// // Currently, you can't test Toast directly; you can use dependency injection or other methods -// } -// -// @Test -// fun signInScreen_signIn_failsInvalidLogin() { -// composeTestRule.setContent { -// SignInScreen(authViewModel, NavigationActions(rememberNavController())) -// } -// -// // Enter valid email and an invalid password -// composeTestRule.onNodeWithTag("signInEmail").performTextInput("test@example.com") -// composeTestRule.onNodeWithTag("signInPassword").performTextInput("InvalidPassword") -// -// // Click on the sign in button -// composeTestRule.onNodeWithTag("signInButton").performClick() -// -// // Check for a failed login Toast (mocking would be required here) -// // You can set up your test to verify that the error message or Toast appears. -// } -// -// @Test -// fun signInScreen_navigatesToSignUp() { -// composeTestRule.setContent { -// SignInScreen(authViewModel, NavigationActions(rememberNavController())) -// } -// -// // Click on the "Not registered yet? Sign up here!" text -// composeTestRule.onNodeWithTag("signInNotRegistered").performClick() -// -// // Check for a navigation action (mocking would be required here) -// // You would verify that the navigation to the sign-up screen is triggered. -// } -// } + +import androidx.compose.runtime.mutableStateOf +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertTextEquals +import androidx.compose.ui.test.junit4.createComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextInput +import com.android.periodpals.model.auth.AuthViewModel +import com.android.periodpals.model.user.UserAuthState +import com.android.periodpals.ui.navigation.NavigationActions +import com.android.periodpals.ui.navigation.Route +import com.android.periodpals.ui.navigation.Screen +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mockito.mock +import org.mockito.Mockito.`when` +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.never +import org.mockito.kotlin.verify + +class SignInScreenTest { + + @get:Rule val composeTestRule = createComposeRule() + private lateinit var navigationActions: NavigationActions + private lateinit var authViewModel: AuthViewModel + + companion object { + private const val email = "test@example.com" + private const val password = "password" + } + + @Before + fun setUp() { + navigationActions = mock(NavigationActions::class.java) + authViewModel = mock(AuthViewModel::class.java) + + `when`(navigationActions.currentRoute()).thenReturn(Route.ALERT_LIST) + `when`(authViewModel.userAuthState) + .thenReturn(mutableStateOf(UserAuthState.Success("User is logged in"))) + composeTestRule.setContent { SignInScreen(authViewModel, navigationActions) } + } + + @Test + fun allComponentsAreDisplayed() { + + composeTestRule.onNodeWithTag("signInScreen").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInBackground").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInTitle").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInInstruction").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInEmail").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInPassword").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInPasswordVisibility").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInButton").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInOrText").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInGoogleButton").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInNotRegistered").assertIsDisplayed() + } + + @Test + fun emptyEmailShowsCorrectError() { + + composeTestRule.onNodeWithTag("signInPassword").performTextInput(password) + composeTestRule.onNodeWithTag("signInButton").performClick() + composeTestRule.onNodeWithTag("signInEmailError").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInEmailError").assertTextEquals("Email cannot be empty") + } + + @Test + fun emptyEmailDoesNotCallVM() { + + composeTestRule.onNodeWithTag("signInPassword").performTextInput(password) + composeTestRule.onNodeWithTag("signInButton").performClick() + composeTestRule.onNodeWithTag("signInEmailError").assertIsDisplayed() + composeTestRule.onNodeWithTag("signInEmailError").assertTextEquals("Email cannot be empty") + verify(authViewModel, never()).logInWithEmail(any(), any(), any()) + } + + @Test + fun emptyPasswordShowsCorrectError() { + + composeTestRule.onNodeWithTag("signInEmail").performTextInput(email) + composeTestRule.onNodeWithTag("signInButton").performClick() + composeTestRule.onNodeWithTag("signInPasswordError").assertIsDisplayed() + composeTestRule + .onNodeWithTag("signInPasswordError") + .assertTextEquals("Password cannot be empty") + } + + @Test + fun emptyPasswordDoesNotCallVM() { + + composeTestRule.onNodeWithTag("signInEmail").performTextInput(email) + composeTestRule.onNodeWithTag("signInButton").performClick() + composeTestRule.onNodeWithTag("signInPasswordError").assertIsDisplayed() + composeTestRule + .onNodeWithTag("signInPasswordError") + .assertTextEquals("Password cannot be empty") + } + + @Test + fun validSignInAttemptNavigatesToProfileScreen() { + + composeTestRule.onNodeWithTag("signInEmail").performTextInput(email) + composeTestRule.onNodeWithTag("signInPassword").performTextInput(password) + composeTestRule.onNodeWithTag("signInButton").performClick() + verify(navigationActions).navigateTo(Screen.PROFILE) + } + + @Test + fun validSignInAttemptCallsVMLogInWithEmail() { + + composeTestRule.onNodeWithTag("signInEmail").performTextInput(email) + composeTestRule.onNodeWithTag("signInPassword").performTextInput(password) + composeTestRule.onNodeWithTag("signInButton").performClick() + verify(authViewModel).logInWithEmail(any(), eq(email), eq(password)) + } + + @Test + fun signUpHereNavigatesToSignUpScreen() { + composeTestRule.onNodeWithTag("signInNotRegistered").performClick() + verify(navigationActions).navigateTo(Screen.SIGN_UP) + } +}