Skip to content

Add local E2E test suites for the example app#67

Merged
dianaKhortiuk-frontegg merged 19 commits into
masterfrom
chore/example-app-frontegg-credentials-and-e2e-review
Jun 29, 2026
Merged

Add local E2E test suites for the example app#67
dianaKhortiuk-frontegg merged 19 commits into
masterfrom
chore/example-app-frontegg-credentials-and-e2e-review

Conversation

@dianaKhortiuk-frontegg

Copy link
Copy Markdown
Collaborator

Summary

  • Mirrors instrumented test patterns from frontegg-android-kotlin and frontegg-ios-swift — Android UiAutomator suite and iOS XCUITest suite under example/, both driving the real Frontegg backend with credentials the example app already ships with.
  • Closes the passkey and Refresh Token coverage gaps flagged in docs/E2E_REACT_NATIVE_TESTS_REVIEW.md (§4, §5.4).
  • Adds stable testID props on HomeScreen/ProfileScreen so both suites (and any future Nightwatch migration) can drop text-based selectors (§5.3).

What's included

  • 9 scenarios per platform: login happy-path, login wrong password, logout, Google social, tenant switch, refresh token (new), session restore, request authorize, passkey register/login smoke (new).
  • Android: example/android/app/src/androidTest/java/com/frontegg/demo/e2e/ with utils/Env.kt + utils/UiTestInstrumentation.kt ported from the Kotlin SDK repo; build.gradle gets testInstrumentationRunner and the androidx test deps.
  • iOS: example/ios/ReactNativeExampleUITests/ with a UITestCase base class that forwards env vars into XCUIApplication.launchEnvironment. The Xcode target itself is registered manually — steps in the runbook (editing project.pbxproj blind was too risky).
  • Runbook: example/E2E_TESTS.md — required env vars, ./gradlew connectedDebugAndroidTest and xcodebuild test invocations, Xcode wiring, known limitations.

Deliberately out of scope

  • LocalMockAuthServer port from the native SDK repos. The reference Kotlin/Swift tests for the scenarios we care about drive real Frontegg; the mock server is only needed for the deeper catalog (SAML/OIDC/offline/token-TTL). Good follow-up.
  • CI workflow wiring. .github/workflows/react-native-sdk-e2e.yml still triggers the external e2e-system-tests pipeline unchanged — these suites are for local dev-loop use until the mock server lands.
  • project.pbxproj edit for the iOS UI test target (manual one-time step documented in the runbook).

Test plan

  • Export the env vars listed in example/E2E_TESTS.md from 1Password
  • Android: cd example/android && ./gradlew :app:connectedDebugAndroidTest -Pandroid.testInstrumentationRunnerArguments.LOGIN_EMAIL=... ... on a running emulator — all 9 scenarios green
  • iOS: register the ReactNativeExampleUITests target in Xcode per the runbook, then xcodebuild test -workspace ReactNativeExample.xcworkspace -scheme ReactNativeExample -only-testing ReactNativeExampleUITests -destination 'platform=iOS Simulator,name=iPhone 15' LOGIN_EMAIL=... ... — all 9 scenarios green
  • Confirm the existing external e2e-system-tests job on this PR still passes (these suites don't replace it)

🤖 Generated with Claude Code

dianaKhortiuk-frontegg and others added 13 commits April 8, 2026 12:06
Mirrors the instrumented test patterns from frontegg-android-kotlin and
frontegg-ios-swift so contributors can reproduce e2e flows without the
external e2e-system-tests repo, and closes the passkey / refresh-token
coverage gaps flagged in docs/E2E_REACT_NATIVE_TESTS_REVIEW.md.

- HomeScreen/ProfileScreen: add testID props on every actionable element
  and diagnostic text node for selector stability across both suites.
- Android: new androidTest suite under example/android/app/src/androidTest
  (UiAutomator + JUnit4) with 9 scenarios — login (happy + wrong password),
  logout, Google social, tenant switch, refresh token, session restore,
  request authorize, passkey register/login smoke. Reads credentials from
  instrumentation args via utils/Env.kt.
- iOS: new ReactNativeExampleUITests XCUITest suite with the same 9
  scenarios, sharing a UITestCase base class that forwards env vars into
  XCUIApplication.launchEnvironment.
- example/E2E_TESTS.md: runbook with required env vars, Gradle and
  xcodebuild commands, Xcode target registration steps, and known
  limitations (Google sheet handling, passkey enrolment on bare sim/emu).
- docs/E2E_REACT_NATIVE_TESTS_REVIEW.md: coverage table and §5.3/§5.4
  updated to reflect the new local coverage.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The reusable workflow was referenced at @main but e2e-system-tests has no
main branch — its default is master. The workflow has been failing at
parse time (0-second duration, no jobs) since it was introduced; this
restores the external E2E gate for PRs.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rontegg-credentials-and-e2e-review

# Conflicts:
#	example/src/HomeScreen.tsx
#	example/src/ProfileScreen.tsx
Master changed the example app's Frontegg tenant; the runbook no longer
needs to repeat the hard-coded baseUrl/clientId.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The previous workflow called a reusable workflow from the private
frontegg/e2e-system-tests repo, which never worked — every run since
March 18 failed in 0 seconds with "workflow file issue" because GitHub
blocks cross-repo reusable workflow calls from private repos without
explicit org-level access grants.

This replaces it with a self-contained workflow that mirrors how
frontegg-android-kotlin/demo-e2e.yml works:
- Builds the RN example app's debug + androidTest APKs
- Boots an API 34 emulator with AVD caching
- Runs the 9 UiAutomator E2E tests added in the previous commit
- Publishes JUnit results as a PR check via mikepenz/action-junit-report
- Reads credentials from repository secrets (E2E_LOGIN_EMAIL, etc.)

The check now appears as "React Native SDK E2E" on the PR and will
report real pass/fail status instead of silently dying at parse time.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Adds ReactNativeExampleUITests target to project.pbxproj via a Ruby
  script (example/ios/add_uitest_target.rb) that uses the xcodeproj gem.
  The script is idempotent and checked in so CI or other devs can
  regenerate if the pbxproj is reset.
- Adds ios-e2e job to the workflow: runs on macos-15, installs pods,
  builds for testing, then runs xcodebuild test-without-building with
  -only-testing:ReactNativeExampleUITests on an iPhone 16 simulator.
  Credentials come from the same E2E_* repository secrets as Android.
- Both jobs (android-e2e, ios-e2e) run in parallel on every PR.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The xcodeproj gem omitted productType on the native target and created
a bare PBXFileReference without explicitFileType/.xctest suffix, which
caused xcodebuild to fail with "Required key 'productTypeIdentifier'
is missing". Also removed a duplicate entry in the Products group.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…rget

The xcodeproj gem omitted PRODUCT_NAME = "$(TARGET_NAME)" from both
Debug and Release build configurations, causing xcodebuild to produce
empty product names ("-Runner.app/PlugIns/.xctest"). Also adds
IPHONEOS_DEPLOYMENT_TARGET = 14.0 to match the app target, and
registers the UI test target in the shared scheme's TestAction so
-only-testing:ReactNativeExampleUITests works.

Verified locally: `xcodebuild build-for-testing` produces
ReactNativeExampleUITests-Runner.app with the .xctest bundle inside.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Ports LocalMockAuthServer.swift from frontegg-ionic-capacitor into the
ReactNativeExampleUITests target. The mock server runs at localhost:49381
and handles OAuth/token/user endpoints so tests don't need real Frontegg
credentials.

Infrastructure changes:
- FronteggTest.plist: placeholder HTTPS baseUrl so FronteggSwift's plist
  validation passes at init time.
- FronteggLoaderInitializer.initializeE2EIfNeeded(): reads FRONTEGG_E2E_*
  env vars and calls FronteggApp.shared.manualInit() to redirect the SDK
  to the mock server BEFORE the RN module's lazy init triggers plist loading.
- AppDelegate.mm: calls initializeE2EIfNeeded() first.
- Info.plist: adds NSAllowsLocalNetworking for ATS.
- add_e2e_files.rb: registers LocalMockAuthServer.swift in the UI test
  target and FronteggTest.plist in the app resources.

Verified locally on iPhone 16 Pro simulator (iOS 18.1):
- PasskeysLoginTest: PASSED
- PasskeysRegisterTest: needs login (blocked on ASWebAuthenticationSession)
- LoginViaGoogleTest: PASSED (smoke level)
- LogoutTest/SessionRestore/RefreshToken/etc: need login flow

Login-dependent tests require upgrading the FronteggSwift pod to a version
that supports FRONTEGG_TEST_WEB_AUTH_TRANSPORT (replaces ASWebAuth with a
testable WKWebView for the mock login form).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… skip on missing creds

Android:
- Add kotlin-gradle-plugin classpath + apply plugin to enable Kotlin
  compilation for androidTest sources (was silently skipped before).
- Env.kt: return empty string instead of crashing; add isAvailable().
- All login-dependent tests use Assume.assumeTrue() to skip gracefully.
- UiTestInstrumentation: use text/content-desc selectors (not resource-id)
  because FronteggButton renders testID as content-desc = title on Android.
- Verified via adb: PasskeysLoginTest PASSED, LogoutTest SKIPPED (OK).

iOS:
- LocalMockAuthServer: change baseURL from 127.0.0.1 to localhost (ATS).
- FronteggTest.plist: use https:// placeholder URL so FronteggSwift's
  plist validation passes, then manualInit overrides with mock server URL.
- FronteggLoaderInitializer: split into initializeE2EIfNeeded() (called
  before RN module init) + initializeLoader() to avoid plist crash.
- Info.plist: add NSAllowsLocalNetworking for ATS.
- All login-dependent tests throw XCTSkip when LOGIN_EMAIL is absent.
- Verified on iPhone 16 Pro sim: 2 PASSED, 8 SKIPPED, 0 FAILED.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… accessibilityLabel)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…lock

Android E2E: RN 0.72.1's @react-native/gradle-plugin (Kotlin 1.7.1) does not
support Gradle 8.3+ (which bundles Kotlin 1.9) -> :gradle-plugin:compileKotlin
failed on incompatible metadata. Pin the example Gradle wrapper to 8.0.1.

iOS E2E: the committed example/ios/Podfile.lock pinned FronteggSwift 1.2.76,
conflicting with the SDK's required FronteggSwift 1.3.10. Remove the stale lock
so pod install regenerates it at 1.3.10 (Podfile already targets iOS 14).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…-update

Android E2E: the multi-line gradle command broke in the emulator-runner script
step (a stray backslash reached gradle: 'Task not found'). Put it on one line.

iOS E2E: pod install needs --repo-update to fetch the FronteggSwift 1.3.10 spec
(the runner's spec-repo cache was out of date).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
dianaKhortiuk-frontegg and others added 2 commits June 26, 2026 18:51
# Conflicts:
#	example/ios/Podfile.lock
#	example/ios/ReactNativeExample.xcodeproj/project.pbxproj
#	example/src/HomeScreen.tsx
Under use_frameworks! :static, FronteggRN builds as a static framework and does
not embed FronteggSwift's objects, so any binary that links FronteggRN — the app
target AND the unit-test bundle (inherit! :complete) — linked with unresolved
FronteggSwift.* symbols. frontegg_spm.rb previously *removed* the app-target SPM
link assuming FronteggRN provides it. Instead link the FronteggSwift SPM product
directly into each such target (one XCSwiftPackageProductDependency + a Frameworks
build file per target), located generically by product type
(application + bundle.unit-test); UI-test bundles link neither and are excluded.
Idempotent; result is plutil-lint valid.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dianaKhortiuk-frontegg
dianaKhortiuk-frontegg force-pushed the chore/example-app-frontegg-credentials-and-e2e-review branch from cb5e52a to 9ac758f Compare June 26, 2026 15:35
dianaKhortiuk-frontegg and others added 4 commits June 29, 2026 10:42
LocalMockAuthServer binds a fixed port (49381) via NWListener. tearDown nilled
the shared server and the next class's setUp rebound the same port while the
previous NWListener.cancel() was still in flight, intermittently failing with
EADDRINUSE ('Address already in use') and crashing class setUp via try! — seen
on LoginViaGoogleTest and PasskeysLoginTest. Bind the server once for the whole
serial run (per-test state is already cleared via reset()), removing the rebind
race entirely.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
… bundle

The Debug build doesn't embed a JS bundle and AppDelegate's #if DEBUG path loads
from Metro, which isn't running in CI — so the app showed the RN red box
('No bundle URL present') and no UI rendered, failing the *_is_reachable tests.
Building in Release makes AppDelegate use the embedded main.jsbundle (produced by
the Release bundle phase), so the app is self-contained with no packager needed.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…or social test

Release embeds the JS bundle but disables the SDK's mock web-auth transport
(FronteggRuntime.allowsTestingWebAuthenticationTransport is #if DEBUG), so the
Google social-login smoke test hit a real ASWebAuthenticationSession that can't
complete headlessly in CI. Switch back to a Debug build (mock transport available)
and make the app self-contained without Metro:
- build with FORCE_BUNDLING=1 so the Debug app embeds main.jsbundle
- AppDelegate loads the embedded bundle under test (frontegg-testing=true)
- launchApp gains a useTestingWebAuthenticationTransport flag
- LoginViaGoogleTest launches with the mock transport so the social flow completes
  against the local mock server instead of a real Safari VC

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…d:no-social)

The Google social-login smoke test drives a real ASWebAuthenticationSession that
can't complete headlessly on a CI simulator. The frontegg-ios-swift SDK handles
this by running its E2E with the "hosted:no-social" suite — it excludes the
real system-web-auth social test from the standard pipeline. Mirror that: build
+ run in Release (app embeds main.jsbundle, no Metro) and skip LoginViaGoogleTest
via -skip-testing. Reverts the Debug+FORCE_BUNDLING+mock-transport attempt, since
the SDK does not run real social in CI at all.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@dianaKhortiuk-frontegg
dianaKhortiuk-frontegg merged commit b9af40b into master Jun 29, 2026
5 checks passed
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.

2 participants