import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; const MP_ROOT = path.resolve('m:/2026项目/CRMmp/mpuniapp'); const casesPath = path.join(MP_ROOT, 'pages/owner/card/cases.uvue'); const articlesPath = path.join(MP_ROOT, 'pages/owner/card/articles.uvue'); test('1. cases.uvue 和 articles.uvue 文件必须存在', () => { assert.ok(fs.existsSync(casesPath), '必须存在 cases.uvue'); assert.ok(fs.existsSync(articlesPath), '必须存在 articles.uvue'); }); test('2. 零 Emoji 违规审查 (严禁使用 Emoji 作为功能图标或排版内容)', () => { const emojiRegex = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u; const casesContent = fs.readFileSync(casesPath, 'utf-8'); const articlesContent = fs.readFileSync(articlesPath, 'utf-8'); assert.equal(emojiRegex.test(casesContent), false, 'cases.uvue 严禁含有 Emoji'); assert.equal(emojiRegex.test(articlesContent), false, 'articles.uvue 严禁含有 Emoji'); }); test('3. 底部悬浮主 CTA 栏适配移动端视口安全区 (env(safe-area-inset-bottom))', () => { const casesContent = fs.readFileSync(casesPath, 'utf-8'); const articlesContent = fs.readFileSync(articlesPath, 'utf-8'); assert.ok( casesContent.includes('env(safe-area-inset-bottom)'), 'cases.uvue 必须适配 safe-area-inset-bottom' ); assert.ok( articlesContent.includes('env(safe-area-inset-bottom)'), 'articles.uvue 必须适配 safe-area-inset-bottom' ); }); test('4. 表单输入框与多行文本框符合移动端 16px 防缩放规范', () => { const casesContent = fs.readFileSync(casesPath, 'utf-8'); const articlesContent = fs.readFileSync(articlesPath, 'utf-8'); assert.ok(casesContent.includes('font-size: 16px;'), 'cases.uvue 输入框必须使用 16px'); assert.ok(articlesContent.includes('font-size: 16px;'), 'articles.uvue 输入框必须使用 16px'); }); test('5. 核心操作按钮触控热区保证 (min-height >= 44px 或 height >= 44px)', () => { const casesContent = fs.readFileSync(casesPath, 'utf-8'); const articlesContent = fs.readFileSync(articlesPath, 'utf-8'); assert.ok(casesContent.includes('min-height: 44px;'), 'cases.uvue 按钮必须保证触控高度 >= 44px'); assert.ok(articlesContent.includes('min-height: 44px;'), 'articles.uvue 按钮必须保证触控高度 >= 44px'); }); test('6. 必须复用 components/image-uploader/image-uploader.uvue', () => { const casesContent = fs.readFileSync(casesPath, 'utf-8'); const articlesContent = fs.readFileSync(articlesPath, 'utf-8'); assert.ok( casesContent.includes("import ImageUploader from '../../../components/image-uploader/image-uploader.uvue'"), 'cases.uvue 必须复用 image-uploader 组件' ); assert.ok( articlesContent.includes("import ImageUploader from '../../../components/image-uploader/image-uploader.uvue'"), 'articles.uvue 必须复用 image-uploader 组件' ); }); test('7. pages.json 中必须注册 cases 与 articles 路由', () => { const pagesJsonPath = path.join(MP_ROOT, 'pages.json'); const pagesJson = JSON.parse(fs.readFileSync(pagesJsonPath, 'utf-8')); const routes = pagesJson.pages.map((p: any) => p.path); assert.ok(routes.includes('pages/owner/card/cases'), 'pages.json 必须注册 cases 页面'); assert.ok(routes.includes('pages/owner/card/articles'), 'pages.json 必须注册 articles 页面'); }); test('8. edit.uvue 中必须包含 cases 与 articles 的 Bento 导航入口', () => { const editPath = path.join(MP_ROOT, 'pages/owner/card/edit.uvue'); const editContent = fs.readFileSync(editPath, 'utf-8'); assert.ok(editContent.includes('handleGoCases'), 'edit.uvue 必须提供 handleGoCases'); assert.ok(editContent.includes('handleGoArticles'), 'edit.uvue 必须提供 handleGoArticles'); assert.ok(editContent.includes('pages/owner/card/cases'), 'edit.uvue 必须跳转至 cases 页面'); assert.ok(editContent.includes('pages/owner/card/articles'), 'edit.uvue 必须跳转至 articles 页面'); }); test('9. index.uvue 中必须提供快捷管理入口', () => { const indexPath = path.join(MP_ROOT, 'pages/owner/card/index.uvue'); const indexContent = fs.readFileSync(indexPath, 'utf-8'); assert.ok(indexContent.includes('handleOpenCases'), 'index.uvue 必须提供 handleOpenCases'); assert.ok(indexContent.includes('handleOpenArticles'), 'index.uvue 必须提供 handleOpenArticles'); });