Skip to content

Commit

Permalink
test: add end-to-end tests for Map and Photo Posting flows
Browse files Browse the repository at this point in the history
- Add Map end-to-end test to verify:
  - GLSurfaceView display
  - Zoom slider functionality
  - Camera movement and touch interactions
  - Planet interaction
  - Navigation back to menu

- Add Photo Posting end-to-end test (renamed from googleMapFlow) to verify:
  - User authentication flow
  - Navigation to Google Map
  - Camera capture functionality
  - Image review and posting process
  - Successful return to map screen after posting

- Add "profile_screen" test tag to Profile screen for test verification
  • Loading branch information
rihabbelmekki committed Dec 19, 2024
1 parent a47bc44 commit 1fd5991
Show file tree
Hide file tree
Showing 2 changed files with 197 additions and 2 deletions.
Original file line number Diff line number Diff line change
@@ -1,21 +1,33 @@
package com.github.lookupgroup27.lookup

import android.Manifest
import android.annotation.SuppressLint
import android.util.Log
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.semantics.SemanticsProperties
import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.hasText
import androidx.compose.ui.test.isNotDisplayed
import androidx.compose.ui.test.junit4.createAndroidComposeRule
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithContentDescription
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.onRoot
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performGesture
import androidx.compose.ui.test.performScrollTo
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.performTouchInput
import androidx.compose.ui.test.printToLog
import androidx.test.espresso.action.ViewActions.swipeDown
import androidx.test.espresso.action.ViewActions.swipeUp
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.rule.GrantPermissionRule
import com.github.lookupgroup27.lookup.ui.navigation.TopLevelDestinations
import com.google.firebase.auth.FirebaseAuth
import io.github.kakaocup.kakao.common.utilities.getResourceString
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
Expand All @@ -27,7 +39,8 @@ class End2EndTest {

@get:Rule
val permissionRule: GrantPermissionRule =
GrantPermissionRule.grant(Manifest.permission.ACCESS_FINE_LOCATION)
GrantPermissionRule.grant(
Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.CAMERA)

@Test
fun quizFlow() {
Expand Down Expand Up @@ -156,4 +169,185 @@ class End2EndTest {
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag("menu_screen").assertIsDisplayed()
}

@SuppressLint("CheckResult")
@Test
fun mapFlow() {
// Start by navigating to the map screen
composeTestRule.onNodeWithContentDescription("Home Icon").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithText("Sky Map").performClick()
composeTestRule.onNodeWithTag(TopLevelDestinations.SKY_MAP.textId).performClick()
// Verify initial map state with extended timeout
composeTestRule.waitUntil(timeoutMillis = 10000) {
composeTestRule.onAllNodesWithTag("glSurfaceView").fetchSemanticsNodes().size == 1
}

// Verify initial map state
composeTestRule.onNodeWithTag("glSurfaceView").assertIsDisplayed()
composeTestRule.onNodeWithTag("map_screen").assertIsDisplayed()
// Wait for zoom slider to be available and verify
composeTestRule.waitUntil(timeoutMillis = 10000) {
composeTestRule
.onAllNodesWithTag(getResourceString(R.string.map_slider_test_tag))
.fetchSemanticsNodes()
.size == 1
}

// Test zoom functionality using the slider
val zoomSlider = composeTestRule.onNodeWithTag(getResourceString(R.string.map_slider_test_tag))
zoomSlider.assertExists()
zoomSlider.assertIsDisplayed()

// Test minimum zoom (MAX_FOV)
zoomSlider.performGesture {
swipeDown() // Uses the GeneralSwipeAction with FAST speed
}
composeTestRule.waitForIdle()

// Test maximum zoom (MIN_FOV)
zoomSlider.performGesture {
swipeUp() // Uses the GeneralSwipeAction with FAST speed
}
composeTestRule.waitForIdle()

// Test reset functionality which should return to DEFAULT_FOV
composeTestRule.onNodeWithText("Reset").performClick()
composeTestRule.waitForIdle()

// Test planet interaction through touch
composeTestRule.onNodeWithTag("glSurfaceView").performTouchInput {
// Use pointerId 0 for primary touch
down(0, Offset(center.x, center.y))
up(0)
}
composeTestRule.waitForIdle()

// Test camera movement
composeTestRule.onNodeWithTag("glSurfaceView").performTouchInput {
down(0, Offset(center.x, center.y))
moveBy(Offset(100f, 100f))
up(0)
}
composeTestRule.waitForIdle()

// Verify screen orientation stays in portrait mode
composeTestRule.onNodeWithTag("glSurfaceView").assertIsDisplayed()

// Navigate back to menu
composeTestRule.onNodeWithText("Menu").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag("menu_screen").assertIsDisplayed()
}

@Test
fun photoPostingFlow() {

// Step 1: Navigate to Auth screen
composeTestRule.onNodeWithContentDescription("Home Icon").performClick()
composeTestRule.waitForIdle()

composeTestRule.onNodeWithTag("profile_button").performClick()
composeTestRule.waitForIdle()

// Wait for auth screen and verify it's displayed
composeTestRule.waitUntil(timeoutMillis = 5000) {
composeTestRule.onAllNodesWithTag("auth_screen").fetchSemanticsNodes().size == 1
}

// Click login and verify fields
composeTestRule.onNodeWithText("Login").performScrollTo().performClick()
composeTestRule.waitForIdle()

// Fill in login details with verification
composeTestRule.onNodeWithTag("email_field").performScrollTo().apply {
assertExists("Email field not found")
performTextInput("lookupswent@gmail.com")
}

composeTestRule.onNodeWithTag("password_field").performScrollTo().apply {
assertExists("Password field not found")
performTextInput("Test123!")
}

composeTestRule.waitForIdle()

// Submit login with extended waiting
composeTestRule.onNodeWithTag("login_button").performScrollTo()
composeTestRule.onNodeWithTag("login_button").performClick()

// Wait for login to complete and navigation to occur
composeTestRule.waitForIdle()
Thread.sleep(3000) // Give time for Firebase auth and navigation

// Try to verify profile screen multiple times
var profileScreenFound = false
repeat(3) { attempt ->
try {
composeTestRule.waitUntil(timeoutMillis = 5000) {
composeTestRule.onAllNodesWithTag("profile_screen").fetchSemanticsNodes().isNotEmpty()
}
composeTestRule.onNodeWithTag("profile_screen").assertIsDisplayed()
profileScreenFound = true
} catch (e: Exception) {
Log.e("TEST", "Failed to find profile screen on attempt ${attempt + 1}: ${e.message}")
if (attempt == 2) {
composeTestRule.onRoot().printToLog("SCREEN_STATE")
}
Thread.sleep(1000)
}
}

if (!profileScreenFound) {
throw AssertionError("Profile screen not found after multiple attempts")
}

// Step 3: Navigate to Google Map
composeTestRule.onNodeWithText("Menu").performClick()
composeTestRule.waitForIdle()
composeTestRule.onNodeWithTag("menu_screen").assertIsDisplayed()
composeTestRule.onNodeWithText("Google Map").performClick()
composeTestRule.waitForIdle()

// Wait for map screen to load
composeTestRule.waitUntil(timeoutMillis = 10000) {
composeTestRule.onAllNodesWithTag("googleMapScreen").fetchSemanticsNodes().size == 1
}

// Step 5: Click take picture FAB
composeTestRule.onNodeWithTag("fab_take_picture").performClick()
composeTestRule.waitForIdle()

// Step 6: Wait for camera screen and take picture
composeTestRule.waitUntil(timeoutMillis = 5000) {
composeTestRule.onAllNodesWithTag("camera_capture").fetchSemanticsNodes().size == 1
}
composeTestRule.onNodeWithTag("take_picture_button").performClick()
composeTestRule.waitForIdle()

// Step 7: Handle the image review screen
composeTestRule.waitUntil(timeoutMillis = 5000) {
composeTestRule.onAllNodesWithTag("image_review").fetchSemanticsNodes().size == 1
}

// Click to add description
composeTestRule.onNodeWithTag("description_text").performClick()
composeTestRule.waitForIdle()

// Enter description in the edit field
composeTestRule.onNodeWithTag("edit_description_field").performTextInput("Test description")
composeTestRule.waitForIdle()

// Click post button to upload
composeTestRule.onNodeWithTag("confirm_button").performClick()
composeTestRule.waitForIdle()

// Give time for the upload and post creation
Thread.sleep(3000)

// Step 7: Verify we're back on map screen
composeTestRule.waitUntil(timeoutMillis = 5000) {
composeTestRule.onAllNodesWithTag("googleMapScreen").fetchSemanticsNodes().size == 1
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
Expand Down Expand Up @@ -52,7 +53,7 @@ fun ProfileScreen(navigationActions: NavigationActions, avatarViewModel: AvatarV
isUserLoggedIn = isUserLoggedIn,
selectedItem = navigationActions.currentRoute())
}) { innerPadding ->
Box(modifier = Modifier.fillMaxSize().padding(innerPadding)) {
Box(modifier = Modifier.fillMaxSize().padding(innerPadding).testTag("profile_screen")) {
// Background Image
BackgroundImage(
painterResId = R.drawable.background_blurred,
Expand Down

0 comments on commit 1fd5991

Please sign in to comment.