18ea4f82fd
支持 Bug/需求提交、截图与聊天同款语音输入,全站反馈分页浏览与详情页,并从聊天与空间页提供入口。 Co-authored-by: Cursor <cursoragent@cursor.com>
163 lines
4.6 KiB
JavaScript
163 lines
4.6 KiB
JavaScript
import assert from 'node:assert/strict';
|
|
import test from 'node:test';
|
|
import { createFeedbackService } from './user-feedback.mjs';
|
|
|
|
function createMockPool() {
|
|
const inserts = [];
|
|
return {
|
|
inserts,
|
|
async query(sql, params) {
|
|
if (/INSERT INTO h5_user_feedback/.test(sql)) {
|
|
inserts.push(params);
|
|
return [{ affectedRows: 1 }];
|
|
}
|
|
throw new Error(`Unexpected query: ${sql}`);
|
|
},
|
|
};
|
|
}
|
|
|
|
test('submit feedback stores normalized payload', async () => {
|
|
const pool = createMockPool();
|
|
const service = createFeedbackService(pool);
|
|
const tinyPng = Buffer.from(
|
|
'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8z8BQDwAEhQGAhKmMIQAAAABJRU5ErkJggg==',
|
|
'base64',
|
|
);
|
|
|
|
const result = await service.submit('user-1', {
|
|
type: 'bug',
|
|
title: '页面无法保存',
|
|
description: '点击保存后没有反应',
|
|
contact: 'test@example.com',
|
|
images: [
|
|
{
|
|
filename: 'screen.png',
|
|
mimeType: 'image/png',
|
|
dataBase64: tinyPng.toString('base64'),
|
|
},
|
|
],
|
|
context: {
|
|
pagePath: '/space/page/abc',
|
|
userAgent: 'Mozilla/5.0',
|
|
},
|
|
});
|
|
|
|
assert.ok(result.id);
|
|
assert.equal(result.type, 'bug');
|
|
assert.equal(result.status, 'pending');
|
|
assert.equal(pool.inserts.length, 1);
|
|
assert.equal(pool.inserts[0][1], 'user-1');
|
|
assert.equal(pool.inserts[0][2], 'bug');
|
|
});
|
|
|
|
test('submit feedback rejects invalid type', async () => {
|
|
const service = createFeedbackService(createMockPool());
|
|
await assert.rejects(
|
|
() =>
|
|
service.submit('user-1', {
|
|
type: 'invalid',
|
|
title: 'x',
|
|
description: 'y',
|
|
}),
|
|
/反馈类型/,
|
|
);
|
|
});
|
|
|
|
test('submit feedback rejects empty description', async () => {
|
|
const service = createFeedbackService(createMockPool());
|
|
await assert.rejects(
|
|
() =>
|
|
service.submit('user-1', {
|
|
type: 'feature',
|
|
title: '想要导出 PDF',
|
|
description: ' ',
|
|
}),
|
|
/详细描述/,
|
|
);
|
|
});
|
|
|
|
test('listAll returns paginated board items without contact or images', async () => {
|
|
let queryCount = 0;
|
|
const pool = {
|
|
async query(sql, params) {
|
|
if (/SELECT COUNT\(\*\)/.test(sql)) {
|
|
return [[{ total: 23 }]];
|
|
}
|
|
if (/FROM h5_user_feedback f/.test(sql) && /LIMIT \? OFFSET \?/.test(sql)) {
|
|
queryCount += 1;
|
|
assert.equal(params[0], 10);
|
|
assert.equal(params[1], 10);
|
|
return [
|
|
[
|
|
{
|
|
id: 'fb-1',
|
|
user_id: 'user-2',
|
|
type: 'bug',
|
|
title: '按钮无响应',
|
|
description: '点击保存没有反应',
|
|
status: 'pending',
|
|
images_json: JSON.stringify([{ mimeType: 'image/png', dataBase64: 'abc' }]),
|
|
context_json: JSON.stringify({ pagePath: '/space' }),
|
|
created_at: 1000,
|
|
updated_at: 1000,
|
|
submitter_display_name: 'Alice',
|
|
},
|
|
],
|
|
];
|
|
}
|
|
throw new Error(`Unexpected query: ${sql}`);
|
|
},
|
|
};
|
|
|
|
const service = createFeedbackService(pool);
|
|
const result = await service.listAll({ page: 2, limit: 10 });
|
|
|
|
assert.equal(queryCount, 1);
|
|
assert.equal(result.page, 2);
|
|
assert.equal(result.pageSize, 10);
|
|
assert.equal(result.total, 23);
|
|
assert.equal(result.totalPages, 3);
|
|
assert.equal(result.items.length, 1);
|
|
assert.equal(result.items[0].submitterDisplayName, 'Alice');
|
|
assert.equal(result.items[0].imageCount, 1);
|
|
assert.equal(result.items[0].images, undefined);
|
|
assert.equal(result.items[0].contact, undefined);
|
|
});
|
|
|
|
test('getById hides contact from non-owner', async () => {
|
|
const pool = {
|
|
async query(sql, params) {
|
|
if (/WHERE f.id = \?/.test(sql)) {
|
|
assert.equal(params[0], 'fb-1');
|
|
return [
|
|
[
|
|
{
|
|
id: 'fb-1',
|
|
user_id: 'user-2',
|
|
type: 'bug',
|
|
title: '按钮无响应',
|
|
description: '详细内容',
|
|
contact: 'secret@example.com',
|
|
status: 'pending',
|
|
images_json: '[]',
|
|
context_json: '{}',
|
|
created_at: 1000,
|
|
updated_at: 1000,
|
|
submitter_display_name: 'Alice',
|
|
},
|
|
],
|
|
];
|
|
}
|
|
throw new Error(`Unexpected query: ${sql}`);
|
|
},
|
|
};
|
|
|
|
const service = createFeedbackService(pool);
|
|
const item = await service.getById('fb-1', 'viewer-1');
|
|
assert.equal(item.title, '按钮无响应');
|
|
assert.equal(item.contact, undefined);
|
|
|
|
const ownItem = await service.getById('fb-1', 'user-2');
|
|
assert.equal(ownItem.contact, 'secret@example.com');
|
|
});
|