Files
NianToB/tests/unit/sidebar-layout.test.tsx

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');
});
});