package io.flamenet.e2e import org.json.JSONObject import kotlin.test.Test import kotlin.test.assertEquals import kotlin.test.assertFalse import kotlin.test.assertNull import kotlin.test.assertTrue /** * Telling somebody their disappearing message was screenshotted (§6.27, ios#36). * * 🔴 **A courtesy from the taker's own client, and nothing else.** A modified * build, a second phone photographing the screen, or the platform's own * recorder all produce nothing. A notice means one was probably taken; no * notice means NOTHING — and most of these tests are about not letting the * interface say otherwise. */ class ScreenshotNoticeTest { @Test fun `the notice carries when it happened`() { val content = Content.parse(Content.screenshot(1_788_000_000)) assertEquals(1_788_000_000, Content.screenshotAt(content)) } /** ⚠️ Control traffic, so an older build stays quiet instead of showing JSON. */ @Test fun `the notice is marked as control traffic`() { val json = JSONObject(Content.screenshot(1)) assertEquals(1, json.getInt("ctl")) assertEquals(Content.Kind.SCREENSHOT, json.getString("kind")) assertTrue(Content.Kind.SCREENSHOT in Content.Kind.CONTROL) } @Test fun `the marker stays first`() { assertTrue(Content.screenshot(1).startsWith("{\"fnc\":1")) } /** A notice is never a message. */ @Test fun `the notice carries no text`() { assertTrue(Content.parse(Content.screenshot(1)).text.isEmpty()) } /** Reading the stamp off anything else must yield nothing. */ @Test fun `an ordinary message has no screenshot stamp`() { assertNull(Content.screenshotAt(Content.parse(Content.text("hi", messageId = "m")))) } // -- When one is sent ----------------------------------------------------- /** 🔴 Only where a timer is set. */ @Test fun `no notice in a conversation with no timer`() { assertFalse(Content.shouldAnnounceScreenshot(0, null, 1_000)) } @Test fun `a notice is sent in a timed conversation`() { assertTrue(Content.shouldAnnounceScreenshot(3600, null, 1_000)) } /** ⚠️ One screenshot often fires several platform events. */ @Test fun `a second event inside the window is suppressed`() { assertFalse(Content.shouldAnnounceScreenshot(3600, 1_000, 2_000)) } @Test fun `a genuine second screenshot later is announced`() { assertTrue(Content.shouldAnnounceScreenshot(3600, 1_000, 10_000)) } /** ⚠️ The timer check comes first, or a window would enable an untimed chat. */ @Test fun `the window does not override the timer rule`() { assertFalse(Content.shouldAnnounceScreenshot(0, 1_000, 999_000)) } // -- What may be claimed -------------------------------------------------- /** * 🔴 **The most important test here.** Presenting the absence of notices as * a fact would be inventing evidence out of silence. */ @Test fun `the disclaimer refuses to turn silence into evidence`() { val t = Content.screenshotDisclaimer assertTrue(t.contains("when their app can tell"), t) assertTrue(t.contains("cannot always"), t) assertTrue(t.contains("was not captured"), t) } /** * ⚠️ Android's half of the asymmetry has to be stated, not hidden. Claiming * a symmetry with iOS that does not exist is worse than the gap, because * somebody would rely on it. */ @Test fun `the disclaimer states the android limitation`() { val t = Content.screenshotDisclaimer assertTrue(t.contains("recent version"), t) assertTrue(t.contains("screen recording"), t) } }