import { beforeEach, describe, expect, it, vi } from 'vitest'; import { act, render, screen } from '@testing-library/react'; import { MemoryRouter } from 'react-router-dom'; import { Today } from '@/pages/Today'; import { useYinianStore } from '@/stores/yinian'; import { useYinianSkillsStore } from '@/stores/yinian-skills'; import { useSkillsStore } from '@/stores/skills'; import { useChatStore } from '@/stores/chat'; import { useGatewayStore } from '@/stores/gateway'; import i18n from '@/i18n'; import type { YinianConfigSnapshot, YinianLocalSkill } from '../../shared/yinian'; const hotelHangzhou = { id: 'workspace_hangzhou_ops', name: '智念企业组织空间', brand: '智念', city: '杭州', role: 'manager' as const, permissions: ['dashboard.read', 'skills.sync'], ota: [], }; const hotelShanghai = { id: 'workspace_shanghai_growth', name: '智念增长组织空间', city: '上海', role: 'viewer' as const, permissions: ['dashboard.read'], ota: [], }; function createConfig(overrides: Partial = {}): YinianConfigSnapshot { return { serverTime: 1, user: { id: 'user_1', name: '王管理员' }, hotel: hotelHangzhou, hotels: [hotelHangzhou, hotelShanghai], entitlements: [], notificationChannels: [], featureFlags: {}, uiPolicy: { defaultPage: 'today', showAdvancedSettings: false }, ...overrides, }; } function createLocalSkill(overrides: Partial): YinianLocalSkill { return { skillId: 'daily-report', name: '日报生成助手', version: '1.0.0', enabled: true, installedAt: 1, lastSyncedAt: Date.now() - 5 * 60_000, status: 'installed', source: 'mock', ...overrides, }; } function renderToday() { return render( , ); } describe('Today page', () => { beforeEach(async () => { vi.clearAllMocks(); await i18n.changeLanguage('zh'); useYinianStore.setState({ status: 'authenticated', session: { authenticated: true, user: { id: 'user_1', name: '王管理员' }, hotels: [hotelHangzhou, hotelShanghai], currentHotelId: hotelHangzhou.id, accessTokenExpiresAt: 100, }, config: createConfig(), error: null, }); useYinianSkillsStore.setState({ localSkills: [], lastSync: null, loading: false, error: null, }); useSkillsStore.setState({ skills: [], loading: false, error: null, }); useChatStore.setState({ sessions: [], sessionLabels: {}, sessionLastActivity: {}, }); useGatewayStore.setState({ status: { state: 'stopped', port: 18789, gatewayReady: false, }, }); }); it('shows work entry, pinned apps, and quick tools when workspace has no entitlements', () => { renderToday(); expect(screen.getAllByText('智念企业组织空间').length).toBeGreaterThan(0); expect(screen.getByText('今天想先做什么?')).toBeInTheDocument(); expect(screen.getByText('开始工作')).toBeInTheDocument(); expect(screen.getByText('常用应用')).toBeInTheDocument(); expect(screen.getByText('智念视频助手')).toBeInTheDocument(); expect(screen.getByText('定时任务')).toBeInTheDocument(); expect(screen.getByText('知识库')).toBeInTheDocument(); expect(screen.queryByText('0/0')).not.toBeInTheDocument(); expect(screen.queryByText('需要关注')).not.toBeInTheDocument(); expect(screen.getByText('最近对话')).toBeInTheDocument(); }); it('shows recent conversations before generic empty history', () => { useYinianStore.setState({ config: createConfig({ entitlements: [ { skillId: 'daily-report', name: '日报生成助手', version: '1.0.0', enabled: true, category: 'reporting', triggers: ['scheduled', 'manual'], lastRunAt: Date.now() - 60 * 60_000, }, { skillId: 'data-check', name: '数据检查助手', version: '1.2.0', enabled: true, category: 'ota-monitoring', triggers: ['scheduled'], }, { skillId: 'customer-reply-helper', name: '客户回复助手', version: '0.9.0', enabled: false, category: 'guest-comm', triggers: ['manual'], }, ], notificationChannels: [ { id: 'ops', kind: 'wecom', label: '业务通知群', recipient: 'room_1', enabled: true, source: 'nianxx', }, ], }), }); useYinianSkillsStore.setState({ localSkills: [ createLocalSkill({ skillId: 'daily-report', name: '日报生成助手', version: '1.0.0', status: 'skipped' }), createLocalSkill({ skillId: 'data-check', name: '数据检查助手', version: '1.1.0', status: 'installed' }), ], }); useSkillsStore.setState({ skills: [ { id: 'daily-report', name: '日报生成助手', description: '', enabled: true, isCore: false, source: 'openclaw-managed', }, { id: 'data-check', name: '数据检查助手', description: '', enabled: true, isCore: false, source: 'openclaw-managed', }, ], }); useChatStore.setState({ sessions: [ { key: 'agent:main:session-a', updatedAt: Date.now() - 2 * 60_000 }, { key: 'agent:main:session-b', updatedAt: Date.now() - 60 * 60_000 }, ], sessionLabels: { 'agent:main:session-a': '整理本周门店内容', 'agent:main:session-b': '又见乌江宣传片', }, sessionLastActivity: { 'agent:main:session-a': Date.now() - 2 * 60_000, 'agent:main:session-b': Date.now() - 60 * 60_000, }, }); renderToday(); expect(screen.queryByText('2/2')).not.toBeInTheDocument(); expect(screen.queryByText('下发 2 个 · 本地 2 个')).not.toBeInTheDocument(); expect(screen.queryByText(/待处理 1 个/)).not.toBeInTheDocument(); expect(screen.getByText('整理本周门店内容')).toBeInTheDocument(); expect(screen.getByText('又见乌江宣传片')).toBeInTheDocument(); expect(screen.queryByText('数据检查助手 有新版本')).not.toBeInTheDocument(); }); it('updates displayed workspace and counters after hotel switch refreshes config', () => { const { rerender } = render( , ); expect(screen.getAllByText('智念企业组织空间').length).toBeGreaterThan(0); act(() => { useYinianStore.setState({ config: createConfig({ hotel: hotelShanghai, entitlements: [ { skillId: 'customer-reply-helper', name: '客户回复助手', version: '0.9.0', enabled: true, category: 'guest-comm', triggers: ['manual'], }, ], }), }); useYinianSkillsStore.setState({ localSkills: [] }); }); rerender( , ); expect(screen.getAllByText('智念增长组织空间').length).toBeGreaterThan(0); expect(screen.queryByText('1/0')).not.toBeInTheDocument(); expect(screen.queryByText(/待处理 1 个/)).not.toBeInTheDocument(); expect(screen.queryByText('智念企业组织空间')).not.toBeInTheDocument(); }); });