also looking at this
feat: native trail maps and complete native mobile screens
#1017
60 files
+2505
-823
Review threads live on the whole diff, not on one commit, so none are shown here - a thread's line means something in the branch's final form, and painting it into an intermediate step would put it on code it is not about.
| @@ -30,6 +30,17 @@ name: Native Android trail maps and sticky save | ||
| 30 | 30 | - extendedWaitUntil: |
| 31 | 31 | visible: '^Save$' |
| 32 | 32 | timeout: 30000 |
| 33 | - scrollUntilVisible: | |
| 34 | element: | |
| 35 | id: trail-elevation-slider | |
| 36 | direction: DOWN | |
| 37 | - tapOn: | |
| 38 | id: trail-elevation-slider | |
| 39 | point: '99.9%,50%' | |
| 40 | - extendedWaitUntil: | |
| 41 | visible: ${INLINE_PROFILE_END_TEXT} | |
| 42 | timeout: 15000 | |
| 43 | - takeScreenshot: android-native-inline-elevation-selection | |
| 33 | 44 | - scrollUntilVisible: |
| 34 | 45 | element: |
| 35 | 46 | id: trail-map-fullscreen |
| @@ -61,6 +72,15 @@ name: Native Android trail maps and sticky save | ||
| 61 | 72 | - tapOn: |
| 62 | 73 | id: map-profile-toggle |
| 63 | 74 | - assertVisible: 'Toggle elevation profile, Elevation profile expanded' |
| 75 | - tapOn: | |
| 76 | id: map-elevation-slider | |
| 77 | point: '99.9%,50%' | |
| 78 | - extendedWaitUntil: | |
| 79 | visible: ${PROFILE_END_TEXT} | |
| 80 | timeout: 15000 | |
| 81 | - takeScreenshot: android-native-fullscreen-elevation-selection | |
| 82 | - tapOn: | |
| 83 | id: map-fit | |
| 64 | 84 | - tapOn: |
| 65 | 85 | id: map-layer |
| 66 | 86 | - tapOn: '^Satellite$' |
| @@ -85,6 +85,13 @@ export async function runNativeTrailAndroidE2e(): Promise<void> { | ||
| 85 | 85 | if (detail.trail?.hasGeometry) { trail = detail.trail; break } |
| 86 | 86 | } |
| 87 | 87 | if (!trail) throw new Error('Native Android QA requires its seeded recorded route.') |
| 88 | const profileResponse = await fetch(`${origin}/api/trails/${trail.id}/profile`) | |
| 89 | if (!profileResponse.ok) throw new Error('Native Android QA requires the trail profile endpoint to be available.') | |
| 90 | const profile = (await profileResponse.json()) as { points: Array<{ mi: number, ft: number | null }> } | |
| 91 | const endpoint = profile.points?.at(-1) | |
| 92 | if (!endpoint || profile.points.length < 2 || !Number.isFinite(endpoint.mi) || (endpoint.ft !== null && !Number.isFinite(endpoint.ft))) | |
| 93 | throw new Error('Native Android QA requires a selectable recorded route profile.') | |
| 94 | const endElevation = endpoint.ft === null ? 'elevation unavailable' : `${endpoint.ft} ft` | |
| 88 | 95 | const first = primaryRoutePart([...decodeRouteParts(trail.geometry), ...(Array.isArray(trail.pieceRoutes) ? trail.pieceRoutes : [])])[0] |
| 89 | 96 | if (!first) throw new Error('The seeded trail has no recorded route start.') |
| 90 | 97 | await adb(['emu', 'geo', 'fix', String(first[1]), String(first[0])], 'seed-location') |
| @@ -102,6 +109,8 @@ export async function runNativeTrailAndroidE2e(): Promise<void> { | ||
| 102 | 109 | APP_ID: mobile.android.packageName, TRAIL_ID: String(trail.id), TRAIL_NAME: trail.name, |
| 103 | 110 | E2E_EMAIL: QA_SOCIAL_ACCOUNT.email, E2E_PASSWORD: QA_SOCIAL_ACCOUNT.sessionSecret, |
| 104 | 111 | REVIEW_TEXT: review, EDITED_REVIEW_TEXT: edit, |
| 112 | PROFILE_END_TEXT: `${endpoint.mi.toFixed(2)} mi · ${endElevation}`, | |
| 113 | INLINE_PROFILE_END_TEXT: `${endpoint.mi.toFixed(2)} mi, ${endElevation}`, | |
| 105 | 114 | GPX_NAME: `${trail.name.replace(/[^a-z0-9]+/gi, '-').replace(/^-|-$/g, '').slice(0, 80)}.gpx`, |
| 106 | 115 | } |
| 107 | 116 | let reviewId: number | undefined |