diff --git a/app/src/androidTest/java/com/android/unio/components/user/UserProfileEditionTest.kt b/app/src/androidTest/java/com/android/unio/components/user/UserProfileEditionTest.kt index e1f382b43..13039891b 100644 --- a/app/src/androidTest/java/com/android/unio/components/user/UserProfileEditionTest.kt +++ b/app/src/androidTest/java/com/android/unio/components/user/UserProfileEditionTest.kt @@ -1,5 +1,7 @@ package com.android.unio.components.user +import android.net.ConnectivityManager +import android.net.Network import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertIsNotDisplayed import androidx.compose.ui.test.junit4.createComposeRule @@ -7,18 +9,27 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performScrollTo import androidx.compose.ui.test.performTextClearance +import androidx.compose.ui.test.performTextInput +import androidx.core.content.ContextCompat +import androidx.core.content.ContextCompat.getSystemService import com.android.unio.TearDown import com.android.unio.addNewUserSocial +import com.android.unio.assertDisplayComponentInScroll import com.android.unio.mocks.user.MockUser import com.android.unio.model.strings.test_tags.InterestsOverlayTestTags import com.android.unio.model.strings.test_tags.SocialsOverlayTestTags import com.android.unio.model.strings.test_tags.UserEditionTestTags +import com.android.unio.model.user.User import com.android.unio.ui.navigation.NavigationAction import com.android.unio.ui.navigation.Screen import com.android.unio.ui.user.UserProfileEditionScreenContent import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import io.mockk.MockKAnnotations +import io.mockk.every +import io.mockk.impl.annotations.MockK +import io.mockk.mockk +import io.mockk.mockkStatic import org.junit.Before import org.junit.Rule import org.junit.Test @@ -32,6 +43,11 @@ class UserProfileEditionTest : TearDown() { @get:Rule val composeTestRule = createComposeRule() @get:Rule val hiltRule = HiltAndroidRule(this) + @MockK private lateinit var connectivityManager: ConnectivityManager + + private lateinit var user: User + private var isOnlineUpdated: Boolean = false + @Before fun setUp() { MockKAnnotations.init(this, relaxed = true) @@ -40,16 +56,116 @@ class UserProfileEditionTest : TearDown() { navigationAction = mock(NavigationAction::class.java) `when`(navigationAction.getCurrentRoute()).thenReturn(Screen.EDIT_PROFILE) - val user = MockUser.createMockUser(interests = emptyList(), profilePicture = "") + user = MockUser.createMockUser(interests = emptyList(), profilePicture = "") + + val onOfflineChange = { newUser: User -> + user = newUser + isOnlineUpdated = false + } + + val onOnlineChange = { newUser: User -> + user = newUser + isOnlineUpdated = true + } + + mockkStatic(Network::class) + mockkStatic(ContextCompat::class) + every { getSystemService(any(), ConnectivityManager::class.java) } returns connectivityManager composeTestRule.setContent { UserProfileEditionScreenContent( - user, onDiscardChanges = { navigationAction.goBack() }, { uri, method -> method("") }, {}) + user, + { navigationAction.goBack() }, + { uri, method -> method("") }, + onOnlineChange, + onOfflineChange) } } + @Test + fun testUpdateUserOffline() { + every { connectivityManager?.activeNetwork } returns null + + composeTestRule + .onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD) + .assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD).performTextClearance() + composeTestRule.onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD).performClick() + composeTestRule + .onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD) + .performTextInput(UserUpdate.FIRST_NAME) + + composeTestRule + .onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD) + .assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD).performTextClearance() + composeTestRule.onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD).performClick() + composeTestRule + .onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD) + .performTextInput(UserUpdate.LAST_NAME) + + composeTestRule + .onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD) + .assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD).performTextClearance() + composeTestRule.onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD).performClick() + composeTestRule + .onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD) + .performTextInput(UserUpdate.BIOGRAPHY) + + composeTestRule.onNodeWithTag(UserEditionTestTags.SAVE_BUTTON).assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.SAVE_BUTTON).performClick() + + assert(user.firstName == UserUpdate.FIRST_NAME) + assert(user.lastName == UserUpdate.LAST_NAME) + assert(user.biography == UserUpdate.BIOGRAPHY) + assert(!isOnlineUpdated) + } + + @Test + fun testUpdateUserOnline() { + every { connectivityManager?.activeNetwork } returns mockk() + + composeTestRule + .onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD) + .assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD).performTextClearance() + composeTestRule.onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD).performClick() + composeTestRule + .onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD) + .performTextInput(UserUpdate.FIRST_NAME) + + composeTestRule + .onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD) + .assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD).performTextClearance() + composeTestRule.onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD).performClick() + composeTestRule + .onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD) + .performTextInput(UserUpdate.LAST_NAME) + + composeTestRule + .onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD) + .assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD).performTextClearance() + composeTestRule.onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD).performClick() + composeTestRule + .onNodeWithTag(UserEditionTestTags.BIOGRAPHY_TEXT_FIELD) + .performTextInput(UserUpdate.BIOGRAPHY) + + composeTestRule.onNodeWithTag(UserEditionTestTags.SAVE_BUTTON).assertDisplayComponentInScroll() + composeTestRule.onNodeWithTag(UserEditionTestTags.SAVE_BUTTON).performClick() + + assert(user.firstName == UserUpdate.FIRST_NAME) + assert(user.lastName == UserUpdate.LAST_NAME) + assert(user.biography == UserUpdate.BIOGRAPHY) + assert(isOnlineUpdated) + } + @Test fun testEverythingIsDisplayed() { + every { connectivityManager?.activeNetwork } returns mockk() + composeTestRule.onNodeWithTag(UserEditionTestTags.DISCARD_TEXT).assertIsDisplayed() composeTestRule .onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT, useUnmergedTree = true) @@ -66,6 +182,8 @@ class UserProfileEditionTest : TearDown() { @Test fun testInterestsButtonWorksCorrectly() { + every { connectivityManager?.activeNetwork } returns mockk() + composeTestRule .onNodeWithTag(UserEditionTestTags.INTERESTS_BUTTON) .performScrollTo() @@ -75,6 +193,8 @@ class UserProfileEditionTest : TearDown() { @Test fun testSocialsButtonWorksCorrectly() { + every { connectivityManager?.activeNetwork } returns mockk() + composeTestRule .onNodeWithTag(UserEditionTestTags.SOCIALS_BUTTON) .performScrollTo() @@ -84,6 +204,8 @@ class UserProfileEditionTest : TearDown() { @Test fun testAddingInterestsCorrectlyModifiesTheFlowRow() { + every { connectivityManager?.activeNetwork } returns mockk() + composeTestRule .onNodeWithTag(UserEditionTestTags.INTERESTS_BUTTON) .performScrollTo() @@ -104,6 +226,8 @@ class UserProfileEditionTest : TearDown() { @Test fun testAddingSocialsCorrectlyModifiesTheFlowRow() { + every { connectivityManager?.activeNetwork } returns mockk() + composeTestRule .onNodeWithTag(UserEditionTestTags.SOCIALS_BUTTON) .performScrollTo() @@ -121,6 +245,8 @@ class UserProfileEditionTest : TearDown() { @Test fun testCorrectlyExitsInterestOverlayScreen() { + every { connectivityManager?.activeNetwork } returns mockk() + composeTestRule .onNodeWithTag(UserEditionTestTags.INTERESTS_BUTTON) .performScrollTo() @@ -131,6 +257,8 @@ class UserProfileEditionTest : TearDown() { @Test fun testCorrectlyDisplaysErrorWhenFirstNameIsEmpty() { + every { connectivityManager?.activeNetwork } returns mockk() + composeTestRule.onNodeWithTag(UserEditionTestTags.FIRST_NAME_TEXT_FIELD).performTextClearance() composeTestRule.onNodeWithTag(UserEditionTestTags.LAST_NAME_TEXT_FIELD).performTextClearance() @@ -142,4 +270,10 @@ class UserProfileEditionTest : TearDown() { .onNodeWithTag(UserEditionTestTags.LAST_NAME_ERROR_TEXT, useUnmergedTree = true) .assertExists() } + + object UserUpdate { + const val FIRST_NAME = "Johnny" + const val LAST_NAME = "Däpp" + const val BIOGRAPHY = "Ich bin ein Testbenutzer" + } } diff --git a/app/src/main/java/com/android/unio/ui/user/UserProfileEdition.kt b/app/src/main/java/com/android/unio/ui/user/UserProfileEdition.kt index 9d2da8782..cf7ee5366 100644 --- a/app/src/main/java/com/android/unio/ui/user/UserProfileEdition.kt +++ b/app/src/main/java/com/android/unio/ui/user/UserProfileEdition.kt @@ -48,14 +48,13 @@ import com.android.unio.model.user.UserSocial import com.android.unio.model.user.UserViewModel import com.android.unio.model.user.checkImageUri import com.android.unio.model.user.checkNewUser +import com.android.unio.model.utils.Utils import com.android.unio.ui.authentication.overlay.InterestOverlay import com.android.unio.ui.authentication.overlay.SocialOverlay import com.android.unio.ui.components.InterestInputChip import com.android.unio.ui.components.ProfilePicturePicker import com.android.unio.ui.components.SocialInputChip import com.android.unio.ui.navigation.NavigationAction -import com.google.firebase.Firebase -import com.google.firebase.auth.auth import kotlinx.coroutines.flow.MutableStateFlow @Composable @@ -66,9 +65,9 @@ fun UserProfileEditionScreen( ) { val context = LocalContext.current - val userId = Firebase.auth.currentUser?.uid val user by userViewModel.user.collectAsState() + val userId = user!!.uid UserProfileEditionScreenContent( user = user!!, @@ -97,7 +96,7 @@ fun UserProfileEditionScreen( } } }, - onUploadUser = { modifiedUser -> + onUploadUserOnline = { modifiedUser -> userViewModel.addUser( modifiedUser, onSuccess = { @@ -108,6 +107,23 @@ fun UserProfileEditionScreen( .show() navigationAction.goBack() }) + }, + onUploadUserOffline = { modifiedUser -> + Toast.makeText( + context, + context.getString(R.string.user_settings_modified_offline), + Toast.LENGTH_LONG) + .show() + userViewModel.addUser( + modifiedUser, + onSuccess = { + Toast.makeText( + context, + context.getString(R.string.user_settings_modified_successfully), + Toast.LENGTH_SHORT) + .show() + }) + navigationAction.goBack() }) } @@ -117,7 +133,8 @@ fun UserProfileEditionScreenContent( user: User, onDiscardChanges: () -> Unit, onModifyUser: (MutableState, (String) -> Unit) -> Unit, - onUploadUser: (User) -> Unit, + onUploadUserOnline: (User) -> Unit, + onUploadUserOffline: (User) -> Unit ) { val context = LocalContext.current @@ -141,7 +158,7 @@ fun UserProfileEditionScreenContent( // But if it's the first time entering the page the uri will be the one from firebase // and therefore cannot be opened in a input stream in the onModifyUser function // Hence we must check whether the user has changed his profile picture or not to get a local URI. - val profilePictureUri = remember { mutableStateOf(user.profilePicture.toUri()) } + val profilePictureUri = remember { mutableStateOf(user.profilePicture.toUri()) } var showInterestsOverlay by remember { mutableStateOf(false) } var showSocialsOverlay by remember { mutableStateOf(false) } @@ -153,6 +170,7 @@ fun UserProfileEditionScreenContent( * simply be copied from the user. */ val createUser: (String) -> Unit = { uri -> + val hasInternet = Utils.checkInternetConnection(context) val newUser = User( uid = user.uid, @@ -169,7 +187,11 @@ fun UserProfileEditionScreenContent( isErrors = checkNewUser(newUser) if (isErrors.isEmpty()) { - onUploadUser(newUser) + if (hasInternet) { + onUploadUserOnline(newUser) + } else { + onUploadUserOffline(newUser) + } } } diff --git a/app/src/main/res/values-fr/strings.xml b/app/src/main/res/values-fr/strings.xml index 55cebc8a2..2ff363fda 100644 --- a/app/src/main/res/values-fr/strings.xml +++ b/app/src/main/res/values-fr/strings.xml @@ -101,6 +101,7 @@ Annuler vos modifications Sauvegarder vos modifications Changements correctement modifié + Changements enregistrés. Connectez-vous à Internet pour appliquer les changements diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 10fbd7c0d..e515e7689 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -106,6 +106,7 @@ "Discard Changes" Save changes Changes Successfully modified + Change has been saved. Turn online to apply change