diff --git a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/accessibility/AccessibilityIdentifiers.kt b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/accessibility/AccessibilityIdentifiers.kt index d94343dfa..95f9b7ad4 100644 --- a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/accessibility/AccessibilityIdentifiers.kt +++ b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/accessibility/AccessibilityIdentifiers.kt @@ -2,6 +2,7 @@ package com.shopify.checkoutkit.androiddemo.accessibility object AccessibilityIdentifiers { const val APP_READY = "checkout-kit-sample-ready" + const val PRELOAD_STATE_PREFIX = "preload-state-" object Cart { const val CHECKOUT_READY = "cart-checkout-ready" diff --git a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartView.kt b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartView.kt index 71e8d0dfc..f2658673c 100644 --- a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartView.kt +++ b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartView.kt @@ -62,6 +62,7 @@ fun CartView( val state = cartViewModel.cartState.collectAsState().value val loading = cartViewModel.loadingState.collectAsState().value val checkoutPresentationMode = cartViewModel.checkoutPresentationMode.collectAsState().value + val preloadStateTestId = cartViewModel.preloadStateTestId.collectAsState().value val activity = LocalActivity.current as ComponentActivity var mutableQuantity by remember { mutableStateOf>(mutableMapOf()) } @@ -90,7 +91,12 @@ fun CartView( it.title to it.quantity } - Column(modifier = Modifier.padding(top = 4.dp)) { + // Exposes the current preload state as a preload identifier. + Column( + modifier = Modifier + .padding(top = 4.dp) + .testTag(preloadStateTestId) + ) { CartLines( lines = state.cartLines, loading = loading, diff --git a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartViewModel.kt b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartViewModel.kt index d8d3d7d62..b2d23fbd6 100644 --- a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartViewModel.kt +++ b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/CartViewModel.kt @@ -11,6 +11,7 @@ import com.shopify.checkoutkit.CheckoutErrorCode import com.shopify.checkoutkit.CheckoutException import com.shopify.checkoutkit.CheckoutPresentation import com.shopify.checkoutkit.CheckoutProtocol +import com.shopify.checkoutkit.PreloadState import com.shopify.checkoutkit.ShopifyCheckoutKit import com.shopify.checkoutkit.androiddemo.MainActivity import com.shopify.checkoutkit.androiddemo.R @@ -22,6 +23,7 @@ import com.shopify.checkoutkit.androiddemo.common.SnackbarEvent import com.shopify.checkoutkit.androiddemo.common.logs.LogLevel import com.shopify.checkoutkit.androiddemo.common.logs.Logger import com.shopify.checkoutkit.androiddemo.common.navigation.Screen +import com.shopify.checkoutkit.androiddemo.e2e.PreloadStateMarker import com.shopify.checkoutkit.androiddemo.settings.PreferencesManager import com.shopify.checkoutkit.androiddemo.settings.authentication.data.AuthenticationState import com.shopify.checkoutkit.androiddemo.settings.authentication.data.CustomerRepository @@ -60,6 +62,9 @@ class CartViewModel( private val _checkoutPresentationMode = MutableStateFlow(CheckoutPresentationMode.CheckoutKitSheet) val checkoutPresentationMode: StateFlow = _checkoutPresentationMode.asStateFlow() + private val _preloadStateTestId = MutableStateFlow(PreloadStateMarker.testId(PreloadState.Idle)) + val preloadStateTestId: StateFlow = _preloadStateTestId.asStateFlow() + private var demoBuyerIdentityEnabled = false private var checkoutPreloadingEnabled = true private var windowOpenHandler = WindowOpenHandler.Default @@ -171,6 +176,7 @@ class CartViewModel( Timber.i("Preloading checkout") ShopifyCheckoutKit.preload(url, activity) { state -> Timber.i("Preload state changed to $state") + _preloadStateTestId.value = PreloadStateMarker.testId(state) } } diff --git a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/e2e/PreloadStateMarker.kt b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/e2e/PreloadStateMarker.kt new file mode 100644 index 000000000..a5034f9ec --- /dev/null +++ b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/e2e/PreloadStateMarker.kt @@ -0,0 +1,12 @@ +package com.shopify.checkoutkit.androiddemo.e2e + +import com.shopify.checkoutkit.PreloadState +import com.shopify.checkoutkit.androiddemo.accessibility.AccessibilityIdentifiers + +/** Maps whether [PreloadState] is ready to a preload identifier. */ +object PreloadStateMarker { + fun testId(state: PreloadState): String { + val value = if (state is PreloadState.Ready) "ready" else "not-ready" + return "${AccessibilityIdentifiers.PRELOAD_STATE_PREFIX}$value" + } +} diff --git a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/test/java/com/shopify/checkoutkit/androiddemo/e2e/PreloadStateMarkerTest.kt b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/test/java/com/shopify/checkoutkit/androiddemo/e2e/PreloadStateMarkerTest.kt new file mode 100644 index 000000000..55fc92bba --- /dev/null +++ b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/test/java/com/shopify/checkoutkit/androiddemo/e2e/PreloadStateMarkerTest.kt @@ -0,0 +1,17 @@ +package com.shopify.checkoutkit.androiddemo.e2e + +import com.shopify.checkoutkit.PreloadState +import org.assertj.core.api.Assertions.assertThat +import org.junit.Test + +class PreloadStateMarkerTest { + @Test + fun `ready marker matches the maestro flow assertion`() { + assertThat(PreloadStateMarker.testId(PreloadState.Ready)).isEqualTo("preload-state-ready") + } + + @Test + fun `non-ready state uses the fallback marker`() { + assertThat(PreloadStateMarker.testId(PreloadState.Idle)).isEqualTo("preload-state-not-ready") + } +} diff --git a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Accessibility/AccessibilityIdentifiers.swift b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Accessibility/AccessibilityIdentifiers.swift index fd3f2a42a..28d942d09 100644 --- a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Accessibility/AccessibilityIdentifiers.swift +++ b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Accessibility/AccessibilityIdentifiers.swift @@ -1,5 +1,6 @@ enum AccessibilityIdentifiers { static let appReady = "checkout-kit-sample-ready" + static let preloadStatePrefix = "preload-state-" enum Cart { static let checkoutReady = "cart-checkout-ready" diff --git a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/E2E/PreloadStateMarker.swift b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/E2E/PreloadStateMarker.swift new file mode 100644 index 000000000..96215d408 --- /dev/null +++ b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/E2E/PreloadStateMarker.swift @@ -0,0 +1,9 @@ +import ShopifyCheckoutKit + +/// Maps whether ``PreloadState`` is ready to a preload identifier. +enum PreloadStateMarker { + static func testId(for state: PreloadState) -> String { + let value = if case .ready = state { "ready" } else { "not-ready" } + return "\(AccessibilityIdentifiers.preloadStatePrefix)\(value)" + } +} diff --git a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Scenes/Cart/CartView.swift b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Scenes/Cart/CartView.swift index 46dadcfec..8d5f10643 100644 --- a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Scenes/Cart/CartView.swift +++ b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Scenes/Cart/CartView.swift @@ -12,6 +12,7 @@ struct CartView: View { @State var isCompleted: Bool = false @State var showCheckoutSheet: Bool = false @State private var checkoutPreload: CheckoutPreload? + @State private var preloadStateTestId = PreloadStateMarker.testId(for: .idle) @ObservedObject var cartManager: CartManager = .shared @@ -37,6 +38,8 @@ struct CartView: View { } .padding(.bottom, 130) } + .accessibilityElement(children: .contain) + .accessibilityIdentifier(preloadStateTestId) VStack(spacing: DesignSystem.buttonSpacing) { if let cartID = cartManager.cart?.id { @@ -159,6 +162,7 @@ struct CartView: View { ShopifyCheckoutKit.invalidate() checkoutPreload = ShopifyCheckoutKit.preload(checkout: url) checkoutPreload?.onStateChange = { state in + preloadStateTestId = PreloadStateMarker.testId(for: state) print("[Preload] state changed to \(state)") ShopifyCheckoutKit.configuration.logger.log("Preload state changed to \(state)") } diff --git a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemoTests/E2E/PreloadStateMarkerTests.swift b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemoTests/E2E/PreloadStateMarkerTests.swift new file mode 100644 index 000000000..389a6c06c --- /dev/null +++ b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemoTests/E2E/PreloadStateMarkerTests.swift @@ -0,0 +1,13 @@ +@testable import CheckoutKitSwiftDemo +import ShopifyCheckoutKit +import XCTest + +class PreloadStateMarkerTests: XCTestCase { + func testReadyMarkerMatchesTheMaestroFlowAssertion() { + XCTAssertEqual(PreloadStateMarker.testId(for: .ready), "preload-state-ready") + } + + func testNonReadyStateUsesTheFallbackMarker() { + XCTAssertEqual(PreloadStateMarker.testId(for: .idle), "preload-state-not-ready") + } +}