about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/view/com/composer/Autocomplete.tsx1
-rw-r--r--src/view/com/composer/ComposePost.tsx16
-rw-r--r--src/view/com/composer/PhotoCarouselPicker.tsx4
-rw-r--r--src/view/com/composer/Prompt.tsx1
-rw-r--r--src/view/com/composer/SelectedPhoto.tsx9
-rw-r--r--src/view/com/discover/SuggestedFollows.tsx5
-rw-r--r--src/view/com/login/CreateAccount.tsx15
-rw-r--r--src/view/com/login/Signin.tsx26
-rw-r--r--src/view/com/notifications/InviteAccepter.tsx9
-rw-r--r--src/view/com/post-thread/PostRepostedBy.tsx5
-rw-r--r--src/view/com/post-thread/PostVotedBy.tsx4
-rw-r--r--src/view/com/posts/Feed.tsx4
-rw-r--r--src/view/com/profile/ProfileFollowers.tsx4
-rw-r--r--src/view/com/profile/ProfileFollows.tsx6
-rw-r--r--src/view/com/profile/ProfileHeader.tsx15
-rw-r--r--src/view/com/profile/ProfileMembers.tsx9
-rw-r--r--src/view/com/util/PostCtrls.tsx3
-rw-r--r--src/view/com/util/error/ErrorMessage.tsx8
-rw-r--r--src/view/com/util/error/ErrorScreen.tsx6
-rw-r--r--src/view/com/util/forms/DropdownButton.tsx3
-rw-r--r--src/view/screens/Contacts.tsx5
-rw-r--r--src/view/screens/Home.tsx53
-rw-r--r--src/view/screens/Login.tsx12
-rw-r--r--src/view/screens/NotFound.tsx8
-rw-r--r--src/view/screens/Profile.tsx8
-rw-r--r--src/view/screens/Search.tsx3
-rw-r--r--src/view/shell/mobile/Menu.tsx5
-rw-r--r--src/view/shell/mobile/TabsSelector.tsx18
-rw-r--r--src/view/shell/mobile/index.tsx6
29 files changed, 182 insertions, 89 deletions
diff --git a/src/view/com/composer/Autocomplete.tsx b/src/view/com/composer/Autocomplete.tsx
index 4ee527ee8..2ccd05653 100644
--- a/src/view/com/composer/Autocomplete.tsx
+++ b/src/view/com/composer/Autocomplete.tsx
@@ -46,6 +46,7 @@ export function Autocomplete({
     <Animated.View style={[styles.outer, pal.view, pal.border, topAnimStyle]}>
       {items.map((item, i) => (
         <TouchableOpacity
+          testID="autocompleteButton"
           key={i}
           style={[pal.border, styles.item]}
           onPress={() => onSelect(item.handle)}>
diff --git a/src/view/com/composer/ComposePost.tsx b/src/view/com/composer/ComposePost.tsx
index dc0bec135..790e0f784 100644
--- a/src/view/com/composer/ComposePost.tsx
+++ b/src/view/com/composer/ComposePost.tsx
@@ -56,11 +56,12 @@ export const ComposePost = observer(function ComposePost({
   const [isSelectingPhotos, setIsSelectingPhotos] = useState(false)
   const [selectedPhotos, setSelectedPhotos] = useState<string[]>([])
 
-  const autocompleteView = useMemo<UserAutocompleteViewModel>(
+  // Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
+  const autocompleteView = React.useMemo<UserAutocompleteViewModel>(
     () => new UserAutocompleteViewModel(store),
     [store],
   )
-  const localPhotos = useMemo<UserLocalPhotosModel>(
+  const localPhotos = React.useMemo<UserLocalPhotosModel>(
     () => new UserLocalPhotosModel(store),
     [store],
   )
@@ -179,11 +180,14 @@ export const ComposePost = observer(function ComposePost({
 
   return (
     <KeyboardAvoidingView
+      testID="composePostView"
       behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
       style={[pal.view, styles.outer]}>
       <SafeAreaView style={s.flex1}>
         <View style={styles.topbar}>
-          <TouchableOpacity onPress={onPressCancel}>
+          <TouchableOpacity
+            testID="composerCancelButton"
+            onPress={onPressCancel}>
             <Text style={[pal.link, s.f18]}>Cancel</Text>
           </TouchableOpacity>
           <View style={s.flex1} />
@@ -192,7 +196,9 @@ export const ComposePost = observer(function ComposePost({
               <ActivityIndicator />
             </View>
           ) : canPost ? (
-            <TouchableOpacity onPress={onPressPublish}>
+            <TouchableOpacity
+              testID="composerPublishButton"
+              onPress={onPressPublish}>
               <LinearGradient
                 colors={[gradients.primary.start, gradients.primary.end]}
                 start={{x: 0, y: 0}}
@@ -257,6 +263,7 @@ export const ComposePost = observer(function ComposePost({
               size={50}
             />
             <TextInput
+              testID="composerTextInput"
               ref={textInput}
               multiline
               scrollEnabled
@@ -283,6 +290,7 @@ export const ComposePost = observer(function ComposePost({
           )}
         <View style={[pal.border, styles.bottomBar]}>
           <TouchableOpacity
+            testID="composerSelectPhotosButton"
             onPress={onPressSelectPhotos}
             style={[s.pl5]}
             hitSlop={HITSLOP}>
diff --git a/src/view/com/composer/PhotoCarouselPicker.tsx b/src/view/com/composer/PhotoCarouselPicker.tsx
index 6c6cd0a47..12dac5825 100644
--- a/src/view/com/composer/PhotoCarouselPicker.tsx
+++ b/src/view/com/composer/PhotoCarouselPicker.tsx
@@ -85,21 +85,25 @@ export const PhotoCarouselPicker = ({
 
   return (
     <ScrollView
+      testID="photoCarouselPickerView"
       horizontal
       style={[pal.view, styles.photosContainer]}
       showsHorizontalScrollIndicator={false}>
       <TouchableOpacity
+        testID="openCameraButton"
         style={[styles.galleryButton, pal.border, styles.photo]}
         onPress={handleOpenCamera}>
         <FontAwesomeIcon icon="camera" size={24} style={pal.link} />
       </TouchableOpacity>
       <TouchableOpacity
+        testID="openGalleryButton"
         style={[styles.galleryButton, pal.border, styles.photo]}
         onPress={handleOpenGallery}>
         <FontAwesomeIcon icon="image" style={pal.link} size={24} />
       </TouchableOpacity>
       {localPhotos.photos.map((item: any, index: number) => (
         <TouchableOpacity
+          testID="openSelectPhotoButton"
           key={`local-image-${index}`}
           style={[pal.border, styles.photoButton]}
           onPress={() => handleSelectPhoto(item.node.image.uri)}>
diff --git a/src/view/com/composer/Prompt.tsx b/src/view/com/composer/Prompt.tsx
index 682a9990b..e8f52f84a 100644
--- a/src/view/com/composer/Prompt.tsx
+++ b/src/view/com/composer/Prompt.tsx
@@ -17,6 +17,7 @@ export function ComposePrompt({
   const pal = usePalette('default')
   return (
     <TouchableOpacity
+      testID="composePromptButton"
       style={[
         pal.view,
         pal.border,
diff --git a/src/view/com/composer/SelectedPhoto.tsx b/src/view/com/composer/SelectedPhoto.tsx
index 7711415f6..393c0b573 100644
--- a/src/view/com/composer/SelectedPhoto.tsx
+++ b/src/view/com/composer/SelectedPhoto.tsx
@@ -25,13 +25,14 @@ export const SelectedPhoto = ({
   )
 
   return selectedPhotos.length !== 0 ? (
-    <View style={styles.imageContainer}>
+    <View testID="selectedPhotosView" style={styles.imageContainer}>
       {selectedPhotos.length !== 0 &&
         selectedPhotos.map((item, index) => (
           <View
             key={`selected-image-${index}`}
             style={[styles.image, imageStyle]}>
             <TouchableOpacity
+              testID="removePhotoButton"
               onPress={() => handleRemovePhoto(item)}
               style={styles.removePhotoButton}>
               <FontAwesomeIcon
@@ -41,7 +42,11 @@ export const SelectedPhoto = ({
               />
             </TouchableOpacity>
 
-            <Image style={[styles.image, imageStyle]} source={{uri: item}} />
+            <Image
+              testID="selectedPhotoImage"
+              style={[styles.image, imageStyle]}
+              source={{uri: item}}
+            />
           </View>
         ))}
     </View>
diff --git a/src/view/com/discover/SuggestedFollows.tsx b/src/view/com/discover/SuggestedFollows.tsx
index 017bd08c8..07f397447 100644
--- a/src/view/com/discover/SuggestedFollows.tsx
+++ b/src/view/com/discover/SuggestedFollows.tsx
@@ -1,4 +1,4 @@
-import React, {useMemo, useEffect, useState} from 'react'
+import React, {useEffect, useState} from 'react'
 import {
   ActivityIndicator,
   FlatList,
@@ -36,7 +36,8 @@ export const SuggestedFollows = observer(
     const store = useStores()
     const [follows, setFollows] = useState<Record<string, string>>({})
 
-    const view = useMemo<SuggestedActorsViewModel>(
+    // Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
+    const view = React.useMemo<SuggestedActorsViewModel>(
       () => new SuggestedActorsViewModel(store),
       [],
     )
diff --git a/src/view/com/login/CreateAccount.tsx b/src/view/com/login/CreateAccount.tsx
index b68d3859e..83d17d374 100644
--- a/src/view/com/login/CreateAccount.tsx
+++ b/src/view/com/login/CreateAccount.tsx
@@ -171,7 +171,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
 
   const isReady = !!email && !!password && !!handle && is13
   return (
-    <ScrollView style={{flex: 1}}>
+    <ScrollView testID="createAccount" style={{flex: 1}}>
       <KeyboardAvoidingView behavior="padding" style={{flex: 1}}>
         <View style={styles.logoHero}>
           <Logo />
@@ -193,6 +193,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
           <View style={styles.groupContent}>
             <FontAwesomeIcon icon="globe" style={styles.groupContentIcon} />
             <TouchableOpacity
+              testID="registerSelectServiceButton"
               style={styles.textBtn}
               onPress={onPressSelectService}>
               <Text style={styles.textBtnLabel}>
@@ -235,6 +236,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
                   style={styles.groupContentIcon}
                 />
                 <TextInput
+                  testID="registerEmailInput"
                   style={[styles.textInput]}
                   placeholder="Email address"
                   placeholderTextColor={colors.blue0}
@@ -248,6 +250,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
               <View style={styles.groupContent}>
                 <FontAwesomeIcon icon="lock" style={styles.groupContentIcon} />
                 <TextInput
+                  testID="registerPasswordInput"
                   style={[styles.textInput]}
                   placeholder="Choose your password"
                   placeholderTextColor={colors.blue0}
@@ -273,6 +276,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
               <View style={styles.groupContent}>
                 <FontAwesomeIcon icon="at" style={styles.groupContentIcon} />
                 <TextInput
+                  testID="registerHandleInput"
                   style={[styles.textInput]}
                   placeholder="eg alice"
                   placeholderTextColor={colors.blue0}
@@ -317,6 +321,7 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
               </View>
               <View style={styles.groupContent}>
                 <TouchableOpacity
+                  testID="registerIs13Input"
                   style={styles.textBtn}
                   onPress={() => setIs13(!is13)}>
                   <View style={is13 ? styles.checkboxFilled : styles.checkbox}>
@@ -339,7 +344,9 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
           </TouchableOpacity>
           <View style={s.flex1} />
           {isReady ? (
-            <TouchableOpacity onPress={onPressNext}>
+            <TouchableOpacity
+              testID="createAccountButton"
+              onPress={onPressNext}>
               {isProcessing ? (
                 <ActivityIndicator color="#fff" />
               ) : (
@@ -347,7 +354,9 @@ export const CreateAccount = ({onPressBack}: {onPressBack: () => void}) => {
               )}
             </TouchableOpacity>
           ) : !serviceDescription && error ? (
-            <TouchableOpacity onPress={onPressRetryConnect}>
+            <TouchableOpacity
+              testID="registerRetryButton"
+              onPress={onPressRetryConnect}>
               <Text style={[s.white, s.f18, s.bold, s.pr5]}>Retry</Text>
             </TouchableOpacity>
           ) : !serviceDescription ? (
diff --git a/src/view/com/login/Signin.tsx b/src/view/com/login/Signin.tsx
index 03c634c46..f60b637b7 100644
--- a/src/view/com/login/Signin.tsx
+++ b/src/view/com/login/Signin.tsx
@@ -69,7 +69,7 @@ export const Signin = ({onPressBack}: {onPressBack: () => void}) => {
   const onPressRetryConnect = () => setRetryDescribeTrigger({})
 
   return (
-    <KeyboardAvoidingView behavior="padding" style={{flex: 1}}>
+    <KeyboardAvoidingView testID="signIn" behavior="padding" style={{flex: 1}}>
       <View style={styles.logoHero}>
         <Logo />
       </View>
@@ -194,8 +194,9 @@ const LoginForm = ({
   const isReady = !!serviceDescription && !!handle && !!password
   return (
     <>
-      <View style={styles.group}>
+      <View testID="loginFormView" style={styles.group}>
         <TouchableOpacity
+          testID="loginSelectServiceButton"
           style={[styles.groupTitle, {paddingRight: 0, paddingVertical: 6}]}
           onPress={onPressSelectService}>
           <Text style={[s.flex1, s.white, s.f18, s.bold]} numberOfLines={1}>
@@ -213,6 +214,7 @@ const LoginForm = ({
         <View style={styles.groupContent}>
           <FontAwesomeIcon icon="at" style={styles.groupContentIcon} />
           <TextInput
+            testID="loginUsernameInput"
             style={styles.textInput}
             placeholder="Username"
             placeholderTextColor={colors.blue0}
@@ -227,6 +229,7 @@ const LoginForm = ({
         <View style={styles.groupContent}>
           <FontAwesomeIcon icon="lock" style={styles.groupContentIcon} />
           <TextInput
+            testID="loginPasswordInput"
             style={styles.textInput}
             placeholder="Password"
             placeholderTextColor={colors.blue0}
@@ -238,6 +241,7 @@ const LoginForm = ({
             editable={!isProcessing}
           />
           <TouchableOpacity
+            testID="forgotPasswordButton"
             style={styles.textInputInnerBtn}
             onPress={onPressForgotPassword}>
             <Text style={styles.textInputInnerBtnLabel}>Forgot</Text>
@@ -260,7 +264,9 @@ const LoginForm = ({
         </TouchableOpacity>
         <View style={s.flex1} />
         {!serviceDescription && error ? (
-          <TouchableOpacity onPress={onPressRetryConnect}>
+          <TouchableOpacity
+            testID="loginRetryButton"
+            onPress={onPressRetryConnect}>
             <Text style={[s.white, s.f18, s.bold, s.pr5]}>Retry</Text>
           </TouchableOpacity>
         ) : !serviceDescription ? (
@@ -271,7 +277,7 @@ const LoginForm = ({
         ) : isProcessing ? (
           <ActivityIndicator color="#fff" />
         ) : isReady ? (
-          <TouchableOpacity onPress={onPressNext}>
+          <TouchableOpacity testID="loginNextButton" onPress={onPressNext}>
             <Text style={[s.white, s.f18, s.bold, s.pr5]}>Next</Text>
           </TouchableOpacity>
         ) : undefined}
@@ -339,8 +345,9 @@ const ForgotPasswordForm = ({
         Enter the email you used to create your account. We'll send you a "reset
         code" so you can set a new password.
       </Text>
-      <View style={styles.group}>
+      <View testID="forgotPasswordView" style={styles.group}>
         <TouchableOpacity
+          testID="forgotPasswordSelectServiceButton"
           style={[styles.groupContent, {borderTopWidth: 0}]}
           onPress={onPressSelectService}>
           <FontAwesomeIcon icon="globe" style={styles.groupContentIcon} />
@@ -359,6 +366,7 @@ const ForgotPasswordForm = ({
         <View style={styles.groupContent}>
           <FontAwesomeIcon icon="envelope" style={styles.groupContentIcon} />
           <TextInput
+            testID="forgotPasswordEmail"
             style={styles.textInput}
             placeholder="Email address"
             placeholderTextColor={colors.blue0}
@@ -391,7 +399,7 @@ const ForgotPasswordForm = ({
         ) : !email ? (
           <Text style={[s.blue1, s.f18, s.bold, s.pr5]}>Next</Text>
         ) : (
-          <TouchableOpacity onPress={onPressNext}>
+          <TouchableOpacity testID="newPasswordButton" onPress={onPressNext}>
             <Text style={[s.white, s.f18, s.bold, s.pr5]}>Next</Text>
           </TouchableOpacity>
         )}
@@ -451,10 +459,11 @@ const SetNewPasswordForm = ({
         You will receive an email with a "reset code." Enter that code here,
         then enter your new password.
       </Text>
-      <View style={styles.group}>
+      <View testID="newPasswordView" style={styles.group}>
         <View style={[styles.groupContent, {borderTopWidth: 0}]}>
           <FontAwesomeIcon icon="ticket" style={styles.groupContentIcon} />
           <TextInput
+            testID="resetCodeInput"
             style={[styles.textInput]}
             placeholder="Reset code"
             placeholderTextColor={colors.blue0}
@@ -469,6 +478,7 @@ const SetNewPasswordForm = ({
         <View style={styles.groupContent}>
           <FontAwesomeIcon icon="lock" style={styles.groupContentIcon} />
           <TextInput
+            testID="newPasswordInput"
             style={styles.textInput}
             placeholder="New password"
             placeholderTextColor={colors.blue0}
@@ -501,7 +511,7 @@ const SetNewPasswordForm = ({
         ) : !resetCode || !password ? (
           <Text style={[s.blue1, s.f18, s.bold, s.pr5]}>Next</Text>
         ) : (
-          <TouchableOpacity onPress={onPressNext}>
+          <TouchableOpacity testID="setNewPasswordButton" onPress={onPressNext}>
             <Text style={[s.white, s.f18, s.bold, s.pr5]}>Next</Text>
           </TouchableOpacity>
         )}
diff --git a/src/view/com/notifications/InviteAccepter.tsx b/src/view/com/notifications/InviteAccepter.tsx
index eefe7a273..a8789b171 100644
--- a/src/view/com/notifications/InviteAccepter.tsx
+++ b/src/view/com/notifications/InviteAccepter.tsx
@@ -1,4 +1,4 @@
-import React, {useState} from 'react'
+import React from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import LinearGradient from 'react-native-linear-gradient'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
@@ -13,7 +13,8 @@ import {s, colors, gradients} from '../../lib/styles'
 
 export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
   const store = useStores()
-  const [confirmationUri, setConfirmationUri] = useState<string>('')
+  // Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
+  const [confirmationUri, setConfirmationUri] = React.useState<string>('')
   const isMember =
     confirmationUri !== '' || store.me.memberships?.isMemberOf(item.author.did)
   const onPressAccept = async () => {
@@ -54,7 +55,7 @@ export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
   return (
     <View style={styles.container}>
       {!isMember ? (
-        <TouchableOpacity onPress={onPressAccept}>
+        <TouchableOpacity testID="acceptInviteButton" onPress={onPressAccept}>
           <LinearGradient
             colors={[gradients.primary.start, gradients.primary.end]}
             start={{x: 0, y: 0}}
@@ -64,7 +65,7 @@ export function InviteAccepter({item}: {item: NotificationsViewItemModel}) {
           </LinearGradient>
         </TouchableOpacity>
       ) : (
-        <View style={styles.inviteAccepted}>
+        <View testID="inviteAccepted" style={styles.inviteAccepted}>
           <FontAwesomeIcon icon="check" size={14} style={s.mr5} />
           <Text style={[s.gray5, s.f15]}>Invite accepted</Text>
         </View>
diff --git a/src/view/com/post-thread/PostRepostedBy.tsx b/src/view/com/post-thread/PostRepostedBy.tsx
index 5dbb6219e..12d5a2177 100644
--- a/src/view/com/post-thread/PostRepostedBy.tsx
+++ b/src/view/com/post-thread/PostRepostedBy.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect} from 'react'
+import React, {useEffect} from 'react'
 import {observer} from 'mobx-react-lite'
 import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
 import {
@@ -18,7 +18,8 @@ export const PostRepostedBy = observer(function PostRepostedBy({
   uri: string
 }) {
   const store = useStores()
-  const [view, setView] = useState<RepostedByViewModel | undefined>()
+  // Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
+  const [view, setView] = React.useState<RepostedByViewModel | undefined>()
 
   useEffect(() => {
     if (view?.params.uri === uri) {
diff --git a/src/view/com/post-thread/PostVotedBy.tsx b/src/view/com/post-thread/PostVotedBy.tsx
index 17ed9f9f8..af5bc2475 100644
--- a/src/view/com/post-thread/PostVotedBy.tsx
+++ b/src/view/com/post-thread/PostVotedBy.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect} from 'react'
+import React, {useEffect} from 'react'
 import {observer} from 'mobx-react-lite'
 import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
 import {
@@ -20,7 +20,7 @@ export const PostVotedBy = observer(function PostVotedBy({
   direction: 'up' | 'down'
 }) {
   const store = useStores()
-  const [view, setView] = useState<VotesViewModel | undefined>()
+  const [view, setView] = React.useState<VotesViewModel | undefined>()
 
   useEffect(() => {
     if (view?.params.uri === uri) {
diff --git a/src/view/com/posts/Feed.tsx b/src/view/com/posts/Feed.tsx
index 76f595cd4..f3402428e 100644
--- a/src/view/com/posts/Feed.tsx
+++ b/src/view/com/posts/Feed.tsx
@@ -25,6 +25,7 @@ export const Feed = observer(function Feed({
   onPressCompose,
   onPressTryAgain,
   onScroll,
+  testID,
 }: {
   feed: FeedModel
   style?: StyleProp<ViewStyle>
@@ -32,6 +33,7 @@ export const Feed = observer(function Feed({
   onPressCompose: () => void
   onPressTryAgain?: () => void
   onScroll?: OnScrollCb
+  testID?: string
 }) {
   // TODO optimize renderItem or FeedItem, we're getting this notice from RN: -prf
   //   VirtualizedList: You have a large list that is slow to update - make sure your
@@ -83,7 +85,7 @@ export const Feed = observer(function Feed({
       <View />
     )
   return (
-    <View style={style}>
+    <View testID={testID} style={style}>
       {!data && <ComposePrompt onPressCompose={onPressCompose} />}
       {feed.isLoading && !data && <PostFeedLoadingPlaceholder />}
       {feed.hasError && (
diff --git a/src/view/com/profile/ProfileFollowers.tsx b/src/view/com/profile/ProfileFollowers.tsx
index e6e710ff3..26939c7ce 100644
--- a/src/view/com/profile/ProfileFollowers.tsx
+++ b/src/view/com/profile/ProfileFollowers.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect} from 'react'
+import React, {useEffect} from 'react'
 import {observer} from 'mobx-react-lite'
 import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
 import {
@@ -19,7 +19,7 @@ export const ProfileFollowers = observer(function ProfileFollowers({
   name: string
 }) {
   const store = useStores()
-  const [view, setView] = useState<UserFollowersViewModel | undefined>()
+  const [view, setView] = React.useState<UserFollowersViewModel | undefined>()
 
   useEffect(() => {
     if (view?.params.user === name) {
diff --git a/src/view/com/profile/ProfileFollows.tsx b/src/view/com/profile/ProfileFollows.tsx
index 73e765d19..03c5b13bb 100644
--- a/src/view/com/profile/ProfileFollows.tsx
+++ b/src/view/com/profile/ProfileFollows.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect} from 'react'
+import React, {useEffect} from 'react'
 import {observer} from 'mobx-react-lite'
 import {ActivityIndicator, FlatList, StyleSheet, View} from 'react-native'
 import {
@@ -10,7 +10,7 @@ import {Link} from '../util/Link'
 import {Text} from '../util/text/Text'
 import {ErrorMessage} from '../util/error/ErrorMessage'
 import {UserAvatar} from '../util/UserAvatar'
-import {s, colors} from '../../lib/styles'
+import {s} from '../../lib/styles'
 import {usePalette} from '../../lib/hooks/usePalette'
 
 export const ProfileFollows = observer(function ProfileFollows({
@@ -19,7 +19,7 @@ export const ProfileFollows = observer(function ProfileFollows({
   name: string
 }) {
   const store = useStores()
-  const [view, setView] = useState<UserFollowsViewModel | undefined>()
+  const [view, setView] = React.useState<UserFollowsViewModel | undefined>()
 
   useEffect(() => {
     if (view?.params.user === name) {
diff --git a/src/view/com/profile/ProfileHeader.tsx b/src/view/com/profile/ProfileHeader.tsx
index a4d7c7a92..4fd766952 100644
--- a/src/view/com/profile/ProfileHeader.tsx
+++ b/src/view/com/profile/ProfileHeader.tsx
@@ -147,7 +147,7 @@ export const ProfileHeader = observer(function ProfileHeader({
   // =
   if (view.hasError) {
     return (
-      <View>
+      <View testID="profileHeaderHasError">
         <Text>{view.error}</Text>
       </View>
     )
@@ -192,6 +192,7 @@ export const ProfileHeader = observer(function ProfileHeader({
         <View style={[styles.buttonsLine]}>
           {isMe ? (
             <TouchableOpacity
+              testID="profileHeaderEditProfileButton"
               onPress={onPressEditProfile}
               style={[styles.btn, styles.mainBtn, pal.btn]}>
               <Text type="button" style={pal.text}>
@@ -214,7 +215,9 @@ export const ProfileHeader = observer(function ProfileHeader({
                   </Text>
                 </TouchableOpacity>
               ) : (
-                <TouchableOpacity onPress={onPressToggleFollow}>
+                <TouchableOpacity
+                  testID="profileHeaderToggleFollowButton"
+                  onPress={onPressToggleFollow}>
                   <LinearGradient
                     colors={[gradient[1], gradient[0]]}
                     start={{x: 0, y: 0}}
@@ -257,6 +260,7 @@ export const ProfileHeader = observer(function ProfileHeader({
         </View>
         <View style={styles.metricsLine}>
           <TouchableOpacity
+            testID="profileHeaderFollowersButton"
             style={[s.flexRow, s.mr10]}
             onPress={onPressFollowers}>
             <Text type="body2" style={[s.bold, s.mr2, pal.text]}>
@@ -268,6 +272,7 @@ export const ProfileHeader = observer(function ProfileHeader({
           </TouchableOpacity>
           {view.isUser ? (
             <TouchableOpacity
+              testID="profileHeaderFollowsButton"
               style={[s.flexRow, s.mr10]}
               onPress={onPressFollows}>
               <Text type="body2" style={[s.bold, s.mr2, pal.text]}>
@@ -280,6 +285,7 @@ export const ProfileHeader = observer(function ProfileHeader({
           ) : undefined}
           {view.isScene ? (
             <TouchableOpacity
+              testID="profileHeaderMembersButton"
               style={[s.flexRow, s.mr10]}
               onPress={onPressMembers}>
               <Text type="body2" style={[s.bold, s.mr2, pal.text]}>
@@ -350,7 +356,9 @@ export const ProfileHeader = observer(function ProfileHeader({
       </View>
       {view.isScene && view.creator === store.me.did ? (
         <View style={[styles.sceneAdminContainer, pal.border]}>
-          <TouchableOpacity onPress={onPressInviteMembers}>
+          <TouchableOpacity
+            testID="profileHeaderInviteMembersButton"
+            onPress={onPressInviteMembers}>
             <LinearGradient
               colors={[gradient[1], gradient[0]]}
               start={{x: 0, y: 0}}
@@ -369,6 +377,7 @@ export const ProfileHeader = observer(function ProfileHeader({
         </View>
       ) : undefined}
       <TouchableOpacity
+        testID="profileHeaderAviButton"
         style={[pal.view, {borderColor: pal.colors.background}, styles.avi]}
         onPress={onPressAvi}>
         <UserAvatar
diff --git a/src/view/com/profile/ProfileMembers.tsx b/src/view/com/profile/ProfileMembers.tsx
index 7f566c198..a63de9e32 100644
--- a/src/view/com/profile/ProfileMembers.tsx
+++ b/src/view/com/profile/ProfileMembers.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect} from 'react'
+import React, {useEffect} from 'react'
 import {observer} from 'mobx-react-lite'
 import {ActivityIndicator, FlatList, View} from 'react-native'
 import {MembersViewModel, MemberItem} from '../../../state/models/members-view'
@@ -12,7 +12,8 @@ export const ProfileMembers = observer(function ProfileMembers({
   name: string
 }) {
   const store = useStores()
-  const [view, setView] = useState<MembersViewModel | undefined>()
+  // Using default import (React.use...) instead of named import (use...) to be able to mock store's data in jest environment
+  const [view, setView] = React.useState<MembersViewModel | undefined>()
 
   useEffect(() => {
     if (view?.params.actor === name) {
@@ -37,7 +38,7 @@ export const ProfileMembers = observer(function ProfileMembers({
     view.params.actor !== name
   ) {
     return (
-      <View>
+      <View testID="profileMembersActivityIndicatorView">
         <ActivityIndicator />
       </View>
     )
@@ -68,7 +69,7 @@ export const ProfileMembers = observer(function ProfileMembers({
     />
   )
   return (
-    <View>
+    <View testID="profileMembersFlatList">
       <FlatList
         data={view.members}
         keyExtractor={item => item._reactKey}
diff --git a/src/view/com/util/PostCtrls.tsx b/src/view/com/util/PostCtrls.tsx
index c0ef412d8..25f171598 100644
--- a/src/view/com/util/PostCtrls.tsx
+++ b/src/view/com/util/PostCtrls.tsx
@@ -115,6 +115,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
     <View style={[styles.ctrls, opts.style]}>
       <View style={s.flex1}>
         <TouchableOpacity
+          testID="postCtrlsReplyButton"
           style={styles.ctrl}
           hitSlop={HITSLOP}
           onPress={opts.onPressReply}>
@@ -130,6 +131,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
       </View>
       <View style={s.flex1}>
         <TouchableOpacity
+          testID="postCtrlsToggleRepostButton"
           hitSlop={HITSLOP}
           onPress={onPressToggleRepostWrapper}
           style={styles.ctrl}>
@@ -156,6 +158,7 @@ export function PostCtrls(opts: PostCtrlsOpts) {
       </View>
       <View style={s.flex1}>
         <TouchableOpacity
+          testID="postCtrlsToggleUpvoteButton"
           style={styles.ctrl}
           hitSlop={HITSLOP}
           onPress={onPressToggleUpvoteWrapper}>
diff --git a/src/view/com/util/error/ErrorMessage.tsx b/src/view/com/util/error/ErrorMessage.tsx
index 905268d3e..ee31ad2cb 100644
--- a/src/view/com/util/error/ErrorMessage.tsx
+++ b/src/view/com/util/error/ErrorMessage.tsx
@@ -8,7 +8,6 @@ import {
 } from 'react-native'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
 import {Text} from '../text/Text'
-import {colors} from '../../../lib/styles'
 import {useTheme} from '../../../lib/ThemeContext'
 import {usePalette} from '../../../lib/hooks/usePalette'
 
@@ -26,7 +25,7 @@ export function ErrorMessage({
   const theme = useTheme()
   const pal = usePalette('error')
   return (
-    <View style={[styles.outer, pal.view, style]}>
+    <View testID="errorMessageView" style={[styles.outer, pal.view, style]}>
       <View
         style={[styles.errorIcon, {backgroundColor: theme.palette.error.icon}]}>
         <FontAwesomeIcon icon="exclamation" style={pal.text} size={16} />
@@ -38,7 +37,10 @@ export function ErrorMessage({
         {message}
       </Text>
       {onPressTryAgain && (
-        <TouchableOpacity style={styles.btn} onPress={onPressTryAgain}>
+        <TouchableOpacity
+          testID="errorMessageTryAgainButton"
+          style={styles.btn}
+          onPress={onPressTryAgain}>
           <FontAwesomeIcon
             icon="arrows-rotate"
             style={{color: theme.palette.error.icon}}
diff --git a/src/view/com/util/error/ErrorScreen.tsx b/src/view/com/util/error/ErrorScreen.tsx
index 6db54a9f2..0033195d9 100644
--- a/src/view/com/util/error/ErrorScreen.tsx
+++ b/src/view/com/util/error/ErrorScreen.tsx
@@ -11,16 +11,18 @@ export function ErrorScreen({
   message,
   details,
   onPressTryAgain,
+  testID,
 }: {
   title: string
   message: string
   details?: string
   onPressTryAgain?: () => void
+  testID?: string
 }) {
   const theme = useTheme()
   const pal = usePalette('error')
   return (
-    <View style={[styles.outer, pal.view]}>
+    <View testID={testID} style={[styles.outer, pal.view]}>
       <View style={styles.errorIconContainer}>
         <View
           style={[
@@ -40,6 +42,7 @@ export function ErrorScreen({
       <Text style={[styles.message, pal.textLight]}>{message}</Text>
       {details && (
         <Text
+          testID={`${testID}-details`}
           type="body2"
           style={[
             styles.details,
@@ -52,6 +55,7 @@ export function ErrorScreen({
       {onPressTryAgain && (
         <View style={styles.btnContainer}>
           <TouchableOpacity
+            testID="errorScreenTryAgainButton"
             style={[styles.btn, {backgroundColor: theme.palette.error.icon}]}
             onPress={onPressTryAgain}>
             <FontAwesomeIcon icon="arrows-rotate" style={pal.text} size={16} />
diff --git a/src/view/com/util/forms/DropdownButton.tsx b/src/view/com/util/forms/DropdownButton.tsx
index c81ccf6c5..33387f894 100644
--- a/src/view/com/util/forms/DropdownButton.tsx
+++ b/src/view/com/util/forms/DropdownButton.tsx
@@ -75,7 +75,8 @@ export function DropdownButton({
         style={style}
         onPress={onPress}
         hitSlop={HITSLOP}
-        ref={ref}>
+        // Fix an issue where specific references cause runtime error in jest environment
+        ref={process.env.JEST_WORKER_ID != null ? null : ref}>
         {children}
       </TouchableOpacity>
     )
diff --git a/src/view/screens/Contacts.tsx b/src/view/screens/Contacts.tsx
index 8de56d79a..b22e52fe5 100644
--- a/src/view/screens/Contacts.tsx
+++ b/src/view/screens/Contacts.tsx
@@ -25,7 +25,9 @@ export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
   return (
     <View>
       <View style={styles.section}>
-        <Text style={styles.title}>Contacts</Text>
+        <Text testID="contactsTitle" style={styles.title}>
+          Contacts
+        </Text>
       </View>
       <View style={styles.section}>
         <View style={styles.searchContainer}>
@@ -35,6 +37,7 @@ export const Contacts = ({navIdx, visible, params}: ScreenParams) => {
             style={styles.searchIcon}
           />
           <TextInput
+            testID="contactsTextInput"
             ref={inputRef}
             value={searchText}
             style={styles.searchInput}
diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx
index d5fe7f1f9..9800c6846 100644
--- a/src/view/screens/Home.tsx
+++ b/src/view/screens/Home.tsx
@@ -1,4 +1,4 @@
-import React, {useState, useEffect} from 'react'
+import React, {useEffect} from 'react'
 import {StyleSheet, TouchableOpacity, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
 import useAppState from 'react-native-appstate-hook'
@@ -24,48 +24,48 @@ export const Home = observer(function Home({
   const store = useStores()
   const onMainScroll = useOnMainScroll(store)
   const safeAreaInsets = useSafeAreaInsets()
-  const [hasSetup, setHasSetup] = useState<boolean>(false)
+  const [wasVisible, setWasVisible] = React.useState<boolean>(false)
   const {appState} = useAppState({
     onForeground: () => doPoll(true),
   })
 
-  const doPoll = (knownActive = false) => {
-    if ((!knownActive && appState !== 'active') || !visible) {
-      return
-    }
-    if (store.me.mainFeed.isLoading) {
-      return
-    }
-    store.log.debug('Polling home feed')
-    store.me.mainFeed.checkForLatest().catch(e => {
-      store.log.error('Failed to poll feed', e)
-    })
-  }
+  const doPoll = React.useCallback(
+    (knownActive = false) => {
+      if ((!knownActive && appState !== 'active') || !visible) {
+        return
+      }
+      if (store.me.mainFeed.isLoading) {
+        return
+      }
+      store.log.debug('Polling home feed')
+      store.me.mainFeed.checkForLatest().catch(e => {
+        store.log.error('Failed to poll feed', e)
+      })
+    },
+    [appState, visible, store],
+  )
 
   useEffect(() => {
-    let aborted = false
     const pollInterval = setInterval(() => doPoll(), 15e3)
     if (!visible) {
+      setWasVisible(false)
+      return
+    } else if (wasVisible) {
       return
     }
+    setWasVisible(true)
 
-    if (hasSetup) {
-      store.log.debug('Updating home feed')
+    store.nav.setTitle(navIdx, 'Home')
+    store.log.debug('Updating home feed')
+    if (store.me.mainFeed.hasContent) {
       store.me.mainFeed.update()
-      doPoll()
     } else {
-      store.nav.setTitle(navIdx, 'Home')
-      store.log.debug('Fetching home feed')
-      store.me.mainFeed.setup().then(() => {
-        if (aborted) return
-        setHasSetup(true)
-      })
+      store.me.mainFeed.setup()
     }
     return () => {
       clearInterval(pollInterval)
-      aborted = true
     }
-  }, [visible, store])
+  }, [visible, store, navIdx, doPoll, wasVisible])
 
   const onPressCompose = () => {
     store.shell.openComposer({})
@@ -82,6 +82,7 @@ export const Home = observer(function Home({
     <View style={s.flex1}>
       <ViewHeader title="Bluesky" subtitle="Private Beta" canGoBack={false} />
       <Feed
+        testID="homeFeed"
         key="default"
         feed={store.me.mainFeed}
         scrollElRef={scrollElRef}
diff --git a/src/view/screens/Login.tsx b/src/view/screens/Login.tsx
index 0315e287e..8363dbfe0 100644
--- a/src/view/screens/Login.tsx
+++ b/src/view/screens/Login.tsx
@@ -35,8 +35,11 @@ const SigninOrCreateAccount = ({
         <Text style={styles.title}>Bluesky</Text>
         <Text style={styles.subtitle}>[ private beta ]</Text>
       </View>
-      <View style={s.flex1}>
-        <TouchableOpacity style={styles.btn} onPress={onPressCreateAccount}>
+      <View testID="signinOrCreateAccount" style={s.flex1}>
+        <TouchableOpacity
+          testID="createAccountButton"
+          style={styles.btn}
+          onPress={onPressCreateAccount}>
           <Text style={styles.btnLabel}>Create a new account</Text>
         </TouchableOpacity>
         <View style={styles.or}>
@@ -60,7 +63,10 @@ const SigninOrCreateAccount = ({
           </Svg>
           <Text style={styles.orLabel}>or</Text>
         </View>
-        <TouchableOpacity style={styles.btn} onPress={onPressSignin}>
+        <TouchableOpacity
+          testID="signInButton"
+          style={styles.btn}
+          onPress={onPressSignin}>
           <Text style={styles.btnLabel}>Sign in</Text>
         </TouchableOpacity>
       </View>
diff --git a/src/view/screens/NotFound.tsx b/src/view/screens/NotFound.tsx
index 3591b696c..79477fa9b 100644
--- a/src/view/screens/NotFound.tsx
+++ b/src/view/screens/NotFound.tsx
@@ -7,7 +7,7 @@ import {useStores} from '../../state'
 export const NotFound = () => {
   const stores = useStores()
   return (
-    <View>
+    <View testID="notFoundView">
       <ViewHeader title="Page not found" />
       <View
         style={{
@@ -16,7 +16,11 @@ export const NotFound = () => {
           paddingTop: 100,
         }}>
         <Text style={{fontSize: 40, fontWeight: 'bold'}}>Page not found</Text>
-        <Button title="Home" onPress={() => stores.nav.navigate('/')} />
+        <Button
+          testID="navigateHomeButton"
+          title="Home"
+          onPress={() => stores.nav.navigate('/')}
+        />
       </View>
     </View>
   )
diff --git a/src/view/screens/Profile.tsx b/src/view/screens/Profile.tsx
index c89c2ad13..64bb4f042 100644
--- a/src/view/screens/Profile.tsx
+++ b/src/view/screens/Profile.tsx
@@ -1,4 +1,4 @@
-import React, {useEffect, useState, useMemo} from 'react'
+import React, {useEffect, useState} from 'react'
 import {ActivityIndicator, StyleSheet, View} from 'react-native'
 import {observer} from 'mobx-react-lite'
 import {FontAwesomeIcon} from '@fortawesome/react-native-fontawesome'
@@ -30,7 +30,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
   const store = useStores()
   const onMainScroll = useOnMainScroll(store)
   const [hasSetup, setHasSetup] = useState<boolean>(false)
-  const uiState = useMemo(
+  const uiState = React.useMemo(
     () => new ProfileUiModel(store, {user: params.name}),
     [params.user],
   )
@@ -201,6 +201,7 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
               ? () => (
                   <>
                     <FontAwesomeIcon
+                      testID="shouldAdminButton"
                       icon="user-xmark"
                       style={[s.mr5]}
                       size={14}
@@ -242,10 +243,11 @@ export const Profile = observer(({navIdx, visible, params}: ScreenParams) => {
   const title =
     uiState.profile.displayName || uiState.profile.handle || params.name
   return (
-    <View style={styles.container}>
+    <View testID="profileView" style={styles.container}>
       <ViewHeader title={title} />
       {uiState.profile.hasError ? (
         <ErrorScreen
+          testID="profileErrorScreen"
           title="Failed to load profile"
           message={`There was an issue when attempting to load ${params.name}`}
           details={uiState.profile.error}
diff --git a/src/view/screens/Search.tsx b/src/view/screens/Search.tsx
index d9d933b7e..5844aa11d 100644
--- a/src/view/screens/Search.tsx
+++ b/src/view/screens/Search.tsx
@@ -57,6 +57,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
       <View style={[pal.view, pal.border, styles.inputContainer]}>
         <MagnifyingGlassIcon style={[pal.text, styles.inputIcon]} />
         <TextInput
+          testID="searchTextInput"
           ref={textInput}
           placeholder="Type your query here..."
           placeholderTextColor={pal.colors.textLight}
@@ -68,7 +69,7 @@ export const Search = ({navIdx, visible, params}: ScreenParams) => {
       </View>
       <View style={styles.outputContainer}>
         {query ? (
-          <ScrollView onScroll={Keyboard.dismiss}>
+          <ScrollView testID="searchScrollView" onScroll={Keyboard.dismiss}>
             {autocompleteView.searchRes.map((item, i) => (
               <TouchableOpacity
                 key={i}
diff --git a/src/view/shell/mobile/Menu.tsx b/src/view/shell/mobile/Menu.tsx
index 99f2bdab6..6a673d25f 100644
--- a/src/view/shell/mobile/Menu.tsx
+++ b/src/view/shell/mobile/Menu.tsx
@@ -75,6 +75,7 @@ export const Menu = observer(
       onPress?: () => void
     }) => (
       <TouchableOpacity
+        testID="menuItemButton"
         style={styles.menuItem}
         onPress={onPress ? onPress : () => onNavigate(url || '/')}>
         <View style={[styles.menuItemIconWrapper]}>
@@ -98,8 +99,9 @@ export const Menu = observer(
     )
 
     return (
-      <ScrollView style={[styles.view, pal.view]}>
+      <ScrollView testID="menuView" style={[styles.view, pal.view]}>
         <TouchableOpacity
+          testID="profileCardButton"
           onPress={() => onNavigate(`/profile/${store.me.handle}`)}
           style={styles.profileCard}>
           <UserAvatar
@@ -123,6 +125,7 @@ export const Menu = observer(
           </View>
         </TouchableOpacity>
         <TouchableOpacity
+          testID="searchBtn"
           style={[styles.searchBtn, pal.btn]}
           onPress={() => onNavigate('/search')}>
           <MagnifyingGlassIcon
diff --git a/src/view/shell/mobile/TabsSelector.tsx b/src/view/shell/mobile/TabsSelector.tsx
index 71aaa200d..433471602 100644
--- a/src/view/shell/mobile/TabsSelector.tsx
+++ b/src/view/shell/mobile/TabsSelector.tsx
@@ -116,11 +116,12 @@ export const TabsSelector = observer(
     }
 
     if (!active) {
-      return <View />
+      return <View testID="emptyView" />
     }
 
     return (
       <Animated.View
+        testID="tabsSelectorView"
         style={[
           styles.wrapper,
           {bottom: insets.bottom + 55},
@@ -129,7 +130,9 @@ export const TabsSelector = observer(
         <View onLayout={onLayout}>
           <View style={[s.p10, styles.section]}>
             <View style={styles.btns}>
-              <TouchableWithoutFeedback onPress={onPressShareTab}>
+              <TouchableWithoutFeedback
+                testID="shareButton"
+                onPress={onPressShareTab}>
                 <View style={[styles.btn]}>
                   <View style={styles.btnIcon}>
                     <FontAwesomeIcon size={16} icon="share" />
@@ -137,7 +140,9 @@ export const TabsSelector = observer(
                   <Text style={styles.btnText}>Share</Text>
                 </View>
               </TouchableWithoutFeedback>
-              <TouchableWithoutFeedback onPress={onPressCloneTab}>
+              <TouchableWithoutFeedback
+                testID="cloneButton"
+                onPress={onPressCloneTab}>
                 <View style={[styles.btn]}>
                   <View style={styles.btnIcon}>
                     <FontAwesomeIcon size={16} icon={['far', 'clone']} />
@@ -145,7 +150,9 @@ export const TabsSelector = observer(
                   <Text style={styles.btnText}>Clone tab</Text>
                 </View>
               </TouchableWithoutFeedback>
-              <TouchableWithoutFeedback onPress={onPressNewTab}>
+              <TouchableWithoutFeedback
+                testID="newTabButton"
+                onPress={onPressNewTab}>
                 <View style={[styles.btn]}>
                   <View style={styles.btnIcon}>
                     <FontAwesomeIcon size={16} icon="plus" />
@@ -164,6 +171,7 @@ export const TabsSelector = observer(
                 return (
                   <Swipeable
                     key={tab.id}
+                    testID="tabsSwipable"
                     renderLeftActions={renderSwipeActions}
                     renderRightActions={renderSwipeActions}
                     leftThreshold={100}
@@ -185,6 +193,7 @@ export const TabsSelector = observer(
                           isActive && styles.active,
                         ]}>
                         <TouchableWithoutFeedback
+                          testID="changeTabButton"
                           onPress={() => onPressChangeTab(tabIndex)}>
                           <View style={styles.tabInner}>
                             <View style={styles.tabIcon}>
@@ -203,6 +212,7 @@ export const TabsSelector = observer(
                           </View>
                         </TouchableWithoutFeedback>
                         <TouchableWithoutFeedback
+                          testID="closeTabButton"
                           onPress={() => onCloseTab(tabIndex)}>
                           <View style={styles.tabClose}>
                             <FontAwesomeIcon
diff --git a/src/view/shell/mobile/index.tsx b/src/view/shell/mobile/index.tsx
index 07fdfc843..ffb22bda9 100644
--- a/src/view/shell/mobile/index.tsx
+++ b/src/view/shell/mobile/index.tsx
@@ -327,7 +327,7 @@ export const MobileShell: React.FC = observer(() => {
         start={{x: 0, y: 0.8}}
         end={{x: 0, y: 1}}
         style={styles.outerContainer}>
-        <SafeAreaView style={styles.innerContainer}>
+        <SafeAreaView testID="noSessionView" style={styles.innerContainer}>
           <ErrorBoundary>
             <Login />
           </ErrorBoundary>
@@ -338,7 +338,7 @@ export const MobileShell: React.FC = observer(() => {
   }
   if (store.onboard.isOnboarding) {
     return (
-      <View style={styles.outerContainer}>
+      <View testID="onboardOuterView" style={styles.outerContainer}>
         <View style={styles.innerContainer}>
           <ErrorBoundary>
             <Onboard />
@@ -355,7 +355,7 @@ export const MobileShell: React.FC = observer(() => {
     backgroundColor: theme.colorScheme === 'dark' ? colors.gray7 : colors.gray1,
   }
   return (
-    <View style={[styles.outerContainer, pal.view]}>
+    <View testID="mobileShellView" style={[styles.outerContainer, pal.view]}>
       <StatusBar
         barStyle={
           theme.colorScheme === 'dark' ? 'light-content' : 'dark-content'