Something went wrong. Try again.
This repository has no description
Something went wrong. Try again.
Kotlin
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248package social.grain
import androidx.lifecycle.SavedStateHandleimport androidx.test.ext.junit.runners.AndroidJUnit4import okhttp3.OkHttpClientimport org.junit.Afterimport org.junit.Assert.assertEqualsimport org.junit.Assert.assertFalseimport org.junit.Assert.assertNullimport org.junit.Assert.assertTrueimport org.junit.Beforeimport org.junit.Ruleimport org.junit.Testimport org.junit.runner.RunWithimport social.grain.data.location.LocationServicesimport social.grain.feature.explore.CamerasViewModelimport social.grain.feature.explore.LocationsViewModelimport social.grain.support.Fixturesimport social.grain.support.Harnessimport social.grain.support.MainDispatcherRuleimport social.grain.support.awaitCallimport social.grain.support.awaitValueimport social.grain.support.jsonimport social.grain.support.settleimport social.grain.support.testJson
/** The two index screens behind explore's "See all". */@RunWith(AndroidJUnit4::class)class BrowseIndexViewModelsTest {
@get:Rule val mainDispatcher = MainDispatcherRule()
private lateinit var harness: Harness
@Before fun setUp() { harness = Harness() harness.server .on( "social.grain.unspecced.getCameras", """{"cameras":[{"camera":"Leica M6","photoCount":120,"thumbs":["https://cdn.grain.test/thumb/1.jpg"]}]}""", ) .on("social.grain.unspecced.getLocations") { call -> if (call.params["pins"] == "true") { """{"locations":[ {"name":"Lisbon, PT","h3Index":"85390cb7fffffff","galleryCount":3}, {"name":"Porto, PT","h3Index":"853913d3fffffff","galleryCount":1} ]}""" } else { """{"locations":[{"name":"Lisbon, PT","h3Index":"85390cb7fffffff","galleryCount":3,"thumbs":["https://cdn.grain.test/thumb/2.jpg"]}]}""" } } }
@After fun tearDown() = harness.tearDown()
private fun cameras() = CamerasViewModel(harness.client, harness.auth)
private fun locations( services: LocationServices = LocationServices(OkHttpClient(), testJson), route: SavedStateHandle = SavedStateHandle(), ) = LocationsViewModel(harness.client, harness.auth, services, harness.labelDefinitions, route)
@Test fun camerasArriveWithTheirThumbnails() { val model = cameras() val state = model.state.awaitValue("the cameras") { !it.isLoading }
assertEquals("Leica M6", state.items.single().camera) assertEquals(1, state.items.single().thumbs.size) assertFalse(state.isEmpty) assertNull(state.errorMessage) }
@Test fun anEmptyCamerasIndexIsEmptyOnlyOnceItHasAnswered() { harness.server.on("social.grain.unspecced.getCameras", """{"cameras":[]}""") val model = cameras()
assertFalse(model.state.value.isEmpty) val state = model.state.awaitValue("the answer") { !it.isLoading } assertTrue(state.isEmpty) }
@Test fun aFailedCamerasLoadSaysSoAndCanBeDismissed() { harness.server.on("social.grain.unspecced.getCameras", """{"error":"no"}""", code = 500) val model = cameras()
val state = model.state.awaitValue("the error") { it.errorMessage != null } assertEquals("HTTP error 500", state.errorMessage) assertFalse(state.isLoading)
model.dismissError() assertNull(model.state.value.errorMessage) }
@Test fun refreshingCamerasShowsTheIndicatorAndReReads() { val model = cameras() model.state.awaitValue("the cameras") { !it.isLoading } val before = harness.server.countOf("social.grain.unspecced.getCameras")
model.refresh()
harness.server.awaitCall("social.grain.unspecced.getCameras", before + 1) model.state.awaitValue("the refresh to finish") { !it.isRefreshing } }
@Test fun locationsAskForTilesAndPinsSeparately() { val model = locations() val state = model.state.awaitValue("the tiles") { !it.isLoading }
assertEquals("Lisbon, PT", state.items.single().name) assertEquals(1, state.items.single().thumbs.size)
// The two go out together, so the tiles can answer before the pins // request has even been recorded. harness.server.awaitCall("social.grain.unspecced.getLocations", 2) val calls = harness.server.callsTo("social.grain.unspecced.getLocations") assertEquals(2, calls.size) assertEquals(setOf(null, "true"), calls.map { it.params["pins"] }.toSet()) }
@Test fun everyPlaceWithACoordinateBecomesAPin() { val services = LocationServices(OkHttpClient(), testJson) val model = locations(services) harness.server.awaitCall("social.grain.unspecced.getLocations", 2) model.state.awaitValue("the tiles") { !it.isLoading }
// h3 is a native library that only ships for arm; where it can't load // there are no coordinates and so no pins, and that is the contract. if (services.isAvailable) { val pins = model.pins.awaitValue("the pins") { it.size == 2 } assertEquals(listOf("Lisbon, PT", "Porto, PT"), pins.map { it.name }) assertEquals(3, pins.first().galleryCount) } else { assertTrue(model.pins.value.isEmpty()) } }
@Test fun pickingAPlaceLoadsItsGalleriesWithoutLeavingTheIndex() { harness.server.on("dev.hatk.getFeed", """{"items":[${json(Fixtures.gallery())}],"cursor":"c1"}""") val model = locations() model.state.awaitValue("the tiles") { !it.isLoading }
model.select("85390cb7fffffff", "Lisbon, PT", 3)
val place = model.selected.awaitValue("the galleries") { it?.galleries?.isNotEmpty() == true }!! assertEquals("Lisbon, PT", place.name) assertEquals(3, place.galleryCount) assertFalse(place.isLoading) val call = harness.server.lastCall("dev.hatk.getFeed") assertEquals("location", call.params["feed"]) assertEquals("85390cb7fffffff", call.params["location"]) assertEquals("Lisbon, PT", call.params["name"]) // The index is still there underneath. assertEquals(1, model.state.value.items.size) }
@Test fun moreOfAPlacesGalleriesAppendBehindTheCursor() { harness.server.on("dev.hatk.getFeed", """{"items":[${json(Fixtures.gallery())}],"cursor":"c1"}""") val model = locations() model.select("85390cb7fffffff", "Lisbon, PT", 3) model.selected.awaitValue("the first page") { it?.galleries?.isNotEmpty() == true }
harness.server.on( "dev.hatk.getFeed", """{"items":[${json(Fixtures.gallery(rkey = "g2", title = "Second"))}]}""", ) model.loadMoreGalleries()
val place = model.selected.awaitValue("the second page") { it?.galleries?.size == 2 }!! assertEquals("Second", place.galleries[1].title) assertEquals("c1", harness.server.lastCall("dev.hatk.getFeed").params["cursor"]) assertFalse(place.isLoadingMore) }
@Test fun pickingThePlaceAlreadyShownDoesNotReload() { harness.server.on("dev.hatk.getFeed", """{"items":[${json(Fixtures.gallery())}]}""") val model = locations() model.select("85390cb7fffffff", "Lisbon, PT", 3) model.selected.awaitValue("the galleries") { it?.galleries?.isNotEmpty() == true } val before = harness.server.countOf("dev.hatk.getFeed")
model.select("85390cb7fffffff", "Lisbon, PT", 3) settle()
assertEquals(before, harness.server.countOf("dev.hatk.getFeed")) }
@Test fun openedFromAChipTheScreenStartsOnThatPlace() { harness.server.on("dev.hatk.getFeed", """{"items":[${json(Fixtures.gallery())}]}""") val model = locations( route = SavedStateHandle( mapOf("cell" to "85390cb7fffffff", "name" to "Lisbon, PT", "galleryCount" to 3), ), )
assertTrue(model.openedOnPlace) val place = model.selected.awaitValue("the galleries") { it?.galleries?.isNotEmpty() == true }!! assertEquals("Lisbon, PT", place.name) assertEquals(3, place.galleryCount) assertEquals("85390cb7fffffff", harness.server.lastCall("dev.hatk.getFeed").params["location"]) }
@Test fun openedFromSeeAllTheScreenStartsOnTheIndex() { val model = locations() assertFalse(model.openedOnPlace) assertNull(model.selected.value) }
@Test fun clearingTheSelectionBringsTheIndexBack() { harness.server.on("dev.hatk.getFeed", """{"items":[]}""") val model = locations() model.select("85390cb7fffffff", "Lisbon, PT", 3) val place = model.selected.awaitValue("the answer") { it?.isLoading == false }!! assertTrue(place.isEmpty)
model.clearSelection()
assertNull(model.selected.value) }
@Test fun aFailedLocationsLoadSaysSo() { harness.server.on("social.grain.unspecced.getLocations", """{"error":"no"}""", code = 500) val model = locations()
val state = model.state.awaitValue("the error") { it.errorMessage != null } assertEquals("HTTP error 500", state.errorMessage) assertTrue(model.pins.value.isEmpty())
model.dismissError() assertNull(model.state.value.errorMessage) }}