import { describe, it, expect } from 'vitest' import { processCitations } from '../../utils/citationParser' import type { SourceMetadata } from '../../types' const mockSources: SourceMetadata[] = [ { filename: 'NEC4 ACC.pdf', upload_date: '2024-01-15', content_summary: 'Summary', chunk_index: 0, page_number: 3, chunk_file_path: 'chunk_0.pdf', }, { filename: 'meeting_notes.docx', upload_date: '2024-01-16', content_summary: 'Minutes', chunk_index: 1, page_number: null, chunk_file_path: 'chunk_1.pdf', }, { filename: 'report.pdf', upload_date: '2024-01-17', content_summary: 'Report', chunk_index: 2, page_number: 5, chunk_file_path: 'chunk_2.pdf', }, ] describe('processCitations', () => { it('returns original text when no sources provided', () => { const text = 'This has [NEC4 ACC.pdf, page 3] citation.' expect(processCitations(text, [])).toBe(text) }) it('replaces matched citation with markdown link', () => { const text = 'Clause info [NEC4 ACC.pdf, page 3] is important.' const result = processCitations(text, mockSources) expect(result).toContain('](') expect(result).toContain('/pdf-viewer') expect(result).toMatch(/\[NEC4 ACC\.pdf, page 3\]\([^)]+\)/) }) it('handles filename-only citation for DOCX (no page)', () => { const text = 'Notes [meeting_notes.docx] from meeting.' const result = processCitations(text, mockSources) expect(result).toContain('/pdf-viewer') expect(result).toContain('meeting_notes.docx') }) it('leaves unmatched citations as plain text', () => { const text = 'Unknown source [unknown_file.pdf, page 10] here.' const result = processCitations(text, mockSources) expect(result).toBe(text) }) it('handles multiple citations in same text', () => { const text = 'A [NEC4 ACC.pdf, page 3] and B [report.pdf, page 5].' const result = processCitations(text, mockSources) const linkCount = (result.match(/\[.+?\]\(/g) || []).length expect(linkCount).toBe(2) }) it('does not break existing markdown links', () => { const text = 'See [label](http://example.com) and [NEC4 ACC.pdf, page 3].' const result = processCitations(text, mockSources) expect(result).toContain('[label](http://example.com)') expect(result).toContain('/pdf-viewer') }) it('does not break markdown images', () => { const text = '![diagram](http://example.com/img.png) and [NEC4 ACC.pdf, page 3].' const result = processCitations(text, mockSources) expect(result).toContain('![diagram]') expect(result).toContain('/pdf-viewer') }) it('matches case-insensitively', () => { const text = 'Cite [nec4 acc.pdf, page 3] lowercase.' const result = processCitations(text, mockSources) expect(result).toContain('/pdf-viewer') }) it('leaves plain bracket text without matching source', () => { const text = 'Some [plain bracket text] without source.' const result = processCitations(text, mockSources) expect(result).toBe(text) }) it('skips sources without chunk_file_path', () => { const sourcesWithoutPath = [ { filename: 'no_path.pdf', upload_date: '2024-01-18', content_summary: 'Summary', chunk_index: 0, page_number: 1, chunk_file_path: null, }, ] const text = 'Source [no_path.pdf, page 1] missing path.' const result = processCitations(text, sourcesWithoutPath) expect(result).toBe(text) }) })