From c893824b6249edc989e286b94b2517e2bdedccd5 Mon Sep 17 00:00:00 2001 From: Paul Hitt Date: Wed, 9 Sep 2026 17:12:40 -0400 Subject: [PATCH] The instrumented suites can get past sign-up again Two of the six suites tapped submit without filling the age gate. Submit is disabled until birth-year holds four digits, and performClick on a disabled Compose node does not throw: it does nothing. Each run then failed up to two minutes later looking for a screen the tap never reached, blaming whatever it had waited for. A JVM test now holds it: any suite that taps submit must fill birth-year, so the next one written cannot quietly do the same thing. android#21 also reports that nav-contacts and nav-account no longer exist anywhere in the app. They do. They are declared in NavDestinations.tabs and applied through a variable, so a grep for the literal testTag("nav-" finds nothing, which reads as the navigation having been rebuilt without them. A third test pins that too, so the claim cannot be made again from a grep. Co-Authored-By: Claude Opus 5 --- .../messenger/app/LiveSignUpFleetTest.kt | 2 + .../flamenet/messenger/app/SignUpFlowTest.kt | 5 ++ .../app/InstrumentedSuitesCanSignUpTest.kt | 70 +++++++++++++++++++ 3 files changed, 77 insertions(+) create mode 100644 app/src/test/kotlin/io/flamenet/messenger/app/InstrumentedSuitesCanSignUpTest.kt diff --git a/app/src/androidTest/kotlin/io/flamenet/messenger/app/LiveSignUpFleetTest.kt b/app/src/androidTest/kotlin/io/flamenet/messenger/app/LiveSignUpFleetTest.kt index 6e4c3b6..4348c9c 100644 --- a/app/src/androidTest/kotlin/io/flamenet/messenger/app/LiveSignUpFleetTest.kt +++ b/app/src/androidTest/kotlin/io/flamenet/messenger/app/LiveSignUpFleetTest.kt @@ -94,6 +94,8 @@ class LiveSignUpFleetTest { compose.onNodeWithTag("handle").performTextInput(name) compose.onNodeWithTag("passphrase").performTextInput("a-long-enough-passphrase") + // See SignUpFlowTest: a disabled submit swallows the tap silently. + compose.onNodeWithTag("birth-year").performTextInput("1990") compose.onNodeWithTag("submit").performClick() // Generous: a network round trip to two services, then a device diff --git a/app/src/androidTest/kotlin/io/flamenet/messenger/app/SignUpFlowTest.kt b/app/src/androidTest/kotlin/io/flamenet/messenger/app/SignUpFlowTest.kt index cf81d87..c6944ab 100644 --- a/app/src/androidTest/kotlin/io/flamenet/messenger/app/SignUpFlowTest.kt +++ b/app/src/androidTest/kotlin/io/flamenet/messenger/app/SignUpFlowTest.kt @@ -160,6 +160,11 @@ class SignUpFlowTest { compose.onNodeWithTag("email").performTextInput("$handle@example.invalid") compose.onNodeWithTag("handle").performTextInput(handle) compose.onNodeWithTag("passphrase").performTextInput("a-long-enough-passphrase") + // Submit is disabled until the age gate has four digits, and + // performClick on a disabled node does NOT throw: it does nothing, and + // the run then fails two minutes later looking for a screen the tap + // never reached. + compose.onNodeWithTag("birth-year").performTextInput("1990") compose.onNodeWithTag("submit").performClick() // Generous: sign-up is a network round trip plus a device registration diff --git a/app/src/test/kotlin/io/flamenet/messenger/app/InstrumentedSuitesCanSignUpTest.kt b/app/src/test/kotlin/io/flamenet/messenger/app/InstrumentedSuitesCanSignUpTest.kt new file mode 100644 index 0000000..e59f638 --- /dev/null +++ b/app/src/test/kotlin/io/flamenet/messenger/app/InstrumentedSuitesCanSignUpTest.kt @@ -0,0 +1,70 @@ +package io.flamenet.messenger.app + +import java.io.File +import kotlin.test.assertTrue +import org.junit.Test + +/** + * The on-device suites have to be able to get past sign-up. + * + * Submit is disabled until the age gate holds four digits, and `performClick()` + * on a disabled Compose node does not throw: it does nothing. A suite that + * skips the age gate taps a dead button and then fails up to two minutes later + * looking for a screen it never reached, blaming whatever it waited for. + * + * These run on the JVM, so they hold whether or not anybody has an emulator. + */ +class InstrumentedSuitesCanSignUpTest { + private fun androidTestDir(): File { + var dir = File(System.getProperty("user.dir")) + while (!File(dir, "app/src/androidTest/kotlin").isDirectory && dir.parentFile != null) { + dir = dir.parentFile + } + return File(dir, "app/src/androidTest/kotlin/io/flamenet/messenger/app") + } + + private fun suites(): List = + androidTestDir().listFiles { f: File -> f.name.endsWith(".kt") }?.toList() ?: emptyList() + + @Test + fun `the suites are where this test expects them`() { + assertTrue(suites().isNotEmpty(), "no instrumented suites found: this test is checking nothing") + } + + @Test + fun `every suite that submits the form also fills the age gate`() { + for (f in suites()) { + val body = f.readText() + if (!body.contains("""onNodeWithTag("submit").performClick()""")) continue + assertTrue( + body.contains("""onNodeWithTag("birth-year")"""), + "${f.name} taps submit without filling birth-year. Submit is disabled " + + "until the age gate has four digits and performClick on a disabled " + + "node does nothing, so the run fails later looking for a screen the " + + "tap never reached. See android#21.", + ) + } + } + + @Test + fun `the tags the suites wait for still exist in the app`() { + var dir = File(System.getProperty("user.dir")) + while (!File(dir, "app/src/main/kotlin").isDirectory && dir.parentFile != null) { + dir = dir.parentFile + } + val main = + File(dir, "app/src/main/kotlin") + .walkTopDown() + .filter { it.extension == "kt" } + .joinToString("\n") { it.readText() } + // Declared in NavDestinations and applied through a variable, so a + // grep for the literal `testTag("nav-` finds nothing and reads as the + // navigation having been rebuilt without them. It has not. + for (tag in listOf("nav-contacts", "nav-account")) { + assertTrue( + main.contains("\"$tag\""), + "the suites wait for $tag and nothing in the app declares it any more", + ) + } + } +}