213 lines
6.7 KiB
TypeScript
213 lines
6.7 KiB
TypeScript
/**
|
|
* Unit tests for desktop/mouse.ts — click, doubleClick, moveTo, drag, scroll, getPosition.
|
|
*/
|
|
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
|
|
const mockMove = vi.fn();
|
|
const mockClick = vi.fn();
|
|
const mockDoubleClick = vi.fn();
|
|
const mockPressButton = vi.fn();
|
|
const mockReleaseButton = vi.fn();
|
|
const mockScrollDown = vi.fn();
|
|
const mockScrollUp = vi.fn();
|
|
const mockScrollLeft = vi.fn();
|
|
const mockScrollRight = vi.fn();
|
|
const mockGetPosition = vi.fn();
|
|
const mockStraightTo = vi.fn();
|
|
|
|
vi.mock('@nut-tree-fork/nut-js', () => {
|
|
class MockPoint {
|
|
constructor(public x: number, public y: number) {}
|
|
}
|
|
return {
|
|
mouse: {
|
|
move: (...args: any[]) => mockMove(...args),
|
|
click: (...args: any[]) => mockClick(...args),
|
|
doubleClick: (...args: any[]) => mockDoubleClick(...args),
|
|
pressButton: (...args: any[]) => mockPressButton(...args),
|
|
releaseButton: (...args: any[]) => mockReleaseButton(...args),
|
|
scrollDown: (...args: any[]) => mockScrollDown(...args),
|
|
scrollUp: (...args: any[]) => mockScrollUp(...args),
|
|
scrollLeft: (...args: any[]) => mockScrollLeft(...args),
|
|
scrollRight: (...args: any[]) => mockScrollRight(...args),
|
|
getPosition: () => mockGetPosition(),
|
|
},
|
|
Button: { LEFT: 'LEFT', RIGHT: 'RIGHT', MIDDLE: 'MIDDLE' },
|
|
Point: MockPoint,
|
|
straightTo: (p: any) => mockStraightTo(p),
|
|
};
|
|
});
|
|
|
|
import { click, doubleClick, moveTo, drag, scroll, getPosition } from '../../src/desktop/mouse.js';
|
|
|
|
describe('click', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockMove.mockResolvedValue(undefined);
|
|
mockClick.mockResolvedValue(undefined);
|
|
mockStraightTo.mockImplementation((p: any) => p);
|
|
});
|
|
|
|
it('moves to position and clicks left button by default', async () => {
|
|
await click(100, 200);
|
|
expect(mockStraightTo).toHaveBeenCalledWith(expect.objectContaining({ x: 100, y: 200 }));
|
|
expect(mockMove).toHaveBeenCalled();
|
|
expect(mockClick).toHaveBeenCalledWith('LEFT');
|
|
});
|
|
|
|
it('supports right button', async () => {
|
|
await click(50, 60, 'right');
|
|
expect(mockClick).toHaveBeenCalledWith('RIGHT');
|
|
});
|
|
|
|
it('supports middle button', async () => {
|
|
await click(50, 60, 'middle');
|
|
expect(mockClick).toHaveBeenCalledWith('MIDDLE');
|
|
});
|
|
|
|
it('wraps errors as DesktopError', async () => {
|
|
mockMove.mockRejectedValue(new Error('screen locked'));
|
|
await expect(click(0, 0)).rejects.toThrow('mouse.click');
|
|
});
|
|
});
|
|
|
|
describe('doubleClick', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockMove.mockResolvedValue(undefined);
|
|
mockDoubleClick.mockResolvedValue(undefined);
|
|
mockStraightTo.mockImplementation((p: any) => p);
|
|
});
|
|
|
|
it('moves and double-clicks', async () => {
|
|
await doubleClick(300, 400);
|
|
expect(mockMove).toHaveBeenCalled();
|
|
expect(mockDoubleClick).toHaveBeenCalledWith('LEFT');
|
|
});
|
|
|
|
it('wraps errors as DesktopError', async () => {
|
|
mockDoubleClick.mockRejectedValue(new Error('fail'));
|
|
await expect(doubleClick(0, 0)).rejects.toThrow('mouse.doubleClick');
|
|
});
|
|
});
|
|
|
|
describe('moveTo', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockMove.mockResolvedValue(undefined);
|
|
mockStraightTo.mockImplementation((p: any) => p);
|
|
});
|
|
|
|
it('moves mouse to coordinates', async () => {
|
|
await moveTo(500, 600);
|
|
expect(mockStraightTo).toHaveBeenCalledWith(expect.objectContaining({ x: 500, y: 600 }));
|
|
expect(mockMove).toHaveBeenCalled();
|
|
});
|
|
|
|
it('wraps errors as DesktopError', async () => {
|
|
mockMove.mockRejectedValue(new Error('fail'));
|
|
await expect(moveTo(0, 0)).rejects.toThrow('mouse.moveTo');
|
|
});
|
|
});
|
|
|
|
describe('drag', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockMove.mockResolvedValue(undefined);
|
|
mockPressButton.mockResolvedValue(undefined);
|
|
mockReleaseButton.mockResolvedValue(undefined);
|
|
mockStraightTo.mockImplementation((p: any) => p);
|
|
});
|
|
|
|
it('moves to start, presses, moves to end, releases', async () => {
|
|
await drag(10, 20, 100, 200);
|
|
|
|
expect(mockMove).toHaveBeenCalledTimes(2);
|
|
expect(mockPressButton).toHaveBeenCalledWith('LEFT');
|
|
expect(mockReleaseButton).toHaveBeenCalledWith('LEFT');
|
|
|
|
// First move to start
|
|
const firstMove = mockStraightTo.mock.calls[0][0];
|
|
expect(firstMove.x).toBe(10);
|
|
expect(firstMove.y).toBe(20);
|
|
|
|
// Second move to end
|
|
const secondMove = mockStraightTo.mock.calls[1][0];
|
|
expect(secondMove.x).toBe(100);
|
|
expect(secondMove.y).toBe(200);
|
|
});
|
|
|
|
it('supports right button drag', async () => {
|
|
await drag(0, 0, 50, 50, 'right');
|
|
expect(mockPressButton).toHaveBeenCalledWith('RIGHT');
|
|
expect(mockReleaseButton).toHaveBeenCalledWith('RIGHT');
|
|
});
|
|
|
|
it('wraps errors as DesktopError', async () => {
|
|
mockPressButton.mockRejectedValue(new Error('fail'));
|
|
await expect(drag(0, 0, 10, 10)).rejects.toThrow('mouse.drag');
|
|
});
|
|
});
|
|
|
|
describe('scroll', () => {
|
|
beforeEach(() => {
|
|
vi.clearAllMocks();
|
|
mockMove.mockResolvedValue(undefined);
|
|
mockScrollDown.mockResolvedValue(undefined);
|
|
mockScrollUp.mockResolvedValue(undefined);
|
|
mockScrollLeft.mockResolvedValue(undefined);
|
|
mockScrollRight.mockResolvedValue(undefined);
|
|
mockStraightTo.mockImplementation((p: any) => p);
|
|
});
|
|
|
|
it('scrolls down with positive deltaY', async () => {
|
|
await scroll(100, 200, 5);
|
|
expect(mockMove).toHaveBeenCalled();
|
|
expect(mockScrollDown).toHaveBeenCalledWith(5);
|
|
expect(mockScrollUp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('scrolls up with negative deltaY', async () => {
|
|
await scroll(100, 200, -3);
|
|
expect(mockScrollUp).toHaveBeenCalledWith(3);
|
|
expect(mockScrollDown).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('does not scroll vertically when deltaY is 0', async () => {
|
|
await scroll(100, 200, 0);
|
|
expect(mockScrollDown).not.toHaveBeenCalled();
|
|
expect(mockScrollUp).not.toHaveBeenCalled();
|
|
});
|
|
|
|
it('scrolls right with positive deltaX', async () => {
|
|
await scroll(100, 200, 0, 4);
|
|
expect(mockScrollRight).toHaveBeenCalledWith(4);
|
|
});
|
|
|
|
it('scrolls left with negative deltaX', async () => {
|
|
await scroll(100, 200, 0, -2);
|
|
expect(mockScrollLeft).toHaveBeenCalledWith(2);
|
|
});
|
|
|
|
it('wraps errors as DesktopError', async () => {
|
|
mockMove.mockRejectedValue(new Error('fail'));
|
|
await expect(scroll(0, 0, 1)).rejects.toThrow('mouse.scroll');
|
|
});
|
|
});
|
|
|
|
describe('getPosition', () => {
|
|
beforeEach(() => vi.clearAllMocks());
|
|
|
|
it('returns cursor position', async () => {
|
|
mockGetPosition.mockResolvedValue({ x: 350, y: 450 });
|
|
const pos = await getPosition();
|
|
expect(pos).toEqual({ x: 350, y: 450 });
|
|
});
|
|
|
|
it('wraps errors as DesktopError', async () => {
|
|
mockGetPosition.mockRejectedValue(new Error('fail'));
|
|
await expect(getPosition()).rejects.toThrow('mouse.getPosition');
|
|
});
|
|
});
|