Expand application functionality

This commit is contained in:
2026-07-20 19:23:11 +08:00 Unverified
parent 6aeed6b880
commit 5b86cdaec2
10 changed files with 3037 additions and 22 deletions
+25 -2
View File
@@ -144,6 +144,11 @@ try {
assert.ok(publicHome.data.notices.length >= 3, '公开首页应返回通知');
assert.ok(publicHome.data.exams.some(exam => exam.subjects.length > 1), '公开考试应包含多个科目');
assert.equal(publicHome.data.selfRegistrationEnabled, false, '自主注册默认应关闭');
const ckeditorAsset = await anonymous.request('/vendor/ckeditor5/ckeditor5.js');
assert.equal(ckeditorAsset.response.status, 200, '服务端应提供自托管 CKEditor 浏览器包');
assert.match(ckeditorAsset.data, /ImageInsertViaUrl/, '自托管 CKEditor 应包含图片 URL 插件');
const ckeditorStyles = await anonymous.request('/vendor/ckeditor5/ckeditor5.css');
assert.equal(ckeditorStyles.response.status, 200, '服务端应提供自托管 CKEditor 样式');
const closedRegister = await candidate.request('/api/auth/register', {
method: 'POST',
@@ -605,10 +610,28 @@ try {
const earlyDownload = await candidate.request(`/api/candidate/registrations/${futureRegistrationId}/admit-card`);
assert.equal(earlyDownload.response.status, 403, '准考证下载窗口开放前必须拒绝下载');
const publishNotice = await admin.request('/api/admin/notices', { method: 'POST', body: { title: '系统测试成绩发布通知', summary: '测试通知将同步到首页', content: '系统测试考试成绩现已发布。', category: '成绩通知', pinned: true, status: 'published' } });
const publishNotice = await admin.request('/api/admin/notices', { method: 'POST', body: {
title: '系统测试成绩发布通知',
summary: '',
content: '<h2>成绩发布</h2><p>系统测试考试成绩现已发布。<a href="https://files.example.com/results.pdf" target="_blank" onclick="alert(1)">下载说明</a></p><figure class="image"><img src="https://files.example.com/banner.png" alt="成绩发布横幅" onerror="alert(1)"><figcaption>成绩发布说明</figcaption></figure><figure class="table"><table><tbody><tr><th>科目</th><th>状态</th></tr><tr><td>语文</td><td>已发布</td></tr></tbody></table></figure><script>alert(1)</script>',
category: '成绩通知',
pinned: true,
status: 'published'
} });
assert.equal(publishNotice.response.status, 201);
assert.match(publishNotice.data.notice.contentHtml, /<h2>成绩发布<\/h2>/, '通知接口应保留 CKEditor 标题格式');
assert.match(publishNotice.data.notice.contentHtml, /href="https:\/\/files\.example\.com\/results\.pdf"/, '通知接口应保留安全超链接');
assert.match(publishNotice.data.notice.contentHtml, /rel="noopener noreferrer"/, '外部链接应带安全关系属性');
assert.match(publishNotice.data.notice.contentHtml, /<img src="https:\/\/files\.example\.com\/banner\.png" alt="成绩发布横幅" \/>/, '通知接口应保留专用服务器图片链接');
assert.match(publishNotice.data.notice.contentHtml, /<table>.*<th><\/th>.*<td><\/td>.*<\/table>/s, '');
assert.doesNotMatch(publishNotice.data.notice.contentHtml, /script|onclick|onerror/i, '通知富文本必须移除脚本和事件属性');
assert.match(publishNotice.data.notice.summary, /成绩发布/, '首页摘要留空时应从富文本正文提取纯文本');
const emptyRichNotice = await admin.request('/api/admin/notices', { method: 'POST', body: { title: '空富文本', content: '<script>alert(1)</script>', status: 'draft' } });
assert.equal(emptyRichNotice.response.status, 400, '清洗后没有正文的通知必须拒绝保存');
const refreshedHome = await anonymous.request('/api/public/home');
assert.ok(refreshedHome.data.notices.some(item => item.title === '系统测试成绩发布通知'), '管理员发布通知后首页应可见');
const publicRichNotice = refreshedHome.data.notices.find(item => item.title === '系统测试成绩发布通知');
assert.ok(publicRichNotice, '管理员发布通知后首页应可见');
assert.equal(publicRichNotice.contentHtml, publishNotice.data.notice.contentHtml, '公开接口应返回已清洗的富文本正文');
const stagedResultFile = Buffer.from(await buildWorkbook('results', [{ candidateNumber, examCode: exam.code, subjectName: exam.subjects[0].name, score: 125, grade: 'A', published: '不发布' }]));
const stagedPreview = await admin.request('/api/admin/excel/results', { method: 'POST', headers: { 'Content-Type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' }, body: stagedResultFile });