Add nightly Maestro E2E workflow (#11181)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -44,6 +44,12 @@ appId: xyz.blueskyweb.app
|
||||
id: "e2eRefreshHome"
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
# Wait for the composer to fully open before typing. Tapping replyBtn right
|
||||
# after the previous publish can race the closing composer on Android.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 10000
|
||||
- inputText: "Reply text only"
|
||||
- tapOn:
|
||||
id: "composerPublishBtn"
|
||||
@@ -51,6 +57,11 @@ appId: xyz.blueskyweb.app
|
||||
id: "composeFAB"
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
# Wait for the composer to fully open before typing.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 10000
|
||||
- inputText: "Reply with an image"
|
||||
- tapOn:
|
||||
id: "openMediaBtn"
|
||||
@@ -63,6 +74,11 @@ appId: xyz.blueskyweb.app
|
||||
id: "composeFAB"
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
# Wait for the composer to fully open before typing.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 10000
|
||||
- inputText: "Reply with a https://example.com link card"
|
||||
- tapOn:
|
||||
id: "composerPublishBtn"
|
||||
|
||||
@@ -29,51 +29,88 @@ appId: xyz.blueskyweb.app
|
||||
id: "homeScreenFeedTabs-selector-1"
|
||||
text: "alice-favs"
|
||||
|
||||
# Set alice-favs first
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
id: "editFeedsBtn"
|
||||
- swipe:
|
||||
label: "Drag feed down"
|
||||
from:
|
||||
id: "feed-drag-handle"
|
||||
direction: "DOWN"
|
||||
duration: 1000
|
||||
- tapOn:
|
||||
label: "Save button"
|
||||
id: "saveChangesBtn"
|
||||
- tapOn: "Go back"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-0"
|
||||
text: "alice-favs"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-1"
|
||||
text: "Following"
|
||||
# Reordering feeds is driven by a drag on the feed-drag-handle. Maestro cannot
|
||||
# activate the RNGH Pan gesture from a synthetic swipe on Android (proven
|
||||
# twice - coordinate swipes never register the pan), so the reorder
|
||||
# verification below runs on iOS only. If Android drag coverage is needed,
|
||||
# revisit with the SavedFeedsA11y move buttons rather than a swipe.
|
||||
- runFlow:
|
||||
when:
|
||||
platform: iOS
|
||||
commands:
|
||||
# Set alice-favs first
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
id: "editFeedsBtn"
|
||||
- swipe:
|
||||
label: "Drag feed down"
|
||||
from:
|
||||
id: "feed-drag-handle"
|
||||
direction: "DOWN"
|
||||
duration: 1000
|
||||
- assertVisible:
|
||||
id: "saveChangesBtn"
|
||||
enabled: true
|
||||
- tapOn:
|
||||
label: "Save button"
|
||||
id: "saveChangesBtn"
|
||||
- tapOn: "Go back"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-0"
|
||||
text: "alice-favs"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-1"
|
||||
text: "Following"
|
||||
|
||||
# Set following first
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
id: "editFeedsBtn"
|
||||
- swipe:
|
||||
label: "Drag feed down"
|
||||
from:
|
||||
id: "feed-drag-handle"
|
||||
direction: "DOWN"
|
||||
duration: 1000
|
||||
- tapOn:
|
||||
label: "Save button"
|
||||
id: "saveChangesBtn"
|
||||
- tapOn: "Go back"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-0"
|
||||
text: "Following"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-1"
|
||||
text: "alice-favs"
|
||||
# Set following first
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
id: "editFeedsBtn"
|
||||
- swipe:
|
||||
label: "Drag feed down"
|
||||
from:
|
||||
id: "feed-drag-handle"
|
||||
direction: "DOWN"
|
||||
duration: 1000
|
||||
- assertVisible:
|
||||
id: "saveChangesBtn"
|
||||
enabled: true
|
||||
- tapOn:
|
||||
label: "Save button"
|
||||
id: "saveChangesBtn"
|
||||
- tapOn: "Go back"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-0"
|
||||
text: "Following"
|
||||
- assertVisible:
|
||||
id: "homeScreenFeedTabs-selector-1"
|
||||
text: "alice-favs"
|
||||
|
||||
# On Android, the reorder path above is skipped. Smoke-test that the feeds
|
||||
# edit screen opens and the pinned feeds render, then return to a valid state.
|
||||
- runFlow:
|
||||
when:
|
||||
platform: Android
|
||||
commands:
|
||||
- tapOn: "Open drawer menu"
|
||||
- tapOn:
|
||||
id: "menuItemButton-Feeds"
|
||||
- tapOn:
|
||||
id: "editFeedsBtn"
|
||||
- assertVisible: "Following"
|
||||
- assertVisible: "alice-favs"
|
||||
# Two back presses to reach Home: the first pops the saved-feeds editor
|
||||
# back to the Feeds screen, the second pops Feeds back to Home. On iOS the
|
||||
# equivalent path saves changes first (saveChangesBtn calls
|
||||
# navigation.goBack), so a single "Go back" there already lands on Home.
|
||||
# This Android smoke branch never saves, so it needs the extra pop to
|
||||
# leave the screen state on Home, which the shared steps below expect.
|
||||
- tapOn: "Go back"
|
||||
- tapOn: "Go back"
|
||||
|
||||
# Remove following
|
||||
- tapOn: "Open drawer menu"
|
||||
|
||||
@@ -15,6 +15,24 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn:
|
||||
id: "e2eStartOnboarding"
|
||||
- tapOn: "Open avatar creator"
|
||||
# The avatar-creator bottom sheet (Dialog.Inner, non-scrollable) opens only
|
||||
# half-expanded on the short E2E emulator (720x1600), a ~220px sliver with the
|
||||
# emoji grid below the fold. It is NOT a scroll view, so scrollUntilVisible's
|
||||
# swipe grabs the sheet's own drag gesture and flings it closed. Instead, drag
|
||||
# the sheet upward to expand it to full height, which brings the picker into
|
||||
# view. iOS opens the sheet fully already, so this is Android-only.
|
||||
- runFlow:
|
||||
when:
|
||||
platform: Android
|
||||
commands:
|
||||
- swipe:
|
||||
label: "Drag the bottom sheet up to expand it"
|
||||
start: "50%, 90%"
|
||||
end: "50%, 20%"
|
||||
duration: 600
|
||||
- extendedWaitUntil:
|
||||
visible: "Select an emoji"
|
||||
timeout: 10000
|
||||
- tapOn: "Select the zap emoji as your avatar"
|
||||
- tapOn:
|
||||
label: "Tap on yellow"
|
||||
@@ -22,6 +40,20 @@ appId: xyz.blueskyweb.app
|
||||
- tapOn: "Done"
|
||||
- waitForAnimationToEnd
|
||||
- tapOn: "Select an avatar"
|
||||
# Reopening the creator sheet lands on the same half-expanded sliver on
|
||||
# Android, so expand it again before reaching for the emoji grid. No-op on iOS.
|
||||
- runFlow:
|
||||
when:
|
||||
platform: Android
|
||||
commands:
|
||||
- swipe:
|
||||
label: "Drag the bottom sheet up to expand it"
|
||||
start: "50%, 90%"
|
||||
end: "50%, 20%"
|
||||
duration: 600
|
||||
- extendedWaitUntil:
|
||||
visible: "Select an emoji"
|
||||
timeout: 10000
|
||||
- tapOn: "Select the atom emoji as your avatar"
|
||||
- tapOn: "Done"
|
||||
- waitForAnimationToEnd
|
||||
|
||||
@@ -16,13 +16,28 @@ appId: xyz.blueskyweb.app
|
||||
id: "e2eStartOnboarding"
|
||||
- tapOn: "Select an avatar"
|
||||
- waitForAnimationToEnd
|
||||
- assertVisible: "Photos"
|
||||
- assertVisible: "Collections"
|
||||
- tapOn:
|
||||
point: "50%,22%"
|
||||
- waitForAnimationToEnd
|
||||
- tapOn: "Done"
|
||||
- waitForAnimationToEnd
|
||||
- runFlow:
|
||||
when:
|
||||
platform: iOS
|
||||
commands:
|
||||
- assertVisible: "Photos"
|
||||
- assertVisible: "Collections"
|
||||
- tapOn:
|
||||
point: "50%,22%"
|
||||
- waitForAnimationToEnd
|
||||
- tapOn: "Done"
|
||||
- waitForAnimationToEnd
|
||||
- runFlow:
|
||||
when:
|
||||
platform: Android
|
||||
commands:
|
||||
# The system photo picker opened here shows MediaStore photos, which
|
||||
# the e2e run doesn't seed (media is seeded into app-scoped storage for
|
||||
# the composer's mocked picker instead). With no photo to pick, dismiss
|
||||
# the picker and continue - onContinue falls back to the generated
|
||||
# placeholder avatar, and nothing later in the flow depends on the image.
|
||||
- back
|
||||
- waitForAnimationToEnd
|
||||
- tapOn:
|
||||
id: "onboardingContinue"
|
||||
- assertVisible: "What are your interests?"
|
||||
|
||||
@@ -45,7 +45,7 @@ appId: xyz.blueskyweb.app
|
||||
id: "editProfileSaveBtn"
|
||||
- assertNotVisible:
|
||||
id: "editProfileModal"
|
||||
- assertVisible: "Alicia"
|
||||
- assertVisible: ".*Alicia.*"
|
||||
- assertVisible: "One cool hacker"
|
||||
|
||||
# Remove display name and description via the edit profile modal
|
||||
@@ -64,7 +64,10 @@ appId: xyz.blueskyweb.app
|
||||
id: "editProfileSaveBtn"
|
||||
- assertNotVisible:
|
||||
id: "editProfileModal"
|
||||
- assertVisible: "alice.test"
|
||||
# The display-name node renders the handle as a Text with a nested badge View
|
||||
# once the display name is cleared, so the a11y text is not the bare handle
|
||||
# string on Android. Match it as a substring instead.
|
||||
- assertVisible: ".*alice\\.test.*"
|
||||
- assertNotVisible: "One cool hacker"
|
||||
|
||||
# Set avi and banner via the edit profile modal
|
||||
|
||||
@@ -22,5 +22,7 @@ appId: xyz.blueskyweb.app
|
||||
text: "Send report to Dev-env Moderation"
|
||||
- tapOn:
|
||||
id: "report:submit"
|
||||
- assertNotVisible:
|
||||
id: "report:dialog"
|
||||
- extendedWaitUntil:
|
||||
notVisible:
|
||||
id: "report:dialog"
|
||||
timeout: 20000
|
||||
|
||||
@@ -22,5 +22,7 @@ appId: xyz.blueskyweb.app
|
||||
text: "Send report to Dev-env Moderation"
|
||||
- tapOn:
|
||||
id: "report:submit"
|
||||
- assertNotVisible:
|
||||
id: "report:dialog"
|
||||
- extendedWaitUntil:
|
||||
notVisible:
|
||||
id: "report:dialog"
|
||||
timeout: 20000
|
||||
|
||||
@@ -39,5 +39,7 @@ appId: xyz.blueskyweb.app
|
||||
text: Your report will be sent to Dev-env Moderation.*
|
||||
- tapOn:
|
||||
id: "report:submit"
|
||||
- assertNotVisible:
|
||||
id: "report:dialog"
|
||||
- extendedWaitUntil:
|
||||
notVisible:
|
||||
id: "report:dialog"
|
||||
timeout: 20000
|
||||
|
||||
@@ -29,5 +29,7 @@ appId: xyz.blueskyweb.app
|
||||
- hideKeyboard
|
||||
- tapOn:
|
||||
id: "report:submit"
|
||||
- assertNotVisible:
|
||||
id: "report:dialog"
|
||||
- extendedWaitUntil:
|
||||
notVisible:
|
||||
id: "report:dialog"
|
||||
timeout: 20000
|
||||
|
||||
@@ -20,6 +20,12 @@ appId: xyz.blueskyweb.app
|
||||
- inputText: "Test thread"
|
||||
- tapOn:
|
||||
id: "composerPublishBtn"
|
||||
# Wait for the composer to close and the home feed to settle before signing
|
||||
# out. Without a settle guard the next action can race the closing composer.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composeFAB"
|
||||
timeout: 10000
|
||||
|
||||
# Login, reply to the thread, and log out
|
||||
- tapOn:
|
||||
@@ -31,9 +37,19 @@ appId: xyz.blueskyweb.app
|
||||
id: "viewHeaderHomeFeedPrefsBtn"
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
# Wait for the composer to fully open before typing.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 10000
|
||||
- inputText: "Reply 1"
|
||||
- tapOn:
|
||||
id: "composerPublishBtn"
|
||||
# Wait for the composer to close before signing out.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composeFAB"
|
||||
timeout: 10000
|
||||
|
||||
# Login, confirm notification exists, mute thread, and log out
|
||||
- tapOn:
|
||||
@@ -45,10 +61,8 @@ appId: xyz.blueskyweb.app
|
||||
id: "viewHeaderHomeFeedPrefsBtn"
|
||||
- tapOn:
|
||||
id: "bottomBarNotificationsBtn"
|
||||
- assertVisible:
|
||||
id: "feedItem-by-bob.test"
|
||||
- tapOn:
|
||||
id: "feedItem-by-bob.test"
|
||||
- assertVisible: ".*Reply 1.*"
|
||||
- tapOn: ".*Reply 1.*"
|
||||
- tapOn:
|
||||
id: "postDropdownBtn"
|
||||
childOf:
|
||||
@@ -67,16 +81,78 @@ appId: xyz.blueskyweb.app
|
||||
id: "bottomBarProfileBtn"
|
||||
- tapOn:
|
||||
id: "profilePager-selector-1"
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
# Both replies target the thread root ("Test thread" by alice), which sits at
|
||||
# the top of bob's Replies tab. That tab renders each post in the thread with
|
||||
# its own replyBtn, so scope the tap to the root post's card
|
||||
# (feedItem-by-alice.test) rather than relying on which replyBtn Maestro picks
|
||||
# first. This keeps both reply taps deterministic regardless of list order or
|
||||
# how many posts have rendered.
|
||||
#
|
||||
# Even with the close-gating below, the replyBtn tap can land on a recycled list
|
||||
# row while the author feed re-renders after a publish, and be swallowed so the
|
||||
# composer never opens. Wrapping the tap + open-wait in retry makes opening the
|
||||
# composer idempotent: a swallowed tap just re-taps until the publish button
|
||||
# appears. A first-try success does not retry.
|
||||
- retry:
|
||||
maxRetries: 3
|
||||
commands:
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
childOf:
|
||||
id: "feedItem-by-alice.test"
|
||||
# Wait for the composer to fully open before typing.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 10000
|
||||
- inputText: "Reply 2"
|
||||
- tapOn:
|
||||
id: "composerPublishBtn"
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
# Wait for the composer to actually close before opening it again. replyBtn
|
||||
# stays in the accessibility tree behind the open composer sheet, so waiting on
|
||||
# its visibility returns immediately and does not gate on the close animation or
|
||||
# the author-feed re-render that follows a post - the next replyBtn tap then
|
||||
# fires mid-transition and is swallowed, so the composer never opens. Gate on
|
||||
# the publish button disappearing (the composer is gone), then confirm the
|
||||
# reply button underneath is back and let animations settle.
|
||||
- extendedWaitUntil:
|
||||
notVisible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 15000
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "replyBtn"
|
||||
timeout: 10000
|
||||
- waitForAnimationToEnd
|
||||
# As with Reply 2: even after gating on the composer close, this tap can hit a
|
||||
# recycled row during the post-publish feed re-render and be swallowed, so wrap
|
||||
# the open in retry to make it idempotent.
|
||||
- retry:
|
||||
maxRetries: 3
|
||||
commands:
|
||||
- tapOn:
|
||||
id: "replyBtn"
|
||||
childOf:
|
||||
id: "feedItem-by-alice.test"
|
||||
# Wait for the composer to fully open before typing.
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 10000
|
||||
- inputText: "Reply 3"
|
||||
- tapOn:
|
||||
id: "composerPublishBtn"
|
||||
# Wait for the composer to actually close before signing out. As above,
|
||||
# replyBtn stays visible behind the sheet, so gate on the publish button
|
||||
# disappearing first, then confirm the reply button underneath has returned.
|
||||
- extendedWaitUntil:
|
||||
notVisible:
|
||||
id: "composerPublishBtn"
|
||||
timeout: 15000
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: "replyBtn"
|
||||
timeout: 10000
|
||||
|
||||
# Login, confirm notifications dont exist, unmute the thread, ~~confirm notifications exist~~
|
||||
# Mute thread behaviour no longer change old notifications after muting/unmuting a thread -sfn
|
||||
@@ -92,10 +168,7 @@ appId: xyz.blueskyweb.app
|
||||
- assertVisible: ".*Reply 1.*"
|
||||
- assertNotVisible: ".*Reply 2.*"
|
||||
- assertNotVisible: ".*Reply 3.*"
|
||||
- assertVisible:
|
||||
id: "feedItem-by-bob.test"
|
||||
- tapOn:
|
||||
id: "feedItem-by-bob.test"
|
||||
- tapOn: ".*Reply 1.*"
|
||||
- tapOn:
|
||||
id: "postDropdownBtn"
|
||||
childOf:
|
||||
|
||||
+17
-13
@@ -9,23 +9,27 @@ appId: xyz.blueskyweb.app
|
||||
when:
|
||||
platform: iOS
|
||||
commands:
|
||||
- openLink: "exp+bluesky://expo-development-client/?url=http%3A%2F%2Flocalhost%3A8081"
|
||||
- runFlow:
|
||||
when:
|
||||
visible: 'Open in "Bluesky"'
|
||||
commands:
|
||||
- tapOn: Open
|
||||
- extendedWaitUntil:
|
||||
visible: "http://localhost:8081"
|
||||
timeout: 60000
|
||||
- tapOn: "http://localhost:8081"
|
||||
- runFlow:
|
||||
when:
|
||||
platform: Android
|
||||
commands:
|
||||
- tapOn: 'http://localhost:8081'
|
||||
- runFlow:
|
||||
label: "Dismiss Expo dev menu"
|
||||
when:
|
||||
visible: "Continue"
|
||||
commands:
|
||||
- back
|
||||
- extendedWaitUntil:
|
||||
visible: "http://10.0.2.2:8081"
|
||||
timeout: 60000
|
||||
- tapOn: "http://10.0.2.2:8081"
|
||||
- extendedWaitUntil:
|
||||
visible: "Continue"
|
||||
timeout: 180000
|
||||
- tapOn: "Continue"
|
||||
- back
|
||||
- extendedWaitUntil:
|
||||
visible:
|
||||
id: e2eProxyHeaderInput
|
||||
timeout: 180000
|
||||
- tapOn:
|
||||
id: e2eProxyHeaderInput
|
||||
- inputText: ${output.result}
|
||||
|
||||
Reference in New Issue
Block a user