const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const test = require('node:test');
const FeedbackPages = require('../public/js/feedback-pages.js');
const feedbackId = '2062179707935264769';
function read(relativePath) {
return fs.readFileSync(path.resolve(__dirname, '..', relativePath), 'utf8');
}
function feedbackFixture(overrides) {
return Object.assign({
feedbackId,
appUserId: '2062179707935264701',
appUserNickName: '叶子',
appUserPhone: '19100000000',
feedbackType: 'bug',
feedbackContent: '页面按钮无响应',
contactInfo: '19181970173',
handleStatus: '2',
handleResult: '已经修复',
handlerId: '2062179707935264702',
handleTime: '2026-07-29 13:00:00',
status: '0',
remark: '感谢反馈'
}, overrides);
}
test('反馈请求只构造 AppFeedbackBody 并裁剪文本', () => {
assert.deepEqual(FeedbackPages.buildFeedbackBody({
feedbackType: ' bug ',
feedbackContent: ' 页面按钮无响应 ',
contactInfo: ' 19181970173 ',
feedbackTitle: 'must-drop',
appUserId: 'must-drop',
handlerId: 'must-drop'
}), {
feedbackType: 'bug',
feedbackContent: '页面按钮无响应',
contactInfo: '19181970173'
});
assert.deepEqual(FeedbackPages.buildFeedbackBody({
feedbackType: '',
feedbackContent: ' 建议增加导出 ',
contactInfo: ' '
}), {
feedbackContent: '建议增加导出'
});
});
test('反馈内容必填且类型只允许后端确认的四种值', () => {
assert.equal(FeedbackPages.validateFeedbackBody({ feedbackContent: '建议增加导出' }), '');
assert.equal(FeedbackPages.validateFeedbackBody({
feedbackType: 'advice',
feedbackContent: '建议增加导出'
}), '');
assert.match(FeedbackPages.validateFeedbackBody({ feedbackContent: '' }), /反馈内容/);
assert.match(FeedbackPages.validateFeedbackBody({
feedbackType: 'suggestion',
feedbackContent: '建议增加导出'
}), /反馈类型/);
});
test('FeedbackVo 只保留用户可见字段并保持长 ID 字符串', () => {
assert.deepEqual(FeedbackPages.normalizeFeedback(feedbackFixture()), {
feedbackId,
feedbackType: 'bug',
feedbackContent: '页面按钮无响应',
contactInfo: '19181970173',
handleStatus: '2',
handleResult: '已经修复',
handleTime: '2026-07-29 13:00:00',
status: '0',
remark: '感谢反馈'
});
assert.equal(FeedbackPages.normalizeFeedback(feedbackFixture({
feedbackId: Number.MAX_SAFE_INTEGER + 1
})), null);
assert.equal(FeedbackPages.normalizeFeedback(feedbackFixture({
feedbackContent: ''
})), null);
assert.equal(FeedbackPages.normalizeFeedback(feedbackFixture({
handleStatus: '9'
})), null);
assert.equal(FeedbackPages.normalizeFeedback(feedbackFixture({
status: '9'
})), null);
});
test('反馈数组包含非法记录时整批拒绝', () => {
assert.equal(FeedbackPages.normalizeFeedbackList([feedbackFixture()]).length, 1);
assert.deepEqual(FeedbackPages.normalizeFeedbackList([
feedbackFixture(),
feedbackFixture({ feedbackId: '' })
]), []);
assert.deepEqual(FeedbackPages.normalizeFeedbackList({ rows: [feedbackFixture()] }), []);
});
test('详情只精确匹配 URL 中的反馈 ID 且不存在时不回退', () => {
const secondId = '2062179707935264770';
const list = [feedbackFixture(), feedbackFixture({ feedbackId: secondId, feedbackContent: '第二条' })];
assert.equal(FeedbackPages.getFeedbackId('?feedbackId=' + secondId), secondId);
assert.equal(FeedbackPages.getFeedbackId('?feedbackId=unsafe'), '');
assert.equal(FeedbackPages.findFeedbackById(list, secondId).feedbackContent, '第二条');
assert.equal(FeedbackPages.findFeedbackById(list, '2062179707935264999'), null);
assert.equal(
FeedbackPages.buildFeedbackDetailUrl(secondId),
'ticket-detail.html?feedbackId=2062179707935264770'
);
});
test('反馈列表和详情转义文本且不暴露内部账号信息', () => {
const item = feedbackFixture({
feedbackContent: '',
handleResult: '
',
appUserId: 'secret-user',
handlerId: 'secret-handler',
appUserPhone: 'secret-account-phone'
});
const listHtml = FeedbackPages.renderFeedbackList([item], {
detailPage: 'ticket-detail.html'
});
const detailHtml = FeedbackPages.renderFeedbackDetail(item);
assert.match(listHtml, /<script>/);
assert.match(detailHtml, /<img src=x>/);
assert.doesNotMatch(listHtml + detailHtml, /