Files
jiapu/tests/notification-pages.test.js
T
2026-08-03 16:49:38 +08:00

132 lines
5.7 KiB
JavaScript

const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const NotificationPages = require('../public/js/notification-pages.js');
function notificationFixture(overrides) {
return Object.assign({
notificationId: '2060000000000000001',
genealogyId: '2060000000000000002',
genealogyNo: 'G20260729001',
genealogyName: '叶氏家谱',
senderUserId: '2060000000000000003',
senderNickName: '叶子',
senderPhone: '19100000000',
noticeType: 'family_feed_comment',
noticeTitle: '新的评论',
noticeContent: '有人评论了家族动态',
bizType: 'family_feed_comment',
bizId: '2060000000000000004',
bizSummary: '家族圈评论',
publishTime: '2026-07-29 10:30:00',
readStatus: '0',
readTime: null,
status: '0',
remark: ''
}, overrides || {});
}
test('通知筛选只允许全部、未读和已读三种查询', () => {
assert.deepEqual(NotificationPages.buildNotificationQuery(''), {});
assert.deepEqual(NotificationPages.buildNotificationQuery('0'), { readStatus: '0' });
assert.deepEqual(NotificationPages.buildNotificationQuery('1'), { readStatus: '1' });
assert.deepEqual(NotificationPages.buildNotificationQuery('unsafe'), {});
});
test('通知列表只接受 PC 的非分页数组并规范完整 NotificationView', () => {
const record = notificationFixture();
assert.deepEqual(NotificationPages.normalizeNotifications([record]), [
NotificationPages.normalizeNotification(record)
]);
assert.deepEqual(NotificationPages.normalizeNotifications({ rows: [record] }), []);
assert.equal(NotificationPages.normalizeNotification({
...record,
notificationId: Number('2060000000000000001')
}), null);
assert.equal(NotificationPages.normalizeNotification({
...record,
readStatus: '2'
}), null);
});
test('通知可选业务 ID 接受 null 但拒绝不安全数字', () => {
assert.ok(NotificationPages.normalizeNotification(notificationFixture({
genealogyId: null,
senderUserId: null,
bizId: null
})));
assert.equal(NotificationPages.normalizeNotification(notificationFixture({
bizId: Number('2060000000000000004')
})), null);
});
test('未读数只接受非负安全整数', () => {
assert.equal(NotificationPages.getUnreadCount(3), 3);
assert.equal(NotificationPages.getUnreadCount('3'), 3);
assert.equal(NotificationPages.getUnreadCount(-1), 0);
assert.equal(NotificationPages.getUnreadCount(1.5), 0);
assert.equal(NotificationPages.getUnreadCount(Number.MAX_SAFE_INTEGER + 1), 0);
});
test('通知列表和详情转义正文且不暴露手机号或内部 ID', () => {
const record = NotificationPages.normalizeNotification(notificationFixture({
noticeTitle: '<script>alert(1)</script>',
noticeContent: '<img src=x onerror=alert(1)>通知正文'
}));
const rowHtml = NotificationPages.renderNotificationRow(record);
const detailHtml = NotificationPages.renderNotificationDetail(record);
assert.doesNotMatch(rowHtml + detailHtml, /<script>|<img/);
assert.match(rowHtml + detailHtml, /通知正文/);
assert.match(rowHtml + detailHtml, /未读/);
assert.doesNotMatch(rowHtml + detailHtml, /19100000000|>206000000000000000[1-4]</);
});
test('通知详情必须与请求的稳定通知编号一致', () => {
const record = notificationFixture();
assert.equal(NotificationPages.matchesNotification(record, '2060000000000000001'), true);
assert.equal(NotificationPages.matchesNotification(record, '2060000000000000009'), false);
assert.equal(NotificationPages.matchesNotification({}, '2060000000000000001'), false);
});
test('只有未读且正常的通知提供单条已读动作', () => {
assert.equal(NotificationPages.canMarkRead({ readStatus: '0', status: '0' }), true);
assert.equal(NotificationPages.canMarkRead({ readStatus: '1', status: '0' }), false);
assert.equal(NotificationPages.canMarkRead({ readStatus: '0', status: '1' }), false);
});
test('通知请求的 403 显式归类为无权限,而不是登录失效', () => {
assert.equal(NotificationPages.isForbidden({ status: 403 }), true);
assert.equal(NotificationPages.isForbidden({ code: '403' }), true);
assert.equal(NotificationPages.isForbidden({ status: 401 }), false);
});
test('消息页提供筛选、业务详情和单条/全部已读,不显示原始 JSON', () => {
const source = fs.readFileSync(path.join(__dirname, '..', 'profile-messages.html'), 'utf8');
assert.doesNotMatch(source, /data-feature-status="pending"|notification-raw|原始记录/);
assert.match(source, /data-notification-filter/);
assert.match(source, /data-notification-list/);
assert.match(source, /data-notification-detail/);
assert.match(source, /data-notification-status[^>]*role="status"/);
assert.match(source, /data-api-state="loading"/);
assert.match(source, /data-notification-unread-count/);
assert.match(source, /data-notification-read-all/);
assert.match(source, /src="public\/js\/notification-pages\.js"/);
assert.doesNotMatch(source, /src="public\/js\/pending-pages\.js"/);
});
test('消息中心将列表、详情与已读操作写入统一状态组件', () => {
const source = fs.readFileSync(path.join(__dirname, '..', 'public/js/notification-pages.js'), 'utf8');
assert.match(source, /root\.ProfileUI && root\.ProfileUI\.setApiState/);
assert.match(source, /setNotificationState\(container, 'empty', '当前暂无通知。'\)/);
assert.match(source, /setNotificationState\(query\('\[data-notification-detail\]'\), 'loading', '正在读取通知详情…'\)/);
assert.match(source, /updateStatus\('loading', '正在标记通知为已读…'\)/);
assert.match(source, /isForbidden\(error\) \? 'forbidden' : 'error'/);
});