fix: 显示下发智能体的云端图标
This commit is contained in:
1 parent
9147b509c7
commit
a3c02e7464
10 files changed
+127
-12
No files matched your search
@@ -17,11 +17,12 @@ export function Layout() {
|
||||
const [selected, setSelected] = useState(0);
|
||||
const agents = Array.from({ length: count }, (_, index) => ({
|
||||
teacher_id: `agent-${index}`, version: index + 1, is_default: index === 0,
|
||||
definition: { config_id: `agent-${index}`, name: `智能体${index + 1}`, avatar_id: 'avatar-06' },
|
||||
definition: { config_id: `agent-${index}`, name: `智能体${index + 1}`, avatar_id: 'avatar-06',
|
||||
icon_url: new URLSearchParams(location.search).has('icons') ? `https://yuxi.example.test/api/storage/public/images/agent-${index}.png` : undefined },
|
||||
}));
|
||||
const companion = {
|
||||
agents, selectedAgent: agents[selected],
|
||||
definition: { name: '编程老师', avatar_id: 'avatar-06' },
|
||||
definition: agents[selected]?.definition ?? { name: '编程老师', avatar_id: 'avatar-06' },
|
||||
invitation: visible ? { response: long ? '你刚才说想做一个宠物游戏。我们一起想想,什么样的小动作,会让你愿意一直陪它玩下去?可以先挑一个最想试的想法。' : '有什么编程问题想一起讨论?' } : null,
|
||||
welcome: null, enabled: true, loading: false, checking: false, error: null,
|
||||
dismiss: () => setVisible(false),
|
||||
|
||||
@@ -70,6 +70,26 @@ async function geometry(page: Page) {
|
||||
});
|
||||
}
|
||||
|
||||
test('distributed icons load over HTTP and broken images fall back in the titlebar', async ({ page }) => {
|
||||
await page.route('https://yuxi.example.test/api/storage/public/images/agent-0.png', route => route.fulfill({
|
||||
contentType: 'image/svg+xml',
|
||||
body: '<svg xmlns="http://www.w3.org/2000/svg" width="32" height="32"><circle cx="16" cy="16" r="15" fill="blue"/></svg>',
|
||||
}));
|
||||
await page.route('https://yuxi.example.test/api/storage/public/images/agent-1.png', route => route.fulfill({ status: 404 }));
|
||||
await page.goto(`${baseUrl}?agents=2&icons=1`);
|
||||
const custom = page.getByRole('button', { name: '与智能体1聊天' }).locator('img');
|
||||
const fallback = page.getByRole('button', { name: '与智能体2聊天' }).locator('img');
|
||||
const speaker = page.getByRole('button', { name: '打开这条智能体消息' }).locator('img');
|
||||
await expect(custom).toHaveAttribute('src', 'https://yuxi.example.test/api/storage/public/images/agent-0.png');
|
||||
await expect(custom).toHaveCSS('image-rendering', 'auto');
|
||||
await expect(speaker).toHaveAttribute('src', 'https://yuxi.example.test/api/storage/public/images/agent-0.png');
|
||||
await expect(fallback).toHaveAttribute('src', /avatar-06\.png/);
|
||||
for (const img of [custom, fallback, speaker]) {
|
||||
await expect.poll(() => img.evaluate(node => (node as HTMLImageElement).naturalWidth)).toBeGreaterThan(0);
|
||||
}
|
||||
await page.screenshot({ path: test.info().outputPath('distributed-icons.png') });
|
||||
});
|
||||
|
||||
for (const size of [{ width: 1440, height: 900 }, { width: 1180, height: 720 }, { width: 1000, height: 640 }]) {
|
||||
test(`preview only clears overlapping speech at ${size.width}x${size.height}`, async ({ page }) => {
|
||||
await page.setViewportSize(size);
|
||||
|
||||
@@ -96,6 +96,16 @@ async function ready() {
|
||||
await waitFor(() => expect(streams.has('first')).toBe(true));
|
||||
}
|
||||
describe('teacher side chat', () => {
|
||||
it('renders the pinned topic icon in the header and assistant messages', async () => {
|
||||
const icon = 'https://yuxi.example.test/api/storage/public/images/topic.png';
|
||||
api.read.mockResolvedValue({ ...first, definition: { ...definition, icon_url: icon }, requests: [request({ response: '继续原来的讨论' })] });
|
||||
const view = render(<TeacherChatPanel projectId="p" sourceId="c" />);
|
||||
await ready();
|
||||
const images = view.container.querySelectorAll('[data-testid="teacher-chat-panel"] img');
|
||||
expect(images).toHaveLength(2);
|
||||
for (const img of images) expect(img).toHaveAttribute('src', icon);
|
||||
});
|
||||
|
||||
it('renders assistant Markdown, tables, code and images instead of literal markup', async () => {
|
||||
const response = '## 项目建议\n\n**先验证跳跃**\n\n- 保留现有玩法\n\n| 文件 | 用途 |\n| --- | --- |\n| main.ts | 入口 |\n\n```json\n{"name":"flybot"}\n```\n\n[说明](https://example.com/readme)\n\n';
|
||||
api.read.mockResolvedValue({ ...first, requests: [request({ response })] });
|
||||
|
||||
@@ -43,6 +43,24 @@ beforeEach(() => {
|
||||
afterEach(() => { cleanup(); vi.useRealTimers(); });
|
||||
|
||||
describe('teacher presence and periodic guidance', () => {
|
||||
it('uses each published icon in the topbar, welcome and panel, and falls back after an image error', async () => {
|
||||
const a = { teacher_id: 'a', version: 1, is_default: true, definition: { ...definition, icon_url: 'https://yuxi.example.test/api/storage/public/images/a.png' } };
|
||||
const b = { teacher_id: 'b', version: 2, is_default: false, definition: { ...definition, name: '另一个智能体', icon_url: 'https://yuxi.example.test/api/storage/public/images/b.webp' } };
|
||||
api.catalog.mockResolvedValue({ items: [a, b] });
|
||||
const view = await mount();
|
||||
const first = screen.getByRole('button', { name: '与运营配置的智能体聊天' }).querySelector('img')!;
|
||||
const second = screen.getByRole('button', { name: '与另一个智能体聊天' }).querySelector('img')!;
|
||||
expect(first).toHaveAttribute('src', a.definition.icon_url);
|
||||
expect(second).toHaveAttribute('src', b.definition.icon_url);
|
||||
fireEvent.error(first);
|
||||
expect(first.src).toContain('avatar-06.png');
|
||||
expect(second).toHaveAttribute('src', b.definition.icon_url);
|
||||
await act(async () => fireEvent.click(screen.getByRole('button', { name: '与另一个智能体聊天' })));
|
||||
const images = view.container.querySelectorAll('[data-testid="teacher-chat-panel"] img');
|
||||
expect(images).toHaveLength(2);
|
||||
for (const img of images) expect(img).toHaveAttribute('src', b.definition.icon_url);
|
||||
});
|
||||
|
||||
it('shows every distributed agent in the header and preserves separate drafts without dispatching on switch', async () => {
|
||||
const a = { teacher_id: 'method', version: 3, is_default: true, definition: { ...definition, config_id: 'method', name: '方法老师' } };
|
||||
const b = { teacher_id: 'experience', version: 9, is_default: false, definition: { ...definition, config_id: 'experience', name: '体验朋友', avatar_id: 'avatar-09' } };
|
||||
|
||||
Reference in new issue
Block a user