文件
OmniCard/admin/tests/tokens-and-locale.test.ts

147 行
6.1 KiB
TypeScript

import test from 'node:test';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
import { lightColors, darkColors, spacing, radius, typography, getCssVariables } from '../src/style/tokens.ts';
import { omniLocales, isRtl, getOmniText } from '../src/locale/omni.ts';
import defaultI18n from '../src/locale/index.ts';
test('1. admin/src/style/tokens.ts - 颜色 Token 校验 (Light & Dark)', () => {
// Light 模式颜色
assert.equal(lightColors.primary, '#0B5CFF');
assert.equal(lightColors.primarySoft, '#EAF1FF');
assert.equal(lightColors.ai, '#7C5CFF');
assert.equal(lightColors.bg, '#FFFFFF');
assert.equal(lightColors.surface, '#F7F8FA');
assert.equal(lightColors.border, '#E3E7ED');
assert.equal(lightColors.text, '#101828');
assert.equal(lightColors.text2, '#667085');
assert.equal(lightColors.success, '#12B76A');
assert.equal(lightColors.warning, '#F79009');
assert.equal(lightColors.error, '#F04438');
// Dark 模式颜色
assert.equal(darkColors.primary, '#4D8DFF');
assert.equal(darkColors.primarySoft, '#102A4D');
assert.equal(darkColors.ai, '#9B8CFF');
assert.equal(darkColors.bg, '#0B0D10');
assert.equal(darkColors.surface, '#12151A');
assert.equal(darkColors.border, '#262B33');
assert.equal(darkColors.text, '#F2F4F7');
assert.equal(darkColors.text2, '#98A2B3');
assert.equal(darkColors.success, '#32D583');
assert.equal(darkColors.warning, '#FDB022');
assert.equal(darkColors.error, '#F97066');
});
test('2. admin/src/style/tokens.ts - 间距、圆角与排版校验', () => {
// 间距 4, 8, 12, 16, 24, 32, 48, 64
assert.equal(spacing[4], '4px');
assert.equal(spacing[8], '8px');
assert.equal(spacing[12], '12px');
assert.equal(spacing[16], '16px');
assert.equal(spacing[24], '24px');
assert.equal(spacing[32], '32px');
assert.equal(spacing[48], '48px');
assert.equal(spacing[64], '64px');
// 圆角 button 12px, card 16px, modal 20px
assert.equal(radius.button, '12px');
assert.equal(radius.card, '16px');
assert.equal(radius.modal, '20px');
// 阴影与排版
assert.equal(typography.shadow, '0 1px 2px rgba(16,24,40,0.04)');
assert.equal(typography.lineHeight.body, 1.6);
assert.equal(typography.lineHeight.title, 1.25);
assert.equal(typography.lineHeight.arabic, 1.7);
});
test('3. admin/src/style/tokens.ts - CSS 变量生成辅助函数', () => {
const lightVars = getCssVariables('light');
assert.ok(lightVars['--omni-color-primary'] === '#0B5CFF');
assert.ok(lightVars['--omni-color-bg'] === '#FFFFFF');
assert.ok(lightVars['--omni-radius-card'] === '16px');
const darkVars = getCssVariables('dark');
assert.ok(darkVars['--omni-color-primary'] === '#4D8DFF');
assert.ok(darkVars['--omni-color-bg'] === '#0B0D10');
});
test('4. admin/src/locale/omni.ts - 多语言字典与 RTL 支持', () => {
const locales = ['zh-CN', 'en-US', 'ar-SA', 'es-ES', 'de-DE', 'fr-FR', 'ja-JP'] as const;
const requiredKeys = [
'common.confirm',
'common.cancel',
'card.save_contact',
'card.exchange',
'card.ai_chat',
'radar.online_visitor',
] as const;
for (const loc of locales) {
assert.ok(omniLocales[loc], `应当包含语言包: ${loc}`);
for (const key of requiredKeys) {
assert.ok(omniLocales[loc].messages[key], `语言 ${loc} 缺少词条 ${key}`);
}
}
// 阿拉伯语 RTL 校验
assert.equal(omniLocales['ar-SA'].dir, 'rtl');
assert.equal(isRtl('ar-SA'), true);
assert.equal(omniLocales['zh-CN'].dir, 'ltr');
assert.equal(isRtl('zh-CN'), false);
assert.equal(isRtl('en-US'), false);
assert.equal(omniLocales['es-ES'].dir, 'ltr');
assert.equal(omniLocales['de-DE'].dir, 'ltr');
assert.equal(omniLocales['fr-FR'].dir, 'ltr');
assert.equal(omniLocales['ja-JP'].dir, 'ltr');
// 获取文案辅助函数
assert.equal(getOmniText('zh-CN', 'card.save_contact'), '保存到通讯录');
assert.equal(getOmniText('en-US', 'card.save_contact'), 'Save Contact');
assert.equal(getOmniText('ar-SA', 'card.save_contact'), 'حفظ جهة الاتصال');
const jaSaveContact = getOmniText('ja-JP', 'card.save_contact');
assert.ok(jaSaveContact.length > 0);
assert.notEqual(jaSaveContact, '保存到通讯录');
// 禁止包含任何 emoji 校验
const emojiRegex = /[\u{1F300}-\u{1F9FF}\u{2600}-\u{26FF}\u{2700}-\u{27BF}]/u;
for (const loc of locales) {
for (const [key, val] of Object.entries(omniLocales[loc].messages)) {
assert.ok(!emojiRegex.test(val), `语言 ${loc} 的词条 ${key} 严禁含有 emoji: ${val}`);
}
}
});
test('5. mpuniapp/common/tokens.uts - uni-app x 跨端 Token 校验', () => {
const utsPath = path.resolve('m:/2026项目/CRMmp/mpuniapp/common/tokens.uts');
assert.ok(fs.existsSync(utsPath), 'tokens.uts 文件必须存在');
const content = fs.readFileSync(utsPath, 'utf-8');
// 必须导出 utsLightColors, utsDarkColors, utsSpacing, utsRadius, utsTypography
assert.ok(content.includes('export const utsLightColors: UtsColorTokens ='));
assert.ok(content.includes('export const utsDarkColors: UtsColorTokens ='));
assert.ok(content.includes('export const utsSpacing: UtsSpacing ='));
assert.ok(content.includes('export const utsRadius: UtsRadius ='));
assert.ok(content.includes('export const utsTypography: UtsTypography ='));
// 校验关键值 (严格遵守 AGENTS.md 权威 Design Token)
assert.ok(content.includes("'#0088FF'"));
assert.ok(content.includes("'#3894FF'"));
assert.ok(content.includes("button: '14px'"));
assert.ok(content.includes("card: '20px'"));
assert.ok(content.includes("modal: '24px'"));
assert.ok(content.includes("space16: '16px'"));
});
test('6. admin/src/locale/index.ts - 成功合并 omni 词条且支持 ar-SA', () => {
assert.ok(defaultI18n['zh-CN']['card.save_contact'] === '保存到通讯录');
assert.ok(defaultI18n['en-US']['card.save_contact'] === 'Save Contact');
assert.ok(defaultI18n['ar-SA']['card.save_contact'] === 'حفظ جهة الاتصال');
assert.ok(defaultI18n['ar-SA']['radar.online_visitor'] === 'الزوار الحاليون');
assert.equal(defaultI18n['zh-CN']['common.app_name'], 'OmniCard');
});