Integrate libraries and correct Home review blockers
This commit is contained in:
commit
14a74ac43b
642 changed files with 2377 additions and 169 deletions
|
|
@ -0,0 +1,81 @@
|
|||
package org.justinvisser.wolfskin.tv
|
||||
|
||||
import android.app.Application
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithTag
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performKeyInput
|
||||
import androidx.compose.ui.test.pressKey
|
||||
import com.github.damontecres.wholphin.data.model.JellyfinServer
|
||||
import com.github.damontecres.wholphin.data.model.JellyfinUser
|
||||
import com.github.damontecres.wholphin.services.NavigationManager
|
||||
import com.github.damontecres.wholphin.ui.nav.Destination
|
||||
import io.mockk.mockk
|
||||
import java.util.UUID
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.justinvisser.wolfskin.design.*
|
||||
import org.justinvisser.wolfskin.tv.read.LibraryReadFacade
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
/** Home opens the complete shared grid and Back restores its original continuation card. */
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34], application = Application::class, qualifiers = "w960dp-h540dp-xhdpi")
|
||||
class HomeSeeAllNavigationTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test fun personalMovieContinuationReachesTheEighteenthTitleAndReturnsToHome() {
|
||||
val snapshot = HomeFixture(rule.activity).snapshot("justin")
|
||||
val row = snapshot.rows.first { it.id == "pk-m" }
|
||||
val navigation = NavigationManager()
|
||||
val lists = WolfskinSeeAllLists()
|
||||
val shell = ShellState(listOf(ShellPerson("justin", "Justin", "J", true)), "justin") {
|
||||
if (it == ShellAction.Pop) navigation.goBack()
|
||||
}.apply {
|
||||
pickerVisible = false
|
||||
view.row = snapshot.rows.indexOf(row)
|
||||
view.home.focusedKey = row.id
|
||||
view.cards[row.id] = row.cards.size
|
||||
}
|
||||
val server = JellyfinServer(UUID.fromString("00000000-0000-0000-0000-000000000001"), "Fixture", "https://example.invalid", null)
|
||||
val user = JellyfinUser(id = UUID.fromString("00000000-0000-0000-0000-000000000002"), name = "Justin", serverId = server.id, accessToken = null)
|
||||
val model = WolfskinHomeViewModel(rule.activity, mockk(), mockk(), navigation, lists)
|
||||
val facade = mockk<LibraryReadFacade>()
|
||||
rule.setContent {
|
||||
val destination = navigation.backStack.last()
|
||||
val route = (destination as? Destination.Wolfskin)?.route ?: "home"
|
||||
LaunchedEffect(destination) { shell.enterDestination(route) }
|
||||
WolfskinTheme { ShellSurface(shell) { modifier ->
|
||||
if (destination is Destination.Home) HomeScreen(shell, snapshot, modifier) { model.act(it, shell, server, user) }
|
||||
else WolfskinLibraryContent(rule.activity, route, shell, user, "fixture", facade, navigation, lists, modifier)
|
||||
} }
|
||||
}
|
||||
rule.onNodeWithTag("home-card:pk-m:5").assertIsDisplayed()
|
||||
press(Key.DirectionCenter)
|
||||
assertEquals("seeall/justin/pk-m", (navigation.backStack.last() as Destination.Wolfskin).route)
|
||||
val grid = requireNotNull(lists.get("justin/pk-m"))
|
||||
assertEquals(18, grid.items.size)
|
||||
repeat(2) { press(Key.DirectionDown) }
|
||||
repeat(5) { press(Key.DirectionRight) }
|
||||
rule.onNodeWithText("Bleach the Movie: Fade to Black").assertIsDisplayed()
|
||||
assertEquals("movie-bleach-the-movie-fade-to-black-2008", grid.focusedTitle?.id)
|
||||
press(Key.Back)
|
||||
rule.waitForIdle()
|
||||
assertTrue(navigation.backStack.last() is Destination.Home)
|
||||
assertEquals(row.id, snapshot.rows[shell.view.row].id)
|
||||
assertEquals(5, shell.view.cards[row.id])
|
||||
rule.onNodeWithTag("home-card:pk-m:5").assertIsDisplayed()
|
||||
}
|
||||
|
||||
private fun press(key: Key) {
|
||||
rule.onNodeWithTag("wolfskin-shell").performKeyInput { pressKey(key) }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
package org.justinvisser.wolfskin.tv
|
||||
|
||||
import android.app.Application
|
||||
import kotlinx.coroutines.flow.toList
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.justinvisser.wolfskin.conductarr.MemoryCacheStore
|
||||
import org.justinvisser.wolfskin.design.LibraryTitle
|
||||
import org.justinvisser.wolfskin.tv.read.LibrarySnapshotStore
|
||||
|
||||
/** A stale library remains readable offline without crossing server/person boundaries. */
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34], application = Application::class)
|
||||
class LibrarySnapshotStoreTest {
|
||||
@Test fun savedCopyArrivesBeforeFailedRefreshAndFreshCopySkipsNetwork() = runBlocking {
|
||||
var now = 1000L
|
||||
val store = LibrarySnapshotStore(MemoryCacheStore()) { now }
|
||||
val items = listOf(LibraryTitle("a", "Film A", "movie", year = 2007, runtimeSeconds = 6000, played = true))
|
||||
store.read("server:justin", "movies") { items }.toList()
|
||||
val fresh = store.read("server:justin", "movies") { error("Fresh snapshot must not read") }.toList()
|
||||
assertEquals(items, fresh.single().items)
|
||||
now += 11 * 60_000
|
||||
val offline = store.read("server:justin", "movies") { throw java.io.IOException("offline") }.toList()
|
||||
assertEquals(2, offline.size)
|
||||
assertFalse(offline.first().unreachable)
|
||||
assertTrue(offline.last().unreachable)
|
||||
assertEquals(1000L, offline.last().savedAtMillis)
|
||||
assertEquals(items, offline.last().items)
|
||||
for (scope in listOf("server:mindel", "other-server:justin")) {
|
||||
val isolated = store.read(scope, "movies") { throw java.io.IOException("offline") }.toList().single()
|
||||
assertNull(isolated.savedAtMillis)
|
||||
assertTrue(isolated.items.isEmpty())
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun explicitRecoveryRefreshesEvenFreshEmptyDestination() = runBlocking {
|
||||
val store = LibrarySnapshotStore(MemoryCacheStore()) { 1000L }
|
||||
store.read("server:justin", "movies") { emptyList() }.toList()
|
||||
val current = listOf(LibraryTitle("b", "New Film", "movie"))
|
||||
val reads = store.read("server:justin", "movies", force = true) { current }.toList()
|
||||
assertTrue(reads.first().items.isEmpty())
|
||||
assertEquals(current, reads.last().items)
|
||||
assertFalse(reads.last().unreachable)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue