Files
memind/mindspace-chat-context.test.mjs
T
john 722b18326f feat(mindspace): 0630004 空间 UI、聊天连接、微信分享与 Agent 能力
含 MindSpace 三列布局与统计修复、聊天加载态与连接降级、平台页脚标记与 og:site_name 微信卡片、勾选资料删除 Agent 接口及内部话术过滤。

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-01 18:08:06 +08:00

297 lines
8.6 KiB
JavaScript
Raw 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.
import assert from 'node:assert/strict';
import test from 'node:test';
import {
buildContextPrefix,
buildMindSpaceChatContext,
buildSelectedAssetGreeting,
formatContextChip,
mindspaceChatContextInternals,
} from './mindspace-chat-context.mjs';
const sampleSpace = {
id: 'space-1',
name: '我的空间',
categories: [
{ code: 'oa', name: 'OA 工作区', itemCount: 12 },
{ code: 'draft', name: '页面草稿', itemCount: 5 },
],
};
const sampleCategory = {
id: 'cat-oa',
code: 'oa',
name: 'OA 工作区',
itemCount: 12,
};
const samplePage = {
id: 'page-1',
title: '项目周报',
summary: '本周项目进展与风险汇总',
status: 'draft',
versionNo: 3,
categoryCode: 'draft',
templateId: 'report',
contentFormat: 'markdown',
pageType: 'article',
content: '# 周报\n\n- 完成 A 模块\n- 风险:进度偏紧',
};
test('buildMindSpaceChatContext resolves home, category, and page views', () => {
const home = buildMindSpaceChatContext({
space: sampleSpace,
ownerUsername: 'john',
selectedCategory: null,
selectedPageId: null,
pages: [samplePage],
route: '/space',
});
assert.equal(home.view, 'home');
assert.equal(home.ownerUsername, 'john');
assert.equal(home.homeCategories?.length, 2);
assert.equal(home.recentPages?.[0]?.title, '项目周报');
const category = buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: sampleCategory,
selectedPageId: null,
pages: [],
assets: [
{
id: 'asset-1',
displayName: 'report.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
],
route: '/space?category=oa',
});
assert.equal(category.view, 'category');
assert.deepEqual(category.category?.assets, [
{
id: 'asset-1',
displayName: 'report.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
},
]);
const page = buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: null,
selectedPageId: 'page-1',
pages: [samplePage],
pageLive: {
page: samplePage,
title: '项目周报(编辑中)',
summary: samplePage.summary,
content: samplePage.content,
},
route: '/space/page/page-1',
});
assert.equal(page.view, 'page');
assert.equal(page.page.title, '项目周报(编辑中)');
assert.match(page.page.contentExcerpt ?? '', /完成 A 模块/);
assert.equal(page.category?.code, 'draft');
});
test('buildContextPrefix includes page excerpt and location hint', () => {
const prefix = buildContextPrefix(
buildMindSpaceChatContext({
space: sampleSpace,
ownerUsername: 'john',
selectedCategory: null,
selectedPageId: 'page-1',
pages: [samplePage],
pageLive: {
page: samplePage,
title: samplePage.title,
summary: samplePage.summary,
content: samplePage.content,
},
route: '/space/page/page-1',
}),
);
assert.match(prefix, /- 账号:john/);
assert.match(prefix, /- 页面摘要:本周项目进展与风险汇总/);
assert.match(prefix, /- 页面模板:分析报告/);
assert.match(prefix, /- 当前正文摘录/);
assert.match(prefix, /完成 A 模块/);
assert.match(prefix, /【定位说明】/);
assert.match(prefix, /指代「这个页面」「改这里」时默认指该页面/);
assert.match(prefix, /\n---\n\n$/);
});
test('buildContextPrefix lists category assets on category view', () => {
const prefix = buildContextPrefix(
buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: sampleCategory,
selectedPageId: null,
pages: [],
assets: [{ id: 'asset-1', displayName: 'report.xlsx', mimeType: 'text/csv' }],
route: '/space?category=oa',
}),
);
assert.match(prefix, /- 分类:OA 工作区(oa,共 12 项)/);
assert.match(prefix, /report\.xlsxid: asset-1/);
assert.match(prefix, /指代「这里的文件」时默认指该分类下的资料/);
});
test('formatContextChip prefers page, then category with count, then space name', () => {
assert.equal(
formatContextChip({
spaceName: '我的空间',
view: 'page',
route: '/space/page/page-1',
page: { id: 'page-1', title: '项目周报', versionNo: 3 },
}),
'项目周报 · v3',
);
assert.equal(
formatContextChip({
spaceName: '我的空间',
view: 'category',
route: '/space?category=oa',
category: { code: 'oa', name: 'OA 工作区', itemCount: 12 },
}),
'OA 工作区 · 12 项',
);
});
test('excerptContent strips html and truncates long text', () => {
const html = '<h1>标题</h1><p>正文内容</p><script>alert(1)</script>';
assert.equal(
mindspaceChatContextInternals.excerptContent(html, 'html'),
'标题 正文内容',
);
assert.equal(
mindspaceChatContextInternals.excerptContent('a'.repeat(20), 'markdown', 10),
`${'a'.repeat(9)}`,
);
});
test('buildMindSpaceChatContext exposes single selected asset as focused asset', () => {
const asset = {
id: 'asset-9',
displayName: 'Q1-report.pdf',
mimeType: 'application/pdf',
filename: 'Q1-report.pdf',
sizeBytes: 1200,
categoryCode: 'oa',
assetType: 'file',
};
const context = buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: sampleCategory,
selectedPageId: null,
pages: [],
assets: [asset],
selectedAssets: [asset],
route: '/space?category=oa',
});
assert.equal(context.selectedAssets?.length, 1);
assert.equal(context.focusedAsset?.id, 'asset-9');
assert.equal(formatContextChip(context), '已选 · Q1-report.pdf');
});
test('buildContextPrefix binds deictic references to a single checked asset', () => {
const asset = {
id: 'asset-9',
displayName: 'sales.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
filename: 'sales.xlsx',
categoryCode: 'oa',
};
const prefix = buildContextPrefix(
buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: sampleCategory,
selectedPageId: null,
pages: [],
assets: [asset],
selectedAssets: [asset],
route: '/space?category=oa',
h5ApiBase: 'http://127.0.0.1:8081',
agentSessionId: 'session-1',
}),
);
assert.match(prefix, /【界面勾选资料(硬性)】/);
assert.match(prefix, /sales\.xlsxid: asset-9/);
assert.match(prefix, /【勾选资料删除(硬性)】/);
assert.match(prefix, /mindspace_asset_delete/);
assert.match(prefix, /完整读写删改权限/);
assert.match(prefix, /指代「这个文件」「这份报告」「这个数据」时\*\*必须\*\*指该勾选资料/);
});
test('buildContextPrefix includes delete fallback when agent session id is missing', () => {
const asset = {
id: 'asset-9',
displayName: 'sales.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
};
const prefix = buildContextPrefix(
buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: sampleCategory,
selectedPageId: null,
pages: [],
assets: [asset],
selectedAssets: [asset],
route: '/space?category=oa',
h5ApiBase: 'http://127.0.0.1:8081',
}),
);
assert.match(prefix, /CURRENT_AGENT_SESSION/);
});
test('buildContextPrefix requires clarification when multiple assets are checked', () => {
const assets = [
{ id: 'asset-a', displayName: 'a.pdf', mimeType: 'application/pdf' },
{ id: 'asset-b', displayName: 'b.csv', mimeType: 'text/csv' },
];
const prefix = buildContextPrefix(
buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: sampleCategory,
selectedPageId: null,
pages: [],
assets,
selectedAssets: assets,
route: '/space?category=oa',
}),
);
assert.match(prefix, /同时勾选了 2 份资料/);
assert.match(prefix, /必须先追问/);
assert.doesNotMatch(prefix, /当前聚焦资料/);
assert.equal(formatContextChip(buildMindSpaceChatContext({
space: sampleSpace,
selectedCategory: sampleCategory,
selectedPageId: null,
pages: [],
assets,
selectedAssets: assets,
route: '/space?category=oa',
})), '已选 2 项资料');
});
test('buildSelectedAssetGreeting adapts wording to asset kind', () => {
assert.match(
buildSelectedAssetGreeting({
displayName: 'sales.xlsx',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
}),
/这份数据表/,
);
assert.match(
buildSelectedAssetGreeting({
displayName: 'review.pdf',
mimeType: 'application/pdf',
}),
/这份报告/,
);
});