mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-05-14 05:42:54 -07:00
feat: support multi-file drag and drop of images (#14832)
This commit is contained in:
@@ -9,6 +9,8 @@ import {
|
||||
clipboardHasImage,
|
||||
saveClipboardImage,
|
||||
cleanupOldClipboardImages,
|
||||
splitEscapedPaths,
|
||||
parsePastedPaths,
|
||||
} from './clipboardUtils.js';
|
||||
|
||||
describe('clipboardUtils', () => {
|
||||
@@ -73,4 +75,166 @@ describe('clipboardUtils', () => {
|
||||
await expect(cleanupOldClipboardImages('.')).resolves.not.toThrow();
|
||||
});
|
||||
});
|
||||
|
||||
describe('splitEscapedPaths', () => {
|
||||
it('should return single path when no spaces', () => {
|
||||
expect(splitEscapedPaths('/path/to/image.png')).toEqual([
|
||||
'/path/to/image.png',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should split simple space-separated paths', () => {
|
||||
expect(splitEscapedPaths('/img1.png /img2.png')).toEqual([
|
||||
'/img1.png',
|
||||
'/img2.png',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should split three paths', () => {
|
||||
expect(splitEscapedPaths('/a.png /b.jpg /c.heic')).toEqual([
|
||||
'/a.png',
|
||||
'/b.jpg',
|
||||
'/c.heic',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should preserve escaped spaces within filenames', () => {
|
||||
expect(splitEscapedPaths('/my\\ image.png')).toEqual(['/my\\ image.png']);
|
||||
});
|
||||
|
||||
it('should handle multiple paths with escaped spaces', () => {
|
||||
expect(splitEscapedPaths('/my\\ img1.png /my\\ img2.png')).toEqual([
|
||||
'/my\\ img1.png',
|
||||
'/my\\ img2.png',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle path with multiple escaped spaces', () => {
|
||||
expect(splitEscapedPaths('/path/to/my\\ cool\\ image.png')).toEqual([
|
||||
'/path/to/my\\ cool\\ image.png',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle multiple consecutive spaces between paths', () => {
|
||||
expect(splitEscapedPaths('/img1.png /img2.png')).toEqual([
|
||||
'/img1.png',
|
||||
'/img2.png',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle trailing and leading whitespace', () => {
|
||||
expect(splitEscapedPaths(' /img1.png /img2.png ')).toEqual([
|
||||
'/img1.png',
|
||||
'/img2.png',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should return empty array for empty string', () => {
|
||||
expect(splitEscapedPaths('')).toEqual([]);
|
||||
});
|
||||
|
||||
it('should return empty array for whitespace only', () => {
|
||||
expect(splitEscapedPaths(' ')).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('parsePastedPaths', () => {
|
||||
it('should return null for empty string', () => {
|
||||
const result = parsePastedPaths('', () => true);
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
|
||||
it('should add @ prefix to single valid path', () => {
|
||||
const result = parsePastedPaths('/path/to/file.txt', () => true);
|
||||
expect(result).toBe('@/path/to/file.txt ');
|
||||
});
|
||||
|
||||
it('should return null for single invalid path', () => {
|
||||
const result = parsePastedPaths('/path/to/file.txt', () => false);
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
|
||||
it('should add @ prefix to all valid paths', () => {
|
||||
// Use Set to model reality: individual paths exist, combined string doesn't
|
||||
const validPaths = new Set(['/path/to/file1.txt', '/path/to/file2.txt']);
|
||||
const result = parsePastedPaths(
|
||||
'/path/to/file1.txt /path/to/file2.txt',
|
||||
(p) => validPaths.has(p),
|
||||
);
|
||||
expect(result).toBe('@/path/to/file1.txt @/path/to/file2.txt ');
|
||||
});
|
||||
|
||||
it('should only add @ prefix to valid paths', () => {
|
||||
const result = parsePastedPaths(
|
||||
'/valid/file.txt /invalid/file.jpg',
|
||||
(p) => p.endsWith('.txt'),
|
||||
);
|
||||
expect(result).toBe('@/valid/file.txt /invalid/file.jpg ');
|
||||
});
|
||||
|
||||
it('should return null if no paths are valid', () => {
|
||||
const result = parsePastedPaths(
|
||||
'/path/to/file1.txt /path/to/file2.txt',
|
||||
() => false,
|
||||
);
|
||||
expect(result).toBe(null);
|
||||
});
|
||||
|
||||
it('should handle paths with escaped spaces', () => {
|
||||
// Use Set to model reality: individual paths exist, combined string doesn't
|
||||
const validPaths = new Set(['/path/to/my file.txt', '/other/path.txt']);
|
||||
const result = parsePastedPaths(
|
||||
'/path/to/my\\ file.txt /other/path.txt',
|
||||
(p) => validPaths.has(p),
|
||||
);
|
||||
expect(result).toBe('@/path/to/my\\ file.txt @/other/path.txt ');
|
||||
});
|
||||
|
||||
it('should unescape paths before validation', () => {
|
||||
// Use Set to model reality: individual paths exist, combined string doesn't
|
||||
const validPaths = new Set(['/my file.txt', '/other.txt']);
|
||||
const validatedPaths: string[] = [];
|
||||
parsePastedPaths('/my\\ file.txt /other.txt', (p) => {
|
||||
validatedPaths.push(p);
|
||||
return validPaths.has(p);
|
||||
});
|
||||
// First checks entire string, then individual unescaped segments
|
||||
expect(validatedPaths).toEqual([
|
||||
'/my\\ file.txt /other.txt',
|
||||
'/my file.txt',
|
||||
'/other.txt',
|
||||
]);
|
||||
});
|
||||
|
||||
it('should handle single path with unescaped spaces from copy-paste', () => {
|
||||
const result = parsePastedPaths('/path/to/my file.txt', () => true);
|
||||
expect(result).toBe('@/path/to/my\\ file.txt ');
|
||||
});
|
||||
|
||||
it('should handle Windows path', () => {
|
||||
const result = parsePastedPaths('C:\\Users\\file.txt', () => true);
|
||||
expect(result).toBe('@C:\\Users\\file.txt ');
|
||||
});
|
||||
|
||||
it('should handle Windows path with unescaped spaces', () => {
|
||||
const result = parsePastedPaths('C:\\My Documents\\file.txt', () => true);
|
||||
expect(result).toBe('@C:\\My\\ Documents\\file.txt ');
|
||||
});
|
||||
|
||||
it('should handle multiple Windows paths', () => {
|
||||
const validPaths = new Set(['C:\\file1.txt', 'D:\\file2.txt']);
|
||||
const result = parsePastedPaths('C:\\file1.txt D:\\file2.txt', (p) =>
|
||||
validPaths.has(p),
|
||||
);
|
||||
expect(result).toBe('@C:\\file1.txt @D:\\file2.txt ');
|
||||
});
|
||||
|
||||
it('should handle Windows UNC path', () => {
|
||||
const result = parsePastedPaths(
|
||||
'\\\\server\\share\\file.txt',
|
||||
() => true,
|
||||
);
|
||||
expect(result).toBe('@\\\\server\\share\\file.txt ');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user