Skip to content

Add an End-to-End test suite for the Android SDK - #27

Open
brionmario wants to merge 3 commits into
thunder-id:mainfrom
brionmario:mobile-e2e-tests
Open

Add an End-to-End test suite for the Android SDK#27
brionmario wants to merge 3 commits into
thunder-id:mainfrom
brionmario:mobile-e2e-tests

Conversation

@brionmario

@brionmario brionmario commented Sep 1, 2026

Copy link
Copy Markdown
Member

Purpose

The Android SDK had unit tests but no coverage that exercised it the way a person uses it. Unit tests stub the server out, so they cannot catch the failures that matter most to someone integrating the SDK: a flow step the SDK renders but cannot submit, a field the server renames, or an application whose configuration the SDK rejects at run time.

This adds a Maestro suite that drives the Quickstart sample against a real ThunderID server. It signs a user in, signs them out, registers a new account, and signs in as that new account.

Writing it surfaced four defects that affect users, not just tests. They are described under Approach.

Approach

Maestro rather than Espresso. The sign-in and sign-up forms are not static native views: the server returns a flow definition and the SDK renders it. All three mobile SDKs tag the resulting fields the same way, thunderid-field-<identifier> and thunderid-action-<ref>, so one set of selectors and one set of flows works across iOS, Android and Flutter.

One script owns the run. tests/e2e/run-e2e.sh starts a server, provisions the test application and user, builds and installs the sample, and runs the flows. CI calls the same script through a composite action, so a green run locally and a green run in CI mean the same thing. run-e2e.ps1 is the PowerShell twin for contributors on Windows, which is the one mobile SDK where Windows development is viable. Stages are skippable and every stage is idempotent.

The test application is declarative. thunderid-config.yaml defines a type: mobile application with attestation.devMode: true. A mobile application must normally prove its binary identity through Play Integrity before it can initiate a flow, which an emulator cannot satisfy. devMode is test only.

Defects fixed

Compose test tags were invisible to anything outside Compose. Modifier.testTag lives in Compose's own semantics tree, so none of the flow fields were reachable from the platform accessibility tree, and therefore not by UI Automator, Maestro, Appium, or any other black box driver. The components that render flow steps now opt their subtree in with testTagsAsResourceId.

Sign-up was completely broken. SignUp handled FlowStatus.PROMPT_ONLY but ignored FlowStatus.INCOMPLETE, and INCOMPLETE is what the server returns for every registration step. The form was silently dropped and the sheet rendered empty, so a user tapping "Get started" saw a title and nothing else. SignIn already treated the two alike. SignUp also tagged none of its fields or actions, unlike SignIn, so it now does.

The sample's sign-up sheet never closed. Nothing reset it when the flow completed, leaving an empty sheet covering the app. Sign-in only appeared unaffected because a successful sign-in swaps RootView to HomeScreen and tears the sheet down as a side effect; sign-up does not establish a session, so the sheet stayed.

allowInsecureConnections disabled certificate validation for every host. It installed a trust-all X509TrustManager and a hostname verifier returning true unconditionally, and nothing in the SDK gated it on a debuggable build. An app that shipped with it enabled had no certificate validation at all on the channel carrying credentials, assertions and refresh tokens, and it failed silently because the app kept working. It now applies only to loopback hosts (localhost, 127.0.0.1, ::1, 10.0.2.2), where no network attacker can sit, and raises a configuration error for anything else. The development workflow is unchanged, which the suite demonstrates by running against 10.0.2.2.

Related Issues

Related PRs

Checklist

  • Followed the contribution guidelines.
  • Manual test round performed and verified.
  • Documentation provided. (Add links if there are any)
    • tests/e2e/README.md, plus the SDK Development section of the contributor docs
  • Tests provided. (Add links if there are any)
    • Unit Tests
    • Integration Tests
      • tests/e2e/flows/signin.yaml, tests/e2e/flows/signup.yaml
  • Breaking changes. (Fill if applicable)
    • Breaking changes section filled.
    • breaking change label added.

Worth a reviewer's attention: narrowing allowInsecureConnections to loopback is a behaviour change. Any caller relying on it for a non-loopback host now receives a configuration error instead of a silently insecure connection. That was the intent, but it is a change in observable behaviour.

Security checks

  • Followed secure coding standards.
  • Confirmed that this PR doesn't commit any keys, passwords, tokens, usernames, or other secrets.

The credentials in this PR (e2e_mobile_user / TestPassword@123) belong to a throwaway account the suite creates on a local server. They are not secrets and grant nothing anywhere else.

Verification

./gradlew ktlintCheck testDebugUnitTest passes. The suite was run end to end against a freshly downloaded ThunderID v1.0.1 on an API 34 emulator, 2 of 2 flows passing, with the hardened certificate check in place.

@coderabbitai

coderabbitai Bot commented Sep 1, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Team

Run ID: 86333293-b002-4865-b6b8-3b2c131c7cd6


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@brionmario brionmario changed the title Add an end-to-end test suite for the Android Quickstart Add an end-to-end test suite for the Android SDK Sep 1, 2026
@brionmario brionmario changed the title Add an end-to-end test suite for the Android SDK Add an End-to-End test suite for the Android SDK Sep 1, 2026
Drive the Quickstart through real authentication against a real ThunderID
server using Maestro: sign in and sign out, then register a new account and
sign in as it. The suite lives in tests/e2e and one script, run-e2e.sh, owns
the whole run, so CI executes exactly what a contributor runs locally. A
PowerShell twin, run-e2e.ps1, covers contributors working on Windows.

Maestro rather than Espresso because the sign-in and sign-up forms are
rendered from the flow definition the server returns, not from static native
views. The iOS and Flutter SDKs tag those fields identically, so one set of
selectors works across all three platforms.

Writing the suite surfaced three defects that affected users, not just tests:

Compose keeps testTag inside its own semantics tree, so none of the flow
fields were reachable from the platform accessibility tree. The components
that render flow steps now opt their subtree in with testTagsAsResourceId.

SignUp ignored FlowStatus.INCOMPLETE, which is what the server returns for
every registration step, so the form never rendered at all and sign-up was
unusable. SignIn already treated INCOMPLETE and PROMPT_ONLY alike. SignUp
also tagged nothing, so it now tags its fields and actions the way SignIn
does.

The sample's sign-up sheet was never dismissed once the flow completed,
leaving an empty sheet covering the app. Sign-in only appeared to work
because a successful sign-in swaps the whole screen and tears the sheet down
with it.

Finally, restrict allowInsecureConnections to loopback hosts. It installed a
trust-all manager and a permissive hostname verifier for any host, so an app
that shipped with it enabled had no certificate validation on the channel
carrying credentials, assertions and refresh tokens. It now applies only to
localhost, 127.0.0.1, ::1 and 10.0.2.2, and raises a configuration error for
anything else.

Refs thunder-id/thunderid#5181

Signed-off-by: Brion <info@brionmario.com>
Every flow starts here, and a bare assertVisible gets Maestro's short
default lookup timeout rather than the generous budget the rest of the
suite uses. On a loaded CI emulator the landing screen does not always
render inside it, which failed the Android sign-up flow 19 seconds in,
before the flow had done anything. Wait for it explicitly instead.

Signed-off-by: Brion <info@brionmario.com>
CI installed whatever Maestro was newest at the time, so the test runner
changed under the suite between runs with nothing in the repository to
show it: CI has been on 2.10.0 since it shipped while a contributor
following the README gets whatever is current, which makes a CI-only
failure impossible to reproduce faithfully. Pin it, and bump
deliberately after checking the flows.

The JUnit report gives a failed run a machine-readable result instead of
a console log to scrape, and CI now collects it with the other debug
artifacts.

Signed-off-by: Brion <info@brionmario.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant