const assert = require('node:assert/strict'); const fs = require('node:fs'); const path = require('node:path'); const test = require('node:test'); const AppPromotionPages = require('../public/js/app-promotion-pages.js'); const appPage = fs.readFileSync(path.resolve(__dirname, '../app.html'), 'utf8'); const promotionId = '2062179707935264769'; function promotionFixture(overrides) { return Object.assign({ promotionId, promotionKey: 'app-download', promotionTitle: '下载移动端', promotionDesc: '随时查看家谱内容', coverOssId: '2062179707935264701', targetUrl: 'https://example.com/download', platform: 'all', sortOrder: 10, status: '0', remark: 'internal-only' }, overrides); } test('推广响应只保留 AppPromotionVo 的安全展示字段并保持长 ID 字符串', () => { assert.deepEqual(AppPromotionPages.normalizePromotion(promotionFixture()), { promotionId, promotionTitle: '下载移动端', promotionDesc: '随时查看家谱内容', targetUrl: 'https://example.com/download' }); assert.equal(AppPromotionPages.normalizePromotion(promotionFixture({ promotionId: Number.MAX_SAFE_INTEGER + 1 })), null); }); test('推广响应拒绝停用记录和空标题', () => { assert.equal(AppPromotionPages.normalizePromotion(promotionFixture({ status: '1' })), null); assert.equal(AppPromotionPages.normalizePromotion(promotionFixture({ promotionTitle: ' ' })), null); }); test('推广列表只接受直接数组且任一非法元素使整批失败', () => { assert.equal(AppPromotionPages.normalizePromotionList([promotionFixture()]).length, 1); assert.deepEqual(AppPromotionPages.normalizePromotionList([]), []); assert.deepEqual(AppPromotionPages.normalizePromotionList({ rows: [promotionFixture()] }), []); assert.deepEqual(AppPromotionPages.normalizePromotionList([ promotionFixture(), promotionFixture({ promotionId: '' }) ]), []); }); test('推广链接只允许绝对 HTTP 或 HTTPS 地址', () => { assert.equal( AppPromotionPages.normalizeTargetUrl(' https://example.com/download '), 'https://example.com/download' ); assert.equal(AppPromotionPages.normalizeTargetUrl('http://example.com'), 'http://example.com/'); ['javascript:alert(1)', 'data:text/html,test', '/download', 'download.html', ''].forEach((value) => { assert.equal(AppPromotionPages.normalizeTargetUrl(value), ''); }); }); test('推广卡片转义响应并隐藏 OSS ID、后台键、排序、状态和备注', () => { const html = AppPromotionPages.renderPromotionList([promotionFixture({ promotionTitle: '', promotionDesc: '', coverOssId: 'secret-oss', promotionKey: 'secret-key', remark: 'secret-remark' })]); assert.match(html, /<script>/); assert.match(html, /<img src=x>/); assert.match(html, /target="_blank"/); assert.match(html, /rel="noopener noreferrer"/); assert.doesNotMatch( html, / { const html = AppPromotionPages.renderPromotionList([ promotionFixture({ targetUrl: 'javascript:alert(1)' }) ]); assert.match(html, /^
)|href=|target=|rel=/); }); test('PC 推广列表只请求 SQL 字典确认的 pc 平台', async () => { const calls = []; const api = { async promotions() { calls.push(Array.from(arguments)); return [promotionFixture()]; } }; assert.equal((await AppPromotionPages.loadPromotions(api)).length, 1); assert.deepEqual(calls, [[{ platform: 'pc' }]]); await assert.rejects( AppPromotionPages.loadPromotions({ async promotions() { return { rows: [promotionFixture()] }; } }), /推广列表响应无效/ ); }); test('推广区域只把 401 视为登录失效', () => { assert.equal(AppPromotionPages.isUnauthorized({ status: 401 }), true); assert.equal(AppPromotionPages.isUnauthorized({ code: '401' }), true); assert.equal(AppPromotionPages.isUnauthorized({ status: 403 }), false); assert.equal(AppPromotionPages.isUnauthorized(new Error('网络失败')), false); }); test('推广区域提供明确的未登录和空列表状态', () => { assert.match( AppPromotionPages.renderPromotionLoginRequired(), /立即登录<\/a>/ ); assert.match(AppPromotionPages.renderPromotionList([]), /当前暂无应用推广/); }); test('应用下载页只通过页面标记和新推广模块接入,不暴露业务 ID 输入', () => { assert.match(appPage, /]*data-promotion-page/); assert.match(appPage, /data-promotion-list/); assert.match(appPage, /src="public\/js\/app-promotion-pages\.js"/); assert.doesNotMatch(appPage, /src="public\/js\/promotion-pages\.js"/); assert.doesNotMatch(appPage, /name="(?:promotionId|coverOssId)"/); });