132 lines
4.9 KiB
TypeScript
132 lines
4.9 KiB
TypeScript
|
|
import { describe, expect, it } from 'vitest'
|
|||
|
|
import zhMessages from '@/locales/langs/zh.json'
|
|||
|
|
import enMessages from '@/locales/langs/en.json'
|
|||
|
|
import {
|
|||
|
|
bindChangeKind,
|
|||
|
|
buildEmbedSnippet,
|
|||
|
|
buildSettingsPayload,
|
|||
|
|
buildSlotEditPayload,
|
|||
|
|
clampIntervalMs,
|
|||
|
|
DEFAULT_EMBED_SETTINGS,
|
|||
|
|
EMBED_ORIGIN_FALLBACK,
|
|||
|
|
embedOriginFromEnv,
|
|||
|
|
type EmbedSettingsDraft
|
|||
|
|
} from './embedSlots'
|
|||
|
|
|
|||
|
|
/** 契约形状的既有条目(编辑载荷断言基准) */
|
|||
|
|
const CURRENT_ITEM: Api.EmbedSlot.Item = {
|
|||
|
|
id: 'slot-1',
|
|||
|
|
publicToken: 'tok0000000000000000000',
|
|||
|
|
name: '官网首页悬浮位',
|
|||
|
|
enabled: true,
|
|||
|
|
albumId: null,
|
|||
|
|
albumTitle: null,
|
|||
|
|
configVersion: 1,
|
|||
|
|
settings: { ...DEFAULT_EMBED_SETTINGS },
|
|||
|
|
updatedAt: '2026-09-20T10:00:00.000Z'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
describe('buildSettingsPayload 表单 → settings 载荷', () => {
|
|||
|
|
it('缺省草稿 → 契约缺省值全集(整体提交:字段齐全)', () => {
|
|||
|
|
expect(buildSettingsPayload({ ...DEFAULT_EMBED_SETTINGS })).toEqual({
|
|||
|
|
position: 'right',
|
|||
|
|
size: 'md',
|
|||
|
|
intervalMs: 5000,
|
|||
|
|
lang: 'auto',
|
|||
|
|
collapsedDefault: false
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('intervalMs 越界/非有限值 → 夹取到契约区间(键盘输入兜底)', () => {
|
|||
|
|
expect(clampIntervalMs(1)).toBe(3000)
|
|||
|
|
expect(clampIntervalMs(2999.6)).toBe(3000)
|
|||
|
|
expect(clampIntervalMs(100000)).toBe(30000)
|
|||
|
|
expect(clampIntervalMs(Number.NaN)).toBe(5000)
|
|||
|
|
expect(clampIntervalMs(Number.POSITIVE_INFINITY)).toBe(5000)
|
|||
|
|
const draft: EmbedSettingsDraft = { ...DEFAULT_EMBED_SETTINGS, intervalMs: 2000 }
|
|||
|
|
expect(buildSettingsPayload(draft).intervalMs).toBe(3000)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('buildSlotEditPayload 编辑载荷收敛', () => {
|
|||
|
|
it('name 与 settings 均未变化 → 空载荷(不制造服务端 no-op)', () => {
|
|||
|
|
expect(
|
|||
|
|
buildSlotEditPayload(CURRENT_ITEM, CURRENT_ITEM.name, { ...DEFAULT_EMBED_SETTINGS })
|
|||
|
|
).toEqual({})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('仅 name 变化 → 只送 name;settings 变化 → 送完整 settings(整体替换)', () => {
|
|||
|
|
expect(
|
|||
|
|
buildSlotEditPayload(CURRENT_ITEM, ' 活动页左侧位 ', { ...DEFAULT_EMBED_SETTINGS })
|
|||
|
|
).toEqual({
|
|||
|
|
name: '活动页左侧位'
|
|||
|
|
})
|
|||
|
|
const changed: EmbedSettingsDraft = {
|
|||
|
|
...DEFAULT_EMBED_SETTINGS,
|
|||
|
|
position: 'left',
|
|||
|
|
size: 'sm',
|
|||
|
|
intervalMs: 8000,
|
|||
|
|
lang: 'zh-CN',
|
|||
|
|
collapsedDefault: true
|
|||
|
|
}
|
|||
|
|
expect(buildSlotEditPayload(CURRENT_ITEM, CURRENT_ITEM.name, changed)).toEqual({
|
|||
|
|
settings: changed
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('buildEmbedSnippet 嵌入代码拼接', () => {
|
|||
|
|
it('EMBED_ORIGIN + token → /embed/v1/loader.js 引用(尾斜杠容忍)', () => {
|
|||
|
|
expect(buildEmbedSnippet('https://view.example.com', 'abc123def456ghi789jklmno')).toBe(
|
|||
|
|
'<script src="https://view.example.com/embed/v1/loader.js" data-slot="abc123def456ghi789jklmno" defer></script>'
|
|||
|
|
)
|
|||
|
|
expect(buildEmbedSnippet('http://127.0.0.1:5175//', 'tok')).toBe(
|
|||
|
|
'<script src="http://127.0.0.1:5175/embed/v1/loader.js" data-slot="tok" defer></script>'
|
|||
|
|
)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('环境基址:VITE_EMBED_ORIGIN 缺省/空白兜底本机 dev', () => {
|
|||
|
|
expect(embedOriginFromEnv({})).toBe(EMBED_ORIGIN_FALLBACK)
|
|||
|
|
expect(embedOriginFromEnv({ VITE_EMBED_ORIGIN: ' ' })).toBe(EMBED_ORIGIN_FALLBACK)
|
|||
|
|
expect(embedOriginFromEnv({ VITE_EMBED_ORIGIN: ' https://cdn.example.com/ ' })).toBe(
|
|||
|
|
'https://cdn.example.com'
|
|||
|
|
)
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('bindChangeKind 绑定变更分类', () => {
|
|||
|
|
it('null→id 绑定 / id→id 换绑 / id→null 解绑 / 值不变 none', () => {
|
|||
|
|
expect(bindChangeKind(null, 'album-1')).toBe('bind')
|
|||
|
|
expect(bindChangeKind('album-1', 'album-2')).toBe('rebind')
|
|||
|
|
expect(bindChangeKind('album-1', null)).toBe('unbind')
|
|||
|
|
expect(bindChangeKind('album-1', 'album-1')).toBe('none')
|
|||
|
|
expect(bindChangeKind(null, null)).toBe('none')
|
|||
|
|
})
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
describe('绑定变更确认文案(A22:configVersion 将递增,官网无需改动)', () => {
|
|||
|
|
const zh = zhMessages.embedSlotsManage as Record<string, unknown>
|
|||
|
|
const en = enMessages.embedSlotsManage as Record<string, unknown>
|
|||
|
|
const zhBind = zh.bind as Record<string, string>
|
|||
|
|
const enBind = en.bind as Record<string, string>
|
|||
|
|
|
|||
|
|
it('zh/en 键集合一致(embedSlotsManage 子树)', () => {
|
|||
|
|
const keysOf = (obj: Record<string, unknown>, prefix = ''): string[] =>
|
|||
|
|
Object.entries(obj).flatMap(([key, value]) =>
|
|||
|
|
typeof value === 'object' && value !== null
|
|||
|
|
? keysOf(value as Record<string, unknown>, `${prefix}${key}.`)
|
|||
|
|
: [`${prefix}${key}`]
|
|||
|
|
)
|
|||
|
|
expect(keysOf(en)).toEqual(keysOf(zh))
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
it('三类确认文案均携带 A22 短语(换绑不改官网:loader 只认 token)', () => {
|
|||
|
|
for (const key of ['confirmBind', 'confirmRebind', 'confirmUnbind']) {
|
|||
|
|
expect(zhBind[key]).toContain('configVersion 将递增')
|
|||
|
|
expect(zhBind[key]).toContain('官网无需改动')
|
|||
|
|
expect(enBind[key]).toContain('configVersion will increase')
|
|||
|
|
expect(enBind[key]).toContain('no change is needed on the host site')
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
})
|