import { describe, expect, it } from 'vitest'; import { extractFileReferences } from '@renderer/utils/groupTransformer'; describe('extractFileReferences', () => { describe('accepts valid file references', () => { it('accepts paths starting with known directories', () => { const refs = extractFileReferences('Check @src/components/App.tsx'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('src/components/App.tsx'); }); it('accepts utils/ paths (known dir)', () => { const refs = extractFileReferences('See @utils/helpers'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('utils/helpers'); }); it('accepts hooks/ paths (known dir)', () => { const refs = extractFileReferences('Use @hooks/auth'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('hooks/auth'); }); it('accepts lib/ paths (known dir)', () => { const refs = extractFileReferences('In @lib/core'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('lib/core'); }); it('accepts config/ paths (known dir)', () => { const refs = extractFileReferences('Edit @config/database'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('config/database'); }); it('accepts relative paths with ./', () => { const refs = extractFileReferences('See @./src/file.ts'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('./src/file.ts'); }); it('accepts relative paths with ../', () => { const refs = extractFileReferences('See @../lib/utils.ts'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('../lib/utils.ts'); }); it('accepts unknown-dir paths with 3+ segments', () => { const refs = extractFileReferences('See @custom/deep/path.ts'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('custom/deep/path.ts'); }); it('accepts unknown-dir paths with file extension', () => { const refs = extractFileReferences('See @foo/bar.ts'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('foo/bar.ts'); }); it('accepts node_modules paths (known dir)', () => { const refs = extractFileReferences('Check @node_modules/lodash/index.js'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('node_modules/lodash/index.js'); }); it('accepts types/ paths (known dir)', () => { const refs = extractFileReferences('See @types/node'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('types/node'); }); it('extracts multiple references from one string', () => { const refs = extractFileReferences('Compare @src/a.ts and @lib/b.ts'); expect(refs).toHaveLength(2); expect(refs[0].path).toBe('src/a.ts'); expect(refs[1].path).toBe('lib/b.ts'); }); it('accepts tilde paths', () => { const refs = extractFileReferences('See @~/projects/foo'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('~/projects/foo'); }); it('accepts Windows backslash paths', () => { const refs = extractFileReferences('Check @src\\components\\App.tsx'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('src\\components\\App.tsx'); }); it('accepts Windows drive absolute paths', () => { const refs = extractFileReferences('Open @C:\\Users\\Alice\\project\\src\\App.tsx'); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('C:\\Users\\Alice\\project\\src\\App.tsx'); }); it('accepts quoted paths with spaces', () => { const refs = extractFileReferences('Open @"src/My Component/App.tsx" now'); expect(refs).toHaveLength(1); expect(refs[0]).toEqual({ path: 'src/My Component/App.tsx', raw: '@"src/My Component/App.tsx"', }); }); }); describe('rejects npm scoped packages', () => { it('rejects @babel/core', () => { const refs = extractFileReferences('Use @babel/core for transpilation'); expect(refs).toHaveLength(0); }); it('rejects @xyflow/react', () => { const refs = extractFileReferences('Install @xyflow/react'); expect(refs).toHaveLength(0); }); it('rejects @dagrejs/dagre', () => { const refs = extractFileReferences('Use @dagrejs/dagre for layout'); expect(refs).toHaveLength(0); }); it('rejects @testing-library/react', () => { const refs = extractFileReferences('Import from @testing-library/react'); expect(refs).toHaveLength(0); }); it('rejects @vitest/ui', () => { const refs = extractFileReferences('Run @vitest/ui'); expect(refs).toHaveLength(0); }); it('rejects @radix-ui/react-dialog', () => { const refs = extractFileReferences('Use @radix-ui/react-dialog'); expect(refs).toHaveLength(0); }); it('rejects @codemirror/lang-javascript', () => { const refs = extractFileReferences('Import @codemirror/lang-javascript'); expect(refs).toHaveLength(0); }); it('rejects @emotion/react', () => { const refs = extractFileReferences('Style with @emotion/react'); expect(refs).toHaveLength(0); }); }); describe('edge cases', () => { it('returns empty for text without @', () => { const refs = extractFileReferences('No mentions here'); expect(refs).toHaveLength(0); }); it('returns empty for empty string', () => { const refs = extractFileReferences(''); expect(refs).toHaveLength(0); }); it('rejects bare @ mentions without path separator', () => { const refs = extractFileReferences('Hello @user'); expect(refs).toHaveLength(0); }); it('handles mixed valid and invalid refs', () => { const refs = extractFileReferences( 'Use @babel/core and check @src/components/App.tsx' ); expect(refs).toHaveLength(1); expect(refs[0].path).toBe('src/components/App.tsx'); }); it('rejects single-segment bare names', () => { const refs = extractFileReferences('@react is a library'); expect(refs).toHaveLength(0); }); }); });