// @vitest-environment jsdom // components.test.js —— Composer 快捷键与禁用逻辑 + AssistantMessage 块联合渲染 import { describe, it, expect, vi, beforeEach } from 'vitest'; import { mount } from '@vue/test-utils'; import { setActivePinia, createPinia } from 'pinia'; import Composer from '../../../../frontend/src/components/chat/Composer.vue'; import AssistantMessage from '../../../../frontend/src/components/chat/AssistantMessage.vue'; import SessionItem from '../../../../frontend/src/components/session/SessionItem.vue'; describe('Composer(FR-CHAT-005/007/011)', () => { beforeEach(() => { setActivePinia(createPinia()); }); const mountComposer = (props = {}) => mount(Composer, { props: { mode: 'ask', busy: false, disabled: false, ...props }, }); it('空输入禁用发送;⌘/Ctrl+Enter 发送;Enter 换行', async () => { const w = mountComposer(); const btn = w.find('.send-btn'); expect(btn.attributes('disabled')).toBeDefined(); const ta = w.find('textarea'); await ta.setValue('水稻SPL基因家族?'); // Enter 单独按下:不触发发送(换行) await ta.trigger('keydown', { key: 'Enter' }); expect(w.emitted('send')).toBeUndefined(); // Ctrl+Enter:发送 await ta.trigger('keydown', { key: 'Enter', ctrlKey: true }); expect(w.emitted('send')).toBeTruthy(); expect(w.emitted('send')[0][0]).toBe('水稻SPL基因家族?'); // 发送后清空输入 expect(ta.element.value).toBe(''); }); it('⌘+Enter 同样发送(mac)', async () => { const w = mountComposer(); const ta = w.find('textarea'); await ta.setValue('问题'); await ta.trigger('keydown', { key: 'Enter', metaKey: true }); expect(w.emitted('send')).toBeTruthy(); }); it('busy 时按钮变「停止」;点击 stop 触发事件(FR-CHAT-011)', async () => { const w = mountComposer({ busy: true }); const btn = w.find('.send-btn'); expect(btn.text()).toContain('停止'); await btn.trigger('click'); expect(w.emitted('stop')).toBeTruthy(); }); it('disabled(健康降级)时输入与发送均不可用', async () => { const w = mountComposer({ disabled: true }); expect(w.find('textarea').attributes('disabled')).toBeDefined(); const ta = w.find('textarea'); await ta.setValue('问题'); expect(w.find('.send-btn').attributes('disabled')).toBeDefined(); }); it('模式 pill 三态切换(labels.MODES)', async () => { const w = mountComposer({ mode: 'ask' }); const pills = w.findAll('.mode-pill'); expect(pills).toHaveLength(3); expect(pills[0].classes()).toContain('on'); await pills[1].trigger('click'); expect(w.emitted('change-mode')[0][0]).toBe('plan'); }); it('输入法组合键 Enter 不触发发送', async () => { const w = mountComposer(); const ta = w.find('textarea'); await ta.setValue('问题'); await ta.trigger('keydown', { key: 'Enter', ctrlKey: true, isComposing: true }); expect(w.emitted('send')).toBeUndefined(); }); }); describe('AssistantMessage 块联合渲染', () => { beforeEach(() => { setActivePinia(createPinia()); }); const mountMsg = (blocks, extra = {}) => mount(AssistantMessage, { props: { message: { role: 'assistant', status: 'done', blocks, timeLabel: '10:00' }, ...extra }, }); it('text 块:markdown 渲染 + 脚本注入被消毒(FR-CHAT-004)', () => { const w = mountMsg([{ type: 'text', html: '
加粗 SPL