mirror of
https://github.com/Ed1s0nZ/CyberStrikeAI.git
synced 2026-08-14 15:10:20 +02:00
* feat(chat): add project-based conversation sidebar * feat(chat): refine Codex-style conversation UI * feat(chat): add Codex-style conversation workflow * feat(ui): 优化对话框与项目侧边栏交互 * fix(chat): 修复暗色输入框圆角填色 * fix(chat): 恢复输入区分层错位布局 * fix(hitl): isolate reviewer state per conversation * feat(hitl): 增加双入口审批与倒计时进度 * fix(hitl): 汇总项目审批并隔离对话状态 * fix(ui): 修复审批状态与无项目新任务 * fix(ui): 优化审批状态与对话切换性能 * fix(ui): 修复中断任务审批仍计时 * fix(ui): 修复多对话并发切换卡顿 * fix(hitl): 主动同步审批并关闭中断状态 * fix(ui): 固定项目审批汇总为绿色 * feat(ui): 同步系统模型与推理强度 * feat(ui): 完善项目侧栏预览与新建入口 * fix(ui): 防止无项目文件夹误展开 * fix(ui): 防止长历史对话滚动误触审批 * fix(ui): 修复对话操作并补充项目置顶 * fix(ui): 移除对话分组并调整项目置顶排序 * feat(ui): 优化迭代导航与审批交互 * fix(hitl): 将 write_file 加入内置免审批工具 * fix(chat): 支持回车发送与 Shift 回车换行 * fix(chat): 优化对话刷新与 Codex 风格交互 * fix(ui): 显示对话具体更新时间 * fix(ui): 优化对话刷新与项目加载 * fix(ui): 修复 Agent 审查文字裁切 * fix(chat): 修复刷新续流与多标签页同步 * fix(chat): 修复滚动跟随与中断任务终态 * fix(ui): 修复流式滚动跳动与工具状态恢复 * fix(ui): 修复刷新后流式输出停止粘底
75 lines
4.1 KiB
JavaScript
75 lines
4.1 KiB
JavaScript
const fs = require('node:fs');
|
|
const test = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
|
|
const chat = fs.readFileSync('web/static/js/chat.js', 'utf8');
|
|
const projects = fs.readFileSync('web/static/js/projects.js', 'utf8');
|
|
const template = fs.readFileSync('web/templates/index.html', 'utf8');
|
|
|
|
function functionSource(source, name, nextName) {
|
|
const start = source.indexOf(`function ${name}(`);
|
|
const end = source.indexOf(`function ${nextName}(`, start);
|
|
assert.notEqual(start, -1, `${name} should exist`);
|
|
assert.notEqual(end, -1, `${nextName} should follow ${name}`);
|
|
return source.slice(start, end);
|
|
}
|
|
|
|
test('全局置顶检查接口结果并即时通知项目文件夹', () => {
|
|
const source = functionSource(chat, 'pinConversation', 'showMoveToGroupSubmenu');
|
|
|
|
assert.match(source, /assertConversationActionResponse\(updateResponse, '更新置顶状态失败'\)/);
|
|
assert.match(source, /notifyConversationPinnedChanged\(convId, newPinned\)/);
|
|
assert.match(source, /loadConversationsWithGroups\(\)/);
|
|
});
|
|
|
|
test('项目文件夹内置顶对话优先排序并显示图钉', () => {
|
|
const sortSource = functionSource(projects, 'sortProjectFolderConversations', 'updateChatProjectConversationPinnedState');
|
|
const itemSource = functionSource(projects, 'appendChatProjectConversationItem', 'selectChatProjectConversationItem');
|
|
|
|
assert.match(sortSource, /Number\(!!b\?\.pinned\) - Number\(!!a\?\.pinned\)/);
|
|
assert.match(itemSource, /if \(conversation\.pinned\)/);
|
|
assert.match(itemSource, /project-conversation-pinned/);
|
|
});
|
|
|
|
test('删除事件立即移除项目缓存并触发权威刷新', () => {
|
|
const removeSource = functionSource(projects, 'removeChatProjectConversation', 'refreshChatProjectFoldersAfterAction');
|
|
|
|
assert.match(removeSource, /chatProjectFolderContext\.conversations = chatProjectFolderContext\.conversations\.filter/);
|
|
assert.match(projects, /document\.addEventListener\('conversation-deleted',[\s\S]{0,300}removeChatProjectConversation\(conversationId\)[\s\S]{0,180}refreshChatProjectFoldersAfterAction\(\)/);
|
|
assert.match(chat, /document\.dispatchEvent\(new CustomEvent\('conversation-deleted'/);
|
|
});
|
|
|
|
test('较旧的项目文件夹请求不能覆盖较新的操作结果', () => {
|
|
const source = functionSource(projects, 'loadChatProjectFolderContext', 'getProjectConversationSortTime');
|
|
|
|
assert.match(source, /const loadSeq = \+\+chatProjectFolderContextLoadSeq/);
|
|
assert.match(source, /if \(loadSeq !== chatProjectFolderContextLoadSeq\) return false/);
|
|
});
|
|
|
|
test('项目文件夹菜单可以置顶并立即更新排序', () => {
|
|
const toggleSource = functionSource(projects, 'toggleProjectPinnedFromListMenu', 'initProjectListActionMenu');
|
|
const folderSource = functionSource(projects, 'appendChatProjectFolderItem', 'appendChatProjectConversationItem');
|
|
|
|
assert.match(template, /onclick="toggleProjectPinnedFromListMenu\(\)"/);
|
|
assert.match(toggleSource, /JSON\.stringify\(\{ pinned: nextPinned \}\)/);
|
|
assert.match(toggleSource, /updateCachedProjectPinnedState\(projectId, nextPinned\)/);
|
|
assert.match(folderSource, /if \(!isUnassigned && project\.pinned\)/);
|
|
assert.match(folderSource, /project-folder-pinned/);
|
|
assert.match(projects, /\[\.\.\.pinnedProjects, unassignedProject, \.\.\.regularProjects\]/);
|
|
});
|
|
|
|
test('对话侧栏不再显示对话分组区域', () => {
|
|
assert.doesNotMatch(template, /class="conversation-groups-section"/);
|
|
assert.doesNotMatch(template, /id="conversation-groups-list"/);
|
|
});
|
|
|
|
test('删除对话分组检查接口结果并先清理本地状态', () => {
|
|
const deleteSource = functionSource(chat, 'deleteConversationGroupById', 'deleteGroup');
|
|
const contextSource = functionSource(chat, 'deleteGroupFromContext', 'closeGroupContextMenu');
|
|
|
|
assert.match(deleteSource, /assertConversationActionResponse\(deleteResponse, '删除分组失败'\)/);
|
|
assert.match(deleteSource, /removeConversationGroupFromLocalState\(groupId\)/);
|
|
assert.match(deleteSource, /if \(currentGroupId === groupId\) exitGroupDetail\(\)/);
|
|
assert.match(contextSource, /deleteConversationGroupById\(groupId, \{ closeContextMenu: true \}\)/);
|
|
});
|