mirror of
https://github.com/jackwener/OpenCLI.git
synced 2026-09-14 18:25:42 +08:00
d2974a9ff6
* refactor(adapters): convert adapter layer from TypeScript to JavaScript Core framework stays TypeScript; adapter layer moves to JS-first. Adapters are essentially "executable config + browser scripts" that barely use TS features — this simplifies the build/distribution pipeline by removing the dist/clis/ intermediate compilation step. Changes: - Convert all 753 adapter files in clis/ from .ts to .js - Update tsconfig to exclude clis/ from compilation - Simplify build-manifest to scan clis/*.js directly (no dist/clis/) - Update discovery, main, fetch-adapters to load JS adapters from clis/ - Update generate-verified to output .js artifacts - Update package.json files field: dist/clis/ → clis/ - Fix all test files for the .ts → .js transition * fix(main): use findPackageRoot for BUILTIN_CLIS path The previous relative path (../../clis from __dirname) only worked for dist/src/main.js but broke dev mode (tsx src/main.ts) where __dirname is <repo>/src — resolving to /clis instead of <repo>/clis. Use findPackageRoot() which works for both dev and prod paths.
101 lines
4.4 KiB
JavaScript
101 lines
4.4 KiB
JavaScript
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||
const baseline = {
|
||
turns: [{ Role: 'Assistant', Text: '旧回答' }],
|
||
transcriptLines: ['baseline'],
|
||
composerHasText: true,
|
||
isGenerating: false,
|
||
structuredTurnsTrusted: true,
|
||
};
|
||
const submission = {
|
||
snapshot: {
|
||
turns: [
|
||
{ Role: 'Assistant', Text: '旧回答' },
|
||
{ Role: 'User', Text: '请只回复:OK' },
|
||
],
|
||
transcriptLines: ['baseline', '请只回复:OK'],
|
||
composerHasText: false,
|
||
isGenerating: true,
|
||
structuredTurnsTrusted: true,
|
||
},
|
||
preSendAssistantCount: 1,
|
||
userAnchorTurn: { Role: 'User', Text: '请只回复:OK' },
|
||
reason: 'user_turn',
|
||
};
|
||
const mocks = vi.hoisted(() => ({
|
||
readGeminiSnapshot: vi.fn(),
|
||
sendGeminiMessage: vi.fn(),
|
||
startNewGeminiChat: vi.fn(),
|
||
waitForGeminiSubmission: vi.fn(),
|
||
waitForGeminiResponse: vi.fn(),
|
||
}));
|
||
vi.mock('./utils.js', async () => {
|
||
const actual = await vi.importActual('./utils.js');
|
||
return {
|
||
...actual,
|
||
readGeminiSnapshot: mocks.readGeminiSnapshot,
|
||
sendGeminiMessage: mocks.sendGeminiMessage,
|
||
startNewGeminiChat: mocks.startNewGeminiChat,
|
||
waitForGeminiSubmission: mocks.waitForGeminiSubmission,
|
||
waitForGeminiResponse: mocks.waitForGeminiResponse,
|
||
};
|
||
});
|
||
import { askCommand } from './ask.js';
|
||
function createPageMock() {
|
||
return {
|
||
goto: vi.fn().mockResolvedValue(undefined),
|
||
evaluate: vi.fn(),
|
||
getCookies: vi.fn().mockResolvedValue([]),
|
||
snapshot: vi.fn().mockResolvedValue(undefined),
|
||
click: vi.fn().mockResolvedValue(undefined),
|
||
typeText: vi.fn().mockResolvedValue(undefined),
|
||
pressKey: vi.fn().mockResolvedValue(undefined),
|
||
scrollTo: vi.fn().mockResolvedValue(undefined),
|
||
getFormState: vi.fn().mockResolvedValue({}),
|
||
wait: vi.fn().mockResolvedValue(undefined),
|
||
tabs: vi.fn().mockResolvedValue([]),
|
||
selectTab: vi.fn().mockResolvedValue(undefined),
|
||
networkRequests: vi.fn().mockResolvedValue([]),
|
||
consoleMessages: vi.fn().mockResolvedValue([]),
|
||
scroll: vi.fn().mockResolvedValue(undefined),
|
||
autoScroll: vi.fn().mockResolvedValue(undefined),
|
||
installInterceptor: vi.fn().mockResolvedValue(undefined),
|
||
getInterceptedRequests: vi.fn().mockResolvedValue([]),
|
||
waitForCapture: vi.fn().mockResolvedValue(undefined),
|
||
screenshot: vi.fn().mockResolvedValue(''),
|
||
nativeType: vi.fn().mockResolvedValue(undefined),
|
||
nativeKeyPress: vi.fn().mockResolvedValue(undefined),
|
||
};
|
||
}
|
||
describe('gemini ask orchestration', () => {
|
||
beforeEach(() => {
|
||
vi.clearAllMocks();
|
||
});
|
||
it('captures baseline, sends, waits for confirmed submission, then waits with the remaining timeout', async () => {
|
||
vi.spyOn(Date, 'now')
|
||
.mockReturnValueOnce(0)
|
||
.mockReturnValueOnce(2000);
|
||
const page = createPageMock();
|
||
mocks.readGeminiSnapshot.mockResolvedValueOnce(baseline);
|
||
mocks.sendGeminiMessage.mockResolvedValueOnce('button');
|
||
mocks.waitForGeminiSubmission.mockResolvedValueOnce(submission);
|
||
mocks.waitForGeminiResponse.mockResolvedValueOnce('OK');
|
||
const result = await askCommand.func(page, { prompt: '请只回复:OK', timeout: '20', new: 'false' });
|
||
expect(mocks.readGeminiSnapshot).toHaveBeenCalledWith(page);
|
||
expect(mocks.waitForGeminiSubmission).toHaveBeenCalledWith(page, baseline, 20);
|
||
expect(mocks.waitForGeminiResponse).toHaveBeenCalledWith(page, submission, '请只回复:OK', 18);
|
||
expect(result).toEqual([{ response: '💬 OK' }]);
|
||
});
|
||
it('does not spend extra response wait time after submission has already consumed the full timeout budget', async () => {
|
||
vi.spyOn(Date, 'now')
|
||
.mockReturnValueOnce(0)
|
||
.mockReturnValueOnce(20000);
|
||
const page = createPageMock();
|
||
mocks.readGeminiSnapshot.mockResolvedValueOnce(baseline);
|
||
mocks.sendGeminiMessage.mockResolvedValueOnce('button');
|
||
mocks.waitForGeminiSubmission.mockResolvedValueOnce(submission);
|
||
mocks.waitForGeminiResponse.mockResolvedValueOnce('');
|
||
await askCommand.func(page, { prompt: '请只回复:OK', timeout: '20', new: 'false' });
|
||
expect(mocks.waitForGeminiResponse).toHaveBeenCalledWith(page, submission, '请只回复:OK', 0);
|
||
});
|
||
});
|