feat: v0.8.2 安全纵深补全 · 协议保真 · 断链修复 — 图片SSRF/根MEMORY.md保护根治 · Anthropic thinking回传+pause_turn续传 · 2523 用例全量回归 + E2E 扩充
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
// @vitest-environment jsdom
|
||||
/**
|
||||
* Sidebar 组件测试(v0.8.2 P3-3 补齐设置面板/侧栏测试空白)
|
||||
*
|
||||
* 覆盖契约:
|
||||
* - 挂载时拉取会话列表并渲染标题
|
||||
* - v0.8.2 P2-4: session:updated 实时刷新 messageCount/updatedAt/title
|
||||
* - 搜索框(标题 + 内容全文搜索入口)存在
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { render, screen, waitFor, act } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { Sidebar } from '../Sidebar';
|
||||
import { useSessionStore } from '@renderer/stores/session-store';
|
||||
|
||||
const noop = (): void => {};
|
||||
|
||||
const sessionUpdatedHandlers: Array<(data: unknown) => void> = [];
|
||||
|
||||
function makeSession(overrides: Record<string, unknown> = {}): {
|
||||
id: string;
|
||||
title: string;
|
||||
createdAt: number;
|
||||
updatedAt: number;
|
||||
messageCount: number;
|
||||
totalTokens: number;
|
||||
pinned: boolean;
|
||||
archived: boolean;
|
||||
} {
|
||||
return {
|
||||
id: 's1',
|
||||
title: '第一个会话',
|
||||
createdAt: Date.now(),
|
||||
updatedAt: Date.now(),
|
||||
messageCount: 2,
|
||||
totalTokens: 0,
|
||||
pinned: false,
|
||||
archived: false,
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
sessionUpdatedHandlers.length = 0;
|
||||
useSessionStore.getState().setSessions([]);
|
||||
useSessionStore.getState().setCurrentSession(null);
|
||||
const bridge = (window as unknown as { metona: Record<string, unknown> }).metona;
|
||||
bridge.sessions = {
|
||||
...(bridge.sessions as Record<string, unknown>),
|
||||
list: vi.fn().mockResolvedValue([makeSession(), makeSession({ id: 's2', title: 'Second' })]),
|
||||
searchContent: vi.fn().mockResolvedValue({ success: true, data: [] }),
|
||||
onSessionUpdated: vi.fn((cb: (data: unknown) => void) => {
|
||||
sessionUpdatedHandlers.push(cb);
|
||||
return noop;
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
describe('Sidebar — 会话列表', () => {
|
||||
it('挂载时拉取并渲染会话标题', async () => {
|
||||
render(<Sidebar />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('第一个会话')).toBeInTheDocument();
|
||||
expect(screen.getByText('Second')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('渲染搜索框与新建会话按钮', () => {
|
||||
render(<Sidebar />);
|
||||
expect(screen.getByPlaceholderText(/搜索|Search/)).toBeInTheDocument();
|
||||
expect(screen.getAllByRole('button').length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('接收 session:updated 后实时刷新条数(P2-4)', async () => {
|
||||
render(<Sidebar />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('第一个会话')).toBeInTheDocument();
|
||||
});
|
||||
expect(sessionUpdatedHandlers.length).toBeGreaterThan(0);
|
||||
|
||||
act(() => {
|
||||
sessionUpdatedHandlers.forEach((h) =>
|
||||
h({ sessionId: 's1', messageCount: 42, updatedAt: Date.now() }),
|
||||
);
|
||||
});
|
||||
await waitFor(() => {
|
||||
// t('sidebar.messageCount') zh 文案为 "{{count}} 条"
|
||||
expect(screen.getByText(/42\s*条|42 messages/)).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
|
||||
it('接收 session:updated 的 title 补丁 → 标题刷新(LLM 标题生成链路)', async () => {
|
||||
render(<Sidebar />);
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('第一个会话')).toBeInTheDocument();
|
||||
});
|
||||
act(() => {
|
||||
sessionUpdatedHandlers.forEach((h) => h({ sessionId: 's1', title: 'LLM 生成的新标题' }));
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(screen.getByText('LLM 生成的新标题')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Sidebar — 搜索', () => {
|
||||
it('输入搜索词触发内容全文搜索(FTS5 通道,300ms 防抖)', async () => {
|
||||
const user = userEvent.setup();
|
||||
render(<Sidebar />);
|
||||
const input = screen.getByPlaceholderText(/搜索|Search/);
|
||||
await user.type(input, '关键词');
|
||||
await waitFor(
|
||||
() => {
|
||||
const bridge = (window as unknown as { metona: Record<string, unknown> }).metona;
|
||||
expect(bridge.sessions).toBeDefined();
|
||||
expect(
|
||||
(bridge.sessions as { searchContent: { mock: { calls: unknown[][] } } }).searchContent
|
||||
.mock.calls.length,
|
||||
).toBeGreaterThan(0);
|
||||
},
|
||||
{ timeout: 2_000 },
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user