Add nightly Maestro E2E workflow (#11181)
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,4 @@
|
||||
import {Asset} from 'expo-asset'
|
||||
import {
|
||||
documentDirectory,
|
||||
getInfoAsync,
|
||||
@@ -9,10 +10,15 @@ import ExpoImageCropTool, {
|
||||
} from '@bsky.app/expo-image-crop-tool'
|
||||
|
||||
import {IMAGE_SIZE_CONFIG_2K_1MB} from '#/lib/constants'
|
||||
import {IS_ANDROID} from '#/env'
|
||||
import {compressIfNeeded} from './manip'
|
||||
import {type PickerImage} from './picker.shared'
|
||||
|
||||
async function getFile() {
|
||||
if (IS_ANDROID) {
|
||||
return await getAndroidFile()
|
||||
}
|
||||
|
||||
const imagesDir = documentDirectory!
|
||||
.split('/')
|
||||
.slice(0, -6)
|
||||
@@ -41,6 +47,44 @@ async function getFile() {
|
||||
)
|
||||
}
|
||||
|
||||
/*
|
||||
* The Android emulator can't reach the iOS simulator's sample photo library,
|
||||
* so we load a jpg bundled with the app instead. It is bundled via require()
|
||||
* (resolved by Metro), so it survives `pm clear`, which Maestro's clearState
|
||||
* runs at the start of every flow. An adb-seeded file in app-scoped external
|
||||
* storage does not survive: pm clear wipes that directory each flow, so the
|
||||
* seeded file is gone before the picker mock ever reads it.
|
||||
*/
|
||||
async function getAndroidFile() {
|
||||
const asset = Asset.fromModule(
|
||||
require('../../../assets/images/welcome-modal-bg.jpg'),
|
||||
)
|
||||
await asset.downloadAsync()
|
||||
|
||||
const path = asset.localUri!
|
||||
const fileInfo = await getInfoAsync(path)
|
||||
|
||||
if (!fileInfo.exists) {
|
||||
throw new Error('Failed to get file info')
|
||||
}
|
||||
|
||||
/*
|
||||
* Dimensions of the bundled asset (assets/images/welcome-modal-bg.jpg). Only
|
||||
* used for downstream aspect-ratio display; the actual bytes are read from
|
||||
* disk by compressIfNeeded.
|
||||
*/
|
||||
return await compressIfNeeded(
|
||||
{
|
||||
path,
|
||||
mime: 'image/jpeg',
|
||||
size: fileInfo.size,
|
||||
width: 1432,
|
||||
height: 1025,
|
||||
},
|
||||
IMAGE_SIZE_CONFIG_2K_1MB,
|
||||
)
|
||||
}
|
||||
|
||||
export async function openPicker(): Promise<PickerImage[]> {
|
||||
return [await getFile()]
|
||||
}
|
||||
|
||||
@@ -18,12 +18,25 @@ LogBox.ignoreAllLogs()
|
||||
|
||||
const BTN = {height: 1, width: 1, backgroundColor: 'red'}
|
||||
|
||||
/*
|
||||
* This component is mounted inside <Fragment key={currentAccount?.did}> in
|
||||
* App.tsx, so it fully remounts whenever the account changes (sign-in /
|
||||
* sign-out). If the "proxy configured" flag lived only in React state it would
|
||||
* reset to false on every remount, hiding the sign-in buttons. Keeping it at
|
||||
* module level lets it survive remounts so the sign-in buttons stay visible
|
||||
* across sign-out during multi-account flows. Module state still resets when
|
||||
* the app relaunches with cleared state at the start of each flow, which is the
|
||||
* desired gating behavior.
|
||||
*/
|
||||
let hasConfiguredProxy = false
|
||||
|
||||
export function TestCtrls() {
|
||||
const agent = useAgent()
|
||||
const queryClient = useQueryClient()
|
||||
const {logoutEveryAccount, login} = useSessionApi()
|
||||
const onboardingDispatch = useOnboardingDispatch()
|
||||
const {setShowLoggedOut} = useLoggedOutViewControls()
|
||||
const [isProxyConfigured, setIsProxyConfigured] = useState(hasConfiguredProxy)
|
||||
const onPressSignInAlice = async () => {
|
||||
console.info('[E2E] Signing in as Alice')
|
||||
await login(
|
||||
@@ -63,21 +76,27 @@ export function TestCtrls() {
|
||||
const header = `${proxyHeader}#bsky_appview`
|
||||
BLUESKY_PROXY_HEADER.set(header)
|
||||
agent.configureProxy(header as any)
|
||||
hasConfiguredProxy = true
|
||||
setIsProxyConfigured(true)
|
||||
}}
|
||||
style={BTN}
|
||||
/>
|
||||
<Pressable
|
||||
testID="e2eSignInAlice"
|
||||
onPress={onPressSignInAlice}
|
||||
accessibilityRole="button"
|
||||
style={BTN}
|
||||
/>
|
||||
<Pressable
|
||||
testID="e2eSignInBob"
|
||||
onPress={onPressSignInBob}
|
||||
accessibilityRole="button"
|
||||
style={BTN}
|
||||
/>
|
||||
{isProxyConfigured && (
|
||||
<>
|
||||
<Pressable
|
||||
testID="e2eSignInAlice"
|
||||
onPress={onPressSignInAlice}
|
||||
accessibilityRole="button"
|
||||
style={BTN}
|
||||
/>
|
||||
<Pressable
|
||||
testID="e2eSignInBob"
|
||||
onPress={onPressSignInBob}
|
||||
accessibilityRole="button"
|
||||
style={BTN}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<Pressable
|
||||
testID="e2eSignOut"
|
||||
onPress={() => logoutEveryAccount('Settings')}
|
||||
|
||||
Reference in New Issue
Block a user