Bluesky app fork with some witchin' additions 💫

[Release prep] 1.109.0 E2E fixes (#9216)

* list tests

* fix create account

* get onboarding e2e tests working

* get curate list test working

* Update thread-muting.yml

authored by samuel.fm and committed by GitHub a482dae9 25d44d89

Changed files
+69 -79
__e2e__
docs
src
components
screens
Onboarding
StepFinished
StepSuggestedAccounts
StepSuggestedStarterpacks
+6 -3
__e2e__/flows/create-account.yml
··· 3 - runScript: 4 file: ../setupServer.js 5 env: 6 - SERVER_PATH: "" 7 - runFlow: 8 file: ../setupApp.yml 9 - tapOn: ··· 25 - tapOn: 26 id: "passwordInput" 27 - inputText: "hunter22" 28 - - hideKeyboard 29 - tapOn: 30 id: "nextBtn" 31 - tapOn: ··· 34 - inputText: "e2e-test" 35 - extendedWaitUntil: 36 visible: 37 - id: "handleAvailableCheck" 38 - tapOn: 39 id: "nextBtn" 40 - assertVisible: "Give your profile a face"
··· 3 - runScript: 4 file: ../setupServer.js 5 env: 6 + SERVER_PATH: "" 7 - runFlow: 8 file: ../setupApp.yml 9 - tapOn: ··· 25 - tapOn: 26 id: "passwordInput" 27 - inputText: "hunter22" 28 + - tapOn: 29 + # hideKeyboard on ios is borked 30 + # https://docs.maestro.dev/troubleshooting/known-issues#hidekeyboard-command-is-flaky 31 + text: "Your account" 32 - tapOn: 33 id: "nextBtn" 34 - tapOn: ··· 37 - inputText: "e2e-test" 38 - extendedWaitUntil: 39 visible: 40 + id: "handleAvailableCheck" 41 - tapOn: 42 id: "nextBtn" 43 - assertVisible: "Give your profile a face"
+19 -33
__e2e__/flows/curate-lists.yml
··· 17 id: "e2eGotoLists" 18 - tapOn: 19 id: "newUserListBtn" 20 - - assertVisible: 21 - id: "createOrEditListModal" 22 - tapOn: 23 - id: "editNameInput" 24 - inputText: "Good Ppl" 25 - tapOn: 26 - id: "editDescriptionInput" 27 - inputText: "They good" 28 - tapOn: "Save" 29 - - tapOn: "Save" 30 - - assertNotVisible: 31 - id: "createOrEditListModal" 32 - tapOn: "People" 33 - assertVisible: "Good Ppl" 34 - assertVisible: "They good" 35 36 - tapOn: 37 - label: "Edit display name and description via the edit curatelist modal" 38 - point: "90%,9%" 39 - tapOn: "Edit list details" 40 - - assertVisible: 41 - id: "createOrEditListModal" 42 - tapOn: 43 - id: "editNameInput" 44 - eraseText 45 - inputText: "Bad Ppl" 46 - hideKeyboard 47 - tapOn: 48 - id: "editDescriptionInput" 49 - eraseText 50 - inputText: "They bad" 51 - tapOn: "Save" 52 - - tapOn: "Save" 53 - - assertNotVisible: 54 - id: "createOrEditListModal" 55 - assertVisible: Bad Ppl 56 - assertVisible: They bad 57 58 - tapOn: 59 - label: "Remove description via the edit curatelist modal" 60 - point: "90%,9%" 61 - tapOn: "Edit list details" 62 - - assertVisible: 63 - id: "createOrEditListModal" 64 - tapOn: 65 - id: "editDescriptionInput" 66 - eraseText 67 - tapOn: "Save" 68 - - tapOn: "Save" 69 - - assertNotVisible: 70 - id: "createOrEditListModal" 71 - assertNotVisible: 72 id: "listDescription" 73 74 - tapOn: 75 - label: "Delete the curatelist" 76 - point: "90%,9%" 77 - tapOn: "Delete List" 78 - tapOn: 79 id: "confirmBtn" ··· 82 id: "newUserListBtn" 83 - tapOn: 84 id: "newUserListBtn" 85 - - assertVisible: 86 - id: "createOrEditListModal" 87 - tapOn: 88 - id: "editNameInput" 89 - inputText: "Good Ppl" 90 - tapOn: 91 - id: "editDescriptionInput" 92 - inputText: "They good" 93 - tapOn: "Save" 94 - - tapOn: "Save" 95 - - assertNotVisible: 96 - id: "createOrEditListModal" 97 - tapOn: "People" 98 - assertVisible: "Good Ppl" 99 - assertVisible: "They good"
··· 17 id: "e2eGotoLists" 18 - tapOn: 19 id: "newUserListBtn" 20 + - waitForAnimationToEnd 21 + - assertVisible: "Create user list" 22 - tapOn: 23 + id: "editListNameInput" 24 - inputText: "Good Ppl" 25 - tapOn: 26 + id: "editListDescriptionInput" 27 - inputText: "They good" 28 - tapOn: "Save" 29 + - assertNotVisible: "Create user list" 30 - tapOn: "People" 31 - assertVisible: "Good Ppl" 32 - assertVisible: "They good" 33 34 - tapOn: 35 + id: "moreOptionsBtn" 36 - tapOn: "Edit list details" 37 + - assertVisible: "Edit user list" 38 - tapOn: 39 + id: "editListNameInput" 40 - eraseText 41 - inputText: "Bad Ppl" 42 - hideKeyboard 43 - tapOn: 44 + id: "editListDescriptionInput" 45 - eraseText 46 - inputText: "They bad" 47 - tapOn: "Save" 48 + - assertNotVisible: "Edit user list" 49 - assertVisible: Bad Ppl 50 - assertVisible: They bad 51 52 - tapOn: 53 + id: "moreOptionsBtn" 54 - tapOn: "Edit list details" 55 + - assertVisible: "Edit user list" 56 - tapOn: 57 + id: "editListDescriptionInput" 58 - eraseText 59 - tapOn: "Save" 60 + - assertNotVisible: "Edit user list" 61 - assertNotVisible: 62 id: "listDescription" 63 64 - tapOn: 65 + id: "moreOptionsBtn" 66 - tapOn: "Delete List" 67 - tapOn: 68 id: "confirmBtn" ··· 71 id: "newUserListBtn" 72 - tapOn: 73 id: "newUserListBtn" 74 + - assertVisible: "Create user list" 75 - tapOn: 76 + id: "editListNameInput" 77 - inputText: "Good Ppl" 78 - tapOn: 79 + id: "editListDescriptionInput" 80 - inputText: "They good" 81 - tapOn: "Save" 82 + - assertNotVisible: "Create user list" 83 - tapOn: "People" 84 - assertVisible: "Good Ppl" 85 - assertVisible: "They good"
+2 -6
__e2e__/flows/mod-lists.yml
··· 19 id: "moderationlistsBtn" 20 - tapOn: "New list" 21 - tapOn: 22 - id: "editNameInput" 23 - inputText: "Muted Users" 24 - tapOn: 25 - id: "editDescriptionInput" 26 - inputText: "Shhh" 27 - tapOn: "Save" 28 - - tapOn: "Save" 29 30 # view modlist 31 - assertVisible: "Muted Users" 32 - assertVisible: "Shhh" 33 34 - # DOES NOT WORK - THE BUTTON IS NOT ACCESSIBLE 35 - # IGNORING FOR NOW, FIX THE COMPONENTS IN THE NEXT RELEASE 36 - # BECAUSE THIS IS A LEGIT A11Y PROBLEM -sfn 37 - tapOn: "Subscribe to this list" 38 - tapOn: "Mute accounts" 39 - tapOn: "Mute list"
··· 19 id: "moderationlistsBtn" 20 - tapOn: "New list" 21 - tapOn: 22 + id: "editListNameInput" 23 - inputText: "Muted Users" 24 - tapOn: 25 + id: "editListDescriptionInput" 26 - inputText: "Shhh" 27 - tapOn: "Save" 28 29 # view modlist 30 - assertVisible: "Muted Users" 31 - assertVisible: "Shhh" 32 33 - tapOn: "Subscribe to this list" 34 - tapOn: "Mute accounts" 35 - tapOn: "Mute list"
+8 -1
__e2e__/flows/onboarding-avatar-creator.yml
··· 30 - assertVisible: "What are your interests?" 31 - tapOn: 32 id: "onboardingContinue" 33 - - assertVisible: "You're ready to go!" 34 - tapOn: 35 id: "onboardingFinish" 36 - tapOn:
··· 30 - assertVisible: "What are your interests?" 31 - tapOn: 32 id: "onboardingContinue" 33 + - assertVisible: "Suggested for you" 34 + # mock server doesn't have suggestions api 35 + - tapOn: "Skip to next step" 36 + - swipe: 37 + direction: "LEFT" 38 + - swipe: 39 + direction: "LEFT" 40 + - assertVisible: "Complete onboarding and start using your account" 41 - tapOn: 42 id: "onboardingFinish" 43 - tapOn:
+10 -2
__e2e__/flows/onboarding.yml
··· 16 id: "e2eStartOnboarding" 17 - tapOn: "Select an avatar" 18 - waitForAnimationToEnd 19 - - assertVisible: "Search your library…" 20 - tapOn: 21 point: "50%,22%" 22 - waitForAnimationToEnd ··· 27 - assertVisible: "What are your interests?" 28 - tapOn: 29 id: "onboardingContinue" 30 - - assertVisible: "You're ready to go!" 31 - tapOn: 32 id: "onboardingFinish" 33 - tapOn:
··· 16 id: "e2eStartOnboarding" 17 - tapOn: "Select an avatar" 18 - waitForAnimationToEnd 19 + - assertVisible: "Photos" 20 + - assertVisible: "Collections" 21 - tapOn: 22 point: "50%,22%" 23 - waitForAnimationToEnd ··· 28 - assertVisible: "What are your interests?" 29 - tapOn: 30 id: "onboardingContinue" 31 + - assertVisible: "Suggested for you" 32 + # mock server doesn't have suggestions api 33 + - tapOn: "Skip to next step" 34 + - swipe: 35 + direction: "LEFT" 36 + - swipe: 37 + direction: "LEFT" 38 + - assertVisible: "Complete onboarding and start using your account" 39 - tapOn: 40 id: "onboardingFinish" 41 - tapOn:
+4
__e2e__/flows/thread-muting.yml
··· 31 id: "viewHeaderHomeFeedPrefsBtn" 32 - tapOn: 33 id: "replyBtn" 34 - inputText: "Reply 1" 35 - tapOn: 36 id: "composerPublishBtn" ··· 67 id: "bottomBarProfileBtn" 68 - tapOn: 69 id: "profilePager-selector-1" 70 - tapOn: 71 id: "replyBtn" 72 - inputText: "Reply 2"
··· 31 id: "viewHeaderHomeFeedPrefsBtn" 32 - tapOn: 33 id: "replyBtn" 34 + - tapOn: 35 + id: "replyBtn" 36 - inputText: "Reply 1" 37 - tapOn: 38 id: "composerPublishBtn" ··· 69 id: "bottomBarProfileBtn" 70 - tapOn: 71 id: "profilePager-selector-1" 72 + - tapOn: 73 + id: "replyBtn" 74 - tapOn: 75 id: "replyBtn" 76 - inputText: "Reply 2"
+6 -6
__e2e__/setupApp.yml
··· 1 appId: xyz.blueskyweb.app 2 --- 3 - launchApp: 4 - appId: "xyz.blueskyweb.app" 5 - clearState: true 6 - waitForAnimationToEnd 7 - tapOn: "http://localhost:8081" 8 - waitForAnimationToEnd 9 - extendedWaitUntil: 10 visible: "Continue" 11 - swipe: 12 - from: "Bluesky" 13 - direction: DOWN 14 - tapOn: 15 - id: e2eProxyHeaderInput 16 - inputText: ${output.result} 17 - pressKey: Enter 18 - - hideKeyboard
··· 1 appId: xyz.blueskyweb.app 2 --- 3 - launchApp: 4 + appId: "xyz.blueskyweb.app" 5 + clearState: true 6 - waitForAnimationToEnd 7 - tapOn: "http://localhost:8081" 8 - waitForAnimationToEnd 9 - extendedWaitUntil: 10 visible: "Continue" 11 - swipe: 12 + from: "Bluesky" 13 + direction: DOWN 14 + duration: 100 15 - tapOn: 16 + id: e2eProxyHeaderInput 17 - inputText: ${output.result} 18 - pressKey: Enter
+1 -1
docs/build.md
··· 75 76 - Start in various console tabs: 77 - `yarn e2e:mock-server` 78 - - `yarn e2e:metro` 79 - Run once: `yarn e2e:build` 80 - Each test run: `yarn e2e:run` 81
··· 75 76 - Start in various console tabs: 77 - `yarn e2e:mock-server` 78 + - `yarn e2e:start` 79 - Run once: `yarn e2e:build` 80 - Each test run: `yarn e2e:run` 81
+1 -1
docs/testing.md
··· 12 ### Running Maestro tests 13 14 - In one tab, run `yarn e2e:mock-server` 15 - - In a second tab, run `yarn e2e:metro` 16 - In a third tab, run `yarn e2e:run` 17 18 ## Using Flashlight for Performance Testing
··· 12 ### Running Maestro tests 13 14 - In one tab, run `yarn e2e:mock-server` 15 + - In a second tab, run `yarn e2e:build` 16 - In a third tab, run `yarn e2e:run` 17 18 ## Using Flashlight for Performance Testing
+3 -2
package.json
··· 46 "lint-native:fix": "swiftlint --fix ./modules && ktlint --format ./modules", 47 "typecheck": "tsc --project ./tsconfig.check.json", 48 "e2e:mock-server": "NODE_ENV=development ./jest/dev-infra/with-test-redis-and-db.sh ts-node --project tsconfig.e2e.json __e2e__/mock-server.ts", 49 - "e2e:metro": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios", 50 - "e2e:metro-android": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:android", 51 "e2e:run": "maestro test __e2e__", 52 "perf:test": "NODE_ENV=test maestro test", 53 "perf:test:run": "NODE_ENV=test maestro test __e2e__/perf-test.yml",
··· 46 "lint-native:fix": "swiftlint --fix ./modules && ktlint --format ./modules", 47 "typecheck": "tsc --project ./tsconfig.check.json", 48 "e2e:mock-server": "NODE_ENV=development ./jest/dev-infra/with-test-redis-and-db.sh ts-node --project tsconfig.e2e.json __e2e__/mock-server.ts", 49 + "e2e:build": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:ios", 50 + "e2e:build-android": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo run:android", 51 + "e2e:start": "EXPO_PUBLIC_ENV=e2e NODE_ENV=test RN_SRC_EXT=e2e.ts,e2e.tsx expo start", 52 "e2e:run": "maestro test __e2e__", 53 "perf:test": "NODE_ENV=test maestro test", 54 "perf:test:run": "NODE_ENV=test maestro test __e2e__/perf-test.yml",
+1 -1
src/components/dialogs/lists/CreateOrEditListDialog.tsx
··· 435 multiline 436 label={_(msg`Description`)} 437 placeholder={descriptionPlaceholder} 438 - testID="editProfileDescriptionInput" 439 /> 440 </TextField.Root> 441 {descriptionTooLong && (
··· 435 multiline 436 label={_(msg`Description`)} 437 placeholder={descriptionPlaceholder} 438 + testID="editListDescriptionInput" 439 /> 440 </TextField.Root> 441 {descriptionTooLong && (
+1
src/screens/Onboarding/StepFinished/index.tsx
··· 330 </Button> 331 )} 332 <Button 333 disabled={saving} 334 key={state.activeStep} // remove focus state on nav 335 color="primary"
··· 330 </Button> 331 )} 332 <Button 333 + testID="onboardingFinish" 334 disabled={saving} 335 key={state.activeStep} // remove focus state on nav 336 color="primary"
+3 -10
src/screens/Onboarding/StepSuggestedAccounts/index.tsx
··· 1 - import {useCallback, useContext, useMemo, useState} from 'react' 2 import {View} from 'react-native' 3 import {type ModerationOpts} from '@atproto/api' 4 import {msg, Trans} from '@lingui/macro' ··· 15 import {useLanguagePrefs} from '#/state/preferences' 16 import {useModerationOpts} from '#/state/preferences/moderation-opts' 17 import {useAgent, useSession} from '#/state/session' 18 - import {useOnboardingDispatch} from '#/state/shell' 19 import {OnboardingControls} from '#/screens/Onboarding/Layout' 20 import {Context} from '#/screens/Onboarding/state' 21 import {useSuggestedUsers} from '#/screens/Search/util/useSuggestedUsers' ··· 42 const queryClient = useQueryClient() 43 44 const {state, dispatch} = useContext(Context) 45 - const onboardDispatch = useOnboardingDispatch() 46 47 const [selectedInterest, setSelectedInterest] = useState<string | null>(null) 48 // keeping track of who was followed via the follow all button ··· 76 const isError = !!error 77 const isEmpty = 78 !isLoading && suggestedUsers && suggestedUsers.actors.length === 0 79 - 80 - const skipOnboarding = useCallback(() => { 81 - onboardDispatch({type: 'finish'}) 82 - dispatch({type: 'finish'}) 83 - }, [onboardDispatch, dispatch]) 84 85 const followableDids = 86 suggestedUsers?.actors ··· 223 <Button 224 color="secondary" 225 size="large" 226 - label={_(msg`Skip this flow`)} 227 - onPress={skipOnboarding}> 228 <ButtonText> 229 <Trans>Skip</Trans> 230 </ButtonText>
··· 1 + import {useContext, useMemo, useState} from 'react' 2 import {View} from 'react-native' 3 import {type ModerationOpts} from '@atproto/api' 4 import {msg, Trans} from '@lingui/macro' ··· 15 import {useLanguagePrefs} from '#/state/preferences' 16 import {useModerationOpts} from '#/state/preferences/moderation-opts' 17 import {useAgent, useSession} from '#/state/session' 18 import {OnboardingControls} from '#/screens/Onboarding/Layout' 19 import {Context} from '#/screens/Onboarding/state' 20 import {useSuggestedUsers} from '#/screens/Search/util/useSuggestedUsers' ··· 41 const queryClient = useQueryClient() 42 43 const {state, dispatch} = useContext(Context) 44 45 const [selectedInterest, setSelectedInterest] = useState<string | null>(null) 46 // keeping track of who was followed via the follow all button ··· 74 const isError = !!error 75 const isEmpty = 76 !isLoading && suggestedUsers && suggestedUsers.actors.length === 0 77 78 const followableDids = 79 suggestedUsers?.actors ··· 216 <Button 217 color="secondary" 218 size="large" 219 + label={_(msg`Skip to next step`)} 220 + onPress={() => dispatch({type: 'next'})}> 221 <ButtonText> 222 <Trans>Skip</Trans> 223 </ButtonText>
+4 -13
src/screens/Onboarding/StepSuggestedStarterpacks/index.tsx
··· 1 - import {useCallback, useContext} from 'react' 2 import {View} from 'react-native' 3 import {msg, Trans} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 import {useModerationOpts} from '#/state/preferences/moderation-opts' 7 import {useOnboardingSuggestedStarterPacksQuery} from '#/state/queries/useOnboardingSuggestedStarterPacksQuery' 8 - import {useOnboardingDispatch} from '#/state/shell' 9 import {OnboardingControls} from '#/screens/Onboarding/Layout' 10 import {Context} from '#/screens/Onboarding/state' 11 import {atoms as a, useBreakpoints} from '#/alf' ··· 22 const moderationOpts = useModerationOpts() 23 24 const {state, dispatch} = useContext(Context) 25 - const onboardDispatch = useOnboardingDispatch() 26 27 const { 28 data: suggestedStarterPacks, 29 isLoading, 30 - error, 31 isRefetching, 32 refetch, 33 } = useOnboardingSuggestedStarterPacksQuery({ 34 enabled: true, 35 overrideInterests: state.interestsStepResults.selectedInterests, 36 }) 37 - 38 - const isError = !!error 39 - 40 - const skipOnboarding = useCallback(() => { 41 - onboardDispatch({type: 'finish'}) 42 - dispatch({type: 'finish'}) 43 - }, [onboardDispatch, dispatch]) 44 45 return ( 46 <View style={[a.align_start]} testID="onboardingInterests"> ··· 105 <Button 106 color="secondary" 107 size="large" 108 - label={_(msg`Skip this flow`)} 109 - onPress={skipOnboarding}> 110 <ButtonText> 111 <Trans>Skip</Trans> 112 </ButtonText>
··· 1 + import {useContext} from 'react' 2 import {View} from 'react-native' 3 import {msg, Trans} from '@lingui/macro' 4 import {useLingui} from '@lingui/react' 5 6 import {useModerationOpts} from '#/state/preferences/moderation-opts' 7 import {useOnboardingSuggestedStarterPacksQuery} from '#/state/queries/useOnboardingSuggestedStarterPacksQuery' 8 import {OnboardingControls} from '#/screens/Onboarding/Layout' 9 import {Context} from '#/screens/Onboarding/state' 10 import {atoms as a, useBreakpoints} from '#/alf' ··· 21 const moderationOpts = useModerationOpts() 22 23 const {state, dispatch} = useContext(Context) 24 25 const { 26 data: suggestedStarterPacks, 27 isLoading, 28 + isError, 29 isRefetching, 30 refetch, 31 } = useOnboardingSuggestedStarterPacksQuery({ 32 enabled: true, 33 overrideInterests: state.interestsStepResults.selectedInterests, 34 }) 35 36 return ( 37 <View style={[a.align_start]} testID="onboardingInterests"> ··· 96 <Button 97 color="secondary" 98 size="large" 99 + label={_(msg`Skip to next step`)} 100 + onPress={() => dispatch({type: 'next'})}> 101 <ButtonText> 102 <Trans>Skip</Trans> 103 </ButtonText>