Files
safe-wallet/apps/mobile/e2e/utils/assertions/verify-review-and-execute.yml
a52474ed96 tests(mobile): stabilize e2e after Tamagui v2 upgrade and staging-data drift (#8220)
* tests(mobile): fix e2e button assertions after Tamagui v2 upgrade

Tamagui v2 (Button.Text compound pattern) renders SafeButton as a single
accessibility element on iOS: the label is exposed on the button node itself,
not as a descendant text node. Matching via containsDescendants therefore never
matched, even though the button is visible.

Match the button's own label by text instead, in the shared assertions:
- verify-tx-details.yml (transaction-details-button)
- verify-explorer-link.yml (view-on-explorer-button)

Verified add-owner-threshold, remove-owner, change-threshold and swap-owner
history flows pass end-to-end.

* tests(mobile): update token filter description assertion

The tokens filter description copy changed; match the new wording.

* tests(mobile): stabilize change-theme e2e against native theme menu

Add a testID to the appearance FloatingMenu and target it from the
change-theme flow, then settle the native MenuView with
waitForAnimationToEnd before each option tap to avoid intermittent
stalls on the theme selection.

* tests(mobile): match simulation View button label after Tamagui v2

When the SafeShield simulation finishes, the SafeButton gains a link
iconAfter and Tamagui v2 exposes it as a single iOS accessibility element
labelled "View, " (trailing comma from the icon). The anchored text: 'View'
assertion therefore never matched even though the button was visible, while
the icon-less Run/Running states matched fine. Match text: 'View.*' instead,
mirroring the earlier verify-explorer-link / verify-tx-details fixes.

Verified conflicting-transactions flow passes end-to-end.

* tests(mobile): tolerate aged limit-order fixture in pending flow

The staging limit-order fixture's validUntil has passed, so the gateway now
reports the order as "Expired". Two assertions assumed a still-pending order:

- Status row expected "Execution needed|Pending"; broaden to the app's
  statusMap labels so an expired (or otherwise progressed) order passes.
- An expired order can't be signed — ConfirmTxForm renders no SignForm and
  thus no "Continue" button — so guard the review-and-confirm flow on
  "Continue" being visible instead of assuming it.

Verified limit-order flow passes end-to-end.

* tests(mobile): match "Sign with:" label in review-and-confirm flow

The confirm-flow branch asserted text 'Sign with', but SelectSigner renders
the label as "Sign with:" (with a colon), so the anchored match never matched.
This branch was previously unexercised (send uses the execute flow, limit-order
skips it when expired); an active TWAP order is the first to reach it. Match
'Sign with.*' instead.

Verified twap-order flow passes end-to-end.

* tests(mobile): add pending, safe-shield and app-update e2e suites

Add suite orchestrators mirroring the existing history suite (first test
clean-starts, the rest run with SKIP_CLEAN_START and re-seed via their own
e2e button):
- transactions/pending (9 tests, grouped by target safe)
- transactions/pending/safe-shield (13 tests, shared e2eSafeShieldSafe)
- app-update (soft + force update)

Also complete the onboarding suite by adding the missing
add-safe-and-import-signer flow.

All suite flows carry the 'suite' tag so they stay out of default test runs.

* tests(mobile): fix safe-shield simulation label and DAI approve token name

- verify-safe-shield-widget: match the post-simulation button as "View.*" — once
  finished it gains a link icon and Tamagui v2 exposes its iOS label as "View, ",
  so the anchored "View" never matched (same fix as verify-tx-checks).
- approve-malicious: the approve token's backend name changed from
  "Dai Stablecoin" to "Polygon PoS Bridged DAI", so match the new name.

Verified send-matic-benign and approve-malicious pass end-to-end.

* tests(mobile): prefix safe name in home navbar address assertions

The home navbar merges the safe name label and shortened address into a
single accessibility node, so Maestro's anchored regex match needs the
name prefix to match the address assertion.

* tests(mobile): match "Add signer" selection screen title in import flows

The import-signer button and signer-menu "Import signer" now route to the
ImportSigners selection screen titled "Add signer" ("Select how you'd like
to add your signer."), replacing the old "Import a signer" title. Update the
selection-screen assertions; the input-screen "Import a signer" title is
unchanged.

* docs(mobile): note biometric enrollment requirement for signer-import e2e

The signer-import flows go through the biometrics opt-in screen, which
dead-ends without biometrics enrolled on the simulator. Document that local
runs need the same enrollment step CI performs.

* tests(mobile): assert "Your signers" header after signer import

SignersList now flattens signers under a single "Your signers" header with
per-item "Imported" badges, replacing the "My signers" section header. Update
the post-import assertions to match; the specific imported signer is still
verified by the following name/address assertions.

* tests(mobile): assert signers screen header in new-user onboarding

The add-signers-form screen no longer renders the "Not imported signers"
section header (signers show in a flat list). Assert the screen heading
"Import your signers to unlock account" instead, matching add-existing-safe.

* tests(mobile): assert "Imported" badge on imported signer in Your signers list

After importing a signer (private key or seed phrase), verify the corresponding
signer row in the "Your signers" list shows the "Imported" badge. Assert the
badge text directly rather than via containsDescendants, since the signer row is
an accessible button that collapses its subtree on iOS.

* tests(mobile): tighten e2e assertions from PR review feedback

Address review findings on the Maestro stabilization PR:
- Require the trailing comma in the simulation "View," label so it no longer
  matches unrelated labels like "View on explorer".
- Require the colon in "Sign with:" so it can't match copy like "Sign with
  biometrics".
- Narrow the limit-order status matcher to the only states the fixture can
  reach (Execution needed | Expired).
- Gate the limit-order review sub-flow on the SignForm continue button's
  testID instead of bare "Continue" text; add that testID to SignForm.
- Tighten the manage-tokens description match to the full "Learn more about
  default tokens" phrase.
- Drop the duplicate "pending" tag from the safe-shield suite so a
  --include-tags pending run doesn't double-run it.
- Add a component test asserting the AppSettings appearance-menu testID.

* tests(mobile): tolerate transient deletion toast in view-account-info e2e

Removing the last safe redirects to onboarding immediately, dismissing the
"...was deleted" toast before it can be asserted. Make the toast assertion
optional and rely on the onboarding-screen assertion as the authoritative
deletion check.

* tests(mobile): scroll the balance-change block into view before asserting

The "No balance change detected" text sits below the fold on the Confirm
transaction screen, so tests that don't otherwise scroll (batch-2-actions,
batch-3-actions-warn) failed to find it. Use scrollUntilVisible instead of a
plain assertion.

* tests(mobile): scroll simulation Run button clear of the sticky footer

On the Confirm transaction screen the SafeShield "Run" simulation button can
sit underneath the sticky "Continue" footer. Maestro considers it visible and
taps it, but the tap lands on the footer button and navigates to "Review and
confirm", so the simulation never runs and the "View" result never appears.
Scroll the Run button into the clear before tapping it.

* tests(mobile): rename "Execution method" to "Pay fees from" in review flow

The execution-method selector on the Review and execute screen was relabeled
"Pay fees from" (SelectExecutor). Update the label assertion and the two taps
that open the "Choose how to execute" sheet to match; the sheet header and
options are unchanged.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-02 10:52:44 +02:00

377 lines
14 KiB
YAML

appId: ${APP_ID}
jsEngine: graaljs
tags:
- util
---
- runScript:
file: ../../utils/scripts/defaults.js
# ===========================================
# Review and Execute/Confirm (Reusable Component)
# ===========================================
# Tests navigating to either Review and execute or Review and confirm screen from Confirm transaction
# and verifies all elements including tabs, execution method/signer, and execute/confirm button
#
# Environment variables:
# EXPECTED_SIGNER_PATTERN: Optional regex pattern to verify signer name/address
# Example: "Signer-2a3b" or ".*2a3b.*"
# Defaults to ".*Signer.*" to match any signer
# TX_EXECUTION_STATUS: Transaction execution status - "succeed" or "fail"
# If "succeed": verifies estimated network fee is displayed
# If "fail": verifies "Can not estimate" and failure warning
# Defaults to "succeed" if not provided
# IS_FULLY_SIGNED: Whether the transaction is fully signed - "true" or "false"
# If "true": navigates to "Review and execute" screen (execution flow)
# If "false": navigates to "Review and confirm" screen (signing flow)
# Defaults to "true" if not provided
#
# ===========================================
# Navigate to Review and Execute
# ===========================================
# Tap the Continue button on Confirm transaction screen
- assertVisible:
text: 'Continue'
- tapOn:
text: 'Continue'
# Wait for navigation to complete
- waitForAnimationToEnd:
timeout: 2000
# ===========================================
# Parse IS_FULLY_SIGNED and TX_EXECUTION_STATUS
# ===========================================
# Parse the IS_FULLY_SIGNED environment variable using GraalJS
# Default to "true" (fully signed = execute flow) if not provided
- evalScript: '${output.isFullySigned = (typeof IS_FULLY_SIGNED !== "undefined" && IS_FULLY_SIGNED === "true") || (typeof IS_FULLY_SIGNED === "undefined")}'
# Set flags for conditional execution
- evalScript: '${output.isExecuteFlow = output.isFullySigned === true}'
- evalScript: '${output.isConfirmFlow = output.isFullySigned === false}'
# Parse TX_EXECUTION_STATUS environment variable
# Default to "succeed" if not provided
- evalScript: '${output.txStatus = (typeof TX_EXECUTION_STATUS !== "undefined" && TX_EXECUTION_STATUS) ? TX_EXECUTION_STATUS.toLowerCase() : "succeed"}'
# Set flags for transaction execution status
- evalScript: '${output.isSuccessFlow = output.txStatus === "succeed"}'
- evalScript: '${output.isFailureFlow = output.txStatus === "fail"}'
# ===========================================
# Verify Review Screen (Execute or Confirm)
# ===========================================
# Verify the correct screen title based on flow type
- runFlow:
when:
true: '${output.isExecuteFlow === true}'
commands:
- assertVisible: 'Review and execute'
- runFlow:
when:
true: '${output.isConfirmFlow === true}'
commands:
- assertVisible: 'Review and confirm'
# Verify the instructional text is visible (same for both screens)
- assertVisible:
text: '.*Review this transaction data.*'
# ===========================================
# Verify Tabs
# ===========================================
# Verify all three tabs are visible: Data, Hashes, JSON
- assertVisible:
text: 'Data'
- assertVisible:
text: 'Hashes'
- assertVisible:
text: 'JSON'
# ===========================================
# Test Data Tab (default selected)
# ===========================================
# Verify Data tab content using existing comprehensive tests
- runFlow:
file: ../components/advanced-details/data.yml
# ===========================================
# Test Tab Switching
# ===========================================
# Tap on Hashes tab
- tapOn:
text: 'Hashes'
# Wait for tab content to load
- waitForAnimationToEnd:
timeout: 1000
# Verify Hashes tab content is visible (should show hash fields)
- extendedWaitUntil:
visible:
text: '.*Domain hash.*'
optional: true
timeout: ${output.defaults.extendedWaitUntilTimeout}
- extendedWaitUntil:
visible:
text: '.*Message hash.*'
optional: true
timeout: ${output.defaults.extendedWaitUntilTimeout}
- extendedWaitUntil:
visible:
text: '.*safeTxHash.*'
optional: true
timeout: ${output.defaults.extendedWaitUntilTimeout}
# Tap on JSON tab
- tapOn:
text: 'JSON'
# Wait for tab content to load
- waitForAnimationToEnd:
timeout: 1000
# Verify JSON tab content is visible (should show JSON data)
- extendedWaitUntil:
visible:
text: '.*"to".*'
optional: true
timeout: ${output.defaults.extendedWaitUntilTimeout}
# Tap back on Data tab
- tapOn:
text: 'Data'
# Wait for tab content to load
- waitForAnimationToEnd:
timeout: 1000
# ===========================================
# Verify Footer Section (Execution Method or Signer Selection)
# ===========================================
# Execute Flow: Verify Execution Method (Dynamically Detected: Relay or Signer)
- runFlow:
when:
true: '${output.isExecuteFlow === true}'
commands:
# Verify "Pay fees from" (execution method) label is visible
- assertVisible:
text: 'Pay fees from'
# ===========================================
# Dynamically Detect: Relay (Sponsored by Safe) or Signer
# ===========================================
# Check initial execution method once at the beginning and store in output variable
# Default to false (signer selected)
- evalScript: '${output.isRelaySelected = false}'
# If "Sponsored by Safe" is visible, set flag to true (relay selected)
- runFlow:
when:
visible:
text: 'Sponsored by Safe'
commands:
- evalScript: '${output.isRelaySelected = true}'
# ===========================================
# Flow: Relay (Sponsored by Safe) - Run if relay was initially selected
# ===========================================
- runFlow:
when:
true: '${output.isRelaySelected === true}'
commands:
# Verify "Sponsored by Safe" is visible
- assertVisible:
text: 'Sponsored by Safe'
# Success scenario: Verify "Free x left / day" is visible
- runFlow:
when:
true: '${output.isSuccessFlow === true}'
commands:
- assertVisible:
text: '.*\d+ left / day.*'
# Failure scenario: Verify "Can not estimate" is visible
- runFlow:
when:
true: '${output.isFailureFlow === true}'
commands:
- assertVisible:
text: '.*Est\. network fee.*'
- assertVisible:
text: '.*Can not estimate.*'
# Tap on execution method to open the sheet
- tapOn:
text: 'Pay fees from'
# Wait for sheet to open
- waitForAnimationToEnd:
timeout: 1000
# Verify the sheet opens (should show "Choose how to execute")
- assertVisible:
text: 'Choose how to execute'
# Verify "Sponsored by Safe" option is visible in the sheet
- assertVisible:
text: 'Sponsored by Safe'
# Verify "Or use your signer:" section is visible
- assertVisible:
text: '.*Or use your signer.*'
# Verify the signer is visible in the signer list
- assertVisible:
text: '${EXPECTED_SIGNER_PATTERN || ".*Signer.*"}'
# Tap on the signer to select it (switch from relay to signer execution)
- tapOn:
text: '${EXPECTED_SIGNER_PATTERN || ".*Signer.*"}'
# Verify we're back on Review and execute screen
- assertVisible: 'Review and execute'
# Verify "Est. network fee" label is now visible (after switching from relay)
- assertVisible:
text: '.*Est\. network fee.*'
# Success scenario after switching: Verify estimated fee is displayed
- runFlow:
when:
true: '${output.isSuccessFlow === true}'
commands:
- extendedWaitUntil:
visible:
text: '.*(<|≈).*\d+.*ETH.*'
optional: true
timeout: ${output.defaults.extendedWaitUntilTimeout}
# Failure scenario after switching: Verify "Can not estimate" is still shown
- runFlow:
when:
true: '${output.isFailureFlow === true}'
commands:
- assertVisible:
text: '.*Can not estimate.*'
# ===========================================
# Flow: Signer (User previously selected signer) - Run if signer was initially selected
# ===========================================
- runFlow:
when:
true: '${output.isRelaySelected === false}'
commands:
# Verify the signer is visible in the execution method
- assertVisible:
text: '${EXPECTED_SIGNER_PATTERN || ".*Signer.*"}'
# Verify "Est. network fee" label is visible
- assertVisible:
text: '.*Est\. network fee.*'
# Verify estimated fee value is displayed (format: "< 0.00001 ETH" or similar)
- extendedWaitUntil:
visible:
text: '.*(<|≈).*\d+.*ETH.*'
optional: true
timeout: ${output.defaults.extendedWaitUntilTimeout}
# Optionally verify we can still open the sheet and see both options
- tapOn:
text: 'Pay fees from'
# Wait for sheet to open
- waitForAnimationToEnd:
timeout: 1000
# Verify the sheet opens
- assertVisible:
text: 'Choose how to execute'
# Verify "Sponsored by Safe" option is visible
- assertVisible:
text: 'Sponsored by Safe'
# Verify "Or use your signer:" section is visible
- assertVisible:
text: '.*Or use your signer.*'
# Verify the signer is visible and selected (should have checkmark)
- assertVisible:
text: '${EXPECTED_SIGNER_PATTERN || ".*Signer.*"}'
# Dismiss the sheet by swiping down
- swipe:
direction: 'DOWN'
startPoint: '50%,50%'
endPoint: '50%,80%'
# Wait for sheet to close
- waitForAnimationToEnd:
timeout: 1000
# Verify we're back on Review and execute screen
- assertVisible: 'Review and execute'
# Confirm Flow: Verify Signer Selection
- runFlow:
when:
true: '${output.isConfirmFlow === true}'
commands:
# Verify "Sign with:" label is visible (require the colon so we don't match
# unrelated copy such as "Sign with biometrics")
- assertVisible:
text: 'Sign with:.*'
# Verify the active signer is visible
- assertVisible:
text: '${EXPECTED_SIGNER_PATTERN || ".*Signer.*"}'
# ===========================================
# Verify Footer Button (Execute or Confirm)
# ===========================================
# Execute Flow: Verify Execute Transaction Button
- runFlow:
when:
true: '${output.isExecuteFlow === true}'
commands:
- assertVisible:
text: 'Execute transaction'
# Confirm Flow: Verify Confirm Transaction Button
- runFlow:
when:
true: '${output.isConfirmFlow === true}'
commands:
- assertVisible:
text: 'Confirm transaction'
# ===========================================
# Success Flow: Verify Estimated Network Fee (Execute Flow with Signer Only)
# ===========================================
# Run success flow if TX_EXECUTION_STATUS is "succeed" AND we're in execute flow AND signer is selected
# Note: Relay execution shows "Free x left / day" instead of estimated fee, so we skip this check
# We check if signer was initially selected (or if relay flow switched to signer)
- runFlow:
when:
true: '${output.isSuccessFlow === true && output.isExecuteFlow === true && output.isRelaySelected === false}'
commands:
# Verify "Est. network fee" label is visible
- assertVisible:
text: '.*Est\. network fee.*'
# Verify estimated fee value is displayed (format: "< 0.00001 ETH" or similar)
- extendedWaitUntil:
visible:
text: '.*(<|≈).*\d+.*ETH.*'
optional: true
timeout: ${output.defaults.extendedWaitUntilTimeout}
# ===========================================
# Failure Flow: Verify Cannot Estimate and Failure Warning (Execute Flow - Both Relay and Signer)
# ===========================================
# Run failure flow if TX_EXECUTION_STATUS is "fail" AND we're in execute flow
# Note: Failure warnings are shown for BOTH relay and signer execution methods
- runFlow:
when:
true: '${output.isFailureFlow === true && output.isExecuteFlow === true}'
commands:
# Verify "Est. network fee" label is visible
- assertVisible:
text: '.*Est\. network fee.*'
# Verify "Can not estimate." message is displayed
- assertVisible:
text: '.*Can not estimate.*'
# Verify failure warning is displayed
- assertVisible:
text: '.*This transaction will most likely fail.*'
- tapOn:
id: 'go-back'