package social.grain import androidx.compose.ui.test.junit4.v2.createComposeRule import androidx.lifecycle.SavedStateHandle import androidx.compose.ui.test.onNodeWithContentDescription import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import okhttp3.OkHttpClient import org.junit.After import org.junit.Assert.assertEquals import org.junit.Before import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import social.grain.data.location.LocationServices import social.grain.feature.explore.CamerasScreen import social.grain.feature.explore.CamerasViewModel import social.grain.feature.explore.LocationsScreen import social.grain.feature.explore.LocationsViewModel import social.grain.feature.feed.CameraFeedScreen import social.grain.feature.feed.CameraFeedViewModel import social.grain.support.Fixtures import social.grain.support.Harness import social.grain.support.MainDispatcherRule import social.grain.support.awaitContentDescription import social.grain.support.awaitText import social.grain.support.json import social.grain.support.testJson import social.grain.ui.theme.GrainTheme /** The screens behind explore: the two indexes, a tile per entry, and the camera feed. */ @RunWith(AndroidJUnit4::class) class ExploreScreensTest { @get:Rule val mainDispatcher = MainDispatcherRule() @get:Rule val composeRule = createComposeRule() private lateinit var harness: Harness @Before fun setUp() { harness = Harness() harness.server .on( "social.grain.gallery.getCameras", """{"cameras":[ {"camera":"Leica M6","photoCount":120,"thumbs":["https://cdn.grain.test/thumb/1.jpg"]}, {"camera":"Ricoh GR III","photoCount":1} ]}""", ) .on( "social.grain.gallery.getLocations", """{"locations":[{"name":"Lisbon, PT","h3Index":"85390cb7fffffff","galleryCount":3}]}""", ) } @After fun tearDown() = harness.tearDown() @Test fun theCamerasIndexDrawsATilePerCameraWithItsCount() { var opened: String? = null composeRule.setContent { GrainTheme { CamerasScreen( onBack = {}, onOpenCamera = { opened = it }, viewModel = CamerasViewModel(harness.client, harness.auth), ) } } composeRule.awaitText("Cameras") composeRule.awaitText("Leica M6") composeRule.awaitText("120 photos") composeRule.awaitText("1 photo") composeRule.onNodeWithText("Ricoh GR III").performClick() assertEquals("Ricoh GR III", opened) } @Test fun aCameraFeedIsTitledByTheCameraAndDrawsItsCards() { harness.server .on("social.grain.feed.getFeed", """{"items":[${json(Fixtures.gallery(title = "Shot on film"))}]}""") composeRule.setContent { GrainTheme { CameraFeedScreen( onBack = {}, onOpenProfile = {}, onOpenHashtag = {}, viewModel = CameraFeedViewModel( client = harness.client, auth = harness.auth, labelDefsCache = harness.labelDefinitions, savedStateHandle = SavedStateHandle(mapOf("name" to "Leica M6")), ), ) } } composeRule.awaitText("Leica M6") composeRule.awaitText("Shot on film") assertEquals("Leica M6", harness.server.lastCall("social.grain.feed.getFeed").params["camera"]) } @Test fun anEmptyCameraFeedSaysSo() { harness.server .on("social.grain.feed.getFeed", """{"items":[]}""") composeRule.setContent { GrainTheme { CameraFeedScreen( onBack = {}, onOpenProfile = {}, onOpenHashtag = {}, viewModel = CameraFeedViewModel( client = harness.client, auth = harness.auth, labelDefsCache = harness.labelDefinitions, savedStateHandle = SavedStateHandle(mapOf("name" to "Leica M6")), ), ) } } composeRule.awaitText("No galleries from this camera yet") } @Test fun anEmptyCamerasIndexSaysSo() { harness.server.on("social.grain.gallery.getCameras", """{"cameras":[]}""") composeRule.setContent { GrainTheme { CamerasScreen( onBack = {}, onOpenCamera = {}, viewModel = CamerasViewModel(harness.client, harness.auth), ) } } composeRule.awaitText("No cameras yet.") } @Test fun pickingAPlaceSwapsTheSheetToItsGalleriesAndBackAgain() { harness.server .on("social.grain.feed.getFeed", """{"items":[${json(Fixtures.gallery(title = "By the river"))}]}""") var opened: String? = null composeRule.setContent { GrainTheme { LocationsScreen( onBack = {}, onOpenGallery = { opened = it }, viewModel = LocationsViewModel( harness.client, harness.auth, LocationServices(OkHttpClient(), testJson), harness.labelDefinitions, SavedStateHandle(), ), ) } } composeRule.awaitText("Locations") composeRule.awaitText("Lisbon, PT") composeRule.awaitText("3 galleries") composeRule.onNodeWithText("Lisbon, PT").performClick() // The place's galleries arrive in the sheet; the screen was not left. composeRule.awaitContentDescription("By the river") composeRule.awaitContentDescription("All locations") assertEquals("85390cb7fffffff", harness.server.lastCall("social.grain.feed.getFeed").params["location"]) composeRule.onNodeWithContentDescription("By the river").performClick() assertEquals(Fixtures.gallery().uri, opened) composeRule.onNodeWithContentDescription("All locations").performClick() composeRule.awaitText("Locations") composeRule.onNodeWithContentDescription("By the river").assertDoesNotExist() } @Test fun anEmptyLocationsIndexSaysSo() { harness.server.on("social.grain.gallery.getLocations", """{"locations":[]}""") composeRule.setContent { GrainTheme { LocationsScreen( onBack = {}, onOpenGallery = {}, viewModel = LocationsViewModel( harness.client, harness.auth, LocationServices(OkHttpClient(), testJson), harness.labelDefinitions, SavedStateHandle(), ), ) } } composeRule.awaitText("No locations yet.") } @Test fun aPlaceWithNoGalleriesSaysSoInItsSheet() { harness.server .on("social.grain.feed.getFeed", """{"items":[]}""") composeRule.setContent { GrainTheme { LocationsScreen( onBack = {}, onOpenGallery = {}, viewModel = LocationsViewModel( harness.client, harness.auth, LocationServices(OkHttpClient(), testJson), harness.labelDefinitions, SavedStateHandle(), ), ) } } composeRule.awaitText("Lisbon, PT") composeRule.onNodeWithText("Lisbon, PT").performClick() composeRule.awaitText("No galleries here yet.") } }