const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const test = require('node:test'); const root = path.resolve(__dirname, '..'); function read(relativePath) { return fs.readFileSync(path.join(root, relativePath), 'utf8'); } test('项目文档不记录测试手机号', () => { const docsRoot = path.join(root, 'docs'); const phonePattern = /(?:^|[^0-9])1[3-9][0-9]{9}(?![0-9])/; fs.readdirSync(docsRoot, { recursive: true }) .filter((entry) => entry.endsWith('.md')) .forEach((entry) => { assert.doesNotMatch( fs.readFileSync(path.join(docsRoot, entry), 'utf8'), phonePattern, '文档不应包含手机号:' + entry ); }); }); test('配置始终使用后端提供的 PC 接口地址', () => { const createConfig = require('../config.js'); const development = createConfig({ location: { hostname: 'localhost', port: '5500' } }); const production = createConfig({ location: { hostname: 'genealogy.example.test', port: '' } }); assert.equal(development.getEnvironment(), 'development'); assert.equal(production.getEnvironment(), 'production'); assert.equal(development.getApiBaseUrl(), 'https://backend-api.ddxcjp.cn/'); assert.equal(production.getApiBaseUrl(), 'https://backend-api.ddxcjp.cn/'); assert.deepEqual(production.getConfig(), { environment: 'production', apiBaseUrl: 'https://backend-api.ddxcjp.cn/', clientId: 'ced7e5f0498645c6ec642dcf450b036f', tenantId: '000000', tokenKey: 'genealogy_auth_token' }); }); test('PC 对接规划记录本轮由用户指定的 YAML 冻结契约', () => { const plan = read('docs/PC接口对接规划.md'); const handover = read('docs/交接文档.md'); assert.match(plan, /genealogy-pc-openapi\.yaml` 对接,因此该 YAML 是本轮冻结契约/); assert.match(plan, /旧 OpenAPI 文件不再作为新增功能依据/); assert.match(handover, /本文第二至第七节均为当日历史快照/); assert.match(handover, /不得据此新增、关闭或阻断任何功能/); }); test('pages do not load scripts for unavailable business APIs', () => { const removedScripts = [ 'genealogy-pages.js', 'join-apply-pages.js', 'promotion-pages.js' ]; const retainedPages = fs.readdirSync(root).filter((entry) => entry.endsWith('.html')); retainedPages.forEach((page) => { const source = read(page); removedScripts.forEach((script) => { assert.equal(source.includes('src="public/js/' + script + '"'), false, `${page} still loads ${script}`); }); }); ['profile-feed.html', 'profile-feed-edit.html'].forEach((page) => { assert.equal(read(page).includes('src="public/js/feed-pages.js"'), true, `${page} must load feed-pages.js`); }); assert.equal(read('profile-generation.html').includes('src="public/js/generation-pages.js"'), true, 'profile-generation.html must load generation-pages.js'); assert.equal(read('join-genealogy.html').includes('src="public/js/join-pages.js"'), true, 'join-genealogy.html must load join-pages.js'); assert.equal(read('profile-join-family.html').includes('src="public/js/join-pages.js"'), true, 'profile-join-family.html must load join-pages.js'); assert.equal(read('profile-join-review.html').includes('src="public/js/join-review-pages.js"'), true, 'profile-join-review.html must load join-review-pages.js'); ['profile-feedback.html', 'submit-ticket.html', 'my-tickets.html', 'ticket-detail.html'].forEach((page) => { assert.equal(read(page).includes('src="public/js/feedback-pages.js"'), true, `${page} must load feedback-pages.js`); }); assert.equal(read('help.html').includes('src="public/js/help-pages.js"'), true, 'help.html must load help-pages.js'); assert.equal(read('app.html').includes('src="public/js/app-promotion-pages.js"'), true, 'app.html must load app-promotion-pages.js'); ['news.html', 'article-detail.html'].forEach((page) => { assert.equal(read(page).includes('src="public/js/site-news-pages.js"'), true, `${page} must load site-news-pages.js`); }); assert.equal(read('profile-tree.html').includes('src="public/js/lineage-pages.js"'), true, 'profile-tree.html must load lineage-pages.js'); assert.equal(read('profile-family-admin.html').includes('src="public/js/member-admin-pages.js"'), true, 'profile-family-admin.html must load member-admin-pages.js'); assert.equal(read('profile-video.html').includes('src="public/js/video-pages.js"'), true, 'profile-video.html must load video-pages.js'); assert.equal(read('profile-gift.html').includes('src="public/js/ceremony-pages.js"'), true, 'profile-gift.html must load ceremony-pages.js'); assert.equal(read('profile-ceremony.html').includes('src="public/js/ceremony-admin-pages.js"'), true, 'profile-ceremony.html must load ceremony-admin-pages.js'); assert.equal(read('profile-relative.html').includes('src="public/js/relative-pages.js"'), true, 'profile-relative.html must load relative-pages.js'); assert.equal(read('profile-memo.html').includes('src="public/js/memo-pages.js"'), true, 'profile-memo.html must load memo-pages.js'); assert.equal(read('profile-services.html').includes('src="public/js/vip-pages.js"'), true, 'profile-services.html must load vip-pages.js'); }); test('个人中心将参考账户与家谱能力映射到 PC 工作台,未声明项不伪造操作', () => { const profile = read('profile.html'); const services = read('profile-services.html'); const share = read('profile-share.html'); const familyHome = read('profile-family-home.html'); const content = read('profile-content.html'); const apiClient = read('utils/ApiClient.js'); const verification = read('docs/验证20%记录.md'); const mapping = read('docs/个人中心能力映射.md'); ['profile-data.html', 'profile-security.html', 'profile-feedback.html', 'profile-services.html'].forEach((page) => { assert.match(profile, new RegExp('href="' + page.replace('.', '\\.') + '"')); }); assert.match(profile, /data-logout-open/); assert.match(services, /href="help\.html"/); assert.match(services, /href="about\.html"/); assert.match(services, /应用分享与收益[\s\S]+?当前不提供不可用的跳转入口/); assert.match(services, /个性化推荐[\s\S]+?暂不能设置推荐开关/); assert.doesNotMatch(services, /data-(?:share|withdraw|recommendation)-(?:form|toggle)/); assert.doesNotMatch(share, /data-feature-status="pending"|pending-pages\.js|data-(?:share|withdraw)-(?:form|toggle)/); assert.match(share, /当前 PC 未声明应用分享、奖励、收益或提现的正式接口/); assert.match(familyHome, /class="module-card is-unavailable" aria-disabled="true">\s*邀<\/span>\s*

邀请家人<\/h3>/); assert.match(familyHome, /重要证件[\s\S]+?暂不能查看或保存/); assert.match(familyHome, /删除家谱[\s\S]+?暂不能查看或执行删除操作/); assert.doesNotMatch(familyHome, /data-genealogy-delete|deleteGenealogy/); assert.match(content, /亲友往来(贺礼簿)/); assert.match(content, /备忘录(家族恩人)/); assert.doesNotMatch(apiClient, /\/pages\/(?:mine|index)\//); assert.doesNotMatch(apiClient, /deleteGenealogy/); assert.doesNotMatch(apiClient, /(?:withdrawal|shareReward|recommendationPreference|documentRecords)/); assert.match(verification, /资料提醒、家谱邀请、家谱删除、家谱排序、分享奖励、推广收益与提现/); assert.match(mapping, /`Jiapu-App` 只作为产品信息架构参考/); ['profile-data.html', 'profile-security.html', 'profile-family-home.html', 'profile-tree.html', 'profile-feed.html', 'profile-services.html'].forEach((page) => { assert.match(mapping, new RegExp('`' + page.replace('.', '\\.') + '`')); assert.equal(fs.existsSync(path.join(root, page)), true, `映射页面不存在:${page}`); }); assert.doesNotMatch(mapping, /\/pages\//); });