返回 oh-my-ppt
master-handlers.test.ts
根目录 / tests / unit / ipc / master-handlers.test.ts
1 import { beforeEach, describe, expect, it, vi } from 'vitest'
2
3 const state = vi.hoisted(() => ({
4 handlers: new Map<string, (...args: unknown[]) => unknown>(),
5 getStatus: vi.fn(),
6 getLayoutLibraryStatus: vi.fn(),
7 saveLayoutLibrary: vi.fn(),
8 save: vi.fn(),
9 setPageOverride: vi.fn()
10 }))
11
12 vi.mock('electron', () => ({
13 ipcMain: {
14 handle: (name: string, handler: (...args: unknown[]) => unknown) =>
15 state.handlers.set(name, handler)
16 }
17 }))
18
19 vi.mock('../../../src/main/session/master-mutation-service', () => ({
20 getSessionMasterStatus: state.getStatus,
21 getSessionLayoutLibraryStatus: state.getLayoutLibraryStatus,
22 saveSessionLayoutLibrary: state.saveLayoutLibrary,
23 saveSessionMaster: state.save,
24 setSessionMasterPageOverride: state.setPageOverride
25 }))
26
27 describe('registerMasterHandlers', () => {
28 beforeEach(() => {
29 vi.resetModules()
30 state.handlers.clear()
31 state.getStatus.mockReset()
32 state.getLayoutLibraryStatus.mockReset()
33 state.saveLayoutLibrary.mockReset()
34 state.save.mockReset()
35 state.setPageOverride.mockReset()
36 })
37
38 it('registers typed handlers and rejects malformed structured config', async () => {
39 const { registerMasterHandlers } = await import('../../../src/main/session/master-handlers')
40 const ctx = {} as never
41 registerMasterHandlers(ctx)
42
43 const get = state.handlers.get('session:getMaster')
44 const save = state.handlers.get('session:saveMaster')
45 const setPageOverride = state.handlers.get('session:setMasterPageOverride')
46 const getLayoutLibrary = state.handlers.get('session:getLayoutLibrary')
47 const saveLayoutLibrary = state.handlers.get('session:saveLayoutLibrary')
48 expect(get).toBeTypeOf('function')
49 expect(save).toBeTypeOf('function')
50 expect(setPageOverride).toBeTypeOf('function')
51 expect(getLayoutLibrary).toBeTypeOf('function')
52 expect(saveLayoutLibrary).toBeTypeOf('function')
53 expect(state.handlers.has('session:applyMasterToPages')).toBe(false)
54
55 await get?.({}, { sessionId: ' session-1 ' })
56 expect(state.getStatus).toHaveBeenCalledWith(ctx, 'session-1')
57
58 await expect(
59 save?.({}, { sessionId: 'session-1', config: { backgroundColor: 'red' } })
60 ).rejects.toThrow('母版配置无效')
61 await save?.(
62 {},
63 {
64 sessionId: 'session-1',
65 config: {
66 backgroundColor: '#112233',
67 backgroundMode: 'override',
68 backgroundStyle: 'gradient',
69 backgroundGradient: {
70 type: 'linear',
71 angle: 135,
72 stops: [
73 { color: '#112233', position: 0 },
74 { color: '#ffffff', position: 100 }
75 ]
76 },
77 backgroundImage: null,
78 titleFontPreset: 'serif',
79 bodyFontPreset: 'sans',
80 titleFontFamily: 'Noto Sans SC',
81 bodyFontFamily: null,
82 titleFontSize: 56,
83 bodyFontSize: null,
84 elements: {
85 logoImage: null,
86 footerText: '',
87 watermarkText: '',
88 showPageNumber: true
89 }
90 }
91 }
92 )
93 expect(state.save).toHaveBeenCalledWith(
94 ctx,
95 'session-1',
96 expect.objectContaining({ backgroundMode: 'override' })
97 )
98
99 await save?.(
100 {},
101 {
102 sessionId: 'session-1',
103 config: {
104 backgroundColor: '#ffffff',
105 backgroundMode: 'override',
106 backgroundStyle: 'image',
107 backgroundGradient: {
108 type: 'linear',
109 angle: 135,
110 stops: [
111 { color: '#112233', position: 0 },
112 { color: '#ffffff', position: 100 }
113 ]
114 },
115 backgroundImage: './images/master-background.png',
116 titleFontPreset: 'inherit',
117 bodyFontPreset: 'inherit',
118 titleFontFamily: null,
119 bodyFontFamily: null,
120 titleFontSize: null,
121 bodyFontSize: null,
122 elements: {
123 logoImage: './images/logo.png',
124 footerText: 'Acme',
125 watermarkText: '',
126 showPageNumber: true
127 }
128 }
129 }
130 )
131 expect(state.save).toHaveBeenLastCalledWith(
132 ctx,
133 'session-1',
134 expect.objectContaining({
135 backgroundStyle: 'image',
136 backgroundImage: './images/master-background.png'
137 })
138 )
139
140 await expect(
141 save?.(
142 {},
143 {
144 sessionId: 'session-1',
145 config: {
146 backgroundColor: '#ffffff',
147 backgroundMode: 'override',
148 backgroundStyle: 'image',
149 backgroundGradient: {
150 type: 'linear',
151 angle: 135,
152 stops: [
153 { color: '#112233', position: 0 },
154 { color: '#ffffff', position: 100 }
155 ]
156 },
157 backgroundImage: '../outside.png',
158 titleFontPreset: 'inherit',
159 bodyFontPreset: 'inherit',
160 titleFontFamily: null,
161 bodyFontFamily: null,
162 titleFontSize: null,
163 bodyFontSize: null,
164 elements: {
165 logoImage: null,
166 footerText: '',
167 watermarkText: '',
168 showPageNumber: true
169 }
170 }
171 }
172 )
173 ).rejects.toThrow('母版配置无效')
174
175 await expect(
176 save?.(
177 {},
178 {
179 sessionId: 'session-1',
180 config: {
181 backgroundColor: '#ffffff',
182 backgroundMode: 'inherit',
183 backgroundStyle: 'solid',
184 backgroundGradient: {
185 type: 'linear',
186 angle: 135,
187 stops: [
188 { color: '#112233', position: 0 },
189 { color: '#ffffff', position: 100 }
190 ]
191 },
192 backgroundImage: null,
193 titleFontPreset: 'inherit',
194 bodyFontPreset: 'inherit',
195 titleFontFamily: null,
196 bodyFontFamily: null,
197 titleFontSize: null,
198 bodyFontSize: null,
199 elements: {
200 logoImage: null,
201 footerText: '',
202 watermarkText: '',
203 showPageNumber: true,
204 logoPosition: { x: 101, y: 8 }
205 }
206 }
207 }
208 )
209 ).rejects.toThrow('母版配置无效')
210
211 await setPageOverride?.({}, { sessionId: 'session-1', pageId: 'page-row-1', disabled: true })
212 expect(state.setPageOverride).toHaveBeenCalledWith(ctx, 'session-1', 'page-row-1', true)
213 await expect(
214 setPageOverride?.({}, { sessionId: 'session-1', pageId: '', disabled: true })
215 ).rejects.toThrow('页面母版设置无效')
216
217 await getLayoutLibrary?.({}, { sessionId: ' session-1 ' })
218 expect(state.getLayoutLibraryStatus).toHaveBeenCalledWith(ctx, 'session-1')
219 await expect(
220 saveLayoutLibrary?.({}, { sessionId: 'session-1', library: { version: 1, mappings: {} } })
221 ).rejects.toThrow('版式母版配置无效')
222 await saveLayoutLibrary?.(
223 {},
224 {
225 sessionId: 'session-1',
226 library: {
227 version: 1,
228 mappings: {
229 cover: 'cover-split',
230 'data-focus': 'data-metrics',
231 comparison: 'comparison-versus',
232 timeline: 'timeline-progress',
233 concept: 'content-editorial',
234 process: 'process-flow',
235 summary: 'summary-takeaway',
236 quote: 'quote-focus',
237 'image-focus': 'image-spotlight'
238 }
239 }
240 }
241 )
242 expect(state.saveLayoutLibrary).toHaveBeenCalledWith(
243 ctx,
244 'session-1',
245 expect.objectContaining({ mappings: expect.objectContaining({ cover: 'cover-split' }) })
246 )
247 })
248 })
249
249 lines TYPESCRIPT