agent-ecosystem/test/renderer/hooks/useFileSuggestions.test.ts

104 lines
3.5 KiB
TypeScript

import { describe, expect, it } from 'vitest';
import { filterFileSuggestions, formatFileMentionPath } from '@renderer/hooks/useFileSuggestions';
import type { QuickOpenFile } from '@shared/types/editor';
function file(name: string, relativePath: string, path?: string): QuickOpenFile {
return {
name,
relativePath,
path: path ?? `/project/${relativePath}`,
};
}
const FILES: QuickOpenFile[] = [
file('index.ts', 'src/index.ts'),
file('App.tsx', 'src/App.tsx'),
file('test.ts', 'src/test.ts'),
file('telemetry.ts', 'src/utils/telemetry.ts'),
file('auth.ts', 'src/services/auth.ts'),
file('authMiddleware.ts', 'src/middleware/authMiddleware.ts'),
file('package.json', 'package.json'),
file('README.md', 'README.md'),
file('config.ts', 'src/config.ts'),
file('database.ts', 'src/services/database.ts'),
file('router.ts', 'src/router.ts'),
file('types.ts', 'src/types.ts'),
];
describe('filterFileSuggestions', () => {
it('returns empty array for empty query', () => {
expect(filterFileSuggestions(FILES, '')).toEqual([]);
});
it('returns empty array for empty file list', () => {
expect(filterFileSuggestions([], 'test')).toEqual([]);
});
it('filters by file name', () => {
const results = filterFileSuggestions(FILES, 'test');
expect(results).toHaveLength(1);
expect(results[0].name).toBe('test.ts');
expect(results[0].type).toBe('file');
expect(results[0].filePath).toBe('/project/src/test.ts');
expect(results[0].relativePath).toBe('src/test.ts');
expect(results[0].insertText).toBe('src/test.ts');
});
it('filters by relative path', () => {
const results = filterFileSuggestions(FILES, 'middleware');
expect(results).toHaveLength(1);
expect(results[0].name).toBe('authMiddleware.ts');
});
it('is case-insensitive', () => {
const results = filterFileSuggestions(FILES, 'APP');
expect(results).toHaveLength(1);
expect(results[0].name).toBe('App.tsx');
});
it('returns multiple matches', () => {
const results = filterFileSuggestions(FILES, 'auth');
expect(results).toHaveLength(2);
expect(results.map((r) => r.name)).toEqual(['auth.ts', 'authMiddleware.ts']);
});
it('matches on name substring', () => {
const results = filterFileSuggestions(FILES, 'te');
// 'te' matches: test.ts, telemetry.ts, and router.ts (rou-te-r)
expect(results.map((r) => r.name)).toEqual(['test.ts', 'telemetry.ts', 'router.ts']);
});
it('limits results to 8', () => {
const results = filterFileSuggestions(FILES, 'ts');
expect(results.length).toBeLessThanOrEqual(8);
});
it('sets id with file: prefix', () => {
const results = filterFileSuggestions(FILES, 'config');
expect(results[0].id).toBe('file:/project/src/config.ts');
});
it('sets subtitle to relativePath', () => {
const results = filterFileSuggestions(FILES, 'config');
expect(results[0].subtitle).toBe('src/config.ts');
});
it('matches partial path segments', () => {
const results = filterFileSuggestions(FILES, 'services/');
expect(results).toHaveLength(2);
expect(results.map((r) => r.name)).toEqual(['auth.ts', 'database.ts']);
});
it('returns results in file list order', () => {
const results = filterFileSuggestions(FILES, '.ts');
expect(results[0].name).toBe('index.ts');
});
it('quotes inserted paths that contain spaces', () => {
expect(formatFileMentionPath('src/My Component/App.tsx')).toBe(
'"src/My Component/App.tsx"'
);
});
});