From 239bf618b5f14844ee2b8658cd5cda446268f79e Mon Sep 17 00:00:00 2001 From: Linus Rath <139418639+rathlinus@users.noreply.github.com> Date: Thu, 22 Oct 2026 20:12:59 +0200 Subject: [PATCH] ci: search for ipad settings pages, tap the attachment chip by name and move to the body with return in the ios flows --- .maestro/features/02-read-messages.yaml | 6 +-- .maestro/features/03-compose-send.yaml | 1 + .maestro/features/08-scheduled-send.yaml | 1 + .maestro/features/12-settings.yaml | 44 ++++++++--------- .maestro/subflows/compose-body.yaml | 47 +++++++++++------- .maestro/subflows/settings-page.yaml | 61 ++++++++---------------- 6 files changed, 79 insertions(+), 81 deletions(-) diff --git a/.maestro/features/02-read-messages.yaml b/.maestro/features/02-read-messages.yaml index b7be998e..15e76c40 100644 --- a/.maestro/features/02-read-messages.yaml +++ b/.maestro/features/02-read-messages.yaml @@ -44,9 +44,9 @@ tags: visible: ".*q3-report.pdf.*" timeout: 20000 - takeScreenshot: read-04-attachment -# The message's attachment chip (name and size); on the iPad the list row -# next to it shows the name too. -- tapOn: ".*q3-report.pdf.*[0-9]+ B.*" +# The message's attachment chip, labelled with the file name alone (on the +# iPad the list row next to it mentions the name in its longer label). +- tapOn: "q3-report.pdf" - extendedWaitUntil: visible: "Share" timeout: 30000 diff --git a/.maestro/features/03-compose-send.yaml b/.maestro/features/03-compose-send.yaml index 0e4a8320..771f4f0e 100644 --- a/.maestro/features/03-compose-send.yaml +++ b/.maestro/features/03-compose-send.yaml @@ -21,6 +21,7 @@ tags: - runFlow: file: ../subflows/compose-body.yaml env: + FROM_SUBJECT: "yes" BODY: "Hello from the iOS simulator test." - takeScreenshot: compose-01-filled diff --git a/.maestro/features/08-scheduled-send.yaml b/.maestro/features/08-scheduled-send.yaml index 0f062ecd..b6eab573 100644 --- a/.maestro/features/08-scheduled-send.yaml +++ b/.maestro/features/08-scheduled-send.yaml @@ -21,6 +21,7 @@ tags: - runFlow: file: ../subflows/compose-body.yaml env: + FROM_SUBJECT: "yes" BODY: "Scheduled from the iOS simulator test." - tapOn: "Schedule send" - extendedWaitUntil: diff --git a/.maestro/features/12-settings.yaml b/.maestro/features/12-settings.yaml index 5af6250a..7d50cc4f 100644 --- a/.maestro/features/12-settings.yaml +++ b/.maestro/features/12-settings.yaml @@ -12,27 +12,27 @@ tags: timeout: 20000 - takeScreenshot: settings-00-list - runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: account, ROW: ".*@example.org, .*", PROOF: "Logged-in accounts|Display Name"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: language, ROW: "Language, Region & Time, .*", PROOF: "Date Format"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: notifications, ROW: "Notifications, .*", PROOF: "Email Notifications"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: appearance, ROW: "Appearance, .*", PROOF: "Choose your preferred color scheme"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: layout, ROW: "Layout, .*", PROOF: "Tune the email list interactions for mobile."}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: reading, ROW: "Reading, .*", PROOF: "Mark as Read"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: composing, ROW: "Composing, .*", PROOF: "Composer"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: identities, ROW: "Identities, .*", PROOF: "Sending Identities"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: vacation, ROW: "Out of Office, .*", PROOF: "Reply automatically"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: filters, ROW: "Filters, .*", PROOF: "Email Filters"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: templates, ROW: "Templates, .*", PROOF: "Email Templates"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: folders, ROW: "Folders, .*", PROOF: ".*Inbox.*"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: tags, ROW: "Tags, .*", PROOF: "Email Tags"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: downloads, ROW: "Downloads, .*", PROOF: "Email .*eml.* filename"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: security, ROW: "Security, .*", PROOF: "Account Security|Detecting server capabilities.*"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: content, ROW: "Content & Senders, .*", PROOF: "External Content"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", PROOF: "Default view"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", PROOF: "Group by first letter"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: files, ROW: "Files, .*", PROOF: "Default View"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: sidebar-apps, ROW: "Sidebar Apps, .*", PROOF: "Keep Apps Loaded"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: themes, ROW: "Themes, .*", PROOF: "The default light and dark palettes."}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: storage, ROW: "Storage & backup, .*", PROOF: "Offline mail.*"}} -- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: about, ROW: "About, .*", PROOF: "This device"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: language, ROW: "Language, Region & Time, .*", Q: "Language", PROOF: "Date Format"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: notifications, ROW: "Notifications, .*", Q: "Notifications", PROOF: "Email Notifications"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: appearance, ROW: "Appearance, .*", Q: "Appearance", PROOF: "Choose your preferred color scheme"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: layout, ROW: "Layout, .*", Q: "Layout", PROOF: "Tune the email list interactions for mobile."}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: reading, ROW: "Reading, .*", Q: "Reading", PROOF: "Mark as Read"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: composing, ROW: "Composing, .*", Q: "Composing", PROOF: "Composer"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: identities, ROW: "Identities, .*", Q: "Identities", PROOF: "Sending Identities"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: vacation, ROW: "Out of Office, .*", Q: "Office", PROOF: "Reply automatically"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: filters, ROW: "Filters, .*", Q: "Filters", PROOF: "Email Filters"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: templates, ROW: "Templates, .*", Q: "Templates", PROOF: "Email Templates"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: folders, ROW: "Folders, .*", Q: "Folders", PROOF: ".*Inbox.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: tags, ROW: "Tags, .*", Q: "Tags", PROOF: "Email Tags"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: downloads, ROW: "Downloads, .*", Q: "Downloads", PROOF: "Email .*eml.* filename"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: security, ROW: "Security, .*", Q: "Security", PROOF: "Account Security|Detecting server capabilities.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: content, ROW: "Content & Senders, .*", Q: "Content", PROOF: "External Content"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", Q: "Calendar", PROOF: "Default view"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", Q: "Contacts", PROOF: "Group by first letter"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: files, ROW: "Files, .*", Q: "Files", PROOF: "Default View"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: sidebar-apps, ROW: "Sidebar Apps, .*", Q: "Sidebar", PROOF: "Keep Apps Loaded"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: themes, ROW: "Themes, .*", Q: "Themes", PROOF: "The default light and dark palettes."}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: storage, ROW: "Storage & backup, .*", Q: "Storage", PROOF: "Offline mail.*"}} +- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: about, ROW: "About, .*", Q: "About", PROOF: "This device"}} - tapOn: id: tab-mail diff --git a/.maestro/subflows/compose-body.yaml b/.maestro/subflows/compose-body.yaml index 2ecac86b..a4968780 100644 --- a/.maestro/subflows/compose-body.yaml +++ b/.maestro/subflows/compose-body.yaml @@ -1,26 +1,41 @@ # In the composer: type BODY into the editor. The rich editor is a WebView # whose placeholder is CSS, so it may not be exposed; then tap the middle of # the screen, which is the body area below Subject (above the keyboard). +# FROM_SUBJECT=yes: the Subject field has the focus; Return there moves it +# to the body (an iPad in landscape shows hardly any body with the keyboard +# up, so there is nothing to tap). appId: org.bulwarkmail.mobile --- - runFlow: when: - visible: "Write your message.*" + true: ${FROM_SUBJECT == 'yes'} commands: - - tapOn: "Write your message.*" -# A tap on the WebView doesn't always take the focus from the Subject field -# (on the iPad the body once went into Subject); a second tap does. -# (hideKeyboard fails here: the composer's keyboard has no dismiss key.) -- runFlow: - when: - notVisible: "Write your message.*" - commands: - - tapOn: - point: "50%,50%" + - pressKey: Enter - waitForAnimationToEnd: timeout: 2000 - - tapOn: - point: "50%,50%" -- waitForAnimationToEnd: - timeout: 3000 -- inputText: ${BODY} + - inputText: ${BODY} +- runFlow: + when: + true: ${FROM_SUBJECT != 'yes'} + commands: + - runFlow: + when: + visible: "Write your message.*" + commands: + - tapOn: "Write your message.*" + # A tap on the WebView doesn't always take the focus from the Subject field + # (on the iPad the body once went into Subject); a second tap does. + # (hideKeyboard fails here: the composer's keyboard has no dismiss key.) + - runFlow: + when: + notVisible: "Write your message.*" + commands: + - tapOn: + point: "50%,50%" + - waitForAnimationToEnd: + timeout: 2000 + - tapOn: + point: "50%,50%" + - waitForAnimationToEnd: + timeout: 3000 + - inputText: ${BODY} diff --git a/.maestro/subflows/settings-page.yaml b/.maestro/subflows/settings-page.yaml index fe46f5a9..3f2e062b 100644 --- a/.maestro/subflows/settings-page.yaml +++ b/.maestro/subflows/settings-page.yaml @@ -1,56 +1,37 @@ -# Settings list: scroll to the row ROW (regex on "Title, summary"), open it, -# check PROOF shows, screenshot, and go back on a phone. On the iPad the list -# is a left column, so scroll there instead of the middle of the screen. -# (Swipe coordinates have to be literals.) +# Settings list: find the row ROW (regex on "Title, summary"), open it, +# check PROOF shows, screenshot, and go back on a phone. +# +# A row can count as visible while it is under the phone's tab bar or half +# off the iPad's screen, and the tap then misses. The phone scrolls it to the +# middle. The iPad's list is a left column that Maestro's scroll (always in +# the middle of the screen) doesn't reach, so it searches for the page (Q, +# a word of its title) instead, which puts the row near the top. appId: org.bulwarkmail.mobile --- -- evalScript: ${output.swiped = false} - runFlow: when: - true: ${DEVICE == 'tablet'} + true: ${DEVICE == 'tablet' && !!Q} commands: - repeat: while: notVisible: ${ROW} - times: 10 + times: 2 commands: - - swipe: - start: 18%,80% - end: 18%,40% - duration: 600 - - evalScript: ${output.swiped = true} - # The row counts as visible as soon as its top edge shows: one more - # short scroll brings all of it on screen, so the tap lands on it. - - runFlow: - when: - true: ${output.swiped} - commands: - - swipe: - start: 18%,70% - end: 18%,55% - duration: 600 + - tapOn: "Search settings" + - eraseText: 40 + - inputText: ${Q} + - waitForAnimationToEnd: + timeout: 3000 - runFlow: when: true: ${DEVICE != 'tablet'} commands: - - repeat: - while: - notVisible: ${ROW} - times: 10 - commands: - - swipe: - start: 50%,80% - end: 50%,40% - duration: 600 - - evalScript: ${output.swiped = true} - - runFlow: - when: - true: ${output.swiped} - commands: - - swipe: - start: 50%,70% - end: 50%,55% - duration: 600 + - scrollUntilVisible: + element: ${ROW} + direction: DOWN + centerElement: true + visibilityPercentage: 100 + timeout: 30000 # A tap while the list still coasts from the swipe only stops it. - waitForAnimationToEnd: timeout: 5000