From 814e529260cd4ffde33d10114aabd0f93c12ed98 Mon Sep 17 00:00:00 2001 From: Kyle Schellen Date: Tue, 19 May 2026 13:54:28 -0400 Subject: [PATCH] Add React Native Android checkout e2e flow --- e2e/README.md | 12 +- .../android/checkout-completion.yaml | 288 ++++++++++++++++++ platforms/react-native/package.json | 3 +- 3 files changed, 299 insertions(+), 4 deletions(-) create mode 100644 e2e/react-native/android/checkout-completion.yaml diff --git a/e2e/README.md b/e2e/README.md index 5fb58a0e..87e8eb5f 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -23,6 +23,11 @@ sample ships to both platforms; its flows are split because some assertions are platform-specific (iOS accessibility-label patterns vs Android resource strings). +The React Native Android checkout flow submits payment and waits for the order +confirmation screen. The React Native iOS checkout flow currently stops at +`Pay now`; the store/address path can show a checkout-web shipping availability +banner after submit, which is outside the Checkout Kit integration contract. + Folders are created when their first flow lands. Don't pre-create empty directories. @@ -35,7 +40,7 @@ Use these in the `appId:` header of every flow. Don't invent new bundle ids. | `swift/` | `com.shopify.example.MobileBuyIntegration` | | `android/` | `com.shopify.checkout_kit_mobile_buy_integration_sample` | | `react-native/ios/` | `com.shopify.example.CheckoutKitReactNative` | -| `react-native/android/` | `com.shopify.example.CheckoutKitReactNative` | +| `react-native/android/` | `com.shopify.checkoutkitreactnative` | ## Running @@ -48,7 +53,7 @@ terminal. | React Native, iOS | `platforms/react-native/` | `pnpm e2e:ios` | | Swift, iOS | TBD | TBD | | Android (native) | TBD | TBD | -| RN, Android | TBD | TBD | +| RN, Android | `platforms/react-native/` | `pnpm e2e:android` | Maestro itself is a system CLI, not an npm dependency. Install once with: @@ -61,7 +66,8 @@ curl -fsSL "https://get.maestro.mobile.dev" | bash 1. Drop a new `.yaml` under the right folder. 2. Set `appId:` from the table above. 3. Keep timeouts in the existing tiers (in-app interactions ~10s, network - transitions ~30s, cold starts and checkout first-paint ~60s). + transitions ~30s, cold starts, checkout first-paint, and order confirmation + ~60s). 4. If the flow needs an npm script wrapper, add an `e2e:` script to the matching `package.json` next to existing scripts. The script should point at the folder, not an individual file, so the whole folder runs. diff --git a/e2e/react-native/android/checkout-completion.yaml b/e2e/react-native/android/checkout-completion.yaml new file mode 100644 index 00000000..5fd7a469 --- /dev/null +++ b/e2e/react-native/android/checkout-completion.yaml @@ -0,0 +1,288 @@ +appId: com.shopify.checkoutkitreactnative +name: Checkout submits and shows result +tags: + - android + - checkout + +env: + # CI shipping fixture + COUNTRY_LABEL: "United States" + ADDRESS_LINE1: "700 S Flower St" + CITY: "Los Angeles" + STATE_FIELD_LABEL: "State" + STATE_LABEL: "California" + POSTAL_CODE: "90017" + POSTAL_FIELD_LABEL: "ZIP code" + + # CI payment fixture + CARD_NUMBER: "1" + CARD_EXPIRY_DISPLAY: "12 / 30" + CARD_SECURITY_CODE: "123" + CARDHOLDER_NAME: "Maestro Shopify" + + # Accepted terminal checkout states for this smoke test. + POST_SUBMIT_RESULT_PATTERN: ".*(Thank you|Your order|Order confirmed|confirmation|Shipping not available|There was a problem|declined|couldn.t process).*" +--- +# Timeout tiers: +# 10000 - in-app interactions (taps, animations) +# 30000 - checkout step transitions (network) +# 60000 - cold starts, first checkout paint, final submit + +# Product and cart +- launchApp: + clearState: true +- extendedWaitUntil: + visible: + id: product-0-add-to-cart-button + timeout: 60000 +- scrollUntilVisible: + element: + id: product-0-add-to-cart-button + direction: DOWN + timeout: 10000 + centerElement: true +- tapOn: + id: product-0-add-to-cart-button + enabled: true +- waitForAnimationToEnd: + timeout: 10000 +- runFlow: + when: + visible: + id: header-cart-icon + commands: + - tapOn: + id: header-cart-icon +- runFlow: + when: + notVisible: + id: checkout-button + commands: + - tapOn: + id: cart-tab +- extendedWaitUntil: + visible: + id: checkout-button + timeout: 30000 +- tapOn: + id: checkout-button + enabled: true + +# Contact +- extendedWaitUntil: + visible: + text: "^Email( or mobile phone number)?$" + timeout: 60000 +- tapOn: + id: email +- inputText: "maestro.e2e@shopify.com" +- extendedWaitUntil: + visible: "^maestro.e2e@shopify.com$" + timeout: 10000 +- scrollUntilVisible: + element: + id: TextField0 + direction: DOWN + timeout: 10000 + centerElement: true +- tapOn: + id: TextField0 +- inputText: "Maestro" +- extendedWaitUntil: + visible: + id: TextField0 + text: "^Maestro$" + timeout: 10000 +- scrollUntilVisible: + element: + text: "^Last name$" + direction: DOWN + timeout: 10000 + visibilityPercentage: 100 + centerElement: true +- tapOn: + id: TextField1 +- inputText: "Shopify" +- extendedWaitUntil: + visible: + id: TextField1 + text: "^Shopify$" + timeout: 10000 + +# Shipping address +- scrollUntilVisible: + element: + id: Select0 + direction: DOWN + timeout: 10000 +- tapOn: + id: Select0 +- waitForAnimationToEnd: + timeout: 10000 +- scrollUntilVisible: + element: + text: "^${COUNTRY_LABEL}$" + direction: UP + timeout: 10000 + visibilityPercentage: 10 + optional: true +- scrollUntilVisible: + element: + text: "^${COUNTRY_LABEL}$" + direction: DOWN + timeout: 10000 + visibilityPercentage: 10 + optional: true +- tapOn: + text: "^${COUNTRY_LABEL}$" +- waitForAnimationToEnd: + timeout: 10000 + +- scrollUntilVisible: + element: + id: shipping-address1 + direction: DOWN + timeout: 10000 +- tapOn: + id: shipping-address1 +- eraseText: 80 +- inputText: "${ADDRESS_LINE1}" +- waitForAnimationToEnd: + timeout: 10000 +- extendedWaitUntil: + visible: "^${ADDRESS_LINE1}$" + timeout: 10000 +- runFlow: + when: + visible: "Close suggestions" + commands: + - tapOn: "Close suggestions" + - waitForAnimationToEnd: + timeout: 10000 +- extendedWaitUntil: + notVisible: + id: shipping-address1-autocomplete-title + timeout: 10000 +- extendedWaitUntil: + notVisible: + id: shipping-address1-options + timeout: 10000 +- scrollUntilVisible: + element: + id: TextField3 + direction: DOWN + timeout: 10000 + centerElement: true +- tapOn: + id: TextField3 +- eraseText: 80 +- inputText: "${CITY}" +- extendedWaitUntil: + visible: "^${CITY}$" + timeout: 10000 +- scrollUntilVisible: + element: + id: Select1 + direction: DOWN + timeout: 10000 + centerElement: true +- runFlow: + when: + notVisible: "^${STATE_LABEL}$" + commands: + - tapOn: + id: Select1 + - waitForAnimationToEnd: + timeout: 10000 + - scrollUntilVisible: + element: + text: "^${STATE_LABEL}$" + direction: UP + timeout: 10000 + visibilityPercentage: 100 + optional: true + - scrollUntilVisible: + element: + text: "^${STATE_LABEL}$" + direction: DOWN + timeout: 10000 + visibilityPercentage: 100 + optional: true + - tapOn: + text: "^${STATE_LABEL}$" + - waitForAnimationToEnd: + timeout: 10000 +- extendedWaitUntil: + notVisible: "Select a state" + timeout: 10000 +- extendedWaitUntil: + visible: "^${STATE_LABEL}$" + timeout: 10000 +- scrollUntilVisible: + element: + id: TextField4 + direction: DOWN + timeout: 10000 + centerElement: true +- tapOn: + id: TextField4 +- eraseText: 80 +- inputText: "${POSTAL_CODE}" +- extendedWaitUntil: + visible: "^${POSTAL_CODE}$" + timeout: 10000 +- waitForAnimationToEnd: + timeout: 10000 + +# Payment +- scrollUntilVisible: + element: + id: number + direction: DOWN + timeout: 10000 + centerElement: true +- tapOn: + id: number +- inputText: "${CARD_NUMBER}" +- tapOn: + id: expiry +- inputText: "1" +- waitForAnimationToEnd: + timeout: 10000 +- inputText: "2" +- waitForAnimationToEnd: + timeout: 10000 +- inputText: "3" +- waitForAnimationToEnd: + timeout: 10000 +- inputText: "0" +- extendedWaitUntil: + visible: "^${CARD_EXPIRY_DISPLAY}$" + timeout: 10000 +- tapOn: + id: verification_value +- inputText: "${CARD_SECURITY_CODE}" +- extendedWaitUntil: + visible: "^${CARD_SECURITY_CODE}$" + timeout: 10000 +- scrollUntilVisible: + element: + id: name + direction: DOWN + timeout: 10000 + centerElement: true +- extendedWaitUntil: + visible: "^${CARDHOLDER_NAME}$" + timeout: 10000 +- scrollUntilVisible: + element: + text: "^(Pay now|Complete order)$" + direction: DOWN + timeout: 10000 + centerElement: true +- tapOn: + text: "^(Pay now|Complete order)$" + enabled: true +- extendedWaitUntil: + visible: "${POST_SUBMIT_RESULT_PATTERN}" + timeout: 60000 diff --git a/platforms/react-native/package.json b/platforms/react-native/package.json index 6acad1ac..43fdb5bf 100644 --- a/platforms/react-native/package.json +++ b/platforms/react-native/package.json @@ -25,7 +25,8 @@ "compare-snapshot": "./scripts/compare_snapshot", "turbo": "turbo", "test": "jest", - "e2e:ios": "maestro --platform ios test --config ../../e2e/config.yaml ../../e2e/react-native/ios" + "e2e:ios": "maestro --platform ios test --config ../../e2e/config.yaml ../../e2e/react-native/ios", + "e2e:android": "maestro --platform android test --config ../../e2e/config.yaml ../../e2e/react-native/android" }, "devDependencies": { "@babel/core": "^7.25.2",