[E2E] Expose native preload state markers - #626
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
Package Size
Android file breakdown
Measured from the PR base SHA and PR head SHA. The file breakdown shows uncompressed sizes within each package artifact, so individual files do not sum to the compressed artifact total. This comment reports package artifact sizes only; it is not a final app binary-size report. |
Install this buildOpen Tophat, select your target device, then click Install. Links open on the Mac running Tophat.
Checkout Kit E2E results
|
558cb61 to
76447c5
Compare
|
Do we need a corresponding change to the react-native sample or is that already using this approach? |
76447c5 to
770e7de
Compare
56cc7f5 to
49478c1
Compare
|
I might wait for RN because it can't use this approach yet. This PR maps the native PreloadState callbacks to identifiers, and until #654there was no equivalent on RN. #656 is already adding RN sample preload observability, so building on that makes sense imo. |
49478c1 to
715c1f5
Compare
| private static func failedText(for reason: PreloadState.FailureReason) -> String { | ||
| switch reason { | ||
| case let .httpError(statusCode): | ||
| return "failed-http-\(statusCode)" | ||
| case .navigationFailed: | ||
| return "failed-navigation" | ||
| case .webContentUnavailable: | ||
| return "failed-web-content-unavailable" | ||
| case .protocolError: | ||
| return "failed-protocol" | ||
| } | ||
| } |
There was a problem hiding this comment.
This might be overkill. It might be hard to force these states
715c1f5 to
25f45e0
Compare
## What changed Proves that presentation actually reused the preloaded WebView, and exposes that proof to Maestro on both native samples. - **Swift SDK:** emit a debug-only diagnostic through the configured `Logger` when presentation selects a matching cached WebView that reached `.ready`. The existing OSLog message is preserved for dashboard compatibility. - **Swift sample:** observe that diagnostic as it is written and republish it as an invisible `preload-cache-hit-*` identifier. Normal launches hide the diagnostic marker from the accessibility tree; #631 enables preload observability only for diagnostic runs. - **Android SDK/sample:** keep the existing cache-hit log behavior, pin its exact internal diagnostic in an SDK test, and read the app's PID-scoped Logcat. Each watcher writes a unique UUID boundary after its reader starts, ignores every line until that boundary appears, and then republishes the cache-hit identifier only when the latest observed preload state is `Ready`. The debug-only watcher owns and closes its reader/process with the `CartViewModel`. - Tests cover ready hits, still-loading entries, non-debug logging, fresh presentation, URL mismatch, exact diagnostic strings, stale hits before the current UUID boundary, old UUID sessions, Android Ready gating, Logcat failures, and reader/process shutdown. ## Why `preload-state-ready` proves background preload completed. It does not prove presentation used that cached WebView — a fresh one looks identical on screen. This closes that gap. The `.ready` condition matters: a cached entry that matches but is still loading would otherwise count as a hit. Both samples end up exposing the **same identifier**, so the E2E flow has no platform branch. Getting there differs only because the SDKs differ: | | How the sample sees the cache hit | | --- | --- | | Swift | Installs a `Logger`, so it observes the message directly | | Kotlin | SDK log sink is `internal`, so it reads its own PID-scoped Logcat. A per-watcher UUID boundary excludes buffered lines from earlier app processes before the observer accepts a cache hit | Each sample matches the message text against its own copy because the SDK diagnostics are not public API. SDK emission tests and sample parser tests pin the exact literals, so accidental drift fails close to the change instead of surfacing as a 30-second E2E timeout. ## Testing - `dev swift test CheckoutWebViewTests` - `dev swift test sample` - `dev android test demo`, `dev android check detekt` - `PreloadCacheHitMarkerTest`: stale pre-boundary hits, old UUID sessions, current-boundary arming, Ready gating, Logcat failures, and reader shutdown - `dev swift lint` - `dev android e2e --tags preload` - `dev swift e2e --tags preload` - Asserted end to end by #631 on both platforms: preload ready → present → real checkout content → close → `preload-cache-hit-observed`, with `preload-cache-hit-none` asserted beforehand so a stuck marker cannot pass. ## Stack 1. #626 — preload state markers in the samples 2. **#627 — this PR:** proof that the ready cache entry was reused 3. #631 — the E2E test and matrix wiring --- ### Before you merge > [!IMPORTANT] > - [x] I've added tests to support my implementation > - [x] I have read and agree with the [Contribution Guidelines](./CONTRIBUTING.md) > - [ ] I have read and agree with the [Code of Conduct](./CODE_OF_CONDUCT.md) > - [ ] I've updated the relevant platform README (`platforms/swift/README.md` and/or `platforms/android/README.md`) --- <details> <summary>Releasing a new Swift version?</summary> - [ ] I have bumped the version in `ShopifyCheckoutKit.podspec` - [ ] I have bumped the version in `platforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swift` - [ ] I have updated the SwiftPM/CocoaPods version snippets in `platforms/swift/README.md` (major version only) </details> <details> <summary>Releasing a new Embedded Checkout Protocol version?</summary> - [ ] I have bumped `embeddedCheckoutProtocolAndroid` in `platforms/android/gradle/libs.versions.toml` - [ ] I have updated `protocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.api` if the public API changed </details> <details> <summary>Releasing a new Android version?</summary> - [ ] I have bumped `checkoutKitAndroid` in `platforms/android/gradle/libs.versions.toml` - [ ] I have updated the Gradle/Maven version snippets in `platforms/android/README.md` </details> > [!TIP] > See the [Contributing documentation](./CONTRIBUTING.md) for the full release process per platform. <!-- github-gate:idempotency=checkout-kit-627-body-uuid-boundary-20260821 -->
## What changed - `e2e/tests/shared/native-preload-ready.yaml` — top-level test, tagged `preload`/`smoke`. - `e2e/flows/checkout/assert-native-preload-ready.yaml` — the reusable subflow, including the existing enabled-button and close-animation safeguards. - `e2e/flows/app/launch.yaml` — enables preload observability for diagnostic runs so the Swift marker stays out of the normal sample accessibility tree. - `e2e/config/matrix.yml` — Swift and Kotlin adopt the `preload` journey. React Native is unchanged, and matrix tests ensure native overrides retain every shared default tag. - `preload` added to the journey-tag taxonomy, with matrix and tag tests. ## Why Presenting checkout is not proof that preload was reused. This asserts the whole chain: ``` preload-state-ready → cached WebView selected → real checkout content ``` Both platforms run the **same flow with no platform branch**, because each sample republishes its SDK's cache-hit diagnostic as the same `preload-cache-hit-*` identifier (#627). The flow pins both ends — `preload-cache-hit-none` before presentation, `preload-cache-hit-observed` after — so a marker stuck on "observed" cannot pass. ## Testing - All `e2e/test/*_test.rb` — 261 runs, 710 assertions, 0 failures - Matrix expansion verified: `launch,checkout,preload` on the two native rows, React Native unchanged - `dev android e2e --tags preload` — passed on Android emulator - `dev swift e2e --tags preload` — passed on iOS simulator ## Stack 1. #626 — preload state markers in the samples 2. #627 — proof that the ready cache entry was reused 3. **#631 — this PR:** the E2E test and matrix wiring --- ### Before you merge > [!IMPORTANT] > - [ ] I've added tests to support my implementation > - [ ] I have read and agree with the [Contribution Guidelines](./CONTRIBUTING.md) > - [ ] I have read and agree with the [Code of Conduct](./CODE_OF_CONDUCT.md) > - [ ] I've updated the relevant platform README (`platforms/swift/README.md` and/or `platforms/android/README.md`) --- <details> <summary>Releasing a new Swift version?</summary> - [ ] I have bumped the version in `ShopifyCheckoutKit.podspec` - [ ] I have bumped the version in `platforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swift` - [ ] I have updated the SwiftPM/CocoaPods version snippets in `platforms/swift/README.md` (major version only) </details> <details> <summary>Releasing a new Embedded Checkout Protocol version?</summary> - [ ] I have bumped `embeddedCheckoutProtocolAndroid` in `platforms/android/gradle/libs.versions.toml` - [ ] I have updated `protocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.api` if the public API changed </details> <details> <summary>Releasing a new Android version?</summary> - [ ] I have bumped `checkoutKitAndroid` in `platforms/android/gradle/libs.versions.toml` - [ ] I have updated the Gradle/Maven version snippets in `platforms/android/README.md` </details> > [!TIP] > See the [Contributing documentation](./CONTRIBUTING.md) for the full release process per platform. <!-- github-gate:idempotency=pr631-observability-body-20260819 -->

What changed
PreloadStatein the Swift and Android sample apps using the existing public preload callbacks.preload-state-not-readypreload-state-readyWhy
Maestro cannot read an in-memory Swift/Kotlin preload state. These markers give native E2E tests a stable condition to wait for before presentation.
The identifier changes to
preload-state-readyonly when the SDK callback reportsReady, so the test does not infer preload completion from timing. The markers add no visible UI and no public SDK API.Testing
dev swift test sampledev android test demodev swift lintdev android check detektStack
Before you merge
Important
platforms/swift/README.mdand/orplatforms/android/README.md)Releasing a new Swift version?
ShopifyCheckoutKit.podspecplatforms/swift/Sources/ShopifyCheckoutKit/ShopifyCheckoutKit.swiftplatforms/swift/README.md(major version only)Releasing a new Embedded Checkout Protocol version?
embeddedCheckoutProtocolAndroidinplatforms/android/gradle/libs.versions.tomlprotocol/languages/kotlin/embedded-checkout-protocol/api/embedded-checkout-protocol.apiif the public API changedReleasing a new Android version?
checkoutKitAndroidinplatforms/android/gradle/libs.versions.tomlplatforms/android/README.mdTip
See the Contributing documentation for the full release process per platform.