## What this PR adds end-to-end test suite for the **Mobile RUM SDK** under `tests/mobile-testing/`, plus a **self-contained CI workflow** that runs it on real emulators/simulators against an OpenObserve **built from this PR's source**. Test / fixture / CI only — no product-code/existing test changes ## How each test works (3 layers) Every scenario is a real end-to-end journey, so a failure points at the actual broken layer: 1. **Drive** — a Maestro flow taps through the app like a user (`maestro/<track>/*.yaml`). 2. **API** — query the `_rumdata` stream and assert the telemetry is correct (`utils/ooClient.js`, poll-and-retry for async ingestion). 3. **UI** — open the RUM dashboard and assert the sessions ## Coverage (42 tests across 4 platforms) The **native platforms (android-native, ios-native) are now at parity with React Native** — each new native test (masking, network, bg/fg, user-identity, no-phone-home) was built + validated on a real emulator/simulator before landing. | Capability | Layer | RN-Android | Android-native | RN-iOS | iOS-native | |---|---|:-:|:-:|:-:|:-:| | Core telemetry (views/actions/handled-error/crash → `_rumdata`) | API | ✅ | ✅ | ✅ | ✅ | | Crash session viewable in dashboard | UI | ✅ | ✅ | ✅ | ✅ | | Attributes: env / service / version tagging | API | ✅ | ✅ | ✅ | ✅ | | User identity (`setUser`) | API | ✅ | ✅ | ✅ | ✅ | | Network: fetch → resource + 4xx/5xx | API | ✅ | ✅ | ✅ | ✅ | | Session-replay PII masking | UI | ✅ | ✅ | ✅ | ✅ | | Background / foreground view transitions | API | ✅ | ✅ | — | ✅ | | No-phone-home (no egress to Datadog hosts) | security | ✅ | ✅ | — | ✅ | - Shared per-capability factories keep the specs DRY (`utils/rumChecks.js`, `utils/coreRumSpec.js`). - Negatives are covered: 4xx capture, PII-must-not-leak (masking), no phone-home, handled-error ≠ crash. - `o2-enterprise#2289` dashboard bugs are kept as inline skips that flip green once fixed. - Full capability × platform detail + on-device status: `docs/COVERAGE-MATRIX.md`. ## Fixture apps + build-time config - Four vendored apps under `apps/`: `o2-rum-tester` (React Native — drives both RN platforms), `o2-native-android` (Kotlin), `o2-native-ios` (Swift / xcodegen). - Each reads its RUM target (endpoint / org / token) and SDK version from a **build-time-overridable** config. The committed default is the current pin, so local dev is unchanged; CI regenerates it to point at the runner's OpenObserve and can pin the latest SDK on release. ## CI — `.github/workflows/mobile-sdk-e2e.yml` (self-contained, no secrets) - **Build once per OS from source.** `build_oo_linux` / `build_oo_macos` compile OpenObserve (binary + embedded web UI) from this PR's code and cache it. Device jobs download and run it **natively**, and the app posts real RUM to it (Android reaches the host at `10.0.2.2`, the iOS simulator via `localhost`). No Docker, no shared cluster, no secrets. - **Device jobs run in parallel.** `android` (Linux + KVM emulator → rn-android + android-native) and `ios` (macOS simulator, pinned Xcode 16.4 → rn-ios + ios-native). - **`merge_reports`** combines both platforms into one browsable HTML report — even when a device job fails. - **`decide` picks which platform runs, to keep scarce macOS runners in check:** - PR touching Android or shared mobile-RUM code → **Android auto-runs**; iOS is gated behind the `mobile-e2e` label. - `mobile-e2e` label → both platforms (full pre-merge gate). - `repository_dispatch(mobile-sdk-release)` from an SDK repo → runs that SDK's platform (companion PRs wire this up: openobserve-react-native-rum#3, openobserve-sdk-android#1, openobserve-sdk-ios#1). - `workflow_dispatch` → manual, choose platforms. *Separate workflow :* it needs macOS **and** Android emulators, builds 4 phone apps, and is dispatch-driven — matching the repo's existing per-suite split ## Status Green end-to-end on GitHub runners (Android + iOS device jobs + merge) : https://github.com/openobserve/openobserve/actions/runs/31733717758/job/94565520891 --------- Co-authored-by: Shrinath Rao <shnath@openobserve.ai>
6.0 KiB
Mobile RUM SDK — End-to-End Test Suite
E2E tests for OpenObserve's Mobile RUM SDK (@openobserve/mobile-react-native and native
tracks). Each test drives a real mobile app on an emulator/simulator, then asserts the telemetry
both at the data layer (OpenObserve Search API) and at the product layer (the RUM
dashboard UI) — because most mobile-RUM defects live in the dashboard, not the pipeline.
The 3-layer pattern
Every scenario is the same shape, threaded by session_id:
1. DRIVE Maestro flow taps through the app (maestro/<track>/*.yaml)
2. API query _rumdata → assert data is correct (utils/ooClient.js)
3. UI open the dashboard → assert it renders (pages/rumDashboardPage.js)
Why both layers? Nearly every bug found in manual testing (Error Tracking, Web Vitals,
breadcrumbs, Traces) was a display bug: the data was correct in _rumdata but the dashboard
failed to show it. An API-only suite would have been green on a broken product.
Layout
mobile-testing/
├── apps/ Vendored fixture apps (o2-rum-tester, o2-native-android, o2-native-ios)
├── maestro/ Maestro flows per track (react-native, android-native, ios-native, ios-react-native)
├── specs/ Playwright specs (rn-android.*, rn-ios.*, android-native.*, ios-native.*)
├── pages/rumDashboardPage.js Dashboard page object (login, open session, masking guard)
├── utils/
│ ├── ooClient.js OpenObserve Search API client (poll-and-retry)
│ ├── rumChecks.js per-capability test factories (attributes/user/network/masking/…)
│ ├── coreRumSpec.js the crash → dashboard core journey
│ ├── maestro.js Maestro flow runner
│ ├── adb.js device-level Android steps
│ └── config.js env loader
├── docs/ Coverage matrix, CI notes, test-case matrix
├── playwright.config.js
└── .env / .env.example instance URL, org, creds, app id
Prerequisites
- Node 20+, JDK 17, Android SDK + an emulator (AVD)
- Maestro CLI:
curl -Ls "https://get.maestro.mobile.dev" | bash - The app under test (
com.o2rumtester) — its source lives in this suite atapps/o2-rum-tester/. Build + install it:cd apps/o2-rum-tester && npm install # postinstall auto-applies the SDK build fix (cd android && ./gradlew assembleRelease) adb install -r android/app/build/outputs/apk/release/app-release.apk - A reachable OpenObserve instance with a RUM token wired into the app (see
.envandapps/o2-rum-tester/App.tsx)
Setup
cp .env.example .env # fill in OO_URL / OO_ORG / OO_USER / OO_PASS (quote passwords with #)
npm install
npx playwright install chromium
Run
# one platform (Playwright projects)
npx playwright test --project=rn-android
npx playwright test --project=android-native
npx playwright test --project=rn-ios # needs a booted iOS simulator
npx playwright test --project=ios-native # needs a booted iOS simulator
# by tag
npx playwright test --grep @crash
npx playwright test --grep @masking
npx playwright test --grep @known-bug # the skipped #2289 markers
Coverage (all four mobile tracks) — 42 tests
Each platform has a core RUM bundle (utils/coreRumSpec.js: views + actions + handled error +
native crash in _rumdata, then the crashed session rendering in the dashboard) plus
per-capability specs built from shared factories in utils/rumChecks.js — so the platforms stay
at parity without copy-paste:
attributesSuite— env / service / version tagginguserIdentitySuite— events attributed to the set user (usr_* fields)networkSuite— a successful fetch is a resource (positive) + a 4xx status is captured (negative)maskingSuite— no PII leaks into the session replay (MASK_ALL)bgFgSuite— a pre-background view and a post-foreground view share one sessionnoPhoneHomeAndroidSuite— the installed APK contains zero Datadog hosts (security/negative)
| Project | App | Service | Specs |
|---|---|---|---|
rn-android |
apps/o2-rum-tester (Android) |
o2-rum-tester |
crash, network(+404), handled-error, views, masking, attributes, user-identity, background-foreground, no-phone-home |
android-native |
apps/o2-native-android |
o2-native-android |
core bundle, attributes, user-identity, no-phone-home |
rn-ios |
apps/o2-rum-tester (iOS) |
o2-rum-tester |
core bundle, network(+404), attributes, user-identity, masking |
ios-native |
apps/o2-native-ios |
o2-native-ios |
core bundle, attributes |
See docs/COVERAGE-MATRIX.md for the full capability×platform matrix, on-device validation
status, and the deferred follow-ups (native/iOS background-foreground, native network/masking,
iOS-native user identity — each needs a fixture-app change or a background harness).
Prereqs: Android emulator booted for the Android projects; an iOS simulator booted (udid in
.env IOS_SIM_UDID) for the iOS projects; the fixture apps built + installed (see apps/).
Tags: @mobile @rn-android @rn-ios @android-native @ios-native @P0 @P1 @crash @network @negative @errors @views @replay @masking @lifecycle @attributes @user-identity @security @known-bug.
Notes
- The known Error-Tracking outage (o2-enterprise#2289) is a
test.fixmeskip inline in the relevant spec — no separate file or project. It stays skipped and turns into a real assertion when the bug is fixed. - Ingestion is async — assertions poll-and-retry; never assert instantly.
- Session isolation — the current specs scope by
service+ time window. For parallel/CI runs, stamp a unique run-id as a RUM global attribute in the app and filter on it (seedocs/CI-NOTES.md). - Fixture apps — the four apps live in
apps/(each builds against the SDK from npm/Maven/SPM); iOS tracks additionally need Xcode/simulator. Build/run steps are in each app +docs/CI-NOTES.md.