From f3279f9ac211230f25c9d9c6a96215587edf2aa5 Mon Sep 17 00:00:00 2001 From: DanThePol Date: Sun, 2 Jun 2024 18:05:35 +0200 Subject: [PATCH 1/5] Refactored User and Event ViewModel and Repository to add user deletion + ktfmtFormat --- .../model/repository/EventRepository.kt | 65 +++++++++++++-- .../gomeet/model/repository/UserRepository.kt | 79 ++++++++++++++++++- .../github/se/gomeet/ui/mainscreens/Trends.kt | 6 +- .../se/gomeet/ui/mainscreens/events/Events.kt | 24 +++--- .../ui/mainscreens/events/MyEventInfo.kt | 3 +- .../gomeet/ui/mainscreens/explore/Explore.kt | 13 ++- .../ui/mainscreens/profile/OthersProfile.kt | 16 +++- .../gomeet/ui/mainscreens/profile/Profile.kt | 20 ++--- .../se/gomeet/viewmodel/EventViewModel.kt | 57 +++++++++---- .../se/gomeet/viewmodel/UserViewModel.kt | 51 ++++++------ 10 files changed, 254 insertions(+), 80 deletions(-) diff --git a/app/src/main/java/com/github/se/gomeet/model/repository/EventRepository.kt b/app/src/main/java/com/github/se/gomeet/model/repository/EventRepository.kt index d0d8f5d5..92625973 100644 --- a/app/src/main/java/com/github/se/gomeet/model/repository/EventRepository.kt +++ b/app/src/main/java/com/github/se/gomeet/model/repository/EventRepository.kt @@ -88,11 +88,65 @@ class EventRepository private constructor() { callback(eventList) } .addOnFailureListener { exception -> - Log.w(TAG, "Error getting documents.", exception) + Log.w(TAG, "Error getting all events", exception) callback(null) } } + /** + * This function enables a user (or a list of users) to leave an event + * + * @param eventID The event ID for the user(s) to leave + * @param userID The user ID if there is only one (default is "") + * @param userIDs The list of user IDs (default is emptyList()) + */ + suspend fun leaveEvent( + eventID: String, + userID: String = "", + userIDs: List = emptyList() + ) { + + val usersToRemove = + if (userIDs.contains(userID) || userID == "") userIDs else userIDs.plus(userID) + if (usersToRemove.isEmpty()) { + Log.w(TAG, "Returning from leave event $eventID: called with no user to remove") + return + } + + val event: Event? = + Firebase.firestore + .collection(EVENT_COLLECTION) + .document(eventID) + .get() + .await() + .data + ?.toEvent() + if (event == null) { + Log.e(TAG, "Error leaving event $eventID: unfindable event") + return + } + + // Can't leave the event if you're the creator + val newParticipants = event.participants.minus(usersToRemove.toSet().minus(event.creator)) + val newPendingParticipants = event.pendingParticipants.minus(usersToRemove.toSet()) + + Firebase.firestore + .collection(EVENT_COLLECTION) + .document(eventID) + .update(PARTICIPANTS, newParticipants.toList()) + + Firebase.firestore + .collection(EVENT_COLLECTION) + .document(eventID) + .update(PENDING_PARTICIPANTS, newPendingParticipants.toList()) + + // Update user's joinedEvents field (again, check that the user is not the creator before + // removing them) + usersToRemove.forEach { uid -> + if (event.creator != uid) UserRepository.leaveEvent(uid, eventID) + } + } + /** * This function adds an event to the database * @@ -168,14 +222,15 @@ class EventRepository private constructor() { } /** - * This function removes an event from the database + * This function removes an event from the database caution: function doesn't update any other + * fields, like for instance user's joinedEvents field * - * @param uid The event ID + * @param eid The event ID */ - fun removeEvent(uid: String) { + fun removeEvent(eid: String) { Firebase.firestore .collection(EVENT_COLLECTION) - .document(uid) + .document(eid) .delete() .addOnSuccessListener { Log.d(TAG, "DocumentSnapshot successfully deleted!") } .addOnFailureListener { e -> Log.w(TAG, "Error deleting document", e) } diff --git a/app/src/main/java/com/github/se/gomeet/model/repository/UserRepository.kt b/app/src/main/java/com/github/se/gomeet/model/repository/UserRepository.kt index d3df7f0c..13841e83 100644 --- a/app/src/main/java/com/github/se/gomeet/model/repository/UserRepository.kt +++ b/app/src/main/java/com/github/se/gomeet/model/repository/UserRepository.kt @@ -97,6 +97,30 @@ class UserRepository private constructor() { .addOnFailureListener { e -> Log.w(TAG, "Error updating document", e) } } + /** + * Function that removes user uid from all other users' following and followers lists. + * + * @param uid the user id + */ + fun removeFollowersFollowing(uid: String) { + getAllUsers { users -> + users.forEach { user -> + if (user.uid != uid) { + val newFollowing = user.following.filter { f -> f != uid } + val newFollowers = user.followers.filter { f -> f != uid } + Firebase.firestore + .collection(USERS_COLLECTION) + .document(user.uid) + .update(FOLLOWING, newFollowing) + Firebase.firestore + .collection(USERS_COLLECTION) + .document(user.uid) + .update(FOLLOWERS, newFollowers) + } + } + } + } + /** * Upload a user profile image to Firebase Storage and return the download URL. * @@ -122,7 +146,8 @@ class UserRepository private constructor() { } /** - * Remove a user from the database. + * Remove a user from the database. Caution: This function doesn't update ant lists that need to + * be updated as a consequence of the deletion. * * @param uid the user id */ @@ -135,6 +160,13 @@ class UserRepository private constructor() { .addOnFailureListener { e -> Log.w(TAG, "Error deleting document", e) } } + /** + * Function to update a user's rating + * + * @param userID the user id + * @param newRating the new rating + * @param oldRating the old rating + */ suspend fun updateUserRating(userID: String, newRating: Long, oldRating: Long) { if (oldRating == newRating || @@ -166,6 +198,51 @@ class UserRepository private constructor() { "Updating rating of user($oldRating to $newRating) ${userID} from ${oldUserRating} to ${newUserRating}") } + /** + * Remove the mentioned favourite events from the user's favourite events list. + * + * @param userID the user id + * @param eventID the event id (defaults to "") + * @param eventIDs the list of event ids (defaults to emptyList()) + */ + suspend fun removeFavouriteEvents( + userID: String, + eventID: String = "", + eventIDs: List = emptyList() + ) { + val eventsToRemove = + (if (eventID == "" || eventIDs.contains(eventID)) eventIDs else eventIDs.plus(eventID)) + .toSet() + if (eventsToRemove.isEmpty()) { + Log.w(TAG, "Can't remove favourites for user $userID: called with no events to remove") + return + } + val user = Firebase.firestore.collection(USERS_COLLECTION).document(userID).get().await() + val myFavorites = user.get(MY_FAVORITES) as List + val newMyFavorites = myFavorites.toSet().minus(eventsToRemove).toList() + Firebase.firestore + .collection(USERS_COLLECTION) + .document(userID) + .update(MY_FAVORITES, newMyFavorites) + } + + /** + * Removes eventID from user userID's joined events list. Caution: no checks are performed + * (don't call this function if the user created the event) + * + * @param userID the user id + * @param eventID the event id + */ + suspend fun leaveEvent(userID: String, eventID: String) { + val user = Firebase.firestore.collection(USERS_COLLECTION).document(userID).get().await() + val joinedEvents = user.get(JOINED_EVENTS) as List + val newJoinedEvents = joinedEvents.toSet().minus(eventID).toList() + Firebase.firestore + .collection(USERS_COLLECTION) + .document(userID) + .update(JOINED_EVENTS, newJoinedEvents) + } + /** * Convert a GoMeetUser to a map. * diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/Trends.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/Trends.kt index 69230e6a..97dde850 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/Trends.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/Trends.kt @@ -106,7 +106,11 @@ fun Trends( } else { Log.e(TAG, "Current user is null") } - eventList.addAll(eventViewModel.getAllEvents()!!.filter { it.display(currentUserId) }) + eventViewModel.getAllEvents { events -> + if (events != null) { + eventList.addAll(events.filter { it.display(currentUserId) }) + } + } eventsLoaded.value = true } diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/Events.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/Events.kt index 0ce60779..6399dd8f 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/Events.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/Events.kt @@ -93,17 +93,19 @@ fun Events(nav: NavigationActions, userViewModel: UserViewModel, eventViewModel: LaunchedEffect(Unit) { coroutineScope.launch { user.value = userViewModel.getUser(currentUID) - // Log.d(TAG, "User is ${user.value!!.username} with ${user.value!!.myEvents.size} events") - val allEvents = (eventViewModel.getAllEvents() ?: emptyList()) - eventList.addAll( - allEvents.filter { e -> - (user.value!!.myEvents.contains(e.eventID) || - user.value!!.myFavorites.contains(e.eventID) || - user.value!!.joinedEvents.contains(e.eventID)) && !e.isPastEvent() - }) - - Log.d(TAG, "Displaying ${eventList.size} events out of ${allEvents.size} total events") - eventsLoaded.value = true + val allEvents = mutableListOf() + eventViewModel.getAllEvents { events -> + events?.forEach { e -> + allEvents.add(e) + if ((user.value!!.myEvents.contains(e.eventID) || + user.value!!.myFavorites.contains(e.eventID) || + user.value!!.joinedEvents.contains(e.eventID)) && !e.isPastEvent()) { + eventList.add(e) + } + } + eventsLoaded.value = true + Log.d(TAG, "Displaying ${eventList.size} events out of ${allEvents.size} total events") + } } } diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt index 79704975..3ddd5a1b 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt @@ -3,7 +3,6 @@ package com.github.se.gomeet.ui.mainscreens.events import android.graphics.Bitmap import android.graphics.BitmapFactory import android.util.Log -import androidx.compose.foundation.background import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column @@ -368,7 +367,7 @@ fun MyEventInfo( allUsers!!.forEach { user -> if (user.myFavorites.contains(eventId)) { - userViewModel.removeFavoriteEvent(eventId, user.uid) + userViewModel.removeFavouriteEvent(eventId) } } diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/explore/Explore.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/explore/Explore.kt index 67daf63b..ee24afe5 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/explore/Explore.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/explore/Explore.kt @@ -89,10 +89,15 @@ fun Explore(nav: NavigationActions, eventViewModel: EventViewModel) { locationPermissionLauncher.launch(locationPermissions) } val currentUser = eventViewModel.currentUID!! - val allEvents = eventViewModel.getAllEvents() - if (allEvents != null) { - eventList.value = allEvents.filter { e -> e.display(currentUser) } - nonFilteredEvents.value = allEvents.filter { e -> e.display(currentUser) } + val allEvents = mutableListOf() + eventViewModel.getAllEvents { events -> + events?.forEach { e -> + allEvents.add(e) + if (e.display(currentUser)) { + eventList.value = eventList.value.plus(e) + nonFilteredEvents.value = nonFilteredEvents.value.plus(e) + } + } } // wait for user input diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfile.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfile.kt index 316080f2..19b3fd10 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfile.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfile.kt @@ -95,10 +95,20 @@ fun OthersProfile( isFollowing = viewedUser.value?.followers?.contains(currentUID) ?: false followerCount = viewedUser.value?.followers?.size ?: 0 - val allEvents = - (eventViewModel.getAllEvents() ?: emptyList()).filter { e -> - viewedUser.value!!.joinedEvents.contains(e.eventID) && e.public + val allEvents = mutableListOf() + eventViewModel.getAllEvents { events -> + events?.forEach { e -> + if (viewedUser.value!!.joinedEvents.contains(e.eventID) && e.public) { + allEvents.add(e) + if (e.isPastEvent()) { + myHistoryList.add(e) + } else { + joinedEventsList.add(e) + } } + } + } + allEvents.forEach { if (!it.isPastEvent()) { joinedEventsList.add(it) diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/Profile.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/Profile.kt index 0aae8c0c..85942c77 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/Profile.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/Profile.kt @@ -103,18 +103,20 @@ fun Profile(nav: NavigationActions, userViewModel: UserViewModel, eventViewModel LaunchedEffect(Unit) { coroutineScope.launch { currentUser = userViewModel.getUser(userId) - val allEvents = - (eventViewModel.getAllEvents() ?: emptyList()).filter { e -> - currentUser!!.joinedEvents.contains(e.eventID) + val allEvents = mutableListOf() + eventViewModel.getAllEvents { events -> + events?.forEach { e -> + if (currentUser!!.joinedEvents.contains(e.eventID)) { + allEvents.add(e) + if (!e.isPastEvent()) { + joinedEventsList.add(e) + } else { + myHistoryList.add(e) + } } - allEvents.forEach { - if (!it.isPastEvent()) { - joinedEventsList.add(it) - } else { - myHistoryList.add(it) } + isProfileLoaded = true } - isProfileLoaded = true } } Scaffold( diff --git a/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt b/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt index e7fd5694..734b97c1 100644 --- a/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt +++ b/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt @@ -19,6 +19,7 @@ import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.Post import com.github.se.gomeet.model.event.location.Location import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.model.user.GoMeetUser import com.google.android.gms.maps.model.BitmapDescriptor import com.google.android.gms.maps.model.BitmapDescriptorFactory @@ -146,13 +147,13 @@ class EventViewModel(val currentUID: String? = null) : ViewModel() { /** * Get an event by its UID. * - * @param uid the UID of the event to get + * @param eid the UID of the event to get * @return the event with the given UID, or null if it does not exist */ - suspend fun getEvent(uid: String): Event? { + suspend fun getEvent(eid: String): Event? { return try { val event = CompletableDeferred() - EventRepository.getEvent(uid) { t -> event.complete(t) } + EventRepository.getEvent(eid) { t -> event.complete(t) } event.await() } catch (e: Exception) { null @@ -211,14 +212,29 @@ class EventViewModel(val currentUID: String? = null) : ViewModel() { ?: throw Exception("Failed to upload image and retrieve URL") } - /** Get all events that exist in the database. */ - suspend fun getAllEvents(): List? { - return try { + /** + * Get all events that exist in the database. Also removes phantom favourite events from the + * current user's favourites list. Caution: will throw null pointer exception if currentUID is + * null (this function shouldn't get called as long as no user has signed in). + * + * @param callback the function to call with the list of events. + */ + fun getAllEvents(callback: (List?) -> Unit) { + viewModelScope.launch { val event = CompletableDeferred?>() EventRepository.getAllEvents { t -> event.complete(t) } - event.await() - } catch (e: Exception) { - null + val events = event.await() + callback(events) + + // Prevent favourite events from being displayed if they are not in the database + if (events != null) { + val illegalEvents = mutableSetOf() + UserRepository.getUser(currentUID!!) { user -> + if (user == null) return@getUser + illegalEvents.addAll(user.myFavorites.toSet().minus(events.map { it.eventID }.toSet())) + } + UserRepository.removeFavouriteEvents(currentUID, eventIDs = illegalEvents.toList()) + } } } @@ -346,7 +362,18 @@ class EventViewModel(val currentUID: String? = null) : ViewModel() { */ fun removeEvent(eventID: String) { lastLoadedEvents = lastLoadedEvents.filter { it.eventID != eventID } - EventRepository.removeEvent(eventID) + viewModelScope.launch { + val event = getEvent(eventID) + if (event == null) { + Log.e(TAG, "Event with ID $eventID couldn't be found to delete") + return@launch + } + event.participants.forEach { participant -> EventRepository.leaveEvent(eventID, participant) } + event.pendingParticipants.forEach { pendingParticipant -> + cancelInvitation(event, pendingParticipant) + } + EventRepository.removeEvent(eventID) + } } /** @@ -368,15 +395,11 @@ class EventViewModel(val currentUID: String? = null) : ViewModel() { * Update the event participants field by removing the given user to the list. Note that this * function should be called at the same time as the equivalent function in the UserViewModel. * - * @param event the event to update + * @param eventID the ID of the event to update * @param userId the ID of the user to remove from the event */ - fun leaveEvent(event: Event, userId: String) { - if (!event.participants.contains(userId)) { - Log.w(TAG, "User $userId is has never joined event ${event.eventID}") - return - } - editEvent(event.copy(participants = event.participants.minus(userId))) + fun leaveEvent(eventID: String, userId: String) { + viewModelScope.launch { EventRepository.leaveEvent(eventID, userId) } } /** diff --git a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt index c95dbf04..377c1fad 100644 --- a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt @@ -8,6 +8,7 @@ import androidx.lifecycle.viewModelScope import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.Invitation import com.github.se.gomeet.model.event.InviteStatus +import com.github.se.gomeet.model.repository.EventRepository import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.model.user.GoMeetUser import java.util.UUID @@ -155,12 +156,23 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { } /** - * Delete the user. + * Delete the user. It removes all the events the user creates. leaves the events the user joined, + * clears following/following lists and cancels pending invitations. * - * @param uid the user id + * @param uid the user id (optional, by default it is the current user id) */ - fun deleteUser(uid: String) { - UserRepository.removeUser(uid) + fun deleteUser(uid: String = currentUID!!) { + viewModelScope.launch { + val user = getUser(uid) + if (user == null) { + Log.e(TAG, "Unable to delete user $uid: not found") + return@launch + } + user.myEvents.forEach { eventId -> EventRepository.removeEvent(eventId) } + UserRepository.removeFollowersFollowing(uid) + user.joinedEvents.forEach { eventID -> EventRepository.leaveEvent(eventID, uid) } + UserRepository.removeUser(uid) + } } /** @@ -189,21 +201,6 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { } } - /** - * Leave an event. - * - * @param eventId The id of the event to leave. - * @param userId The id of the user leaving the event. - */ - suspend fun leaveEvent(eventId: String, userId: String = currentUID!!) { - try { - val goMeetUser = getUser(userId)!! - editUser(goMeetUser.copy(joinedEvents = goMeetUser.joinedEvents.minus(eventId))) - } catch (e: Exception) { - Log.w(TAG, "Couldn't join the event", e) - } - } - /** * User creates an event and adds it to their list of myEvents. It is used when a user creates an * event. @@ -239,16 +236,16 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { /** * Removes the given event from the user's list of favorites. * - * @param eventId The id of the event to remove from favorites. * @param userId The id of the user for which we remove the event from favorites. + * @param eventId The id of the event to remove from favorites. + * @param eventIds The list of event IDs to remove from favorites. */ - suspend fun removeFavoriteEvent(eventId: String, userId: String = currentUID!!) { - try { - val goMeetUser = getUser(userId)!! - editUser(goMeetUser.copy(myFavorites = goMeetUser.myFavorites.minus(eventId))) - } catch (e: Exception) { - Log.w(TAG, "Couldn't remove the event from favorites", e) - } + fun removeFavouriteEvent( + userId: String = currentUID!!, + eventId: String = "", + eventIds: List = emptyList() + ) { + viewModelScope.launch { UserRepository.removeFavouriteEvents(userId, eventId, eventIds) } } /** From 12fb71c7811b4d7d13d8b0fc1a268badfa5943ba Mon Sep 17 00:00:00 2001 From: DanThePol Date: Sun, 2 Jun 2024 18:25:35 +0200 Subject: [PATCH 2/5] Updated function signatures in tests + ktfmtFormat --- .../java/com/github/se/gomeet/endtoend/EndToEnd.kt | 4 ++-- .../com/github/se/gomeet/endtoend/EndToEnd2.kt | 5 ++--- .../com/github/se/gomeet/endtoend/EndToEnd3.kt | 4 ++-- .../se/gomeet/ui/mainscreens/SearchModuleTest.kt | 4 ++-- .../ui/mainscreens/create/CreateEventTest.kt | 6 ++---- .../gomeet/ui/mainscreens/events/EditEventTest.kt | 11 +++-------- .../se/gomeet/ui/mainscreens/events/EventsTest.kt | 4 ++-- .../ui/mainscreens/profile/ProfileEventListTest.kt | 11 +++++------ .../se/gomeet/viewmodel/EventViewModelTest.kt | 14 ++++++++++---- .../se/gomeet/viewmodel/UserViewModelTest.kt | 5 +++-- .../github/se/gomeet/viewmodel/UserViewModel.kt | 11 +++++++++++ 11 files changed, 44 insertions(+), 35 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt index 8e114295..63f7778f 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt @@ -87,8 +87,8 @@ class EndToEndTest : TestCase() { @JvmStatic fun tearDown() = runBlocking { - // Clean up the event - eventVM.getAllEvents()?.forEach { eventVM.removeEvent(it.eventID) } + // Clean up the events + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } // Clean up the user Firebase.auth.currentUser?.delete()?.await() userVM.deleteUser(uid) diff --git a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt index cf51b811..fe1ee03d 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt @@ -170,9 +170,8 @@ class EndToEndTest2 : TestCase() { @AfterClass @JvmStatic fun tearDown() = runBlocking { - // clean up the event - eventVM.getAllEvents()?.forEach { eventVM.removeEvent(it.eventID) } - + // clean up the events + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } // clean up the users Firebase.auth.currentUser?.delete()?.await() userVM.deleteUser(uid1) diff --git a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt index 3b1b8935..78c47b51 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt @@ -155,8 +155,8 @@ class EndToEndTest3 : TestCase() { @JvmStatic fun tearDown() = runBlocking { - // clean up the event - eventVM.getAllEvents()?.forEach { eventVM.removeEvent(it.eventID) } + // clean up the events + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } // clean up the users Firebase.auth.currentUser?.delete()?.await() diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt index 5ac27294..72761dfe 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt @@ -105,8 +105,8 @@ class SearchModuleTest { @AfterClass @JvmStatic fun tearDown() = runBlocking { - // Clean up the event - eventVM.getAllEvents()?.forEach { eventVM.removeEvent(it.eventID) } + // Clean up the events + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } // Clean up the user Firebase.auth.currentUser?.delete()?.await() diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt index 8029639a..ce409116 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt @@ -37,14 +37,12 @@ class CreateEventTest { fun tearDown() = runBlocking { // Clean up the events - eventVM.getAllEvents()?.forEach { eventVM.removeEvent(it.eventID) } - - return@runBlocking + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } } } @Test - fun testCratePrivateEvent() { + fun testCreatePrivateEvent() { val eventVM = EventViewModel(uid) composeTestRule.setContent { diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt index 8a45da22..da95d451 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt @@ -18,7 +18,6 @@ import com.github.se.gomeet.viewmodel.UserViewModel import io.github.kakaocup.kakao.common.utilities.getResourceString import java.time.LocalDate import java.time.LocalTime -import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import org.junit.AfterClass import org.junit.BeforeClass @@ -57,18 +56,14 @@ class EditEventTest { UserViewModel(""), "eventid") - while (eventVM.getAllEvents()!!.isEmpty()) { - TimeUnit.SECONDS.sleep(1) - } - - eventUid = eventVM.getAllEvents()!![0].eventID + eventVM.getAllEvents { if (it != null) eventUid = it[0].eventID } } @AfterClass @JvmStatic fun tearDown() = runBlocking { - // Clean up the event - eventVM.getAllEvents()?.forEach { eventVM.removeEvent(it.eventID) } + // Clean up the events + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt index bed6d336..be8beb11 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt @@ -87,8 +87,8 @@ class EventsTest { @AfterClass @JvmStatic fun tearDown() = runBlocking { - // Clean up the event - eventVM.getAllEvents()?.forEach { eventVM.removeEvent(it.eventID) } + // Clean up the events + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } // Clean up the user Firebase.auth.currentUser?.delete()?.await() diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt index 0bc7bc15..b96077cb 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt @@ -94,13 +94,12 @@ class ProfileEventListTest { @JvmStatic fun tearDown() = runBlocking { // Clean up the event - eventVM.getAllEvents()?.forEach { - eventVM.removeEvent(it.eventID) + eventVM.getAllEvents { it?.forEach { eventVM.removeEvent(it.eventID) } } + + // Clean up the user + Firebase.auth.currentUser?.delete() + userVM.deleteUser(uid) - // Clean up the user - Firebase.auth.currentUser?.delete() - userVM.deleteUser(uid) - } return@runBlocking } } diff --git a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt index b629487a..6094936e 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt @@ -2,6 +2,7 @@ package com.github.se.gomeet.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.model.Tag +import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location import com.github.se.gomeet.model.user.GoMeetUser import java.time.LocalDate @@ -77,7 +78,7 @@ class EventViewModelTest { @JvmStatic fun tearDown() { // Clean up the events - runBlocking { eventVM.getAllEvents()!!.forEach { eventVM.removeEvent(it.eventID) } } + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } } } @@ -180,7 +181,8 @@ class EventViewModelTest { TimeUnit.SECONDS.sleep(1) - val events = eventViewModel.getAllEvents()!!.toMutableList() + val events = mutableListOf() + eventViewModel.getAllEvents { if (it != null) events.addAll(it) } EventViewModel.sortEvents(currentUser.tags, events) assert(events[0].eventID == eid3) assert(events[1].eventID == eventId) @@ -197,7 +199,11 @@ class EventViewModelTest { @Test fun getAllEventsTest() { - runBlocking { assert(eventVM.getAllEvents()!!.any { it.eventID == eventId }) } + runBlocking { + val events = mutableListOf() + eventVM.getAllEvents { if (it != null) events.addAll(it) } + assert(events.any { it.eventID == eventId }) + } } @Test @@ -298,7 +304,7 @@ class EventViewModelTest { runBlocking { eventVM.joinEvent(eventVM.getEvent(eventId)!!, userId) } // Make the user leave the event - runBlocking { eventVM.leaveEvent(eventVM.getEvent(eventId)!!, userId) } + runBlocking { eventVM.leaveEvent(eventId, userId) } // Make sure that the event participants list is empty runBlocking { assert(!eventVM.getEvent(eventId)!!.participants.any { it == userId }) } diff --git a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt index 30a46a6f..83716d28 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt @@ -78,7 +78,8 @@ class UserViewModelTest { fun tearDown() = runBlocking { // Clean up the user userVM.deleteUser(uid) - eventVM.getAllEvents()!!.forEach { eventVM.removeEvent(it.eventID) } + // Clean up the events + eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } } } @@ -216,7 +217,7 @@ class UserViewModelTest { runBlocking { userVM.editUser(user.copy(myFavorites = listOf(eventId))) } // Remove it from the user's favorites - runBlocking { userVM.removeFavoriteEvent(eventId, uid) } + runBlocking { userVM.removeFavouriteEvent(eventId, uid) } // Make sure that the user's favorites list is empty runBlocking { assert(userVM.getUser(uid)!!.myFavorites.isEmpty()) } diff --git a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt index 377c1fad..3984d323 100644 --- a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt @@ -233,6 +233,17 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { } } + /** + * Function to leave an event. + * + * @param eventId The id of the event to leave. + * @param userId The id of the user leaving the event (optional, by default it is the current user + * id). + */ + fun leaveEvent(eventId: String, userId: String = currentUID!!) { + viewModelScope.launch { EventRepository.leaveEvent(eventId, userId) } + } + /** * Removes the given event from the user's list of favorites. * From 87c390a52fdd144fedc70dc447889ab5703a577c Mon Sep 17 00:00:00 2001 From: DanThePol Date: Sun, 2 Jun 2024 22:04:42 +0200 Subject: [PATCH 3/5] Fixed tests --- .../com/github/se/gomeet/endtoend/EndToEnd.kt | 19 +++++-- .../github/se/gomeet/endtoend/EndToEnd2.kt | 19 +++++-- .../github/se/gomeet/endtoend/EndToEnd3.kt | 20 +++++-- .../gomeet/ui/mainscreens/SearchModuleTest.kt | 18 +++++-- .../ui/mainscreens/create/CreateEventTest.kt | 15 +++++- .../ui/mainscreens/events/EditEventTest.kt | 25 ++++++++- .../ui/mainscreens/events/EventsTest.kt | 19 +++++-- .../ui/mainscreens/profile/AddFriendTest.kt | 7 +-- .../ui/mainscreens/profile/EditProfileTest.kt | 3 +- .../mainscreens/profile/NotificationsTest.kt | 6 ++- .../mainscreens/profile/OthersProfileTest.kt | 5 +- .../profile/ProfileEventListTest.kt | 17 ++++-- .../ui/mainscreens/profile/ProfileTest.kt | 3 +- .../se/gomeet/ui/navigation/NavigationTest.kt | 5 +- .../se/gomeet/viewmodel/EventViewModelTest.kt | 50 +++++++++++++---- .../se/gomeet/viewmodel/UserViewModelTest.kt | 53 ++++++++++++++++--- .../se/gomeet/viewmodel/EventViewModel.kt | 12 +++-- .../se/gomeet/viewmodel/UserViewModel.kt | 21 ++++++-- 18 files changed, 257 insertions(+), 60 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt index 63f7778f..68ad8192 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd.kt @@ -14,6 +14,9 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.GrantPermissionRule import com.github.se.gomeet.MainActivity import com.github.se.gomeet.R +import com.github.se.gomeet.model.event.Event +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.screens.CreateEventScreen import com.github.se.gomeet.screens.CreateScreen import com.github.se.gomeet.screens.EventsScreen @@ -29,6 +32,7 @@ import com.google.firebase.ktx.Firebase import com.kaspersky.kaspresso.testcases.api.testcase.TestCase import io.github.kakaocup.compose.node.element.ComposeScreen import io.github.kakaocup.kakao.common.utilities.getResourceString +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await @@ -88,11 +92,18 @@ class EndToEndTest : TestCase() { fun tearDown() = runBlocking { // Clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } - // Clean up the user + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } // Clean up the user + UserRepository.removeUser(uid) Firebase.auth.currentUser?.delete()?.await() - userVM.deleteUser(uid) - return@runBlocking } } diff --git a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt index fe1ee03d..6d9807b8 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd2.kt @@ -16,7 +16,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.GrantPermissionRule import com.github.se.gomeet.MainActivity import com.github.se.gomeet.R +import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.screens.EventInfoScreen import com.github.se.gomeet.screens.ExploreScreen import com.github.se.gomeet.screens.FollowScreen @@ -35,6 +38,7 @@ import io.github.kakaocup.compose.node.element.ComposeScreen import io.github.kakaocup.kakao.common.utilities.getResourceString import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await @@ -171,11 +175,20 @@ class EndToEndTest2 : TestCase() { @JvmStatic fun tearDown() = runBlocking { // clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } // clean up the users Firebase.auth.currentUser?.delete()?.await() - userVM.deleteUser(uid1) - userVM.deleteUser(uid2) + UserRepository.removeUser(uid1) + UserRepository.removeUser(uid2) Firebase.auth.signInWithEmailAndPassword(email1, pwd1).await() Firebase.auth.currentUser?.delete()?.await() diff --git a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt index 78c47b51..3b63c17d 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/endtoend/EndToEnd3.kt @@ -18,7 +18,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.rule.GrantPermissionRule import com.github.se.gomeet.MainActivity import com.github.se.gomeet.R +import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.screens.AddParticipantsScreen import com.github.se.gomeet.screens.CreateEventScreen import com.github.se.gomeet.screens.CreateScreen @@ -38,6 +41,7 @@ import io.github.kakaocup.compose.node.element.ComposeScreen import io.github.kakaocup.kakao.common.utilities.getResourceString import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await @@ -156,12 +160,20 @@ class EndToEndTest3 : TestCase() { fun tearDown() = runBlocking { // clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } - + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } // clean up the users + UserRepository.removeUser(uid1) + UserRepository.removeUser(uid2) Firebase.auth.currentUser?.delete()?.await() - userVM.deleteUser(uid1) - userVM.deleteUser(uid2) Firebase.auth.signInWithEmailAndPassword(email2, pwd2).await() Firebase.auth.currentUser?.delete()?.await() diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt index 72761dfe..aeba77a1 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/SearchModuleTest.kt @@ -15,7 +15,10 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.platform.app.InstrumentationRegistry import androidx.test.uiautomator.UiDevice import com.github.se.gomeet.R +import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventViewModel import com.github.se.gomeet.viewmodel.UserViewModel @@ -24,6 +27,7 @@ import com.google.firebase.ktx.Firebase import io.github.kakaocup.kakao.common.utilities.getResourceString import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await @@ -105,12 +109,20 @@ class SearchModuleTest { @AfterClass @JvmStatic fun tearDown() = runBlocking { + UserRepository.removeUser(uid) // Clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } - + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } // Clean up the user Firebase.auth.currentUser?.delete()?.await() - userVM.deleteUser(uid) return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt index ce409116..add9d9b8 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/create/CreateEventTest.kt @@ -11,11 +11,15 @@ import androidx.compose.ui.test.performTextInput import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R +import com.github.se.gomeet.model.event.Event +import com.github.se.gomeet.model.repository.EventRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventCreationViewModel import com.github.se.gomeet.viewmodel.EventViewModel import com.github.se.gomeet.viewmodel.UserViewModel import io.github.kakaocup.kakao.common.utilities.getResourceString +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import org.junit.AfterClass import org.junit.Rule @@ -37,7 +41,16 @@ class CreateEventTest { fun tearDown() = runBlocking { // Clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } } } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt index da95d451..c07bff46 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EditEventTest.kt @@ -11,13 +11,18 @@ import androidx.compose.ui.test.performScrollTo import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R +import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventViewModel import com.github.se.gomeet.viewmodel.UserViewModel import io.github.kakaocup.kakao.common.utilities.getResourceString import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import org.junit.AfterClass import org.junit.BeforeClass @@ -31,11 +36,16 @@ class EditEventTest { companion object { private lateinit var eventUid: String - private val eventVM = EventViewModel("") + private val uid = "EditEventTestUserUid" + private val eventVM = EventViewModel(uid) + private val userVM = UserViewModel(uid) @JvmStatic @BeforeClass fun setup() = runBlocking { + // Create a user + userVM.createUserIfNew(uid, "uname", "fn", "ln", "email", "phone", "country") + // Create an event eventVM.createEvent( "title", @@ -62,8 +72,19 @@ class EditEventTest { @AfterClass @JvmStatic fun tearDown() = runBlocking { + // Clean up the user + UserRepository.removeUser(uid) // Clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt index be8beb11..4e2ade72 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/events/EventsTest.kt @@ -5,7 +5,10 @@ import androidx.compose.ui.test.junit4.createComposeRule import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R +import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventViewModel import com.github.se.gomeet.viewmodel.UserViewModel @@ -14,6 +17,8 @@ import com.google.firebase.ktx.Firebase import io.github.kakaocup.kakao.common.utilities.getResourceString import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch +import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await import org.junit.AfterClass @@ -88,11 +93,19 @@ class EventsTest { @JvmStatic fun tearDown() = runBlocking { // Clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } - + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } // Clean up the user + UserRepository.removeUser(uid) Firebase.auth.currentUser?.delete()?.await() - userVM.deleteUser(uid) return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/AddFriendTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/AddFriendTest.kt index d7ab6efc..fb2fa6d5 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/AddFriendTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/AddFriendTest.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.test.performClick import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.UserViewModel import com.google.firebase.auth.ktx.auth @@ -75,9 +76,9 @@ class AddFriendTest { fun tearDown() = runBlocking { // Clean up the users - Firebase.auth.currentUser?.delete() - userVM.deleteUser(uid1) - userVM.deleteUser(uid2) + UserRepository.removeUser(uid1) + UserRepository.removeUser(uid2) + Firebase.auth.currentUser?.delete()?.await() return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/EditProfileTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/EditProfileTest.kt index 00833796..5528f99e 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/EditProfileTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/EditProfileTest.kt @@ -12,6 +12,7 @@ import androidx.compose.ui.test.performTextInput import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.UserViewModel import com.google.firebase.auth.ktx.auth @@ -58,8 +59,8 @@ class EditProfileTest { @JvmStatic fun tearDown() = runBlocking { // Clean up the user + UserRepository.removeUser(uid) Firebase.auth.currentUser!!.delete().await() - userVM.deleteUser(uid) return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/NotificationsTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/NotificationsTest.kt index 8aa0f365..423b0e9b 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/NotificationsTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/NotificationsTest.kt @@ -11,6 +11,8 @@ import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.mainscreens.notifications.Notifications import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventViewModel @@ -92,8 +94,8 @@ class NotificationsTest { @JvmStatic fun tearDown() = runBlocking { // Clean up the user and the event - userVM.deleteUser(uid1) - eventVM.removeEvent(eventId) + UserRepository.removeUser(uid1) + EventRepository.removeEvent(eventId) return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfileTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfileTest.kt index d7a7ae75..c7ddd0e7 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfileTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/OthersProfileTest.kt @@ -14,6 +14,7 @@ import androidx.compose.ui.test.performClick import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventViewModel import com.github.se.gomeet.viewmodel.UserViewModel @@ -95,8 +96,8 @@ class OthersProfileTest { fun tearDown() = runBlocking { // clean up the users Firebase.auth.currentUser?.delete()?.await() - userVM.deleteUser(uid1) - userVM.deleteUser(uid2) + UserRepository.removeUser(uid1) + UserRepository.removeUser(uid2) Firebase.auth.signInWithEmailAndPassword(email2, pwd2).await() Firebase.auth.currentUser?.delete()?.await() return@runBlocking diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt index b96077cb..a844a77b 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileEventListTest.kt @@ -12,6 +12,8 @@ import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventViewModel import com.github.se.gomeet.viewmodel.UserViewModel @@ -19,6 +21,7 @@ import com.google.firebase.auth.ktx.auth import com.google.firebase.ktx.Firebase import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await @@ -93,12 +96,20 @@ class ProfileEventListTest { @AfterClass @JvmStatic fun tearDown() = runBlocking { + UserRepository.removeUser(uid) // Clean up the event - eventVM.getAllEvents { it?.forEach { eventVM.removeEvent(it.eventID) } } - + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } // Clean up the user Firebase.auth.currentUser?.delete() - userVM.deleteUser(uid) return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileTest.kt index ea837388..33f0ac1b 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/ProfileTest.kt @@ -13,6 +13,7 @@ import androidx.compose.ui.test.performClick import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.viewmodel.EventViewModel import com.github.se.gomeet.viewmodel.UserViewModel @@ -59,8 +60,8 @@ class ProfileTest { @JvmStatic fun tearDown() = runBlocking { // Clean up the user + UserRepository.removeUser(uid) Firebase.auth.currentUser!!.delete().await() - userVM.deleteUser(uid) return@runBlocking } diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/navigation/NavigationTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/navigation/NavigationTest.kt index 818776a4..15afaff2 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/navigation/NavigationTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/navigation/NavigationTest.kt @@ -6,6 +6,7 @@ import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.ui.mainscreens.Trends import com.github.se.gomeet.ui.mainscreens.create.Create import com.github.se.gomeet.ui.mainscreens.events.Events @@ -129,10 +130,10 @@ class NavigationTest { @AfterClass @JvmStatic - fun tearDown() = runBlocking { + fun tearDown(): Unit = runBlocking { // Clean up the user view model + UserRepository.removeUser(currentUserId) Firebase.auth.currentUser!!.delete().await() - userVM.deleteUser(currentUserId) } } } diff --git a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt index 6094936e..75971557 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/EventViewModelTest.kt @@ -4,9 +4,12 @@ import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.model.Tag import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.model.user.GoMeetUser import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.runTest @@ -77,8 +80,16 @@ class EventViewModelTest { @AfterClass @JvmStatic fun tearDown() { - // Clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } } } @@ -182,14 +193,21 @@ class EventViewModelTest { TimeUnit.SECONDS.sleep(1) val events = mutableListOf() - eventViewModel.getAllEvents { if (it != null) events.addAll(it) } + val latch = CountDownLatch(1) + eventViewModel.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) EventViewModel.sortEvents(currentUser.tags, events) assert(events[0].eventID == eid3) assert(events[1].eventID == eventId) assert(events[2].eventID == eid1) assert(events[3].eventID == eid2) - userViewModel.deleteUser(userID) + UserRepository.removeUser(userID) } @Test @@ -199,11 +217,17 @@ class EventViewModelTest { @Test fun getAllEventsTest() { - runBlocking { - val events = mutableListOf() - eventVM.getAllEvents { if (it != null) events.addAll(it) } - assert(events.any { it.eventID == eventId }) + val events = mutableListOf() + val latch = CountDownLatch(1) + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + // If it is null then there has been an error + } } + assert(latch.await(3, TimeUnit.SECONDS)) + assert(events.any { it.eventID == eventId }) } @Test @@ -300,14 +324,20 @@ class EventViewModelTest { fun leaveEventTest() { val userId = "uid6" + userVM.createUserIfNew( + userId, "EventViewModelTest", "firstName", "lastName", "email", "phoneNumber", "country") + // Make a user join the event runBlocking { eventVM.joinEvent(eventVM.getEvent(eventId)!!, userId) } // Make the user leave the event - runBlocking { eventVM.leaveEvent(eventId, userId) } - + val latch = CountDownLatch(1) + eventVM.leaveEvent(eventId, userId) { latch.countDown() } + assert(latch.await(3, TimeUnit.SECONDS)) // Make sure that the event participants list is empty runBlocking { assert(!eventVM.getEvent(eventId)!!.participants.any { it == userId }) } + + UserRepository.removeUser(userId) } @Test diff --git a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt index 83716d28..8928bc40 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/viewmodel/UserViewModelTest.kt @@ -3,9 +3,12 @@ package com.github.se.gomeet.viewmodel import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.model.event.Event import com.github.se.gomeet.model.event.location.Location +import com.github.se.gomeet.model.repository.EventRepository +import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.model.user.GoMeetUser import java.time.LocalDate import java.time.LocalTime +import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit import kotlinx.coroutines.runBlocking import org.junit.AfterClass @@ -42,7 +45,7 @@ class UserViewModelTest { private lateinit var event: Event private val userVM = UserViewModel(uid) - private val eventVM = EventViewModel(uid) + private var eventVM = EventViewModel(uid) @BeforeClass @JvmStatic @@ -70,16 +73,25 @@ class UserViewModelTest { TimeUnit.SECONDS.sleep(1) } event = eventVM.getEvent("testevent1")!! - TimeUnit.SECONDS.sleep(3) + TimeUnit.SECONDS.sleep(1) } @AfterClass @JvmStatic fun tearDown() = runBlocking { + val latch = CountDownLatch(1) // Clean up the user - userVM.deleteUser(uid) + UserRepository.removeUser(uid) // Clean up the events - eventVM.getAllEvents { events -> events?.forEach { eventVM.removeEvent(it.eventID) } } + val events = mutableListOf() + eventVM.getAllEvents { + if (it != null) { + events.addAll(it) + latch.countDown() + } + } + assert(latch.await(3, TimeUnit.SECONDS)) + events.forEach { event -> EventRepository.removeEvent(event.eventID) } } } @@ -185,14 +197,38 @@ class UserViewModelTest { fun leaveEventTest() { val eventId = "event4" + eventVM = EventViewModel("UserViewModelTestUser2") + eventVM.createEvent( + "title", + "description", + Location(0.0, 0.0, ""), + LocalDate.now(), + LocalTime.now(), + 0.0, + "", + emptyList(), + emptyList(), + emptyList(), + 1, + true, + emptyList(), + emptyList(), + null, + userVM, + eventId) // Join an event runBlocking { userVM.joinEvent(eventId, uid) } // Leave the event - runBlocking { userVM.leaveEvent(eventId, uid) } - + val latch = CountDownLatch(1) + userVM.leaveEvent(eventId, uid) { latch.countDown() } + assert(latch.await(3, TimeUnit.SECONDS)) // Verify that the user's joinedEvents list was correctly updated runBlocking { assert(!userVM.getUser(uid)!!.joinedEvents.any { it == eventId }) } + + eventVM.removeEvent(eventId) + TimeUnit.SECONDS.sleep(1) + eventVM = EventViewModel(uid) } @Test @@ -216,9 +252,10 @@ class UserViewModelTest { // Add an event to the user's favorites runBlocking { userVM.editUser(user.copy(myFavorites = listOf(eventId))) } + val latch = CountDownLatch(1) // Remove it from the user's favorites - runBlocking { userVM.removeFavouriteEvent(eventId, uid) } - + userVM.removeFavouriteEvent(uid, eventId) { latch.countDown() } + assert(latch.await(3, TimeUnit.SECONDS)) // Make sure that the user's favorites list is empty runBlocking { assert(userVM.getUser(uid)!!.myFavorites.isEmpty()) } } diff --git a/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt b/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt index 734b97c1..ecce56b9 100644 --- a/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt +++ b/app/src/main/java/com/github/se/gomeet/viewmodel/EventViewModel.kt @@ -359,8 +359,9 @@ class EventViewModel(val currentUID: String? = null) : ViewModel() { * Remove an event by its UID. * * @param eventID the ID of the event to remove + * @param callback the function to call after the event has been removed (optional) */ - fun removeEvent(eventID: String) { + fun removeEvent(eventID: String, callback: () -> Unit = {}) { lastLoadedEvents = lastLoadedEvents.filter { it.eventID != eventID } viewModelScope.launch { val event = getEvent(eventID) @@ -373,6 +374,7 @@ class EventViewModel(val currentUID: String? = null) : ViewModel() { cancelInvitation(event, pendingParticipant) } EventRepository.removeEvent(eventID) + callback() } } @@ -397,9 +399,13 @@ class EventViewModel(val currentUID: String? = null) : ViewModel() { * * @param eventID the ID of the event to update * @param userId the ID of the user to remove from the event + * @param callback the function to call after the user has left the event (optional) */ - fun leaveEvent(eventID: String, userId: String) { - viewModelScope.launch { EventRepository.leaveEvent(eventID, userId) } + fun leaveEvent(eventID: String, userId: String, callback: () -> Unit = {}) { + viewModelScope.launch { + EventRepository.leaveEvent(eventID, userId) + callback() + } } /** diff --git a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt index 3984d323..f781e283 100644 --- a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt @@ -160,8 +160,9 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { * clears following/following lists and cancels pending invitations. * * @param uid the user id (optional, by default it is the current user id) + * @param callback the callback to be executed after the user is deleted (optional) */ - fun deleteUser(uid: String = currentUID!!) { + fun deleteUser(uid: String = currentUID!!, callback: () -> Unit = {}) { viewModelScope.launch { val user = getUser(uid) if (user == null) { @@ -172,6 +173,7 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { UserRepository.removeFollowersFollowing(uid) user.joinedEvents.forEach { eventID -> EventRepository.leaveEvent(eventID, uid) } UserRepository.removeUser(uid) + callback() } } @@ -240,8 +242,11 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { * @param userId The id of the user leaving the event (optional, by default it is the current user * id). */ - fun leaveEvent(eventId: String, userId: String = currentUID!!) { - viewModelScope.launch { EventRepository.leaveEvent(eventId, userId) } + fun leaveEvent(eventId: String, userId: String = currentUID!!, callback: () -> Unit = {}) { + viewModelScope.launch { + EventRepository.leaveEvent(eventId, userId) + callback() + } } /** @@ -250,13 +255,19 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { * @param userId The id of the user for which we remove the event from favorites. * @param eventId The id of the event to remove from favorites. * @param eventIds The list of event IDs to remove from favorites. + * @param callback The callback to be executed after the event is removed from favorites + * (optional). */ fun removeFavouriteEvent( userId: String = currentUID!!, eventId: String = "", - eventIds: List = emptyList() + eventIds: List = emptyList(), + callback: () -> Unit = {} ) { - viewModelScope.launch { UserRepository.removeFavouriteEvents(userId, eventId, eventIds) } + viewModelScope.launch { + UserRepository.removeFavouriteEvents(userId, eventId, eventIds) + callback() + } } /** From ecf546f7a3b0be41d90a57dbc1a8c2aba842f314 Mon Sep 17 00:00:00 2001 From: DanThePol Date: Sun, 2 Jun 2024 22:37:37 +0200 Subject: [PATCH 4/5] Finalised user deletion with the UI. Repurposed existing function by Antoine to add confirmation dialogue to delete account and to sign out --- .../profile/settings/SettingsScreenTest.kt | 5 +- .../com/github/se/gomeet/InitFunctions.kt | 2 +- .../ui/mainscreens/events/MyEventInfo.kt | 78 +++++--- .../profile/settings/SettingsScreen.kt | 177 ++++++++++-------- .../se/gomeet/viewmodel/UserViewModel.kt | 3 + 5 files changed, 156 insertions(+), 109 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreenTest.kt b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreenTest.kt index 94af51fc..caf966c6 100644 --- a/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreenTest.kt +++ b/app/src/androidTest/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreenTest.kt @@ -12,6 +12,7 @@ import androidx.navigation.compose.rememberNavController import androidx.test.ext.junit.runners.AndroidJUnit4 import com.github.se.gomeet.R import com.github.se.gomeet.ui.navigation.NavigationActions +import com.github.se.gomeet.viewmodel.UserViewModel import io.github.kakaocup.kakao.common.utilities.getResourceString import org.junit.Rule import org.junit.Test @@ -23,7 +24,9 @@ class SettingsScreenTest { @Test fun testSettingsScreen() { - composeTestRule.setContent { SettingsScreen(NavigationActions(rememberNavController())) {} } + composeTestRule.setContent { + SettingsScreen(NavigationActions(rememberNavController()), UserViewModel()) {} + } composeTestRule.waitForIdle() diff --git a/app/src/main/java/com/github/se/gomeet/InitFunctions.kt b/app/src/main/java/com/github/se/gomeet/InitFunctions.kt index d5c384c4..e781cca9 100644 --- a/app/src/main/java/com/github/se/gomeet/InitFunctions.kt +++ b/app/src/main/java/com/github/se/gomeet/InitFunctions.kt @@ -352,7 +352,7 @@ fun InitNavigation(nav: NavHostController, client: ChatClient, applicationContex } composable(Route.SETTINGS) { - SettingsScreen(navAction) { + SettingsScreen(navAction, userViewModel.value) { logOut( navAction, LOGIN_ITEMS.first { it.route == startScreen }, diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt index b94d1b0c..c3c8d155 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/events/MyEventInfo.kt @@ -349,7 +349,7 @@ fun MyEventInfo( } if (showDeleteEventDialog) { - DeleteEventDialog( + ConfirmDialog( onConfirm = { coroutineScope.launch { myEvent.value!!.participants.forEach { participant -> @@ -376,7 +376,9 @@ fun MyEventInfo( nav.goBack() } }, - onDismiss = { showDeleteEventDialog = false }) + onDismiss = { showDeleteEventDialog = false }, + confirmationMessage = "Are you sure you want to delete this event?", + title = "Delete Event") } } } @@ -447,35 +449,49 @@ private fun MapViewComposable( * * @param onConfirm Callback function to be called when the user confirms the deletion * @param onDismiss Callback function to be called when the user dismisses the dialog + * @param confirmationMessage Message to be displayed in the dialog + * @param title Title of the dialog */ @Composable -fun DeleteEventDialog(onConfirm: () -> Unit, onDismiss: () -> Unit) { - AlertDialog( - onDismissRequest = onDismiss, - confirmButton = { - Button( - onClick = onConfirm, - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.outlineVariant)) { - Text("Confirm", color = Color.White) - } - }, - dismissButton = { - Button( - onClick = onDismiss, - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.primaryContainer)) { - Text("Cancel", color = MaterialTheme.colorScheme.onBackground) - } - }, - title = { Text("Delete Event") }, - text = { - Text( - "Are you sure you want to delete this event?", - modifier = Modifier.testTag("DeleteEventConfirmationText")) - }, - containerColor = MaterialTheme.colorScheme.background, - ) +fun ConfirmDialog( + onConfirm: () -> Unit, + onDismiss: () -> Unit, + confirmationMessage: String, + title: String +) { + + var confirmed by remember { mutableStateOf(false) } + if (confirmed) { + LoadingText() + } else { + AlertDialog( + onDismissRequest = onDismiss, + confirmButton = { + Button( + onClick = { + confirmed = true + onConfirm() + }, + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.outlineVariant)) { + Text("Confirm", color = Color.White) + } + }, + dismissButton = { + Button( + onClick = onDismiss, + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.primaryContainer)) { + Text("Cancel", color = MaterialTheme.colorScheme.onBackground) + } + }, + title = { Text(title) }, + text = { + Text(confirmationMessage, modifier = Modifier.testTag("DeleteEventConfirmationText")) + }, + containerColor = MaterialTheme.colorScheme.background, + ) + } } diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt index bd67adbc..df944bb9 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt @@ -24,6 +24,10 @@ import androidx.compose.material3.Scaffold import androidx.compose.material3.Surface import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color @@ -36,23 +40,27 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.unit.dp import com.github.se.gomeet.R +import com.github.se.gomeet.ui.mainscreens.events.ConfirmDialog import com.github.se.gomeet.ui.navigation.BottomNavigationMenu import com.github.se.gomeet.ui.navigation.NavigationActions import com.github.se.gomeet.ui.navigation.Route import com.github.se.gomeet.ui.navigation.TOP_LEVEL_DESTINATIONS import com.github.se.gomeet.ui.theme.GoMeetTheme +import com.github.se.gomeet.viewmodel.UserViewModel /** * Composable function for the profile Settings screen. * * @param nav The navigation actions for the screen. - * @param authViewModel The view model for the authentication (for signing out) + * @param userViewModel The view model for the user (to delete the account). * @param logOut The navigation action to go back to the start screen after signing out. */ @Composable -fun SettingsScreen(nav: NavigationActions, /*userViewModel: UserViewModel*/ logOut: () -> Unit) { +fun SettingsScreen(nav: NavigationActions, userViewModel: UserViewModel, logOut: () -> Unit) { val screenHeight = LocalConfiguration.current.screenHeightDp.dp val screenWidth = LocalConfiguration.current.screenWidthDp.dp + var deleting by remember { mutableStateOf(false) } + var signingOut by remember { mutableStateOf(false) } GoMeetTheme { Scaffold( modifier = Modifier.testTag("SettingsScreen"), @@ -94,80 +102,97 @@ fun SettingsScreen(nav: NavigationActions, /*userViewModel: UserViewModel*/ logO tabList = TOP_LEVEL_DESTINATIONS, selectedItem = Route.PROFILE) }) { innerPadding -> - Column( - modifier = - Modifier.padding(innerPadding) - .verticalScroll(rememberScrollState()) - .testTag("Settings"), - horizontalAlignment = Alignment.CenterHorizontally, - verticalArrangement = Arrangement.Center) { - SettingsSubtitle( - "Who can see your content", Modifier.padding(15.dp).align(Alignment.Start)) - - SettingsComposable(R.drawable.privacy_icon, "Account privacy") - SettingsComposable(R.drawable.star, "Close friends") - SettingsComposable(R.drawable.blocked_icon, "Blocked") - SettingsComposable(R.drawable.mail, "Messages") - - SettingsSubtitle( - "Your app and media", Modifier.padding(15.dp).align(Alignment.Start)) - - SettingsComposable(R.drawable.folder, "Suggested content") - SettingsComposable( - R.drawable.mobile_friendly, - "Device permissions", - true, - { nav.navigateToScreen(Route.PERMISSIONS) }) - SettingsComposable(R.drawable.accessibility_icon, "Accessibility") - SettingsComposable(R.drawable.language, "Language") - - SettingsSubtitle( - "More info and support", Modifier.padding(15.dp).align(Alignment.Start)) - - SettingsComposable( - R.drawable.baseline_chat_bubble_outline_24, - "Help", - true, - { nav.navigateToScreen(Route.HELP) }) - SettingsComposable( - R.drawable.gomeet_icon, "About", true, { nav.navigateToScreen(Route.ABOUT) }) - - Button( - onClick = { logOut() }, - shape = RoundedCornerShape(10.dp), - modifier = Modifier.fillMaxWidth(0.5f), - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.secondaryContainer, - contentColor = MaterialTheme.colorScheme.tertiary)) { - Text( - text = "Log out", - color = Color.Red, - fontStyle = FontStyle.Normal, - fontWeight = FontWeight.SemiBold, - fontFamily = FontFamily.Default, - textAlign = TextAlign.Start, - style = MaterialTheme.typography.bodySmall) - } - - Button( - onClick = { /* TODO */}, - shape = RoundedCornerShape(10.dp), - modifier = Modifier.fillMaxWidth(0.5f), - colors = - ButtonDefaults.buttonColors( - containerColor = MaterialTheme.colorScheme.secondaryContainer, - contentColor = MaterialTheme.colorScheme.tertiary)) { - Text( - text = "Delete account", - color = Color.Red, - fontStyle = FontStyle.Normal, - fontWeight = FontWeight.SemiBold, - fontFamily = FontFamily.Default, - textAlign = TextAlign.Start, - style = MaterialTheme.typography.bodySmall) - } - } + if (deleting) { + ConfirmDialog( + onConfirm = { userViewModel.deleteUser { logOut() } }, + onDismiss = { deleting = false }, + title = "Delete account", + confirmationMessage = + "Are you sure you want to delete your account? This action cannot be undone.", + ) + } else if (signingOut) { + ConfirmDialog( + onConfirm = { logOut() }, + onDismiss = { signingOut = false }, + confirmationMessage = "Are you sure you want to log out?", + title = "Log out") + } else { + + Column( + modifier = + Modifier.padding(innerPadding) + .verticalScroll(rememberScrollState()) + .testTag("Settings"), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center) { + SettingsSubtitle( + "Who can see your content", Modifier.padding(15.dp).align(Alignment.Start)) + + SettingsComposable(R.drawable.privacy_icon, "Account privacy") + SettingsComposable(R.drawable.star, "Close friends") + SettingsComposable(R.drawable.blocked_icon, "Blocked") + SettingsComposable(R.drawable.mail, "Messages") + + SettingsSubtitle( + "Your app and media", Modifier.padding(15.dp).align(Alignment.Start)) + + SettingsComposable(R.drawable.folder, "Suggested content") + SettingsComposable( + R.drawable.mobile_friendly, + "Device permissions", + true, + { nav.navigateToScreen(Route.PERMISSIONS) }) + SettingsComposable(R.drawable.accessibility_icon, "Accessibility") + SettingsComposable(R.drawable.language, "Language") + + SettingsSubtitle( + "More info and support", Modifier.padding(15.dp).align(Alignment.Start)) + + SettingsComposable( + R.drawable.baseline_chat_bubble_outline_24, + "Help", + true, + { nav.navigateToScreen(Route.HELP) }) + SettingsComposable( + R.drawable.gomeet_icon, "About", true, { nav.navigateToScreen(Route.ABOUT) }) + + Button( + onClick = { signingOut = true }, + shape = RoundedCornerShape(10.dp), + modifier = Modifier.fillMaxWidth(0.5f), + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.secondaryContainer, + contentColor = MaterialTheme.colorScheme.tertiary)) { + Text( + text = "Log out", + color = Color.Red, + fontStyle = FontStyle.Normal, + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.Default, + textAlign = TextAlign.Start, + style = MaterialTheme.typography.bodySmall) + } + + Button( + onClick = { deleting = true }, + shape = RoundedCornerShape(10.dp), + modifier = Modifier.fillMaxWidth(0.5f), + colors = + ButtonDefaults.buttonColors( + containerColor = MaterialTheme.colorScheme.secondaryContainer, + contentColor = MaterialTheme.colorScheme.tertiary)) { + Text( + text = "Delete account", + color = Color.Red, + fontStyle = FontStyle.Normal, + fontWeight = FontWeight.SemiBold, + fontFamily = FontFamily.Default, + textAlign = TextAlign.Start, + style = MaterialTheme.typography.bodySmall) + } + } + } } } } diff --git a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt index f781e283..81859c1f 100644 --- a/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt +++ b/app/src/main/java/com/github/se/gomeet/viewmodel/UserViewModel.kt @@ -11,6 +11,8 @@ import com.github.se.gomeet.model.event.InviteStatus import com.github.se.gomeet.model.repository.EventRepository import com.github.se.gomeet.model.repository.UserRepository import com.github.se.gomeet.model.user.GoMeetUser +import com.google.firebase.auth.ktx.auth +import com.google.firebase.ktx.Firebase import java.util.UUID import kotlinx.coroutines.CompletableDeferred import kotlinx.coroutines.CoroutineScope @@ -173,6 +175,7 @@ class UserViewModel(val currentUID: String? = null) : ViewModel() { UserRepository.removeFollowersFollowing(uid) user.joinedEvents.forEach { eventID -> EventRepository.leaveEvent(eventID, uid) } UserRepository.removeUser(uid) + Firebase.auth.currentUser!!.delete() callback() } } From 68e0c4a7d0f30687e2a4e7d0a61572e108c93571 Mon Sep 17 00:00:00 2001 From: DanThePol Date: Sun, 2 Jun 2024 22:59:07 +0200 Subject: [PATCH 5/5] ktfmtFormat --- .../ui/mainscreens/profile/settings/SettingsScreen.kt | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt index fa5ea338..e3de5cea 100644 --- a/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt +++ b/app/src/main/java/com/github/se/gomeet/ui/mainscreens/profile/settings/SettingsScreen.kt @@ -51,11 +51,9 @@ import com.github.se.gomeet.viewmodel.UserViewModel /** * Composable function for the profile Settings screen. * - * @param nav The navigation actions for the screen. -<<<<<<< HEAD - * @param userViewModel The view model for the user (to delete the account). -======= ->>>>>>> f672fdec067d471ff33e98d50ae036e48c8a88bf + * @param nav The navigation actions for the screen. <<<<<<< HEAD + * @param userViewModel The view model for the user (to delete the account). ======= >>>>>>> + * f672fdec067d471ff33e98d50ae036e48c8a88bf * @param logOut The navigation action to go back to the start screen after signing out. */ @Composable