309598bfa6
- 在ApiClient中新增submitFeedback、myFeedback、helpArticles、helpArticleDetail、 siteArticles、promotions、vipPackages、createVipOrder、vipOrders等方法 - 添加帮助文章和站点资讯的参数验证逻辑 - 更新测试文件添加新的API方法测试用例 - 在HTML页面中添加反馈、帮助和VIP服务相关页面的脚本引用 - 更新加入家谱页面为完整的申请流程界面 - 修改资讯详情页面为站点资讯展示页面 - 更新AxiosRequestUtil中认证处理逻辑 - 添加世系树渲染的HTML生成函数用于页面复用 - 更新文档中的API契约说明和页面规划
187 lines
5.8 KiB
JavaScript
187 lines
5.8 KiB
JavaScript
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
|
|
const FamilyHomePages = require('../public/js/family-home-pages.js');
|
|
|
|
const genealogyId = '2062179707935264769';
|
|
const root = path.resolve(__dirname, '..');
|
|
|
|
function overviewFixture(overrides) {
|
|
return Object.assign({
|
|
genealogyId,
|
|
genealogyNo: 'G20260729001',
|
|
genealogyName: '叶氏家谱',
|
|
surname: '叶',
|
|
ancestralHall: '南阳堂',
|
|
originPlace: '四川成都',
|
|
regionCode: '510100',
|
|
regionName: '成都市',
|
|
regionFullName: '四川省 成都市',
|
|
addressDetail: '高新区',
|
|
coverOssId: '2062179707935264700',
|
|
intro: '叶氏源流',
|
|
ownerUserId: '2062179707935264701',
|
|
visibility: '1',
|
|
joinMode: '1',
|
|
memberCount: 12,
|
|
personCount: 36,
|
|
status: '0',
|
|
roleType: 'owner',
|
|
canManage: true,
|
|
canEditContent: true,
|
|
memberStatus: '0',
|
|
joinTime: '2026-07-29 12:00:00'
|
|
}, overrides);
|
|
}
|
|
|
|
test('家谱主页只保留 AppGenealogyVo 的安全展示字段并匹配当前上下文', () => {
|
|
assert.deepEqual(FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture(),
|
|
genealogyId
|
|
), {
|
|
genealogyId,
|
|
genealogyNo: 'G20260729001',
|
|
genealogyName: '叶氏家谱',
|
|
surname: '叶',
|
|
ancestralHall: '南阳堂',
|
|
originPlace: '四川成都',
|
|
regionFullName: '四川省 成都市',
|
|
memberCount: 12,
|
|
personCount: 36,
|
|
canManage: true,
|
|
canEditContent: true
|
|
});
|
|
|
|
assert.equal(FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture({ genealogyId: '2062179707935264770' }),
|
|
genealogyId
|
|
), null);
|
|
assert.equal(FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture({ genealogyId: Number.MAX_SAFE_INTEGER + 1 }),
|
|
genealogyId
|
|
), null);
|
|
});
|
|
|
|
test('家谱主页拒绝停用、空名称和非法计数响应', () => {
|
|
assert.equal(FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture({ status: '1' }),
|
|
genealogyId
|
|
), null);
|
|
assert.equal(FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture({ genealogyName: '' }),
|
|
genealogyId
|
|
), null);
|
|
assert.equal(FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture({ memberCount: -1 }),
|
|
genealogyId
|
|
), null);
|
|
assert.equal(FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture({ personCount: Number.MAX_SAFE_INTEGER + 1 }),
|
|
genealogyId
|
|
), null);
|
|
});
|
|
|
|
test('家谱概览渲染转义文本并隐藏内部用户与文件编号', () => {
|
|
const overview = FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture({
|
|
genealogyName: '<叶氏家谱>',
|
|
ancestralHall: '<南阳堂>'
|
|
}),
|
|
genealogyId
|
|
);
|
|
const html = FamilyHomePages.renderFamilyOverview(overview);
|
|
|
|
assert.match(html, /<叶氏家谱>/);
|
|
assert.match(html, /<南阳堂>/);
|
|
assert.match(html, /成员 12 人/);
|
|
assert.match(html, /世系人物 36 人/);
|
|
assert.doesNotMatch(html, /ownerUserId|coverOssId|206217970793526470[01]/);
|
|
});
|
|
|
|
test('家谱管理入口只接受后端布尔能力字段', () => {
|
|
assert.equal(FamilyHomePages.shouldShowFamilyManagement(
|
|
FamilyHomePages.normalizeFamilyOverview(overviewFixture({ canManage: true }), genealogyId)
|
|
), true);
|
|
assert.equal(FamilyHomePages.shouldShowFamilyManagement(
|
|
FamilyHomePages.normalizeFamilyOverview(overviewFixture({ canManage: 'true' }), genealogyId)
|
|
), false);
|
|
});
|
|
|
|
test('家谱主页只把缺失登录态和 401 视为登录失效', () => {
|
|
assert.equal(FamilyHomePages.shouldRedirectToLogin({
|
|
getToken() { return 'token'; }
|
|
}, { status: 403 }), false);
|
|
assert.equal(FamilyHomePages.shouldRedirectToLogin({
|
|
getToken() { return 'token'; }
|
|
}, { status: 401 }), true);
|
|
assert.equal(FamilyHomePages.shouldRedirectToLogin({
|
|
getToken() { return ''; }
|
|
}), true);
|
|
});
|
|
|
|
test('家谱主页只读取当前家谱概览和世系树', async () => {
|
|
const calls = [];
|
|
const tree = [{
|
|
personId: '2062179707935264770',
|
|
genealogyId,
|
|
name: '始祖',
|
|
spouses: [],
|
|
children: []
|
|
}];
|
|
const result = await FamilyHomePages.loadFamilyHome({
|
|
genealogyOverview(receivedId) {
|
|
calls.push(['overview', receivedId]);
|
|
return Promise.resolve(overviewFixture());
|
|
},
|
|
lineageTree(receivedId) {
|
|
calls.push(['tree', receivedId]);
|
|
return Promise.resolve(tree);
|
|
}
|
|
}, genealogyId);
|
|
|
|
assert.deepEqual(calls, [
|
|
['overview', genealogyId],
|
|
['tree', genealogyId]
|
|
]);
|
|
assert.deepEqual(result.overview, FamilyHomePages.normalizeFamilyOverview(
|
|
overviewFixture(),
|
|
genealogyId
|
|
));
|
|
assert.deepEqual(result.tree, tree);
|
|
});
|
|
|
|
test('家谱主页开放真实只读概览且不制造邀请或聚合统计入口', () => {
|
|
const source = fs.readFileSync(path.join(root, 'profile-family-home.html'), 'utf8');
|
|
|
|
assert.doesNotMatch(source, /data-feature-status="pending"|pending-pages\.js/);
|
|
assert.doesNotMatch(source, /四川武胜汤氏族/);
|
|
assert.doesNotMatch(source, /href="profile-invite\.html"/);
|
|
assert.match(source, /PC 暂未开放邀请/);
|
|
assert.match(source, /data-family-home-title/);
|
|
assert.match(source, /data-family-home-overview/);
|
|
assert.match(source, /data-family-home-member-count/);
|
|
assert.match(source, /data-family-home-person-count/);
|
|
assert.match(source, /data-family-home-management/);
|
|
assert.match(source, /data-lineage-home-tree/);
|
|
assert.match(source, /src="public\/js\/lineage-pages\.js"/);
|
|
assert.match(source, /src="public\/js\/family-home-pages\.js"/);
|
|
|
|
[
|
|
'profile-feed.html',
|
|
'profile-tree.html',
|
|
'profile-article.html',
|
|
'profile-album.html',
|
|
'profile-video.html',
|
|
'profile-merit.html',
|
|
'profile-ceremony.html'
|
|
].forEach((href) => {
|
|
assert.match(
|
|
source,
|
|
new RegExp('href="' + href.replace('.', '\\.') + '"[^>]*data-genealogy-context-link'),
|
|
`${href} 缺少家谱上下文`
|
|
);
|
|
});
|
|
});
|