Files
everything-is-suitable/everything-is-suitable-uniapp/src/algorithms/__tests__/i18n-completeness.test.ts
T
zhangxiang 810081ac0a feat: 小程序端 UI 优化与纯客户端化改造
- 移除微信订阅推送功能(云函数/订阅管理页),回归纯客户端零后端架构
- 新增今日黄历默认展示卡片 TodayAlmanacCard(9 语言翻译)
- i18n 修复:启用 globalInjection 修复 $t 未注入,插值消息全部改为
  Messages Functions 适配小程序端(63 处)
- 修复底部导航切换失效与运势页数据不刷新
- 统一设计令牌(深褐主色 + 印红强调色),组件 UI 规范重构
- 图标 iconfont 化(FontAwesome 子集化,修复 Android 豆腐块)
- 新增 postcss-px2rpx 机型自适应(仅 mp-weixin 构建生效)
- 新增 5 个用户旅程 E2E 测试(31 用例)与验收报告
- 补充微信小程序项目配置(appid)
2026-08-28 07:51:02 +08:00

135 lines
4.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { describe, it, expect } from 'vitest'
import zhCN from '../../locales/zh-CN'
import zhTW from '../../locales/zh-TW'
import en from '../../locales/en'
import ja from '../../locales/ja'
import ko from '../../locales/ko'
import de from '../../locales/de'
import fr from '../../locales/fr'
import es from '../../locales/es'
import pt from '../../locales/pt'
import { i18n, I18N_OPTIONS } from '../../locales'
// 递归收集所有键路径
function collectKeys(obj: Record<string, any>, prefix = ''): string[] {
const keys: string[] = []
for (const [key, value] of Object.entries(obj)) {
const fullKey = prefix ? `${prefix}.${key}` : key
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
keys.push(...collectKeys(value, fullKey))
} else {
keys.push(fullKey)
}
}
return keys.sort()
}
// 递归收集键路径及其值,用于比较值是否为空
function collectKeysWithValues(obj: Record<string, any>, prefix = ''): Map<string, string> {
const result = new Map<string, string>()
for (const [key, value] of Object.entries(obj)) {
const fullKey = prefix ? `${prefix}.${key}` : key
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
const nested = collectKeysWithValues(value, fullKey)
nested.forEach((v, k) => result.set(k, v))
} else {
result.set(fullKey, String(value))
}
}
return result
}
const zhCNKeys = collectKeys(zhCN)
const localeModules = {
'zh-TW': zhTW,
'en': en,
'ja': ja,
'ko': ko,
'de': de,
'fr': fr,
'es': es,
'pt': pt,
} as const
describe('TC-I18N-001~009: 国际化完整性验证', () => {
for (const [locale, module] of Object.entries(localeModules)) {
const localeName = {
'zh-TW': '繁体中文',
'en': '英文',
'ja': '日文',
'ko': '韩文',
'de': '德文',
'fr': '法文',
'es': '西班牙文',
'pt': '葡萄牙文',
}[locale]
describe(`${locale} (${localeName})`, () => {
it(`应包含所有 zh-CN 中的键`, () => {
const localeKeys = collectKeys(module)
const missingKeys = zhCNKeys.filter(k => !localeKeys.includes(k))
if (missingKeys.length > 0) {
console.warn(`[${locale}] 缺少以下键:`, missingKeys)
}
expect(missingKeys).toEqual([])
})
it(`不应包含多余的键`, () => {
const localeKeys = collectKeys(module)
const extraKeys = localeKeys.filter(k => !zhCNKeys.includes(k))
if (extraKeys.length > 0) {
console.warn(`[${locale}] 存在额外键:`, extraKeys)
}
expect(extraKeys).toEqual([])
})
it(`所有键的值不应为空字符串`, () => {
const values = collectKeysWithValues(module)
const emptyValues: string[] = []
values.forEach((value, key) => {
if (value.trim() === '') emptyValues.push(key)
})
expect(emptyValues).toEqual([])
})
it(`应包含正确数量的键 (${zhCNKeys.length} 个)`, () => {
const localeKeys = collectKeys(module)
expect(localeKeys.length).toBe(zhCNKeys.length)
})
})
}
})
describe('TC-I18N-010: i18n 配置验证(防 P0 回归)', () => {
it('createI18n 应启用 globalInjection,确保小程序端模板 $t 可用', () => {
// legacy:false 模式下 globalInjection 必须为 true
// 否则小程序端模板渲染报 "TypeError: a.$t is not a function",全部页面异常
expect(I18N_OPTIONS.globalInjection).toBe(true)
})
it('createI18n 应使用 Composition APIlegacy: false', () => {
expect(I18N_OPTIONS.legacy).toBe(false)
expect((i18n as any).mode).toBe('composition')
})
it('app.use(i18n) 后应在 globalProperties 注入 $t(小程序端模板可用)', async () => {
const { createSSRApp } = await import('vue')
const app = createSSRApp({ render: () => null })
app.use(i18n)
// globalInjection:true 会将 $t/$rt 注入 Vue 全局属性,供模板/render 使用
const globalProperties = (app as any).config.globalProperties
expect(globalProperties).toBeDefined()
expect(typeof globalProperties.$t).toBe('function')
expect(typeof globalProperties.$rt).toBe('function')
})
it('Messages Functions 应正确插值(uni-app 小程序端不支持 {var} 字符串插值,必须用函数)', () => {
i18n.global.locale.value = 'zh-CN'
// 小程序端 {count} 字符串插值失效,函数形式是官方兼容方案
expect(i18n.global.t('search.daysUnit', { count: 30 })).toBe('30天')
expect(i18n.global.t('search.resultCount', { count: 20 })).toBe('共20条结果')
expect(i18n.global.t('fortune.overallScore', { score: 55 })).toBe('综合运势:55分')
expect(i18n.global.t('search.dateFormat', { year: 2026, month: 8, day: 18 })).toBe('2026年8月18日')
expect(i18n.global.t('search.historyConditionDays', { count: 2, days: 30 })).toBe('2个条件,30天范围')
})
})