From f345b96352aafaeb163eefe3a77576e0d3250fa8 Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:47:13 -0300 Subject: [PATCH 01/13] feat(maestro): add shared WalletConnect Pay E2E test actions Add three reusable composite actions for Maestro Pay E2E testing across wallet platforms (React Native, Kotlin, Swift, Flutter): - maestro/setup: Install Maestro CLI - maestro/pay-tests: Copy 11 test flows + 3 sub-flows + 2 scripts - maestro/run: Execute tests with secret passthrough + artifact upload Includes README documenting all 28 required testIDs, 6 merchant secrets, test input field requirement with RN reference implementation, test catalog, and iOS/Android usage examples. Co-Authored-By: Claude Opus 4.6 --- .../flows/pay_confirm_and_verify.yaml | 21 ++ .../flows/pay_open_and_paste_url.yaml | 30 +++ .../.maestro/flows/pay_open_via_deeplink.yaml | 7 + .../.maestro/pay_cancel_from_kyc.yaml | 85 ++++++ .../.maestro/pay_cancel_from_review.yaml | 61 +++++ maestro/pay-tests/.maestro/pay_cancelled.yaml | 33 +++ .../pay-tests/.maestro/pay_double_scan.yaml | 81 ++++++ .../pay-tests/.maestro/pay_expired_link.yaml | 33 +++ .../.maestro/pay_insufficient_funds.yaml | 38 +++ .../.maestro/pay_kyc_back_navigation.yaml | 66 +++++ .../.maestro/pay_multiple_options_kyc.yaml | 101 ++++++++ .../.maestro/pay_multiple_options_nokyc.yaml | 76 ++++++ .../.maestro/pay_single_option_nokyc.yaml | 47 ++++ .../pay_single_option_nokyc_deeplink.yaml | 41 +++ .../.maestro/scripts/cancel-payment.js | 21 ++ .../.maestro/scripts/create-payment.js | 29 +++ maestro/pay-tests/README.md | 243 ++++++++++++++++++ maestro/pay-tests/action.yml | 39 +++ maestro/run/action.yml | 69 +++++ maestro/setup/action.yml | 26 ++ 20 files changed, 1147 insertions(+) create mode 100644 maestro/pay-tests/.maestro/flows/pay_confirm_and_verify.yaml create mode 100644 maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml create mode 100644 maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml create mode 100644 maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml create mode 100644 maestro/pay-tests/.maestro/pay_cancel_from_review.yaml create mode 100644 maestro/pay-tests/.maestro/pay_cancelled.yaml create mode 100644 maestro/pay-tests/.maestro/pay_double_scan.yaml create mode 100644 maestro/pay-tests/.maestro/pay_expired_link.yaml create mode 100644 maestro/pay-tests/.maestro/pay_insufficient_funds.yaml create mode 100644 maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml create mode 100644 maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml create mode 100644 maestro/pay-tests/.maestro/pay_multiple_options_nokyc.yaml create mode 100644 maestro/pay-tests/.maestro/pay_single_option_nokyc.yaml create mode 100644 maestro/pay-tests/.maestro/pay_single_option_nokyc_deeplink.yaml create mode 100644 maestro/pay-tests/.maestro/scripts/cancel-payment.js create mode 100644 maestro/pay-tests/.maestro/scripts/create-payment.js create mode 100644 maestro/pay-tests/README.md create mode 100644 maestro/pay-tests/action.yml create mode 100644 maestro/run/action.yml create mode 100644 maestro/setup/action.yml diff --git a/maestro/pay-tests/.maestro/flows/pay_confirm_and_verify.yaml b/maestro/pay-tests/.maestro/flows/pay_confirm_and_verify.yaml new file mode 100644 index 0000000..92e9adf --- /dev/null +++ b/maestro/pay-tests/.maestro/flows/pay_confirm_and_verify.yaml @@ -0,0 +1,21 @@ +appId: ${APP_ID} +--- +# Shared flow: Tap Pay, verify loading, wait for success, tap "Got it!" + +# On review screen, tap Pay button +- extendedWaitUntil: + visible: + id: "pay-button-pay" + timeout: 10000 +- tapOn: + id: "pay-button-pay" + +# Wait for success screen (generous timeout for on-chain confirmation) +- extendedWaitUntil: + visible: + id: "pay-result-success-icon" + timeout: 30000 + +# Tap "Got it!" button +- tapOn: + id: "pay-button-result-action-success" diff --git a/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml b/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml new file mode 100644 index 0000000..a218a8e --- /dev/null +++ b/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml @@ -0,0 +1,30 @@ +appId: ${APP_ID} +--- +# Shared flow: Launch wallet, open scanner, paste payment URL, wait for merchant info +# Requires: ${output.gateway_url} to be set by a prior runScript step + +# Launch wallet app +- launchApp: + appId: ${APP_ID} + permissions: + all: allow + +# Tap scan button to open scanner options modal +- tapOn: + id: "button-scan" + +# Type the payment URL into the test input field +- tapOn: + id: "input-paste-url" + +# Dismiss iOS keyboard language prompt if it appears +- runFlow: + when: + visible: "Continue" + commands: + - tapOn: "Continue" + +- inputText: ${output.gateway_url} +- pressKey: Enter +- tapOn: + id: "button-submit-url" diff --git a/maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml b/maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml new file mode 100644 index 0000000..27e5887 --- /dev/null +++ b/maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml @@ -0,0 +1,7 @@ +appId: ${APP_ID} +--- +# Shared flow: Open payment URL via deep link +# Requires: ${output.gateway_url} to be set by a prior runScript step + +# Open the payment URL as a deep link +- openLink: ${output.gateway_url} diff --git a/maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml b/maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml new file mode 100644 index 0000000..664b581 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml @@ -0,0 +1,85 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Cancel from KYC Webview +tags: + - pay +--- +# Create payment via API (multi-option, KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_MULTI_KYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_MULTI_KYC} + +- startRecording: "WalletConnect Pay Cancel from KYC" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Verify first option is pre-selected +- assertVisible: + id: "pay-option-0-selected" + +# Tap Continue to go to collectData webview +- tapOn: + id: "pay-button-continue" + +# Wait for KYC webview to load +- extendedWaitUntil: + visible: "Add your personal details" + timeout: 10000 + +# Cancel the payment server-side while in KYC webview +- runScript: + file: scripts/cancel-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_MULTI_KYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_MULTI_KYC} + PAYMENT_ID: ${output.payment_id} + +# Complete KYC form (data is autocompleted, just tap Add) +- tapOn: "Add" + +# Retry tap on "Add" if "Confirm your details" doesn't appear (webview can be slow) +- runFlow: + when: + notVisible: "Confirm your details" + commands: + - tapOn: "Add" + +# Confirm your details popup +- extendedWaitUntil: + visible: "Confirm your details" + timeout: 10000 + +# Tap the checkbox / label for terms agreement +- tapOn: + text: "I agree to the Terms and Conditions and Privacy Policy" + retryTapIfNoChange: true +- tapOn: "Confirm" + +# Wait for result screen +- extendedWaitUntil: + visible: + id: "pay-result-container" + timeout: 30000 + +# Verify cancelled icon +- assertVisible: + id: "pay-result-cancelled-icon" + +# Verify action button +- assertVisible: + id: "pay-button-result-action-cancelled" + +# Dismiss +- tapOn: + id: "pay-button-result-action-cancelled" + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_cancel_from_review.yaml b/maestro/pay-tests/.maestro/pay_cancel_from_review.yaml new file mode 100644 index 0000000..970a852 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_cancel_from_review.yaml @@ -0,0 +1,61 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Cancel from Review Screen +tags: + - pay +--- +# Create payment via API (single-option, no-KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + +- startRecording: "WalletConnect Pay Cancel from Review" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Single option auto-selects — verify review screen +- extendedWaitUntil: + visible: + id: "pay-button-pay" + timeout: 10000 + +# Cancel the payment server-side while on review screen +- runScript: + file: scripts/cancel-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + PAYMENT_ID: ${output.payment_id} + +# Tap Pay — payment was cancelled, should show cancelled result +- tapOn: + id: "pay-button-pay" + +# Wait for result screen +- extendedWaitUntil: + visible: + id: "pay-result-container" + timeout: 30000 + +# Verify cancelled icon +- assertVisible: + id: "pay-result-cancelled-icon" + +# Verify action button +- assertVisible: + id: "pay-button-result-action-cancelled" + +# Dismiss +- tapOn: + id: "pay-button-result-action-cancelled" + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_cancelled.yaml b/maestro/pay-tests/.maestro/pay_cancelled.yaml new file mode 100644 index 0000000..43bd2b8 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_cancelled.yaml @@ -0,0 +1,33 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Cancelled Payment +tags: + - pay +--- +# Use a hardcoded cancelled payment URL (no API call needed) +- evalScript: ${output.gateway_url = 'https://pay.walletconnect.com/?pid=pay_24a2ecc101KNHMZ1B8GJMR16VXAZ2EXXMN'} + +- startRecording: "WalletConnect Pay Cancelled Payment" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Payment is cancelled — app shows error result screen directly +- extendedWaitUntil: + visible: + id: "pay-result-container" + timeout: 15000 + +# Verify cancelled icon is shown +- assertVisible: + id: "pay-result-cancelled-icon" + +# Verify result action button is visible +- assertVisible: + id: "pay-button-result-action-cancelled" + +# Dismiss the error dialog +- tapOn: + id: "pay-button-result-action-cancelled" + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_double_scan.yaml b/maestro/pay-tests/.maestro/pay_double_scan.yaml new file mode 100644 index 0000000..c25efb6 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_double_scan.yaml @@ -0,0 +1,81 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Double Scan Same Payment +tags: + - pay +--- +# Create payment via API (single-option, no-KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + +- startRecording: "WalletConnect Pay Double Scan" + +# === First scan: complete the payment normally === + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Single option auto-selects — verify pay button +- extendedWaitUntil: + visible: + id: "pay-button-pay" + timeout: 10000 +- copyTextFrom: + id: "pay-button-pay" +- assertTrue: + condition: "${maestro.copiedText == 'Pay $0.01'}" + +# Tap Pay, verify success, tap "Got it!" +- runFlow: + file: flows/pay_confirm_and_verify.yaml + +# === Second scan: re-open the same gateway URL === + +# Tap scan button to open scanner +- tapOn: + id: "button-scan" + +# Type the same payment URL +- tapOn: + id: "input-paste-url" + +# Dismiss iOS keyboard language prompt if it appears +- runFlow: + when: + visible: "Continue" + commands: + - tapOn: "Continue" + +- inputText: ${output.gateway_url} +- pressKey: Enter +- tapOn: + id: "button-submit-url" + +# Wait for error result screen (payment already completed) +- extendedWaitUntil: + visible: + id: "pay-result-container" + timeout: 20000 + +# Verify generic error icon is shown (not success) +- assertVisible: + id: "pay-result-error-icon" + +# Verify result action button is visible +- assertVisible: + id: "pay-button-result-action-generic" + +# Dismiss the error dialog +- tapOn: + id: "pay-button-result-action-generic" + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_expired_link.yaml b/maestro/pay-tests/.maestro/pay_expired_link.yaml new file mode 100644 index 0000000..719475e --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_expired_link.yaml @@ -0,0 +1,33 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Expired Link +tags: + - pay +--- +# Use a hardcoded expired payment URL (no API call needed) +- evalScript: ${output.gateway_url = 'https://pay.walletconnect.com/?pid=pay_b8a2ecc101KNHRNWXD2VF8SGZDS7WK19ZA'} + +- startRecording: "WalletConnect Pay Expired Link" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Payment is expired — app shows error result screen directly +- extendedWaitUntil: + visible: + id: "pay-result-container" + timeout: 15000 + +# Verify expired icon is shown +- assertVisible: + id: "pay-result-expired-icon" + +# Verify result action button is visible +- assertVisible: + id: "pay-button-result-action-expired" + +# Dismiss the error dialog +- tapOn: + id: "pay-button-result-action-expired" + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_insufficient_funds.yaml b/maestro/pay-tests/.maestro/pay_insufficient_funds.yaml new file mode 100644 index 0000000..f71a25c --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_insufficient_funds.yaml @@ -0,0 +1,38 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Insufficient Funds +tags: + - pay +--- +# Create payment with amount exceeding wallet balance ($9.99) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + WPAY_AMOUNT: "999" + +- startRecording: "WalletConnect Pay Insufficient Funds" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# No options available — app jumps directly to error result screen +- extendedWaitUntil: + visible: + id: "pay-result-container" + timeout: 15000 + +# Verify insufficient funds icon is shown +- assertVisible: + id: "pay-result-insufficient-funds-icon" + +# Verify result action button is visible +- assertVisible: + id: "pay-button-result-action-insufficient_funds" + +# Dismiss the error dialog +- tapOn: + id: "pay-button-result-action-insufficient_funds" + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml b/maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml new file mode 100644 index 0000000..5691f78 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml @@ -0,0 +1,66 @@ +appId: ${APP_ID} +name: WalletConnect Pay - KYC Header Buttons +tags: + - pay +--- +# Create payment via API (multi-option, KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_MULTI_KYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_MULTI_KYC} + +- startRecording: "WalletConnect Pay KYC Header Buttons" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Verify first option is pre-selected +- assertVisible: + id: "pay-option-0-selected" + +# Tap Continue to go to collectData webview +- tapOn: + id: "pay-button-continue" + +# Wait for KYC webview to load +- extendedWaitUntil: + visible: "Add your personal details" + timeout: 10000 + +# Verify the close button (X) is visible in the header +- assertVisible: + id: "pay-button-close" + +# Verify the back button is visible +- assertVisible: + id: "pay-button-back" + +# Tap back to return to option selection +- tapOn: + id: "pay-button-back" + +# Verify we're back at the selectOption view +- extendedWaitUntil: + visible: + id: "pay-option-0-selected" + timeout: 10000 + +# Dismiss the modal +- tapOn: + id: "pay-button-close" + +# Verify modal is dismissed and we're back at the main wallet screen +- extendedWaitUntil: + visible: + id: "button-scan" + timeout: 10000 + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml b/maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml new file mode 100644 index 0000000..dbbcda3 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml @@ -0,0 +1,101 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Multiple Options with KYC +tags: + - pay +--- +# Create payment via API (multi-option, KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_MULTI_KYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_MULTI_KYC} + +- startRecording: "WalletConnect Pay Multiple Options KYC" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Verify first option is pre-selected (index 0) +- assertVisible: + id: "pay-option-0-selected" + +# Verify at least a second option exists (multiple options) +- assertVisible: + id: "pay-option-1" + +# Verify "Info required" badge is visible on KYC options +- assertVisible: + id: "pay-info-required-badge" + +# Verify "?" info button is visible in the header +- assertVisible: + id: "pay-button-info" + +# Select the second option (index 1) +- tapOn: + id: "pay-option-1" + +# Verify second option is now selected +- assertVisible: + id: "pay-option-1-selected" + +# Verify first option is now deselected +- assertVisible: + id: "pay-option-0" + +# Copy the network name from the selected option's accessibilityLabel +- copyTextFrom: + id: "pay-option-1-selected" + +# Tap Continue to proceed +- tapOn: + id: "pay-button-continue" + +# Handle personal details webview (KYC) +- extendedWaitUntil: + visible: "Add your personal details" + timeout: 10000 + +# Data is autocompleted, just tap Add +- tapOn: "Add" + +# Retry tap on "Add" if "Confirm your details" doesn't appear (webview can be slow) +- runFlow: + when: + notVisible: "Confirm your details" + commands: + - tapOn: "Add" + +# Confirm your details popup +- extendedWaitUntil: + visible: "Confirm your details" + timeout: 10000 + +# Tap the checkbox / label for terms agreement +- tapOn: + text: "I agree to the Terms and Conditions and Privacy Policy" + retryTapIfNoChange: true +- tapOn: "Confirm" + +# Verify review screen shows the same token we selected +- assertVisible: + id: "pay-review-token-${maestro.copiedText}" + +# Verify pay button shows the correct amount +- copyTextFrom: + id: "pay-button-pay" +- assertTrue: + condition: "${maestro.copiedText == 'Pay $0.01'}" + +# Tap Pay, verify success +- runFlow: + file: flows/pay_confirm_and_verify.yaml + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_multiple_options_nokyc.yaml b/maestro/pay-tests/.maestro/pay_multiple_options_nokyc.yaml new file mode 100644 index 0000000..1e73d85 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_multiple_options_nokyc.yaml @@ -0,0 +1,76 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Multiple Options No KYC +tags: + - pay +--- +# Create payment via API (multi-option, no-KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_MULTI_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_MULTI_NOKYC} + +- startRecording: "WalletConnect Pay Multiple Options No KYC" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Verify first option is pre-selected (index 0) +- assertVisible: + id: "pay-option-0-selected" + +# Verify at least a second option exists (multiple options) +- assertVisible: + id: "pay-option-1" + +# Verify "?" info button is NOT visible (no KYC merchant) +- assertNotVisible: + id: "pay-button-info" + +# Verify "info required" badge is NOT visible (no KYC) +- assertNotVisible: + id: "pay-info-required-badge" + +# Select the second option (index 1) +- tapOn: + id: "pay-option-1" + +# Verify second option is now selected +- assertVisible: + id: "pay-option-1-selected" + +# Verify first option is now deselected +- assertVisible: + id: "pay-option-0" + +# Copy the network name from the selected option's accessibilityLabel +- copyTextFrom: + id: "pay-option-1-selected" + +# Tap Continue to proceed +- tapOn: + id: "pay-button-continue" + +# No KYC — goes straight to review screen +# Verify review screen shows the same token we selected +- assertVisible: + id: "pay-review-token-${maestro.copiedText}" + +# Verify pay button shows the correct amount +- copyTextFrom: + id: "pay-button-pay" +- assertTrue: + condition: "${maestro.copiedText == 'Pay $0.01'}" + +# Tap Pay, verify success +- runFlow: + file: flows/pay_confirm_and_verify.yaml + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_single_option_nokyc.yaml b/maestro/pay-tests/.maestro/pay_single_option_nokyc.yaml new file mode 100644 index 0000000..63cd064 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_single_option_nokyc.yaml @@ -0,0 +1,47 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Single Option No KYC +tags: + - pay +--- +# Create payment via API (single-option, no-KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + +- startRecording: "WalletConnect Pay Single Option No KYC" + +# Open wallet, paste payment URL +- runFlow: + file: flows/pay_open_and_paste_url.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Single option auto-selects — go straight to review screen + +# Verify pay button shows the correct amount +- extendedWaitUntil: + visible: + id: "pay-button-pay" + timeout: 10000 +- copyTextFrom: + id: "pay-button-pay" +- assertTrue: + condition: "${maestro.copiedText == 'Pay $0.01'}" + +# Tap Pay, verify success +- runFlow: + file: flows/pay_confirm_and_verify.yaml + +# Verify payment dialog is dismissed and we're back at the main wallet screen +- extendedWaitUntil: + visible: + id: "button-scan" + timeout: 10000 + +- stopRecording diff --git a/maestro/pay-tests/.maestro/pay_single_option_nokyc_deeplink.yaml b/maestro/pay-tests/.maestro/pay_single_option_nokyc_deeplink.yaml new file mode 100644 index 0000000..d7e2953 --- /dev/null +++ b/maestro/pay-tests/.maestro/pay_single_option_nokyc_deeplink.yaml @@ -0,0 +1,41 @@ +appId: ${APP_ID} +name: WalletConnect Pay - Single Option No KYC (Deep Link) +tags: + - pay +--- +# Create payment via API (single-option, no-KYC merchant) +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + +- startRecording: "WalletConnect Pay Single Option No KYC Deep Link" + +# Open wallet via deep link with payment URL +- runFlow: + file: flows/pay_open_via_deeplink.yaml + +# Wait for payment options to load +- extendedWaitUntil: + visible: + id: "pay-merchant-info" + timeout: 15000 + +# Single option auto-selects — go straight to review screen + +# Verify pay button shows the correct amount +- extendedWaitUntil: + visible: + id: "pay-button-pay" + timeout: 10000 +- copyTextFrom: + id: "pay-button-pay" +- assertTrue: + condition: "${maestro.copiedText == 'Pay $0.01'}" + +# Tap Pay, verify success +- runFlow: + file: flows/pay_confirm_and_verify.yaml + +- stopRecording diff --git a/maestro/pay-tests/.maestro/scripts/cancel-payment.js b/maestro/pay-tests/.maestro/scripts/cancel-payment.js new file mode 100644 index 0000000..dcd548b --- /dev/null +++ b/maestro/pay-tests/.maestro/scripts/cancel-payment.js @@ -0,0 +1,21 @@ +// Cancels a WalletConnect Pay payment via the API. +// Expects WPAY_CUSTOMER_KEY, WPAY_MERCHANT_ID, and PAYMENT_ID env vars from Maestro. + +if (typeof WPAY_CUSTOMER_KEY === 'undefined') throw new Error('Missing env var: WPAY_CUSTOMER_KEY'); +if (typeof WPAY_MERCHANT_ID === 'undefined') throw new Error('Missing env var: WPAY_MERCHANT_ID'); +if (typeof PAYMENT_ID === 'undefined') throw new Error('Missing env var: PAYMENT_ID'); + +var response = http.post('https://api.pay.walletconnect.com/v1/payments/' + PAYMENT_ID + '/cancel', { + headers: { + 'Content-Type': 'application/json', + 'Api-Key': WPAY_CUSTOMER_KEY, + 'Merchant-Id': WPAY_MERCHANT_ID, + }, + body: JSON.stringify({}), +}); + +if (response.status < 200 || response.status >= 300) { + throw new Error('Cancel API returned HTTP ' + response.status + ': ' + response.body); +} + +console.log('Payment cancelled: ' + PAYMENT_ID); diff --git a/maestro/pay-tests/.maestro/scripts/create-payment.js b/maestro/pay-tests/.maestro/scripts/create-payment.js new file mode 100644 index 0000000..2a17ce0 --- /dev/null +++ b/maestro/pay-tests/.maestro/scripts/create-payment.js @@ -0,0 +1,29 @@ +// Creates a WalletConnect Pay payment via the API. +// Expects WPAY_CUSTOMER_KEY and WPAY_MERCHANT_ID env vars from Maestro. +// Sets output.gateway_url and output.payment_id for use in subsequent flow steps. + +var response = http.post('https://api.pay.walletconnect.com/v1/payments', { + headers: { + 'Content-Type': 'application/json', + 'Api-Key': WPAY_CUSTOMER_KEY, + 'Merchant-Id': WPAY_MERCHANT_ID, + }, + body: JSON.stringify({ + referenceId: '' + Date.now() + Math.random().toString(36).substring(2, 10), + amount: { value: typeof WPAY_AMOUNT !== 'undefined' ? WPAY_AMOUNT : '1', unit: 'iso4217/USD' }, + }), +}); + +if (response.status < 200 || response.status >= 300) { + throw new Error('API returned HTTP ' + response.status + ': ' + response.body); +} + +var data = json(response.body); + +if (!data.gatewayUrl) { + throw new Error('No gatewayUrl in response: ' + response.body); +} + +console.log('Payment created: ' + data.paymentId); +output.gateway_url = data.gatewayUrl; +output.payment_id = data.paymentId; diff --git a/maestro/pay-tests/README.md b/maestro/pay-tests/README.md new file mode 100644 index 0000000..5534e1d --- /dev/null +++ b/maestro/pay-tests/README.md @@ -0,0 +1,243 @@ +# Maestro Pay Tests + +Shared [Maestro](https://maestro.mobile.dev/) E2E test flows for **WalletConnect Pay**. These tests verify the full payment lifecycle — from scanning a payment link to confirming on-chain — and are designed to run on any wallet that integrates WalletConnect Pay (React Native, Kotlin, Swift, Flutter). + +## How It Works + +This action copies shared Maestro test flows and helper scripts into your workspace. Your CI workflow then runs `maestro test` against your built app. The flows use accessibility IDs (testIDs) to interact with UI elements, so every wallet platform must implement the same set of IDs on the corresponding components. + +## Prerequisites + +- **Maestro CLI** installed — use [`WalletConnect/actions/maestro/setup`](../setup) action +- **App built and installed** on simulator/emulator +- **Test mode enabled** in the wallet app (to expose the URL input field) + +## Required TestIDs + +Every wallet platform must add these accessibility identifiers to the corresponding UI elements. The tests will fail if any are missing. + +### Scanner / URL Entry + +| TestID | Element | Description | +|---|---|---| +| `button-scan` | Scan button on home screen | Opens the scanner/QR modal | +| `input-paste-url` | Text input in scan modal | For pasting payment URLs (**test mode only** — see below) | +| `button-submit-url` | Submit button in scan modal | Submits the pasted URL (**test mode only**) | + +### Payment Modal — Header + +| TestID | Element | Description | +|---|---|---| +| `pay-button-back` | Back arrow button | Returns to previous step | +| `pay-button-close` | Close (X) button | Dismisses the payment modal | + +### Payment Modal — Merchant Info & Loading + +| TestID | Element | Description | +|---|---|---| +| `pay-merchant-info` | Merchant display | Shows merchant name and payment amount | +| `pay-loading-message` | Loading text | Shown during payment processing | + +### Payment Modal — Option Selection + +| TestID | Element | Description | +|---|---|---| +| `pay-option-{index}` | Payment option (unselected) | 0-based index from the payment options array | +| `pay-option-{index}-selected` | Payment option (selected) | Same element when selected | +| `pay-info-required-badge` | "Info required" badge | Shown on options that require KYC | +| `pay-button-info` | Info (?) button in header | Explains KYC requirement | +| `pay-button-continue` | Continue button | Proceeds after selecting a payment option | + +### Payment Modal — Review Screen + +| TestID | Element | Description | +|---|---|---| +| `pay-review-token-{networkName}` | Token/network display | Dynamic — lowercase network name (e.g. `pay-review-token-base`) | +| `pay-button-pay` | Pay button | Confirms and submits the payment | + +### Payment Modal — Result Screen + +| TestID | Element | Description | +|---|---|---| +| `pay-result-container` | Result screen wrapper | Container for the result view | +| `pay-result-success-icon` | Success icon | Checkmark shown on successful payment | +| `pay-result-insufficient-funds-icon` | Insufficient funds icon | Shown when wallet balance is too low | +| `pay-result-expired-icon` | Expired icon | Shown for expired payment links | +| `pay-result-cancelled-icon` | Cancelled icon | Shown for cancelled payments | +| `pay-result-error-icon` | Generic error icon | Shown for other errors (e.g. already completed) | +| `pay-button-result-action-success` | "Got it!" button (success) | Dismisses the success result | +| `pay-button-result-action-insufficient_funds` | Action button (insufficient funds) | Dismisses the insufficient funds error | +| `pay-button-result-action-expired` | Action button (expired) | Dismisses the expired error | +| `pay-button-result-action-cancelled` | Action button (cancelled) | Dismisses the cancelled error | +| `pay-button-result-action-generic` | Action button (generic error) | Dismisses the generic error | + +### Dynamic TestID Patterns + +Some testIDs include dynamic values: + +- **`pay-option-{index}`** / **`pay-option-{index}-selected`** — `index` is 0-based from the payment options array. Example: `pay-option-0`, `pay-option-1-selected` +- **`pay-review-token-{networkName}`** — lowercase network name. Example: `pay-review-token-base`, `pay-review-token-ethereum` +- **`pay-button-result-action-{type}`** — one of: `success`, `insufficient_funds`, `expired`, `cancelled`, `generic` + +## Test Input Field Requirement + +Each wallet must add a **text input field** and **submit button** inside the scan/QR modal. This is required for Maestro to bypass camera/QR scanning and submit payment URLs directly. + +**Important:** This input should only be visible when a test mode flag is enabled (e.g. `ENV_TEST_MODE=true`). It should never appear in production builds. + +### Reference Implementation (React Native) + +From `ScannerOptionsModal.tsx` in the React Native wallet sample: + +```tsx +import Config from 'react-native-config'; + +const showTestInput = Config.ENV_TEST_MODE === 'true'; + +// Inside the modal component's render: +{showTestInput && ( + + + + +)} +``` + +The key points for any platform: +1. Gate visibility behind a test/debug build flag +2. Use `input-paste-url` as the accessibility ID for the text input +3. Use `button-submit-url` as the accessibility ID for the submit button +4. On submit, pass the URL to the same handler that processes scanned QR codes or deep links + +## Required Secrets + +These secrets must be configured in your repository for the tests to create and manipulate payments: + +| Secret | Description | +|---|---| +| `WPAY_CUSTOMER_KEY_SINGLE_NOKYC` | API key for single-option, no-KYC merchant | +| `WPAY_MERCHANT_ID_SINGLE_NOKYC` | Merchant ID for single-option, no-KYC merchant | +| `WPAY_CUSTOMER_KEY_MULTI_NOKYC` | API key for multi-option, no-KYC merchant | +| `WPAY_MERCHANT_ID_MULTI_NOKYC` | Merchant ID for multi-option, no-KYC merchant | +| `WPAY_CUSTOMER_KEY_MULTI_KYC` | API key for multi-option, KYC-required merchant | +| `WPAY_MERCHANT_ID_MULTI_KYC` | Merchant ID for multi-option, KYC-required merchant | + +Each merchant pair represents a different test configuration. The tests use these to create payments with specific option counts and KYC requirements. + +## Test Catalog + +| Flow | Description | Merchant Config | +|---|---|---| +| `pay_single_option_nokyc` | Happy path: single payment option, no KYC | SINGLE_NOKYC | +| `pay_single_option_nokyc_deeplink` | Same as above but opened via deep link | SINGLE_NOKYC | +| `pay_multiple_options_nokyc` | Select between multiple options, no KYC | MULTI_NOKYC | +| `pay_multiple_options_kyc` | Multiple options with KYC webview | MULTI_KYC | +| `pay_cancel_from_review` | Server-side cancellation on review screen | SINGLE_NOKYC | +| `pay_cancel_from_kyc` | Server-side cancellation during KYC | MULTI_KYC | +| `pay_kyc_back_navigation` | Back/close button navigation in KYC | MULTI_KYC | +| `pay_insufficient_funds` | Payment amount exceeds wallet balance | SINGLE_NOKYC | +| `pay_double_scan` | Re-scan same QR after completion | SINGLE_NOKYC | +| `pay_expired_link` | Hardcoded expired payment URL | None (hardcoded) | +| `pay_cancelled` | Hardcoded cancelled payment URL | None (hardcoded) | + +All flows are tagged with `pay` for filtering via `--include-tags`. + +## Deep Link Support + +The `pay_single_option_nokyc_deeplink` test uses Maestro's `openLink` command to open a `https://pay.walletconnect.com` URL. Your wallet must be configured to handle these URLs as deep links / universal links for this test to work. + +## Usage + +### iOS (composite action) + +```yaml +steps: + - uses: actions/checkout@v4 + + # ... your platform-specific build steps ... + + - name: Copy shared Pay test flows + uses: WalletConnect/actions/maestro/pay-tests@main + + - name: Install Maestro + uses: WalletConnect/actions/maestro/setup@main + + # ... boot simulator, install app ... + + - name: Run Pay E2E tests + uses: WalletConnect/actions/maestro/run@main + with: + app-id: com.example.wallet.internal + wpay-customer-key-single-nokyc: ${{ secrets.WPAY_CUSTOMER_KEY_SINGLE_NOKYC }} + wpay-merchant-id-single-nokyc: ${{ secrets.WPAY_MERCHANT_ID_SINGLE_NOKYC }} + wpay-customer-key-multi-nokyc: ${{ secrets.WPAY_CUSTOMER_KEY_MULTI_NOKYC }} + wpay-merchant-id-multi-nokyc: ${{ secrets.WPAY_MERCHANT_ID_MULTI_NOKYC }} + wpay-customer-key-multi-kyc: ${{ secrets.WPAY_CUSTOMER_KEY_MULTI_KYC }} + wpay-merchant-id-multi-kyc: ${{ secrets.WPAY_MERCHANT_ID_MULTI_KYC }} +``` + +### Android (emulator runner) + +The `reactivecircus/android-emulator-runner` action runs everything inside a `script:` block, which cannot call composite actions. Use `maestro/pay-tests` and `maestro/setup` *before* the emulator step, then run `maestro test` inline: + +```yaml +steps: + - uses: actions/checkout@v4 + + # ... your platform-specific build steps ... + + - name: Copy shared Pay test flows + uses: WalletConnect/actions/maestro/pay-tests@main + + - name: Install Maestro + uses: WalletConnect/actions/maestro/setup@main + + - name: Run E2E tests on Android Emulator + id: maestro + uses: reactivecircus/android-emulator-runner@v2 + with: + api-level: 34 + arch: x86_64 + script: | + adb install path/to/app.apk + $HOME/.maestro/bin/maestro test \ + --env APP_ID="com.example.wallet.internal" \ + --env WPAY_CUSTOMER_KEY_SINGLE_NOKYC="${{ secrets.WPAY_CUSTOMER_KEY_SINGLE_NOKYC }}" \ + --env WPAY_MERCHANT_ID_SINGLE_NOKYC="${{ secrets.WPAY_MERCHANT_ID_SINGLE_NOKYC }}" \ + --env WPAY_CUSTOMER_KEY_MULTI_NOKYC="${{ secrets.WPAY_CUSTOMER_KEY_MULTI_NOKYC }}" \ + --env WPAY_MERCHANT_ID_MULTI_NOKYC="${{ secrets.WPAY_MERCHANT_ID_MULTI_NOKYC }}" \ + --env WPAY_CUSTOMER_KEY_MULTI_KYC="${{ secrets.WPAY_CUSTOMER_KEY_MULTI_KYC }}" \ + --env WPAY_MERCHANT_ID_MULTI_KYC="${{ secrets.WPAY_MERCHANT_ID_MULTI_KYC }}" \ + --include-tags pay \ + --test-output-dir maestro-artifacts \ + --debug-output maestro-artifacts \ + .maestro/ + + - name: Upload Maestro artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: maestro-android-artifacts + path: | + maestro-artifacts/ + maestro-output.log + retention-days: 14 +``` diff --git a/maestro/pay-tests/action.yml b/maestro/pay-tests/action.yml new file mode 100644 index 0000000..d527982 --- /dev/null +++ b/maestro/pay-tests/action.yml @@ -0,0 +1,39 @@ +name: Maestro Pay Tests +description: Copy shared WalletConnect Pay E2E test flows into the consumer workspace + +inputs: + target-dir: + description: 'Directory to copy test flows into (relative to workspace root)' + required: false + default: '.maestro' + +outputs: + maestro-dir: + description: 'Absolute path to the directory containing the test flows' + value: ${{ steps.copy.outputs.maestro-dir }} + +runs: + using: composite + steps: + - name: Copy shared Maestro Pay test flows + id: copy + shell: bash + run: | + ACTION_DIR="${{ github.action_path }}" + TARGET_DIR="${{ github.workspace }}/${{ inputs.target-dir }}" + + mkdir -p "$TARGET_DIR/flows" "$TARGET_DIR/scripts" + + # Copy root-level pay test flows + cp "$ACTION_DIR"/.maestro/pay_*.yaml "$TARGET_DIR/" + + # Copy shared sub-flows + cp "$ACTION_DIR"/.maestro/flows/pay_*.yaml "$TARGET_DIR/flows/" + + # Copy scripts + cp "$ACTION_DIR"/.maestro/scripts/*.js "$TARGET_DIR/scripts/" + + echo "Copied Maestro Pay test flows to $TARGET_DIR" + ls -la "$TARGET_DIR" + + echo "maestro-dir=$TARGET_DIR" >> "$GITHUB_OUTPUT" diff --git a/maestro/run/action.yml b/maestro/run/action.yml new file mode 100644 index 0000000..5cf6380 --- /dev/null +++ b/maestro/run/action.yml @@ -0,0 +1,69 @@ +name: Maestro Pay Test Run +description: Execute Maestro Pay E2E tests with WalletConnect Pay secrets and upload artifacts + +inputs: + app-id: + description: 'Application bundle ID (iOS) or package name (Android)' + required: true + maestro-dir: + description: 'Directory containing Maestro test flows' + required: false + default: '.maestro' + tags: + description: 'Maestro --include-tags value (comma-separated)' + required: false + default: 'pay' + wpay-customer-key-single-nokyc: + description: 'WalletConnect Pay API key (single-option, no-KYC merchant)' + required: true + wpay-merchant-id-single-nokyc: + description: 'WalletConnect Pay merchant ID (single-option, no-KYC merchant)' + required: true + wpay-customer-key-multi-nokyc: + description: 'WalletConnect Pay API key (multi-option, no-KYC merchant)' + required: true + wpay-merchant-id-multi-nokyc: + description: 'WalletConnect Pay merchant ID (multi-option, no-KYC merchant)' + required: true + wpay-customer-key-multi-kyc: + description: 'WalletConnect Pay API key (multi-option, KYC-required merchant)' + required: true + wpay-merchant-id-multi-kyc: + description: 'WalletConnect Pay merchant ID (multi-option, KYC-required merchant)' + required: true + artifact-name: + description: 'Name for the uploaded artifact' + required: false + default: 'maestro-artifacts' + +runs: + using: composite + steps: + - name: Run Maestro tests + id: maestro + shell: bash + run: | + set -o pipefail + maestro test \ + --env APP_ID="${{ inputs.app-id }}" \ + --env WPAY_CUSTOMER_KEY_SINGLE_NOKYC="${{ inputs.wpay-customer-key-single-nokyc }}" \ + --env WPAY_MERCHANT_ID_SINGLE_NOKYC="${{ inputs.wpay-merchant-id-single-nokyc }}" \ + --env WPAY_CUSTOMER_KEY_MULTI_NOKYC="${{ inputs.wpay-customer-key-multi-nokyc }}" \ + --env WPAY_MERCHANT_ID_MULTI_NOKYC="${{ inputs.wpay-merchant-id-multi-nokyc }}" \ + --env WPAY_CUSTOMER_KEY_MULTI_KYC="${{ inputs.wpay-customer-key-multi-kyc }}" \ + --env WPAY_MERCHANT_ID_MULTI_KYC="${{ inputs.wpay-merchant-id-multi-kyc }}" \ + --include-tags "${{ inputs.tags }}" \ + --test-output-dir maestro-artifacts \ + --debug-output maestro-artifacts \ + "${{ inputs.maestro-dir }}" 2>&1 | tee maestro-output.log + + - name: Upload Maestro artifacts + if: always() + uses: actions/upload-artifact@v4 + with: + name: ${{ inputs.artifact-name }} + path: | + maestro-artifacts/ + maestro-output.log + if-no-files-found: warn + retention-days: 14 diff --git a/maestro/setup/action.yml b/maestro/setup/action.yml new file mode 100644 index 0000000..46d95ca --- /dev/null +++ b/maestro/setup/action.yml @@ -0,0 +1,26 @@ +name: Maestro Setup +description: Install the Maestro CLI for mobile E2E testing + +inputs: + version: + description: 'Maestro version to install (e.g. 1.39.15). Leave empty for latest.' + required: false + default: '' + +runs: + using: composite + steps: + - name: Install Maestro CLI + shell: bash + run: | + if command -v maestro &>/dev/null; then + echo "Maestro already installed: $(maestro --version)" + exit 0 + fi + + if [ -n "${{ inputs.version }}" ]; then + export MAESTRO_VERSION="${{ inputs.version }}" + fi + + curl -fsSL "https://get.maestro.mobile.dev" | bash + echo "$HOME/.maestro/bin" >> "$GITHUB_PATH" From 2febfad710562a68b409e741fbc4880e94fe5cec Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Tue, 7 Apr 2026 15:59:28 -0300 Subject: [PATCH 02/13] fix: address review feedback on shell safety, env guards, and docs - Add `set -euo pipefail` to setup and pay-tests action scripts - Add explicit env var checks in create-payment.js (matching cancel-payment.js) - Remove non-existent maestro-output.log from Android artifact upload example Co-Authored-By: Claude Opus 4.6 --- maestro/pay-tests/.maestro/scripts/create-payment.js | 3 +++ maestro/pay-tests/README.md | 1 - maestro/pay-tests/action.yml | 1 + maestro/setup/action.yml | 1 + 4 files changed, 5 insertions(+), 1 deletion(-) diff --git a/maestro/pay-tests/.maestro/scripts/create-payment.js b/maestro/pay-tests/.maestro/scripts/create-payment.js index 2a17ce0..1fc11ae 100644 --- a/maestro/pay-tests/.maestro/scripts/create-payment.js +++ b/maestro/pay-tests/.maestro/scripts/create-payment.js @@ -2,6 +2,9 @@ // Expects WPAY_CUSTOMER_KEY and WPAY_MERCHANT_ID env vars from Maestro. // Sets output.gateway_url and output.payment_id for use in subsequent flow steps. +if (typeof WPAY_CUSTOMER_KEY === 'undefined') throw new Error('Missing env var: WPAY_CUSTOMER_KEY'); +if (typeof WPAY_MERCHANT_ID === 'undefined') throw new Error('Missing env var: WPAY_MERCHANT_ID'); + var response = http.post('https://api.pay.walletconnect.com/v1/payments', { headers: { 'Content-Type': 'application/json', diff --git a/maestro/pay-tests/README.md b/maestro/pay-tests/README.md index 5534e1d..0e2cc28 100644 --- a/maestro/pay-tests/README.md +++ b/maestro/pay-tests/README.md @@ -238,6 +238,5 @@ steps: name: maestro-android-artifacts path: | maestro-artifacts/ - maestro-output.log retention-days: 14 ``` diff --git a/maestro/pay-tests/action.yml b/maestro/pay-tests/action.yml index d527982..1bb4a08 100644 --- a/maestro/pay-tests/action.yml +++ b/maestro/pay-tests/action.yml @@ -19,6 +19,7 @@ runs: id: copy shell: bash run: | + set -euo pipefail ACTION_DIR="${{ github.action_path }}" TARGET_DIR="${{ github.workspace }}/${{ inputs.target-dir }}" diff --git a/maestro/setup/action.yml b/maestro/setup/action.yml index 46d95ca..ac65f91 100644 --- a/maestro/setup/action.yml +++ b/maestro/setup/action.yml @@ -13,6 +13,7 @@ runs: - name: Install Maestro CLI shell: bash run: | + set -euo pipefail if command -v maestro &>/dev/null; then echo "Maestro already installed: $(maestro --version)" exit 0 From d1848620ff15892241fe539501ff4a2363d58f89 Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Tue, 7 Apr 2026 16:42:57 -0300 Subject: [PATCH 03/13] docs(maestro): add local development section to README Add setup and run instructions for local development, including the auto-download script and .env.maestro secrets file pattern. Co-Authored-By: Claude Opus 4.6 --- maestro/pay-tests/README.md | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/maestro/pay-tests/README.md b/maestro/pay-tests/README.md index 0e2cc28..c68c397 100644 --- a/maestro/pay-tests/README.md +++ b/maestro/pay-tests/README.md @@ -164,7 +164,39 @@ All flows are tagged with `pay` for filtering via `--include-tags`. The `pay_single_option_nokyc_deeplink` test uses Maestro's `openLink` command to open a `https://pay.walletconnect.com` URL. Your wallet must be configured to handle these URLs as deep links / universal links for this test to work. -## Usage +## Local Development + +For running tests locally during development. Requires [Maestro CLI](https://maestro.mobile.dev/) installed on your machine. + +### Setup + +1. **Create your secrets file** (one-time): + ```bash + cp .env.maestro.example .env.maestro + # Fill in the WPAY_* values (get them from your team or the WalletConnect Pay dashboard) + ``` + +2. **Run tests** (auto-downloads flows if not present): + ```bash + ./scripts/run-maestro-pay-tests.sh + ``` + +That's it. The script will automatically download the shared test flows from this repo if they're not already present, load secrets from `.env.maestro`, and run all pay-tagged tests. + +### Other local commands + +```bash +# Run a single test +./scripts/run-maestro-pay-tests.sh .maestro/pay_cancelled.yaml + +# Re-download flows (e.g. after an update) +./scripts/setup-maestro-pay-tests.sh + +# Download flows from a specific branch +./scripts/setup-maestro-pay-tests.sh feat/my-branch +``` + +## CI Usage ### iOS (composite action) From 11af8735bfabdb3d8c6eccf2c36a05716cab24c7 Mon Sep 17 00:00:00 2001 From: Ignacio Santise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:33:17 -0300 Subject: [PATCH 04/13] Apply suggestion from @ignaciosantise --- maestro/run/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro/run/action.yml b/maestro/run/action.yml index 5cf6380..068517a 100644 --- a/maestro/run/action.yml +++ b/maestro/run/action.yml @@ -14,7 +14,7 @@ inputs: required: false default: 'pay' wpay-customer-key-single-nokyc: - description: 'WalletConnect Pay API key (single-option, no-KYC merchant)' + description: 'WalletConnect Pay Customer API key (single-offramp-option, no-KYC)' required: true wpay-merchant-id-single-nokyc: description: 'WalletConnect Pay merchant ID (single-option, no-KYC merchant)' From 1e19722cc99a10966a4761f460efac33934f3582 Mon Sep 17 00:00:00 2001 From: Ignacio Santise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:33:26 -0300 Subject: [PATCH 05/13] Apply suggestion from @ignaciosantise --- maestro/run/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro/run/action.yml b/maestro/run/action.yml index 068517a..e41adbb 100644 --- a/maestro/run/action.yml +++ b/maestro/run/action.yml @@ -29,7 +29,7 @@ inputs: description: 'WalletConnect Pay API key (multi-option, KYC-required merchant)' required: true wpay-merchant-id-multi-kyc: - description: 'WalletConnect Pay merchant ID (multi-option, KYC-required merchant)' + description: 'WalletConnect Pay merchant ID (multiple-offramp-options, KYC-required)' required: true artifact-name: description: 'Name for the uploaded artifact' From e9824290f2e432ddb36ef43b04eb9c08596adbb2 Mon Sep 17 00:00:00 2001 From: Ignacio Santise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:33:40 -0300 Subject: [PATCH 06/13] Apply suggestion from @ignaciosantise --- maestro/run/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro/run/action.yml b/maestro/run/action.yml index e41adbb..d09cca2 100644 --- a/maestro/run/action.yml +++ b/maestro/run/action.yml @@ -17,7 +17,7 @@ inputs: description: 'WalletConnect Pay Customer API key (single-offramp-option, no-KYC)' required: true wpay-merchant-id-single-nokyc: - description: 'WalletConnect Pay merchant ID (single-option, no-KYC merchant)' + description: 'WalletConnect Pay merchant ID (single-offramp-option, no-KYC)' required: true wpay-customer-key-multi-nokyc: description: 'WalletConnect Pay API key (multi-option, no-KYC merchant)' From 51e75956b283cd92ab6a91c2e4fc6cc12fcc378f Mon Sep 17 00:00:00 2001 From: Ignacio Santise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:33:47 -0300 Subject: [PATCH 07/13] Apply suggestion from @ignaciosantise --- maestro/run/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro/run/action.yml b/maestro/run/action.yml index d09cca2..92132fc 100644 --- a/maestro/run/action.yml +++ b/maestro/run/action.yml @@ -20,7 +20,7 @@ inputs: description: 'WalletConnect Pay merchant ID (single-offramp-option, no-KYC)' required: true wpay-customer-key-multi-nokyc: - description: 'WalletConnect Pay API key (multi-option, no-KYC merchant)' + description: 'WalletConnect Pay Customer API key (multiple-offramp-options, no-KYC)' required: true wpay-merchant-id-multi-nokyc: description: 'WalletConnect Pay merchant ID (multi-option, no-KYC merchant)' From 06790d0d44aef529705b52f0dfa6006c0d269a1c Mon Sep 17 00:00:00 2001 From: Ignacio Santise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:33:55 -0300 Subject: [PATCH 08/13] Apply suggestion from @ignaciosantise --- maestro/run/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro/run/action.yml b/maestro/run/action.yml index 92132fc..b78ed14 100644 --- a/maestro/run/action.yml +++ b/maestro/run/action.yml @@ -23,7 +23,7 @@ inputs: description: 'WalletConnect Pay Customer API key (multiple-offramp-options, no-KYC)' required: true wpay-merchant-id-multi-nokyc: - description: 'WalletConnect Pay merchant ID (multi-option, no-KYC merchant)' + description: 'WalletConnect Pay merchant ID (multiple-offramp-options, no-KYC))' required: true wpay-customer-key-multi-kyc: description: 'WalletConnect Pay API key (multi-option, KYC-required merchant)' From 0be8ff30f74baab7a0b2f699d98d490883633dec Mon Sep 17 00:00:00 2001 From: Ignacio Santise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 10:34:03 -0300 Subject: [PATCH 09/13] Apply suggestion from @ignaciosantise --- maestro/run/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/maestro/run/action.yml b/maestro/run/action.yml index b78ed14..a9ea474 100644 --- a/maestro/run/action.yml +++ b/maestro/run/action.yml @@ -26,7 +26,7 @@ inputs: description: 'WalletConnect Pay merchant ID (multiple-offramp-options, no-KYC))' required: true wpay-customer-key-multi-kyc: - description: 'WalletConnect Pay API key (multi-option, KYC-required merchant)' + description: 'WalletConnect Pay Customer API key (multiple-offramp-options, KYC-required)' required: true wpay-merchant-id-multi-kyc: description: 'WalletConnect Pay merchant ID (multiple-offramp-options, KYC-required)' From fb95e543f3b0c72f4e6fc9d7bb6f1de256b8154a Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 12:20:00 -0300 Subject: [PATCH 10/13] fix(maestro): stabilize shared flows for Android - Add extendedWaitUntil after launchApp in paste-URL flow to wait for app to fully load before tapping - Use stopApp + openLink in deeplink flow instead of bare openLink to avoid Android stale intent race condition Ported from reown-com/react-native-examples#467 Co-Authored-By: Claude Opus 4.6 --- .../pay-tests/.maestro/flows/pay_open_and_paste_url.yaml | 6 ++++++ maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml | 4 +++- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml b/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml index a218a8e..954738f 100644 --- a/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml +++ b/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml @@ -9,6 +9,12 @@ appId: ${APP_ID} permissions: all: allow +# Wait for the app to fully load before interacting +- extendedWaitUntil: + visible: + id: "button-scan" + timeout: 15000 + # Tap scan button to open scanner options modal - tapOn: id: "button-scan" diff --git a/maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml b/maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml index 27e5887..93ded1b 100644 --- a/maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml +++ b/maestro/pay-tests/.maestro/flows/pay_open_via_deeplink.yaml @@ -3,5 +3,7 @@ appId: ${APP_ID} # Shared flow: Open payment URL via deep link # Requires: ${output.gateway_url} to be set by a prior runScript step -# Open the payment URL as a deep link +# Stop app then open via deeplink so openLink is the launch intent. +- stopApp: + appId: ${APP_ID} - openLink: ${output.gateway_url} From 157dbb7461a482a5ae92afadd1a04a765b0572a6 Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Wed, 8 Apr 2026 15:47:55 -0300 Subject: [PATCH 11/13] fix(maestro): increase KYC webview timeout to 30s MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Bump extendedWaitUntil timeout for "Add your personal details" from 10s to 30s in all three KYC flows — the webview can be slow to load on CI. Ported from reown-com/react-native-examples#467 Co-Authored-By: Claude Opus 4.6 --- maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml | 2 +- maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml | 2 +- maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml b/maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml index 664b581..5e57391 100644 --- a/maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml +++ b/maestro/pay-tests/.maestro/pay_cancel_from_kyc.yaml @@ -33,7 +33,7 @@ tags: # Wait for KYC webview to load - extendedWaitUntil: visible: "Add your personal details" - timeout: 10000 + timeout: 30000 # Cancel the payment server-side while in KYC webview - runScript: diff --git a/maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml b/maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml index 5691f78..7f0b063 100644 --- a/maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml +++ b/maestro/pay-tests/.maestro/pay_kyc_back_navigation.yaml @@ -33,7 +33,7 @@ tags: # Wait for KYC webview to load - extendedWaitUntil: visible: "Add your personal details" - timeout: 10000 + timeout: 30000 # Verify the close button (X) is visible in the header - assertVisible: diff --git a/maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml b/maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml index dbbcda3..2e9a69b 100644 --- a/maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml +++ b/maestro/pay-tests/.maestro/pay_multiple_options_kyc.yaml @@ -61,7 +61,7 @@ tags: # Handle personal details webview (KYC) - extendedWaitUntil: visible: "Add your personal details" - timeout: 10000 + timeout: 30000 # Data is autocompleted, just tap Add - tapOn: "Add" From 9f430c35ca091a0de01bde73f65d337e93d909a7 Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Fri, 10 Apr 2026 12:58:12 -0300 Subject: [PATCH 12/13] fix(maestro): wait for submit button before tapping in paste-URL flow Add extendedWaitUntil for button-submit-url after entering the URL to avoid tapping before the button is rendered. Co-Authored-By: Claude Opus 4.6 --- .../pay-tests/.maestro/flows/pay_open_and_paste_url.yaml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml b/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml index 954738f..8d1871e 100644 --- a/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml +++ b/maestro/pay-tests/.maestro/flows/pay_open_and_paste_url.yaml @@ -31,6 +31,13 @@ appId: ${APP_ID} - tapOn: "Continue" - inputText: ${output.gateway_url} + - pressKey: Enter + +- extendedWaitUntil: + visible: + id: "button-submit-url" + timeout: 10000 + - tapOn: id: "button-submit-url" From 3b43662986c53debe03f16fd3efa3f509db86f9d Mon Sep 17 00:00:00 2001 From: ignaciosantise <25931366+ignaciosantise@users.noreply.github.com> Date: Fri, 10 Apr 2026 13:56:32 -0300 Subject: [PATCH 13/13] fix(maestro): replace hardcoded cancelled payment URL with dynamic create+cancel Instead of relying on a hardcoded stale payment ID, create a fresh payment via the API and immediately cancel it before opening the URL. Co-Authored-By: Claude Opus 4.6 --- maestro/pay-tests/.maestro/pay_cancelled.yaml | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/maestro/pay-tests/.maestro/pay_cancelled.yaml b/maestro/pay-tests/.maestro/pay_cancelled.yaml index 43bd2b8..b2144e3 100644 --- a/maestro/pay-tests/.maestro/pay_cancelled.yaml +++ b/maestro/pay-tests/.maestro/pay_cancelled.yaml @@ -3,8 +3,19 @@ name: WalletConnect Pay - Cancelled Payment tags: - pay --- -# Use a hardcoded cancelled payment URL (no API call needed) -- evalScript: ${output.gateway_url = 'https://pay.walletconnect.com/?pid=pay_24a2ecc101KNHMZ1B8GJMR16VXAZ2EXXMN'} +# Create a fresh payment, then cancel it immediately via API +- runScript: + file: scripts/create-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + +- runScript: + file: scripts/cancel-payment.js + env: + WPAY_CUSTOMER_KEY: ${WPAY_CUSTOMER_KEY_SINGLE_NOKYC} + WPAY_MERCHANT_ID: ${WPAY_MERCHANT_ID_SINGLE_NOKYC} + PAYMENT_ID: ${output.payment_id} - startRecording: "WalletConnect Pay Cancelled Payment"