[draft] Android XML Views reference implementation + shared E2E matrix#286
Draft
Alex Freas (akfreas) wants to merge 32 commits into
Draft
[draft] Android XML Views reference implementation + shared E2E matrix#286Alex Freas (akfreas) wants to merge 32 commits into
Alex Freas (akfreas) wants to merge 32 commits into
Conversation
…single @contentful/optimization-js-bridge under packages/universal that builds one shared src/index.ts into both native UMD bundles
…merged @contentful/optimization-js-bridge package
…generated by the merged bridge package
…imization-js-bridge so the JS bridge matches the iOS and Android native SDKs that already call them
… adapter on top of the existing core API so XML/Views-based Android apps can integrate the SDK without Compose dependencies
…ibrary module under com.contentful.optimization.shared so both the Compose reference impl and the upcoming XML Views impl can consume the same AppConfig, ContentfulFetcher, EventStore, MockPreviewContentfulClient, and RichText
…e and reroute every script, gradle build path, IDE run config, and CI workflow line to the new module so it sits as a sibling of the upcoming :views XML reference impl, mirroring the iOS swiftui/uikit pair
…se impl screen-for-screen, using OptimizedEntryView, ScreenTracker, and OptimizationManager from the new SDK views adapter and exposing every Compose testTag through an AccessibilityNodeInfoCompat.setViewIdResourceName helper so the shared UI Automator suite resolves the same selectors against both apps
…arguments so the same UI Automator suite can drive both the Compose reference impl and the new XML Views reference impl, defaulting to the Compose impl when no override is passed
…ws, build:apks) and teach scripts/run-e2e.sh to pick the gradle module + APK file + am instrument APP_PACKAGE arg from the APP_PACKAGE env var so the same script drives either reference impl
…ompose, views, and uitests APKs and a matrix-fanout stage that boots one emulator per app and runs the same UI Automator suite against each with APP_PACKAGE forwarded as an instrumentation argument, mirroring the iOS SwiftUI/UIKit dual-target CI pattern
…st-id contract, the APP_PACKAGE instrumentation argument, and the new com.contentful.optimization.views SDK adapter in the AGENTS.md files
…g AnalyticsTests against both apps: wait for client.isInitialized before driving consent/page so the profile state flow actually advances, memoize the OptimizedEntryView view-tracking controller on its (entry, personalization) tuple so personalization re-emissions don't reset the dwell timer, and tighten the entry content padding so the analytics block sits inside the viewport like it does in Compose; also fetch entries exactly once per Activity instead of re-rendering on every profile state change
…ity.onCreate after the reset-prefs check so cold launches with reset=true do not race against the bridge loading the persisted profile, gate the Views identify and reset button handlers behind client.isInitialized so a quick test tap before the bridge boots no longer no-ops, attach the preview-panel floating action button from the post-init coroutine to mirror OptimizationRoot's previewPanel config, derive the nested entry test tag from the resolved entry's sys.id so personalization-variant ids surface like they do on the Compose side, and force-stop both reference impls at the start of scripts/run-e2e.sh so local back-to-back runs against different apps cannot leak stale focused windows into the next instrumentation run
…nside LiveUpdatesTestActivity.loadEntry, force-close the preview panel on every entry into LiveUpdatesTestActivity so a prior testTogglePreviewPanel cannot leave shouldLive=true sticky for the next test's locked slots, and suppress the AccessibilityNodeInfoCompat ACTION_CLICK from TestTagging.setTestTag so the shared TestHelpers.tapElement helper's accessibility-then-coordinate double click does not double-fire onClick listeners on stock Android Buttons the way it would have to on Compose's debounced gesture handler
…x artifact handoff so actions/upload-artifact v7 keys the in-zip paths off the LCA correctly and the matrix leg's adb install lines find compose-debug.apk and views-debug.apk where they expect them
…rom the matrix script — the emulator-runner action already does this via disable-animations: true and the duplicate adb commands exit non-zero under the action's bash -e wrapper on the freshly-booted API 35 image, terminating the script before any apk install or instrument run
…okes it via /usr/bin/sh which errors out with 'Illegal option -o pipefail' on the first line, terminating the script before any apk install or instrument run. The grep checks on the test-output log already detect instrumentation failures regardless of pipe-status propagation
…too in setTestTag, so UiAutomator's By.desc fallback finds buttons in the CI x86_64 headless emulator config where the AccessibilityDelegate-driven viewIdResourceName override appears to not always propagate to the accessibility node tree on time
…n setTestTag right before installing the AccessibilityDelegate, so the framework's onInitializeAccessibilityNodeInfoInternal can't populate viewIdResourceName from Resources.getResourceName(mID) and clobber our delegate's setViewIdResourceName(testTag) before UiAutomator's By.res reads it on the CI x86_64 emulator. Compose's testTagsAsResourceId works because Compose nodes have no underlying XML id to conflict; this brings the Views path to the same precondition
…init renderer at declaration, mark afterEach as void, replace Object.create(null) cast with a type-predicate factory
…thResolvers in createDeferred, void the beforeEach/afterEach calls, init renderer at declaration, replace Object.create(null) cast with a type-predicate factory
…l and 0.4 ratio to named constants, destructure mock.mock.calls all the way through, init renderer at declaration, void the beforeEach/afterEach calls
… test: void the beforeEach call, extract the inner subscriber-notify forEach callback to a named arrow, capture the original destroy via Reflect.get with an explicit this-typed annotation to silence both prefer-destructuring and unbound-method
…eact-web test: add a void-this reference inside the mock destroy method to acknowledge class-methods-use-this, switch indexed array access to computed-key object destructuring
…r tests so they actually validate the mocked destroy/screen/states.eventStream shape at runtime instead of returning true unconditionally; the lint-clean type narrowing now matches what the runtime check claims
…ollView — the e2e Detox suite preview-panel-overrides.test.js was changed in 95d6afc to match the panel scroll container by that id but the SDK side of the rename never landed on final-mobile-sdk-fixes, so all 8 scenarios were failing with 'No views in hierarchy found matching: view.getTag() is preview-panel-scroll'
…eanup-order test that broke the CI Format Check; the cherry-picked lint commit was line-wrapped one way and prettier wants it the other way
…view panel and update Detox scenario 6 to tap by testID
…via sdk.page in the demo app so the override interceptor can be exercised end-to-end from Detox
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Adds a legacy XML Views reference implementation for the Android SDK and re-runs the existing UI Automator suite against both the Compose impl and the new Views impl in parallel, mirroring the iOS swiftui/uikit + matrix CI pattern.
What's in this PR
com.contentful.optimization.viewsSDK adapter —OptimizationManager,OptimizedEntryView,ScreenTracker,TrackingRecyclerView. Thin wrapper around the existingcoreAPI; no personalization or tracking logic lives here.implementations/android-sdk/shared— newcom.android.librarymodule hostingAppConfig,ContentfulFetcher,EventStore,MockPreviewContentfulClient,RichText. Both:composeand:viewsdepend on it.implementations/android-sdk/compose— renamed from:app. Pure rename, no behavior change.implementations/android-sdk/views— newcom.android.applicationmodule with applicationIdcom.contentful.optimization.app.views. Mirrors every Compose screen/component with XML layouts and a per-ViewsetTestTaghelper that exposes the same kebab-case test ids to UI Automator.APP_PACKAGE—AppLauncherreads the target package fromInstrumentationRegistry.getArguments(); defaults to the Compose app so IDE runs keep working.e2e-android-sdkjob into a build-once stage (compose, views, uitests APKs) and a matrix-fanout stage that boots one emulator per app and runs the same uitests against each.Test-id contract for XML Views
UI Automator's
By.res("identify-button")matchesAccessibilityNodeInfo.viewIdResourceName. Compose'stestTagsAsResourceId = truewrites the test tag verbatim there; XMLandroid:idcan't contain hyphens. The Views path uses anAccessibilityDelegateCompatthat overridesviewIdResourceNameto the kebab tag — and clearsview.idtoView.NO_IDfirst, becauseView.onInitializeAccessibilityNodeInfoInternalre-populates the field fromResources.getResourceName(mID)and clobbers the override on the CI x86_64 emulator. The helper also mirrors the test tag ontocontentDescriptionso the sharedTestHelpers.findElementBy.res → By.descfallback resolves the same selector.Status
testScenario5ResettingSingleVariantOverrideRestoresVariantandtestScenario3ResettingAudienceOverrideRestoresVariantinPreviewPanelOverridesTests) are pre-existing flakes that also fail on thefinal-mobile-sdk-fixesbranch this PR builds on — not caused by anything in this PR.testScenario3...(same pre-existing flake as Compose) plustestTracksEntryViewEventsForVisibleEntriesinAnalyticsTests— the first entry's view-tracking component event isn't surfacing on CI's slow x86_64 headless emulator even though events for other entries do; passes locally on arm64. Worth a follow-up investigation.Pre-existing CI gaps this PR also fixes
The Android CI job has been red on
final-mobile-sdk-fixesfor unrelated reasons that were masked by commit6a596527(the silent-pass guard). Bringing CI back to green required:set -o pipefailfrom the emulator-runner script — the action invokes the script via/usr/bin/sh -c, which errors withIllegal option -o pipefailand terminates the script before any test runs.adb shell settings put global window_animation_scale 0lines — the action'sdisable-animations: truealready does this and the redundant adb commands exit non-zero on the freshly-booted API 35 image under the action'sbash -ewrapper.android-apks/directory for the build→matrix artifact handoff soactions/upload-artifact@v7keys the in-zip paths off the LCA correctly.🤖 Generated with Claude Code