Merge branch 'main' into tp

This commit is contained in:
JzoNg
2026-07-17 13:06:50 +08:00
4 changed files with 74 additions and 22 deletions
@@ -3,14 +3,17 @@ import type { AppContextStateMockState } from '@/__tests__/utils/mock-app-contex
import type { ModalContextState } from '@/context/modal-context'
import type { ProviderContextState } from '@/context/provider-context'
import { fireEvent, screen, waitFor } from '@testing-library/react'
import userEvent from '@testing-library/user-event'
import { renderToString } from 'react-dom/server'
import { renderWithSystemFeatures } from '@/__tests__/utils/mock-system-features'
import { Plan } from '@/app/components/billing/type'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import AccountSection from '@/app/components/main-nav/components/account-section'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { useRouter } from '@/next/navigation'
import { useLogout } from '@/service/use-common'
import { createAccountProfileQueryClient } from '@/test/account-profile-query'
import AppSelector from '../index'
type DeepPartial<T> =
@@ -147,15 +150,17 @@ vi.mock('@/config', async (importOriginal) => {
})
vi.mock('@/env', () => mockEnv)
const baseUserProfile = {
id: '1',
name: 'Test User',
email: 'test@example.com',
avatar: '',
avatar_url: 'avatar.png',
is_password_set: false,
}
const baseAppContextValue: AppContextStateMockState = {
userProfile: {
id: '1',
name: 'Test User',
email: 'test@example.com',
avatar: '',
avatar_url: 'avatar.png',
is_password_set: false,
},
userProfile: baseUserProfile,
mutateUserProfile: vi.fn(),
currentWorkspace: {
id: '1',
@@ -201,7 +206,12 @@ describe('AccountDropdown', () => {
ui: React.ReactElement,
options: { systemFeatures?: DeepPartial<GetSystemFeaturesResponse> } = {},
) => {
const queryClient = createAccountProfileQueryClient({
...baseUserProfile,
...(mockAppContextState.current?.userProfile ?? {}),
})
return renderWithSystemFeatures(ui, {
queryClient,
systemFeatures: options.systemFeatures ?? { branding: { enabled: false } },
})
}
@@ -238,6 +248,29 @@ describe('AccountDropdown', () => {
})
describe('Rendering', () => {
it('should show the signed-in account in the main navigation menu', async () => {
const user = userEvent.setup()
const queryClient = createAccountProfileQueryClient({
id: 'current-user',
name: 'Current User',
email: 'current@example.com',
avatar_url: 'current-avatar.png',
})
renderWithSystemFeatures(<AccountSection />, {
queryClient,
systemFeatures: { branding: { enabled: false } },
})
expect(screen.getByText('Current User')).toBeInTheDocument()
expect(screen.queryByText('Test User')).not.toBeInTheDocument()
await user.click(screen.getByRole('button', { name: 'common.account.account' }))
expect(await screen.findByText('current@example.com')).toBeInTheDocument()
expect(screen.queryByText('test@example.com')).not.toBeInTheDocument()
})
it('should render user profile correctly', () => {
// Act
renderWithRouter(<AppSelector />)
@@ -16,14 +16,14 @@ import {
DropdownMenuSubContent,
DropdownMenuSubTrigger,
} from '@langgenius/dify-ui/dropdown-menu'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import { useTheme } from 'next-themes'
import { useTranslation } from 'react-i18next'
import PremiumBadge from '@/app/components/base/premium-badge'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { userProfileAtom } from '@/context/account-state'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import Link from '@/next/link'
import { ExternalLinkIndicator, MenuItemContent } from './menu-item-content'
@@ -102,7 +102,10 @@ type MainNavMenuContentProps = {
export function MainNavMenuContent({ onLogout }: MainNavMenuContentProps) {
const { t } = useTranslation()
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
const { isEducationAccount } = useProviderContext()
const { setShowAccountSettingModal } = useModalContext()
@@ -19,6 +19,7 @@ import { gotoAnythingDialogHandle } from '@/app/components/goto-anything/dialog-
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
import { useModalContext } from '@/context/modal-context'
import { useProviderContext } from '@/context/provider-context'
import { userProfileQueryOptions } from '@/features/account-profile/client'
import { usePathname, useRouter } from '@/next/navigation'
import { consoleQuery } from '@/service/client'
import { useGetInstalledApps, useUninstallApp, useUpdateAppPinStatus } from '@/service/use-explore'
@@ -211,15 +212,17 @@ const createInstalledApp = (overrides: Partial<InstalledApp> = {}): InstalledApp
},
})
const mainNavUserProfile = {
id: 'user-1',
name: 'Evan Z',
email: 'evan@example.com',
avatar: '',
avatar_url: '',
is_password_set: true,
}
const appContextValue: AppContextStateMockState = {
userProfile: {
id: 'user-1',
name: 'Evan Z',
email: 'evan@example.com',
avatar: '',
avatar_url: '',
is_password_set: true,
},
userProfile: mainNavUserProfile,
mutateUserProfile: vi.fn(),
currentWorkspace: {
id: 'workspace-1',
@@ -273,6 +276,16 @@ const renderMainNav = (
consoleQuery.workspaces.current.post.queryKey(),
currentAppContext.currentWorkspace as ICurrentWorkspace,
)
queryClient.setQueryData(userProfileQueryOptions().queryKey, {
profile: {
...mainNavUserProfile,
...(currentAppContext.userProfile ?? {}),
},
meta: {
currentVersion: null,
currentEnv: null,
},
})
queryClient.setQueryData(consoleQuery.workspaces.get.queryKey(), { workspaces: mockWorkspaces })
const resolvedSystemFeatures = {
...defaultMainNavSystemFeatures,
@@ -2,16 +2,19 @@
import { Avatar } from '@langgenius/dify-ui/avatar'
import { cn } from '@langgenius/dify-ui/cn'
import { useAtomValue } from 'jotai'
import { useSuspenseQuery } from '@tanstack/react-query'
import AccountDropdown from '@/app/components/header/account-dropdown'
import { userProfileAtom } from '@/context/account-state'
import { userProfileQueryOptions } from '@/features/account-profile/client'
type AccountSectionProps = {
compact?: boolean
}
const AccountSection = ({ compact = false }: AccountSectionProps) => {
const userProfile = useAtomValue(userProfileAtom)
const { data: userProfile } = useSuspenseQuery({
...userProfileQueryOptions(),
select: (data) => data.profile,
})
return (
<AccountDropdown