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 pagePath = path.join(adminRoot, 'src/pages/platform/subscribers/index.tsx'); const localePath = path.join(adminRoot, 'src/pages/platform/subscribers/locale/index.ts'); const EMOJI_RE = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u; const HARDCODED_SUCCESS = [ '会员配置已更新', '延长有效期', '会员状态已切换', ]; function readPage(): string { assert.ok(fs.existsSync(pagePath), '必须存在 platform/subscribers/index.tsx'); return fs.readFileSync(pagePath, 'utf-8'); } function readLocale(): string { assert.ok(fs.existsSync(localePath), '必须存在 platform/subscribers/locale/index.ts'); return fs.readFileSync(localePath, 'utf-8'); } test('1. R20: 必须有页面 locale,且含成功/失败词条与三语', () => { const localeContent = readLocale(); const keys = [ 'platformSubscribers.update.success', 'platformSubscribers.update.fail', 'platformSubscribers.extend.success', 'platformSubscribers.extend.fail', 'platformSubscribers.status.success', 'platformSubscribers.status.fail', ]; for (const key of keys) { assert.ok(localeContent.includes(key), `locale 必须包含: ${key}`); } assert.ok(localeContent.includes("'en-US'"), 'locale 必须含 en-US'); assert.ok(localeContent.includes("'zh-CN'"), 'locale 必须含 zh-CN'); assert.ok(localeContent.includes("'ar-SA'"), 'locale 必须含 ar-SA'); assert.ok(!EMOJI_RE.test(localeContent), '语言包严禁 emoji'); assert.ok(!/\bany\b/.test(localeContent), 'locale 禁止 any'); }); test('2. R20: 页面必须引入 locale,成功文案走 t[...]', () => { const content = readPage(); assert.ok( content.includes("from './locale'") || content.includes('from "./locale"'), '必须引入页面 locale' ); assert.ok(content.includes('useLocale(locale)'), '必须 useLocale(locale)'); const successKeys = [ 'platformSubscribers.update.success', 'platformSubscribers.extend.success', 'platformSubscribers.status.success', ]; for (const key of successKeys) { assert.ok(content.includes(key), `页面必须引用成功 locale key: ${key}`); } const failKeys = [ 'platformSubscribers.update.fail', 'platformSubscribers.extend.fail', 'platformSubscribers.status.fail', ]; for (const key of failKeys) { assert.ok(content.includes(key), `页面必须引用失败 locale key: ${key}`); } }); test('3. R20: 延期/改配置/切换状态仅在 code===1000 时 Message.success', () => { const content = readPage(); assert.ok( content.includes('/api/admin/team/platform/subscribers/update'), '操作必须走 subscribers/update' ); const successMatches = [...content.matchAll(/Message\.success\s*\(/g)]; assert.ok(successMatches.length >= 3, '改配置/延期/切状态成功时仍应 Message.success'); for (const match of successMatches) { const index = match.index ?? 0; const preceding = content.slice(Math.max(0, index - 280), index); assert.ok( /code\s*===\s*1000/.test(preceding), `Message.success 必须位于 code === 1000 分支内,附近片段: ${preceding.slice(-100)}` ); } }); test('4. R20: 失败不得报延期成功或状态已切换;catch/非1000 不得 Message.success', () => { const content = readPage(); assert.ok( content.includes('code !== 1000') || content.includes('code === 1000'), '必须按 Cool-Admin code 校验' ); // 硬编码中文成功句不得再出现在页面源码(尤其 Message.success) for (const phrase of HARDCODED_SUCCESS) { assert.ok( !content.includes(phrase), `页面禁止硬编码成功句: ${phrase}` ); } const failBlocks = content.match( /else\s*\{[\s\S]{0,220}\}|catch\s*(?:\([^)]*\))?\s*\{[\s\S]{0,220}\}/g ); if (failBlocks) { for (const block of failBlocks) { assert.ok( !block.includes('Message.success'), `失败分支内不得 Message.success: ${block.slice(0, 120)}` ); } } // 非 1000 分支须 Message.error(正分支 return 后、或 !== 1000 早退) assert.ok( /code\s*!==\s*1000[\s\S]{0,160}Message\.error/.test(content) || /if\s*\(\s*res\.data\?\.code\s*!==\s*1000[\s\S]{0,200}Message\.error/.test( content ) || (/code\s*===\s*1000[\s\S]{0,400}Message\.error/.test(content) && content.includes('Message.error')), 'code 非 1000 时必须 Message.error' ); assert.ok(content.includes('Message.error'), '失败须 Message.error'); }); test('5. R20: 禁止造假租户;操作不写演示企业名', () => { const content = readPage(); assert.ok(!content.includes('Vanguard'), '禁止硬编码演示租户名 Vanguard'); assert.ok(!content.includes('Acme Corp'), '禁止硬编码演示租户名'); assert.ok(!content.includes('演示企业'), '禁止硬编码演示企业'); assert.ok(!content.includes('Mock.js') && !content.includes("from 'mockjs'"), '禁止 Mock.js'); assert.ok(!/\bany\b/.test(content), '严禁 any'); assert.ok(!EMOJI_RE.test(content), '页面严禁 emoji'); });