From df1ebe4b2e82537640384fab709d8928d71230bf Mon Sep 17 00:00:00 2001 From: kientrinh-dev Date: Thu, 27 Nov 2025 14:54:18 +0700 Subject: [PATCH 01/14] add e2e --- apps/chat/src/app/pages/channel/index.tsx | 7 +- .../pages/directMessage/FriendsPage/index.tsx | 5 +- apps/chat/src/app/pages/guide/GuideBody.tsx | 6 +- apps/mobile/android/app/build.gradle | 4 +- .../Authentication/BadgeAppIconLoader.tsx | 2 +- .../ChatBox/ChatMessageSending/index.tsx | 48 ++----- .../components/ButtonSwitchCustom/index.tsx | 2 + .../src/lib/components/Canvas/index.tsx | 5 +- .../Component/Modal/addMemRoleModal.tsx | 11 +- .../Component/Modal/listMembers.tsx | 28 ++++- .../Component/Modal/listRoles.tsx | 15 ++- .../PermissionManage/ListPermission.tsx | 3 +- .../PermissionManage/ListRoleMember.tsx | 45 ++++++- .../Component/PermissionsChannel/index.tsx | 6 +- .../ChannelSetting/channelSettingItem.tsx | 1 + .../Canvas/CanvasModal/GroupCanvas.tsx | 12 +- .../Canvas/CanvasModal/index.tsx | 7 +- .../Threads/ThreadModal/ThreadList.tsx | 5 +- .../lib/components/ChannelTopbar/index.tsx | 5 +- .../ClanHeader/ModalCreateCategory.tsx | 9 +- .../src/lib/components/ClanHeader/index.tsx | 7 +- .../SystemMessagesManagement/index.tsx | 29 ++++- .../ClanWebhookItemModal/index.tsx | 6 +- .../SettingChannel/QuickMenuAccessManager.tsx | 38 +++++- .../SettingOnBoarding/GuideItemLayout.tsx | 2 +- .../lib/components/ForwardMessage/index.tsx | 6 +- .../components/Message/ChannelMessageOpt.tsx | 1 + .../ReactionMentionInput.tsx | 41 ++---- .../MessageWithUser/MessageContent.tsx | 2 +- .../MessageReply/MessageReply.tsx | 4 +- .../ModalListClans/SidebarLogoItem.tsx | 4 +- .../NotificationList/AllNotificationItem.tsx | 5 +- .../src/lib/components/SearchModal/index.tsx | 17 ++- .../SettingAccount/SettingPassword.tsx | 16 +-- .../SettingAccount/SettingPhone.tsx | 2 +- libs/store/src/lib/threads/threads.slice.ts | 37 +----- libs/ui/src/lib/Login/PasswordInput.tsx | 14 ++- libs/utils/src/lib/e2e-testing/constants.ts | 118 ++++++++++++++++-- libs/utils/src/lib/types/config.ts | 2 - package.json | 10 +- 40 files changed, 388 insertions(+), 199 deletions(-) diff --git a/apps/chat/src/app/pages/channel/index.tsx b/apps/chat/src/app/pages/channel/index.tsx index ac05beb8e3..33e8be74b2 100644 --- a/apps/chat/src/app/pages/channel/index.tsx +++ b/apps/chat/src/app/pages/channel/index.tsx @@ -578,7 +578,10 @@ export const BanCountDown = ({ banTime, clanId, channelId, userId }: { banTime: }, [time]); return ( -
+
{t('timeoutDesc')}
- {time ? `${time}s` : banTime !== Infinity && countdown} + {time ? `${time}s` : banTime !== Infinity && countdown}
); }; diff --git a/apps/chat/src/app/pages/directMessage/FriendsPage/index.tsx b/apps/chat/src/app/pages/directMessage/FriendsPage/index.tsx index 12fdc22c64..8896821b47 100644 --- a/apps/chat/src/app/pages/directMessage/FriendsPage/index.tsx +++ b/apps/chat/src/app/pages/directMessage/FriendsPage/index.tsx @@ -211,7 +211,10 @@ const FriendsPage = () => { {tab.title} {tab.value === 'pending' && quantityPendingRequest !== 0 && ( -
+
{quantityPendingRequest}
)} diff --git a/apps/chat/src/app/pages/guide/GuideBody.tsx b/apps/chat/src/app/pages/guide/GuideBody.tsx index 66cb2454ea..453aeeaa2e 100644 --- a/apps/chat/src/app/pages/guide/GuideBody.tsx +++ b/apps/chat/src/app/pages/guide/GuideBody.tsx @@ -262,7 +262,7 @@ const QuestionItems = ({ question }: { question: ApiOnboardingItem }) => { const hightLight = useCallback( (index: number) => { if (selectAnswer.includes(index)) { - return 'bg-item-theme text-theme-primary-active border-theme-primary cursor-pointer'; + return 'bg-blue-50 dark:bg-bgSurface hover:border-blue-200 dark:hover:border-bgSurface hover:bg-blue-100 dark:hover:bg-[#212121] text-blue-700 dark:text-primary hover:border-gray-400 dark:hover:border-white border-blue-500 dark:border-channelActiveColor'; } return; }, @@ -270,7 +270,7 @@ const QuestionItems = ({ question }: { question: ApiOnboardingItem }) => { ); return (
-

{question.title}

+

{question.title}

{question.answers && question.answers.map((answer, index) => ( @@ -281,7 +281,7 @@ const QuestionItems = ({ question }: { question: ApiOnboardingItem }) => { title={answer.title} height={'h-auto'} onClick={() => handleOnClickQuestion(index)} - className={` w-fit h-fit rounded-xl hover:bg-transparent justify-center items-center px-4 py-2 border-2 bg-item-theme-hover cursor-pointer font-medium flex gap-2 ${hightLight(index)}`} + className={` w-fit h-fit rounded-xl hover:bg-transparent justify-center items-center px-4 py-2 border-2 border-gray-200 dark:border-[#4e5058] hover:border-gray-400 dark:hover:border-[#7d808c] font-medium flex gap-2 ${hightLight(index)}`} background="bg-white dark:bg-transparent" /> ))} diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle index 00bec35edc..565fa772dc 100644 --- a/apps/mobile/android/app/build.gradle +++ b/apps/mobile/android/app/build.gradle @@ -93,8 +93,8 @@ android { applicationId "com.mezon.mobile" minSdkVersion rootProject.ext.minSdkVersion targetSdkVersion rootProject.ext.targetSdkVersion - versionCode 765 - versionName "1.1.127" + versionCode 761 + versionName "1.1.125" ndk { abiFilters "armeabi-v7a", "arm64-v8a", "x86", "x86_64" } diff --git a/apps/mobile/src/app/navigation/Authentication/BadgeAppIconLoader.tsx b/apps/mobile/src/app/navigation/Authentication/BadgeAppIconLoader.tsx index 2e7a272ae6..a1d92b7d9a 100644 --- a/apps/mobile/src/app/navigation/Authentication/BadgeAppIconLoader.tsx +++ b/apps/mobile/src/app/navigation/Authentication/BadgeAppIconLoader.tsx @@ -10,7 +10,7 @@ export const BadgeAppIconLoader = () => { const allNotificationReplyMentionAllClan = useSelector(selectBadgeCountAllClan); const totalUnreadMessages = useSelector(selectTotalUnreadDM); const { quantityPendingRequest } = useFriends(); - const appState = useRef(AppState.currentState); + const appState = useRef(AppState.currentState); const updateBadgeCount = () => { try { diff --git a/apps/mobile/src/app/screens/home/homedrawer/components/ChatBox/ChatMessageSending/index.tsx b/apps/mobile/src/app/screens/home/homedrawer/components/ChatBox/ChatMessageSending/index.tsx index ed82d2d5ef..655ccd0b17 100644 --- a/apps/mobile/src/app/screens/home/homedrawer/components/ChatBox/ChatMessageSending/index.tsx +++ b/apps/mobile/src/app/screens/home/homedrawer/components/ChatBox/ChatMessageSending/index.tsx @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import { useChannelMembers, useChatSending } from '@mezon/core'; import type { IRoleMention } from '@mezon/mobile-components'; -import { ActionEmitEvent, ID_MENTION_HERE, load, STORAGE_MY_USER_ID } from '@mezon/mobile-components'; +import { ActionEmitEvent, ID_MENTION_HERE, STORAGE_MY_USER_ID, load } from '@mezon/mobile-components'; import { baseColor, size, useTheme } from '@mezon/mobile-ui'; import type { ChannelsEntity } from '@mezon/store-mobile'; import { @@ -29,13 +29,7 @@ import type { IMentionOnMessage, IMessageSendPayload } from '@mezon/utils'; -import { - checkIsThread, - filterEmptyArrays, - THREAD_ARCHIVE_DURATION_SECONDS, - ThreadStatus, - uniqueUsers -} from '@mezon/utils'; +import { ThreadStatus, checkIsThread, filterEmptyArrays, uniqueUsers } from '@mezon/utils'; import { ChannelStreamMode } from 'mezon-js'; import type { ApiMessageMention, ApiMessageRef } from 'mezon-js/api.gen'; import type { MutableRefObject } from 'react'; @@ -166,29 +160,6 @@ export const ChatMessageSending = memo( return usersNotExistingInThread || []; }; - const handleThreadActivation = useCallback( - async (channel: ChannelsEntity | null | undefined) => { - const currentTime = Math.floor(Date.now() / 1000); - const lastMessageTimestamp = channel.last_sent_message?.timestamp_seconds; - const isArchived = lastMessageTimestamp && currentTime - Number(lastMessageTimestamp) > THREAD_ARCHIVE_DURATION_SECONDS; - const needsJoin = channel.active === ThreadStatus.activePublic; - - if (isArchived || (needsJoin && joinningToThread)) { - await dispatch( - threadsActions.writeActiveArchivedThread({ - clanId: channel.clan_id ?? '', - channelId: channel.channel_id ?? '' - }) - ); - } - if (needsJoin && joinningToThread) { - dispatch(threadsActions.updateActiveCodeThread({ channelId: channel.id, activeCode: ThreadStatus.joined })); - joinningToThread(channel, [userId ?? '']); - } - }, - [dispatch, joinningToThread, userId] - ); - const handleSendMessage = async () => { const simplifiedMentionList = !mentionsOnMessage?.current ? [] @@ -209,11 +180,16 @@ export const ChatMessageSending = memo( }; } }); - if (checkIsThread(currentChannel as ChannelsEntity)) { - const usersNotExistingInThread = getUsersNotExistingInThread(simplifiedMentionList); + const usersNotExistingInThread = getUsersNotExistingInThread(simplifiedMentionList); + if (checkIsThread(currentChannel as ChannelsEntity) && usersNotExistingInThread.length > 0) { + await addMemberToThread(currentChannel, usersNotExistingInThread); + } - if (usersNotExistingInThread?.length > 0) await addMemberToThread(currentChannel, usersNotExistingInThread); - await handleThreadActivation(currentChannel); + if (currentChannel?.parent_id !== '0' && currentChannel?.active === ThreadStatus.activePublic) { + await dispatch( + threadsActions.updateActiveCodeThread({ channelId: currentChannel.channel_id ?? '', activeCode: ThreadStatus.joined }) + ); + joinningToThread(currentChannel, [userId ?? '']); } const payloadSendMessage: IMessageSendPayload = { t: removeTags(valueInputRef?.current), @@ -315,7 +291,7 @@ export const ChatMessageSending = memo( simplifiedMentionList || [] ); } else { - const isMentionEveryOne = simplifiedMentionList?.some?.((mention) => mention.user_id === ID_MENTION_HERE); + const isMentionEveryOne = mentionsOnMessage?.current?.some?.((mention) => mention.user_id === ID_MENTION_HERE); await sendMessage( filterEmptyArrays(payloadSendMessage), simplifiedMentionList || [], diff --git a/libs/components/src/lib/components/ButtonSwitchCustom/index.tsx b/libs/components/src/lib/components/ButtonSwitchCustom/index.tsx index 0049e61f47..082f9c18bf 100644 --- a/libs/components/src/lib/components/ButtonSwitchCustom/index.tsx +++ b/libs/components/src/lib/components/ButtonSwitchCustom/index.tsx @@ -1,3 +1,4 @@ +import { generateE2eId } from '@mezon/utils'; import React, { useCallback, useEffect, useMemo, useState } from 'react'; export interface ButtonSwitchProps { @@ -44,6 +45,7 @@ export const ButtonSwitch: React.FC = ({ title, duration = 10 className={`flex items-center p-1 rounded-sm bg-bgSecondary bg-item-theme-hover ${className ?? ''} `} onClick={handleOnClickButton} disabled={disabled} + data-e2e={generateE2eId('button.copy')} > {displayedIcon} {title &&

{title}

} diff --git a/libs/components/src/lib/components/Canvas/index.tsx b/libs/components/src/lib/components/Canvas/index.tsx index 3d302312eb..21d798e7dc 100644 --- a/libs/components/src/lib/components/Canvas/index.tsx +++ b/libs/components/src/lib/components/Canvas/index.tsx @@ -13,7 +13,7 @@ import { selectTheme, selectTitle } from '@mezon/store'; -import { EEventAction } from '@mezon/utils'; +import { EEventAction, generateE2eId } from '@mezon/utils'; import { lazy, Suspense, useCallback, useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; @@ -201,8 +201,9 @@ const Canvas = () => { rows={1} disabled={!isEditAndDelCanvas} className="w-full px-4 py-2 mt-[25px] text-theme-message bg-inherit focus:outline-none text-[28px] resize-none leading-[34px] font-bold " + data-e2e={generateE2eId('clan_page.screen.canvas_editor.input.title')} /> -
+
}> = ({ return (
-
+

{t('addMembersRoles.title')}

{isPrivate === ChannelStatusEnum.isPrivate && channel.type === ChannelType.CHANNEL_TYPE_MEZON_VOICE && ( @@ -230,7 +233,7 @@ export const AddMemRole: React.FC = ({ {filterItem.listRolesNotAddChannel.length !== 0 && (

{t('addMembersRoles.roles')}

-
+
= ({ {filterItem.listMembersNotInChannel.length !== 0 && (

{t('addMembersRoles.members')}

-
+
{ const namePrioritize = getNameForPrioritize(clanName, displayName, username); const avatarPrioritize = getAvatarForPrioritize(clanAvatar, avatar); return ( -
+
); diff --git a/libs/components/src/lib/components/ChannelSetting/Component/Modal/listRoles.tsx b/libs/components/src/lib/components/ChannelSetting/Component/Modal/listRoles.tsx index 1ebabd6bf5..d215be9de8 100644 --- a/libs/components/src/lib/components/ChannelSetting/Component/Modal/listRoles.tsx +++ b/libs/components/src/lib/components/ChannelSetting/Component/Modal/listRoles.tsx @@ -1,5 +1,6 @@ import type { RolesClanEntity } from '@mezon/store'; import { Icons } from '@mezon/ui'; +import { generateE2eId } from '@mezon/utils'; type ListRoleProps = { listItem: RolesClanEntity[]; @@ -10,7 +11,11 @@ type ListRoleProps = { const ListRole = (props: ListRoleProps) => { const { listItem, selectedRoleIds, handleCheckboxRoleChange } = props; return listItem.map((role, index) => ( -
+
)); diff --git a/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListPermission.tsx b/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListPermission.tsx index 5bd15e4ba6..d66b7ca25f 100644 --- a/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListPermission.tsx +++ b/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListPermission.tsx @@ -42,7 +42,8 @@ const ListPermission = forwardRef permissionRoleChannelActions.fetchPermissionRoleChannel({ channelId: props.channelId, roleId: currentRoleId.type === ENTITY_TYPE.ROLE ? currentRoleId.id : '', - userId: currentRoleId.type === ENTITY_TYPE.USER ? currentRoleId.id : '' + userId: currentRoleId.type === ENTITY_TYPE.USER ? currentRoleId.id : '', + noCache: true }) ); } diff --git a/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListRoleMember.tsx b/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListRoleMember.tsx index 55cc2d6926..cb3bc3de2c 100644 --- a/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListRoleMember.tsx +++ b/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/PermissionManage/ListRoleMember.tsx @@ -1,9 +1,16 @@ import type { RolesClanEntity } from '@mezon/store'; -import { channelUsersActions, selectChannelById, selectCurrentClanId, useAppDispatch, useAppSelector } from '@mezon/store'; +import { + channelUsersActions, + permissionRoleChannelActions, + selectChannelById, + selectCurrentClanId, + useAppDispatch, + useAppSelector +} from '@mezon/store'; import { Icons } from '@mezon/ui'; import type { UsersClanEntity } from '@mezon/utils'; -import { createImgproxyUrl, getAvatarForPrioritize, getNameForPrioritize, searchNormalizeText } from '@mezon/utils'; -import { memo, useMemo, useRef, useState } from 'react'; +import { createImgproxyUrl, generateE2eId, getAvatarForPrioritize, getNameForPrioritize, searchNormalizeText } from '@mezon/utils'; +import { memo, useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { AvatarImage } from '../../../../AvatarImage/AvatarImage'; @@ -27,18 +34,47 @@ type ListRoleMemberProps = { const ListRoleMember = memo((props: ListRoleMemberProps) => { const { listManageInChannel, usersClan, channelId, onSelect, canChange, listManageNotInChannel } = props; const [selectedItemId, setSelectedItemId] = useState(listManageInChannel[0].id); + const dispatch = useAppDispatch(); + + useEffect(() => { + if (listManageInChannel.length > 0) { + onSelect(listManageInChannel[0].id, listManageInChannel[0].type); + if (listManageInChannel[0].type === 0) { + dispatch( + permissionRoleChannelActions.fetchPermissionRoleChannel({ + channelId, + roleId: listManageInChannel[0].id, + userId: '' + }) + ); + } else { + dispatch( + permissionRoleChannelActions.fetchPermissionRoleChannel({ + channelId, + roleId: '', + userId: listManageInChannel[0].id + }) + ); + } + } + }, [channelId]); const handleItemClick = (item: any) => { if (canChange) { setSelectedItemId(item.id); onSelect(item.id, item.type); + if (item.type === 0) { + dispatch(permissionRoleChannelActions.fetchPermissionRoleChannel({ channelId, roleId: item.id, userId: '', noCache: true })); + } else { + dispatch(permissionRoleChannelActions.fetchPermissionRoleChannel({ channelId, roleId: '', userId: item.id, noCache: true })); + } } }; return (
-
+
{listManageInChannel.map((item) => (
{ className={`w-full py-1.5 px-[10px] text-[15px] text-theme-primary bg-item-hover font-medium inline-flex gap-x-2 items-center rounded ${ selectedItemId === item.id ? 'bg-item-theme' : '' }`} + data-e2e={generateE2eId('channel_setting_page.permissions.section.list_roles_members.role_member_item')} > {item.title}
diff --git a/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/index.tsx b/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/index.tsx index 0e1c454bc2..8240f253c3 100644 --- a/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/index.tsx +++ b/libs/components/src/lib/components/ChannelSetting/Component/PermissionsChannel/index.tsx @@ -141,7 +141,11 @@ const PermissionsChannel = (props: PermissionsChannelProps) => { >

{t('channelPermission.whoCanAccess')}

-
diff --git a/libs/components/src/lib/components/ChannelSetting/channelSettingItem.tsx b/libs/components/src/lib/components/ChannelSetting/channelSettingItem.tsx index afac9c896e..294322128c 100644 --- a/libs/components/src/lib/components/ChannelSetting/channelSettingItem.tsx +++ b/libs/components/src/lib/components/ChannelSetting/channelSettingItem.tsx @@ -161,6 +161,7 @@ const ChannelSettingItem = (props: ChannelSettingItemProps) => { onClick={() => { setShowModal(true); }} + data-e2e={generateE2eId('button.base')} > {isThread ? t('fields.threadDelete.delete') : t('fields.channelDelete.delete')} diff --git a/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/GroupCanvas.tsx b/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/GroupCanvas.tsx index 5393382aee..345c325410 100644 --- a/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/GroupCanvas.tsx +++ b/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/GroupCanvas.tsx @@ -1,6 +1,6 @@ import { useAuth } from '@mezon/core'; import { appActions, canvasActions, canvasAPIActions, selectIdCanvas, useAppDispatch } from '@mezon/store'; -import { ICanvas } from '@mezon/utils'; +import { generateE2eId, ICanvas } from '@mezon/utils'; import { ButtonCopy } from 'libs/components/src/lib/components'; import { useSelector } from 'react-redux'; import { Link } from 'react-router-dom'; @@ -58,7 +58,7 @@ const GroupCanvas = ({ canvas, channelId, clanId, onClose, creatorIdChannel, sel : `/chat/clans/${clanId}/channels/${channelId}/canvas/${canvasId}`; return ( -
+
-
{canvas.title ? canvas.title : 'Untitled'}
+
+ {canvas.title ? canvas.title : 'Untitled'} +
X diff --git a/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/index.tsx b/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/index.tsx index 43cb593ccf..2550925fe5 100644 --- a/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/index.tsx +++ b/libs/components/src/lib/components/ChannelTopbar/TopBarComponents/Canvas/CanvasModal/index.tsx @@ -13,6 +13,7 @@ import { useAppSelector } from '@mezon/store'; import { Icons } from '@mezon/ui'; +import { generateE2eId } from '@mezon/utils'; import type { RefObject } from 'react'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -91,7 +92,11 @@ const CanvasModal = ({ onClose, rootRef }: CanvasProps) => {
-
@@ -331,6 +356,7 @@ const QuickMenuAccessManager: React.FC = ({ channel type="submit" disabled={loading || !formData.menu_name?.trim() || (activeTab === 'flash' && !formData.action_msg?.trim())} className="bg-[#5865f2] hover:bg-[#4752c4] disabled:bg-gray-600 disabled:cursor-not-allowed text-white px-4 py-1.5 text-sm rounded-lg font-medium transition-colors duration-200 flex items-center gap-2" + data-e2e={generateE2eId('channel_setting_page.quick_menu.modal.button.submit')} > {loading && ( diff --git a/libs/components/src/lib/components/ClanSettings/SettingOnBoarding/GuideItemLayout.tsx b/libs/components/src/lib/components/ClanSettings/SettingOnBoarding/GuideItemLayout.tsx index 8b9f8f0523..efdae36d23 100644 --- a/libs/components/src/lib/components/ClanSettings/SettingOnBoarding/GuideItemLayout.tsx +++ b/libs/components/src/lib/components/ClanSettings/SettingOnBoarding/GuideItemLayout.tsx @@ -36,7 +36,7 @@ export const GuideItemLayout = ({ onClick={onClick} > {icon && ( -
+
{icon}
)} diff --git a/libs/components/src/lib/components/ForwardMessage/index.tsx b/libs/components/src/lib/components/ForwardMessage/index.tsx index 8aa123e882..e83e93c769 100644 --- a/libs/components/src/lib/components/ForwardMessage/index.tsx +++ b/libs/components/src/lib/components/ForwardMessage/index.tsx @@ -26,6 +26,7 @@ import { ModeResponsive, TypeSearch, addAttributesSearchList, + generateE2eId, getAvatarForPrioritize, normalizeString, removeDuplicatesById @@ -429,7 +430,7 @@ const ForwardMessageModal = () => { return ( -
+

{t('modal.title')}

@@ -440,6 +441,7 @@ const ForwardMessageModal = () => { placeholder={t('modal.searchPlaceholder')} onChange={(e) => setSearchText(e.target.value)} onKeyDown={(e) => handleInputKeyDown(e)} + data-e2e={generateE2eId('modal.forward_message.input.search')} />
{!normalizedSearchText.startsWith('@') && !normalizedSearchText.startsWith('#') ? ( @@ -523,6 +525,7 @@ const FooterButtonsModal = (props: FooterButtonsModalProps) => { type="button" onClick={onClose} disabled={loading} + data-e2e={generateE2eId('modal.forward_message.button.cancel')} > {t('modal.cancel')} @@ -530,6 +533,7 @@ const FooterButtonsModal = (props: FooterButtonsModalProps) => { onClick={handleSend} className="py-2 h-10 px-4 rounded text-white bg-bgSelectItem hover:!bg-bgSelectItemHover focus:ring-transparent" disabled={loading} + data-e2e={generateE2eId('modal.forward_message.button.send')} > {loading ? t('modal.sending') : t('modal.send')} diff --git a/libs/components/src/lib/components/Message/ChannelMessageOpt.tsx b/libs/components/src/lib/components/Message/ChannelMessageOpt.tsx index 4222827f27..6dcf7ded0d 100644 --- a/libs/components/src/lib/components/Message/ChannelMessageOpt.tsx +++ b/libs/components/src/lib/components/Message/ChannelMessageOpt.tsx @@ -102,6 +102,7 @@ const ChannelMessageOpt = ({ return (
diff --git a/libs/components/src/lib/components/MessageBox/ReactionMentionInput/ReactionMentionInput.tsx b/libs/components/src/lib/components/MessageBox/ReactionMentionInput/ReactionMentionInput.tsx index a2bf89e561..81d833d5cd 100644 --- a/libs/components/src/lib/components/MessageBox/ReactionMentionInput/ReactionMentionInput.tsx +++ b/libs/components/src/lib/components/MessageBox/ReactionMentionInput/ReactionMentionInput.tsx @@ -46,7 +46,6 @@ import { QUICK_MENU_TYPE, RECENT_EMOJI_CATEGORY, SubPanelName, - THREAD_ARCHIVE_DURATION_SECONDS, TITLE_MENTION_HERE, ThreadStatus, addMention, @@ -250,33 +249,6 @@ export const MentionReactBase = memo((props: MentionReactBaseProps): ReactElemen ] ); - const handleThreadActivation = useCallback( - async (channel: ChannelsEntity | null | undefined) => { - if (!checkIsThread(channel as ChannelsEntity) || !channel) { - return; - } - - const currentTime = Math.floor(Date.now() / 1000); - const lastMessageTimestamp = channel.last_sent_message?.timestamp_seconds; - const isArchived = lastMessageTimestamp && currentTime - Number(lastMessageTimestamp) > THREAD_ARCHIVE_DURATION_SECONDS; - const needsJoin = channel.active === ThreadStatus.activePublic; - - if (isArchived || (needsJoin && joinningToThread)) { - await dispatch( - threadsActions.writeActiveArchivedThread({ - clanId: channel.clan_id ?? '', - channelId: channel.channel_id ?? '' - }) - ); - } - if (needsJoin && joinningToThread) { - dispatch(threadsActions.updateActiveCodeThread({ channelId: channel.id, activeCode: ThreadStatus.joined })); - joinningToThread(channel, [userProfile?.user?.id ?? '']); - } - }, - [userProfile?.user?.id] - ); - const handleSendInternal = useCallback( async (checkedRequest: RequestInput, anonymousMessage?: boolean) => { //TODO: break logic send width thread box, channel, topic box, dm @@ -372,7 +344,10 @@ export const MentionReactBase = memo((props: MentionReactBaseProps): ReactElemen await addMemberToThread(currentChannel!, usersNotExistingInThread); } - await handleThreadActivation(currentChannel); + if (checkIsThread(currentChannel as ChannelsEntity) && currentChannel?.active === ThreadStatus.activePublic && joinningToThread) { + dispatch(threadsActions.updateActiveCodeThread({ channelId: currentChannel.channel_id ?? '', activeCode: ThreadStatus.joined })); + joinningToThread(currentChannel, [userProfile?.user?.id ?? '']); + } if (isReplyOnChannel) { props.onSend( @@ -577,7 +552,10 @@ export const MentionReactBase = memo((props: MentionReactBaseProps): ReactElemen addMemberToThread(currentChannel!, usersNotExistingInThread); } - await handleThreadActivation(currentChannel); + if (checkIsThread(currentChannel as ChannelsEntity) && currentChannel?.active === ThreadStatus.activePublic && joinningToThread) { + dispatch(threadsActions.updateActiveCodeThread({ channelId: currentChannel.channel_id ?? '', activeCode: ThreadStatus.joined })); + joinningToThread(currentChannel, [userProfile?.user?.id ?? '']); + } if (isReplyOnChannel) { props.onSend( @@ -711,8 +689,7 @@ export const MentionReactBase = memo((props: MentionReactBaseProps): ReactElemen currentChannel, setOpenThreadMessageState, updateDraft, - setSubPanelActive, - handleThreadActivation + setSubPanelActive ] ); diff --git a/libs/components/src/lib/components/MessageWithUser/MessageContent.tsx b/libs/components/src/lib/components/MessageWithUser/MessageContent.tsx index 1f6a040d40..cd19c9367a 100644 --- a/libs/components/src/lib/components/MessageWithUser/MessageContent.tsx +++ b/libs/components/src/lib/components/MessageWithUser/MessageContent.tsx @@ -103,7 +103,7 @@ export const TopicViewButton = ({ message }: { message: IMessageWithUser }) => { src={avatarToDisplay} />
-

+

{rplCount > 0 && (rplCount === 1 ? t('reply', { number: 1 }) : t('numberReplies', { number: rplCount > 99 ? '99+' : rplCount }))}

diff --git a/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx b/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx index fd92c1e9ca..145e9ddf2a 100644 --- a/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx +++ b/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx @@ -105,10 +105,10 @@ const MessageReply: React.FC = ({ message, onClick, isTopic, {!isClanView ? message?.references?.[0]?.message_sender_display_name || messageUsernameSenderRef : nameShowed} {hasAttachmentInMessageRef || isEmbedMessage ? ( -
+
Click to see attachment
diff --git a/libs/components/src/lib/components/ModalListClans/SidebarLogoItem.tsx b/libs/components/src/lib/components/ModalListClans/SidebarLogoItem.tsx index 5688af8acb..f55538b782 100644 --- a/libs/components/src/lib/components/ModalListClans/SidebarLogoItem.tsx +++ b/libs/components/src/lib/components/ModalListClans/SidebarLogoItem.tsx @@ -12,7 +12,7 @@ import { useAppSelector } from '@mezon/store'; import { Image } from '@mezon/ui'; -import { ModeResponsive, createImgproxyUrl } from '@mezon/utils'; +import { ModeResponsive, createImgproxyUrl, generateE2eId } from '@mezon/utils'; import { useCallback, useState } from 'react'; import { useModal } from 'react-modal-hook'; import type { Coords } from '../ChannelLink'; @@ -69,7 +69,7 @@ const SidebarLogoItem = () => { draggable="false" > -
+
{category === NotificationCategory.MENTIONS || category === NotificationCategory.MESSAGES ? ( -
+
-
-
+
+
-
+
-
-
+
+
{hasPassword ? t('setPasswordAccount.changePassword') : t('setPasswordModal.title')}
-

{t('setPasswordModal.description')}

+

{t('setPasswordModal.description')}

-
✕ diff --git a/libs/store/src/lib/threads/threads.slice.ts b/libs/store/src/lib/threads/threads.slice.ts index 419e73f28c..9aa0cb6cdb 100644 --- a/libs/store/src/lib/threads/threads.slice.ts +++ b/libs/store/src/lib/threads/threads.slice.ts @@ -39,7 +39,6 @@ export interface ThreadsState extends EntityState { loadingStatusSearchedThread?: LoadingStatus; threadSearchedResult?: Record; inputSearchThread?: Record; - shouldTriggerSendFromThreadName?: boolean; } export const threadsAdapter = createEntityAdapter({ @@ -243,8 +242,7 @@ export const initialThreadsState: ThreadsState = threadsAdapter.getInitialState( isThreadModalVisible: false, loadingStatusSearchedThread: 'not loaded', threadSearchedResult: {}, - inputSearchThread: {}, - shouldTriggerSendFromThreadName: false + inputSearchThread: {} }); export const checkDuplicateThread = createAsyncThunk( @@ -283,21 +281,6 @@ export const leaveThread = createAsyncThunk( } ); -export const writeActiveArchivedThread = createAsyncThunk( - 'threads/writeActiveArchivedThread', - async ({ clanId, channelId }: { clanId: string; channelId: string }, thunkAPI) => { - try { - const mezon = await ensureSocket(getMezonCtx(thunkAPI)); - await mezon.socketRef.current?.writeActiveArchivedThread(clanId, channelId); - thunkAPI.dispatch(threadsActions.updateActiveCodeThread({ channelId, activeCode: ThreadStatus.joined })); - return { channelId, activeCode: ThreadStatus.joined }; - } catch (error) { - captureSentryError(error, 'threads/writeActiveArchivedThread'); - return thunkAPI.rejectWithValue(error); - } - } -); - export const threadsSlice = createSlice({ name: THREADS_FEATURE_KEY, initialState: initialThreadsState, @@ -413,12 +396,6 @@ export const threadsSlice = createSlice({ if (state?.threadSearchedResult) { state.threadSearchedResult[channelId] = null; } - }, - triggerSendFromThreadName: (state: ThreadsState) => { - state.shouldTriggerSendFromThreadName = true; - }, - resetTriggerSendFromThreadName: (state: ThreadsState) => { - state.shouldTriggerSendFromThreadName = false; } }, extraReducers: (builder) => { @@ -524,15 +501,7 @@ export const threadsReducer = threadsSlice.reducer; * * See: https://react-redux.js.org/next/api/hooks#usedispatch */ -export const threadsActions = { - ...threadsSlice.actions, - fetchThreads, - fetchThread, - leaveThread, - updateCacheOnThreadCreation, - searchedThreads, - writeActiveArchivedThread -}; +export const threadsActions = { ...threadsSlice.actions, fetchThreads, fetchThread, leaveThread, updateCacheOnThreadCreation, searchedThreads }; /* * Export selectors to query state. For use with the `useSelector` hook. @@ -607,5 +576,3 @@ export const selectThreadsByParentChannelId = createSelector( return selectAll(channelState); } ); - -export const selectShouldTriggerSendFromThreadName = createSelector(getThreadsState, (state) => state.shouldTriggerSendFromThreadName); diff --git a/libs/ui/src/lib/Login/PasswordInput.tsx b/libs/ui/src/lib/Login/PasswordInput.tsx index 8820fbc3a4..53bde59a50 100644 --- a/libs/ui/src/lib/Login/PasswordInput.tsx +++ b/libs/ui/src/lib/Login/PasswordInput.tsx @@ -26,7 +26,7 @@ const PasswordInput = memo(({ id, label, value, onChange, error, isLoading, onFo return (
-
diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index bc56b29fff..c553f3e3b3 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -19,7 +19,8 @@ export const DATA_E2E_IDENTIFIER = { name: '' }, button: { - base: '' + base: '', + copy: '', }, base_profile: { display_name: '' @@ -29,6 +30,9 @@ export const DATA_E2E_IDENTIFIER = { selected_file: '', text: { about_me: '' + }, + banned: { + time: '' } }, discussion: { @@ -90,6 +94,7 @@ export const DATA_E2E_IDENTIFIER = { } }, side_bar: { + DM_item: '', clan_item: { name: '' }, @@ -138,7 +143,8 @@ export const DATA_E2E_IDENTIFIER = { button: { confirm: '', cancel: '' - } + }, + error_message: '' }, create_clan: { input: { @@ -247,7 +253,11 @@ export const DATA_E2E_IDENTIFIER = { integrations: { create_clan_webhook_button: '', new_clan_webhook_button: '', - navigate_webhook_button: '' + navigate_webhook_button: '', + webhook_item: { + webhook_title: '', + webhook_description: '' + } }, sidebar: { delete: '', @@ -285,6 +295,18 @@ export const DATA_E2E_IDENTIFIER = { overview: { input: { clan_name: '' + }, + system_messages_channel: { + selection: { + item: { + channel_name: '', + category_name: '' + }, + selected: { + channel_name: '', + category_name: '' + } + } } }, upload: { @@ -351,6 +373,12 @@ export const DATA_E2E_IDENTIFIER = { button: { join_voice: '' } + }, + canvas_editor: { + input: { + title: '', + content: '' + } } }, channel_management: { @@ -372,6 +400,24 @@ export const DATA_E2E_IDENTIFIER = { }, section: { member_role_management: { + button: { + add: '' + }, + modal: { + role_list: { + role_item: { + input: '', + title: '' + } + }, + member_list: { + member_item: { + input: '', + name_prioritize: '', + username: '' + } + } + }, role_list: { role_item: '' }, @@ -379,7 +425,10 @@ export const DATA_E2E_IDENTIFIER = { member_item: '' } }, - advanced_permissions: '' + advanced_permissions: '', + list_roles_members: { + role_member_item: '' + } }, modal: { ask_change: { @@ -399,6 +448,27 @@ export const DATA_E2E_IDENTIFIER = { input: { avatar_channel_webhook: '' } + }, + quick_menu: { + tab: '', + button: { + add: '' + }, + modal: { + input: { + command_name: '', + message_content: '' + }, + button: { + submit: '', + cancel: '' + } + }, + item: { + command: '', + type: '', + message_content: '' + } } }, chat: { @@ -438,7 +508,8 @@ export const DATA_E2E_IDENTIFIER = { button: { add_user: '', create_group: '', - button_plus: '' + button_plus: '', + search: '' }, edit_group: { button: '', @@ -492,7 +563,21 @@ export const DATA_E2E_IDENTIFIER = { pin: { pin_badge: '' }, - canvas: '', + canvas: { + modal: { + canvas_management: { + button: { + create_canvas: '' + } + } + }, + item: { + title: '', + button: { + delete: '' + } + } + }, thread: { modal: { thread_management: { @@ -532,6 +617,9 @@ export const DATA_E2E_IDENTIFIER = { item: { actions: '' } + }, + inbox: { + mentions: '' } }, mention: { @@ -575,7 +663,8 @@ export const DATA_E2E_IDENTIFIER = { }, button: { view_topic: '' - } + }, + number_replies: '' } }, onboarding: { @@ -661,6 +750,18 @@ export const DATA_E2E_IDENTIFIER = { button: { control_item: '' } + }, + forward_message: { + button: { + cancel: '', + send: '' + }, + input: { + search: '' + } + }, + search: { + input: '' } }, icon: { @@ -701,6 +802,9 @@ export const DATA_E2E_IDENTIFIER = { }, suggest_item: { username: '' + }, + badge: { + friend_pending: '' } }; type DotNestedKeys = T extends object diff --git a/libs/utils/src/lib/types/config.ts b/libs/utils/src/lib/types/config.ts index 137d0b17e4..a9ff50860b 100644 --- a/libs/utils/src/lib/types/config.ts +++ b/libs/utils/src/lib/types/config.ts @@ -31,8 +31,6 @@ export const MESSAGE_LIST_SLICE = isBigScreen ? 60 : 40; export const MESSAGE_LIST_VIEWPORT_LIMIT = MESSAGE_LIST_SLICE * 2; export const ARCHIVE_MINIMIZED_HEIGHT = 36; - -export const THREAD_ARCHIVE_DURATION_SECONDS = 7 * 24 * 60 * 60; export const CHAT_HEIGHT_PX = 72; export const TOPIC_HEIGHT_PX = 65; export const PEER_PICKER_ITEM_HEIGHT_PX = 56; diff --git a/package.json b/package.json index 6cd6daef4a..52195c3dbc 100644 --- a/package.json +++ b/package.json @@ -41,8 +41,8 @@ "@babel/runtime": "^7.27.3", "@floating-ui/react": "^0.27.16", "@hookform/resolvers": "^3.3.4", - "@livekit/components-react": "^2.9.16", - "@livekit/components-styles": "^1.2.0", + "@livekit/components-react": "^2.9.13", + "@livekit/components-styles": "^1.1.6", "@livekit/track-processors": "^0.4.0", "@loadable/component": "^5.16.3", "@react-native-async-storage/async-storage": "^2.1.2", @@ -64,13 +64,13 @@ "i18next-browser-languagedetector": "^8.0.0", "i18next-http-backend": "^2.5.1", "is-electron": "^2.2.2", - "livekit-client": "^2.16.0", + "livekit-client": "^2.15.2", "localforage": "^1.10.0", "lodash.debounce": "^4.0.8", "lodash.isequal": "^4.5.0", "mezon-active-windows": "0.1.38", - "mezon-js": "2.13.51", - "mezon-js-protobuf": "1.8.60", + "mezon-js": "2.13.48", + "mezon-js-protobuf": "1.8.56", "mmn-client-js": "1.0.11", "minio": "^7.1.3", "nx-electron": "^20.0.2", From add40d926fc9397f455f20ddbc2e550fe9dc8c96 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Fri, 28 Nov 2025 12:05:27 +0700 Subject: [PATCH 02/14] e2e kick user from clan --- .../components/MemberProfile/ModalRemoveMemberClan.tsx | 4 ++++ libs/utils/src/lib/e2e-testing/constants.ts | 9 ++++++++- 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/libs/components/src/lib/components/MemberProfile/ModalRemoveMemberClan.tsx b/libs/components/src/lib/components/MemberProfile/ModalRemoveMemberClan.tsx index 3665018c61..976699ab8e 100644 --- a/libs/components/src/lib/components/MemberProfile/ModalRemoveMemberClan.tsx +++ b/libs/components/src/lib/components/MemberProfile/ModalRemoveMemberClan.tsx @@ -1,4 +1,5 @@ import { selectCurrentClanName } from '@mezon/store'; +import { generateE2eId } from '@mezon/utils'; import { useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; @@ -65,6 +66,7 @@ const ModalRemoveMemberClan = ({ username, onClose, onRemoveMember }: ModalRemov value={value ?? ''} onChange={handleChange} className="text-theme-primary-active outline-none w-full h-16 p-[10px] bg-input-theme text-base rounded placeholder:text-sm" + data-e2e={generateE2eId('clan_page.modal.kick_member.reason_input')} />
@@ -72,12 +74,14 @@ const ModalRemoveMemberClan = ({ username, onClose, onRemoveMember }: ModalRemov className="w-20 py-2.5 h-10 text-sm font-medium text-theme-primary-active dark:text-zinc-50 bg-bgTextarea dark:bg-gray-700 border border-color-theme hover:bg-gray-300 dark:hover:bg-gray-600 rounded-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 dark:focus:ring-offset-gray-800 shadow-sm" type="button" onClick={onClose} + data-e2e={generateE2eId('clan_page.modal.kick_member.button.cancel')} > {t('buttons.cancel')} diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index c553f3e3b3..6f44160f73 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -20,7 +20,7 @@ export const DATA_E2E_IDENTIFIER = { }, button: { base: '', - copy: '', + copy: '' }, base_profile: { display_name: '' @@ -244,6 +244,13 @@ export const DATA_E2E_IDENTIFIER = { } } } + }, + kick_member: { + reason_input: '', + button: { + kick: '', + cancel: '' + } } }, settings: { From 0557842d4f9473387e7a3896c21f80076eb56f74 Mon Sep 17 00:00:00 2001 From: thieuthang-dev Date: Mon, 1 Dec 2025 11:43:38 +0700 Subject: [PATCH 03/14] feat: add e2e for case verify username in short profile --- .../MessageReply/MessageReply.tsx | 9 +++++++-- .../lib/components/ModalUserProfile/index.tsx | 19 +++++++++++++++---- libs/utils/src/lib/e2e-testing/constants.ts | 15 +++++++++++++++ 3 files changed, 37 insertions(+), 6 deletions(-) diff --git a/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx b/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx index fd92c1e9ca..6649f8301f 100644 --- a/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx +++ b/libs/components/src/lib/components/MessageWithUser/MessageReply/MessageReply.tsx @@ -2,7 +2,7 @@ import { getShowName, useUserById } from '@mezon/core'; import { getStoreAsync, messagesActions, selectClanView, selectCurrentChannelId, useAppDispatch } from '@mezon/store'; import { Icons } from '@mezon/ui'; import type { IMessageWithUser } from '@mezon/utils'; -import { MEZON_AVATAR_URL, createImgproxyUrl, getAvatarForPrioritize } from '@mezon/utils'; +import { MEZON_AVATAR_URL, createImgproxyUrl, generateE2eId, getAvatarForPrioritize } from '@mezon/utils'; import { useCallback, useRef } from 'react'; import { AvatarImage } from '../../AvatarImage/AvatarImage'; @@ -81,7 +81,11 @@ const MessageReply: React.FC = ({ message, onClick, isTopic, const avatarProps = getAvatarProps(); return ( -
+
{message.references?.[0].message_ref_id ? (
@@ -101,6 +105,7 @@ const MessageReply: React.FC = ({ message, onClick, isTopic, {!isClanView ? message?.references?.[0]?.message_sender_display_name || messageUsernameSenderRef : nameShowed} diff --git a/libs/components/src/lib/components/ModalUserProfile/index.tsx b/libs/components/src/lib/components/ModalUserProfile/index.tsx index 0ea11668af..64950a2ade 100644 --- a/libs/components/src/lib/components/ModalUserProfile/index.tsx +++ b/libs/components/src/lib/components/ModalUserProfile/index.tsx @@ -15,7 +15,7 @@ import type { RootState } from '@mezon/store'; import { EStateFriend, selectAllAccount, selectChannelByChannelId, selectFriendById, selectStatusInVoice, useAppSelector } from '@mezon/store'; import { Icons } from '@mezon/ui'; import type { ChannelMembersEntity, IMessageWithUser } from '@mezon/utils'; -import { EUserStatus } from '@mezon/utils'; +import { EUserStatus, generateE2eId } from '@mezon/utils'; import { ChannelStreamMode } from 'mezon-js'; import type { RefObject } from 'react'; import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'; @@ -250,7 +250,10 @@ const ModalUserProfile = ({
-

+

{isUserRemoved ? t('labels.unknownUser') : checkAnonymous @@ -261,7 +264,10 @@ const ModalUserProfile = ({ currentUserId?.display_name || usernameShow}

-

+

{isUserRemoved ? t('labels.unknownUser') : usernameShow || currentUserId?.username || ''}

@@ -296,6 +302,7 @@ const ModalUserProfile = ({ value={content} onKeyPress={handleOnKeyPress} onChange={handleContent} + data-e2e={generateE2eId('short_profile.input.send_message')} />
) : ( @@ -307,7 +314,11 @@ const ModalUserProfile = ({ ) ) : null} {!isFooterProfile && checkUser && ( - )} diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index 6f44160f73..e4c218aa31 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -25,6 +25,17 @@ export const DATA_E2E_IDENTIFIER = { base_profile: { display_name: '' }, + short_profile : { + display_name: '', + username: '', + input: { + send_message: '' + }, + button: { + edit_profile: '', + add_role: '' + } + }, mention: { input: '', selected_file: '', @@ -802,6 +813,10 @@ export const DATA_E2E_IDENTIFIER = { message: { item: '' }, + replied_message: { + item: '', + username: '' + }, acceptModal: { button: { acceptInvite: '' From 85edda501c3ddbc851a4bacd69a2ddf8c4761a09 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Thu, 4 Dec 2025 11:15:20 +0700 Subject: [PATCH 04/14] mark e2e to flow custom user status --- .../lib/components/FooterProfile/index.tsx | 5 +++- .../MemberProfile/MemberProfile.tsx | 5 +++- .../ModalUserProfile/AvatarProfile.tsx | 7 ++++- .../StatusProfile/ItemStatus.tsx | 2 ++ .../StatusProfile/ModalCustomStatus.tsx | 12 ++++++-- libs/utils/src/lib/e2e-testing/constants.ts | 28 +++++++++++++++++-- 6 files changed, 51 insertions(+), 8 deletions(-) diff --git a/libs/components/src/lib/components/FooterProfile/index.tsx b/libs/components/src/lib/components/FooterProfile/index.tsx index 0c85cc9360..11f2dc3579 100644 --- a/libs/components/src/lib/components/FooterProfile/index.tsx +++ b/libs/components/src/lib/components/FooterProfile/index.tsx @@ -351,7 +351,10 @@ function FooterProfile({ name, status, avatar, userId, isDM }: FooterProfileProp > {name}

-

+

{userCustomStatus}

diff --git a/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx b/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx index 61d6a464c5..9fd43c6d39 100644 --- a/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx +++ b/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx @@ -70,7 +70,10 @@ export const BaseMemberProfile = ({ id, user, userMeta, username, avatar, isOwne
-

+

{userStatus}

diff --git a/libs/components/src/lib/components/ModalUserProfile/AvatarProfile.tsx b/libs/components/src/lib/components/ModalUserProfile/AvatarProfile.tsx index db2d010d8a..84fafb2d27 100644 --- a/libs/components/src/lib/components/ModalUserProfile/AvatarProfile.tsx +++ b/libs/components/src/lib/components/ModalUserProfile/AvatarProfile.tsx @@ -97,7 +97,10 @@ const AvatarProfile = ({
- + {activityStatus} {isFooterProfile && ( @@ -105,12 +108,14 @@ const AvatarProfile = ({
diff --git a/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ItemStatus.tsx b/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ItemStatus.tsx index ce4d3c3c7e..4788d7082a 100644 --- a/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ItemStatus.tsx +++ b/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ItemStatus.tsx @@ -1,4 +1,5 @@ import { Icons } from '@mezon/ui'; +import { generateE2eId } from '@mezon/utils'; import type { ReactNode } from 'react'; type ItemStatusProps = { @@ -17,6 +18,7 @@ const ItemStatus = ({ children, description, dropdown, startIcon, type, onClick,
{startIcon &&
{startIcon}
} diff --git a/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ModalCustomStatus.tsx b/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ModalCustomStatus.tsx index f5da927ca8..b7bb1b0cb6 100644 --- a/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ModalCustomStatus.tsx +++ b/libs/components/src/lib/components/ModalUserProfile/StatusProfile/ModalCustomStatus.tsx @@ -1,5 +1,6 @@ import { channelMembersActions, selectCurrentClanId, useAppDispatch, userClanProfileActions } from '@mezon/store'; import { Icons, Menu } from '@mezon/ui'; +import { generateE2eId } from '@mezon/utils'; import type { ReactElement, ReactNode } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -118,7 +119,7 @@ const ModalCustomStatus = ({ name, status, onClose, time_reset = 0 }: ModalCusto return ( -
+

{t('title')}

@@ -135,6 +136,7 @@ const ModalCustomStatus = ({ name, status, onClose, time_reset = 0 }: ModalCusto maxLength={128} autoFocus onChange={handleChangeCustomStatus} + data-e2e={generateE2eId('short_profile.modal.custom_status.input')} />
@@ -149,13 +151,19 @@ const ModalCustomStatus = ({ name, status, onClose, time_reset = 0 }: ModalCusto
- diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index e4c218aa31..64227ccd38 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -16,7 +16,8 @@ export const DATA_E2E_IDENTIFIER = { }, footer_profile: { avatar: '', - name: '' + name: '', + user_status: '' }, button: { base: '', @@ -25,15 +26,35 @@ export const DATA_E2E_IDENTIFIER = { base_profile: { display_name: '' }, - short_profile : { + short_profile: { display_name: '', username: '', + activity_status: { + button: { + custom: '', + clear: '' + } + }, input: { send_message: '' }, button: { edit_profile: '', add_role: '' + }, + action: { + button: { + base: '' + } + }, + modal: { + custom_status: { + input: '', + button: { + cancel: '', + save: '' + } + } } }, mention: { @@ -383,7 +404,8 @@ export const DATA_E2E_IDENTIFIER = { }, secondary_side_bar: { member: { - in_voice: '' + in_voice: '', + user_status: '' } }, screen: { From a2ffec21d05e736c613e75d8a7b2de847142d941 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Fri, 5 Dec 2025 11:44:29 +0700 Subject: [PATCH 05/14] e2e for custom status on friend list --- .../src/lib/components/MemberProfile/BaseProfile.tsx | 9 ++++++++- libs/utils/src/lib/e2e-testing/constants.ts | 3 ++- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/libs/components/src/lib/components/MemberProfile/BaseProfile.tsx b/libs/components/src/lib/components/MemberProfile/BaseProfile.tsx index 84d70d4bb2..6ca4dc11dd 100644 --- a/libs/components/src/lib/components/MemberProfile/BaseProfile.tsx +++ b/libs/components/src/lib/components/MemberProfile/BaseProfile.tsx @@ -41,7 +41,14 @@ const BaseProfile = ({ {displayName || name} )} - {userStatus && {userStatus}} + {userStatus && ( + + {userStatus} + + )}
); diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index 64227ccd38..9b0fac7358 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -24,7 +24,8 @@ export const DATA_E2E_IDENTIFIER = { copy: '' }, base_profile: { - display_name: '' + display_name: '', + user_status: '' }, short_profile: { display_name: '', From bede668eddae4adc50183a283fb36e3f9214bd43 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Tue, 9 Dec 2025 11:33:09 +0700 Subject: [PATCH 06/14] mark e2e to send gif on voice channel chat box --- .../GifsStickersEmojis/gifs/FeaturedGifs.tsx | 8 +++++++- .../GifsStickersEmojis/gifs/GifCategory.tsx | 10 ++++++++-- .../GifsStickersEmojis/gifs/TenorGifCategories.tsx | 6 ++++-- .../MessageBox/GifsStickerEmojiButtons.tsx | 11 ++++++----- libs/utils/src/lib/e2e-testing/constants.ts | 13 +++++++++++++ 5 files changed, 38 insertions(+), 10 deletions(-) diff --git a/libs/components/src/lib/components/GifsStickersEmojis/gifs/FeaturedGifs.tsx b/libs/components/src/lib/components/GifsStickersEmojis/gifs/FeaturedGifs.tsx index 1bee0a7637..8d5c719293 100644 --- a/libs/components/src/lib/components/GifsStickersEmojis/gifs/FeaturedGifs.tsx +++ b/libs/components/src/lib/components/GifsStickersEmojis/gifs/FeaturedGifs.tsx @@ -1,5 +1,6 @@ import { useGifs } from '@mezon/core'; import { Icons } from '@mezon/ui'; +import { generateE2eId } from '@mezon/utils'; type FeaturedGifsProps = { channelId: string; @@ -13,7 +14,12 @@ type FeaturedGifsProps = { function FeaturedGifs({ onClickToTrending }: FeaturedGifsProps) { const { dataGifsFeartured } = useGifs(); return ( -
+
diff --git a/libs/components/src/lib/components/GifsStickersEmojis/gifs/GifCategory.tsx b/libs/components/src/lib/components/GifsStickersEmojis/gifs/GifCategory.tsx index d3d5aa8b7b..16d76c7235 100644 --- a/libs/components/src/lib/components/GifsStickersEmojis/gifs/GifCategory.tsx +++ b/libs/components/src/lib/components/GifsStickersEmojis/gifs/GifCategory.tsx @@ -1,5 +1,6 @@ import { useGifs, useGifsStickersEmoji } from '@mezon/core'; -import { IGifCategory } from '@mezon/utils'; +import type { IGifCategory } from '@mezon/utils'; +import { generateE2eId } from '@mezon/utils'; type GifCategoryProps = { gifCategory: IGifCategory; @@ -17,7 +18,12 @@ function GifCategory({ gifCategory }: GifCategoryProps) { }; return ( -
+
{gifCategory.searchterm} diff --git a/libs/components/src/lib/components/GifsStickersEmojis/gifs/TenorGifCategories.tsx b/libs/components/src/lib/components/GifsStickersEmojis/gifs/TenorGifCategories.tsx index 80ab9a96ce..091a8d6747 100644 --- a/libs/components/src/lib/components/GifsStickersEmojis/gifs/TenorGifCategories.tsx +++ b/libs/components/src/lib/components/GifsStickersEmojis/gifs/TenorGifCategories.tsx @@ -1,8 +1,9 @@ import { useChatSending, useCurrentInbox, useEscapeKeyClose, useGifs, useGifsStickersEmoji } from '@mezon/core'; import { referencesActions, selectDataReferences, useAppSelector } from '@mezon/store'; import { Loading } from '@mezon/ui'; -import { EMimeTypes, IGifCategory, SubPanelName, blankReferenceObj } from '@mezon/utils'; -import { ApiChannelDescription, ApiMessageRef } from 'mezon-js/api.gen'; +import type { IGifCategory } from '@mezon/utils'; +import { EMimeTypes, SubPanelName, blankReferenceObj, generateE2eId } from '@mezon/utils'; +import type { ApiChannelDescription, ApiMessageRef } from 'mezon-js/api.gen'; import { useEffect, useRef, useState } from 'react'; import { useDispatch } from 'react-redux'; import FeaturedGifs from './FeaturedGifs'; @@ -109,6 +110,7 @@ function TenorGifCategories({ channelOrDirect, mode, onClose, isTopic = false }: className={`order-${index} overflow-hidden cursor-pointer`} onClick={() => handleClickGif(gif.media_formats.gif.url)} role="button" + data-e2e={generateE2eId('mention.popover.gifs.item')} > {gif.media_formats.gif.url}
diff --git a/libs/components/src/lib/components/MessageBox/GifsStickerEmojiButtons.tsx b/libs/components/src/lib/components/MessageBox/GifsStickerEmojiButtons.tsx index 4bb92c39e9..a02ae9e7c3 100644 --- a/libs/components/src/lib/components/MessageBox/GifsStickerEmojiButtons.tsx +++ b/libs/components/src/lib/components/MessageBox/GifsStickerEmojiButtons.tsx @@ -1,8 +1,8 @@ import { useGifs, useGifsStickersEmoji } from '@mezon/core'; import { reactionActions, referencesActions, useAppDispatch } from '@mezon/store'; import { Icons } from '@mezon/ui'; -import type { E2eKeyType } from '@mezon/utils'; -import { ILongPressType, SubPanelName, generateE2eId } from '@mezon/utils'; +import type { ILongPressType } from '@mezon/utils'; +import { SubPanelName, generateE2eId } from '@mezon/utils'; import { memo, useCallback } from 'react'; export type GifStickerEmojiButtonsProps = { @@ -88,6 +88,7 @@ const GifStickerEmojiButtons = memo(
@@ -98,7 +99,7 @@ const GifStickerEmojiButtons = memo( onClick={handleOpenGifs} className={`block text-theme-primary-hover } max-sm:hidden w-5 h-5 ${cursorPointer ? 'cursor-pointer' : 'cursor-not-allowed'}`} - + data-e2e={generateE2eId('mention.button.gif')} > Date: Wed, 10 Dec 2025 12:04:10 +0700 Subject: [PATCH 07/14] add e2e to gif gallery modal --- .../components/ChannelTopbar/GalleryModal.tsx | 16 +++++++++++++++- .../src/lib/components/ChannelTopbar/index.tsx | 2 +- .../MarkdownFormatText/ModalUnknowChannel.tsx | 4 +--- libs/utils/src/lib/e2e-testing/constants.ts | 13 ++++++++++++- 4 files changed, 29 insertions(+), 6 deletions(-) diff --git a/libs/components/src/lib/components/ChannelTopbar/GalleryModal.tsx b/libs/components/src/lib/components/ChannelTopbar/GalleryModal.tsx index 097afbb498..3b6f189571 100644 --- a/libs/components/src/lib/components/ChannelTopbar/GalleryModal.tsx +++ b/libs/components/src/lib/components/ChannelTopbar/GalleryModal.tsx @@ -29,7 +29,15 @@ import { } from '@mezon/store'; import { Icons } from '@mezon/ui'; import type { IImageWindowProps } from '@mezon/utils'; -import { EMimeTypes, ETypeLinkMedia, LoadMoreDirection, convertDateStringI18n, createImgproxyUrl, getAttachmentDataForWindow } from '@mezon/utils'; +import { + EMimeTypes, + ETypeLinkMedia, + LoadMoreDirection, + convertDateStringI18n, + createImgproxyUrl, + generateE2eId, + getAttachmentDataForWindow +} from '@mezon/utils'; import { endOfDay, format, getUnixTime, isSameDay, startOfDay } from 'date-fns'; import isElectron from 'is-electron'; import type { RefObject } from 'react'; @@ -635,6 +643,7 @@ export function GalleryModal({ onClose, rootRef }: GalleryModalProps) { ref={modalRef} tabIndex={-1} className="absolute top-8 right-0 rounded-md dark:shadow-shadowBorder shadow-shadowInbox z-[9999] origin-top-right" + data-e2e={generateE2eId('clan_page.modal.gallery')} >
@@ -656,6 +665,7 @@ export function GalleryModal({ onClose, rootRef }: GalleryModalProps) { ? 'bg-theme-primary text-white' : 'bg-theme-surface text-theme-primary hover:bg-theme-surface-hover' }`} + data-e2e={generateE2eId('clan_page.modal.gallery.tab.all')} > {t('gallery.filters.all')} @@ -666,6 +676,7 @@ export function GalleryModal({ onClose, rootRef }: GalleryModalProps) { ? 'bg-theme-primary text-white' : 'bg-theme-surface text-theme-primary hover:bg-theme-surface-hover' }`} + data-e2e={generateE2eId('clan_page.modal.gallery.tab.image')} > {t('gallery.filters.images')} @@ -676,6 +687,7 @@ export function GalleryModal({ onClose, rootRef }: GalleryModalProps) { ? 'bg-theme-primary text-white' : 'bg-theme-surface text-theme-primary hover:bg-theme-surface-hover' }`} + data-e2e={generateE2eId('clan_page.modal.gallery.tab.video')} > {t('gallery.filters.videos')} @@ -930,6 +942,7 @@ const ImageWithLoading = React.memo( preload="metadata" muted playsInline + data-e2e={generateE2eId('clan_page.modal.gallery.video')} /> ) : (
@@ -949,6 +962,7 @@ const ImageWithLoading = React.memo( className={`w-full h-full object-cover ${isLoading ? 'opacity-0' : 'opacity-100'} ${className}`} onLoad={handleLoad} onError={handleError} + data-e2e={generateE2eId('clan_page.modal.gallery.image')} /> )}
diff --git a/libs/components/src/lib/components/ChannelTopbar/index.tsx b/libs/components/src/lib/components/ChannelTopbar/index.tsx index 7de0a20123..689350b466 100644 --- a/libs/components/src/lib/components/ChannelTopbar/index.tsx +++ b/libs/components/src/lib/components/ChannelTopbar/index.tsx @@ -1093,7 +1093,7 @@ function GalleryButton() { }, []); return ( -
+
) : null} - @@ -186,6 +191,7 @@ const RoleListItem = ({ role, onAddRole }: { role: RolesClanEntity; onAddRole: (
onAddRole(role.id)} + data-e2e={generateE2eId('short_profile.role.popover.item')} >
{role?.role_icon && } diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index ccbb184713..65be408067 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -56,6 +56,14 @@ export const DATA_E2E_IDENTIFIER = { save: '' } } + }, + role: { + button: { + add: '' + }, + popover: { + item: '' + } } }, mention: { From db4af22234ed2a6ccd040d2ead15e13b4a344c3e Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Fri, 12 Dec 2025 11:41:41 +0700 Subject: [PATCH 09/14] add e2e to verify topic on inbox popover --- .../NotificationTooltipContent.tsx | 3 ++- .../NotificationList/TopicNotificationItem.tsx | 15 +++++++++++---- libs/utils/src/lib/e2e-testing/constants.ts | 12 +++++++++++- 3 files changed, 24 insertions(+), 6 deletions(-) diff --git a/libs/components/src/lib/components/NotificationList/NotificationTooltipContent.tsx b/libs/components/src/lib/components/NotificationList/NotificationTooltipContent.tsx index a2fb49b1fe..78f695785a 100644 --- a/libs/components/src/lib/components/NotificationList/NotificationTooltipContent.tsx +++ b/libs/components/src/lib/components/NotificationList/NotificationTooltipContent.tsx @@ -11,7 +11,7 @@ import { } from '@mezon/store'; import { Icons } from '@mezon/ui'; import type { INotification } from '@mezon/utils'; -import { NotificationCategory, sortNotificationsByDate } from '@mezon/utils'; +import { NotificationCategory, generateE2eId, sortNotificationsByDate } from '@mezon/utils'; import type { ApiSdTopic } from 'mezon-js/dist/api.gen'; import { useEffect, useMemo, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; @@ -147,6 +147,7 @@ export function NotificationTooltipContent({ onCloseTooltip }: NotificationToolt className={`px-2 py-[4px] rounded-[4px] text-base font-medium ${currentTabNotify === tab.value ? 'btn-primary btn-primary-hover' : ''}`} tabIndex={index} onClick={() => handleChangeTab(tab.value)} + data-e2e={generateE2eId('chat.channel_message.inbox.action_tabs')} > {tab.title} diff --git a/libs/components/src/lib/components/NotificationList/TopicNotificationItem.tsx b/libs/components/src/lib/components/NotificationList/TopicNotificationItem.tsx index c80b8cc7c6..8c87139cb4 100644 --- a/libs/components/src/lib/components/NotificationList/TopicNotificationItem.tsx +++ b/libs/components/src/lib/components/NotificationList/TopicNotificationItem.tsx @@ -17,7 +17,7 @@ import { useAppSelector } from '@mezon/store'; import type { IMessageWithUser } from '@mezon/utils'; -import { createImgproxyUrl } from '@mezon/utils'; +import { createImgproxyUrl, generateE2eId } from '@mezon/utils'; import { safeJSONParse } from 'mezon-js'; import type { ApiChannelMessageHeader, ApiSdTopic } from 'mezon-js/dist/api.gen'; import { useEffect, useMemo, useState } from 'react'; @@ -121,10 +121,11 @@ function TopicNotificationItem({ topic, onCloseTooltip }: TopicProps) { }; return ( -
+
@@ -179,12 +180,18 @@ function AllTabContent({ messageReplied, subject, lastMessageTopic, topic }: ITo
{subject}
-
+
Replied to: {messageRl ? messageRl?.t : 'Unreachable message'}
-
+
{lastSentUser ? lastSentUser?.user?.username : 'Sender'}:{' '} {lastMsgTopic ? lastMsgTopic?.t : 'Unreachable message'}
diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index 65be408067..ca6fa664b9 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -692,7 +692,17 @@ export const DATA_E2E_IDENTIFIER = { } }, inbox: { - mentions: '' + mentions: '', + topics: { + init_message: '', + last_reply_message: '', + button: { + jump: '' + } + }, + message: '', + for_you: '', + action_tabs: '' } }, mention: { From 0f261f7c982cf5f3b56d63520ab13d1a533f3378 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Tue, 16 Dec 2025 11:21:03 +0700 Subject: [PATCH 10/14] add e2e for role color for user in clan --- .../SettingDisplayRole/RoleColor/index.tsx | 3 ++- .../src/lib/components/MemberProfile/MemberProfile.tsx | 2 +- .../src/lib/components/ModalUserProfile/RoleUserProfile.tsx | 1 + libs/utils/src/lib/e2e-testing/constants.ts | 6 ++++-- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingDisplayRole/RoleColor/index.tsx b/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingDisplayRole/RoleColor/index.tsx index d14834591b..806675b073 100644 --- a/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingDisplayRole/RoleColor/index.tsx +++ b/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingDisplayRole/RoleColor/index.tsx @@ -1,5 +1,5 @@ import { getNewColorRole, setColorRoleNew } from '@mezon/store'; -import { DEFAULT_ROLE_COLOR } from '@mezon/utils'; +import { DEFAULT_ROLE_COLOR, generateE2eId } from '@mezon/utils'; import { useEffect, useRef, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; @@ -87,6 +87,7 @@ const RoleColor = () => { setSelectedColor(color); dispatch(setColorRoleNew(color)); }} + data-e2e={generateE2eId('clan_page.settings.role.container.role_color')} > {selectedColor === color && !isCustoms && ( diff --git a/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx b/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx index 9fd43c6d39..a7b1d105bb 100644 --- a/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx +++ b/libs/components/src/lib/components/MemberProfile/MemberProfile.tsx @@ -90,7 +90,7 @@ export function ClanUserName({ name, userId, isOwner }: { name: string; userId: {name} diff --git a/libs/components/src/lib/components/ModalUserProfile/RoleUserProfile.tsx b/libs/components/src/lib/components/ModalUserProfile/RoleUserProfile.tsx index ea46cbff59..2828fba262 100644 --- a/libs/components/src/lib/components/ModalUserProfile/RoleUserProfile.tsx +++ b/libs/components/src/lib/components/ModalUserProfile/RoleUserProfile.tsx @@ -268,6 +268,7 @@ const RoleClanItem = ({ style={buttonStyle} onMouseEnter={() => setIsHovered(true)} onMouseLeave={() => setIsHovered(false)} + data-e2e={generateE2eId('clan_page.channel_list.members.role.role_color')} > diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index adc9c84727..f12486a093 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -398,7 +398,8 @@ export const DATA_E2E_IDENTIFIER = { permissions: '', manage_members: '' }, - name_input: '' + name_input: '', + role_color: '' } } }, @@ -430,7 +431,8 @@ export const DATA_E2E_IDENTIFIER = { }, members: { role: { - role_name: '' + role_name: '', + role_color: '' } } }, From 7b4dc3c878393098b6a67931151fdb3abb5f9f55 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Mon, 22 Dec 2025 12:10:18 +0700 Subject: [PATCH 11/14] e2e for mark as read on clan --- .../src/lib/components/ChannelLink/ChannelLink.tsx | 1 + .../src/lib/components/ChannelTopbar/index.tsx | 1 + .../src/lib/components/ModalListClans/index.tsx | 1 + libs/utils/src/lib/e2e-testing/constants.ts | 9 ++++++--- 4 files changed, 9 insertions(+), 3 deletions(-) diff --git a/libs/components/src/lib/components/ChannelLink/ChannelLink.tsx b/libs/components/src/lib/components/ChannelLink/ChannelLink.tsx index 660f38444e..05d9f1acd5 100644 --- a/libs/components/src/lib/components/ChannelLink/ChannelLink.tsx +++ b/libs/components/src/lib/components/ChannelLink/ChannelLink.tsx @@ -274,6 +274,7 @@ const ChannelLinkComponent = ({ />
{countNumberNotification}
diff --git a/libs/components/src/lib/components/ChannelTopbar/index.tsx b/libs/components/src/lib/components/ChannelTopbar/index.tsx index 689350b466..7323d65d02 100644 --- a/libs/components/src/lib/components/ChannelTopbar/index.tsx +++ b/libs/components/src/lib/components/ChannelTopbar/index.tsx @@ -976,6 +976,7 @@ export function RedDot() { className="absolute border-theme-primary w-[8px] h-[8px] rounded-full bg-colorDanger outline outline-1 outline-transparent font-bold text-[11px] flex items-center justify-center -bottom-[0.05rem] -right-[0.075rem]" + data-e2e={generateE2eId('chat.channel_message.header.badge')} >
); } diff --git a/libs/components/src/lib/components/ModalListClans/index.tsx b/libs/components/src/lib/components/ModalListClans/index.tsx index 6f1c9901a4..79c627ca33 100644 --- a/libs/components/src/lib/components/ModalListClans/index.tsx +++ b/libs/components/src/lib/components/ModalListClans/index.tsx @@ -112,6 +112,7 @@ const SidebarClanItem = ({ option, active, onMouseDown, className = '', onClanCl className={`flex items-center justify-center text-[12px] font-bold rounded-full bg-colorDanger absolute bottom-[-1px] right-[-2px] outline outline-[1px] outline-white ${ badgeCountClan >= 10 ? 'w-[22px] h-[16px]' : 'w-[16px] h-[16px]' }`} + data-e2e={generateE2eId('clan_page.badge')} > {badgeCountClan >= 100 ? '99+' : badgeCountClan}
diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index f12486a093..365a5ff6d5 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -418,7 +418,8 @@ export const DATA_E2E_IDENTIFIER = { user_list_collapsed: { item: '', item_count: '' - } + }, + badge: '' }, thread_item: { name: '' @@ -461,7 +462,8 @@ export const DATA_E2E_IDENTIFIER = { messages_count: '', channel_name: '' } - } + }, + badge: '' }, channel_setting_page: { side_bar: { @@ -664,7 +666,8 @@ export const DATA_E2E_IDENTIFIER = { }, chat: '', gallery: '' - } + }, + badge: '' }, actions: { add_reaction: '', From 50a5e08213d4a45dc4596ed144de5c15dda32433 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Tue, 23 Dec 2025 12:01:15 +0700 Subject: [PATCH 12/14] e2e mark as read direct message --- apps/chat/src/app/pages/main/directUnreads.tsx | 4 ++-- libs/utils/src/lib/e2e-testing/constants.ts | 3 +++ 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/chat/src/app/pages/main/directUnreads.tsx b/apps/chat/src/app/pages/main/directUnreads.tsx index d3122f56eb..7ad339196e 100644 --- a/apps/chat/src/app/pages/main/directUnreads.tsx +++ b/apps/chat/src/app/pages/main/directUnreads.tsx @@ -2,7 +2,7 @@ import { AvatarImage } from '@mezon/components'; import { useCustomNavigate } from '@mezon/core'; import type { DMMetaEntity } from '@mezon/store'; import { directActions, selectDirectById, useAppDispatch, useAppSelector } from '@mezon/store'; -import { createImgproxyUrl } from '@mezon/utils'; +import { createImgproxyUrl, generateE2eId } from '@mezon/utils'; import { ChannelType } from 'mezon-js'; import { memo, useCallback, useMemo } from 'react'; import { NavLink } from 'react-router-dom'; @@ -77,7 +77,7 @@ const DirectUnreadComponent = ({ directMessage, shouldAnimateOut = false, onMemb return ( -
+
{badgeContent &&
{badgeContent}
}
diff --git a/libs/utils/src/lib/e2e-testing/constants.ts b/libs/utils/src/lib/e2e-testing/constants.ts index 365a5ff6d5..0c11fc08b8 100644 --- a/libs/utils/src/lib/e2e-testing/constants.ts +++ b/libs/utils/src/lib/e2e-testing/constants.ts @@ -622,6 +622,9 @@ export const DATA_E2E_IDENTIFIER = { leave_group: { button: '' } + }, + side_bar: { + item: '' } }, channel_message: { From 8f93f5467db57b6311ceb8e7506d7db42948ce03 Mon Sep 17 00:00:00 2001 From: "trinh.truongthiphuong" Date: Thu, 25 Dec 2025 11:48:10 +0700 Subject: [PATCH 13/14] e2e for clan template --- .../SettingPermissions/index.tsx | 9 +++++++-- .../ClanSettings/SettingSidebar/index.tsx | 5 ++++- .../src/lib/components/CreateClanModal/index.tsx | 2 ++ libs/utils/src/lib/e2e-testing/constants.ts | 14 +++++++++++++- 4 files changed, 26 insertions(+), 4 deletions(-) diff --git a/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingPermissions/index.tsx b/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingPermissions/index.tsx index 0a7577f94a..2640b25eb1 100644 --- a/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingPermissions/index.tsx +++ b/libs/components/src/lib/components/ClanSettings/SettingRoleManagement/SettingPermissions/index.tsx @@ -13,7 +13,7 @@ import { toggleIsShowTrue } from '@mezon/store'; import { InputField } from '@mezon/ui'; -import { EOverriddenPermission, SlugPermission } from '@mezon/utils'; +import { EOverriddenPermission, SlugPermission, generateE2eId } from '@mezon/utils'; import { useEffect, useMemo, useState } from 'react'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; @@ -105,9 +105,13 @@ const SettingPermissions = ({ RolesClan, hasPermissionEdit }: { RolesClan: Roles
  • -
    +
    {permission.slug ? getPermissionTitle(permission.slug) || permission.title : permission.title}
    @@ -140,6 +144,7 @@ const SettingPermissions = ({ RolesClan, hasPermissionEdit }: { RolesClan: Roles !hasPermissionEdit || (activeRole?.slug?.startsWith('everyone-') && permission.slug === EOverriddenPermission.sendMessage) } + data-e2e={generateE2eId('clan_page.settings.role.container.role_option.permissions.item.switch')} />
  • diff --git a/libs/components/src/lib/components/ClanSettings/SettingSidebar/index.tsx b/libs/components/src/lib/components/ClanSettings/SettingSidebar/index.tsx index 0b44cffa00..0df31a8960 100644 --- a/libs/components/src/lib/components/ClanSettings/SettingSidebar/index.tsx +++ b/libs/components/src/lib/components/ClanSettings/SettingSidebar/index.tsx @@ -100,7 +100,10 @@ const SettingSidebar = ({ onClickItem, handleMenu, currentSetting, setIsShowDele className={`${sidebarItem.listItem.length > 0 ? 'mt-[5px] border-b-theme-primary' : ''}`} > {sidebarItem.title && sidebarItem.listItem.length > 0 && ( -

    +

    {getTranslatedSectionTitle(sidebarItem.title)}

    )} diff --git a/libs/components/src/lib/components/CreateClanModal/index.tsx b/libs/components/src/lib/components/CreateClanModal/index.tsx index 237815d05a..5ebbf10eec 100644 --- a/libs/components/src/lib/components/CreateClanModal/index.tsx +++ b/libs/components/src/lib/components/CreateClanModal/index.tsx @@ -374,6 +374,7 @@ const ModalCreateClans = (props: ModalCreateClansProps) => {