import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; const adminRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..'); const batchPagePath = path.join(adminRoot, 'src/pages/nfc/batch/index.tsx'); const batchLocalePath = path.join(adminRoot, 'src/pages/nfc/batch/locale/index.ts'); test('1. nfc/batch 页面必须循环请求 admin/nfc/device/add', () => { assert.ok(fs.existsSync(batchPagePath), '批量制卡页面必须存在'); const content = fs.readFileSync(batchPagePath, 'utf-8'); assert.ok( content.includes('admin/nfc/device/add'), '必须请求 POST /api/admin/nfc/device/add' ); assert.ok( content.includes("from '@arco-design/web-react'"), '必须从 @arco-design/web-react 引入组件' ); assert.ok( content.includes('code === 1000') || content.includes('code ===1000'), '必须按 Cool-Admin code === 1000 判断成功' ); assert.ok(content.includes('Message.error'), '失败必须使用 Arco Message.error'); assert.ok(content.includes('unbound'), '入库 payload 必须带 status: unbound'); assert.ok(content.includes('batchNo'), '入库 payload 必须带 batchNo'); assert.ok( !content.includes('Mock.js') && !content.includes("from 'mockjs'"), '禁止用 Mock.js 假数据冒充成功' ); assert.ok(!/\bany\b/.test(content), '禁止使用 any'); }); test('2. nfc/batch 必须使用 Form + Table + copy', () => { const content = fs.readFileSync(batchPagePath, 'utf-8'); assert.ok(content.includes('Form'), '必须使用 Arco Form'); assert.ok(content.includes('Table'), '必须使用 Arco Table'); assert.ok( content.includes("from 'copy-to-clipboard'") || content.includes('copy-to-clipboard'), '必须使用 copy-to-clipboard 复制短链' ); assert.ok( content.includes('portal?uid=') || content.includes('portal?uid='), '短链必须包含 portal?uid=' ); assert.ok(content.includes('lightColors'), '颜色必须引用 lightColors token'); assert.ok(content.includes('NFCMaterial'), '必须使用 NFCMaterial 类型'); assert.ok(content.includes("'wood'"), '必须支持材质 wood'); assert.ok(content.includes("'metal'"), '必须支持材质 metal'); assert.ok(content.includes("'pvc'"), '必须支持材质 pvc'); assert.ok(!/\bPagination\b/.test(content), '禁止手写自定义分页器'); }); test('3. nfc/batch 必须有 count 上限或校验文案', () => { const pageContent = fs.readFileSync(batchPagePath, 'utf-8'); const localeContent = fs.readFileSync(batchLocalePath, 'utf-8'); const hasCountCap = pageContent.includes('200') || pageContent.includes('max={200}') || localeContent.includes('nfcBatch.validation.count'); assert.ok(hasCountCap, '必须限制 count 上限 200 或提供校验文案'); assert.ok(pageContent.includes('count'), '表单必须包含 count'); }); test('4. nfc/batch locale 必须包含 menu.nfc.batch', () => { assert.ok(fs.existsSync(batchLocalePath), '批量制卡语言包必须存在'); const content = fs.readFileSync(batchLocalePath, 'utf-8'); assert.ok(content.includes("'menu.nfc.batch'"), '页面语言包必须包含 menu.nfc.batch'); assert.ok(content.includes('nfcBatch.request.fail'), '必须包含失败提示文案'); assert.ok(!/\bany\b/.test(content), 'locale 禁止使用 any'); assert.ok( !content.includes('Mock.js') && !content.includes("from 'mockjs'"), 'locale 禁止 Mock.js' ); });