Skip to content

Commit e16aa22

Browse files
committed
test: 更新测试以适配新的服务导入路径
- 测试文件从 core/services 导入 digitalHumanEngine 和 asrService - 移除 digitalHumanStore.initSession,改为多 store 协调 - 更新 voiceCommands 从 core/services 导入服务 - useAdvancedDigitalHumanController 协调 initChatSession 和 resetSystemState
1 parent b0ffd91 commit e16aa22

10 files changed

Lines changed: 303 additions & 82 deletions

src/__tests__/dialogueOrchestrator.test.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ vi.mock('../core/dialogue/chatTransport', () => {
3434
});
3535

3636
// Mock digitalHumanEngine to avoid store dependency
37-
vi.mock('../core/avatar', () => ({
37+
vi.mock('../core/services', () => ({
3838
digitalHumanEngine: {
3939
setBehavior: vi.fn(),
4040
setEmotion: vi.fn(),
@@ -201,19 +201,19 @@ describe('handleDialogueResponse', () => {
201201
});
202202

203203
it('sets emotion on digitalHumanEngine', async () => {
204-
const { digitalHumanEngine } = await import('../core/avatar');
204+
const { digitalHumanEngine } = await import('../core/services');
205205
await handleDialogueResponse({ replyText: 'hi', emotion: 'happy', action: 'idle' });
206206
expect(digitalHumanEngine.setEmotion).toHaveBeenCalledWith('happy');
207207
});
208208

209209
it('plays animation for non-idle action', async () => {
210-
const { digitalHumanEngine } = await import('../core/avatar');
210+
const { digitalHumanEngine } = await import('../core/services');
211211
await handleDialogueResponse({ replyText: 'hi', emotion: 'neutral', action: 'wave' });
212212
expect(digitalHumanEngine.playAnimation).toHaveBeenCalledWith('wave');
213213
});
214214

215215
it('does not play animation for idle action', async () => {
216-
const { digitalHumanEngine } = await import('../core/avatar');
216+
const { digitalHumanEngine } = await import('../core/services');
217217
(digitalHumanEngine.playAnimation as ReturnType<typeof vi.fn>).mockClear();
218218
await handleDialogueResponse({ replyText: 'hi', emotion: 'neutral', action: 'idle' });
219219
expect(digitalHumanEngine.playAnimation).not.toHaveBeenCalled();

src/__tests__/digitalHuman.test.tsx

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { useDigitalHumanStore } from '../store/digitalHumanStore';
66
import { useChatSessionStore } from '../store/chatSessionStore';
77
import { useSystemStore } from '../store/systemStore';
88
import { TTSService, ASRService } from '../core/audio/audioService';
9+
import { processVoiceCommand } from '../core/audio/voiceCommandProcessor';
910
import { handleDialogueResponse } from '../core/dialogue/dialogueOrchestrator';
1011
import React from 'react';
1112

@@ -515,11 +516,17 @@ describe('ASRService', () => {
515516
expect(asrService).toBeDefined();
516517
});
517518

518-
it('handles cancel mute voice command correctly', () => {
519-
asrService = new ASRService({}, mockState, localTts);
519+
it('voice command processor handles cancel mute correctly', () => {
520+
// Voice command processing is now in voiceCommandProcessor.ts
521+
// This test verifies the integration via processVoiceCommand
520522
useDigitalHumanStore.getState().setMuted(true);
521523

522-
const matched = (asrService as any).tryLocalCommand('取消静音');
524+
const matched = processVoiceCommand('取消静音', {
525+
play: () => useDigitalHumanStore.getState().play(),
526+
pause: () => useDigitalHumanStore.getState().pause(),
527+
reset: () => useDigitalHumanStore.getState().reset(),
528+
setMuted: (m: boolean) => useDigitalHumanStore.getState().setMuted(m),
529+
});
523530

524531
expect(matched).toBe(true);
525532
expect(useDigitalHumanStore.getState().isMuted).toBe(false);
@@ -621,7 +628,9 @@ describe('Error throttle and session lifecycle', () => {
621628

622629
const oldSessionId = useChatSessionStore.getState().sessionId;
623630

624-
useDigitalHumanStore.getState().initSession();
631+
// Coordinate multi-store initialization (previously in digitalHumanStore.initSession)
632+
useChatSessionStore.getState().initSession();
633+
useSystemStore.getState().resetSystemState();
625634

626635
const state = useSystemStore.getState();
627636
const sessionState = useChatSessionStore.getState();

src/__tests__/useAdvancedDigitalHumanController.test.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,13 @@ vi.mock('../hooks/useConnectionHealth', () => ({
4040
}),
4141
}));
4242

43-
vi.mock('../core/audio', () => ({
43+
vi.mock('../core/services', () => ({
4444
asrService: {
4545
start: mocks.asrStartMock,
4646
stop: mocks.asrStopMock,
4747
performGreeting: mocks.asrPerformGreetingMock,
4848
performDance: mocks.asrPerformDanceMock,
4949
},
50-
}));
51-
52-
vi.mock('../core/avatar', () => ({
5350
digitalHumanEngine: {
5451
dispose: mocks.digitalHumanDisposeMock,
5552
pause: mocks.digitalHumanPauseMock,
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
import { describe, it, expect, vi } from 'vitest';
2+
import {
3+
parseVoiceCommand,
4+
executeVoiceCommandAction,
5+
processVoiceCommand,
6+
type VoiceCommandAction,
7+
} from '../core/audio/voiceCommandProcessor';
8+
9+
describe('parseVoiceCommand', () => {
10+
// System control commands
11+
it('matches "播放" (play)', () => {
12+
const result = parseVoiceCommand('播放');
13+
expect(result.matched).toBe(true);
14+
expect(result.action).toEqual({ type: 'play' });
15+
});
16+
17+
it('matches "开始" (start) as play', () => {
18+
const result = parseVoiceCommand('开始');
19+
expect(result.matched).toBe(true);
20+
expect(result.action).toEqual({ type: 'play' });
21+
});
22+
23+
it('matches "暂停" (pause)', () => {
24+
const result = parseVoiceCommand('暂停');
25+
expect(result.matched).toBe(true);
26+
expect(result.action).toEqual({ type: 'pause' });
27+
});
28+
29+
it('matches "停止" (stop) as pause', () => {
30+
const result = parseVoiceCommand('停止');
31+
expect(result.matched).toBe(true);
32+
expect(result.action).toEqual({ type: 'pause' });
33+
});
34+
35+
it('matches "重置" (reset)', () => {
36+
const result = parseVoiceCommand('重置');
37+
expect(result.matched).toBe(true);
38+
expect(result.action).toEqual({ type: 'reset' });
39+
});
40+
41+
it('matches "复位" as reset', () => {
42+
const result = parseVoiceCommand('复位');
43+
expect(result.matched).toBe(true);
44+
expect(result.action).toEqual({ type: 'reset' });
45+
});
46+
47+
it('matches "静音" (mute)', () => {
48+
const result = parseVoiceCommand('静音');
49+
expect(result.matched).toBe(true);
50+
expect(result.action).toEqual({ type: 'mute' });
51+
});
52+
53+
it('matches "取消静音" (unmute)', () => {
54+
const result = parseVoiceCommand('取消静音');
55+
expect(result.matched).toBe(true);
56+
expect(result.action).toEqual({ type: 'unmute' });
57+
});
58+
59+
// Quick action commands
60+
it('matches "打招呼" (greeting)', () => {
61+
const result = parseVoiceCommand('打招呼');
62+
expect(result.matched).toBe(true);
63+
expect(result.action).toEqual({ type: 'greeting' });
64+
});
65+
66+
it('matches "问好" as greeting', () => {
67+
const result = parseVoiceCommand('问好');
68+
expect(result.matched).toBe(true);
69+
expect(result.action).toEqual({ type: 'greeting' });
70+
});
71+
72+
it('matches "跳舞" (dance)', () => {
73+
const result = parseVoiceCommand('跳舞');
74+
expect(result.matched).toBe(true);
75+
expect(result.action).toEqual({ type: 'dance' });
76+
});
77+
78+
it('matches "点头" (nod)', () => {
79+
const result = parseVoiceCommand('点头');
80+
expect(result.matched).toBe(true);
81+
expect(result.action).toEqual({ type: 'nod' });
82+
});
83+
84+
it('matches "摇头" (shakeHead)', () => {
85+
const result = parseVoiceCommand('摇头');
86+
expect(result.matched).toBe(true);
87+
expect(result.action).toEqual({ type: 'shakeHead' });
88+
});
89+
90+
// Non-matching commands
91+
it('returns false for unknown command', () => {
92+
const result = parseVoiceCommand('你好世界');
93+
expect(result.matched).toBe(false);
94+
expect(result.action).toBeUndefined();
95+
});
96+
97+
it('returns false for empty string', () => {
98+
const result = parseVoiceCommand('');
99+
expect(result.matched).toBe(false);
100+
});
101+
102+
it('returns false for whitespace only', () => {
103+
const result = parseVoiceCommand(' ');
104+
expect(result.matched).toBe(false);
105+
});
106+
107+
// Case and whitespace handling
108+
it('handles leading/trailing whitespace', () => {
109+
const result = parseVoiceCommand(' 播放 ');
110+
expect(result.matched).toBe(true);
111+
expect(result.action).toEqual({ type: 'play' });
112+
});
113+
114+
it('handles uppercase (though Chinese has no case)', () => {
115+
// This is mainly for documentation - Chinese doesn't have case
116+
const result = parseVoiceCommand('播放');
117+
expect(result.matched).toBe(true);
118+
});
119+
120+
// Partial matches should not work
121+
it('does not match partial command "播放音乐"', () => {
122+
const result = parseVoiceCommand('播放音乐');
123+
expect(result.matched).toBe(false);
124+
});
125+
126+
it('does not match partial command "请播放"', () => {
127+
const result = parseVoiceCommand('请播放');
128+
expect(result.matched).toBe(false);
129+
});
130+
});
131+
132+
describe('executeVoiceCommandAction', () => {
133+
it('executes play action', () => {
134+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
135+
const action: VoiceCommandAction = { type: 'play' };
136+
const result = executeVoiceCommandAction(action, state);
137+
expect(result).toBe(true);
138+
expect(state.play).toHaveBeenCalledTimes(1);
139+
});
140+
141+
it('executes pause action', () => {
142+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
143+
const action: VoiceCommandAction = { type: 'pause' };
144+
const result = executeVoiceCommandAction(action, state);
145+
expect(result).toBe(true);
146+
expect(state.pause).toHaveBeenCalledTimes(1);
147+
});
148+
149+
it('executes reset action', () => {
150+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
151+
const action: VoiceCommandAction = { type: 'reset' };
152+
const result = executeVoiceCommandAction(action, state);
153+
expect(result).toBe(true);
154+
expect(state.reset).toHaveBeenCalledTimes(1);
155+
});
156+
157+
it('executes mute action', () => {
158+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
159+
const action: VoiceCommandAction = { type: 'mute' };
160+
const result = executeVoiceCommandAction(action, state);
161+
expect(result).toBe(true);
162+
expect(state.setMuted).toHaveBeenCalledWith(true);
163+
});
164+
165+
it('executes unmute action', () => {
166+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
167+
const action: VoiceCommandAction = { type: 'unmute' };
168+
const result = executeVoiceCommandAction(action, state);
169+
expect(result).toBe(true);
170+
expect(state.setMuted).toHaveBeenCalledWith(false);
171+
});
172+
173+
it('executes greeting action with preset handler', () => {
174+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
175+
const greeting = vi.fn();
176+
const action: VoiceCommandAction = { type: 'greeting' };
177+
const result = executeVoiceCommandAction(action, state, { greeting });
178+
expect(result).toBe(true);
179+
expect(greeting).toHaveBeenCalledTimes(1);
180+
});
181+
182+
it('returns false for greeting action without preset handler', () => {
183+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
184+
const action: VoiceCommandAction = { type: 'greeting' };
185+
const result = executeVoiceCommandAction(action, state);
186+
expect(result).toBe(false);
187+
});
188+
189+
it('executes dance action with preset handler', () => {
190+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
191+
const dance = vi.fn();
192+
const action: VoiceCommandAction = { type: 'dance' };
193+
const result = executeVoiceCommandAction(action, state, { dance });
194+
expect(result).toBe(true);
195+
expect(dance).toHaveBeenCalledTimes(1);
196+
});
197+
198+
it('executes nod action with preset handler', () => {
199+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
200+
const nod = vi.fn();
201+
const action: VoiceCommandAction = { type: 'nod' };
202+
const result = executeVoiceCommandAction(action, state, { nod });
203+
expect(result).toBe(true);
204+
expect(nod).toHaveBeenCalledTimes(1);
205+
});
206+
207+
it('executes shakeHead action with preset handler', () => {
208+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
209+
const shakeHead = vi.fn();
210+
const action: VoiceCommandAction = { type: 'shakeHead' };
211+
const result = executeVoiceCommandAction(action, state, { shakeHead });
212+
expect(result).toBe(true);
213+
expect(shakeHead).toHaveBeenCalledTimes(1);
214+
});
215+
});
216+
217+
describe('processVoiceCommand', () => {
218+
it('parses and executes play command', () => {
219+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
220+
const result = processVoiceCommand('播放', state);
221+
expect(result).toBe(true);
222+
expect(state.play).toHaveBeenCalledTimes(1);
223+
});
224+
225+
it('parses and executes greeting command with preset', () => {
226+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
227+
const greeting = vi.fn();
228+
const result = processVoiceCommand('打招呼', state, { greeting });
229+
expect(result).toBe(true);
230+
expect(greeting).toHaveBeenCalledTimes(1);
231+
});
232+
233+
it('returns false for unknown command', () => {
234+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
235+
const result = processVoiceCommand('你好', state);
236+
expect(result).toBe(false);
237+
});
238+
239+
it('returns false for greeting without preset handler', () => {
240+
const state = { play: vi.fn(), pause: vi.fn(), reset: vi.fn(), setMuted: vi.fn() };
241+
const result = processVoiceCommand('打招呼', state);
242+
expect(result).toBe(false);
243+
});
244+
});

0 commit comments

Comments
 (0)