dify/web/__tests__/header/account-dropdown-flow.test.tsx
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

199 lines
6.4 KiB
TypeScript

import { fireEvent, screen, waitFor } from '@testing-library/react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import { renderWithSystemFeatures } from '@/__tests__/utils/mock-system-features'
import { Plan } from '@/app/components/billing/type'
import AccountDropdown from '@/app/components/header/account-dropdown'
import { ACCOUNT_SETTING_TAB } from '@/app/components/header/account-setting/constants'
vi.mock('@/context/i18n', () => ({
useDocLink: () => (path: string) => `https://docs.example.com${path}`,
}))
const { mockAppContextState, mockPush, mockLogout, mockResetUser, mockSetShowAccountSettingModal } =
vi.hoisted(() => ({
mockAppContextState: {
userProfile: {
id: 'user-1',
name: 'Ada Lovelace',
email: 'ada@example.com',
avatar: '',
avatar_url: '',
is_password_set: true,
},
langGeniusVersionInfo: {
current_env: 'CLOUD',
current_version: '1.0.0',
latest_version: '1.1.0',
release_date: '',
release_notes: 'https://example.com/releases/1.1.0',
version: '1.0.0',
can_auto_update: false,
},
isCurrentWorkspaceOwner: false,
},
mockPush: vi.fn(),
mockLogout: vi.fn(),
mockResetUser: vi.fn(),
mockSetShowAccountSettingModal: vi.fn(),
}))
vi.mock('react-i18next', async () => {
const { withSelectorKey } = await import('@/test/i18n-mock')
return {
useTranslation: () => ({
t: withSelectorKey((key: string, options?: { ns?: string; version?: string }) => {
if (options?.version) return `${options.ns}.${key}:${options.version}`
return options?.ns ? `${options.ns}.${key}` : key
}),
}),
}
})
vi.mock('@/context/account-state', async (importOriginal) => {
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
return createAppContextStateAtomMock(importOriginal, () => mockAppContextState)
})
vi.mock('@/context/workspace-state', async (importOriginal) => {
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
return createAppContextStateAtomMock(importOriginal, () => mockAppContextState)
})
vi.mock('@/context/permission-state', async (importOriginal) => {
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
return createAppContextStateAtomMock(importOriginal, () => mockAppContextState)
})
vi.mock('@/context/version-state', async (importOriginal) => {
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
return createAppContextStateAtomMock(importOriginal, () => mockAppContextState)
})
vi.mock('@/context/system-features-state', async (importOriginal) => {
const { createAppContextStateAtomMock } = await import('@/__tests__/utils/mock-app-context-state')
return createAppContextStateAtomMock(importOriginal, () => mockAppContextState)
})
vi.mock('jotai', async (importOriginal) => {
const { createAppContextStateJotaiMock } =
await import('@/__tests__/utils/mock-app-context-state')
return createAppContextStateJotaiMock(importOriginal)
})
vi.mock('@/context/provider-context', () => ({
useProviderContext: () => ({
isEducationAccount: false,
plan: {
type: Plan.professional,
},
}),
}))
vi.mock('@/context/modal-context', () => ({
useModalContext: () => ({
setShowAccountSettingModal: mockSetShowAccountSettingModal,
}),
}))
vi.mock('@/service/use-common', async (importOriginal) => ({
...(await importOriginal<typeof import('@/service/use-common')>()),
useLogout: () => ({
mutateAsync: mockLogout,
}),
}))
vi.mock('@/app/components/base/amplitude/utils', () => ({
resetUser: mockResetUser,
}))
vi.mock('@/next/navigation', () => ({
useRouter: () => ({
push: mockPush,
}),
}))
vi.mock('@/next/link', () => ({
default: ({
href,
children,
...props
}: {
href: string
children?: React.ReactNode
} & Record<string, unknown>) => (
<a href={href} {...props}>
{children}
</a>
),
}))
const renderAccountDropdown = () => {
return renderWithSystemFeatures(<AccountDropdown />, {
systemFeatures: {
branding: {
enabled: false,
workspace_logo: '',
},
},
})
}
describe('Header Account Dropdown Flow', () => {
beforeEach(() => {
vi.clearAllMocks()
vi.spyOn(globalThis, 'fetch').mockResolvedValue(
new Response(
JSON.stringify({
repo: { stars: 123456 },
}),
{
status: 200,
headers: { 'Content-Type': 'application/json' },
},
),
)
localStorage.clear()
})
it('opens account actions, fetches github stars, and opens the settings and about flows', async () => {
renderAccountDropdown()
fireEvent.click(screen.getByRole('button', { name: 'common.account.account' }))
expect(screen.getByText('Ada Lovelace')).toBeInTheDocument()
expect(screen.getByText('ada@example.com')).toBeInTheDocument()
expect(await screen.findByText('123,456')).toBeInTheDocument()
fireEvent.click(screen.getByText('common.userProfile.settings'))
expect(mockSetShowAccountSettingModal).toHaveBeenCalledWith({
payload: ACCOUNT_SETTING_TAB.MEMBERS,
})
fireEvent.click(screen.getByRole('button', { name: 'common.account.account' }))
fireEvent.click(screen.getByText('common.userProfile.about'))
await waitFor(() => {
expect(screen.getByText(/Version/)).toBeInTheDocument()
expect(screen.getByText(/1\.0\.0/)).toBeInTheDocument()
})
})
it('logs out, resets cached user markers, and redirects to signin', async () => {
localStorage.setItem('education-reverify-prev-expire-at', '1')
localStorage.setItem('education-reverify-has-noticed', '1')
localStorage.setItem('education-expired-has-noticed', '1')
renderAccountDropdown()
fireEvent.click(screen.getByRole('button', { name: 'common.account.account' }))
fireEvent.click(screen.getByText('common.userProfile.logout'))
await waitFor(() => {
expect(mockLogout).toHaveBeenCalledTimes(1)
expect(mockResetUser).toHaveBeenCalledTimes(1)
expect(mockPush).toHaveBeenCalledWith('/signin')
})
expect(localStorage.getItem('education-reverify-prev-expire-at')).toBeNull()
expect(localStorage.getItem('education-reverify-has-noticed')).toBeNull()
expect(localStorage.getItem('education-expired-has-noticed')).toBeNull()
})
})