mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-03-13 15:40:57 -07:00
162 lines
4.7 KiB
TypeScript
162 lines
4.7 KiB
TypeScript
/**
|
|
* @license
|
|
* Copyright 2025 Google LLC
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
import { describe, it, expect } from 'vitest';
|
|
import {
|
|
parseSGRMouseEvent,
|
|
parseX11MouseEvent,
|
|
isIncompleteMouseSequence,
|
|
parseMouseEvent,
|
|
} from './mouse.js';
|
|
import { ESC } from './input.js';
|
|
|
|
describe('mouse utils', () => {
|
|
describe('parseSGRMouseEvent', () => {
|
|
it('parses a valid SGR mouse press', () => {
|
|
// Button 0 (left), col 37, row 25, press (M)
|
|
const input = `${ESC}[<0;37;25M`;
|
|
const result = parseSGRMouseEvent(input);
|
|
expect(result).not.toBeNull();
|
|
expect(result!.event).toEqual({
|
|
name: 'left-press',
|
|
col: 37,
|
|
row: 25,
|
|
shift: false,
|
|
meta: false,
|
|
ctrl: false,
|
|
button: 'left',
|
|
});
|
|
expect(result!.length).toBe(input.length);
|
|
});
|
|
|
|
it('parses a valid SGR mouse release', () => {
|
|
// Button 0 (left), col 37, row 25, release (m)
|
|
const input = `${ESC}[<0;37;25m`;
|
|
const result = parseSGRMouseEvent(input);
|
|
expect(result).not.toBeNull();
|
|
expect(result!.event).toEqual({
|
|
name: 'left-release',
|
|
col: 37,
|
|
row: 25,
|
|
shift: false,
|
|
meta: false,
|
|
ctrl: false,
|
|
button: 'left',
|
|
});
|
|
});
|
|
|
|
it('parses SGR with modifiers', () => {
|
|
// Button 0 + Shift(4) + Meta(8) + Ctrl(16) = 0 + 4 + 8 + 16 = 28
|
|
const input = `${ESC}[<28;10;20M`;
|
|
const result = parseSGRMouseEvent(input);
|
|
expect(result).not.toBeNull();
|
|
expect(result!.event).toEqual({
|
|
name: 'left-press',
|
|
col: 10,
|
|
row: 20,
|
|
shift: true,
|
|
meta: true,
|
|
ctrl: true,
|
|
button: 'left',
|
|
});
|
|
});
|
|
|
|
it('parses SGR move event', () => {
|
|
// Button 0 + Move(32) = 32
|
|
const input = `${ESC}[<32;10;20M`;
|
|
const result = parseSGRMouseEvent(input);
|
|
expect(result).not.toBeNull();
|
|
expect(result!.event.name).toBe('move');
|
|
expect(result!.event.button).toBe('left');
|
|
});
|
|
|
|
it('parses SGR scroll events', () => {
|
|
expect(parseSGRMouseEvent(`${ESC}[<64;1;1M`)!.event.name).toBe(
|
|
'scroll-up',
|
|
);
|
|
expect(parseSGRMouseEvent(`${ESC}[<65;1;1M`)!.event.name).toBe(
|
|
'scroll-down',
|
|
);
|
|
});
|
|
|
|
it('returns null for invalid SGR', () => {
|
|
expect(parseSGRMouseEvent(`${ESC}[<;1;1M`)).toBeNull();
|
|
expect(parseSGRMouseEvent(`${ESC}[<0;1;M`)).toBeNull();
|
|
expect(parseSGRMouseEvent(`not sgr`)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('parseX11MouseEvent', () => {
|
|
it('parses a valid X11 mouse press', () => {
|
|
// Button 0 (left) + 32 = ' ' (space)
|
|
// Col 1 + 32 = '!'
|
|
// Row 1 + 32 = '!'
|
|
const input = `${ESC}[M !!`;
|
|
const result = parseX11MouseEvent(input);
|
|
expect(result).not.toBeNull();
|
|
expect(result!.event).toEqual({
|
|
name: 'left-press',
|
|
col: 1,
|
|
row: 1,
|
|
shift: false,
|
|
meta: false,
|
|
ctrl: false,
|
|
button: 'left',
|
|
});
|
|
expect(result!.length).toBe(6);
|
|
});
|
|
|
|
it('returns null for incomplete X11', () => {
|
|
expect(parseX11MouseEvent(`${ESC}[M !`)).toBeNull();
|
|
});
|
|
});
|
|
|
|
describe('isIncompleteMouseSequence', () => {
|
|
it('returns true for prefixes', () => {
|
|
expect(isIncompleteMouseSequence(ESC)).toBe(true);
|
|
expect(isIncompleteMouseSequence(`${ESC}[`)).toBe(true);
|
|
expect(isIncompleteMouseSequence(`${ESC}[<`)).toBe(true);
|
|
expect(isIncompleteMouseSequence(`${ESC}[M`)).toBe(true);
|
|
});
|
|
|
|
it('returns true for partial SGR', () => {
|
|
expect(isIncompleteMouseSequence(`${ESC}[<0;10;20`)).toBe(true);
|
|
});
|
|
|
|
it('returns true for partial X11', () => {
|
|
expect(isIncompleteMouseSequence(`${ESC}[M `)).toBe(true);
|
|
expect(isIncompleteMouseSequence(`${ESC}[M !`)).toBe(true);
|
|
});
|
|
|
|
it('returns false for complete SGR', () => {
|
|
expect(isIncompleteMouseSequence(`${ESC}[<0;10;20M`)).toBe(false);
|
|
});
|
|
|
|
it('returns false for complete X11', () => {
|
|
expect(isIncompleteMouseSequence(`${ESC}[M !!!`)).toBe(false);
|
|
});
|
|
|
|
it('returns false for non-mouse sequences', () => {
|
|
expect(isIncompleteMouseSequence('a')).toBe(false);
|
|
expect(isIncompleteMouseSequence(`${ESC}[A`)).toBe(false); // Arrow up
|
|
});
|
|
|
|
it('returns false for garbage that started like a mouse sequence but got too long (SGR)', () => {
|
|
const longGarbage = `${ESC}[<` + '0'.repeat(100);
|
|
expect(isIncompleteMouseSequence(longGarbage)).toBe(false);
|
|
});
|
|
});
|
|
|
|
describe('parseMouseEvent', () => {
|
|
it('parses SGR', () => {
|
|
expect(parseMouseEvent(`${ESC}[<0;1;1M`)).not.toBeNull();
|
|
});
|
|
it('parses X11', () => {
|
|
expect(parseMouseEvent(`${ESC}[M !!!`)).not.toBeNull();
|
|
});
|
|
});
|
|
});
|