From 8a2cecd0f24191a75e9d97a471b9c084fd1d94ce Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Fri, 31 Jul 2026 11:37:57 +0100 Subject: [PATCH 01/13] feat(e2e): add the guest checkout order test on all four targets Moves the guest checkout test out of tests/react-native and into tests/shared, so Swift, Kotlin, React Native iOS and React Native Android all run it. The old file carried a wip tag to keep it out of the matrix; both go away together. Three flow fixes came out of running it: - fill-shipping-address.yaml asked for visibilityPercentage 10 in four places. A value under 100 rounds down to zero, so those scrolls stopped before they moved. centerElement replaces all four. - The shop requires a phone number on a delivery address, and no flow filled it. The field only appears when a shop asks for it, so the new step is optional. - Checkout copies the delivery name into "Name on card", and inputText appends to it. fill-payment-card.yaml now erases the field first. Co-Authored-By: Claude Opus 5 (1M context) Assisted-By: devx/5b4b8d5b-690a-4389-841e-039723ade1da --- e2e/README.md | 8 +-- e2e/flows/checkout/fill-shipping-address.yaml | 35 +++++++++-- e2e/tests/react-native/checkout-guest.yaml | 42 ------------- e2e/tests/shared/checkout-guest.yaml | 59 +++++++++++++++++++ 4 files changed, 94 insertions(+), 50 deletions(-) delete mode 100644 e2e/tests/react-native/checkout-guest.yaml create mode 100644 e2e/tests/shared/checkout-guest.yaml diff --git a/e2e/README.md b/e2e/README.md index 9a317eccf..160c2b568 100644 --- a/e2e/README.md +++ b/e2e/README.md @@ -148,10 +148,10 @@ ruby e2e/scripts/e2e_matrix_to_browserstack_run_plan count - `tests/shared/launch-smoke.yaml` is the shared launch smoke test. - `tests/shared/checkout-present-and-close.yaml` seeds a cart through the control link, presents checkout, closes it, and asserts dismissal. -- `tests/react-native/checkout-guest.yaml` composes the React Native guest - checkout smoke test from those subflows. -- `tests/react-native/checkout-hardcoded-buyer-identity.yaml` verifies checkout - from a bootstrapped cart with hardcoded buyer identity. +- `tests/shared/checkout-hardcoded-buyer-identity.yaml` orders from a cart that + already carries the contact and the delivery address. +- `tests/shared/checkout-guest.yaml` orders from an empty identity, so checkout + asks for the contact and the delivery address as well as the payment. - `scripts/run_local_e2e` builds and installs any of the four local targets. - `scripts/run_maestro` is their single Maestro invocation. It holds the environment contract and target-specific test-file selection in one place. diff --git a/e2e/flows/checkout/fill-shipping-address.yaml b/e2e/flows/checkout/fill-shipping-address.yaml index 3efa178a8..c911d1055 100644 --- a/e2e/flows/checkout/fill-shipping-address.yaml +++ b/e2e/flows/checkout/fill-shipping-address.yaml @@ -1,9 +1,13 @@ appId: ${E2E_APP_ID} --- +# Every scroll below asks for `centerElement`. A `visibilityPercentage` under 100 rounds +# down to zero instead, which lets the scroll stop before it moves, and the tap that +# follows then lands on whatever already sat there. - scrollUntilVisible: element: text: "Country/Region" direction: DOWN + centerElement: true # If the country is already selected and visible, skip this. Otherwise open the # picker, only scroll if needed, then select the configured country. @@ -22,7 +26,7 @@ appId: ${E2E_APP_ID} element: text: "^${COUNTRY_LABEL}$" direction: DOWN - visibilityPercentage: 10 + centerElement: true optional: true - runFlow: when: @@ -32,7 +36,7 @@ appId: ${E2E_APP_ID} element: text: "^${COUNTRY_LABEL}$" direction: UP - visibilityPercentage: 10 + centerElement: true - tapOn: text: "^${COUNTRY_LABEL}$" - waitForAnimationToEnd @@ -72,7 +76,7 @@ appId: ${E2E_APP_ID} element: text: "^${STATE_LABEL}$" direction: DOWN - visibilityPercentage: 10 + centerElement: true optional: true - runFlow: when: @@ -82,12 +86,35 @@ appId: ${E2E_APP_ID} element: text: "^${STATE_LABEL}$" direction: UP - visibilityPercentage: 10 + centerElement: true - tapOn: text: "^${STATE_LABEL}$" - waitForAnimationToEnd +- scrollUntilVisible: + element: + text: "^${POSTAL_FIELD_LABEL}$" + direction: DOWN + centerElement: true - tapOn: text: "^${POSTAL_FIELD_LABEL}$" - waitForAnimationToEnd - inputText: "${POSTAL_CODE}" - runFlow: dismiss-active-field.yaml + +# A shop decides whether the delivery address asks for a phone number, and it can make +# that field required. The scroll is optional so a shop without the field still passes. +- scrollUntilVisible: + element: + text: "^Phone$" + direction: DOWN + centerElement: true + optional: true +- runFlow: + when: + visible: "^Phone$" + commands: + - tapOn: + text: "^Phone$" + - waitForAnimationToEnd + - inputText: "${PHONE}" + - runFlow: dismiss-active-field.yaml diff --git a/e2e/tests/react-native/checkout-guest.yaml b/e2e/tests/react-native/checkout-guest.yaml deleted file mode 100644 index 958dfcafc..000000000 --- a/e2e/tests/react-native/checkout-guest.yaml +++ /dev/null @@ -1,42 +0,0 @@ -appId: ${E2E_APP_ID} -name: React Native checkout - guest -tags: - - checkout - - full - # Only React Native runs this today, and every row now runs the whole tests folder. - # The shared guest test replaces this file, and this tag goes with it. - - wip - -env: - # Sample app buyer identity configuration - E2E_CART_PARAMS: "productIndex=0&quantity=1&buyerIdentityMode=guest" - - # Checkout contact fixture - EMAIL: "maestro.e2e@shopify.com" - FIRST_NAME: "Maestro" - LAST_NAME: "Shopify" - - # Checkout 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" - - # Checkout payment fixture - CARD_NUMBER: "1" - CARD_SECURITY_CODE: "123" - - # Accepted successful checkout states for this smoke test. - POST_SUBMIT_RESULT_PATTERN: ".*(Thank you|Your order|Order confirmed|confirmation).*" ---- -- runFlow: ../../flows/app/bootstrap-cart-from-link.yaml -- runFlow: ../../flows/checkout/present.yaml -- runFlow: ../../flows/checkout/fill-contact.yaml -- runFlow: ../../flows/checkout/fill-shipping-address.yaml -- runFlow: ../../flows/checkout/fill-payment-card.yaml -- runFlow: ../../flows/checkout/submit.yaml -- runFlow: ../../flows/checkout/assert-complete.yaml -- runFlow: ../../flows/checkout/assert-returned-to-empty-cart.yaml diff --git a/e2e/tests/shared/checkout-guest.yaml b/e2e/tests/shared/checkout-guest.yaml new file mode 100644 index 000000000..7a6e6ca6a --- /dev/null +++ b/e2e/tests/shared/checkout-guest.yaml @@ -0,0 +1,59 @@ +appId: ${E2E_APP_ID} +name: Checkout as a guest +tags: + - checkout + # This test submits a real order, so it belongs to the expensive tier. + - full + +env: + # The cart carries no contact and no delivery address, so checkout asks a guest for + # both. That makes this the longest real order, and the widest cover of the form. + E2E_CART_PARAMS: "productIndex=0&quantity=1&buyerIdentityMode=guest" + + # Checkout contact fixture. + EMAIL: "maestro.e2e@shopify.com" + FIRST_NAME: "Maestro" + LAST_NAME: "Shopify" + + # Checkout delivery fixture. The shop opens on its own country, so this address picks a + # different one and makes the flow use the country picker. The two field labels follow + # the country: the United States names them State and ZIP code. + COUNTRY_LABEL: "United States" + ADDRESS_LINE1: "700 S Flower St" + CITY: "Los Angeles" + STATE_FIELD_LABEL: "State" + STATE_LABEL: "California" + POSTAL_FIELD_LABEL: "ZIP code" + POSTAL_CODE: "90017" + # The shop requires a phone number on the delivery address. 555-01xx is the number range + # reserved for fiction, so this value reaches nobody. + PHONE: "213 555 0142" + + # Checkout payment fixture. The shop runs its credit card gateway in test mode, which + # accepts this number only. Checkout groups the digits, so the flow needs both forms. + CARD_NUMBER: "4242424242424242" + CARD_NUMBER_DISPLAY: "4242 4242 4242 4242" + CARD_SECURITY_CODE: "123" + CARD_HOLDER_NAME: "Maestro Shopify" + + # Checkout billing fixture. A guest types the delivery address into checkout itself, so + # checkout can copy it and the billing section stays closed. These values cover the + # target that opens the section anyway. + BILLING_FIRST_NAME: "Maestro" + BILLING_LAST_NAME: "Shopify" + BILLING_ADDRESS_LINE1: "700 S Flower St" + BILLING_CITY: "Los Angeles" + BILLING_POSTAL_CODE: "90017" + + # Accepted successful checkout states for this smoke test. + POST_SUBMIT_RESULT_PATTERN: ".*(Thank you|Your order|Order confirmed|confirmation).*" +--- +- runFlow: ../../flows/app/bootstrap-cart-from-link.yaml +- runFlow: ../../flows/checkout/present.yaml +- runFlow: ../../flows/checkout/fill-contact.yaml +- runFlow: ../../flows/checkout/fill-shipping-address.yaml +- runFlow: ../../flows/checkout/fill-payment-card.yaml +- runFlow: ../../flows/checkout/fill-billing-address.yaml +- runFlow: ../../flows/checkout/submit.yaml +- runFlow: ../../flows/checkout/assert-complete.yaml +- runFlow: ../../flows/checkout/assert-returned-to-empty-cart.yaml From 4bf237e00ec602b0119a05e60a4f5ceb8aeb09a3 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Tue, 18 Aug 2026 09:58:44 +0100 Subject: [PATCH 02/13] Update checkout-guest.yaml --- e2e/flows/app/hide-keyboard.yaml | 14 ++++ e2e/flows/checkout/fill-billing-address.yaml | 50 ++++++------- e2e/flows/checkout/fill-contact.yaml | 16 ---- e2e/flows/checkout/fill-payment-card.yaml | 16 +--- e2e/flows/checkout/fill-shipping-address.yaml | 74 +++++++------------ e2e/flows/checkout/fill-text-field.yaml | 14 ++++ e2e/test/checkout_payment_fixture_test.rb | 31 -------- e2e/tests/shared/checkout-guest.yaml | 4 +- .../checkout-hardcoded-buyer-identity.yaml | 2 +- 9 files changed, 85 insertions(+), 136 deletions(-) create mode 100644 e2e/flows/checkout/fill-text-field.yaml delete mode 100644 e2e/test/checkout_payment_fixture_test.rb diff --git a/e2e/flows/app/hide-keyboard.yaml b/e2e/flows/app/hide-keyboard.yaml index 9b14a8004..0360dbd4f 100644 --- a/e2e/flows/app/hide-keyboard.yaml +++ b/e2e/flows/app/hide-keyboard.yaml @@ -21,3 +21,17 @@ appId: ${E2E_APP_ID} commands: - tapOn: "selected" - waitForAnimationToEnd +- runFlow: + when: + platform: ios + visible: "Done" + commands: + - hideKeyboard + - waitForAnimationToEnd +- runFlow: + when: + platform: ios + visible: "selected" + commands: + - hideKeyboard + - waitForAnimationToEnd diff --git a/e2e/flows/checkout/fill-billing-address.yaml b/e2e/flows/checkout/fill-billing-address.yaml index 1519aff0a..b962a21a4 100644 --- a/e2e/flows/checkout/fill-billing-address.yaml +++ b/e2e/flows/checkout/fill-billing-address.yaml @@ -15,29 +15,29 @@ appId: ${E2E_APP_ID} commands: # Country/Region and Province already hold the shop defaults, so this flow leaves # them alone and supplies an address that suits those defaults. - - scrollUntilVisible: - element: - text: "^First name( \\(optional\\))?$" - direction: DOWN - centerElement: true - - tapOn: - text: "^First name( \\(optional\\))?$" - - inputText: "${BILLING_FIRST_NAME}" - - runFlow: dismiss-active-field.yaml - - tapOn: - text: "^Last name$" - - inputText: "${BILLING_LAST_NAME}" - - runFlow: dismiss-active-field.yaml - - tapOn: - text: "^Address$" - - inputText: "${BILLING_ADDRESS_LINE1}" - - runFlow: dismiss-active-field.yaml - - tapOn: - text: "^City$" - - inputText: "${BILLING_CITY}" - - runFlow: dismiss-active-field.yaml - - tapOn: - text: "^${BILLING_POSTAL_FIELD_LABEL}$" - - inputText: "${BILLING_POSTAL_CODE}" - - runFlow: dismiss-active-field.yaml + - runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^First name( \\(optional\\))?$" + TEXT_TO_INPUT: "${BILLING_FIRST_NAME}" + - runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^Last name$" + TEXT_TO_INPUT: "${BILLING_LAST_NAME}" + - runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^Address$" + TEXT_TO_INPUT: "${BILLING_ADDRESS_LINE1}" + - runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^City$" + TEXT_TO_INPUT: "${BILLING_CITY}" + - runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^${BILLING_POSTAL_FIELD_LABEL}$" + TEXT_TO_INPUT: "${BILLING_POSTAL_CODE}" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/fill-contact.yaml b/e2e/flows/checkout/fill-contact.yaml index 3e0d6dde2..22516ebbb 100644 --- a/e2e/flows/checkout/fill-contact.yaml +++ b/e2e/flows/checkout/fill-contact.yaml @@ -7,19 +7,3 @@ appId: ${E2E_APP_ID} - runFlow: dismiss-active-field.yaml - extendedWaitUntil: visible: "^${EMAIL}$" -- tapOn: - text: "^First name( \\(optional\\))?$" -- waitForAnimationToEnd -- inputText: "${FIRST_NAME}" -- runFlow: dismiss-active-field.yaml -- scrollUntilVisible: - element: - text: "^Last name$" - direction: DOWN - visibilityPercentage: 100 - centerElement: true -- tapOn: - text: "^Last name$" -- waitForAnimationToEnd -- inputText: "${LAST_NAME}" -- runFlow: dismiss-active-field.yaml diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index 0504c8515..59c34fc68 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -12,12 +12,6 @@ appId: ${E2E_APP_ID} # Checkout groups the digits, so the field never reads back the value the test typed. - extendedWaitUntil: visible: "^${CARD_NUMBER_DISPLAY}$" -- runFlow: dismiss-active-field.yaml -- scrollUntilVisible: - element: - text: "^Expiration date \\(MM / YY\\)$" - direction: DOWN - centerElement: true - tapOn: text: "^Expiration date \\(MM / YY\\)$" - waitForAnimationToEnd @@ -32,10 +26,8 @@ appId: ${E2E_APP_ID} - waitForAnimationToEnd - inputText: "${CARD_SECURITY_CODE}" - runFlow: dismiss-active-field.yaml -- tapOn: - text: "^Name on card$" -- waitForAnimationToEnd -- eraseText -- inputText: "${CARD_HOLDER_NAME}" -- runFlow: dismiss-active-field.yaml +- extendedWaitUntil: + visible: "^${CARD_HOLDER_NAME}$" +- extendedWaitUntil: + visible: "^${CARD_NUMBER_DISPLAY}$" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/fill-shipping-address.yaml b/e2e/flows/checkout/fill-shipping-address.yaml index c911d1055..319d90921 100644 --- a/e2e/flows/checkout/fill-shipping-address.yaml +++ b/e2e/flows/checkout/fill-shipping-address.yaml @@ -40,26 +40,26 @@ appId: ${E2E_APP_ID} - tapOn: text: "^${COUNTRY_LABEL}$" - waitForAnimationToEnd -- scrollUntilVisible: - element: - text: "Address" - direction: DOWN - centerElement: true -- tapOn: - text: "Address" -- waitForAnimationToEnd -- inputText: "${ADDRESS_LINE1}" -- runFlow: dismiss-active-field.yaml -- scrollUntilVisible: - element: - text: "^City$" - direction: DOWN - centerElement: true -- tapOn: - text: "^City$" -- waitForAnimationToEnd -- inputText: "${CITY}" -- runFlow: dismiss-active-field.yaml +- runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^First name( \\(optional\\))?$" + TEXT_TO_INPUT: "${FIRST_NAME}" +- runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^Last name$" + TEXT_TO_INPUT: "${LAST_NAME}" +- runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "Address" + TEXT_TO_INPUT: "${ADDRESS_LINE1}" +- runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^City$" + TEXT_TO_INPUT: "${CITY}" - scrollUntilVisible: element: text: "^${STATE_FIELD_LABEL}$" @@ -68,38 +68,14 @@ appId: ${E2E_APP_ID} - tapOn: text: "^${STATE_FIELD_LABEL}$" - waitForAnimationToEnd -- runFlow: - when: - notVisible: "^${STATE_LABEL}$" - commands: - - scrollUntilVisible: - element: - text: "^${STATE_LABEL}$" - direction: DOWN - centerElement: true - optional: true -- runFlow: - when: - notVisible: "^${STATE_LABEL}$" - commands: - - scrollUntilVisible: - element: - text: "^${STATE_LABEL}$" - direction: UP - centerElement: true - tapOn: text: "^${STATE_LABEL}$" - waitForAnimationToEnd -- scrollUntilVisible: - element: - text: "^${POSTAL_FIELD_LABEL}$" - direction: DOWN - centerElement: true -- tapOn: - text: "^${POSTAL_FIELD_LABEL}$" -- waitForAnimationToEnd -- inputText: "${POSTAL_CODE}" -- runFlow: dismiss-active-field.yaml +- runFlow: + file: fill-text-field.yaml + env: + LOCATOR: "^${POSTAL_FIELD_LABEL}$" + TEXT_TO_INPUT: "${POSTAL_CODE}" # A shop decides whether the delivery address asks for a phone number, and it can make # that field required. The scroll is optional so a shop without the field still passes. diff --git a/e2e/flows/checkout/fill-text-field.yaml b/e2e/flows/checkout/fill-text-field.yaml new file mode 100644 index 000000000..11dd9aec2 --- /dev/null +++ b/e2e/flows/checkout/fill-text-field.yaml @@ -0,0 +1,14 @@ +appId: ${E2E_APP_ID} +--- +- scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: DOWN + centerElement: true +- tapOn: + text: "${LOCATOR}" +- waitForAnimationToEnd +- inputText: "${TEXT_TO_INPUT}" +- runFlow: dismiss-active-field.yaml +- extendedWaitUntil: + visible: "^${TEXT_TO_INPUT}$" diff --git a/e2e/test/checkout_payment_fixture_test.rb b/e2e/test/checkout_payment_fixture_test.rb deleted file mode 100644 index 63f30f751..000000000 --- a/e2e/test/checkout_payment_fixture_test.rb +++ /dev/null @@ -1,31 +0,0 @@ -# frozen_string_literal: true - -require "minitest/autorun" -require "yaml" - -class CheckoutPaymentFixtureTest < Minitest::Test - E2E_ROOT = File.expand_path("..", __dir__) - CHECKOUT_PATH = File.join(E2E_ROOT, "tests", "shared", "checkout-hardcoded-buyer-identity.yaml") - PAYMENT_FLOW_PATH = File.join(E2E_ROOT, "flows", "checkout", "fill-payment-card.yaml") - - def yaml_documents(path) - File.read(path).split(/^---\s*$/).map { |document| YAML.safe_load(document) } - end - - def test_successful_checkout_uses_the_bogus_gateway_approval_number - checkout = yaml_documents(CHECKOUT_PATH).first - - assert_equal "1", checkout.fetch("env").fetch("CARD_NUMBER") - assert_equal "1", checkout.fetch("env").fetch("CARD_NUMBER_DISPLAY") - end - - def test_payment_flow_replaces_the_prefilled_cardholder_name - commands = yaml_documents(PAYMENT_FLOW_PATH).last - name_field_index = commands.index { |command| command.is_a?(Hash) && command.dig("tapOn", "text") == "^Name on card$" } - erase_index = commands.index("eraseText") - cardholder_name_index = commands.index { |command| command == {"inputText" => "${CARD_HOLDER_NAME}"} } - - assert_equal name_field_index + 2, erase_index - assert_equal erase_index + 1, cardholder_name_index - end -end diff --git a/e2e/tests/shared/checkout-guest.yaml b/e2e/tests/shared/checkout-guest.yaml index 7a6e6ca6a..e5e183fdf 100644 --- a/e2e/tests/shared/checkout-guest.yaml +++ b/e2e/tests/shared/checkout-guest.yaml @@ -31,8 +31,8 @@ env: # Checkout payment fixture. The shop runs its credit card gateway in test mode, which # accepts this number only. Checkout groups the digits, so the flow needs both forms. - CARD_NUMBER: "4242424242424242" - CARD_NUMBER_DISPLAY: "4242 4242 4242 4242" + CARD_NUMBER: "1" + CARD_NUMBER_DISPLAY: "1" CARD_SECURITY_CODE: "123" CARD_HOLDER_NAME: "Maestro Shopify" diff --git a/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml b/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml index ffba5d9c1..2120d6612 100644 --- a/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml +++ b/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml @@ -16,7 +16,7 @@ env: CARD_NUMBER: "1" CARD_NUMBER_DISPLAY: "1" CARD_SECURITY_CODE: "123" - CARD_HOLDER_NAME: "Maestro Shopify" + CARD_HOLDER_NAME: "Evelyn Hartley" # Checkout billing fixture. The cart supplies a one-time delivery address, which the # billing section cannot copy, so the test types a billing address of its own. Country From a2bf027e1cd0a9f1637b9f4a1581f9bb02ae2c92 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Wed, 19 Aug 2026 19:45:50 +0100 Subject: [PATCH 03/13] Stabilize checkout field interactions --- e2e/flows/app/hide-keyboard.yaml | 24 +------------------ e2e/flows/checkout/dismiss-active-field.yaml | 6 ----- e2e/flows/checkout/fill-billing-address.yaml | 6 +++++ e2e/flows/checkout/fill-payment-card.yaml | 17 ++++++++++++- e2e/flows/checkout/fill-shipping-address.yaml | 15 ++++++++++++ 5 files changed, 38 insertions(+), 30 deletions(-) diff --git a/e2e/flows/app/hide-keyboard.yaml b/e2e/flows/app/hide-keyboard.yaml index 0360dbd4f..a0b9d67b3 100644 --- a/e2e/flows/app/hide-keyboard.yaml +++ b/e2e/flows/app/hide-keyboard.yaml @@ -10,28 +10,6 @@ appId: ${E2E_APP_ID} - runFlow: when: platform: ios - visible: "Done" commands: - - tapOn: "Done" - - waitForAnimationToEnd -- runFlow: - when: - platform: ios - visible: "selected" - commands: - - tapOn: "selected" - - waitForAnimationToEnd -- runFlow: - when: - platform: ios - visible: "Done" - commands: - - hideKeyboard - - waitForAnimationToEnd -- runFlow: - when: - platform: ios - visible: "selected" - commands: - - hideKeyboard + - tapOn: "^(Done|selected)$" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/dismiss-active-field.yaml b/e2e/flows/checkout/dismiss-active-field.yaml index 1f51ca805..417df4131 100644 --- a/e2e/flows/checkout/dismiss-active-field.yaml +++ b/e2e/flows/checkout/dismiss-active-field.yaml @@ -1,9 +1,3 @@ appId: ${E2E_APP_ID} --- - runFlow: ../app/hide-keyboard.yaml -- runFlow: - when: - visible: "Close suggestions" - commands: - - tapOn: "Close suggestions" - - waitForAnimationToEnd diff --git a/e2e/flows/checkout/fill-billing-address.yaml b/e2e/flows/checkout/fill-billing-address.yaml index b962a21a4..2b64be2ef 100644 --- a/e2e/flows/checkout/fill-billing-address.yaml +++ b/e2e/flows/checkout/fill-billing-address.yaml @@ -30,6 +30,12 @@ appId: ${E2E_APP_ID} env: LOCATOR: "^Address$" TEXT_TO_INPUT: "${BILLING_ADDRESS_LINE1}" + - runFlow: + when: + visible: "Close suggestions" + commands: + - tapOn: "Close suggestions" + - waitForAnimationToEnd - runFlow: file: fill-text-field.yaml env: diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index 59c34fc68..a7d52f87f 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -26,8 +26,23 @@ appId: ${E2E_APP_ID} - waitForAnimationToEnd - inputText: "${CARD_SECURITY_CODE}" - runFlow: dismiss-active-field.yaml +- runFlow: + when: + notVisible: + text: "^${CARD_HOLDER_NAME}$" + below: + text: "^Security code$" + commands: + - tapOn: + text: "^Name on card$" + - waitForAnimationToEnd + - inputText: "${CARD_HOLDER_NAME}" + - runFlow: dismiss-active-field.yaml - extendedWaitUntil: - visible: "^${CARD_HOLDER_NAME}$" + visible: + text: "^${CARD_HOLDER_NAME}$" + below: + text: "^Security code$" - extendedWaitUntil: visible: "^${CARD_NUMBER_DISPLAY}$" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/fill-shipping-address.yaml b/e2e/flows/checkout/fill-shipping-address.yaml index 319d90921..359cf5a9d 100644 --- a/e2e/flows/checkout/fill-shipping-address.yaml +++ b/e2e/flows/checkout/fill-shipping-address.yaml @@ -55,6 +55,12 @@ appId: ${E2E_APP_ID} env: LOCATOR: "Address" TEXT_TO_INPUT: "${ADDRESS_LINE1}" +- runFlow: + when: + visible: "Close suggestions" + commands: + - tapOn: "Close suggestions" + - waitForAnimationToEnd - runFlow: file: fill-text-field.yaml env: @@ -68,6 +74,15 @@ appId: ${E2E_APP_ID} - tapOn: text: "^${STATE_FIELD_LABEL}$" - waitForAnimationToEnd +- runFlow: + when: + notVisible: "^${STATE_LABEL}$" + commands: + - scrollUntilVisible: + element: + text: "^${STATE_LABEL}$" + direction: UP + centerElement: true - tapOn: text: "^${STATE_LABEL}$" - waitForAnimationToEnd From 026f305019860e5a67e1eb149c2b1732a25caf4a Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Wed, 19 Aug 2026 21:21:51 +0100 Subject: [PATCH 04/13] Target hosted payment fields precisely --- e2e/flows/checkout/fill-payment-card.yaml | 49 ++++++++++++++++++++--- 1 file changed, 43 insertions(+), 6 deletions(-) diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index a7d52f87f..290f6ac3e 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -11,18 +11,50 @@ appId: ${E2E_APP_ID} - inputText: "${CARD_NUMBER}" # Checkout groups the digits, so the field never reads back the value the test typed. - extendedWaitUntil: - visible: "^${CARD_NUMBER_DISPLAY}$" -- tapOn: - text: "^Expiration date \\(MM / YY\\)$" + visible: + text: "^${CARD_NUMBER_DISPLAY}$" + below: + text: "^Card number$" + above: + text: "^Expiration date \\(MM / YY\\)$" +- runFlow: + when: + platform: ios + commands: + - tapOn: + text: "^Field container for: Expiration date \\(MM / YY\\)$" +- runFlow: + when: + platform: android + commands: + - tapOn: + text: "^Expiration date \\(MM / YY\\)$" - waitForAnimationToEnd - inputText: "1" - inputText: "2" - waitForAnimationToEnd - inputText: "3" - inputText: "0" +- extendedWaitUntil: + visible: + text: "^12 / 30$" + below: + text: "^Expiration date \\(MM / YY\\)$" + above: + text: "^Security code$" - runFlow: dismiss-active-field.yaml -- tapOn: - text: "^Security code$" +- runFlow: + when: + platform: ios + commands: + - tapOn: + text: "^Field container for: Security code$" +- runFlow: + when: + platform: android + commands: + - tapOn: + text: "^Security code$" - waitForAnimationToEnd - inputText: "${CARD_SECURITY_CODE}" - runFlow: dismiss-active-field.yaml @@ -44,5 +76,10 @@ appId: ${E2E_APP_ID} below: text: "^Security code$" - extendedWaitUntil: - visible: "^${CARD_NUMBER_DISPLAY}$" + visible: + text: "^${CARD_NUMBER_DISPLAY}$" + below: + text: "^Card number$" + above: + text: "^Expiration date \\(MM / YY\\)$" - waitForAnimationToEnd From e971a6b1576fea53c0be9cdd0dc4965bab075e69 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Wed, 19 Aug 2026 21:53:08 +0100 Subject: [PATCH 05/13] Use platform-specific payment targets --- e2e/flows/checkout/fill-payment-card.yaml | 86 +++++++++++++++++------ 1 file changed, 63 insertions(+), 23 deletions(-) diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index 290f6ac3e..793ca463d 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -5,18 +5,38 @@ appId: ${E2E_APP_ID} text: "^Card number$" direction: DOWN centerElement: true -- tapOn: - text: "^Card number$" +- runFlow: + when: + platform: ios + commands: + - tapOn: + text: "^Field container for: Card number$" +- runFlow: + when: + platform: android + commands: + - tapOn: + text: "^Card number$" - waitForAnimationToEnd - inputText: "${CARD_NUMBER}" # Checkout groups the digits, so the field never reads back the value the test typed. -- extendedWaitUntil: - visible: - text: "^${CARD_NUMBER_DISPLAY}$" - below: - text: "^Card number$" - above: - text: "^Expiration date \\(MM / YY\\)$" +- runFlow: + when: + platform: ios + commands: + - extendedWaitUntil: + visible: + text: "^${CARD_NUMBER_DISPLAY}$" + below: + text: "^Card number$" + above: + text: "^Expiration date \\(MM / YY\\)$" +- runFlow: + when: + platform: android + commands: + - extendedWaitUntil: + visible: "^${CARD_NUMBER_DISPLAY}$" - runFlow: when: platform: ios @@ -35,13 +55,23 @@ appId: ${E2E_APP_ID} - waitForAnimationToEnd - inputText: "3" - inputText: "0" -- extendedWaitUntil: - visible: - text: "^12 / 30$" - below: - text: "^Expiration date \\(MM / YY\\)$" - above: - text: "^Security code$" +- runFlow: + when: + platform: ios + commands: + - extendedWaitUntil: + visible: + text: "^12 / 30$" + below: + text: "^Expiration date \\(MM / YY\\)$" + above: + text: "^Security code$" +- runFlow: + when: + platform: android + commands: + - extendedWaitUntil: + visible: "^12 / 30$" - runFlow: dismiss-active-field.yaml - runFlow: when: @@ -75,11 +105,21 @@ appId: ${E2E_APP_ID} text: "^${CARD_HOLDER_NAME}$" below: text: "^Security code$" -- extendedWaitUntil: - visible: - text: "^${CARD_NUMBER_DISPLAY}$" - below: - text: "^Card number$" - above: - text: "^Expiration date \\(MM / YY\\)$" +- runFlow: + when: + platform: ios + commands: + - extendedWaitUntil: + visible: + text: "^${CARD_NUMBER_DISPLAY}$" + below: + text: "^Card number$" + above: + text: "^Expiration date \\(MM / YY\\)$" +- runFlow: + when: + platform: android + commands: + - extendedWaitUntil: + visible: "^${CARD_NUMBER_DISPLAY}$" - waitForAnimationToEnd From f464f2c812fee4a413be03ea0f7ac1b913f7fcf1 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Wed, 19 Aug 2026 22:21:47 +0100 Subject: [PATCH 06/13] Verify card number after keyboard dismissal --- e2e/flows/checkout/fill-payment-card.yaml | 38 +++++++++++++---------- 1 file changed, 21 insertions(+), 17 deletions(-) diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index 793ca463d..14dbe98ec 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -20,23 +20,8 @@ appId: ${E2E_APP_ID} - waitForAnimationToEnd - inputText: "${CARD_NUMBER}" # Checkout groups the digits, so the field never reads back the value the test typed. -- runFlow: - when: - platform: ios - commands: - - extendedWaitUntil: - visible: - text: "^${CARD_NUMBER_DISPLAY}$" - below: - text: "^Card number$" - above: - text: "^Expiration date \\(MM / YY\\)$" -- runFlow: - when: - platform: android - commands: - - extendedWaitUntil: - visible: "^${CARD_NUMBER_DISPLAY}$" +- extendedWaitUntil: + visible: "^${CARD_NUMBER_DISPLAY}$" - runFlow: when: platform: ios @@ -109,6 +94,25 @@ appId: ${E2E_APP_ID} when: platform: ios commands: + - scrollUntilVisible: + element: + text: "^Field container for: Card number$" + direction: UP + centerElement: true + - runFlow: + when: + notVisible: + text: "^${CARD_NUMBER_DISPLAY}$" + below: + text: "^Card number$" + above: + text: "^Expiration date \\(MM / YY\\)$" + commands: + - tapOn: + text: "^Field container for: Card number$" + - waitForAnimationToEnd + - inputText: "${CARD_NUMBER}" + - runFlow: dismiss-active-field.yaml - extendedWaitUntil: visible: text: "^${CARD_NUMBER_DISPLAY}$" From 70a03dd6773dc39f953b1a85f65d888f489c4464 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Wed, 19 Aug 2026 22:57:32 +0100 Subject: [PATCH 07/13] Dismiss iOS keyboard between payment fields --- e2e/flows/checkout/fill-payment-card.yaml | 25 ++++++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index 14dbe98ec..513e58be3 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -20,14 +20,31 @@ appId: ${E2E_APP_ID} - waitForAnimationToEnd - inputText: "${CARD_NUMBER}" # Checkout groups the digits, so the field never reads back the value the test typed. -- extendedWaitUntil: - visible: "^${CARD_NUMBER_DISPLAY}$" - runFlow: when: platform: ios commands: + - runFlow: dismiss-active-field.yaml + - scrollUntilVisible: + element: + text: "^Field container for: Card number$" + direction: UP + centerElement: true + - extendedWaitUntil: + visible: + text: "^${CARD_NUMBER_DISPLAY}$" + below: + text: "^Card number$" + above: + text: "^Expiration date \\(MM / YY\\)$" - tapOn: text: "^Field container for: Expiration date \\(MM / YY\\)$" +- runFlow: + when: + platform: android + commands: + - extendedWaitUntil: + visible: "^${CARD_NUMBER_DISPLAY}$" - runFlow: when: platform: android @@ -44,6 +61,7 @@ appId: ${E2E_APP_ID} when: platform: ios commands: + - runFlow: dismiss-active-field.yaml - extendedWaitUntil: visible: text: "^12 / 30$" @@ -57,7 +75,7 @@ appId: ${E2E_APP_ID} commands: - extendedWaitUntil: visible: "^12 / 30$" -- runFlow: dismiss-active-field.yaml + - runFlow: dismiss-active-field.yaml - runFlow: when: platform: ios @@ -111,6 +129,7 @@ appId: ${E2E_APP_ID} - tapOn: text: "^Field container for: Card number$" - waitForAnimationToEnd + - eraseText - inputText: "${CARD_NUMBER}" - runFlow: dismiss-active-field.yaml - extendedWaitUntil: From 2ab80335b477be9a0098aa5654aa5fd137dcce4c Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Wed, 19 Aug 2026 23:23:48 +0100 Subject: [PATCH 08/13] Retry unfocused iOS card input --- e2e/flows/checkout/fill-payment-card.yaml | 35 +++++++++++++++++++---- 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index 513e58be3..a8795d9f3 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -1,20 +1,25 @@ appId: ${E2E_APP_ID} --- -- scrollUntilVisible: - element: - text: "^Card number$" - direction: DOWN - centerElement: true - runFlow: when: platform: ios commands: + - scrollUntilVisible: + element: + text: "^Field container for: Card number$" + direction: DOWN + centerElement: true - tapOn: text: "^Field container for: Card number$" - runFlow: when: platform: android commands: + - scrollUntilVisible: + element: + text: "^Card number$" + direction: DOWN + centerElement: true - tapOn: text: "^Card number$" - waitForAnimationToEnd @@ -24,12 +29,30 @@ appId: ${E2E_APP_ID} when: platform: ios commands: - - runFlow: dismiss-active-field.yaml + - runFlow: + when: + visible: "^(Done|selected)$" + commands: + - runFlow: dismiss-active-field.yaml - scrollUntilVisible: element: text: "^Field container for: Card number$" direction: UP centerElement: true + - runFlow: + when: + notVisible: + text: "^${CARD_NUMBER_DISPLAY}$" + below: + text: "^Card number$" + above: + text: "^Expiration date \\(MM / YY\\)$" + commands: + - tapOn: + text: "^Field container for: Card number$" + - waitForAnimationToEnd + - inputText: "${CARD_NUMBER}" + - runFlow: dismiss-active-field.yaml - extendedWaitUntil: visible: text: "^${CARD_NUMBER_DISPLAY}$" From c462167340a03a6d46551329d8384b2ed2eff40f Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Thu, 20 Aug 2026 11:35:46 +0100 Subject: [PATCH 09/13] refactor: extract fill-hosted-text-field script --- .../checkout/fill-hosted-text-field.yaml | 77 ++++++++ e2e/flows/checkout/fill-payment-card.yaml | 176 +++--------------- 2 files changed, 103 insertions(+), 150 deletions(-) create mode 100644 e2e/flows/checkout/fill-hosted-text-field.yaml diff --git a/e2e/flows/checkout/fill-hosted-text-field.yaml b/e2e/flows/checkout/fill-hosted-text-field.yaml new file mode 100644 index 000000000..fa7f307ae --- /dev/null +++ b/e2e/flows/checkout/fill-hosted-text-field.yaml @@ -0,0 +1,77 @@ +appId: ${E2E_APP_ID} +--- +- runFlow: + when: + platform: ios + commands: + - scrollUntilVisible: + element: + text: "^Field container for: ${FIELD_LABEL}$" + direction: DOWN + centerElement: true + - assertVisible: + text: "^Field container for: ${FIELD_LABEL}$" + enabled: true + - tapOn: + text: "^Field container for: ${FIELD_LABEL}$" +- runFlow: + when: + platform: android + commands: + - scrollUntilVisible: + element: + id: "^${ANDROID_FIELD_ID}$" + direction: DOWN + centerElement: true + - assertVisible: + id: "^${ANDROID_FIELD_ID}$" + enabled: true + - tapOn: + id: "^${ANDROID_FIELD_ID}$" +- waitForAnimationToEnd +- runFlow: + when: + platform: ios + commands: + - assertVisible: "^(Done|selected)$" +- runFlow: + when: + platform: android + commands: + - assertVisible: + id: "^${ANDROID_FIELD_ID}$" + enabled: true + focused: true +- inputText: "${TEXT_TO_INPUT}" +- runFlow: + when: + true: ${TEXT_TO_INPUT_AFTER_WAIT != ''} + commands: + - waitForAnimationToEnd + - inputText: "${TEXT_TO_INPUT_AFTER_WAIT}" +- runFlow: dismiss-active-field.yaml +- runFlow: + when: + platform: ios + commands: + - scrollUntilVisible: + element: + text: "^Field container for: ${FIELD_LABEL}$" + direction: UP + centerElement: true + - extendedWaitUntil: + visible: + text: "^${EXPECTED_TEXT}$" + below: + text: "^${FIELD_LABEL}$" + above: + text: "^${NEXT_FIELD_LABEL}$" +- runFlow: + when: + platform: android + commands: + - extendedWaitUntil: + visible: + id: "^${ANDROID_FIELD_ID}$" + text: "^${EXPECTED_TEXT}$" + enabled: true diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index a8795d9f3..23140c557 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -1,119 +1,32 @@ appId: ${E2E_APP_ID} --- - runFlow: - when: - platform: ios - commands: - - scrollUntilVisible: - element: - text: "^Field container for: Card number$" - direction: DOWN - centerElement: true - - tapOn: - text: "^Field container for: Card number$" -- runFlow: - when: - platform: android - commands: - - scrollUntilVisible: - element: - text: "^Card number$" - direction: DOWN - centerElement: true - - tapOn: - text: "^Card number$" -- waitForAnimationToEnd -- inputText: "${CARD_NUMBER}" -# Checkout groups the digits, so the field never reads back the value the test typed. -- runFlow: - when: - platform: ios - commands: - - runFlow: - when: - visible: "^(Done|selected)$" - commands: - - runFlow: dismiss-active-field.yaml - - scrollUntilVisible: - element: - text: "^Field container for: Card number$" - direction: UP - centerElement: true - - runFlow: - when: - notVisible: - text: "^${CARD_NUMBER_DISPLAY}$" - below: - text: "^Card number$" - above: - text: "^Expiration date \\(MM / YY\\)$" - commands: - - tapOn: - text: "^Field container for: Card number$" - - waitForAnimationToEnd - - inputText: "${CARD_NUMBER}" - - runFlow: dismiss-active-field.yaml - - extendedWaitUntil: - visible: - text: "^${CARD_NUMBER_DISPLAY}$" - below: - text: "^Card number$" - above: - text: "^Expiration date \\(MM / YY\\)$" - - tapOn: - text: "^Field container for: Expiration date \\(MM / YY\\)$" -- runFlow: - when: - platform: android - commands: - - extendedWaitUntil: - visible: "^${CARD_NUMBER_DISPLAY}$" -- runFlow: - when: - platform: android - commands: - - tapOn: - text: "^Expiration date \\(MM / YY\\)$" -- waitForAnimationToEnd -- inputText: "1" -- inputText: "2" -- waitForAnimationToEnd -- inputText: "3" -- inputText: "0" -- runFlow: - when: - platform: ios - commands: - - runFlow: dismiss-active-field.yaml - - extendedWaitUntil: - visible: - text: "^12 / 30$" - below: - text: "^Expiration date \\(MM / YY\\)$" - above: - text: "^Security code$" -- runFlow: - when: - platform: android - commands: - - extendedWaitUntil: - visible: "^12 / 30$" - - runFlow: dismiss-active-field.yaml -- runFlow: - when: - platform: ios - commands: - - tapOn: - text: "^Field container for: Security code$" -- runFlow: - when: - platform: android - commands: - - tapOn: - text: "^Security code$" -- waitForAnimationToEnd -- inputText: "${CARD_SECURITY_CODE}" -- runFlow: dismiss-active-field.yaml + file: fill-hosted-text-field.yaml + env: + FIELD_LABEL: "Card number" + NEXT_FIELD_LABEL: "Expiration date \\(MM / YY\\)" + ANDROID_FIELD_ID: "number" + TEXT_TO_INPUT: "${CARD_NUMBER}" + TEXT_TO_INPUT_AFTER_WAIT: "" + EXPECTED_TEXT: "${CARD_NUMBER_DISPLAY}" +- runFlow: + file: fill-hosted-text-field.yaml + env: + FIELD_LABEL: "Expiration date \\(MM / YY\\)" + NEXT_FIELD_LABEL: "Security code" + ANDROID_FIELD_ID: "expiry" + TEXT_TO_INPUT: "12" + TEXT_TO_INPUT_AFTER_WAIT: "30" + EXPECTED_TEXT: "12 / 30" +- runFlow: + file: fill-hosted-text-field.yaml + env: + FIELD_LABEL: "Security code" + NEXT_FIELD_LABEL: "Name on card" + ANDROID_FIELD_ID: "verification_value" + TEXT_TO_INPUT: "${CARD_SECURITY_CODE}" + TEXT_TO_INPUT_AFTER_WAIT: "" + EXPECTED_TEXT: "${CARD_SECURITY_CODE}" - runFlow: when: notVisible: @@ -131,41 +44,4 @@ appId: ${E2E_APP_ID} text: "^${CARD_HOLDER_NAME}$" below: text: "^Security code$" -- runFlow: - when: - platform: ios - commands: - - scrollUntilVisible: - element: - text: "^Field container for: Card number$" - direction: UP - centerElement: true - - runFlow: - when: - notVisible: - text: "^${CARD_NUMBER_DISPLAY}$" - below: - text: "^Card number$" - above: - text: "^Expiration date \\(MM / YY\\)$" - commands: - - tapOn: - text: "^Field container for: Card number$" - - waitForAnimationToEnd - - eraseText - - inputText: "${CARD_NUMBER}" - - runFlow: dismiss-active-field.yaml - - extendedWaitUntil: - visible: - text: "^${CARD_NUMBER_DISPLAY}$" - below: - text: "^Card number$" - above: - text: "^Expiration date \\(MM / YY\\)$" -- runFlow: - when: - platform: android - commands: - - extendedWaitUntil: - visible: "^${CARD_NUMBER_DISPLAY}$" - waitForAnimationToEnd From 139c5301957592c8a8a676ddf95cec2df797d121 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Thu, 20 Aug 2026 11:39:40 +0100 Subject: [PATCH 10/13] refactor: move close suggestions into fill-text-field --- e2e/flows/checkout/fill-billing-address.yaml | 7 +------ e2e/flows/checkout/fill-shipping-address.yaml | 7 +------ e2e/flows/checkout/fill-text-field.yaml | 10 ++++++++++ 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/e2e/flows/checkout/fill-billing-address.yaml b/e2e/flows/checkout/fill-billing-address.yaml index 2b64be2ef..9c9659bbe 100644 --- a/e2e/flows/checkout/fill-billing-address.yaml +++ b/e2e/flows/checkout/fill-billing-address.yaml @@ -30,12 +30,7 @@ appId: ${E2E_APP_ID} env: LOCATOR: "^Address$" TEXT_TO_INPUT: "${BILLING_ADDRESS_LINE1}" - - runFlow: - when: - visible: "Close suggestions" - commands: - - tapOn: "Close suggestions" - - waitForAnimationToEnd + DISMISS_SUGGESTIONS: "true" - runFlow: file: fill-text-field.yaml env: diff --git a/e2e/flows/checkout/fill-shipping-address.yaml b/e2e/flows/checkout/fill-shipping-address.yaml index 359cf5a9d..ed8b9546c 100644 --- a/e2e/flows/checkout/fill-shipping-address.yaml +++ b/e2e/flows/checkout/fill-shipping-address.yaml @@ -55,12 +55,7 @@ appId: ${E2E_APP_ID} env: LOCATOR: "Address" TEXT_TO_INPUT: "${ADDRESS_LINE1}" -- runFlow: - when: - visible: "Close suggestions" - commands: - - tapOn: "Close suggestions" - - waitForAnimationToEnd + DISMISS_SUGGESTIONS: "true" - runFlow: file: fill-text-field.yaml env: diff --git a/e2e/flows/checkout/fill-text-field.yaml b/e2e/flows/checkout/fill-text-field.yaml index 11dd9aec2..f61daf5e2 100644 --- a/e2e/flows/checkout/fill-text-field.yaml +++ b/e2e/flows/checkout/fill-text-field.yaml @@ -12,3 +12,13 @@ appId: ${E2E_APP_ID} - runFlow: dismiss-active-field.yaml - extendedWaitUntil: visible: "^${TEXT_TO_INPUT}$" +- runFlow: + when: + true: ${typeof DISMISS_SUGGESTIONS !== 'undefined' && DISMISS_SUGGESTIONS == 'true'} + commands: + - runFlow: + when: + visible: "Close suggestions" + commands: + - tapOn: "Close suggestions" + - waitForAnimationToEnd From 76fcfe3fab447cc2204b48b319fe40341d87d566 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Thu, 20 Aug 2026 11:59:39 +0100 Subject: [PATCH 11/13] refactor: bidirectional scroll --- e2e/flows/checkout/fill-shipping-address.yaml | 34 ++++--------- e2e/flows/checkout/fill-text-field.yaml | 10 ++-- .../scroll-until-visible-bidirectional.yaml | 48 +++++++++++++++++++ 3 files changed, 61 insertions(+), 31 deletions(-) create mode 100644 e2e/flows/checkout/scroll-until-visible-bidirectional.yaml diff --git a/e2e/flows/checkout/fill-shipping-address.yaml b/e2e/flows/checkout/fill-shipping-address.yaml index ed8b9546c..58ea9f09f 100644 --- a/e2e/flows/checkout/fill-shipping-address.yaml +++ b/e2e/flows/checkout/fill-shipping-address.yaml @@ -19,24 +19,10 @@ appId: ${E2E_APP_ID} text: "Country/Region" - waitForAnimationToEnd - runFlow: - when: - notVisible: "^${COUNTRY_LABEL}$" - commands: - - scrollUntilVisible: - element: - text: "^${COUNTRY_LABEL}$" - direction: DOWN - centerElement: true - optional: true - - runFlow: - when: - notVisible: "^${COUNTRY_LABEL}$" - commands: - - scrollUntilVisible: - element: - text: "^${COUNTRY_LABEL}$" - direction: UP - centerElement: true + file: scroll-until-visible-bidirectional.yaml + env: + LOCATOR: "^${COUNTRY_LABEL}$" + INITIAL_DIRECTION: "DOWN" - tapOn: text: "^${COUNTRY_LABEL}$" - waitForAnimationToEnd @@ -70,14 +56,10 @@ appId: ${E2E_APP_ID} text: "^${STATE_FIELD_LABEL}$" - waitForAnimationToEnd - runFlow: - when: - notVisible: "^${STATE_LABEL}$" - commands: - - scrollUntilVisible: - element: - text: "^${STATE_LABEL}$" - direction: UP - centerElement: true + file: scroll-until-visible-bidirectional.yaml + env: + LOCATOR: "^${STATE_LABEL}$" + INITIAL_DIRECTION: "UP" - tapOn: text: "^${STATE_LABEL}$" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/fill-text-field.yaml b/e2e/flows/checkout/fill-text-field.yaml index f61daf5e2..818adf41f 100644 --- a/e2e/flows/checkout/fill-text-field.yaml +++ b/e2e/flows/checkout/fill-text-field.yaml @@ -1,10 +1,10 @@ appId: ${E2E_APP_ID} --- -- scrollUntilVisible: - element: - text: "${LOCATOR}" - direction: DOWN - centerElement: true +- runFlow: + file: scroll-until-visible-bidirectional.yaml + env: + LOCATOR: "${LOCATOR}" + INITIAL_DIRECTION: "DOWN" - tapOn: text: "${LOCATOR}" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/scroll-until-visible-bidirectional.yaml b/e2e/flows/checkout/scroll-until-visible-bidirectional.yaml new file mode 100644 index 000000000..00226580c --- /dev/null +++ b/e2e/flows/checkout/scroll-until-visible-bidirectional.yaml @@ -0,0 +1,48 @@ +appId: ${E2E_APP_ID} +--- +- runFlow: + when: + true: ${INITIAL_DIRECTION == 'UP'} + commands: + - runFlow: + when: + notVisible: "${LOCATOR}" + commands: + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: UP + centerElement: true + optional: true + - runFlow: + when: + notVisible: "${LOCATOR}" + commands: + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: DOWN + centerElement: true +- runFlow: + when: + true: ${INITIAL_DIRECTION == 'DOWN'} + commands: + - runFlow: + when: + notVisible: "${LOCATOR}" + commands: + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: DOWN + centerElement: true + optional: true + - runFlow: + when: + notVisible: "${LOCATOR}" + commands: + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: UP + centerElement: true From 94e987c84e4853ecf95d1d506afb4cd01f991793 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Thu, 20 Aug 2026 13:00:03 +0100 Subject: [PATCH 12/13] Refactor hosted payment field interactions --- .../assert-hosted-text-field-value.yaml | 42 ++++++++++ .../checkout/fill-hosted-text-field.yaml | 81 +++++-------------- e2e/flows/checkout/fill-payment-card.yaml | 70 +++++++++++----- 3 files changed, 113 insertions(+), 80 deletions(-) create mode 100644 e2e/flows/checkout/assert-hosted-text-field-value.yaml diff --git a/e2e/flows/checkout/assert-hosted-text-field-value.yaml b/e2e/flows/checkout/assert-hosted-text-field-value.yaml new file mode 100644 index 000000000..cfe6b5cb5 --- /dev/null +++ b/e2e/flows/checkout/assert-hosted-text-field-value.yaml @@ -0,0 +1,42 @@ +appId: ${E2E_APP_ID} +--- +- runFlow: + when: + platform: ios + commands: + - scrollUntilVisible: + element: + text: "^Field container for: ${FIELD_LABEL}$" + direction: UP + centerElement: true + - runFlow: + when: + true: ${HAS_NEXT_FIELD == 'true'} + commands: + - extendedWaitUntil: + visible: + text: "^${EXPECTED_TEXT}$" + below: + text: "^${FIELD_LABEL}$" + above: + text: "^${NEXT_FIELD_LABEL}$" + - runFlow: + when: + true: ${HAS_NEXT_FIELD == 'false'} + commands: + - extendedWaitUntil: + visible: + text: "^${EXPECTED_TEXT}$" + below: + text: "^${FIELD_LABEL}$" +- runFlow: + when: + platform: android + commands: + - scrollUntilVisible: + element: + text: "^${FIELD_LABEL}$" + direction: UP + centerElement: true + - extendedWaitUntil: + visible: "^${EXPECTED_TEXT}$" diff --git a/e2e/flows/checkout/fill-hosted-text-field.yaml b/e2e/flows/checkout/fill-hosted-text-field.yaml index fa7f307ae..f647b91f7 100644 --- a/e2e/flows/checkout/fill-hosted-text-field.yaml +++ b/e2e/flows/checkout/fill-hosted-text-field.yaml @@ -4,74 +4,31 @@ appId: ${E2E_APP_ID} when: platform: ios commands: - - scrollUntilVisible: - element: - text: "^Field container for: ${FIELD_LABEL}$" - direction: DOWN - centerElement: true - - assertVisible: - text: "^Field container for: ${FIELD_LABEL}$" - enabled: true + - runFlow: + when: + true: ${SCROLL_TO_FIELD == 'true'} + commands: + - scrollUntilVisible: + element: + text: "^Field container for: ${FIELD_LABEL}$" + direction: DOWN + centerElement: true - tapOn: text: "^Field container for: ${FIELD_LABEL}$" - runFlow: when: platform: android commands: - - scrollUntilVisible: - element: - id: "^${ANDROID_FIELD_ID}$" - direction: DOWN - centerElement: true - - assertVisible: - id: "^${ANDROID_FIELD_ID}$" - enabled: true + - runFlow: + when: + true: ${SCROLL_TO_FIELD == 'true'} + commands: + - scrollUntilVisible: + element: + text: "^${FIELD_LABEL}$" + direction: DOWN + centerElement: true - tapOn: - id: "^${ANDROID_FIELD_ID}$" + text: "^${FIELD_LABEL}$" - waitForAnimationToEnd -- runFlow: - when: - platform: ios - commands: - - assertVisible: "^(Done|selected)$" -- runFlow: - when: - platform: android - commands: - - assertVisible: - id: "^${ANDROID_FIELD_ID}$" - enabled: true - focused: true - inputText: "${TEXT_TO_INPUT}" -- runFlow: - when: - true: ${TEXT_TO_INPUT_AFTER_WAIT != ''} - commands: - - waitForAnimationToEnd - - inputText: "${TEXT_TO_INPUT_AFTER_WAIT}" -- runFlow: dismiss-active-field.yaml -- runFlow: - when: - platform: ios - commands: - - scrollUntilVisible: - element: - text: "^Field container for: ${FIELD_LABEL}$" - direction: UP - centerElement: true - - extendedWaitUntil: - visible: - text: "^${EXPECTED_TEXT}$" - below: - text: "^${FIELD_LABEL}$" - above: - text: "^${NEXT_FIELD_LABEL}$" -- runFlow: - when: - platform: android - commands: - - extendedWaitUntil: - visible: - id: "^${ANDROID_FIELD_ID}$" - text: "^${EXPECTED_TEXT}$" - enabled: true diff --git a/e2e/flows/checkout/fill-payment-card.yaml b/e2e/flows/checkout/fill-payment-card.yaml index 23140c557..1018199f3 100644 --- a/e2e/flows/checkout/fill-payment-card.yaml +++ b/e2e/flows/checkout/fill-payment-card.yaml @@ -1,32 +1,41 @@ appId: ${E2E_APP_ID} --- +# Enter Card number and dismiss its keyboard before moving to Expiration. - runFlow: file: fill-hosted-text-field.yaml env: FIELD_LABEL: "Card number" - NEXT_FIELD_LABEL: "Expiration date \\(MM / YY\\)" - ANDROID_FIELD_ID: "number" + SCROLL_TO_FIELD: "true" TEXT_TO_INPUT: "${CARD_NUMBER}" - TEXT_TO_INPUT_AFTER_WAIT: "" - EXPECTED_TEXT: "${CARD_NUMBER_DISPLAY}" +- runFlow: + when: + platform: ios + visible: "^(Done|selected)$" + commands: + - runFlow: dismiss-active-field.yaml + +# Enter expiration in two pairs so Checkout has time to format the separator. - runFlow: file: fill-hosted-text-field.yaml env: FIELD_LABEL: "Expiration date \\(MM / YY\\)" - NEXT_FIELD_LABEL: "Security code" - ANDROID_FIELD_ID: "expiry" - TEXT_TO_INPUT: "12" - TEXT_TO_INPUT_AFTER_WAIT: "30" - EXPECTED_TEXT: "12 / 30" + SCROLL_TO_FIELD: "false" + TEXT_TO_INPUT: "1" +- inputText: "2" +- waitForAnimationToEnd +- inputText: "3" +- inputText: "0" +- runFlow: dismiss-active-field.yaml + - runFlow: file: fill-hosted-text-field.yaml env: FIELD_LABEL: "Security code" - NEXT_FIELD_LABEL: "Name on card" - ANDROID_FIELD_ID: "verification_value" + SCROLL_TO_FIELD: "false" TEXT_TO_INPUT: "${CARD_SECURITY_CODE}" - TEXT_TO_INPUT_AFTER_WAIT: "" - EXPECTED_TEXT: "${CARD_SECURITY_CODE}" +- runFlow: dismiss-active-field.yaml + +# Preserve Checkout's cardholder prefill and only supply the name when it is absent. - runFlow: when: notVisible: @@ -39,9 +48,34 @@ appId: ${E2E_APP_ID} - waitForAnimationToEnd - inputText: "${CARD_HOLDER_NAME}" - runFlow: dismiss-active-field.yaml -- extendedWaitUntil: - visible: - text: "^${CARD_HOLDER_NAME}$" - below: - text: "^Security code$" + +# Validate every hosted field from the bottom up after input is complete. +- runFlow: + file: assert-hosted-text-field-value.yaml + env: + FIELD_LABEL: "Name on card" + NEXT_FIELD_LABEL: "" + HAS_NEXT_FIELD: "false" + EXPECTED_TEXT: "${CARD_HOLDER_NAME}" +- runFlow: + file: assert-hosted-text-field-value.yaml + env: + FIELD_LABEL: "Security code" + NEXT_FIELD_LABEL: "Name on card" + HAS_NEXT_FIELD: "true" + EXPECTED_TEXT: "${CARD_SECURITY_CODE}" +- runFlow: + file: assert-hosted-text-field-value.yaml + env: + FIELD_LABEL: "Expiration date \\(MM / YY\\)" + NEXT_FIELD_LABEL: "Security code" + HAS_NEXT_FIELD: "true" + EXPECTED_TEXT: "12 / 30" +- runFlow: + file: assert-hosted-text-field-value.yaml + env: + FIELD_LABEL: "Card number" + NEXT_FIELD_LABEL: "Expiration date \\(MM / YY\\)" + HAS_NEXT_FIELD: "true" + EXPECTED_TEXT: "${CARD_NUMBER_DISPLAY}" - waitForAnimationToEnd From 54bb03e9797aa444ab9f85696fdac50bfdb91859 Mon Sep 17 00:00:00 2001 From: Kieran Osgood Date: Thu, 20 Aug 2026 17:42:56 +0100 Subject: [PATCH 13/13] Align checkout address and field interactions --- e2e/flows/checkout/fill-billing-address.yaml | 6 +- .../checkout/fill-hosted-text-field.yaml | 25 ++++---- e2e/flows/checkout/fill-text-field.yaml | 10 ++-- .../scroll-until-visible-bidirectional.yaml | 60 +++++++------------ ...e-shipping-address-as-billing-address.yaml | 8 +++ e2e/tests/shared/checkout-guest.yaml | 11 +--- .../checkout-hardcoded-buyer-identity.yaml | 17 +----- .../cart/data/DemoBuyerIdentity.kt | 1 - .../cart/data/DemoBuyerIdentityTest.kt | 4 +- .../sample/src/__tests__/utils.test.ts | 53 ++++++++++++++++ platforms/react-native/sample/src/utils.ts | 33 ++++++---- .../Sources/Api/StorefrontClient.swift | 3 +- .../Api/StorefrontInputFactoryTests.swift | 27 +++++++++ 13 files changed, 159 insertions(+), 99 deletions(-) create mode 100644 e2e/flows/checkout/use-shipping-address-as-billing-address.yaml create mode 100644 platforms/react-native/sample/src/__tests__/utils.test.ts create mode 100644 platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemoTests/Api/StorefrontInputFactoryTests.swift diff --git a/e2e/flows/checkout/fill-billing-address.yaml b/e2e/flows/checkout/fill-billing-address.yaml index 9c9659bbe..ad55de65c 100644 --- a/e2e/flows/checkout/fill-billing-address.yaml +++ b/e2e/flows/checkout/fill-billing-address.yaml @@ -1,9 +1,7 @@ appId: ${E2E_APP_ID} --- -# Checkout offers a "use the shipping address" control only when it can copy the address -# the cart supplied. The samples build their carts through different mutations, so one -# target gets the control and another gets an empty billing form. The scroll below stops -# at whichever the target shows, and the branch fills the form only when there is one. +# Checkout may offer the shipping address or display an open billing form. Stop at +# whichever state the journey presents and fill the form only when it is open. - scrollUntilVisible: element: text: "^(Billing address|Use shipping address as billing address)$" diff --git a/e2e/flows/checkout/fill-hosted-text-field.yaml b/e2e/flows/checkout/fill-hosted-text-field.yaml index f647b91f7..f493f067f 100644 --- a/e2e/flows/checkout/fill-hosted-text-field.yaml +++ b/e2e/flows/checkout/fill-hosted-text-field.yaml @@ -8,11 +8,16 @@ appId: ${E2E_APP_ID} when: true: ${SCROLL_TO_FIELD == 'true'} commands: - - scrollUntilVisible: - element: - text: "^Field container for: ${FIELD_LABEL}$" - direction: DOWN - centerElement: true + - runFlow: + file: scroll-until-visible-bidirectional.yaml + env: + LOCATOR: "^Field container for: ${FIELD_LABEL}$" + INITIAL_DIRECTION: "DOWN" + # The first post-scroll tap positions the hosted container. The second gives + # its iframe input focus before text entry. + - tapOn: + text: "^Field container for: ${FIELD_LABEL}$" + - waitForAnimationToEnd - tapOn: text: "^Field container for: ${FIELD_LABEL}$" - runFlow: @@ -23,11 +28,11 @@ appId: ${E2E_APP_ID} when: true: ${SCROLL_TO_FIELD == 'true'} commands: - - scrollUntilVisible: - element: - text: "^${FIELD_LABEL}$" - direction: DOWN - centerElement: true + - runFlow: + file: scroll-until-visible-bidirectional.yaml + env: + LOCATOR: "^${FIELD_LABEL}$" + INITIAL_DIRECTION: "DOWN" - tapOn: text: "^${FIELD_LABEL}$" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/fill-text-field.yaml b/e2e/flows/checkout/fill-text-field.yaml index 818adf41f..f61daf5e2 100644 --- a/e2e/flows/checkout/fill-text-field.yaml +++ b/e2e/flows/checkout/fill-text-field.yaml @@ -1,10 +1,10 @@ appId: ${E2E_APP_ID} --- -- runFlow: - file: scroll-until-visible-bidirectional.yaml - env: - LOCATOR: "${LOCATOR}" - INITIAL_DIRECTION: "DOWN" +- scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: DOWN + centerElement: true - tapOn: text: "${LOCATOR}" - waitForAnimationToEnd diff --git a/e2e/flows/checkout/scroll-until-visible-bidirectional.yaml b/e2e/flows/checkout/scroll-until-visible-bidirectional.yaml index 00226580c..593cce322 100644 --- a/e2e/flows/checkout/scroll-until-visible-bidirectional.yaml +++ b/e2e/flows/checkout/scroll-until-visible-bidirectional.yaml @@ -4,45 +4,29 @@ appId: ${E2E_APP_ID} when: true: ${INITIAL_DIRECTION == 'UP'} commands: - - runFlow: - when: - notVisible: "${LOCATOR}" - commands: - - scrollUntilVisible: - element: - text: "${LOCATOR}" - direction: UP - centerElement: true - optional: true - - runFlow: - when: - notVisible: "${LOCATOR}" - commands: - - scrollUntilVisible: - element: - text: "${LOCATOR}" - direction: DOWN - centerElement: true + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: UP + centerElement: true + optional: true + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: DOWN + centerElement: true - runFlow: when: true: ${INITIAL_DIRECTION == 'DOWN'} commands: - - runFlow: - when: - notVisible: "${LOCATOR}" - commands: - - scrollUntilVisible: - element: - text: "${LOCATOR}" - direction: DOWN - centerElement: true - optional: true - - runFlow: - when: - notVisible: "${LOCATOR}" - commands: - - scrollUntilVisible: - element: - text: "${LOCATOR}" - direction: UP - centerElement: true + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: DOWN + centerElement: true + optional: true + - scrollUntilVisible: + element: + text: "${LOCATOR}" + direction: UP + centerElement: true diff --git a/e2e/flows/checkout/use-shipping-address-as-billing-address.yaml b/e2e/flows/checkout/use-shipping-address-as-billing-address.yaml new file mode 100644 index 000000000..4ec783365 --- /dev/null +++ b/e2e/flows/checkout/use-shipping-address-as-billing-address.yaml @@ -0,0 +1,8 @@ +appId: ${E2E_APP_ID} +--- +- scrollUntilVisible: + element: + text: "^Use shipping address as billing address$" + direction: DOWN + centerElement: true +- assertVisible: "^Use shipping address as billing address$" diff --git a/e2e/tests/shared/checkout-guest.yaml b/e2e/tests/shared/checkout-guest.yaml index e5e183fdf..82b212d9b 100644 --- a/e2e/tests/shared/checkout-guest.yaml +++ b/e2e/tests/shared/checkout-guest.yaml @@ -36,15 +36,6 @@ env: CARD_SECURITY_CODE: "123" CARD_HOLDER_NAME: "Maestro Shopify" - # Checkout billing fixture. A guest types the delivery address into checkout itself, so - # checkout can copy it and the billing section stays closed. These values cover the - # target that opens the section anyway. - BILLING_FIRST_NAME: "Maestro" - BILLING_LAST_NAME: "Shopify" - BILLING_ADDRESS_LINE1: "700 S Flower St" - BILLING_CITY: "Los Angeles" - BILLING_POSTAL_CODE: "90017" - # Accepted successful checkout states for this smoke test. POST_SUBMIT_RESULT_PATTERN: ".*(Thank you|Your order|Order confirmed|confirmation).*" --- @@ -53,7 +44,7 @@ env: - runFlow: ../../flows/checkout/fill-contact.yaml - runFlow: ../../flows/checkout/fill-shipping-address.yaml - runFlow: ../../flows/checkout/fill-payment-card.yaml -- runFlow: ../../flows/checkout/fill-billing-address.yaml +- runFlow: ../../flows/checkout/use-shipping-address-as-billing-address.yaml - runFlow: ../../flows/checkout/submit.yaml - runFlow: ../../flows/checkout/assert-complete.yaml - runFlow: ../../flows/checkout/assert-returned-to-empty-cart.yaml diff --git a/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml b/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml index 2120d6612..9ad5e9c67 100644 --- a/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml +++ b/e2e/tests/shared/checkout-hardcoded-buyer-identity.yaml @@ -6,9 +6,8 @@ tags: - full env: - # The cart carries the contact and the delivery address, so checkout asks for payment - # only. That makes this the shortest real order, and the first one to fix when checkout - # breaks. + # The cart carries the contact and a selected reusable delivery address, so checkout + # uses that address for billing and asks for payment only. E2E_CART_PARAMS: "productIndex=0&quantity=1&buyerIdentityMode=hardcoded" # Checkout payment fixture. The shop runs the bogus gateway, which rejects real card @@ -18,23 +17,13 @@ env: CARD_SECURITY_CODE: "123" CARD_HOLDER_NAME: "Evelyn Hartley" - # Checkout billing fixture. The cart supplies a one-time delivery address, which the - # billing section cannot copy, so the test types a billing address of its own. Country - # and province keep the shop defaults, so this address stays in Canada. - BILLING_FIRST_NAME: "Maestro" - BILLING_LAST_NAME: "Shopify" - BILLING_ADDRESS_LINE1: "620 King Street West" - BILLING_CITY: "Toronto" - BILLING_POSTAL_FIELD_LABEL: "Postal code" - BILLING_POSTAL_CODE: "M5V 1M7" - # Accepted successful checkout states for this smoke test. POST_SUBMIT_RESULT_PATTERN: ".*(Thank you|Your order|Order confirmed|confirmation).*" --- - runFlow: ../../flows/app/bootstrap-cart-from-link.yaml - runFlow: ../../flows/checkout/present.yaml - runFlow: ../../flows/checkout/fill-payment-card.yaml -- runFlow: ../../flows/checkout/fill-billing-address.yaml +- runFlow: ../../flows/checkout/use-shipping-address-as-billing-address.yaml - runFlow: ../../flows/checkout/submit.yaml - runFlow: ../../flows/checkout/assert-complete.yaml - runFlow: ../../flows/checkout/assert-returned-to-empty-cart.yaml diff --git a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentity.kt b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentity.kt index 044be913e..b5860ecbe 100644 --- a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentity.kt +++ b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/main/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentity.kt @@ -39,7 +39,6 @@ object DemoBuyerIdentity { ) ), selected = Optional.present(true), - oneTimeUse = Optional.present(true), ) ) ) diff --git a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/test/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentityTest.kt b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/test/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentityTest.kt index df60dcfc8..af2a7ec80 100644 --- a/platforms/android/samples/CheckoutKitAndroidDemo/app/src/test/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentityTest.kt +++ b/platforms/android/samples/CheckoutKitAndroidDemo/app/src/test/java/com/shopify/checkoutkit/androiddemo/cart/data/DemoBuyerIdentityTest.kt @@ -17,12 +17,12 @@ class DemoBuyerIdentityTest { } @Test - fun `delivery carries one pre-selected one-time address`() { + fun `delivery carries one pre-selected reusable address`() { val addresses = DemoBuyerIdentity.delivery.addresses.getOrThrow()!! assertThat(addresses).hasSize(1) assertThat(addresses.first().selected.getOrThrow()).isTrue() - assertThat(addresses.first().oneTimeUse.getOrThrow()).isTrue() + assertThat(addresses.first().oneTimeUse.getOrNull()).isNull() } @Test diff --git a/platforms/react-native/sample/src/__tests__/utils.test.ts b/platforms/react-native/sample/src/__tests__/utils.test.ts new file mode 100644 index 000000000..b8fd6c759 --- /dev/null +++ b/platforms/react-native/sample/src/__tests__/utils.test.ts @@ -0,0 +1,53 @@ +import Config from 'react-native-config'; +import {BuyerIdentityMode} from '../auth/types'; +import type {AppConfig} from '../context/Config'; +import {createBuyerIdentityCartInput} from '../utils'; + +jest.mock('react-native-config', () => ({ + EMAIL: 'buyer@example.com', + ADDRESS_1: '151 O Connor Street', + ADDRESS_2: '', + CITY: 'Ottawa', + COMPANY: '', + COUNTRY: 'CA', + FIRST_NAME: 'Evelyn', + LAST_NAME: 'Hartley', + PROVINCE: 'ON', + ZIP: 'K2P 2L8', + PHONE: '+16135550142', +})); + +describe('createBuyerIdentityCartInput', () => { + it('adds a selected reusable delivery address to a hardcoded cart', () => { + const input = createBuyerIdentityCartInput({ + buyerIdentityMode: BuyerIdentityMode.Hardcoded, + } as AppConfig); + + expect(input).toEqual({ + buyerIdentity: { + email: Config.EMAIL, + }, + delivery: { + addresses: [ + { + address: { + deliveryAddress: { + address1: Config.ADDRESS_1, + address2: Config.ADDRESS_2, + city: Config.CITY, + company: Config.COMPANY, + countryCode: Config.COUNTRY, + firstName: Config.FIRST_NAME, + lastName: Config.LAST_NAME, + phone: Config.PHONE, + provinceCode: Config.PROVINCE, + zip: Config.ZIP, + }, + }, + selected: true, + }, + ], + }, + }); + }); +}); diff --git a/platforms/react-native/sample/src/utils.ts b/platforms/react-native/sample/src/utils.ts index d061a08be..f34b83b6e 100644 --- a/platforms/react-native/sample/src/utils.ts +++ b/platforms/react-native/sample/src/utils.ts @@ -27,20 +27,27 @@ export function createBuyerIdentityCartInput( return { buyerIdentity: { email: EMAIL, - deliveryAddressPreferences: { - deliveryAddress: { - address1: ADDRESS_1, - address2: ADDRESS_2, - city: CITY, - company: COMPANY, - country: COUNTRY, - firstName: FIRST_NAME, - lastName: LAST_NAME, - phone: PHONE, - province: PROVINCE, - zip: ZIP, + }, + delivery: { + addresses: [ + { + address: { + deliveryAddress: { + address1: ADDRESS_1, + address2: ADDRESS_2, + city: CITY, + company: COMPANY, + countryCode: COUNTRY, + firstName: FIRST_NAME, + lastName: LAST_NAME, + phone: PHONE, + provinceCode: PROVINCE, + zip: ZIP, + }, + }, + selected: true, }, - }, + ], }, }; case BuyerIdentityMode.CustomerAccount: diff --git a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Api/StorefrontClient.swift b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Api/StorefrontClient.swift index 8f712e0d8..191da463f 100644 --- a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Api/StorefrontClient.swift +++ b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemo/Sources/Api/StorefrontClient.swift @@ -90,8 +90,7 @@ class StorefrontInputFactory { address: Storefront.CartAddressInput( deliveryAddress: .some(deliveryAddress) ), - selected: .some(true), - oneTimeUse: .some(true) + selected: .some(true) ) ]) ) diff --git a/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemoTests/Api/StorefrontInputFactoryTests.swift b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemoTests/Api/StorefrontInputFactoryTests.swift new file mode 100644 index 000000000..c8906d1f3 --- /dev/null +++ b/platforms/swift/Samples/CheckoutKitSwiftDemo/CheckoutKitSwiftDemoTests/Api/StorefrontInputFactoryTests.swift @@ -0,0 +1,27 @@ +@testable import CheckoutKitSwiftDemo +import XCTest + +@MainActor +final class StorefrontInputFactoryTests: XCTestCase { + func testHardcodedCartUsesASelectedReusableDeliveryAddress() { + let originalBuyerIdentityMode = appConfiguration.buyerIdentityMode + defer { appConfiguration.buyerIdentityMode = originalBuyerIdentityMode } + appConfiguration.buyerIdentityMode = .hardcoded + + let input = StorefrontInputFactory.shared.createCartInput() + + guard case let .some(delivery) = input.delivery, + case let .some(addresses) = delivery.addresses, + let address = addresses.first + else { + return XCTFail("Expected a delivery address") + } + XCTAssertEqual(addresses.count, 1) + guard case .some(true) = address.selected else { + return XCTFail("Expected the delivery address to be selected") + } + guard case .none = address.oneTimeUse else { + return XCTFail("Expected one-time use to be omitted") + } + } +}