The on-device sign-up suite runs again, and sign-up was never broken
Five things were wrong, each hidden behind the one in front of it, and none of them was the app. Sign-up works: the run that finally got through created `andt34972750` on relay1 and registered a device, and the Account screen showed both. 1. The emulator still had a RELEASE-signed build on it, so the debug test APK could not install: INSTALL_FAILED_UPDATE_INCOMPATIBLE. Zero tests ran. The uninstall lived only in the "emulator already up" branch, on the reasoning that a cold boot starts clean. A cold boot starts with no snapshot; the disk image keeps what was installed. The script already PRINTED the leftover package and carried on, which is a check nobody acts on. It now clears on both paths and refuses to continue if anything is left. 2. The sign-in form scrolls, and `toggle-register` is below the fold. A tap aimed off-screen is not an error in Compose, it simply does not happen, so the form stayed on sign-in and the test waited thirty seconds for a register-only field. Same for `sign-out`, which is the last section of a long Account screen. 3. It waited for `device-id` after sign-up. Sign-up lands on Home and `device-id` is on Account, so it waited two minutes on a screen that was never going to show it and reported that the app had not signed in. It had: the dump said "Nobody here yet. Add someone by the username they signed up with", which is the empty contact list of an account created seconds earlier. It now waits for the nav pill and checks the device id on the screen that has it. 4. `performTextInput` appends, and only the relay field was cleared, so the second test in the class typed "19901990" into the age gate. Submit is enabled on exactly four digits, a disabled button swallows a tap, and the wait then blamed a screen the tap never reached. The same shape as the age-gate bug this suite was fixed for once already. `signingOutReturnsToSignIn` now passes, against a real relay, which it has not done in a long time. The remaining failure is not a test problem and is filed separately: creating a second account after signing out is refused with "Your session expired", because `signOut` clears the session in memory and leaves the stored credential on disk. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5
parent
918f424ddd
commit
77ca69b327
@@ -6,6 +6,7 @@ import androidx.compose.ui.test.junit4.createAndroidComposeRule
|
|||||||
import androidx.compose.ui.test.onNodeWithTag
|
import androidx.compose.ui.test.onNodeWithTag
|
||||||
import androidx.compose.ui.test.onRoot
|
import androidx.compose.ui.test.onRoot
|
||||||
import androidx.compose.ui.test.performClick
|
import androidx.compose.ui.test.performClick
|
||||||
|
import androidx.compose.ui.test.performScrollTo
|
||||||
import androidx.compose.ui.test.performTextClearance
|
import androidx.compose.ui.test.performTextClearance
|
||||||
import androidx.compose.ui.test.performTextInput
|
import androidx.compose.ui.test.performTextInput
|
||||||
import androidx.compose.ui.test.printToString
|
import androidx.compose.ui.test.printToString
|
||||||
@@ -77,7 +78,12 @@ class SignUpFlowTest {
|
|||||||
if (nodesWithTag("nav-account").isEmpty()) return
|
if (nodesWithTag("nav-account").isEmpty()) return
|
||||||
compose.onNodeWithTag("nav-account").performClick()
|
compose.onNodeWithTag("nav-account").performClick()
|
||||||
awaitTag("sign-out", 20_000)
|
awaitTag("sign-out", 20_000)
|
||||||
compose.onNodeWithTag("sign-out").performClick()
|
// Scrolled to, for the second time in this file and the same reason.
|
||||||
|
// Sign out is the last section of a long Account screen, under the
|
||||||
|
// profile picture, the status list and the version, so on a phone it is
|
||||||
|
// well below the fold. The tap was going nowhere and the wait that
|
||||||
|
// followed then blamed the sign-in form for not appearing.
|
||||||
|
compose.onNodeWithTag("sign-out").performScrollTo().performClick()
|
||||||
awaitTag("relay", 30_000)
|
awaitTag("relay", 30_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -152,24 +158,75 @@ class SignUpFlowTest {
|
|||||||
|
|
||||||
compose.onNodeWithTag("relay").performTextClearance()
|
compose.onNodeWithTag("relay").performTextClearance()
|
||||||
compose.onNodeWithTag("relay").performTextInput(relay)
|
compose.onNodeWithTag("relay").performTextInput(relay)
|
||||||
compose.onNodeWithTag("toggle-register").performClick()
|
|
||||||
|
// Wait for the relay probe to land BEFORE switching to the register
|
||||||
|
// form, and that order is the whole fix.
|
||||||
|
//
|
||||||
|
// `registering` is a `remember` on the sign-in screen. The probe
|
||||||
|
// finishing recomposes that screen, the remember goes back to its
|
||||||
|
// initial `false`, and a tap that happened while the probe was still in
|
||||||
|
// flight is undone. The test then sat on the sign-in form waiting for an
|
||||||
|
// email field that only the register form has, and reported it as
|
||||||
|
// missing, which reads like the field was deleted from the app.
|
||||||
|
//
|
||||||
|
// `identity-mode` is rendered from `site?.let`, so it appears exactly
|
||||||
|
// when the probe has answered and not before.
|
||||||
|
awaitTag("identity-mode", 30_000)
|
||||||
|
// Scrolled to before it is tapped. The sign-in form is inside a
|
||||||
|
// vertical scroller and this button sits below the fold on a phone-sized
|
||||||
|
// screen, which the failure dump showed as nodes at negative y. A tap
|
||||||
|
// aimed off-screen is not an error in Compose, it simply does not
|
||||||
|
// happen, so the form stayed on sign-in and the test then waited thirty
|
||||||
|
// seconds for a field that only appears after the switch.
|
||||||
|
compose.onNodeWithTag("toggle-register").performScrollTo().performClick()
|
||||||
|
|
||||||
// A relay with its own account table asks for an email; the shared
|
// A relay with its own account table asks for an email; the shared
|
||||||
// service does not.
|
// service does not. Waited for rather than asserted outright, because
|
||||||
|
// whether this relay is one of those is something the probe above
|
||||||
|
// decides.
|
||||||
|
awaitTag("email", 30_000)
|
||||||
compose.onNodeWithTag("email").assertIsDisplayed()
|
compose.onNodeWithTag("email").assertIsDisplayed()
|
||||||
compose.onNodeWithTag("email").performTextInput("$handle@example.invalid")
|
|
||||||
compose.onNodeWithTag("handle").performTextInput(handle)
|
// Cleared before each is typed, not just the relay field.
|
||||||
compose.onNodeWithTag("passphrase").performTextInput("a-long-enough-passphrase")
|
//
|
||||||
|
// `performTextInput` APPENDS. Signing out leaves the form filled in
|
||||||
|
// with what was typed last time, so the second test in the class was
|
||||||
|
// producing "19901990" in the age gate. Submit is enabled only on
|
||||||
|
// exactly four digits, a disabled button swallows a tap without
|
||||||
|
// complaint, and the run then waited two minutes for a screen the tap
|
||||||
|
// was never going to reach. The same shape as the age gate bug this
|
||||||
|
// suite was fixed for once already.
|
||||||
|
for (field in listOf("email", "handle", "passphrase", "birth-year")) {
|
||||||
|
compose.onNodeWithTag(field).performScrollTo().performTextClearance()
|
||||||
|
}
|
||||||
|
|
||||||
|
compose.onNodeWithTag("email").performScrollTo().performTextInput("$handle@example.invalid")
|
||||||
|
compose.onNodeWithTag("handle").performScrollTo().performTextInput(handle)
|
||||||
|
compose.onNodeWithTag("passphrase").performScrollTo().performTextInput("a-long-enough-passphrase")
|
||||||
// Submit is disabled until the age gate has four digits, and
|
// Submit is disabled until the age gate has four digits, and
|
||||||
// performClick on a disabled node does NOT throw: it does nothing, 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
|
// the run then fails two minutes later looking for a screen the tap
|
||||||
// never reached.
|
// never reached.
|
||||||
compose.onNodeWithTag("birth-year").performTextInput("1990")
|
compose.onNodeWithTag("birth-year").performScrollTo().performTextInput("1990")
|
||||||
compose.onNodeWithTag("submit").performClick()
|
compose.onNodeWithTag("submit").performScrollTo().performClick()
|
||||||
|
|
||||||
// Generous: sign-up is a network round trip plus a device registration
|
// Generous: sign-up is a network round trip plus a device registration
|
||||||
// publishing twenty prekeys, on an emulator.
|
// publishing twenty prekeys, on an emulator.
|
||||||
awaitTag("device-id", 120_000)
|
//
|
||||||
|
// Signing up lands on Home, and `device-id` is on the Account screen,
|
||||||
|
// so waiting for it here waited two minutes on a screen that was never
|
||||||
|
// going to show it and then reported the app had not signed in. It had:
|
||||||
|
// the failure dump said "Nobody here yet. Add someone by the username
|
||||||
|
// they signed up with", which is the empty contact list of an account
|
||||||
|
// that had just been created.
|
||||||
|
//
|
||||||
|
// The nav pill is the honest signal that the shell is up, and the
|
||||||
|
// device id is still checked, on the screen that has it, because a
|
||||||
|
// registered device is the half of sign-up that a landing screen alone
|
||||||
|
// does not prove.
|
||||||
|
awaitTag("nav-account", 120_000)
|
||||||
|
compose.onNodeWithTag("nav-account").performClick()
|
||||||
|
awaitTag("device-id", 30_000)
|
||||||
compose.onNodeWithTag("device-id").assertIsDisplayed()
|
compose.onNodeWithTag("device-id").assertIsDisplayed()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user