diff --git a/apps/mobile/src/app/componentUI/MezonButton/index.tsx b/apps/mobile/src/app/componentUI/MezonButton/index.tsx index cf9014f3aa..58879c82a7 100644 --- a/apps/mobile/src/app/componentUI/MezonButton/index.tsx +++ b/apps/mobile/src/app/componentUI/MezonButton/index.tsx @@ -1,6 +1,7 @@ import { useTheme } from '@mezon/mobile-ui'; import { memo, useMemo } from 'react'; import { Pressable, StyleProp, Text, TextStyle, ViewStyle } from 'react-native'; +import { testProperties } from '../../configs/testProperties'; import { style } from './styles'; export enum EMezonButtonTheme { @@ -83,6 +84,7 @@ const MezonButton = ({ ]} disabled={disabled} onPress={onPress} + {...testProperties('mezonButton')} > {icon} {title && {title}} diff --git a/apps/mobile/src/app/componentUI/MezonButtonIcon/index.tsx b/apps/mobile/src/app/componentUI/MezonButtonIcon/index.tsx index 3985b004b6..fa6361877f 100644 --- a/apps/mobile/src/app/componentUI/MezonButtonIcon/index.tsx +++ b/apps/mobile/src/app/componentUI/MezonButtonIcon/index.tsx @@ -1,6 +1,7 @@ import { useTheme } from '@mezon/mobile-ui'; import { Text, View } from 'react-native'; import { Pressable } from 'react-native-gesture-handler'; +import { testProperties } from '../../configs/testProperties'; import { style } from './styles'; interface IMezonButtonIconProps { @@ -12,9 +13,13 @@ interface IMezonButtonIconProps { export default function MezonButtonIcon({ title, icon, onPress }: IMezonButtonIconProps) { const styles = style(useTheme().themeValue); return ( - - {icon} - {title} + + + {icon} + + + {title} + ); } diff --git a/apps/mobile/src/app/componentUI/MezonClanAvatar/index.tsx b/apps/mobile/src/app/componentUI/MezonClanAvatar/index.tsx index 0c5dd8d474..1a560f40cd 100644 --- a/apps/mobile/src/app/componentUI/MezonClanAvatar/index.tsx +++ b/apps/mobile/src/app/componentUI/MezonClanAvatar/index.tsx @@ -5,6 +5,7 @@ import { StyleProp, Text, TextStyle, View } from 'react-native'; import FastImage from 'react-native-fast-image'; import Images from '../../../assets/Images'; import ImageNative from '../../components/ImageNative'; +import { testProperties } from '../../configs/testProperties'; import { IconCDN } from '../../constants/icon_cdn'; import { style } from './styles'; @@ -53,7 +54,10 @@ export default memo(function MezonClanAvatar({ ); } return ( - + {!noDefaultText ? ( ) : null} diff --git a/apps/mobile/src/app/componentUI/MezonConfirm/index.tsx b/apps/mobile/src/app/componentUI/MezonConfirm/index.tsx index 553cf2efd8..c591a06094 100644 --- a/apps/mobile/src/app/componentUI/MezonConfirm/index.tsx +++ b/apps/mobile/src/app/componentUI/MezonConfirm/index.tsx @@ -3,6 +3,7 @@ import { useTheme } from '@mezon/mobile-ui'; import type { ReactNode } from 'react'; import { useTranslation } from 'react-i18next'; import { DeviceEventEmitter, Text, TouchableOpacity, View } from 'react-native'; +import { testProperties } from '../../configs/testProperties'; import useTabletLandscape from '../../hooks/useTabletLandscape'; import { style } from './styles'; @@ -34,16 +35,22 @@ export default function MezonConfirm({ children, title, confirmText, content, is - {title} + + {title} + {children ? children : {content || ''}} - handleConfirm()}> + handleConfirm()} + {...testProperties('confirm.button')} + > {confirmText} - handleClose()}> + handleClose()} {...testProperties('confirm.button.cancel')}> {t('buzz.cancel')} diff --git a/apps/mobile/src/app/componentUI/MezonInput/index.tsx b/apps/mobile/src/app/componentUI/MezonInput/index.tsx index 94c9f4b4ca..6e71f65c67 100644 --- a/apps/mobile/src/app/componentUI/MezonInput/index.tsx +++ b/apps/mobile/src/app/componentUI/MezonInput/index.tsx @@ -5,6 +5,7 @@ import { useEffect, useRef, useState } from 'react'; import type { KeyboardType, StyleProp, TextStyle, ViewStyle } from 'react-native'; import { Text, TextInput, TouchableOpacity, View } from 'react-native'; import { ErrorInput } from '../../components/ErrorInput'; +import { testProperties } from '../../configs/testProperties'; import { IconCDN } from '../../constants/icon_cdn'; import { validInput } from '../../utils/validate'; import MezonIconCDN from '../MezonIconCDN'; @@ -111,7 +112,11 @@ export default function MezonInput({ return ( - {label && {label}} + {label && ( + + {label} + + )} {prefixIcon} @@ -132,11 +137,12 @@ export default function MezonInput({ editable={!disabled} defaultValue={defaultValue} keyboardType={keyboardType} + {...testProperties('mezonInput.input')} /> {postfixIcon} {!textarea && value?.length > 0 && !disabled && ( - + )} diff --git a/apps/mobile/src/app/components/FriendItem/FriendItem.tsx b/apps/mobile/src/app/components/FriendItem/FriendItem.tsx index 3e4cba622f..9e00ba5b68 100644 --- a/apps/mobile/src/app/components/FriendItem/FriendItem.tsx +++ b/apps/mobile/src/app/components/FriendItem/FriendItem.tsx @@ -5,6 +5,7 @@ import React, { useMemo } from 'react'; import { Pressable, Text, TouchableOpacity, View } from 'react-native'; import BouncyCheckbox from 'react-native-bouncy-checkbox/build/dist/BouncyCheckbox'; import MezonIconCDN from '../../componentUI/MezonIconCDN'; +import { testProperties } from '../../configs/testProperties'; import { IconCDN } from '../../constants/icon_cdn'; import ImageNative from '../ImageNative'; import { UserStatus } from '../UserStatus'; @@ -105,11 +106,18 @@ export const FriendItem = React.memo( {isPendingFriendRequest && showAction && !selectMode ? ( - onPressAction(EFriendItemAction.Delete)}> + onPressAction(EFriendItemAction.Delete)} + {...testProperties(`friend.button.deleteFriend.${friend.user.username}`)} + > {!isSentRequestFriend ? ( - onPressAction(EFriendItemAction.Approve)} style={styles.approveIcon}> + onPressAction(EFriendItemAction.Approve)} + style={styles.approveIcon} + {...testProperties(`friend.button.approveFriend`)} + > ) : null} diff --git a/apps/mobile/src/app/configs/testProperties.ts b/apps/mobile/src/app/configs/testProperties.ts new file mode 100644 index 0000000000..cd5a4a8b77 --- /dev/null +++ b/apps/mobile/src/app/configs/testProperties.ts @@ -0,0 +1,12 @@ +import { Platform } from 'react-native'; + +export const IS_IOS = Platform.OS === 'ios'; + +function testProperties(id: string, disableAccessible = false): { accessible?: boolean; accessibilityLabel?: string; testID?: string } { + const disableAccessibility = disableAccessible ? { accessible: false } : {}; + + if (IS_IOS) return { ...disableAccessibility, testID: id }; + return { ...disableAccessibility, accessibilityLabel: id }; +} + +export { testProperties }; diff --git a/apps/mobile/src/app/navigation/Authentication/stacks/FriendStacks.tsx b/apps/mobile/src/app/navigation/Authentication/stacks/FriendStacks.tsx index 26c2b82cf6..eb1aa98c05 100644 --- a/apps/mobile/src/app/navigation/Authentication/stacks/FriendStacks.tsx +++ b/apps/mobile/src/app/navigation/Authentication/stacks/FriendStacks.tsx @@ -3,6 +3,7 @@ import { createStackNavigator } from '@react-navigation/stack'; import React from 'react'; import { useTranslation } from 'react-i18next'; import { Platform, Pressable, Text } from 'react-native'; +import { testProperties } from '../../../configs/testProperties'; import { FriendScreen } from '../../../screens/friend'; import { AddFriendScreen } from '../../../screens/friend/AddFriend'; import { RequestFriendScreen } from '../../../screens/friend/RequestFriend'; @@ -13,7 +14,11 @@ import { styles } from './styles'; const AddFriendButton = ({ navigation }: { navigation: any }) => { const { t } = useTranslation(['screen']); return ( - navigation.navigate(APP_SCREEN.FRIENDS.STACK, { screen: APP_SCREEN.FRIENDS.ADD_FRIEND })} style={styles.addFriendButton}> + navigation.navigate(APP_SCREEN.FRIENDS.STACK, { screen: APP_SCREEN.FRIENDS.ADD_FRIEND })} + style={styles.addFriendButton} + {...testProperties('friend.button.addFriends')} + > {t('headerRight.addFriends')} ); diff --git a/apps/mobile/src/app/screens/auth/Login/WelcomeScreen.tsx b/apps/mobile/src/app/screens/auth/Login/WelcomeScreen.tsx index ced3c3d4c7..c0d2d5e30b 100644 --- a/apps/mobile/src/app/screens/auth/Login/WelcomeScreen.tsx +++ b/apps/mobile/src/app/screens/auth/Login/WelcomeScreen.tsx @@ -6,6 +6,7 @@ import LinearGradient from 'react-native-linear-gradient'; import { APP_SCREEN } from '../../../navigation/ScreenTypes'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-expect-error +import { testProperties } from '../../../configs/testProperties'; import FEATURE_BG from './featureBg.png'; import { style } from './styles'; @@ -25,7 +26,7 @@ const WelcomeScreen = ({ navigation }) => { {t('login.desWelcomeToMezon')} - onGetStarted()}> + onGetStarted()} {...testProperties('started.button')}> {t('login.getStarted')} diff --git a/apps/mobile/src/app/screens/auth/Login/index.tsx b/apps/mobile/src/app/screens/auth/Login/index.tsx index 59ef6df201..7077688e5d 100644 --- a/apps/mobile/src/app/screens/auth/Login/index.tsx +++ b/apps/mobile/src/app/screens/auth/Login/index.tsx @@ -26,6 +26,7 @@ import LinearGradient from 'react-native-linear-gradient'; import Toast from 'react-native-toast-message'; import MezonIconCDN from '../../../componentUI/MezonIconCDN'; import { ErrorInput } from '../../../components/ErrorInput'; +import { testProperties } from '../../../configs/testProperties'; import { IconCDN } from '../../../constants/icon_cdn'; import useTabletLandscape from '../../../hooks/useTabletLandscape'; import { APP_SCREEN } from '../../../navigation/ScreenTypes'; @@ -380,6 +381,7 @@ const LoginScreen = ({ navigation }) => { autoFocus={true} onSubmitEditing={handlePrimaryAction} underlineColorAndroid="transparent" + {...testProperties('login.phone.input')} /> { autoFocus={true} onSubmitEditing={handlePrimaryAction} underlineColorAndroid="transparent" + {...testProperties('login.email.input')} /> )} @@ -434,6 +437,7 @@ const LoginScreen = ({ navigation }) => { autoCapitalize="none" autoCorrect={false} onSubmitEditing={handlePrimaryAction} + {...testProperties('login.password.input')} /> @@ -458,6 +462,7 @@ const LoginScreen = ({ navigation }) => { style={[styles.otpButton, !isFormValid && styles.otpButtonDisabled]} onPress={handlePrimaryAction} disabled={!isFormValid || isLoading} + {...testProperties('login.primary.button')} > {isLoading ? ( @@ -485,10 +490,16 @@ const LoginScreen = ({ navigation }) => { : t('login.passwordNotSet')} - handleSMSLogin() : () => switchToOTPMode()}> + handleSMSLogin() : () => switchToOTPMode()} + {...testProperties('login.switch.SMS')} + > {loginMode === 'otp' ? t('login.loginWithSMS') : t('login.loginWithEmailOTP')} - + {loginMode !== 'password' ? t('login.loginWithPassword') : t('login.loginWithSMS')} diff --git a/apps/mobile/src/app/screens/friend/AddFriend/components/AddFriendModal/index.tsx b/apps/mobile/src/app/screens/friend/AddFriend/components/AddFriendModal/index.tsx index 141bdb5482..f011371527 100644 --- a/apps/mobile/src/app/screens/friend/AddFriend/components/AddFriendModal/index.tsx +++ b/apps/mobile/src/app/screens/friend/AddFriend/components/AddFriendModal/index.tsx @@ -9,9 +9,10 @@ import { DeviceEventEmitter, Platform, Pressable, StatusBar, Text, TextInput, Vi import { KeyboardAvoidingView } from 'react-native-keyboard-controller'; import Toast from 'react-native-toast-message'; import { useDispatch, useSelector } from 'react-redux'; +import StatusBarHeight from '../../../../../components/StatusBarHeight/StatusBarHeight'; import MezonButton from '../../../../../componentUI/MezonButton'; import MezonIconCDN from '../../../../../componentUI/MezonIconCDN'; -import StatusBarHeight from '../../../../../components/StatusBarHeight/StatusBarHeight'; +import { testProperties } from '../../../../../configs/testProperties'; import { IconCDN } from '../../../../../constants/icon_cdn'; import { style } from './styles'; @@ -149,6 +150,7 @@ export const AddFriendModal = React.memo(() => { style={styles.searchInput} onChangeText={handleTextChange} autoCapitalize="none" + {...testProperties('addFriend.input.username')} /> diff --git a/apps/mobile/src/app/screens/friend/AddFriend/index.tsx b/apps/mobile/src/app/screens/friend/AddFriend/index.tsx index 240b2f0938..3df029f9bf 100644 --- a/apps/mobile/src/app/screens/friend/AddFriend/index.tsx +++ b/apps/mobile/src/app/screens/friend/AddFriend/index.tsx @@ -9,6 +9,7 @@ import { DeviceEventEmitter, FlatList, Text, TouchableOpacity, View } from 'reac import { SeparatorWithLine } from '../../../components/Common'; import { EFriendItemAction, FriendItem } from '../../../components/FriendItem'; import { UserInformationBottomSheet } from '../../../components/UserInformationBottomSheet'; +import { testProperties } from '../../../configs/testProperties'; import { EFriendRequest } from '../RequestFriend'; import { EmptyFriendRequest } from '../RequestFriend/EmptyFriendRequest'; import { AddFriendModal } from './components/AddFriendModal'; @@ -61,7 +62,7 @@ export const AddFriendScreen = () => { return ( - + {t('addFriend.addByUserName')} diff --git a/apps/mobile/src/app/screens/friend/index.tsx b/apps/mobile/src/app/screens/friend/index.tsx index 3770f0788c..4a1c846204 100644 --- a/apps/mobile/src/app/screens/friend/index.tsx +++ b/apps/mobile/src/app/screens/friend/index.tsx @@ -13,6 +13,7 @@ import MezonIconCDN from '../../componentUI/MezonIconCDN'; import { EFriendItemAction } from '../../components/FriendItem'; import { FriendListByAlphabet } from '../../components/FriendListByAlphabet'; import { UserInformationBottomSheet } from '../../components/UserInformationBottomSheet'; +import { testProperties } from '../../configs/testProperties'; import { IconCDN } from '../../constants/icon_cdn'; import { APP_SCREEN } from '../../navigation/ScreenTypes'; import { normalizeString } from '../../utils/helpers'; @@ -164,6 +165,7 @@ export const FriendScreen = React.memo(({ navigation }: { navigation: any }) => placeholderTextColor={themeValue.textDisabled} style={styles.searchInput} onChangeText={(text) => typingSearchDebounce(text)} + {...testProperties('friend.input.search')} /> @@ -174,16 +176,20 @@ export const FriendScreen = React.memo(({ navigation }: { navigation: any }) => ) : null} {!searchText?.trim()?.length || filteredFriendList?.length === 0 ? ( - navigateToRequestFriendScreen()}> + navigateToRequestFriendScreen()} + {...testProperties('friend.button.requestFriend')} + > {t('friends:friendRequest.title')} - + {friendRequestCount.received} {t('friends:friendRequest.received')} - + {friendRequestCount.sent} {t('friends:friendRequest.sent')} diff --git a/apps/mobile/src/app/screens/home/homedrawer/components/CreateClanModal/index.tsx b/apps/mobile/src/app/screens/home/homedrawer/components/CreateClanModal/index.tsx index 0fdfc0563c..1dafc7d758 100644 --- a/apps/mobile/src/app/screens/home/homedrawer/components/CreateClanModal/index.tsx +++ b/apps/mobile/src/app/screens/home/homedrawer/components/CreateClanModal/index.tsx @@ -4,6 +4,7 @@ import { size, useTheme } from '@mezon/mobile-ui'; import { channelsActions, checkDuplicateNameClan, clansActions, getStoreAsync } from '@mezon/store-mobile'; import { handleUploadFileMobile, useMezon } from '@mezon/transport'; import { MAX_FILE_SIZE_1MB } from '@mezon/utils'; +import { testProperties } from 'apps/mobile/src/app/configs/testProperties'; import React, { memo, useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { DeviceEventEmitter, StyleSheet, Text, TouchableOpacity, View } from 'react-native'; @@ -134,14 +135,14 @@ const CreateClanModal = memo(() => { /> - + {t('title')} {t('subTitle')} - + {!urlImage ? ( - + )} - + @@ -353,6 +358,7 @@ export const ListClanPopup = React.memo(({ hideActive = false }: { hideActive?: ); }} activationDistance={40} + {...testProperties('listClanPopup.list')} /> ); diff --git a/apps/mobile/src/app/screens/home/homedrawer/components/OTPInput/index.tsx b/apps/mobile/src/app/screens/home/homedrawer/components/OTPInput/index.tsx index fb750affa8..12c581fb6a 100644 --- a/apps/mobile/src/app/screens/home/homedrawer/components/OTPInput/index.tsx +++ b/apps/mobile/src/app/screens/home/homedrawer/components/OTPInput/index.tsx @@ -1,6 +1,7 @@ import { useTheme } from '@mezon/mobile-ui'; import React, { memo, useCallback, useEffect, useRef, useState } from 'react'; import { NativeEventEmitter, NativeModules, Platform, TextInput, TextStyle, View } from 'react-native'; +import { testProperties } from '../../../../../configs/testProperties'; import { style } from './styles'; const OTP_LENGTH = 6; @@ -155,6 +156,7 @@ const OTPInput: React.FC = ({ onOtpChange, onOtpComplete, isError selection={digit !== '' ? { start: 1, end: 1 } : undefined} autoComplete={isSms ? 'sms-otp' : undefined} textContentType={isSms ? 'oneTimeCode' : undefined} + {...testProperties(`otp.input.${index}`)} /> ))} diff --git a/apps/mobile/src/app/screens/profile/ProfileScreen.tsx b/apps/mobile/src/app/screens/profile/ProfileScreen.tsx index 4d613c3aa1..10b9664df0 100644 --- a/apps/mobile/src/app/screens/profile/ProfileScreen.tsx +++ b/apps/mobile/src/app/screens/profile/ProfileScreen.tsx @@ -27,6 +27,7 @@ import { AddStatusUserModal } from '../../components/AddStatusUserModal'; import { CustomStatusUser } from '../../components/CustomStatusUser'; import ImageNative from '../../components/ImageNative'; import { SendTokenUser } from '../../components/SendTokenUser'; +import { testProperties } from '../../configs/testProperties'; import { IconCDN } from '../../constants/icon_cdn'; import { useMixImageColor } from '../../hooks/useMixImageColor'; import useTabletLandscape from '../../hooks/useTabletLandscape'; @@ -239,7 +240,7 @@ const ProfileScreen = ({ navigation }: { navigation: any }) => { )} - showUpdateCustomStatus()}> + showUpdateCustomStatus()} {...testProperties('profile.touch.addStatus')}> {userCustomStatus ? userCustomStatus : t('addStatus')} @@ -269,7 +270,7 @@ const ProfileScreen = ({ navigation }: { navigation: any }) => { )} - + @@ -355,7 +356,11 @@ const ProfileScreen = ({ navigation }: { navigation: any }) => { - navigateToFriendScreen()}> + navigateToFriendScreen()} + {...testProperties('profile.touch.yourFriend')} + > {t('yourFriend')}