返回 oh-my-ppt
style-case.test.ts
根目录 / tests / unit / styles / style-case.test.ts
1 import { describe, expect, it } from 'vitest'
2 import {
3 buildStyleCaseOptions,
4 filterByStyleCase,
5 filterByStyleKeyword,
6 parseStyleCases
7 } from '../../../src/renderer/src/lib/style-case'
8
9 describe('style case filters', () => {
10 it('splits, trims and deduplicates style cases', () => {
11 expect(parseStyleCases('技术分享、 产品发布、技术分享')).toEqual(['技术分享', '产品发布'])
12 expect(parseStyleCases('教学,培训;工作坊')).toEqual(['教学', '培训', '工作坊'])
13 })
14
15 it('counts style case options and sorts popular cases first', () => {
16 expect(
17 buildStyleCaseOptions([
18 { styleCase: '技术分享、产品发布' },
19 { styleCase: '技术分享、年度总结' },
20 { styleCase: '' }
21 ])
22 ).toEqual([
23 { label: '技术分享', count: 2 },
24 { label: '产品发布', count: 1 },
25 { label: '年度总结', count: 1 }
26 ])
27 })
28
29 it('filters styles by an exact style case tag', () => {
30 const styles = [
31 { id: 'one', styleCase: '技术分享、产品发布' },
32 { id: 'two', styleCase: '产品发布会、年度总结' }
33 ]
34
35 expect(filterByStyleCase(styles, '产品发布')).toEqual([styles[0]])
36 expect(filterByStyleCase(styles, '')).toEqual(styles)
37 })
38
39 // 风格库页(styles.tsx)的筛选契约:tag 栏来自 buildStyleCaseOptions,列表来自 filterByStyleCase。
40 // 风格库现在是「每个风格 3 个用途」格式,需保证一个风格能从它挂的任意一个 tag 筛到。
41 it('powers the styles page filter: a style is reachable from each of its tags', () => {
42 const options = [
43 { id: 'tokyo-night', label: '东京夜', styleCase: '技术分享、教学科普、产品发布' },
44 { id: 'gold-ivory', label: '金象牙', styleCase: '餐饮美食、品牌营销、艺术视觉' },
45 { id: 'minimal-white', label: '极简白', styleCase: '技术分享、商业汇报、教学科普' }
46 ]
47
48 // tag 栏聚合了所有出现过的用途,按命中数排序
49 const tags = buildStyleCaseOptions(options).map((tag) => tag.label)
50 expect(tags).toContain('技术分享')
51 expect(tags).toContain('餐饮美食')
52 expect(tags).toContain('商业汇报')
53
54 // 技术分享 同时命中 tokyo-night 和 minimal-white
55 expect(filterByStyleCase(options, '技术分享').map((o) => o.id)).toEqual([
56 'tokyo-night',
57 'minimal-white'
58 ])
59 // 餐饮美食 只命中 gold-ivory
60 expect(filterByStyleCase(options, '餐饮美食').map((o) => o.id)).toEqual(['gold-ivory'])
61 // 空标签 = 全部
62 expect(filterByStyleCase(options, '')).toEqual(options)
63 // 没有风格挂的用途 = 空列表(下拉显示「没有匹配的风格」)
64 expect(filterByStyleCase(options, '融资路演')).toEqual([])
65 })
66
67 // StyleSelect 下拉的搜索框:按名称/描述/用途模糊匹配,与用途 tag 筛选叠加(AND)。
68 it('filters styles by keyword over name, description and use cases', () => {
69 const options = [
70 { id: 'tokyo-night', label: '东京夜', description: '深色技术风', styleCase: '技术分享、教学科普' },
71 { id: 'gold-ivory', label: '金象牙', description: '奢华品牌', styleCase: '餐饮美食、品牌营销' },
72 { id: 'arctic-cool', label: '北极冷', description: '商业数据汇报', styleCase: '商业汇报' }
73 ]
74
75 // 命中名称
76 expect(filterByStyleKeyword(options, '东京').map((o) => o.id)).toEqual(['tokyo-night'])
77 // 命中描述
78 expect(filterByStyleKeyword(options, '奢华').map((o) => o.id)).toEqual(['gold-ivory'])
79 // 命中用途
80 expect(filterByStyleKeyword(options, '商业').map((o) => o.id)).toEqual(['arctic-cool'])
81 // 大小写无关 + 首尾空格容错;未命中返回空
82 expect(filterByStyleKeyword(options, ' 融资 ').map((o) => o.id)).toEqual([])
83 // 空关键词 = 全部
84 expect(filterByStyleKeyword(options, '')).toEqual(options)
85 // 与 tag 筛选叠加:先按用途「品牌营销」再按关键词「金」
86 expect(
87 filterByStyleKeyword(filterByStyleCase(options, '品牌营销'), '金').map((o) => o.id)
88 ).toEqual(['gold-ivory'])
89 })
90 })
91
91 lines TYPESCRIPT