补齐平台订阅、外部登录、CRM 同步与用户端页面;Nginx 转发用户端接口,登录页使用 Google 与 Apple 标志。 Co-authored-by: Cursor <cursoragent@cursor.com>
243 行
9.5 KiB
TypeScript
243 行
9.5 KiB
TypeScript
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';
|
|
import defaultI18n from '../src/locale/index.ts';
|
|
|
|
const adminRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
|
|
const brandKitPagePath = path.join(
|
|
adminRoot,
|
|
'src/pages/team/brand-kit/index.tsx'
|
|
);
|
|
const brandKitLocalePath = path.join(
|
|
adminRoot,
|
|
'src/pages/team/brand-kit/locale/index.ts'
|
|
);
|
|
|
|
function readPage(): string {
|
|
assert.ok(fs.existsSync(brandKitPagePath), 'Brand Kit 页面必须存在');
|
|
return fs.readFileSync(brandKitPagePath, 'utf-8');
|
|
}
|
|
|
|
test('1. team/brand-kit 必须请求 brand/page 与 add 或 update,并使用 Arco Form', () => {
|
|
const content = readPage();
|
|
|
|
assert.ok(
|
|
content.includes('admin/team/brand/page'),
|
|
'必须请求 POST /api/admin/team/brand/page'
|
|
);
|
|
assert.ok(
|
|
content.includes('admin/team/brand/add') ||
|
|
content.includes('admin/team/brand/update'),
|
|
'必须请求 POST /api/admin/team/brand/add 或 update'
|
|
);
|
|
assert.ok(content.includes('Form'), '必须使用 Arco Form');
|
|
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('Mock.js') && !content.includes("from 'mockjs'"),
|
|
'禁止用 Mock.js 假数据冒充成功'
|
|
);
|
|
assert.ok(!/\bany\b/.test(content), '禁止使用 any');
|
|
});
|
|
|
|
test('2. team/brand-kit 必须拉取 size=1、保存按 id 分流,并预览 token 主色', () => {
|
|
const content = readPage();
|
|
|
|
assert.ok(content.includes("from '@/types/omni'"), '必须复用 admin/src/types/omni.ts');
|
|
assert.ok(content.includes('BrandKit'), '必须使用 BrandKit 类型');
|
|
assert.ok(content.includes('TenantProfile'), '必须使用 TenantProfile 类型');
|
|
assert.ok(
|
|
content.includes('lightColors') || content.includes('#0B5CFF'),
|
|
'必须引用 lightColors.primary 或默认 #0B5CFF'
|
|
);
|
|
assert.ok(content.includes('lightColors.primary') || content.includes('#0B5CFF'));
|
|
|
|
assert.ok(content.includes('companyName'), '表单必须包含 companyName');
|
|
assert.ok(content.includes('logoUrl'), '表单必须包含 logoUrl');
|
|
assert.ok(content.includes('primaryColor'), '表单必须包含 primaryColor');
|
|
assert.ok(
|
|
content.includes('size: 1') || content.includes('size:1'),
|
|
'page 必须 size=1 取当前租户品牌'
|
|
);
|
|
assert.ok(
|
|
content.includes('admin/team/brand/add'),
|
|
'无 id 保存必须请求 add'
|
|
);
|
|
assert.ok(
|
|
content.includes('admin/team/brand/update'),
|
|
'有 id 保存必须请求 update'
|
|
);
|
|
assert.ok(content.includes('type="primary"'), '必须有一个主 CTA');
|
|
assert.ok(
|
|
!content.includes('#000') && !content.includes('#000000'),
|
|
'禁止纯黑'
|
|
);
|
|
assert.ok(
|
|
!content.includes('Mock.js') && !content.includes("from 'mockjs'"),
|
|
'禁止用 Mock.js'
|
|
);
|
|
assert.ok(!/\bany\b/.test(content), '禁止使用 any');
|
|
});
|
|
|
|
test('3. team/brand-kit locale 必须包含 menu.team.brandKit 与中英文案', () => {
|
|
assert.ok(fs.existsSync(brandKitLocalePath), 'Brand Kit 语言包必须存在');
|
|
const content = fs.readFileSync(brandKitLocalePath, 'utf-8');
|
|
|
|
assert.ok(
|
|
content.includes("'menu.team.brandKit'"),
|
|
'页面语言包必须包含 menu.team.brandKit'
|
|
);
|
|
assert.ok(content.includes("'en-US'"), '必须包含英文案');
|
|
assert.ok(content.includes("'zh-CN'"), '必须包含中文案');
|
|
assert.ok(content.includes('teamBrandKit.request.fail'), '必须包含失败提示文案');
|
|
assert.ok(content.includes('teamBrandKit.form.save'), '必须包含保存主 CTA 文案');
|
|
assert.ok(content.includes('companyName'), '必须包含 companyName 文案');
|
|
assert.ok(content.includes('logoUrl'), '必须包含 logoUrl 文案');
|
|
assert.ok(content.includes('primaryColor'), '必须包含 primaryColor 文案');
|
|
assert.ok(!/\bany\b/.test(content), 'locale 禁止使用 any');
|
|
assert.ok(
|
|
!/[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u.test(content),
|
|
'locale 禁止使用 emoji'
|
|
);
|
|
|
|
assert.equal(defaultI18n['en-US']['menu.team.brandKit'], 'Brand Kit');
|
|
assert.equal(defaultI18n['zh-CN']['menu.team.brandKit'], '品牌规范');
|
|
});
|
|
|
|
test('4. 版权页脚:无真实保存字段时不得提交/假装已生效,须禁用或提示暂不可保存', () => {
|
|
const content = readPage();
|
|
const localeContent = fs.readFileSync(brandKitLocalePath, 'utf-8');
|
|
|
|
// 当前 brand 保存契约仅有 companyName / logoUrl / primaryColor,无 footer|copyright|legalNotice
|
|
const savePayloadMatch = content.match(
|
|
/interface BrandSavePayload\s*\{[\s\S]*?\}/
|
|
);
|
|
const savePayloadBlock = savePayloadMatch?.[0] ?? '';
|
|
const hasFooterInSavePayload = /\b(footer|copyright|legalNotice)\b/.test(
|
|
savePayloadBlock
|
|
);
|
|
const hasFooterAssignedToBody =
|
|
/body\.(footer|copyright|legalNotice)\s*=/.test(content) ||
|
|
/(?:const|let)\s+body\s*[:=][^{]*\{[\s\S]*?\b(footer|copyright|legalNotice)\s*:/.test(
|
|
content
|
|
);
|
|
|
|
assert.ok(
|
|
localeContent.includes("'teamBrandKit.footer.unavailable'") ||
|
|
localeContent.includes("'teamBrandKit.copyright.unavailable'") ||
|
|
localeContent.includes("'teamBrandKit.legalNotice.unavailable'"),
|
|
'locale 必须包含页脚/版权暂不可保存词条'
|
|
);
|
|
assert.ok(
|
|
localeContent.includes("'teamBrandKit.form.footer'") ||
|
|
localeContent.includes("'teamBrandKit.form.copyright'") ||
|
|
localeContent.includes("'teamBrandKit.form.legalNotice'"),
|
|
'locale 必须包含页脚输入标签文案'
|
|
);
|
|
assert.ok(
|
|
content.includes("t['teamBrandKit.footer.unavailable']") ||
|
|
content.includes("t['teamBrandKit.copyright.unavailable']") ||
|
|
content.includes("t['teamBrandKit.legalNotice.unavailable']") ||
|
|
content.includes('teamBrandKit.footer.unavailable') ||
|
|
content.includes('teamBrandKit.copyright.unavailable') ||
|
|
content.includes('teamBrandKit.legalNotice.unavailable'),
|
|
'页面必须引用页脚暂不可保存文案'
|
|
);
|
|
|
|
// 禁止把写死版权句当成已保存生效内容
|
|
assert.ok(
|
|
!/©\s*20\d{2}|All rights reserved|版权所有|保留所有权利/i.test(content),
|
|
'禁止页面写死版权声明并当成已生效'
|
|
);
|
|
assert.ok(
|
|
!hasFooterInSavePayload && !hasFooterAssignedToBody,
|
|
'无后端页脚字段时禁止把 footer/copyright/legalNotice 写入保存 payload'
|
|
);
|
|
|
|
// 空值不得提交假声明(保存体不得硬编码版权句子)
|
|
assert.ok(
|
|
!/BrandSavePayload[\s\S]{0,400}(©|All rights reserved|版权所有)/i.test(
|
|
content
|
|
),
|
|
'空值不得向保存接口提交假版权声明'
|
|
);
|
|
|
|
const hasFooterSaveField = hasFooterInSavePayload || hasFooterAssignedToBody;
|
|
if (!hasFooterSaveField) {
|
|
const footerInputShown =
|
|
/field=["'](footer|copyright|legalNotice)["']/.test(content) ||
|
|
content.includes('teamBrandKit.form.footer') ||
|
|
content.includes('teamBrandKit.form.copyright') ||
|
|
content.includes('teamBrandKit.form.legalNotice');
|
|
assert.ok(footerInputShown, '可展示页脚输入,但不得当作已保存字段');
|
|
|
|
const footerDisabled =
|
|
/disabled[\s\S]{0,160}(footer|copyright|legalNotice)|(footer|copyright|legalNotice)[\s\S]{0,200}disabled/i.test(
|
|
content
|
|
) ||
|
|
content.includes('disabled={!FOOTER_SAVE_SUPPORTED}') ||
|
|
content.includes('disabled={true}');
|
|
const footerUnavailableHint =
|
|
content.includes("t['teamBrandKit.footer.unavailable']") ||
|
|
content.includes("t['teamBrandKit.copyright.unavailable']") ||
|
|
content.includes("t['teamBrandKit.legalNotice.unavailable']") ||
|
|
content.includes('teamBrandKit.footer.unavailable') ||
|
|
content.includes('teamBrandKit.copyright.unavailable') ||
|
|
content.includes('teamBrandKit.legalNotice.unavailable');
|
|
assert.ok(
|
|
footerDisabled || footerUnavailableHint,
|
|
'无页脚保存字段时必须禁用输入/保存或提示暂不可保存'
|
|
);
|
|
}
|
|
|
|
// Message.success 仅允许出现在 code === 1000 成功分支
|
|
const successMatches = [
|
|
...content.matchAll(/Message\.success\s*\([^)]*\)/g),
|
|
];
|
|
for (const match of successMatches) {
|
|
const idx = match.index ?? 0;
|
|
const windowBefore = content.slice(Math.max(0, idx - 280), idx);
|
|
assert.ok(
|
|
/code\s*===\s*1000/.test(windowBefore),
|
|
'保存失败不得 Message.success;成功提示必须在 code === 1000 分支内'
|
|
);
|
|
}
|
|
});
|
|
|
|
test('5. locale 页脚词条须说明暂不可保存,且中英文齐全', () => {
|
|
const localeContent = fs.readFileSync(brandKitLocalePath, 'utf-8');
|
|
const unavailableKey =
|
|
localeContent.includes("'teamBrandKit.footer.unavailable'")
|
|
? 'teamBrandKit.footer.unavailable'
|
|
: localeContent.includes("'teamBrandKit.copyright.unavailable'")
|
|
? 'teamBrandKit.copyright.unavailable'
|
|
: 'teamBrandKit.legalNotice.unavailable';
|
|
|
|
assert.ok(
|
|
localeContent.includes(`'${unavailableKey}'`),
|
|
'必须包含暂不可保存词条'
|
|
);
|
|
assert.ok(
|
|
/暂不可保存|暂不可用|not available|cannot be saved|unavailable/i.test(
|
|
localeContent
|
|
),
|
|
'暂不可保存文案须明确不可落库'
|
|
);
|
|
assert.ok(!/\bany\b/.test(localeContent), 'locale 禁止使用 any');
|
|
assert.ok(
|
|
!/[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u.test(
|
|
localeContent
|
|
),
|
|
'locale 禁止使用 emoji'
|
|
);
|
|
});
|