Skip to content

Commit

Permalink
Jvc android search recent stops (#622)
Browse files Browse the repository at this point in the history
* Add recent stops to populate empty search results
  • Loading branch information
JackVCurtis authored Jan 9, 2025
1 parent 91572fc commit 1b6251a
Show file tree
Hide file tree
Showing 18 changed files with 266 additions and 97 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ import com.mbta.tid.mbta_app.model.response.PredictionsByStopJoinResponse
import com.mbta.tid.mbta_app.model.response.PredictionsByStopMessageResponse
import com.mbta.tid.mbta_app.model.response.PredictionsStreamDataResponse
import com.mbta.tid.mbta_app.repositories.IErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.IGlobalRepository
import com.mbta.tid.mbta_app.repositories.INearbyRepository
import com.mbta.tid.mbta_app.repositories.IPinnedRoutesRepository
import com.mbta.tid.mbta_app.repositories.IPredictionsRepository
Expand All @@ -54,13 +55,17 @@ import com.mbta.tid.mbta_app.repositories.ISchedulesRepository
import com.mbta.tid.mbta_app.repositories.ISearchResultRepository
import com.mbta.tid.mbta_app.repositories.ISettingsRepository
import com.mbta.tid.mbta_app.repositories.IVehiclesRepository
import com.mbta.tid.mbta_app.repositories.IVisitHistoryRepository
import com.mbta.tid.mbta_app.repositories.MockErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.MockGlobalRepository
import com.mbta.tid.mbta_app.repositories.MockRailRouteShapeRepository
import com.mbta.tid.mbta_app.repositories.MockScheduleRepository
import com.mbta.tid.mbta_app.repositories.MockSearchResultRepository
import com.mbta.tid.mbta_app.repositories.MockSettingsRepository
import com.mbta.tid.mbta_app.repositories.MockVehiclesRepository
import com.mbta.tid.mbta_app.repositories.MockVisitHistoryRepository
import com.mbta.tid.mbta_app.usecases.TogglePinnedRouteUsecase
import com.mbta.tid.mbta_app.usecases.VisitHistoryUsecase
import io.github.dellisd.spatialk.geojson.Position
import kotlin.time.Duration.Companion.minutes
import kotlinx.coroutines.flow.Flow
Expand Down Expand Up @@ -207,6 +212,9 @@ class NearbyTransitPageTest : KoinTest {
module {
single<ISettingsRepository> { MockSettingsRepository() }
single<IErrorBannerStateRepository> { MockErrorBannerStateRepository() }
single<IGlobalRepository> {
MockGlobalRepository(response = GlobalResponse(builder))
}
single<ISchedulesRepository> { MockScheduleRepository() }
single<IPredictionsRepository> {
object : IPredictionsRepository {
Expand Down Expand Up @@ -263,6 +271,8 @@ class NearbyTransitPageTest : KoinTest {
single<IVehiclesRepository> { MockVehiclesRepository() }
single<ISearchResultRepository> { MockSearchResultRepository() }
viewModelOf(::NearbyTransitViewModel)
single<IVisitHistoryRepository> { MockVisitHistoryRepository() }
single<VisitHistoryUsecase> { VisitHistoryUsecase(get()) }
}
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,16 @@ import com.mbta.tid.mbta_app.repositories.IPredictionsRepository
import com.mbta.tid.mbta_app.repositories.IRailRouteShapeRepository
import com.mbta.tid.mbta_app.repositories.ISchedulesRepository
import com.mbta.tid.mbta_app.repositories.ISettingsRepository
import com.mbta.tid.mbta_app.repositories.IVisitHistoryRepository
import com.mbta.tid.mbta_app.repositories.MockErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.MockNearbyRepository
import com.mbta.tid.mbta_app.repositories.MockPredictionsRepository
import com.mbta.tid.mbta_app.repositories.MockRailRouteShapeRepository
import com.mbta.tid.mbta_app.repositories.MockScheduleRepository
import com.mbta.tid.mbta_app.repositories.MockSettingsRepository
import com.mbta.tid.mbta_app.repositories.MockVisitHistoryRepository
import com.mbta.tid.mbta_app.usecases.TogglePinnedRouteUsecase
import com.mbta.tid.mbta_app.usecases.VisitHistoryUsecase
import io.github.dellisd.spatialk.geojson.Position
import kotlin.time.Duration.Companion.minutes
import kotlinx.datetime.Instant
Expand Down Expand Up @@ -236,6 +239,8 @@ class NearbyTransitViewTest : KoinTest {
single<IRailRouteShapeRepository> { MockRailRouteShapeRepository() }
single<TogglePinnedRouteUsecase> { TogglePinnedRouteUsecase(get()) }
viewModelOf(::NearbyTransitViewModel)
single<IVisitHistoryRepository> { MockVisitHistoryRepository() }
single<VisitHistoryUsecase> { VisitHistoryUsecase(get()) }
}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.mbta.tid.mbta_app.android.search

import android.os.Bundle
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Text
import androidx.compose.runtime.mutableStateOf
Expand All @@ -12,16 +13,26 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.compose.ui.test.performTextInput
import androidx.compose.ui.test.requestFocus
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavDestination
import com.mbta.tid.mbta_app.history.Visit
import com.mbta.tid.mbta_app.history.VisitHistory
import com.mbta.tid.mbta_app.model.ObjectCollectionBuilder
import com.mbta.tid.mbta_app.model.RouteType
import com.mbta.tid.mbta_app.model.SearchResults
import com.mbta.tid.mbta_app.model.StopResult
import com.mbta.tid.mbta_app.model.StopResultRoute
import com.mbta.tid.mbta_app.model.response.ApiResult
import com.mbta.tid.mbta_app.model.response.GlobalResponse
import com.mbta.tid.mbta_app.repositories.IErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.IGlobalRepository
import com.mbta.tid.mbta_app.repositories.ISearchResultRepository
import com.mbta.tid.mbta_app.repositories.IVisitHistoryRepository
import com.mbta.tid.mbta_app.repositories.MockErrorBannerStateRepository
import com.mbta.tid.mbta_app.repositories.MockGlobalRepository
import com.mbta.tid.mbta_app.repositories.MockVisitHistoryRepository
import com.mbta.tid.mbta_app.usecases.VisitHistoryUsecase
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test
Expand All @@ -33,11 +44,23 @@ import org.koin.test.KoinTest
@ExperimentalTestApi
@ExperimentalMaterial3Api
class SearchBarOverlayTest : KoinTest {
val mockVisitHistoryRepository = MockVisitHistoryRepository()
val builder = ObjectCollectionBuilder()
val visitedStop =
builder.stop {
id = "visitedStopId"
name = "visitedStopName"
}
val koinApplication = koinApplication {
modules(
module {
single<IErrorBannerStateRepository> { MockErrorBannerStateRepository() }
single<IGlobalRepository> { MockGlobalRepository() }
single<IGlobalRepository> {
MockGlobalRepository(response = GlobalResponse(builder))
}
single<IVisitHistoryRepository> { mockVisitHistoryRepository }
single<VisitHistoryUsecase> { VisitHistoryUsecase(get()) }
single<ISearchResultRepository> {
object : ISearchResultRepository {
override suspend fun getSearchResults(
Expand Down Expand Up @@ -77,13 +100,14 @@ class SearchBarOverlayTest : KoinTest {
@Test
fun testSearchBarOverlayBehavesCorrectly() = runTest {
val navigated = mutableStateOf(false)
var navBackStackEntry = mutableStateOf<NavBackStackEntry?>(null)

composeTestRule.setContent {
KoinContext(koinApplication.koin) {
val focusRequester = remember { FocusRequester() }
SearchBarOverlay(
onStopNavigation = { navigated.value = true },
currentNavEntry = null,
currentNavEntry = navBackStackEntry.value,
inputFieldFocusRequester = focusRequester,
) {
Text("Content")
Expand All @@ -92,10 +116,30 @@ class SearchBarOverlayTest : KoinTest {
}

composeTestRule.onNodeWithText("Content").assertExists()

// Simulate navigating to a stop and back
runBlocking {
mockVisitHistoryRepository.setVisitHistory(
VisitHistory().apply { add(Visit.StopVisit(visitedStop.id)) }
)
}
navBackStackEntry.value =
NavBackStackEntry.create(
context = null,
arguments = Bundle().apply { putString("stopId", "visitedStopId") },
destination = NavDestination("stop")
)
composeTestRule.awaitIdle()
navBackStackEntry.value = null

composeTestRule.waitUntilAtLeastOneExists(hasText("Search by stop"))
val searchNode = composeTestRule.onNodeWithText("Search by stop")
searchNode.assertExists()
searchNode.requestFocus()
composeTestRule.awaitIdle()
composeTestRule.onNodeWithText("Recently Viewed").assertExists()
composeTestRule.waitUntilAtLeastOneExists(hasText(visitedStop.name))
composeTestRule.onNodeWithText(visitedStop.name).assertExists()

searchNode.performTextInput("sto")
composeTestRule.waitUntilAtLeastOneExists(hasText("stopName"))
Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,31 @@
package com.mbta.tid.mbta_app.android.state

import androidx.compose.ui.test.junit4.createComposeRule
import com.mbta.tid.mbta_app.history.Visit
import com.mbta.tid.mbta_app.history.VisitHistory
import com.mbta.tid.mbta_app.model.ObjectCollectionBuilder
import com.mbta.tid.mbta_app.model.RouteType
import com.mbta.tid.mbta_app.model.SearchResults
import com.mbta.tid.mbta_app.model.StopResult
import com.mbta.tid.mbta_app.model.StopResultRoute
import com.mbta.tid.mbta_app.model.response.ApiResult
import com.mbta.tid.mbta_app.model.response.GlobalResponse
import com.mbta.tid.mbta_app.repositories.ISearchResultRepository
import com.mbta.tid.mbta_app.repositories.MockVisitHistoryRepository
import com.mbta.tid.mbta_app.usecases.VisitHistoryUsecase
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.test.runTest
import org.junit.Rule
import org.junit.Test

class GetSearchResultTest {
val builder = ObjectCollectionBuilder()
val visitedStop =
builder.stop {
id = "visitedStopId"
name = "visitedStopName"
}

val searchResults =
SearchResults(
routes = emptyList(),
Expand Down Expand Up @@ -39,26 +53,42 @@ class GetSearchResultTest {
@Test
fun testSearchResults() = runTest {
var actualSearchResultsViewModel: SearchResultsViewModel? = null
val mockVisitHistoryRepository = MockVisitHistoryRepository()
val visitHistory = VisitHistory()

runBlocking {
visitHistory.add(Visit.StopVisit(visitedStop.id))
mockVisitHistoryRepository.setVisitHistory(visitHistory)
}

composeTestRule.setContent {
actualSearchResultsViewModel =
getSearchResultsVm(
GlobalResponse(builder),
object : ISearchResultRepository {
override suspend fun getSearchResults(
query: String
): ApiResult<SearchResults>? {
return ApiResult.Ok(searchResults)
}
}
},
VisitHistoryUsecase(mockVisitHistoryRepository)
)
}

composeTestRule.waitUntil { actualSearchResultsViewModel != null }
composeTestRule.awaitIdle()

actualSearchResultsViewModel?.getSearchResults("query")

actualSearchResultsViewModel?.getSearchResults("")
composeTestRule.waitUntil { actualSearchResultsViewModel?.searchResults?.value != null }
assert(
actualSearchResultsViewModel?.searchResults?.value?.stops?.first()?.id == visitedStop.id
)

actualSearchResultsViewModel?.getSearchResults("query")
composeTestRule.waitUntil {
actualSearchResultsViewModel?.searchResults?.value == searchResults
}
assert(actualSearchResultsViewModel?.searchResults?.value == searchResults)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -101,13 +101,25 @@ fun ContentView(
hideNavBar = { navBarVisible = false },
bottomBar = {
if (navBarVisible) {
BottomNavBar(navController = navController)
BottomNavBar(
currentDestination = navController.currentBackStackEntry?.destination,
navigateToNearby = { navController.navigate(Routes.NearbyTransit) },
navigateToMore = { navController.navigate(Routes.More) }
)
}
}
)
}
composable<Routes.More> {
MorePage(bottomBar = { BottomNavBar(navController = navController) })
MorePage(
bottomBar = {
BottomNavBar(
currentDestination = navController.currentBackStackEntry?.destination,
navigateToNearby = { navController.navigate(Routes.NearbyTransit) },
navigateToMore = { navController.navigate(Routes.More) }
)
}
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@ package com.mbta.tid.mbta_app.android

import kotlinx.serialization.Serializable

object Routes {
@Serializable object NearbyTransit
sealed class Routes {
@Serializable object NearbyTransit : Routes()

@Serializable object More
@Serializable object More : Routes()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,22 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import androidx.navigation.NavHostController
import androidx.navigation.NavDestination
import com.mbta.tid.mbta_app.android.R
import com.mbta.tid.mbta_app.android.Routes

@Composable
fun BottomNavBar(navController: NavHostController) {

val currentDestination = navController.currentBackStackEntry?.destination

fun BottomNavBar(
currentDestination: NavDestination?,
navigateToNearby: () -> Unit,
navigateToMore: () -> Unit
) {
BottomAppBar(
modifier = Modifier.height(83.dp),
containerColor = MaterialTheme.colorScheme.surfaceVariant,
actions = {
BottomNavIconButton(
modifier = Modifier.fillMaxSize().weight(1f),
onClick = { navController.navigate(Routes.NearbyTransit) },
onClick = navigateToNearby,
icon = R.drawable.map_pin,
label = stringResource(R.string.nearby_transit_link),
// currentDestination?.hiearchy?.any { it.hasRoute(Routes.NearbyTransit::class)
Expand All @@ -34,7 +34,7 @@ fun BottomNavBar(navController: NavHostController) {

BottomNavIconButton(
modifier = Modifier.fillMaxSize().weight(1f),
onClick = { navController.navigate(Routes.More) },
onClick = navigateToMore,
icon = R.drawable.more,
label = stringResource(R.string.more_link),
active = currentDestination?.route?.contains("More") ?: true
Expand Down
Loading

0 comments on commit 1b6251a

Please sign in to comment.