Files
CyberStrikeAI/web/static/js/turn-duration-refresh.test.cjs
T
RuoJi6andGitHub eb6bab574f 优化项目对话、刷新续流、实时滚动与工具状态恢复 (#245)
* 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): 修复刷新后流式输出停止粘底
2026-08-13 21:28:37 +08:00

119 lines
3.6 KiB
JavaScript

const fs = require('node:fs');
const vm = require('node:vm');
const test = require('node:test');
const assert = require('node:assert/strict');
const chat = fs.readFileSync('web/static/js/chat.js', 'utf8');
function timingSource(source) {
const start = source.indexOf('function formatAssistantTurnDuration(');
const end = source.indexOf('window.setAssistantTurnTiming = setAssistantTurnTiming;', start);
assert.notEqual(start, -1, 'formatAssistantTurnDuration should exist');
assert.notEqual(end, -1, 'timing exports should exist');
return source.slice(start, end);
}
function createClassList() {
return {
toggle() {},
};
}
function createMessage() {
const label = {
innerHTML: '',
classList: createClassList(),
setAttribute() {},
};
return {
dataset: {},
querySelector(selector) {
if (selector === '.mcp-call-label.turn-process-summary') return label;
return null;
},
label,
};
}
function createHarness(nowMs) {
const RealDate = Date;
class TestDate extends RealDate {
static now() {
return nowMs;
}
}
const context = {
Date: TestDate,
Number,
Math,
String,
document: {
querySelector() { return null; },
querySelectorAll() { return []; },
getElementById() { return null; },
},
window: {},
escapeHtml(value) { return String(value); },
setInterval() { return 1; },
clearInterval() {},
};
vm.runInNewContext(
`${timingSource(chat)}; this.setAssistantTurnTiming = setAssistantTurnTiming;`,
context
);
return context;
}
test('刷新运行中任务时忽略摘要中的零耗时并按开始时间恢复', () => {
const startedAt = '2026-08-12T02:00:00.000Z';
const startedMs = Date.parse(startedAt);
const context = createHarness(startedMs + 65_000);
const message = createMessage();
context.setAssistantTurnTiming(message, {
startedAt,
durationMs: 0,
status: 'running',
});
assert.equal(message.dataset.turnDurationMs, undefined);
assert.match(message.label.innerHTML, /已处理 1 分钟 5 秒/);
});
test('已完成任务仍优先使用持久化耗时', () => {
const context = createHarness(Date.parse('2026-08-12T02:05:00.000Z'));
const message = createMessage();
context.setAssistantTurnTiming(message, {
startedAt: '2026-08-12T02:00:00.000Z',
completedAt: '2026-08-12T02:01:05.000Z',
durationMs: 65_000,
status: 'completed',
});
assert.equal(message.dataset.turnDurationMs, '65000');
assert.match(message.label.innerHTML, /耗时 1 分钟 5 秒/);
});
test('已中断任务使用固定终态耗时且不再按当前时间增长', () => {
const context = createHarness(Date.parse('2026-08-13T12:00:00.000Z'));
const message = createMessage();
context.setAssistantTurnTiming(message, {
startedAt: '2026-08-11T09:47:14.000Z',
completedAt: '2026-08-11T09:51:39.000Z',
durationMs: 265_000,
status: 'cancelled',
});
assert.equal(message.dataset.turnDurationMs, '265000');
assert.match(message.label.innerHTML, /已中断 · 耗时 4 分钟 25 秒/);
assert.doesNotMatch(message.label.innerHTML, /已处理/);
});
test('历史占位消息存在取消事件时不会再判定为运行中', () => {
assert.match(chat, /function assistantTurnTerminalState\(processDetails\)/);
assert.match(chat, /const isRunning = isAssistantPlaceholder && !terminalState/);
assert.match(chat, /status: status/);
});