Skip to content

[E2E] Expose native preload state markers - #626

Merged
kyle-schellen merged 1 commit into
mainfrom
ks-e2e-preload-sample-markers
Aug 21, 2026
Merged

[E2E] Expose native preload state markers#626
kyle-schellen merged 1 commit into
mainfrom
ks-e2e-preload-sample-markers

Conversation

@kyle-schellen

@kyle-schellen kyle-schellen commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

What changed

  • Observe PreloadState in the Swift and Android sample apps using the existing public preload callbacks.
  • Expose the readiness result through matching invisible test IDs on both platforms:
    • preload-state-not-ready
    • preload-state-ready
  • Add unit tests for the shared readiness-to-ID contract.

Why

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-ready only when the SDK callback reports Ready, so the test does not infer preload completion from timing. The markers add no visible UI and no public SDK API.

Testing

Stack

  1. [E2E] Expose native preload state markers #626 — this PR: preload state markers in the samples
  2. [E2E] Prove ready preload cache hits on both native samples #627 — ready cache-hit proof and sample markers
  3. [E2E] Prepare native preload ready-flow validation #631 — the E2E test, matrix wiring, and BrowserStack validation

Before you merge

Important

  • I've added tests to support my implementation
  • I have read and agree with the Contribution Guidelines
  • I have read and agree with the Code of Conduct
  • I've updated the relevant platform README (platforms/swift/README.md and/or platforms/android/README.md)

Releasing a new Swift version?
  • 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)
Releasing a new Embedded Checkout Protocol version?
  • 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
Releasing a new Android version?
  • I have bumped checkoutKitAndroid in platforms/android/gradle/libs.versions.toml
  • I have updated the Gradle/Maven version snippets in platforms/android/README.md

Tip

See the Contributing documentation for the full release process per platform.

@github-actions github-actions Bot added the #gsd:50662 Rebase Checkout Kit on UCP label Aug 11, 2026

kyle-schellen commented Aug 11, 2026

Copy link
Copy Markdown
Contributor Author

@kyle-schellen kyle-schellen changed the title test(e2e): expose native preload state markers [E2E]: expose native preload state markers Aug 11, 2026
@kyle-schellen kyle-schellen changed the title [E2E]: expose native preload state markers [E2E] Expose native preload state markers Aug 11, 2026
@kyle-schellen
kyle-schellen marked this pull request as ready for review August 11, 2026 17:40
@kyle-schellen
kyle-schellen requested a review from a team as a code owner August 11, 2026 17:40
@kyle-schellen
kyle-schellen marked this pull request as draft August 11, 2026 17:42
@github-actions

github-actions Bot commented Aug 11, 2026

Copy link
Copy Markdown

Package Size

Platform Artifact Base Head Delta
Android release AAR 291.2 KiB 291.2 KiB 0 B
Android file breakdown
File Base Head Delta
classes.jar 308.4 KiB 308.4 KiB 0 B
res/layout/checkout_view_content.xml 2.6 KiB 2.6 KiB 0 B
res/layout/checkout_sheet_content.xml 2.0 KiB 2.0 KiB 0 B
res/values/values.xml 1.3 KiB 1.3 KiB 0 B
R.txt 1.2 KiB 1.2 KiB 0 B
AndroidManifest.xml 922 B 922 B 0 B
proguard.txt 798 B 798 B 0 B
res/drawable/close.xml 431 B 431 B 0 B
res/menu/checkout_menu.xml 354 B 354 B 0 B
META-INF/com/android/build/gradle/aar-metadata.properties 157 B 157 B 0 B

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.

@bitrise

bitrise Bot commented Aug 11, 2026

Copy link
Copy Markdown

Install this build

Open Tophat, select your target device, then click Install. Links open on the Mac running Tophat.

SDK Install
Swift Install with Tophat
Kotlin Install with Tophat

Checkout Kit E2E results

Status Suite Target Platform OS version tag Device
kotlin-android kotlin android latest Google Pixel 11
Android 17.0
swift-ios swift ios latest iPhone 15
iOS 27 Beta

@kyle-schellen
kyle-schellen force-pushed the ks-e2e-preload-sample-markers branch from 558cb61 to 76447c5 Compare August 11, 2026 19:11
@kyle-schellen kyle-schellen self-assigned this Aug 11, 2026
@kyle-schellen
kyle-schellen marked this pull request as ready for review August 11, 2026 22:39

Copy link
Copy Markdown
Contributor

Do we need a corresponding change to the react-native sample or is that already using this approach?

@kyle-schellen
kyle-schellen marked this pull request as draft August 12, 2026 12:31
@kyle-schellen
kyle-schellen force-pushed the ks-e2e-preload-sample-markers branch from 76447c5 to 770e7de Compare August 12, 2026 17:58
@kyle-schellen
kyle-schellen force-pushed the ks-e2e-preload-sample-markers branch 2 times, most recently from 56cc7f5 to 49478c1 Compare August 17, 2026 23:30

Copy link
Copy Markdown
Contributor Author

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.

@kyle-schellen
kyle-schellen force-pushed the ks-e2e-preload-sample-markers branch from 49478c1 to 715c1f5 Compare August 18, 2026 13:30
@kyle-schellen
kyle-schellen marked this pull request as ready for review August 19, 2026 19:17
Comment on lines +24 to +35
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"
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This might be overkill. It might be hard to force these states

@kyle-schellen
kyle-schellen force-pushed the ks-e2e-preload-sample-markers branch from 715c1f5 to 25f45e0 Compare August 21, 2026 16:22
@kyle-schellen
kyle-schellen merged commit 0fdad6c into main Aug 21, 2026
35 checks passed
@kyle-schellen
kyle-schellen deleted the ks-e2e-preload-sample-markers branch August 21, 2026 19:22
kyle-schellen added a commit that referenced this pull request Aug 26, 2026
## 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 -->
kyle-schellen added a commit that referenced this pull request Aug 26, 2026
## 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 -->
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

#gsd:50662 Rebase Checkout Kit on UCP

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants