advancedInteraction.test.ts•6.87 kB
import { DragTool, PressKeyTool } from '../../../tools/browser/interaction.js';
import { ToolContext } from '../../../tools/common/types.js';
import { Page, Browser, ElementHandle } from 'playwright';
import { jest } from '@jest/globals';
// Mock page functions
const mockWaitForSelector = jest.fn();
const mockMouseMove = jest.fn().mockImplementation(() => Promise.resolve());
const mockMouseDown = jest.fn().mockImplementation(() => Promise.resolve());
const mockMouseUp = jest.fn().mockImplementation(() => Promise.resolve());
const mockKeyboardPress = jest.fn().mockImplementation(() => Promise.resolve());
const mockFocus = jest.fn().mockImplementation(() => Promise.resolve());
const mockIsClosed = jest.fn().mockReturnValue(false);
// Mock element handle
const mockBoundingBox = jest.fn().mockReturnValue({ x: 10, y: 10, width: 100, height: 50 });
const mockElementHandle = {
  boundingBox: mockBoundingBox
} as unknown as ElementHandle;
// Wait for selector returns element handle
mockWaitForSelector.mockImplementation(() => Promise.resolve(mockElementHandle));
// Mock mouse
const mockMouse = {
  move: mockMouseMove,
  down: mockMouseDown,
  up: mockMouseUp
};
// Mock keyboard
const mockKeyboard = {
  press: mockKeyboardPress
};
// Mock the Page object with proper typing
const mockPage = {
  waitForSelector: mockWaitForSelector,
  mouse: mockMouse,
  keyboard: mockKeyboard,
  focus: mockFocus,
  isClosed: mockIsClosed
} as unknown as Page;
// Mock the browser
const mockIsConnected = jest.fn().mockReturnValue(true);
const mockBrowser = {
  isConnected: mockIsConnected
} as unknown as Browser;
// Mock the server
const mockServer = {
  sendMessage: jest.fn()
};
// Mock context
const mockContext = {
  page: mockPage,
  browser: mockBrowser,
  server: mockServer
} as ToolContext;
describe('Advanced Browser Interaction Tools', () => {
  let dragTool: DragTool;
  let pressKeyTool: PressKeyTool;
  beforeEach(() => {
    jest.clearAllMocks();
    dragTool = new DragTool(mockServer);
    pressKeyTool = new PressKeyTool(mockServer);
    // Reset browser and page mocks
    mockIsConnected.mockReturnValue(true);
    mockIsClosed.mockReturnValue(false);
  });
  describe('DragTool', () => {
    test('should drag an element to a target location', async () => {
      const args = {
        sourceSelector: '#source-element',
        targetSelector: '#target-element'
      };
      const result = await dragTool.execute(args, mockContext);
      expect(mockWaitForSelector).toHaveBeenCalledWith('#source-element');
      expect(mockWaitForSelector).toHaveBeenCalledWith('#target-element');
      expect(mockBoundingBox).toHaveBeenCalledTimes(2);
      expect(mockMouseMove).toHaveBeenCalledWith(60, 35); // Source center (10+100/2, 10+50/2)
      expect(mockMouseDown).toHaveBeenCalled();
      expect(mockMouseMove).toHaveBeenCalledWith(60, 35); // Target center (same mock values)
      expect(mockMouseUp).toHaveBeenCalled();
      expect(result.isError).toBe(false);
      expect(result.content[0].text).toContain('Dragged element from');
    });
    test('should handle errors when element positions cannot be determined', async () => {
      const args = {
        sourceSelector: '#source-element',
        targetSelector: '#target-element'
      };
      // Mock failure to get bounding box
      mockBoundingBox.mockReturnValueOnce(null);
      const result = await dragTool.execute(args, mockContext);
      expect(mockWaitForSelector).toHaveBeenCalledWith('#source-element');
      expect(mockBoundingBox).toHaveBeenCalled();
      expect(mockMouseMove).not.toHaveBeenCalled();
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Could not get element positions');
    });
    test('should handle drag errors', async () => {
      const args = {
        sourceSelector: '#source-element',
        targetSelector: '#target-element'
      };
      // Mock a mouse operation error
      mockMouseDown.mockImplementationOnce(() => Promise.reject(new Error('Mouse operation failed')));
      const result = await dragTool.execute(args, mockContext);
      expect(mockWaitForSelector).toHaveBeenCalledWith('#source-element');
      expect(mockWaitForSelector).toHaveBeenCalledWith('#target-element');
      expect(mockBoundingBox).toHaveBeenCalled();
      expect(mockMouseMove).toHaveBeenCalled();
      expect(mockMouseDown).toHaveBeenCalled();
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Operation failed');
    });
    test('should handle missing page', async () => {
      const args = {
        sourceSelector: '#source-element',
        targetSelector: '#target-element'
      };
      const result = await dragTool.execute(args, { server: mockServer } as ToolContext);
      expect(mockWaitForSelector).not.toHaveBeenCalled();
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Browser page not initialized');
    });
  });
  describe('PressKeyTool', () => {
    test('should press a keyboard key', async () => {
      const args = {
        key: 'Enter'
      };
      const result = await pressKeyTool.execute(args, mockContext);
      expect(mockKeyboardPress).toHaveBeenCalledWith('Enter');
      expect(result.isError).toBe(false);
      expect(result.content[0].text).toContain('Pressed key: Enter');
    });
    test('should focus an element before pressing a key if selector provided', async () => {
      const args = {
        key: 'Enter',
        selector: '#input-field'
      };
      const result = await pressKeyTool.execute(args, mockContext);
      expect(mockWaitForSelector).toHaveBeenCalledWith('#input-field');
      expect(mockFocus).toHaveBeenCalledWith('#input-field');
      expect(mockKeyboardPress).toHaveBeenCalledWith('Enter');
      expect(result.isError).toBe(false);
      expect(result.content[0].text).toContain('Pressed key: Enter');
    });
    test('should handle key press errors', async () => {
      const args = {
        key: 'Enter'
      };
      // Mock a keyboard operation error
      mockKeyboardPress.mockImplementationOnce(() => Promise.reject(new Error('Keyboard operation failed')));
      const result = await pressKeyTool.execute(args, mockContext);
      expect(mockKeyboardPress).toHaveBeenCalledWith('Enter');
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Operation failed');
    });
    test('should handle missing page', async () => {
      const args = {
        key: 'Enter'
      };
      const result = await pressKeyTool.execute(args, { server: mockServer } as ToolContext);
      expect(mockKeyboardPress).not.toHaveBeenCalled();
      expect(result.isError).toBe(true);
      expect(result.content[0].text).toContain('Browser page not initialized');
    });
  });
});