This commit is contained in:
@@ -2,12 +2,63 @@ package com.ponzischeme89.memby.data
|
||||
|
||||
import com.ponzischeme89.memby.data.model.BaseItem
|
||||
import com.ponzischeme89.memby.data.model.EmbyPerson
|
||||
import kotlinx.coroutines.test.runTest
|
||||
import kotlinx.serialization.json.Json
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class CastMetadataTest {
|
||||
|
||||
@Test
|
||||
fun `episode without cast falls back to its series cast`() = runTest {
|
||||
val seriesCast = listOf(actor("series-lead"))
|
||||
val items = mapOf(
|
||||
"episode" to BaseItem(id = "episode", type = "Episode", seriesId = "series"),
|
||||
"series" to BaseItem(id = "series", type = "Series", people = seriesCast),
|
||||
)
|
||||
val requested = mutableListOf<String>()
|
||||
|
||||
val result = resolveCast("episode") { id ->
|
||||
requested += id
|
||||
requireNotNull(items[id])
|
||||
}
|
||||
|
||||
assertEquals(seriesCast, result)
|
||||
assertEquals(listOf("episode", "series"), requested)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `episode cast takes precedence over series cast`() = runTest {
|
||||
val guestCast = listOf(actor("guest"))
|
||||
val requested = mutableListOf<String>()
|
||||
|
||||
val result = resolveCast("episode") { id ->
|
||||
requested += id
|
||||
BaseItem(
|
||||
id = id,
|
||||
type = "Episode",
|
||||
seriesId = "series",
|
||||
people = guestCast,
|
||||
)
|
||||
}
|
||||
|
||||
assertEquals(guestCast, result)
|
||||
assertEquals(listOf("episode"), requested)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `non-episode without cast does not use a series fallback`() = runTest {
|
||||
val requested = mutableListOf<String>()
|
||||
|
||||
val result = resolveCast("movie") { id ->
|
||||
requested += id
|
||||
BaseItem(id = id, type = "Movie", seriesId = "series")
|
||||
}
|
||||
|
||||
assertEquals(emptyList<EmbyPerson>(), result)
|
||||
assertEquals(listOf("movie"), requested)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `cast contains actors and preserves Emby order`() {
|
||||
val item = BaseItem(
|
||||
@@ -45,4 +96,6 @@ class CastMetadataTest {
|
||||
assertEquals("person-1", item.cast.single().id)
|
||||
assertEquals("portrait-tag", item.cast.single().primaryImageTag)
|
||||
}
|
||||
|
||||
private fun actor(id: String) = EmbyPerson(id = id, name = id, type = "Actor")
|
||||
}
|
||||
|
||||
@@ -118,11 +118,12 @@ class GatewayPayloadTest {
|
||||
@Test
|
||||
fun `decodes explicit client server protocol mismatch`() {
|
||||
val status = json.decodeFromString<GatewayServiceStatus>(
|
||||
"""{"maintenance":false,"compatible":false,"compatibilityMessage":"App protocol 2, server protocol 1.","clientVersion":"0.9.1","clientProtocol":"2","serverProtocol":1}""",
|
||||
"""{"maintenance":false,"compatible":false,"compatibilityMessage":"App protocol 2, server protocol 1.","clientVersion":"0.9.1","clientProtocol":"2","gatewayVersion":"0.1.29","serverProtocol":1}""",
|
||||
)
|
||||
|
||||
assertEquals(false, status.compatible)
|
||||
assertEquals("App protocol 2, server protocol 1.", status.compatibilityMessage)
|
||||
assertEquals("0.1.29", status.gatewayVersion)
|
||||
assertEquals(1, status.serverProtocol)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ponzischeme89.memby.ui
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class ProfileInitialsTest {
|
||||
@Test
|
||||
fun `uses camel case capitals for compact usernames`() {
|
||||
assertEquals("AV", profileInitials("AlwynV"))
|
||||
assertEquals("MC", profileInitials("MattC"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `uses word initials and safe short fallbacks`() {
|
||||
assertEquals("MC", profileInitials("Matt Cohen"))
|
||||
assertEquals("AL", profileInitials("alwyn"))
|
||||
assertEquals("?", profileInitials(" "))
|
||||
}
|
||||
}
|
||||
@@ -58,4 +58,12 @@ class GenreBrowseTest {
|
||||
assertTrue(item.withFavourite(true).isFavorite)
|
||||
assertFalse(item.withFavourite(true).withFavourite(false).isFavorite)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `watched overrides update a paged poster immediately`() {
|
||||
val item = BaseItem(id = "film", name = "Film", type = "Movie")
|
||||
assertFalse(item.userData?.played == true)
|
||||
assertTrue(item.withPlayed(true).userData?.played == true)
|
||||
assertFalse(item.withPlayed(true).withPlayed(false).userData?.played == true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package com.ponzischeme89.memby.ui.whatsnew
|
||||
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onRoot
|
||||
import com.github.takahirom.roborazzi.captureRoboImage
|
||||
import com.ponzischeme89.memby.ui.PreviewSurface
|
||||
import com.ponzischeme89.memby.ui.settings.ReleaseNote
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.robolectric.RobolectricTestRunner
|
||||
import org.robolectric.annotation.Config
|
||||
import org.robolectric.annotation.GraphicsMode
|
||||
|
||||
/**
|
||||
* The panel a television shows once, after it has updated itself overnight. Nobody sees it
|
||||
* twice, so being able to look at it without reinstalling on hardware is the point.
|
||||
*/
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@GraphicsMode(GraphicsMode.Mode.NATIVE)
|
||||
@Config(sdk = [34], qualifiers = "w960dp-h540dp-television-xhdpi")
|
||||
class WhatsNewScreenshotTest {
|
||||
|
||||
@get:Rule
|
||||
val compose = createComposeRule()
|
||||
|
||||
@Test
|
||||
fun `a typical release`() {
|
||||
compose.setContent {
|
||||
PreviewSurface {
|
||||
WhatsNewOverlay(
|
||||
release = ReleaseNote(
|
||||
version = "0.2.24",
|
||||
date = "2026-08-05",
|
||||
changes = listOf(
|
||||
"Fixed: The centre button on the remote now pauses straight away.",
|
||||
"Changed: Subtitles now open in a small menu above the button.",
|
||||
"Added: Continuing and ended tags on My Shows.",
|
||||
),
|
||||
),
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
compose.onRoot().captureRoboImage("build/screenshots/whats-new/whats-new.png")
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a long release is capped`() {
|
||||
compose.setContent {
|
||||
PreviewSurface {
|
||||
WhatsNewOverlay(
|
||||
release = ReleaseNote(
|
||||
version = "0.2.24",
|
||||
date = "2026-08-05",
|
||||
changes = List(9) { "Fixed: Change number ${it + 1} in a long release." },
|
||||
),
|
||||
onDismiss = {},
|
||||
)
|
||||
}
|
||||
}
|
||||
compose.onRoot().captureRoboImage("build/screenshots/whats-new/whats-new-long.png")
|
||||
}
|
||||
}
|
||||
@@ -1,26 +1,19 @@
|
||||
package com.ponzischeme89.memby.ui.whatsnew
|
||||
|
||||
import com.ponzischeme89.memby.ui.settings.ReleaseNote
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class WhatsNewTest {
|
||||
|
||||
private val history = listOf(
|
||||
ReleaseNote("0.2.24", "2026-08-05", listOf("Fixed: A thing.", "Added: Another thing.")),
|
||||
ReleaseNote("0.2.23", "2026-08-04", listOf("Added: An older thing.")),
|
||||
)
|
||||
|
||||
private fun decide(
|
||||
installed: String = "0.2.24",
|
||||
seen: String? = "0.2.23",
|
||||
signedIn: Boolean = true,
|
||||
releases: List<ReleaseNote> = history,
|
||||
) = whatsNewDecision(installed, seen, signedIn, releases)
|
||||
) = whatsNewDecision(installed, seen, signedIn)
|
||||
|
||||
@Test
|
||||
fun `an updated television is shown the notes for the build it is running`() {
|
||||
assertEquals(WhatsNewDecision.Show(history[0]), decide())
|
||||
fun `an updated television is notified of the build it is running`() {
|
||||
assertEquals(WhatsNewDecision.Notify("0.2.24"), decide())
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -38,23 +31,17 @@ class WhatsNewTest {
|
||||
|
||||
@Test
|
||||
fun `an install that predates the record is announced once it is signed in`() {
|
||||
assertEquals(WhatsNewDecision.Show(history[0]), decide(seen = null, signedIn = true))
|
||||
assertEquals(WhatsNewDecision.Notify("0.2.24"), decide(seen = null, signedIn = true))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a signed-out television with a record waits rather than burning the notes`() {
|
||||
fun `a signed-out television with a record waits rather than consuming the notice`() {
|
||||
assertEquals(WhatsNewDecision.Nothing, decide(seen = "0.2.23", signedIn = false))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a build the changelog does not describe is recorded silently`() {
|
||||
assertEquals(WhatsNewDecision.MarkSeen("0.9.0"), decide(installed = "0.9.0"))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `a release with no bullets is not shown as an empty panel`() {
|
||||
val empty = listOf(ReleaseNote("0.2.24", "2026-08-05", emptyList()))
|
||||
assertEquals(WhatsNewDecision.MarkSeen("0.2.24"), decide(releases = empty))
|
||||
fun `an updated build does not depend on a changelog entry`() {
|
||||
assertEquals(WhatsNewDecision.Notify("0.9.0"), decide(installed = "0.9.0"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -62,13 +49,4 @@ class WhatsNewTest {
|
||||
assertEquals(WhatsNewDecision.Nothing, decide(installed = " "))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun `the bullet count is cut to what the screen can hold`() {
|
||||
// A 720p television, which is the small case this exists for.
|
||||
assertEquals(4, maxChangesFor(540))
|
||||
// Room for more does not mean an unbounded list.
|
||||
assertEquals(6, maxChangesFor(1200))
|
||||
// Never zero: a panel with a heading and nothing under it says nothing at all.
|
||||
assertEquals(1, maxChangesFor(200))
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user