import assert from "node:assert/strict"; import { existsSync, readFileSync } from "node:fs"; import { resolve } from "node:path"; const root = resolve(import.meta.dirname, ".."); const previewUtilsPath = resolve(root, "src/utils/trainingReviewPreview.js"); assert.equal(existsSync(previewUtilsPath), true, "评阅预览上下文工具应存在"); const previewUtils = readFileSync(previewUtilsPath, "utf8"); const answerApi = readFileSync(resolve(root, "src/api/studentTrainingAnswer.js"), "utf8"); const trainingTaskApi = readFileSync(resolve(root, "src/api/trainingTask.js"), "utf8"); const requestApi = readFileSync(resolve(root, "src/utils/request.js"), "utf8"); const previewMapPath = resolve(root, "src/views/teacherEnd/taskReview/studentTaskPreviewMap.js"); const previewShellPath = resolve(root, "src/views/teacherEnd/taskReview/StudentTaskReadonlyPreview.vue"); const router = readFileSync(resolve(root, "src/router/index.js"), "utf8"); const reviewPanel = readFileSync(resolve(root, "src/views/teacherEnd/score/TaskReviewPanel.vue"), "utf8"); assert.match(previewUtils, /export function writeTrainingReviewPreview/); assert.match(previewUtils, /export function getTrainingReviewPreviewContext/); assert.match(previewUtils, /student\/project-study-time/); assert.match(answerApi, /getTrainingReviewPreviewContext\(\)/); assert.match(trainingTaskApi, /getTrainingReviewPreviewContext/); assert.match(trainingTaskApi, /preview\.taskConfig/); assert.match(trainingTaskApi, /评阅案例配置未加载/); assert.match(requestApi, /isTrainingReviewPreviewRequest/); assert.equal(existsSync(previewMapPath), true, "全部学生任务的预览映射应存在"); assert.equal(existsSync(previewShellPath), true, "学生任务只读预览容器应存在"); const previewMap = readFileSync(previewMapPath, "utf8"); const previewShell = readFileSync(previewShellPath, "utf8"); const expectedTaskKeys = [ "new-product-survey", "product-development-factors", "product-development-process", "industry-demand-analysis", "competitive-environment-analysis", "market-opportunity-selection", "business-feasibility", "target-user-profile", "consumer-behavior", "consumer-scenario", "feature-conversion", "product-value-positioning", "product-structure", "after-sales-service", "requirements-doc", "hardware-cost", "supplier-evaluation", "purchase-demand-forecast", "product-pricing", "category-optimization", "release-channel-evaluation", "promotion-effect-optimization", "product-diagnosis-iteration", "comprehensive-case-training", ]; for (const taskKey of expectedTaskKeys) assert.match(previewMap, new RegExp(`"${taskKey}"`)); assert.match(router, /path:\s*"\/task-review-preview"[\s\S]*?hidden:\s*true/); assert.match(previewShell, /@click\.capture="guardPreviewInteraction"/); assert.match(previewShell, /function guardPreviewInteraction/); assert.match(previewShell, /getAttribute\("aria-label"\)/); assert.doesNotMatch(previewShell, /:global\(html\)|:global\(body\)/, "预览壳不得改写学生页面的全局样式"); assert.doesNotMatch(previewShell, /pointer-events\s*:/, "预览壳不得用样式禁用学生页面控件"); assert.match(previewShell, /training-ai-sidebar/, "评阅预览应隐藏 AI 助学"); assert.match(previewShell, /training-ai-evaluation/, "评阅预览应隐藏 AI 助评"); assert.match(previewShell, /training-task-brief/, "评阅预览应隐藏实训背景、目标和要求"); assert.match(previewShell, /teacher-task-review-preview-height/, "预览页应向外层同步内容高度"); assert.match(previewShell, /ResizeObserver/, "预览页内容变化后应重新同步高度"); assert.match(reviewPanel, /writeTrainingReviewPreview/); assert.match(reviewPanel, /listClassTrainingTasks\(filters\.value\.classId/); assert.match(reviewPanel, /find\(\(task\) => task\.taskKey === taskKey\)/); assert.match(reviewPanel, /taskConfig:\s*effectiveTask/); assert.match(reviewPanel, /previewRequestVersion/); assert.match(reviewPanel, /studentPreviewError/); assert.match(reviewPanel, /class="task-review__student-preview-scroll"/); assert.match(reviewPanel, /]+studentPreviewSrc/); assert.match(reviewPanel, /scrolling="yes"/); assert.match(reviewPanel, /min-width:\s*1280px/, "嵌入学生页面应保留桌面端视口,窄区域改为滚动查看"); assert.match(reviewPanel, /previewFrameHeight/, "外层应使用嵌入页同步的真实高度"); assert.match(reviewPanel, /teacher-task-review-preview-height/, "外层应接收预览内容高度"); assert.match(reviewPanel, /overflow:\s*scroll/, "外层预览应始终显示滚动轨道"); assert.match(reviewPanel, /grid-template-columns: minmax\(360px, 400px\) minmax\(0, 1fr\)/); assert.match(reviewPanel, /class="task-review__student-table"/); assert.match(reviewPanel, /type="selection" width="38"/); assert.match(reviewPanel, /prop="name" label="姓名" width="74"/); assert.match(reviewPanel, /prop="username" label="学号" width="88"/); assert.match(reviewPanel, /label="AI评分" width="66"/); assert.match(reviewPanel, /label="老师评分" width="94"/); assert.doesNotMatch(reviewPanel, /task-review__student-preview\s*\{[^}]*background:/, "嵌入框不得覆盖学生页面底色"); assert.doesNotMatch(reviewPanel, /