Files
hfb_sys/frontend/src/features/listings/api/homeConfig.spec.ts
T

151 lines
5.0 KiB
TypeScript

import { describe, expect, it } from 'vitest'
import {
BANNER_HOTSPOT_DEFAULT_HOVER_SCALE,
createBannerHotspot,
hasBannerImage,
mergeHomeConfig,
normalizeHomeBanner,
} from './homeConfig'
describe('normalizeHomeBanner', () => {
it('把旧的扁平 image_url 结构升级为 PC / 移动两端共用同一张图', () => {
const banner = normalizeHomeBanner({
title: '旧结构',
eyebrow: '首页',
tone: 'green',
image_url: ' https://cdn.example/legacy.png ',
})
expect(banner.pc.image_url).toBe('https://cdn.example/legacy.png')
expect(banner.mobile.image_url).toBe('https://cdn.example/legacy.png')
expect(banner.tone).toBe('green')
expect(banner.enabled).toBe(true)
expect(banner.pc.hotspots).toEqual([])
})
it('两端分别配图时互不覆盖', () => {
const banner = normalizeHomeBanner({
title: '两端分图',
pc: { image_url: 'https://cdn.example/pc.png' },
mobile: { image_url: 'https://cdn.example/mobile.png' },
})
expect(banner.pc.image_url).toBe('https://cdn.example/pc.png')
expect(banner.mobile.image_url).toBe('https://cdn.example/mobile.png')
})
it('只配移动图时 PC 端不会误用移动图', () => {
const banner = normalizeHomeBanner({
title: '仅移动图',
mobile: { image_url: 'https://cdn.example/mobile.png' },
})
expect(banner.pc.image_url).toBe('')
expect(banner.mobile.image_url).toBe('https://cdn.example/mobile.png')
expect(hasBannerImage(banner.pc)).toBe(false)
})
it('收敛热区坐标并丢弃无效热区', () => {
const banner = normalizeHomeBanner({
title: '热区',
pc: {
image_url: '/pc.png',
hotspots: [
{
id: ' h1 ',
x: 90,
y: 95,
width: 20,
height: 20,
action: { type: 'route', target: '/x' },
},
{ id: 'zero', y: 10, width: 0, height: 10, action: { type: 'route', target: '/x' } },
],
},
})
expect(banner.pc.hotspots).toHaveLength(1)
expect(banner.pc.hotspots[0]).toMatchObject({ id: 'h1', x: 80, y: 80 })
})
it('缺失目标的跳转退化为不跳转,避免死热区', () => {
const banner = normalizeHomeBanner({
title: '死区',
pc: { image_url: '/pc.png', action: { type: 'route', target: ' ' } },
mobile: { image_url: '/m.png', action: { type: 'link', target: 'https://a.example' } },
})
expect(banner.pc.action).toEqual({ type: 'none', target: '', open_new_tab: false })
expect(banner.mobile.action).toEqual({
type: 'link',
target: 'https://a.example',
open_new_tab: false,
})
})
it('热区按钮图去空格,悬浮倍数默认与收敛', () => {
const banner = normalizeHomeBanner({
title: '按钮图',
pc: {
image_url: '/pc.png',
hotspots: [
{ id: 'a', width: 20, height: 20, image_url: ' https://cdn.example/btn.png ' },
{ id: 'b', width: 20, height: 20, hover_scale: 9 },
{ id: 'c', width: 20, height: 20, hover_scale: 0.5 },
],
},
})
expect(banner.pc.hotspots[0]?.image_url).toBe('https://cdn.example/btn.png')
expect(banner.pc.hotspots[0]?.hover_scale).toBe(BANNER_HOTSPOT_DEFAULT_HOVER_SCALE)
expect(banner.pc.hotspots[1]?.hover_scale).toBe(1.5)
expect(banner.pc.hotspots[2]?.hover_scale).toBe(1)
})
it('保留停用标记', () => {
expect(normalizeHomeBanner({ title: '停用', enabled: false }).enabled).toBe(false)
})
})
describe('mergeHomeConfig', () => {
it('过滤掉既无文案也无图片的空轮播', () => {
const config = mergeHomeConfig({
banners: [
{ title: '有效' },
{ title: ' ', pc: { image_url: 'https://cdn.example/pc.png' } },
{ title: '' },
],
})
// 只有图片没有文案的轮播要保留,全空的条目被丢弃
expect(config.banners.map(item => item.pc.image_url)).toEqual([
'',
'https://cdn.example/pc.png',
])
})
it('全部无效时回落到默认轮播', () => {
const config = mergeHomeConfig({ banners: [{ title: '' }] })
expect(config.banners.length).toBeGreaterThan(0)
expect(config.banners[0]?.pc.image_url).toBe('')
})
it('首页业务入口总开关缺省关闭,显式开启时透传', () => {
expect(mergeHomeConfig({}).home_entries_enabled).toBe(false)
expect(mergeHomeConfig({ home_entries_enabled: true }).home_entries_enabled).toBe(true)
expect(mergeHomeConfig({ home_entries_enabled: false }).home_entries_enabled).toBe(false)
})
})
describe('createBannerHotspot', () => {
it('生成带默认行为的热区,便于后台直接框选', () => {
const hotspot = createBannerHotspot({ x: 10, y: 12 })
expect(hotspot.id).toBeTruthy()
expect(hotspot.action).toEqual({ type: 'none', target: '', open_new_tab: false })
expect(hotspot.image_url).toBe('')
expect(hotspot.hover_scale).toBe(BANNER_HOTSPOT_DEFAULT_HOVER_SCALE)
expect(hotspot).toMatchObject({ x: 10, y: 12, width: 24, height: 24 })
})
})