From cbc5d581066b327125850cb79dcb569e408f99cd Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Wed, 11 Dec 2024 17:16:32 +0100 Subject: [PATCH 01/19] test(e2e): Add start of activity addition Add first draft of a new E2E test with activities. --- .../e2e/ActivityCreationAndEdit.kt | 199 ++++++++++++++++++ 1 file changed, 199 insertions(+) create mode 100644 app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt new file mode 100644 index 00000000..b2adb4ba --- /dev/null +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt @@ -0,0 +1,199 @@ +package com.github.se.travelpouch.e2e + +import android.icu.util.GregorianCalendar +import androidx.compose.ui.test.assert +import androidx.compose.ui.test.assertIsDisplayed +import androidx.compose.ui.test.assertTextEquals +import androidx.compose.ui.test.hasText +import androidx.compose.ui.test.isDisplayed +import androidx.compose.ui.test.isNotDisplayed +import androidx.compose.ui.test.junit4.createAndroidComposeRule +import androidx.compose.ui.test.onNodeWithTag +import androidx.compose.ui.test.onNodeWithText +import androidx.compose.ui.test.performClick +import androidx.compose.ui.test.performTextClearance +import androidx.compose.ui.test.performTextInput +import com.github.se.travelpouch.MainActivity +import com.github.se.travelpouch.di.AppModule +import com.google.firebase.Timestamp +import com.google.firebase.auth.FirebaseAuth +import com.google.firebase.firestore.FirebaseFirestore +import dagger.hilt.android.testing.HiltAndroidRule +import dagger.hilt.android.testing.HiltAndroidTest +import dagger.hilt.android.testing.UninstallModules +import javax.inject.Inject +import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.tasks.await +import kotlinx.coroutines.test.runTest +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test + +@HiltAndroidTest +@UninstallModules(AppModule::class) +class ActivityCreationAndEdit { + + + private val DEFAULT_TIMEOUT = 10000L + + @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) + + @get:Rule(order = 1) val composeTestRule = createAndroidComposeRule() + + @Inject lateinit var firestore: FirebaseFirestore + @Inject lateinit var auth: FirebaseAuth + + @Before + fun setUp() { + hiltRule.inject() + + // seed DB with existing trave @Inject lateinit var auth: FirebaseAuthl and user + runBlocking { + val uid = + auth.createUserWithEmailAndPassword("example@example.com", "password").await().user!!.uid + + firestore + .collection("allTravels") + .document("w2HGCwaJ4KgcXJ5nVxkF") + .set( + mapOf( + "allAttachments" to emptyMap(), + "allParticipants" to mapOf(uid to "OWNER"), + "description" to "Description of the test travel", + "endTime" to Timestamp(GregorianCalendar(2025, 8, 24).time), + "fsUid" to "w2HGCwaJ4KgcXJ5nVxkF", + "listParticipant" to listOf(uid), + "location" to + mapOf( + "insertTime" to Timestamp.now(), + "latitude" to 44.9305652, + "longitude" to 5.7630211, + "name" to + "trou, câble, Susville, Grenoble, Isère, Auvergne-Rhône-Alpes, France métropolitaine, 38350, France"), + "startTime" to Timestamp(GregorianCalendar(2025, 8, 23).time), + "title" to "Test")) + .await() + + firestore + .collection("userslist") + .document(uid) + .set( + mapOf( + "email" to "example.example.com", + "friends" to emptyList(), + "fsUid" to uid, + "name" to "Example", + "username" to "example", + "userTravelList" to listOf("w2HGCwaJ4KgcXJ5nVxkF"), + "needsOnboarding" to true)) + .await() + + auth.signOut() + } + + } + + @After + fun tearDown() { + runBlocking { + auth.signOut() + auth.signInWithEmailAndPassword("example@example.com", "password").await() + val uid = auth.currentUser!!.uid + auth.currentUser!!.delete().await() + + firestore + .collection("allTravels/w2HGCwaJ4KgcXJ5nVxkF/activities") + .get() + .await() + .documents + .forEach { it.reference.delete().await() } // delete all activities + firestore.collection("allTravels").document("w2HGCwaJ4KgcXJ5nVxkF").delete().await() + firestore.collection("userslist").document(uid).delete().await() + firestore.terminate().await() + } + } + + @Test + fun verifyUserFlowForTravelCreation() = + runTest(timeout = 300.seconds) { + + // assert that login screen is displayed + composeTestRule.onNodeWithTag("appLogo").assertIsDisplayed() + composeTestRule.onNodeWithTag("welcomText").assertIsDisplayed() + composeTestRule.onNodeWithText("Sign in with email and password").assertIsDisplayed() + + // go to sign in screen with email and password and log in + composeTestRule.onNodeWithText("Sign in with email and password").performClick() + + composeTestRule.onNodeWithTag("emailField").assertIsDisplayed() + composeTestRule.onNodeWithTag("passwordField").assertIsDisplayed() + composeTestRule.onNodeWithText("Sign up").assertIsDisplayed() + composeTestRule.onNodeWithText("Log in").assertIsDisplayed() + + composeTestRule.onNodeWithTag("emailField").performTextInput("example@example.com") + composeTestRule.onNodeWithTag("passwordField").performTextInput("password") + composeTestRule.onNodeWithText("Log in").performClick() + + // Skip onboarding + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { + composeTestRule.onNodeWithTag("OnboardingScreen", useUnmergedTree = true).isDisplayed() + } + composeTestRule.onNodeWithTag("SkipButton").performClick() + + // wait until we are in the travel list screen + composeTestRule.waitUntil(timeoutMillis = 2000) { + composeTestRule.onNodeWithTag("emptyTravelPrompt", useUnmergedTree = true).isDisplayed() + } + + composeTestRule.onNodeWithText("Test").assertIsDisplayed() + composeTestRule.onNodeWithTag("travelListItem").performClick() + + // assert that there are no activities at the moment + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { + composeTestRule.onNodeWithTag("emptyTravel", useUnmergedTree = true).isDisplayed() + } + + // add an activity button + composeTestRule.onNodeWithTag("addActivityButton").assertIsDisplayed().performClick() + // add activity screen + composeTestRule.onNodeWithTag("addTravelScreen").assertIsDisplayed() + composeTestRule.onNodeWithTag("activityTitle").assertIsDisplayed() + + // fill in the activity fields + composeTestRule.onNodeWithTag("titleField").performTextClearance() + composeTestRule.onNodeWithTag("titleField").performTextInput("epic activity") + + composeTestRule.onNodeWithTag("descriptionField").performTextClearance() + composeTestRule.onNodeWithTag("descriptionField").performTextInput("this is an epic activity") + + composeTestRule.onNodeWithTag("dateField").performTextClearance() + composeTestRule.onNodeWithTag("dateField").performTextInput("01022024") + + composeTestRule.onNodeWithTag("timeField").performTextClearance() + composeTestRule.onNodeWithTag("timeField").performTextInput("15:24") + + composeTestRule.onNodeWithTag("inputTravelLocation").performTextClearance() + composeTestRule.onNodeWithTag("inputTravelLocation").performTextInput("L") + + // wait to have La paz displayed + composeTestRule.waitUntil(timeoutMillis = 4000) { + composeTestRule.onNodeWithText("La Paz, Bolivia").isDisplayed() + } + + composeTestRule.onNodeWithText("La Paz, Bolivia").performClick() + // save it + composeTestRule.onNodeWithText("Save").assertIsDisplayed().performClick() + // there is an activity + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { + composeTestRule.onNodeWithTag("emptyTravel", useUnmergedTree = true).isNotDisplayed() + } + // check the activity is displayed + composeTestRule.onNodeWithText("epic activity").assertIsDisplayed() + composeTestRule.onNodeWithText("epic activity").assert(hasText("epic activity")) + composeTestRule.onNodeWithText("epic activity").assert(hasText("01/02/2024")) + composeTestRule.onNodeWithText("epic activity").assert(hasText("La Paz, Bolivia")) + + } +} From 1feaa2ee70d38720306941c16d9115f1d42b1bf6 Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Wed, 11 Dec 2024 17:21:00 +0100 Subject: [PATCH 02/19] chore(e2e): Ktfmt compliance Ktfmt format compliance --- .../e2e/ActivityCreationAndEdit.kt | 82 +++++++++---------- 1 file changed, 40 insertions(+), 42 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt index b2adb4ba..bcdf4bb3 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt @@ -3,7 +3,6 @@ package com.github.se.travelpouch.e2e import android.icu.util.GregorianCalendar import androidx.compose.ui.test.assert import androidx.compose.ui.test.assertIsDisplayed -import androidx.compose.ui.test.assertTextEquals import androidx.compose.ui.test.hasText import androidx.compose.ui.test.isDisplayed import androidx.compose.ui.test.isNotDisplayed @@ -35,7 +34,6 @@ import org.junit.Test @UninstallModules(AppModule::class) class ActivityCreationAndEdit { - private val DEFAULT_TIMEOUT = 10000L @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @@ -52,47 +50,46 @@ class ActivityCreationAndEdit { // seed DB with existing trave @Inject lateinit var auth: FirebaseAuthl and user runBlocking { val uid = - auth.createUserWithEmailAndPassword("example@example.com", "password").await().user!!.uid + auth.createUserWithEmailAndPassword("example@example.com", "password").await().user!!.uid firestore - .collection("allTravels") - .document("w2HGCwaJ4KgcXJ5nVxkF") - .set( - mapOf( - "allAttachments" to emptyMap(), - "allParticipants" to mapOf(uid to "OWNER"), - "description" to "Description of the test travel", - "endTime" to Timestamp(GregorianCalendar(2025, 8, 24).time), - "fsUid" to "w2HGCwaJ4KgcXJ5nVxkF", - "listParticipant" to listOf(uid), - "location" to - mapOf( - "insertTime" to Timestamp.now(), - "latitude" to 44.9305652, - "longitude" to 5.7630211, - "name" to + .collection("allTravels") + .document("w2HGCwaJ4KgcXJ5nVxkF") + .set( + mapOf( + "allAttachments" to emptyMap(), + "allParticipants" to mapOf(uid to "OWNER"), + "description" to "Description of the test travel", + "endTime" to Timestamp(GregorianCalendar(2025, 8, 24).time), + "fsUid" to "w2HGCwaJ4KgcXJ5nVxkF", + "listParticipant" to listOf(uid), + "location" to + mapOf( + "insertTime" to Timestamp.now(), + "latitude" to 44.9305652, + "longitude" to 5.7630211, + "name" to "trou, câble, Susville, Grenoble, Isère, Auvergne-Rhône-Alpes, France métropolitaine, 38350, France"), - "startTime" to Timestamp(GregorianCalendar(2025, 8, 23).time), - "title" to "Test")) - .await() + "startTime" to Timestamp(GregorianCalendar(2025, 8, 23).time), + "title" to "Test")) + .await() firestore - .collection("userslist") - .document(uid) - .set( - mapOf( - "email" to "example.example.com", - "friends" to emptyList(), - "fsUid" to uid, - "name" to "Example", - "username" to "example", - "userTravelList" to listOf("w2HGCwaJ4KgcXJ5nVxkF"), - "needsOnboarding" to true)) - .await() + .collection("userslist") + .document(uid) + .set( + mapOf( + "email" to "example.example.com", + "friends" to emptyList(), + "fsUid" to uid, + "name" to "Example", + "username" to "example", + "userTravelList" to listOf("w2HGCwaJ4KgcXJ5nVxkF"), + "needsOnboarding" to true)) + .await() auth.signOut() } - } @After @@ -104,11 +101,11 @@ class ActivityCreationAndEdit { auth.currentUser!!.delete().await() firestore - .collection("allTravels/w2HGCwaJ4KgcXJ5nVxkF/activities") - .get() - .await() - .documents - .forEach { it.reference.delete().await() } // delete all activities + .collection("allTravels/w2HGCwaJ4KgcXJ5nVxkF/activities") + .get() + .await() + .documents + .forEach { it.reference.delete().await() } // delete all activities firestore.collection("allTravels").document("w2HGCwaJ4KgcXJ5nVxkF").delete().await() firestore.collection("userslist").document(uid).delete().await() firestore.terminate().await() @@ -166,7 +163,9 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithTag("titleField").performTextInput("epic activity") composeTestRule.onNodeWithTag("descriptionField").performTextClearance() - composeTestRule.onNodeWithTag("descriptionField").performTextInput("this is an epic activity") + composeTestRule + .onNodeWithTag("descriptionField") + .performTextInput("this is an epic activity") composeTestRule.onNodeWithTag("dateField").performTextClearance() composeTestRule.onNodeWithTag("dateField").performTextInput("01022024") @@ -194,6 +193,5 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithText("epic activity").assert(hasText("epic activity")) composeTestRule.onNodeWithText("epic activity").assert(hasText("01/02/2024")) composeTestRule.onNodeWithText("epic activity").assert(hasText("La Paz, Bolivia")) - } } From 93fff7298ea5623a4846890b513cfdda27a7ba93 Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Wed, 11 Dec 2024 17:55:29 +0100 Subject: [PATCH 03/19] fix(e2e): Fix end to end test tags and display Fix faulty test tags to make E2E work correctly. --- .../github/se/travelpouch/e2e/ActivityCreationAndEdit.kt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt index bcdf4bb3..8f5d9c95 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt @@ -108,6 +108,7 @@ class ActivityCreationAndEdit { .forEach { it.reference.delete().await() } // delete all activities firestore.collection("allTravels").document("w2HGCwaJ4KgcXJ5nVxkF").delete().await() firestore.collection("userslist").document(uid).delete().await() + auth.signOut() firestore.terminate().await() } } @@ -141,7 +142,7 @@ class ActivityCreationAndEdit { // wait until we are in the travel list screen composeTestRule.waitUntil(timeoutMillis = 2000) { - composeTestRule.onNodeWithTag("emptyTravelPrompt", useUnmergedTree = true).isDisplayed() + composeTestRule.onNodeWithText("Test", useUnmergedTree = true).isDisplayed() } composeTestRule.onNodeWithText("Test").assertIsDisplayed() @@ -155,8 +156,8 @@ class ActivityCreationAndEdit { // add an activity button composeTestRule.onNodeWithTag("addActivityButton").assertIsDisplayed().performClick() // add activity screen - composeTestRule.onNodeWithTag("addTravelScreen").assertIsDisplayed() - composeTestRule.onNodeWithTag("activityTitle").assertIsDisplayed() + composeTestRule.onNodeWithTag("AddActivityScreen").assertIsDisplayed() + composeTestRule.onNodeWithTag("travelTitle").assertIsDisplayed() // fill in the activity fields composeTestRule.onNodeWithTag("titleField").performTextClearance() @@ -191,7 +192,7 @@ class ActivityCreationAndEdit { // check the activity is displayed composeTestRule.onNodeWithText("epic activity").assertIsDisplayed() composeTestRule.onNodeWithText("epic activity").assert(hasText("epic activity")) - composeTestRule.onNodeWithText("epic activity").assert(hasText("01/02/2024")) + composeTestRule.onNodeWithText("epic activity").assert(hasText("1/2/2024")) composeTestRule.onNodeWithText("epic activity").assert(hasText("La Paz, Bolivia")) } } From 30892a6b21249f6c6992b2f1dccd55d785cb1681 Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Wed, 11 Dec 2024 18:16:34 +0100 Subject: [PATCH 04/19] test(e2e): Expand E2E to edit an activity Expand scope of E2E to handle activity edit and saving fields. --- .../e2e/ActivityCreationAndEdit.kt | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt index 8f5d9c95..9e569d24 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt @@ -194,5 +194,30 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithText("epic activity").assert(hasText("epic activity")) composeTestRule.onNodeWithText("epic activity").assert(hasText("1/2/2024")) composeTestRule.onNodeWithText("epic activity").assert(hasText("La Paz, Bolivia")) + + // edit the activity + composeTestRule.onNodeWithText("epic activity").performClick() + composeTestRule.onNodeWithTag("EditActivityScreen").assertIsDisplayed() + composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) + composeTestRule.onNodeWithTag("descriptionField").assert(hasText("this is an epic activity")) + composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) + composeTestRule.onNodeWithTag("locationField").assert(hasText("La Paz, Bolivia")) + + composeTestRule.onNodeWithTag("titleField").performTextClearance() + composeTestRule.onNodeWithTag("titleField").performTextInput("more epic activity") + + composeTestRule.onNodeWithTag("descriptionField").performTextClearance() + composeTestRule.onNodeWithTag("descriptionField").performTextInput("this is a more epic activity") + + composeTestRule.onNodeWithTag("dateField").performTextClearance() + composeTestRule.onNodeWithTag("dateField").performTextInput("02022024") + // save the new info + composeTestRule.onNodeWithText("Save").assertIsDisplayed().performClick() + // check the activity is displayed + composeTestRule.onNodeWithText("more epic activity").assertIsDisplayed() + composeTestRule.onNodeWithText("more epic activity").assert(hasText("more epic activity")) + composeTestRule.onNodeWithText("more epic activity").assert(hasText("2/2/2024")) + composeTestRule.onNodeWithText("more epic activity").assert(hasText("La Paz, Bolivia")) + } } From a67a4f33c72da7286848431791611543636c2a11 Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Wed, 11 Dec 2024 18:17:33 +0100 Subject: [PATCH 05/19] chore(e2e): Ktfmt format compliance Ktfmt format to comply with the linting standards. --- .../github/se/travelpouch/e2e/ActivityCreationAndEdit.kt | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt index 9e569d24..eb2d6bf0 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt @@ -199,7 +199,9 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithText("epic activity").performClick() composeTestRule.onNodeWithTag("EditActivityScreen").assertIsDisplayed() composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) - composeTestRule.onNodeWithTag("descriptionField").assert(hasText("this is an epic activity")) + composeTestRule + .onNodeWithTag("descriptionField") + .assert(hasText("this is an epic activity")) composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) composeTestRule.onNodeWithTag("locationField").assert(hasText("La Paz, Bolivia")) @@ -207,7 +209,9 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithTag("titleField").performTextInput("more epic activity") composeTestRule.onNodeWithTag("descriptionField").performTextClearance() - composeTestRule.onNodeWithTag("descriptionField").performTextInput("this is a more epic activity") + composeTestRule + .onNodeWithTag("descriptionField") + .performTextInput("this is a more epic activity") composeTestRule.onNodeWithTag("dateField").performTextClearance() composeTestRule.onNodeWithTag("dateField").performTextInput("02022024") @@ -218,6 +222,5 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithText("more epic activity").assert(hasText("more epic activity")) composeTestRule.onNodeWithText("more epic activity").assert(hasText("2/2/2024")) composeTestRule.onNodeWithText("more epic activity").assert(hasText("La Paz, Bolivia")) - } } From 43a83075640293b0acb621f58db2ba16d47bc627 Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Wed, 11 Dec 2024 18:43:23 +0100 Subject: [PATCH 06/19] fix(e2e): Try to fix E2E conflicts Try to fix E2E issues relating to shared firebase state. --- .../java/com/github/se/travelpouch/e2e/DocumentUpload.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt index 5f9b78f2..35c3cb9a 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt @@ -127,6 +127,7 @@ class DocumentUpload { .forEach { it.reference.delete().await() } firestore.collection("allTravels").document("w2HGCwaJ4KgcXJ5nVxkF").delete().await() firestore.collection("userslist").document(uid).delete().await() + auth.signOut() firestore.terminate().await() } From 5e11dff39d6c6c029bd4e6a4c813d9d03fce3218 Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Wed, 11 Dec 2024 20:12:08 +0100 Subject: [PATCH 07/19] fix(e2e): Try to fix E2E errors Try to fix E2E issues relating with some strange stuff found on SO. --- .../com/github/se/travelpouch/e2e/DocumentUpload.kt | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt index 35c3cb9a..adab994a 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt @@ -5,6 +5,7 @@ import android.app.Instrumentation import android.content.Intent import android.icu.util.GregorianCalendar import android.net.Uri +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.isDisplayed import androidx.compose.ui.test.junit4.createAndroidComposeRule @@ -30,6 +31,7 @@ import dagger.hilt.android.testing.UninstallModules import java.io.File import javax.inject.Inject import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await import kotlinx.coroutines.test.runTest @@ -47,7 +49,10 @@ class DocumentUpload { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) - @get:Rule(order = 1) val composeTestRule = createAndroidComposeRule() + @OptIn(ExperimentalTestApi::class) + @get:Rule(order = 1) + val composeTestRule = + createAndroidComposeRule(effectContext = Dispatchers.Main.immediate) @get:Rule(order = 2) val intentsTestRule = IntentsTestRule(MainActivity::class.java) @@ -129,9 +134,8 @@ class DocumentUpload { firestore.collection("userslist").document(uid).delete().await() auth.signOut() firestore.terminate().await() + file.delete() } - - file.delete() } @Test From 378e0a262e41c0a9cad5152b9185158b9553ce47 Mon Sep 17 00:00:00 2001 From: RemIsMyWaifuu Date: Thu, 12 Dec 2024 00:34:35 +0100 Subject: [PATCH 08/19] test(e2e): Try to fix issues by refactor Try to see if putting tests in different packages affects results. --- .../com/github/se/travelpouch/e2e/TravelCreation.kt | 12 +++++++----- ...reationAndEdit.kt => UActivityCreationAndEdit.kt} | 2 +- .../e2e/{ => documents}/DocumentUpload.kt | 2 +- 3 files changed, 9 insertions(+), 7 deletions(-) rename app/src/androidTest/java/com/github/se/travelpouch/e2e/{ActivityCreationAndEdit.kt => UActivityCreationAndEdit.kt} (99%) rename app/src/androidTest/java/com/github/se/travelpouch/e2e/{ => documents}/DocumentUpload.kt (99%) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt index 1ab78287..b06d8d9d 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt @@ -31,6 +31,8 @@ import org.junit.Test @UninstallModules(AppModule::class) class TravelCreation { + private val DEFAULT_TIMEOUT = 10000L + @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) @get:Rule(order = 1) val composeTestRule = createAndroidComposeRule() @@ -68,13 +70,13 @@ class TravelCreation { composeTestRule.onNodeWithText("Sign up").performClick() // Skip onboarding - composeTestRule.waitUntil(timeoutMillis = 2000) { + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { composeTestRule.onNodeWithTag("OnboardingScreen", useUnmergedTree = true).isDisplayed() } composeTestRule.onNodeWithTag("SkipButton").performClick() // wait until we are in the travel list screen - composeTestRule.waitUntil(timeoutMillis = 2000) { + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { composeTestRule.onNodeWithTag("emptyTravelPrompt", useUnmergedTree = true).isDisplayed() } @@ -86,7 +88,7 @@ class TravelCreation { composeTestRule.onNodeWithTag("createTravelFab").performClick() // wait until we are in the screen to add a travel - composeTestRule.waitUntil(timeoutMillis = 2000) { + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { composeTestRule.onNodeWithTag("travelTitle", useUnmergedTree = true).isDisplayed() } @@ -109,7 +111,7 @@ class TravelCreation { composeTestRule.onNodeWithTag("inputTravelLocation").performTextInput("L") // wait to have La paz displayed - composeTestRule.waitUntil(timeoutMillis = 4000) { + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { composeTestRule.onNodeWithText("La Paz, Bolivia").isDisplayed() } @@ -127,7 +129,7 @@ class TravelCreation { composeTestRule.onNodeWithTag("createTravelFab").assertIsDisplayed() // verify that the empty travel prompt does not exist since we saved a travel - composeTestRule.waitUntil(timeoutMillis = 5000) { + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { composeTestRule .onNodeWithTag("emptyTravelPrompt", useUnmergedTree = true) .isNotDisplayed() diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt similarity index 99% rename from app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt rename to app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt index eb2d6bf0..266d138d 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt @@ -114,7 +114,7 @@ class ActivityCreationAndEdit { } @Test - fun verifyUserFlowForTravelCreation() = + fun verifyActivityCreationAndEditFlow() = runTest(timeout = 300.seconds) { // assert that login screen is displayed diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/documents/DocumentUpload.kt similarity index 99% rename from app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt rename to app/src/androidTest/java/com/github/se/travelpouch/e2e/documents/DocumentUpload.kt index adab994a..a596db46 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/documents/DocumentUpload.kt @@ -1,4 +1,4 @@ -package com.github.se.travelpouch.e2e +package com.github.se.travelpouch.e2e.documents import android.app.Activity import android.app.Instrumentation From 2cdeaf7faad7a1c188a6b404349677865dbc236b Mon Sep 17 00:00:00 2001 From: Remismywaifuu Date: Thu, 12 Dec 2024 16:52:37 +0100 Subject: [PATCH 09/19] test(e2e): Change email between E2E Change email between E2E but it doesn't help with the conflict state. --- .../e2e/{documents => }/DocumentUpload.kt | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) rename app/src/androidTest/java/com/github/se/travelpouch/e2e/{documents => }/DocumentUpload.kt (93%) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/documents/DocumentUpload.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt similarity index 93% rename from app/src/androidTest/java/com/github/se/travelpouch/e2e/documents/DocumentUpload.kt rename to app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt index a596db46..18491446 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/documents/DocumentUpload.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt @@ -1,4 +1,4 @@ -package com.github.se.travelpouch.e2e.documents +package com.github.se.travelpouch.e2e import android.app.Activity import android.app.Instrumentation @@ -67,18 +67,18 @@ class DocumentUpload { // seed the db runBlocking { val uid = - auth.createUserWithEmailAndPassword("example@example.com", "password").await().user!!.uid + auth.createUserWithEmailAndPassword("example2@example.com", "password").await().user!!.uid firestore .collection("allTravels") - .document("w2HGCwaJ4KgcXJ5nVxkF") + .document("skibidiJ4KgcXJ5nVxkF") .set( mapOf( "allAttachments" to emptyMap(), "allParticipants" to mapOf(uid to "OWNER"), "description" to "Description of the test travel", "endTime" to Timestamp(GregorianCalendar(2025, 8, 24).time), - "fsUid" to "w2HGCwaJ4KgcXJ5nVxkF", + "fsUid" to "skibidiJ4KgcXJ5nVxkF", "listParticipant" to listOf(uid), "location" to mapOf( @@ -101,7 +101,7 @@ class DocumentUpload { "fsUid" to uid, "name" to "Example", "username" to "example", - "userTravelList" to listOf("w2HGCwaJ4KgcXJ5nVxkF"), + "userTravelList" to listOf("skibidiJ4KgcXJ5nVxkF"), "needsOnboarding" to true)) .await() @@ -120,17 +120,17 @@ class DocumentUpload { fun tearDown() { runBlocking { auth.signOut() - auth.signInWithEmailAndPassword("example@example.com", "password").await() + auth.signInWithEmailAndPassword("example2@example.com", "password").await() val uid = auth.currentUser!!.uid auth.currentUser!!.delete().await() firestore - .collection("allTravels/w2HGCwaJ4KgcXJ5nVxkF/documents") + .collection("allTravels/skibidiJ4KgcXJ5nVxkF/documents") .get() .await() .documents .forEach { it.reference.delete().await() } - firestore.collection("allTravels").document("w2HGCwaJ4KgcXJ5nVxkF").delete().await() + firestore.collection("allTravels").document("skibidiJ4KgcXJ5nVxkF").delete().await() firestore.collection("userslist").document(uid).delete().await() auth.signOut() firestore.terminate().await() @@ -160,7 +160,7 @@ class DocumentUpload { composeTestRule.onNodeWithText("Sign up").assertIsDisplayed() composeTestRule.onNodeWithText("Log in").assertIsDisplayed() - composeTestRule.onNodeWithTag("emailField").performTextInput("example@example.com") + composeTestRule.onNodeWithTag("emailField").performTextInput("example2@example.com") composeTestRule.onNodeWithTag("passwordField").performTextInput("password") composeTestRule.onNodeWithText("Log in").performClick() From 3b98808b8f743a9e21a800becdbc51bf652ab3db Mon Sep 17 00:00:00 2001 From: yassine04e Date: Thu, 12 Dec 2024 23:13:23 +0100 Subject: [PATCH 10/19] fix: Unifying the emails acrros the e2e tests --- .../se/travelpouch/e2e/DocumentUpload.kt | 12 +- .../e2e/UActivityCreationAndEdit.kt | 8 +- .../ui/dashboard/EditActivityScreenTest.kt | 46 ++++-- .../ui/dashboard/TravelActivityScreen.kt | 4 +- .../ui/travel/ParticipantListScreenTest.kt | 21 ++- .../com/github/se/travelpouch/MainActivity.kt | 4 +- .../travelpouch/ui/dashboard/AddActivity.kt | 86 +++++------ .../travelpouch/ui/dashboard/EditActivity.kt | 145 +++++++++--------- .../ui/dashboard/TravelActivity.kt | 57 +++---- .../ui/fields/DateTimeInputField.kt | 49 +++--- .../ui/fields/LocationQueryField.kt | 97 ++++++------ .../se/travelpouch/ui/home/TravelList.kt | 24 +-- .../ui/travel/EditTravelSettings.kt | 25 ++- .../ui/travel/ParticipantListScreen.kt | 3 +- 14 files changed, 314 insertions(+), 267 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt index 6c3f8f96..050739a4 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt @@ -67,7 +67,11 @@ class DocumentUpload { // seed the db runBlocking { val uid = - auth.createUserWithEmailAndPassword("example2@example.com", "password").await().user!!.uid + auth + .createUserWithEmailAndPassword("example2@example.com", "password2") + .await() + .user!! + .uid firestore .collection("allTravels") @@ -96,7 +100,7 @@ class DocumentUpload { .document(uid) .set( mapOf( - "email" to "example.example.com", + "email" to "example.example2.com", "friends" to emptyList(), "fsUid" to uid, "name" to "Example", @@ -161,7 +165,7 @@ class DocumentUpload { composeTestRule.onNodeWithText("Log in").assertIsDisplayed() composeTestRule.onNodeWithTag("emailField").performTextInput("example2@example.com") - composeTestRule.onNodeWithTag("passwordField").performTextInput("password") + composeTestRule.onNodeWithTag("passwordField").performTextInput("password2") composeTestRule.onNodeWithText("Log in").performClick() // Skip onboarding @@ -181,7 +185,7 @@ class DocumentUpload { composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { composeTestRule.onNodeWithTag("emptyTravel1", useUnmergedTree = true).isDisplayed() - composeTestRule.onNodeWithTag("emptyTravel2", useUnmergedTree = true).isDisplayed() + composeTestRule.onNodeWithTag("emptyTravel2", useUnmergedTree = true).isDisplayed() } composeTestRule.onNodeWithTag("travelActivitiesScreen").performTouchInput { swipeLeft() } diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt index 266d138d..13d3f705 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt @@ -50,7 +50,7 @@ class ActivityCreationAndEdit { // seed DB with existing trave @Inject lateinit var auth: FirebaseAuthl and user runBlocking { val uid = - auth.createUserWithEmailAndPassword("example@example.com", "password").await().user!!.uid + auth.createUserWithEmailAndPassword("example3@example.com", "password").await().user!!.uid firestore .collection("allTravels") @@ -79,7 +79,7 @@ class ActivityCreationAndEdit { .document(uid) .set( mapOf( - "email" to "example.example.com", + "email" to "example3@example.com", "friends" to emptyList(), "fsUid" to uid, "name" to "Example", @@ -96,7 +96,7 @@ class ActivityCreationAndEdit { fun tearDown() { runBlocking { auth.signOut() - auth.signInWithEmailAndPassword("example@example.com", "password").await() + auth.signInWithEmailAndPassword("example3@example.com", "password").await() val uid = auth.currentUser!!.uid auth.currentUser!!.delete().await() @@ -130,7 +130,7 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithText("Sign up").assertIsDisplayed() composeTestRule.onNodeWithText("Log in").assertIsDisplayed() - composeTestRule.onNodeWithTag("emailField").performTextInput("example@example.com") + composeTestRule.onNodeWithTag("emailField").performTextInput("example3@example.com") composeTestRule.onNodeWithTag("passwordField").performTextInput("password") composeTestRule.onNodeWithText("Log in").performClick() diff --git a/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/EditActivityScreenTest.kt b/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/EditActivityScreenTest.kt index fb39afc4..97c6d328 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/EditActivityScreenTest.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/EditActivityScreenTest.kt @@ -59,7 +59,9 @@ class EditActivityScreenTest { @Test fun everythingIsDisplayed() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } composeTestRule.onNodeWithTag("EditActivityScreen").isDisplayed() composeTestRule.onNodeWithTag("titleField").isDisplayed() @@ -81,7 +83,10 @@ class EditActivityScreenTest { composeTestRule.onNodeWithTag("descriptionField").fetchSemanticsNode().config[EditableText] assert(result.text == activity.description) result = - composeTestRule.onNodeWithTag("inputTravelLocation").fetchSemanticsNode().config[EditableText] + composeTestRule + .onNodeWithTag("inputTravelLocation") + .fetchSemanticsNode() + .config[EditableText] assert(result.text == activity.location.name) result = composeTestRule.onNodeWithTag("dateField").fetchSemanticsNode().config[EditableText] assert(result.text == "01/01/1970") @@ -89,14 +94,18 @@ class EditActivityScreenTest { @Test fun deletesCallsDeleteById() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } composeTestRule.onNodeWithTag("deleteButton").performClick() verify(mockActivityRepositoryFirebase).deleteActivityById(anyOrNull(), anyOrNull(), anyOrNull()) } @Test fun verifiesDisplayWorksAndModificationPlusSaving() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView,locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } VerifyFieldsAreCorrect(composeTestRule) @@ -114,7 +123,10 @@ class EditActivityScreenTest { composeTestRule.onNodeWithTag("descriptionField").fetchSemanticsNode().config[EditableText] assert(result.text == "newDescription") result = - composeTestRule.onNodeWithTag("inputTravelLocation").fetchSemanticsNode().config[EditableText] + composeTestRule + .onNodeWithTag("inputTravelLocation") + .fetchSemanticsNode() + .config[EditableText] assert(result.text == "location") result = composeTestRule.onNodeWithTag("dateField").fetchSemanticsNode().config[EditableText] assert(result.text == "23/06/2025") @@ -125,7 +137,9 @@ class EditActivityScreenTest { @Test fun dateFormattingWorksCorrectly() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } composeTestRule.onNodeWithTag("dateField").performTextClearance() composeTestRule.onNodeWithTag("dateField").performTextInput("00000000") val result = @@ -135,7 +149,9 @@ class EditActivityScreenTest { @Test fun savesWhenFieldsAreCorrect() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } VerifyFieldsAreCorrect(composeTestRule) composeTestRule.onNodeWithTag("dateField").performTextClearance() composeTestRule.onNodeWithTag("dateField").performTextInput("23122024") @@ -146,7 +162,9 @@ class EditActivityScreenTest { @Test fun doesNotSaveWhenDateIsWrong() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } VerifyFieldsAreCorrect(composeTestRule) composeTestRule.onNodeWithTag("dateField").performTextClearance() composeTestRule.onNodeWithTag("dateField").performTextInput("00000000") @@ -158,7 +176,9 @@ class EditActivityScreenTest { @Test fun noCharacterAllowedInDateField() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } composeTestRule.onNodeWithTag("dateField").performTextClearance() composeTestRule.onNodeWithTag("dateField").performTextInput("mdkdk") var result = @@ -176,7 +196,9 @@ class EditActivityScreenTest { @Test fun limitOfEightCharactersInDateField() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } composeTestRule.onNodeWithTag("dateField").performTextClearance() composeTestRule.onNodeWithTag("dateField").performTextInput("01234567") composeTestRule.onNodeWithTag("dateField").performTextInput("8") @@ -187,7 +209,9 @@ class EditActivityScreenTest { @Test fun datePickerDialogOpensOnClick() { - composeTestRule.setContent { EditActivity(navigationActions, mockActivityModelView, locationViewModel) } + composeTestRule.setContent { + EditActivity(navigationActions, mockActivityModelView, locationViewModel) + } // The date field should be displayed composeTestRule.onNodeWithTag("datePickerButton").assertIsDisplayed() diff --git a/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/TravelActivityScreen.kt b/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/TravelActivityScreen.kt index 266f66ef..33dd0257 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/TravelActivityScreen.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/ui/dashboard/TravelActivityScreen.kt @@ -82,9 +82,7 @@ class TravelActivityScreenCopy { } composeTestRule.onNodeWithTag("emptyTravel").isDisplayed() - composeTestRule - .onNodeWithTag("emptyTravel1") - .assertTextEquals("No activities planned") + composeTestRule.onNodeWithTag("emptyTravel1").assertTextEquals("No activities planned") composeTestRule.onNodeWithTag("emptyTravel2").assertTextEquals("for this trip") } diff --git a/app/src/androidTest/java/com/github/se/travelpouch/ui/travel/ParticipantListScreenTest.kt b/app/src/androidTest/java/com/github/se/travelpouch/ui/travel/ParticipantListScreenTest.kt index 1ac08142..6b7ced12 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/ui/travel/ParticipantListScreenTest.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/ui/travel/ParticipantListScreenTest.kt @@ -690,8 +690,9 @@ class ParticipantListScreenTest { .`when`(profileRepository) .getFsUidByEmail(any(), any(), any()) doAnswer { "abcdefghijklmnopqrst" }.`when`(notificationRepository).getNewUid() - doNothing().`when`(travelRepository).updateTravel(any(), any(), anyOrNull(), any(), any(), - anyOrNull()) + doNothing() + .`when`(travelRepository) + .updateTravel(any(), any(), anyOrNull(), any(), any(), anyOrNull()) doThrow(RuntimeException("Impossible Exception")) .`when`(notificationRepository) .addNotification(anyOrNull()) @@ -711,7 +712,11 @@ class ParticipantListScreenTest { listTravelViewModel.selectTravel(travelContainer) composeTestRule.setContent { ParticipantListScreen( - listTravelViewModel, navigationActions, notificationViewModel, profileModelView, eventViewModel) + listTravelViewModel, + navigationActions, + notificationViewModel, + profileModelView, + eventViewModel) } composeTestRule.onNodeWithTag("addUserFab").performClick() composeTestRule.onNodeWithTag("addViaFriendListButton").performClick() @@ -744,7 +749,11 @@ class ParticipantListScreenTest { composeTestRule.setContent { ParticipantListScreen( - listTravelViewModel, navigationActions, notificationViewModel, profileModelView, eventViewModel) + listTravelViewModel, + navigationActions, + notificationViewModel, + profileModelView, + eventViewModel) } composeTestRule.onNodeWithTag("addUserFab").performClick() composeTestRule.onNodeWithTag("addViaFriendListButton").performClick() @@ -770,7 +779,9 @@ class ParticipantListScreenTest { .`when`(profileRepository) .getFsUidByEmail(any(), any(), any()) doAnswer { "abcdefghijklmnopqrst" }.`when`(notificationRepository).getNewUid() - doNothing().`when`(travelRepository).updateTravel(any(), any(), anyOrNull(), any(), any(), anyOrNull()) + doNothing() + .`when`(travelRepository) + .updateTravel(any(), any(), anyOrNull(), any(), any(), anyOrNull()) composeTestRule.onNodeWithTag("friendCard").assertIsDisplayed() composeTestRule.onNodeWithTag("friendCard").assertTextContains("example@mail.com") composeTestRule.onNodeWithTag("friendCard").performClick() diff --git a/app/src/main/java/com/github/se/travelpouch/MainActivity.kt b/app/src/main/java/com/github/se/travelpouch/MainActivity.kt index 1b46c16e..ccb692b2 100644 --- a/app/src/main/java/com/github/se/travelpouch/MainActivity.kt +++ b/app/src/main/java/com/github/se/travelpouch/MainActivity.kt @@ -115,7 +115,9 @@ class MainActivity : ComponentActivity() { composable(Screen.ADD_ACTIVITY) { AddActivityScreen(navigationActions, activityModelView, eventViewModel = eventsViewModel) } - composable(Screen.EDIT_ACTIVITY) { EditActivity(navigationActions, activityModelView, locationViewModel) } + composable(Screen.EDIT_ACTIVITY) { + EditActivity(navigationActions, activityModelView, locationViewModel) + } composable(Screen.ADD_TRAVEL) { AddTravelScreen( listTravelViewModel, diff --git a/app/src/main/java/com/github/se/travelpouch/ui/dashboard/AddActivity.kt b/app/src/main/java/com/github/se/travelpouch/ui/dashboard/AddActivity.kt index 9eaadb5a..24dc14d0 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/dashboard/AddActivity.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/dashboard/AddActivity.kt @@ -47,15 +47,13 @@ fun AddActivityScreen( var title by remember { mutableStateOf("") } var description by remember { mutableStateOf("") } - var dateText by remember { mutableStateOf("") } - var timeText by remember { mutableStateOf("") } + var dateText by remember { mutableStateOf("") } + var timeText by remember { mutableStateOf("") } - val locationQuery = remember { mutableStateOf("") } - val queryFromViewModel by locationViewModel.query.collectAsState() + val locationQuery = remember { mutableStateOf("") } + val queryFromViewModel by locationViewModel.query.collectAsState() - LaunchedEffect(queryFromViewModel) { - locationQuery.value = queryFromViewModel - } + LaunchedEffect(queryFromViewModel) { locationQuery.value = queryFromViewModel } var showDropdown by remember { mutableStateOf(false) } val locationSuggestions by locationViewModel.locationSuggestions.collectAsState(initial = emptyList()) @@ -103,51 +101,51 @@ fun AddActivityScreen( placeholder = { Text("Description") }, modifier = Modifier.testTag("descriptionField").fillMaxWidth()) - // Date input - DateTimeInputField( - value = dateText, - onValueChange = { newDate -> + // Date input + DateTimeInputField( + value = dateText, + onValueChange = { newDate -> if (newDate.isDigitsOnly() && newDate.length <= 8) { // Validate date format - dateText = newDate + dateText = newDate } - }, - label = "Date", - placeholder = "DD/MM/YYYY", - visualTransformation = DateVisualTransformation(), - keyboardType = KeyboardType.Number, - onDatePickerClick = { + }, + label = "Date", + placeholder = "DD/MM/YYYY", + visualTransformation = DateVisualTransformation(), + keyboardType = KeyboardType.Number, + onDatePickerClick = { dateTimeUtils.showDatePicker(context) { selectedDate -> - dateText = selectedDate.replace("/", "") // Set the selected date + dateText = selectedDate.replace("/", "") // Set the selected date } - }, - onTimePickerClick = { /* Empty, not needed for date */ }, - isTime = false // Specify this is a date picker - ) - - // Time input - DateTimeInputField( - value = timeText, - onValueChange = { timeText = it }, - label = "Time", - placeholder = "HH:mm", - visualTransformation = VisualTransformation.None, - keyboardType = KeyboardType.Number, - onDatePickerClick = { /* Empty, not needed for time */ }, - onTimePickerClick = { + }, + onTimePickerClick = { /* Empty, not needed for date */}, + isTime = false // Specify this is a date picker + ) + + // Time input + DateTimeInputField( + value = timeText, + onValueChange = { timeText = it }, + label = "Time", + placeholder = "HH:mm", + visualTransformation = VisualTransformation.None, + keyboardType = KeyboardType.Number, + onDatePickerClick = { /* Empty, not needed for time */}, + onTimePickerClick = { dateTimeUtils.showTimePicker(context) { selectedTime -> - timeText = selectedTime // Set the selected time + timeText = selectedTime // Set the selected time } - }, - isTime = true // Specify this is a time picker - ) + }, + isTime = true // Specify this is a time picker + ) LocationInputField( - locationQuery = locationQuery, - locationSuggestions = locationSuggestions, - showDropdown = showDropdown, - locationViewModel = locationViewModel, - setSelectedLocation = { selectedLocation = it }, - setShowDropdown = { showDropdown = it }) + locationQuery = locationQuery, + locationSuggestions = locationSuggestions, + showDropdown = showDropdown, + locationViewModel = locationViewModel, + setSelectedLocation = { selectedLocation = it }, + setShowDropdown = { showDropdown = it }) Button( enabled = title.isNotBlank() && description.isNotBlank() && dateText.isNotBlank(), diff --git a/app/src/main/java/com/github/se/travelpouch/ui/dashboard/EditActivity.kt b/app/src/main/java/com/github/se/travelpouch/ui/dashboard/EditActivity.kt index 8e5de5ea..23ab6d66 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/dashboard/EditActivity.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/dashboard/EditActivity.kt @@ -59,32 +59,36 @@ import java.util.Locale */ @OptIn(ExperimentalMaterial3Api::class) @Composable -fun EditActivity(navigationActions: NavigationActions, activityViewModel: ActivityViewModel, locationViewModel: LocationViewModel) { +fun EditActivity( + navigationActions: NavigationActions, + activityViewModel: ActivityViewModel, + locationViewModel: LocationViewModel +) { val context = LocalContext.current val selectedActivity = activityViewModel.selectedActivity.collectAsState() - val zoneId = ZoneId.systemDefault() - val timeHour = ZonedDateTime.ofInstant(selectedActivity.value!!.date.toDate().toInstant(), zoneId) + val zoneId = ZoneId.systemDefault() + val timeHour = ZonedDateTime.ofInstant(selectedActivity.value!!.date.toDate().toInstant(), zoneId) var title by remember { mutableStateOf(selectedActivity.value!!.title) } var description by remember { mutableStateOf(selectedActivity.value!!.description) } - val time = timeHour.format(DateTimeFormatter.ofPattern("HH:mm")) - var timeText by remember { mutableStateOf(time) } - var date by remember { + val time = timeHour.format(DateTimeFormatter.ofPattern("HH:mm")) + var timeText by remember { mutableStateOf(time) } + var date by remember { mutableStateOf(convertDateToString(selectedActivity.value!!.date.toDate())) } - var selectedLocation by remember { mutableStateOf(selectedActivity.value!!.location) } - val locationQuery = remember { - mutableStateOf(selectedActivity.value!!.location.name) - } // Use mutable state for location query - // locationViewModel.setQuery(selectedTravel!!.location.name) - var showDropdown by remember { mutableStateOf(false) } - val locationSuggestions by - locationViewModel.locationSuggestions.collectAsState(initial = emptyList()) + var selectedLocation by remember { mutableStateOf(selectedActivity.value!!.location) } + val locationQuery = remember { + mutableStateOf(selectedActivity.value!!.location.name) + } // Use mutable state for location query + // locationViewModel.setQuery(selectedTravel!!.location.name) + var showDropdown by remember { mutableStateOf(false) } + val locationSuggestions by + locationViewModel.locationSuggestions.collectAsState(initial = emptyList()) - val dateTimeUtils = DateTimeUtils("dd/MM/yyyy HH:mm") + val dateTimeUtils = DateTimeUtils("dd/MM/yyyy HH:mm") Scaffold( modifier = Modifier.testTag("EditActivityScreen"), @@ -123,52 +127,51 @@ fun EditActivity(navigationActions: NavigationActions, activityViewModel: Activi label = { Text("Description") }, modifier = Modifier.fillMaxWidth().testTag("descriptionField")) - // Date Input - DateTimeInputField( - value = date, - onValueChange = { newDate -> + // Date Input + DateTimeInputField( + value = date, + onValueChange = { newDate -> if (newDate.isDigitsOnly() && newDate.length <= 8) { // Validate date format - date = newDate + date = newDate } - }, - label = "Date", - placeholder = "DD/MM/YYYY", - visualTransformation = DateVisualTransformation(), - keyboardType = KeyboardType.Number, - onDatePickerClick = { + }, + label = "Date", + placeholder = "DD/MM/YYYY", + visualTransformation = DateVisualTransformation(), + keyboardType = KeyboardType.Number, + onDatePickerClick = { dateTimeUtils.showDatePicker(context) { selectedDate -> - date = selectedDate.replace("/", "") // Set the selected date + date = selectedDate.replace("/", "") // Set the selected date } - }, - onTimePickerClick = { /* Empty, not needed for date */ }, - isTime = false // Specify this is a date picker - ) + }, + onTimePickerClick = { /* Empty, not needed for date */}, + isTime = false // Specify this is a date picker + ) - // Time Input - DateTimeInputField( - value = timeText, - onValueChange = { timeText = it }, - label = "Time", - placeholder = "HH:mm", - visualTransformation = VisualTransformation.None, - keyboardType = KeyboardType.Number, - onDatePickerClick = { /* Empty, not needed for time */ }, - onTimePickerClick = { + // Time Input + DateTimeInputField( + value = timeText, + onValueChange = { timeText = it }, + label = "Time", + placeholder = "HH:mm", + visualTransformation = VisualTransformation.None, + keyboardType = KeyboardType.Number, + onDatePickerClick = { /* Empty, not needed for time */}, + onTimePickerClick = { dateTimeUtils.showTimePicker(context) { selectedTime -> - timeText = selectedTime // Set the selected time + timeText = selectedTime // Set the selected time } - }, - isTime = true // Specify this is a time picker - ) + }, + isTime = true // Specify this is a time picker + ) - LocationInputField( - locationQuery = locationQuery, - locationSuggestions = locationSuggestions, - showDropdown = showDropdown, - setShowDropdown = { showDropdown = it }, - locationViewModel = locationViewModel, - setSelectedLocation = { selectedLocation = it } - ) + LocationInputField( + locationQuery = locationQuery, + locationSuggestions = locationSuggestions, + showDropdown = showDropdown, + setShowDropdown = { showDropdown = it }, + locationViewModel = locationViewModel, + setSelectedLocation = { selectedLocation = it }) Button( enabled = @@ -187,27 +190,27 @@ fun EditActivity(navigationActions: NavigationActions, activityViewModel: Activi // cases) } - val finalDate = - dateTimeUtils.convertStringToTimestamp("$formattedDateText $timeText") - val newLocation: Location - try { - newLocation = - Location( - latitude = selectedLocation.latitude, - longitude = selectedLocation.longitude, - name = selectedLocation.name, - insertTime = Timestamp.now()) - } catch (e: NumberFormatException) { - Toast.makeText( + val finalDate = + dateTimeUtils.convertStringToTimestamp("$formattedDateText $timeText") + val newLocation: Location + try { + newLocation = + Location( + latitude = selectedLocation.latitude, + longitude = selectedLocation.longitude, + name = selectedLocation.name, + insertTime = Timestamp.now()) + } catch (e: NumberFormatException) { + Toast.makeText( context, "Error: latitude and longitude must be numbers", Toast.LENGTH_SHORT) - .show() - return@Button - } catch (e: IllegalArgumentException) { - Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show() - return@Button - } + .show() + return@Button + } catch (e: IllegalArgumentException) { + Toast.makeText(context, "Error: ${e.message}", Toast.LENGTH_SHORT).show() + return@Button + } if (finalDate == null) { Log.e("EditActivityScreen", "Invalid date or format") diff --git a/app/src/main/java/com/github/se/travelpouch/ui/dashboard/TravelActivity.kt b/app/src/main/java/com/github/se/travelpouch/ui/dashboard/TravelActivity.kt index 326d1300..87a3fcea 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/dashboard/TravelActivity.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/dashboard/TravelActivity.kt @@ -94,35 +94,38 @@ fun TravelActivitiesScreen( .padding(horizontal = 16.dp) .testTag("activityColumn")) { if (listOfActivities.value.isEmpty()) { - item( - key = "empty_travel", // You can provide a key if necessary - contentType = "textItem" // Optional, can be useful for dynamic lists - ) { + item( + key = "empty_travel", // You can provide a key if necessary + contentType = "textItem" // Optional, can be useful for dynamic lists + ) { // Centering text in the LazyItemScope Box( - modifier = Modifier - .fillMaxSize() // Fill the available space - .padding(16.dp).testTag("emptyTravelBox") // Optional padding, adjust as needed - ) { - Text( - text = "No activities planned", // Add newline - style = MaterialTheme.typography.bodyLarge.copy( - fontWeight = FontWeight.Bold - ), - color = MaterialTheme.colorScheme.onBackground, - modifier = Modifier.align(Alignment.Center).testTag("emptyTravel1") - ) - Text( - text = "for this trip", - style = MaterialTheme.typography.bodyLarge.copy( - fontWeight = FontWeight.Bold - ), - color = MaterialTheme.colorScheme.onBackground, - modifier = Modifier.align(Alignment.Center).padding(top = 42.dp).testTag("emptyTravel2") - ) - } - } - + modifier = + Modifier.fillMaxSize() // Fill the available space + .padding(16.dp) + .testTag( + "emptyTravelBox") // Optional padding, adjust as needed + ) { + Text( + text = "No activities planned", // Add newline + style = + MaterialTheme.typography.bodyLarge.copy( + fontWeight = FontWeight.Bold), + color = MaterialTheme.colorScheme.onBackground, + modifier = + Modifier.align(Alignment.Center).testTag("emptyTravel1")) + Text( + text = "for this trip", + style = + MaterialTheme.typography.bodyLarge.copy( + fontWeight = FontWeight.Bold), + color = MaterialTheme.colorScheme.onBackground, + modifier = + Modifier.align(Alignment.Center) + .padding(top = 42.dp) + .testTag("emptyTravel2")) + } + } } else { items(listOfActivities.value.size) { idx -> ActivityItem( diff --git a/app/src/main/java/com/github/se/travelpouch/ui/fields/DateTimeInputField.kt b/app/src/main/java/com/github/se/travelpouch/ui/fields/DateTimeInputField.kt index 41c688ed..6a9a2980 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/fields/DateTimeInputField.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/fields/DateTimeInputField.kt @@ -27,31 +27,28 @@ fun DateTimeInputField( onTimePickerClick: () -> Unit, isTime: Boolean = false // Flag to distinguish between date and time ) { - OutlinedTextField( - value = value, - onValueChange = onValueChange, - enabled = true, - label = { Text(label) }, - placeholder = { Text(placeholder) }, - visualTransformation = visualTransformation, - keyboardOptions = KeyboardOptions(keyboardType = keyboardType), - modifier = Modifier.fillMaxWidth().testTag("${label.toLowerCase()}Field"), - trailingIcon = { - IconButton( - onClick = { - if (isTime) { - onTimePickerClick() // Show time picker - } else { - onDatePickerClick() // Show date picker - } - }, - modifier = Modifier.testTag("${label.toLowerCase()}PickerButton") - ) { - Icon( - imageVector = if (isTime) Icons.Filled.AccessTime else Icons.Default.DateRange, - contentDescription = "Select $label" - ) + OutlinedTextField( + value = value, + onValueChange = onValueChange, + enabled = true, + label = { Text(label) }, + placeholder = { Text(placeholder) }, + visualTransformation = visualTransformation, + keyboardOptions = KeyboardOptions(keyboardType = keyboardType), + modifier = Modifier.fillMaxWidth().testTag("${label.toLowerCase()}Field"), + trailingIcon = { + IconButton( + onClick = { + if (isTime) { + onTimePickerClick() // Show time picker + } else { + onDatePickerClick() // Show date picker + } + }, + modifier = Modifier.testTag("${label.toLowerCase()}PickerButton")) { + Icon( + imageVector = if (isTime) Icons.Filled.AccessTime else Icons.Default.DateRange, + contentDescription = "Select $label") } - } - ) + }) } diff --git a/app/src/main/java/com/github/se/travelpouch/ui/fields/LocationQueryField.kt b/app/src/main/java/com/github/se/travelpouch/ui/fields/LocationQueryField.kt index c1cf02be..0c9d14b3 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/fields/LocationQueryField.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/fields/LocationQueryField.kt @@ -27,60 +27,51 @@ fun LocationInputField( setShowDropdown: (Boolean) -> Unit, // Function to update showDropdown setSelectedLocation: (Location) -> Unit // Function to update selectedLocation ) { - Box(modifier = Modifier.fillMaxWidth()) { - OutlinedTextField( - value = locationQuery.value, - onValueChange = { - locationQuery.value = it - locationViewModel.setQuery(it) // Update the query in the ViewModel - setShowDropdown(true) // Show the dropdown when the text changes - }, - label = { Text("Location") }, - placeholder = { Text("Enter an Address or Location") }, - modifier = Modifier.fillMaxWidth().testTag("inputTravelLocation") - ) + Box(modifier = Modifier.fillMaxWidth()) { + OutlinedTextField( + value = locationQuery.value, + onValueChange = { + locationQuery.value = it + locationViewModel.setQuery(it) // Update the query in the ViewModel + setShowDropdown(true) // Show the dropdown when the text changes + }, + label = { Text("Location") }, + placeholder = { Text("Enter an Address or Location") }, + modifier = Modifier.fillMaxWidth().testTag("inputTravelLocation")) - // Dropdown for location suggestions - DropdownMenu( - expanded = showDropdown && locationSuggestions.isNotEmpty(), - onDismissRequest = { setShowDropdown(false) }, // Hide dropdown when dismissed - properties = PopupProperties(focusable = false), - modifier = Modifier - .fillMaxWidth(1f) - .heightIn(max = 200.dp) - .testTag("locationDropdownMenu") - ) { - locationSuggestions.filterNotNull().take(3).forEach { location -> - DropdownMenuItem( - text = { - Text( - text = location.name.take(30) + - if (location.name.length > 30) "..." else "", - maxLines = 1, - modifier = Modifier.testTag("suggestionText_${location.name}") - ) - }, - onClick = { - locationViewModel.setQuery(location.name) // Set query to the selected location name - setSelectedLocation(location) // Update the selected location - locationQuery.value = location.name // Update the input field with the selected location name - setShowDropdown(false) // Close dropdown after selection - }, - modifier = Modifier - .padding(8.dp) - .testTag("suggestion_${location.name}") - ) - HorizontalDivider() // Separate items with a divider - } + // Dropdown for location suggestions + DropdownMenu( + expanded = showDropdown && locationSuggestions.isNotEmpty(), + onDismissRequest = { setShowDropdown(false) }, // Hide dropdown when dismissed + properties = PopupProperties(focusable = false), + modifier = + Modifier.fillMaxWidth(1f).heightIn(max = 200.dp).testTag("locationDropdownMenu")) { + locationSuggestions.filterNotNull().take(3).forEach { location -> + DropdownMenuItem( + text = { + Text( + text = location.name.take(30) + if (location.name.length > 30) "..." else "", + maxLines = 1, + modifier = Modifier.testTag("suggestionText_${location.name}")) + }, + onClick = { + locationViewModel.setQuery( + location.name) // Set query to the selected location name + setSelectedLocation(location) // Update the selected location + locationQuery.value = + location.name // Update the input field with the selected location name + setShowDropdown(false) // Close dropdown after selection + }, + modifier = Modifier.padding(8.dp).testTag("suggestion_${location.name}")) + HorizontalDivider() // Separate items with a divider + } - if (locationSuggestions.size > 3) { - DropdownMenuItem( - text = { Text("More...") }, - onClick = { /* Optionally show more results */ }, - modifier = Modifier.padding(8.dp).testTag("moreSuggestions") - ) - } + if (locationSuggestions.size > 3) { + DropdownMenuItem( + text = { Text("More...") }, + onClick = { /* Optionally show more results */}, + modifier = Modifier.padding(8.dp).testTag("moreSuggestions")) + } } - } + } } - diff --git a/app/src/main/java/com/github/se/travelpouch/ui/home/TravelList.kt b/app/src/main/java/com/github/se/travelpouch/ui/home/TravelList.kt index 7470400f..93dadeda 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/home/TravelList.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/home/TravelList.kt @@ -31,7 +31,6 @@ import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Adjust import androidx.compose.material.icons.filled.Close import androidx.compose.material.icons.filled.Menu -import androidx.compose.material.icons.filled.StopCircle import androidx.compose.material3.Card import androidx.compose.material3.CardColors import androidx.compose.material3.CircularProgressIndicator @@ -371,17 +370,17 @@ fun TravelItem(travelContainer: TravelContainer, onClick: () -> Unit) { Row( modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) { - Text( - text = travelContainer.title, - style = MaterialTheme.typography.bodyMedium, - fontWeight = FontWeight.Bold) + Text( + text = travelContainer.title, + style = MaterialTheme.typography.bodyMedium, + fontWeight = FontWeight.Bold) Row(verticalAlignment = Alignment.CenterVertically) { - Text( - text = - SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) - .format(travelContainer.startTime.toDate()), - style = MaterialTheme.typography.bodySmall) + Text( + text = + SimpleDateFormat("dd/MM/yyyy", Locale.getDefault()) + .format(travelContainer.startTime.toDate()), + style = MaterialTheme.typography.bodySmall) Icon( imageVector = Icons.AutoMirrored.Filled.KeyboardArrowRight, contentDescription = null) @@ -391,9 +390,10 @@ fun TravelItem(travelContainer: TravelContainer, onClick: () -> Unit) { Spacer(modifier = Modifier.height(4.dp)) // Description - Text(text = travelContainer.description, + Text( + text = travelContainer.description, style = MaterialTheme.typography.bodySmall, - fontWeight = FontWeight.Normal) + fontWeight = FontWeight.Normal) // Location Name Text( diff --git a/app/src/main/java/com/github/se/travelpouch/ui/travel/EditTravelSettings.kt b/app/src/main/java/com/github/se/travelpouch/ui/travel/EditTravelSettings.kt index 64397ff2..c0b33628 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/travel/EditTravelSettings.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/travel/EditTravelSettings.kt @@ -234,7 +234,10 @@ fun EditTravelSettingsScreen( OutlinedTextField( value = titleText.value, onValueChange = { keystroke -> titleText.value = keystroke }, - modifier = Modifier.testTag("inputTravelTitle").fillMaxWidth(1f).padding(horizontal = 12.dp), + modifier = + Modifier.testTag("inputTravelTitle") + .fillMaxWidth(1f) + .padding(horizontal = 12.dp), label = { Text("Title") }, placeholder = { Text("Name the Travel") }, shape = RoundedCornerShape(6.dp), @@ -242,7 +245,10 @@ fun EditTravelSettingsScreen( OutlinedTextField( value = descriptionText.value, onValueChange = { keystroke -> descriptionText.value = keystroke }, - modifier = Modifier.testTag("inputTravelDescription").fillMaxWidth(1f).padding(horizontal = 12.dp), + modifier = + Modifier.testTag("inputTravelDescription") + .fillMaxWidth(1f) + .padding(horizontal = 12.dp), label = { Text("Description") }, placeholder = { Text("Describe the Travel") }, shape = RoundedCornerShape(6.dp)) @@ -257,7 +263,10 @@ fun EditTravelSettingsScreen( }, label = { Text("Location") }, placeholder = { Text("Enter an Address or Location") }, - modifier = Modifier.fillMaxWidth().padding(horizontal = 12.dp).testTag("inputTravelLocation")) + modifier = + Modifier.fillMaxWidth() + .padding(horizontal = 12.dp) + .testTag("inputTravelLocation")) // Dropdown for location suggestions DropdownMenu( @@ -307,7 +316,10 @@ fun EditTravelSettingsScreen( onValueChange = { keystroke -> startTime.value = keystroke }, // Allow manual input label = { Text("Start Date") }, placeholder = { Text("DD/MM/YYYY") }, - modifier = Modifier.testTag("inputTravelStartTime").fillMaxWidth(1f).padding(horizontal = 12.dp), + modifier = + Modifier.testTag("inputTravelStartTime") + .fillMaxWidth(1f) + .padding(horizontal = 12.dp), shape = RoundedCornerShape(6.dp), trailingIcon = { IconButton( @@ -330,7 +342,10 @@ fun EditTravelSettingsScreen( }, // Allow manual input label = { Text("End Date") }, placeholder = { Text("DD/MM/YYYY") }, - modifier = Modifier.testTag("inputTravelEndTime").fillMaxWidth(1f).padding(horizontal = 12.dp), + modifier = + Modifier.testTag("inputTravelEndTime") + .fillMaxWidth(1f) + .padding(horizontal = 12.dp), shape = RoundedCornerShape(6.dp), trailingIcon = { IconButton( diff --git a/app/src/main/java/com/github/se/travelpouch/ui/travel/ParticipantListScreen.kt b/app/src/main/java/com/github/se/travelpouch/ui/travel/ParticipantListScreen.kt index 43f403fe..1ad3a391 100644 --- a/app/src/main/java/com/github/se/travelpouch/ui/travel/ParticipantListScreen.kt +++ b/app/src/main/java/com/github/se/travelpouch/ui/travel/ParticipantListScreen.kt @@ -315,7 +315,8 @@ fun ParticipantListScreen( listTravelViewModel.updateTravel( updatedContainer, TravelRepository.UpdateMode.REMOVE_PARTICIPANT, - participant.key, eventViewModel.getNewDocumentReference()) + participant.key, + eventViewModel.getNewDocumentReference()) listTravelViewModel.selectTravel(updatedContainer) listTravelViewModel.fetchAllParticipantsInfo() setExpanded(false) From 7bb4838d6a6008dbbb42dc51b06e5aa4beeb4a8e Mon Sep 17 00:00:00 2001 From: yassine04e Date: Thu, 12 Dec 2024 23:29:28 +0100 Subject: [PATCH 11/19] deleting a user after travelCreationTravel e2e --- .../se/travelpouch/e2e/DocumentUpload.kt | 6 +-- .../se/travelpouch/e2e/TravelCreation.kt | 51 +++++++++++++++++-- 2 files changed, 48 insertions(+), 9 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt index 050739a4..caa9b995 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/DocumentUpload.kt @@ -24,7 +24,6 @@ import com.github.se.travelpouch.di.AppModule import com.google.firebase.Timestamp import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore -import com.google.firebase.storage.FirebaseStorage import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.UninstallModules @@ -57,7 +56,6 @@ class DocumentUpload { @get:Rule(order = 2) val intentsTestRule = IntentsTestRule(MainActivity::class.java) @Inject lateinit var firestore: FirebaseFirestore - @Inject lateinit var storage: FirebaseStorage @Inject lateinit var auth: FirebaseAuth @Before @@ -100,7 +98,7 @@ class DocumentUpload { .document(uid) .set( mapOf( - "email" to "example.example2.com", + "email" to "example2@example.com", "friends" to emptyList(), "fsUid" to uid, "name" to "Example", @@ -124,7 +122,7 @@ class DocumentUpload { fun tearDown() { runBlocking { auth.signOut() - auth.signInWithEmailAndPassword("example2@example.com", "password").await() + auth.signInWithEmailAndPassword("example2@example.com", "password2").await() val uid = auth.currentUser!!.uid auth.currentUser!!.delete().await() diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt index b06d8d9d..d9e8ec9c 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/TravelCreation.kt @@ -1,5 +1,6 @@ package com.github.se.travelpouch.e2e +import androidx.compose.ui.test.ExperimentalTestApi import androidx.compose.ui.test.assert import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.assertTextEquals @@ -11,14 +12,17 @@ import androidx.compose.ui.test.onNodeWithTag import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.compose.ui.test.performTextInput +import androidx.test.espresso.intent.rule.IntentsTestRule import com.github.se.travelpouch.MainActivity import com.github.se.travelpouch.di.AppModule +import com.google.firebase.auth.FirebaseAuth import com.google.firebase.firestore.FirebaseFirestore import dagger.hilt.android.testing.HiltAndroidRule import dagger.hilt.android.testing.HiltAndroidTest import dagger.hilt.android.testing.UninstallModules import javax.inject.Inject import kotlin.time.Duration.Companion.seconds +import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.runBlocking import kotlinx.coroutines.tasks.await import kotlinx.coroutines.test.runTest @@ -35,13 +39,50 @@ class TravelCreation { @get:Rule(order = 0) val hiltRule = HiltAndroidRule(this) - @get:Rule(order = 1) val composeTestRule = createAndroidComposeRule() + @OptIn(ExperimentalTestApi::class) + @get:Rule(order = 1) + val composeTestRule = + createAndroidComposeRule(effectContext = Dispatchers.Main.immediate) + + @get:Rule(order = 2) val intentsTestRule = IntentsTestRule(MainActivity::class.java) @Inject lateinit var firestore: FirebaseFirestore + @Inject lateinit var auth: FirebaseAuth + + // @Before + // fun setUp() { + // hiltRule.inject() + // } @Before fun setUp() { hiltRule.inject() + + // seed DB with existing trave @Inject lateinit var auth: FirebaseAuthl and user + runBlocking { + val uid = + auth + .createUserWithEmailAndPassword("example1@example.com", "password1") + .await() + .user!! + .uid + + firestore + .collection("userslist") + .document(uid) + .set( + mapOf( + "email" to "example1@example.com", + "friends" to emptyList(), + "fsUid" to uid, + "name" to "Example", + "username" to "example1", + "userTravelList" to listOf("w2HGCwaJ4KgcXJ5nVxkF"), + "needsOnboarding" to true)) + .await() + + auth.signOut() + } } @After @@ -63,11 +104,11 @@ class TravelCreation { composeTestRule.onNodeWithTag("emailField").assertIsDisplayed() composeTestRule.onNodeWithTag("passwordField").assertIsDisplayed() - composeTestRule.onNodeWithText("Sign up").assertIsDisplayed() + composeTestRule.onNodeWithText("Log in").assertIsDisplayed() - composeTestRule.onNodeWithTag("emailField").performTextInput("travelpouchtest2@gmail.com") - composeTestRule.onNodeWithTag("passwordField").performTextInput("travelpouchtest2password") - composeTestRule.onNodeWithText("Sign up").performClick() + composeTestRule.onNodeWithTag("emailField").performTextInput("example1@example.com") + composeTestRule.onNodeWithTag("passwordField").performTextInput("password1") + composeTestRule.onNodeWithText("Log in").performClick() // Skip onboarding composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { From ce19784a02d13c4817c7bb86406864b3fd8120a8 Mon Sep 17 00:00:00 2001 From: yassine04e Date: Thu, 12 Dec 2024 23:40:13 +0100 Subject: [PATCH 12/19] chore: adding diffrent timeouts in activity e2e to diffrenciate the failure on the CI --- .../com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt index 13d3f705..593fe350 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt @@ -149,7 +149,7 @@ class ActivityCreationAndEdit { composeTestRule.onNodeWithTag("travelListItem").performClick() // assert that there are no activities at the moment - composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT + 1) { composeTestRule.onNodeWithTag("emptyTravel", useUnmergedTree = true).isDisplayed() } @@ -186,7 +186,7 @@ class ActivityCreationAndEdit { // save it composeTestRule.onNodeWithText("Save").assertIsDisplayed().performClick() // there is an activity - composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT) { + composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT + 2) { composeTestRule.onNodeWithTag("emptyTravel", useUnmergedTree = true).isNotDisplayed() } // check the activity is displayed From 1cf1f0a3681689c34af96caa3954583cb7546aa3 Mon Sep 17 00:00:00 2001 From: yassine04e Date: Thu, 12 Dec 2024 23:50:05 +0100 Subject: [PATCH 13/19] fix: correcting the test tag on the activity e2e when no activities planned --- .../com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt index 593fe350..3c33750c 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt @@ -150,7 +150,7 @@ class ActivityCreationAndEdit { // assert that there are no activities at the moment composeTestRule.waitUntil(timeoutMillis = DEFAULT_TIMEOUT + 1) { - composeTestRule.onNodeWithTag("emptyTravel", useUnmergedTree = true).isDisplayed() + composeTestRule.onNodeWithTag("emptyTravelBox", useUnmergedTree = true).isDisplayed() } // add an activity button From 666cc435097a5270b3938816eaedfeb07e1242ee Mon Sep 17 00:00:00 2001 From: yassine04e Date: Fri, 13 Dec 2024 00:10:43 +0100 Subject: [PATCH 14/19] fix: Downloading the firebase emulators with npm and not curl --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index c3b965af..8b82c522 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -103,7 +103,7 @@ jobs: - name: Install Firebase CLI run: | - curl -sL https://firebase.tools | bash + npm install -g firebase-tools - name: Build functions run: | From 2127110af030551888bfd0cfa9aaaf59c5577431 Mon Sep 17 00:00:00 2001 From: RemIsMyWaifuu Date: Fri, 13 Dec 2024 00:17:15 +0100 Subject: [PATCH 15/19] test(e2e): Fix E2E tags due to refactor Fix E2E test tag by replacing it with the new proper name from main. --- .../com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt index 3c33750c..ad3f57c6 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt @@ -203,7 +203,7 @@ class ActivityCreationAndEdit { .onNodeWithTag("descriptionField") .assert(hasText("this is an epic activity")) composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) - composeTestRule.onNodeWithTag("locationField").assert(hasText("La Paz, Bolivia")) + composeTestRule.onNodeWithTag("inputTravelLocation").assert(hasText("La Paz, Bolivia")) composeTestRule.onNodeWithTag("titleField").performTextClearance() composeTestRule.onNodeWithTag("titleField").performTextInput("more epic activity") From 93380cf204ba7eb35bb63bb858fc8aab1d463b02 Mon Sep 17 00:00:00 2001 From: RemIsMyWaifuu Date: Fri, 13 Dec 2024 00:19:20 +0100 Subject: [PATCH 16/19] chore(e2e): Rename test file Rename ActivityCreationAndEdit by removing an unnecessary U. Looks cleaner. --- .../{UActivityCreationAndEdit.kt => ActivityCreationAndEdit.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/androidTest/java/com/github/se/travelpouch/e2e/{UActivityCreationAndEdit.kt => ActivityCreationAndEdit.kt} (100%) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt similarity index 100% rename from app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt rename to app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt From d7b9f6297c7ae5806fd540c6dfd66950a7622a7d Mon Sep 17 00:00:00 2001 From: yassine04e Date: Fri, 13 Dec 2024 00:20:48 +0100 Subject: [PATCH 17/19] fix: removing the test on the fields in activity e2e --- .../se/travelpouch/e2e/UActivityCreationAndEdit.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt index 3c33750c..c1277176 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/UActivityCreationAndEdit.kt @@ -198,12 +198,12 @@ class ActivityCreationAndEdit { // edit the activity composeTestRule.onNodeWithText("epic activity").performClick() composeTestRule.onNodeWithTag("EditActivityScreen").assertIsDisplayed() - composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) - composeTestRule - .onNodeWithTag("descriptionField") - .assert(hasText("this is an epic activity")) - composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) - composeTestRule.onNodeWithTag("locationField").assert(hasText("La Paz, Bolivia")) + // composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) + // composeTestRule + // .onNodeWithTag("descriptionField") + // .assert(hasText("this is an epic activity")) + // composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) + // composeTestRule.onNodeWithTag("locationField").assert(hasText("La Paz, Bolivia")) composeTestRule.onNodeWithTag("titleField").performTextClearance() composeTestRule.onNodeWithTag("titleField").performTextInput("more epic activity") From a05985e8eed8d846f2c50206c75f8f9accafc8e6 Mon Sep 17 00:00:00 2001 From: RemIsMyWaifuu Date: Fri, 13 Dec 2024 00:49:25 +0100 Subject: [PATCH 18/19] chore(e2e): Uncomment test tags and checks Uncomment previously commented checks, by providing the correct test tag. --- .../se/travelpouch/e2e/ActivityCreationAndEdit.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt index c1277176..71cb1bc6 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt @@ -198,12 +198,12 @@ class ActivityCreationAndEdit { // edit the activity composeTestRule.onNodeWithText("epic activity").performClick() composeTestRule.onNodeWithTag("EditActivityScreen").assertIsDisplayed() - // composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) - // composeTestRule - // .onNodeWithTag("descriptionField") - // .assert(hasText("this is an epic activity")) - // composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) - // composeTestRule.onNodeWithTag("locationField").assert(hasText("La Paz, Bolivia")) + composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) + composeTestRule + .onNodeWithTag("descriptionField") + .assert(hasText("this is an epic activity")) + composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) + composeTestRule.onNodeWithTag("inputTravelLocation").assert(hasText("La Paz, Bolivia")) composeTestRule.onNodeWithTag("titleField").performTextClearance() composeTestRule.onNodeWithTag("titleField").performTextInput("more epic activity") From 29369ba680cc1f2bde79f93e4ef7b4657e6be810 Mon Sep 17 00:00:00 2001 From: RemIsMyWaifuu Date: Fri, 13 Dec 2024 00:50:24 +0100 Subject: [PATCH 19/19] chore(e2e): Ktfmt format compliance Ktfmt format in order to comply with the project's linting standards. --- .../se/travelpouch/e2e/ActivityCreationAndEdit.kt | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt index 71cb1bc6..ad3f57c6 100644 --- a/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt +++ b/app/src/androidTest/java/com/github/se/travelpouch/e2e/ActivityCreationAndEdit.kt @@ -198,12 +198,12 @@ class ActivityCreationAndEdit { // edit the activity composeTestRule.onNodeWithText("epic activity").performClick() composeTestRule.onNodeWithTag("EditActivityScreen").assertIsDisplayed() - composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) - composeTestRule - .onNodeWithTag("descriptionField") - .assert(hasText("this is an epic activity")) - composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) - composeTestRule.onNodeWithTag("inputTravelLocation").assert(hasText("La Paz, Bolivia")) + composeTestRule.onNodeWithTag("titleField").assert(hasText("epic activity")) + composeTestRule + .onNodeWithTag("descriptionField") + .assert(hasText("this is an epic activity")) + composeTestRule.onNodeWithTag("dateField").assert(hasText("01/02/2024")) + composeTestRule.onNodeWithTag("inputTravelLocation").assert(hasText("La Paz, Bolivia")) composeTestRule.onNodeWithTag("titleField").performTextClearance() composeTestRule.onNodeWithTag("titleField").performTextInput("more epic activity")