Read live shell state inside cached navigation entries
This commit is contained in:
parent
d401ebc355
commit
afff383b75
4 changed files with 140 additions and 2 deletions
|
|
@ -27,6 +27,7 @@ internal class FakeConductarr : Closeable {
|
|||
@Volatile var seasonTwoScore = 8.1
|
||||
@Volatile var localArt = false
|
||||
@Volatile var beforeResponse: ((String) -> Unit)? = null
|
||||
@Volatile var audiencesBody: String? = null
|
||||
private val imageBytes = java.util.Base64.getDecoder().decode(
|
||||
"iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jYioAAAAASUVORK5CYII=")
|
||||
|
||||
|
|
@ -66,6 +67,7 @@ internal class FakeConductarr : Closeable {
|
|||
}
|
||||
if (key == "ratings" && ratingsStatus != 200) return response("", ratingsStatus)
|
||||
if (key == "audiences" && audiencesStatus != 200) return response("", audiencesStatus)
|
||||
if (key == "audiences") audiencesBody?.let { return response(it) }
|
||||
if (notModified && request.headers["If-None-Match"] != null) return response("", 304)
|
||||
if (key == "requests" && !existingRequests) return response("{\"requests\":[]}")
|
||||
var body = wire.getValue(key).jsonObject
|
||||
|
|
|
|||
|
|
@ -0,0 +1,122 @@
|
|||
package org.justinvisser.wolfskin.tv
|
||||
|
||||
import android.app.Application
|
||||
import android.os.Looper
|
||||
import androidx.activity.ComponentActivity
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.input.key.Key
|
||||
import androidx.compose.ui.test.*
|
||||
import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.github.damontecres.wholphin.data.ServerRepository
|
||||
import com.github.damontecres.wholphin.data.model.*
|
||||
import com.github.damontecres.wholphin.preferences.AppPreferences
|
||||
import com.github.damontecres.wholphin.preferences.UserPreferences
|
||||
import com.github.damontecres.wholphin.services.NavigationManager
|
||||
import com.github.damontecres.wholphin.ui.successQueryResult
|
||||
import io.mockk.coEvery
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import java.util.UUID
|
||||
import kotlinx.coroutines.cancel
|
||||
import org.jellyfin.sdk.api.client.ApiClient
|
||||
import org.jellyfin.sdk.api.client.extensions.itemsApi
|
||||
import org.jellyfin.sdk.api.client.extensions.userViewsApi
|
||||
import org.jellyfin.sdk.api.operations.ItemsApi
|
||||
import org.jellyfin.sdk.api.operations.UserViewsApi
|
||||
import org.jellyfin.sdk.model.api.*
|
||||
import org.junit.Assert.*
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.justinvisser.wolfskin.conductarr.MemoryCacheStore
|
||||
import org.justinvisser.wolfskin.design.*
|
||||
import org.justinvisser.wolfskin.tv.read.LibraryReadFacade
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.Shadows
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
/** Fresh login exercises the actual NavDisplay and renders user-prefixed Jellyfin libraries. */
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(sdk = [34], application = Application::class, qualifiers = "w960dp-h540dp-xhdpi")
|
||||
class FirstSignInNavigationTest {
|
||||
@get:Rule val rule = createAndroidComposeRule<ComponentActivity>()
|
||||
|
||||
@Test fun firstContinueThenMoviesRendersMindelsGridWithoutReenteringThePicker() {
|
||||
ConductarrPreference.initialize(rule.activity)
|
||||
ConductarrPreference.updateFixtures(false)
|
||||
WolfskinScreenPreference.initialize(rule.activity)
|
||||
WolfskinScreenPreference.enabled = true
|
||||
val previousUrl = ConductarrPreference.baseUrl
|
||||
FakeConductarr().use { fake ->
|
||||
fake.audiencesBody = """{"audiences":[{"is_anime":false,"libraries":["Mindel Movies"],"media_type":"movie","owner":"mindel","users":["mindel"]},{"is_anime":false,"libraries":["Mindel Shows"],"media_type":"tv","owner":"mindel","users":["mindel"]}]}"""
|
||||
ConductarrPreference.updateBaseUrl(fake.url)
|
||||
ConductarrSession(MemoryCacheStore()).use { conductarr ->
|
||||
val server = JellyfinServer(UUID.randomUUID(), "Jellyfin", fake.url, null)
|
||||
val user = JellyfinUser(id = UUID.randomUUID(), name = "mindel", serverId = server.id, accessToken = MINDEL_TOKEN)
|
||||
val repository = mockk<ServerRepository>(relaxed = true)
|
||||
coEvery { repository.serverDao.getServer(server.id) } returns JellyfinServerUsers(server, listOf(user))
|
||||
val api = mockk<ApiClient>()
|
||||
val views = mockk<UserViewsApi>()
|
||||
val items = mockk<ItemsApi>()
|
||||
every { api.userViewsApi } returns views
|
||||
every { api.itemsApi } returns items
|
||||
val movies = BaseItemDto(id = UUID.randomUUID(), name = "Mindel Movies", type = BaseItemKind.COLLECTION_FOLDER, collectionType = CollectionType.MOVIES)
|
||||
val shows = BaseItemDto(id = UUID.randomUUID(), name = "Mindel Shows", type = BaseItemKind.COLLECTION_FOLDER, collectionType = CollectionType.TVSHOWS)
|
||||
coEvery { views.getUserViews(userId = user.id) } returns successQueryResult(listOf(movies, shows))
|
||||
for ((library, kind, count) in listOf(Triple(movies, BaseItemKind.MOVIE, 6), Triple(shows, BaseItemKind.SERIES, 29))) {
|
||||
coEvery { items.getItems(userId = user.id, parentId = library.id, recursive = true,
|
||||
includeItemTypes = listOf(kind), fields = listOf(ItemFields.PROVIDER_IDS, ItemFields.SORT_NAME, ItemFields.DATE_CREATED),
|
||||
sortBy = listOf(ItemSortBy.SORT_NAME), startIndex = 0, limit = 200) } returns successQueryResult(
|
||||
List(count) { BaseItemDto(id = UUID.randomUUID(), name = "${library.name} title $it", type = kind) })
|
||||
}
|
||||
val navigation = NavigationManager()
|
||||
val session = WolfskinSession()
|
||||
val model = WolfskinShellViewModel(rule.activity, session, repository, navigation, mockk(relaxed = true), mockk(relaxed = true),
|
||||
LibraryReadFacade(api), WolfskinSeeAllLists(), conductarr, mockk(relaxed = true))
|
||||
val home = HomeSnapshot(listOf(HomeRow("resume", HomeRowKind.Hero, cards = listOf(
|
||||
HomeTitle("episode", "Me and My Town", "episode", seriesTitle = "Desperate Housewives", season = 5, episode = 9)), heroKind = "resume")))
|
||||
try {
|
||||
rule.setContent {
|
||||
WolfskinApp(server, user, navigation, UserPreferences(AppPreferences.getDefaultInstance(), null), Modifier, model,
|
||||
homeContent = { shell, _, _, modifier -> HomeScreen(shell, home, modifier) {} })
|
||||
}
|
||||
rule.waitUntil(10_000) {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
conductarr.identity.value?.signedIn == true && model.libraries.audiences != null
|
||||
}
|
||||
rule.onNodeWithText("Mindel").assertIsDisplayed()
|
||||
rule.onNodeWithTag("wolfskin-shell").performKeyInput { pressKey(Key.DirectionCenter) }
|
||||
rule.onNodeWithText("Desperate Housewives").assertIsDisplayed()
|
||||
assertFalse("Continue must close the session's real picker, not a discarded launch shell", session.state!!.pickerVisible)
|
||||
rule.onNodeWithTag("wolfskin-shell").performKeyInput {
|
||||
pressKey(Key.DirectionUp)
|
||||
pressKey(Key.DirectionRight)
|
||||
pressKey(Key.DirectionRight)
|
||||
pressKey(Key.DirectionCenter)
|
||||
}
|
||||
rule.waitUntil(10_000) {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
rule.onAllNodesWithTag("library-grid").fetchSemanticsNodes().isNotEmpty()
|
||||
}
|
||||
rule.onNodeWithTag("library-grid").assertIsDisplayed()
|
||||
rule.onAllNodesWithText("Mindel Movies title 0")[0].assertIsDisplayed()
|
||||
rule.onNodeWithText("Desperate Housewives").assertDoesNotExist()
|
||||
assertEquals(6, session.state!!.view.library!!.source.size)
|
||||
assertFalse(session.state!!.person.hasAnime)
|
||||
rule.onNodeWithText("Shows").performClick()
|
||||
rule.waitUntil(10_000) {
|
||||
Shadows.shadowOf(Looper.getMainLooper()).idle()
|
||||
session.state!!.view.library?.source?.size == 29
|
||||
}
|
||||
rule.onNodeWithTag("library-grid").assertIsDisplayed()
|
||||
rule.onAllNodesWithText("Mindel Shows title 0")[0].assertIsDisplayed()
|
||||
rule.onNodeWithText("Home").performClick()
|
||||
rule.onNodeWithText("Desperate Housewives").assertIsDisplayed()
|
||||
assertFalse("Returning Home must not reopen the picker", session.state!!.pickerVisible)
|
||||
} finally { model.viewModelScope.cancel() }
|
||||
}
|
||||
}
|
||||
ConductarrPreference.updateBaseUrl(previousUrl)
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue