diff --git a/e2e/config/matrix.yml b/e2e/config/matrix.yml index a551be192..4053c22a4 100644 --- a/e2e/config/matrix.yml +++ b/e2e/config/matrix.yml @@ -7,9 +7,14 @@ tags: include: - launch - cart + - checkout exclude: - flaky - wip + # The remaining `full` tests place a real order and live under tests/react-native/, + # so they cannot run on the Swift and Kotlin rows. Drop this entry when the shared + # ordering tests replace them. + - full applications: - id: react-native-ios target: react-native diff --git a/e2e/flows/checkout/assert-dismissed.yaml b/e2e/flows/checkout/assert-dismissed.yaml new file mode 100644 index 000000000..77ad554a5 --- /dev/null +++ b/e2e/flows/checkout/assert-dismissed.yaml @@ -0,0 +1,14 @@ +appId: ${E2E_APP_ID} +--- +# The close control is the one element that exists only while checkout is on screen, so its +# absence proves dismissal. Asserting on checkout copy instead would be wrong: the React +# Native catalog sells a product called "Email", which the checkout field pattern matches. +# close.yaml lists why the identifier differs per SDK version. Only one of the three is ever +# present, so all three must be absent once the sheet is gone. +- extendedWaitUntil: + notVisible: + id: "shopify_checkout_kit_close_button" + timeout: 30000 +- assertNotVisible: + id: "checkoutKitCloseBtn" +- assertNotVisible: "Close" diff --git a/e2e/flows/checkout/assert-returned-to-empty-cart.yaml b/e2e/flows/checkout/assert-returned-to-empty-cart.yaml index ba7f925ed..34ef280a4 100644 --- a/e2e/flows/checkout/assert-returned-to-empty-cart.yaml +++ b/e2e/flows/checkout/assert-returned-to-empty-cart.yaml @@ -1,20 +1,6 @@ appId: ${E2E_APP_ID} --- -# RN E2E runs against released native SDK artifacts. Android 4.0.0-alpha.2 -# still exposes checkoutKitCloseBtn; #333 normalized the source id to -# shopify_checkout_kit_close_button. When RN nativeSdkVersions point at native -# SDK releases containing #333, replace this platform split with that id. -- runFlow: - when: - platform: android - commands: - - tapOn: - id: checkoutKitCloseBtn -- runFlow: - when: - platform: ios - commands: - - tapOn: "Close" +- runFlow: close.yaml - extendedWaitUntil: visible: diff --git a/e2e/flows/checkout/close.yaml b/e2e/flows/checkout/close.yaml new file mode 100644 index 000000000..8bd61d89f --- /dev/null +++ b/e2e/flows/checkout/close.yaml @@ -0,0 +1,27 @@ +appId: ${E2E_APP_ID} +--- +# The close control carries a different identifier per SDK version, not per platform. +# The Swift and Kotlin samples build the in-repo SDK, which uses the normalized id from +# #333. React Native resolves released 4.0.0-alpha.2 artifacts, where Android still +# exposes checkoutKitCloseBtn and iOS exposes only the "Close" label. Delete the two +# fallback branches once nativeSdkVersions point at releases containing #333. +- runFlow: + when: + visible: + id: "shopify_checkout_kit_close_button" + commands: + - tapOn: + id: "shopify_checkout_kit_close_button" +- runFlow: + when: + visible: + id: "checkoutKitCloseBtn" + commands: + - tapOn: + id: "checkoutKitCloseBtn" +- runFlow: + when: + visible: "Close" + commands: + - tapOn: "Close" +- waitForAnimationToEnd diff --git a/e2e/scripts/run_maestro b/e2e/scripts/run_maestro index 93a138ee0..b3d19513c 100755 --- a/e2e/scripts/run_maestro +++ b/e2e/scripts/run_maestro @@ -6,6 +6,9 @@ # # Every platform runner calls this script, so the environment contract has exactly one # definition. Maestro exits non-zero when a tag or the flows glob matches nothing. +# +# Set E2E_DEVICE_ID when the caller already knows which device holds the installed app. +# Maestro otherwise picks a device itself, and it can pick one the app is missing from. set -euo pipefail PLATFORM="${1:?platform (ios|android) is required}" @@ -13,6 +16,7 @@ APP_ID="${2:?app id is required}" READY_MARKER="${3:?ready marker is required}" INCLUDE_TAGS="${4:-}" EXCLUDE_TAGS="${5:-}" +DEVICE_ID="${E2E_DEVICE_ID:-}" E2E_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" @@ -26,7 +30,13 @@ MAESTRO="$("$E2E_ROOT/scripts/maestro_bin")" # than passed. e2e/lib/e2e_matrix_to_browserstack_run_plan.rb derives it the same way for CI. CONTROL_LINK="${APP_ID}://e2e" -MAESTRO_ARGS=(--platform "$PLATFORM" test --config config.yaml) +MAESTRO_ARGS=(--platform "$PLATFORM") + +if [ -n "$DEVICE_ID" ]; then + MAESTRO_ARGS+=(--device "$DEVICE_ID") +fi + +MAESTRO_ARGS+=(test --config config.yaml) if [ -n "$INCLUDE_TAGS" ]; then MAESTRO_ARGS+=(--include-tags "$INCLUDE_TAGS") diff --git a/e2e/test/e2e_matrix_to_browserstack_run_plan_test.rb b/e2e/test/e2e_matrix_to_browserstack_run_plan_test.rb index 5b67a9d62..b37a8c13b 100644 --- a/e2e/test/e2e_matrix_to_browserstack_run_plan_test.rb +++ b/e2e/test/e2e_matrix_to_browserstack_run_plan_test.rb @@ -41,8 +41,8 @@ def test_a_run_executes_the_workspace_root_so_the_config_glob_resolves def test_a_run_carries_the_default_tags run = run_for("swift-ios") - assert_equal ["launch", "cart"], run.fetch("include_tags") - assert_equal ["flaky", "wip"], run.fetch("exclude_tags") + assert_equal ["launch", "cart", "checkout"], run.fetch("include_tags") + assert_equal ["flaky", "wip", "full"], run.fetch("exclude_tags") end def test_an_application_overrides_the_default_tags diff --git a/e2e/tests/react-native/checkout-hardcoded-buyer-identity.yaml b/e2e/tests/react-native/checkout-hardcoded-buyer-identity.yaml index 386d21710..47215670e 100644 --- a/e2e/tests/react-native/checkout-hardcoded-buyer-identity.yaml +++ b/e2e/tests/react-native/checkout-hardcoded-buyer-identity.yaml @@ -2,7 +2,8 @@ appId: ${E2E_APP_ID} name: React Native checkout - hardcoded buyer identity tags: - checkout - - smoke + # This test submits a real order, so it belongs to the expensive tier. + - full env: # Sample app buyer identity configuration diff --git a/e2e/tests/shared/checkout-present-and-close.yaml b/e2e/tests/shared/checkout-present-and-close.yaml new file mode 100644 index 000000000..9ce835c1e --- /dev/null +++ b/e2e/tests/shared/checkout-present-and-close.yaml @@ -0,0 +1,16 @@ +appId: ${E2E_APP_ID} +name: Present checkout and close it +tags: + - checkout + - smoke + +env: + # No buyerIdentityMode: this test asserts presentation and dismissal, not identity. + E2E_CART_PARAMS: "productIndex=0&quantity=1" +--- +- runFlow: ../../flows/app/bootstrap-cart-from-link.yaml +- runFlow: ../../flows/checkout/present.yaml +- runFlow: ../../flows/checkout/close.yaml +# The four targets return to different screens, so this test asserts dismissal only. +# The tests that place an order assert what the cart holds afterwards. +- runFlow: ../../flows/checkout/assert-dismissed.yaml diff --git a/platforms/android/scripts/e2e_maestro b/platforms/android/scripts/e2e_maestro index 2ffa70a2c..7a27701f9 100755 --- a/platforms/android/scripts/e2e_maestro +++ b/platforms/android/scripts/e2e_maestro @@ -71,17 +71,22 @@ require_maestro() { fi } -require_emulator() { - if ! adb devices | grep -q "[[:space:]]device$"; then - echo "No running Android device or emulator found." >&2 - echo "Start an emulator first, because Maestro drives the device the app runs on." >&2 - return 1 - fi +attached_device() { + adb devices | awk '$2 == "device" { print $1; exit }' } require_maestro -require_emulator + +DEVICE_ID="$(attached_device || true)" +if [ -z "$DEVICE_ID" ]; then + echo "No running Android device or emulator found." >&2 + echo "Start an emulator first, because Maestro drives the device the app runs on." >&2 + exit 1 +fi + +echo "Using device: $DEVICE_ID" "$SAMPLE_DIR/gradlew" -p "$SAMPLE_DIR" :app:installDebug --console=plain -"$REPO_ROOT/e2e/scripts/run_maestro" android "$APP_ID" "$READY_MARKER" "$INCLUDE_TAGS" "$EXCLUDE_TAGS" +E2E_DEVICE_ID="$DEVICE_ID" \ + "$REPO_ROOT/e2e/scripts/run_maestro" android "$APP_ID" "$READY_MARKER" "$INCLUDE_TAGS" "$EXCLUDE_TAGS" diff --git a/platforms/swift/Scripts/e2e_maestro b/platforms/swift/Scripts/e2e_maestro index 0ee23c493..456de3cbb 100755 --- a/platforms/swift/Scripts/e2e_maestro +++ b/platforms/swift/Scripts/e2e_maestro @@ -113,4 +113,5 @@ fi xcrun simctl install "$SIMULATOR_ID" "$APP_PATH" -"$REPO_ROOT/e2e/scripts/run_maestro" ios "$APP_ID" "$READY_MARKER" "$INCLUDE_TAGS" "$EXCLUDE_TAGS" +E2E_DEVICE_ID="$SIMULATOR_ID" \ + "$REPO_ROOT/e2e/scripts/run_maestro" ios "$APP_ID" "$READY_MARKER" "$INCLUDE_TAGS" "$EXCLUDE_TAGS"