返回 oh-my-ppt
model-action-button.test.ts
根目录 / tests / unit / model / model-action-button.test.ts
1 /**
2 * @vitest-environment happy-dom
3 */
4 import React, { act } from 'react'
5 import { afterEach, describe, expect, it, vi } from 'vitest'
6 import { createRoot } from 'react-dom/client'
7 import { ModelSplitButton } from '../../../src/renderer/src/components/model/ModelActionButton'
8 import type { ModelActionState } from '../../../src/renderer/src/hooks/useModelAction'
9
10 vi.mock('@renderer/i18n', () => ({ useT: () => (key: string) => key }))
11
12 vi.mock('../../../src/renderer/src/components/ui/DropdownMenu', () => ({
13 DropdownMenu: ({ children }: { children: React.ReactNode }) =>
14 React.createElement('div', null, children),
15 DropdownMenuTrigger: ({ children }: { children: React.ReactNode }) =>
16 React.createElement(React.Fragment, null, children),
17 DropdownMenuContent: ({ children }: { children: React.ReactNode }) =>
18 React.createElement('div', null, children),
19 DropdownMenuItem: ({
20 children,
21 onSelect,
22 className
23 }: {
24 children: React.ReactNode
25 onSelect?: () => void
26 className?: string
27 }) =>
28 React.createElement(
29 'button',
30 {
31 type: 'button',
32 className,
33 onClick: () => onSelect?.()
34 },
35 children
36 )
37 }))
38
39 const createModelAction = (
40 ensureModelActive: ModelActionState['ensureModelActive']
41 ): ModelActionState => ({
42 modelConfigs: [
43 {
44 id: 'model-a',
45 name: 'Model A',
46 provider: 'openai',
47 model: 'gpt-a',
48 apiKey: 'key-a',
49 baseUrl: '',
50 maxTokens: 4096,
51 disableTemperature: false,
52 thinkingParameterMode: 'auto',
53 active: true,
54 createdAt: 1,
55 updatedAt: 1
56 },
57 {
58 id: 'model-b',
59 name: 'Model B',
60 provider: 'openai',
61 model: 'gpt-b',
62 apiKey: 'key-b',
63 baseUrl: '',
64 maxTokens: 4096,
65 disableTemperature: false,
66 thinkingParameterMode: 'auto',
67 active: false,
68 createdAt: 2,
69 updatedAt: 2
70 }
71 ],
72 selectedModelConfigId: 'model-a',
73 activatingModelConfigId: null,
74 hasMultipleModelConfigs: true,
75 currentModelConfig: null,
76 ensureModelActive
77 })
78
79 describe('ModelSplitButton', () => {
80 afterEach(() => {
81 vi.restoreAllMocks()
82 document.body.innerHTML = ''
83 })
84
85 it('runs with the model chosen from its menu instead of the previously selected model', async () => {
86 const ensureModelActive = vi.fn(async (modelConfigId?: string) => modelConfigId || null)
87 const onRun = vi.fn()
88 const container = document.createElement('div')
89 document.body.appendChild(container)
90 const root = createRoot(container)
91
92 await act(async () => {
93 root.render(
94 React.createElement(ModelSplitButton, {
95 modelAction: createModelAction(ensureModelActive),
96 label: 'Continue',
97 onRun
98 })
99 )
100 })
101
102 try {
103 const modelBButton = Array.from(container.querySelectorAll('button')).find((button) =>
104 button.textContent?.includes('Model B')
105 ) as HTMLButtonElement | undefined
106 const continueButton = Array.from(container.querySelectorAll('button')).find(
107 (button) => button.textContent?.trim() === 'Continue'
108 ) as HTMLButtonElement | undefined
109
110 expect(modelBButton).toBeTruthy()
111 expect(continueButton).toBeTruthy()
112
113 await act(async () => {
114 modelBButton!.click()
115 })
116
117 expect(ensureModelActive).not.toHaveBeenCalled()
118
119 await act(async () => {
120 continueButton!.click()
121 await Promise.resolve()
122 })
123
124 expect(ensureModelActive).toHaveBeenCalledTimes(1)
125 expect(ensureModelActive).toHaveBeenCalledWith('model-b')
126 expect(onRun).toHaveBeenCalledTimes(1)
127 expect(onRun).toHaveBeenCalledWith('model-b')
128 } finally {
129 await act(async () => root.unmount())
130 container.remove()
131 }
132 })
133 })
134
134 lines TYPESCRIPT