Skip to content

Commit b9af40b

Browse files
Add local E2E test suites for the example app (#67)
* Add local E2E test suites for the example app 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> * Fix react-native-sdk-e2e workflow ref 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> * Drop stale credential refs from E2E_TESTS.md 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> * Replace broken cross-repo e2e workflow with local Android emulator suite 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> * Add iOS E2E job and register XCUITest target in Xcode project - 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> * Fix pbxproj for UI test target: add productType and fix product refs 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> * Fix iOS UI test target: add PRODUCT_NAME, scheme entry, deployment target 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> * Wire iOS E2E tests to LocalMockAuthServer 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> * Verify E2E tests locally: fix Android Kotlin plugin, iOS mock server, 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> * fix(e2e): pin example Gradle to 8.0.1 (RN 0.72) + drop stale Podfile.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> * fix(e2e): single-line the android gradle command + pod install --repo-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> * fix(ios/spm): link FronteggSwift into every target that links FronteggRN 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> * test(e2e/ios): bind the mock auth server once instead of per test class 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> * ci(e2e/ios): build + run UI tests in Release so the app embeds the JS 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> * ci(e2e/ios): Debug build with embedded JS + mock web-auth transport for 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> * ci(e2e/ios): exclude real social-login test from CI (mirror SDK hosted: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> --------- Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent bc90fb5 commit b9af40b

42 files changed

Lines changed: 4118 additions & 733 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 219 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,227 @@
1-
# Runs React Native SDK E2E tests from e2e-system-tests on every PR.
2-
# The check runs in the e2e-system-tests repo and reports back here; merge is blocked until it passes.
3-
# Require this check in branch protection: Settings → Branches → rule → "React Native SDK E2E".
1+
# Local E2E tests for the React Native example app (Android + iOS).
2+
# Mirrors frontegg-android-kotlin's demo-e2e.yml and frontegg-ios-swift's
3+
# demo-e2e.yml: builds the example app, boots a device/simulator, runs the
4+
# instrumented tests, and reports results as PR checks.
5+
#
6+
# The previous cross-repo call to e2e-system-tests was never functional
7+
# (0-second failure on every run since the workflow was created) due to
8+
# GitHub's private-repo reusable-workflow access restrictions. This replaces
9+
# it with self-contained local suites that actually run.
410

511
name: React Native SDK E2E
612

713
on:
814
pull_request:
915
types: [opened, synchronize, reopened]
16+
workflow_dispatch:
17+
18+
permissions:
19+
contents: read
20+
checks: write
1021

1122
jobs:
12-
react-native-sdk-e2e:
13-
name: React Native SDK E2E
14-
uses: frontegg/e2e-system-tests/.github/workflows/react-native-sdk-e2e-reusable.yml@main
15-
with:
16-
repo_ref: ${{ github.event.pull_request.head.sha }}
17-
test_suite: hosted:no-social
18-
secrets: inherit
23+
# ──────────────────────────────────────────────────────────────────────
24+
# Android — UiAutomator on an API 34 emulator
25+
# ──────────────────────────────────────────────────────────────────────
26+
android-e2e:
27+
name: Android E2E
28+
runs-on: ubuntu-latest
29+
timeout-minutes: 45
30+
steps:
31+
- name: Checkout
32+
uses: actions/checkout@v4
33+
34+
- name: Set up JDK 17
35+
uses: actions/setup-java@v4
36+
with:
37+
distribution: temurin
38+
java-version: '17'
39+
40+
- name: Set up Node.js
41+
uses: actions/setup-node@v4
42+
with:
43+
node-version: 18
44+
45+
- name: Install JS dependencies
46+
run: |
47+
yarn install --frozen-lockfile
48+
cd example && yarn install --frozen-lockfile
49+
50+
- name: Cache Gradle dependencies
51+
uses: actions/cache@v4
52+
with:
53+
path: |
54+
~/.gradle/caches
55+
~/.gradle/wrapper
56+
key: gradle-${{ runner.os }}-${{ hashFiles('example/android/**/*.gradle*', 'example/android/gradle/wrapper/gradle-wrapper.properties') }}
57+
restore-keys: |
58+
gradle-${{ runner.os }}-
59+
60+
- name: Build debug APK + androidTest APK
61+
working-directory: example/android
62+
run: |
63+
chmod +x gradlew
64+
./gradlew :app:assembleDebug :app:assembleDebugAndroidTest --no-daemon
65+
66+
- name: Enable KVM
67+
run: |
68+
echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules
69+
sudo udevadm control --reload-rules
70+
sudo udevadm trigger --name-match=kvm
71+
72+
- name: AVD cache
73+
uses: actions/cache@v4
74+
id: avd-cache
75+
with:
76+
path: |
77+
~/.android/avd/*
78+
~/.android/adb*
79+
key: avd-api34-google_apis-x86_64-pixel6-v1
80+
81+
- name: Create AVD snapshot if cache missing
82+
if: steps.avd-cache.outputs.cache-hit != 'true'
83+
uses: reactivecircus/android-emulator-runner@v2
84+
with:
85+
api-level: 34
86+
arch: x86_64
87+
target: google_apis
88+
profile: pixel_6
89+
cores: 4
90+
ram-size: 4096M
91+
heap-size: 1024M
92+
force-avd-creation: false
93+
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
94+
disable-animations: true
95+
script: echo "AVD snapshot generated"
96+
97+
- name: Run E2E tests on emulator
98+
uses: reactivecircus/android-emulator-runner@v2
99+
env:
100+
LOGIN_EMAIL: ${{ secrets.E2E_LOGIN_EMAIL }}
101+
LOGIN_PASSWORD: ${{ secrets.E2E_LOGIN_PASSWORD }}
102+
LOGIN_WRONG_PASSWORD: ${{ secrets.E2E_LOGIN_WRONG_PASSWORD }}
103+
TENANT_NAME_1: ${{ secrets.E2E_TENANT_NAME_1 }}
104+
TENANT_NAME_2: ${{ secrets.E2E_TENANT_NAME_2 }}
105+
GOOGLE_EMAIL: ${{ secrets.E2E_GOOGLE_EMAIL }}
106+
GOOGLE_PASSWORD: ${{ secrets.E2E_GOOGLE_PASSWORD }}
107+
with:
108+
api-level: 34
109+
arch: x86_64
110+
target: google_apis
111+
profile: pixel_6
112+
cores: 4
113+
ram-size: 4096M
114+
heap-size: 1024M
115+
force-avd-creation: false
116+
emulator-options: -no-snapshot-save -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
117+
disable-animations: true
118+
working-directory: example/android
119+
script: |
120+
adb shell input keyevent 82
121+
adb shell settings put global window_animation_scale 0
122+
adb shell settings put global transition_animation_scale 0
123+
adb shell settings put global animator_duration_scale 0
124+
sleep 5
125+
./gradlew :app:connectedDebugAndroidTest --no-daemon -Pandroid.testInstrumentationRunnerArguments.LOGIN_EMAIL="$LOGIN_EMAIL" -Pandroid.testInstrumentationRunnerArguments.LOGIN_PASSWORD="$LOGIN_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.LOGIN_WRONG_PASSWORD="$LOGIN_WRONG_PASSWORD" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_1="$TENANT_NAME_1" -Pandroid.testInstrumentationRunnerArguments.TENANT_NAME_2="$TENANT_NAME_2" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_EMAIL="$GOOGLE_EMAIL" -Pandroid.testInstrumentationRunnerArguments.GOOGLE_PASSWORD="$GOOGLE_PASSWORD"
126+
127+
- name: Collect JUnit reports
128+
if: always()
129+
run: |
130+
mkdir -p e2e-artifacts
131+
find example/android -path "*/androidTest-results/*" -name "*.xml" -exec cp -f {} e2e-artifacts/ \; 2>/dev/null || true
132+
find example/android -path "*/build/outputs/*" -name "TEST-*.xml" -exec cp -f {} e2e-artifacts/ \; 2>/dev/null || true
133+
134+
- name: Publish JUnit report
135+
uses: mikepenz/action-junit-report@v5
136+
if: always()
137+
with:
138+
report_paths: e2e-artifacts/*.xml
139+
check_name: Android E2E
140+
141+
- name: Upload test artifacts
142+
uses: actions/upload-artifact@v4
143+
if: always()
144+
with:
145+
name: android-e2e-results
146+
path: e2e-artifacts/
147+
if-no-files-found: ignore
148+
149+
# ──────────────────────────────────────────────────────────────────────
150+
# iOS — XCUITest on an iPhone simulator (macOS runner)
151+
# Mirrors frontegg-ios-swift's demo-e2e.yml.
152+
# ──────────────────────────────────────────────────────────────────────
153+
ios-e2e:
154+
name: iOS E2E
155+
runs-on: macos-15
156+
timeout-minutes: 60
157+
env:
158+
LOGIN_EMAIL: ${{ secrets.E2E_LOGIN_EMAIL }}
159+
LOGIN_PASSWORD: ${{ secrets.E2E_LOGIN_PASSWORD }}
160+
LOGIN_WRONG_PASSWORD: ${{ secrets.E2E_LOGIN_WRONG_PASSWORD }}
161+
TENANT_NAME_1: ${{ secrets.E2E_TENANT_NAME_1 }}
162+
TENANT_NAME_2: ${{ secrets.E2E_TENANT_NAME_2 }}
163+
GOOGLE_EMAIL: ${{ secrets.E2E_GOOGLE_EMAIL }}
164+
GOOGLE_PASSWORD: ${{ secrets.E2E_GOOGLE_PASSWORD }}
165+
steps:
166+
- name: Checkout
167+
uses: actions/checkout@v4
168+
169+
- name: Set up Node.js
170+
uses: actions/setup-node@v4
171+
with:
172+
node-version: 18
173+
174+
- name: Install JS dependencies
175+
run: |
176+
yarn install --frozen-lockfile
177+
cd example && yarn install --frozen-lockfile
178+
179+
- name: Install CocoaPods dependencies
180+
working-directory: example/ios
181+
run: pod install --repo-update
182+
183+
- name: Build for testing
184+
working-directory: example/ios
185+
run: |
186+
set -o pipefail
187+
xcodebuild \
188+
build-for-testing \
189+
-workspace ReactNativeExample.xcworkspace \
190+
-scheme ReactNativeExample \
191+
-configuration Release \
192+
-destination "platform=iOS Simulator,name=iPhone 16" \
193+
-derivedDataPath "$RUNNER_TEMP/DerivedData" \
194+
CODE_SIGNING_ALLOWED=NO \
195+
2>&1 | tee "$RUNNER_TEMP/build.log"
196+
197+
- name: Run UI tests
198+
working-directory: example/ios
199+
run: |
200+
set -o pipefail
201+
# Exclude the real social-login test from CI: it drives a real
202+
# ASWebAuthenticationSession that can't complete headlessly on a simulator.
203+
# This mirrors the frontegg-ios-swift SDK E2E, which runs the
204+
# "hosted:no-social" suite and excludes real system-web-auth social login.
205+
xcodebuild \
206+
test-without-building \
207+
-workspace ReactNativeExample.xcworkspace \
208+
-scheme ReactNativeExample \
209+
-configuration Release \
210+
-only-testing:ReactNativeExampleUITests \
211+
-skip-testing:ReactNativeExampleUITests/LoginViaGoogleTest \
212+
-destination "platform=iOS Simulator,name=iPhone 16" \
213+
-derivedDataPath "$RUNNER_TEMP/DerivedData" \
214+
-resultBundlePath "$RUNNER_TEMP/ios-e2e.xcresult" \
215+
-parallel-testing-enabled NO \
216+
2>&1 | tee "$RUNNER_TEMP/test.log"
217+
218+
- name: Upload test artifacts
219+
uses: actions/upload-artifact@v4
220+
if: always()
221+
with:
222+
name: ios-e2e-results
223+
path: |
224+
${{ runner.temp }}/build.log
225+
${{ runner.temp }}/test.log
226+
${{ runner.temp }}/ios-e2e.xcresult
227+
if-no-files-found: ignore

docs/E2E_REACT_NATIVE_TESTS_REVIEW.md

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,10 +68,15 @@ React Native’s default accessibility behavior (button `title` → accessibilit
6868
| User profile (email/name on screen) | Yes — `react-native-sdk-user-profile-test.ts` | Asserts user info displayed. |
6969
| Tenant switching | Yes — `react-native-sdk-tenant-switching-test.ts` | Asserts "Active Tenant" text; handles case where tenant UI is absent. |
7070
| Social login (e.g. Google) | Yes — `react-native-sdk-social-login-test.ts` ||
71-
| Passkeys (Register / Login with Passkeys) | No | Buttons exist in example app; no dedicated e2e yet. |
72-
| Refresh Token button | No | Selector exists; no test that explicitly uses it. |
71+
| Passkeys (Register / Login with Passkeys) | Partial | External suite: no. Local suite (`example/android/app/src/androidTest/.../PasskeysRegisterTest.kt`, `PasskeysLoginTest.kt` + iOS equivalents) adds smoke coverage that verifies the buttons are reachable and the app survives the system credential-manager sheet. Full biometric-backed enrolment still TODO. |
72+
| Refresh Token button | Partial | External suite: no. Local suite (`RefreshTokenTest.kt` / `RefreshTokenTest.swift`) now asserts that tapping the button rotates the access-token suffix while the user stays authenticated. |
7373

74-
So: core auth, MFA, step-up, tenant, and social flows are covered; Passkeys and “Refresh Token” are not.
74+
So: core auth, MFA, step-up, tenant, and social flows are covered by the
75+
external Nightwatch suite. Passkeys and "Refresh Token" now have
76+
developer-runnable coverage in `example/` — see
77+
[`example/E2E_TESTS.md`](../example/E2E_TESTS.md) for the full local suite
78+
(Android UiAutomator + iOS XCUITest) that mirrors the patterns used by
79+
`frontegg-android-kotlin` and `frontegg-ios-swift`.
7580

7681
---
7782

@@ -97,12 +102,12 @@ So: core auth, MFA, step-up, tenant, and social flows are covered; Passkeys and
97102
### 5.3 Optional: more stable selectors
98103

99104
- Selectors are text-based (button title / static text). They are correct for the current example app but can break if copy or i18n changes.
100-
- **Recommendation (optional):** In the example app, add `testID` (and optionally `accessibilityLabel`) to key elements (e.g. `Login`, `Logout`, `Request Authorization`), and in e2e use `testID`-based or accessibility selectors where the framework supports it, to reduce fragility.
105+
- **Status:** Addressed. `example/src/HomeScreen.tsx` now sets `testID` on every actionable element (`loginButton`, `logoutButton`, `loginWithGoogleButton`, `requestAuthorizeButton`, `refreshTokenButton`, `registerPasskeysButton`, `loginWithPasskeysButton`, `tenantSwitchButton-$tenantId`) and on the diagnostic text nodes (`accessTokenValue`, `userEmailValue`, `activeTenantValue`, …). The new local suite under `example/` uses these; the Nightwatch suite can migrate off text-based selectors at its own pace.
101106

102107
### 5.4 Unused selector
103108

104109
- `ReactNativeSDKUserPageSelectors.REFRESH_TOKEN_BUTTON_*` is defined but the page object has no `clickRefreshToken()`. No test uses it.
105-
- **Recommendation:** Either add a small test that taps “Refresh Token” and asserts token/state (if useful), or remove the selector to avoid dead code.
110+
- **Status:** Addressed locally. `example/android/app/src/androidTest/.../RefreshTokenTest.kt` and `example/ios/ReactNativeExampleUITests/RefreshTokenTest.swift` exercise the button and assert the access-token value changes. The Nightwatch selector can now either be wired up to a similar test or removed.
106111

107112
### 5.5 Step-up test assertion
108113

0 commit comments

Comments
 (0)