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')}