Improve e2e tests (#8927)

* get e2e image picker working

* verify create account actually reaches onboarding

* wait for image to actually be attached before posting

* wait until login finishes before moving on

* sign out before switch accounts then wait until logged in

* disable onboarding experiments in e2e

* add testId to handle availability checkmark

* fix too long username

* update thread muting test to reflect current behaviour

* hackfix for the british english translation

* unflake the onboarding tests

* fix curate list flow

* admit defeat on the most list one
This commit is contained in:
Samuel Newman
2025-08-29 18:15:05 +03:00
committed by GitHub
parent 541502c7ee
commit d6dc52b6ea
24 changed files with 200 additions and 92 deletions
+9 -3
View File
@@ -3,20 +3,26 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: ?users SERVER_PATH: ?users
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
# Post an image with the porn label # Post an image with the porn label
- assertVisible: - assertVisible:
id: "composeFAB" id: "composeFAB"
- tapOn: - tapOn:
id: "composeFAB" id: "composeFAB"
- inputText: "Post with an image" - inputText: "Post with an image"
- tapOn: - tapOn:
id: "openGalleryBtn" id: "openMediaBtn"
- extendedWaitUntil:
visible:
id: "selectedPhotosView"
- tapOn: "Content warnings" - tapOn: "Content warnings"
- tapOn: "Porn" - tapOn: "Porn"
- tapOn: - tapOn:
+18 -5
View File
@@ -3,13 +3,17 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: ?users SERVER_PATH: ?users
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- assertVisible: - assertVisible:
id: "composeFAB" id: "composeFAB"
- tapOn: - tapOn:
id: "composeFAB" id: "composeFAB"
- inputText: "Post text only" - inputText: "Post text only"
@@ -21,7 +25,10 @@ appId: xyz.blueskyweb.app
id: "composeFAB" id: "composeFAB"
- inputText: "Post with an image" - inputText: "Post with an image"
- tapOn: - tapOn:
id: "openGalleryBtn" id: "openMediaBtn"
- extendedWaitUntil:
visible:
id: "selectedPhotosView"
- tapOn: - tapOn:
id: "composerPublishBtn" id: "composerPublishBtn"
- assertVisible: - assertVisible:
@@ -46,7 +53,10 @@ appId: xyz.blueskyweb.app
id: "replyBtn" id: "replyBtn"
- inputText: "Reply with an image" - inputText: "Reply with an image"
- tapOn: - tapOn:
id: "openGalleryBtn" id: "openMediaBtn"
- extendedWaitUntil:
visible:
id: "selectedPhotosView"
- tapOn: - tapOn:
id: "composerPublishBtn" id: "composerPublishBtn"
- assertVisible: - assertVisible:
@@ -73,7 +83,10 @@ appId: xyz.blueskyweb.app
id: "quoteBtn" id: "quoteBtn"
- inputText: "QP with an image" - inputText: "QP with an image"
- tapOn: - tapOn:
id: "openGalleryBtn" id: "openMediaBtn"
- extendedWaitUntil:
visible:
id: "selectedPhotosView"
- tapOn: - tapOn:
id: "composerPublishBtn" id: "composerPublishBtn"
- assertVisible: - assertVisible:
+4 -1
View File
@@ -32,6 +32,9 @@ appId: xyz.blueskyweb.app
text: "Not Now" text: "Not Now"
optional: true optional: true
- inputText: "e2e-test" - inputText: "e2e-test"
- extendedWaitUntil:
visible:
id: "handleAvailableCheck"
- tapOn: - tapOn:
id: "nextBtn" id: "nextBtn"
- assertVisible: "Give your profile a face"
+7 -5
View File
@@ -3,11 +3,14 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users&follows&posts" SERVER_PATH: "?users&follows&posts"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
label: "Create a curate list" label: "Create a curate list"
@@ -75,9 +78,8 @@ appId: xyz.blueskyweb.app
- tapOn: - tapOn:
id: "confirmBtn" id: "confirmBtn"
- tapOn: - assertVisible:
label: "Create a new curatelist" id: "newUserListBtn"
id: "e2eGotoLists"
- tapOn: - tapOn:
id: "newUserListBtn" id: "newUserListBtn"
- assertVisible: - assertVisible:
@@ -146,7 +148,7 @@ appId: xyz.blueskyweb.app
- tapOn: - tapOn:
id: "bottomBarSearchBtn" id: "bottomBarSearchBtn"
- tapOn: "Search for posts, users, or feeds" - tapOn: "Search for posts, users[,]? or feeds"
- inputText: "bob" - inputText: "bob"
- tapOn: - tapOn:
id: "searchAutoCompleteResult-bob.test" id: "searchAutoCompleteResult-bob.test"
+10 -10
View File
@@ -3,11 +3,14 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users&follows&labels" SERVER_PATH: "?users&follows&labels"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
# create a modlist # create a modlist
- tapOn: - tapOn:
@@ -28,20 +31,17 @@ appId: xyz.blueskyweb.app
- assertVisible: "Muted Users" - assertVisible: "Muted Users"
- assertVisible: "Shhh" - assertVisible: "Shhh"
- tapOn: # DOES NOT WORK - THE BUTTON IS NOT ACCESSIBLE
label: "Dropdown" # IGNORING FOR NOW, FIX THE COMPONENTS IN THE NEXT RELEASE
point: "71%,9%" # BECAUSE THIS IS A LEGIT A11Y PROBLEM -sfn
- tapOn: "Subscribe to this list"
- tapOn: "Mute accounts" - tapOn: "Mute accounts"
- tapOn: "Mute list" - tapOn: "Mute list"
- tapOn: "Unmute" - tapOn: "Unmute"
- tapOn: - tapOn: "Subscribe to this list"
label: "Dropdown"
point: "71%,9%"
- tapOn: "Block accounts" - tapOn: "Block accounts"
- tapOn: "Block list" - tapOn: "Block list"
- tapOn: "Unblock" - tapOn: "Unblock"
# the rest of the behaviors are tested in curate-lists.yml # the rest of the behaviors are tested in curate-lists.yml
+10 -6
View File
@@ -3,11 +3,15 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users" SERVER_PATH: "?users"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
id: "e2eStartOnboarding" id: "e2eStartOnboarding"
- tapOn: "Open avatar creator" - tapOn: "Open avatar creator"
@@ -21,12 +25,12 @@ appId: xyz.blueskyweb.app
- tapOn: "Select the atom emoji as your avatar" - tapOn: "Select the atom emoji as your avatar"
- tapOn: "Done" - tapOn: "Done"
- waitForAnimationToEnd - waitForAnimationToEnd
- tapOn: "Continue to next step" - tapOn:
id: "onboardingContinue"
- assertVisible: "What are your interests?" - assertVisible: "What are your interests?"
- tapOn: - tapOn:
label: "Tap on continue" id: "onboardingContinue"
point: "50%,92%"
- assertVisible: "You're ready to go!" - assertVisible: "You're ready to go!"
- tapOn: - tapOn:
label: "Tap on Lets go" id: "onboardingFinish"
point: "50%,92%" - assertVisible: "Following"
+10 -6
View File
@@ -3,11 +3,15 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users" SERVER_PATH: "?users"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
id: "e2eStartOnboarding" id: "e2eStartOnboarding"
- tapOn: "Select an avatar" - tapOn: "Select an avatar"
@@ -18,12 +22,12 @@ appId: xyz.blueskyweb.app
- waitForAnimationToEnd - waitForAnimationToEnd
- tapOn: "Done" - tapOn: "Done"
- waitForAnimationToEnd - waitForAnimationToEnd
- tapOn: "Continue to next step" - tapOn:
id: "onboardingContinue"
- assertVisible: "What are your interests?" - assertVisible: "What are your interests?"
- tapOn: - tapOn:
label: "Tap on continue" id: "onboardingContinue"
point: "50%,92%"
- assertVisible: "You're ready to go!" - assertVisible: "You're ready to go!"
- tapOn: - tapOn:
label: "Tap on Lets go" id: "onboardingFinish"
point: "50%,92%" - assertVisible: "Following"
+4 -1
View File
@@ -3,11 +3,14 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users&follows&posts" SERVER_PATH: "?users&follows&posts"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
id: "postDropdownBtn" id: "postDropdownBtn"
+3 -3
View File
@@ -3,7 +3,7 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users&posts&feeds" SERVER_PATH: "?users&posts&feeds"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
@@ -12,10 +12,10 @@ appId: xyz.blueskyweb.app
# Navigate to another user profile # Navigate to another user profile
- extendedWaitUntil: - extendedWaitUntil:
visible: visible:
id: "bottomBarSearchBtn" id: "bottomBarSearchBtn"
- tapOn: - tapOn:
id: "bottomBarSearchBtn" id: "bottomBarSearchBtn"
- tapOn: "Search for posts, users, or feeds" - tapOn: "Search for posts, users[,]? or feeds"
- inputText: "b" - inputText: "b"
- tapOn: - tapOn:
id: "searchAutoCompleteResult-bob.test" id: "searchAutoCompleteResult-bob.test"
+6 -4
View File
@@ -3,20 +3,22 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users" SERVER_PATH: "?users"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
# Navigate to another user profile via autocomplete # Navigate to another user profile via autocomplete
- extendedWaitUntil:
visible:
id: "bottomBarSearchBtn"
- tapOn: - tapOn:
id: "bottomBarSearchBtn" id: "bottomBarSearchBtn"
- assertVisible: "Search for posts, users, or feeds" - assertVisible: "Search for posts, users[,]? or feeds"
- tapOn: "Search for posts, users, or feeds" - tapOn: "Search for posts, users[,]? or feeds"
- inputText: "b" - inputText: "b"
- tapOn: - tapOn:
id: "searchAutoCompleteResult-bob.test" id: "searchAutoCompleteResult-bob.test"
- assertVisible: - assertVisible:
id: "profileView" id: "profileView"
+5 -1
View File
@@ -8,8 +8,12 @@ appId: xyz.blueskyweb.app
file: ../setupApp.yml file: ../setupApp.yml
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- assertVisible: - assertVisible:
id: "storybookBtn" id: "storybookBtn"
- tapOn: - tapOn:
id: "storybookBtn" id: "storybookBtn"
- tapOn: - tapOn:
+38 -9
View File
@@ -3,13 +3,16 @@ appId: xyz.blueskyweb.app
- runScript: - runScript:
file: ../setupServer.js file: ../setupServer.js
env: env:
SERVER_PATH: "?users&follows" SERVER_PATH: "?users&follows"
- runFlow: - runFlow:
file: ../setupApp.yml file: ../setupApp.yml
# Login, create a thread, and log out # Login, create a thread, and log out
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- assertVisible: - assertVisible:
id: "composeFAB" id: "composeFAB"
- tapOn: - tapOn:
@@ -19,8 +22,13 @@ appId: xyz.blueskyweb.app
id: "composerPublishBtn" id: "composerPublishBtn"
# Login, reply to the thread, and log out # Login, reply to the thread, and log out
- tapOn:
id: "e2eSignOut"
- tapOn: - tapOn:
id: "e2eSignInBob" id: "e2eSignInBob"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
id: "replyBtn" id: "replyBtn"
- inputText: "Reply 1" - inputText: "Reply 1"
@@ -28,8 +36,13 @@ appId: xyz.blueskyweb.app
id: "composerPublishBtn" id: "composerPublishBtn"
# Login, confirm notification exists, mute thread, and log out # Login, confirm notification exists, mute thread, and log out
- tapOn:
id: "e2eSignOut"
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
id: "bottomBarNotificationsBtn" id: "bottomBarNotificationsBtn"
- assertVisible: - assertVisible:
@@ -39,12 +52,17 @@ appId: xyz.blueskyweb.app
- tapOn: - tapOn:
id: "postDropdownBtn" id: "postDropdownBtn"
childOf: childOf:
id: "postThreadItem-by-bob.test" id: "postThreadItem-by-bob.test"
- tapOn: "Mute thread" - tapOn: "Mute thread"
# Login, reply to the thread twice, and log out # Login, reply to the thread twice, and log out
- tapOn:
id: "e2eSignOut"
- tapOn: - tapOn:
id: "e2eSignInBob" id: "e2eSignInBob"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
id: "bottomBarProfileBtn" id: "bottomBarProfileBtn"
- tapOn: - tapOn:
@@ -60,24 +78,35 @@ appId: xyz.blueskyweb.app
- tapOn: - tapOn:
id: "composerPublishBtn" id: "composerPublishBtn"
# Login, confirm notifications dont exist, unmute the thread, ~~confirm notifications exist~~
# 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
- tapOn:
id: "e2eSignOut"
- tapOn: - tapOn:
id: "e2eSignInAlice" id: "e2eSignInAlice"
- extendedWaitUntil:
visible:
id: "viewHeaderHomeFeedPrefsBtn"
- tapOn: - tapOn:
id: "bottomBarNotificationsBtn" id: "bottomBarNotificationsBtn"
- assertNotVisible: - assertVisible: ".*Reply 1.*"
- assertNotVisible: ".*Reply 2.*"
- assertNotVisible: ".*Reply 3.*"
- assertVisible:
id: "feedItem-by-bob.test" id: "feedItem-by-bob.test"
- tapOn: - tapOn:
id: "bottomBarHomeBtn" id: "feedItem-by-bob.test"
- tapOn: - tapOn:
id: "postDropdownBtn" id: "postDropdownBtn"
childOf:
id: "postThreadItem-by-bob.test"
- tapOn: "Unmute thread" - tapOn: "Unmute thread"
- tapOn: - tapOn:
id: "bottomBarNotificationsBtn" id: "bottomBarNotificationsBtn"
- swipe: - swipe:
from: from:
id: "notifsFeed" id: "notifsFeed"
direction: DOWN direction: DOWN
- assertVisible: - assertVisible: ".*Reply 1.*"
id: "feedItem-by-bob.test" - assertNotVisible: ".*Reply 2.*"
- assertNotVisible: ".*Reply 3.*"
+12 -12
View File
@@ -181,10 +181,10 @@ async function main() {
'warn-profile', 'warn-profile',
'warn-posts', 'warn-posts',
'muted-account', 'muted-account',
'muted-by-list-account', 'muted-by-list-acc',
'blocking-account', 'blocking-account',
'blockedby-account', 'blockedby-account',
'mutual-block-account', 'mutual-block-acc',
]) { ]) {
await server.mocker.createUser(user) await server.mocker.createUser(user)
await server.mocker.follow('alice', user) await server.mocker.follow('alice', user)
@@ -411,16 +411,16 @@ async function main() {
await server.mocker.addToMuteList( await server.mocker.addToMuteList(
'alice', 'alice',
list, list,
server.mocker.users['muted-by-list-account'].did, server.mocker.users['muted-by-list-acc'].did,
) )
await server.mocker.createPost('muted-by-list-account', 'muted post') await server.mocker.createPost('muted-by-list-acc', 'muted post')
await server.mocker.createQuotePost( await server.mocker.createQuotePost(
'muted-by-list-account', 'muted-by-list-acc',
'account quote post', 'account quote post',
anchorPost, anchorPost,
) )
await server.mocker.createReply( await server.mocker.createReply(
'muted-by-list-account', 'muted-by-list-acc',
'account reply', 'account reply',
anchorPost, anchorPost,
) )
@@ -470,16 +470,16 @@ async function main() {
) )
await server.mocker.createPost( await server.mocker.createPost(
'mutual-block-account', 'mutual-block-acc',
'mutual-block post', 'mutual-block post',
) )
await server.mocker.createQuotePost( await server.mocker.createQuotePost(
'mutual-block-account', 'mutual-block-acc',
'mutual-block quote post', 'mutual-block quote post',
anchorPost, anchorPost,
) )
await server.mocker.createReply( await server.mocker.createReply(
'mutual-block-account', 'mutual-block-acc',
'mutual-block reply', 'mutual-block reply',
anchorPost, anchorPost,
) )
@@ -488,15 +488,15 @@ async function main() {
repo: server.mocker.users.alice.did, repo: server.mocker.users.alice.did,
}, },
{ {
subject: server.mocker.users['mutual-block-account'].did, subject: server.mocker.users['mutual-block-acc'].did,
createdAt: new Date().toISOString(), createdAt: new Date().toISOString(),
}, },
) )
await server.mocker.users[ await server.mocker.users[
'mutual-block-account' 'mutual-block-acc'
].agent.app.bsky.graph.block.create( ].agent.app.bsky.graph.block.create(
{ {
repo: server.mocker.users['mutual-block-account'].did, repo: server.mocker.users['mutual-block-acc'].did,
}, },
{ {
subject: server.mocker.users.alice.did, subject: server.mocker.users.alice.did,
+2 -2
View File
@@ -1,8 +1,8 @@
// eslint-disable-next-line // eslint-disable-next-line no-undef
var res = http.post('http://localhost:1986/' + SERVER_PATH, { var res = http.post('http://localhost:1986/' + SERVER_PATH, {
headers: {'Content-Type': 'text/plain'}, headers: {'Content-Type': 'text/plain'},
body: '', body: '',
}) })
// eslint-disable-next-line // eslint-disable-next-line no-undef
output.result = json(res.body).appviewDid output.result = json(res.body).appviewDid
+17
View File
@@ -7,6 +7,7 @@ import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool'
import {compressIfNeeded} from './manip' import {compressIfNeeded} from './manip'
import {type PickerImage} from './picker.shared' import {type PickerImage} from './picker.shared'
import {ImagePickerResult} from 'expo-image-picker'
async function getFile() { async function getFile() {
const imagesDir = documentDirectory! const imagesDir = documentDirectory!
@@ -38,6 +39,22 @@ export async function openPicker(): Promise<PickerImage[]> {
return [await getFile()] return [await getFile()]
} }
export async function openUnifiedPicker(): Promise<ImagePickerResult> {
const file = await getFile()
return {
assets: [
{
type: 'image',
uri: file.path,
mimeType: file.mime,
...file,
},
],
canceled: false,
}
}
export async function openCamera(): Promise<PickerImage> { export async function openCamera(): Promise<PickerImage> {
return await getFile() return await getFile()
} }
+22
View File
@@ -1,11 +1,14 @@
import { import {
type ImagePickerOptions, type ImagePickerOptions,
launchImageLibraryAsync, launchImageLibraryAsync,
UIImagePickerPreferredAssetRepresentationMode,
} from 'expo-image-picker' } from 'expo-image-picker'
import {t} from '@lingui/macro' import {t} from '@lingui/macro'
import {isIOS, isWeb} from '#/platform/detection'
import {type ImageMeta} from '#/state/gallery' import {type ImageMeta} from '#/state/gallery'
import * as Toast from '#/view/com/util/Toast' import * as Toast from '#/view/com/util/Toast'
import {VIDEO_MAX_DURATION_MS} from '../constants'
import {getDataUriSize} from './util' import {getDataUriSize} from './util'
export type PickerImage = ImageMeta & { export type PickerImage = ImageMeta & {
@@ -36,3 +39,22 @@ export async function openPicker(opts?: ImagePickerOptions) {
size: getDataUriSize(image.uri), size: getDataUriSize(image.uri),
})) }))
} }
export async function openUnifiedPicker({
selectionCountRemaining,
}: {
selectionCountRemaining: number
}) {
return await launchImageLibraryAsync({
exif: false,
mediaTypes: ['images', 'videos'],
quality: 1,
allowsMultipleSelection: true,
legacy: true,
base64: isWeb,
selectionLimit: isIOS ? selectionCountRemaining : undefined,
preferredAssetRepresentationMode:
UIImagePickerPreferredAssetRepresentationMode.Current,
videoMaxDuration: VIDEO_MAX_DURATION_MS / 1000,
})
}
+5 -1
View File
@@ -1,7 +1,11 @@
import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool' import ExpoImageCropTool, {type OpenCropperOptions} from 'expo-image-crop-tool'
import {type ImagePickerOptions, launchCameraAsync} from 'expo-image-picker' import {type ImagePickerOptions, launchCameraAsync} from 'expo-image-picker'
export {openPicker, type PickerImage as RNImage} from './picker.shared' export {
openPicker,
openUnifiedPicker,
type PickerImage as RNImage,
} from './picker.shared'
export async function openCamera(customOpts: ImagePickerOptions) { export async function openCamera(customOpts: ImagePickerOptions) {
const opts: ImagePickerOptions = { const opts: ImagePickerOptions = {
+1 -1
View File
@@ -3,7 +3,7 @@ import {type OpenCropperOptions} from 'expo-image-crop-tool'
import {type PickerImage} from './picker.shared' import {type PickerImage} from './picker.shared'
import {type CameraOpts} from './types' import {type CameraOpts} from './types'
export {openPicker} from './picker.shared' export {openPicker, openUnifiedPicker} from './picker.shared'
export async function openCamera(_opts: CameraOpts): Promise<PickerImage> { export async function openCamera(_opts: CameraOpts): Promise<PickerImage> {
throw new Error('openCamera is not supported on web') throw new Error('openCamera is not supported on web')
+1
View File
@@ -580,6 +580,7 @@ function LegacyFinalStep({
<OnboardingControls.Portal> <OnboardingControls.Portal>
<Button <Button
testID="onboardingFinish"
disabled={saving} disabled={saving}
key={state.activeStep} // remove focus state on nav key={state.activeStep} // remove focus state on nav
color="primary" color="primary"
@@ -244,6 +244,7 @@ export function StepInterests() {
) : ( ) : (
<Button <Button
disabled={saving || !data} disabled={saving || !data}
testID="onboardingContinue"
variant="solid" variant="solid"
color="primary" color="primary"
size="large" size="large"
@@ -268,6 +268,7 @@ export function StepProfile() {
<OnboardingControls.Portal> <OnboardingControls.Portal>
<View style={[a.gap_md, gtMobile && a.flex_row_reverse]}> <View style={[a.gap_md, gtMobile && a.flex_row_reverse]}>
<Button <Button
testID="onboardingContinue"
variant="solid" variant="solid"
color="primary" color="primary"
size="large" size="large"
@@ -279,6 +280,7 @@ export function StepProfile() {
<ButtonIcon icon={ChevronRight} position="right" /> <ButtonIcon icon={ChevronRight} position="right" />
</Button> </Button>
<Button <Button
testID="onboardingAvatarCreator"
variant="ghost" variant="ghost"
color="primary" color="primary"
size="large" size="large"
+4 -2
View File
@@ -13,13 +13,15 @@ import {StepFinished} from '#/screens/Onboarding/StepFinished'
import {StepInterests} from '#/screens/Onboarding/StepInterests' import {StepInterests} from '#/screens/Onboarding/StepInterests'
import {StepProfile} from '#/screens/Onboarding/StepProfile' import {StepProfile} from '#/screens/Onboarding/StepProfile'
import {Portal} from '#/components/Portal' import {Portal} from '#/components/Portal'
import {ENV} from '#/env'
import {StepSuggestedAccounts} from './StepSuggestedAccounts' import {StepSuggestedAccounts} from './StepSuggestedAccounts'
export function Onboarding() { export function Onboarding() {
const {_} = useLingui() const {_} = useLingui()
const gate = useGate() const gate = useGate()
const showValueProp = gate('onboarding_value_prop') const showValueProp = ENV !== 'e2e' && gate('onboarding_value_prop')
const showSuggestedAccounts = gate('onboarding_suggested_accounts') const showSuggestedAccounts =
ENV !== 'e2e' && gate('onboarding_suggested_accounts')
const [state, dispatch] = useReducer(reducer, { const [state, dispatch] = useReducer(reducer, {
...initialState, ...initialState,
totalSteps: showSuggestedAccounts ? 4 : 3, totalSteps: showSuggestedAccounts ? 4 : 3,
+4 -1
View File
@@ -168,7 +168,10 @@ export function StepHandle() {
</TextField.GhostText> </TextField.GhostText>
)} )}
{isHandleAvailable?.available && ( {isHandleAvailable?.available && (
<CheckIcon style={[{color: t.palette.positive_600}, a.z_20]} /> <CheckIcon
testID="handleAvailableCheck"
style={[{color: t.palette.positive_600}, a.z_20]}
/>
)} )}
</TextField.Root> </TextField.Root>
</View> </View>
+4 -18
View File
@@ -1,10 +1,6 @@
import {useCallback} from 'react' import {useCallback} from 'react'
import {Keyboard} from 'react-native' import {Keyboard} from 'react-native'
import { import {type ImagePickerAsset} from 'expo-image-picker'
type ImagePickerAsset,
launchImageLibraryAsync,
UIImagePickerPreferredAssetRepresentationMode,
} from 'expo-image-picker'
import {msg, plural} from '@lingui/macro' import {msg, plural} from '@lingui/macro'
import {useLingui} from '@lingui/react' import {useLingui} from '@lingui/react'
@@ -13,8 +9,9 @@ import {
usePhotoLibraryPermission, usePhotoLibraryPermission,
useVideoLibraryPermission, useVideoLibraryPermission,
} from '#/lib/hooks/usePermissions' } from '#/lib/hooks/usePermissions'
import {openUnifiedPicker} from '#/lib/media/picker'
import {extractDataUriMime} from '#/lib/media/util' import {extractDataUriMime} from '#/lib/media/util'
import {isIOS, isNative, isWeb} from '#/platform/detection' import {isNative, isWeb} from '#/platform/detection'
import {MAX_IMAGES} from '#/view/com/composer/state/composer' import {MAX_IMAGES} from '#/view/com/composer/state/composer'
import {atoms as a, useTheme} from '#/alf' import {atoms as a, useTheme} from '#/alf'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
@@ -448,18 +445,7 @@ export function SelectMediaButton({
} }
const {assets, canceled} = await sheetWrapper( const {assets, canceled} = await sheetWrapper(
launchImageLibraryAsync({ openUnifiedPicker({selectionCountRemaining}),
exif: false,
mediaTypes: ['images', 'videos'],
quality: 1,
allowsMultipleSelection: true,
legacy: true,
base64: isWeb,
selectionLimit: isIOS ? selectionCountRemaining : undefined,
preferredAssetRepresentationMode:
UIImagePickerPreferredAssetRepresentationMode.Current,
videoMaxDuration: VIDEO_MAX_DURATION_MS / 1000,
}),
) )
if (canceled) return if (canceled) return