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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .maestro/16-wallet-connect-cancel.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
# 16-wallet-connect-cancel.yaml
#
# Maestro E2E flow: Wallet connection — user cancels in the wallet app
#
# Scenario:
# The user starts a wallet connection, is deep-linked out to the (mocked)
# wallet app, and cancels the connection request. The app should return to
# the connection screen in a non-connected state and surface a clear,
# dismissible "connection cancelled" message. No session/address should be
# persisted.
#
# Deterministic local runs:
# Real wallet apps cannot be driven reliably in CI, so this flow assumes the
# app is launched with the wallet interaction stubbed. Set the launch
# argument `MOCK_WALLET=cancel` (see .maestro/README.md) so the mock wallet
# provider immediately resolves the deep-link callback with a user-rejected
# result instead of opening an external app.
#
# End state asserted (distinct to this flow):
# - "Connection cancelled" message is visible
# - The primary CTA reads "Connect Wallet" again (i.e. still disconnected)
# - No connected address / guild content is shown

appId: ${APP_ID}
env:
APP_ID: com.example.app
MOCK_WALLET: cancel
---
- launchApp:
appId: ${APP_ID}
clearState: true
arguments:
MOCK_WALLET: ${MOCK_WALLET}

# Reach the wallet connection entry point.
- assertVisible:
id: "connect-wallet-button"
- tapOn:
id: "connect-wallet-button"

# The mock wallet provider stands in for the external wallet app. When
# MOCK_WALLET=cancel it resolves the callback as a user rejection.
- assertVisible:
id: "wallet-provider-list"
- tapOn:
id: "wallet-provider-metamask"

# Simulate the user cancelling inside the (mocked) wallet app.
- assertVisible:
id: "mock-wallet-cancel-button"
- tapOn:
id: "mock-wallet-cancel-button"

# Distinct end state: cancellation surfaced, still disconnected.
- assertVisible:
id: "connection-cancelled-message"
text: ".*[Cc]ancel.*"
- assertVisible:
id: "connect-wallet-button"
- assertNotVisible:
id: "connected-address"
- assertNotVisible:
id: "guild-list"

# The cancellation message can be dismissed and the flow retried.
- tapOn:
id: "connection-cancelled-dismiss"
- assertNotVisible:
id: "connection-cancelled-message"
82 changes: 82 additions & 0 deletions .maestro/17-wallet-connect-timeout.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
# 17-wallet-connect-timeout.yaml
#
# E2E flow: Wallet connection times out.
#
# Scenario:
# The user initiates a wallet connection, gets deep-linked out to the wallet
# app (mocked/stubbed for deterministic local runs — see .maestro/README.md),
# but the wallet never returns an approval within the allotted window. The app
# must surface a timeout error and offer a retry affordance, leaving the user
# in a disconnected-but-recoverable state.
#
# Determinism note:
# Relies on the wallet-mock harness documented in .maestro/README.md. Launch
# with WALLET_MOCK_MODE=timeout so the stubbed wallet bridge never resolves
# the connection request, forcing the app-side timeout to fire.
#
appId: ${APP_ID}
env:
WALLET_MOCK_MODE: "timeout"
# Keep this comfortably above the app's connection timeout so the UI has
# settled into its error state before we assert on it.
TIMEOUT_ASSERT_MS: "45000"
tags:
- wallet
- error-path
- regression
---
- runFlow: subflows/reset-app-state.yaml

- launchApp:
clearState: true
arguments:
walletMockMode: ${WALLET_MOCK_MODE}

# --- Arrive at the wallet connection entry point ----------------------------
- assertVisible:
id: "onboarding-connect-wallet-cta"
- tapOn:
id: "onboarding-connect-wallet-cta"

- assertVisible:
id: "wallet-select-list"
- tapOn:
id: "wallet-option-mock"

# --- Deep link out to the (stubbed) wallet app -----------------------------
# The mock harness intercepts the outbound deep link and, in "timeout" mode,
# deliberately does NOT dispatch a return deep link. Control returns to the app
# with the connection request left pending.
- assertVisible:
id: "wallet-connect-pending-indicator"
- assertVisible:
text: "Waiting for wallet.*"

# --- Wait for the app-side connection timeout to fire -----------------------
- extendedWaitUntil:
visible:
id: "wallet-connect-timeout-error"
timeout: ${TIMEOUT_ASSERT_MS}

# --- Assert the specific, distinct end state --------------------------------
- assertVisible:
id: "wallet-connect-timeout-error"
- assertVisible:
text: "Connection timed out.*"
- assertVisible:
id: "wallet-connect-retry-button"

# The pending spinner must be gone: we are in a settled error state, not limbo.
- assertNotVisible:
id: "wallet-connect-pending-indicator"

# User is NOT connected — no wallet address chip should be shown.
- assertNotVisible:
id: "connected-wallet-address"

# Retry must return the user to a re-attemptable connection entry point rather
# than a dead end.
- tapOn:
id: "wallet-connect-retry-button"
- assertVisible:
id: "wallet-select-list"
61 changes: 61 additions & 0 deletions .maestro/18-wallet-connect-unsupported-chain.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# 18-wallet-connect-unsupported-chain.yaml
#
# E2E flow: Wallet connection succeeds but returns an UNSUPPORTED CHAIN.
#
# Scenario:
# The user initiates a wallet connection, approves it in the (mocked) wallet
# app, but the returned account is on a chain the app does not support.
# The app must reject the session and surface an "unsupported network" error
# while remaining in a disconnected state.
#
# Deterministic mocking:
# Wallet deep-link round-trips are non-deterministic in CI, so this flow
# relies on the app's E2E wallet mock (enabled via the launch arguments
# below). The mock intercepts the WalletConnect/deep-link handshake and
# returns a canned response.
# - MAESTRO_E2E=true -> enables the mock wallet layer
# - MOCK_WALLET_RESULT=unsupported_chain -> mock returns a connected
# account on an unsupported chain
# (chainId 999 by default)
# See .maestro/README.md for how to build a local dev build with the mock
# wallet layer compiled in.
#
# Distinct end state asserted by this flow:
# - An "unsupported network" / "unsupported chain" error is visible.
# - The wallet remains DISCONNECTED (a "Connect Wallet" affordance is still
# present and no connected address is shown).

appId: ${MAESTRO_APP_ID}
env:
MAESTRO_E2E: "true"
MOCK_WALLET_RESULT: "unsupported_chain"
MOCK_WALLET_CHAIN_ID: "999"
tags:
- wallet
- wallet-error-paths
---
- launchApp:
clearState: true
arguments:
e2e: ${MAESTRO_E2E}
mockWalletResult: ${MOCK_WALLET_RESULT}
mockWalletChainId: ${MOCK_WALLET_CHAIN_ID}

# Reach the wallet connection entry point.
- assertVisible: "Connect Wallet"
- tapOn: "Connect Wallet"

# The mock wallet auto-approves and returns to the app with an
# account on an unsupported chain. Wait for the app to process the callback.
- extendedWaitUntil:
visible:
text: "Unsupported network|Unsupported chain|Network not supported"
timeout: 20000

# End state: an explicit, distinct unsupported-network error is shown.
- assertVisible:
text: "Unsupported network|Unsupported chain|Network not supported"

# End state: the app did NOT establish a session - it stays disconnected.
- assertVisible: "Connect Wallet"
- assertNotVisible: "Disconnect"
4 changes: 4 additions & 0 deletions .maestro/config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,10 @@ flows:
- 12-access-qr-malformed-json.yaml
- 13-deep-link-access-check-missing-resource-id.yaml
- 14-deep-link-guild-detail-invalid-guild-id.yaml
- 15-wallet-connect-cancel.yaml
- 16-wallet-connect-timeout.yaml
- 17-wallet-connect-unsupported-chain.yaml
- 18-wallet-connect-reconnect-after-disconnect.yaml

env:
# Default test environment variables
Expand Down