dify/web/__tests__/app-sidebar/sidebar-shell-flow.test.tsx
Coding On Star 9331024d91
refactor(web): portal to follow elem migration (#35892)
Co-authored-by: CodingOnStar <hanxujiang@dify.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: yyh <yuanyouhuilyz@gmail.com>
2026-05-07 13:02:11 +00:00

171 lines
4.7 KiB
TypeScript

import type { SVGProps } from 'react'
import { fireEvent, render, screen } from '@testing-library/react'
import * as React from 'react'
import { beforeEach, describe, expect, it, vi } from 'vitest'
import AppDetailNav from '@/app/components/app-sidebar'
const mockSetAppSidebarExpand = vi.fn()
let mockAppSidebarExpand = 'expand'
let mockPathname = '/app/app-1/logs'
let mockSelectedSegment = 'logs'
let mockIsHovering = true
let keyPressHandler: ((event: { preventDefault: () => void }) => void) | null = null
vi.mock('react-i18next', () => ({
useTranslation: () => ({
t: (key: string) => key,
}),
}))
vi.mock('@/app/components/app/store', () => ({
useStore: (selector: (state: Record<string, unknown>) => unknown) => selector({
appDetail: {
id: 'app-1',
name: 'Demo App',
mode: 'chat',
icon: '🤖',
icon_type: 'emoji',
icon_background: '#FFEAD5',
icon_url: null,
},
appSidebarExpand: mockAppSidebarExpand,
setAppSidebarExpand: mockSetAppSidebarExpand,
}),
}))
vi.mock('zustand/react/shallow', () => ({
useShallow: (selector: unknown) => selector,
}))
vi.mock('@/next/navigation', () => ({
usePathname: () => mockPathname,
useSelectedLayoutSegment: () => mockSelectedSegment,
}))
vi.mock('@/next/link', () => ({
default: ({
href,
children,
className,
title,
}: {
href: string
children?: React.ReactNode
className?: string
title?: string
}) => (
<a href={href} className={className} title={title}>
{children}
</a>
),
}))
vi.mock('ahooks', () => ({
useHover: () => mockIsHovering,
useKeyPress: (_key: string, handler: (event: { preventDefault: () => void }) => void) => {
keyPressHandler = handler
},
}))
vi.mock('@/hooks/use-breakpoints', () => ({
default: () => 'desktop',
MediaType: {
mobile: 'mobile',
desktop: 'desktop',
},
}))
vi.mock('@/context/event-emitter', () => ({
useEventEmitterContextContext: () => ({
eventEmitter: {
useSubscription: vi.fn(),
},
}),
}))
vi.mock('@/context/app-context', () => ({
useAppContext: () => ({
isCurrentWorkspaceEditor: true,
}),
}))
vi.mock('@/app/components/workflow/utils', () => ({
getKeyboardKeyCodeBySystem: () => 'ctrl',
getKeyboardKeyNameBySystem: (key: string) => key,
}))
vi.mock('@langgenius/dify-ui/dropdown-menu', () => import('@/__mocks__/base-ui-dropdown-menu'))
vi.mock('@langgenius/dify-ui/tooltip', () => import('@/__mocks__/base-ui-tooltip'))
vi.mock('@/app/components/app-sidebar/app-info', () => ({
default: ({
expand,
onlyShowDetail,
openState,
}: {
expand: boolean
onlyShowDetail?: boolean
openState?: boolean
}) => (
<div
data-testid={onlyShowDetail ? 'app-info-detail' : 'app-info'}
data-expand={expand}
data-open={openState}
/>
),
}))
const MockIcon = (props: SVGProps<SVGSVGElement>) => <svg {...props} />
const navigation = [
{ name: 'Overview', href: '/app/app-1/overview', icon: MockIcon, selectedIcon: MockIcon },
{ name: 'Logs', href: '/app/app-1/logs', icon: MockIcon, selectedIcon: MockIcon },
]
describe('App Sidebar Shell Flow', () => {
beforeEach(() => {
vi.clearAllMocks()
localStorage.clear()
mockAppSidebarExpand = 'expand'
mockPathname = '/app/app-1/logs'
mockSelectedSegment = 'logs'
mockIsHovering = true
keyPressHandler = null
})
it('renders the expanded sidebar, marks the active nav item, and toggles collapse by click and shortcut', () => {
render(<AppDetailNav navigation={navigation} />)
expect(screen.getByTestId('app-info')).toHaveAttribute('data-expand', 'true')
const logsLink = screen.getByRole('link', { name: /Logs/i })
expect(logsLink.className).toContain('bg-components-menu-item-bg-active')
fireEvent.click(screen.getByRole('button'))
expect(mockSetAppSidebarExpand).toHaveBeenCalledWith('collapse')
const preventDefault = vi.fn()
keyPressHandler?.({ preventDefault })
expect(preventDefault).toHaveBeenCalled()
expect(mockSetAppSidebarExpand).toHaveBeenCalledWith('collapse')
})
it('switches to the workflow fullscreen dropdown shell and opens its navigation menu', async () => {
mockPathname = '/app/app-1/workflow'
mockSelectedSegment = 'workflow'
localStorage.setItem('workflow-canvas-maximize', 'true')
render(<AppDetailNav navigation={navigation} />)
expect(screen.queryByTestId('app-info')).not.toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: 'operation.more' }))
expect(await screen.findByText('Demo App')).toBeInTheDocument()
expect(screen.getByRole('link', { name: /Overview/i })).toBeInTheDocument()
expect(screen.getByRole('link', { name: /Logs/i })).toBeInTheDocument()
})
})