184 lines
7.6 KiB
TypeScript
184 lines
7.6 KiB
TypeScript
import { beforeEach, describe, expect, it } from 'vitest';
|
|
import { fireEvent, render, screen, waitFor } from '@testing-library/react';
|
|
import { MemoryRouter } from 'react-router-dom';
|
|
import { Sidebar } from '@/components/layout/Sidebar';
|
|
import { useChatStore } from '@/stores/chat';
|
|
import { useCronStore } from '@/stores/cron';
|
|
import { useGatewayStore } from '@/stores/gateway';
|
|
import { useSettingsStore } from '@/stores/settings';
|
|
import { useTaskCenterStore } from '@/stores/task-center';
|
|
import { useYinianStore } from '@/stores/yinian';
|
|
import i18n from '@/i18n';
|
|
|
|
const hotel = {
|
|
id: 'hotel-1',
|
|
name: '智念空间',
|
|
city: '杭州',
|
|
role: 'manager' as const,
|
|
permissions: [],
|
|
ota: [],
|
|
};
|
|
|
|
function renderSidebar(initialEntry = '/chat') {
|
|
return render(
|
|
<MemoryRouter initialEntries={[initialEntry]}>
|
|
<Sidebar />
|
|
</MemoryRouter>,
|
|
);
|
|
}
|
|
|
|
function getTestIdOrder(): string[] {
|
|
const sidebar = screen.getByTestId('sidebar');
|
|
return [...sidebar.querySelectorAll('[data-testid]')]
|
|
.map((element) => element.getAttribute('data-testid'))
|
|
.filter((value): value is string => Boolean(value));
|
|
}
|
|
|
|
describe('Sidebar layout', () => {
|
|
beforeEach(async () => {
|
|
window.localStorage.clear();
|
|
await i18n.changeLanguage('zh');
|
|
useSettingsStore.setState({
|
|
sidebarCollapsed: false,
|
|
devModeUnlocked: false,
|
|
});
|
|
useGatewayStore.setState({
|
|
status: { state: 'stopped', port: 18789, gatewayReady: false },
|
|
});
|
|
useYinianStore.setState({
|
|
status: 'authenticated',
|
|
session: {
|
|
authenticated: true,
|
|
user: { id: 'user-1', name: '王管理员' },
|
|
hotels: [hotel],
|
|
currentHotelId: hotel.id,
|
|
accessTokenExpiresAt: 100,
|
|
},
|
|
config: {
|
|
serverTime: 1,
|
|
user: { id: 'user-1', name: '王管理员' },
|
|
hotel,
|
|
hotels: [hotel],
|
|
entitlements: [],
|
|
notificationChannels: [],
|
|
featureFlags: {},
|
|
uiPolicy: { defaultPage: 'today', showAdvancedSettings: false },
|
|
},
|
|
error: null,
|
|
});
|
|
useChatStore.setState({
|
|
currentSessionKey: 'agent:main:main',
|
|
currentAgentId: 'main',
|
|
sessions: [{ key: 'agent:main:main', displayName: 'main' }],
|
|
messages: [],
|
|
sessionLabels: {},
|
|
sessionLastActivity: {},
|
|
sending: false,
|
|
activeRunId: null,
|
|
activeRunSessionKey: null,
|
|
streamingText: '',
|
|
streamingMessage: null,
|
|
streamingTools: [],
|
|
pendingFinal: false,
|
|
lastUserMessageAt: null,
|
|
pendingToolImages: [],
|
|
error: null,
|
|
loading: false,
|
|
});
|
|
useCronStore.setState({
|
|
jobs: Array.from({ length: 6 }, (_, index) => ({
|
|
id: `cron-${index + 1}`,
|
|
name: `快捷任务 ${index + 1}`,
|
|
message: `执行快捷任务 ${index + 1}`,
|
|
schedule: '0 9 * * *',
|
|
enabled: true,
|
|
createdAt: '2026-05-13T00:00:00.000Z',
|
|
updatedAt: '2026-05-13T00:00:00.000Z',
|
|
agentId: 'main',
|
|
})),
|
|
loading: false,
|
|
error: null,
|
|
});
|
|
useTaskCenterStore.setState({
|
|
scheduledBindings: {},
|
|
runRecords: [],
|
|
pinnedTaskIds: ['cron-1', 'cron-2', 'cron-3', 'cron-4', 'cron-5', 'cron-6'],
|
|
});
|
|
});
|
|
|
|
it('places chat first, keeps pinned tasks under task center, and keeps account identity in the footer', () => {
|
|
renderSidebar();
|
|
|
|
const order = getTestIdOrder();
|
|
const indexOf = (testId: string) => order.indexOf(testId);
|
|
|
|
expect(order[0]).toBe('sidebar-new-chat');
|
|
expect(indexOf('sidebar-new-chat')).toBeLessThan(indexOf('sidebar-collapse-toggle'));
|
|
expect(indexOf('sidebar-collapse-toggle')).toBeLessThan(indexOf('sidebar-chat-history'));
|
|
expect(indexOf('sidebar-new-chat')).toBeLessThan(indexOf('sidebar-chat-history'));
|
|
expect(indexOf('sidebar-chat-history')).toBeLessThan(indexOf('sidebar-nav-tasks'));
|
|
expect(indexOf('sidebar-nav-tasks')).toBeLessThan(indexOf('sidebar-pinned-task-cron-1'));
|
|
expect(indexOf('sidebar-pinned-task-cron-5')).toBeLessThan(indexOf('sidebar-pinned-task-more'));
|
|
expect(indexOf('sidebar-pinned-task-more')).toBeLessThan(indexOf('sidebar-nav-app-center'));
|
|
expect(indexOf('sidebar-nav-app-center')).toBeLessThan(indexOf('sidebar-nav-knowledge'));
|
|
expect(indexOf('sidebar-nav-knowledge')).toBeLessThan(indexOf('sidebar-account-summary'));
|
|
expect(indexOf('sidebar-account-summary')).toBeLessThan(indexOf('sidebar-account-identity'));
|
|
expect(indexOf('sidebar-account-identity')).toBeLessThan(indexOf('sidebar-nav-settings'));
|
|
expect(screen.queryByTestId('sidebar-pinned-task-cron-6')).not.toBeInTheDocument();
|
|
expect(screen.getByTestId('sidebar-pinned-task-more')).toHaveTextContent('查看更多 +1');
|
|
expect(screen.getByTestId('sidebar-pinned-task-more')).toHaveAttribute('href', '/tasks');
|
|
const newChat = screen.getByTestId('sidebar-new-chat');
|
|
expect(newChat).toHaveAttribute('title', '新对话');
|
|
expect(newChat.className).toContain('backdrop-blur-md');
|
|
expect(screen.getByTestId('sidebar-collapse-toggle')).toHaveAttribute('title', '收起侧栏');
|
|
expect(screen.getByTestId('sidebar-chat-history')).toHaveAttribute('title', '历史会话');
|
|
expect(screen.getByTestId('sidebar-nav-tasks')).toHaveAttribute('title', '任务中心');
|
|
expect(screen.getByTestId('sidebar-pinned-task-cron-1')).toHaveAttribute('title', '快捷任务 1');
|
|
expect(screen.getByTestId('sidebar-account-summary')).toContainElement(screen.getByTestId('sidebar-account-identity'));
|
|
expect(screen.getByTestId('sidebar-account-summary')).toContainElement(screen.getByTestId('sidebar-nav-settings'));
|
|
expect(screen.getByTestId('sidebar-account-workspace')).toHaveTextContent('智念空间');
|
|
expect(screen.getByTestId('sidebar-account-user')).toHaveTextContent('王管理员');
|
|
expect(screen.queryByText('对话')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('任务')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('快捷触发')).not.toBeInTheDocument();
|
|
expect(screen.queryByText('快速使用')).not.toBeInTheDocument();
|
|
});
|
|
|
|
it('hides pinned quick tasks when collapsed and shows hover labels on icon buttons', async () => {
|
|
useSettingsStore.setState({
|
|
sidebarCollapsed: true,
|
|
devModeUnlocked: false,
|
|
});
|
|
|
|
renderSidebar();
|
|
|
|
expect(screen.queryByTestId('sidebar-pinned-task-cron-1')).not.toBeInTheDocument();
|
|
expect(screen.queryByTestId('sidebar-pinned-task-more')).not.toBeInTheDocument();
|
|
expect(screen.getByTestId('sidebar-new-chat')).toHaveAttribute('title', '新对话');
|
|
expect(screen.getByTestId('sidebar-collapse-toggle')).toHaveAttribute('title', '展开侧栏');
|
|
expect(screen.getByTestId('sidebar-chat-history')).toHaveAttribute('title', '历史会话');
|
|
expect(screen.getByTestId('sidebar-nav-tasks')).toHaveAttribute('title', '任务中心');
|
|
expect(screen.getByTestId('sidebar-nav-settings')).toHaveAttribute('title', '设置');
|
|
|
|
fireEvent.pointerMove(screen.getByTestId('sidebar-nav-tasks'), { pointerType: 'mouse' });
|
|
fireEvent.pointerEnter(screen.getByTestId('sidebar-nav-tasks'), { pointerType: 'mouse' });
|
|
|
|
await waitFor(() => {
|
|
expect(screen.getAllByText('任务中心').length).toBeGreaterThan(0);
|
|
});
|
|
});
|
|
|
|
it('does not mark history active just because a new chat is open', () => {
|
|
renderSidebar('/chat');
|
|
|
|
const historyButton = screen.getByTestId('sidebar-chat-history');
|
|
expect(historyButton).toHaveAttribute('aria-expanded', 'false');
|
|
expect(historyButton.className).not.toContain('ring-[#D5E8F3]/80');
|
|
|
|
fireEvent.click(historyButton);
|
|
|
|
expect(historyButton).toHaveAttribute('aria-expanded', 'true');
|
|
expect(historyButton.className).toContain('ring-[#D5E8F3]/80');
|
|
});
|
|
});
|