diff --git a/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformationKtTest.kt b/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformationKtTest.kt index 29192e9a7..c6e06ecc7 100644 --- a/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformationKtTest.kt +++ b/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformationKtTest.kt @@ -23,104 +23,103 @@ import org.mockito.Mockito.`when` class ProfileInformationScreenTest { - private lateinit var profileRepository: ProfileRepository - private lateinit var profileViewModel: ProfileViewModel - private lateinit var navigationActions: NavigationActions - private lateinit var firebaseAuth: FirebaseAuth - - @get:Rule val composeTestRule = createComposeRule() - - private val mockUserProfile = - UserProfile(username = "JohnDoe", bio = "This is a bio", email = "john.doe@example.com") - - @Before - fun setUp() { - - profileRepository = mock(ProfileRepository::class.java) - profileViewModel = ProfileViewModel(profileRepository) - navigationActions = mock(NavigationActions::class.java) - firebaseAuth = mock(FirebaseAuth::class.java) - - // Define navigation action behavior - `when`(navigationActions.currentRoute()).thenReturn(Screen.PROFILE_INFORMATION) - } - - @Test - fun displayAllComponents() { - composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } - - // Check that the main components are displayed - composeTestRule.onNodeWithTag("editProfileScreen").assertIsDisplayed() - composeTestRule.onNodeWithTag("editProfileTitle").assertIsDisplayed() - composeTestRule.onNodeWithTag("goBackButton").assertIsDisplayed() - composeTestRule.onNodeWithTag("editProfileUsername").assertIsDisplayed() - composeTestRule.onNodeWithTag("editProfileEmail").assertIsDisplayed() - composeTestRule.onNodeWithTag("editProfileBio").assertIsDisplayed() - composeTestRule.onNodeWithTag("profileSave").assertIsDisplayed() - composeTestRule.onNodeWithTag("profileLogout").assertIsDisplayed() - - // Check button texts - composeTestRule.onNodeWithTag("profileSave").assertTextEquals("Save") - composeTestRule.onNodeWithTag("profileLogout").assertTextEquals("Sign out") - } - - @Test - fun saveButtonDisabledWhenFieldsAreEmpty() { - composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } - - // Initially, all fields are empty, so the save button should be disabled - composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() - - // Fill in username, bio, and email - composeTestRule.onNodeWithTag("editProfileUsername").performTextInput("JohnDoe") - composeTestRule.onNodeWithTag("editProfileEmail").performTextInput("john.doe@example.com") - composeTestRule.onNodeWithTag("editProfileBio").performTextInput("This is a bio") - - // Now all fields are filled, so the save button should be enabled - composeTestRule.onNodeWithTag("profileSave").assertIsEnabled() - } - - @Test - fun saveButtonDisabledWhenAnyFieldIsEmpty() { - composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } - - // Initially, all fields are empty, so the save button should be disabled - composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() - - // Fill only the username, leave other fields empty - composeTestRule.onNodeWithTag("editProfileUsername").performTextInput("JohnDoe") - composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() - - // Fill in the email and leave the bio empty - composeTestRule.onNodeWithTag("editProfileEmail").performTextInput("john.doe@example.com") - composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() - - // Now fill the bio - composeTestRule.onNodeWithTag("editProfileBio").performTextInput("This is a bio") - - // Now all fields are filled, the save button should be enabled - composeTestRule.onNodeWithTag("profileSave").assertIsEnabled() - } - - @Test - fun saveButtonWorks() { - composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } - - // Perform click on the save button - composeTestRule.onNodeWithTag("profileSave").performClick() - - // Mock behavior can be asserted in ViewModel (not shown here, as it's logic dependent) - } - - @Test - fun logoutButtonWorks() { - composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } - - // Perform click on the sign-out button - composeTestRule.onNodeWithTag("profileLogout").performClick() - - // Verify that the navigation action to the landing screen was triggered - Mockito.verify(navigationActions).navigateTo(Screen.LANDING) - } - -} \ No newline at end of file + private lateinit var profileRepository: ProfileRepository + private lateinit var profileViewModel: ProfileViewModel + private lateinit var navigationActions: NavigationActions + private lateinit var firebaseAuth: FirebaseAuth + + @get:Rule val composeTestRule = createComposeRule() + + private val mockUserProfile = + UserProfile(username = "JohnDoe", bio = "This is a bio", email = "john.doe@example.com") + + @Before + fun setUp() { + + profileRepository = mock(ProfileRepository::class.java) + profileViewModel = ProfileViewModel(profileRepository) + navigationActions = mock(NavigationActions::class.java) + firebaseAuth = mock(FirebaseAuth::class.java) + + // Define navigation action behavior + `when`(navigationActions.currentRoute()).thenReturn(Screen.PROFILE_INFORMATION) + } + + @Test + fun displayAllComponents() { + composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } + + // Check that the main components are displayed + composeTestRule.onNodeWithTag("editProfileScreen").assertIsDisplayed() + composeTestRule.onNodeWithTag("editProfileTitle").assertIsDisplayed() + composeTestRule.onNodeWithTag("goBackButton").assertIsDisplayed() + composeTestRule.onNodeWithTag("editProfileUsername").assertIsDisplayed() + composeTestRule.onNodeWithTag("editProfileEmail").assertIsDisplayed() + composeTestRule.onNodeWithTag("editProfileBio").assertIsDisplayed() + composeTestRule.onNodeWithTag("profileSave").assertIsDisplayed() + composeTestRule.onNodeWithTag("profileLogout").assertIsDisplayed() + + // Check button texts + composeTestRule.onNodeWithTag("profileSave").assertTextEquals("Save") + composeTestRule.onNodeWithTag("profileLogout").assertTextEquals("Sign out") + } + + @Test + fun saveButtonDisabledWhenFieldsAreEmpty() { + composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } + + // Initially, all fields are empty, so the save button should be disabled + composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() + + // Fill in username, bio, and email + composeTestRule.onNodeWithTag("editProfileUsername").performTextInput("JohnDoe") + composeTestRule.onNodeWithTag("editProfileEmail").performTextInput("john.doe@example.com") + composeTestRule.onNodeWithTag("editProfileBio").performTextInput("This is a bio") + + // Now all fields are filled, so the save button should be enabled + composeTestRule.onNodeWithTag("profileSave").assertIsEnabled() + } + + @Test + fun saveButtonDisabledWhenAnyFieldIsEmpty() { + composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } + + // Initially, all fields are empty, so the save button should be disabled + composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() + + // Fill only the username, leave other fields empty + composeTestRule.onNodeWithTag("editProfileUsername").performTextInput("JohnDoe") + composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() + + // Fill in the email and leave the bio empty + composeTestRule.onNodeWithTag("editProfileEmail").performTextInput("john.doe@example.com") + composeTestRule.onNodeWithTag("profileSave").assertIsNotEnabled() + + // Now fill the bio + composeTestRule.onNodeWithTag("editProfileBio").performTextInput("This is a bio") + + // Now all fields are filled, the save button should be enabled + composeTestRule.onNodeWithTag("profileSave").assertIsEnabled() + } + + @Test + fun saveButtonWorks() { + composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } + + // Perform click on the save button + composeTestRule.onNodeWithTag("profileSave").performClick() + + // Mock behavior can be asserted in ViewModel (not shown here, as it's logic dependent) + } + + @Test + fun logoutButtonWorks() { + composeTestRule.setContent { ProfileInformationScreen(profileViewModel, navigationActions) } + + // Perform click on the sign-out button + composeTestRule.onNodeWithTag("profileLogout").performClick() + + // Verify that the navigation action to the landing screen was triggered + Mockito.verify(navigationActions).navigateTo(Screen.LANDING) + } +} diff --git a/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileKtTest.kt b/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileKtTest.kt index 028437ed5..4413fbcfb 100644 --- a/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileKtTest.kt +++ b/app/src/androidTest/java/com/github/lookupgroup27/lookup/ui/profile/ProfileKtTest.kt @@ -111,4 +111,4 @@ class ProfileKtTest { composeTestRule.onNodeWithText("Map").assertExists() composeTestRule.onNodeWithText("Menu").assertExists() } -} \ No newline at end of file +} diff --git a/app/src/main/java/com/github/lookupgroup27/lookup/MainActivity.kt b/app/src/main/java/com/github/lookupgroup27/lookup/MainActivity.kt index 2f23adaae..2bf5c198c 100644 --- a/app/src/main/java/com/github/lookupgroup27/lookup/MainActivity.kt +++ b/app/src/main/java/com/github/lookupgroup27/lookup/MainActivity.kt @@ -89,7 +89,9 @@ fun LookUpApp() { navigation(startDestination = Screen.PROFILE, route = Route.PROFILE) { composable(Screen.COLLECTION) { CollectionScreen(navigationActions) } composable(Screen.PROFILE) { ProfileScreen(navigationActions) } - composable(Screen.PROFILE_INFORMATION) { ProfileInformationScreen(profileViewModel, navigationActions) } + composable(Screen.PROFILE_INFORMATION) { + ProfileInformationScreen(profileViewModel, navigationActions) + } } } } diff --git a/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepository.kt b/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepository.kt index 2f07f24ed..25c9b57d5 100644 --- a/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepository.kt +++ b/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepository.kt @@ -1,11 +1,11 @@ package com.github.lookupgroup27.lookup.model.profile interface ProfileRepository { - fun init(onSuccess: () -> Unit) + fun init(onSuccess: () -> Unit) - fun getUserProfile(onSuccess: (UserProfile?) -> Unit, onFailure: (Exception) -> Unit) + fun getUserProfile(onSuccess: (UserProfile?) -> Unit, onFailure: (Exception) -> Unit) - fun updateUserProfile(profile: UserProfile, onSuccess: () -> Unit, onFailure: (Exception) -> Unit) + fun updateUserProfile(profile: UserProfile, onSuccess: () -> Unit, onFailure: (Exception) -> Unit) - fun logoutUser() -} \ No newline at end of file + fun logoutUser() +} diff --git a/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepositoryFirestore.kt b/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepositoryFirestore.kt index 9721fceac..ab1cf689d 100644 --- a/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepositoryFirestore.kt +++ b/app/src/main/java/com/github/lookupgroup27/lookup/model/profile/ProfileRepositoryFirestore.kt @@ -11,7 +11,7 @@ data class UserProfile(val username: String = " ", val email: String = " ", val class ProfileRepositoryFirestore( private val db: FirebaseFirestore, private val auth: FirebaseAuth -) : ProfileRepository{ +) : ProfileRepository { // private val auth = FirebaseAuth.getInstance() private val collectionPath = "users" diff --git a/app/src/main/java/com/github/lookupgroup27/lookup/ui/navigation/BottomNavigationMenu.kt b/app/src/main/java/com/github/lookupgroup27/lookup/ui/navigation/BottomNavigationMenu.kt index f17d85d77..4a93b741e 100644 --- a/app/src/main/java/com/github/lookupgroup27/lookup/ui/navigation/BottomNavigationMenu.kt +++ b/app/src/main/java/com/github/lookupgroup27/lookup/ui/navigation/BottomNavigationMenu.kt @@ -4,7 +4,6 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme import androidx.compose.material3.NavigationBar import androidx.compose.material3.NavigationBarItem import androidx.compose.material3.Text diff --git a/app/src/main/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformation.kt b/app/src/main/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformation.kt index 7129ec132..556703c12 100644 --- a/app/src/main/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformation.kt +++ b/app/src/main/java/com/github/lookupgroup27/lookup/ui/profile/ProfileInformation.kt @@ -43,28 +43,28 @@ fun ProfileInformationScreen( navigationActions: NavigationActions ) { - profileViewModel.fetchUserProfile() - val profile = profileViewModel.userProfile.value - val user = FirebaseAuth.getInstance().currentUser // Get the current signed-in user - val userEmail = user?.email ?: "" - var username by remember { mutableStateOf(profile?.username ?: "") } - var bio by remember { mutableStateOf(profile?.bio ?: "") } - var email by remember { mutableStateOf(userEmail) } - val context = LocalContext.current + profileViewModel.fetchUserProfile() + val profile = profileViewModel.userProfile.value + val user = FirebaseAuth.getInstance().currentUser // Get the current signed-in user + val userEmail = user?.email ?: "" + var username by remember { mutableStateOf(profile?.username ?: "") } + var bio by remember { mutableStateOf(profile?.bio ?: "") } + var email by remember { mutableStateOf(userEmail) } + val context = LocalContext.current - Column( - verticalArrangement = Arrangement.Top, - horizontalAlignment = Alignment.Start, - modifier = Modifier.padding(8.dp).width(412.dp).height(892.dp).testTag("editProfileScreen")) { + Column( + verticalArrangement = Arrangement.Top, + horizontalAlignment = Alignment.Start, + modifier = Modifier.padding(8.dp).width(412.dp).height(892.dp).testTag("editProfileScreen")) { Spacer(modifier = Modifier.height(16.dp)) Icon( imageVector = Icons.Default.ArrowBack, contentDescription = "Back", modifier = - Modifier.padding(start = 16.dp) - .size(30.dp) - .clickable { navigationActions.goBack() } - .testTag("goBackButton")) + Modifier.padding(start = 16.dp) + .size(30.dp) + .clickable { navigationActions.goBack() } + .testTag("goBackButton")) Spacer(modifier = Modifier.height(16.dp)) Text( text = "Your Personal Information", @@ -75,74 +75,74 @@ fun ProfileInformationScreen( verticalArrangement = Arrangement.spacedBy(0.dp, Alignment.Top), horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier.padding(20.dp).fillMaxWidth()) { - Spacer(modifier = Modifier.height(16.dp)) - OutlinedTextField( - value = username, - onValueChange = { new_name -> username = new_name }, - label = { Text("Username") }, - placeholder = { Text("Enter username") }, - shape = RoundedCornerShape(16.dp), - modifier = - Modifier.padding(0.dp) - .width(312.dp) - .height(60.dp) - .testTag("editProfileUsername")) - Spacer(modifier = Modifier.height(30.dp)) - OutlinedTextField( - value = email, - onValueChange = { + Spacer(modifier = Modifier.height(16.dp)) + OutlinedTextField( + value = username, + onValueChange = { new_name -> username = new_name }, + label = { Text("Username") }, + placeholder = { Text("Enter username") }, + shape = RoundedCornerShape(16.dp), + modifier = + Modifier.padding(0.dp) + .width(312.dp) + .height(60.dp) + .testTag("editProfileUsername")) + Spacer(modifier = Modifier.height(30.dp)) + OutlinedTextField( + value = email, + onValueChange = { if (userEmail.isEmpty()) { - email = it + email = it } - }, - label = { Text("E-mail") }, - placeholder = { Text("Enter your e-mail") }, - shape = RoundedCornerShape(16.dp), - modifier = - Modifier.padding(0.dp) - .width(312.dp) - .height(60.dp) - .testTag("editProfileEmail")) - Spacer(modifier = Modifier.height(30.dp)) - OutlinedTextField( - value = bio, - onValueChange = { new_description -> bio = new_description }, - label = { Text("Bio") }, - placeholder = { Text("Enter a bio") }, - shape = RoundedCornerShape(16.dp), - modifier = - Modifier.padding(0.dp).width(312.dp).height(80.dp).testTag("editProfileBio")) - Spacer(modifier = Modifier.height(72.dp)) - Spacer(modifier = Modifier.height(30.dp)) + }, + label = { Text("E-mail") }, + placeholder = { Text("Enter your e-mail") }, + shape = RoundedCornerShape(16.dp), + modifier = + Modifier.padding(0.dp) + .width(312.dp) + .height(60.dp) + .testTag("editProfileEmail")) + Spacer(modifier = Modifier.height(30.dp)) + OutlinedTextField( + value = bio, + onValueChange = { new_description -> bio = new_description }, + label = { Text("Bio") }, + placeholder = { Text("Enter a bio") }, + shape = RoundedCornerShape(16.dp), + modifier = + Modifier.padding(0.dp).width(312.dp).height(80.dp).testTag("editProfileBio")) + Spacer(modifier = Modifier.height(72.dp)) + Spacer(modifier = Modifier.height(30.dp)) - Button( - onClick = { + Button( + onClick = { var newProfile: UserProfile if (profile != null) { - newProfile = profile.copy(username = username, bio = bio, email = email) + newProfile = profile.copy(username = username, bio = bio, email = email) } else { - newProfile = UserProfile(username = username, bio = bio, email = email) + newProfile = UserProfile(username = username, bio = bio, email = email) } profileViewModel.updateUserProfile(newProfile) - }, - enabled = username.isNotEmpty() && bio.isNotEmpty() && email.isNotEmpty(), - colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF30315B)), - modifier = Modifier.width(131.dp).height(40.dp).testTag("profileSave")) { - Text(text = "Save", color = Color.White) - } + }, + enabled = username.isNotEmpty() && bio.isNotEmpty() && email.isNotEmpty(), + colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF30315B)), + modifier = Modifier.width(131.dp).height(40.dp).testTag("profileSave")) { + Text(text = "Save", color = Color.White) + } - Spacer(modifier = Modifier.height(30.dp)) - Button( - onClick = { + Spacer(modifier = Modifier.height(30.dp)) + Button( + onClick = { profileViewModel.logoutUser() navigationActions.navigateTo(Screen.LANDING) - }, - enabled = true, - colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF410002)), - modifier = Modifier.width(131.dp).height(40.dp).testTag("profileLogout")) { - Text(text = "Sign out", color = Color.White) + }, + enabled = true, + colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF410002)), + modifier = Modifier.width(131.dp).height(40.dp).testTag("profileLogout")) { + Text(text = "Sign out", color = Color.White) + } } - } - } -} \ No newline at end of file + } +}