Files
Notes/tests/unit/ai-chat-panel-placeholder.test.js
2026-10-16 16:34:15 +08:00

133 lines
4.8 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// P2-23(audit):AI 面板 placeholder 区分「无文件」vs「未配置」。
//
// 设计:纯函数 pickPlaceholder 决策三态文案,
// - !hasFile → 「请先打开一个 Markdown 文件」
// - hasFile + !configured → 「AI 未配置 — 点「⚙ 设置」填 Base URL 与 API Key」
// - hasFile + configured → 「描述你想如何修改当前文档...」
//
// 把决策抽成纯函数方便单测(避免起 jsdom 跑 DOM 副作用)。panel 内部
// setEnabled / setConfigured 走它。
//
// 另测:setEnabled / setConfigured 在 jsdom 下同步 DOM 状态(input.placeholder +
// .ai-chat-configure-link.hidden),保证「内联设置链」只在该显示时显示。
// @vitest-environment jsdom
import { describe, it, expect, beforeEach } from 'vitest';
import { pickPlaceholder, AiChatPanel } from '../../src/ai/ai-chat-panel.js';
describe('pickPlaceholder —— P2-23 三态文案', () => {
it('无文件 → 提示先打开文件', () => {
expect(pickPlaceholder({ hasFile: false, configured: true }))
.toBe('请先打开一个 Markdown 文件');
});
it('有文件 + 未配置 → 提示去设置里配置', () => {
const text = pickPlaceholder({ hasFile: true, configured: false });
expect(text).toContain('AI 未配置');
expect(text).toContain('设置');
});
it('有文件 + 已配置 → 默认描述型 placeholder', () => {
expect(pickPlaceholder({ hasFile: true, configured: true }))
.toBe('描述你想如何修改当前文档...');
});
it('优先级:!hasFile 永远盖过 !configured(即便都没配)', () => {
expect(pickPlaceholder({ hasFile: false, configured: false }))
.toBe('请先打开一个 Markdown 文件');
});
});
describe('AiChatPanel.setEnabled / setConfigured —— DOM 同步', () => {
/** @returns {HTMLElement} 重建一份 index.html 里的 ai-chat-dock 骨架 */
function buildDock() {
const dock = document.createElement('div');
dock.className = 'ai-chat-dock';
dock.innerHTML = `
<div class="ai-chat-form-row">
<button class="ai-chat-toggle" type="button">×</button>
<div class="ai-chat-input-wrap">
<input type="text" class="ai-chat-input" />
<button class="ai-chat-configure-link" type="button" hidden>⚙ 设置</button>
</div>
<button class="ai-chat-submit" type="button" data-mode="submit" disabled>生成修改</button>
</div>
`;
return dock;
}
let dock;
beforeEach(() => {
dock = buildDock();
});
it('未初始化时默认 configured=true(避免中间态被误判)', () => {
const panel = new AiChatPanel({
element: dock,
onSubmit: () => {},
onCancel: () => {},
});
// 默认 _configured !== false === true
panel.setEnabled(false); // 无文件
expect(dock.querySelector('.ai-chat-input').placeholder)
.toBe('请先打开一个 Markdown 文件');
});
it('hasFile + !configured → placeholder 切到「未配置」+ 内联链显示', () => {
const panel = new AiChatPanel({
element: dock,
onSubmit: () => {},
onCancel: () => {},
});
panel.setEnabled(true); // 有文件
panel.setConfigured(false); // 未配置
const input = dock.querySelector('.ai-chat-input');
const link = dock.querySelector('.ai-chat-configure-link');
expect(input.placeholder).toContain('AI 未配置');
expect(link.hidden).toBe(false);
});
it('hasFile + configured → placeholder 切回「描述修改」+ 内联链隐藏', () => {
const panel = new AiChatPanel({
element: dock,
onSubmit: () => {},
onCancel: () => {},
});
panel.setEnabled(true);
panel.setConfigured(false);
panel.setConfigured(true); // 配置完成
const input = dock.querySelector('.ai-chat-input');
const link = dock.querySelector('.ai-chat-configure-link');
expect(input.placeholder).toBe('描述你想如何修改当前文档...');
expect(link.hidden).toBe(true);
});
it('!hasFile 永远隐藏内联链(不论配置与否)', () => {
const panel = new AiChatPanel({
element: dock,
onSubmit: () => {},
onCancel: () => {},
});
panel.setEnabled(false); // 无文件
panel.setConfigured(false); // 未配置
expect(dock.querySelector('.ai-chat-configure-link').hidden).toBe(true);
});
it('点击内联链调 onOpenSettings 回调(如果传了)', () => {
let called = 0;
const panel = new AiChatPanel({
element: dock,
onSubmit: () => {},
onCancel: () => {},
onOpenSettings: () => { called += 1; },
});
panel.setEnabled(true);
panel.setConfigured(false);
const link = dock.querySelector('.ai-chat-configure-link');
link.click();
expect(called).toBe(1);
});
});