Skip to content

Commit

Permalink
Feature/calendar implement navigation to & from the screen (#108)
Browse files Browse the repository at this point in the history
* feat(calendar): add CalendarScreen

Added a calendar and it's view for activity tracking

* fix(firestore): CalendarScreen now updates

The calendar now takes data from firestore and displays activities

* fix(firestore): CalendarScreen now updates

The calendar now takes data from firestore and displays activities

* test: add UI tests

* test: add UI tests

* test(coverage): improve coverage

* test(coverage): improve coverage

* chore(refactor): separate composables in files

* chore(refactor): make activities val again

Made ActivityViewModel.activities back into a val, as opposed to a var

* chore(refactor): refactor LaunchedEffect

Moved the LaunchedEffect in the CalendarScreen to a separate file

* chore(formatting): moved files to UI

* feat(navigation): implement navigation

* feat(navigation): implement navigation

* tests(navigation): refactor tests for navigation

* tests(navigation): refactor tests for navigation

* tests(coverage): improve coverage
  • Loading branch information
StefanPetersTM authored Oct 31, 2024
1 parent 778df3f commit c3b731a
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 7 deletions.
1 change: 1 addition & 0 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ dependencies {
implementation(libs.firebase.common.ktx)
implementation(libs.firebase.auth.ktx)
implementation(libs.androidx.runtime.livedata)
implementation(libs.androidx.navigation.testing)
testImplementation(libs.junit)
globalTestImplementation(libs.androidx.junit)
globalTestImplementation(libs.androidx.espresso.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import com.github.se.travelpouch.model.activity.ActivityRepository
import com.github.se.travelpouch.model.activity.ActivityViewModel
import com.github.se.travelpouch.model.dashboard.CalendarViewModel
import com.github.se.travelpouch.ui.dashboard.CalendarScreen
import com.github.se.travelpouch.ui.navigation.NavigationActions
import com.google.firebase.Timestamp
import java.util.Calendar
import kotlinx.coroutines.ExperimentalCoroutinesApi
Expand All @@ -28,10 +29,13 @@ class CalendarScreenTest {
private lateinit var mockActivityRepositoryFirebase: ActivityRepository
private lateinit var mockActivityViewModel: ActivityViewModel

private lateinit var navigationActions: NavigationActions

@get:Rule val composeTestRule = createComposeRule()

@Before
fun setUp() {
navigationActions = mock(NavigationActions::class.java)
mockActivityRepositoryFirebase = mock(ActivityRepository::class.java)
mockActivityViewModel = ActivityViewModel(mockActivityRepositoryFirebase)
calendarViewModel = CalendarViewModel(activityViewModel = mockActivityViewModel)
Expand All @@ -40,7 +44,9 @@ class CalendarScreenTest {
@Test
fun hasRequiredComponents() {
// Act
composeTestRule.setContent { CalendarScreen(calendarViewModel = calendarViewModel) }
composeTestRule.setContent {
CalendarScreen(calendarViewModel = calendarViewModel, navigationActions)
}
composeTestRule.waitForIdle()

// Assert
Expand Down Expand Up @@ -81,7 +87,9 @@ class CalendarScreenTest {
mockActivityViewModel.getAllActivities()

// Act
composeTestRule.setContent { CalendarScreen(calendarViewModel = calendarViewModel) }
composeTestRule.setContent {
CalendarScreen(calendarViewModel = calendarViewModel, navigationActions = navigationActions)
}
composeTestRule.waitForIdle()

// Assert activity for today is displayed
Expand All @@ -93,4 +101,30 @@ class CalendarScreenTest {
// Click on the back icon to test navigation
composeTestRule.onNodeWithTag("goBackIcon").performClick()
}

@Test
fun testNavigationGoBack() {
// Act
composeTestRule.setContent {
CalendarScreen(calendarViewModel = calendarViewModel, navigationActions)
}
composeTestRule.waitForIdle()

// Click on the back icon to test navigation
composeTestRule.onNodeWithTag("goBackIcon").performClick()
composeTestRule.waitForIdle()
}

@Test
fun testNavigationBottomBar() {
// Act
composeTestRule.setContent {
CalendarScreen(calendarViewModel = calendarViewModel, navigationActions)
}
composeTestRule.waitForIdle()

// Click on the Map icon to test navigation
composeTestRule.onNodeWithTag("navigationBarItemMap").performClick()
composeTestRule.waitForIdle()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import com.github.se.travelpouch.model.ListTravelViewModel
import com.github.se.travelpouch.model.Location
import com.github.se.travelpouch.model.Participant
Expand All @@ -12,13 +13,15 @@ import com.github.se.travelpouch.model.TravelRepository
import com.github.se.travelpouch.ui.home.MapScreen
import com.github.se.travelpouch.ui.home.TravelListScreen
import com.github.se.travelpouch.ui.navigation.NavigationActions
import com.github.se.travelpouch.ui.navigation.Screen
import com.google.firebase.Timestamp
import java.util.Date
import kotlinx.coroutines.ExperimentalCoroutinesApi
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.Mockito.verify
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.whenever
Expand Down Expand Up @@ -146,4 +149,62 @@ class TravelListScreenTest {
// Assert
composeTestRule.onNodeWithTag("mapScreen").assertIsDisplayed()
}

@Test
fun testNavigationBottomBar() {
// Act
composeTestRule.setContent {
TravelListScreen(
navigationActions = navigationActions, listTravelViewModel = listTravelViewModel)
}
composeTestRule.waitForIdle()

// Click on the "Activities" navigation item
composeTestRule.onNodeWithTag("navigationBarItemActivities").performClick()
composeTestRule.waitForIdle()

// Verify that the navigation action was called for "Activities"
verify(navigationActions).navigateTo(Screen.TRAVEL_ACTIVITIES)

// Click on the "Calendar" navigation item
composeTestRule.onNodeWithTag("navigationBarItemCalendar").performClick()
composeTestRule.waitForIdle()

// Verify that the navigation action was called for "Calendar"
verify(navigationActions).navigateTo(Screen.CALENDAR)
}

@Test
fun testTravelItemClickNavigatesToTravelActivities() {
// Act
composeTestRule.setContent {
TravelListScreen(
navigationActions = navigationActions, listTravelViewModel = listTravelViewModel)
}
composeTestRule.waitForIdle()

// Find and click on a travel list item
composeTestRule.onNodeWithTag("travelListItem").performClick()
composeTestRule.waitForIdle()

// Verify that the navigation action was called for TRAVEL_ACTIVITIES
verify(navigationActions).navigateTo(Screen.TRAVEL_ACTIVITIES)
}

@Test
fun testCreateTravelFabClickNavigatesToAddTravel() {
// Act
composeTestRule.setContent {
TravelListScreen(
navigationActions = navigationActions, listTravelViewModel = listTravelViewModel)
}
composeTestRule.waitForIdle()

// Find and click on the create travel FAB
composeTestRule.onNodeWithTag("createTravelFab").performClick()
composeTestRule.waitForIdle()

// Verify that the navigation action was called for ADD_TRAVEL
verify(navigationActions).navigateTo(Screen.ADD_TRAVEL)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@ fun TravelPouchApp() {
}
composable(Screen.DOCUMENT_PREVIEW) { DocumentPreview(documentViewModel, navigationActions) }
composable(Screen.TIMELINE) { TimelineScreen(eventsViewModel) }
composable(Screen.CALENDAR) { CalendarScreen(calendarViewModel) }
composable(Screen.CALENDAR) { CalendarScreen(calendarViewModel, navigationActions) }
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,13 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.filled.Home
import androidx.compose.material.icons.filled.Place
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
Expand All @@ -24,6 +28,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import com.github.se.travelpouch.model.dashboard.CalendarViewModel
import com.github.se.travelpouch.ui.navigation.NavigationActions
import com.github.se.travelpouch.ui.navigation.Screen
import java.text.SimpleDateFormat
import java.util.Locale

Expand All @@ -34,15 +40,19 @@ import java.util.Locale
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun CalendarScreen(
calendarViewModel: CalendarViewModel,
) {
fun CalendarScreen(calendarViewModel: CalendarViewModel, navigationActions: NavigationActions) {
// Observe the state of activities from the ViewModel
val calendarState by calendarViewModel.calendarState.collectAsState(initial = emptyList())

// Initial Setup
CalendarScreenLaunchedEffect(calendarViewModel = calendarViewModel)

// List of destinations for the bottom navigation bar
val listOfDestinations =
listOf(
BottomNavigationItem("Activities", Icons.Default.Home),
BottomNavigationItem("Map", Icons.Default.Place))

// Application
Scaffold(
topBar = {
Expand All @@ -52,7 +62,7 @@ fun CalendarScreen(
navigationIcon = {
IconButton(
modifier = Modifier.testTag("goBackIcon"),
onClick = { /* TODO: Implement go back navigation logic */}) {
onClick = { navigationActions.goBack() }) {
// Back icon for navigation
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
Expand All @@ -64,6 +74,26 @@ fun CalendarScreen(
TopAppBarDefaults.topAppBarColors(
containerColor = Color(0xFF6200EE), titleContentColor = Color.White),
modifier = Modifier.testTag("calendarTopAppBar"))
},
bottomBar = {
NavigationBar(modifier = Modifier.testTag("navigationBarCalendar")) {
listOfDestinations.forEach { destination ->
NavigationBarItem(
onClick = {
when (destination.title) {
"Activities" -> navigationActions.navigateTo(Screen.TRAVEL_ACTIVITIES)
"Map" -> navigationActions.navigateTo(Screen.TRAVEL_LIST)
}
},
icon = { Icon(destination.icon, contentDescription = null) },
selected = false,
label = { Text(destination.title) },
modifier =
Modifier.testTag(
if (destination.title == "Map") "navigationBarItemMap"
else "navigationBarItem"))
}
}
}) { innerPadding ->
Column(
modifier = Modifier.fillMaxSize().padding(innerPadding).testTag("calendarScreenColumn"),
Expand Down
31 changes: 31 additions & 0 deletions app/src/main/java/com/github/se/travelpouch/ui/home/TravelList.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.KeyboardArrowRight
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.DateRange
import androidx.compose.material.icons.filled.Home
import androidx.compose.material3.Card
import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
Expand All @@ -32,6 +36,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.github.se.travelpouch.model.ListTravelViewModel
import com.github.se.travelpouch.model.TravelContainer
import com.github.se.travelpouch.ui.dashboard.BottomNavigationItem
import com.github.se.travelpouch.ui.navigation.NavigationActions
import com.github.se.travelpouch.ui.navigation.Screen
import java.util.Locale
Expand All @@ -56,6 +61,11 @@ fun TravelListScreen(
// travelContainers.getTravels()
val travelList = listTravelViewModel.travels.collectAsState().value

val listOfDestinations =
listOf(
BottomNavigationItem("Activities", Icons.Default.Home),
BottomNavigationItem("Calendar", Icons.Default.DateRange))

Scaffold(
modifier = Modifier.testTag("TravelListScreen"),
floatingActionButton = {
Expand All @@ -65,6 +75,27 @@ fun TravelListScreen(
Icon(imageVector = Icons.Default.Add, contentDescription = "Add")
}
},
bottomBar = {
NavigationBar(modifier = Modifier.testTag("navigationBarTravelList")) {
listOfDestinations.forEach { destination ->
NavigationBarItem(
onClick = {
when (destination.title) {
"Activities" -> navigationActions.navigateTo(Screen.TRAVEL_ACTIVITIES)
"Calendar" -> navigationActions.navigateTo(Screen.CALENDAR)
}
},
icon = { Icon(destination.icon, contentDescription = null) },
selected = false,
label = { Text(destination.title) },
modifier =
Modifier.testTag(
if (destination.title == "Activities") "navigationBarItemActivities"
else "navigationBarItemCalendar"))
// modifier = Modifier.testTag("navigationBarItem"))
}
}
},
content = { pd ->
Column {
// Add the map to display the travels
Expand Down
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ robolectric = "4.11.1"
sonar = "4.4.1.3373"
uiTestJunit4 = "1.7.3"
runtimeLivedata = "1.7.4"
navigationTesting = "2.8.3"


[libraries]
Expand Down Expand Up @@ -116,6 +117,7 @@ androidx-espresso-intents = { module = "androidx.test.espresso:espresso-intents"
# Networking
okhttp = { module = "com.squareup.okhttp3:okhttp", version.ref = "okhttp" }
androidx-runtime-livedata = { group = "androidx.compose.runtime", name = "runtime-livedata", version.ref = "runtimeLivedata" }
androidx-navigation-testing = { group = "androidx.navigation", name = "navigation-testing", version.ref = "navigationTesting" }

[plugins]
androidApplication = { id = "com.android.application", version.ref = "agp" }
Expand Down

0 comments on commit c3b731a

Please sign in to comment.