From 869966e518154653a858d1e53925f88d7d9d3797 Mon Sep 17 00:00:00 2001 From: Anmol Verma Date: Sun, 12 Apr 2026 00:00:26 +0530 Subject: [PATCH 01/17] add testIDs and fix iOS navigation in Maestro flows --- .../flows/dashboard/dashboard-tiles-flow.yaml | 6 +- .../flows/packs/add-item-actions-flow.yaml | 34 +++++++--- .maestro/flows/packs/create-pack-flow.yaml | 4 +- .maestro/flows/packs/pack-detail-flow.yaml | 16 ++++- apps/expo/app/auth/(login)/index.tsx | 62 ++++++++++--------- .../features/ai/components/AIChatTile.tsx | 1 + .../features/packs/components/PackCard.tsx | 1 + .../features/trips/components/TripForm.tsx | 2 + apps/expo/lib/testIds.ts | 10 +++ 9 files changed, 91 insertions(+), 45 deletions(-) diff --git a/.maestro/flows/dashboard/dashboard-tiles-flow.yaml b/.maestro/flows/dashboard/dashboard-tiles-flow.yaml index 26db78b5c2..f4280fd513 100644 --- a/.maestro/flows/dashboard/dashboard-tiles-flow.yaml +++ b/.maestro/flows/dashboard/dashboard-tiles-flow.yaml @@ -5,16 +5,16 @@ appId: ${APP_ID} # Navigate to Dashboard (home tab) - tapOn: - text: "Home" + text: "Dashboard" - waitForAnimationToEnd # Verify key dashboard tiles are present - scrollUntilVisible: element: - text: "PackRat AI" + id: "dashboard-tile-packrat-ai" direction: DOWN - assertVisible: - text: "PackRat AI" + id: "dashboard-tile-packrat-ai" # Scroll to check more tiles - scrollUntilVisible: diff --git a/.maestro/flows/packs/add-item-actions-flow.yaml b/.maestro/flows/packs/add-item-actions-flow.yaml index 3fb75568eb..cea65dd36b 100644 --- a/.maestro/flows/packs/add-item-actions-flow.yaml +++ b/.maestro/flows/packs/add-item-actions-flow.yaml @@ -10,7 +10,7 @@ appId: ${APP_ID} # Tap the test pack - tapOn: - text: ${PACK_NAME} + id: "pack-row-${PACK_NAME}" - waitForAnimationToEnd # Tap Add Item button @@ -26,10 +26,30 @@ appId: ${APP_ID} - assertVisible: id: "add-from-catalog-option" -# Dismiss the bottom sheet -- back -- waitForAnimationToEnd - -# Go back to packs list -- back +# Dismiss the bottom sheet - platform specific +- runFlow: + when: + platform: Android + commands: + - back +- runFlow: + when: + platform: iOS + commands: + - swipe: + direction: DOWN + duration: 300 - waitForAnimationToEnd +# Go back to packs list - platform specific +- runFlow: + when: + platform: Android + commands: + - back +- runFlow: + when: + platform: iOS + commands: + - tapOn: + text: ".*Back.*" +- waitForAnimationToEnd \ No newline at end of file diff --git a/.maestro/flows/packs/create-pack-flow.yaml b/.maestro/flows/packs/create-pack-flow.yaml index e6cf6b4284..0ed6693307 100644 --- a/.maestro/flows/packs/create-pack-flow.yaml +++ b/.maestro/flows/packs/create-pack-flow.yaml @@ -46,9 +46,9 @@ appId: ${APP_ID} id: "create-pack-button" - scrollUntilVisible: element: - text: ${PACK_NAME} + id: "pack-row-${PACK_NAME}" direction: DOWN timeout: 60000 speed: 99 - assertVisible: - text: ${PACK_NAME} + id: "pack-row-${PACK_NAME}" diff --git a/.maestro/flows/packs/pack-detail-flow.yaml b/.maestro/flows/packs/pack-detail-flow.yaml index c34e961b74..c7d9e5072a 100644 --- a/.maestro/flows/packs/pack-detail-flow.yaml +++ b/.maestro/flows/packs/pack-detail-flow.yaml @@ -10,7 +10,7 @@ appId: ${APP_ID} # Tap the test pack - tapOn: - text: ${PACK_NAME} + id: "pack-row-${PACK_NAME}" - waitForAnimationToEnd # Assert pack detail elements via testIDs @@ -25,6 +25,16 @@ appId: ${APP_ID} - assertVisible: text: ${PACK_NAME} -# Go back to packs list -- back +# Go back to packs list - platform specific +- runFlow: + when: + platform: Android + commands: + - back +- runFlow: + when: + platform: iOS + commands: + - tapOn: + text: ".*Back.*" - waitForAnimationToEnd diff --git a/apps/expo/app/auth/(login)/index.tsx b/apps/expo/app/auth/(login)/index.tsx index 18c0152b43..db0725bc1f 100644 --- a/apps/expo/app/auth/(login)/index.tsx +++ b/apps/expo/app/auth/(login)/index.tsx @@ -116,36 +116,38 @@ export default function LoginScreen() { accessible={Platform.OS === 'ios' ? false : undefined} > - - {(field) => ( - KeyboardController.setFocusTo('next')} - submitBehavior="submit" - autoFocus - onFocus={() => setFocusedTextField('email')} - onBlur={() => { - setFocusedTextField(null); - field.handleBlur(); - }} - keyboardType="email-address" - textContentType="emailAddress" - returnKeyType="next" - value={field.state.value} - onChangeText={field.handleChange} - errorMessage={field.state.meta.errors[0]?.message} - /> - )} - + + + {(field) => ( + KeyboardController.setFocusTo('next')} + submitBehavior="submit" + autoFocus + onFocus={() => setFocusedTextField('email')} + onBlur={() => { + setFocusedTextField(null); + field.handleBlur(); + }} + keyboardType="email-address" + textContentType="emailAddress" + returnKeyType="next" + value={field.state.value} + onChangeText={field.handleChange} + errorMessage={field.state.meta.errors[0]?.message} + /> + )} + + diff --git a/apps/expo/features/ai/components/AIChatTile.tsx b/apps/expo/features/ai/components/AIChatTile.tsx index a3fbad23a1..2ebbccb6a3 100644 --- a/apps/expo/features/ai/components/AIChatTile.tsx +++ b/apps/expo/features/ai/components/AIChatTile.tsx @@ -38,6 +38,7 @@ export function AIChatTile() { } + testID="dashboard-tile-packrat-ai" // add this item={{ title: t('ai.packratAI'), }} diff --git a/apps/expo/features/packs/components/PackCard.tsx b/apps/expo/features/packs/components/PackCard.tsx index 64eb89870f..f2327e52d3 100644 --- a/apps/expo/features/packs/components/PackCard.tsx +++ b/apps/expo/features/packs/components/PackCard.tsx @@ -85,6 +85,7 @@ export function PackCard({ pack: packArg, onPress, isGenUI = false }: PackCardPr return ( onPress?.(pack)} > {pack.image && ( diff --git a/apps/expo/features/trips/components/TripForm.tsx b/apps/expo/features/trips/components/TripForm.tsx index 2e38c31c03..cf30833619 100644 --- a/apps/expo/features/trips/components/TripForm.tsx +++ b/apps/expo/features/trips/components/TripForm.tsx @@ -281,6 +281,7 @@ export const TripForm = ({ trip }: { trip?: Trip }) => { return ( setShowStartPicker(true)} className={`flex-row items-center justify-between border rounded-lg p-3 bg-card ${ field.state.meta.errors.length > 0 ? 'border-destructive' : 'border-border' @@ -324,6 +325,7 @@ export const TripForm = ({ trip }: { trip?: Trip }) => { setShowEndPicker(true)} + testID={TestIds.EndDateInput} className={`flex-row items-center justify-between border rounded-lg p-3 bg-card ${ field.state.meta.errors.length > 0 ? 'border-destructive' : 'border-border' }`} diff --git a/apps/expo/lib/testIds.ts b/apps/expo/lib/testIds.ts index d4d2e41363..1867ba8d0b 100644 --- a/apps/expo/lib/testIds.ts +++ b/apps/expo/lib/testIds.ts @@ -13,14 +13,24 @@ export enum TestIds { PasswordInput = 'password-input', ContinueButton = 'continue-button', + //Dashboard + DashboardTilePackratAI = 'dashboard-tile-packrat-ai', + // Trips CreateTripButton = 'create-trip-button', SubmitTripButton = 'submit-trip-button', + // Trip form + StartDateInput = 'start-date-input', + EndDateInput = 'end-date-input', + // Packs CreatePackButton = 'create-pack-button', SubmitPackButton = 'submit-pack-button', + // Packs list rows + PackRow = 'pack-row-', + // Pack detail AskAIButton = 'ask-ai-button', AddItemButton = 'add-item-button', From ee3b44a5eaab0f562f1ae77be8c49c3d28e474ac Mon Sep 17 00:00:00 2001 From: Anmol Verma Date: Mon, 13 Apr 2026 13:57:47 +0530 Subject: [PATCH 02/17] fix all iOS Maestro flows --- .github/scripts/e2e.sh | 5 +- .../flows/catalog/catalog-browse-flow.yaml | 18 ++-- .../catalog/catalog-item-detail-flow.yaml | 47 +++++++---- .../negative/empty-pack-submit-flow.yaml | 26 +++--- .../negative/empty-trip-submit-flow.yaml | 26 +++--- .maestro/flows/trips/create-trip-flow.yaml | 83 ++++++++++--------- .maestro/flows/trips/trip-detail-flow.yaml | 16 +++- apps/expo/config.ts | 2 +- .../catalog/components/CatalogItemCard.tsx | 2 +- .../features/trips/components/TripCard.tsx | 1 + apps/expo/lib/testIds.ts | 4 + 11 files changed, 134 insertions(+), 96 deletions(-) diff --git a/.github/scripts/e2e.sh b/.github/scripts/e2e.sh index ad82b15456..7ef585cd81 100644 --- a/.github/scripts/e2e.sh +++ b/.github/scripts/e2e.sh @@ -17,7 +17,7 @@ UNIQUE_ID=$(date +%s) if [ "$PLATFORM" = "ios" ]; then START_DATE=$(date -j -v+7d +"%Y-%m-%d") END_DATE=$(date -j -v+14d +"%Y-%m-%d") - + TODAY_DATE=$(date -j +"%-d %b %Y") # e.g. "11 Apr 2026" get_month() { date -j -f "%Y-%m-%d" "$1" +"%B"; } get_day() { date -j -f "%Y-%m-%d" "$1" +"%-d"; } get_year() { date -j -f "%Y-%m-%d" "$1" +"%Y"; } @@ -25,7 +25,7 @@ if [ "$PLATFORM" = "ios" ]; then else START_DATE=$(date -d "+7 days" +"%Y-%m-%d") END_DATE=$(date -d "+14 days" +"%Y-%m-%d") - + TODAY_DATE=$(date +"%-d %b %Y") get_month() { date -d "$1" +"%B"; } get_day() { date -d "$1" +"%-d"; } get_year() { date -d "$1" +"%Y"; } @@ -53,6 +53,7 @@ if [ "$PLATFORM" = "ios" ]; then -e END_MONTH="$(get_month "$END_DATE")" \ -e END_DAY="$(get_day "$END_DATE")" \ -e END_TAPS="$END_TAPS" \ + -e TODAY_DATE="$TODAY_DATE" \ .maestro/master-flow.yaml else maestro test --config .maestro/config-android.yaml "$@" \ diff --git a/.maestro/flows/catalog/catalog-browse-flow.yaml b/.maestro/flows/catalog/catalog-browse-flow.yaml index 141038e65f..01886c127c 100644 --- a/.maestro/flows/catalog/catalog-browse-flow.yaml +++ b/.maestro/flows/catalog/catalog-browse-flow.yaml @@ -2,23 +2,21 @@ appId: ${APP_ID} --- # Catalog Browse Flow: Verify catalog tab loads items and categories - waitForAnimationToEnd - -# Navigate to Catalog tab - tapOn: text: "Catalog" - waitForAnimationToEnd - -# Assert catalog loaded with items - assertVisible: text: "Catalog" - -# Scroll to verify items render +- assertVisible: + text: "All" +- assertVisible: + text: ".*items.*" - scrollUntilVisible: element: - text: ".*\\$.*" + text: "Showing.*items" direction: DOWN - -# Go back to a stable state + timeout: 10000 + speed: 20 - tapOn: text: "Packs" -- waitForAnimationToEnd +- waitForAnimationToEnd \ No newline at end of file diff --git a/.maestro/flows/catalog/catalog-item-detail-flow.yaml b/.maestro/flows/catalog/catalog-item-detail-flow.yaml index 19b3cd47a7..6516bd2d32 100644 --- a/.maestro/flows/catalog/catalog-item-detail-flow.yaml +++ b/.maestro/flows/catalog/catalog-item-detail-flow.yaml @@ -2,29 +2,44 @@ appId: ${APP_ID} --- # Catalog Item Detail Flow: Tap a catalog item and verify detail page - waitForAnimationToEnd - -# Navigate to Catalog tab - tapOn: text: "Catalog" - waitForAnimationToEnd - -# Scroll to find an item and tap it +# Wait for items to load +- assertVisible: + text: ".*items.*" +# Tap first visible item using index +- tapOn: + id: "catalog-item" + index: 0 +- waitForAnimationToEnd +# Scroll down to find action buttons - scrollUntilVisible: element: - text: ".*\\$.*" + id: "add-to-pack-button" direction: DOWN - -# Tap the first visible priced item -- tapOn: - text: ".*\\$.*" -- waitForAnimationToEnd - -# Assert item detail elements via testIDs + speed: 10 + timeout: 10000 - assertVisible: id: "add-to-pack-button" +- scrollUntilVisible: + element: + id: "view-retailer-button" + direction: DOWN + speed: 10 + timeout: 10000 - assertVisible: id: "view-retailer-button" - -# Go back -- back -- waitForAnimationToEnd +# Go back - platform specific +- runFlow: + when: + platform: Android + commands: + - back +- runFlow: + when: + platform: iOS + commands: + - tapOn: + text: ".*Back.*" +- waitForAnimationToEnd \ No newline at end of file diff --git a/.maestro/flows/negative/empty-pack-submit-flow.yaml b/.maestro/flows/negative/empty-pack-submit-flow.yaml index b3a34962ef..2564865a31 100644 --- a/.maestro/flows/negative/empty-pack-submit-flow.yaml +++ b/.maestro/flows/negative/empty-pack-submit-flow.yaml @@ -2,26 +2,30 @@ appId: ${APP_ID} --- # Empty Pack Submit Flow: Verify form validation prevents empty pack creation - waitForAnimationToEnd - -# Navigate to Packs tab - tapOn: text: "Packs" - waitForAnimationToEnd - -# Tap create pack button - tapOn: id: "create-pack-button" - waitForAnimationToEnd - # Try to submit without filling any fields - tapOn: id: "submit-pack-button" - waitForAnimationToEnd - -# Should still be on the form (submit button still visible = didn't navigate away) +# Should still be on the form (submit button still visible = validation blocked submit) - assertVisible: id: "submit-pack-button" - -# Go back -- back -- waitForAnimationToEnd +# Dismiss the sheet - platform specific +- runFlow: + when: + platform: Android + commands: + - back +- runFlow: + when: + platform: iOS + commands: + - swipe: + direction: DOWN + duration: 300 +- waitForAnimationToEnd \ No newline at end of file diff --git a/.maestro/flows/negative/empty-trip-submit-flow.yaml b/.maestro/flows/negative/empty-trip-submit-flow.yaml index bee3716982..4907fdc477 100644 --- a/.maestro/flows/negative/empty-trip-submit-flow.yaml +++ b/.maestro/flows/negative/empty-trip-submit-flow.yaml @@ -2,26 +2,30 @@ appId: ${APP_ID} --- # Empty Trip Submit Flow: Verify form validation prevents empty trip creation - waitForAnimationToEnd - -# Navigate to Trips tab - tapOn: text: "Trips" - waitForAnimationToEnd - -# Tap create trip button - tapOn: id: "create-trip-button" - waitForAnimationToEnd - # Try to submit without filling any fields - tapOn: id: "submit-trip-button" - waitForAnimationToEnd - -# Should still be on the form +# Should still be on the form (validation blocked submit) - assertVisible: id: "submit-trip-button" - -# Go back -- back -- waitForAnimationToEnd +# Dismiss - platform specific +- runFlow: + when: + platform: Android + commands: + - back +- runFlow: + when: + platform: iOS + commands: + - swipe: + direction: DOWN + duration: 300 +- waitForAnimationToEnd \ No newline at end of file diff --git a/.maestro/flows/trips/create-trip-flow.yaml b/.maestro/flows/trips/create-trip-flow.yaml index ca9b1ce285..766ce38e41 100644 --- a/.maestro/flows/trips/create-trip-flow.yaml +++ b/.maestro/flows/trips/create-trip-flow.yaml @@ -1,85 +1,86 @@ appId: ${APP_ID} --- -# Create Trip Flow: Navigate to trips tab and create a new trip +# Create Trip Flow - waitForAnimationToEnd - -# Navigate to the Trips tab - tapOn: text: "Trips" - - waitForAnimationToEnd - -# Tap the header "+" button (testID: create-trip-button) to open the trip creation form - tapOn: id: "create-trip-button" - - waitForAnimationToEnd - -# Fill in the Trip Name field - tapOn: text: "Trip Name" - inputText: ${TRIP_NAME} - -# Fill in the Description field - tapOn: text: "Description" - inputText: "Created by Maestro E2E test" - -# Dismiss the keyboard before submitting +# Tap neutral area to dismiss keyboard on iOS multiline field +- tapOn: + text: "TRIP DETAILS" +- waitForAnimationToEnd - hideKeyboard +- waitForAnimationToEnd # --- Start Date --- - tapOn: - text: "Start Date" + id: "start-date-input" - waitForAnimationToEnd - -- repeat: - times: ${START_TAPS} - commands: - - tapOn: "Next Month" - - runFlow: when: platform: Android commands: - - tapOn: "${START_DAY} ${START_MONTH} ${START_YEAR}" - - tapOn: "OK" - + - repeat: + times: ${START_TAPS} + commands: + - tapOn: + text: "Next Month" + - tapOn: + text: "${START_DAY} ${START_MONTH} ${START_YEAR}" + - tapOn: + text: "OK" - runFlow: when: platform: iOS commands: - - tapOn: "${START_MONTH} ${START_DAY}" - tapOn: - point: "5%,5%" - + text: "${TODAY_DATE}" + - waitForAnimationToEnd + - tapOn: + text: "${START_DAY}" + - waitForAnimationToEnd + - tapOn: + point: "50%,90%" - waitForAnimationToEnd # --- End Date --- - tapOn: - text: "End Date" + id: "end-date-input" - waitForAnimationToEnd - -- repeat: - times: ${END_TAPS} - commands: - - tapOn: "Next Month" - - runFlow: when: platform: Android commands: - - tapOn: "${END_DAY} ${END_MONTH} ${END_YEAR}" - - tapOn: "OK" - + - repeat: + times: ${END_TAPS} + commands: + - tapOn: + text: "Next Month" + - tapOn: + text: "${END_DAY} ${END_MONTH} ${END_YEAR}" + - tapOn: + text: "OK" - runFlow: when: platform: iOS commands: - - tapOn: "${END_MONTH} ${END_DAY}" - tapOn: - point: "5%,5%" - + text: "${TODAY_DATE}" + - waitForAnimationToEnd + - tapOn: + text: "${END_DAY}" + - waitForAnimationToEnd + - tapOn: + point: "50%,90%" - waitForAnimationToEnd # --- Submit --- @@ -100,10 +101,10 @@ appId: ${APP_ID} id: "create-trip-button" - scrollUntilVisible: element: - text: ${TRIP_NAME} + id: "trip-row-${TRIP_NAME}" direction: DOWN timeout: 60000 speed: 99 - assertVisible: - text: ${TRIP_NAME} + id: "trip-row-${TRIP_NAME}" diff --git a/.maestro/flows/trips/trip-detail-flow.yaml b/.maestro/flows/trips/trip-detail-flow.yaml index 92be7ddf57..d4f223f8e6 100644 --- a/.maestro/flows/trips/trip-detail-flow.yaml +++ b/.maestro/flows/trips/trip-detail-flow.yaml @@ -10,13 +10,23 @@ appId: ${APP_ID} # Tap the test trip - tapOn: - text: ${TRIP_NAME} + id: "trip-row-${TRIP_NAME}" - waitForAnimationToEnd # Verify trip name is shown - assertVisible: text: ${TRIP_NAME} -# Go back to trips list -- back +# Go back to trips list - platform specific +- runFlow: + when: + platform: Android + commands: + - back +- runFlow: + when: + platform: iOS + commands: + - tapOn: + text: ".*Back.*" - waitForAnimationToEnd diff --git a/apps/expo/config.ts b/apps/expo/config.ts index e2c23aa59e..98097ffec6 100644 --- a/apps/expo/config.ts +++ b/apps/expo/config.ts @@ -1,6 +1,6 @@ export const featureFlags = { enableOAuth: true, - enableTrips: false, + enableTrips: true, enablePackInsights: false, enableShoppingList: false, enableSharedPacks: false, diff --git a/apps/expo/features/catalog/components/CatalogItemCard.tsx b/apps/expo/features/catalog/components/CatalogItemCard.tsx index d38a4da7f2..de06c0e261 100644 --- a/apps/expo/features/catalog/components/CatalogItemCard.tsx +++ b/apps/expo/features/catalog/components/CatalogItemCard.tsx @@ -24,7 +24,7 @@ export function CatalogItemCard({ item, onPress }: CatalogItemCardProps) { const { t } = useTranslation(); return ( - + onPress?.(trip)} > diff --git a/apps/expo/lib/testIds.ts b/apps/expo/lib/testIds.ts index 1867ba8d0b..300f16f3c7 100644 --- a/apps/expo/lib/testIds.ts +++ b/apps/expo/lib/testIds.ts @@ -20,6 +20,9 @@ export enum TestIds { CreateTripButton = 'create-trip-button', SubmitTripButton = 'submit-trip-button', + //Trip List rows + TripRow = 'trip-row-', + // Trip form StartDateInput = 'start-date-input', EndDateInput = 'end-date-input', @@ -45,6 +48,7 @@ export enum TestIds { SignOutButton = 'sign-out-button', // Catalog item detail + CatalogItem = 'catalog-item', AddToPackButton = 'add-to-pack-button', ViewRetailerButton = 'view-retailer-button', } From 0262939c1cea936ed041755cdb1f468ee94590b0 Mon Sep 17 00:00:00 2001 From: Ibrahim Isa Jajere Date: Wed, 15 Apr 2026 12:25:31 +0100 Subject: [PATCH 03/17] fix(maestro): use placeholder text for iOS login form inputs as a workaround for interacting with elements in a modal in Maestro --- .maestro/flows/auth/login-flow.yaml | 34 +++++++++++++++++++++++----- apps/expo/app/auth/(login)/index.tsx | 22 ++++++------------ 2 files changed, 35 insertions(+), 21 deletions(-) diff --git a/.maestro/flows/auth/login-flow.yaml b/.maestro/flows/auth/login-flow.yaml index 2cbc7ba35e..7926a1c0f9 100644 --- a/.maestro/flows/auth/login-flow.yaml +++ b/.maestro/flows/auth/login-flow.yaml @@ -14,14 +14,36 @@ appId: ${APP_ID} - waitForAnimationToEnd # Fill in the email field -- tapOn: - id: "email-input" -- inputText: "${TEST_EMAIL}" +- runFlow: + when: + platform: iOS + commands: + - tapOn: + text: "Email" + - inputText: "${TEST_EMAIL}" +- runFlow: + when: + platform: Android + commands: + - tapOn: + id: "email-input" + - inputText: "${TEST_EMAIL}" # Fill in the password field -- tapOn: - id: "password-input" -- inputText: ${TEST_PASSWORD} +- runFlow: + when: + platform: iOS + commands: + - tapOn: + text: "Password" + - inputText: ${TEST_PASSWORD} +- runFlow: + when: + platform: Android + commands: + - tapOn: + id: "password-input" + - inputText: ${TEST_PASSWORD} # Dismiss the keyboard before submitting - hideKeyboard diff --git a/apps/expo/app/auth/(login)/index.tsx b/apps/expo/app/auth/(login)/index.tsx index db0725bc1f..f248710202 100644 --- a/apps/expo/app/auth/(login)/index.tsx +++ b/apps/expo/app/auth/(login)/index.tsx @@ -110,18 +110,14 @@ export default function LoginScreen() { )} -
- - + + + {(field) => ( - + {(field) => ( {Platform.OS === 'ios' ? ( - + [state.canSubmit, state.isSubmitting]}> {([canSubmit, _isSubmitting]) => ( )} - + [state.canSubmit, state.isSubmitting]}> {([canSubmit, _isSubmitting]) => (