import test from 'node:test' import assert from 'node:assert/strict' import { readSource } from './helpers.ts' import { adminPages } from '../src/router/routes.ts' /** 教程管理页(工具台「立即下载教程」的后台入口):上传走 presign 直传、列表取最新、下载/删除留驻反馈。 */ test('align_tutorial_page_registered', () => { const page = adminPages.find((p) => p.path === 'records/tutorial') assert.ok(page, '路由注册表应包含 records/tutorial') assert.equal(page.menuKey, 'admin_tutorial') assert.equal(page.title, '教程管理') }) test('align_tutorial_page_wiring', () => { const page = readSource('src/pages/records/RecordsTutorialPage.vue') // 上传入口:版本号(可空)→ 选择 zip → 直传(进度条)→ 成功提示留驻弹窗。 assert.match(page, /上传教程包/, '存在上传入口按钮') assert.match(page, /uploadTutorialPackage/, '上传走教程包直传链路') assert.match(page, /label="版本号"/, '上传弹窗提供版本号输入') assert.match(page, /newVersion\.value\.trim\(\)/, '版本号去空白后随上传提交') assert.match(page, /请选择 zip 压缩包/, '未选文件时提示') assert.match(page, /仅支持 \.zip 格式/, '格式校验提示') assert.match(page, /上传成功,工具台「立即下载教程」已切换为该包/, '成功提示说明生效位置') assert.match(page, /上传完成,正在登记教程包/, '进度文案') // 列表:版本号列 + 上传时间列 + 当前生效标记 + 下载 + 删除 + 空态。 assert.match(page, /当前生效/, '最新上传的包标记当前生效') assert.match(page, /formatFileSize/, '展示包体大小') assert.match(page, /row\.version/, '展示版本号列') assert.match(page, /toggleSort\('version'\)/, '版本号表头可点击排序') assert.match(page, /toggleSort\('createdAt'\)/, '上传时间表头可点击排序') assert.match(page, /const sortKey = ref\('createdAt'\)/, '默认排序字段为上传时间') assert.match(page, /const sortAsc = ref\(false\)/, '默认降序') assert.match(page, /下载/, '操作列提供下载') assert.match(page, /]*download/, '下载链接带 download 强制下载') assert.match(page, /确认删除选中的/, '批量删除二次确认') assert.match(page, /暂无教程包记录/, '空态文案') }) test('align_tutorial_api_contract', () => { const api = readSource('src/pages/records/tutorial-api.ts') assert.match(api, /\/api\/admin\/tutorials/, '列表端点') assert.match(api, /TUTORIAL_UPLOAD_ENDPOINT = '\/api\/admin\/tutorial'/, '上传端点前缀') assert.match(api, /\$\{TUTORIAL_UPLOAD_ENDPOINT\}\/presign/, '直传预签名端点') assert.match(api, /\$\{TUTORIAL_UPLOAD_ENDPOINT\}\/confirm/, '确认端点') assert.match(api, /\$\{TUTORIAL_UPLOAD_ENDPOINT\}\/delete/, '删除端点') assert.match(api, /params: \{ file_name: fileName, version \}/, '预签名回传文件名与版本号') assert.match(api, /params: \{ object_key: objectKey, file_name: fileName, version \}/, '确认回传对象 key、文件名与版本号') assert.match(api, /withCredentials: false/, '直传实例不挂会话拦截器') }) test('align_tutorial_model_parsers', () => { const model = readSource('src/pages/records/tutorial-model.ts') assert.match(model, /r\.file_name \?\? r\.fileName/, '兼容 snake/camel 文件名') assert.match(model, /r\.file_url \?\? r\.fileUrl/, '兼容 snake/camel 下载链接') assert.match(model, /r\.file_size \?\? r\.fileSize/, '兼容 snake/camel 文件大小') assert.match(model, /version: text\(r\.version\)/, '解析版本号(缺失为空串,兼容历史行)') })