Merge branch 'cu-e2e' into catch-up-2026-11

This commit is contained in:
Linus Rath
2026-10-31 16:56:15 +01:00
13 changed files with 111 additions and 27 deletions
+3 -1
View File
@@ -32,7 +32,9 @@ tags:
visible: "Photos & Videos"
timeout: 10000
- assertVisible: "Camera"
- assertVisible: "Files"
# A server with Files offers "Upload from this device" and "Attach from
# Files" instead of a plain "Files".
- assertVisible: "Upload from this device|Files"
- takeScreenshot: compose-02-attach-sheet
- evalScript: ${output.pickPhoto = ORIENTATION != 'landscape'}
# The system photo picker runs in another process: XCTest sees none of its
+11 -3
View File
@@ -11,8 +11,12 @@ tags:
direction: DOWN
centerElement: true
timeout: 20000
# Swipe the row itself: the avatar checkbox's label has the subject too, and
# a swipe that starts on it only selects the row (#91).
- swipe:
from: ".*Grüße aus München.*"
from:
id: email-row
text: ".*Grüße aus München.*"
direction: LEFT
duration: 400
# The undo bar shows for 5 s; when it goes, the Compose button slides into
@@ -27,14 +31,18 @@ tags:
- takeScreenshot: swipe-02-undone
- swipe:
from: ".*Grüße aus München.*"
from:
id: email-row
text: ".*Grüße aus München.*"
direction: RIGHT
duration: 400
- waitForAnimationToEnd:
timeout: 3000
- takeScreenshot: swipe-03-read-toggled
- swipe:
from: ".*Grüße aus München.*"
from:
id: email-row
text: ".*Grüße aus München.*"
direction: RIGHT
duration: 400
- waitForAnimationToEnd:
+7 -2
View File
@@ -63,7 +63,10 @@ tags:
direction: DOWN
centerElement: true
timeout: 20000
- longPressOn: ".*Grüße aus München.*"
# The row, not its avatar checkbox (whose label has the subject too).
- longPressOn:
id: email-row
text: ".*Grüße aus München.*"
- extendedWaitUntil:
visible: "Clear selection"
timeout: 10000
@@ -76,7 +79,9 @@ tags:
visible: "Clear selection"
commands:
- tapOn: "Clear selection"
- longPressOn: ".*Grüße aus München.*"
- longPressOn:
id: email-row
text: ".*Grüße aus München.*"
- tapOn: "Unstar|Star"
- runFlow:
when:
+3 -1
View File
@@ -48,9 +48,11 @@ tags:
- extendedWaitUntil:
visible: "Keep scheduled"
timeout: 10000
# The dialog's button (below its message), not its title or the button
# behind it. Cancelling now moves the message back to Drafts.
- tapOn:
text: "Cancel send"
below: "Keep scheduled|.*will not be delivered.*"
below: ".*will not be (sent|delivered).*"
- extendedWaitUntil:
notVisible: ".*${output.subject}.*"
timeout: 30000
+19 -1
View File
@@ -34,7 +34,12 @@ tags:
timeout: 5000
- takeScreenshot: calendar-04-week
# Create
# Create. The button starts the event on the selected day: make that today
# (a stray tap, such as one through the notification prompt, can select
# another day), so the schedule below finds it after Go to today.
- tapOn: "Go to today"
- waitForAnimationToEnd:
timeout: 3000
- tapOn: "New event"
- extendedWaitUntil:
visible: "Save"
@@ -46,6 +51,19 @@ tags:
- pressKey: Enter
- waitForAnimationToEnd:
timeout: 3000
# A new event starts on the calendar's default reminders; Customize turns
# them into the event's own list, which has Add reminder.
- scrollUntilVisible:
element: "Customize|Add reminder"
direction: DOWN
timeout: 10000
- runFlow:
when:
visible: "Customize"
commands:
- tapOn: "Customize"
- waitForAnimationToEnd:
timeout: 3000
- scrollUntilVisible:
element: "Add reminder"
direction: DOWN
+10 -8
View File
@@ -44,20 +44,22 @@ tags:
timeout: 20000
- takeScreenshot: files-04-folder-created
- longPressOn: ".*${output.folder}.*"
# Delete moves it to the trash at once, with Undo and no question (like the
# webmail). A tap while the actions sheet still slides in can miss: tap
# again while Delete is still there.
- extendedWaitUntil:
visible: "Delete"
timeout: 10000
- repeat:
while:
notVisible: "Delete this item.*"
visible: "Delete"
times: 3
commands:
- tapOn: "Delete"
- tapOn:
text: "Delete"
optional: true
- waitForAnimationToEnd:
timeout: 3000
- extendedWaitUntil:
visible: "Delete this item.*"
timeout: 10000
- tapOn:
text: "Delete"
below: "Delete this item.*"
- extendedWaitUntil:
notVisible: ".*${output.folder}.*"
timeout: 20000
+10 -5
View File
@@ -11,6 +11,11 @@ tags:
visible: "Search settings"
timeout: 20000
- takeScreenshot: settings-00-list
# The search also lists the settings inside each page under it, so a common
# word ("Folders", "Calendar") pushes the page's row below the keyboard.
# Each Q matches its page near the top. A PROOF the search lists under the
# row would pass on the iPad without the page, so those differ. The Themes
# cards are one element each, named by the theme: check the page's text.
- 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, .*", Q: "Language", PROOF: "Date Format"}}
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: notifications, ROW: "Notifications, .*", Q: "Notifications", PROOF: "Email Notifications"}}
@@ -22,16 +27,16 @@ tags:
- 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: folders, ROW: "Folders, .*", Q: "New folder", 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: content, ROW: "Content & Senders, .*", Q: "Content &", PROOF: "External Content"}}
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: calendar, ROW: "Calendar, .*", Q: "Week start", PROOF: "Default view"}}
- runFlow: {file: ../subflows/settings-page.yaml, env: {NAME: contacts, ROW: "Contacts, .*", Q: "Group by", PROOF: "Sort by last name"}}
- 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: themes, ROW: "Themes, .*", Q: "Themes", PROOF: "Pick a colou?r theme.*"}}
- 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:
+4 -1
View File
@@ -10,8 +10,11 @@ tags:
env:
CAMERA: deny
- tapOn: "Scan a sign-in code.*"
# Denied for good: "Camera access is disabled..."; not asked yet (the
# simulator doesn't always apply the denial up front): "Bulwark Mail needs
# camera access..." (English catalog) or the built-in fallback wording.
- extendedWaitUntil:
visible: "Camera access is.*|Scanning a sign-in QR code uses the camera.*"
visible: "Camera access is.*|.*needs camera access.*|Scanning a sign-in QR code uses the camera.*"
timeout: 15000
- takeScreenshot: scanner-01-denied
- tapOn: "Close"
@@ -11,9 +11,13 @@ appId: org.bulwarkmail.mobile
- setOrientation: PORTRAIT
- waitForAnimationToEnd:
timeout: 3000
# Maestro repeats a tap that seems to change nothing; here the repeat
# landed on the app once the alert was gone (a day in the calendar's
# month grid, which opened that day). Tap once.
- tapOn:
text: "Allow"
optional: true
retryTapIfNoChange: false
- runFlow:
when:
true: ${ORIENTATION == 'landscape'}
+12 -3
View File
@@ -3,6 +3,9 @@
# covers it (the tap then opens another tab): on the phone, scroll it to the
# middle. The iPad's list is a column Maestro's scroll (in the middle of the
# screen) doesn't reach; its rows fit on screen.
# The avatar's "Select <sender>: <subject>" checkbox mentions the subject
# too, and a tap on it selects the row instead of opening it (#91): match
# the row itself by its id.
appId: org.bulwarkmail.mobile
---
- runFlow:
@@ -10,7 +13,9 @@ appId: org.bulwarkmail.mobile
true: ${DEVICE != 'tablet'}
commands:
- scrollUntilVisible:
element: ".*${SUBJECT}.*"
element:
id: email-row
text: ".*${SUBJECT}.*"
direction: DOWN
centerElement: true
visibilityPercentage: 100
@@ -20,13 +25,17 @@ appId: org.bulwarkmail.mobile
true: ${DEVICE == 'tablet'}
commands:
- scrollUntilVisible:
element: ".*${SUBJECT}.*"
element:
id: email-row
text: ".*${SUBJECT}.*"
direction: DOWN
timeout: 30000
# A tap while the list still moves lands on another row.
- waitForAnimationToEnd:
timeout: 5000
- tapOn: ".*${SUBJECT}.*"
- tapOn:
id: email-row
text: ".*${SUBJECT}.*"
# The message toolbar's "more" button: "Reply All" shows on the phone, but
# the iPad pane has a quick reply box instead for mail to one recipient.
- extendedWaitUntil:
+8
View File
@@ -25,6 +25,14 @@ appId: org.bulwarkmail.mobile
element: "Sign out"
direction: DOWN
timeout: 30000
# The scroll can stop with the button half under the tab bar, where
# a tap reaches the tab bar instead: scroll on to the end of the list.
- swipe:
start: 50%,70%
end: 50%,30%
duration: 400
- waitForAnimationToEnd:
timeout: 3000
- tapOn: "Sign out"
- extendedWaitUntil:
visible: "The account is removed from this device.*"
+7 -2
View File
@@ -4,10 +4,15 @@
# field and type again. The caret is still at the end of what was just
# typed, so eraseText (which deletes before the caret) empties it; selecting
# the text with a long press didn't work and appended instead. SECRET=yes
# skips the check (a password field shows dots).
# skips the check (a password field shows dots). TAP=no types into the
# field that has the focus already.
appId: org.bulwarkmail.mobile
---
- tapOn: ${HINT}
- runFlow:
when:
true: ${TAP != 'no'}
commands:
- tapOn: ${HINT}
- waitForAnimationToEnd:
timeout: 3000
- inputText: ${TEXT}
+13
View File
@@ -11,12 +11,25 @@ appId: org.bulwarkmail.mobile
env:
HINT: "user@example.com"
TEXT: ${LOGIN}
# On the iPhone, Safari's keyboard toolbar (Previous, Next, Done) can sit
# over the password field, and a tap there leaves the focus in Email (the
# password then went into Email). Its Next moves the focus to the password.
- evalScript: ${output.webNext = false}
- runFlow:
when:
visible: "Next"
commands:
- tapOn: "Next"
- waitForAnimationToEnd:
timeout: 2000
- evalScript: ${output.webNext = true}
- runFlow:
file: type-web.yaml
env:
HINT: "Enter your password"
TEXT: ${PASSWORD}
SECRET: "yes"
TAP: "${output.webNext ? 'no' : 'yes'}"
- tapOn: "Sign in"
- runFlow:
when: