const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const test = require('node:test'); const GenealogyEntryPages = require('../public/js/genealogy-entry-pages.js'); function read(relativePath) { return fs.readFileSync(path.resolve(__dirname, '..', relativePath), 'utf8'); } test('家谱入口只接受本地家谱业务页作为返回目标', () => { assert.equal(GenealogyEntryPages.getTargetPage('?next=profile-feed.html'), 'profile-feed.html'); assert.equal(GenealogyEntryPages.getTargetPage('?next=https%3A%2F%2Fevil.example'), 'profile-family-home.html'); }); test('家谱入口只使用 AppGenealogyVo 的真实字段并保持大整数 ID 字符串', () => { assert.deepEqual(GenealogyEntryPages.normalizeGenealogy({ genealogyId: '2062179707935264769', genealogyName: '汤氏家谱', surname: '汤', regionFullName: '四川省广安市武胜县', memberCount: 12, personCount: 34, roleType: 'owner', canManage: true, legacyId: 'forbidden' }), { genealogyId: '2062179707935264769', genealogyName: '汤氏家谱', surname: '汤', regionFullName: '四川省广安市武胜县', memberCount: 12, personCount: 34, roleType: 'owner', canManage: true }); assert.equal( GenealogyEntryPages.buildEntryUrl('2062179707935264769', 'profile-feed.html'), 'profile-feed.html?genealogyId=2062179707935264769' ); }); test('家谱创建只构造 AppGenealogyCreateBody 并省略空可选字段', () => { assert.deepEqual(GenealogyEntryPages.buildGenealogyCreateBody({ genealogyName: ' 汤氏家谱 ', surname: ' 汤 ', regionCode: '511622', ancestralHall: '', originPlace: ' 四川 ', addressDetail: '', coverOssId: '2062179707935264769', intro: ' 家谱简介 ', visibility: '1', joinMode: '1', genealogyId: 'must-drop', ownerUserId: 'must-drop' }), { genealogyName: '汤氏家谱', surname: '汤', regionCode: '511622', originPlace: '四川', coverOssId: '2062179707935264769', intro: '家谱简介', visibility: '1', joinMode: '1' }); }); test('家谱创建校验必填、枚举、上传派生 ID 和真实额度', () => { assert.equal( GenealogyEntryPages.validateGenealogyCreateBody( GenealogyEntryPages.buildGenealogyCreateBody({ surname: '汤', regionCode: '511622' }) ), '请填写谱名' ); assert.equal( GenealogyEntryPages.validateGenealogyCreateBody( GenealogyEntryPages.buildGenealogyCreateBody({ genealogyName: '汤氏家谱', regionCode: '511622' }) ), '请填写姓氏' ); assert.equal( GenealogyEntryPages.validateGenealogyCreateBody( GenealogyEntryPages.buildGenealogyCreateBody({ genealogyName: '汤氏家谱', surname: '汤' }) ), '请选择家谱所在地区' ); assert.equal( GenealogyEntryPages.validateGenealogyCreateBody( GenealogyEntryPages.buildGenealogyCreateBody({ genealogyName: '汤氏家谱', surname: '汤', regionCode: '511622', visibility: '9' }) ), '可见范围选项无效' ); assert.equal( GenealogyEntryPages.validateGenealogyCreateBody( GenealogyEntryPages.buildGenealogyCreateBody({ genealogyName: '汤氏家谱', surname: '汤', regionCode: '511622', joinMode: '9' }) ), '加入方式选项无效' ); assert.equal( GenealogyEntryPages.validateGenealogyCreateBody( GenealogyEntryPages.buildGenealogyCreateBody({ genealogyName: '汤氏家谱', surname: '汤', regionCode: '511622', coverOssId: Number.MAX_SAFE_INTEGER + 1 }) ), '封面文件无效,请重新选择' ); assert.equal(GenealogyEntryPages.canCreateGenealogy({ createRemaining: 1 }), true); assert.equal(GenealogyEntryPages.canCreateGenealogy({ createRemaining: 0 }), false); assert.equal(GenealogyEntryPages.canCreateGenealogy({ createRemaining: -1, canCreate: true }), true); assert.equal(GenealogyEntryPages.canCreateGenealogy({ createRemaining: 3, canCreate: false }), false); }); test('创建响应必须提供稳定家谱 ID、谱名和姓氏', () => { const created = GenealogyEntryPages.normalizeCreatedGenealogy({ genealogyId: '2062179707935264769', genealogyName: '汤氏家谱', surname: '汤', regionFullName: '四川省广安市武胜县', status: '0' }); assert.deepEqual(created, { genealogyId: '2062179707935264769', genealogyName: '汤氏家谱', surname: '汤', regionFullName: '四川省广安市武胜县' }); assert.equal( GenealogyEntryPages.buildCreatedGenealogyUrl(created), 'profile-family-home.html?genealogyId=2062179707935264769' ); assert.equal(GenealogyEntryPages.normalizeCreatedGenealogy({ genealogyId: Number.MAX_SAFE_INTEGER + 1, genealogyName: '汤氏家谱', surname: '汤' }), null); assert.equal(GenealogyEntryPages.normalizeCreatedGenealogy({ genealogyId: '2062179707935264769', genealogyName: '汤氏家谱' }), null); }); test('负数额度不向用户展示为可用数量', () => { const status = GenealogyEntryPages.buildStatus({ createRemaining: -1, joinRemaining: -1 }); assert.doesNotMatch(status, /-1/); assert.match(status, /创建不限/); assert.match(status, /加入不限/); }); test('我的家谱页是可用的真实列表入口', () => { const source = read('profile-families.html'); assert.doesNotMatch(source, /data-feature-status="pending"/); assert.doesNotMatch(source, /pending-pages\.js/); assert.match(source, /data-genealogy-list="mine"/); assert.match(source, /genealogy-entry-pages\.js/); }); test('创建家谱页开放真实 PC 表单且不允许手填业务 ID', () => { const source = read('profile-create-family.html'); assert.doesNotMatch(source, /data-feature-status="pending"/); assert.doesNotMatch(source, /pending-pages\.js/); assert.match(source, /data-genealogy-create-page/); assert.match(source, /data-genealogy-create-quota/); assert.match(source, /data-genealogy-create-status/); assert.match(source, /name="coverOssId"\s+type="hidden"/); assert.match(source, /data-upload-target="#familyCoverOssId"/); assert.match(source, /public\/js\/region-pages\.js/); assert.match(source, /public\/js\/md5\.js/); assert.match(source, /public\/js\/upload-pages\.js/); assert.match(source, /public\/js\/genealogy-entry-pages\.js/); assert.doesNotMatch(source, /name="(?:genealogyId|applyId|inviterUserId)"/); assert.doesNotMatch(source, /name="coverOssId"[^>]*type="text"/); }); test('加入家谱页面不允许用户手工填写 genealogyId', () => { const source = read('join-genealogy.html'); assert.doesNotMatch(source, /name="genealogyId"/); assert.doesNotMatch(source, /请输入家谱ID/); }); test('家谱业务模块运行时统一委托 ProfileUI 读取和传播上下文', () => { [ 'feed-pages.js', 'growth-pages.js', 'lineage-pages.js', 'memo-pages.js', 'relative-pages.js', 'generation-pages.js' ].forEach((file) => { const source = read('public/js/' + file); assert.match(source, /root\.ProfileUI && root\.ProfileUI\.getGenealogyId/); assert.match(source, /root\.ProfileUI && root\.ProfileUI\.syncGenealogyContextLinks/); }); }); test('个人中心和内容发布在缺少家谱上下文时统一进入家谱入口', () => { const profile = read('profile.html'); const content = read('profile-content.html'); const familyHome = read('profile-family-home.html'); const familyEntry = read('profile-families.html'); const profileCommon = read('public/js/profile-common.js'); assert.match(profile, /进入我的家谱/); assert.doesNotMatch(profile, /四川武胜汤氏族|四川达州刘氏族/); assert.match(profile, /href="profile-family-admin\.html" data-genealogy-context-link/); assert.match(content, /href="profile-feed\.html" data-feature-link="available" data-genealogy-context-link/); assert.match(familyHome, /href="profile-family-admin\.html" data-genealogy-context-link/); assert.match(familyEntry, /href="profile-family-admin\.html" data-genealogy-context-link/); assert.match(profileCommon, /function getGenealogyEntryUrl\(href\)/); assert.match(profileCommon, /genealogyId \? withGenealogyId\([^\n]+\) : getGenealogyEntryUrl\(/); });