Files
zn-ai/tests/browser-shortcut.test.ts

145 lines
4.0 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @vitest-environment node
import { beforeEach, describe, expect, it, vi } from 'vitest';
const mocks = vi.hoisted(() => ({
appendMessage: vi.fn(),
setActiveRun: vi.fn(),
clearActiveRun: vi.fn(),
extractBrowserOpenIntent: vi.fn(),
openUrlInBrowser: vi.fn(),
appendTranscriptLine: vi.fn(),
logger: {
error: vi.fn(),
},
}));
vi.mock('../electron/gateway/session-store', () => ({
sessionStore: {
appendMessage: mocks.appendMessage,
setActiveRun: mocks.setActiveRun,
clearActiveRun: mocks.clearActiveRun,
},
}));
vi.mock('@electron/service/browser-open-service', () => ({
extractBrowserOpenIntent: mocks.extractBrowserOpenIntent,
openUrlInBrowser: mocks.openUrlInBrowser,
}));
vi.mock('@electron/utils/token-usage-writer', () => ({
appendTranscriptLine: mocks.appendTranscriptLine,
}));
vi.mock('@electron/service/logger', () => ({
default: mocks.logger,
}));
function flushAsyncTasks(): Promise<void> {
return new Promise((resolve) => {
setTimeout(resolve, 0);
});
}
describe('gateway browser shortcut', () => {
beforeEach(() => {
vi.resetModules();
vi.clearAllMocks();
mocks.extractBrowserOpenIntent.mockImplementation((text: string) => (
text === '打开 http://www.baidu.com'
? { url: 'http://www.baidu.com/' }
: null
));
});
it('starts a browser-open run and emits a final assistant message on success', async () => {
mocks.openUrlInBrowser.mockResolvedValue({
url: 'http://www.baidu.com/',
pageUrl: 'http://www.baidu.com/',
title: '百度一下,你就知道',
});
const { maybeHandleBrowserOpenMessage } = await import('../electron/gateway/browser-shortcut');
const broadcast = vi.fn();
const handled = maybeHandleBrowserOpenMessage(
'agent:test:main',
'run-1',
{ role: 'user', content: '打开 http://www.baidu.com' },
broadcast,
);
expect(handled).toBe(true);
expect(mocks.setActiveRun).toHaveBeenCalledWith(
'agent:test:main',
'run-1',
expect.any(AbortController),
);
await flushAsyncTasks();
expect(mocks.openUrlInBrowser).toHaveBeenCalledWith(
'http://www.baidu.com/',
expect.objectContaining({ signal: expect.any(AbortSignal) }),
);
expect(mocks.appendMessage).toHaveBeenCalledWith(
'agent:test:main',
expect.objectContaining({
role: 'assistant',
content: '已为你打开 http://www.baidu.com/(百度一下,你就知道)',
}),
);
expect(broadcast).toHaveBeenCalledWith(expect.objectContaining({
type: 'chat:final',
sessionKey: 'agent:test:main',
runId: 'run-1',
}));
});
it('emits a failure assistant message when browser open fails', async () => {
mocks.openUrlInBrowser.mockRejectedValue(new Error('No browser context available'));
const { maybeHandleBrowserOpenMessage } = await import('../electron/gateway/browser-shortcut');
const broadcast = vi.fn();
const handled = maybeHandleBrowserOpenMessage(
'agent:test:main',
'run-2',
{ role: 'user', content: '打开 http://www.baidu.com' },
broadcast,
);
expect(handled).toBe(true);
await flushAsyncTasks();
expect(mocks.appendMessage).toHaveBeenCalledWith(
'agent:test:main',
expect.objectContaining({
role: 'assistant',
content: '打开失败No browser context available',
}),
);
expect(broadcast).toHaveBeenCalledWith(expect.objectContaining({
type: 'chat:final',
runId: 'run-2',
}));
});
it('returns false when the user message is not an explicit browser-open command', async () => {
mocks.extractBrowserOpenIntent.mockReturnValue(null);
const { maybeHandleBrowserOpenMessage } = await import('../electron/gateway/browser-shortcut');
expect(
maybeHandleBrowserOpenMessage(
'agent:test:main',
'run-3',
{ role: 'user', content: '帮我总结一下百度首页' },
vi.fn(),
),
).toBe(false);
expect(mocks.openUrlInBrowser).not.toHaveBeenCalled();
});
});