import { createServer } from 'vite' import assert from 'node:assert/strict' const vite = await createServer({ server: { middlewareMode: true }, appType: 'custom', logLevel: 'error' }) try { const { editableOutlinePages, confirmedCoursewareOutline, inspectConfirmedOutline } = await vite.ssrLoadModule('/src/utils/aiCoursewareOutline.ts') const { aiCoursewareToSlides } = await vite.ssrLoadModule('/src/utils/aiCoursewareToSlides.ts') const { aiCoursewareHttpError } = await vite.ssrLoadModule('/src/utils/aiCoursewareHttpError.ts') const { teachingReviewIssues, blockingImageIssues, coursewareCompletionMessage } = await vite.ssrLoadModule('/src/utils/aiPptFeedback.ts') assert.equal(teachingReviewIssues('整课一致性复核暂未完成,请核对跨页的角色、动作、乐器与练习安排')[0].severity, 'warning') assert.equal(teachingReviewIssues('第2页:缺少听辨活动')[0].severity, 'error') assert.equal(teachingReviewIssues('第2页:建议增加同伴交流', '')[0].severity, 'warning') const review = teachingReviewIssues('第2页:建议增加同伴交流', '第3页:故事内容未完成') assert.deepEqual(review.map(issue => [issue.pageNo, issue.severity]), [[3, 'error'], [2, 'warning']]) assert.match(coursewareCompletionMessage({blockingMessage:'第3页:缺少storyScript',reviewMessage:'第2页:建议交流'}, '课堂内容尚未完成修正'), /第3页:缺少故事讲稿/) assert.match(coursewareCompletionMessage({reviewMessage:'整课一致性复核暂未完成'}, '第2页配图尚未完成;已保留生成结果'), /第2页配图尚未完成/) assert.match(coursewareCompletionMessage({reviewMessage:'第2页:缺少studentContent.example'}, '课堂内容尚未完成修正'), /第2页:缺少学生可见范例/) assert.equal(blockingImageIssues([{code:'missing_required_visual'}, {code:'resource_unavailable',message:'图片无法加载'}, {code:'missing_page_audio'}, {code:'missing_score'}]).length, 2) assert.match(aiCoursewareHttpError({ response: { status: 504, data: {} } }).message, /超时/) assert.match(aiCoursewareHttpError({ code: 'ECONNABORTED' }).message, /超时/) assert.match(aiCoursewareHttpError({ code: 'ERR_NETWORK' }).message, /网络/) assert.match(aiCoursewareHttpError({ response: { status: 401 } }).message, /登录/) assert.equal(aiCoursewareHttpError({ response: { status: 422, data: { message: '大纲复核后页数不符' } } }).message, '大纲复核后页数不符') const outline = { title: '课堂', pages: Array.from({ length: 8 }, (_, i) => ({ pageNo: i + 1, type: i ? 'practice_check' : 'cover', title: `页面${i}`, teachingBrief: '比较声音并模仿', durationMinutes: 5, bullets: ['旧正文'], studentContent: { points: ['旧正文'] }, imageKeywords: [] })) } const pages = editableOutlinePages(outline) outline.teacherRequirements = '五年级,喜欢真实乐器图片,保留合奏活动' pages[1].page.title = '听辨小挑战' pages[1].brief = ' 听两组声音,辨别强弱,再用拍手模仿 ' pages.splice(2, 0, pages.splice(1, 1)[0]) const confirmed = confirmedCoursewareOutline(outline, pages) assert.deepEqual(inspectConfirmedOutline(confirmed), []) // 无需教师填写目标与分钟数 assert.equal(confirmed.teacherRequirements, outline.teacherRequirements) assert.equal(confirmed.pages[2].title, '听辨小挑战') assert.equal(confirmed.pages[2].teachingBrief, '听两组声音,辨别强弱,再用拍手模仿') assert.deepEqual(confirmed.pages.map(p => p.pageNo), [1,2,3,4,5,6,7,8]) assert.equal(confirmed.pages[2].studentContent, undefined) assert.equal(confirmed.pages[2].durationMinutes, undefined) assert.deepEqual(confirmed.pages[2].bullets, []) assert.equal(outline.pages[1].title, '页面1') const invalid = structuredClone(confirmed) invalid.pages[1].title = '' invalid.pages[2].teachingBrief = '' assert.equal(inspectConfirmedOutline(invalid).length, 2) const legacy = editableOutlinePages({ pages: [{ title: '旧大纲', bullets: ['保留老师旧修改'], imageKeywords: [] }] }) assert.equal(legacy[0].brief, '保留老师旧修改') // 摘要不渲染进学生画面;扩写后的短句仍然显示。 const result = aiCoursewareToSlides({ ...outline, pages: [{ ...outline.pages[0], teachingBrief: '仅供教师审阅的摘要', studentContent: { points: ['听一听哪种声音更轻'] }, bullets: ['听一听哪种声音更轻'] }] }) const text = JSON.stringify(result.slides) assert.ok(text.includes('听一听哪种声音更轻')) assert.ok(!text.includes('仅供教师审阅的摘要')) console.log('PASS: editable teaching plan, no manual goals/timing, preserved order, legacy migration, separate slide content') } finally { await vite.close() }