package social.grain import androidx.compose.material3.Surface import androidx.compose.ui.test.assertIsDisplayed import androidx.compose.ui.test.junit4.v2.createComposeRule import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.performClick import androidx.test.ext.junit.runners.AndroidJUnit4 import org.junit.Rule import org.junit.Test import org.junit.runner.RunWith import social.grain.data.model.AspectRatio import social.grain.data.model.AtLabel import social.grain.data.model.GrainGallery import social.grain.data.model.GrainPhoto import social.grain.data.model.GrainProfile import social.grain.data.model.LabelDefinition import social.grain.data.model.LabelLocale import social.grain.ui.components.GalleryCard import social.grain.ui.theme.GrainTheme /** * That a labelled gallery actually draws its label. * * `resolveLabels` deciding correctly is worth nothing on its own — the bug this * guards against is a card that resolves `WARN_MEDIA` and then renders the * photo anyway, or resolves `BADGE` and renders nothing at all, which is what * happened to every label the server defined for itself. */ @RunWith(AndroidJUnit4::class) class LabelRenderTest { @get:Rule val composeRule = createComposeRule() private fun definition(identifier: String, blurs: String, setting: String, name: String) = LabelDefinition( identifier = identifier, locales = listOf(LabelLocale(name)), blurs = blurs, defaultSetting = setting, ) private fun gallery(vararg labels: String) = GrainGallery( uri = "at://did:plc:sample/social.grain.gallery/1", title = "Yosemite, winter", creator = GrainProfile(did = "did:plc:sample", handle = "ansel.grain.social"), items = listOf( GrainPhoto( uri = "at://did:plc:sample/social.grain.photo/1", thumb = "", fullsize = "", aspectRatio = AspectRatio(3, 2), ), ), labels = labels.map { AtLabel(`val` = it) }, indexedAt = "2026-01-04T09:12:00Z", ) private fun setCard(gallery: GrainGallery, definitions: List) { composeRule.setContent { GrainTheme(dynamicColor = false) { Surface { GalleryCard(gallery = gallery, labelDefinitions = definitions) } } } } /** The gap this closes: a server-defined label that blurs nothing used to * resolve to a badge and then render as nothing whatsoever. */ @Test fun aBadgeLabelIsNamedNextToTheCaption() { setCard( gallery("spoiler"), listOf(definition("spoiler", "none", "warn", "Spoiler")), ) composeRule.onNodeWithText("Spoiler").assertIsDisplayed() // Still a card, not a cover. composeRule.onNodeWithText("Yosemite, winter").assertIsDisplayed() } @Test fun coveredMediaNamesTheLabelAndRevealsOnShow() { setCard( gallery("nudity"), listOf(definition("nudity", "media", "warn", "Nudity")), ) // The name is the point — "Tap to show" told the reader nothing about // what they were being asked to consent to. composeRule.onNodeWithText("Nudity").assertIsDisplayed() composeRule.onNodeWithText("Show").assertIsDisplayed() composeRule.onNodeWithText("Show").performClick() composeRule.waitForIdle() composeRule.onNodeWithText("Show").assertDoesNotExist() } @Test fun contentWarningsCoverTheWholeCardAndCanBeRevealed() { setCard( gallery("!warn"), emptyList(), ) composeRule.onNodeWithText("Warning").assertIsDisplayed() composeRule.onNodeWithText("Yosemite, winter").assertDoesNotExist() composeRule.onNodeWithText("Show anyway").performClick() composeRule.waitForIdle() composeRule.onNodeWithText("Yosemite, winter").assertIsDisplayed() } /** A hidden gallery gets no way through — the difference between "you may * not want to see this" and "this shouldn't be shown". */ @Test fun aHiddenGalleryOffersNoRevealAtAll() { setCard(gallery("!hide"), emptyList()) composeRule.onNodeWithText("Hidden").assertIsDisplayed() composeRule.onNodeWithText("Show anyway").assertDoesNotExist() composeRule.onNodeWithText("Yosemite, winter").assertDoesNotExist() } @Test fun aNegatedLabelGatesNothing() { composeRule.setContent { GrainTheme(dynamicColor = false) { Surface { GalleryCard( gallery = gallery().copy( labels = listOf(AtLabel(`val` = "!hide", neg = true)), ), labelDefinitions = emptyList(), ) } } } composeRule.onNodeWithText("Yosemite, winter").assertIsDisplayed() composeRule.onNodeWithText("Hidden").assertDoesNotExist() } }