/ clis / gemini / ask.test.js
ask.test.js
  1  import { beforeEach, describe, expect, it, vi } from 'vitest';
  2  const baseline = {
  3      turns: [{ Role: 'Assistant', Text: '旧回答' }],
  4      transcriptLines: ['baseline'],
  5      composerHasText: true,
  6      isGenerating: false,
  7      structuredTurnsTrusted: true,
  8  };
  9  const submission = {
 10      snapshot: {
 11          turns: [
 12              { Role: 'Assistant', Text: '旧回答' },
 13              { Role: 'User', Text: '请只回复:OK' },
 14          ],
 15          transcriptLines: ['baseline', '请只回复:OK'],
 16          composerHasText: false,
 17          isGenerating: true,
 18          structuredTurnsTrusted: true,
 19      },
 20      preSendAssistantCount: 1,
 21      userAnchorTurn: { Role: 'User', Text: '请只回复:OK' },
 22      reason: 'user_turn',
 23  };
 24  const mocks = vi.hoisted(() => ({
 25      readGeminiSnapshot: vi.fn(),
 26      sendGeminiMessage: vi.fn(),
 27      startNewGeminiChat: vi.fn(),
 28      waitForGeminiSubmission: vi.fn(),
 29      waitForGeminiResponse: vi.fn(),
 30  }));
 31  vi.mock('./utils.js', async () => {
 32      const actual = await vi.importActual('./utils.js');
 33      return {
 34          ...actual,
 35          readGeminiSnapshot: mocks.readGeminiSnapshot,
 36          sendGeminiMessage: mocks.sendGeminiMessage,
 37          startNewGeminiChat: mocks.startNewGeminiChat,
 38          waitForGeminiSubmission: mocks.waitForGeminiSubmission,
 39          waitForGeminiResponse: mocks.waitForGeminiResponse,
 40      };
 41  });
 42  import { askCommand } from './ask.js';
 43  function createPageMock() {
 44      return {
 45          goto: vi.fn().mockResolvedValue(undefined),
 46          evaluate: vi.fn(),
 47          getCookies: vi.fn().mockResolvedValue([]),
 48          snapshot: vi.fn().mockResolvedValue(undefined),
 49          click: vi.fn().mockResolvedValue(undefined),
 50          typeText: vi.fn().mockResolvedValue(undefined),
 51          pressKey: vi.fn().mockResolvedValue(undefined),
 52          scrollTo: vi.fn().mockResolvedValue(undefined),
 53          getFormState: vi.fn().mockResolvedValue({}),
 54          wait: vi.fn().mockResolvedValue(undefined),
 55          tabs: vi.fn().mockResolvedValue([]),
 56          selectTab: vi.fn().mockResolvedValue(undefined),
 57          networkRequests: vi.fn().mockResolvedValue([]),
 58          consoleMessages: vi.fn().mockResolvedValue([]),
 59          scroll: vi.fn().mockResolvedValue(undefined),
 60          autoScroll: vi.fn().mockResolvedValue(undefined),
 61          installInterceptor: vi.fn().mockResolvedValue(undefined),
 62          getInterceptedRequests: vi.fn().mockResolvedValue([]),
 63          waitForCapture: vi.fn().mockResolvedValue(undefined),
 64          screenshot: vi.fn().mockResolvedValue(''),
 65          nativeType: vi.fn().mockResolvedValue(undefined),
 66          nativeKeyPress: vi.fn().mockResolvedValue(undefined),
 67      };
 68  }
 69  describe('gemini ask orchestration', () => {
 70      beforeEach(() => {
 71          vi.clearAllMocks();
 72      });
 73      it('captures baseline, sends, waits for confirmed submission, then waits with the remaining timeout', async () => {
 74          vi.spyOn(Date, 'now')
 75              .mockReturnValueOnce(0)
 76              .mockReturnValueOnce(2000);
 77          const page = createPageMock();
 78          mocks.readGeminiSnapshot.mockResolvedValueOnce(baseline);
 79          mocks.sendGeminiMessage.mockResolvedValueOnce('button');
 80          mocks.waitForGeminiSubmission.mockResolvedValueOnce(submission);
 81          mocks.waitForGeminiResponse.mockResolvedValueOnce('OK');
 82          const result = await askCommand.func(page, { prompt: '请只回复:OK', timeout: '20', new: 'false' });
 83          expect(mocks.readGeminiSnapshot).toHaveBeenCalledWith(page);
 84          expect(mocks.waitForGeminiSubmission).toHaveBeenCalledWith(page, baseline, 20);
 85          expect(mocks.waitForGeminiResponse).toHaveBeenCalledWith(page, submission, '请只回复:OK', 18);
 86          expect(result).toEqual([{ response: '💬 OK' }]);
 87      });
 88      it('does not spend extra response wait time after submission has already consumed the full timeout budget', async () => {
 89          vi.spyOn(Date, 'now')
 90              .mockReturnValueOnce(0)
 91              .mockReturnValueOnce(20000);
 92          const page = createPageMock();
 93          mocks.readGeminiSnapshot.mockResolvedValueOnce(baseline);
 94          mocks.sendGeminiMessage.mockResolvedValueOnce('button');
 95          mocks.waitForGeminiSubmission.mockResolvedValueOnce(submission);
 96          mocks.waitForGeminiResponse.mockResolvedValueOnce('');
 97          await askCommand.func(page, { prompt: '请只回复:OK', timeout: '20', new: 'false' });
 98          expect(mocks.waitForGeminiResponse).toHaveBeenCalledWith(page, submission, '请只回复:OK', 0);
 99      });
100  });