Files
2026-09-17 21:29:08 +08:00

110 lines
4.0 KiB
JavaScript
Raw Permalink 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 { beforeEach, describe, expect, it, vi } from 'vitest'
import { createTestPinia } from '../helpers/app'
import { useI18nStore } from '@/stores/i18n'
import { BUILTIN_MESSAGES } from '@/i18n/builtin-messages'
const loadDataApi = vi.fn()
vi.mock('@/services/api', () => ({
loadDataApi: (...args) => loadDataApi(...args),
}))
/** 按 s_i18n 行结构构造返回值 */
function rows(locale, entries) {
return {
data: entries.map(([b_key, b_value]) => ({ b_key, b_locale: locale, b_value, b_canuse: 1 })),
}
}
beforeEach(() => {
loadDataApi.mockReset()
createTestPinia()
const i18n = useI18nStore()
i18n.setLocale('zh-CN')
i18n.clearMessages()
})
describe('i18n store · 译文加载(s_i18n)', () => {
it('加载当前语言译文并写入运行时(t 生效)', async () => {
loadDataApi.mockResolvedValue(rows('zh-CN', [['field.x.b_name', '客户名称']]))
const i18n = useI18nStore()
await i18n.loadMessages()
expect(loadDataApi).toHaveBeenCalledTimes(1)
const [viewName, condition] = loadDataApi.mock.calls[0]
expect(viewName).toBe('s_i18n')
expect(condition).toContain("b_locale = N'zh-CN'")
expect(condition).toContain('b_canuse = 1')
expect(i18n.t('field.x.b_name', '名称')).toBe('客户名称')
})
it('英文模式:内置 UI 文案兜底,库表译文覆盖同名 key', async () => {
loadDataApi.mockResolvedValue(rows('en-US', [['common.cancel', 'Cancel!!']]))
const i18n = useI18nStore()
i18n.setLocale('en-US')
await i18n.loadMessages()
// 库里有的以库为准
expect(i18n.t('common.cancel', '取消')).toBe('Cancel!!')
// 库里没有的走前端内置英文
expect(i18n.t('common.applyQuery', '应用')).toBe(BUILTIN_MESSAGES['en-US']['common.applyQuery'])
expect(i18n.t('common.applyQuery', '应用')).toBe('Apply')
})
it('切换语言后重新加载,且缓存命中不再发请求', async () => {
loadDataApi.mockResolvedValue(rows('zh-CN', [['k', '中文']]))
const i18n = useI18nStore()
await i18n.loadMessages()
expect(loadDataApi).toHaveBeenCalledTimes(1)
// 切到英文:新语言未缓存 → 发请求
loadDataApi.mockResolvedValue(rows('en-US', [['k', 'EN']]))
i18n.setLocale('en-US')
await i18n.loadMessages('en-US')
expect(loadDataApi).toHaveBeenCalledTimes(2)
expect(i18n.t('k', 'x')).toBe('EN')
// 切回中文:命中缓存 → 不再请求,但译文要切回来
i18n.setLocale('zh-CN')
await i18n.loadMessages()
expect(loadDataApi).toHaveBeenCalledTimes(2)
expect(i18n.t('k', 'x')).toBe('中文')
})
it('并发同语言只发一个请求', async () => {
loadDataApi.mockResolvedValue(rows('zh-CN', [['k', 'v']]))
const i18n = useI18nStore()
await Promise.all([i18n.loadMessages(), i18n.loadMessages(), i18n.loadMessages()])
expect(loadDataApi).toHaveBeenCalledTimes(1)
})
it('接口失败不抛出,界面回落兜底文案', async () => {
loadDataApi.mockRejectedValue(new Error('boom'))
const i18n = useI18nStore()
await expect(i18n.loadMessages()).resolves.toEqual({})
expect(i18n.t('common.cancel', '取消')).toBe('取消')
expect(i18n.error).toBe('boom')
})
it('b_locale 走转义,单引号不会拼坏 SQL', async () => {
loadDataApi.mockResolvedValue({ data: [] })
const i18n = useI18nStore()
await i18n.loadMessages("zh'; drop table s_i18n --")
expect(loadDataApi.mock.calls[0][1]).toContain("N'zh''; drop table s_i18n --'")
})
it('请求回来时语言已被切走则不覆盖当前译文', async () => {
let resolveQuery
loadDataApi.mockReturnValue(new Promise((r) => (resolveQuery = r)))
const i18n = useI18nStore()
const pending = i18n.loadMessages()
i18n.setLocale('en-US')
resolveQuery({ data: [{ b_key: 'k', b_value: '中文值' }] })
await pending
// 当前是英文,中文译文不应写进运行时(英文下 k 无译文 → 回落 fallback)
expect(i18n.t('k', 'fallback')).toBe('fallback')
})
})