Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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<Map<String, Int>>(mutableMapOf()) }
Expand Down Expand Up @@ -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,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -60,6 +62,9 @@ class CartViewModel(
private val _checkoutPresentationMode = MutableStateFlow(CheckoutPresentationMode.CheckoutKitSheet)
val checkoutPresentationMode: StateFlow<CheckoutPresentationMode> = _checkoutPresentationMode.asStateFlow()

private val _preloadStateTestId = MutableStateFlow(PreloadStateMarker.testId(PreloadState.Idle))
val preloadStateTestId: StateFlow<String> = _preloadStateTestId.asStateFlow()

private var demoBuyerIdentityEnabled = false
private var checkoutPreloadingEnabled = true
private var windowOpenHandler = WindowOpenHandler.Default
Expand Down Expand Up @@ -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)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.shopify.checkoutkit.androiddemo.e2e

object E2ETestIds {
const val APP_READY = "checkout-kit-sample-ready"
const val PRELOAD_STATE_PREFIX = "preload-state-"

object Cart {
const val CHECKOUT_READY = "cart-checkout-ready"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.shopify.checkoutkit.androiddemo.e2e

import com.shopify.checkoutkit.PreloadState

/** Maps [PreloadState] to a preload identifier. */
object PreloadStateMarker {
fun testId(state: PreloadState): String = "${E2ETestIds.PRELOAD_STATE_PREFIX}${text(state)}"

fun text(state: PreloadState): String = when (state) {
is PreloadState.Idle -> "idle"
is PreloadState.Loading -> "loading"
is PreloadState.Ready -> "ready"
is PreloadState.Expired -> "expired"
is PreloadState.Failed -> failedText(state.reason)
}

private fun failedText(reason: PreloadState.FailureReason): String = when (reason) {
is PreloadState.FailureReason.HttpError -> "failed-http-${reason.statusCode}"
is PreloadState.FailureReason.NavigationFailed -> "failed-navigation"
is PreloadState.FailureReason.WebContentUnavailable -> "failed-web-content-unavailable"
is PreloadState.FailureReason.ProtocolError -> "failed-protocol"
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
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 `lifecycle marker texts match the maestro flow assertions`() {
assertThat(PreloadStateMarker.text(PreloadState.Idle)).isEqualTo("idle")
assertThat(PreloadStateMarker.text(PreloadState.Loading)).isEqualTo("loading")
assertThat(PreloadStateMarker.text(PreloadState.Ready)).isEqualTo("ready")
assertThat(PreloadStateMarker.text(PreloadState.Expired)).isEqualTo("expired")
}

@Test
fun `http failure marker text includes the status code`() {
assertThat(markerFor(PreloadState.FailureReason.HttpError(statusCode = 403)))
.isEqualTo("failed-http-403")
assertThat(markerFor(PreloadState.FailureReason.HttpError(statusCode = 500)))
.isEqualTo("failed-http-500")
}

@Test
fun `non-http failure marker texts`() {
assertThat(markerFor(PreloadState.FailureReason.NavigationFailed)).isEqualTo("failed-navigation")
assertThat(markerFor(PreloadState.FailureReason.WebContentUnavailable))
.isEqualTo("failed-web-content-unavailable")
assertThat(markerFor(PreloadState.FailureReason.ProtocolError)).isEqualTo("failed-protocol")
}

@Test
fun `dynamic test ids match the maestro flow assertions`() {
assertThat(PreloadStateMarker.testId(PreloadState.Idle)).isEqualTo("preload-state-idle")
assertThat(PreloadStateMarker.testId(PreloadState.Ready)).isEqualTo("preload-state-ready")
assertThat(
PreloadStateMarker.testId(
PreloadState.Failed(
reason = PreloadState.FailureReason.HttpError(statusCode = 403),
message = "Forbidden",
)
)
).isEqualTo("preload-state-failed-http-403")
}

private fun markerFor(reason: PreloadState.FailureReason): String =
PreloadStateMarker.text(PreloadState.Failed(reason = reason, message = "Test failure"))
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
enum E2ETestIds {
static let appReady = "checkout-kit-sample-ready"
static let preloadStatePrefix = "preload-state-"

enum Cart {
static let checkoutReady = "cart-checkout-ready"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import ShopifyCheckoutKit

/// Maps ``PreloadState`` to a preload identifier.
enum PreloadStateMarker {
static func testId(for state: PreloadState) -> String {
"\(E2ETestIds.preloadStatePrefix)\(text(for: state))"
}

static func text(for state: PreloadState) -> String {
switch state {
case .idle:
return "idle"
case .loading:
return "loading"
case .ready:
return "ready"
case .expired:
return "expired"
case let .failed(reason, _):
return failedText(for: reason)
}
}

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"
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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 {
Expand Down Expand Up @@ -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)")
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
@testable import CheckoutKitSwiftDemo
import ShopifyCheckoutKit
import XCTest

class PreloadStateMarkerTests: XCTestCase {
func testLifecycleMarkerTextsMatchTheMaestroFlowAssertions() {
XCTAssertEqual(PreloadStateMarker.text(for: .idle), "idle")
XCTAssertEqual(PreloadStateMarker.text(for: .loading), "loading")
XCTAssertEqual(PreloadStateMarker.text(for: .ready), "ready")
XCTAssertEqual(PreloadStateMarker.text(for: .expired), "expired")
}

func testHttpFailureMarkerTextIncludesTheStatusCode() {
XCTAssertEqual(
PreloadStateMarker.text(for: .failed(reason: .httpError(statusCode: 403), message: "Forbidden")),
"failed-http-403"
)
XCTAssertEqual(
PreloadStateMarker.text(for: .failed(reason: .httpError(statusCode: 500), message: "Server error")),
"failed-http-500"
)
}

func testNonHttpFailureMarkerTexts() {
XCTAssertEqual(
PreloadStateMarker.text(for: .failed(reason: .navigationFailed, message: "Navigation failed")),
"failed-navigation"
)
XCTAssertEqual(
PreloadStateMarker.text(for: .failed(reason: .webContentUnavailable, message: "Web content unavailable")),
"failed-web-content-unavailable"
)
XCTAssertEqual(
PreloadStateMarker.text(for: .failed(reason: .protocolError, message: "Protocol error")),
"failed-protocol"
)
}

func testDynamicTestIdsMatchTheMaestroFlowAssertions() {
XCTAssertEqual(PreloadStateMarker.testId(for: .idle), "preload-state-idle")
XCTAssertEqual(PreloadStateMarker.testId(for: .ready), "preload-state-ready")
XCTAssertEqual(
PreloadStateMarker.testId(for: .failed(reason: .httpError(statusCode: 403), message: "Forbidden")),
"preload-state-failed-http-403"
)
}
}
Loading