Files
Vamsi Karthik RandShrinath Rao 6930a9b724 test(mobile): RUM SDK end-to-end suite across 4 platforms + self-contained CI (#13628)
## 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>
2026-08-20 08:13:35 +00:00

6.0 KiB
Raw Permalink Blame History

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 at apps/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 .env and apps/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 tagging
  • userIdentitySuite — 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 session
  • noPhoneHomeAndroidSuite — 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.fixme skip 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 (see docs/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.