132 lines
5.7 KiB
JavaScript
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'/);
|
|
});
|