121 行
5.1 KiB
TypeScript
121 行
5.1 KiB
TypeScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import fs from 'node:fs';
|
|
import path from 'path';
|
|
import { fileURLToPath } from 'node:url';
|
|
|
|
const adminRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const pagePath = path.join(adminRoot, 'src/pages/dashboard/workplace/index.tsx');
|
|
const localePath = path.join(adminRoot, 'src/pages/dashboard/workplace/locale/index.ts');
|
|
const routesPath = path.join(adminRoot, 'src/routes.ts');
|
|
|
|
const EMOJI_RE =
|
|
/[\u{1F300}-\u{1F5FF}\u{1F600}-\u{1F64F}\u{1F680}-\u{1F6FF}\u{1F700}-\u{1F77F}\u{1F780}-\u{1F7FF}\u{1F800}-\u{1F8FF}\u{1F900}-\u{1F9FF}\u{1FA00}-\u{1FA6F}\u{1FA70}-\u{1FAFF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u;
|
|
|
|
test('1. routes.ts 必须在 dashboard 下注册 workplace 且未被 ignore', () => {
|
|
const routesContent = fs.readFileSync(routesPath, 'utf-8');
|
|
assert.ok(
|
|
routesContent.includes("key: 'dashboard/workplace'"),
|
|
'必须注册 dashboard/workplace'
|
|
);
|
|
assert.ok(
|
|
!/key:\s*'dashboard\/workplace'[\s\S]{0,80}ignore:\s*true/.test(routesContent),
|
|
'工作台必须在侧边栏展示'
|
|
);
|
|
});
|
|
|
|
test('2. 工作台必须对接商务概览 API,并使用 PRD 指定的 Arco 组件', () => {
|
|
assert.ok(fs.existsSync(pagePath), '必须存在 workplace/index.tsx');
|
|
const content = fs.readFileSync(pagePath, 'utf-8');
|
|
|
|
assert.ok(
|
|
content.includes('/api/admin/crm/radar/workplace'),
|
|
'必须调用 /api/admin/crm/radar/workplace'
|
|
);
|
|
assert.ok(content.includes('@arco-design/web-react'), '必须使用 Arco Design');
|
|
assert.ok(content.includes('Statistic'), '必须使用 Statistic 展示 KPI');
|
|
assert.ok(content.includes('Timeline'), '必须使用 Timeline 展示雷达动态');
|
|
assert.ok(content.includes('Card'), '必须使用 Card');
|
|
assert.ok(content.includes('Grid') || content.includes('Row'), '必须使用 Grid');
|
|
assert.ok(content.includes('Button'), '必须使用 Button');
|
|
assert.ok(!content.includes("from './mock'"), '禁止继续引用 Arco Pro Demo mock');
|
|
assert.ok(!content.includes('PopularContents'), '禁止继续引用内容运营 Demo 组件');
|
|
});
|
|
|
|
test('3. 快捷入口必须覆盖新建名片、发卡、线索导出,且仅 1 个主 CTA', () => {
|
|
const content = fs.readFileSync(pagePath, 'utf-8');
|
|
assert.ok(content.includes('/card/list'), '新建名片必须跳转名片资产库');
|
|
assert.ok(content.includes('/nfc/batch'), '发卡必须跳转批量制卡');
|
|
assert.ok(
|
|
content.includes('/api/admin/crm/lead/page'),
|
|
'线索导出必须请求线索分页接口'
|
|
);
|
|
|
|
const primaryButtons = content.match(/type=["']primary["']/g) || [];
|
|
assert.equal(primaryButtons.length, 1, '一屏只允许 1 个主 CTA');
|
|
});
|
|
|
|
test('4. 必须引用设计 Token,且零 any、零 emoji', () => {
|
|
const content = fs.readFileSync(pagePath, 'utf-8');
|
|
assert.ok(
|
|
content.includes('@/style/tokens') || content.includes('tokens'),
|
|
'必须引用设计 Token'
|
|
);
|
|
assert.ok(!/\bany\b/.test(content), '严禁 any');
|
|
assert.ok(!EMOJI_RE.test(content), '严禁 emoji');
|
|
});
|
|
|
|
test('5. 工作台 i18n 必须覆盖 KPI、雷达动态与快捷入口,且含中英阿', () => {
|
|
assert.ok(fs.existsSync(localePath), '必须存在 workplace locale');
|
|
const localeContent = fs.readFileSync(localePath, 'utf-8');
|
|
assert.ok(localeContent.includes("'en-US'"));
|
|
assert.ok(localeContent.includes("'zh-CN'"));
|
|
assert.ok(localeContent.includes("'ar-SA'"));
|
|
assert.ok(localeContent.includes('workplace.kpi.taps'));
|
|
assert.ok(localeContent.includes('workplace.kpi.views'));
|
|
assert.ok(localeContent.includes('workplace.kpi.leads'));
|
|
assert.ok(localeContent.includes('workplace.kpi.conversion'));
|
|
assert.ok(localeContent.includes('workplace.radar.title'));
|
|
assert.ok(localeContent.includes('workplace.cta.newCard'));
|
|
assert.ok(localeContent.includes('workplace.shortcut.issueCard'));
|
|
assert.ok(localeContent.includes('workplace.shortcut.exportLeads'));
|
|
assert.ok(!EMOJI_RE.test(localeContent), '语言包严禁 emoji');
|
|
});
|
|
|
|
test('6. 专业出海运营官视角:包含全球雷达监控、渠道漏斗与效率工具,全屏仅 1 个主 CTA', () => {
|
|
const content = fs.readFileSync(pagePath, 'utf-8');
|
|
const localeContent = fs.readFileSync(localePath, 'utf-8');
|
|
|
|
// 1. 全球雷达大屏导航
|
|
assert.ok(
|
|
content.includes('/dashboard/radar-map'),
|
|
'必须包含全球雷达热力图入口'
|
|
);
|
|
|
|
// 2. 运营词条在中英阿三语完整定义
|
|
const requiredKeys = [
|
|
'workplace.radar.liveBadge',
|
|
'workplace.funnel.title',
|
|
'workplace.channel.title',
|
|
'workplace.shortcut.radarMap',
|
|
'workplace.shortcut.aiConfig',
|
|
];
|
|
for (const key of requiredKeys) {
|
|
assert.ok(
|
|
localeContent.includes(key),
|
|
`语言包必须包含运营官核心词条: ${key}`
|
|
);
|
|
}
|
|
|
|
// 3. 铁律:无论如何扩展,全屏主 CTA 数量必须严格为 1
|
|
const primaryButtons = content.match(/type=["']primary["']/g) || [];
|
|
assert.equal(
|
|
primaryButtons.length,
|
|
1,
|
|
'扩展后全屏依然必须严格保持唯 1 一个主 CTA (type="primary")'
|
|
);
|
|
assert.ok(!EMOJI_RE.test(localeContent), '语言包严禁 emoji');
|
|
assert.ok(!EMOJI_RE.test(content), '页面代码严禁 emoji');
|
|
});
|
|
|